horiba-sdk 0.3.3__py3-none-any.whl → 0.5.2__py3-none-any.whl

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 (27) hide show
  1. horiba_sdk/__init__.py +5 -3
  2. horiba_sdk/communication/websocket_communicator.py +2 -2
  3. horiba_sdk/core/__init__.py +0 -0
  4. horiba_sdk/core/acquisition_format.py +20 -0
  5. horiba_sdk/core/clean_count_mode.py +13 -0
  6. horiba_sdk/core/timer_resolution.py +14 -0
  7. horiba_sdk/core/x_axis_conversion_type.py +18 -0
  8. horiba_sdk/devices/ccd_discovery.py +10 -12
  9. horiba_sdk/devices/device_manager.py +24 -10
  10. horiba_sdk/devices/fake_responses/ccd.json +261 -12
  11. horiba_sdk/devices/fake_responses/monochromator.json +38 -10
  12. horiba_sdk/devices/monochromator_discovery.py +16 -9
  13. horiba_sdk/devices/single_devices/abstract_device.py +46 -1
  14. horiba_sdk/devices/single_devices/ccd.py +388 -143
  15. horiba_sdk/devices/single_devices/monochromator.py +87 -71
  16. horiba_sdk/sync/communication/abstract_communicator.py +2 -3
  17. horiba_sdk/sync/communication/websocket_communicator.py +38 -18
  18. horiba_sdk/sync/devices/device_discovery.py +13 -37
  19. horiba_sdk/sync/devices/device_manager.py +14 -10
  20. horiba_sdk/sync/devices/fake_icl_server.py +9 -6
  21. horiba_sdk/sync/devices/single_devices/abstract_device.py +11 -7
  22. horiba_sdk/sync/devices/single_devices/ccd.py +517 -62
  23. horiba_sdk/sync/devices/single_devices/monochromator.py +288 -25
  24. {horiba_sdk-0.3.3.dist-info → horiba_sdk-0.5.2.dist-info}/METADATA +166 -92
  25. {horiba_sdk-0.3.3.dist-info → horiba_sdk-0.5.2.dist-info}/RECORD +27 -22
  26. {horiba_sdk-0.3.3.dist-info → horiba_sdk-0.5.2.dist-info}/LICENSE +0 -0
  27. {horiba_sdk-0.3.3.dist-info → horiba_sdk-0.5.2.dist-info}/WHEEL +0 -0
@@ -1,14 +1,15 @@
1
- from enum import Enum
2
1
  from types import TracebackType
3
- from typing import Any, Optional, Union, final
2
+ from typing import Any, Optional, final
4
3
 
5
- import pint
6
4
  from loguru import logger
7
5
  from overrides import override
8
6
 
9
- from horiba_sdk import ureg
10
7
  from horiba_sdk.communication import Response
8
+ from horiba_sdk.core.acquisition_format import AcquisitionFormat
9
+ from horiba_sdk.core.clean_count_mode import CleanCountMode
11
10
  from horiba_sdk.core.resolution import Resolution
11
+ from horiba_sdk.core.timer_resolution import TimerResolution
12
+ from horiba_sdk.core.x_axis_conversion_type import XAxisConversionType
12
13
  from horiba_sdk.icl_error import AbstractErrorDB
13
14
  from horiba_sdk.sync.communication.abstract_communicator import AbstractCommunicator
14
15
  from horiba_sdk.sync.devices.single_devices.abstract_device import AbstractDevice
@@ -22,17 +23,12 @@ class ChargeCoupledDevice(AbstractDevice):
22
23
  should be used to access the detected CCDs on the system.
