horiba-sdk 0.3.3__py3-none-any.whl → 0.4.0__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.
@@ -1,14 +1,15 @@
1
- from enum import Enum
2
1
  from types import TracebackType
3
- from typing import Any, Optional, final
2
+ from typing import Any, List, 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 AbstractCommunicator, 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
 
14
15
  from .abstract_device import AbstractDevice
@@ -22,40 +23,6 @@ class ChargeCoupledDevice(AbstractDevice):
22
23
  should be used to access the detected CCDs on the system.
23
24
  """
24
25
 
25
- @final
26
- class Gain(Enum):
27
- HIGH_LIGHT = 0
28
- BEST_DYNAMIC_RANGE = 1
29
- HIGH_SENSITIVITY = 2
30
-
31
- @final
32
- class Speed(Enum):
33
- SLOW_45_kHz = 0
34
- MEDIUM_1_MHz = 1
35
- FAST_1_MHz_Ultra = 2
36
-
37
- @final
38
- class AcquisitionFormat(Enum):
39
- SPECTRA = 0
40
- IMAGE = 1
41
- CROP = 2
42
- FAST_KINETICS = 3
43
-
44
- @final
45
- class CleanCountMode(Enum):
46
- Mode1 = 238
47
-
48
- @final
49
- class XAxisConversionType(Enum):
50
- """
51
- Enumeration of possible XAxisConversionTypes
52
- None = 0, CCD-Firmware = 1, ICL ini settings file = 2
53
- """
54
-
55
- NONE = 0
56
- FROM_CCD_FIRMWARE = 1
57
- FROM_ICL_SETTINGS_INI = 2
58
-
59
26
  def __init__(self, device_id: int, communicator: AbstractCommunicator, error_db: AbstractErrorDB) -> None:
60
27
  super().__init__(device_id, communicator, error_db)
61
28
 
@@ -82,6 +49,7 @@ class ChargeCoupledDevice(AbstractDevice):
82
49
  """
83
50
  await super().open()
84
51
  await super()._execute_command('ccd_open', {'index': self._id})
52
+ self._config: dict[str, Any] = await self.get_configuration()
85
53
 
86
54
  @override
87
55
  async def close(self) -> None:
@@ -121,131 +89,135 @@ class ChargeCoupledDevice(AbstractDevice):
121
89
  Exception: When an error occurred on the device side
122
90
  """
123
91
  response: Response = await super()._execute_command('ccd_getConfig', {'index': self._id})
124
- return response.results
92
+ return response.results['configuration']
125
93
 
126
- async def get_number_of_averages(self) -> int:
127
- """Returns the number of averages of the CCD
94
+ async def get_gain_token(self) -> int:
95
+ """Returns the current gain token.
128
96
 
129
- Returns:
130
- int: Number of averages
131
-
132
- Raises:
133
- Exception: When an error occurred on the device side
134
- """
135
- response: Response = await super()._execute_command('ccd_getNumberOfAvgs', {'index': self._id})
136
- return int(response.results['count'])
137
-
138
- async def set_number_of_averages(self, number_of_averages: int) -> None:
139
- """Sets the number of averages of the CCD
140
-
141
- Args:
142
- number_of_averages (int): Number of averages
143
-
144
- Raises:
145
- Exception: When an error occurred on the device side
146
- """
147
- await super()._execute_command('ccd_setNumberOfAvgs', {'index': self._id, 'count': number_of_averages})
148
-
149
- async def get_gain(self) -> str:
150
- """Returns the gain of the CCD
97
+ .. note:: The CCD can have different sensors installed, which can have different gain values. This is why only
98
+ the token to the gain is returned. You need to first check what gain values are available for the CCD using the
99
+ get_configuration function. Please see the according "Gain and Speed" documentation.
151
100
 
152
101
  Returns:
153
- str: Gain
102
+ int: Gain token of the ccd
154
103
 
155
104
  Raises:
156
105
  Exception: When an error occurred on the device side
157
106
  """
158
107
  response: Response = await super()._execute_command('ccd_getGain', {'index': self._id})
