horiba-sdk 0.5.4__tar.gz → 0.7.0__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 (63) hide show
  1. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/PKG-INFO +2 -1
  2. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/communication/messages.py +7 -3
  3. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/communication/websocket_communicator.py +1 -1
  4. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/core/acquisition_format.py +1 -2
  5. horiba_sdk-0.7.0/horiba_sdk/core/stitching/__init__.py +6 -0
  6. horiba_sdk-0.7.0/horiba_sdk/core/stitching/labspec6_spectra_stitch.py +90 -0
  7. horiba_sdk-0.7.0/horiba_sdk/core/stitching/linear_spectra_stitch.py +107 -0
  8. horiba_sdk-0.7.0/horiba_sdk/core/stitching/simple_cut_spectra_stitch.py +84 -0
  9. horiba_sdk-0.7.0/horiba_sdk/core/stitching/spectra_stitch.py +16 -0
  10. horiba_sdk-0.7.0/horiba_sdk/core/stitching/y_displacement_spectra_stitch.py +87 -0
  11. horiba_sdk-0.7.0/horiba_sdk/core/trigger_input_polarity.py +6 -0
  12. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/devices/device_manager.py +19 -3
  13. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/devices/fake_icl_server.py +7 -0
  14. horiba_sdk-0.7.0/horiba_sdk/devices/fake_responses/spectracq3.json +217 -0
  15. {horiba_sdk-0.5.4/horiba_sdk/sync → horiba_sdk-0.7.0/horiba_sdk}/devices/single_devices/__init__.py +2 -1
  16. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/devices/single_devices/ccd.py +4 -2
  17. horiba_sdk-0.7.0/horiba_sdk/devices/single_devices/spectracq3.py +392 -0
  18. horiba_sdk-0.7.0/horiba_sdk/devices/spectracq3_discovery.py +55 -0
  19. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/pyproject.toml +5 -1
  20. horiba_sdk-0.5.4/horiba_sdk/devices/single_devices/__init__.py +0 -5
  21. horiba_sdk-0.5.4/horiba_sdk/sync/__init__.py +0 -0
  22. horiba_sdk-0.5.4/horiba_sdk/sync/communication/__init__.py +0 -7
  23. horiba_sdk-0.5.4/horiba_sdk/sync/communication/abstract_communicator.py +0 -47
  24. horiba_sdk-0.5.4/horiba_sdk/sync/communication/test_client.py +0 -16
  25. horiba_sdk-0.5.4/horiba_sdk/sync/communication/websocket_communicator.py +0 -232
  26. horiba_sdk-0.5.4/horiba_sdk/sync/devices/__init__.py +0 -15
  27. horiba_sdk-0.5.4/horiba_sdk/sync/devices/abstract_device_discovery.py +0 -17
  28. horiba_sdk-0.5.4/horiba_sdk/sync/devices/abstract_device_manager.py +0 -68
  29. horiba_sdk-0.5.4/horiba_sdk/sync/devices/device_discovery.py +0 -58
  30. horiba_sdk-0.5.4/horiba_sdk/sync/devices/device_manager.py +0 -213
  31. horiba_sdk-0.5.4/horiba_sdk/sync/devices/fake_device_manager.py +0 -91
  32. horiba_sdk-0.5.4/horiba_sdk/sync/devices/fake_icl_server.py +0 -82
  33. horiba_sdk-0.5.4/horiba_sdk/sync/devices/single_devices/abstract_device.py +0 -87
  34. horiba_sdk-0.5.4/horiba_sdk/sync/devices/single_devices/ccd.py +0 -674
  35. horiba_sdk-0.5.4/horiba_sdk/sync/devices/single_devices/monochromator.py +0 -413
  36. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/LICENSE +0 -0
  37. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/README.md +0 -0
  38. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/__init__.py +0 -0
  39. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/communication/__init__.py +0 -0
  40. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/communication/abstract_communicator.py +0 -0
  41. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/communication/communication_exception.py +0 -0
  42. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/core/__init__.py +0 -0
  43. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/core/clean_count_mode.py +0 -0
  44. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/core/resolution.py +0 -0
  45. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/core/timer_resolution.py +0 -0
  46. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/core/x_axis_conversion_type.py +0 -0
  47. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/devices/__init__.py +0 -0
  48. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/devices/abstract_device_discovery.py +0 -0
  49. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/devices/abstract_device_manager.py +0 -0
  50. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/devices/ccd_discovery.py +0 -0
  51. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/devices/fake_device_manager.py +0 -0
  52. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/devices/fake_responses/ccd.json +0 -0
  53. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/devices/fake_responses/icl.json +0 -0
  54. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/devices/fake_responses/monochromator.json +0 -0
  55. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/devices/monochromator_discovery.py +0 -0
  56. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/devices/single_devices/abstract_device.py +0 -0
  57. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/devices/single_devices/monochromator.py +0 -0
  58. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/icl_error/__init__.py +0 -0
  59. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/icl_error/abstract_error.py +0 -0
  60. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/icl_error/abstract_error_db.py +0 -0
  61. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/icl_error/error_list.json +0 -0
  62. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/icl_error/icl_error.py +0 -0
  63. {horiba_sdk-0.5.4 → horiba_sdk-0.7.0}/horiba_sdk/icl_error/icl_error_db.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: horiba-sdk