23
24
  """
24
25
 
25
- @final
26
- class XAxisConversionType(Enum):
27
- NONE = 0
28
- FROM_CCD_FIRMWARE = 1
29
- FROM_ICL_SETTINGS_INI = 2
30
-
31
26
  def __init__(self, device_id: int, communicator: AbstractCommunicator, error_db: AbstractErrorDB) -> None:
32
27
  super().__init__(device_id, communicator, error_db)
33
28
 
34
29
  def __enter__(self) -> 'ChargeCoupledDevice':
35
30
  self.open()
31
+ self._config: dict[str, Any] = self.get_configuration()
36
32
  return self
37
33
 
38
34
  def __exit__(
@@ -41,8 +37,7 @@ class ChargeCoupledDevice(AbstractDevice):
41
37
  exc_value: Optional[BaseException],
42
38
  traceback: Optional[TracebackType],
43
39
  ) -> None:
44
- is_open = self.is_open()
45
- if not is_open:
40
+ if not self.is_open():
46
41
  logger.debug('CCD is already closed')
47
42
  return
48
43
 
@@ -76,88 +71,168 @@ class ChargeCoupledDevice(AbstractDevice):
76
71
  response: Response = super()._execute_command('ccd_isOpen', {'index': self._id})
77
72
  return bool(response.results['open'])
78
73
 
79
- def get_temperature(self) -> pint.Quantity:
80
- """Chip temperature of the CCD.
74
+ def restart(self) -> None:
75
+ """Restarts the charge coupled device.
76
+
77
+ This command only works if the camera has been opened before.
78
+ The connection to the camera stays open after the restart.
79
+
80
+ Raises:
81
+ Exception: When an error occurred on the device side
82
+ """
83
+ super()._execute_command('ccd_restart', {'index': self._id})
84
+
85
+ def get_configuration(self) -> dict[str, Any]:
86
+ """Returns the configuration of the CCD
81
87
 
82
88
  Returns:
83
- pint.Quantity: chip's temperature in degree Celsius
89
+ dict[str, Any]: Configuration of the CCD
84
90
 
85
91
  Raises:
86
92
  Exception: When an error occurred on the device side
87
93
  """
88
- response: Response = super()._execute_command('ccd_getChipTemperature', {'index': self._id})
89
- return ureg.Quantity(response.results['temperature'], ureg.degC) # type: ignore
94
+ response: Response = super()._execute_command('ccd_getConfig', {'index': self._id})
95
+ return response.results['configuration']
90
96
 
91
- def get_chip_size(self) -> Resolution:
92
- """Chip resolution of the CCD.
97
+ def get_gain_token(self) -> int:
98
+ """Returns the current gain token.
93
99
 
94
100
  Returns:
95
- Resolution: chip resolution (width, height)
101
+ int: Gain token of the ccd
96
102
 
97
103
  Raises:
98
104
  Exception: When an error occurred on the device side
99
105
  """
100
- response: Response = super()._execute_command('ccd_getChipSize', {'index': self._id})
101
- width: int = response.results['x']
102
- height: int = response.results['y']
103
- resolution: Resolution = Resolution(width, height)
104
- return resolution
106
+ response: Response = super()._execute_command('ccd_getGain', {'index': self._id})
107
+ gain: int = int(response.results['token'])
108
+ return gain
109
+
110
+ def set_gain(self, gain_token: int) -> None:
111
+ """Sets the gain of the CCD
112
+
113
+ Args:
114
+ gain_token (int): Token of the desired gain
115
+
116
+ Raises:
117
+ Exception: When an error occurred on the device side
118
+ """
119
+ super()._execute_command('ccd_setGain', {'index': self._id, 'token': gain_token})
105
120
 
106
- def get_speed(self) -> Union[pint.Quantity, None]:
107
- """Chip transfer speed in kHz
121
+ def get_speed_token(self) -> int:
122
+ """Returns the speed token.
108
123
 
109
124
  Returns:
110
- pint.Quantity: Transfer speed in kilo Hertz
125
+ int: Speed token of the CCD.
111
126
 
112
127
  Raises:
113
128
  Exception: When an error occurred on the device side
114
129
  """
115
130
  response: Response = super()._execute_command('ccd_getSpeed', {'index': self._id})
116
- return ureg(response.results['info'])
131
+ speed_token: int = int(response.results['token'])
132
+ return speed_token
117
133
 
118
- def get_exposure_time(self) -> Union[pint.Quantity, None]:
119
- """Returns the exposure time in ms
134
+ def set_speed(self, speed_token: int) -> None:
135
+ """Sets the speed of the CCD
136
+
137
+ Args:
138
+ speed_token (int): Token of the desired speed.
139
+
140
+ Raises:
141
+ Exception: When an error occurred on the device side
142
+ """
143
+ super()._execute_command('ccd_setSpeed', {'index': self._id, 'token': speed_token})
144
+
145
+ def get_parallel_speed(self) -> int:
146
+ """Gets the current parallel speed token
120
147
 
