pyglaze 0.4.1__tar.gz → 0.4.5__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 (33) hide show
  1. {pyglaze-0.4.1/src/pyglaze.egg-info → pyglaze-0.4.5}/PKG-INFO +1 -1
  2. {pyglaze-0.4.1 → pyglaze-0.4.5}/pyproject.toml +2 -2
  3. pyglaze-0.4.5/src/pyglaze/__init__.py +1 -0
  4. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/datamodels/pulse.py +1 -1
  5. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/device/ampcom.py +17 -12
  6. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/device/configuration.py +22 -2
  7. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/devtools/mock_device.py +2 -0
  8. {pyglaze-0.4.1 → pyglaze-0.4.5/src/pyglaze.egg-info}/PKG-INFO +1 -1
  9. pyglaze-0.4.1/src/pyglaze/__init__.py +0 -1
  10. {pyglaze-0.4.1 → pyglaze-0.4.5}/LICENSE +0 -0
  11. {pyglaze-0.4.1 → pyglaze-0.4.5}/MANIFEST.in +0 -0
  12. {pyglaze-0.4.1 → pyglaze-0.4.5}/README.md +0 -0
  13. {pyglaze-0.4.1 → pyglaze-0.4.5}/setup.cfg +0 -0
  14. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/datamodels/__init__.py +0 -0
  15. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/datamodels/waveform.py +0 -0
  16. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/device/__init__.py +0 -0
  17. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/devtools/__init__.py +0 -0
  18. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/devtools/thz_pulse.py +0 -0
  19. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/helpers/__init__.py +0 -0
  20. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/helpers/_types.py +0 -0
  21. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/helpers/utilities.py +0 -0
  22. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/interpolation/__init__.py +0 -0
  23. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/interpolation/interpolation.py +0 -0
  24. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/py.typed +0 -0
  25. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/scanning/__init__.py +0 -0
  26. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/scanning/_asyncscanner.py +0 -0
  27. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/scanning/_exceptions.py +0 -0
  28. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/scanning/client.py +0 -0
  29. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze/scanning/scanner.py +0 -0
  30. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze.egg-info/SOURCES.txt +0 -0
  31. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze.egg-info/dependency_links.txt +0 -0
  32. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze.egg-info/requires.txt +0 -0
  33. {pyglaze-0.4.1 → pyglaze-0.4.5}/src/pyglaze.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyglaze
3
- Version: 0.4.1
3
+ Version: 0.4.5
4
4
  Summary: Pyglaze is a library used to operate the devices of Glaze Technologies
5
5
  Author: GLAZE Technologies ApS
6
6
  License: BSD 3-Clause License
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyglaze"
3
- version = "0.4.1"
3
+ version = "0.4.5"
4
4
  description = "Pyglaze is a library used to operate the devices of Glaze Technologies"
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
@@ -74,7 +74,7 @@ convention = "google"
74
74
  ]
75
75
 
76
76
  [tool.bumpver]
77
- current_version = "0.4.1"
77
+ current_version = "0.4.5"
78
78
  version_pattern = "MAJOR.MINOR.PATCH[-TAG]"
79
79
  commit_message = "BUMP VERSION {old_version} -> {new_version}"
80
80
  tag_message = "v{new_version}"
@@ -0,0 +1 @@
1
+ __version__ = "0.4.5"
@@ -174,7 +174,7 @@ class Pulse:
174
174
  """
175
175
  extrema = [scan._get_min_or_max_idx(wrt_max=wrt_max) for scan in scans] # noqa: SLF001
176
176
  n_before = min(extrema)
177
- n_after = min(len(scan) - index - 1 for scan, index in zip(scans, extrema))
177
+ n_after = min(len(scan) - index for scan, index in zip(scans, extrema))
178
178
  roughly_aligned = [
179
179
  cls._from_slice(scan, slice(index - n_before, index + n_after))
180
180
  for index, scan in zip(extrema, scans)
@@ -15,6 +15,8 @@ from bitstring import BitArray
15
15
  from serial import serialutil
16
16
 
17
17
  from pyglaze.device.configuration import (
18
+ BYTES_PER_CHANNEL,
19
+ N_CHANNELS,
18
20
  DeviceConfiguration,
19
21
  Interval,
20
22
  LeDeviceConfiguration,
@@ -78,7 +80,7 @@ class _LeAmpCom:
78
80
 
79
81
  We expect to receive 3 arrays of floats (delays, X and Y), each with self.scanning_points elements.
80
82
  """