159
- return str(response.results['info'])
108
+ gain: int = int(response.results['token'])
109
+ return gain
110
+
111
+ async def set_gain(self, gain_token: int) -> None:
112
+ """Sets the gain of the CCD.
160
113
 
161
- async def set_gain(self, gain: Gain) -> None:
162
- """Sets the gain of the CCD
114
+ .. note:: The CCD can have different sensors installed, which can have different gain values. Therefore you need
115
+ to first check what gain values are available for the CCD using the get_configuration function. Please see the
116
+ according "Gain and Speed" documentation.
163
117
 
164
118
  Args:
165
- gain (Gain): Gain
119
+ gain_token (int): Token of the desired gain
166
120
 
167
121
  Raises:
168
122
  Exception: When an error occurred on the device side
169
123
  """
170
- await super()._execute_command('ccd_setGain', {'index': self._id, 'token': gain.value})
124
+ await super()._execute_command('ccd_setGain', {'index': self._id, 'token': gain_token})
171
125
 
172
- async def get_speed(self) -> str:
173
- """Returns the speed of the CCD
126
+ async def get_speed_token(self) -> int:
127
+ """Returns the speed token.
128
+
129
+ .. note:: The CCD can have different sensors installed, which can have different speed values. This is why only
130
+ the token to the speed is returned. You need to first check what speed values are available for the CCD using
131
+ the get_configuration function. Please see the according "Gain and Speed" documentation.
174
132
 
175
133
  Returns:
176
- str: Speed
134
+ int: Speed token of the CCD.
177
135
 
178
136
  Raises:
179
137
  Exception: When an error occurred on the device side
180
138
  """
181
139
  response: Response = await super()._execute_command('ccd_getSpeed', {'index': self._id})
182
- return str(response.results['info'])
140
+ speed_token: int = int(response.results['token'])
141
+ return speed_token
183
142
 
184
- async def set_speed(self, speed: Speed) -> None:
143
+ async def set_speed(self, speed_token: int) -> None:
185
144
  """Sets the speed of the CCD
186
145
 
146
+ .. note:: The CCD can have different sensors installed, which can have different speed values. Therefore you
147
+ need to first check what speed values are available for the CCD using the get_configuration function. Please
148
+ see the according "Gain and Speed" documentation.
149
+
187
150
  Args:
188
- speed (Speed): Speed
151
+ speed_token (int): Token of the desired speed.
189
152
 
190
153
  Raises:
191
154
  Exception: When an error occurred on the device side
192
155
  """
193
- await super()._execute_command('ccd_setSpeed', {'index': self._id, 'token': speed.value})
156
+ await super()._execute_command('ccd_setSpeed', {'index': self._id, 'token': speed_token})
194
157
 
195
- async def get_fit_params(self) -> str:
158
+ async def get_fit_parameters(self) -> list[int]:
196
159
  """Returns the fit parameters of the CCD
197
160
 
198
161
  Returns:
199
- str: Fit parameters
162
+ List[int]: Fit parameters
200
163
 
201
164
  Raises:
202
165
  Exception: When an error occurred on the device side
203
166
  """
204
167
  response: Response = await super()._execute_command('ccd_getFitParams', {'index': self._id})
205
- return str(response.results['params'])
168
+ fit_params: list[int] = response.results['fitParameters']
169
+ return fit_params
206
170
 
207
- async def set_fit_params(self, fit_params: str) -> None:
171
+ async def set_fit_parameters(self, fit_params: list[int]) -> None:
208
172
  """Sets the fit parameters of the CCD
209
173
 
210
174
  Args:
211
- fit_params (str): Fit parameters
175
+ fit_params (List[int]): Fit parameters
212
176
 
213
177
  Raises:
214
178
  Exception: When an error occurred on the device side
215
179
  """
216
- await super()._execute_command('ccd_setFitParams', {'index': self._id, 'params': fit_params})
180
+ fit_params_str: str = ','.join(map(str, fit_params))
181
+ await super()._execute_command('ccd_setFitParams', {'index': self._id, 'params': fit_params_str})
217
182
 
