Wasatch.NET 2.4.14
.NET application driver for Wasatch Photonics spectrometers
Loading...
Searching...
No Matches
Public Member Functions | Static Public Member Functions | Public Attributes | Properties | Private Member Functions | Private Attributes | Static Private Attributes | List of all members
WasatchNET.MultiChannelWrapper Class Reference

WasatchNET has always provided support to control multiple spectrometers in parallel. However, such control is inherently manual, with all operations and timing left to the user. Other than basic synchronization of the USB bus, no automated timing or coordination is provided across multiple devices. More...

Collaboration diagram for WasatchNET.MultiChannelWrapper:
[legend]

Public Member Functions

void clearDark ()
 Clears the stored dark from all spectrometers.
 
void clearReference ()
 Clears the stored reference from all spectrometers.
 
void close ()
 Release all resources at end of application session.
 
async Task< List< ChannelSpectrum > > getSpectraAsync (bool sendTrigger=true)
 Get one spectrum from each populated position.
 
Spectrometer getSpectrometer (int pos)
 Get a handle to the spectrometer at a given position.
 
async Task< ChannelSpectrumgetSpectrumAsync (int pos, bool computeTimeout=true)
 Get a spectrum from one spectrometer. Does not call startAcquisition, and assumes that EITHER a hardware trigger has been initiated through other means, or that the system is in software triggered mode.
 
async Task< bool > openAsync ()
 Initialize all connected spectrometers, determine positions in the multi-channel system, find out which are configured to provide the external hardware trigger and fan control, etc.
 
List< int > positionsByIntegTime ()
 Provide a list of spectrometer positions, sorted in ascending order by integration time.
 
async Task< bool > sendHWTrigger ()
 Sends a hardware trigger using the configured spectrometer.
 
bool setIntegrationTimeMS (uint ms)
 A convenience method to set all spectrometers in the system to a common integration time.
 
async Task< bool > startAcquisitionAsync ()
 Use the configured spectrometer's laserEnable output to raise a brief HW trigger pulse.
 
async Task< List< ChannelSpectrum > > takeDarkAsync (bool sendTrigger=true)
 Takes a spectrum from each spectrometer, and stores it internally as a new dark reference.
 
async Task< List< ChannelSpectrum > > takeReferenceAsync (bool sendTrigger=true)
 Takes a spectrum from each spectrometer, and stores it internally as a new reference.
 

Static Public Member Functions

static MultiChannelWrapper getInstance ()
 Get a handle to the MultiChannelWrapper singleton.
 

Public Attributes

Driver driver = Driver.getInstance()
 
X_AXIS_TYPE xAxisType = X_AXIS_TYPE.WAVELENGTH
 Configure how the whole system will report spectra, so it doesn't vary per-channel.
 

Properties

bool fanEnabled [get, set]
 Use the configured spectrometer's laserEnable output to turn the system fans on or off.
 
int fanPos [get]
 The position of the spectrometer configured to control the fans.
 
bool hardwareTriggeringEnabled [get, set]
 Whether external hardware triggering is currently enabled.
 
bool integrationThrowaways [get, set]
 Whether spectrometers in the system should automatically take "throwaway" acquisitions after changing integration time (default).
 
SortedSet< int > positions = new SortedSet<int>() [get, private set]
 A convenience accessor to iterate over populated positions (channels).
 
bool reflectanceEnabled [get, set]
 If enabled, and if reference measurements have been recorded, calls to getSpectraAsync() and getSpectrumAsync() will populate ChannelSpectrum intensities with computed reflectance rather than raw counts.
 
uint scanAveraging [get, set]
 At the moment, we have no validated way to encapsulate scan averaging within WasatchNET when using HW drivers. Therefore, we're doing the looping and averaging in getSpectraAsync, controlled by this field.
 
int spectrometerCount [get]
 How many spectrometers (channels) were found.
 
int triggerPos [get]
 The position of the spectrometer configured to generate the external hardware trigger.
 
