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
 
@@ -24,25 +24,23 @@ from .._register import register_batch
24
24
  @dataclass(frozen=True)
25
25
  class _BatchExtension:
26
26
  """Supported extensions for a `IQStreamBatch`.
27
-
27
+
28
28
  :ivar FITS: Corresponds to the `.fits` file extension.
29
29
  :ivar BIN: Corresponds to the `.bin` file extension.
30
30
  :ivar HDR: Corresponds to the `.hdr` file extension.
31
31
  """
32
+
32
33
  FITS: str = "fits"
33
- BIN : str = "bin"
34
- HDR : str = "hdr"
35
-
36
-
34
+ BIN: str = "bin"
35
+ HDR: str = "hdr"
36
+
37
+
37
38
  class _BinFile(BatchFile[npt.NDArray[np.complex64]]):
38
- """Stores complex IQ samples in the binary format, as produced by the `gr-spectre`
39
+ """Stores complex IQ samples in the binary format, as produced by the `gr-spectre`
39
40
  OOT module block `batched_file_sink`.
40
41
  """
41
- def __init__(
42
- self,
43
- batch_parent_dir_path: str,
44
- batch_name: str
45
- ) -> None:
42
+
43
+ def __init__(self, batch_parent_dir_path: str, batch_name: str) -> None:
46
44
  """Initialise a `_BinFile` instance.
47
45
 
48
46
  :param batch_parent_dir_path: The parent directory for the batch.
@@ -50,48 +48,46 @@ class _BinFile(BatchFile[npt.NDArray[np.complex64]]):
50
48
  """
51
49
  super().__init__(batch_parent_dir_path, batch_name, _BatchExtension.BIN)
52
50
 
53
-
54
- def _read(
55
- self
56
- ) -> npt.NDArray[np.complex64]:
51
+ def _read(self) -> npt.NDArray[np.complex64]:
57
52
  """Reads the binary file and returns the stored complex IQ samples.
58
53
 
59
54
  :return: The raw 32-bit floats in the binary file, interpreted as 64-bit complex IQ samples.
60
55
  """
61
56
  with open(self.file_path, "rb") as fh:
62
57
  return np.fromfile(fh, dtype=np.complex64)
63
-
58
+
64
59
 
65
60
  @dataclass
66
61
  class IQMetadata:
67
62
  """Represents metadata for IQ samples produced by the `gr-spectre` OOT module block `batched_file_sink`.
68
63
 
69
64
  :ivar millisecond_correction: The millisecond component of the batch start time.
70
- :ivar center_frequencies: Center frequencies for each IQ sample, if the stream was frequency tagged.
65
+ :ivar center_frequencies: Center frequencies for each IQ sample, if the stream was frequency tagged.
71
66
  None otherwise.
72
- :ivar num_samples: Number of samples collected at each center frequency, if the stream was frequency
67
+ :ivar num_samples: Number of samples collected at each center frequency, if the stream was frequency
73
68
  tagged. None otherwise.
74
69
  """
70
+
75
71
  millisecond_correction: int
76
72
  center_frequencies: Optional[npt.NDArray[np.float32]] = None
77
73
  num_samples: Optional[npt.NDArray[np.int32]] = None
78
-
74
+
79
75
  def is_frequency_tagged(self) -> bool:
80
76
  """Check if the IQ metadata contains frequency tagging information.
81
77
 
82
78
  :return: True if frequency tagging information is present; False otherwise.
83
79
  """
84
80
  return (self.center_frequencies is not None) and (self.num_samples is not None)
85
-
81
+
86
82
 
87
83
  class _HdrFile(BatchFile[IQMetadata]):
88
- """Stores IQ sample metadata produced by the `gr-spectre` OOT module block `batched_file_sink`, used
84
+ """Stores IQ sample metadata produced by the `gr-spectre` OOT module block `batched_file_sink`, used
89
85
  to help parse the corresponding `.bin` file.
90
86
 
91
87
  File Structure:
92
88
  - If frequency tagged:
93
89
  (`<millisecond_correction>`, `<freq_0>`, `<num_samples_0>`, `<freq_1>`, `<num_samples_1>`, ...)
94
- All values are stored as 32-bit floats.
90
+ All values are stored as 32-bit floats.
95
91
  - The first value is the millisecond component for the batch start time.
96
92
  - Subsequent tuples (`<freq_n>`, `<num_samples_n>`) indicate that `<num_samples_n>` samples were collected at `<freq_n>`.
97
93
  - If not frequency tagged:
@@ -100,11 +96,8 @@ class _HdrFile(BatchFile[IQMetadata]):
100
96
 
101
97
  This format enables mapping IQ samples in the binary file to their corresponding center frequencies, if applicable.
102
98
  """