218
- async def get_timer_resolution(self) -> int:
219
- """Returns the timer resolution of the CCD
183
+ async def get_timer_resolution(self) -> TimerResolution:
184
+ """Returns the timer resolution of the CCD in microseconds [μs]
220
185
 
221
186
  Returns:
222
- int: Timer resolution
187
+ int: Timer resolution in microseconds [μs]
223
188
 
224
189
  Raises:
225
190
  Exception: When an error occurred on the device side
226
191
  """
227
192
  response: Response = await super()._execute_command('ccd_getTimerResolution', {'index': self._id})
228
- return int(response.results['resolution'])
193
+ timer_resolution: int = int(response.results['resolutionToken'])
194
+ return TimerResolution(timer_resolution)
229
195
 
230
- async def set_timer_resolution(self, timer_resolution: int) -> None:
196
+ async def set_timer_resolution(self, timer_resolution: TimerResolution) -> None:
231
197
  """Sets the timer resolution of the CCD
232
198
 
199
+ .. note:: The timer resolution value of 1 microsecond is not supported by all CCDs.
200
+
233
201
  Args:
234
202
  timer_resolution (int): Timer resolution
235
203
 
236
204
  Raises:
237
205
  Exception: When an error occurred on the device side
238
206
  """
239
- await super()._execute_command('ccd_setTimerResolution', {'index': self._id, 'resolution': timer_resolution})
207
+ await super()._execute_command(
208
+ 'ccd_setTimerResolution', {'index': self._id, 'resolutionToken': timer_resolution.value}
209
+ )
240
210
 
241
211
  async def set_acquisition_format(self, number_of_rois: int, acquisition_format: AcquisitionFormat) -> None:
242
- """Sets the acquisition format and the number of ROIs (Regions of Interest) or areas. After using this command
243
- to set the number of ROIs and format, the ccd_setRoi command should be used to define each ROI.
244
- Note: The Crop (2) and Fast Kinetics (3) acquisition formats are not supported by every CCD.
212
+ """Sets the acquisition format and the number of ROIs (Regions of Interest) or areas.
213
+
214
+ After using this command to set the number of ROIs and format, the set_region_of_interest function
215
+ should be used to define each ROI. Note: The Crop and Fast Kinetics acquisition formats are not
216
+ supported by every CCD.
245
217
 
246
218
  Args:
247
- acquisition_format (AcquisitionFormat): Acquisition format
248
219
  number_of_rois (int): Number of regions of interest
220
+ acquisition_format (AcquisitionFormat): Acquisition format
249
221
 
250
222
  Raises:
251
223
  Exception: When an error occurred on the device side
@@ -254,6 +226,45 @@ class ChargeCoupledDevice(AbstractDevice):
254
226
  'ccd_setAcqFormat', {'index': self._id, 'format': acquisition_format.value, 'numberOfRois': number_of_rois}
255
227
  )
256
228
 
229
+ async def set_region_of_interest(
230
+ self,
231
+ roi_index: int = 1,
232
+ x_origin: int = 0,
233
+ y_origin: int = 0,
234
+ x_size: int = 1024,
235
+ y_size: int = 256,
236
+ x_bin: int = 1,
237
+ y_bin: int = 256,
238
+ ) -> None:
239
+ """Sets the region of interest of the CCD
240
+ an example json command looks like this:
241
+
242
+ Args:
243
+ roi_index (int, optional): One based index of the region of interest. Defaults to 1.
244
+ x_origin (int, optional): X origin of the region of interest. Defaults to 0.
245
+ y_origin (int, optional): Y origin of the region of interest. Defaults to 0.
246
+ x_size (int, optional): X size of the region of interest. Defaults to 1024.
247
+ y_size (int, optional): Y size of the region of interest. Defaults to 256.
248
+ x_bin (int, optional): X bin of the region of interest. Defaults to 1.
249
+ y_bin (int, optional): Y bin of the region of interest. Defaults to 256.
250
+
251
+ Raises:
252
+ Exception: When an error occurred on the device side
253
+ """
254
+ await super()._execute_command(
255
+ 'ccd_setRoi',
256
+ {
257
+ 'index': self._id,
258
+ 'roiIndex': roi_index,
259
+ 'xOrigin': x_origin,
260
+ 'yOrigin': y_origin,
261
+ 'xSize': x_size,
262
+ 'ySize': y_size,
263
+ 'xBin': x_bin,
264
+ 'yBin': y_bin,
265
+ },
266
+ )
267
+
257
268
  async def set_x_axis_conversion_type(self, conversion_type: XAxisConversionType) -> None:
258
269
  """Sets the X-axis pixel conversion type to be used when retrieving the acquisition data with the