81
- return self.scanning_points * 12
83
+ return self.scanning_points * N_CHANNELS * BYTES_PER_CHANNEL
82
84
 
83
85
  @property
84
86
  def serial_number_bytes(self: _LeAmpCom) -> int:
@@ -150,9 +152,11 @@ class _LeAmpCom:
150
152
  angle = np.arctan2(np.array(Ys), np.array(Xs))
151
153
  return r, np.rad2deg(angle)
152
154
 
153
- def _encode_send_response(self: _LeAmpCom, command: str) -> str:
155
+ def _encode_send_response(
156
+ self: _LeAmpCom, command: str, *, check_ack: bool = True
157
+ ) -> str:
154
158
  self._encode_and_send(command)
155
- return self._get_response(command)
159
+ return self._get_response(command, check_ack=check_ack)
156
160
 
157
161
  def _encode_and_send(self: _LeAmpCom, command: str) -> None:
158
162
  self.__ser.write(command.encode(self.ENCODING))
@@ -180,13 +184,13 @@ class _LeAmpCom:
180
184
  @_BackoffRetry(
181
185
  backoff_base=1e-2, max_tries=3, logger=logging.getLogger(LOGGER_NAME)
182
186
  )
183
- def _get_response(self: _LeAmpCom, command: str) -> str:
187
+ def _get_response(self: _LeAmpCom, command: str, *, check_ack: bool = True) -> str:
184
188
  response = self.__ser.read_until().decode(self.ENCODING).strip()
185
189
 
186
190
  if len(response) == 0:
187
191
  msg = f"Command: '{command}'. Empty response received"
188
192
  raise serialutil.SerialException(msg)
189
- if response[: len(self.OK_RESPONSE)] != self.OK_RESPONSE:
193
+ if check_ack and response[: len(self.OK_RESPONSE)] != self.OK_RESPONSE:
190
194
  msg = f"Command: '{command}'. Expected response '{self.OK_RESPONSE}', received: '{response}'"
191
195
  raise DeviceComError(msg)
192
196
  return response
@@ -220,13 +224,14 @@ class _LeAmpCom:
220
224
  ]
221
225
 
222
226
  def _get_status(self: _LeAmpCom) -> _LeStatus:
223
- msg = self._encode_send_response(self.STATUS_COMMAND)
224
- if msg == _LeStatus.SCANNING.value:
227
+ response = self._encode_send_response(self.STATUS_COMMAND, check_ack=False)
228
+
229
+ if response == _LeStatus.SCANNING.value:
225
230
  return _LeStatus.SCANNING
226
- if msg == _LeStatus.IDLE.value:
231
+ if response == _LeStatus.IDLE.value:
227
232
  return _LeStatus.IDLE
228
- msg = f"Unknown status: {msg}"
229
- raise ValueError(msg)
233
+ msg = f"Unknown status: {response}"
234
+ raise DeviceComError(msg)
230
235
 
231
236
 
232
237
  class _LeStatus(Enum):
@@ -238,8 +243,8 @@ def _serial_factory(config: DeviceConfiguration) -> serial.Serial | LeMockDevice
238
243
  if "mock_device" in config.amp_port:
239
244
  return _mock_device_factory(config)
240
245
 