103
- def __init__(
104
- self,
105
- parent_dir_path: str,
106
- base_file_name: str
107
- ) -> None:
99
+
100
+ def __init__(self, parent_dir_path: str, base_file_name: str) -> None:
108
101
  """Initialise a `_HdrFile` instance.
109
102
 
110
103
  :param parent_dir_path: The parent directory for the batch.
@@ -112,44 +105,30 @@ class _HdrFile(BatchFile[IQMetadata]):
112
105
  """
113
106
  super().__init__(parent_dir_path, base_file_name, _BatchExtension.HDR)
114
107
 
115
-
116
- def _read(
117
- self
118
- ) -> IQMetadata:
108
+ def _read(self) -> IQMetadata:
119
109
  """Parses the binary contents of the `.hdr` file to extract IQ sample metadata.
120
110
 
121
- :return: An instance of `IQMetadata` containing the parsed metadata, including the millisecond correction
111
+ :return: An instance of `IQMetadata` containing the parsed metadata, including the millisecond correction
122
112
  and, if applicable, frequency tagging details.
123
113
  """
124
- hdr_contents = self._extract_raw_contents()
114
+ hdr_contents = self._extract_raw_contents()
125
115
  millisecond_correction = self._get_millisecond_correction(hdr_contents)
126
116
  if hdr_contents.size == 1:
127
117
  return IQMetadata(millisecond_correction)
128
118
  else:
129
- center_frequencies = self._get_center_frequencies(hdr_contents)
130
- num_samples = self._get_num_samples(hdr_contents)
131
- self._validate_frequencies_and_samples(center_frequencies,
132
- num_samples)
133
- return IQMetadata(millisecond_correction,
134
- center_frequencies,
135
- num_samples)
136
-
119
+ center_frequencies = self._get_center_frequencies(hdr_contents)
120
+ num_samples = self._get_num_samples(hdr_contents)
121
+ self._validate_frequencies_and_samples(center_frequencies, num_samples)
122
+ return IQMetadata(millisecond_correction, center_frequencies, num_samples)
137
123
 
138
-
139
- def _extract_raw_contents(
140
- self
141
- ) -> npt.NDArray[np.float32]:
124
+ def _extract_raw_contents(self) -> npt.NDArray[np.float32]:
142
125
  """Reads the raw contents of the `.hdr` file."""
143
126
  with open(self.file_path, "rb") as fh:
144
- # the `batched_file_sink` block in the `gr-spectre` GNU Radio OOT module stores
127
+ # the `batched_file_sink` block in the `gr-spectre` GNU Radio OOT module stores
145
128
  # the integral millisecond component as a 32-bit float.
146
129
  return np.fromfile(fh, dtype=np.float32)
147
130
 
148
-
149
- def _get_millisecond_correction(
150
- self,
151
- hdr_contents: npt.NDArray[np.float32]
152
- ) -> int:
131
+ def _get_millisecond_correction(self, hdr_contents: npt.NDArray[np.float32]) -> int:
153
132
  """Extracts and validates the millisecond component of the batch start time.
154
133
 
155
134
  The value is stored as a 32-bit float but interpreted as an integer.
@@ -157,14 +136,14 @@ class _HdrFile(BatchFile[IQMetadata]):
157
136
  millisecond_correction = float(hdr_contents[0])
158
137
 
159
138
  if not millisecond_correction.is_integer():
160
- raise TypeError(f"Expected integer value for millisecond correction, but got {millisecond_correction}")
161
-
139
+ raise TypeError(
140
+ f"Expected integer value for millisecond correction, but got {millisecond_correction}"
141
+ )
142
+
162
143
  return int(millisecond_correction)
163
-
164
144
 
165
145
  def _get_center_frequencies(
166
- self,
167
- hdr_contents: npt.NDArray[np.float32]
146
+ self, hdr_contents: npt.NDArray[np.float32]
168
147
  ) -> npt.NDArray[np.float32]:
169
148
  """Extracts the center frequencies from the `.hdr` file contents.