259
270
  ccd_getAcquisitionData command.
@@ -274,13 +285,15 @@ class ChargeCoupledDevice(AbstractDevice):
274
285
  2 = Mono Wavelength parameters contained in the icl_settings.ini file
275
286
  """
276
287
  response: Response = await super()._execute_command('ccd_getXAxisConversionType', {'index': self._id})
277
- return self.XAxisConversionType(self.XAxisConversionType(response.results['type']))
288
+ return XAxisConversionType(response.results['type'])
278
289
 
279
290
  async def set_acquisition_count(self, count: int) -> None:
280
- """Sets the number of acquisitions to be performed. The acquisition count is used to perform multiple
281
- acquisitions in a row.
291
+ """Sets the number of acquisition measurements to be performed sequentially by the hardware.
292
+
293
+ A count > 1 is commonly referred to as "MultiAcq".
294
+
282
295
  Args:
283
- count (int): The number of acquisitions to be performed.
296
+ count (int): The number of acquisition measurements.
284
297
  """
285
298
  await super()._execute_command('ccd_setAcqCount', {'index': self._id, 'count': count})
286
299
 
@@ -291,11 +304,18 @@ class ChargeCoupledDevice(AbstractDevice):
291
304
  response: Response = await super()._execute_command('ccd_getAcqCount', {'index': self._id})
292
305
  return int(response.results['count'])
293
306
 
294
- async def get_clean_count(self) -> str:
295
- """Gets the clean count mode of the CCD and the according mode"""
307
+ async def get_clean_count(self) -> tuple[int, CleanCountMode]:
308
+ """Gets the number of cleans to be performed prior to measurement.
309
+
310
+ Returns:
311
+ Tuple[int, CleanCountMode]:
312
+ count: Number of cleans,
313
+ mode: Specifies how the cleans will be performed.
314
+ """
296
315
  response: Response = await super()._execute_command('ccd_getCleanCount', {'index': self._id})
297
- answer: str = 'count: ' + str(response.results['count']) + ' ' + 'mode: ' + str(response.results['mode'])
298
- return answer
316
+ count: int = int(response.results['count'])
317
+ mode: CleanCountMode = CleanCountMode(response.results['mode'])
318
+ return count, mode
299
319
 
300
320
  async def set_clean_count(self, count: int, mode: CleanCountMode) -> None:
301
321
  """Sets the clean count mode of the CCD and the according mode
@@ -305,8 +325,8 @@ class ChargeCoupledDevice(AbstractDevice):
305
325
  """
306
326
  await super()._execute_command('ccd_setCleanCount', {'index': self._id, 'count': count, 'mode': mode.value})
307
327
 
308
- async def get_data_size(self) -> int:
309
- """Returns the size of the data of the CCD
328
+ async def get_acquisition_data_size(self) -> int:
329
+ """Returns the size of the acquisition data of the CCD
310
330
 
311
331
  Returns:
312
332
  int: Size of the data
@@ -317,17 +337,17 @@ class ChargeCoupledDevice(AbstractDevice):
317
337
  response: Response = await super()._execute_command('ccd_getDataSize', {'index': self._id})
318
338
  return int(response.results['size'])
319
339
 
320
- async def get_temperature(self) -> pint.Quantity:
340
+ async def get_temperature(self) -> float:
321
341
  """Chip temperature of the CCD.
322
342
 
323
343
  Returns:
324
- pint.Quantity: chip's temperature in degree Celsius
344
+ float: chip's temperature in degree Celsius
325
345
 
326
346
  Raises:
327
347
  Exception: When an error occurred on the device side
328
348
  """