121
148
  Returns:
122
- pint.Quantity: Exposure time in ms
149
+ int: current parallel speed token
150
+
123
151
  Raises:
124
152
  Exception: When an error occurred on the device side
125
153
  """
126
- response: Response = super()._execute_command('ccd_getExposureTime', {'index': self._id})
127
- exposure = ureg.Quantity(response.results['time'], 'ms')
128
- return exposure
154
+ response: Response = super()._execute_command('ccd_getParallelSpeed', {'index': self._id})
155
+ parallel_speed_token: int = int(response.results['token'])
156
+ return parallel_speed_token
129
157
 
130
- def set_exposure_time(self, exposure_time_ms: int) -> None:
131
- """Sets the exposure time in ms
158
+ def set_parallel_speed(self, parallel_speed_token: int) -> None:
159
+ """Sets the desired parallel speed token
132
160
 
133
- Args:
134
- exposure_time_ms (int): Exposure time in ms
135
161
  Raises:
136
162
  Exception: When an error occurred on the device side
137
163
  """
164
+ super()._execute_command('ccd_setParallelSpeed', {'index': self._id, 'token': parallel_speed_token})
138
165
 
139
- super()._execute_command('ccd_setExposureTime', {'index': self._id, 'time': exposure_time_ms})
166
+ def get_fit_parameters(self) -> list[int]:
167
+ """Returns the fit parameters of the CCD
140
168
 
141
- def get_acquisition_ready(self) -> bool:
142
- """Returns true if the CCD is ready to acquire
169
+ Returns:
170
+ List[int]: Fit parameters
171
+
172
+ Raises:
173
+ Exception: When an error occurred on the device side
174
+ """
175
+ response: Response = super()._execute_command('ccd_getFitParams', {'index': self._id})
176
+ fit_params: list[int] = response.results['fitParameters']
177
+ return fit_params
178
+
179
+ def set_fit_parameters(self, fit_params: list[int]) -> None:
180
+ """Sets the fit parameters of the CCD
181
+
182
+ Args:
183
+ fit_params (List[int]): Fit parameters
184
+
185
+ Raises:
186
+ Exception: When an error occurred on the device side
187
+ """
188
+ fit_params_str: str = ','.join(map(str, fit_params))
189
+ super()._execute_command('ccd_setFitParams', {'index': self._id, 'params': fit_params_str})
190
+
191
+ def get_timer_resolution(self) -> TimerResolution:
192
+ """Returns the timer resolution of the CCD in microseconds [μs]
143
193
 
144
194
  Returns:
145
- bool: True if the CCD is ready to acquire
195
+ int: Timer resolution in microseconds [μs]
196
+
146
197
  Raises:
147
198
  Exception: When an error occurred on the device side
148
199
  """
149
- response: Response = super()._execute_command('ccd_getAcquisitionReady', {'index': self._id})
150
- return bool(response.results['ready'])
200
+ response: Response = super()._execute_command('ccd_getTimerResolution', {'index': self._id})
201
+ timer_resolution: int = int(response.results['resolutionToken'])
202
+ return TimerResolution(timer_resolution)
203
+
204
+ def set_timer_resolution(self, timer_resolution: TimerResolution) -> None:
205
+ """Sets the timer resolution of the CCD
206
+
207
+ .. note:: The timer resolution value of 1 microsecond is not supported by all CCDs.
208
+
209
+ Args:
210
+ timer_resolution (int): Timer resolution
211
+
212
+ Raises:
213
+ Exception: When an error occurred on the device side
214
+ """
215
+ super()._execute_command(
216
+ 'ccd_setTimerResolution', {'index': self._id, 'resolutionToken': timer_resolution.value}
217
+ )
151
218
 
152
- def set_acquisition_start(self, open_shutter: bool) -> None:
153
- """Starts the acquisition of the CCD
219
+ def set_acquisition_format(self, number_of_rois: int, acquisition_format: AcquisitionFormat) -> None:
220
+ """Sets the acquisition format and the number of ROIs (Regions of Interest) or areas.
221
+
222
+ After using this command to set the number of ROIs and format, the set_region_of_interest function
223
+ should be used to define each ROI. Note: The Crop and Fast Kinetics acquisition formats are not
224
+ supported by every CCD.
154
225
 
155
226
  Args:
156
- open_shutter (bool): Whether the shutter of the camera should be open
227
+ number_of_rois (int): Number of regions of interest
228
+ acquisition_format (AcquisitionFormat): Acquisition format
229
+
157
230
  Raises:
158
231
  Exception: When an error occurred on the device side
159
232
  """
