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.
Files changed (88) hide show
  1. spectre_core/_file_io/__init__.py +1 -3
  2. spectre_core/_file_io/file_handlers.py +163 -58
  3. spectre_core/batches/__init__.py +10 -11
  4. spectre_core/batches/_base.py +170 -78
  5. spectre_core/batches/_batches.py +149 -99
  6. spectre_core/batches/_factory.py +56 -14
  7. spectre_core/batches/_register.py +23 -8
  8. spectre_core/batches/plugins/_batch_keys.py +16 -0
  9. spectre_core/batches/plugins/_callisto.py +183 -0
  10. spectre_core/batches/plugins/_iq_stream.py +354 -0
  11. spectre_core/capture_configs/__init__.py +17 -13
  12. spectre_core/capture_configs/_capture_config.py +93 -34
  13. spectre_core/capture_configs/_capture_modes.py +22 -0
  14. spectre_core/capture_configs/_capture_templates.py +207 -122
  15. spectre_core/capture_configs/_parameters.py +115 -42
  16. spectre_core/capture_configs/_pconstraints.py +86 -35
  17. spectre_core/capture_configs/_pnames.py +49 -0
  18. spectre_core/capture_configs/_ptemplates.py +389 -346
  19. spectre_core/capture_configs/_pvalidators.py +117 -73
  20. spectre_core/config/__init__.py +6 -8
  21. spectre_core/config/_paths.py +65 -25
  22. spectre_core/config/_time_formats.py +15 -10
  23. spectre_core/exceptions.py +2 -4
  24. spectre_core/jobs/__init__.py +14 -0
  25. spectre_core/jobs/_jobs.py +111 -0
  26. spectre_core/jobs/_workers.py +171 -0
  27. spectre_core/logs/__init__.py +17 -0
  28. spectre_core/logs/_configure.py +67 -0
  29. spectre_core/logs/_decorators.py +33 -0
  30. spectre_core/logs/_logs.py +228 -0
  31. spectre_core/logs/_process_types.py +14 -0
  32. spectre_core/plotting/__init__.py +4 -2
  33. spectre_core/plotting/_base.py +204 -102
  34. spectre_core/plotting/_format.py +17 -4
  35. spectre_core/plotting/_panel_names.py +18 -0
  36. spectre_core/plotting/_panel_stack.py +167 -53
  37. spectre_core/plotting/_panels.py +341 -141
  38. spectre_core/post_processing/__init__.py +8 -6
  39. spectre_core/post_processing/_base.py +70 -44
  40. spectre_core/post_processing/_factory.py +42 -12
  41. spectre_core/post_processing/_post_processor.py +24 -26
  42. spectre_core/post_processing/_register.py +22 -6
  43. spectre_core/post_processing/plugins/_event_handler_keys.py +16 -0
  44. spectre_core/post_processing/plugins/_fixed_center_frequency.py +129 -0
  45. spectre_core/post_processing/{library → plugins}/_swept_center_frequency.py +215 -143
  46. spectre_core/py.typed +0 -0
  47. spectre_core/receivers/__init__.py +10 -7
  48. spectre_core/receivers/_base.py +220 -69
  49. spectre_core/receivers/_factory.py +53 -7
  50. spectre_core/receivers/_register.py +30 -9
  51. spectre_core/receivers/_spec_names.py +26 -15
  52. spectre_core/receivers/plugins/__init__.py +0 -0
  53. spectre_core/receivers/plugins/_receiver_names.py +16 -0
  54. spectre_core/receivers/plugins/_rsp1a.py +59 -0
  55. spectre_core/receivers/plugins/_rspduo.py +67 -0
  56. spectre_core/receivers/plugins/_sdrplay_receiver.py +190 -0
  57. spectre_core/receivers/plugins/_test.py +218 -0
  58. spectre_core/receivers/plugins/gr/_base.py +80 -0
  59. spectre_core/receivers/{gr → plugins/gr}/_rsp1a.py +42 -52
  60. spectre_core/receivers/{gr → plugins/gr}/_rspduo.py +61 -74
  61. spectre_core/receivers/{gr → plugins/gr}/_test.py +33 -31
  62. spectre_core/spectrograms/__init__.py +5 -3
  63. spectre_core/spectrograms/_analytical.py +121 -66
  64. spectre_core/spectrograms/_array_operations.py +103 -36
  65. spectre_core/spectrograms/_spectrogram.py +380 -207
  66. spectre_core/spectrograms/_transform.py +197 -169
  67. spectre_core/wgetting/__init__.py +4 -2
  68. spectre_core/wgetting/_callisto.py +173 -118
  69. {spectre_core-0.0.12.dist-info → spectre_core-0.0.13.dist-info}/METADATA +14 -7
  70. spectre_core-0.0.13.dist-info/RECORD +75 -0
  71. {spectre_core-0.0.12.dist-info → spectre_core-0.0.13.dist-info}/WHEEL +1 -1
  72. spectre_core/batches/library/_callisto.py +0 -96
  73. spectre_core/batches/library/_fixed_center_frequency.py +0 -133
  74. spectre_core/batches/library/_swept_center_frequency.py +0 -105
  75. spectre_core/logging/__init__.py +0 -11
  76. spectre_core/logging/_configure.py +0 -35
  77. spectre_core/logging/_decorators.py +0 -19
  78. spectre_core/logging/_log_handlers.py +0 -176
  79. spectre_core/post_processing/library/_fixed_center_frequency.py +0 -114
  80. spectre_core/receivers/gr/_base.py +0 -33
  81. spectre_core/receivers/library/_rsp1a.py +0 -61
  82. spectre_core/receivers/library/_rspduo.py +0 -69
  83. spectre_core/receivers/library/_sdrplay_receiver.py +0 -185
  84. spectre_core/receivers/library/_test.py +0 -221
  85. spectre_core-0.0.12.dist-info/RECORD +0 -64
  86. /spectre_core/receivers/{gr → plugins/gr}/__init__.py +0 -0
  87. {spectre_core-0.0.12.dist-info → spectre_core-0.0.13.dist-info}/LICENSE +0 -0
  88. {spectre_core-0.0.12.dist-info → spectre_core-0.0.13.dist-info}/top_level.txt +0 -0
