spectre-core 0.0.21__py3-none-any.whl → 0.0.23__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.
- spectre_core/_file_io/__init__.py +5 -5
- spectre_core/_file_io/file_handlers.py +61 -107
- spectre_core/batches/__init__.py +21 -4
- spectre_core/batches/_base.py +86 -135
- spectre_core/batches/_batches.py +56 -100
- spectre_core/batches/_factory.py +22 -21
- spectre_core/batches/_register.py +9 -9
- spectre_core/batches/plugins/_batch_keys.py +8 -7
- spectre_core/batches/plugins/_callisto.py +66 -98
- spectre_core/batches/plugins/_iq_stream.py +106 -170
- spectre_core/capture_configs/__init__.py +47 -18
- spectre_core/capture_configs/_capture_config.py +26 -53
- spectre_core/capture_configs/_capture_modes.py +9 -7
- spectre_core/capture_configs/_capture_templates.py +51 -111
- spectre_core/capture_configs/_parameters.py +38 -75
- spectre_core/capture_configs/_pconstraints.py +41 -41
- spectre_core/capture_configs/_pnames.py +37 -35
- spectre_core/capture_configs/_ptemplates.py +261 -348
- spectre_core/capture_configs/_pvalidators.py +99 -102
- spectre_core/config/__init__.py +14 -9
- spectre_core/config/_paths.py +19 -36
- spectre_core/config/_time_formats.py +7 -6
- spectre_core/exceptions.py +39 -1
- spectre_core/jobs/__init__.py +4 -7
- spectre_core/jobs/_duration.py +12 -0
- spectre_core/jobs/_jobs.py +73 -44
- spectre_core/jobs/_workers.py +56 -106
- spectre_core/logs/__init__.py +8 -3
- spectre_core/logs/_configure.py +14 -18
- spectre_core/logs/_decorators.py +7 -5
- spectre_core/logs/_logs.py +38 -90
- spectre_core/logs/_process_types.py +6 -4
- spectre_core/plotting/__init__.py +14 -4
- spectre_core/plotting/_base.py +65 -139
- spectre_core/plotting/_format.py +11 -9
- spectre_core/plotting/_panel_names.py +8 -6
- spectre_core/plotting/_panel_stack.py +83 -116
- spectre_core/plotting/_panels.py +121 -156
- spectre_core/post_processing/__init__.py +7 -4
- spectre_core/post_processing/_base.py +69 -69
- spectre_core/post_processing/_factory.py +15 -12
- spectre_core/post_processing/_post_processor.py +17 -13
- spectre_core/post_processing/_register.py +11 -8
- spectre_core/post_processing/plugins/_event_handler_keys.py +5 -4
- spectre_core/post_processing/plugins/_fixed_center_frequency.py +55 -48
- spectre_core/post_processing/plugins/_swept_center_frequency.py +200 -175
- spectre_core/receivers/__init__.py +10 -3
- spectre_core/receivers/_base.py +83 -149
- spectre_core/receivers/_factory.py +21 -31
- spectre_core/receivers/_register.py +8 -11
- spectre_core/receivers/_spec_names.py +18 -16
- spectre_core/receivers/plugins/_b200mini.py +48 -61
- spectre_core/receivers/plugins/_receiver_names.py +9 -7
- spectre_core/receivers/plugins/_rsp1a.py +45 -41
- spectre_core/receivers/plugins/_rspduo.py +60 -45
- spectre_core/receivers/plugins/_sdrplay_receiver.py +68 -84
- spectre_core/receivers/plugins/_test.py +137 -130
- spectre_core/receivers/plugins/_usrp.py +94 -86
- spectre_core/receivers/plugins/gr/__init__.py +2 -2
- spectre_core/receivers/plugins/gr/_base.py +15 -23
- spectre_core/receivers/plugins/gr/_rsp1a.py +53 -60
- spectre_core/receivers/plugins/gr/_rspduo.py +78 -90
- spectre_core/receivers/plugins/gr/_test.py +50 -58
- spectre_core/receivers/plugins/gr/_usrp.py +61 -59
- spectre_core/spectrograms/__init__.py +22 -14
- spectre_core/spectrograms/_analytical.py +109 -100
- spectre_core/spectrograms/_array_operations.py +40 -47
- spectre_core/spectrograms/_spectrogram.py +290 -323
- spectre_core/spectrograms/_transform.py +107 -74
- spectre_core/wgetting/__init__.py +2 -4
- spectre_core/wgetting/_callisto.py +88 -94
- {spectre_core-0.0.21.dist-info → spectre_core-0.0.23.dist-info}/METADATA +9 -23
- spectre_core-0.0.23.dist-info/RECORD +79 -0
- {spectre_core-0.0.21.dist-info → spectre_core-0.0.23.dist-info}/WHEEL +1 -1
- spectre_core-0.0.21.dist-info/RECORD +0 -78
- {spectre_core-0.0.21.dist-info → spectre_core-0.0.23.dist-info}/licenses/LICENSE +0 -0
- {spectre_core-0.0.21.dist-info → spectre_core-0.0.23.dist-info}/top_level.txt +0 -0
spectre_core/receivers/_base.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
|
1
|
+
# SPDX-FileCopyrightText: © 2024-2025 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
|
2
2
|
# This file is part of SPECTRE
|
3
3
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
4
|
|
@@ -6,19 +6,15 @@ from abc import ABC, abstractmethod
|
|
6
6
|
from typing import Callable, Optional, Literal, overload, Any
|
7
7
|
|
8
8
|
from spectre_core.exceptions import ModeNotFoundError
|
9
|
-
from spectre_core.capture_configs import
|
10
|
-
CaptureTemplate, Parameters, CaptureConfig
|
11
|
-
)
|
9
|
+
from spectre_core.capture_configs import CaptureTemplate, Parameters, CaptureConfig
|
12
10
|
from .plugins._receiver_names import ReceiverName
|
13
11
|
from ._spec_names import SpecName
|
14
12
|
|
13
|
+
|
15
14
|
class BaseReceiver(ABC):
|
16
15
|
"""Abstract base class for software-defined radio receivers."""
|
17
|
-
|
18
|
-
|
19
|
-
name: ReceiverName,
|
20
|
-
mode: Optional[str] = None
|
21
|
-
) -> None:
|
16
|
+
|
17
|
+
def __init__(self, name: ReceiverName, mode: Optional[str] = None) -> None:
|
22
18
|
"""Initialise an instance of `BaseReceiver`.
|
23
19
|
|
24
20
|
:param name: The name of the receiver.
|
@@ -26,165 +22,126 @@ class BaseReceiver(ABC):
|
|
26
22
|
"""
|
27
23
|
self._name = name
|
28
24
|
|
29
|
-
self._specs: dict[SpecName, float|int|list[float|int]] = {}
|
25
|
+
self._specs: dict[SpecName, float | int | list[float | int]] = {}
|
30
26
|
self._add_specs()
|
31
|
-
|
27
|
+
|
32
28
|
self._capture_methods: dict[str, Callable[[str, Parameters], None]] = {}
|
33
29
|
self._add_capture_methods()
|
34
|
-
|
30
|
+
|
35
31
|
self._capture_templates: dict[str, CaptureTemplate] = {}
|
36
32
|
self._add_capture_templates()
|
37
|
-
|
33
|
+
|
38
34
|
self._pvalidators: dict[str, Callable[[Parameters], None]] = {}
|
39
35
|
self._add_pvalidators()
|
40
36
|
|
41
|
-
self._mode =
|
42
|
-
if mode is not None:
|
43
|
-
self.mode = mode
|
44
|
-
|
37
|
+
self._mode: Optional[str] = mode
|
45
38
|
|
46
39
|
@abstractmethod
|
47
|
-
def _add_specs(
|
48
|
-
self
|
49
|
-
) -> None:
|
40
|
+
def _add_specs(self) -> None:
|
50
41
|
"""Subclasses must use `add_spec` to add hardware specifications."""
|
51
|
-
|
52
42
|
|
53
43
|
@abstractmethod
|
54
|
-
def _add_capture_methods(
|
55
|
-
|
56
|
-
) -> None:
|
57
|
-
"""Subclasses must use `add_capture_method` to specify which method is called to capture
|
44
|
+
def _add_capture_methods(self) -> None:
|
45
|
+
"""Subclasses must use `add_capture_method` to specify which method is called to capture
|
58
46
|
data, for each operating mode."""
|
59
|
-
|
60
47
|
|
61
48
|
@abstractmethod
|
62
|
-
def _add_capture_templates(
|
63
|
-
self
|
64
|
-
) -> None:
|
49
|
+
def _add_capture_templates(self) -> None:
|
65
50
|
"""Subclasses must use `add_capture_template` to define a `CaptureTemplate` for each operating mode."""
|
66
51
|
|
67
|
-
|
68
52
|
@abstractmethod
|
69
|
-
def _add_pvalidators(
|
70
|
-
|
71
|
-
) -> None:
|
72
|
-
"""Subclasses must use `add_pvalidator` to add a parameter validation function (pvalidator)
|
53
|
+
def _add_pvalidators(self) -> None:
|
54
|
+
"""Subclasses must use `add_pvalidator` to add a parameter validation function (pvalidator)
|
73
55
|
for each operating mode."""
|
74
56
|
|
75
|
-
|
76
57
|
@property
|
77
|
-
def name(
|
78
|
-
self
|
79
|
-
) -> ReceiverName:
|
58
|
+
def name(self) -> ReceiverName:
|
80
59
|
"""The name of the receiver."""
|
81
60
|
return self._name
|
82
|
-
|
83
61
|
|
84
62
|
@property
|
85
|
-
def capture_methods(
|
86
|
-
self
|
87
|
-
) -> dict[str, Callable[[str, Parameters], None]]:
|
63
|
+
def capture_methods(self) -> dict[str, Callable[[str, Parameters], None]]:
|
88
64
|
"""For each operating mode, the method which is called to capture data."""
|
89
65
|
return self._capture_methods
|
90
|
-
|
91
|
-
|
66
|
+
|
92
67
|
@property
|
93
|
-
def capture_templates(
|
94
|
-
self
|
95
|
-
) -> dict[str, CaptureTemplate]:
|
68
|
+
def capture_templates(self) -> dict[str, CaptureTemplate]:
|
96
69
|
"""For each operating mode, the corresponding `CaptureTemplate`."""
|
97
|
-
return self._capture_templates
|
98
|
-
|
70
|
+
return self._capture_templates
|
99
71
|
|
100
72
|
@property
|
101
|
-
def pvalidators(
|
102
|
-
self
|
103
|
-
) -> dict[str, Callable[[Parameters], None]]:
|
73
|
+
def pvalidators(self) -> dict[str, Callable[[Parameters], None]]:
|
104
74
|
"""For each operating mode, the corresponding parameter validation function (pvalidator)."""
|
105
75
|
return self._pvalidators
|
106
76
|
|
107
|
-
|
108
77
|
@property
|
109
|
-
def specs(
|
110
|
-
self
|
111
|
-
) -> dict[SpecName, float|int|list[float|int]]:
|
78
|
+
def specs(self) -> dict[SpecName, float | int | list[float | int]]:
|
112
79
|
"""The hardware specifications."""
|
113
80
|
return self._specs
|
114
81
|
|
115
|
-
|
116
82
|
@property
|
117
|
-
def modes(
|
118
|
-
self
|
119
|
-
) -> list[str]:
|
83
|
+
def modes(self) -> list[str]:
|
120
84
|
"""The operating modes for the receiver.
|
121
85
|
|
122
86
|
:raises ValueError: If the modes are inconsistent between `capture_methods`,
|
123
87
|
`pvalidators` and `capture_templates`.
|
124
88
|
"""
|
125
|
-
capture_method_modes
|
126
|
-
pvalidator_modes
|
127
|
-
capture_template_modes
|
128
|
-
|
89
|
+
capture_method_modes = list(self.capture_methods.keys())
|
90
|
+
pvalidator_modes = list(self.pvalidators.keys())
|
91
|
+
capture_template_modes = list(self.capture_templates.keys())
|
92
|
+
|
129
93
|
if not capture_method_modes == pvalidator_modes == capture_template_modes:
|
130
94
|
raise ValueError(f"Mode mismatch for the receiver {self.name}.")
|
131
95
|
return capture_method_modes
|
132
96
|
|
133
|
-
|
134
97
|
@property
|
135
|
-
def mode(
|
136
|
-
|
137
|
-
) -> str:
|
138
|
-
"""The active operating mode for the receiver."""
|
139
|
-
if self._mode is None:
|
140
|
-
raise ValueError(f"The operating mode for the receiver `{self.name.value}` is not set.")
|
98
|
+
def mode(self) -> Optional[str]:
|
99
|
+
"""The active operating mode for the receiver, if set."""
|
141
100
|
return self._mode
|
142
101
|
|
143
|
-
|
144
102
|
@mode.setter
|
145
|
-
def mode(
|
146
|
-
self,
|
147
|
-
value: str,
|
148
|
-
) -> None:
|
103
|
+
def mode(self, value: str) -> None:
|
149
104
|
"""Set the active operating mode.
|
150
105
|
|
151
106
|
:param value: The new operating mode to activate.
|
152
107
|
:raises ModeNotFoundError: If the specified mode is not defined in `modes`.
|
153
108
|
"""
|
154
|
-
if
|
155
|
-
raise ModeNotFoundError(
|
156
|
-
|
109
|
+
if value not in self.modes:
|
110
|
+
raise ModeNotFoundError(
|
111
|
+
f"{value} is not a defined mode for the receiver {self.name}. "
|
112
|
+
f"Expected one of {self.modes}"
|
113
|
+
)
|
157
114
|
self._mode = value
|
158
115
|
|
116
|
+
def _get_active_mode(self) -> str:
|
117
|
+
"""Get the current mode, raising an error if none is set.
|
118
|
+
|
119
|
+
:raises ValueError: If no mode is currently set
|
120
|
+
:return: The current mode
|
121
|
+
"""
|
122
|
+
if self._mode is None:
|
123
|
+
raise ValueError(
|
124
|
+
f"This operation requires an active mode for receiver `{self.name.value}`"
|
125
|
+
)
|
126
|
+
return self._mode
|
159
127
|
|
160
128
|
@property
|
161
|
-
def capture_method(
|
162
|
-
self
|
163
|
-
) -> Callable[[str, Parameters], None]:
|
129
|
+
def capture_method(self) -> Callable[[str, Parameters], None]:
|
164
130
|
"""Start capturing data under the active operating mode."""
|
165
|
-
return self.capture_methods[self.
|
166
|
-
|
131
|
+
return self.capture_methods[self._get_active_mode()]
|
167
132
|
|
168
133
|
@property
|
169
|
-
def pvalidator(
|
170
|
-
self
|
171
|
-
) -> Callable[[Parameters], None]:
|
134
|
+
def pvalidator(self) -> Callable[[Parameters], None]:
|
172
135
|
"""The parameter validation function for the active operating mode."""
|
173
|
-
return self.pvalidators[self.
|
174
|
-
|
136
|
+
return self.pvalidators[self._get_active_mode()]
|
175
137
|
|
176
138
|
@property
|
177
|
-
def capture_template(
|
178
|
-
self
|
179
|
-
) -> CaptureTemplate:
|
139
|
+
def capture_template(self) -> CaptureTemplate:
|
180
140
|
"""The `CaptureTemplate` for the active operating mode."""
|
181
|
-
return self._capture_templates[self.
|
182
|
-
|
141
|
+
return self._capture_templates[self._get_active_mode()]
|
183
142
|
|
184
143
|
def add_capture_method(
|
185
|
-
self,
|
186
|
-
mode: str,
|
187
|
-
capture_method: Callable[[str, Parameters], None]
|
144
|
+
self, mode: str, capture_method: Callable[[str, Parameters], None]
|
188
145
|
) -> None:
|
189
146
|
"""
|
190
147
|
Add a capture method for a specific operating mode.
|
@@ -194,11 +151,8 @@ class BaseReceiver(ABC):
|
|
194
151
|
"""
|
195
152
|
self._capture_methods[mode] = capture_method
|
196
153
|
|
197
|
-
|
198
154
|
def add_pvalidator(
|
199
|
-
self,
|
200
|
-
mode: str,
|
201
|
-
pvalidator: Callable[[Parameters], None]
|
155
|
+
self, mode: str, pvalidator: Callable[[Parameters], None]
|
202
156
|
) -> None:
|
203
157
|
"""
|
204
158
|
Add a parameter validation function for a specific operating mode.
|
@@ -208,11 +162,8 @@ class BaseReceiver(ABC):
|
|
208
162
|
"""
|
209
163
|
self._pvalidators[mode] = pvalidator
|
210
164
|
|
211
|
-
|
212
165
|
def add_capture_template(
|
213
|
-
self,
|
214
|
-
mode: str,
|
215
|
-
capture_template: CaptureTemplate
|
166
|
+
self, mode: str, capture_template: CaptureTemplate
|
216
167
|
) -> None:
|
217
168
|
"""
|
218
169
|
Add a `CaptureTemplate` for a specific operating mode.
|
@@ -222,12 +173,7 @@ class BaseReceiver(ABC):
|
|
222
173
|
"""
|
223
174
|
self._capture_templates[mode] = capture_template
|
224
175
|
|
225
|
-
|
226
|
-
def add_spec(
|
227
|
-
self,
|
228
|
-
name: SpecName,
|
229
|
-
value: Any
|
230
|
-
) -> None:
|
176
|
+
def add_spec(self, name: SpecName, value: Any) -> None:
|
231
177
|
"""
|
232
178
|
Add a hardware specification.
|
233
179
|
|
@@ -236,11 +182,7 @@ class BaseReceiver(ABC):
|
|
236
182
|
"""
|
237
183
|
self.specs[name] = value
|
238
184
|
|
239
|
-
|
240
|
-
def get_spec(
|
241
|
-
self,
|
242
|
-
spec_name: SpecName
|
243
|
-
) -> Any:
|
185
|
+
def get_spec(self, spec_name: SpecName) -> Any:
|
244
186
|
"""
|
245
187
|
Retrieve a hardware specification.
|
246
188
|
|
@@ -249,60 +191,52 @@ class BaseReceiver(ABC):
|
|
249
191
|
:return: The specification's value.
|
250
192
|
"""
|
251
193
|
if spec_name not in self.specs:
|
252
|
-
raise KeyError(
|
253
|
-
|
194
|
+
raise KeyError(
|
195
|
+
f"Spec not found with name '{spec_name}' "
|
196
|
+
f"for the receiver '{self.name}'"
|
197
|
+
)
|
254
198
|
return self.specs[spec_name]
|
255
199
|
|
256
|
-
|
257
|
-
def start_capture(
|
258
|
-
self,
|
259
|
-
tag: str
|
260
|
-
) -> None:
|
200
|
+
def start_capture(self, tag: str) -> None:
|
261
201
|
"""Start capturing data in the active operating mode.
|
262
202
|
|
263
203
|
:param tag: The tag of the capture config to load.
|
204
|
+
:raises ValueError: If no mode is currently set
|
264
205
|
"""
|
265
|
-
|
266
|
-
|
206
|
+
active_mode = self._get_active_mode()
|
207
|
+
self.capture_methods[active_mode](tag, self.load_parameters(tag))
|
267
208
|
|
268
209
|
def save_parameters(
|
269
|
-
self,
|
270
|
-
tag: str,
|
271
|
-
parameters: Parameters,
|
272
|
-
force: bool = False
|
210
|
+
self, tag: str, parameters: Parameters, force: bool = False
|
273
211
|
) -> None:
|
274
|
-
"""Create a capture config according to the active operating mode and save the
|
212
|
+
"""Create a capture config according to the active operating mode and save the
|
275
213
|
input parameters.
|
276
214
|
|
277
|
-
The input parameters are validated before being written to file.
|
278
|
-
|
279
215
|
:param tag: The tag identifying the capture config.
|
280
216
|
:param parameters: The parameters to save in the capture config.
|
281
|
-
:param force: If True, overwrites
|
217
|
+
:param force: If True, overwrites existing file if it exists.
|
218
|
+
:raises ValueError: If no mode is currently set
|
282
219
|
"""
|
283
|
-
|
284
|
-
self.
|
220
|
+
active_mode = self._get_active_mode()
|
221
|
+
parameters = self.capture_templates[active_mode].apply_template(parameters)
|
222
|
+
self.pvalidators[active_mode](parameters)
|
285
223
|
|
286
224
|
capture_config = CaptureConfig(tag)
|
287
|
-
capture_config.save_parameters(self.name.value,
|
288
|
-
self.mode,
|
289
|
-
parameters,
|
290
|
-
force)
|
291
|
-
|
292
|
-
def load_parameters(
|
293
|
-
self,
|
294
|
-
tag: str
|
295
|
-
) -> Parameters:
|
296
|
-
"""Load a capture config, and return the parameters it stores.
|
225
|
+
capture_config.save_parameters(self.name.value, active_mode, parameters, force)
|
297
226
|
|
298
|
-
|
227
|
+
def load_parameters(self, tag: str) -> Parameters:
|
228
|
+
"""Load a capture config, and return the parameters it stores.
|
299
229
|
|
300
230
|
:param tag: The tag identifying the capture config.
|
231
|
+
:raises ValueError: If no mode is currently set
|
301
232
|
:return: The validated parameters stored in the capture config.
|
302
233
|
"""
|
234
|
+
active_mode = self._get_active_mode()
|
303
235
|
capture_config = CaptureConfig(tag)
|
304
236
|
|
305
|
-
parameters = self.
|
306
|
-
|
307
|
-
|
237
|
+
parameters = self.capture_templates[active_mode].apply_template(
|
238
|
+
capture_config.parameters
|
239
|
+
)
|
240
|
+
self.pvalidators[active_mode](parameters)
|
241
|
+
|
308
242
|
return parameters
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
|
1
|
+
# SPDX-FileCopyrightText: © 2024-2025 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
|
2
2
|
# This file is part of SPECTRE
|
3
3
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
4
|
|
@@ -16,47 +16,36 @@ from .plugins._b200mini import B200mini
|
|
16
16
|
|
17
17
|
@overload
|
18
18
|
def get_receiver(
|
19
|
-
receiver_name: Literal[ReceiverName.RSP1A],
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
receiver_name: Literal[ReceiverName.RSP1A], mode: Optional[str] = None
|
20
|
+
) -> RSP1A: ...
|
21
|
+
|
22
|
+
|
25
23
|
@overload
|
26
24
|
def get_receiver(
|
27
|
-
receiver_name: Literal[ReceiverName.RSPDUO],
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
25
|
+
receiver_name: Literal[ReceiverName.RSPDUO], mode: Optional[str] = None
|
26
|
+
) -> RSPduo: ...
|
27
|
+
|
28
|
+
|
33
29
|
@overload
|
34
30
|
def get_receiver(
|
35
|
-
receiver_name: Literal[ReceiverName.TEST],
|
36
|
-
|
37
|
-
) -> Test:
|
38
|
-
...
|
31
|
+
receiver_name: Literal[ReceiverName.TEST], mode: Optional[str] = None
|
32
|
+
) -> Test: ...
|
39
33
|
|
40
34
|
|
41
35
|
@overload
|
42
36
|
def get_receiver(
|
43
|
-
receiver_name: Literal[ReceiverName.B200MINI],
|
44
|
-
|
45
|
-
) -> B200mini:
|
46
|
-
...
|
37
|
+
receiver_name: Literal[ReceiverName.B200MINI], mode: Optional[str] = None
|
38
|
+
) -> B200mini: ...
|
47
39
|
|
48
40
|
|
49
41
|
@overload
|
50
42
|
def get_receiver(
|
51
|
-
receiver_name: ReceiverName,
|
52
|
-
|
53
|
-
) -> BaseReceiver:
|
54
|
-
...
|
43
|
+
receiver_name: ReceiverName, mode: Optional[str] = None
|
44
|
+
) -> BaseReceiver: ...
|
55
45
|
|
56
46
|
|
57
47
|
def get_receiver(
|
58
|
-
receiver_name: ReceiverName,
|
59
|
-
mode: Optional[str] = None
|
48
|
+
receiver_name: ReceiverName, mode: Optional[str] = None
|
60
49
|
) -> BaseReceiver:
|
61
50
|
"""Get a registered receiver.
|
62
51
|
|
@@ -68,7 +57,8 @@ def get_receiver(
|
|
68
57
|
receiver_cls = receivers.get(receiver_name)
|
69
58
|
if receiver_cls is None:
|
70
59
|
valid_receivers = list(receivers.keys())
|
71
|
-
raise ReceiverNotFoundError(
|
72
|
-
|
73
|
-
|
74
|
-
|
60
|
+
raise ReceiverNotFoundError(
|
61
|
+
f"No class found for the receiver: {receiver_name}. "
|
62
|
+
f"Please specify one of the following receivers {valid_receivers}"
|
63
|
+
)
|
64
|
+
return receiver_cls(receiver_name, mode=mode)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
|
1
|
+
# SPDX-FileCopyrightText: © 2024-2025 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
|
2
2
|
# This file is part of SPECTRE
|
3
3
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
4
|
|
@@ -10,7 +10,9 @@ from ._base import BaseReceiver
|
|
10
10
|
# map populated at runtime via the `register_receiver` decorator.
|
11
11
|
receivers: dict[ReceiverName, Type[BaseReceiver]] = {}
|
12
12
|
|
13
|
-
T = TypeVar(
|
13
|
+
T = TypeVar("T", bound=BaseReceiver)
|
14
|
+
|
15
|
+
|
14
16
|
def register_receiver(
|
15
17
|
receiver_name: ReceiverName,
|
16
18
|
) -> Callable[[Type[T]], Type[T]]:
|
@@ -20,24 +22,19 @@ def register_receiver(
|
|
20
22
|
:raises ValueError: If the provided `receiver_name` is already registered.
|
21
23
|
:return: A decorator that registers the `BaseReceiver` subclass under the given `receiver_name`.
|
22
24
|
"""
|
23
|
-
|
24
|
-
|
25
|
-
) -> Type[T]:
|
25
|
+
|
26
|
+
def decorator(cls: Type[T]) -> Type[T]:
|
26
27
|
if receiver_name in receivers:
|
27
28
|
raise ValueError(f"The receiver '{receiver_name}' is already registered!")
|
28
29
|
receivers[receiver_name] = cls
|
29
30
|
return cls
|
31
|
+
|
30
32
|
return decorator
|
31
33
|
|
32
34
|
|
33
|
-
def get_registered_receivers(
|
34
|
-
) -> list[str]:
|
35
|
+
def get_registered_receivers() -> list[str]:
|
35
36
|
"""List all registered receivers.
|
36
37
|
|
37
38
|
:return: The string values of all registered `ReceiverName` enum keys.
|
38
39
|
"""
|
39
40
|
return [k.value for k in receivers.keys()]
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
@@ -1,12 +1,13 @@
|
|
1
|
-
# SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
|
1
|
+
# SPDX-FileCopyrightText: © 2024-2025 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
|
2
2
|
# This file is part of SPECTRE
|
3
3
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
4
|
|
5
5
|
from enum import Enum
|
6
6
|
|
7
|
+
|
7
8
|
class SpecName(Enum):
|
8
9
|
"""A hardware specification name.
|
9
|
-
|
10
|
+
|
10
11
|
:ivar FREQUENCY_LOWER_BOUND: The lower bound for the center frequency, in Hz.
|
11
12
|
:ivar FREQUENCY_UPPER_BOUND: The upper bound for the center frequency, in Hz.
|
12
13
|
:ivar SAMPLE_RATE_LOWER_BOUND: The lower bound for the sampling rate, in Hz.
|
@@ -16,26 +17,27 @@ class SpecName(Enum):
|
|
16
17
|
:ivar BANDWIDTH_OPTIONS: The permitted bandwidths for the receiver, in Hz.
|
17
18
|
:ivar IF_GAIN_UPPER_BOUND: The upper bound for the intermediate frequency gain, in dB.
|
18
19
|
Negative values indicate attenuation.
|
19
|
-
:ivar RF_GAIN_UPPER_BOUND: The upper bound for the radio frequency gain, in dB.
|
20
|
+
:ivar RF_GAIN_UPPER_BOUND: The upper bound for the radio frequency gain, in dB.
|
20
21
|
Negative values indicate attenuation.
|
21
22
|
:ivar GAIN_UPPER_BOUND: The upper bound for the gain, in dB.
|
22
23
|
:ivar WIRE_FORMATS: Supported data types transferred over the bus/network.
|
23
24
|
:ivar MASTER_CLOCK_RATE_LOWER_BOUND: The lower bound for the SDR reference clock rate, in Hz.
|
24
25
|
:ivar MASTER_CLOCK_RATE_UPPER_BOUND: The upper bound for the SDR reference clock rate, in Hz.
|
25
26
|
:ivar API_RETUNING_LATENCY: An empirical estimate of the delay between issuing a command
|
26
|
-
for a receiver to retune its center frequency and the actual physical update of the center frequency.
|
27
|
+
for a receiver to retune its center frequency and the actual physical update of the center frequency.
|
27
28
|
"""
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
29
|
+
|
30
|
+
FREQUENCY_LOWER_BOUND = "frequency_lower_bound"
|
31
|
+
FREQUENCY_UPPER_BOUND = "frequency_upper_bound"
|
32
|
+
SAMPLE_RATE_LOWER_BOUND = "sample_rate_lower_bound"
|
33
|
+
SAMPLE_RATE_UPPER_BOUND = "sample_rate_upper_bound"
|
34
|
+
BANDWIDTH_LOWER_BOUND = "bandwidth_lower_bound"
|
35
|
+
BANDWIDTH_UPPER_BOUND = "bandwidth_upper_bound"
|
36
|
+
BANDWIDTH_OPTIONS = "bandwidth_options"
|
37
|
+
IF_GAIN_UPPER_BOUND = "if_gain_upper_bound"
|
38
|
+
RF_GAIN_UPPER_BOUND = "rf_gain_upper_bound"
|
39
|
+
GAIN_UPPER_BOUND = "gain_upper_bound"
|
40
|
+
WIRE_FORMATS = "wire_formats"
|
39
41
|
MASTER_CLOCK_RATE_LOWER_BOUND = "master_clock_rate_lower_bound"
|
40
42
|
MASTER_CLOCK_RATE_UPPER_BOUND = "master_clock_rate_upper_bound"
|
41
|
-
API_RETUNING_LATENCY
|
43
|
+
API_RETUNING_LATENCY = "api_retuning_latency"
|