329
349
  response: Response = await super()._execute_command('ccd_getChipTemperature', {'index': self._id})
330
- return ureg.Quantity(response.results['temperature'], ureg.degC) # type: ignore
350
+ return float(response.results['temperature'])
331
351
 
332
352
  async def get_chip_size(self) -> Resolution:
333
353
  """Chip resolution of the CCD.
@@ -356,16 +376,176 @@ class ChargeCoupledDevice(AbstractDevice):
356
376
  exposure = int(response.results['time'])
357
377
  return exposure
358
378
 
359
- async def set_exposure_time(self, exposure_time_ms: int) -> None:
360
- """Sets the exposure time in ms
379
+ async def set_exposure_time(self, exposure_time: int) -> None:
380
+ """Sets the exposure time in timer resolution units (1us or 1000us)
381
+
382
+ Examples:
383
+ - If exposure_time is set to 50, and the timer resolution value is 1000, the CCD exposure time
384
+ (integration time) = 50 milliseconds.
385
+ - If exposure_time is set to 50, and the timer resolution value is 1, the CCD exposure time
386
+ (integration time) = 50 microseconds.
361
387
 
362
388
  Args:
363
- exposure_time_ms (int): Exposure time in ms
389
+ exposure_time (int): Exposure time in timer resolution units (1us or 1000us)
364
390
  Raises:
365
391
  Exception: When an error occurred on the device side
366
392
  """
367
393
 