@@ -3,212 +3,314 @@
3
3
  # SPDX-License-Identifier: GPL-3.0-or-later
4
4
 
5
5
  from abc import ABC, abstractmethod
6
- from typing import Optional
7
- import numpy as np
8
- from dataclasses import dataclass
6
+ from typing import Optional, Literal
7
+ from enum import Enum
9
8
 
10
- from matplotlib import cm
9
+ import numpy.typing as npt
10
+ import numpy as np
11
11
  import matplotlib.dates as mdates
12
12
  from matplotlib.axes import Axes
13
13
  from matplotlib.figure import Figure
14
14
 
15
- from spectre_core.spectrograms import Spectrogram, TimeTypes
15
+ from spectre_core.spectrograms import Spectrogram, TimeType
16
16
  from ._format import PanelFormat
17
+ from ._panel_names import PanelName
17
18
 
18
19
 
19
- @dataclass(frozen=True)
20
- class XAxisTypes:
21
- TIME : str = "time"
22
- FREQUENCY: str = "frequency"
20
+ class XAxisType(Enum):
21
+ """The x-axis type for a panel.
22
+
23
+ Axes are shared in a stack between panels with common `XAxisType`.
24
+
25
+ :ivar TIME: The xaxis has units of time.
26
+ :ivar FREQUENCY: The xaxis has units of frequency.
27
+ """
28
+ TIME = "time"
29
+ FREQUENCY = "frequency"
23
30
 
24
31
 
25
32
  class BasePanel(ABC):
26
- def __init__(self,
27
- name: str,
28
- spectrogram: Spectrogram):
33
+ """Abstract base class for a panel used to visualise spectrogram data.
34
+
35
+ `BasePanel` instances are designed to be part of a `PanelStack`, where multiple
36
+ panels contribute to a composite plot. Subclasses must implement methods to define
37
+ how the panel is drawn and annotated, and specify its x-axis type.
38
+ """
39
+ def __init__(
40
+ self,
41
+ name: PanelName,
42
+ spectrogram: Spectrogram
43
+ ) -> None:
44
+ """Initialize an instance of `BasePanel`.
45
+
46
+ :param name: The name of the panel.
47
+ :param spectrogram: The spectrogram being visualised.
48
+ """
29
49
  self._name = name
30
50
  self._spectrogram = spectrogram
31
51
 
32
- self._x_axis_type: Optional[str] = None
33
- self._set_x_axis_type()
34
-
35
- # defined while stacking
52
+ # internal attributes set by `PanelStack` during stacking.
36
53
  self._panel_format: Optional[PanelFormat] = None
37
- self._time_type: Optional[str] = None
38
- self._ax: Optional[Axes] = None
39
- self._fig: Optional[Figure] = None
40
- # defined if specified by the user
41
- self._identifier: Optional[str] = None
54
+ self._time_type : Optional[TimeType] = None
55
+ self._ax : Optional[Axes] = None
56
+ self._fig : Optional[Figure] = None
57
+ self._identifier : Optional[str] = None
42
58
 
