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.
Files changed (78) hide show
  1. spectre_core/__init__.py +5 -0
  2. spectre_core/_file_io/__init__.py +4 -4
  3. spectre_core/_file_io/file_handlers.py +60 -106
  4. spectre_core/batches/__init__.py +20 -3
  5. spectre_core/batches/_base.py +85 -134
  6. spectre_core/batches/_batches.py +55 -99
  7. spectre_core/batches/_factory.py +21 -20
  8. spectre_core/batches/_register.py +8 -8
  9. spectre_core/batches/plugins/_batch_keys.py +7 -6
  10. spectre_core/batches/plugins/_callisto.py +65 -97
  11. spectre_core/batches/plugins/_iq_stream.py +105 -169
  12. spectre_core/capture_configs/__init__.py +46 -17
  13. spectre_core/capture_configs/_capture_config.py +25 -52
  14. spectre_core/capture_configs/_capture_modes.py +8 -6
  15. spectre_core/capture_configs/_capture_templates.py +50 -110
  16. spectre_core/capture_configs/_parameters.py +37 -74
  17. spectre_core/capture_configs/_pconstraints.py +40 -40
  18. spectre_core/capture_configs/_pnames.py +36 -34
  19. spectre_core/capture_configs/_ptemplates.py +260 -347
  20. spectre_core/capture_configs/_pvalidators.py +99 -102
  21. spectre_core/config/__init__.py +19 -8
  22. spectre_core/config/_paths.py +25 -47
  23. spectre_core/config/_time_formats.py +6 -5
  24. spectre_core/exceptions.py +38 -0
  25. spectre_core/jobs/__init__.py +3 -6
  26. spectre_core/jobs/_duration.py +12 -0
  27. spectre_core/jobs/_jobs.py +72 -43
  28. spectre_core/jobs/_workers.py +55 -105
  29. spectre_core/logs/__init__.py +7 -2
  30. spectre_core/logs/_configure.py +13 -17
  31. spectre_core/logs/_decorators.py +6 -4
  32. spectre_core/logs/_logs.py +37 -89
  33. spectre_core/logs/_process_types.py +5 -3
  34. spectre_core/plotting/__init__.py +19 -3
  35. spectre_core/plotting/_base.py +112 -177
  36. spectre_core/plotting/_format.py +10 -8
  37. spectre_core/plotting/_panel_names.py +7 -5
  38. spectre_core/plotting/_panel_stack.py +138 -130
  39. spectre_core/plotting/_panels.py +152 -162
  40. spectre_core/post_processing/__init__.py +6 -3
  41. spectre_core/post_processing/_base.py +41 -55
  42. spectre_core/post_processing/_factory.py +14 -11
  43. spectre_core/post_processing/_post_processor.py +16 -12
  44. spectre_core/post_processing/_register.py +10 -7
  45. spectre_core/post_processing/plugins/_event_handler_keys.py +4 -3
  46. spectre_core/post_processing/plugins/_fixed_center_frequency.py +54 -47
  47. spectre_core/post_processing/plugins/_swept_center_frequency.py +199 -174
  48. spectre_core/receivers/__init__.py +9 -2
  49. spectre_core/receivers/_base.py +82 -148
  50. spectre_core/receivers/_factory.py +20 -30
  51. spectre_core/receivers/_register.py +7 -10
  52. spectre_core/receivers/_spec_names.py +17 -15
  53. spectre_core/receivers/plugins/_b200mini.py +47 -60
  54. spectre_core/receivers/plugins/_receiver_names.py +8 -6
  55. spectre_core/receivers/plugins/_rsp1a.py +44 -40
  56. spectre_core/receivers/plugins/_rspduo.py +59 -44
  57. spectre_core/receivers/plugins/_sdrplay_receiver.py +67 -83
  58. spectre_core/receivers/plugins/_test.py +136 -129
  59. spectre_core/receivers/plugins/_usrp.py +93 -85
  60. spectre_core/receivers/plugins/gr/__init__.py +1 -1
  61. spectre_core/receivers/plugins/gr/_base.py +14 -22
  62. spectre_core/receivers/plugins/gr/_rsp1a.py +53 -60
  63. spectre_core/receivers/plugins/gr/_rspduo.py +77 -89
  64. spectre_core/receivers/plugins/gr/_test.py +49 -57
  65. spectre_core/receivers/plugins/gr/_usrp.py +61 -59
  66. spectre_core/spectrograms/__init__.py +21 -13
  67. spectre_core/spectrograms/_analytical.py +108 -99
  68. spectre_core/spectrograms/_array_operations.py +39 -46
  69. spectre_core/spectrograms/_spectrogram.py +293 -324
  70. spectre_core/spectrograms/_transform.py +106 -73
  71. spectre_core/wgetting/__init__.py +1 -3
  72. spectre_core/wgetting/_callisto.py +87 -93
  73. {spectre_core-0.0.22.dist-info → spectre_core-0.0.24.dist-info}/METADATA +9 -23
  74. spectre_core-0.0.24.dist-info/RECORD +79 -0
  75. {spectre_core-0.0.22.dist-info → spectre_core-0.0.24.dist-info}/WHEEL +1 -1
  76. spectre_core-0.0.22.dist-info/RECORD +0 -78
  77. {spectre_core-0.0.22.dist-info → spectre_core-0.0.24.dist-info}/licenses/LICENSE +0 -0
  78. {spectre_core-0.0.22.dist-info → spectre_core-0.0.24.dist-info}/top_level.txt +0 -0