368
- await super()._execute_command('ccd_setExposureTime', {'index': self._id, 'time': exposure_time_ms})
394
+ await super()._execute_command('ccd_setExposureTime', {'index': self._id, 'time': exposure_time})
395
+
396
+ async def get_trigger_input(self) -> tuple[bool, int, int, int]:
397
+ """This command is used to get the current setting of the input trigger.
398
+
399
+ The address, event, and signalType parameters are used to define the input trigger based on the
400
+ supported options of that particular CCD.
401
+
402
+ The supported trigger options are retrieved using the get_configuration function, and begin with the
403
+ “Triggers” string contained in the configuration.
404
+
405
+ Returns:
406
+ Tuple[bool, int, int, int]:
407
+ enabled: Specifies if the signal is enabled (e.g. False = Disabled),
408
+ address: used to specify where the trigger is located. (e.g. 0 = Trigger Input).
409
+ Note: Value of -1 indicates that the input trigger is disabled,
410
+ event: used to specify when the trigger event should occur. (e.g. 0 = Once - Start All)
411
+ Note: Value of -1 indicates that the input trigger is disabled,
412
+ signal type: used to specify how the signal will cause the input trigger. (e.g. 0 = TTL Falling Edge)
413
+ Note: Value of -1 indicates that the input trigger is disabled,
414
+
415
+ Raises:
416
+ Exception: When an error occurred on the device side
417
+ """
418
+ response: Response = await super()._execute_command('ccd_getTriggerIn', {'index': self._id})
419
+ address = int(response.results['address'])
420
+ event = int(response.results['event'])
421
+ signal_type = int(response.results['signalType'])
422
+ enabled = address > -1 and event > -1 and signal_type > -1
423
+ return enabled, address, event, signal_type
424
+
425
+ async def set_trigger_input(self, enabled: bool, address: int, event: int, signal_type: int) -> None:
426
+ """This command is used to enable or disable the trigger input.
427
+
428
+ When enabling the trigger input, the address, event, and signalType parameters are used to define
429
+ the input trigger based on the supported options of that particular CCD.
430
+
431
+ The supported trigger options are retrieved using the get_configuration function, and begin with the
432
+ “Triggers” string contained in the configuration.
433
+
434
+ Args:
435
+ enabled (bool): Enable or disable the trigger input. Note: When disabling the input trigger,
436
+ the address, event, and signalType parameters are ignored.
437
+ address (int): Used to specify where the trigger is located. (e.g. 0 = Trigger Input)
438
+ event (int): Used to specify when the trigger event should occur. (e.g. 0 = Once - Start All)
439
+ signal_type (int): Used to specify how the signal will cause the input trigger. (e.g. 0 = TTL Falling Edge)
440
+
441
+ Raises:
442
+ Exception: When an error occurred on the device side
443
+ """
444
+ if not enabled:
445
+ address = -1
446
+ event = -1
447
+ signal_type = -1
448
+
449
+ await super()._execute_command(
450
+ 'ccd_setTriggerIn',
451
+ {'index': self._id, 'enable': enabled, 'address': address, 'event': event, 'signalType': signal_type},
452
+ )
453
+ return
454
+
455
+ found_triggers = [trigger for trigger in self._config['triggers'] if trigger['token'] == address]
456
+ if not found_triggers:
457
+ raise Exception(f'Trigger address {address} not found in the configuration')
458
+
459
+ found_events = [
460
+ trigger_event for trigger_event in found_triggers[0]['events'] if trigger_event['token'] == event
461
+ ]
462
+ if not found_events:
463
+ raise Exception(f'Trigger event {event} not found in the configuration')
464
+
465
+ found_signal_types = [signal for signal in found_events[0]['types'] if signal['token'] == signal_type]
466
+ if not found_signal_types:
467
+ raise Exception(f'Trigger signal type {signal_type} not found in the configuration')
468
+
469
+ await super()._execute_command(
470
+ 'ccd_setTriggerIn',
471
+ {'index': self._id, 'enable': enabled, 'address': address, 'event': event, 'signalType': signal_type},
472
+ )
473
+
474
+ async def get_signal_output(self) -> tuple[bool, int, int, int]:
475
+ """This command is used to get the current setting of the signal output.
476
+
477
+ The address, event, and signalType parameters are used to define the signal based on the supported
478
+ options of that particular CCD.
479
+
480
+ The supported signal options are retrieved using the get_configuration command, and begin with the
481
+ “Signals” string contained in the configuration.
482
+
483
+ Returns:
484
+ Tuple[bool, int, int, int]:
485
+ enabled: Specifies if the signal is enabled (e.g. False = Disabled),
486
+ address: Used to specify where the signal is located (e.g. 0 = Signal Output),
487
+ Note: Value of -1 indicates that the signal output is disabled,
488
+ event: Used to specify when the signal event should occur. (e.g. 3 = Shutter Open)
489
+ Note: Value of -1 indicates that the signal output is disabled,
490
+ signal type: how the signal will cause the event. (e.g. 0 = TTL Active High)
491
+ Note: Value of -1 indicates that the signal output is disabled,
492
+
493
+ Raises:
494
+ Exception: When an error occurred on the device side
495
+ """
496
+ response: Response = await super()._execute_command('ccd_getSignalOut', {'index': self._id})
497
+ address = int(response.results['address'])
498
+ event = int(response.results['event'])
499
+ signal_type = int(response.results['signalType'])
500
+ enabled = address > -1 and event > -1 and signal_type > -1
501
+ return enabled, address, event, signal_type
502
+
503
+ async def set_signal_output(self, enabled: bool, address: int, event: int, signal_type: int) -> None:
504
+ """This command is used to enable or disable the signal output.
505
+
506
+ When enabling the signal output, the address, event, and signalType parameters are used to
507
+ define the signal based on the supported options of that particular CCD.
508
+
509
+ The supported signal options are retrieved using the ccd_getConfig command, and begin with the
510
+ “Signals” string contained in the configuration.
511
+
512
+ Args:
513
+ enabled (bool): Enable or disable the signal output. Note: When disabling the signal output,
514
+ the address, event, and signal_type parameters are ignored.
515
+ address (int): Used to specify where the signal is located (e.g. 0 = Signal Output)
516
+ event (int): Used to specify when the signal event should occur. (e.g. 3 = Shutter Open)
517
+ signal_type (int): How the signal will cause the event. (e.g. 0 = TTL Active High)
518
+
519
+ """
520
+ if not enabled:
521
+ address = -1
522
+ event = -1
523
+ signal_type = -1
524
+
525
+ await super()._execute_command(
526
+ 'ccd_setSignalOut',
527
+ {'index': self._id, 'enable': enabled, 'address': address, 'event': event, 'signalType': signal_type},
528
+ )
529
+ return
530
+
531
+ found_triggers = [trigger for trigger in self._config['signals'] if trigger['token'] == address]
532
+ if not found_triggers:
533
+ raise Exception(f'Signal address {address} not found in the configuration')
534
+
535
+ found_events = [
536
+ trigger_event for trigger_event in found_triggers[0]['events'] if trigger_event['token'] == event
537
+ ]
538
+ if not found_events:
539
+ raise Exception(f'Signal event {event} not found in the configuration')
540
+
541
+ found_signal_types = [signal for signal in found_events[0]['types'] if signal['token'] == signal_type]
542
+ if not found_signal_types:
543
+ raise Exception(f'Signal type {signal_type} not found in the configuration')
544
+
545
+ await super()._execute_command(
546
+ 'ccd_setSignalOut',
547
+ {'index': self._id, 'enable': enabled, 'address': address, 'event': event, 'signalType': signal_type},
548
+ )
369
549
 