43
59
 
44
60
  @abstractmethod
45
- def draw(self):
46
- pass
61
+ def draw(
62
+ self
63
+ ) -> None:
64
+ """Modify the `ax` attribute to draw the panel contents."""
47
65
 
48
66
 
49
67
  @abstractmethod
50
- def annotate_x_axis(self):
51
- pass
68
+ def annotate_xaxis(
69
+ self
70
+ ) -> None:
71
+ """Modify the `ax` attribute to annotate the x-axis of the panel."""
52
72
 
53
73
 
54
74
  @abstractmethod
55
- def annotate_y_axis(self):
56
- pass
75
+ def annotate_yaxis(
76
+ self
77
+ ) -> None:
78
+ """Modify the `ax` attribute to annotate the y-axis of the panel."""
57
79
 
58
80
 
81
+ @property
59
82
  @abstractmethod
60
- def _set_x_axis_type(self):
61
- """Required to allow for axes sharing in the stack"""
62
- pass
83
+ def xaxis_type(
84
+ self
85
+ ) -> XAxisType:
86
+ """Specify the x-axis type for the panel."""
63
87
 
64
88
 
65
89
  @property
66
- def spectrogram(self) -> Spectrogram:
90
+ def spectrogram(
91
+ self
92
+ ) -> Spectrogram:
93
+ """The spectrogram being visualised on this panel."""
67
94
  return self._spectrogram
68
95
 
69
96
 
70
97
  @property
71
- def tag(self) -> str:
98
+ def tag(
99
+ self
100
+ ) -> str:
101
+ """The tag of the spectrogram being visualised."""
72
102
  return self._spectrogram.tag
73
103
 
74
104
 
75
105
  @property
76
- def time_type(self) -> str:
106
+ def time_type(
107
+ self
108
+ ) -> TimeType:
109
+ """The time type of the spectrogram.
110
+
111
+ :raises ValueError: If the `time_type` has not been set.
112
+ """
113
+ if self._time_type is None:
114
+ raise ValueError(f"`time_type` for the panel '{self.name}' must be set.")
77
115
  return self._time_type
78
116
 
79
117
 
80
118
  @time_type.setter
81
- def time_type(self, value: str) -> None:
82
- self._validate_time_type(value)
119
+ def time_type(
120
+ self,
121
+ value: TimeType
122
+ ) -> None:
123
+ """Set the `TimeType` for the spectrogram.
124
+
125
+ This controls how time is represented and annotated on the panel.
126
+
127
+ :param value: The `TimeType` to assign to the spectrogram.
128
+ """
83
129
  self._time_type = value
84
130
 
85
131
 
86
132
  @property
87
- def name(self) -> str:
88
- if self._name is None:
89
- raise AttributeError(f"Name for this panel has not yet been set")
133
+ def name(
134
+ self
135
+ ) -> PanelName:
136
+ """The name of the panel."""
90
137
  return self._name
91
138
 
92
-
93
- @name.setter
94
- def name(self, value: str) -> None:
95
- self._name = value
96
-
97
139
 
98
140
  @property
99
- def panel_format(self) -> PanelFormat:
141
+ def panel_format(
142
+ self
143
+ ) -> PanelFormat:
144
+ """Retrieve the panel format, which controls the style of the panel.
145
+
146
+ :raises ValueError: If the `panel_format` has not been set.
147
+ """
100
148
  if self._panel_format is None:
101
- raise AttributeError(f"Panel format has not yet been specified for this panel")
149
+ raise ValueError(f"`panel_format` for the panel '{self.name}' must be set.")
102
150
  return self._panel_format
103
151
 
104
152
 
105
153
  @panel_format.setter
106
- def panel_format(self, value: PanelFormat):
154
+ def panel_format(
155
+ self,
156
+ value: PanelFormat
157
+ ) -> None:
158
+ """Set the panel format to control the style of the panel.
159
+
160
+ :param value: The `PanelFormat` to assign to the panel.
161
+ """
107
162
  self._panel_format = value
108
163
 
109
164
 
110
165
  @property
111
- def ax(self) -> Axes:
166
+ def ax(
167
+ self
168
+ ) -> Axes:
169
+ """The `Axes` object bound to this panel.
170
+
171
+ :raises AttributeError: If the `Axes` object has not been set.
172
+ """
112
173
  if self._ax is None:
113
- raise AttributeError(f"Axes have not yet been set for this panel")
174
+ raise AttributeError(f"`ax` must be set for the panel `{self.name}`")
114
175
  return self._ax
115
176
 
116
177
 
117
178
  @ax.setter