170
149
 
@@ -172,182 +151,139 @@ class _HdrFile(BatchFile[IQMetadata]):
172
151
  """
173
152
  return hdr_contents[1::2]
174
153
 
175
-
176
154
  def _get_num_samples(
177
- self,
178
- hdr_contents: npt.NDArray[np.float32]
155
+ self, hdr_contents: npt.NDArray[np.float32]
179
156
  ) -> npt.NDArray[np.int32]:
180
157
  """Extracts the number of samples per frequency from the `.hdr` file contents.
181
158
 
182
- The values are stored as 32-bit floats but are interpreted as integers.
183
- Sample counts are located at every second entry, starting from the second index.
159
+ The values are stored as 32-bit floats but are interpreted as integers.
160
+ Sample counts are located at every second entry, starting from the second index.
184
161
  """
185
162
  num_samples_as_float = hdr_contents[2::2]
186
163
  if not all(num_samples_as_float == num_samples_as_float.astype(int)):
187
- raise InvalidSweepMetadataError("Number of samples per frequency is expected to describe an integer")
164
+ raise InvalidSweepMetadataError(
165
+ "Number of samples per frequency is expected to describe an integer"
166
+ )
188
167
  return num_samples_as_float.astype(np.int32)
189
168
 
190
-
191
169
  def _validate_frequencies_and_samples(
192
- self,
193
- center_frequencies: npt.NDArray[np.float32],
194
- num_samples: npt.NDArray[np.int32]
170
+ self,
171
+ center_frequencies: npt.NDArray[np.float32],
172
+ num_samples: npt.NDArray[np.int32],
195
173
  ) -> None:
196
174
  """Ensures that each center frequency has a corresponding sample count."""
197
175
  if len(center_frequencies) != len(num_samples):
198
- raise InvalidSweepMetadataError("Center frequencies and number of samples arrays are not the same length")
176
+ raise InvalidSweepMetadataError(
177
+ "Center frequencies and number of samples arrays are not the same length"
178
+ )
199
179
 
200
180
 
201
181
  class _FitsFile(BatchFile[Spectrogram]):
202
182
  """Stores spectrogram data in the FITS file format, as generated by `spectre` from a stream of IQ samples."""
203
- def __init__(
204
- self,
205
- parent_dir_path: str,
206
- base_file_name: str
207
- ) -> None:
183
+
184
+ def __init__(self, parent_dir_path: str, base_file_name: str) -> None:
208
185
  """Initialise a `_FitsFile` instance.
209
186
 
210
187
  :param parent_dir_path: The parent directory for the batch.
211
188
  :param base_file_name: The batch name.
212
189
  """
213
190
  super().__init__(parent_dir_path, base_file_name, _BatchExtension.FITS)
214
-
215
191
 
216
- def _read(
217
- self
218
- ) -> Spectrogram:
192
+ def _read(self) -> Spectrogram:
219
193
  """Read the FITS file and create a spectrogram.
220
194
 
221
195
  :return: A `Spectrogram` instance containing the parsed FITS file data.