3
- Version: 0.5.4
3
+ Version: 0.7.0
4
4
  Summary: 'horiba-sdk' is a package that provides source code for the development with Horiba devices
5
5
  Home-page: https://github.com/ThatsTheEnd/horiba-python-sdk
6
6
  License: MIT
@@ -21,6 +21,7 @@ Requires-Dist: loguru (>=0.7.2,<0.8.0)
21
21
  Requires-Dist: overrides (>=7.4.0,<8.0.0)
22
22
  Requires-Dist: pint (>=0.23,<0.24)
23
23
  Requires-Dist: psutil (>=5.9.7,<6.0.0)
24
+ Requires-Dist: pydantic (>=2.10.6,<3.0.0)
24
25
  Requires-Dist: websockets (>=12.0,<13.0)
25
26
  Project-URL: Repository, https://github.com/ThatsTheEnd/horiba-python-sdk
26
27
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  from itertools import count
3
- from typing import Any, Dict, List, Optional, final
3
+ from typing import Any, Optional, Union, final
4
4
 
5
5
 
6
6
  class Command:
@@ -21,7 +21,7 @@ class Command:
21
21
 
22
22
  _id_counter = count(start=1) # Starts counting from 1
23
23
 
24
- def __init__(self, command: str, parameters: Dict[str, Any]):
24
+ def __init__(self, command: str, parameters: dict[str, Any]):
25
25
  self.id = next(self._id_counter) # Automatically assigns the next unique ID
26
26
  self.command = command
27
27
  self.parameters = parameters
@@ -43,7 +43,11 @@ class Response:
43
43
  """
44
44
 
45
45
  def __init__(
46
- self, id: int, command: str, results: Optional[Dict[str, Any]] = None, errors: Optional[List[str]] = None
46
+ self,
47
+ id: int,
48
+ command: str,
49
+ results: Optional[Union[dict[str, Any], Any]] = None,
50
+ errors: Optional[list[str]] = None,
47
51
  ):
48
52
  self.id = id
49
53
  self.command = command
@@ -63,7 +63,7 @@ class WebsocketCommunicator(AbstractCommunicator):
63
63
  raise CommunicationException(None, 'websocket already opened')
64
64
 
65
65
  try:
66
- self.websocket = await websockets.connect(self.uri)
66
+ self.websocket = await websockets.connect(self.uri, max_size=None) # type: ignore
67
67
  # self.flush_incoming_messages(self) # Flush any incoming messages (if any
68
68
 
69
69
  except websockets.WebSocketException as e:
@@ -14,7 +14,6 @@ class AcquisitionFormat(Enum):
14
14
 
15
15
  """