118
- def ax(self, value: Axes) -> None:
179
+ def ax(
180
+ self,
181
+ value: Axes
182
+ ) -> None:
183
+ """Assign a Matplotlib `Axes` object to this panel.
184
+
185
+ This `Axes` will be used for drawing and annotations.
186
+
187
+ :param value: The Matplotlib `Axes` to assign to the panel.
188
+ """
119
189
  self._ax = value
120
190
 
121
191
 
122
192
  @property
123
- def fig(self) -> Figure:
193
+ def fig(
194
+ self
195
+ ) -> Figure:
196
+ """
197
+ The `Figure` object bound to this panel.
198
+
199
+ :raises AttributeError: If the `Figure` object has not been set.
200
+ """
124
201
  if self._fig is None:
125
- raise AttributeError(f"Figure has not yet been set for this panel")
202
+ raise AttributeError(f"`fig` must be set for the panel `{self.name}`")
126
203
  return self._fig
127
204
 
128
205
 
129
206
  @fig.setter
130
- def fig(self, value: Figure) -> None:
131
- self._fig = value
132
-
207
+ def fig(
208
+ self,
209
+ value: Figure
210
+ ) -> None:
211
+ """
212
+ Assign a Matplotlib `Figure` object to this panel.
133
213
 
214
+ This `Figure` is shared across all panels in the `PanelStack`.
134
215
 
135
- @property
136
- def x_axis_type(self) -> str:
137
- if self._x_axis_type is None:
138
- raise AttributeError(f"x-axis type has not been defined for this panel")
139
- return self._x_axis_type
216
+ :param value: The Matplotlib `Figure` to assign to the panel.
217
+ """
218
+ self._fig = value
219
+
140
220
 
141
221
  @property
142
- def identifier(self) -> Optional[str]:
222
+ def identifier(
223
+ self
224
+ ) -> Optional[str]:
225
+ """Optional identifier for the panel.
226
+
227
+ This identifier can be used to distinguish panels or aid in superimposing
228
+ panels in a stack.
229
+ """
143
230
  return self._identifier
144
231
 
145
232
 
146
233
  @identifier.setter
147
- def identifier(self, value: str) -> None:
234
+ def identifier(
235
+ self,
236
+ value: str
237
+ ) -> None:
238
+ """Set the optional identifier for the panel.
239
+
240
+ This can be used to distinguish panels or aid in superimposing panels.
241
+ """
148
242
  self._identifier = value
149
-
150
-
151
- def _validate_time_type(self,
152
- time_type: str):
153
- valid_time_types = [TimeTypes.SECONDS, TimeTypes.DATETIMES]
154
- if time_type not in valid_time_types:
155
- raise ValueError(f"Invalid time type. "
156
- f"Expected one of {valid_time_types} "
157
- f"but received {time_type}")
158
-
159
-
160
- def bind_to_colors(self,
161
- values: np.ndarray,
162
- cmap: str = "winter"):
163
- cmap = cm.get_cmap(cmap)
164
- rgbas = cmap(np.linspace(0.1, 0.9, len(values)))
165
- return zip(values, rgbas) # assign each RGBA array to each value
166
243
 
167
244
 
168
- def hide_x_axis_labels(self) -> None:
245
+ def hide_xaxis_labels(
246
+ self
247
+ ) -> None:
248
+ """Hide the x-axis labels for this panel."""
169
249
  self.ax.tick_params(axis='x', labelbottom=False)
170
250
 
171
251
 
172
- def hide_y_axis_labels(self) -> None:
252
+ def hide_yaxis_labels(
253
+ self
254
+ ) -> None:
255
+ """Hide the y-axis labels for this panel."""
173
256
  self.ax.tick_params(axis='y', labelbottom=False)
174
257
 
175
258
 
176
259
  class BaseTimeSeriesPanel(BasePanel):
177
- def __init__(self, *args, **kwargs):
178
- super().__init__(*args, **kwargs)
179
-
260
+ """
261
+ Abstract subclass of `BasePanel` designed for visualising time series data.
180
262
 
263
+ Subclasses must implement any remaining abstract methods from `BasePanel`.
264
+ """
181
265
  @property
182
- def times(self):
183
- return self.spectrogram.times if self.time_type == TimeTypes.SECONDS else self.spectrogram.datetimes
266
+ def xaxis_type(
267
+ self
268
+ ) -> Literal[XAxisType.TIME]:
269
+ return XAxisType.TIME
270
+
271
+
272
+ @property
273
+ def times(
274
+ self
275
+ ) -> npt.NDArray[np.float32 | np.datetime64]:
276
+ """The times assigned to each spectrum according to the `TimeType`."""
277
+ return self.spectrogram.times if self.time_type == TimeType.RELATIVE else self.spectrogram.datetimes
184
278
 
