wasatch 2.3.8__tar.gz → 2.3.24__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 (96) hide show
  1. {wasatch-2.3.8 → wasatch-2.3.24}/.gitignore +1 -0
  2. {wasatch-2.3.8 → wasatch-2.3.24}/CHANGELOG.md +79 -0
  3. {wasatch-2.3.8 → wasatch-2.3.24}/PKG-INFO +1 -1
  4. {wasatch-2.3.8 → wasatch-2.3.24}/README_PYPI.md +6 -0
  5. {wasatch-2.3.8 → wasatch-2.3.24}/demo-ids-raman.py +16 -0
  6. {wasatch-2.3.8 → wasatch-2.3.24}/pyproject.toml +1 -1
  7. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/AndorDevice.py +12 -4
  8. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/AutoRaman.py +27 -3
  9. wasatch-2.3.24/wasatch/BLEDevice.py +1411 -0
  10. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/DeviceFinderBLE.py +1 -1
  11. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/DeviceID.py +3 -0
  12. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/EEPROM.py +80 -20
  13. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/FeatureIdentificationDevice.py +76 -40
  14. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/IDSCamera.py +87 -27
  15. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/IDSDevice.py +59 -37
  16. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/InterfaceDevice.py +8 -0
  17. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/MockUSBDevice.py +0 -6
  18. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/Reading.py +17 -2
  19. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/SpectrometerSettings.py +23 -25
  20. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/SpectrometerState.py +8 -0
  21. wasatch-2.3.24/wasatch/USBCPowerConnectionState.py +67 -0
  22. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/WasatchDevice.py +63 -30
  23. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/WasatchDeviceWrapper.py +85 -48
  24. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/WrapperWorker.py +40 -38
  25. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/__init__.py +1 -1
  26. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/utils.py +23 -5
  27. wasatch-2.3.8/wasatch/BLEDevice.py +0 -1032
  28. {wasatch-2.3.8 → wasatch-2.3.24}/.example_pypirc +0 -0
  29. {wasatch-2.3.8 → wasatch-2.3.24}/.flake8 +0 -0
  30. {wasatch-2.3.8 → wasatch-2.3.24}/.pylintrc +0 -0
  31. {wasatch-2.3.8 → wasatch-2.3.24}/Doxyfile +0 -0
  32. {wasatch-2.3.8 → wasatch-2.3.24}/LICENSE +0 -0
  33. {wasatch-2.3.8 → wasatch-2.3.24}/MANIFEST.in +0 -0
  34. {wasatch-2.3.8 → wasatch-2.3.24}/Makefile +0 -0
  35. {wasatch-2.3.8 → wasatch-2.3.24}/README.md +0 -0
  36. {wasatch-2.3.8 → wasatch-2.3.24}/README_RPI.md +0 -0
  37. {wasatch-2.3.8 → wasatch-2.3.24}/README_SETTINGS.md +0 -0
  38. {wasatch-2.3.8 → wasatch-2.3.24}/README_VIRTUAL.md +0 -0
  39. {wasatch-2.3.8 → wasatch-2.3.24}/WasatchShell/README.md +0 -0
  40. {wasatch-2.3.8 → wasatch-2.3.24}/WasatchShell/laser-test.py +0 -0
  41. {wasatch-2.3.8 → wasatch-2.3.24}/WasatchShell/load-test.py +0 -0
  42. {wasatch-2.3.8 → wasatch-2.3.24}/WasatchShell/one-shot.py +0 -0
  43. {wasatch-2.3.8 → wasatch-2.3.24}/WasatchShell/wasatch-shell.py +0 -0
  44. {wasatch-2.3.8 → wasatch-2.3.24}/demo-auto-raman.py +0 -0
  45. {wasatch-2.3.8 → wasatch-2.3.24}/demo-simple.py +0 -0
  46. {wasatch-2.3.8 → wasatch-2.3.24}/demo-virtual.py +0 -0
  47. {wasatch-2.3.8 → wasatch-2.3.24}/demo-workflow.py +0 -0
  48. {wasatch-2.3.8 → wasatch-2.3.24}/demo.py +0 -0
  49. {wasatch-2.3.8 → wasatch-2.3.24}/environments/conda-linux.yml +0 -0
  50. {wasatch-2.3.8 → wasatch-2.3.24}/environments/conda-macos.yml +0 -0
  51. {wasatch-2.3.8 → wasatch-2.3.24}/environments/conda-rpi.yml +0 -0
  52. {wasatch-2.3.8 → wasatch-2.3.24}/environments/conda-win10.yml +0 -0
  53. {wasatch-2.3.8 → wasatch-2.3.24}/screenshots/multiplatform.png +0 -0
  54. {wasatch-2.3.8 → wasatch-2.3.24}/scripts/dark-vs-integ.py +0 -0
  55. {wasatch-2.3.8 → wasatch-2.3.24}/scripts/deploy +0 -0
  56. {wasatch-2.3.8 → wasatch-2.3.24}/testSpectrometers/SiG_785/eeprom/EEPROM-EM-9c65d19f4c.json +0 -0
  57. {wasatch-2.3.8 → wasatch-2.3.24}/testSpectrometers/SiG_785/readings/enlighten-20210329-094722-799520-WP-00686.csv +0 -0
  58. {wasatch-2.3.8 → wasatch-2.3.24}/testSpectrometers/SiG_785/readings/enlighten-20210329-094755-288958-WP-00686.csv +0 -0
  59. {wasatch-2.3.8 → wasatch-2.3.24}/testSpectrometers/WP-00887/eeprom/WP-00887-mock.json +0 -0
  60. {wasatch-2.3.8 → wasatch-2.3.24}/testSpectrometers/WP-00904/eeprom/WP-00904-mock.json +0 -0
  61. {wasatch-2.3.8 → wasatch-2.3.24}/testSpectrometers/WP-00913/eeprom/WP-00913-mock.json +0 -0
  62. {wasatch-2.3.8 → wasatch-2.3.24}/tests/existing_implementations.py +0 -0
  63. {wasatch-2.3.8 → wasatch-2.3.24}/tests/noise-by-setpoint.py +0 -0
  64. {wasatch-2.3.8 → wasatch-2.3.24}/tests/queues.py +0 -0
  65. {wasatch-2.3.8 → wasatch-2.3.24}/tests/unit.py +0 -0
  66. {wasatch-2.3.8 → wasatch-2.3.24}/udev/10-wasatch.rules +0 -0
  67. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/AbstractUSBDevice.py +0 -0
  68. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/AreaScanImage.py +0 -0
  69. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/AutoRamanRequest.py +0 -0
  70. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/BalanceAcquisition.py +0 -0
  71. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/CSVLoader.py +0 -0
  72. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/CommandSettings.py +0 -0
  73. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/ControlObject.py +0 -0
  74. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/DetectorROI.py +0 -0
  75. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/DetectorRegions.py +0 -0
  76. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/DeviceFinderTCP.py +0 -0
  77. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/DeviceFinderUSB.py +0 -0
  78. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/EEPROM_multical.sav +0 -0
  79. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/FPGAOptions.py +0 -0
  80. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/FirmwareRequirements.py +0 -0
  81. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/HardwareInfo.py +0 -0
  82. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/IMX385.py +0 -0
  83. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/OceanDevice.py +0 -0
  84. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/PollStatus.py +0 -0
  85. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/ProcessedReading.py +0 -0
  86. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/ROI.py +0 -0
  87. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/RealUSBDevice.py +0 -0
  88. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/SPIDevice.py +0 -0
  89. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/SpectrometerRequest.py +0 -0
  90. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/SpectrometerResponse.py +0 -0
  91. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/StatusMessage.py +0 -0
  92. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/TCPDevice.py +0 -0
  93. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/TakeOneRequest.py +0 -0
  94. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/WasatchBus.py +0 -0
  95. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/applog.py +0 -0
  96. {wasatch-2.3.8 → wasatch-2.3.24}/wasatch/simulation_protocol.py +0 -0