16
16
 
17
- SPECTRA = 0
18
- IMAGE = 1
17
+ SPECTRA_IMAGE = 1
19
18
  CROP = 2
20
19
  FAST_KINETICS = 3
@@ -0,0 +1,6 @@
1
+ from .linear_spectra_stitch import LinearSpectraStitch
2
+ from .simple_cut_spectra_stitch import SimpleCutSpectraStitch
3
+ from .spectra_stitch import SpectraStitch
4
+ from .y_displacement_spectra_stitch import YDisplacementSpectraStitch
5
+
6
+ __all__ = ['LinearSpectraStitch', 'SimpleCutSpectraStitch', 'YDisplacementSpectraStitch', 'SpectraStitch']
@@ -0,0 +1,90 @@
1
+ from typing import Any, List
2
+
3
+ from loguru import logger
4
+ from numpy import array, concatenate, dtype, ndarray
5
+ from overrides import override
6
+
7
+ from horiba_sdk.core.stitching.spectra_stitch import SpectraStitch
8
+
9
+
10
+ class LabSpec6SpectraStitch(SpectraStitch):
11
+ """Stitches a list of spectra using a weighted average as in LabSpec6"""
12
+
13
+ def __init__(self, spectra_list: List[List[List[float]]]) -> None:
14
+ """Constructs a linear stitch of spectra.
15
+
16
+ .. warning:: The spectra in the list must overlap
17
+
18
+ Parameters
19
+ spectra_list : List[List[List[float]]] List of spectra to stitch in the form [[x1_values, y1_values],
20
+ [x2_values, y2_values], etc].
21
+ """
22
+ stitched_spectrum = spectra_list[0]
23
+
24
+ for i in range(1, len(spectra_list)):
25
+ stitched_spectrum = self._stitch_spectra(stitched_spectrum, spectra_list[i])
26
+
27
+ self._stitched_spectrum: List[List[float]] = stitched_spectrum
28
+
29
+ @override
30
+ def stitch_with(self, other_stitch: SpectraStitch) -> SpectraStitch:
31
+ """Stitches this stitch with another stitch.
32
+
33
+ Parameters
34
+ other_stitch : SpectraStitch The other stitch to stitch with
35
+
36
+ Returns:
37
+ SpectraStitch: The stitched spectra.
38
+ """
39
+ new_stitch = LabSpec6SpectraStitch([self.stitched_spectra(), other_stitch.stitched_spectra()])
40
+ return new_stitch
41
+
42
+ @override
43
+ def stitched_spectra(self) -> Any:
44
+ """Returns the raw data of the stitched spectra.
45
+
46
+ Returns:
47
+ Any: The stitched spectra.
48
+ """
49
+ return self._stitched_spectrum
50
+
51
+ def _stitch_spectra(self, spectrum1: List[List[float]], spectrum2: List[List[float]]) -> List[List[float]]:
52
+ fx1, fy1 = spectrum1
53
+ fx2, fy2 = spectrum2
54
+
55
+ x1: ndarray[Any, dtype[Any]] = array(fx1)
56
+ x2: ndarray[Any, dtype[Any]] = array(fx2)
57
+ y1: ndarray[Any, dtype[Any]] = array(fy1)
58
+ y2: ndarray[Any, dtype[Any]] = array(fy2)
59
+
60
+ overlap_start = max(x1[0], x2[0])
61
+ overlap_end = min(x1[-1], x2[-1])
62
+
63
+ if overlap_start >= overlap_end:
64
+ logger.error(f'No overlap between two spectra: {spectrum1}, {spectrum2}')
65
+ raise Exception('No overlapping region between spectra')
66
+
67
+ mask1 = (x1 >= overlap_start) & (x1 <= overlap_end)
68
+ mask2 = (x2 >= overlap_start) & (x2 <= overlap_end)
69
+
70
+ x1_overlap = x1[mask1]
71
+ y1_overlap = y1[mask1]
72
+
73
+ x2_overlap = x2[mask2]
74
+ y2_overlap = y2[mask2]
75
+
76
+ A = (x1_overlap - overlap_start) / (overlap_end - overlap_start)
77
+ B = (overlap_end - x2_overlap) / (overlap_end - overlap_start)
78
+
79
+ y_stitched = (y1_overlap * A + y2_overlap * B) / (A + B)
80
+
81
+ x_before = x1[x1 < overlap_start]
82
+ y_before = y1[x1 < overlap_start]
83
+
84
+ x_after = x2[x2 > overlap_end]
85
+ y_after = y2[x2 > overlap_end]
86
+
87
+ x_stitched = concatenate([x_before, x1_overlap, x_after])
88
+ y_stitched_final = concatenate([y_before, y_stitched, y_after])
89
+
90
+ return [x_stitched.tolist(), y_stitched_final.tolist()]
@@ -0,0 +1,107 @@
1
+ from typing import Any
2
+
3
+ from loguru import logger
4
+ from numpy import argsort, array, concatenate, dtype, interp, ndarray
5
+ from overrides import override
6
+
7
+ from horiba_sdk.core.stitching.spectra_stitch import SpectraStitch
8
+
9
+
10
+ class LinearSpectraStitch(SpectraStitch):
11
+ """Stitches a list of spectra using a linear model"""
12
+
13
+ def __init__(self, spectra_list: list[list[list[float]]]) -> None:
14
+ """Constructs a linear stitch of spectra.
15
+
16
+ .. warning:: The spectra in the list must overlap
17
+
18
+ Parameters
19
+ spectra_list : List[List[List[float]]] List of spectra to stitch in the form [[x1_values, y1_values],
20
+ [x2_values, y2_values], etc].
21
+ """
22
+ stitched_spectrum = spectra_list[0]
23
+
24
+ for i in range(1, len(spectra_list)):
25
+ stitched_spectrum = self._stitch_spectra(stitched_spectrum, spectra_list[i])
26
+
27
+ self._stitched_spectrum: list[list[float]] = stitched_spectrum
28
+
29
+ @override
30
+ def stitch_with(self, other_stitch: SpectraStitch) -> SpectraStitch:
31
+ """Stitches this stitch with another stitch.
32
+
33
+ Parameters
34
+ other_stitch : SpectraStitch The other stitch to stitch with
35
+
36
+ Returns:
37
+ SpectraStitch: The stitched spectra.
38
+ """
39
+ new_stitch = LinearSpectraStitch([self.stitched_spectra(), other_stitch.stitched_spectra()])
40
+ return new_stitch
41
+
42
+ @override
43
+ def stitched_spectra(self) -> Any:
44
+ """Returns the raw data of the stitched spectra.
45
+
46
+ Returns:
47
+ Any: The stitched spectra.
48
+ """
49
+ return self._stitched_spectrum
50
+
51
+ def _stitch_spectra(self, spectrum1: list[list[float]], spectrum2: list[list[float]]) -> list[list[float]]:
52
+ fx1 = spectrum1[0]
53
+ fy1 = spectrum1[1][0]
54
+ fx2 = spectrum2[0]
55
+ fy2 = spectrum2[1][0]
56
+
57
+ # Convert to numpy arrays
58
+ x1: ndarray[Any, dtype[Any]] = array(fx1)
59
+ x2: ndarray[Any, dtype[Any]] = array(fx2)
60
+ y1: ndarray[Any, dtype[Any]] = array(fy1)
61
+ y2: ndarray[Any, dtype[Any]] = array(fy2)
62
+
63
+ # Sort spectra while maintaining x-y correspondence
64
+ sort1 = argsort(x1)
65
+ sort2 = argsort(x2)
66
+
67
+ # Create sorted views of both arrays
68
+ x1_sorted = x1[sort1]
69
+ y1_sorted = y1[sort1]
70
+ x2_sorted = x2[sort2]
71
+ y2_sorted = y2[sort2]
72
+
73
+ # Calculate true overlap region
74
+ x1_min, x1_max = x1_sorted[0], x1_sorted[-1]
75
+ x2_min, x2_max = x2_sorted[0], x2_sorted[-1]
76
+
77
+ overlap_start = max(x1_min, x2_min)
78
+ overlap_end = min(x1_max, x2_max)
79
+
80
+ logger.debug(f'Spectrum 1 range: {x1_min} to {x1_max}')
81
+ logger.debug(f'Spectrum 2 range: {x2_min} to {x2_max}')
82
+ logger.debug(f'Overlap region: {overlap_start} to {overlap_end}')
83
+
84
+ if overlap_start >= overlap_end:
85
+ logger.error(f'No overlap between spectra: [{x1_min}, {x1_max}] and [{x2_min}, {x2_max}]')
86
+ raise Exception('No overlapping region between spectra')
87
+
88
+ # Create masks for overlapping regions using sorted arrays
89
+ mask1 = (x1_sorted >= overlap_start) & (x1_sorted <= overlap_end)
90
+ mask2 = (x2_sorted >= overlap_start) & (x2_sorted <= overlap_end)
91
+
92
+ # Interpolate second spectrum onto first spectrum's x points in overlap region
93
+ y2_interp = interp(x1_sorted[mask1], x2_sorted[mask2], y2_sorted[mask2])
94
+
95
+ # Average the overlapping region
96
+ y_combined_overlap = (y1_sorted[mask1] + y2_interp) / 2
97
+
98
+ # Combine non-overlapping and overlapping regions
99
+ x_combined = concatenate((x1_sorted[~mask1], x1_sorted[mask1], x2_sorted[~mask2]))
100
+ y_combined = concatenate((y1_sorted[~mask1], y_combined_overlap, y2_sorted[~mask2]))
101
+
102
+ # Ensure final result is sorted
103
+ sort_indices = argsort(x_combined)
104
+ x_combined = x_combined[sort_indices]
105
+ y_combined = y_combined[sort_indices]
106
+
107
+ return [x_combined.tolist(), [y_combined.tolist()]]
@@ -0,0 +1,84 @@
1
+ from typing import Any, List
2
+
3
+ from loguru import logger
4
+ from numpy import array, concatenate, dtype, ndarray
5
+ from overrides import override
6
+
7
+ from horiba_sdk.core.stitching.spectra_stitch import SpectraStitch
8
+
9
+
10
+ class SimpleCutSpectraStitch(SpectraStitch):
11
+ """Stitches a list of spectra by always keeping the values from the next spectrum in the overlap region.
12
+
13
+ .. warning:: Produces a stitched spectrum with stairs
14
+ """
15
+
16
+ def __init__(self, spectra_list: List[List[List[float]]]) -> None:
17
+ """Constructs a linear stitch of spectra.
18
+
19
+ .. warning:: The spectra in the list must overlap
20
+
21
+ Parameters
22
+ spectra_list : List[List[List[float]]] List of spectra to stitch in the form [[x1_values, y1_values],
23
+ [x2_values, y2_values], etc].
24
+ """
25
+ stitched_spectrum = spectra_list[0]
26
+
27
+ for i in range(1, len(spectra_list)):
28
+ stitched_spectrum = self._stitch_spectra(stitched_spectrum, spectra_list[i])
29
+
30
+ self._stitched_spectrum: List[List[float]] = stitched_spectrum
31
+
32
+ @override
33
+ def stitch_with(self, other_stitch: SpectraStitch) -> SpectraStitch:
34
+ """Stitches this stitch with another stitch.
35
+
36
+ Parameters
37
+ other_stitch : SpectraStitch The other stitch to stitch with
38
+
39
+ Returns:
40
+ SpectraStitch: The stitched spectra.
41
+ """
42
+ new_stitch = SimpleCutSpectraStitch([self.stitched_spectra(), other_stitch.stitched_spectra()])
43
+ return new_stitch
44
+
45
+ @override
46
+ def stitched_spectra(self) -> Any:
47
+ """Returns the raw data of the stitched spectra.
48
+
49
+ Returns:
50
+ Any: The stitched spectra.
51
+ """
52
+ return self._stitched_spectrum
53
+
54
+ def _stitch_spectra(self, spectrum1: List[List[float]], spectrum2: List[List[float]]) -> List[List[float]]:
55
+ fx1, fy1 = spectrum1
56
+ fx2, fy2 = spectrum2
57
+
58
+ x1: ndarray[Any, dtype[Any]] = array(fx1)
59
+ x2: ndarray[Any, dtype[Any]] = array(fx2)
60
+ y1: ndarray[Any, dtype[Any]] = array(fy1)
61
+ y2: ndarray[Any, dtype[Any]] = array(fy2)
62
+
63
+ overlap_start = max(x1[0], x2[0])
64
+ overlap_end = min(x1[-1], x2[-1])
65
+
66
+ if overlap_start >= overlap_end:
67
+ logger.error(f'No overlap between two spectra: {spectrum1}, {spectrum2}')
68
+ raise Exception('No overlapping region between spectra')
69
+
70
+ mask2 = (x2 >= overlap_start) & (x2 <= overlap_end)
71
+
72
+ x2_overlap = x2[mask2]
73
+ y2_overlap = y2[mask2]
74
+
75
+ x1_before_overlap = x1[x1 < overlap_start]
76
+ y1_before_overlap = y1[x1 < overlap_start]
77
+
78
+ x2_after_overlap = x2[x2 > overlap_end]
79
+ y2_after_overlap = y2[x2 > overlap_end]
80
+
81
+ x_stitched = concatenate([x1_before_overlap, x2_overlap, x2_after_overlap])
82
+ y_stitched = concatenate([y1_before_overlap, y2_overlap, y2_after_overlap])
83
+
84
+ return [x_stitched.tolist(), y_stitched.tolist()]
@@ -0,0 +1,16 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any
3
+
4
+
5
+ class SpectraStitch(ABC):
6
+ """Stitches multiple spectra into a single spectrum."""
7
+
8
+ @abstractmethod
9
+ def stitch_with(self, other_stitch: 'SpectraStitch') -> 'SpectraStitch':
10
+ """Stitches this stitch with another stitch."""
11
+ pass
12
+
13
+ @abstractmethod
14
+ def stitched_spectra(self) -> Any:
15
+ """Returns the raw data of the stitched spectra."""
16
+ pass
@@ -0,0 +1,87 @@
1
+ from typing import Any, List
2
+
3
+ from loguru import logger
4
+ from numpy import array, concatenate, dtype, ndarray
5
+ from overrides import override
6
+
7
+ from horiba_sdk.core.stitching.spectra_stitch import SpectraStitch
8
+
9
+
10
+ class YDisplacementSpectraStitch(SpectraStitch):
11
+ """Stiches a list of spectra using a linear model"""
12
+
13
+ def __init__(self, spectrum1: List[List[float]], spectrum2: List[List[float]], y_displacement_count: int) -> None:
14
+ """Constructs a linear stitch of spectra.
15
+
16
+ .. warning:: The spectra in the list must overlap
17
+
18
+ Parameters
19
+ spectra_list : List[List[List[float]]] List of spectra to stitch in the form [[x1_values, y1_values],
20
+ [x2_values, y2_values], etc].
21
+ y_displacement_count : int The amount of displacement in the y direction for the second spectrum
22
+ """
23
+ self._y_displacement_count = y_displacement_count
24
+ stitched_spectrum = self._stitch_spectra(spectrum1, spectrum2)
25
+
26
+ self._stitched_spectrum: List[List[float]] = stitched_spectrum
27
+
28
+ @override
29
+ def stitch_with(self, other_stitch: SpectraStitch) -> SpectraStitch:
30
+ """Stitches this stitch with another stitch.
31
+
32
+ Parameters
33
+ other_stitch : SpectraStitch The other stitch to stitch with
34
+
35
+ Returns:
36
+ SpectraStitch: The stitched spectra.
37
+ """
38
+ new_stitch = YDisplacementSpectraStitch([self.stitched_spectra(), other_stitch.stitched_spectra()])
39
+ return new_stitch
40
+
41
+ @override
42
+ def stitched_spectra(self) -> Any:
43
+ """Returns the raw data of the stitched spectra.
44
+
45
+ Returns:
46
+ Any: The stitched spectra.
47
+ """
48
+ return self._stitched_spectrum
49
+
50
+ def _stitch_spectra(self, spectrum1: List[List[float]], spectrum2: List[List[float]]) -> List[List[float]]:
51
+ fx1, fy1 = spectrum1
52
+ fx2, fy2 = spectrum2
53
+
54
+ x1: ndarray[Any, dtype[Any]] = array(fx1)
55
+ x2: ndarray[Any, dtype[Any]] = array(fx2)
56
+ y1: ndarray[Any, dtype[Any]] = array(fy1)
57
+ y2: ndarray[Any, dtype[Any]] = array(fy2)
58
+
59
+ overlap_start = max(x1[0], x2[0])
60
+ overlap_end = min(x1[-1], x2[-1])
61
+
62
+ if overlap_start >= overlap_end:
63
+ logger.error(f'No overlap between two spectra: {spectrum1}, {spectrum2}')
64
+ raise Exception('No overlapping region between spectra')
65
+
66
+ # Masks for overlapping region
67
+ mask1 = (x1 >= overlap_start) & (x1 <= overlap_end)
68
+ mask2 = (x2 >= overlap_start) & (x2 <= overlap_end)
69
+
70
+ x1_overlap = x1[mask1]
71
+ y1_overlap = y1[mask1]
72
+
73
+ y2_displaced = y2 + self._y_displacement_count
74
+ y2_overlap = y2_displaced[mask2]
75
+
76
+ y_stitched_overlap = (y1_overlap + y2_overlap) / 2
77
+
78
+ x1_before_overlap = x1[x1 < overlap_start]
79
+ y1_before_overlap = y1[x1 < overlap_start]
80
+
81
+ x2_after_overlap = x2[x2 > overlap_end]
82
+ y2_after_overlap = y2_displaced[x2 > overlap_end]
83
+
84
+ x_stitched = concatenate([x1_before_overlap, x1_overlap, x2_after_overlap])
85
+ y_stitched = concatenate([y1_before_overlap, y_stitched_overlap, y2_after_overlap])
86
+
87
+ return [x_stitched.tolist(), y_stitched.tolist()]
@@ -0,0 +1,6 @@
1
+ from enum import Enum
2
+
3
+
4
+ class TriggerInputPolarity(Enum):
5
+ ACTIVE_LOW = 0
6
+ ACTIVE_HIGH = 1
@@ -50,7 +50,8 @@ from horiba_sdk.communication import (
50
50
  from horiba_sdk.devices import AbstractDeviceManager
51
51
  from horiba_sdk.devices.ccd_discovery import ChargeCoupledDevicesDiscovery
52
52
  from horiba_sdk.devices.monochromator_discovery import MonochromatorsDiscovery
53
- from horiba_sdk.devices.single_devices import ChargeCoupledDevice, Monochromator
53
+ from horiba_sdk.devices.single_devices import ChargeCoupledDevice, Monochromator, SpectrAcq3
54
+ from horiba_sdk.devices.spectracq3_discovery import SpectrAcq3Discovery
54
55
  from horiba_sdk.icl_error import AbstractError, AbstractErrorDB, ICLErrorDB
55
56
 
56
57
 
@@ -66,7 +67,7 @@ class DeviceManager(AbstractDeviceManager):
66
67
  icl_ip: str = '127.0.0.1',
67
68
  icl_port: str = '25010',
68
69
  enable_binary_messages: bool = True,
69
- enable_logging: bool = False
70
+ enable_logging: bool = False,
70
71
  ):
