spectre-core 0.0.22__py3-none-any.whl → 0.0.24__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/__init__.py +5 -0
- spectre_core/_file_io/__init__.py +4 -4
- spectre_core/_file_io/file_handlers.py +60 -106
- spectre_core/batches/__init__.py +20 -3
- spectre_core/batches/_base.py +85 -134
- spectre_core/batches/_batches.py +55 -99
- spectre_core/batches/_factory.py +21 -20
- spectre_core/batches/_register.py +8 -8
- spectre_core/batches/plugins/_batch_keys.py +7 -6
- spectre_core/batches/plugins/_callisto.py +65 -97
- spectre_core/batches/plugins/_iq_stream.py +105 -169
- spectre_core/capture_configs/__init__.py +46 -17
- spectre_core/capture_configs/_capture_config.py +25 -52
- spectre_core/capture_configs/_capture_modes.py +8 -6
- spectre_core/capture_configs/_capture_templates.py +50 -110
- spectre_core/capture_configs/_parameters.py +37 -74
- spectre_core/capture_configs/_pconstraints.py +40 -40
- spectre_core/capture_configs/_pnames.py +36 -34
- spectre_core/capture_configs/_ptemplates.py +260 -347
- spectre_core/capture_configs/_pvalidators.py +99 -102
- spectre_core/config/__init__.py +19 -8
- spectre_core/config/_paths.py +25 -47
- spectre_core/config/_time_formats.py +6 -5
- spectre_core/exceptions.py +38 -0
- spectre_core/jobs/__init__.py +3 -6
- spectre_core/jobs/_duration.py +12 -0
- spectre_core/jobs/_jobs.py +72 -43
- spectre_core/jobs/_workers.py +55 -105
- spectre_core/logs/__init__.py +7 -2
- spectre_core/logs/_configure.py +13 -17
- spectre_core/logs/_decorators.py +6 -4
- spectre_core/logs/_logs.py +37 -89
- spectre_core/logs/_process_types.py +5 -3
- spectre_core/plotting/__init__.py +19 -3
- spectre_core/plotting/_base.py +112 -177
- spectre_core/plotting/_format.py +10 -8
- spectre_core/plotting/_panel_names.py +7 -5
- spectre_core/plotting/_panel_stack.py +138 -130
- spectre_core/plotting/_panels.py +152 -162
- spectre_core/post_processing/__init__.py +6 -3
- spectre_core/post_processing/_base.py +41 -55
- spectre_core/post_processing/_factory.py +14 -11
- spectre_core/post_processing/_post_processor.py +16 -12
- spectre_core/post_processing/_register.py +10 -7
- spectre_core/post_processing/plugins/_event_handler_keys.py +4 -3
- spectre_core/post_processing/plugins/_fixed_center_frequency.py +54 -47
- spectre_core/post_processing/plugins/_swept_center_frequency.py +199 -174
- spectre_core/receivers/__init__.py +9 -2
- spectre_core/receivers/_base.py +82 -148
- spectre_core/receivers/_factory.py +20 -30
- spectre_core/receivers/_register.py +7 -10
- spectre_core/receivers/_spec_names.py +17 -15
- spectre_core/receivers/plugins/_b200mini.py +47 -60
- spectre_core/receivers/plugins/_receiver_names.py +8 -6
- spectre_core/receivers/plugins/_rsp1a.py +44 -40
- spectre_core/receivers/plugins/_rspduo.py +59 -44
- spectre_core/receivers/plugins/_sdrplay_receiver.py +67 -83
- spectre_core/receivers/plugins/_test.py +136 -129
- spectre_core/receivers/plugins/_usrp.py +93 -85
- spectre_core/receivers/plugins/gr/__init__.py +1 -1
- spectre_core/receivers/plugins/gr/_base.py +14 -22
- spectre_core/receivers/plugins/gr/_rsp1a.py +53 -60
- spectre_core/receivers/plugins/gr/_rspduo.py +77 -89
- spectre_core/receivers/plugins/gr/_test.py +49 -57
- spectre_core/receivers/plugins/gr/_usrp.py +61 -59
- spectre_core/spectrograms/__init__.py +21 -13
- spectre_core/spectrograms/_analytical.py +108 -99
- spectre_core/spectrograms/_array_operations.py +39 -46
- spectre_core/spectrograms/_spectrogram.py +293 -324
- spectre_core/spectrograms/_transform.py +106 -73
- spectre_core/wgetting/__init__.py +1 -3
- spectre_core/wgetting/_callisto.py +87 -93
- {spectre_core-0.0.22.dist-info → spectre_core-0.0.24.dist-info}/METADATA +9 -23
- spectre_core-0.0.24.dist-info/RECORD +79 -0
- {spectre_core-0.0.22.dist-info → spectre_core-0.0.24.dist-info}/WHEEL +1 -1
- spectre_core-0.0.22.dist-info/RECORD +0 -78
- {spectre_core-0.0.22.dist-info → spectre_core-0.0.24.dist-info}/licenses/LICENSE +0 -0
- {spectre_core-0.0.22.dist-info → spectre_core-0.0.24.dist-info}/top_level.txt +0 -0
spectre_core/batches/_factory.py
CHANGED
@@ -11,25 +11,22 @@ from ._register import batch_map
|
|
11
11
|
from .plugins._batch_keys import BatchKey
|
12
12
|
from .plugins._callisto import CallistoBatch
|
13
13
|
from .plugins._iq_stream import IQStreamBatch
|
14
|
-
|
14
|
+
|
15
15
|
|
16
16
|
@overload
|
17
17
|
def get_batch_cls(
|
18
18
|
batch_key: Literal[BatchKey.CALLISTO],
|
19
|
-
) -> Type[CallistoBatch]:
|
20
|
-
...
|
19
|
+
) -> Type[CallistoBatch]: ...
|
21
20
|
|
22
21
|
|
23
22
|
@overload
|
24
23
|
def get_batch_cls(
|
25
24
|
batch_key: Literal[BatchKey.IQ_STREAM],
|
26
|
-
) -> Type[IQStreamBatch]:
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
) -> Type[IQStreamBatch]: ...
|
26
|
+
|
27
|
+
|
30
28
|
@overload
|
31
|
-
def get_batch_cls(batch_key: BatchKey) -> Type[BaseBatch]:
|
32
|
-
...
|
29
|
+
def get_batch_cls(batch_key: BatchKey) -> Type[BaseBatch]: ...
|
33
30
|
|
34
31
|
|
35
32
|
def get_batch_cls(
|
@@ -44,14 +41,14 @@ def get_batch_cls(
|
|
44
41
|
batch_cls = batch_map.get(batch_key)
|
45
42
|
if batch_cls is None:
|
46
43
|
valid_batch_keys = list(batch_map.keys())
|
47
|
-
raise BatchNotFoundError(
|
48
|
-
|
44
|
+
raise BatchNotFoundError(
|
45
|
+
f"No batch found for the batch key: {batch_key}. "
|
46
|
+
f"Valid batch keys are: {valid_batch_keys}"
|
47
|
+
)
|
49
48
|
return batch_cls
|
50
49
|
|
51
50
|
|
52
|
-
def get_batch_cls_from_tag(
|
53
|
-
tag: str
|
54
|
-
) -> Type[BaseBatch]:
|
51
|
+
def get_batch_cls_from_tag(tag: str) -> Type[BaseBatch]:
|
55
52
|
# if the tag is reserved (i.e., corresponds to third-party spectrogram data)
|
56
53
|
# directly fetch the right class.
|
57
54
|
if "callisto" in tag:
|
@@ -61,9 +58,13 @@ def get_batch_cls_from_tag(
|
|
61
58
|
else:
|
62
59
|
capture_config = CaptureConfig(tag)
|
63
60
|
if PName.BATCH_KEY not in capture_config.parameters.name_list:
|
64
|
-
raise ValueError(
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
61
|
+
raise ValueError(
|
62
|
+
f"Could not infer batch class from the tag 'tag'. "
|
63
|
+
f"A parameter with name `{PName.BATCH_KEY.value}` "
|
64
|
+
f"does not exist."
|
65
|
+
)
|
66
|
+
|
67
|
+
batch_key = BatchKey(
|
68
|
+
cast(str, capture_config.get_parameter_value(PName.BATCH_KEY))
|
69
|
+
)
|
70
|
+
return get_batch_cls(batch_key)
|
@@ -10,21 +10,21 @@ from .plugins._batch_keys import BatchKey
|
|
10
10
|
# Map populated at runtime via the `register_batch` decorator.
|
11
11
|
batch_map: dict[BatchKey, Type[BaseBatch]] = {}
|
12
12
|
|
13
|
-
T = TypeVar(
|
14
|
-
|
15
|
-
|
16
|
-
) -> Callable[[Type[T]], Type[T]]:
|
13
|
+
T = TypeVar("T", bound=BaseBatch)
|
14
|
+
|
15
|
+
|
16
|
+
def register_batch(batch_key: BatchKey) -> Callable[[Type[T]], Type[T]]:
|
17
17
|
"""Decorator to register a `BaseBatch` subclass under a specified `BatchKey`.
|
18
18
|
|
19
19
|
:param batch_key: The key to register the `BaseBatch` subclass under.
|
20
20
|
:raises ValueError: If the provided `batch_key` is already registered.
|
21
21
|
:return: A decorator that registers the `BaseBatch` subclass under the given `batch_key`.
|
22
22
|
"""
|
23
|
-
|
24
|
-
|
25
|
-
) -> Type[T]:
|
23
|
+
|
24
|
+
def decorator(cls: Type[T]) -> Type[T]:
|
26
25
|
if batch_key in batch_map:
|
27
26
|
raise ValueError(f"A batch with key '{batch_key}' is already registered!")
|
28
27
|
batch_map[batch_key] = cls
|
29
28
|
return cls
|
30
|
-
|
29
|
+
|
30
|
+
return decorator
|
@@ -4,13 +4,14 @@
|
|
4
4
|
|
5
5
|
from enum import Enum
|
6
6
|
|
7
|
-
|
7
|
+
|
8
|
+
class BatchKey(Enum):
|
8
9
|
"""Key bound to a `Batch` plugin class.
|
9
|
-
|
10
|
-
:ivar IQ_STREAM: Represents the default batch data generated by `spectre`,
|
10
|
+
|
11
|
+
:ivar IQ_STREAM: Represents the default batch data generated by `spectre`,
|
11
12
|
containing IQ stream data and other data derived from it.
|
12
13
|
:ivar CALLISTO: Represents FITS files generated by the e-Callisto network.
|
13
14
|
"""
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
|
16
|
+
IQ_STREAM = "iq_stream"
|
17
|
+
CALLISTO = "callisto"
|
@@ -21,107 +21,89 @@ from .._register import register_batch
|
|
21
21
|
@dataclass(frozen=True)
|
22
22
|
class _BatchExtension:
|
23
23
|
"""Supported extensions for a `CallistoBatch`.
|
24
|
-
|
24
|
+
|
25
25
|
:ivar FITS: Corresponds to the `.fits` file extension.
|
26
26
|
"""
|
27
|
+
|
27
28
|
FITS: str = "fits"
|
28
|
-
|
29
|
-
|
29
|
+
|
30
|
+
|
30
31
|
class _FitsFile(BatchFile[Spectrogram]):
|
31
32
|
"""Stores spectrogram data in the FITS format generated by the e-Callisto network."""
|
32
|
-
|
33
|
-
|
34
|
-
parent_dir_path: str,
|
35
|
-
base_file_name: str
|
36
|
-
) -> None:
|
33
|
+
|
34
|
+
def __init__(self, parent_dir_path: str, base_file_name: str) -> None:
|
37
35
|
"""Initialise a `_FitsFile` instance.
|
38
36
|
|
39
37
|
:param parent_dir_path: The parent directory for the batch.
|
40
38
|
:param base_file_name: The batch name.
|
41
39
|
"""
|
42
|
-
super().__init__(parent_dir_path,
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
def _read(
|
48
|
-
self
|
49
|
-
) -> Spectrogram:
|
40
|
+
super().__init__(parent_dir_path, base_file_name, _BatchExtension.FITS)
|
41
|
+
|
42
|
+
def _read(self) -> Spectrogram:
|
50
43
|
"""Parses a FITS file to generate a `Spectrogram` instance.
|
51
44
|
|
52
|
-
Reverses the spectra along the frequency axis and converts units to linearised
|
45
|
+
Reverses the spectra along the frequency axis and converts units to linearised
|
53
46
|
values if necessary. Infers the spectrum type from the `BUNIT` header.
|
54
47
|
|
55
48
|
:raises NotImplementedError: If the `BUNIT` header value represents an unsupported spectrum type.
|
56
49
|
:return: A `Spectrogram` instance containing the parsed FITS file data.
|
57
50
|
"""
|
58
|
-
with fits.open(self.file_path, mode=
|
59
|
-
primary_hdu
|
60
|
-
dynamic_spectra
|
61
|
-
spectrogram_start_datetime = self._get_spectrogram_start_datetime(
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
51
|
+
with fits.open(self.file_path, mode="readonly") as hdulist:
|
52
|
+
primary_hdu = self._get_primary_hdu(hdulist)
|
53
|
+
dynamic_spectra = self._get_dynamic_spectra(primary_hdu)
|
54
|
+
spectrogram_start_datetime = self._get_spectrogram_start_datetime(
|
55
|
+
primary_hdu
|
56
|
+
)
|
57
|
+
bintable_hdu = self._get_bintable_hdu(hdulist)
|
58
|
+
times = self._get_times(bintable_hdu)
|
59
|
+
frequencies = self._get_frequencies(bintable_hdu)
|
60
|
+
bunit = self._get_bunit(primary_hdu)
|
66
61
|
|
67
62
|
# bunit is interpreted as a SpectrumUnit
|
68
63
|
spectrum_unit = SpectrumUnit(bunit)
|
69
64
|
if spectrum_unit == SpectrumUnit.DIGITS:
|
70
|
-
dynamic_spectra_linearised = self._convert_units_to_linearised(
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
65
|
+
dynamic_spectra_linearised = self._convert_units_to_linearised(
|
66
|
+
dynamic_spectra
|
67
|
+
)
|
68
|
+
|
69
|
+
return Spectrogram(
|
70
|
+
dynamic_spectra_linearised[
|
71
|
+
::-1, :
|
72
|
+
], # reverse the spectra along the frequency axis
|
73
|
+
times,
|
74
|
+
frequencies[::-1], # sort the frequencies in ascending order
|
75
|
+
self.tag,
|
76
|
+
spectrum_unit,
|
77
|
+
spectrogram_start_datetime,
|
78
|
+
)
|
78
79
|
else:
|
79
|
-
raise NotImplementedError(
|
80
|
+
raise NotImplementedError(
|
81
|
+
f"SPECTRE does not currently support spectrum type with BUNITS '{spectrum_unit}'"
|
82
|
+
)
|
80
83
|
|
84
|
+
def _get_primary_hdu(self, hdulist: HDUList) -> PrimaryHDU:
|
85
|
+
return hdulist["PRIMARY"]
|
81
86
|
|
82
|
-
def
|
83
|
-
self, hdulist: HDUList
|
84
|
-
) -> PrimaryHDU:
|
85
|
-
return hdulist['PRIMARY']
|
86
|
-
|
87
|
-
|
88
|
-
def _get_bintable_hdu(
|
89
|
-
self,
|
90
|
-
hdulist: HDUList
|
91
|
-
) -> BinTableHDU:
|
87
|
+
def _get_bintable_hdu(self, hdulist: HDUList) -> BinTableHDU:
|
92
88
|
return hdulist[1]
|
93
|
-
|
94
|
-
|
95
|
-
def _get_dynamic_spectra(
|
96
|
-
self,
|
97
|
-
primary_hdu: PrimaryHDU
|
98
|
-
) -> npt.NDArray[np.float32]:
|
99
|
-
return primary_hdu.data.astype(np.float32)
|
100
89
|
|
90
|
+
def _get_dynamic_spectra(self, primary_hdu: PrimaryHDU) -> npt.NDArray[np.float32]:
|
91
|
+
return primary_hdu.data.astype(np.float32)
|
101
92
|
|
102
|
-
def _get_spectrogram_start_datetime(
|
103
|
-
|
104
|
-
primary_hdu
|
105
|
-
) -> datetime:
|
106
|
-
date_obs = primary_hdu.header['DATE-OBS']
|
107
|
-
time_obs = primary_hdu.header['TIME-OBS']
|
93
|
+
def _get_spectrogram_start_datetime(self, primary_hdu: PrimaryHDU) -> datetime:
|
94
|
+
date_obs = primary_hdu.header["DATE-OBS"]
|
95
|
+
time_obs = primary_hdu.header["TIME-OBS"]
|
108
96
|
return datetime.strptime(f"{date_obs}T{time_obs}", "%Y/%m/%dT%H:%M:%S.%f")
|
109
97
|
|
110
|
-
|
111
|
-
|
112
|
-
self,
|
113
|
-
primary_hdu: PrimaryHDU
|
114
|
-
) -> str:
|
115
|
-
return primary_hdu.header['BUNIT']
|
116
|
-
|
98
|
+
def _get_bunit(self, primary_hdu: PrimaryHDU) -> str:
|
99
|
+
return primary_hdu.header["BUNIT"]
|
117
100
|
|
118
101
|
def _convert_units_to_linearised(
|
119
|
-
self,
|
120
|
-
raw_digits: npt.NDArray[np.float32]
|
102
|
+
self, raw_digits: npt.NDArray[np.float32]
|
121
103
|
) -> npt.NDArray[np.float32]:
|
122
104
|
"""Converts spectrogram data from raw digit values to linearised units.
|
123
105
|
|
124
|
-
Applies a transformation based on ADC specifications to convert raw values
|
106
|
+
Applies a transformation based on ADC specifications to convert raw values
|
125
107
|
to dB and then to linearised units.
|
126
108
|
|
127
109
|
:param dynamic_spectra: Raw dynamic spectra in digit values.
|
@@ -130,54 +112,40 @@ class _FitsFile(BatchFile[Spectrogram]):
|
|
130
112
|
# conversion as per ADC specs [see email from C. Monstein]
|
131
113
|
dB = (raw_digits / 255) * (2500 / 25)
|
132
114
|
return 10 ** (dB / 10)
|
133
|
-
|
134
|
-
|
135
|
-
def _get_times(
|
136
|
-
self,
|
137
|
-
bintable_hdu: BinTableHDU
|
138
|
-
) -> npt.NDArray[np.float32]:
|
115
|
+
|
116
|
+
def _get_times(self, bintable_hdu: BinTableHDU) -> npt.NDArray[np.float32]:
|
139
117
|
"""Extracts the elapsed times for each spectrum in seconds, with the first spectrum set to t=0
|
140
118
|
by convention.
|
141
119
|
"""
|
142
|
-
return bintable_hdu.data[
|
120
|
+
return bintable_hdu.data["TIME"][0] # already in seconds
|
143
121
|
|
144
|
-
|
145
|
-
def _get_frequencies(
|
146
|
-
self,
|
147
|
-
bintable_hdu: BinTableHDU
|
148
|
-
) -> npt.NDArray[np.float32]:
|
122
|
+
def _get_frequencies(self, bintable_hdu: BinTableHDU) -> npt.NDArray[np.float32]:
|
149
123
|
"""Extracts the frequencies for each spectral component."""
|
150
|
-
frequencies_MHz = bintable_hdu.data[
|
151
|
-
return frequencies_MHz * 1e6
|
152
|
-
|
153
|
-
|
124
|
+
frequencies_MHz = bintable_hdu.data["FREQUENCY"][0]
|
125
|
+
return frequencies_MHz * 1e6 # convert to Hz
|
126
|
+
|
127
|
+
|
154
128
|
@register_batch(BatchKey.CALLISTO)
|
155
129
|
class CallistoBatch(BaseBatch):
|
156
130
|
"""A batch of data generated by the e-Callisto network.
|
157
|
-
|
131
|
+
|
158
132
|
Supports the following file extensions:
|
159
133
|
- `.fits` (via the `spectrogram_file` attribute)
|
160
134
|
"""
|
161
|
-
|
162
|
-
|
163
|
-
start_time: str,
|
164
|
-
tag: str
|
165
|
-
) -> None:
|
135
|
+
|
136
|
+
def __init__(self, start_time: str, tag: str) -> None:
|
166
137
|
"""Initialise a `CallistoBatch` instance.
|
167
138
|
|
168
139
|
:param start_time: The start time of the batch.
|
169
140
|
:param tag: The batch name tag.
|
170
141
|
"""
|
171
|
-
super().__init__(start_time, tag)
|
142
|
+
super().__init__(start_time, tag)
|
172
143
|
self._fits_file = _FitsFile(self.parent_dir_path, self.name)
|
173
|
-
|
144
|
+
|
174
145
|
# add files formally to the batch
|
175
|
-
self.add_file(
|
176
|
-
|
177
|
-
|
146
|
+
self.add_file(self.spectrogram_file)
|
147
|
+
|
178
148
|
@property
|
179
|
-
def spectrogram_file(
|
180
|
-
self
|
181
|
-
) -> _FitsFile:
|
149
|
+
def spectrogram_file(self) -> _FitsFile:
|
182
150
|
"""The batch file corresponding to the `.fits` extension."""
|
183
151
|
return self._fits_file
|