@@ -20,14 +20,15 @@ from ._panel_names import PanelName
20
20
 
21
21
 
22
22
  class XAxisType(Enum):
23
- """The x-axis type for a panel.
24
-
23
+ """The xaxis type for a panel.
24
+
25
25
  Axes are shared in a stack between panels with common `XAxisType`.
26
-
26
+
27
27
  :ivar TIME: The xaxis has units of time.
28
28
  :ivar FREQUENCY: The xaxis has units of frequency.
29
29
  """
30
- TIME = "time"
30
+
31
+ TIME = "time"
31
32
  FREQUENCY = "frequency"
32
33
 
33
34
 
@@ -36,92 +37,72 @@ class BasePanel(ABC):
36
37
 
37
38
  `BasePanel` instances are designed to be part of a `PanelStack`, where multiple
38
39
  panels contribute to a composite plot. Subclasses must implement methods to define
39
- how the panel is drawn and annotated, and specify its x-axis type.
40
+ how the panel is drawn and annotated, and specify its xaxis type.
40
41
  """
42
+
41
43
  def __init__(
42
- self,
44
+ self,
43
45
  name: PanelName,
44
- spectrogram: Spectrogram
46
+ spectrogram: Spectrogram,
47
+ time_type: TimeType = TimeType.RELATIVE,
45
48
  ) -> None:
46
49
  """Initialize an instance of `BasePanel`.
47
50
 
48
51
  :param name: The name of the panel.
49
52
  :param spectrogram: The spectrogram being visualised.
53
+ :param time_type: Indicates whether the times of each spectrum are relative to the first
54
+ spectrum in the spectrogram, or datetimes.
50
55
  """
51
56
  self._name = name
52
57
  self._spectrogram = spectrogram
58
+ self._time_type = time_type
53
59
 
54
- # internal attributes set by `PanelStack` during stacking.
60
+ # These attributes should be set by instances of `PanelStack`.
61
+ self._ax: Optional[Axes] = None
62
+ self._fig: Optional[Figure] = None
55
63
  self._panel_format: Optional[PanelFormat] = None
56
- self._time_type : Optional[TimeType] = None
57
- self._ax : Optional[Axes] = None
58
- self._fig : Optional[Figure] = None
59
- self._identifier : Optional[str] = None
60
-
64
+ self._identifier: Optional[str] = None
61
65
 