222
196
  """
223
- with fits.open(self.file_path, mode='readonly') as hdulist:
224
- primary_hdu = self._get_primary_hdu(hdulist)
225
- dynamic_spectra = self._get_dynamic_spectra(primary_hdu)
226
- bunit = self._get_bunit(primary_hdu)
227
- spectrogram_start_datetime = self._get_spectrogram_start_datetime(primary_hdu)
228
- bintable_hdu = self._get_bintable_hdu(hdulist)
229
- times = self._get_times(bintable_hdu)
230
- frequencies = self._get_frequencies(bintable_hdu)
231
-
197
+ with fits.open(self.file_path, mode="readonly") as hdulist:
198
+ primary_hdu = self._get_primary_hdu(hdulist)
199
+ dynamic_spectra = self._get_dynamic_spectra(primary_hdu)
200
+ bunit = self._get_bunit(primary_hdu)
201
+ spectrogram_start_datetime = self._get_spectrogram_start_datetime(
202
+ primary_hdu
203
+ )
204
+ bintable_hdu = self._get_bintable_hdu(hdulist)
205
+ times = self._get_times(bintable_hdu)
206
+ frequencies = self._get_frequencies(bintable_hdu)
207
+
232
208
  # bunit is interpreted as a SpectrumUnit.
233
209
  spectrum_unit = SpectrumUnit(bunit)
234
- return Spectrogram(dynamic_spectra,
235
- times,
236
- frequencies,
237
- self.tag,
238
- spectrum_unit,
239
- spectrogram_start_datetime)
240
-
241
-
242
- def _get_primary_hdu(
243
- self,
244
- hdulist: HDUList
245
- ) -> PrimaryHDU:
246
- return hdulist['PRIMARY']
247
-
248
-
249
- def _get_bintable_hdu(
250
- self,
251
- hdulist: HDUList
252
- ) -> BinTableHDU:
210
+ return Spectrogram(
211
+ dynamic_spectra,
212
+ times,
213
+ frequencies,
214
+ self.tag,
215
+ spectrum_unit,
216
+ spectrogram_start_datetime,
217
+ )
218
+
219
+ def _get_primary_hdu(self, hdulist: HDUList) -> PrimaryHDU:
220
+ return hdulist["PRIMARY"]
221
+
222
+ def _get_bintable_hdu(self, hdulist: HDUList) -> BinTableHDU:
253
223
  return hdulist[1]
254
-
255
-
256
- def _get_bunit(
257
- self,
258
- primary_hdu: PrimaryHDU
259
- ) -> str:
224
+
225
+ def _get_bunit(self, primary_hdu: PrimaryHDU) -> str:
260
226
  """Get the units corresponding to the elements of the dynamic spectra."""
261
- return primary_hdu.header['BUNIT']
262
-
263
-
264
- def _get_dynamic_spectra(
265
- self,
266
- primary_hdu: PrimaryHDU
267
- ) -> npt.NDArray[np.float32]:
268
- return primary_hdu.data
227
+ return primary_hdu.header["BUNIT"]
269
228
 
229
+ def _get_dynamic_spectra(self, primary_hdu: PrimaryHDU) -> npt.NDArray[np.float32]:
230
+ return primary_hdu.data
270
231
 
271
- def _get_spectrogram_start_datetime(
272
- self,
273
- primary_hdu: PrimaryHDU
274
- ) -> datetime:
232
+ def _get_spectrogram_start_datetime(self, primary_hdu: PrimaryHDU) -> datetime:
275
233
  """Get the start time of the spectrogram, up to the full precision available."""
276
- date_obs = primary_hdu.header['DATE-OBS']
277
- time_obs = primary_hdu.header['TIME-OBS']
234
+ date_obs = primary_hdu.header["DATE-OBS"]
235
+ time_obs = primary_hdu.header["TIME-OBS"]
278
236
  return datetime.strptime(f"{date_obs}T{time_obs}", TimeFormat.PRECISE_DATETIME)
279
237
 
280
-
281
- def _get_times(
282
- self,
283
- bintable_hdu: BinTableHDU
284
- ) -> npt.NDArray[np.float32]:
238
+ def _get_times(self, bintable_hdu: BinTableHDU) -> npt.NDArray[np.float32]:
285
239
  """Extracts the elapsed times for each spectrum in seconds, with the first spectrum set to t=0
286
240
  by convention.