370
550
  async def get_acquisition_ready(self) -> bool:
371
551
  """Returns true if the CCD is ready to acquire
@@ -379,65 +559,91 @@ class ChargeCoupledDevice(AbstractDevice):
379
559
  return bool(response.results['ready'])
380
560
 
381
561
  async def set_acquisition_start(self, open_shutter: bool) -> None:
382
- """Starts the acquisition of the CCD
562
+ """Starts an acquisition that has been set up according to the previously defined acquisition parameters.
563
+
564
+ Note: To specify the acquisiton parameters please see set_region_of_interest, set_x_axis_conversion_type.
565
+ If there are no acquisition parameters set at the time of acquisition it may result in no data being generated.
383
566
 
384
567
  Args:
385
- open_shutter (bool): Whether the shutter of the camera should be open
568
+ open_shutter (bool): Whether the shutter of the camera should be open during the acquisition.
386
569
  Raises:
387
570
  Exception: When an error occurred on the device side
388
571
  """
389
572
  await super()._execute_command('ccd_setAcquisitionStart', {'index': self._id, 'openShutter': open_shutter})
390
573
 
391
- async def set_region_of_interest(
392
- self,
393
- roi_index: int = 1,
394
- x_origin: int = 0,
395
- y_origin: int = 0,
396
- x_size: int = 1024,
397
- y_size: int = 256,
398
- x_bin: int = 1,
399
- y_bin: int = 256,
400
- ) -> None:
401
- """Sets the region of interest of the CCD
402
- an example json command looks like this:
574
+ async def get_acquisition_busy(self) -> bool:
575
+ """Returns true if the CCD is busy with the acquisition"""
576
+ response: Response = await super()._execute_command('ccd_getAcquisitionBusy', {'index': self._id})
577
+ return bool(response.results['isBusy'])
578
+
579
+ async def set_acquisition_abort(self, reset_port: bool = True) -> None:
580
+ """Stops the acquisition of the CCD"""
581
+ await super()._execute_command('ccd_setAcquisitionAbort', {'index': self._id, 'resetPort': reset_port})
582
+
583
+ async def get_acquisition_data(self) -> dict[Any, Any]:
584
+ """Retrieves data from the last acquisition.
585
+
586
+ The acquisition description string consists of the following information:
587
+ - acqIndex: Acquisition number
588
+ - roiIndex: Region of Interest number
589
+ - xOrigin: ROI’s X Origin
590
+ - yOrigin: ROI’s Y Origin
591
+ - xSize: ROI’s X Size
592
+ - ySize: ROI’s Y Size
593
+ - xBinning: ROI’s X Bin
594
+ - yBinning: ROI’s Y Bin
595
+ - Timestamp: This is a timestamp that relates to the time when the all the programmed acquisitions have
596
+ completed. The data from all programmed acquisitions are retrieve from the CCD after all
597
+ acquisitions have completed, therefore the same timestamp is used for all acquisitions.
598
+ """
599
+ response: Response = await super()._execute_command('ccd_getAcquisitionData', {'index': self._id})
600
+ return response.results['acquisition']
601
+
602
+ async def set_center_wavelength(self, center_wavelength: float) -> None:
603
+ """Sets the center wavelength value to be used in the grating equation.
604
+
605
+ Used when X axis conversion is XAxisConversionType.FROM_ICL_SETTINGS_INI
403
606
 