62
66
  @abstractmethod
63
- def draw(
64
- self
65
- ) -> None:
67
+ def draw(self) -> None:
66
68
  """Modify the `ax` attribute to draw the panel contents."""
67
69
 
68
-
69
70
  @abstractmethod
70
- def annotate_xaxis(
71
- self
72
- ) -> None:
73
- """Modify the `ax` attribute to annotate the x-axis of the panel."""
74
-
71
+ def annotate_xaxis(self) -> None:
72
+ """Modify the `ax` attribute to annotate the xaxis of the panel."""
75
73
 
76
74
  @abstractmethod
77
- def annotate_yaxis(
78
- self
79
- ) -> None:
80
- """Modify the `ax` attribute to annotate the y-axis of the panel."""
81
-
75
+ def annotate_yaxis(self) -> None:
76
+ """Modify the `ax` attribute to annotate the yaxis of the panel."""
82
77
 
83
78
  @property
84
79
  @abstractmethod
85
- def xaxis_type(
86
- self
87
- ) -> XAxisType:
88
- """Specify the x-axis type for the panel."""
89
-
80
+ def xaxis_type(self) -> XAxisType:
81
+ """Specify the xaxis type for the panel."""
90
82
 
91
83
  @property
92
- def spectrogram(
93
- self
94
- ) -> Spectrogram:
84
+ def spectrogram(self) -> Spectrogram:
95
85
  """The spectrogram being visualised on this panel."""
96
86
  return self._spectrogram
97
-
98
87
 
99
88
  @property
100
- def tag(
101
- self
102
- ) -> str:
89
+ def tag(self) -> str:
103
90
  """The tag of the spectrogram being visualised."""
104
91
  return self._spectrogram.tag
105
-
106
92
 
107
93
  @property
108
- def time_type(
109
- self
110
- ) -> TimeType:
94
+ def name(self) -> PanelName:
95
+ """The name of the panel."""
96
+ return self._name
97
+
98
+ def get_time_type(self) -> TimeType:
111
99
  """The time type of the spectrogram.
112
100
 
113
101
  :raises ValueError: If the `time_type` has not been set.
114
102
  """
115
- if self._time_type is None:
116
- raise ValueError(f"`time_type` for the panel '{self.name}' must be set.")
117
103
  return self._time_type
118
-
119
104
 
120
- @time_type.setter
121
- def time_type(
122
- self,
123
- value: TimeType
124
- ) -> None:
105
+ def set_time_type(self, value: TimeType) -> None:
125
106
  """Set the `TimeType` for the spectrogram.
126
107
 
127
108
  This controls how time is represented and annotated on the panel.
@@ -129,59 +110,36 @@ class BasePanel(ABC):
129
110
  :param value: The `TimeType` to assign to the spectrogram.
130
111
  """
131
112
  self._time_type = value
132
-
133
113
 
134
- @property
135
- def name(
136
- self
137
- ) -> PanelName:
138
- """The name of the panel."""
139
- return self._name
140
-
141
-
142
- @property
143
- def panel_format(
144
- self
145
- ) -> PanelFormat:
114
+ def get_panel_format(self) -> PanelFormat:
146
115
  """Retrieve the panel format, which controls the style of the panel.
147
116
 
148
117
  :raises ValueError: If the `panel_format` has not been set.
149
118
  """
150
119
  if self._panel_format is None:
151
- raise ValueError(f"`panel_format` for the panel '{self.name}' must be set.")
120
+ raise ValueError(f"`panel_format` must be set for the panel `{self.name}`")
152
121
  return self._panel_format
153
122
 
154
-
155
- @panel_format.setter
156
- def panel_format(
157
- self,
158
- value: PanelFormat
159
- ) -> None:
123
+ def set_panel_format(self, value: PanelFormat) -> None:
160
124
  """Set the panel format to control the style of the panel.
161
-
125
+
162
126
  :param value: The `PanelFormat` to assign to the panel.
163
127
  """