287
241
  """
288
- return bintable_hdu.data['TIME'][0] # already in seconds
289
-
242
+ return bintable_hdu.data["TIME"][0] # already in seconds
290
243
 
291
- def _get_frequencies(
292
- self,
293
- bintable_hdu: BinTableHDU
294
- ) -> npt.NDArray[np.float32]:
244
+ def _get_frequencies(self, bintable_hdu: BinTableHDU) -> npt.NDArray[np.float32]:
295
245
  """Extracts the frequencies for each spectral component."""
296
- frequencies_MHz = bintable_hdu.data['FREQUENCY'][0]
297
- return frequencies_MHz * 1e6 # convert to Hz
298
-
299
-
246
+ frequencies_MHz = bintable_hdu.data["FREQUENCY"][0]
247
+ return frequencies_MHz * 1e6 # convert to Hz
248
+
249
+
300
250
  @register_batch(BatchKey.IQ_STREAM)
301
251
  class IQStreamBatch(BaseBatch):
302
252
  """A batch of data derived from a stream of IQ samples from some receiver.
303
-
253
+
304
254
  Supports the following extensions:
305
255
  - `.fits` (via the `spectrogram_file` attribute)
306
256
  - `.bin` (via the `bin_file` attribute)
307
257
  - `.hdr` (via the `hdr_file` attribute)
308
258
  """
309
- def __init__(
310
- self,
311
- start_time: str,
312
- tag: str
313
- ) -> None:
314
- """Initialise a `IQStreamBatch` instance.
259
+
260
+ def __init__(self, start_time: str, tag: str) -> None:
261
+ """Initialise a `IQStreamBatch` instance.
315
262
 
316
263
  :param start_time: The start time of the batch.
317
264
  :param tag: The batch name tag.
318
265
  """
319
- super().__init__(start_time, tag)
266
+ super().__init__(start_time, tag)
320
267
  self._fits_file = _FitsFile(self.parent_dir_path, self.name)
321
- self._bin_file = _BinFile (self.parent_dir_path, self.name)
322
- self._hdr_file = _HdrFile (self.parent_dir_path, self.name)
323
-
268
+ self._bin_file = _BinFile(self.parent_dir_path, self.name)
269
+ self._hdr_file = _HdrFile(self.parent_dir_path, self.name)
270
+
324
271
  # add files formally to the batch
325
- self.add_file( self.spectrogram_file )
326
- self.add_file( self.bin_file )
327
- self.add_file( self.hdr_file )
328
-
272
+ self.add_file(self.spectrogram_file)
273
+ self.add_file(self.bin_file)
274
+ self.add_file(self.hdr_file)
329
275
 
330
276
  @property
331
- def spectrogram_file(
332
- self
333
- ) -> _FitsFile:
277
+ def spectrogram_file(self) -> _FitsFile:
334
278
  """The batch file corresponding to the `.fits` extension."""
335
279
  return self._fits_file
336
-
337
-
280
+
338
281
  @property
339
- def bin_file(
340
- self
341
- ) -> _BinFile:
282
+ def bin_file(self) -> _BinFile:
342
283
  """The batch file corresponding to the `.bin` extension."""
343
284
  return self._bin_file
344
-
345
-
285
+
346
286
  @property
347
- def hdr_file(
348
- self
349
- ) -> _HdrFile:
287
+ def hdr_file(self) -> _HdrFile:
350
288
  """The batch file corresponding to the `.hdr` extension."""
351
289
  return self._hdr_file
352
-
353
-
@@ -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,28 +6,57 @@
6
6
 
7
7
  from ._pnames import PName
8
8
  from ._capture_modes import CaptureMode