160
- super()._execute_command('ccd_setAcquisitionStart', {'index': self._id, 'openShutter': open_shutter})
233
+ super()._execute_command(
234
+ 'ccd_setAcqFormat', {'index': self._id, 'format': acquisition_format.value, 'numberOfRois': number_of_rois}
235
+ )
161
236
 
162
237
  def set_region_of_interest(
163
238
  self,
@@ -173,7 +248,7 @@ class ChargeCoupledDevice(AbstractDevice):
173
248
  an example json command looks like this:
174
249
 
175
250
  Args:
176
- roi_index (int, optional): Index of the region of interest. Defaults to 1.
251
+ roi_index (int, optional): One based index of the region of interest. Defaults to 1.
177
252
  x_origin (int, optional): X origin of the region of interest. Defaults to 0.
178
253
  y_origin (int, optional): Y origin of the region of interest. Defaults to 0.
179
254
  x_size (int, optional): X size of the region of interest. Defaults to 1024.
@@ -198,22 +273,402 @@ class ChargeCoupledDevice(AbstractDevice):
198
273
  },
199
274
  )
200
275
 
201
- def get_acquisition_data(self) -> dict[Any, Any]:
202
- """Returns the acquisition data of the CCD
203
- nina: atm this returns data still formatted for telnet communication, not formatted as json"""
204
- response: Response = super()._execute_command('ccd_getAcquisitionData', {'index': self._id})
205
- return response.results
276
+ def set_x_axis_conversion_type(self, conversion_type: XAxisConversionType) -> None:
277
+ """Sets the X-axis pixel conversion type to be used when retrieving the acquisition data with the
278
+ ccd_getAcquisitionData command.
279
+ 0 = None (default)
280
+ 1 = CCD FIT parameters contained in the CCD firmware
281
+ 2 = Mono Wavelength parameters contained in the icl_settings.ini file
282
+
283
+ Args:
284
+ conversion_type (XAxisConversionType): Conversion type Integer. The X-axis pixel conversion type to be used.
285
+
286
+ """
287
+ super()._execute_command('ccd_setXAxisConversionType', {'index': self._id, 'type': conversion_type.value})
288
+
289
+ def get_x_axis_conversion_type(self) -> XAxisConversionType:
290
+ """Gets the conversion type of the x axis.
291
+ 0 = None (default)
292
+ 1 = CCD FIT parameters contained in the CCD firmware
293
+ 2 = Mono Wavelength parameters contained in the icl_settings.ini file
294
+ """
295
+ response: Response = super()._execute_command('ccd_getXAxisConversionType', {'index': self._id})
296
+ return XAxisConversionType(response.results['type'])
297
+
298
+ def set_acquisition_count(self, count: int) -> None:
299
+ """Sets the number of acquisition measurements to be performed sequentially by the hardware.
300
+
301
+ A count > 1 is commonly referred to as "MultiAcq".
302
+
303
+ Args:
304
+ count (int): The number of acquisition measurements.
305
+ """
306
+ super()._execute_command('ccd_setAcqCount', {'index': self._id, 'count': count})
307
+
308
+ def get_acquisition_count(self) -> int:
309
+ """Gets the number of acquisitions to be performed. The acquisition count is used to perform multiple
310
+ acquisitions in a row.
311
+ """
312
+ response: Response = super()._execute_command('ccd_getAcqCount', {'index': self._id})
313
+ return int(response.results['count'])
314
+
315
+ def get_clean_count(self) -> tuple[int, CleanCountMode]:
316
+ """Gets the number of cleans to be performed prior to measurement.
317
+
318
+ Returns:
319
+ Tuple[int, CleanCountMode]:
320
+ count: Number of cleans,
321
+ mode: Specifies how the cleans will be performed.
322
+ """
323
+ response: Response = super()._execute_command('ccd_getCleanCount', {'index': self._id})
324
+ count: int = int(response.results['count'])
325
+ mode: CleanCountMode = CleanCountMode(response.results['mode'])
326
+ return count, mode
327
+
328
+ def set_clean_count(self, count: int, mode: CleanCountMode) -> None:
329
+ """Sets the clean count mode of the CCD and the according mode
330
+ Args:
331
+ count (int): The number of acquisitions to be performed.
332
+ mode (CleanCountMode): The mode of the clean count
333
+ """
334
+ super()._execute_command('ccd_setCleanCount', {'index': self._id, 'count': count, 'mode': mode.value})
335
+
336
+ def get_acquisition_data_size(self) -> int:
337
+ """Returns the size of the acquisition data of the CCD
338
+
339
+ Returns:
340
+ int: Size of the data
341
+
342
+ Raises:
343
+ Exception: When an error occurred on the device side
344
+ """
345
+ response: Response = super()._execute_command('ccd_getDataSize', {'index': self._id})
346
+ return int(response.results['size'])
347
+
348
+ def get_chip_temperature(self) -> float:
349
+ """Chip temperature of the CCD.
350
+
351
+ Returns:
352
+ float: chip's temperature in degree Celsius
353
+
354
+ Raises:
355
+ Exception: When an error occurred on the device side
356
+ """
357
+ response: Response = super()._execute_command('ccd_getChipTemperature', {'index': self._id})
358
+ return float(response.results['temperature'])
359
+
360
+ def get_chip_size(self) -> Resolution:
361
+ """Chip resolution of the CCD.
362
+
363
+ Returns:
364
+ Resolution: chip resolution (width, height)
365
+
366
+ Raises:
367
+ Exception: When an error occurred on the device side
368
+ """
369
+ response: Response = super()._execute_command('ccd_getChipSize', {'index': self._id})
370
+ width: int = response.results['x']
371
+ height: int = response.results['y']
372
+ resolution: Resolution = Resolution(width, height)
373
+ return resolution
374
+
375
+ def get_exposure_time(self) -> int:
376
+ """Returns the exposure time in ms
377
+
378
+ Returns:
379
+ pint.Quantity: Exposure time in ms
380
+ Raises:
381
+ Exception: When an error occurred on the device side
382
+ """
383
+ response: Response = super()._execute_command('ccd_getExposureTime', {'index': self._id})
384
+ exposure = int(response.results['time'])
385
+ return exposure
386
+
387
+ def set_exposure_time(self, exposure_time: int) -> None:
388
+ """Sets the exposure time in timer resolution units (1us or 1000us)
389
+
390
+ Examples:
391
+ - If exposure_time is set to 50, and the timer resolution value is 1000, the CCD exposure time
392
+ (integration time) = 50 milliseconds.
393
+ - If exposure_time is set to 50, and the timer resolution value is 1, the CCD exposure time
394
+ (integration time) = 50 microseconds.
395
+
396
+ Args:
397
+ exposure_time (int): Exposure time in timer resolution units (1us or 1000us)
398
+ Raises:
399
+ Exception: When an error occurred on the device side
400
+ """
401
+
402
+ super()._execute_command('ccd_setExposureTime', {'index': self._id, 'time': exposure_time})
403
+
404
+ def get_trigger_input(self) -> tuple[bool, int, int, int]:
405
+ """This command is used to get the current setting of the input trigger.
406
+
407
+ The address, event, and signalType parameters are used to define the input trigger based on the
408
+ supported options of that particular CCD.
409
+
410
+ The supported trigger options are retrieved using the get_configuration function, and begin with the
411
+ “Triggers” string contained in the configuration.
412
+
413
+ Returns:
414
+ Tuple[bool, int, int, int]:
415
+ enabled: Specifies if the signal is enabled (e.g. False = Disabled),
416
+ address: used to specify where the trigger is located. (e.g. 0 = Trigger Input).
417
+ Note: Value of -1 indicates that the input trigger is disabled,
418
+ event: used to specify when the trigger event should occur. (e.g. 0 = Once - Start All)
419
+ Note: Value of -1 indicates that the input trigger is disabled,
420
+ signal type: used to specify how the signal will cause the input trigger. (e.g. 0 = TTL Falling Edge)
421
+ Note: Value of -1 indicates that the input trigger is disabled,
422
+
423
+ Raises:
424
+ Exception: When an error occurred on the device side
425
+ """
426
+ response: Response = super()._execute_command('ccd_getTriggerIn', {'index': self._id})
427
+ address = int(response.results['address'])
428
+ event = int(response.results['event'])
429
+ signal_type = int(response.results['signalType'])
430
+ enabled = address > -1 and event > -1 and signal_type > -1
431
+ return enabled, address, event, signal_type
432
+
433
+ def set_trigger_input(self, enabled: bool, address: int, event: int, signal_type: int) -> None:
434
+ """This command is used to enable or disable the trigger input.
435
+
436
+ When enabling the trigger input, the address, event, and signalType parameters are used to define
437
+ the input trigger based on the supported options of that particular CCD.
438
+
439
+ The supported trigger options are retrieved using the get_configuration function, and begin with the
440
+ “Triggers” string contained in the configuration.
441
+
442
+ Args:
443
+ enabled (bool): Enable or disable the trigger input. Note: When disabling the input trigger,
444
+ the address, event, and signalType parameters are ignored.
445
+ address (int): Used to specify where the trigger is located. (e.g. 0 = Trigger Input)
446
+ event (int): Used to specify when the trigger event should occur. (e.g. 0 = Once - Start All)
447
+ signal_type (int): Used to specify how the signal will cause the input trigger. (e.g. 0 = TTL Falling Edge)
448
+
449
+ Raises:
450
+ Exception: When an error occurred on the device side
451
+ """
452
+ if not enabled:
453
+ address = -1
454
+ event = -1
455
+ signal_type = -1
456
+
457
+ super()._execute_command(
458
+ 'ccd_setTriggerIn',
459
+ {'index': self._id, 'enable': enabled, 'address': address, 'event': event, 'signalType': signal_type},
460
+ )
461
+ return
462
+
463
+ found_triggers = [trigger for trigger in self._config['triggers'] if trigger['token'] == address]
464
+ if not found_triggers:
465
+ raise Exception(f'Trigger address {address} not found in the configuration')
466
+
467
+ found_events = [
468
+ trigger_event for trigger_event in found_triggers[0]['events'] if trigger_event['token'] == event
469
+ ]
470
+ if not found_events:
471
+ raise Exception(f'Trigger event {event} not found in the configuration')
472
+
473
+ found_signal_types = [signal for signal in found_events[0]['types'] if signal['token'] == signal_type]
474
+ if not found_signal_types:
475
+ raise Exception(f'Trigger signal type {signal_type} not found in the configuration')
476
+
477
+ super()._execute_command(
478
+ 'ccd_setTriggerIn',
479
+ {'index': self._id, 'enable': enabled, 'address': address, 'event': event, 'signalType': signal_type},
480
+ )
481
+
482
+ def get_signal_output(self) -> tuple[bool, int, int, int]:
483
+ """This command is used to get the current setting of the signal output.
484
+
485
+ The address, event, and signalType parameters are used to define the signal based on the supported
486
+ options of that particular CCD.
487
+
488
+ The supported signal options are retrieved using the get_configuration command, and begin with the
489
+ “Signals” string contained in the configuration.
490
+
491
+ Returns:
492
+ Tuple[bool, int, int, int]:
493
+ enabled: Specifies if the signal is enabled (e.g. False = Disabled),
494
+ address: Used to specify where the signal is located (e.g. 0 = Signal Output),
495
+ Note: Value of -1 indicates that the signal output is disabled,
496
+ event: Used to specify when the signal event should occur. (e.g. 3 = Shutter Open)
497
+ Note: Value of -1 indicates that the signal output is disabled,
498
+ signal type: how the signal will cause the event. (e.g. 0 = TTL Active High)
499
+ Note: Value of -1 indicates that the signal output is disabled,
500
+
501
+ Raises:
502
+ Exception: When an error occurred on the device side
503
+ """
504
+ response: Response = super()._execute_command('ccd_getSignalOut', {'index': self._id})
505
+ address = int(response.results['address'])
506
+ event = int(response.results['event'])
507
+ signal_type = int(response.results['signalType'])
508
+ enabled = address > -1 and event > -1 and signal_type > -1
509
+ return enabled, address, event, signal_type
510
+
511
+ def set_signal_output(self, enabled: bool, address: int, event: int, signal_type: int) -> None:
512
+ """This command is used to enable or disable the signal output.
513
+
514
+ When enabling the signal output, the address, event, and signalType parameters are used to
515
+ define the signal based on the supported options of that particular CCD.
516
+
517
+ The supported signal options are retrieved using the ccd_getConfig command, and begin with the
518
+ “Signals” string contained in the configuration.
519
+
520
+ Args:
521
+ enabled (bool): Enable or disable the signal output. Note: When disabling the signal output,
522
+ the address, event, and signal_type parameters are ignored.
523
+ address (int): Used to specify where the signal is located (e.g. 0 = Signal Output)
524
+ event (int): Used to specify when the signal event should occur. (e.g. 3 = Shutter Open)
525
+ signal_type (int): How the signal will cause the event. (e.g. 0 = TTL Active High)
526
+
527
+ """
528
+ if not enabled:
529
+ address = -1
530
+ event = -1
531
+ signal_type = -1
532
+
533
+ super()._execute_command(
534
+ 'ccd_setSignalOut',
535
+ {'index': self._id, 'enable': enabled, 'address': address, 'event': event, 'signalType': signal_type},
536
+ )
537
+ return
538
+
539
+ found_triggers = [trigger for trigger in self._config['signals'] if trigger['token'] == address]
540
+ if not found_triggers:
541
+ raise Exception(f'Signal address {address} not found in the configuration')
542
+
543
+ found_events = [
544
+ trigger_event for trigger_event in found_triggers[0]['events'] if trigger_event['token'] == event
545
+ ]
546
+ if not found_events:
547
+ raise Exception(f'Signal event {event} not found in the configuration')
548
+
549
+ found_signal_types = [signal for signal in found_events[0]['types'] if signal['token'] == signal_type]
550
+ if not found_signal_types:
551
+ raise Exception(f'Signal type {signal_type} not found in the configuration')
552
+
553
+ super()._execute_command(
554
+ 'ccd_setSignalOut',
555
+ {'index': self._id, 'enable': enabled, 'address': address, 'event': event, 'signalType': signal_type},
556
+ )
557
+
558
+ def get_acquisition_ready(self) -> bool:
559
+ """Returns true if the CCD is ready to acquire
560
+
561
+ Returns:
562
+ bool: True if the CCD is ready to acquire
563
+ Raises:
564
+ Exception: When an error occurred on the device side
565
+ """
566
+ response: Response = super()._execute_command('ccd_getAcquisitionReady', {'index': self._id})
567
+ return bool(response.results['ready'])
568
+
569
+ def acquisition_start(self, open_shutter: bool) -> None:
570
+ """Starts an acquisition that has been set up according to the previously defined acquisition parameters.
571
+
572
+ Note: To specify the acquisiton parameters please see set_region_of_interest, set_x_axis_conversion_type.
573
+ If there are no acquisition parameters set at the time of acquisition it may result in no data being generated.
574
+
575
+ Args:
576
+ open_shutter (bool): Whether the shutter of the camera should be open during the acquisition.
577
+ Raises:
578
+ Exception: When an error occurred on the device side
579
+ """
580
+ super()._execute_command('ccd_acquisitionStart', {'index': self._id, 'openShutter': open_shutter})
206
581
 