241
- return serial.Serial(
242
- port=config.amp_port,
246
+ return serial.serial_for_url(
247
+ url=config.amp_port,
243
248
  baudrate=config.amp_baudrate,
244
249
  timeout=config.amp_timeout_seconds,
245
250
  )
@@ -11,6 +11,14 @@ if TYPE_CHECKING:
11
11
  T = TypeVar("T", bound="DeviceConfiguration")
12
12
 
13
13
 
14
+ # Serial protocol constants for timeout calculation
15
+ SERIAL_BITS_PER_BYTE = 10 # 8 data bits + start + stop bits
16
+ N_CHANNELS = 3 # delays, X, Y arrays transmitted
17
+ BYTES_PER_CHANNEL = 4 # 32-bit float = 4 bytes
18
+ TIMEOUT_SAFETY_FACTOR = 2.5 # Safety multiplier for network/processing delays
19
+ TIMEOUT_BASELINE_S = 0.05 # Fixed additive latency
20
+
21
+
14
22
  @dataclass
15
23
  class Interval:
16
24
  """An interval with a lower and upper bounds between 0 and 1 to scan."""
@@ -50,9 +58,10 @@ class Interval:
50
58
  class DeviceConfiguration(ABC):
51
59
  """Base class for device configurations."""
52
60
 
53
- amp_timeout_seconds: float
61
+ amp_timeout_seconds: float | None
54
62
  amp_port: str
55
63
  amp_baudrate: ClassVar[int]
64
+ n_points: int
56
65
 
57
66
  @property
58
67
  @abstractmethod
@@ -92,11 +101,22 @@ class LeDeviceConfiguration(DeviceConfiguration):
92
101
  n_points: int = 1000
93
102
  scan_intervals: list[Interval] = field(default_factory=lambda: [Interval(0.0, 1.0)])
94
103
  integration_periods: int = 10
95
- amp_timeout_seconds: float = 0.2
104
+ amp_timeout_seconds: float | None = None
96
105
  modulation_frequency: int = 10000 # Hz
97
106
 
98
107
  amp_baudrate: ClassVar[int] = 1000000 # bit/s
99
108
 
109
+ def __post_init__(self: LeDeviceConfiguration) -> None:
110
+ """Calculate dynamic timeout if not explicitly set."""
111
+ if self.amp_timeout_seconds is None:
112
+ # Calculate timeout based on data transfer requirements
113
+ bytes_to_receive = self.n_points * N_CHANNELS * BYTES_PER_CHANNEL
114
+ bits_to_transfer = bytes_to_receive * SERIAL_BITS_PER_BYTE
115
+ transfer_time = bits_to_transfer / self.amp_baudrate
116
+ self.amp_timeout_seconds = (
117
+ transfer_time + TIMEOUT_BASELINE_S
118
+ ) * TIMEOUT_SAFETY_FACTOR
119
+
100
120
  @property
101
121
  def _sweep_length_ms(self: LeDeviceConfiguration) -> float:
102
122
  return self.n_points * self._time_constant_ms
@@ -154,6 +154,8 @@ class LeMockDevice(MockDevice):
154
154
  self._scan_start_time = time.time()
155
155
  elif msg == "R":
156
156
  self._scan_has_finished()
157
+ elif msg == "H":
158
+ self.state = _LeMockState.RECEIVED_STATUS_REQUEST
157
159
  elif msg == "s":
158
160
  self.state = _LeMockState.RECEIVED_SERIAL_NUMBER_REQUEST
159
161
  elif msg == "v":
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyglaze
3
- Version: 0.4.1
3
+ Version: 0.4.5
4
4
  Summary: Pyglaze is a library used to operate the devices of Glaze Technologies
5
5
  Author: GLAZE Technologies ApS
6
6
  License: BSD 3-Clause License
@@ -1 +0,0 @@
1
- __version__ = "0.4.1"
File without changes
File without changes
File without changes
File without changes
File without changes