164
128
  self._panel_format = value
165
129
 
130
+ def _get_ax(self) -> Axes:
131
+ """Return the `Axes` object bound to this panel.
166
132
 
167
- @property
168
- def ax(
169
- self
170
- ) -> Axes:
171
- """The `Axes` object bound to this panel.
133
+ This method is protected to restrict direct access to `matplotlib` functionality,
134
+ promoting encapsulation, promoting encapsulation.
172
135
 
173
- :raises AttributeError: If the `Axes` object has not been set.
136
+ :raises ValueError: If the `Axes` object has not been set.
174
137
  """
175
138
  if self._ax is None:
176
- raise AttributeError(f"`ax` must be set for the panel `{self.name}`")
139
+ raise ValueError(f"`ax` must be set for the panel `{self.name}`")
177
140
  return self._ax
178
-
179
141
 
180
- @ax.setter
181
- def ax(
182
- self,
183
- value: Axes
184
- ) -> None:
142
+ def set_ax(self, value: Axes) -> None:
185
143
  """Assign a Matplotlib `Axes` object to this panel.
186
144
 
187
145
  This `Axes` will be used for drawing and annotations.
@@ -190,26 +148,19 @@ class BasePanel(ABC):
190
148
  """
191
149
  self._ax = value
192
150
 
151
+ def _get_fig(self) -> Figure:
152
+ """Return the `Figure` object bound to this panel.
193
153
 
194
- @property
195
- def fig(
196
- self
197
- ) -> Figure:
198
- """
199
- The `Figure` object bound to this panel.
154
+ This method is protected to restrict direct access to `matplotlib` functionality,
155
+ promoting encapsulation, promoting encapsulation.
200
156
 
201
- :raises AttributeError: If the `Figure` object has not been set.
157
+ :raises ValueError: If the `Figure` object has not been set.
202
158
  """
203
159
  if self._fig is None:
204
- raise AttributeError(f"`fig` must be set for the panel `{self.name}`")
160
+ raise ValueError(f"`fig` must be set for the panel `{self.name}`")
205
161
  return self._fig
206
-
207
162
 
208
- @fig.setter
209
- def fig(
210
- self,
211
- value: Figure
212
- ) -> None:
163
+ def set_fig(self, value: Figure) -> None:
213
164
  """
214
165
  Assign a Matplotlib `Figure` object to this panel.
215
166
 
@@ -218,104 +169,88 @@ class BasePanel(ABC):
218
169
  :param value: The Matplotlib `Figure` to assign to the panel.
219
170
  """
220
171
  self._fig = value
221
-
222
-
223
- @property
224
- def identifier(
225
- self
226
- ) -> Optional[str]:
172
+
173
+ def get_identifier(self) -> Optional[str]:
227
174
  """Optional identifier for the panel.
228
175
 
229
- This identifier can be used to distinguish panels or aid in superimposing
176
+ This identifier can be used to distinguish panels or aid in superimposing
230
177
  panels in a stack.
231
178
  """
232
179
  return self._identifier
233
-
234
-
235
- @identifier.setter
236
- def identifier(
237
- self,
238
- value: str
239
- ) -> None:
180
+
181
+ def set_identifier(self, value: str) -> None:
240
182
  """Set the optional identifier for the panel.
241
-
183
+
242
184
  This can be used to distinguish panels or aid in superimposing panels.
243
185
  """
244
186
  self._identifier = value
245
-
246
187
 
247
- def hide_xaxis_labels(
248
- self
249
- ) -> None:
250
- """Hide the x-axis labels for this panel."""
251
- self.ax.tick_params(axis='x', labelbottom=False)
188
+ def hide_xaxis_labels(self) -> None:
189
+ """Hide the labels for xaxis ticks in the panel."""
190
+ self._get_ax().tick_params(axis="x", labelbottom=False)
252
191
 