207
582
  def get_acquisition_busy(self) -> bool:
208
583
  """Returns true if the CCD is busy with the acquisition"""
209
584
  response: Response = super()._execute_command('ccd_getAcquisitionBusy', {'index': self._id})
210
585
  return bool(response.results['isBusy'])
211
586
 
212
- def set_x_axis_conversion_type(self, conversion_type: XAxisConversionType) -> None:
213
- """Sets the conversion type of the x axis"""
214
- super()._execute_command('ccd_setXAxisConversionType', {'index': self._id, 'type': conversion_type.value})
587
+ def acquisition_abort(self) -> None:
588
+ """Stops the acquisition of the CCD"""
589
+ super()._execute_command('ccd_acquisitionStart', {'index': self._id})
215
590
 
216
- def get_x_axis_conversion_type(self) -> XAxisConversionType:
217
- """Gets the conversion type of the x axis"""
218
- response: Response = super()._execute_command('ccd_getXAxisConversionType', {'index': self._id})
219
- return self.XAxisConversionType(response.results['type'])
591
+ def get_acquisition_data(self) -> dict[Any, Any]:
592
+ """Retrieves data from the last acquisition.
593
+
594
+ The acquisition description string consists of the following information:
595
+ - acqIndex: Acquisition number
596
+ - roiIndex: Region of Interest number
597
+ - xOrigin: ROI’s X Origin
598
+ - yOrigin: ROI’s Y Origin
599
+ - xSize: ROI’s X Size
600
+ - ySize: ROI’s Y Size
601
+ - xBinning: ROI’s X Bin
602
+ - yBinning: ROI’s Y Bin
603
+ - Timestamp: This is a timestamp that relates to the time when the all the programmed acquisitions have
604
+ completed. The data from all programmed acquisitions are retrieve from the CCD after all
605
+ acquisitions have completed, therefore the same timestamp is used for all acquisitions.
606
+ """
607
+ response: Response = super()._execute_command('ccd_getAcquisitionData', {'index': self._id})
608
+ return response.results['acquisition']
609
+
610
+ def set_center_wavelength(self, center_wavelength: float) -> None:
611
+ """Sets the center wavelength value to be used in the grating equation.
612
+
613
+ Used when X axis conversion is XAxisConversionType.FROM_ICL_SETTINGS_INI
614
+
615
+ Args:
616
+ center_wavelength (float): Center wavelength
617
+
618
+ Raises:
619
+ Exception: When an error occurred on the device side
620
+ """
621
+ super()._execute_command(
622
+ 'ccd_setCenterWavelength',
623
+ {'index': self._id, 'wavelength': center_wavelength},
624
+ )
625
+
626
+ def range_mode_center_wavelengths(
627
+ self, monochromator_index: int, start_wavelength: float, end_wavelength: float, pixel_overlap: int
628
+ ) -> list[float]:
629
+ """Finds the center wavelength positions based on the input range and pixel overlap.
630
+
631
+ The following commands are prerequisites and should be called prior to using this command:
632
+ - :func:`ChargeCoupledDevice.set_x`,
633
+ - :func:`ChargeCoupledDevice.ccd_setAcqFormat`,
634
+ - :func:`ChargeCoupledDevice.ccd_setRoi`
635
+
636
+ Args:
637
+ monochromator_index (int): Index of the monochromator that is connected to the setup
638
+ start_wavelength (float): Start wavelength
639
+ end_wavelength (float): End wavelength
640
+ pixel_overlap (int): Overlap size in pixels between the scans.
641
+
642
+ Returns:
643
+ List[float]: List of center wavelength positions to cover the desired range.
644
+ Raises:
645
+ Exception: When an error occurred on the device side
646
+ """
647
+ response: Response = super()._execute_command(
648
+ 'ccd_calculateRangeModePositions',
649
+ {
650
+ 'index': self._id,
651
+ 'monoIndex': monochromator_index,
652
+ 'start': start_wavelength,
653
+ 'end': end_wavelength,
654
+ 'overlap': pixel_overlap,
655
+ },
656
+ )
657
+ return response.results['centerWavelengths']
658
+
659
+ @staticmethod
660
+ def raman_convert(spectrum: list[float], excitation_wavelength: float) -> list[float]:
661
+ """Calculates the raman shift for every wavelength in the list relative to the excitation wavelength.
662
+
663
+ Args:
664
+ spectrum (list[float]): Wavelengths
665
+ excitation_wavelength: Excitation wavelength
666
+
667
+ Returns:
668
+ list[float]: Wavelengths converted to raman shifts
669
+ """
670
+ raman_values = []
671
+ for wave_length in spectrum:
672
+ raman_shift = ((1 / excitation_wavelength) - (1 / wave_length)) * (10**7)
673
+ raman_values.append(raman_shift)
674
+ return raman_values