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.
Files changed (89) hide show
  1. {wasatch-2.2.0 → wasatch-2.2.14}/CHANGELOG.md +77 -0
  2. {wasatch-2.2.0 → wasatch-2.2.14}/PKG-INFO +1 -1
  3. wasatch-2.2.14/demo-auto-raman.py +130 -0
  4. {wasatch-2.2.0 → wasatch-2.2.14}/demo.py +30 -28
  5. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/AndorDevice.py +17 -17
  6. wasatch-2.2.14/wasatch/AutoRaman.py +451 -0
  7. wasatch-2.2.14/wasatch/AutoRamanRequest.py +96 -0
  8. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/BLEDevice.py +74 -56
  9. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/ControlObject.py +3 -1
  10. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/DeviceFinderUSB.py +3 -3
  11. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/EEPROM.py +285 -154
  12. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/FeatureIdentificationDevice.py +564 -384
  13. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/FirmwareRequirements.py +14 -6
  14. wasatch-2.2.14/wasatch/IMX385.py +179 -0
  15. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/OceanDevice.py +2 -1
  16. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/Reading.py +5 -0
  17. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/SPIDevice.py +5 -4
  18. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/SpectrometerResponse.py +1 -1
  19. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/SpectrometerSettings.py +53 -171
  20. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/SpectrometerState.py +2 -0
  21. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/TakeOneRequest.py +11 -2
  22. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/WasatchBus.py +2 -2
  23. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/WasatchDevice.py +65 -34
  24. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/WasatchDeviceWrapper.py +10 -11
  25. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/WrapperWorker.py +7 -3
  26. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/__init__.py +1 -1
  27. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/utils.py +3 -0
  28. {wasatch-2.2.0 → wasatch-2.2.14}/.example_pypirc +0 -0
  29. {wasatch-2.2.0 → wasatch-2.2.14}/.flake8 +0 -0
  30. {wasatch-2.2.0 → wasatch-2.2.14}/.gitignore +0 -0
  31. {wasatch-2.2.0 → wasatch-2.2.14}/.pylintrc +0 -0
  32. {wasatch-2.2.0 → wasatch-2.2.14}/Doxyfile +0 -0
  33. {wasatch-2.2.0 → wasatch-2.2.14}/LICENSE +0 -0
  34. {wasatch-2.2.0 → wasatch-2.2.14}/MANIFEST.in +0 -0
  35. {wasatch-2.2.0 → wasatch-2.2.14}/Makefile +0 -0
  36. {wasatch-2.2.0 → wasatch-2.2.14}/README.md +0 -0
  37. {wasatch-2.2.0 → wasatch-2.2.14}/README_PYPI.md +0 -0
  38. {wasatch-2.2.0 → wasatch-2.2.14}/README_RPI.md +0 -0
  39. {wasatch-2.2.0 → wasatch-2.2.14}/README_SETTINGS.md +0 -0
  40. {wasatch-2.2.0 → wasatch-2.2.14}/README_VIRTUAL.md +0 -0
  41. {wasatch-2.2.0 → wasatch-2.2.14}/WasatchShell/README.md +0 -0
  42. {wasatch-2.2.0 → wasatch-2.2.14}/WasatchShell/laser-test.py +0 -0
  43. {wasatch-2.2.0 → wasatch-2.2.14}/WasatchShell/load-test.py +0 -0
  44. {wasatch-2.2.0 → wasatch-2.2.14}/WasatchShell/one-shot.py +0 -0
  45. {wasatch-2.2.0 → wasatch-2.2.14}/WasatchShell/wasatch-shell.py +0 -0
  46. {wasatch-2.2.0 → wasatch-2.2.14}/demo-simple.py +0 -0
  47. {wasatch-2.2.0 → wasatch-2.2.14}/demo-virtual.py +0 -0
  48. {wasatch-2.2.0 → wasatch-2.2.14}/demo-workflow.py +0 -0
  49. {wasatch-2.2.0 → wasatch-2.2.14}/environments/conda-linux.yml +0 -0
  50. {wasatch-2.2.0 → wasatch-2.2.14}/environments/conda-macos.yml +0 -0
  51. {wasatch-2.2.0 → wasatch-2.2.14}/environments/conda-rpi.yml +0 -0
  52. {wasatch-2.2.0 → wasatch-2.2.14}/environments/conda-win10.yml +0 -0
  53. {wasatch-2.2.0 → wasatch-2.2.14}/pyproject.toml +0 -0
  54. {wasatch-2.2.0 → wasatch-2.2.14}/requirements.txt +0 -0
  55. {wasatch-2.2.0 → wasatch-2.2.14}/screenshots/multiplatform.png +0 -0
  56. {wasatch-2.2.0 → wasatch-2.2.14}/scripts/dark-vs-integ.py +0 -0
  57. {wasatch-2.2.0 → wasatch-2.2.14}/scripts/deploy +0 -0
  58. {wasatch-2.2.0 → wasatch-2.2.14}/setup.cfg +0 -0
  59. {wasatch-2.2.0 → wasatch-2.2.14}/testSpectrometers/SiG_785/eeprom/EEPROM-EM-9c65d19f4c.json +0 -0
  60. {wasatch-2.2.0 → wasatch-2.2.14}/testSpectrometers/SiG_785/readings/enlighten-20210329-094722-799520-WP-00686.csv +0 -0
  61. {wasatch-2.2.0 → wasatch-2.2.14}/testSpectrometers/SiG_785/readings/enlighten-20210329-094755-288958-WP-00686.csv +0 -0
  62. {wasatch-2.2.0 → wasatch-2.2.14}/testSpectrometers/WP-00887/eeprom/WP-00887-mock.json +0 -0
  63. {wasatch-2.2.0 → wasatch-2.2.14}/testSpectrometers/WP-00904/eeprom/WP-00904-mock.json +0 -0
  64. {wasatch-2.2.0 → wasatch-2.2.14}/testSpectrometers/WP-00913/eeprom/WP-00913-mock.json +0 -0
  65. {wasatch-2.2.0 → wasatch-2.2.14}/tests/existing_implementations.py +0 -0
  66. {wasatch-2.2.0 → wasatch-2.2.14}/tests/noise-by-setpoint.py +0 -0
  67. {wasatch-2.2.0 → wasatch-2.2.14}/tests/queues.py +0 -0
  68. {wasatch-2.2.0 → wasatch-2.2.14}/tests/unit.py +0 -0
  69. {wasatch-2.2.0 → wasatch-2.2.14}/udev/10-wasatch.rules +0 -0
  70. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/AbstractUSBDevice.py +0 -0
  71. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/BalanceAcquisition.py +0 -0
  72. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/CSVLoader.py +0 -0
  73. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/CommandSettings.py +0 -0
  74. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/DetectorROI.py +0 -0
  75. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/DetectorRegions.py +0 -0
  76. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/DeviceID.py +0 -0
  77. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/EEPROM_multical.sav +0 -0
  78. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/FPGAOptions.py +0 -0
  79. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/HardwareInfo.py +0 -0
  80. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/InterfaceDevice.py +0 -0
  81. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/MockUSBDevice.py +0 -0
  82. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/PollStatus.py +0 -0
  83. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/ProcessedReading.py +0 -0
  84. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/ROI.py +0 -0
  85. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/RealUSBDevice.py +0 -0
  86. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/SpectrometerRequest.py +0 -0
  87. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/StatusMessage.py +0 -0
  88. {wasatch-2.2.0 → wasatch-2.2.14}/wasatch/applog.py +0 -0
  89. {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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: wasatch
3
- Version: 2.2.0
3
+ Version: 2.2.14
4
4
  Summary: This package is a driver for Wasatch Photonics spectrometers
5
5
  Author-email: Wasatch Photonics <edort@wasatchphotonics.com>
6
6
  Description-Content-Type: text/markdown
@@ -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
- # ENVIRONMENT: (if using Miniconda3) #
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, argv=None):
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(argv)
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, argv):
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
- # parse argv into dict
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(use_sim = False)
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 reading
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
- # polling the subprocess until a reading is ready. In other apps,
220
- # we could do other things (like respond to GUI events) if
221
- # device.acquire_data() was None (meaning the next spectrum wasn't
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
- (self.args.scans_to_average > 1 and not reading.averaged) or
245
- (self.args.max > 0 and self.reading_count >= self.args.max)):
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 Memory: %11d" % (
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(sys.argv)
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): # -> 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): # -> dict[str, Callable[..., Any]]
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: bool): # -> SpectrometerResponse
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: list[float]): # -> None
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: bool): # -> SpectrometerResponse
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): # -> str
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): # -> bool
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): # -> SpectrometerResponse
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): # -> SpectrometerResponse
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): # -> SpectrometerResponse
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): # -> SpectrometerResponse
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: bool): # -> SpectrometerResponse
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: float): # -> SpectrometerResponse
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): # -> SpectrometerResponse
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): # -> SpectrometerResponse
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: int): # -> SpectrometerResponse
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)