71
72
  """
72
73
  Initializes the DeviceManager with the specified communicator class.
@@ -81,7 +82,7 @@ class DeviceManager(AbstractDeviceManager):
81
82
  # By default, logging is disabled for a library, so if desired it can be enabled
82
83
  root_name_space: str = __name__.split('.')[0]
83
84
  if enable_logging:
84
- logger.info(f"Initializing logger for namespace: {root_name_space}")
85
+ logger.info(f'Initializing logger for namespace: {root_name_space}')
85
86
  logger.enable(root_name_space)
86
87
  else:
87
88
  logger.disable(root_name_space)
@@ -95,6 +96,7 @@ class DeviceManager(AbstractDeviceManager):
95
96
  self._binary_messages: bool = enable_binary_messages
96
97
  self._charge_coupled_devices: list[ChargeCoupledDevice] = []
97
98
  self._monochromators: list[Monochromator] = []
99
+ self._spectracq3_devices: list[SpectrAcq3] = []
98
100
 
99
101
  error_list_path: Path = Path(str(importlib.resources.files('horiba_sdk.icl_error') / 'error_list.json'))
100
102
  self._icl_error_db: AbstractErrorDB = ICLErrorDB(error_list_path)
@@ -230,6 +232,10 @@ class DeviceManager(AbstractDeviceManager):
230
232
  await monochromators_discovery.execute(error_on_no_device)
231
233
  self._monochromators = monochromators_discovery.monochromators()
232
234
 
235
+ spectracq3_discovery: SpectrAcq3Discovery = SpectrAcq3Discovery(self._icl_communicator, self._icl_error_db)
236
+ await spectracq3_discovery.execute(error_on_no_device)
237
+ self._spectracq3_devices = spectracq3_discovery.spectracq3_devices()
238
+
233
239
  @property
234
240
  @override
235
241
  def communicator(self) -> AbstractCommunicator:
@@ -262,3 +268,13 @@ class DeviceManager(AbstractDeviceManager):
262
268
  List[ChargeCoupledDevice]: The detected CCDS.
263
269
  """