int triggerPulseWidthMS = 5 [get, set]
 How long the triggerSpec's laserEnable signal is raised high to generate an acquisition trigger to all spectrometers.
 

Private Member Functions

void computeReflectance (Spectrometer spec, ChannelSpectrum cs)
 It is assumed that either the sample and the reference were both dark-corrected, or that neither were.
 
 MultiChannelWrapper ()
 
void reset ()
 

Private Attributes

bool _hardwareTriggeringEnabled
 
bool _integrationThrowaways = true
 
uint _scanAveraging = 1
 
DateTime? lastTriggerSent = null
 
Logger logger = Logger.getInstance()
 
SortedDictionary< int, SpectrometerspecByPos
 
Spectrometer specFan = null
 
Spectrometer specTrigger = null
 

Static Private Attributes

static MultiChannelWrapper instance = null
 
static object mut = new object()
 

Detailed Description

WasatchNET has always provided support to control multiple spectrometers in parallel. However, such control is inherently manual, with all operations and timing left to the user. Other than basic synchronization of the USB bus, no automated timing or coordination is provided across multiple devices.

MultiChannelWrapper simplifies operation for customers creating multi-channel spectroscopic applications in which multiple spectrometers are expected to start acquisitions at the same time (simultaneous measurements of a single event), typically via hardware triggering.

As WP-OEM spectrometers do not currently expose an accessory connector with an obvious GPIO output useable as a trigger source, and as the immediate customer is using non-laser models excited via external light sources, the laserEnable output is being used as a quasi-GPIO, both for raising trigger signals and to control connected fans.

Constructor & Destructor Documentation

◆ MultiChannelWrapper()

WasatchNET.MultiChannelWrapper.MultiChannelWrapper ( )
inlineprivate

Member Function Documentation

◆ clearDark()

void WasatchNET.MultiChannelWrapper.clearDark ( )
inline

Clears the stored dark from all spectrometers.

◆ clearReference()

void WasatchNET.MultiChannelWrapper.clearReference ( )
inline

Clears the stored reference from all spectrometers.

◆ close()

void WasatchNET.MultiChannelWrapper.close ( )
inline

Release all resources at end of application session.

◆ computeReflectance()

void WasatchNET.MultiChannelWrapper.computeReflectance ( Spectrometer  spec,
ChannelSpectrum  cs 
)
inlineprivate

It is assumed that either the sample and the reference were both dark-corrected, or that neither were.

◆ getInstance()

static MultiChannelWrapper WasatchNET.MultiChannelWrapper.getInstance ( )
inlinestatic

Get a handle to the MultiChannelWrapper singleton.

◆ getSpectraAsync()

async Task< List< ChannelSpectrum > > WasatchNET.MultiChannelWrapper.getSpectraAsync ( bool  sendTrigger = true)
inline

Get one spectrum from each populated position.

This reads one spectrum from each selected device. The reads are performed in serial for two reasons. First, the USB bus is itself physically serial, so truely parallel communication isn't technically possible. Secondly, the attempt to interleave spectral packets over bulk endpoints, and the randomized inter-packet timing and buffer sizing thus introduced, has been observed to confuse some hardware USB controllers.

Sorting by integration time should ensure total read-out time is close to minimal.

At present, all scan averaging (SW and HW triggered) is provided by this method.

Parameters
sendTriggerwhether to automatically send trigger(s) to start the acquisitions
Returns
a list of ChannelSpectra in position order

◆ getSpectrometer()

Spectrometer WasatchNET.MultiChannelWrapper.getSpectrometer ( int  pos)
inline

Get a handle to the spectrometer at a given position.

Parameters
posWhich channel

◆ getSpectrumAsync()

async Task< ChannelSpectrum > WasatchNET.MultiChannelWrapper.getSpectrumAsync ( int  pos,
bool  computeTimeout = true 
)
inline

Get a spectrum from one spectrometer. Does not call startAcquisition, and assumes that EITHER a hardware trigger has been initiated through other means, or that the system is in software triggered mode.