@@ -111,3 +111,4 @@ wasatch_applog.txt
111
111
  .DS_Store
112
112
  .vs/*
113
113
  environment.yml
114
+ data/
@@ -1,5 +1,84 @@
1
1
  # Changelog
2
2
 
3
+ - 2026-04-18 2.3.24
4
+ - add avg_resolution to IDSDevice EEPROM
5
+ - change XS attenuator default from 127 to 40
6
+ - 2026-04-16 2.3.23
7
+ - XS BLE
8
+ - tidied BLEDevice
9
+ - added USBCPowerConnectionState
10
+ - added SpectrometerState.power_connection_state
11
+ - added SpectrometerSettings.cpu_unique_id
12
+ - added SET_POWER_OFF, SET_RESET_UNIT, GET/SET_AUTO_RAMAN_PARAMS,
13
+ GET_IMAGE_SENSOR_STATE, GET_CPU_UNIQUE_ID, GET_USB_ADAPTER_INFO,
14
+ GET/SET_LASER_OFF_DELAY_MS
15
+ - suffixed all async methods with _async
16
+ - provided synchronous wrappers around user-facing asynchronous calls
17
+ - added BLEDevice.testing for enlighten.plugins.RnD.BluetoothTester
18
+ - battery percentage matches USB
19
+ - ambient_temperature_deg_c now flowing up to ENLIGHTEN
20
+ - POWER_OFF working
21
+ - decoding STM32 CPU_UNIQUE_ID (debug-only)
22
+ - ignore 1-line vertical ROI
23
+ - SET_AUTO_RAMAN_PARAMS bugfix
24
+ - 2026-04-10 2.3.22
25
+ - added Reading.protocol
26
+ - XS
27
+ - disable detector timeout during area scan
28
+ - adding Auto-Raman to BLEDevice
29
+ - only use one ACQUIRE to start Area Scan readout
30
+ - send extra ACQUIRE on occasional timeout
31
+ - 2026-04-09 2.3.21
32
+ - XS Area Scan
33
+ - fixed line increment
34
+ - disable area scan while changing line increment
35
+ - XS BLE
36
+ - move firmware revisions to SpectrometerSettings
37
+ - fix gain
38
+ - fix laser enable
39
+ - fixed heartbeat
40
+ - working on LASER_STATE and BATTERY_STATE
41
+ - 2026-04-09 2.3.20
42
+ - XS BLE
43
+ - fix None in utils.to_hex
44
+ - only parse first 8 EEPROM fields until BLE FW updated to support 9
45
+ - stop checking for old FX2 code string in corrupted EEPROMs
46
+ - support V2.EVT prefix
47
+ - add new ACQUIRE status codes
48
+ - add new SPECTRA Characteristic
49
+ - read as many EEPROM bytes per page as supported by Peripheral
50
+ - 2026-04-06 2.3.18
51
+ - replaced WrapperWorker.is_FOO with .class_name
52
+ - XL / Andor
53
+ - only warn once on missing driver
54
+ - more robust support for blank model
55
+ - default EEPROM.model
56
+ - XS
57
+ - support is_laser_firing on all XS
58
+ - 2026-04-01 2.3.17
59
+ - adding Reading.is_auto_raman()
60
+ - temporarily bumping XS spectra timeout
61
+ - filling in standard device meta-data in Auto-Raman Readings
62
+ - 2026-03-30 2.3.16
63
+ - assume all XS have interlock feedback
64
+ - 2026-03-13 2.3.15
65
+ - capture changes to vertical ROI in IDSDevice's EEPROM
66
+ - 2026-03-09 2.3.14
67
+ - save IDS detector in EEPROM
68
+ - 2026-03-02 2.3.13
69
+ - add experimental FPGA reset for InGaAs
70
+ - add Reading.image_format
71
+ - 2026-02-17 2.3.12
72
+ - added laser_password, startup_scans_to_average, laser_attenuator
73
+ - added [un]pack_field
74
+ - un-rotated Hamamatsu area scan 180 deg :-(
75
+ - allow IDS config files to be located by serial number prefix
76
+ - 2026-02-09 2.3.11
77
+ - rotate Hamamatsu area scan 180 deg
78
+ - 2026-01-16 2.3.10
79
+ - support 2min integrations on STARVIS
80
+ - 2026-01-15 2.3.9
81
+ - added --normalize to demo-ids-raman.py
3
82
  - 2026-01-12 2.3.8
4
83
  - added settings.has_detector
5
84
  - improved support for Andor .json "virtual EEPROMs"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wasatch
3
- Version: 2.3.8
3
+ Version: 2.3.24
4
4
  Summary: Application-level driver for Wasatch Photonics spectrometers
5
5
  Requires-Python: >=3.9
6
6
  Description-Content-Type: text/markdown
@@ -50,6 +50,12 @@ before uploading to the "public, global, default" pypi repository.
50
50
  When you are ready to publish run `flit publish` or `flit publish --repository pypi`
51
51
  (or just "make publish").
52
52
 
53
+ ## Common Errors
54
+
55
+ ### requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://test.pypi.org/legacy/
56
+
57
+ Ensure wasatch/__init__.py version matches pyproject.toml.
58
+
53
59
  # Installing 'wasatch' package from source
54
60
 
55
61
  If you've cloned the Wasatch.PY repo and want to just install the `wasatch`
@@ -26,6 +26,7 @@ import argparse
26
26
 
27
27
  from datetime import datetime
28
28
  from time import sleep
29
+ from PIL import Image, ImageStat # for image normalization
29
30
 
30
31
  import wasatch
31
32
  from wasatch import applog
@@ -59,6 +60,7 @@ class WasatchDemo:
59
60
  parser.add_argument("--save-spectra", action="store_true", help="save vertically-binned spectra to row-ordered CSV")
60
61
  parser.add_argument("--save-png", action="store_true", help="save PNG of each image frame")
61
62
  parser.add_argument("--save-data", action="store_true", help="save 2D array of each image frame")
63
+ parser.add_argument("--normalize", action="store_true", help="normalize PNG image")
62
64
  parser.add_argument("--save-dir", type=str, default=".", help="directory to save files")
63
65
  parser.add_argument("--prefix", type=str, default="ids-raman", help="filename prefix")
64
66
 
@@ -150,6 +152,9 @@ class WasatchDemo:
150
152
  ############################################################################
151
153
 
152
154
  def run(self):
155
+ if self.args.save_dir != ".":
156
+ os.makedirs(self.args.save_dir, exist_ok=True)
157
+
153
158
  self.camera_device.set_integration_time_ms(self.args.integration_time_ms)
154
159
 
155
160
  # initialize outfile if saving spectra
@@ -229,6 +234,17 @@ class WasatchDemo:
229
234
  os.replace(asi.pathname_png, filename)
230
235
  print(f"\tsaved {filename}")
231
236
 
237
+ # normalize PNG (just the image file, not the raw 2D data)
238
+ if self.args.normalize:
239
+ img = Image.open(filename).convert('L')
240
+ stat = ImageStat.Stat(img)
241
+ mean_brightness = stat.mean[0]
242
+ img_array = np.array(img)
243
+ normalized_img_array = img_array / mean_brightness * 100
244
+ normalized_img = Image.fromarray(np.uint8(normalized_img_array))
245
+ normalized_img.save(filename)
246
+ print(f"\tnormalized {filename}")
247
+
232
248
  # save area scan data
233
249
  if self.args.save_data and asi.data is not None:
234
250
  filename = os.path.join(self.args.save_dir, f"{self.args.prefix}-{self.reading_count:04d}.csv")
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "wasatch"
3
- version = "2.3.8"
3
+ version = "2.3.24"
4
4
  description= "Application-level driver for Wasatch Photonics spectrometers"
5
5
  requires-python = ">=3.9"
6
6
  readme = "README.md"
@@ -10,7 +10,7 @@ from ctypes import *
10
10
 
11
11
  from .SpectrometerSettings import SpectrometerSettings
12
12
  from .SpectrometerResponse import SpectrometerResponse
13
- from .InterfaceDevice import InterfaceDevice
13
+ from .InterfaceDevice import InterfaceDevice, InterfaceDeviceClassUnavailable
14
14
  from .StatusMessage import StatusMessage
15
15
  from .DeviceID import DeviceID
16
16
  from .Reading import Reading
@@ -52,9 +52,15 @@ class AndorDevice(InterfaceDevice):
52
52
  SHUTTER_SPEED_MS = 50 #!< allow time for mechanical shutter to stabilize
53
53
  TEMPERATURE_CACHE_SEC = 1.0
54
54
 
55
+ DRIVER_NOT_INSTALLED = False
56
+
55
57
  def __init__(self, device_id, message_queue=None, alert_queue=None):
56
58
  # if passed a string representation of a DeviceID, deserialize it
57
59
  super().__init__()
60
+
61
+ if self.DRIVER_NOT_INSTALLED:
62
+ raise InterfaceDeviceClassUnavailable("Andor driver not installed")
63
+
58
64
  if type(device_id) is str:
59
65
  device_id = DeviceID(label=device_id)
60
66
 
@@ -119,8 +125,9 @@ class AndorDevice(InterfaceDevice):
119
125
  break
120
126
 
121
127
  if self.driver is None:
122
- log.error(f"could not find {filename} in search path: {dll_paths}")
123
- # MZ: interesting that we don't return here
128
+ # only need to display / log this once per session
129
+ self.DRIVER_NOT_INSTALLED = True
130
+ raise InterfaceDeviceClassUnavailable(f"could not find {filename} in search path: {dll_paths}")
124
131
 
125
132
  # "serial_number", "model" etc are ambiguous in an Andor configuration
126
133
  # file -- do they refer to the camera (Andor), or the spectrometer
@@ -135,8 +142,9 @@ class AndorDevice(InterfaceDevice):
135
142
  # (all but has_cooling can be overridden via config file)
136
143
 
137
144
  # Andor API doesn't(?) seem to have access to detector info.
138
- # Note that we use non-iDus cameras, including the Newton.
145
+ # Note that we use non-iDus cameras, including the Newton and iVac.
139
146
  self.settings.eeprom.detector = "iDus"
147
+ self.settings.eeprom.model = "WP-XL"
140
148
 
141
149
  self.settings.eeprom.has_cooling = True
142
150
  self.settings.eeprom.startup_integration_time_ms = 10
@@ -56,7 +56,7 @@ class AutoRaman:
56
56
  any point.
57
57
  """
58
58
 
59
- INTER_SPECTRA_DELAY_MS = 50
59
+ INTER_SPECTRA_DELAY_MS = 100
60
60
 
61
61
  def __init__(self, wasatch_device):
62
62
  self.wasatch_device = wasatch_device
@@ -109,6 +109,18 @@ class AutoRaman:
109
109
  reading.laser_power_perc = hardware.settings.state.laser_power_perc
110
110
  reading.laser_power_mW = hardware.settings.state.laser_power_mW
111
111
 
112
+ # fill-out some otherwise missing attributes from WasatchDevice.acquire_spectrum_standard
113
+ setting_to_attr = [ ('can_laser_fire', 'laser_can_fire'),
114
+ ('is_laser_firing', 'laser_is_firing') ]
115
+ if self.settings.is_xs() and self.settings.eeprom.sig_laser_tec:
116
+ setting_to_attr.extend( [ ('get_laser_tec_mode', 'laser_tec_enabled'),
117
+ ('get_ambient_temperature_degC', 'ambient_temperature_degC') ] )
118
+ for (setting, attr_name) in setting_to_attr:
119
+ request = SpectrometerRequest(setting)
120
+ result = self.hardware.handle_requests([request])[0]
121
+ if result is not None:
122
+ setattr(reading, attr_name, result.data)
123
+
112
124
  return SpectrometerResponse(data=reading)
113
125
 
114
126
  ############################################################################
@@ -236,7 +248,7 @@ class AutoRaman:
236
248
 
237
249
  # enable the laser and wait for it to fire
238
250
  self.set_laser_enable(True)
239
- self.hardware.queue_message("laser_firing_indicators", True)
251
+ self.hardware.queue_message("laser_firing_indicators", "firing")
240
252
  warning_delay_sec = self.get_laser_warning_delay_sec()
241
253
  if warning_delay_sec > 0:
242
254
  self.hardware.queue_message("marquee_info", f"waiting {warning_delay_sec}sec for laser to fire")
@@ -399,7 +411,7 @@ class AutoRaman:
399
411
 
400
412
  # 2. turn laser off
401
413
  self.set_laser_enable(False)
402
- self.hardware.queue_message("laser_firing_indicators", False)
414
+ self.hardware.queue_message("laser_firing_indicators", "not firing")
403
415
 
404
416
  # 3. take dark
405
417
  self.hardware.queue_message("marquee_info", f"averaging {num_avg} dark spectra at {int_time}ms")
@@ -422,6 +434,18 @@ class AutoRaman:
422
434
  reading.laser_power_perc = self.hardware.settings.state.laser_power_perc
423
435
  reading.laser_power_mW = self.hardware.settings.state.laser_power_mW
424
436
 
437
+ # fill-out some otherwise missing attributes from WasatchDevice.acquire_spectrum_standard
438
+ setting_to_attr = [ ('can_laser_fire', 'laser_can_fire'),
439
+ ('is_laser_firing', 'laser_is_firing') ]
440
+ if self.settings.is_xs() and self.settings.eeprom.sig_laser_tec:
441
+ setting_to_attr.extend( [ ('get_laser_tec_mode', 'laser_tec_enabled'),
442
+ ('get_ambient_temperature_degC', 'ambient_temperature_degC') ] )
443
+ for (setting, attr_name) in setting_to_attr:
444
+ request = SpectrometerRequest(setting)
445
+ result = self.hardware.handle_requests([request])[0]
446
+ if result is not None:
447
+ setattr(reading, attr_name, result.data)
448
+
425
449
  log.debug("done")
426
450
  return reading
427
451