pyglaze 0.2.1__tar.gz → 0.2.2__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.
- {pyglaze-0.2.1/src/pyglaze.egg-info → pyglaze-0.2.2}/PKG-INFO +1 -1
- {pyglaze-0.2.1 → pyglaze-0.2.2}/pyproject.toml +2 -2
- pyglaze-0.2.2/src/pyglaze/__init__.py +1 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze/device/ampcom.py +14 -6
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze/devtools/mock_device.py +11 -1
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze/scanning/_asyncscanner.py +1 -0
- pyglaze-0.2.2/src/pyglaze/scanning/_exceptions.py +8 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze/scanning/scanner.py +34 -2
- {pyglaze-0.2.1 → pyglaze-0.2.2/src/pyglaze.egg-info}/PKG-INFO +1 -1
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze.egg-info/SOURCES.txt +1 -0
- pyglaze-0.2.1/src/pyglaze/__init__.py +0 -1
- {pyglaze-0.2.1 → pyglaze-0.2.2}/LICENSE +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/MANIFEST.in +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/README.md +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/setup.cfg +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze/datamodels/__init__.py +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze/datamodels/pulse.py +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze/datamodels/waveform.py +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze/device/__init__.py +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze/device/configuration.py +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze/devtools/__init__.py +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze/devtools/thz_pulse.py +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze/helpers/__init__.py +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze/helpers/types.py +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze/helpers/utilities.py +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze/interpolation/__init__.py +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze/interpolation/interpolation.py +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze/py.typed +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze/scanning/__init__.py +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze/scanning/client.py +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze.egg-info/dependency_links.txt +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze.egg-info/requires.txt +0 -0
- {pyglaze-0.2.1 → pyglaze-0.2.2}/src/pyglaze.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pyglaze"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.2"
|
|
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.2.
|
|
77
|
+
current_version = "0.2.2"
|
|
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.2.2"
|
|
@@ -105,9 +105,7 @@ class _ForceAmpCom:
|
|
|
105
105
|
|
|
106
106
|
def __del__(self: _ForceAmpCom) -> None:
|
|
107
107
|
"""Closes connection when class instance goes out of scope."""
|
|
108
|
-
|
|
109
|
-
# If the serial device does not exist, self.__ser is never created - hence catch
|
|
110
|
-
self.__ser.close()
|
|
108
|
+
self.disconnect()
|
|
111
109
|
|
|
112
110
|
def write_all(self: _ForceAmpCom) -> list[str]:
|
|
113
111
|
responses = []
|
|
@@ -190,6 +188,12 @@ class _ForceAmpCom:
|
|
|
190
188
|
output_array[iteration, 2] = angle
|
|
191
189
|
return output_array
|
|
192
190
|
|
|
191
|
+
def disconnect(self: _ForceAmpCom) -> None:
|
|
192
|
+
"""Closes connection."""
|
|
193
|
+
with contextlib.suppress(AttributeError):
|
|
194
|
+
# If the serial device does not exist, self.__ser is never created - hence catch
|
|
195
|
+
self.__ser.close()
|
|
196
|
+
|
|
193
197
|
def _encode_send_response(self: _ForceAmpCom, command: str) -> str:
|
|
194
198
|
self._encode_and_send(command)
|
|
195
199
|
return self._get_response()
|
|
@@ -264,9 +268,7 @@ class _LeAmpCom:
|
|
|
264
268
|
|
|
265
269
|
def __del__(self: _LeAmpCom) -> None:
|
|
266
270
|
"""Closes connection when class instance goes out of scope."""
|
|
267
|
-
|
|
268
|
-
# If the serial device does not exist, self.__ser is never created - hence catch
|
|
269
|
-
self.__ser.close()
|
|
271
|
+
self.disconnect()
|
|
270
272
|
|
|
271
273
|
def write_all(self: _LeAmpCom) -> list[str]:
|
|
272
274
|
responses: list[str] = []
|
|
@@ -294,6 +296,12 @@ class _LeAmpCom:
|
|
|
294
296
|
radii, angles = self._convert_to_r_angle(Xs, Ys)
|
|
295
297
|
return self.START_COMMAND, np.array(times), np.array(radii), np.array(angles)
|
|
296
298
|
|
|
299
|
+
def disconnect(self: _LeAmpCom) -> None:
|
|
300
|
+
"""Closes connection when class instance goes out of scope."""
|
|
301
|
+
with contextlib.suppress(AttributeError):
|
|
302
|
+
# If the serial device does not exist, self.__ser is never created - hence catch
|
|
303
|
+
self.__ser.close()
|
|
304
|
+
|
|
297
305
|
@cached_property
|
|
298
306
|
def _intervals(self: _LeAmpCom) -> list[Interval]:
|
|
299
307
|
"""Intervals squished into effective DAC range."""
|
|
@@ -28,6 +28,7 @@ class MockDevice(ABC):
|
|
|
28
28
|
n_fails: float = np.inf,
|
|
29
29
|
*,
|
|
30
30
|
empty_responses: bool = False,
|
|
31
|
+
instant_response: bool = False,
|
|
31
32
|
) -> None:
|
|
32
33
|
pass
|
|
33
34
|
|
|
@@ -42,6 +43,8 @@ class ForceMockDevice(MockDevice):
|
|
|
42
43
|
self: ForceMockDevice,
|
|
43
44
|
fail_after: float = np.inf,
|
|
44
45
|
n_fails: float = np.inf,
|
|
46
|
+
*,
|
|
47
|
+
instant_response: bool = False,
|
|
45
48
|
) -> None:
|
|
46
49
|
self.fail_after = fail_after
|
|
47
50
|
self.fails_wanted = n_fails
|
|
@@ -50,6 +53,7 @@ class ForceMockDevice(MockDevice):
|
|
|
50
53
|
self.rng = np.random.default_rng()
|
|
51
54
|
self.valid_input = True
|
|
52
55
|
self.experiment_running = False
|
|
56
|
+
self.instant_response = instant_response
|
|
53
57
|
|
|
54
58
|
self._periods = None
|
|
55
59
|
self._frequency = None
|
|
@@ -131,7 +135,8 @@ class ForceMockDevice(MockDevice):
|
|
|
131
135
|
for _ in range(self.in_waiting):
|
|
132
136
|
return_string += self.__create_random_datapoint
|
|
133
137
|
return_string += "!D,DONE\\r"
|
|
134
|
-
|
|
138
|
+
if not self.instant_response:
|
|
139
|
+
sleep(self.sweep_length * 1e-3)
|
|
135
140
|
self.n_scans += 1
|
|
136
141
|
if self.n_scans > self.fail_after and self.n_failures < self.fails_wanted:
|
|
137
142
|
self.n_failures += 1
|
|
@@ -190,6 +195,7 @@ class LeMockDevice(MockDevice):
|
|
|
190
195
|
n_fails: float = np.inf,
|
|
191
196
|
*,
|
|
192
197
|
empty_responses: bool = False,
|
|
198
|
+
instant_response: bool = False,
|
|
193
199
|
) -> None:
|
|
194
200
|
self.fail_after = fail_after
|
|
195
201
|
self.fails_wanted = n_fails
|
|
@@ -204,6 +210,7 @@ class LeMockDevice(MockDevice):
|
|
|
204
210
|
self.scanning_list: list[float] | None = None
|
|
205
211
|
self._scan_start_time: float | None = None
|
|
206
212
|
self.empty_responses = empty_responses
|
|
213
|
+
self.instant_response = instant_response
|
|
207
214
|
|
|
208
215
|
def write(self: LeMockDevice, input_bytes: bytes) -> None:
|
|
209
216
|
"""Mock-write to the serial connection."""
|
|
@@ -365,6 +372,7 @@ def list_mock_devices() -> list[str]:
|
|
|
365
372
|
"mock_device_scan_should_fail",
|
|
366
373
|
"mock_device_fail_first_scan",
|
|
367
374
|
"mock_device_empty_responses",
|
|
375
|
+
"mock_device_instant",
|
|
368
376
|
]
|
|
369
377
|
|
|
370
378
|
|
|
@@ -374,6 +382,8 @@ def _mock_device_factory(config: DeviceConfiguration) -> MockDevice:
|
|
|
374
382
|
return mock_class(fail_after=0)
|
|
375
383
|
if config.amp_port == "mock_device":
|
|
376
384
|
return mock_class()
|
|
385
|
+
if config.amp_port == "mock_device_instant":
|
|
386
|
+
return mock_class(instant_response=True)
|
|
377
387
|
if config.amp_port == "mock_device_fail_first_scan":
|
|
378
388
|
return mock_class(fail_after=0, n_fails=1)
|
|
379
389
|
if config.amp_port == "mock_device_empty_responses":
|
|
@@ -4,6 +4,7 @@ from abc import ABC, abstractmethod
|
|
|
4
4
|
from typing import TYPE_CHECKING, Generic, TypeVar
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
|
+
from serial import SerialException
|
|
7
8
|
|
|
8
9
|
from pyglaze.datamodels import UnprocessedWaveform
|
|
9
10
|
from pyglaze.device.ampcom import _ForceAmpCom, _LeAmpCom
|
|
@@ -12,6 +13,7 @@ from pyglaze.device.configuration import (
|
|
|
12
13
|
ForceDeviceConfiguration,
|
|
13
14
|
LeDeviceConfiguration,
|
|
14
15
|
)
|
|
16
|
+
from pyglaze.scanning._exceptions import ScanError
|
|
15
17
|
|
|
16
18
|
if TYPE_CHECKING:
|
|
17
19
|
from pyglaze.helpers.types import FloatArray
|
|
@@ -42,6 +44,10 @@ class _ScannerImplementation(ABC, Generic[TConfig]):
|
|
|
42
44
|
def update_config(self: _ScannerImplementation, new_config: TConfig) -> None:
|
|
43
45
|
pass
|
|
44
46
|
|
|
47
|
+
@abstractmethod
|
|
48
|
+
def disconnect(self: _ScannerImplementation) -> None:
|
|
49
|
+
pass
|
|
50
|
+
|
|
45
51
|
|
|
46
52
|
class Scanner:
|
|
47
53
|
"""A synchronous scanner for Glaze terahertz devices."""
|
|
@@ -76,6 +82,10 @@ class Scanner:
|
|
|
76
82
|
"""
|
|
77
83
|
self._scanner_impl.update_config(new_config)
|
|
78
84
|
|
|
85
|
+
def disconnect(self: Scanner) -> None:
|
|
86
|
+
"""Close serial connection."""
|
|
87
|
+
self._scanner_impl.disconnect()
|
|
88
|
+
|
|
79
89
|
|
|
80
90
|
class ForceScanner(_ScannerImplementation[ForceDeviceConfiguration]):
|
|
81
91
|
"""Perform synchronous terahertz scanning using a given DeviceConfiguration.
|
|
@@ -87,7 +97,7 @@ class ForceScanner(_ScannerImplementation[ForceDeviceConfiguration]):
|
|
|
87
97
|
|
|
88
98
|
def __init__(self: ForceScanner, config: ForceDeviceConfiguration) -> None:
|
|
89
99
|
self._config: ForceDeviceConfiguration
|
|
90
|
-
self._ampcom: _ForceAmpCom
|
|
100
|
+
self._ampcom: _ForceAmpCom | None = None
|
|
91
101
|
self.config = config
|
|
92
102
|
self._phase_estimator = _LockinPhaseEstimator()
|
|
93
103
|
|
|
@@ -133,6 +143,9 @@ class ForceScanner(_ScannerImplementation[ForceDeviceConfiguration]):
|
|
|
133
143
|
Returns:
|
|
134
144
|
Unprocessed scan.
|
|
135
145
|
"""
|
|
146
|
+
if self._ampcom is None:
|
|
147
|
+
msg = "Scanner not configured"
|
|
148
|
+
raise ScanError(msg)
|
|
136
149
|
_, responses = self._ampcom.start_scan()
|
|
137
150
|
|
|
138
151
|
time = responses[:, 0]
|
|
@@ -152,6 +165,14 @@ class ForceScanner(_ScannerImplementation[ForceDeviceConfiguration]):
|
|
|
152
165
|
"""
|
|
153
166
|
self.config = new_config
|
|
154
167
|
|
|
168
|
+
def disconnect(self: ForceScanner) -> None:
|
|
169
|
+
"""Close serial connection."""
|
|
170
|
+
if self._ampcom is None:
|
|
171
|
+
msg = "Scanner not connected"
|
|
172
|
+
raise SerialException(msg)
|
|
173
|
+
self._ampcom.disconnect()
|
|
174
|
+
self._ampcom = None
|
|
175
|
+
|
|
155
176
|
|
|
156
177
|
class LeScanner(_ScannerImplementation[LeDeviceConfiguration]):
|
|
157
178
|
"""Perform synchronous terahertz scanning using a given DeviceConfiguration.
|
|
@@ -162,7 +183,7 @@ class LeScanner(_ScannerImplementation[LeDeviceConfiguration]):
|
|
|
162
183
|
|
|
163
184
|
def __init__(self: LeScanner, config: LeDeviceConfiguration) -> None:
|
|
164
185
|
self._config: LeDeviceConfiguration
|
|
165
|
-
self._ampcom: _LeAmpCom
|
|
186
|
+
self._ampcom: _LeAmpCom | None = None
|
|
166
187
|
self.config = config
|
|
167
188
|
self._phase_estimator = _LockinPhaseEstimator()
|
|
168
189
|
|
|
@@ -198,6 +219,9 @@ class LeScanner(_ScannerImplementation[LeDeviceConfiguration]):
|
|
|
198
219
|
Returns:
|
|
199
220
|
Unprocessed scan.
|
|
200
221
|
"""
|
|
222
|
+
if self._ampcom is None:
|
|
223
|
+
msg = "Scanner not configured"
|
|
224
|
+
raise ScanError(msg)
|
|
201
225
|
_, time, radius, theta = self._ampcom.start_scan()
|
|
202
226
|
self._phase_estimator.update_estimate(radius=radius, theta=theta)
|
|
203
227
|
|
|
@@ -213,6 +237,14 @@ class LeScanner(_ScannerImplementation[LeDeviceConfiguration]):
|
|
|
213
237
|
"""
|
|
214
238
|
self.config = new_config
|
|
215
239
|
|
|
240
|
+
def disconnect(self: LeScanner) -> None:
|
|
241
|
+
"""Close serial connection."""
|
|
242
|
+
if self._ampcom is None:
|
|
243
|
+
msg = "Scanner not connected"
|
|
244
|
+
raise ScanError(msg)
|
|
245
|
+
self._ampcom.disconnect()
|
|
246
|
+
self._ampcom = None
|
|
247
|
+
|
|
216
248
|
|
|
217
249
|
def _scanner_factory(config: DeviceConfiguration) -> _ScannerImplementation:
|
|
218
250
|
if isinstance(config, ForceDeviceConfiguration):
|
|
@@ -25,5 +25,6 @@ src/pyglaze/interpolation/__init__.py
|
|
|
25
25
|
src/pyglaze/interpolation/interpolation.py
|
|
26
26
|
src/pyglaze/scanning/__init__.py
|
|
27
27
|
src/pyglaze/scanning/_asyncscanner.py
|
|
28
|
+
src/pyglaze/scanning/_exceptions.py
|
|
28
29
|
src/pyglaze/scanning/client.py
|
|
29
30
|
src/pyglaze/scanning/scanner.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|