wasatch 2.2.13__tar.gz → 2.2.15__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {wasatch-2.2.13 → wasatch-2.2.15}/CHANGELOG.md +24 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/PKG-INFO +1 -1
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/AndorDevice.py +110 -54
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/AutoRamanRequest.py +20 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/DeviceFinderUSB.py +3 -3
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/EEPROM.py +256 -162
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/FeatureIdentificationDevice.py +34 -17
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/ROI.py +12 -4
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/SPIDevice.py +2 -2
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/SpectrometerSettings.py +64 -207
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/SpectrometerState.py +1 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/WasatchBus.py +2 -2
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/__init__.py +1 -1
- {wasatch-2.2.13 → wasatch-2.2.15}/.example_pypirc +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/.flake8 +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/.gitignore +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/.pylintrc +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/Doxyfile +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/LICENSE +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/MANIFEST.in +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/Makefile +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/README.md +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/README_PYPI.md +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/README_RPI.md +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/README_SETTINGS.md +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/README_VIRTUAL.md +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/WasatchShell/README.md +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/WasatchShell/laser-test.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/WasatchShell/load-test.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/WasatchShell/one-shot.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/WasatchShell/wasatch-shell.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/demo-auto-raman.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/demo-simple.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/demo-virtual.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/demo-workflow.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/demo.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/environments/conda-linux.yml +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/environments/conda-macos.yml +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/environments/conda-rpi.yml +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/environments/conda-win10.yml +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/pyproject.toml +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/requirements.txt +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/screenshots/multiplatform.png +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/scripts/dark-vs-integ.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/scripts/deploy +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/setup.cfg +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/testSpectrometers/SiG_785/eeprom/EEPROM-EM-9c65d19f4c.json +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/testSpectrometers/SiG_785/readings/enlighten-20210329-094722-799520-WP-00686.csv +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/testSpectrometers/SiG_785/readings/enlighten-20210329-094755-288958-WP-00686.csv +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/testSpectrometers/WP-00887/eeprom/WP-00887-mock.json +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/testSpectrometers/WP-00904/eeprom/WP-00904-mock.json +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/testSpectrometers/WP-00913/eeprom/WP-00913-mock.json +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/tests/existing_implementations.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/tests/noise-by-setpoint.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/tests/queues.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/tests/unit.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/udev/10-wasatch.rules +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/AbstractUSBDevice.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/AutoRaman.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/BLEDevice.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/BalanceAcquisition.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/CSVLoader.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/CommandSettings.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/ControlObject.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/DetectorROI.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/DetectorRegions.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/DeviceID.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/EEPROM_multical.sav +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/FPGAOptions.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/FirmwareRequirements.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/HardwareInfo.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/IMX385.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/InterfaceDevice.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/MockUSBDevice.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/OceanDevice.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/PollStatus.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/ProcessedReading.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/Reading.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/RealUSBDevice.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/SpectrometerRequest.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/SpectrometerResponse.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/StatusMessage.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/TakeOneRequest.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/WasatchDevice.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/WasatchDeviceWrapper.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/WrapperWorker.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/applog.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/simulation_protocol.py +0 -0
- {wasatch-2.2.13 → wasatch-2.2.15}/wasatch/utils.py +0 -0
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
- 2024-12-06 2.2.15
|
|
4
|
+
- XL Fixes
|
|
5
|
+
- fixed scan averaging
|
|
6
|
+
- fixed detector temperature readout
|
|
7
|
+
- support vertical ROI (successfully tested (118, 138) ie (center 128, height 20))
|
|
8
|
+
- flag non-downloaded Andor EEPROMs as 'stubbed'
|
|
9
|
+
- EEPROM
|
|
10
|
+
- allow MultiWavelengthCalibration.set to support any EEPROM property
|
|
11
|
+
- fixed Raman Intensity Calibration (broke in 2.2.14)
|
|
12
|
+
- raman_intensity_calibration_order (standardized on 5th-order polynomial)
|
|
13
|
+
- force integral ROI
|
|
14
|
+
- 2024-11-25 2.2.14
|
|
15
|
+
- EEPROM
|
|
16
|
+
- added subformat 5 (EEPROM.MultiWavelengthCalibration)
|
|
17
|
+
- bumped rev to 17
|
|
18
|
+
- deprecated subformat 4 (regions)
|
|
19
|
+
- SpectrometerSettings
|
|
20
|
+
- deprecated integer excitation_nm
|
|
21
|
+
- moved vertical ROI to wasatch.ROI
|
|
22
|
+
- added calibrations(), select_calibration(n)
|
|
23
|
+
- FeatureInterfaceDevice
|
|
24
|
+
- added set/get_scans_to_average
|
|
25
|
+
- SpectrometerState
|
|
26
|
+
- added onboard_averaging
|
|
3
27
|
- 2024-11-01 2.2.13
|
|
4
28
|
- support for InGaAs linear pixel calibration
|
|
5
29
|
- re-enable STM32 serial number
|
|
@@ -11,6 +11,7 @@ from .SpectrometerResponse import SpectrometerResponse
|
|
|
11
11
|
from .InterfaceDevice import InterfaceDevice
|
|
12
12
|
from .DeviceID import DeviceID
|
|
13
13
|
from .Reading import Reading
|
|
14
|
+
from .ROI import ROI
|
|
14
15
|
|
|
15
16
|
log = logging.getLogger(__name__)
|
|
16
17
|
|
|
@@ -47,7 +48,7 @@ class AndorDevice(InterfaceDevice):
|
|
|
47
48
|
SUCCESS = 20002 #!< see load_error_codes()
|
|
48
49
|
SHUTTER_SPEED_MS = 50 #!< allow time for mechanical shutter to stabilize
|
|
49
50
|
|
|
50
|
-
def __init__(self, device_id, message_queue=None, alert_queue=None):
|
|
51
|
+
def __init__(self, device_id, message_queue=None, alert_queue=None):
|
|
51
52
|
# if passed a string representation of a DeviceID, deserialize it
|
|
52
53
|
super().__init__()
|
|
53
54
|
if type(device_id) is str:
|
|
@@ -67,6 +68,8 @@ class AndorDevice(InterfaceDevice):
|
|
|
67
68
|
|
|
68
69
|
self.immediate_mode = False
|
|
69
70
|
|
|
71
|
+
# An AndorDevice has-a SpectrometerSettings, which has-a EEPROM, which
|
|
72
|
+
# has-a MultiWavelengthCalibration
|
|
70
73
|
self.settings = SpectrometerSettings(self.device_id)
|
|
71
74
|
self.summed_spectra = None
|
|
72
75
|
self.sum_count = 0
|
|
@@ -125,51 +128,55 @@ class AndorDevice(InterfaceDevice):
|
|
|
125
128
|
# set Andor defaults for important "EEPROM" settings
|
|
126
129
|
# (all but has_cooling can be overridden via config file)
|
|
127
130
|
|
|
128
|
-
# Andor API doesn't have access to detector info
|
|
129
|
-
# Note that we use non-iDus cameras, including the Newton
|
|
131
|
+
# Andor API doesn't(?) seem to have access to detector info.
|
|
132
|
+
# Note that we use non-iDus cameras, including the Newton.
|
|
130
133
|
self.settings.eeprom.detector = "iDus"
|
|
131
|
-
|
|
134
|
+
|
|
132
135
|
self.settings.eeprom.has_cooling = True
|
|
133
136
|
self.settings.eeprom.startup_integration_time_ms = 10
|
|
134
137
|
self.settings.eeprom.startup_temp_degC = -60
|
|
135
138
|
self.settings.eeprom.detector_gain = 1
|
|
136
139
|
self.settings.eeprom.detector_gain_odd = 1
|
|
137
140
|
|
|
141
|
+
# MultiWavelengthCalibration attributes
|
|
142
|
+
self.settings.eeprom.multi_wavelength_calibration.set("wavelength_coeffs", [0,1,0,0,0])
|
|
143
|
+
|
|
138
144
|
self.process_f = self._init_process_funcs()
|
|
139
145
|
|
|
140
146
|
###############################################################
|
|
141
147
|
# Private Methods
|
|
142
148
|
###############################################################
|
|
143
149
|
|
|
144
|
-
def _init_process_funcs(self):
|
|
150
|
+
def _init_process_funcs(self):
|
|
145
151
|
process_f = {}
|
|
146
152
|
|
|
147
|
-
process_f["connect"]
|
|
148
|
-
process_f["acquire_data"]
|
|
149
|
-
process_f["set_shutter_enable"]
|
|
150
|
-
process_f["set_integration_time_ms"]
|
|
151
|
-
process_f["get_serial_number"]
|
|
152
|
-
process_f["init_tec_setpoint"]
|
|
153
|
-
process_f["set_tec_setpoint"]
|
|
154
|
-
process_f["init_detector_area"]
|
|
155
|
-
process_f["scans_to_average"]
|
|
156
|
-
process_f["high_gain_mode_enable"]
|
|
157
|
-
process_f["save_config"]
|
|
153
|
+
process_f["connect"] = self.connect
|
|
154
|
+
process_f["acquire_data"] = self.acquire_data
|
|
155
|
+
process_f["set_shutter_enable"] = self.set_shutter_enable
|
|
156
|
+
process_f["set_integration_time_ms"] = self.set_integration_time_ms
|
|
157
|
+
process_f["get_serial_number"] = self.get_serial_number
|
|
158
|
+
process_f["init_tec_setpoint"] = self.init_tec_setpoint
|
|
159
|
+
process_f["set_tec_setpoint"] = self.set_tec_setpoint
|
|
160
|
+
process_f["init_detector_area"] = self.init_detector_area
|
|
161
|
+
process_f["scans_to_average"] = self.scans_to_average
|
|
162
|
+
process_f["high_gain_mode_enable"] = self.high_gain_mode_enable
|
|
163
|
+
process_f["save_config"] = self.save_config
|
|
164
|
+
process_f["vertical_binning"] = self.set_vertical_binning
|
|
158
165
|
|
|
159
166
|
##################################################################
|
|
160
167
|
# What follows is the old init-lambdas that are squashed into process_f
|
|
161
168
|
# Long term, the upstream requests should be changed to match the new format
|
|
162
169
|
# This is an easy fix for the time being to make things behave
|
|
163
170
|
##################################################################
|
|
164
|
-
process_f["integration_time_ms"]
|
|
165
|
-
process_f["fan_enable"]
|
|
166
|
-
process_f["shutter_enable"]
|
|
167
|
-
process_f["detector_tec_enable"]
|
|
168
|
-
process_f["detector_tec_setpoint_degC"]
|
|
171
|
+
process_f["integration_time_ms"] = lambda x: self.set_integration_time_ms(x)
|
|
172
|
+
process_f["fan_enable"] = lambda x: self.set_fan_enable(bool(x))
|
|
173
|
+
process_f["shutter_enable"] = lambda x: self.set_shutter_enable(bool(x))
|
|
174
|
+
process_f["detector_tec_enable"] = lambda x: self.toggle_tec(bool(x))
|
|
175
|
+
process_f["detector_tec_setpoint_degC"] = lambda x: self.set_tec_setpoint(int(round(x)))
|
|
169
176
|
|
|
170
177
|
return process_f
|
|
171
178
|
|
|
172
|
-
def high_gain_mode_enable(self, enabled
|
|
179
|
+
def high_gain_mode_enable(self, enabled):
|
|
173
180
|
if enabled:
|
|
174
181
|
result = self.driver.SetPreAmpGain(self.gain_idx[-1])
|
|
175
182
|
assert(self.SUCCESS == result), f"unable to set detector gain, got value of {result}"
|
|
@@ -181,22 +188,16 @@ class AndorDevice(InterfaceDevice):
|
|
|
181
188
|
log.debug(f"for {enabled} setting gain to {self.gain_options[0]}")
|
|
182
189
|
return
|
|
183
190
|
|
|
184
|
-
|
|
185
|
-
def _update_wavelength_coeffs(self, coeffs: list[float]): # -> None
|
|
186
|
-
self.settings.eeprom.wavelength_coeffs = coeffs
|
|
187
|
-
self.config_values['wavelength_coeffs'] = coeffs
|
|
188
|
-
self.save_config()
|
|
189
|
-
|
|
190
|
-
def set_fan_enable(self, x: bool): # -> SpectrometerResponse
|
|
191
|
+
def set_fan_enable(self, x):
|
|
191
192
|
self.check_result(self.driver.SetFanMode(int(x)), f"Andor Fan On {x}")
|
|
192
193
|
return SpectrometerResponse()
|
|
193
194
|
|
|
194
|
-
def _get_default_data_dir(self):
|
|
195
|
+
def _get_default_data_dir(self):
|
|
195
196
|
if os.name == "nt":
|
|
196
197
|
return os.path.join(os.path.expanduser("~"), "Documents", "EnlightenSpectra")
|
|
197
198
|
return os.path.join(os.environ["HOME"], "EnlightenSpectra")
|
|
198
199
|
|
|
199
|
-
def _check_config_file(self):
|
|
200
|
+
def _check_config_file(self):
|
|
200
201
|
self.config_dir = os.path.join(self._get_default_data_dir(), 'config')
|
|
201
202
|
self.config_file = os.path.join(self.config_dir, self.serial + '.json')
|
|
202
203
|
if not os.path.exists(self.config_dir):
|
|
@@ -228,10 +229,10 @@ class AndorDevice(InterfaceDevice):
|
|
|
228
229
|
|
|
229
230
|
return convertedSpec
|
|
230
231
|
|
|
231
|
-
def _take_one_averaged_reading(self):
|
|
232
|
+
def _take_one_averaged_reading(self):
|
|
232
233
|
averaging_enabled = (self.settings.state.scans_to_average > 1)
|
|
233
234
|
|
|
234
|
-
if averaging_enabled
|
|
235
|
+
if averaging_enabled:
|
|
235
236
|
# collect the entire averaged spectrum at once (added for
|
|
236
237
|
# BatchCollection with laser delay)
|
|
237
238
|
#
|
|
@@ -258,7 +259,7 @@ class AndorDevice(InterfaceDevice):
|
|
|
258
259
|
if self.tec_enabled:
|
|
259
260
|
log.debug("TEC enabled, so reading temperature")
|
|
260
261
|
|
|
261
|
-
use_float = True # seems to work on 785XL (WP-01635)
|
|
262
|
+
use_float = True # seems to work on 785XL (WP-01635 and WP-01491)
|
|
262
263
|
|
|
263
264
|
if use_float:
|
|
264
265
|
c_temp = c_float()
|
|
@@ -269,7 +270,11 @@ class AndorDevice(InterfaceDevice):
|
|
|
269
270
|
|
|
270
271
|
label = self.get_error_code(result)
|
|
271
272
|
|
|
272
|
-
if label in ["DRV_SUCCESS",
|
|
273
|
+
if label in [ "DRV_SUCCESS",
|
|
274
|
+
"DRV_TEMPERATURE_DRIFT",
|
|
275
|
+
"DRV_TEMPERATURE_STABILIZED",
|
|
276
|
+
"DRV_TEMPERATURE_NOT_REACHED",
|
|
277
|
+
"DRV_TEMPERATURE_NOT_STABILIZED" ]:
|
|
273
278
|
reading.detector_temperature_degC = c_temp.value
|
|
274
279
|
log.debug(f"Andor temperature {reading.detector_temperature_degC:.2f} ({label})")
|
|
275
280
|
else:
|
|
@@ -336,12 +341,12 @@ class AndorDevice(InterfaceDevice):
|
|
|
336
341
|
# reading.dump_area_scan()
|
|
337
342
|
return SpectrometerResponse(data=reading)
|
|
338
343
|
|
|
339
|
-
def _close_ex_shutter(self):
|
|
344
|
+
def _close_ex_shutter(self):
|
|
340
345
|
self.check_result(self.driver.SetShutterEx(1, 1, self.SHUTTER_SPEED_MS, self.SHUTTER_SPEED_MS, 2), "SetShutterEx(2)")
|
|
341
346
|
self.settings.state.shutter_enabled = False
|
|
342
347
|
return SpectrometerResponse(True)
|
|
343
348
|
|
|
344
|
-
def _open_ex_shutter(self):
|
|
349
|
+
def _open_ex_shutter(self):
|
|
345
350
|
self.check_result(self.driver.SetShutterEx(1, 1, self.SHUTTER_SPEED_MS, self.SHUTTER_SPEED_MS, 1), "SetShutterEx(1)")
|
|
346
351
|
self.settings.state.shutter_enabled = True
|
|
347
352
|
return SpectrometerResponse(True)
|
|
@@ -358,7 +363,7 @@ class AndorDevice(InterfaceDevice):
|
|
|
358
363
|
raise RuntimeError(msg)
|
|
359
364
|
log.debug(f"successfully called {func}")
|
|
360
365
|
|
|
361
|
-
def connect(self):
|
|
366
|
+
def connect(self):
|
|
362
367
|
if self.dll_fail:
|
|
363
368
|
return SpectrometerResponse(False, error_msg="can't find Andor DLL; please confirm Andor Driver Pack 2 installed")
|
|
364
369
|
|
|
@@ -380,13 +385,20 @@ class AndorDevice(InterfaceDevice):
|
|
|
380
385
|
self.init_detector_area() # step 7
|
|
381
386
|
|
|
382
387
|
if not self._check_config_file():
|
|
388
|
+
log.debug("stubbing Andor EEPROM")
|
|
389
|
+
self.settings.eeprom.stubbed = True
|
|
383
390
|
self.config_values = {
|
|
384
391
|
'detector_serial_number': self.serial,
|
|
385
|
-
'wavelength_coeffs': [0,1,0,0],
|
|
392
|
+
'wavelength_coeffs': [0,1,0,0,0],
|
|
386
393
|
'excitation_nm_float': 0,
|
|
387
394
|
'raman_intensity_coeffs': [],
|
|
388
395
|
'raman_intensity_calibration_order': 0,
|
|
389
|
-
'invert_x_axis': False
|
|
396
|
+
'invert_x_axis': False,
|
|
397
|
+
'roi_horizontal_start': 0,
|
|
398
|
+
'roi_horizontal_end': 0,
|
|
399
|
+
'roi_vertical_region_1_start': 0,
|
|
400
|
+
'roi_vertical_region_1_end': 0,
|
|
401
|
+
'stubbed': True
|
|
390
402
|
}
|
|
391
403
|
log.debug(f"connect: config file not found, so defaulting to these: {self.config_values}")
|
|
392
404
|
self.save_config()
|
|
@@ -411,6 +423,14 @@ class AndorDevice(InterfaceDevice):
|
|
|
411
423
|
# step 17 (WasatchNET doesn't do this)
|
|
412
424
|
self._obtain_gain_info()
|
|
413
425
|
|
|
426
|
+
# step 18 (WasatchNET doesn't do this)
|
|
427
|
+
roi = ROI(self.settings.eeprom.roi_vertical_region_1_start,
|
|
428
|
+
self.settings.eeprom.roi_vertical_region_1_end)
|
|
429
|
+
if roi.start != 0 and roi.end != 0:
|
|
430
|
+
# although camera can probably support the (0, 0) case, it's
|
|
431
|
+
# convenient to treat as unconfigured defaults
|
|
432
|
+
self.set_vertical_binning(roi)
|
|
433
|
+
|
|
414
434
|
# success!
|
|
415
435
|
log.info("AndorDevice successfully connected")
|
|
416
436
|
|
|
@@ -419,6 +439,40 @@ class AndorDevice(InterfaceDevice):
|
|
|
419
439
|
self.settings.eeprom.has_cooling = True
|
|
420
440
|
return SpectrometerResponse(data=True)
|
|
421
441
|
|
|
442
|
+
def set_vertical_binning(self, roi):
|
|
443
|
+
"""
|
|
444
|
+
Note that this follows the same (start, end) vertical ROI API as
|
|
445
|
+
FeatureInterfaceDevice.set_vertical_binning, and dynamically translates
|
|
446
|
+
that into the (middle, height) Andor API.
|
|
447
|
+
"""
|
|
448
|
+
|
|
449
|
+
if isinstance(roi, ROI):
|
|
450
|
+
start, end = roi.start, roi.end
|
|
451
|
+
elif len(roi) == 2:
|
|
452
|
+
start, end = roi[0], roi[1]
|
|
453
|
+
else:
|
|
454
|
+
log.error("set_vertical_binning requires an ROI object or tuple of (start, stop) lines")
|
|
455
|
+
return SpectrometerResponse(data=False, error_msg="invalid start and stop lines")
|
|
456
|
+
|
|
457
|
+
if start < 0 or end < 0:
|
|
458
|
+
log.error("set_vertical_binning requires POSITIVE (start, stop) lines")
|
|
459
|
+
return SpectrometerResponse(data=False, error_msg="invalid start and stop lines")
|
|
460
|
+
|
|
461
|
+
# enforce ascending order
|
|
462
|
+
if start >= end:
|
|
463
|
+
log.error("set_vertical_binning requires ascending order (ignoring %d, %d)", start, end)
|
|
464
|
+
return SpectrometerResponse(data=False, error_msg="invalid start and stop lines")
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
height = end - start
|
|
468
|
+
center = int(round(height / 2, 0)) + start
|
|
469
|
+
|
|
470
|
+
log.debug(f"setting Single-Track vertical binning of ROI (start {start}, end {end}) (center {center}, height {height})")
|
|
471
|
+
self.check_result(self.driver.SetReadMode(3), "SetReadMode(single-track)")
|
|
472
|
+
self.check_result(self.driver.SetSingleTrack(center, height), "SetSingleTrack")
|
|
473
|
+
|
|
474
|
+
return SpectrometerResponse(data=True)
|
|
475
|
+
|
|
422
476
|
def save_config(self, eeprom=None):
|
|
423
477
|
"""
|
|
424
478
|
The user has edited the "virtual EEPROM", for instance using ENLIGHTEN's
|
|
@@ -428,7 +482,6 @@ class AndorDevice(InterfaceDevice):
|
|
|
428
482
|
@param eeprom: if provided, overwrite current settings with those in the
|
|
429
483
|
passed dict before writing to disk
|
|
430
484
|
"""
|
|
431
|
-
log.debug("save_config: here")
|
|
432
485
|
if eeprom is not None:
|
|
433
486
|
self.update_config_from_eeprom(eeprom)
|
|
434
487
|
|
|
@@ -444,13 +497,13 @@ class AndorDevice(InterfaceDevice):
|
|
|
444
497
|
# first, copy over any EEPROM fields which have a different name in
|
|
445
498
|
# wasatch.EEPROM vs the external JSON file
|
|
446
499
|
for json_name, python_name in self.config_names_to_eeprom.items():
|
|
447
|
-
self.config_values[json_name] =
|
|
500
|
+
self.config_values[json_name] = eeprom.multi_wavelength_calibration.get(python_name)
|
|
448
501
|
|
|
449
502
|
# now do all the standard attributes of the wasatch.EEPROM, adding
|
|
450
503
|
# their values into the same dict
|
|
451
|
-
for python_name
|
|
504
|
+
for python_name in eeprom.__dict__:
|
|
452
505
|
if python_name in self.config_values:
|
|
453
|
-
self.config_values[python_name] =
|
|
506
|
+
self.config_values[python_name] = eeprom.multi_wavelength_calibration.get(python_name)
|
|
454
507
|
|
|
455
508
|
def _load_config_values(self):
|
|
456
509
|
"""
|
|
@@ -463,10 +516,11 @@ class AndorDevice(InterfaceDevice):
|
|
|
463
516
|
# handle wp_ prefixes
|
|
464
517
|
for k, v in self.config_names_to_eeprom.items():
|
|
465
518
|
if k in self.config_values:
|
|
466
|
-
|
|
519
|
+
self.settings.eeprom.multi_wavelength_calibration.set(v, self.config_values[k])
|
|
467
520
|
|
|
468
521
|
# same spelling
|
|
469
522
|
for k in [ 'model',
|
|
523
|
+
'stubbed',
|
|
470
524
|
'detector',
|
|
471
525
|
'serial_number',
|
|
472
526
|
'invert_x_axis',
|
|
@@ -474,33 +528,35 @@ class AndorDevice(InterfaceDevice):
|
|
|
474
528
|
'excitation_nm_float',
|
|
475
529
|
'roi_horizontal_end',
|
|
476
530
|
'roi_horizontal_start',
|
|
531
|
+
'roi_vertical_region_1_start',
|
|
532
|
+
'roi_vertical_region_1_end',
|
|
477
533
|
'raman_intensity_coeffs',
|
|
478
534
|
'raman_intensity_calibration_order',
|
|
479
535
|
'startup_temp_degC',
|
|
480
536
|
'startup_integration_time_ms' ]:
|
|
481
537
|
if k in self.config_values:
|
|
482
|
-
|
|
538
|
+
self.settings.eeprom.multi_wavelength_calibration.set(k, self.config_values[k])
|
|
483
539
|
|
|
484
540
|
# default missing-but-obvious fields
|
|
485
541
|
if "raman_intensity_coeffs" in self.config_values:
|
|
486
542
|
if "raman_intensity_calibration_order" not in self.config_values:
|
|
487
|
-
self.settings.eeprom.raman_intensity_calibration_order
|
|
543
|
+
self.settings.eeprom.multi_wavelength_calibration.set("raman_intensity_calibration_order", len(self.settings.eeprom.raman_intensity_coeffs) - 1)
|
|
488
544
|
|
|
489
545
|
# post-load initialization
|
|
490
546
|
if 'startup_temp_degC' in self.config_values:
|
|
491
547
|
self.set_tec_setpoint(self.settings.eeprom.startup_temp_degC)
|
|
492
548
|
|
|
493
|
-
def acquire_data(self):
|
|
549
|
+
def acquire_data(self):
|
|
494
550
|
reading = self._take_one_averaged_reading()
|
|
495
551
|
return reading
|
|
496
552
|
|
|
497
|
-
def set_shutter_enable(self, enable
|
|
553
|
+
def set_shutter_enable(self, enable):
|
|
498
554
|
if enable:
|
|
499
555
|
return self._open_ex_shutter()
|
|
500
556
|
else:
|
|
501
557
|
return self._close_ex_shutter()
|
|
502
558
|
|
|
503
|
-
def set_integration_time_ms(self, ms
|
|
559
|
+
def set_integration_time_ms(self, ms):
|
|
504
560
|
self.integration_time_ms = ms
|
|
505
561
|
log.debug(f"setting integration time to {self.integration_time_ms}ms")
|
|
506
562
|
|
|
@@ -566,13 +622,13 @@ class AndorDevice(InterfaceDevice):
|
|
|
566
622
|
self.check_result(self.driver.SetTemperature(self.setpoint_deg_c), f"SetTemperature({self.setpoint_deg_c})")
|
|
567
623
|
return SpectrometerResponse(True)
|
|
568
624
|
|
|
569
|
-
def init_detector_area(self):
|
|
625
|
+
def init_detector_area(self):
|
|
570
626
|
xPixels = c_int()
|
|
571
627
|
yPixels = c_int()
|
|
572
628
|
self.check_result(self.driver.GetDetector(byref(xPixels), byref(yPixels)), "GetDetector(x, y)")
|
|
573
|
-
log.debug(f"detector {xPixels.value} width x {yPixels.value} height")
|
|
574
629
|
self.pixels = xPixels.value
|
|
575
630
|
self.height = yPixels.value
|
|
631
|
+
log.debug(f"detector {self.pixels} width x {self.height} height")
|
|
576
632
|
return SpectrometerResponse(True)
|
|
577
633
|
|
|
578
634
|
def _obtain_gain_info(self):
|
|
@@ -592,7 +648,7 @@ class AndorDevice(InterfaceDevice):
|
|
|
592
648
|
self.gain_options = self.gain_options[::-1]
|
|
593
649
|
log.debug(f"obtained gain options for spec, values were {self.gain_options}")
|
|
594
650
|
|
|
595
|
-
def init_detector_speed(self):
|
|
651
|
+
def init_detector_speed(self):
|
|
596
652
|
speed = c_float()
|
|
597
653
|
|
|
598
654
|
# for CCDs, set vertical to recommended
|
|
@@ -623,7 +679,7 @@ class AndorDevice(InterfaceDevice):
|
|
|
623
679
|
log.debug(f"set AD channel {ADnumber} with horizontal speed {HSnumber} ({STemp})")
|
|
624
680
|
return SpectrometerResponse(True)
|
|
625
681
|
|
|
626
|
-
def scans_to_average(self, value
|
|
682
|
+
def scans_to_average(self, value):
|
|
627
683
|
self.sum_count = 0
|
|
628
684
|
self.settings.state.scans_to_average = int(value)
|
|
629
685
|
return SpectrometerResponse(True)
|
|
@@ -74,3 +74,23 @@ class AutoRamanRequest:
|
|
|
74
74
|
buf.extend([self.saturation & 0xff, (self.saturation >> 8) & 0xff])
|
|
75
75
|
buf.extend([self.max_avg & 0xff ])
|
|
76
76
|
return buf
|
|
77
|
+
|
|
78
|
+
if __name__ == "__main__":
|
|
79
|
+
aar = AutoRamanRequest(
|
|
80
|
+
max_ms = 10000,
|
|
81
|
+
start_integ_ms = 100,
|
|
82
|
+
start_gain_db = 0,
|
|
83
|
+
max_integ_ms = 2000,
|
|
84
|
+
min_integ_ms = 10,
|
|
85
|
+
max_gain_db = 32,
|
|
86
|
+
min_gain_db = 0,
|
|
87
|
+
target_counts = 45000,
|
|
88
|
+
max_counts = 50000,
|
|
89
|
+
min_counts = 40000,
|
|
90
|
+
max_factor = 5,
|
|
91
|
+
drop_factor = 0.8,
|
|
92
|
+
saturation = 65000,
|
|
93
|
+
max_avg = 100)
|
|
94
|
+
|
|
95
|
+
buf = aar.serialize()
|
|
96
|
+
print("0x" + " ".join([f"{b:02x}" for b in buf]))
|
|
@@ -161,14 +161,14 @@ class DeviceFinderUSB:
|
|
|
161
161
|
return [DeviceID(device) for device in pyusb_devices]
|
|
162
162
|
|
|
163
163
|
def find_usb_devices(self, poll = False):
|
|
164
|
-
log.debug("DeviceFinderUSB.find_usb_devices: starting")
|
|
164
|
+
# log.debug("DeviceFinderUSB.find_usb_devices: starting")
|
|
165
165
|
device_ids = []
|
|
166
166
|
|
|
167
167
|
# MZ/ED: If USE_MONITORING is True, I had to disable the call to remove_all in enlighten.Controller
|
|
168
168
|
if self.startup_scan < self.MIN_POLLING_SCANS or not self.USE_MONITORING or poll:
|
|
169
169
|
# our first few scans should always be a bus poll
|
|
170
170
|
# this is because no events will be registered
|
|
171
|
-
log.debug(f"DeviceFinderUSB.find_usb_devices: just doing a bus poll for startup_scan {self.startup_scan}")
|
|
171
|
+
# log.debug(f"DeviceFinderUSB.find_usb_devices: just doing a bus poll for startup_scan {self.startup_scan}")
|
|
172
172
|
device_ids = self.bus_polling()
|
|
173
173
|
self.startup_scan += 1
|
|
174
174
|
elif platform.system() == "Windows":
|
|
@@ -177,7 +177,7 @@ class DeviceFinderUSB:
|
|
|
177
177
|
device_ids = self.linux_monitoring()
|
|
178
178
|
else:
|
|
179
179
|
device_ids = self.bus_polling()
|
|
180
|
-
log.debug(f"DeviceFinderUSB.find_usb_devices: returning {len(device_ids)} devices")
|
|
180
|
+
# log.debug(f"DeviceFinderUSB.find_usb_devices: returning {len(device_ids)} devices")
|
|
181
181
|
return device_ids
|
|
182
182
|
|
|
183
183
|
def mac_monitoring(self):
|