192
+ def hide_yaxis_labels(self) -> None:
193
+ """Hide the labels for yaxis ticks in the panel."""
194
+ self._get_ax().tick_params(axis="y", labelleft=False)
195
+
196
+ def sharex(self, axes: Axes):
197
+ """Share the xaxis with another axes."""
198
+ self._get_ax().sharex(axes)
199
+
200
+ def get_xaxis_labels(self) -> list[str]:
201
+ """Get the text string of all xaxis tick labels."""
202
+ tick_labels = self._get_ax().get_xaxis().get_ticklabels(which="both")
203
+ return [tick_label.get_text() for tick_label in tick_labels]
204
+
205
+ def get_yaxis_labels(self) -> list[str]:
206
+ """Get the text string of all yaxis tick labels."""
207
+ tick_labels = self._get_ax().get_yaxis().get_ticklabels(which="both")
208
+ return [tick_label.get_text() for tick_label in tick_labels]
209
+
210
+ def get_xlabel(self) -> str:
211
+ """Get the xlabel text string."""
212
+ return self._get_ax().get_xlabel()
213
+
214
+ def get_ylabel(self) -> str:
215
+ """Get the ylabel text string."""
216
+ return self._get_ax().get_ylabel()
217
+
218
+ def share_axes(self, panel: "BasePanel") -> None:
219
+ # TODO: More elegantly share axes, rather than access protected method from
220
+ # another instance. Probably, this should just be entirely managed by the `PanelStack`
221
+ self.set_ax(panel._get_ax())
222
+ self.set_fig(panel._get_fig())
253
223
 
254
- def hide_yaxis_labels(
255
- self
256
- ) -> None:
257
- """Hide the y-axis labels for this panel."""
258
- self.ax.tick_params(axis='y', labelbottom=False)
259
-
260
224
 
261
225
  class BaseTimeSeriesPanel(BasePanel):