404
607
  Args:
405
- roi_index (int, optional): Index of the region of interest. Defaults to 1.
406
- x_origin (int, optional): X origin of the region of interest. Defaults to 0.
407
- y_origin (int, optional): Y origin of the region of interest. Defaults to 0.
408
- x_size (int, optional): X size of the region of interest. Defaults to 1024.
409
- y_size (int, optional): Y size of the region of interest. Defaults to 256.
410
- x_bin (int, optional): X bin of the region of interest. Defaults to 1.
411
- y_bin (int, optional): Y bin of the region of interest. Defaults to 256.
608
+ center_wavelength (float): Center wavelength
412
609
 
413
610
  Raises:
414
611
  Exception: When an error occurred on the device side
415
612
  """
416
613
  await super()._execute_command(
417
- 'ccd_setRoi',
418
- {
419
- 'index': self._id,
420
- 'roiIndex': roi_index,
421
- 'xOrigin': x_origin,
422
- 'yOrigin': y_origin,
423
- 'xSize': x_size,
424
- 'ySize': y_size,
425
- 'xBin': x_bin,
426
- 'yBin': y_bin,
427
- },
614
+ 'ccd_setCenterWavelength',
615
+ {'index': self._id, 'wavelength': center_wavelength},
428
616
  )
429
617
 
430
- async def get_acquisition_data(self) -> dict[Any, Any]:
431
- """Returns the acquisition data of the CCD
432
- nina: atm this returns data still formatted for telnet communication, not formatted as json"""
433
- response: Response = await super()._execute_command('ccd_getAcquisitionData', {'index': self._id})
434
- return response.results
618
+ async def range_mode_center_wavelengths(
619
+ self, monochromator_index: int, start_wavelength: float, end_wavelength: float, pixel_overlap: int
620
+ ) -> List[float]:
621
+ """Finds the center wavelength positions based on the input range and pixel overlap.
435
622
 
436
- async def get_acquisition_busy(self) -> bool:
437
- """Returns true if the CCD is busy with the acquisition"""
438
- response: Response = await super()._execute_command('ccd_getAcquisitionBusy', {'index': self._id})
439
- return bool(response.results['isBusy'])
623
+ The following commands are prerequisites and should be called prior to using this command:
624
+ - :func:`ChargeCoupledDevice.set_x`,
625
+ - :func:`ChargeCoupledDevice.ccd_setAcqFormat`,
626
+ - :func:`ChargeCoupledDevice.ccd_setRoi`
440
627
 
441
- async def set_acquisition_abort(self) -> None:
442
- """Stops the acquisition of the CCD"""
443
- await super()._execute_command('ccd_setAcquisitionAbort', {'index': self._id})
628
+ Args:
629
+ monochromator_index (int): Index of the monochromator that is connected to the setup
630
+ start_wavelength (float): Start wavelength
631
+ end_wavelength (float): End wavelength
632
+ pixel_overlap (int): Overlap size in pixels between the scans.
633
+
634
+ Returns:
635
+ List[float]: List of center wavelength positions to cover the desired range.
636
+ Raises:
637
+ Exception: When an error occurred on the device side
638
+ """
639
+ response: Response = await super()._execute_command(
640
+ 'ccd_calculateRangeModePositions',
641
+ {
642
+ 'index': self._id,
643
+ 'monoIndex': monochromator_index,
644
+ 'start': start_wavelength,
645
+ 'end': end_wavelength,
646
+ 'overlap': pixel_overlap,
647
+ },
648
+ )
649
+ return response.results['centerWavelengths']