|
Wasatch.PY
Python application driver for Wasatch Photonics spectrometers
|
Public Member Functions | |
| __init__ (self, device_id, message_queue=None, alert_queue=None) | |
| acquire_area_scan (self) | |
| acquire_data (self) | |
| acquire_spectrum (self) | |
| Generate one Reading from the spectrometer, including one optionally-averaged spectrum, device temperatures and other hardware status. | |
| acquire_spectrum_auto_raman (self) | |
| acquire_spectrum_standard (self) | |
| balance_acquisition (self, device=None, mode=None, intensity=45000, threshold=2500, pixel=None, max_integration_time_ms=5000, max_tries=20) | |
| connect (self) | |
| connect_feature_identification (self) | |
| Given a specified universal identifier, attempt to connect to the device using FID protocol. | |
| disconnect (self) | |
| handle_requests (self, requests) | |
| initialize_settings (self) | |
| monitor_memory (self) | |
| perform_optional_throwaways (self) | |
| It's unclear how many throwaways are really needed for a stable Raman spectrum, and whether they're really based on number of integrations (sensor stabilization) or time (laser warmup); I suspect both. | |
| set_take_one_request (self, tor) | |
| take_one_averaged_reading (self, label=None) | |
Public Member Functions inherited from wasatch.InterfaceDevice.InterfaceDevice | |
| __init__ (self, device_id, message_queue=None, alert_queue=None) | |
| change_setting (self, setting, value=None) | |
| Request Handlers. | |
| check_alert (self, name) | |
| handle_cmd (self, cmd, arg=None, force=False) | |
| handle_request (self, request) | |
| queue_message (self, setting, value) | |
| Outgoing Sideline Messages. | |
Public Attributes | |
| auto_raman = None | |
| bool | connected = False |
| hardware = None | |
| bool | immediate_mode = False |
| int | last_battery_percentage = 0 |
| last_complete_acquisition = None | |
| last_memory_check = datetime.now() | |
| late_arriving_timestamp = datetime.now() | |
| process_id = os.getpid() | |
| int | session_reading_count = 0 |
| settings = SpectrometerSettings() | |
| int | sum_count = 0 |
| Aggregate scan averaging. | |
| list | summed_spectra = None |
| Aggregate scan averaging. | |
| take_one_request = None | |
Public Attributes inherited from wasatch.InterfaceDevice.InterfaceDevice | |
| alert_queue = alert_queue | |
| device_id = device_id | |
| message_queue = message_queue | |
| dict | process_f = {} |
| int | remaining_throwaways = 0 |
Protected Member Functions | |
| _init_process_funcs (self) | |
Protected Member Functions inherited from wasatch.InterfaceDevice.InterfaceDevice | |
| _refresh_alerts (self) | |
| Incoming Sideline Messages. | |
Additional Inherited Members | |
Protected Attributes inherited from wasatch.InterfaceDevice.InterfaceDevice | |
| _alerts = set() | |
| dict | _supported_message_queue_types |
This is the top-level interface for controlling and communicating with Wasatch Photonics USB 2.0 spectrometers using the FeatureInterfaceDevice (FID) protocol as defined in ENG-0001. This class is essentially a clunky wrapper over FeatureInterfaceDevice, providing the higher-level InterfaceDevice interface making WasatchDevice a peer to AndorDevice, OceanDevice, TCPDevice, IDSDevice etc. Several points will stand out: 1. WasatchDevice should probably be renamed to FIDDevice, because Wasatch Photonics is also the manufacturer for spectrometers using AndorDevice, TCPDevice, IDSDevice etc. 2. This class could be simplified considerably, and possibly merged with FeatureIdentificationDevice. ENLIGHTEN does not instantiate WasatchDevices directly, but instead uses a WasatchDeviceWrapper to access a single WasatchDevice in a dedicated child thread. Other users of Wasatch.PY may of course instantiate a WasatchDevice directly, or go straight to a FeatureInterfaceDevice. Main things this "wrapper" provides: - Implements software-based, library-based scan averaging (badly). Although ARM-based spectrometers can do this in firmware, FX2-based models have to do it in software. Between Wasatch.PY and ENLIGHTEN, it seems preferable to have this in the Wasatch.PY library, where it is easier for customers to use. - Distinguishes between normal, averaged, AutoRaman and AreaScan acquisitions. @par History This class exists because it used to wrap TWO types of spectrometers: - FeatureIdentificationDevices, which follow the ENG-0001 API and have EEPROMs - StrokerDevices, which didn't have EEPROMs and didn't obey ENG-0001. I don't know if we even have an ENG document specifying the protocol that the old Stroker electronics used. It was already deprecated when I started, and was rapidly removed from Wasatch.PY...which kind of makes this class superfluous :-( This class (and all who inherit InterfaceDevice) use a somewhat clunky data-passing and error-handling mechanism via SpectrometerRequest and SpectrometerResponse objects. That should probably be streamlined. Part of the legacy "ugly-isms" in this class date back to when ENLIGHTEN was multi-process rather than multi-threaded, and all data flows between Wasatch.PY and ENLIGHTEN were via pickled queues :-(
| wasatch.WasatchDevice.WasatchDevice.__init__ | ( | self, | |
| device_id, | |||
| message_queue = None, | |||
| alert_queue = None ) |
@param device_id a DeviceID instance OR string label thereof
|
protected |
| wasatch.WasatchDevice.WasatchDevice.acquire_area_scan | ( | self | ) |
FeatureIdentificationDevice.get_area_scan returns a Reading because it really needs to return both the AreaScanImage (whatever form that may take) and the vertically-binned spectrum (for live display). @returns a SpectrometerResponse(data=Reading)
| wasatch.WasatchDevice.WasatchDevice.acquire_data | ( | self | ) |
Process all enqueued settings, then generate one Reading from the device (a spectrum, followed by all the miscellaneous device metadata like temperatures, battery and laser state). ENLIGHTEN calls this function in a continuous loop in a Thread via WrapperWorker.run(). The most important thing is at the end of the function, it calls acquire_spectrum() which generates the actual Reading. @see Controller.acquire_reading
| wasatch.WasatchDevice.WasatchDevice.acquire_spectrum | ( | self | ) |
Generate one Reading from the spectrometer, including one optionally-averaged spectrum, device temperatures and other hardware status.
This is normally called by self.acquire_data when that function decides it is time to perform an acquisition.
IF the driver is in free-running mode, AND performing scan averaging, THEN scan averaging is NOT encapsulated within a single call to this function. Instead, we let ths spectrometer run in free-running mode, collecting individual spectra as per normal, and returning each "partial" readings while "building up" to the final averaged measurement.
That is to say, if scan averaging is set to 10, then this function will get called 10 times, as ticked by the regular free-running timers, before the fully averaged spectrum is returned. A total of 10 (not 11) spectra will be generated and sent upstream: the first 9 "partial" (unaveraged) reads, and the final 10th spectrum which will contain the average of all 10 measurements.
This gives the user-facing GUI an opportunity to update the "collected X-of-Y" readout on-screen, and potentially even graph the traces of in-process partial readings.
HOWEVER, this doesn't make as much sense if we're not in free-running mode, i.e. the subprocess has been slaved to explicit control by the Controller (likely a feature object like BatchCollection), and is collecting exactly those measurements we're being commanded, as they're commanded.
THEREFORE, if the driver IS NOT in free-running mode, then we ONLY return the final averaged spectrum as one atomic operation.
In this case, if scan averaging is set to 10, then A SINGLE CALL to this function will "block" while the full 10 measurements are made, and then a single, fully-averaged spectrum will be returned upstream.
@returns a SpectrometerResponse(data=Reading)
| wasatch.WasatchDevice.WasatchDevice.acquire_spectrum_auto_raman | ( | self | ) |
@returns a SpectrometerResponse(data=Reading)
@todo fold-in a lot of the post-reading sensor measurements
(temperature, interlock etc) provided by acquire_spectrum_standard
| wasatch.WasatchDevice.WasatchDevice.acquire_spectrum_standard | ( | self | ) |
@returns a SpectrometerResponse(data=Reading)
| wasatch.WasatchDevice.WasatchDevice.balance_acquisition | ( | self, | |
| device = None, | |||
| mode = None, | |||
| intensity = 45000, | |||
| threshold = 2500, | |||
| pixel = None, | |||
| max_integration_time_ms = 5000, | |||
| max_tries = 20 ) |
| wasatch.WasatchDevice.WasatchDevice.connect | ( | self | ) |
Attempt low level connection to the specified DeviceID
| wasatch.WasatchDevice.WasatchDevice.connect_feature_identification | ( | self | ) |
Given a specified universal identifier, attempt to connect to the device using FID protocol.
| wasatch.WasatchDevice.WasatchDevice.disconnect | ( | self | ) |
| wasatch.WasatchDevice.WasatchDevice.handle_requests | ( | self, | |
| requests ) |
Override handle_requests to distinguish between requests handled by WasatchDevice (CONNECT, ACQUIRE, DISCONNECT) and FeatureInterfaceDevice (everything else).
Reimplemented from wasatch.InterfaceDevice.InterfaceDevice.
| wasatch.WasatchDevice.WasatchDevice.initialize_settings | ( | self | ) |
| wasatch.WasatchDevice.WasatchDevice.monitor_memory | ( | self | ) |
| wasatch.WasatchDevice.WasatchDevice.perform_optional_throwaways | ( | self | ) |
It's unclear how many throwaways are really needed for a stable Raman spectrum, and whether they're really based on number of integrations (sensor stabilization) or time (laser warmup); I suspect both.
Also note the potential need for sensor warm-up, but I think that's handled inside FW.
Optimal would probably be something like "As many integrations as it takes to span 2sec, but not fewer than two."
These are NOT the same throwaways added to smooth spectra over changes to integration time and gain. These are separate throwaways potentially required when waking the sensor from sleep. However, I'm using the same mechanism for tracking (self.remaining_throwaways) for commonality.
| wasatch.WasatchDevice.WasatchDevice.set_take_one_request | ( | self, | |
| tor ) |
This is in WasatchDevice rather than FID because it's WasatchDevice that decides how to handle each acquire_data.
| wasatch.WasatchDevice.WasatchDevice.take_one_averaged_reading | ( | self, | |
| label = None ) |
Okay, let's talk about averaging. We only perform averaging as a blocking call in Wasatch.PY when given a TakeOneRequest with non-zero scans_to_average, typically from ENLIGHTEN's TakeOneFeature (perhaps via VCRControls.step, possibly with AutoRaman, or BatchCollection). Otherwise, normally this thread (the background thread owned by a WasatchDeviceWrapper object and running WrapperWorker.run) is in "free- running" mode, taking spectra just as fast as it can in an endless loop, feeding them back to the consumer (ENLIGHTEN) over a queue. To keep that pipeline "moving," generally we don't do heavy blocking operations down here in the background thread. The new AutoRaman feature makes increased using of scan averaging, and again kind of wants to be encapsulated down here. And by implication, all TakeOneRequests should really support encapsulated, atomic averaging. So the current design is that ATOMIC scans_to_average comes from TakeOneRequest, ENLIGHTEN-based averaging in SpectrometerState. @returns Reading on success, true or false on "stop processing" conditions
| wasatch.WasatchDevice.WasatchDevice.auto_raman = None |
| bool wasatch.WasatchDevice.WasatchDevice.connected = False |
| wasatch.WasatchDevice.WasatchDevice.hardware = None |
| bool wasatch.WasatchDevice.WasatchDevice.immediate_mode = False |
| int wasatch.WasatchDevice.WasatchDevice.last_battery_percentage = 0 |
| wasatch.WasatchDevice.WasatchDevice.last_complete_acquisition = None |
| wasatch.WasatchDevice.WasatchDevice.last_memory_check = datetime.now() |
| wasatch.WasatchDevice.WasatchDevice.late_arriving_timestamp = datetime.now() |
| wasatch.WasatchDevice.WasatchDevice.process_id = os.getpid() |
| int wasatch.WasatchDevice.WasatchDevice.session_reading_count = 0 |
| wasatch.WasatchDevice.WasatchDevice.settings = SpectrometerSettings() |
| int wasatch.WasatchDevice.WasatchDevice.sum_count = 0 |
Aggregate scan averaging.
| wasatch.WasatchDevice.WasatchDevice.summed_spectra = None |
Aggregate scan averaging.
| wasatch.WasatchDevice.WasatchDevice.take_one_request = None |