The intended use-case for "computeTimeout" is to ensure that, when the higher-level getSpectraAsync() is called and generates a startAcquisition trigger, we don't completely throw the notion of "reasonable timeouts" out the window. That is, even though getSpectraAsync is reading spectra in serial, and therefore spectrometers further down the line will actually have had more time to complete their acquisitions, we don't unfairly "penalize" earlier spectrometers by holding them to strict timeouts, to which later spectrometers avoided by happenstance. Partially this is so we can continue to use timeouts as for their intended purpose, to correctly alert the user and system developers when a component is failing to meet specified tolerances and expectations of performance. Timeouts are there to tell us when a component is failing to perform as intended, and we don't want to lose that important feedback even when measurements are artificially slowed by having to read back spectra from numerous devices.

Parameters
posspectrometer position to acquire
computeTimeoutcompute a dynamic timeout from lastTriggerSent

◆ openAsync()

async Task< bool > WasatchNET.MultiChannelWrapper.openAsync ( )
inline

Initialize all connected spectrometers, determine positions in the multi-channel system, find out which are configured to provide the external hardware trigger and fan control, etc.

<todo> could provide some "filter" options to let the caller determine which connected spectrometers should be considered valid channels, e.g. regex patterns for model, serial and/or userData. </todo>

Returns
true on success

◆ positionsByIntegTime()

List< int > WasatchNET.MultiChannelWrapper.positionsByIntegTime ( )
inline

Provide a list of spectrometer positions, sorted in ascending order by integration time.

This is provided so that operations which sequentially iterate over each spectrometer as they complete an acquisition can complete in the shortest possible time, without actually being parallel.

◆ reset()

void WasatchNET.MultiChannelWrapper.reset ( )
inlineprivate

◆ sendHWTrigger()

async Task< bool > WasatchNET.MultiChannelWrapper.sendHWTrigger ( )
inline

Sends a hardware trigger using the configured spectrometer.

Returns
true on success

◆ setIntegrationTimeMS()

bool WasatchNET.MultiChannelWrapper.setIntegrationTimeMS ( uint  ms)
inline

A convenience method to set all spectrometers in the system to a common integration time.

Would run faster if parallized with Tasks and WaitAll, but reducing opportunities for bus collisions.

Parameters
msdesired integration time
Returns
true on success

◆ startAcquisitionAsync()

async Task< bool > WasatchNET.MultiChannelWrapper.startAcquisitionAsync ( )
inline

Use the configured spectrometer's laserEnable output to raise a brief HW trigger pulse.

◆ takeDarkAsync()

async Task< List< ChannelSpectrum > > WasatchNET.MultiChannelWrapper.takeDarkAsync ( bool  sendTrigger = true)
inline

Takes a spectrum from each spectrometer, and stores it internally as a new dark reference.

Subsequent calls to getSpectra will be automatically dark-corrected, until clearDark() is called.

Returns
The darks collected (also stored in Spectrometer)

◆ takeReferenceAsync()

async Task< List< ChannelSpectrum > > WasatchNET.MultiChannelWrapper.takeReferenceAsync ( bool  sendTrigger = true)
inline

Takes a spectrum from each spectrometer, and stores it internally as a new reference.

Returns
The collected references (also stored in Spectrometer)

Member Data Documentation

◆ _hardwareTriggeringEnabled

bool WasatchNET.MultiChannelWrapper._hardwareTriggeringEnabled
private

◆ _integrationThrowaways

bool WasatchNET.MultiChannelWrapper._integrationThrowaways = true
private

◆ _scanAveraging

uint WasatchNET.MultiChannelWrapper._scanAveraging = 1
private

◆ driver

Driver WasatchNET.MultiChannelWrapper.driver = Driver.getInstance()

◆ instance

MultiChannelWrapper WasatchNET.MultiChannelWrapper.instance = null
staticprivate

◆ lastTriggerSent