262
226
  """
263
227
  Abstract subclass of `BasePanel` designed for visualising time series data.
264
228
 
265
229
  Subclasses must implement any remaining abstract methods from `BasePanel`.
266
- """
230
+ """
231
+
267
232
  @property
268
- def xaxis_type(
269
- self
270
- ) -> Literal[XAxisType.TIME]:
233
+ def xaxis_type(self) -> Literal[XAxisType.TIME]:
271
234
  return XAxisType.TIME
272
-
273
-
235
+
274
236
  @property
275
- def times(
276
- self
277
- ) -> npt.NDArray[np.float32 | np.datetime64]:
237
+ def times(self) -> npt.NDArray[np.float32 | np.datetime64]:
278
238
  """The times assigned to each spectrum according to the `TimeType`."""
279
- return self.spectrogram.times if self.time_type == TimeType.RELATIVE else self.spectrogram.datetimes
280
-
281
-
282
- def annotate_xaxis(
283
- self
284
- ) -> None:
285
- """Annotate the x-axis according to the specified `TimeType`."""
286
- if self.time_type == TimeType.RELATIVE:
287
- self.ax.set_xlabel('Time [s]')
239
+ return (
240
+ self.spectrogram.times
241
+ if self.get_time_type() == TimeType.RELATIVE
242
+ else self.spectrogram.datetimes
243
+ )
244
+
245
+ def annotate_xaxis(self) -> None:
246
+ """Annotate the xaxis according to the specified `TimeType`."""
247
+ ax = self._get_ax()
248
+ if self.get_time_type() == TimeType.RELATIVE:
249
+ ax.set_xlabel("Time [s]")
288
250
  else:
289
- #TODO: Adapt for time ranges greater than one day
290
- start_date = datetime.strftime(self.spectrogram.start_datetime.astype(datetime),
291
- TimeFormat.DATE)
292
- self.ax.set_xlabel(f'Time [UTC] (Start Date: {start_date})')
293
- self.ax.xaxis.set_major_formatter(mdates.DateFormatter(TimeFormat.TIME))
294
-
295
-
296
-
297
- class BaseSpectrumPanel(BasePanel):
298
- """An abstract subclass of `BasePanel` tailored for visualising spectrum data.
299
-
300
- Subclasses must implement any remaining abstract methods as described by `BasePanel`.
301
- """
302
- @property
303
- def xaxis_type(
304
- self
305
- ) -> Literal[XAxisType.FREQUENCY]:
306
- return XAxisType.FREQUENCY
307
-
308
-
309
- @property
310
- def frequencies(
311
- self
312
- ) -> npt.NDArray[np.float32]:
313
- """The physical frequencies assigned to each spectral component."""
314
- return self._spectrogram.frequencies
315
-
316
-
317
- def annotate_xaxis(
318
- self
319
- ) -> None:
320
- """Annotate the x-axis assuming frequency in units of Hz."""
321
- self.ax.set_xlabel('Frequency [Hz]')
251
+ # TODO: Adapt for time ranges greater than one day
252
+ start_date = datetime.strftime(
253
+ self.spectrogram.start_datetime.astype(datetime), TimeFormat.DATE
254
+ )
255
+ ax.set_xlabel(f"Time [UTC] (Start Date: {start_date})")
256
+ ax.xaxis.set_major_formatter(mdates.DateFormatter(TimeFormat.TIME))
@@ -4,9 +4,10 @@
4
4
 
5
5
  from dataclasses import dataclass
6
6
 
7
+
7
8
  @dataclass
8
9
  class PanelFormat:
9
- """Specifies formatting options for a panel, including font sizes, line styles,
10
+ """Specifies formatting options for a panel, including font sizes, line styles,
10
11
  colour maps, and general visual settings.
11
12
 
12
13
  These formatting values can be applied consistently across all panels within a `PanelStack`,
@@ -21,11 +22,12 @@ class PanelFormat:
21
22
  :ivar style: Matplotlib style applied to the panel, defaults to "dark_background".
22
23
  :ivar spectrogram_cmap: Colormap applied to spectrogram plots, defaults to "gnuplot2".
23
24
  """
24
- small_size : int = 18
25
- medium_size : int = 21
26
- large_size : int = 24
27
- line_width : int = 3
28
- line_color : str = "lime"
29
- line_cmap : str = "winter"
30
- style : str = "dark_background"
25
+
26
+ small_size: int = 18
27
+ medium_size: int = 21
28
+ large_size: int = 24
29
+ line_width: int = 3
30
+ line_color: str = "lime"
31
+ line_cmap: str = "winter"
32
+ style: str = "dark_background"
31
33
  spectrogram_cmap: str = "gnuplot2"
@@ -4,15 +4,17 @@
4
4
 
5
5
  from enum import Enum
6
6
 
7
+
7
8
  class PanelName(Enum):
8
9
  """Literal corresponding to a fully implemented `BasePanel` subclass.
9
-
10
+
10
11
  :ivar SPECTROGRAM: Panel for visualising the full spectrogram.
11
12
  :ivar FREQUENCY_CUTS: Panel for visualising individual spectrums in a spectrogram.
12
13
  :ivar TIME_CUTS: Panel for visualising spectrogram data as time series of spectral components.
13
14
  :ivar INTEGRAL_OVER_FREQUENCY: Panel for visualising the spectrogram integrated over frequency.
14
15
  """
15
- SPECTROGRAM = "spectrogram"
16
- FREQUENCY_CUTS = "frequency_cuts"
17
- TIME_CUTS = "time_cuts"
18
- INTEGRAL_OVER_FREQUENCY = "integral_over_frequency"
16
+
17
+ SPECTROGRAM = "spectrogram"
18
+ FREQUENCY_CUTS = "frequency_cuts"
19
+ TIME_CUTS = "time_cuts"
20
+ INTEGRAL_OVER_FREQUENCY = "integral_over_frequency"