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
@@ -3,68 +3,126 @@
|
|
3
3
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
4
|
|
5
5
|
import numpy as np
|
6
|
-
|
6
|
+
import numpy.typing as npt
|
7
|
+
from typing import Optional, Tuple, cast
|
7
8
|
import matplotlib.pyplot as plt
|
8
9
|
from matplotlib.figure import Figure
|
9
10
|
from matplotlib.axes import Axes
|
10
11
|
|
11
|
-
from spectre_core.spectrograms import
|
12
|
-
from ._base import BasePanel
|
13
|
-
from ._format import PanelFormat
|
14
|
-
from ._panels import
|
12
|
+
from spectre_core.spectrograms import TimeType
|
13
|
+
from ._base import BasePanel, XAxisType
|
14
|
+
from ._format import PanelFormat
|
15
|
+
from ._panels import PanelName, SpectrogramPanel, TimeCutsPanel, FrequencyCutsPanel
|
15
16
|
|
16
17
|
|
17
|
-
def _is_cuts_panel(
|
18
|
-
|
18
|
+
def _is_cuts_panel(
|
19
|
+
panel: BasePanel
|
20
|
+
) -> bool:
|
21
|
+
"""Check if a panel contains spectrogram cuts.
|
19
22
|
|
23
|
+
:param panel: The panel to check.
|
24
|
+
:return: True if the panel is a frequency or time cuts panel, otherwise False.
|
25
|
+
"""
|
26
|
+
return panel.name in {PanelName.FREQUENCY_CUTS, PanelName.TIME_CUTS}
|
20
27
|
|
21
|
-
|
22
|
-
|
28
|
+
|
29
|
+
def _is_spectrogram_panel(
|
30
|
+
panel: BasePanel
|
31
|
+
) -> bool:
|
32
|
+
"""Check if a panel is a spectrogram panel.
|
33
|
+
|
34
|
+
:param panel: The panel to check.
|
35
|
+
:return: True if the panel is a spectrogram panel, otherwise False.
|
36
|
+
"""
|
37
|
+
return panel.name == PanelName.SPECTROGRAM
|
23
38
|
|
24
39
|
|
25
40
|
class PanelStack:
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
41
|
+
"""Visualise spectrogram data in a stack of panels."""
|
42
|
+
def __init__(
|
43
|
+
self,
|
44
|
+
panel_format: PanelFormat = PanelFormat(),
|
45
|
+
time_type: TimeType = TimeType.RELATIVE,
|
46
|
+
figsize: Tuple[int, int] = (10, 10)
|
47
|
+
) -> None:
|
48
|
+
"""Initialize an instance of `PanelStack`.
|
49
|
+
|
50
|
+
:param panel_format: Formatting applied across all panels in the stack. Defaults to `PanelFormat()`.
|
51
|
+
:param time_type: The type of time assigned to spectrograms, defaults to `TimeType.RELATIVE`.
|
52
|
+
:param figsize: The size of the `matplotlib` figure as (width, height). Defaults to (10, 10).
|
53
|
+
"""
|
30
54
|
self._panel_format = panel_format
|
31
55
|
self._time_type = time_type
|
32
56
|
self._figsize = figsize
|
33
57
|
|
34
|
-
self._panels: list[BasePanel] = []
|
58
|
+
self._panels : list[BasePanel] = []
|
35
59
|
self._superimposed_panels: list[BasePanel] = []
|
60
|
+
|
36
61
|
self._fig: Optional[Figure] = None
|
37
|
-
self._axs: Optional[np.ndarray
|
62
|
+
self._axs: Optional[np.ndarray] = None
|
38
63
|
|
39
64
|
|
40
65
|
@property
|
41
|
-
def time_type(
|
66
|
+
def time_type(
|
67
|
+
self
|
68
|
+
) -> TimeType:
|
69
|
+
"""The type of time we assign to the spectrograms"""
|
42
70
|
return self._time_type
|
43
71
|
|
44
72
|
|
45
73
|
@property
|
46
|
-
def panels(
|
47
|
-
|
74
|
+
def panels(
|
75
|
+
self
|
76
|
+
) -> list[BasePanel]:
|
77
|
+
"""Get the panels in the stack, sorted by their `XAxisType`."""
|
78
|
+
return list(sorted(self._panels, key=lambda panel: panel.xaxis_type.value))
|
48
79
|
|
49
80
|
|
50
81
|
@property
|
51
|
-
def fig(
|
82
|
+
def fig(
|
83
|
+
self
|
84
|
+
) -> Figure:
|
85
|
+
"""Get the shared `matplotlib` figure for the panel stack.
|
86
|
+
|
87
|
+
:raises ValueError: If the axes have not been initialized.
|
88
|
+
"""
|
89
|
+
if self._fig is None:
|
90
|
+
raise ValueError(f"An unexpected error has occured, `fig` must be set for the panel stack.")
|
52
91
|
return self._fig
|
53
92
|
|
54
93
|
|
55
94
|
@property
|
56
|
-
def axs(
|
95
|
+
def axs(
|
96
|
+
self
|
97
|
+
) -> np.ndarray:
|
98
|
+
"""Get the `matplotlib` axes in the stack.
|
99
|
+
|
100
|
+
:return: An array of `matplotlib.axes.Axes`, one for each panel in the stack.
|
101
|
+
:raises ValueError: If the axes have not been initialized.
|
102
|
+
"""
|
103
|
+
if self._axs is None:
|
104
|
+
raise ValueError(f"An unexpected error has occured, `axs` must be set for the panel stack.")
|
57
105
|
return np.atleast_1d(self._axs)
|
58
106
|
|
59
107
|
|
60
108
|
@property
|
61
|
-
def num_panels(
|
109
|
+
def num_panels(
|
110
|
+
self
|
111
|
+
) -> int:
|
112
|
+
"""Get the number of panels in the stack."""
|
62
113
|
return len(self._panels)
|
63
114
|
|
64
115
|
|
65
|
-
def add_panel(
|
66
|
-
|
67
|
-
|
116
|
+
def add_panel(
|
117
|
+
self,
|
118
|
+
panel: BasePanel,
|
119
|
+
identifier: Optional[str] = None
|
120
|
+
) -> None:
|
121
|
+
"""Add a panel to the stack.
|
122
|
+
|
123
|
+
:param panel: An instance of a `BasePanel` subclass to be added to the stack.
|
124
|
+
:param identifier: An optional string to link the panel with others for superimposing.
|
125
|
+
"""
|
68
126
|
panel.panel_format = self._panel_format
|
69
127
|
panel.time_type = self._time_type
|
70
128
|
if identifier:
|
@@ -72,54 +130,102 @@ class PanelStack:
|
|
72
130
|
self._panels.append(panel)
|
73
131
|
|
74
132
|
|
75
|
-
def superimpose_panel(
|
76
|
-
|
77
|
-
|
133
|
+
def superimpose_panel(
|
134
|
+
self,
|
135
|
+
panel: BasePanel,
|
136
|
+
identifier: Optional[str] = None
|
137
|
+
) -> None:
|
138
|
+
"""Superimpose a panel onto an existing panel in the stack.
|
139
|
+
|
140
|
+
:param panel: The panel to superimpose.
|
141
|
+
:param identifier: An optional identifier to link panels during superimposing, defaults to None
|
142
|
+
"""
|
78
143
|
if identifier:
|
79
144
|
panel.identifier = identifier
|
80
145
|
panel.panel_format = self._panel_format
|
81
146
|
self._superimposed_panels.append(panel)
|
82
147
|
|
83
148
|
|
84
|
-
def _init_plot_style(
|
85
|
-
|
149
|
+
def _init_plot_style(
|
150
|
+
self
|
151
|
+
) -> None:
|
152
|
+
"""Initialize the global plot style for the stack.
|
86
153
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
plt.rc('
|
154
|
+
This method sets `matplotlib` styles and font sizes based on the `panel_format`.
|
155
|
+
"""
|
156
|
+
plt.style.use(self._panel_format.style)
|
157
|
+
|
158
|
+
plt.rc('font', size=self._panel_format.small_size)
|
159
|
+
|
160
|
+
plt.rc('axes', titlesize=self._panel_format.medium_size,
|
161
|
+
labelsize=self._panel_format.medium_size)
|
162
|
+
|
163
|
+
plt.rc('xtick', labelsize=self._panel_format.small_size)
|
164
|
+
plt.rc('ytick', labelsize=self._panel_format.small_size)
|
165
|
+
|
92
166
|
plt.rc('legend', fontsize=self._panel_format.small_size)
|
93
167
|
plt.rc('figure', titlesize=self._panel_format.large_size)
|
94
168
|
|
95
169
|
|
96
|
-
def _create_figure_and_axes(
|
170
|
+
def _create_figure_and_axes(
|
171
|
+
self
|
172
|
+
) -> None:
|
173
|
+
"""Create the `matplotlib` figure and axes for the panel stack.
|
174
|
+
|
175
|
+
This initializes a figure with a specified number of vertically stacked axes.
|
176
|
+
"""
|
97
177
|
self._fig, self._axs = plt.subplots(self.num_panels,
|
98
178
|
1,
|
99
179
|
figsize=self._figsize,
|
100
180
|
layout="constrained")
|
101
181
|
|
102
182
|
|
103
|
-
def _assign_axes(
|
104
|
-
|
183
|
+
def _assign_axes(
|
184
|
+
self
|
185
|
+
) -> None:
|
186
|
+
"""Assign each axes in the figure to some panel in the stack.
|
187
|
+
|
188
|
+
Axes are shared between panels with common `xaxis_type`.
|
189
|
+
"""
|
190
|
+
shared_axes: dict[XAxisType, Axes] = {}
|
105
191
|
for i, panel in enumerate(self.panels):
|
106
192
|
panel.ax = self.axs[i]
|
107
193
|
panel.fig = self._fig
|
108
|
-
if panel.
|
109
|
-
panel.ax.sharex(shared_axes[panel.
|
194
|
+
if panel.xaxis_type in shared_axes:
|
195
|
+
panel.ax.sharex( shared_axes[panel.xaxis_type] )
|
110
196
|
else:
|
111
|
-
shared_axes[panel.
|
197
|
+
shared_axes[panel.xaxis_type] = panel.ax
|
112
198
|
|
113
199
|
|
114
|
-
def _overlay_cuts(
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
panel.overlay_cuts(cuts_panel)
|
200
|
+
def _overlay_cuts(
|
201
|
+
self,
|
202
|
+
cuts_panel: BasePanel
|
203
|
+
) -> None:
|
204
|
+
"""Overlay cuts onto corresponding spectrogram panels.
|
120
205
|
|
206
|
+
For a given cuts panel, locate any spectrogram panels in the stack with a matching tag
|
207
|
+
and add the appropriate frequency or time cuts overlay.
|
121
208
|
|
122
|
-
|
209
|
+
:param cuts_panel: A panel containing frequency or time cuts.
|
210
|
+
"""
|
211
|
+
for panel in self.panels:
|
212
|
+
if _is_spectrogram_panel(panel) and panel.tag == cuts_panel.tag:
|
213
|
+
panel = cast(SpectrogramPanel, panel)
|
214
|
+
if cuts_panel.name == PanelName.FREQUENCY_CUTS:
|
215
|
+
panel.overlay_frequency_cuts( cast(FrequencyCutsPanel, cuts_panel) )
|
216
|
+
elif cuts_panel.name == PanelName.TIME_CUTS:
|
217
|
+
panel.overlay_time_cuts( cast(TimeCutsPanel, cuts_panel) )
|
218
|
+
|
219
|
+
|
220
|
+
def _overlay_superimposed_panels(
|
221
|
+
self
|
222
|
+
) -> None:
|
223
|
+
"""Superimpose panels onto matching panels in the stack.
|
224
|
+
|
225
|
+
For each superimposed panel, find a matching panel in the stack with the same name
|
226
|
+
and identifier. Share the axes and figure, then draw the superimposed panel. If the
|
227
|
+
panel contains cuts, overlay those cuts onto the corresponding spectrogram panels.
|
228
|
+
"""
|
123
229
|
for super_panel in self._superimposed_panels:
|
124
230
|
for panel in self._panels:
|
125
231
|
if panel.name == super_panel.name and (panel.identifier == super_panel.identifier):
|
@@ -129,19 +235,27 @@ class PanelStack:
|
|
129
235
|
self._overlay_cuts(super_panel)
|
130
236
|
|
131
237
|
|
132
|
-
def show(
|
238
|
+
def show(
|
239
|
+
self
|
240
|
+
) -> None:
|
241
|
+
"""Display the panel stack figure."""
|
133
242
|
self._init_plot_style()
|
134
243
|
self._create_figure_and_axes()
|
135
244
|
self._assign_axes()
|
136
|
-
|
245
|
+
|
246
|
+
last_panel_per_axis = {panel.xaxis_type: panel for panel in self.panels}
|
247
|
+
|
137
248
|
for panel in self.panels:
|
138
249
|
panel.draw()
|
139
|
-
panel.
|
140
|
-
|
141
|
-
|
250
|
+
panel.annotate_yaxis()
|
251
|
+
|
252
|
+
if panel == last_panel_per_axis[panel.xaxis_type]:
|
253
|
+
panel.annotate_xaxis()
|
142
254
|
else:
|
143
|
-
panel.
|
255
|
+
panel.hide_xaxis_labels()
|
256
|
+
|
144
257
|
if _is_cuts_panel(panel):
|
145
258
|
self._overlay_cuts(panel)
|
259
|
+
|
146
260
|
self._overlay_superimposed_panels()
|
147
261
|
plt.show()
|