wasatch 2.2.0__tar.gz → 2.2.14__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.0 → wasatch-2.2.14}/CHANGELOG.md +77 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/PKG-INFO +1 -1
- wasatch-2.2.14/demo-auto-raman.py +130 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/demo.py +30 -28
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/AndorDevice.py +17 -17
- wasatch-2.2.14/wasatch/AutoRaman.py +451 -0
- wasatch-2.2.14/wasatch/AutoRamanRequest.py +96 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/BLEDevice.py +74 -56
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/ControlObject.py +3 -1
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/DeviceFinderUSB.py +3 -3
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/EEPROM.py +285 -154
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/FeatureIdentificationDevice.py +564 -384
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/FirmwareRequirements.py +14 -6
- wasatch-2.2.14/wasatch/IMX385.py +179 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/OceanDevice.py +2 -1
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/Reading.py +5 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/SPIDevice.py +5 -4
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/SpectrometerResponse.py +1 -1
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/SpectrometerSettings.py +53 -171
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/SpectrometerState.py +2 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/TakeOneRequest.py +11 -2
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/WasatchBus.py +2 -2
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/WasatchDevice.py +65 -34
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/WasatchDeviceWrapper.py +10 -11
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/WrapperWorker.py +7 -3
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/__init__.py +1 -1
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/utils.py +3 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/.example_pypirc +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/.flake8 +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/.gitignore +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/.pylintrc +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/Doxyfile +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/LICENSE +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/MANIFEST.in +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/Makefile +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/README.md +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/README_PYPI.md +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/README_RPI.md +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/README_SETTINGS.md +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/README_VIRTUAL.md +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/WasatchShell/README.md +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/WasatchShell/laser-test.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/WasatchShell/load-test.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/WasatchShell/one-shot.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/WasatchShell/wasatch-shell.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/demo-simple.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/demo-virtual.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/demo-workflow.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/environments/conda-linux.yml +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/environments/conda-macos.yml +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/environments/conda-rpi.yml +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/environments/conda-win10.yml +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/pyproject.toml +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/requirements.txt +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/screenshots/multiplatform.png +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/scripts/dark-vs-integ.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/scripts/deploy +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/setup.cfg +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/testSpectrometers/SiG_785/eeprom/EEPROM-EM-9c65d19f4c.json +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/testSpectrometers/SiG_785/readings/enlighten-20210329-094722-799520-WP-00686.csv +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/testSpectrometers/SiG_785/readings/enlighten-20210329-094755-288958-WP-00686.csv +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/testSpectrometers/WP-00887/eeprom/WP-00887-mock.json +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/testSpectrometers/WP-00904/eeprom/WP-00904-mock.json +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/testSpectrometers/WP-00913/eeprom/WP-00913-mock.json +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/tests/existing_implementations.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/tests/noise-by-setpoint.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/tests/queues.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/tests/unit.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/udev/10-wasatch.rules +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/AbstractUSBDevice.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/BalanceAcquisition.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/CSVLoader.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/CommandSettings.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/DetectorROI.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/DetectorRegions.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/DeviceID.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/EEPROM_multical.sav +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/FPGAOptions.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/HardwareInfo.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/InterfaceDevice.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/MockUSBDevice.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/PollStatus.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/ProcessedReading.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/ROI.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/RealUSBDevice.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/SpectrometerRequest.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/StatusMessage.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/applog.py +0 -0
- {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/simulation_protocol.py +0 -0
|
@@ -1,5 +1,82 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
- 2024-11-25 2.2.14
|
|
4
|
+
- EEPROM
|
|
5
|
+
- added subformat 5 (EEPROM.MultiWavelengthCalibration)
|
|
6
|
+
- bumped rev to 17
|
|
7
|
+
- deprecated subformat 4 (regions)
|
|
8
|
+
- SpectrometerSettings
|
|
9
|
+
- deprecated integer excitation_nm
|
|
10
|
+
- moved vertical ROI to wasatch.ROI
|
|
11
|
+
- added calibrations(), select_calibration(n)
|
|
12
|
+
- FeatureInterfaceDevice
|
|
13
|
+
- added set/get_scans_to_average
|
|
14
|
+
- SpectrometerState
|
|
15
|
+
- added onboard_averaging
|
|
16
|
+
- 2024-11-01 2.2.13
|
|
17
|
+
- support for InGaAs linear pixel calibration
|
|
18
|
+
- re-enable STM32 serial number
|
|
19
|
+
- 2024-10-31 2.2.12
|
|
20
|
+
- prefer new EEPROM location for startup laser TEC setpoint
|
|
21
|
+
- 2024-10-30 2.2.11
|
|
22
|
+
- IMX385.bin_4x2_avg tweak
|
|
23
|
+
- 2024-10-30 2.2.10
|
|
24
|
+
- don't configure laser TEC and laser warning delay unless laser present
|
|
25
|
+
- 2024-10-29 2.2.9
|
|
26
|
+
- add BIN_4X2_AVG
|
|
27
|
+
- add more firmware version checks around new features to avoid breaking old FW
|
|
28
|
+
- Auto-Raman
|
|
29
|
+
- support new onboard Auto-Raman opcode and parameters
|
|
30
|
+
- start refresh of BLEDevice
|
|
31
|
+
- 2024-10-16 2.2.8
|
|
32
|
+
- add alert_queue to allow caller (ENLIGHTEN) to send hints and interrupts
|
|
33
|
+
downstream into Wasatch.PY, visible even within long-running operations
|
|
34
|
+
like Auto-Raman which are encapsulated within a single call from
|
|
35
|
+
WrapperWorker and therefore unsuitable for the normal "command_queue"
|
|
36
|
+
- added AutoRaman support for new "auto_raman_cancel" alert
|
|
37
|
+
- 2024-10-15 2.2.7
|
|
38
|
+
- fixed AutoRaman on X series
|
|
39
|
+
- added IMX385.bin_4x2_interp
|
|
40
|
+
- 2024-10-14 2.2.6
|
|
41
|
+
- removed
|
|
42
|
+
- EEPROM.ssc_enabled
|
|
43
|
+
- changed
|
|
44
|
+
- EEPROM.LATEST_REV to 16
|
|
45
|
+
- EEPROM.bin_2x2 --> .horiz_binning_enabled
|
|
46
|
+
- EEPROM.actual_horizontal to .actual_pixels_horizontal
|
|
47
|
+
- EEPROM.actual_vertical to .actual_pixels_vertical
|
|
48
|
+
- added
|
|
49
|
+
- EEPROM.horiz_binning_mode
|
|
50
|
+
- EEPROM.power_timeout_sec
|
|
51
|
+
- EEPROM.detector_timeout_sec
|
|
52
|
+
- IMX385.bin_4x2
|
|
53
|
+
- 2024-10-10 2.2.5
|
|
54
|
+
- added IMX385
|
|
55
|
+
- added EEPROM.ssc_enabled
|
|
56
|
+
- added AutoRaman throwaway before dark to let laser fully disable
|
|
57
|
+
- bugfix in SpectrometerSettings.has_excitation
|
|
58
|
+
- 2024-10-03 2.2.4
|
|
59
|
+
- added EEPROM.disable_ble_power
|
|
60
|
+
- added EEPROM.disable_laser_armed_indicator
|
|
61
|
+
- added SpectrometerSettings.ble_firmware_version
|
|
62
|
+
- 2024-09-17 2.2.3
|
|
63
|
+
- ensure all queued messages are flowed upstream
|
|
64
|
+
- AutoRaman
|
|
65
|
+
- output progress bar updates
|
|
66
|
+
- support configurable Laser Warning Delay
|
|
67
|
+
- averaging bugfix
|
|
68
|
+
- trying to support non-zero initial gain dB
|
|
69
|
+
- enable saving individual spectral components via environment variable
|
|
70
|
+
- don't perform dark-correction; leave that for caller
|
|
71
|
+
- 2024-09-12 2.2.2
|
|
72
|
+
- added set/get_laser_warning_delay through change_settings
|
|
73
|
+
- updated reset_fpga to restore key FPGA settings
|
|
74
|
+
- enriched AutoRaman status messages
|
|
75
|
+
- AutoRaman bugfix
|
|
76
|
+
- 2024-09-12 2.2.1
|
|
77
|
+
- fixed get_detector_gain on XS
|
|
78
|
+
- reduced number of implicit throwaways after acquisition parameter changes
|
|
79
|
+
- added AutoRaman via TakeOneRequest
|
|
3
80
|
- 2024-08-22 2.2.0
|
|
4
81
|
- cutting release for ENLIGHTEN 4.1.0
|
|
5
82
|
- 2024-06-27 2.1.73
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import sys
|
|
4
|
+
import time
|
|
5
|
+
import numpy
|
|
6
|
+
import signal
|
|
7
|
+
import logging
|
|
8
|
+
import datetime
|
|
9
|
+
import argparse
|
|
10
|
+
|
|
11
|
+
import wasatch
|
|
12
|
+
from wasatch import utils
|
|
13
|
+
from wasatch import applog
|
|
14
|
+
from wasatch.WasatchBus import WasatchBus
|
|
15
|
+
from wasatch.WasatchDevice import WasatchDevice
|
|
16
|
+
from wasatch.WasatchDeviceWrapper import WasatchDeviceWrapper
|
|
17
|
+
from wasatch.TakeOneRequest import TakeOneRequest
|
|
18
|
+
from wasatch.AutoRamanRequest import AutoRamanRequest
|
|
19
|
+
|
|
20
|
+
log = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
class Fixture:
|
|
23
|
+
|
|
24
|
+
def __init__(self):
|
|
25
|
+
self.bus = None
|
|
26
|
+
self.device = None
|
|
27
|
+
self.outfile = None
|
|
28
|
+
self.reading_count = 0
|
|
29
|
+
|
|
30
|
+
self.parse_args()
|
|
31
|
+
|
|
32
|
+
self.logger = applog.MainLogger("DEBUG" if self.args.debug else "INFO")
|
|
33
|
+
|
|
34
|
+
def parse_args(self):
|
|
35
|
+
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
|
36
|
+
parser.add_argument("--debug", action="store_true", help="logging level")
|
|
37
|
+
parser.add_argument("--outfile", type=str, default=None, help="output filename (e.g. path/to/spectra.csv)")
|
|
38
|
+
parser.add_argument("--max-ms", type=int, default=10000, help="maximum measurement time (ms)")
|
|
39
|
+
parser.add_argument("--start-integ-ms", type=int, default=100, help="initial integration time (ms)")
|
|
40
|
+
parser.add_argument("--start-gain-db", type=float, default=0, help="initial gain (dB)")
|
|
41
|
+
parser.add_argument("--max-integ-ms", type=int, default=2000, help="maximum integration time (ms)")
|
|
42
|
+
parser.add_argument("--min-integ-ms", type=int, default=10, help="minimum integration time (ms)")
|
|
43
|
+
parser.add_argument("--max-gain-db", type=float, default=32, help="maximum gain (dB)")
|
|
44
|
+
parser.add_argument("--min-gain-db", type=float, default=0, help="minimum gain (dB)")
|
|
45
|
+
parser.add_argument("--target-counts", type=int, default=45000, help="target counts")
|
|
46
|
+
parser.add_argument("--max-counts", type=int, default=50000, help="max counts")
|
|
47
|
+
parser.add_argument("--min-counts", type=int, default=40000, help="min counts")
|
|
48
|
+
parser.add_argument("--max-factor", type=float, default=5, help="max scaling factor")
|
|
49
|
+
parser.add_argument("--drop-factor", type=float, default=0.5, help="factor when scaling down")
|
|
50
|
+
parser.add_argument("--saturation", type=int, default=65000, help="counts")
|
|
51
|
+
|
|
52
|
+
self.args = parser.parse_args()
|
|
53
|
+
|
|
54
|
+
def connect(self):
|
|
55
|
+
""" Connect to all discoverable Wasatch spectrometers. """
|
|
56
|
+
|
|
57
|
+
self.bus = WasatchBus()
|
|
58
|
+
if not self.bus.device_ids:
|
|
59
|
+
print("No Wasatch USB spectrometers found.")
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
for device_id in self.bus.device_ids:
|
|
63
|
+
device = WasatchDevice(device_id)
|
|
64
|
+
if not device.connect():
|
|
65
|
+
log.critical("connect: can't connect to %s", device_id)
|
|
66
|
+
continue
|
|
67
|
+
|
|
68
|
+
settings = device.settings
|
|
69
|
+
print(f"connected to {settings.full_model()} {settings.eeprom.serial_number} with {settings.pixels()} pixels " +
|
|
70
|
+
f"from ({settings.wavelengths[0]:.2f}, {settings.wavelengths[-1]:.2f}nm)" +
|
|
71
|
+
( f" ({settings.wavenumbers[0]:.2f}, {settings.wavenumbers[-1]:.2f}cm⁻¹)" if settings.has_excitation() else "") +
|
|
72
|
+
f" (Microcontroller {device.settings.microcontroller_firmware_version}," +
|
|
73
|
+
f" FPGA {settings.fpga_firmware_version})")
|
|
74
|
+
|
|
75
|
+
self.device = device
|
|
76
|
+
return True
|
|
77
|
+
|
|
78
|
+
def run(self):
|
|
79
|
+
start_time = datetime.datetime.now()
|
|
80
|
+
|
|
81
|
+
# instantiate an AutoRamanRequest, passing in the configured options
|
|
82
|
+
arr = AutoRamanRequest(max_ms = self.args.max_ms,
|
|
83
|
+
start_integ_ms = self.args.start_integ_ms,
|
|
84
|
+
start_gain_db = self.args.start_gain_db,
|
|
85
|
+
max_integ_ms = self.args.max_integ_ms,
|
|
86
|
+
min_integ_ms = self.args.min_integ_ms,
|
|
87
|
+
max_gain_db = self.args.max_gain_db,
|
|
88
|
+
min_gain_db = self.args.min_gain_db,
|
|
89
|
+
target_counts = self.args.target_counts,
|
|
90
|
+
max_counts = self.args.max_counts,
|
|
91
|
+
min_counts = self.args.min_counts,
|
|
92
|
+
max_factor = self.args.max_factor,
|
|
93
|
+
drop_factor = self.args.drop_factor,
|
|
94
|
+
saturation = self.args.saturation)
|
|
95
|
+
log.debug(f"instantiated {arr}")
|
|
96
|
+
|
|
97
|
+
# add the AutoRamanRequest to a TakeOneRequest
|
|
98
|
+
tor = TakeOneRequest(auto_raman_request=arr)
|
|
99
|
+
log.debug(f"instantiated {tor}")
|
|
100
|
+
|
|
101
|
+
# send the TakeOneRequest to the driver
|
|
102
|
+
self.device.change_setting("take_one_request", tor)
|
|
103
|
+
log.debug("sent TakeOneRequest to spectrometer")
|
|
104
|
+
|
|
105
|
+
# block on completion of the TakeOneRequest (ignore keepalives)
|
|
106
|
+
while True:
|
|
107
|
+
log.debug("calling WasatchDevice.acquire_data")
|
|
108
|
+
reading = self.device.acquire_data().data
|
|
109
|
+
log.debug(f"WasatchDevice.acquire_data returned {reading}")
|
|
110
|
+
if reading is not None:
|
|
111
|
+
break
|
|
112
|
+
|
|
113
|
+
time.sleep(1)
|
|
114
|
+
|
|
115
|
+
elapsed_sec = (datetime.datetime.now() - start_time).total_seconds()
|
|
116
|
+
print(f"Auto-Raman measurement completed in {elapsed_sec:.2f} sec")
|
|
117
|
+
|
|
118
|
+
# process Reading
|
|
119
|
+
settings = self.device.settings
|
|
120
|
+
|
|
121
|
+
# ASCII-Art
|
|
122
|
+
print("\n".join(wasatch.utils.ascii_spectrum(reading.spectrum, rows=20, cols=80, x_axis=settings.wavenumbers, x_unit="cm⁻¹")))
|
|
123
|
+
|
|
124
|
+
if self.outfile:
|
|
125
|
+
self.outfile.write("{datetime.datetime.now()}, {aar}, {', '.join([f'.2f' for x in reading.spectrum])}\n")
|
|
126
|
+
|
|
127
|
+
# main()
|
|
128
|
+
fixture = Fixture()
|
|
129
|
+
if fixture.connect():
|
|
130
|
+
fixture.run()
|
|
@@ -6,13 +6,7 @@
|
|
|
6
6
|
# DESCRIPTION: Simple cmd-line demo to confirm that Wasatch.PY is working #
|
|
7
7
|
# and can connect to and control a spectrometer. #
|
|
8
8
|
# #
|
|
9
|
-
#
|
|
10
|
-
# $ rm -f environment.yml #
|
|
11
|
-
# $ ln -s environments/conda-linux.yml (or macos, etc) #
|
|
12
|
-
# $ conda env create -n wasatch3 #
|
|
13
|
-
# $ conda activate wasatch3 #
|
|
14
|
-
# INVOCATION: #
|
|
15
|
-
# $ python -u demo.py #
|
|
9
|
+
# INVOCATION: $ python -u demo.py #
|
|
16
10
|
# #
|
|
17
11
|
################################################################################
|
|
18
12
|
|
|
@@ -22,7 +16,6 @@ import sys
|
|
|
22
16
|
import time
|
|
23
17
|
import numpy
|
|
24
18
|
import signal
|
|
25
|
-
import psutil
|
|
26
19
|
import logging
|
|
27
20
|
import datetime
|
|
28
21
|
import argparse
|
|
@@ -33,7 +26,6 @@ from wasatch import applog
|
|
|
33
26
|
from wasatch.WasatchBus import WasatchBus
|
|
34
27
|
from wasatch.WasatchDevice import WasatchDevice
|
|
35
28
|
from wasatch.WasatchDeviceWrapper import WasatchDeviceWrapper
|
|
36
|
-
from wasatch.RealUSBDevice import RealUSBDevice
|
|
37
29
|
|
|
38
30
|
log = logging.getLogger(__name__)
|
|
39
31
|
|
|
@@ -45,7 +37,7 @@ class WasatchDemo:
|
|
|
45
37
|
# #
|
|
46
38
|
############################################################################
|
|
47
39
|
|
|
48
|
-
def __init__(self
|
|
40
|
+
def __init__(self):
|
|
49
41
|
self.bus = None
|
|
50
42
|
self.devices = {}
|
|
51
43
|
self.logger = None
|
|
@@ -53,7 +45,7 @@ class WasatchDemo:
|
|
|
53
45
|
self.exiting = False
|
|
54
46
|
self.reading_count = 0
|
|
55
47
|
|
|
56
|
-
self.args = self.parse_args(
|
|
48
|
+
self.args = self.parse_args()
|
|
57
49
|
|
|
58
50
|
if self.args.log_level != "NEVER":
|
|
59
51
|
self.logger = applog.MainLogger(self.args.log_level)
|
|
@@ -65,7 +57,7 @@ class WasatchDemo:
|
|
|
65
57
|
# #
|
|
66
58
|
############################################################################
|
|
67
59
|
|
|
68
|
-
def parse_args(self
|
|
60
|
+
def parse_args(self):
|
|
69
61
|
parser = argparse.ArgumentParser(description="Simple demo to acquire spectra from command-line interface")
|
|
70
62
|
parser.add_argument("--log-level", type=str, default="INFO", help="logging level", choices=["DEBUG","INFO","WARNING","ERROR","CRITICAL","NEVER"])
|
|
71
63
|
parser.add_argument("--integration-time-ms", type=int, default=10, help="integration time (ms, default 10)")
|
|
@@ -78,8 +70,7 @@ class WasatchDemo:
|
|
|
78
70
|
parser.add_argument("--ascii-art", action="store_true", help="graph spectra in ASCII")
|
|
79
71
|
parser.add_argument("--version", action="store_true", help="display Wasatch.PY version and exit")
|
|
80
72
|
|
|
81
|
-
|
|
82
|
-
args = parser.parse_args(argv[1:])
|
|
73
|
+
args = parser.parse_args()
|
|
83
74
|
if args.version:
|
|
84
75
|
print("Wasatch.PY %s" % wasatch.__version__)
|
|
85
76
|
sys.exit(0)
|
|
@@ -100,7 +91,7 @@ class WasatchDemo:
|
|
|
100
91
|
# lazy-load a USB bus
|
|
101
92
|
if self.bus is None:
|
|
102
93
|
log.debug("instantiating WasatchBus")
|
|
103
|
-
self.bus = WasatchBus(
|
|
94
|
+
self.bus = WasatchBus()
|
|
104
95
|
|
|
105
96
|
if not self.bus.device_ids:
|
|
106
97
|
print("No Wasatch USB spectrometers found.")
|
|
@@ -170,16 +161,30 @@ class WasatchDemo:
|
|
|
170
161
|
# read spectra until user presses Control-Break
|
|
171
162
|
while not self.exiting:
|
|
172
163
|
start_time = datetime.datetime.now()
|
|
164
|
+
|
|
173
165
|
if not self.args.non_blocking:
|
|
166
|
+
# if not non-blocking (i.e., blocking calls to Wasatch.PY), call
|
|
167
|
+
# device.acquire_data() directly -- this will block on each
|
|
168
|
+
# spectrometer in turn until we have collected one spectrum from each
|
|
169
|
+
# connected spectromter
|
|
174
170
|
for device_id, device in self.devices.items():
|
|
175
171
|
self.attempt_reading(device)
|
|
172
|
+
else:
|
|
173
|
+
# if we are non-blocking (meaning each spectrometer is free-
|
|
174
|
+
# running in its own background thread, then we don't need to do
|
|
175
|
+
# anything here -- spectra will magically "show up" at
|
|
176
|
+
# WasatchDeviceWrapper_callback without us having to do anything,
|
|
177
|
+
# and all we need to here is check whether we've collected enough
|
|
178
|
+
# spectra to shutdown
|
|
179
|
+
pass
|
|
180
|
+
|
|
176
181
|
end_time = datetime.datetime.now()
|
|
177
182
|
|
|
178
183
|
if self.args.max > 0 and self.reading_count >= self.args.max:
|
|
179
184
|
log.debug("max spectra reached, exiting")
|
|
180
185
|
self.exiting = True
|
|
181
186
|
else:
|
|
182
|
-
# compute how much longer we should wait before the next
|
|
187
|
+
# compute how much longer we should wait before the next "tick"
|
|
183
188
|
reading_time_ms = int((end_time - start_time).microseconds / 1000)
|
|
184
189
|
sleep_ms = self.args.delay_ms - reading_time_ms
|
|
185
190
|
if sleep_ms > 0:
|
|
@@ -215,11 +220,10 @@ class WasatchDemo:
|
|
|
215
220
|
self.process_reading(reading_response.data)
|
|
216
221
|
|
|
217
222
|
def acquire_reading(self, device):
|
|
218
|
-
# We want the demo to effectively block on new scans, so keep
|
|
219
|
-
#
|
|
220
|
-
#
|
|
221
|
-
#
|
|
222
|
-
# yet ready).
|
|
223
|
+
# We want the demo to effectively block on new scans, so keep polling the
|
|
224
|
+
# background thread until a reading is ready. In other apps, we could do
|
|
225
|
+
# other things (like respond to GUI events) if device.acquire_data() was
|
|
226
|
+
# None (meaning the next spectrum wasn't yet ready).
|
|
223
227
|
while True:
|
|
224
228
|
reading = device.acquire_data()
|
|
225
229
|
if reading is None:
|
|
@@ -241,8 +245,8 @@ class WasatchDemo:
|
|
|
241
245
|
|
|
242
246
|
def process_reading(self, reading):
|
|
243
247
|
if (self.exiting or
|
|
244
|
-
|
|
245
|
-
|
|
248
|
+
(self.args.scans_to_average > 1 and not reading.averaged) or
|
|
249
|
+
(self.args.max > 0 and self.reading_count >= self.args.max)):
|
|
246
250
|
return
|
|
247
251
|
|
|
248
252
|
device_id = reading.device_id
|
|
@@ -264,9 +268,8 @@ class WasatchDemo:
|
|
|
264
268
|
spectrum_max = numpy.amax(spectrum)
|
|
265
269
|
spectrum_avg = numpy.mean(spectrum)
|
|
266
270
|
spectrum_std = numpy.std (spectrum)
|
|
267
|
-
size_in_bytes = psutil.Process(os.getpid()).memory_info().rss
|
|
268
271
|
|
|
269
|
-
print("%s: %s %4d Detector: %5.2f degC Min: %8.2f Max: %8.2f Avg: %8.2f StdDev: %8.2f
|
|
272
|
+
print("%s: %s %4d Detector: %5.2f degC Min: %8.2f Max: %8.2f Avg: %8.2f StdDev: %8.2f" % (
|
|
270
273
|
reading.timestamp,
|
|
271
274
|
settings.eeprom.serial_number,
|
|
272
275
|
self.reading_count,
|
|
@@ -274,8 +277,7 @@ class WasatchDemo:
|
|
|
274
277
|
spectrum_min,
|
|
275
278
|
spectrum_max,
|
|
276
279
|
spectrum_avg,
|
|
277
|
-
spectrum_std
|
|
278
|
-
size_in_bytes))
|
|
280
|
+
spectrum_std))
|
|
279
281
|
log.debug("%s", str(reading))
|
|
280
282
|
|
|
281
283
|
if self.outfile:
|
|
@@ -313,7 +315,7 @@ demo = None
|
|
|
313
315
|
if __name__ == "__main__":
|
|
314
316
|
signal.signal(signal.SIGINT, signal_handler)
|
|
315
317
|
|
|
316
|
-
demo = WasatchDemo(
|
|
318
|
+
demo = WasatchDemo()
|
|
317
319
|
if demo.connect():
|
|
318
320
|
# Note that on Windows, Control-Break (SIGBREAK) differs from
|
|
319
321
|
# Control-C (SIGINT); see https://stackoverflow.com/a/1364199
|
|
@@ -47,7 +47,7 @@ class AndorDevice(InterfaceDevice):
|
|
|
47
47
|
SUCCESS = 20002 #!< see load_error_codes()
|
|
48
48
|
SHUTTER_SPEED_MS = 50 #!< allow time for mechanical shutter to stabilize
|
|
49
49
|
|
|
50
|
-
def __init__(self, device_id, message_queue=None):
|
|
50
|
+
def __init__(self, device_id, message_queue=None, alert_queue=None):
|
|
51
51
|
# if passed a string representation of a DeviceID, deserialize it
|
|
52
52
|
super().__init__()
|
|
53
53
|
if type(device_id) is str:
|
|
@@ -55,6 +55,7 @@ class AndorDevice(InterfaceDevice):
|
|
|
55
55
|
|
|
56
56
|
self.device_id = device_id
|
|
57
57
|
self.message_queue = message_queue
|
|
58
|
+
self.alert_queue = alert_queue
|
|
58
59
|
|
|
59
60
|
self.load_error_codes()
|
|
60
61
|
|
|
@@ -140,7 +141,7 @@ class AndorDevice(InterfaceDevice):
|
|
|
140
141
|
# Private Methods
|
|
141
142
|
###############################################################
|
|
142
143
|
|
|
143
|
-
def _init_process_funcs(self):
|
|
144
|
+
def _init_process_funcs(self):
|
|
144
145
|
process_f = {}
|
|
145
146
|
|
|
146
147
|
process_f["connect"] = self.connect
|
|
@@ -168,7 +169,7 @@ class AndorDevice(InterfaceDevice):
|
|
|
168
169
|
|
|
169
170
|
return process_f
|
|
170
171
|
|
|
171
|
-
def high_gain_mode_enable(self, enabled
|
|
172
|
+
def high_gain_mode_enable(self, enabled):
|
|
172
173
|
if enabled:
|
|
173
174
|
result = self.driver.SetPreAmpGain(self.gain_idx[-1])
|
|
174
175
|
assert(self.SUCCESS == result), f"unable to set detector gain, got value of {result}"
|
|
@@ -181,21 +182,21 @@ class AndorDevice(InterfaceDevice):
|
|
|
181
182
|
return
|
|
182
183
|
|
|
183
184
|
# MZ: nothing seems to call this?
|
|
184
|
-
def _update_wavelength_coeffs(self, coeffs
|
|
185
|
+
def _update_wavelength_coeffs(self, coeffs):
|
|
185
186
|
self.settings.eeprom.wavelength_coeffs = coeffs
|
|
186
187
|
self.config_values['wavelength_coeffs'] = coeffs
|
|
187
188
|
self.save_config()
|
|
188
189
|
|
|
189
|
-
def set_fan_enable(self, x
|
|
190
|
+
def set_fan_enable(self, x):
|
|
190
191
|
self.check_result(self.driver.SetFanMode(int(x)), f"Andor Fan On {x}")
|
|
191
192
|
return SpectrometerResponse()
|
|
192
193
|
|
|
193
|
-
def _get_default_data_dir(self):
|
|
194
|
+
def _get_default_data_dir(self):
|
|
194
195
|
if os.name == "nt":
|
|
195
196
|
return os.path.join(os.path.expanduser("~"), "Documents", "EnlightenSpectra")
|
|
196
197
|
return os.path.join(os.environ["HOME"], "EnlightenSpectra")
|
|
197
198
|
|
|
198
|
-
def _check_config_file(self):
|
|
199
|
+
def _check_config_file(self):
|
|
199
200
|
self.config_dir = os.path.join(self._get_default_data_dir(), 'config')
|
|
200
201
|
self.config_file = os.path.join(self.config_dir, self.serial + '.json')
|
|
201
202
|
if not os.path.exists(self.config_dir):
|
|
@@ -329,19 +330,18 @@ class AndorDevice(InterfaceDevice):
|
|
|
329
330
|
log.debug("completed take_one")
|
|
330
331
|
self.change_setting("cancel_take_one", True)
|
|
331
332
|
|
|
332
|
-
|
|
333
333
|
log.debug("device.take_one_averaged_reading: returning %s", reading)
|
|
334
334
|
if reading.spectrum is not None and reading.spectrum != []:
|
|
335
335
|
self.failure_count = 0
|
|
336
336
|
# reading.dump_area_scan()
|
|
337
337
|
return SpectrometerResponse(data=reading)
|
|
338
338
|
|
|
339
|
-
def _close_ex_shutter(self):
|
|
339
|
+
def _close_ex_shutter(self):
|
|
340
340
|
self.check_result(self.driver.SetShutterEx(1, 1, self.SHUTTER_SPEED_MS, self.SHUTTER_SPEED_MS, 2), "SetShutterEx(2)")
|
|
341
341
|
self.settings.state.shutter_enabled = False
|
|
342
342
|
return SpectrometerResponse(True)
|
|
343
343
|
|
|
344
|
-
def _open_ex_shutter(self):
|
|
344
|
+
def _open_ex_shutter(self):
|
|
345
345
|
self.check_result(self.driver.SetShutterEx(1, 1, self.SHUTTER_SPEED_MS, self.SHUTTER_SPEED_MS, 1), "SetShutterEx(1)")
|
|
346
346
|
self.settings.state.shutter_enabled = True
|
|
347
347
|
return SpectrometerResponse(True)
|
|
@@ -358,7 +358,7 @@ class AndorDevice(InterfaceDevice):
|
|
|
358
358
|
raise RuntimeError(msg)
|
|
359
359
|
log.debug(f"successfully called {func}")
|
|
360
360
|
|
|
361
|
-
def connect(self):
|
|
361
|
+
def connect(self):
|
|
362
362
|
if self.dll_fail:
|
|
363
363
|
return SpectrometerResponse(False, error_msg="can't find Andor DLL; please confirm Andor Driver Pack 2 installed")
|
|
364
364
|
|
|
@@ -490,17 +490,17 @@ class AndorDevice(InterfaceDevice):
|
|
|
490
490
|
if 'startup_temp_degC' in self.config_values:
|
|
491
491
|
self.set_tec_setpoint(self.settings.eeprom.startup_temp_degC)
|
|
492
492
|
|
|
493
|
-
def acquire_data(self):
|
|
493
|
+
def acquire_data(self):
|
|
494
494
|
reading = self._take_one_averaged_reading()
|
|
495
495
|
return reading
|
|
496
496
|
|
|
497
|
-
def set_shutter_enable(self, enable
|
|
497
|
+
def set_shutter_enable(self, enable):
|
|
498
498
|
if enable:
|
|
499
499
|
return self._open_ex_shutter()
|
|
500
500
|
else:
|
|
501
501
|
return self._close_ex_shutter()
|
|
502
502
|
|
|
503
|
-
def set_integration_time_ms(self, ms
|
|
503
|
+
def set_integration_time_ms(self, ms):
|
|
504
504
|
self.integration_time_ms = ms
|
|
505
505
|
log.debug(f"setting integration time to {self.integration_time_ms}ms")
|
|
506
506
|
|
|
@@ -566,7 +566,7 @@ class AndorDevice(InterfaceDevice):
|
|
|
566
566
|
self.check_result(self.driver.SetTemperature(self.setpoint_deg_c), f"SetTemperature({self.setpoint_deg_c})")
|
|
567
567
|
return SpectrometerResponse(True)
|
|
568
568
|
|
|
569
|
-
def init_detector_area(self):
|
|
569
|
+
def init_detector_area(self):
|
|
570
570
|
xPixels = c_int()
|
|
571
571
|
yPixels = c_int()
|
|
572
572
|
self.check_result(self.driver.GetDetector(byref(xPixels), byref(yPixels)), "GetDetector(x, y)")
|
|
@@ -592,7 +592,7 @@ class AndorDevice(InterfaceDevice):
|
|
|
592
592
|
self.gain_options = self.gain_options[::-1]
|
|
593
593
|
log.debug(f"obtained gain options for spec, values were {self.gain_options}")
|
|
594
594
|
|
|
595
|
-
def init_detector_speed(self):
|
|
595
|
+
def init_detector_speed(self):
|
|
596
596
|
speed = c_float()
|
|
597
597
|
|
|
598
598
|
# for CCDs, set vertical to recommended
|
|
@@ -623,7 +623,7 @@ class AndorDevice(InterfaceDevice):
|
|
|
623
623
|
log.debug(f"set AD channel {ADnumber} with horizontal speed {HSnumber} ({STemp})")
|
|
624
624
|
return SpectrometerResponse(True)
|
|
625
625
|
|
|
626
|
-
def scans_to_average(self, value
|
|
626
|
+
def scans_to_average(self, value):
|
|
627
627
|
self.sum_count = 0
|
|
628
628
|
self.settings.state.scans_to_average = int(value)
|
|
629
629
|
return SpectrometerResponse(True)
|