DateTime? WasatchNET.MultiChannelWrapper.lastTriggerSent = null
private

◆ logger

Logger WasatchNET.MultiChannelWrapper.logger = Logger.getInstance()
private

◆ mut

object WasatchNET.MultiChannelWrapper.mut = new object()
staticprivate

◆ specByPos

SortedDictionary<int, Spectrometer> WasatchNET.MultiChannelWrapper.specByPos
private

◆ specFan

Spectrometer WasatchNET.MultiChannelWrapper.specFan = null
private

◆ specTrigger

Spectrometer WasatchNET.MultiChannelWrapper.specTrigger = null
private

◆ xAxisType

X_AXIS_TYPE WasatchNET.MultiChannelWrapper.xAxisType = X_AXIS_TYPE.WAVELENGTH

Configure how the whole system will report spectra, so it doesn't vary per-channel.

Property Documentation

◆ fanEnabled

bool WasatchNET.MultiChannelWrapper.fanEnabled
getset

Use the configured spectrometer's laserEnable output to turn the system fans on or off.

Obviously dangerous on spectrometers with physical lasers.

<todo>move to GPIO</todo>

◆ fanPos

int WasatchNET.MultiChannelWrapper.fanPos
get

The position of the spectrometer configured to control the fans.

Returns
-1 if none found

◆ hardwareTriggeringEnabled

bool WasatchNET.MultiChannelWrapper.hardwareTriggeringEnabled
getset

Whether external hardware triggering is currently enabled.

Note that with ARM spectrometers, whether hardware triggering is enabled or not may seem somewhat philosophical; after all, ARM spectrometers ALWAYS respond to both HW and SW triggers, so this value doesn't actually change anything inside the spectrometer.

What it does is change behavior within WasatchNET, as normally a spectrometer with TriggerSource.INTERNAL (and autoTrigger) will automatically generate a SW /trigger whenever Spectrometer.getSpectrum() is called. Likewise, timeout behavior changes depending on this setting, as a HW-triggered acquisition should generally continue to block until a HW trigger is detected.

Of course, things are actually more complicated than that, as Spectrometer.autoTrigger also feeds into whether the spectrometer will internally generate a SW trigger, and there are various explicit timeouts which can be applied even to HW-triggered spectrometers.

◆ integrationThrowaways

bool WasatchNET.MultiChannelWrapper.integrationThrowaways
getset

Whether spectrometers in the system should automatically take "throwaway" acquisitions after changing integration time (default).

◆ positions

SortedSet<int> WasatchNET.MultiChannelWrapper.positions = new SortedSet<int>()
getprivate set

A convenience accessor to iterate over populated positions (channels).

◆ reflectanceEnabled

bool WasatchNET.MultiChannelWrapper.reflectanceEnabled
getset

If enabled, and if reference measurements have been recorded, calls to getSpectraAsync() and getSpectrumAsync() will populate ChannelSpectrum intensities with computed reflectance rather than raw counts.

◆ scanAveraging

uint WasatchNET.MultiChannelWrapper.scanAveraging
getset

At the moment, we have no validated way to encapsulate scan averaging within WasatchNET when using HW drivers. Therefore, we're doing the looping and averaging in getSpectraAsync, controlled by this field.

◆ spectrometerCount

int WasatchNET.MultiChannelWrapper.spectrometerCount
get

How many spectrometers (channels) were found.

◆ triggerPos

int WasatchNET.MultiChannelWrapper.triggerPos
get

The position of the spectrometer configured to generate the external hardware trigger.

Returns
-1 if none found

◆ triggerPulseWidthMS

int WasatchNET.MultiChannelWrapper.triggerPulseWidthMS = 5
getset

How long the triggerSpec's laserEnable signal is raised high to generate an acquisition trigger to all spectrometers.

Per Nicolas Baron, minimum width is probably 1ms. Note that CSharp timing is not particularly precise, so the value entered here is really more of a floor (even values of zero may work).


The documentation for this class was generated from the following file: