spectre-core 0.0.12__py3-none-any.whl → 0.0.13__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 +1 -3
- spectre_core/_file_io/file_handlers.py +163 -58
- spectre_core/batches/__init__.py +10 -11
- spectre_core/batches/_base.py +170 -78
- spectre_core/batches/_batches.py +149 -99
- spectre_core/batches/_factory.py +56 -14
- spectre_core/batches/_register.py +23 -8
- spectre_core/batches/plugins/_batch_keys.py +16 -0
- spectre_core/batches/plugins/_callisto.py +183 -0
- spectre_core/batches/plugins/_iq_stream.py +354 -0
- spectre_core/capture_configs/__init__.py +17 -13
- spectre_core/capture_configs/_capture_config.py +93 -34
- spectre_core/capture_configs/_capture_modes.py +22 -0
- spectre_core/capture_configs/_capture_templates.py +207 -122
- spectre_core/capture_configs/_parameters.py +115 -42
- spectre_core/capture_configs/_pconstraints.py +86 -35
- spectre_core/capture_configs/_pnames.py +49 -0
- spectre_core/capture_configs/_ptemplates.py +389 -346
- spectre_core/capture_configs/_pvalidators.py +117 -73
- spectre_core/config/__init__.py +6 -8
- spectre_core/config/_paths.py +65 -25
- spectre_core/config/_time_formats.py +15 -10
- spectre_core/exceptions.py +2 -4
- spectre_core/jobs/__init__.py +14 -0
- spectre_core/jobs/_jobs.py +111 -0
- spectre_core/jobs/_workers.py +171 -0
- spectre_core/logs/__init__.py +17 -0
- spectre_core/logs/_configure.py +67 -0
- spectre_core/logs/_decorators.py +33 -0
- spectre_core/logs/_logs.py +228 -0
- spectre_core/logs/_process_types.py +14 -0
- spectre_core/plotting/__init__.py +4 -2
- spectre_core/plotting/_base.py +204 -102
- spectre_core/plotting/_format.py +17 -4
- spectre_core/plotting/_panel_names.py +18 -0
- spectre_core/plotting/_panel_stack.py +167 -53
- spectre_core/plotting/_panels.py +341 -141
- spectre_core/post_processing/__init__.py +8 -6
- spectre_core/post_processing/_base.py +70 -44
- spectre_core/post_processing/_factory.py +42 -12
- spectre_core/post_processing/_post_processor.py +24 -26
- spectre_core/post_processing/_register.py +22 -6
- spectre_core/post_processing/plugins/_event_handler_keys.py +16 -0
- spectre_core/post_processing/plugins/_fixed_center_frequency.py +129 -0
- spectre_core/post_processing/{library → plugins}/_swept_center_frequency.py +215 -143
- spectre_core/py.typed +0 -0
- spectre_core/receivers/__init__.py +10 -7
- spectre_core/receivers/_base.py +220 -69
- spectre_core/receivers/_factory.py +53 -7
- spectre_core/receivers/_register.py +30 -9
- spectre_core/receivers/_spec_names.py +26 -15
- spectre_core/receivers/plugins/__init__.py +0 -0
- spectre_core/receivers/plugins/_receiver_names.py +16 -0
- spectre_core/receivers/plugins/_rsp1a.py +59 -0
- spectre_core/receivers/plugins/_rspduo.py +67 -0
- spectre_core/receivers/plugins/_sdrplay_receiver.py +190 -0
- spectre_core/receivers/plugins/_test.py +218 -0
- spectre_core/receivers/plugins/gr/_base.py +80 -0
- spectre_core/receivers/{gr → plugins/gr}/_rsp1a.py +42 -52
- spectre_core/receivers/{gr → plugins/gr}/_rspduo.py +61 -74
- spectre_core/receivers/{gr → plugins/gr}/_test.py +33 -31
- spectre_core/spectrograms/__init__.py +5 -3
- spectre_core/spectrograms/_analytical.py +121 -66
- spectre_core/spectrograms/_array_operations.py +103 -36
- spectre_core/spectrograms/_spectrogram.py +380 -207
- spectre_core/spectrograms/_transform.py +197 -169
- spectre_core/wgetting/__init__.py +4 -2
- spectre_core/wgetting/_callisto.py +173 -118
- {spectre_core-0.0.12.dist-info → spectre_core-0.0.13.dist-info}/METADATA +14 -7
- spectre_core-0.0.13.dist-info/RECORD +75 -0
- {spectre_core-0.0.12.dist-info → spectre_core-0.0.13.dist-info}/WHEEL +1 -1
- spectre_core/batches/library/_callisto.py +0 -96
- spectre_core/batches/library/_fixed_center_frequency.py +0 -133
- spectre_core/batches/library/_swept_center_frequency.py +0 -105
- spectre_core/logging/__init__.py +0 -11
- spectre_core/logging/_configure.py +0 -35
- spectre_core/logging/_decorators.py +0 -19
- spectre_core/logging/_log_handlers.py +0 -176
- spectre_core/post_processing/library/_fixed_center_frequency.py +0 -114
- spectre_core/receivers/gr/_base.py +0 -33
- spectre_core/receivers/library/_rsp1a.py +0 -61
- spectre_core/receivers/library/_rspduo.py +0 -69
- spectre_core/receivers/library/_sdrplay_receiver.py +0 -185
- spectre_core/receivers/library/_test.py +0 -221
- spectre_core-0.0.12.dist-info/RECORD +0 -64
- /spectre_core/receivers/{gr → plugins/gr}/__init__.py +0 -0
- {spectre_core-0.0.12.dist-info → spectre_core-0.0.13.dist-info}/LICENSE +0 -0
- {spectre_core-0.0.12.dist-info → spectre_core-0.0.13.dist-info}/top_level.txt +0 -0
@@ -2,107 +2,157 @@
|
|
2
2
|
# This file is part of SPECTRE
|
3
3
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
4
|
|
5
|
-
from typing import Optional, TypeVar, Any
|
6
|
-
from copy import deepcopy
|
5
|
+
from typing import Optional, TypeVar, Any, Generic, Callable
|
7
6
|
from textwrap import dedent
|
8
|
-
from
|
7
|
+
from copy import deepcopy
|
9
8
|
|
10
|
-
from .
|
9
|
+
from ._pnames import PName
|
10
|
+
from ._pconstraints import BasePConstraint, EnforceSign, PowerOfTwo
|
11
11
|
from ._parameters import Parameter
|
12
|
-
|
13
|
-
T = TypeVar('T')
|
14
|
-
|
15
|
-
class PTemplate:
|
16
|
-
"""A parameter template.
|
17
12
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
13
|
+
# value type
|
14
|
+
VT = TypeVar('VT')
|
15
|
+
class PTemplate(Generic[VT]):
|
16
|
+
"""A template which constrains the value and type of a capture config parameter."""
|
17
|
+
def __init__(
|
18
|
+
self,
|
19
|
+
name: PName,
|
20
|
+
ptype: Callable[[Any], VT],
|
21
|
+
default: Optional[VT] = None,
|
22
|
+
nullable: bool = False,
|
23
|
+
enforce_default: bool = False,
|
24
|
+
help: Optional[str] = None,
|
25
|
+
pconstraints: Optional[list[BasePConstraint]] = None
|
26
|
+
) -> None:
|
27
|
+
"""Initialise an instance of `PTemplate`.
|
30
28
|
|
29
|
+
:param name: The name of the parameter.
|
30
|
+
:param ptype: The required type of the parameter value.
|
31
|
+
:param default: The parameter value if not explicitly specified. Defaults to None.
|
32
|
+
:param nullable: Whether the value of the parameter can be `None`. Defaults to False.
|
33
|
+
:param enforce_default: Indicates whether the value must match the specified `default`. Defaults to False.
|
34
|
+
:param help: A helpful description of what the parameter is and the value it stores. Defaults to None.
|
35
|
+
:param pconstraints: Custom constraints to be applied to the value of the parameter. Defaults to None.
|
36
|
+
"""
|
31
37
|
self._name = name
|
32
38
|
self._ptype = ptype
|
33
39
|
self._default = default
|
34
40
|
self._nullable = nullable
|
35
41
|
self._enforce_default = enforce_default
|
36
42
|
self._help = dedent(help).strip().replace("\n", " ") if help else "No help has been provided."
|
37
|
-
self._pconstraints: list[
|
43
|
+
self._pconstraints: list[BasePConstraint] = pconstraints or []
|
38
44
|
|
39
45
|
|
40
46
|
@property
|
41
|
-
def name(
|
47
|
+
def name(
|
48
|
+
self
|
49
|
+
) -> PName:
|
42
50
|
"""The name of the parameter."""
|
43
51
|
return self._name
|
44
52
|
|
45
53
|
|
46
54
|
@property
|
47
|
-
def ptype(
|
48
|
-
|
55
|
+
def ptype(
|
56
|
+
self
|
57
|
+
) -> Callable[[object], VT]:
|
58
|
+
"""The required type of the parameter. The value must be castable as this type."""
|
49
59
|
return self._ptype
|
50
60
|
|
51
61
|
|
52
62
|
@property
|
53
|
-
def default(
|
54
|
-
|
63
|
+
def default(
|
64
|
+
self
|
65
|
+
) -> Optional[VT]:
|
66
|
+
"""The parameter value if not explictly specified."""
|
55
67
|
return self._default
|
56
68
|
|
57
69
|
|
58
70
|
@default.setter
|
59
|
-
def default(
|
60
|
-
|
61
|
-
|
71
|
+
def default(
|
72
|
+
self,
|
73
|
+
value: VT
|
74
|
+
) -> None:
|
75
|
+
"""Update the `default` of this parameter template.
|
76
|
+
|
77
|
+
:param value: The new default value to set.
|
78
|
+
"""
|
79
|
+
self._default = self._cast(value)
|
62
80
|
|
63
81
|
|
64
82
|
@property
|
65
|
-
def nullable(
|
66
|
-
|
83
|
+
def nullable(
|
84
|
+
self
|
85
|
+
) -> bool:
|
86
|
+
"""Whether the value of the parameter can be `None`."""
|
67
87
|
return self._nullable
|
68
88
|
|
69
89
|
|
70
90
|
@property
|
71
|
-
def enforce_default(
|
72
|
-
|
91
|
+
def enforce_default(
|
92
|
+
self
|
93
|
+
) -> bool:
|
94
|
+
"""Indicates whether the value must match the specified `default`."""
|
73
95
|
return self._enforce_default
|
74
96
|
|
75
97
|
|
76
98
|
@enforce_default.setter
|
77
|
-
def enforce_default(
|
78
|
-
|
99
|
+
def enforce_default(
|
100
|
+
self,
|
101
|
+
value: bool
|
102
|
+
) -> None:
|
103
|
+
"""Update whether to `enforce_default` for this parameter template.
|
104
|
+
|
105
|
+
:param value: Whether to enforce the default value.
|
106
|
+
"""
|
79
107
|
self._enforce_default = value
|
80
108
|
|
81
109
|
|
82
110
|
@property
|
83
|
-
def help(
|
84
|
-
|
111
|
+
def help(
|
112
|
+
self
|
113
|
+
) -> str:
|
114
|
+
"""A helpful description of what the parameter is, and the value it stores."""
|
85
115
|
return self._help
|
86
116
|
|
87
117
|
|
88
|
-
def add_pconstraint(
|
89
|
-
|
118
|
+
def add_pconstraint(
|
119
|
+
self,
|
120
|
+
pconstraint: BasePConstraint
|
121
|
+
) -> None:
|
122
|
+
"""Add a parameter constraint to this template.
|
123
|
+
|
124
|
+
:param pconstraint: A `PConstraint` instance compatible with the `ptype`.
|
125
|
+
"""
|
90
126
|
self._pconstraints.append(pconstraint)
|
91
127
|
|
92
128
|
|
93
|
-
def _cast(
|
94
|
-
|
95
|
-
|
129
|
+
def _cast(
|
130
|
+
self,
|
131
|
+
value: Any
|
132
|
+
) -> VT:
|
133
|
+
"""Cast the input value to the `ptype` for this parameter template.
|
134
|
+
|
135
|
+
:param value: The value to be type casted.
|
136
|
+
:raises ValueError: If there is any trouble casting `value` as the `ptype` for this parameter template.
|
137
|
+
:return: The input value cast as `ptype` for this parameter template.
|
138
|
+
"""
|
96
139
|
try:
|
97
140
|
return self._ptype(value)
|
98
141
|
except (TypeError, ValueError) as e:
|
99
142
|
raise ValueError(f"Could not cast '{value}' to '{self._ptype.__name__}': {e}")
|
100
143
|
|
101
144
|
|
102
|
-
def _constrain(
|
103
|
-
|
104
|
-
|
145
|
+
def _constrain(
|
146
|
+
self,
|
147
|
+
value: VT
|
148
|
+
) -> VT:
|
149
|
+
"""Constrain the input value according to constraints of the template.
|
105
150
|
|
151
|
+
:param value: The value to be constrained.
|
152
|
+
:raises ValueError: If a custom `PConstraint` fails for the input value.
|
153
|
+
:raises RuntimeError: If an unexpected error occurs during constraint validation.
|
154
|
+
:return: The input value unchanged if it passes validation.
|
155
|
+
"""
|
106
156
|
if self._enforce_default and value != self._default:
|
107
157
|
raise ValueError(f"The default value of '{self._default}' "
|
108
158
|
f"is required for the parameter '{self._name}'.")
|
@@ -119,9 +169,16 @@ class PTemplate:
|
|
119
169
|
return value
|
120
170
|
|
121
171
|
|
122
|
-
def apply_template(
|
123
|
-
|
124
|
-
|
172
|
+
def apply_template(
|
173
|
+
self,
|
174
|
+
value: Optional[Any]
|
175
|
+
) -> Optional[VT]:
|
176
|
+
"""Cast a value and validate it according to this parameter template.
|
177
|
+
|
178
|
+
:param value: The input value.
|
179
|
+
:raises ValueError: If the value is `None`, no `default` is specified, and the parameter is not nullable.
|
180
|
+
:return: The input value type cast and validated according to the parameter template.
|
181
|
+
"""
|
125
182
|
if value is None:
|
126
183
|
if self._default is not None:
|
127
184
|
value = self._default
|
@@ -132,319 +189,305 @@ class PTemplate:
|
|
132
189
|
else:
|
133
190
|
return None
|
134
191
|
|
135
|
-
|
136
|
-
value = self._constrain(value)
|
137
|
-
return value
|
192
|
+
return self._constrain( self._cast(value) )
|
138
193
|
|
139
194
|
|
140
|
-
def make_parameter(
|
141
|
-
|
195
|
+
def make_parameter(
|
196
|
+
self,
|
197
|
+
value: Optional[Any] = None
|
198
|
+
) -> Parameter:
|
199
|
+
"""Create a `Parameter` compliant with this template.
|
200
|
+
|
201
|
+
:param value: The provided value for the parameter. Defaults to None.
|
202
|
+
:return: A `Parameter` object validated according to this template.
|
203
|
+
"""
|
142
204
|
value = self.apply_template(value)
|
143
205
|
return Parameter(self._name, value)
|
144
206
|
|
145
207
|
|
146
|
-
def to_dict(
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
208
|
+
def to_dict(
|
209
|
+
self
|
210
|
+
) -> dict[str, str]:
|
211
|
+
"""Convert this parameter template to a serialisable dictionary.
|
212
|
+
|
213
|
+
:return: A dictionary representation of this parameter template with string formatted values.
|
214
|
+
"""
|
215
|
+
d = {
|
216
|
+
"name": self._name.value,
|
217
|
+
"type": self._ptype.__name__,
|
151
218
|
"default": self._default,
|
152
219
|
"enforce_default": self._enforce_default,
|
153
220
|
"help": self._help,
|
154
|
-
"
|
221
|
+
"pconstraints": [f"{constraint}" for constraint in self._pconstraints]
|
155
222
|
}
|
156
|
-
|
223
|
+
return {k: f"{v}" for k,v in d.items()}
|
224
|
+
|
225
|
+
|
226
|
+
# ------------------------------------------------------------------------------------------ #
|
227
|
+
# `_base_ptemplates` holds all shared base parameter templates. They are 'base' templates,
|
228
|
+
# in the sense that they should be configured according to specific use-cases. For example,
|
229
|
+
# `default` values should be set, and `pconstraints` added according to specific SDR specs.
|
230
|
+
# ------------------------------------------------------------------------------------------ #
|
157
231
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
SAMPLES_PER_STEP : str = "samples_per_step"
|
183
|
-
MIN_SAMPLES_PER_STEP : str = "min_samples_per_step"
|
184
|
-
MAX_SAMPLES_PER_STEP : str = "max_samples_per_step"
|
185
|
-
STEP_INCREMENT : str = "step_increment"
|
186
|
-
ORIGIN : str = "origin"
|
187
|
-
TELESCOPE : str = "telescope"
|
188
|
-
INSTRUMENT : str = "instrument"
|
189
|
-
OBJECT : str = "object"
|
190
|
-
OBS_LAT : str = "obs_lat"
|
191
|
-
OBS_LON : str = "obs_lon"
|
192
|
-
OBS_ALT : str = "obs_alt"
|
193
|
-
|
194
|
-
#
|
195
|
-
# All stored base ptemplates
|
196
|
-
#
|
197
|
-
_base_ptemplates = {
|
198
|
-
PNames.CENTER_FREQUENCY: PTemplate(PNames.CENTER_FREQUENCY,
|
199
|
-
float,
|
200
|
-
help = """
|
201
|
-
The center frequency of the SDR in Hz.
|
202
|
-
This value determines the midpoint of the frequency range
|
203
|
-
being processed.
|
204
|
-
""",
|
205
|
-
pconstraints=[
|
206
|
-
PConstraints.enforce_positive
|
207
|
-
]),
|
208
|
-
PNames.MIN_FREQUENCY: PTemplate(PNames.MIN_FREQUENCY,
|
209
|
-
float,
|
210
|
-
help = """
|
211
|
-
The minimum center frequency, in Hz, for the frequency sweep.
|
212
|
-
""",
|
213
|
-
pconstraints=[
|
214
|
-
PConstraints.enforce_positive
|
215
|
-
]),
|
216
|
-
PNames.MAX_FREQUENCY: PTemplate(PNames.MAX_FREQUENCY,
|
217
|
-
float,
|
218
|
-
help = """
|
219
|
-
The maximum center frequency, in Hz, for the frequency sweep.
|
220
|
-
""",
|
221
|
-
pconstraints=[
|
222
|
-
PConstraints.enforce_positive
|
223
|
-
]),
|
224
|
-
PNames.FREQUENCY_STEP: PTemplate(PNames.FREQUENCY_STEP,
|
225
|
-
float,
|
226
|
-
help = """
|
227
|
-
The amount, in Hz, by which the center frequency is incremented
|
228
|
-
for each step in the frequency sweep.
|
229
|
-
""",
|
230
|
-
pconstraints=[
|
231
|
-
PConstraints.enforce_positive
|
232
|
-
]),
|
233
|
-
PNames.BANDWIDTH: PTemplate(PNames.BANDWIDTH,
|
234
|
-
float,
|
235
|
-
help = """
|
236
|
-
The frequency range in Hz the signal will occupy without
|
237
|
-
significant attenutation.
|
238
|
-
""",
|
239
|
-
pconstraints=[
|
240
|
-
PConstraints.enforce_positive
|
241
|
-
]),
|
242
|
-
PNames.SAMPLE_RATE: PTemplate(PNames.SAMPLE_RATE,
|
243
|
-
int,
|
244
|
-
help = """
|
245
|
-
The number of samples per second in Hz.
|
246
|
-
""",
|
247
|
-
pconstraints=[
|
248
|
-
PConstraints.enforce_positive
|
249
|
-
]),
|
250
|
-
PNames.IF_GAIN: PTemplate(PNames.IF_GAIN,
|
251
|
-
float,
|
252
|
-
help = """
|
253
|
-
The intermediate frequency gain, in dB.
|
254
|
-
Negative value indicates attenuation.
|
255
|
-
""",
|
256
|
-
pconstraints=[
|
257
|
-
PConstraints.enforce_negative
|
258
|
-
]),
|
259
|
-
PNames.RF_GAIN: PTemplate(PNames.RF_GAIN,
|
260
|
-
float,
|
261
|
-
help = """
|
262
|
-
The radio frequency gain, in dB.
|
263
|
-
Negative value indicates attenuation.
|
264
|
-
""",
|
265
|
-
pconstraints=[
|
266
|
-
PConstraints.enforce_non_positive
|
267
|
-
]),
|
268
|
-
PNames.EVENT_HANDLER_KEY: PTemplate(PNames.EVENT_HANDLER_KEY,
|
269
|
-
str,
|
270
|
-
help = """
|
271
|
-
Identifies which post-processing functions to invoke
|
272
|
-
on newly created files.
|
273
|
-
"""),
|
274
|
-
PNames.BATCH_KEY: PTemplate(PNames.BATCH_KEY,
|
275
|
-
str,
|
276
|
-
help = """
|
277
|
-
Identifies the type of data is stored in each batch.
|
278
|
-
""",
|
279
|
-
),
|
280
|
-
PNames.WINDOW_SIZE: PTemplate(PNames.WINDOW_SIZE,
|
281
|
-
int,
|
282
|
-
help = """
|
283
|
-
The size of the window, in samples, when performing the
|
284
|
-
Short Time FFT.
|
285
|
-
""",
|
286
|
-
pconstraints=[
|
287
|
-
PConstraints.enforce_positive,
|
288
|
-
PConstraints.power_of_two
|
289
|
-
]),
|
290
|
-
PNames.WINDOW_HOP: PTemplate(PNames.WINDOW_HOP,
|
291
|
-
int,
|
292
|
-
help = """
|
293
|
-
How much the window is shifted, in samples,
|
294
|
-
when performing the Short Time FFT.
|
295
|
-
""",
|
296
|
-
pconstraints=[
|
297
|
-
PConstraints.enforce_positive
|
298
|
-
]),
|
299
|
-
PNames.WINDOW_TYPE: PTemplate(PNames.WINDOW_TYPE,
|
300
|
-
str,
|
301
|
-
help = """
|
302
|
-
The type of window applied when performing the Short
|
303
|
-
Time FFT.
|
304
|
-
""",
|
305
|
-
),
|
306
|
-
PNames.WATCH_EXTENSION: PTemplate(PNames.WATCH_EXTENSION,
|
307
|
-
str,
|
308
|
-
help = """
|
309
|
-
Post-processing is triggered by newly created files with this extension.
|
310
|
-
Extensions are specified without the '.' character.
|
311
|
-
""",
|
312
|
-
),
|
313
|
-
PNames.TIME_RESOLUTION: PTemplate(PNames.TIME_RESOLUTION,
|
314
|
-
float,
|
315
|
-
nullable=True,
|
316
|
-
help = """
|
317
|
-
Batched spectrograms are smoothed by averaging up to the time resolution,
|
318
|
-
specified in seconds.
|
319
|
-
""",
|
320
|
-
pconstraints=[
|
321
|
-
PConstraints.enforce_non_negative
|
322
|
-
]),
|
323
|
-
PNames.FREQUENCY_RESOLUTION: PTemplate(PNames.FREQUENCY_RESOLUTION,
|
324
|
-
float,
|
325
|
-
nullable=True,
|
326
|
-
help = """
|
327
|
-
Batched spectrograms are smoothed by averaging up to the frequency resolution,
|
328
|
-
specified in Hz.
|
329
|
-
""",
|
330
|
-
pconstraints=[
|
331
|
-
PConstraints.enforce_non_negative
|
332
|
-
]),
|
333
|
-
PNames.TIME_RANGE: PTemplate(PNames.TIME_RANGE,
|
334
|
-
float,
|
335
|
-
nullable=True,
|
336
|
-
help = """
|
337
|
-
Batched spectrograms are stitched together until
|
338
|
-
the time range, in seconds, is surpassed.
|
339
|
-
""",
|
340
|
-
pconstraints=[
|
341
|
-
PConstraints.enforce_non_negative
|
342
|
-
]),
|
343
|
-
PNames.BATCH_SIZE: PTemplate(PNames.BATCH_SIZE,
|
344
|
-
int,
|
345
|
-
help = """
|
346
|
-
SDR data is collected in batches of this size, specified
|
347
|
-
in seconds.
|
348
|
-
""",
|
349
|
-
pconstraints=[
|
350
|
-
PConstraints.enforce_positive
|
351
|
-
]),
|
352
|
-
PNames.SAMPLES_PER_STEP: PTemplate(PNames.SAMPLES_PER_STEP,
|
353
|
-
int,
|
354
|
-
help = """
|
355
|
-
The number of samples taken at each center frequency in the sweep.
|
356
|
-
This may vary slightly from what is specified due to the nature of
|
357
|
-
GNU Radio runtime.
|
358
|
-
""",
|
359
|
-
pconstraints=[
|
360
|
-
PConstraints.enforce_positive
|
361
|
-
]),
|
362
|
-
PNames.ORIGIN: PTemplate(PNames.ORIGIN,
|
363
|
-
str,
|
364
|
-
nullable=True,
|
365
|
-
help="""
|
366
|
-
Corresponds to the FITS keyword ORIGIN.
|
367
|
-
"""),
|
368
|
-
PNames.TELESCOPE: PTemplate(PNames.TELESCOPE,
|
369
|
-
str,
|
370
|
-
nullable=True,
|
371
|
-
help="""
|
372
|
-
Corresponds to the FITS keyword TELESCOP.
|
373
|
-
"""),
|
374
|
-
PNames.INSTRUMENT: PTemplate(PNames.INSTRUMENT,
|
375
|
-
str,
|
376
|
-
nullable=True,
|
377
|
-
help="""
|
378
|
-
Corresponds to the FITS keyword INSTRUME.
|
379
|
-
"""),
|
380
|
-
PNames.OBJECT: PTemplate(PNames.OBJECT,
|
381
|
-
str,
|
382
|
-
nullable=True,
|
383
|
-
help="""
|
384
|
-
Corresponds to the FITS keyword OBJECT.
|
385
|
-
"""),
|
386
|
-
PNames.OBS_LAT: PTemplate(PNames.OBS_LAT,
|
387
|
-
float,
|
388
|
-
nullable=True,
|
389
|
-
help="""
|
390
|
-
Corresponds to the FITS keyword OBS_LAT.
|
391
|
-
"""),
|
392
|
-
PNames.OBS_LON: PTemplate(PNames.OBS_LON,
|
393
|
-
float,
|
394
|
-
nullable=True,
|
395
|
-
help="""
|
396
|
-
Corresponds to the FITS keyword OBS_LONG.
|
397
|
-
"""),
|
398
|
-
PNames.OBS_ALT: PTemplate(PNames.OBS_ALT,
|
399
|
-
float,
|
400
|
-
nullable=True,
|
401
|
-
help="""
|
402
|
-
Corresponds to the FITS keyword OBS_ALT.
|
403
|
-
"""),
|
404
|
-
PNames.AMPLITUDE: PTemplate(PNames.AMPLITUDE,
|
405
|
-
float,
|
406
|
-
help="""
|
407
|
-
The amplitude of the signal.
|
408
|
-
"""),
|
409
|
-
PNames.FREQUENCY: PTemplate(PNames.FREQUENCY,
|
410
|
-
float,
|
411
|
-
help="""
|
412
|
-
The frequency of the signal, in Hz.
|
413
|
-
"""),
|
414
|
-
PNames.MIN_SAMPLES_PER_STEP: PTemplate(PNames.MIN_SAMPLES_PER_STEP,
|
415
|
-
int,
|
416
|
-
help="""
|
417
|
-
The number of samples in the shortest step of the staircase.
|
418
|
-
""",
|
419
|
-
pconstraints=[
|
420
|
-
PConstraints.enforce_positive
|
421
|
-
]),
|
422
|
-
PNames.MAX_SAMPLES_PER_STEP: PTemplate(PNames.MAX_SAMPLES_PER_STEP,
|
423
|
-
int,
|
424
|
-
help="""
|
425
|
-
The number of samples in the longest step of the staircase.
|
426
|
-
""",
|
232
|
+
_base_ptemplates: dict[PName, PTemplate] = {
|
233
|
+
PName.CENTER_FREQUENCY: PTemplate(PName.CENTER_FREQUENCY,
|
234
|
+
float,
|
235
|
+
help = """
|
236
|
+
The center frequency of the SDR in Hz.
|
237
|
+
This value determines the midpoint of the frequency range
|
238
|
+
being processed.
|
239
|
+
""",
|
240
|
+
pconstraints=[
|
241
|
+
EnforceSign.positive
|
242
|
+
]),
|
243
|
+
PName.MIN_FREQUENCY: PTemplate(PName.MIN_FREQUENCY,
|
244
|
+
float,
|
245
|
+
help = """
|
246
|
+
The minimum center frequency, in Hz, for the frequency sweep.
|
247
|
+
""",
|
248
|
+
pconstraints=[
|
249
|
+
EnforceSign.positive
|
250
|
+
]),
|
251
|
+
PName.MAX_FREQUENCY: PTemplate(PName.MAX_FREQUENCY,
|
252
|
+
float,
|
253
|
+
help = """
|
254
|
+
The maximum center frequency, in Hz, for the frequency sweep.
|
255
|
+
""",
|
427
256
|
pconstraints=[
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
257
|
+
EnforceSign.positive
|
258
|
+
]),
|
259
|
+
PName.FREQUENCY_STEP: PTemplate(PName.FREQUENCY_STEP,
|
260
|
+
float,
|
261
|
+
help = """
|
262
|
+
The amount, in Hz, by which the center frequency is incremented
|
263
|
+
for each step in the frequency sweep.
|
264
|
+
""",
|
265
|
+
pconstraints=[
|
266
|
+
EnforceSign.positive
|
267
|
+
]),
|
268
|
+
PName.BANDWIDTH: PTemplate(PName.BANDWIDTH,
|
269
|
+
float,
|
270
|
+
help = """
|
271
|
+
The frequency range in Hz the signal will occupy without
|
272
|
+
significant attenutation.
|
273
|
+
""",
|
274
|
+
pconstraints=[
|
275
|
+
EnforceSign.positive
|
276
|
+
]),
|
277
|
+
PName.SAMPLE_RATE: PTemplate(PName.SAMPLE_RATE,
|
278
|
+
int,
|
279
|
+
help = """
|
280
|
+
The number of samples per second in Hz.
|
281
|
+
""",
|
282
|
+
pconstraints=[
|
283
|
+
EnforceSign.positive
|
284
|
+
]),
|
285
|
+
PName.IF_GAIN: PTemplate(PName.IF_GAIN,
|
286
|
+
float,
|
287
|
+
help = """
|
288
|
+
The intermediate frequency gain, in dB.
|
289
|
+
Negative value indicates attenuation.
|
290
|
+
""",
|
291
|
+
pconstraints=[
|
292
|
+
EnforceSign.negative
|
293
|
+
]),
|
294
|
+
PName.RF_GAIN: PTemplate(PName.RF_GAIN,
|
295
|
+
float,
|
296
|
+
help = """
|
297
|
+
The radio frequency gain, in dB.
|
298
|
+
Negative value indicates attenuation.
|
299
|
+
""",
|
300
|
+
pconstraints=[
|
301
|
+
EnforceSign.non_positive
|
302
|
+
]),
|
303
|
+
PName.EVENT_HANDLER_KEY: PTemplate(PName.EVENT_HANDLER_KEY,
|
304
|
+
str,
|
305
|
+
help = """
|
306
|
+
Identifies which post-processing functions to invoke
|
307
|
+
on newly created files.
|
308
|
+
"""),
|
309
|
+
PName.BATCH_KEY: PTemplate(PName.BATCH_KEY,
|
310
|
+
str,
|
311
|
+
help = """
|
312
|
+
Identifies the type of data is stored in each batch.
|
313
|
+
""",
|
314
|
+
),
|
315
|
+
PName.WINDOW_SIZE: PTemplate(PName.WINDOW_SIZE,
|
316
|
+
int,
|
317
|
+
help = """
|
318
|
+
The size of the window, in samples, when performing the
|
319
|
+
Short Time FFT.
|
320
|
+
""",
|
321
|
+
pconstraints=[
|
322
|
+
EnforceSign.positive,
|
323
|
+
PowerOfTwo(),
|
324
|
+
]),
|
325
|
+
PName.WINDOW_HOP: PTemplate(PName.WINDOW_HOP,
|
326
|
+
int,
|
327
|
+
help = """
|
328
|
+
How much the window is shifted, in samples,
|
329
|
+
when performing the Short Time FFT.
|
330
|
+
""",
|
331
|
+
pconstraints=[
|
332
|
+
EnforceSign.positive
|
333
|
+
]),
|
334
|
+
PName.WINDOW_TYPE: PTemplate(PName.WINDOW_TYPE,
|
335
|
+
str,
|
336
|
+
help = """
|
337
|
+
The type of window applied when performing the Short
|
338
|
+
Time FFT.
|
339
|
+
""",
|
340
|
+
),
|
341
|
+
PName.WATCH_EXTENSION: PTemplate(PName.WATCH_EXTENSION,
|
342
|
+
str,
|
343
|
+
help = """
|
344
|
+
Post-processing is triggered by newly created files with this extension.
|
345
|
+
Extensions are specified without the '.' character.
|
346
|
+
""",
|
347
|
+
),
|
348
|
+
PName.TIME_RESOLUTION: PTemplate(PName.TIME_RESOLUTION,
|
349
|
+
float,
|
350
|
+
nullable=True,
|
351
|
+
help = """
|
352
|
+
Batched spectrograms are smoothed by averaging up to the time resolution,
|
353
|
+
specified in seconds.
|
354
|
+
""",
|
355
|
+
pconstraints=[
|
356
|
+
EnforceSign.non_negative
|
357
|
+
]),
|
358
|
+
PName.FREQUENCY_RESOLUTION: PTemplate(PName.FREQUENCY_RESOLUTION,
|
359
|
+
float,
|
360
|
+
nullable=True,
|
361
|
+
help = """
|
362
|
+
Batched spectrograms are smoothed by averaging up to the frequency resolution,
|
363
|
+
specified in Hz.
|
364
|
+
""",
|
365
|
+
pconstraints=[
|
366
|
+
EnforceSign.non_negative
|
367
|
+
]),
|
368
|
+
PName.TIME_RANGE: PTemplate(PName.TIME_RANGE,
|
369
|
+
float,
|
370
|
+
nullable=True,
|
371
|
+
help = """
|
372
|
+
Batched spectrograms are stitched together until
|
373
|
+
the time range, in seconds, is surpassed.
|
374
|
+
""",
|
375
|
+
pconstraints=[
|
376
|
+
EnforceSign.non_negative
|
377
|
+
]),
|
378
|
+
PName.BATCH_SIZE: PTemplate(PName.BATCH_SIZE,
|
379
|
+
int,
|
380
|
+
help = """
|
381
|
+
SDR data is collected in batches of this size, specified
|
382
|
+
in seconds.
|
383
|
+
""",
|
384
|
+
pconstraints=[
|
385
|
+
EnforceSign.positive
|
386
|
+
]),
|
387
|
+
PName.SAMPLES_PER_STEP: PTemplate(PName.SAMPLES_PER_STEP,
|
388
|
+
int,
|
389
|
+
help = """
|
390
|
+
The number of samples taken at each center frequency in the sweep.
|
391
|
+
This may vary slightly from what is specified due to the nature of
|
392
|
+
GNU Radio runtime.
|
393
|
+
""",
|
394
|
+
pconstraints=[
|
395
|
+
EnforceSign.positive
|
396
|
+
]),
|
397
|
+
PName.ORIGIN: PTemplate(PName.ORIGIN,
|
398
|
+
str,
|
399
|
+
nullable=True,
|
400
|
+
help="""
|
401
|
+
Corresponds to the FITS keyword ORIGIN.
|
402
|
+
"""),
|
403
|
+
PName.TELESCOPE: PTemplate(PName.TELESCOPE,
|
404
|
+
str,
|
405
|
+
nullable=True,
|
406
|
+
help="""
|
407
|
+
Corresponds to the FITS keyword TELESCOP.
|
408
|
+
"""),
|
409
|
+
PName.INSTRUMENT: PTemplate(PName.INSTRUMENT,
|
410
|
+
str,
|
411
|
+
nullable=True,
|
412
|
+
help="""
|
413
|
+
Corresponds to the FITS keyword INSTRUME.
|
414
|
+
"""),
|
415
|
+
PName.OBJECT: PTemplate(PName.OBJECT,
|
416
|
+
str,
|
417
|
+
nullable=True,
|
418
|
+
help="""
|
419
|
+
Corresponds to the FITS keyword OBJECT.
|
420
|
+
"""),
|
421
|
+
PName.OBS_LAT: PTemplate(PName.OBS_LAT,
|
422
|
+
float,
|
423
|
+
nullable=True,
|
424
|
+
help="""
|
425
|
+
Corresponds to the FITS keyword OBS_LAT.
|
426
|
+
"""),
|
427
|
+
PName.OBS_LON: PTemplate(PName.OBS_LON,
|
428
|
+
float,
|
429
|
+
nullable=True,
|
430
|
+
help="""
|
431
|
+
Corresponds to the FITS keyword OBS_LONG.
|
432
|
+
"""),
|
433
|
+
PName.OBS_ALT: PTemplate(PName.OBS_ALT,
|
434
|
+
float,
|
435
|
+
nullable=True,
|
436
|
+
help="""
|
437
|
+
Corresponds to the FITS keyword OBS_ALT.
|
438
|
+
"""),
|
439
|
+
PName.AMPLITUDE: PTemplate(PName.AMPLITUDE,
|
440
|
+
float,
|
441
|
+
help="""
|
442
|
+
The amplitude of the signal.
|
443
|
+
"""),
|
444
|
+
PName.FREQUENCY: PTemplate(PName.FREQUENCY,
|
445
|
+
float,
|
446
|
+
help="""
|
447
|
+
The frequency of the signal, in Hz.
|
448
|
+
"""),
|
449
|
+
PName.MIN_SAMPLES_PER_STEP: PTemplate(PName.MIN_SAMPLES_PER_STEP,
|
450
|
+
int,
|
451
|
+
help="""
|
452
|
+
The number of samples in the shortest step of the staircase.
|
453
|
+
""",
|
454
|
+
pconstraints=[
|
455
|
+
EnforceSign.positive
|
456
|
+
]),
|
457
|
+
PName.MAX_SAMPLES_PER_STEP: PTemplate(PName.MAX_SAMPLES_PER_STEP,
|
458
|
+
int,
|
459
|
+
help="""
|
460
|
+
The number of samples in the longest step of the staircase.
|
461
|
+
""",
|
462
|
+
pconstraints=[
|
463
|
+
EnforceSign.positive
|
464
|
+
]),
|
465
|
+
PName.STEP_INCREMENT: PTemplate(PName.STEP_INCREMENT,
|
466
|
+
int,
|
467
|
+
help="""
|
468
|
+
The length by which each step in the staircase is incremented.
|
469
|
+
""",
|
470
|
+
pconstraints=[
|
471
|
+
EnforceSign.positive,
|
472
|
+
])
|
438
473
|
|
439
474
|
|
440
475
|
}
|
441
476
|
|
442
|
-
|
477
|
+
|
443
478
|
def get_base_ptemplate(
|
444
|
-
|
479
|
+
pname: PName,
|
445
480
|
) -> PTemplate:
|
446
|
-
"""
|
447
|
-
|
448
|
-
|
481
|
+
"""Get a pre-defined base parameter template, to be configured according to the specific use case.
|
482
|
+
|
483
|
+
:param pname: The parameter name for the template.
|
484
|
+
:raises KeyError: If there is no base parameter template corresponding to the input name.
|
485
|
+
:return: A deep copy of the corresponding base parameter template, if it exists.
|
486
|
+
"""
|
487
|
+
if pname not in _base_ptemplates:
|
488
|
+
raise KeyError(f"No ptemplate found for the parameter name '{pname}'. "
|
449
489
|
f"Expected one of {list(_base_ptemplates.keys())}")
|
450
|
-
|
490
|
+
# A deep copy is required as each receiver instance may mutate the original instance
|
491
|
+
# according to its particular use case. Copying preserves the original instance,
|
492
|
+
# enabling reuse.
|
493
|
+
return deepcopy( _base_ptemplates[pname] )
|