264
270
  return self._charge_coupled_devices
271
+
272
+ @property
273
+ def spectracq3_devices(self) -> list[SpectrAcq3]:
274
+ """
275
+ The detected SpectrAcq3 devices, should be called after :meth:`discover_devices`
276
+
277
+ Returns:
278
+ List[SpectrAcq3]: The detected SpectrAcq3 devices.
279
+ """
280
+ return self._spectracq3_devices
@@ -26,6 +26,10 @@ class FakeICLServer:
26
26
  with open(ccd_fake_responses_path) as json_file:
27
27
  self.ccd_responses = json.load(json_file)
28
28
 
29
+ spectracq3_fake_responses_path = os.path.join(fake_responses_path, 'spectracq3.json')
30
+ with open(spectracq3_fake_responses_path) as json_file:
31
+ self.spectracq3_responses = json.load(json_file)
32
+
29
33
  async def echo(self, websocket):
30
34
  async for message in websocket:
31
35
  logger.info('received: {message}', message=message)
@@ -43,6 +47,9 @@ class FakeICLServer:
43
47
  elif command['command'].startswith('ccd_'):
44
48
  response = json.dumps(self.ccd_responses[command['command']])
45
49
  await websocket.send(response)
50
+ elif command['command'].startswith('saq3_'):
51
+ response = json.dumps(self.spectracq3_responses[command['command']])
52
+ await websocket.send(response)
46
53
  else:
47
54
  logger.info('unknown command, responding with message')
48
55
  await websocket.send(message)