9
- from ._pvalidators import (
10
- validate_fixed_center_frequency, validate_non_overlapping_steps, validate_num_samples_per_step,
11
- validate_num_steps_per_sweep, validate_nyquist_criterion, validate_step_interval, validate_sweep_interval,
12
- validate_swept_center_frequency, validate_window, validate_sample_rate_with_master_clock_rate
9
+ from ._pvalidators import (
10
+ validate_fixed_center_frequency,
11
+ validate_non_overlapping_steps,
12
+ validate_num_samples_per_step,
13
+ validate_num_steps_per_sweep,
14
+ validate_nyquist_criterion,
15
+ validate_step_interval,
16
+ validate_sweep_interval,
17
+ validate_swept_center_frequency,
18
+ validate_window,
19
+ validate_sample_rate_with_master_clock_rate,
13
20
  )
14
21
  from ._capture_config import CaptureConfig
15
22
  from ._ptemplates import PTemplate, get_base_ptemplate
16
- from ._parameters import (
17
- Parameter, Parameters, parse_string_parameters, make_parameters
18
- )
23
+ from ._parameters import Parameter, Parameters, parse_string_parameters, make_parameters
19
24
  from ._capture_templates import (
20
- CaptureTemplate, get_base_capture_template, make_base_capture_template
21
- )
22
- from ._pconstraints import (
23
- BasePConstraint, EnforceSign, Bound, OneOf, PowerOfTwo
25
+ CaptureTemplate,
26
+ get_base_capture_template,
27
+ make_base_capture_template,
24
28
  )
29
+ from ._pconstraints import BasePConstraint, EnforceSign, Bound, OneOf, PowerOfTwo
25
30
 
26
31
  __all__ = [
27
- "PTemplate", "PValidator", "CaptureConfig", "Parameter", "Parameters", "parse_string_parameters",
28
- "make_parameters", "CaptureTemplate", "CaptureMode", "get_base_capture_template", "make_base_capture_template"
29
- "PConstraint", "PConstraint", "Bound", "OneOf", "EnforceSign", "PowerOfTwo", "make_base_capture_template", "PName",
30
- "get_base_ptemplate", "BasePConstraint", "validate_fixed_center_frequency", "validate_non_overlapping_steps",
31
- "validate_num_samples_per_step", "validate_num_steps_per_sweep", "validate_nyquist_criterion", "validate_step_interval",
32
- "validate_sweep_interval", "validate_swept_center_frequency", "validate_window", "validate_sample_rate_with_master_clock_rate"
32
+ "PTemplate",
33
+ "PValidator",
34
+ "CaptureConfig",
35
+ "Parameter",
36
+ "Parameters",
37
+ "parse_string_parameters",
38
+ "make_parameters",
39
+ "CaptureTemplate",
40
+ "CaptureMode",
41
+ "get_base_capture_template",
42
+ "make_base_capture_template" "PConstraint",
43
+ "PConstraint",
44
+ "Bound",
45
+ "OneOf",
46
+ "EnforceSign",
47
+ "PowerOfTwo",
48
+ "make_base_capture_template",
49
+ "PName",
50
+ "get_base_ptemplate",
51
+ "BasePConstraint",
52
+ "validate_fixed_center_frequency",
53
+ "validate_non_overlapping_steps",
54
+ "validate_num_samples_per_step",
55
+ "validate_num_steps_per_sweep",
56
+ "validate_nyquist_criterion",
57
+ "validate_step_interval",
58
+ "validate_sweep_interval",
59
+ "validate_swept_center_frequency",
60
+ "validate_window",
61
+ "validate_sample_rate_with_master_clock_rate",
33
62
  ]
@@ -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,91 +10,69 @@ from spectre_core._file_io import JsonHandler
10
10
  from spectre_core.config import get_configs_dir_path
11
11
  from spectre_core.exceptions import InvalidTagError
12
12
  from ._ptemplates import PName
13
- from ._parameters import (
14
- Parameter,
15
- Parameters,
16
- make_parameters
17
- )
13
+ from ._parameters import Parameter, Parameters, make_parameters
14
+
18
15
 
19
16
  @dataclass(frozen=True)
20
17
  class _CaptureConfigKey:
21
18
  """Defined JSON keys for capture configs.
22
-
19
+
23
20
  :ivar RECEIVER_NAME: The name of the receiver used for capture.
24
21
  :ivar RECEIVER_MODE: The operating mode for the receiver to be used for capture.
25
22
  :ivar PARAMETERS: The user configured parameters given to the receiver at the time of capture.
26
23
  """
24
+
27
25
  RECEIVER_NAME = "receiver_name"
28
26
  RECEIVER_MODE = "receiver_mode"
29
- PARAMETERS = "parameters"
27
+ PARAMETERS = "parameters"
30
28
 
31
29
 
32
30
  class CaptureConfig(JsonHandler):
33
31
  """Simple IO interface for a capture configs under a particular tag."""
34
- def __init__(
35
- self,
36
- tag: str
37
- ) -> None:
32
+
33
+ def __init__(self, tag: str) -> None:
38
34
  """Initialise an instance of `CaptureConfig`.
39
35
 
40
36
  :param tag: The tag identifier for the capture config.
41
37
  """
42
38
  self._validate_tag(tag)
43
39
  self._tag = tag
44
- super().__init__(get_configs_dir_path(),
45
- tag)
46
-
40
+ super().__init__(get_configs_dir_path(), tag)
41
+
47
42
  @property
48
- def tag(
49
- self
50
- ) -> str:
43
+ def tag(self) -> str:
51
44
  """The tag identifier for the capture config."""
52
45
  return self._tag
53
46
 
54
-
55
- def _validate_tag(
56
- self,
57
- tag: str
58
- ) -> None:
47
+ def _validate_tag(self, tag: str) -> None:
59
48
  """Validate the tag of the capture config.
60
-
49
+
61
50
  Some substrings are reserved for third-party spectrogram data.
62
51
  """
63
52
  if "callisto" in tag:
64
- raise ValueError(f"The substring `callisto` is reserved, and is not allowed in a capture config tag.")
65
-
53
+ raise ValueError(
54
+ f"The substring `callisto` is reserved, and is not allowed in a capture config tag."
55
+ )
66
56
 
67
57
  @property
68
- def receiver_name(
69
- self
70
- ) -> str:
58
+ def receiver_name(self) -> str:
71
59
  """The name of the receiver to be used for capture."""
72
60
  d = self.read(cache=True)
73
61
  return d[_CaptureConfigKey.RECEIVER_NAME]
74
-
75
62
 
76
63
  @property
77
- def receiver_mode(
78
- self
79
- ) -> str:
64
+ def receiver_mode(self) -> str:
80
65
  """The operating mode for the receiver to be used for capture."""
81
66
  d = self.read(cache=True)
82
67
  return d[_CaptureConfigKey.RECEIVER_MODE]
83
-
84
68
 
85
69
  @cached_property
86
- def parameters(
87
- self
88
- ) -> Parameters:
70
+ def parameters(self) -> Parameters:
89
71
  """The user-configured parameters provided to the receiver at the time of capture."""
90
72
  d = self.read(cache=True)
91
- return make_parameters( d[_CaptureConfigKey.PARAMETERS] )
92
-
73
+ return make_parameters(d[_CaptureConfigKey.PARAMETERS])
93
74
 
94
- def get_parameter(
95
- self,
96
- name: PName
97
- ) -> Parameter:
75
+ def get_parameter(self, name: PName) -> Parameter:
98
76
  """Get a parameter stored by the capture config.
99
77
 
100
78
  :param name: The name of the parameter.
@@ -102,14 +80,10 @@ class CaptureConfig(JsonHandler):
102
80
  configuration file.
103
81
  """
104
82
  return self.parameters.get_parameter(name)
105
-
106
83
 
107
- def get_parameter_value(
108
- self,
109
- name: PName
110
- ) -> Optional[Any]:
84
+ def get_parameter_value(self, name: PName) -> Optional[Any]:
111
85
  """Get the value of a parameter stored by the capture config.
112
-
86
+
113
87
  For static typing, should be `cast` after return according to the corresponding `PTemplate`.
114
88
 
115
89
  :param name: The name of the parameter.
@@ -118,13 +92,12 @@ class CaptureConfig(JsonHandler):
118
92
  """
119
93
  return self.parameters.get_parameter_value(name)
120
94
 
121
-
122
95
  def save_parameters(
123
96
  self,
124
97
  receiver_name: str,
125
98
  receiver_mode: str,
126
99
  parameters: Parameters,
127
- force: bool = False
100
+ force: bool = False,
128
101
  ):
129
102
  """Write the input parameters to a capture config.
130
103
 
@@ -136,6 +109,6 @@ class CaptureConfig(JsonHandler):
136
109
  d = {
137
110
  _CaptureConfigKey.RECEIVER_MODE: receiver_mode,
138
111
  _CaptureConfigKey.RECEIVER_NAME: receiver_name,
139
- _CaptureConfigKey.PARAMETERS : parameters.to_dict()
112
+ _CaptureConfigKey.PARAMETERS: parameters.to_dict(),
140
113
  }
141
- self.save(d, force=force)
114
+ self.save(d, force=force)