185
279
 
186
- def annotate_x_axis(self):
187
- if self.time_type == TimeTypes.SECONDS:
280
+ def annotate_xaxis(
281
+ self
282
+ ) -> None:
283
+ """Annotate the x-axis according to the specified `TimeType`."""
284
+ if self.time_type == TimeType.RELATIVE:
188
285
  self.ax.set_xlabel('Time [s]')
189
286
  else:
190
287
  self.ax.set_xlabel('Time [UTC]')
191
288
  self.ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S'))
192
-
193
-
194
- def _set_x_axis_type(self):
195
- self._x_axis_type = XAxisTypes.TIME
196
289
 
197
290
 
198
291
 
199
292
  class BaseSpectrumPanel(BasePanel):
200
- def __init__(self, *args, **kwargs):
201
- super().__init__(*args, **kwargs)
202
-
203
-
293
+ """An abstract subclass of `BasePanel` tailored for visualising spectrum data.
294
+
295
+ Subclasses must implement any remaining abstract methods as described by `BasePanel`.
296
+ """
204
297
  @property
205
- def frequencies(self):
298
+ def xaxis_type(
299
+ self
300
+ ) -> Literal[XAxisType.FREQUENCY]:
301
+ return XAxisType.FREQUENCY
302
+
303
+
304
+ @property
305
+ def frequencies(
306
+ self
307
+ ) -> npt.NDArray[np.float32]:
308
+ """The physical frequencies assigned to each spectral component."""
206
309
  return self._spectrogram.frequencies
207
310
 
208
311
 
209
- def annotate_x_axis(self):
210
- self.ax.set_xlabel('Frequency [MHz]')
211
-
212
-
213
- def _set_x_axis_type(self):
214
- self._x_axis_type = XAxisTypes.FREQUENCY
312
+ def annotate_xaxis(
313
+ self
314
+ ) -> None:
315
+ """Annotate the x-axis assuming frequency in units of Hz."""
316
+ self.ax.set_xlabel('Frequency [Hz]')
@@ -6,13 +6,26 @@ from dataclasses import dataclass
6
6
 
7
7
  @dataclass
8
8
  class PanelFormat:
9
+ """Specifies formatting options for a panel, including font sizes, line styles,
10
+ colour maps, and general visual settings.
11
+
12
+ These formatting values can be applied consistently across all panels within a `PanelStack`,
13
+ but are optional.
14
+
15
+ :ivar small_size: Font size for small text elements, defaults to 18.
16
+ :ivar medium_size: Font size for medium text elements, defaults to 21.
17
+ :ivar large_size: Font size for large text elements, defaults to 24.
18
+ :ivar line_width: Thickness of lines in the plot, defaults to 3.
19
+ :ivar line_color: Colour used for line elements, defaults to "lime".
20
+ :ivar line_cmap: Colormap applied to line-based visual elements, defaults to "winter".
21
+ :ivar style: Matplotlib style applied to the panel, defaults to "dark_background".
22
+ :ivar spectrogram_cmap: Colormap applied to spectrogram plots, defaults to "gnuplot2".
23
+ """
9
24
  small_size : int = 18
10
25
  medium_size : int = 21
11
26
  large_size : int = 24
12
27
  line_width : int = 3
28
+ line_color : str = "lime"
29
+ line_cmap : str = "winter"
13
30
  style : str = "dark_background"
14
31
  spectrogram_cmap: str = "gnuplot2"
15
- cuts_cmap : str = "winter"
16
- integral_color : str = "lime"
17
-
18
- DEFAULT_FORMAT = PanelFormat()
@@ -0,0 +1,18 @@
1
+ # SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
2
+ # This file is part of SPECTRE
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ from enum import Enum
6
+
7
+ class PanelName(Enum):
8
+ """Literal corresponding to a fully implemented `BasePanel` subclass.
9
+
10
+ :ivar SPECTROGRAM: Panel for visualising the full spectrogram.
11
+ :ivar FREQUENCY_CUTS: Panel for visualising individual spectrums in a spectrogram.
12
+ :ivar TIME_CUTS: Panel for visualising spectrogram data as time series of spectral components.
13
+ :ivar INTEGRAL_OVER_FREQUENCY: Panel for visualising the spectrogram integrated over frequency.
14
+ """
15
+ SPECTROGRAM = "spectrogram"
16
+ FREQUENCY_CUTS = "frequency_cuts"
17
+ TIME_CUTS = "time_cuts"
18
+ INTEGRAL_OVER_FREQUENCY = "integral_over_frequency"