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