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/plotting/_panels.py
CHANGED
@@ -2,28 +2,30 @@
|
|
2
2
|
# This file is part of SPECTRE
|
3
3
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
4
|
|
5
|
-
from typing import TypeVar, Tuple, Iterator, Optional
|
5
|
+
from typing import TypeVar, Tuple, Iterator, Optional, Literal
|
6
6
|
from datetime import datetime
|
7
7
|
|
8
8
|
from matplotlib.colors import LogNorm
|
9
9
|
from matplotlib import cm
|
10
|
+
from matplotlib.colorbar import Colorbar
|
10
11
|
import numpy as np
|
11
12
|
import numpy.typing as npt
|
12
13
|
|
13
14
|
from spectre_core.spectrograms import Spectrogram, FrequencyCut, TimeCut
|
14
|
-
from ._base import
|
15
|
+
from ._base import BasePanel, BaseTimeSeriesPanel, XAxisType
|
15
16
|
from ._panel_names import PanelName
|
16
17
|
|
17
18
|
|
18
|
-
T = TypeVar(
|
19
|
+
T = TypeVar("T")
|
20
|
+
|
21
|
+
|
19
22
|
def _bind_to_colors(
|
20
|
-
values: list[T],
|
21
|
-
|
22
|
-
) -> Iterator[Tuple[T, npt.NDArray[np.float32]]]:
|
23
|
+
values: list[T], cmap: str = "winter"
|
24
|
+
) -> Iterator[Tuple[T, npt.NDArray[np.float32]]]:
|
23
25
|
"""
|
24
26
|
Assign RGBA colors to a list of values using a colormap.
|
25
27
|
|
26
|
-
Each value is mapped linearly to a subset of the unit interval and then converted
|
28
|
+
Each value is mapped linearly to a subset of the unit interval and then converted
|
27
29
|
to an RGBA color using the specified colormap.
|
28
30
|
|
29
31
|
:param values: List of values to map to colors.
|
@@ -35,65 +37,74 @@ def _bind_to_colors(
|
|
35
37
|
return zip(values, rgbas)
|
36
38
|
|
37
39
|
|
38
|
-
class FrequencyCutsPanel(
|
40
|
+
class FrequencyCutsPanel(BasePanel):
|
39
41
|
"""
|
40
42
|
Panel for visualising spectrogram data as frequency cuts.
|
41
43
|
|
42
|
-
This panel plots spectrums corresponding to specific time instances
|
43
|
-
in the spectrogram. Each cut is drawn as a line plot, optionally normalized
|
44
|
+
This panel plots spectrums corresponding to specific time instances
|
45
|
+
in the spectrogram. Each cut is drawn as a line plot, optionally normalized
|
44
46
|
or converted to decibels above the background.
|
45
47
|
"""
|
48
|
+
|
46
49
|
def __init__(
|
47
|
-
self,
|
48
|
-
spectrogram: Spectrogram,
|
50
|
+
self,
|
51
|
+
spectrogram: Spectrogram,
|
49
52
|
*times: float | str,
|
50
53
|
dBb: bool = False,
|
51
|
-
peak_normalise: bool = False
|
54
|
+
peak_normalise: bool = False,
|
52
55
|
) -> None:
|
53
56
|
"""Initialise an instance of `FrequencyCutsPanel`.
|
54
57
|
|
55
58
|
:param spectrogram: The spectrogram to be visualised.
|
56
|
-
:param *times: Times at which to take frequency cuts. Can be floats (relative time) or
|
59
|
+
:param *times: Times at which to take frequency cuts. Can be floats (relative time) or
|
57
60
|
strings (formatted datetimes).
|
58
61
|
:param dBb: If True, plots the spectrums in decibels above the background. Defaults to False.
|
59
|
-
:param peak_normalise: If True, normalizes each spectrum such that its peak value is 1.
|
62
|
+
:param peak_normalise: If True, normalizes each spectrum such that its peak value is 1.
|
60
63
|
Ignored if `dBb` is True. Defaults to False.
|
61
64
|
"""
|
62
|
-
super().__init__(PanelName.FREQUENCY_CUTS,
|
63
|
-
|
64
|
-
|
65
|
+
super().__init__(PanelName.FREQUENCY_CUTS, spectrogram)
|
66
|
+
|
65
67
|
if len(times) == 0:
|
66
|
-
raise ValueError(
|
68
|
+
raise ValueError(
|
69
|
+
f"You must specify the time of at least one cut in `*times`"
|
70
|
+
)
|
67
71
|
self._times = times
|
68
|
-
|
72
|
+
|
69
73
|
self._dBb = dBb
|
70
74
|
self._peak_normalise = peak_normalise
|
71
75
|
self._frequency_cuts: dict[float | datetime, FrequencyCut] = {}
|
72
76
|
|
77
|
+
@property
|
78
|
+
def xaxis_type(self) -> Literal[XAxisType.FREQUENCY]:
|
79
|
+
return XAxisType.FREQUENCY
|
73
80
|
|
74
|
-
|
75
|
-
|
76
|
-
|
81
|
+
@property
|
82
|
+
def frequencies(self) -> npt.NDArray[np.float32]:
|
83
|
+
"""The physical frequencies assigned to each spectral component."""
|
84
|
+
return self._spectrogram.frequencies
|
85
|
+
|
86
|
+
def annotate_xaxis(self) -> None:
|
87
|
+
"""Annotate the x-axis assuming frequency in units of Hz."""
|
88
|
+
self._get_ax().set_xlabel("Frequency [Hz]")
|
89
|
+
|
90
|
+
def get_frequency_cuts(self) -> dict[float | datetime, FrequencyCut]:
|
77
91
|
"""
|
78
92
|
Get the frequency cuts for the specified times.
|
79
93
|
|
80
|
-
Computes and caches the spectrum for each requested time. The results are
|
94
|
+
Computes and caches the spectrum for each requested time. The results are
|
81
95
|
stored as a mapping from time to the corresponding `FrequencyCut`.
|
82
96
|
|
83
97
|
:return: A dictionary mapping each time to its corresponding frequency cut.
|
84
98
|
"""
|
85
99
|
if not self._frequency_cuts:
|
86
100
|
for time in self._times:
|
87
|
-
frequency_cut = self._spectrogram.get_frequency_cut(
|
88
|
-
|
89
|
-
|
101
|
+
frequency_cut = self._spectrogram.get_frequency_cut(
|
102
|
+
time, dBb=self._dBb, peak_normalise=self._peak_normalise
|
103
|
+
)
|
90
104
|
self._frequency_cuts[frequency_cut.time] = frequency_cut
|
91
105
|
return self._frequency_cuts
|
92
|
-
|
93
106
|
|
94
|
-
def get_cut_times(
|
95
|
-
self
|
96
|
-
) -> list[float | datetime]:
|
107
|
+
def get_cut_times(self) -> list[float | datetime]:
|
97
108
|
"""
|
98
109
|
Get the exact times of the frequency cuts.
|
99
110
|
|
@@ -103,40 +114,36 @@ class FrequencyCutsPanel(BaseSpectrumPanel):
|
|
103
114
|
"""
|
104
115
|
frequency_cuts = self.get_frequency_cuts()
|
105
116
|
return list(frequency_cuts.keys())
|
106
|
-
|
107
117
|
|
108
|
-
def draw(
|
109
|
-
self
|
110
|
-
) -> None:
|
118
|
+
def draw(self) -> None:
|
111
119
|
"""Draw the frequency cuts onto the panel."""
|
112
120
|
frequency_cuts = self.get_frequency_cuts()
|
113
121
|
for time, color in self.bind_to_colors():
|
114
122
|
frequency_cut = frequency_cuts[time]
|
115
|
-
self.
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
) -> None:
|
123
|
+
self._get_ax().step(
|
124
|
+
self.frequencies, # convert to MHz
|
125
|
+
frequency_cut.cut,
|
126
|
+
where="mid",
|
127
|
+
color=color,
|
128
|
+
)
|
129
|
+
|
130
|
+
def annotate_yaxis(self) -> None:
|
124
131
|
"""Annotate the y-axis of the panel based on the current state.
|
125
132
|
|
126
133
|
The y-axis label reflects whether the data is in decibels above the background (`dBb`),
|
127
134
|
normalized to peak values, or in the original units of the spectrogram.
|
128
135
|
"""
|
136
|
+
ax = self._get_ax()
|
129
137
|
if self._dBb:
|
130
|
-
|
138
|
+
ax.set_ylabel("dBb")
|
131
139
|
elif self._peak_normalise:
|
132
140
|
# no y-axis label if we are peak normalising
|
133
|
-
return
|
141
|
+
return
|
134
142
|
else:
|
135
|
-
|
143
|
+
ax.set_ylabel(f"{self._spectrogram.spectrum_unit.value.capitalize()}")
|
136
144
|
|
137
|
-
|
138
145
|
def bind_to_colors(
|
139
|
-
self
|
146
|
+
self,
|
140
147
|
) -> Iterator[Tuple[float | datetime, npt.NDArray[np.float32]]]:
|
141
148
|
"""
|
142
149
|
Bind each frequency cut time to an RGBA color.
|
@@ -145,73 +152,73 @@ class FrequencyCutsPanel(BaseSpectrumPanel):
|
|
145
152
|
|
146
153
|
:return: An iterator of tuples, each containing a cut time and its corresponding RGBA color.
|
147
154
|
"""
|
148
|
-
return _bind_to_colors(
|
149
|
-
|
150
|
-
|
155
|
+
return _bind_to_colors(
|
156
|
+
self.get_cut_times(), cmap=self.get_panel_format().line_cmap
|
157
|
+
)
|
158
|
+
|
159
|
+
|
151
160
|
class TimeCutsPanel(BaseTimeSeriesPanel):
|
152
161
|
"""
|
153
162
|
Panel for visualising spectrogram data as time series of spectral components.
|
154
163
|
|
155
|
-
This panel plots the time evolution of spectral components at specific
|
156
|
-
frequencies in the spectrogram. Each time series is drawn as a line plot,
|
164
|
+
This panel plots the time evolution of spectral components at specific
|
165
|
+
frequencies in the spectrogram. Each time series is drawn as a line plot,
|
157
166
|
optionally normalized, background-subtracted, or converted to decibels above the background.
|
158
167
|
"""
|
168
|
+
|
159
169
|
def __init__(
|
160
|
-
self,
|
161
|
-
spectrogram: Spectrogram,
|
170
|
+
self,
|
171
|
+
spectrogram: Spectrogram,
|
162
172
|
*frequencies: float,
|
163
173
|
dBb: bool = False,
|
164
174
|
peak_normalise: bool = False,
|
165
|
-
background_subtract: bool = False
|
175
|
+
background_subtract: bool = False,
|
166
176
|
) -> None:
|
167
177
|
"""Initialise an instance of `TimeCutsPanel`.
|
168
178
|
|
169
179
|
:param spectrogram: The spectrogram to be visualised.
|
170
180
|
:param *frequencies: Frequencies at which to extract time series.
|
171
181
|
:param dBb: If True, returns the cuts in decibels above the background. Defaults to False.
|
172
|
-
:param peak_normalise: If True, normalizes each time series so its peak value is 1.
|
182
|
+
:param peak_normalise: If True, normalizes each time series so its peak value is 1.
|
173
183
|
Ignored if `dBb` is True. Defaults to False.
|
174
|
-
:param background_subtract: If True, subtracts the background from each time series.
|
184
|
+
:param background_subtract: If True, subtracts the background from each time series.
|
175
185
|
Ignored if `dBb` is True. Defaults to False.
|
176
186
|
"""
|
177
|
-
super().__init__(PanelName.TIME_CUTS,
|
178
|
-
|
179
|
-
|
187
|
+
super().__init__(PanelName.TIME_CUTS, spectrogram)
|
188
|
+
|
180
189
|
if len(frequencies) == 0:
|
181
|
-
raise ValueError(
|
190
|
+
raise ValueError(
|
191
|
+
f"You must specify the frequency of at least one cut in `*frequencies`."
|
192
|
+
)
|
182
193
|
self._frequencies = frequencies
|
183
|
-
|
194
|
+
|
184
195
|
self._dBb = dBb
|
185
196
|
self._peak_normalise = peak_normalise
|
186
197
|
self._background_subtract = background_subtract
|
187
|
-
self._time_cuts: dict[float, TimeCut] = {}
|
188
|
-
|
198
|
+
self._time_cuts: dict[float, TimeCut] = {}
|
189
199
|
|
190
|
-
def get_time_cuts(
|
191
|
-
self
|
192
|
-
) -> dict[float, TimeCut]:
|
200
|
+
def get_time_cuts(self) -> dict[float, TimeCut]:
|
193
201
|
"""
|
194
202
|
Get the time cuts for the specified frequencies.
|
195
203
|
|
196
|
-
Computes and caches the time series for each requested frequency. The results
|
204
|
+
Computes and caches the time series for each requested frequency. The results
|
197
205
|
are stored as a mapping from frequency to `TimeCut`.
|
198
206
|
|
199
207
|
:return: A dictionary mapping each frequency to its corresponding time cut.
|
200
208
|
"""
|
201
209
|
if not self._time_cuts:
|
202
210
|
for frequency in self._frequencies:
|
203
|
-
time_cut =
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
211
|
+
time_cut = self._spectrogram.get_time_cut(
|
212
|
+
frequency,
|
213
|
+
dBb=self._dBb,
|
214
|
+
peak_normalise=self._peak_normalise,
|
215
|
+
correct_background=self._background_subtract,
|
216
|
+
return_time_type=self.get_time_type(),
|
217
|
+
)
|
208
218
|
self._time_cuts[time_cut.frequency] = time_cut
|
209
219
|
return self._time_cuts
|
210
|
-
|
211
220
|
|
212
|
-
def get_frequencies(
|
213
|
-
self
|
214
|
-
) -> list[float]:
|
221
|
+
def get_frequencies(self) -> list[float]:
|
215
222
|
"""
|
216
223
|
Get the exact frequencies for the spectral components being plotted.
|
217
224
|
|
@@ -220,40 +227,29 @@ class TimeCutsPanel(BaseTimeSeriesPanel):
|
|
220
227
|
time_cuts = self.get_time_cuts()
|
221
228
|
return list(time_cuts.keys())
|
222
229
|
|
223
|
-
|
224
|
-
def draw(
|
225
|
-
self
|
226
|
-
) -> None:
|
230
|
+
def draw(self) -> None:
|
227
231
|
"""Draw the time series for each spectral component onto the panel."""
|
228
232
|
time_cuts = self.get_time_cuts()
|
229
233
|
for frequency, color in self.bind_to_colors():
|
230
234
|
time_cut = time_cuts[frequency]
|
231
|
-
self.
|
232
|
-
|
233
|
-
|
234
|
-
color = color)
|
235
|
-
|
236
|
-
|
237
|
-
def annotate_yaxis(
|
238
|
-
self
|
239
|
-
) -> None:
|
235
|
+
self._get_ax().step(self.times, time_cut.cut, where="mid", color=color)
|
236
|
+
|
237
|
+
def annotate_yaxis(self) -> None:
|
240
238
|
"""
|
241
239
|
Annotate the y-axis of the panel based on the current state.
|
242
240
|
|
243
241
|
The y-axis label reflects whether the data is in decibels above the background (`dBb`),
|
244
242
|
normalized to peak values, or in the original units of the spectrogram.
|
245
243
|
"""
|
244
|
+
ax = self._get_ax()
|
246
245
|
if self._dBb:
|
247
|
-
|
246
|
+
ax.set_ylabel("dBb")
|
248
247
|
elif self._peak_normalise:
|
249
|
-
return
|
248
|
+
return # no y-axis label if we are peak normalising.
|
250
249
|
else:
|
251
|
-
|
250
|
+
ax.set_ylabel(f"{self._spectrogram.spectrum_unit.value.capitalize()}")
|
252
251
|
|
253
|
-
|
254
|
-
def bind_to_colors(
|
255
|
-
self
|
256
|
-
) -> Iterator[Tuple[float, npt.NDArray[np.float32]]]:
|
252
|
+
def bind_to_colors(self) -> Iterator[Tuple[float, npt.NDArray[np.float32]]]:
|
257
253
|
"""
|
258
254
|
Bind each spectral component's frequency to an RGBA color.
|
259
255
|
|
@@ -261,53 +257,60 @@ class TimeCutsPanel(BaseTimeSeriesPanel):
|
|
261
257
|
|
262
258
|
:return: An iterator of tuples, each containing a frequency and its corresponding RGBA color.
|
263
259
|
"""
|
264
|
-
return _bind_to_colors(
|
265
|
-
|
260
|
+
return _bind_to_colors(
|
261
|
+
self.get_frequencies(), cmap=self.get_panel_format().line_cmap
|
262
|
+
)
|
263
|
+
|
266
264
|
|
267
265
|
class IntegralOverFrequencyPanel(BaseTimeSeriesPanel):
|
268
266
|
"""Panel for visualising the spectrogram integrated over frequency.
|
269
267
|
|
270
|
-
This panel plots the spectrogram numerically integrated over frequency as a time
|
271
|
-
series. The result can be normalized to its peak value or adjusted by subtracting
|
268
|
+
This panel plots the spectrogram numerically integrated over frequency as a time
|
269
|
+
series. The result can be normalized to its peak value or adjusted by subtracting
|
272
270
|
the background.
|
273
271
|
"""
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
272
|
+
|
273
|
+
def __init__(
|
274
|
+
self,
|
275
|
+
spectrogram: Spectrogram,
|
276
|
+
peak_normalise: bool = False,
|
277
|
+
background_subtract: bool = False,
|
278
|
+
):
|
278
279
|
"""Initialise an instance of `IntegralOverFrequencyPanel`.
|
279
|
-
|
280
|
+
|
280
281
|
:param spectrogram: The spectrogram to be visualised.
|
281
282
|
:param peak_normalise: If True, normalizes the integral so its peak value is 1. Defaults to False.
|
282
283
|
:param background_subtract: If True, subtracts the background after computing the integral. Defaults to False.
|
283
284
|
"""
|
284
|
-
super().__init__(PanelName.INTEGRAL_OVER_FREQUENCY,
|
285
|
-
spectrogram)
|
285
|
+
super().__init__(PanelName.INTEGRAL_OVER_FREQUENCY, spectrogram)
|
286
286
|
self._peak_normalise = peak_normalise
|
287
287
|
self._background_subtract = background_subtract
|
288
288
|
|
289
|
-
|
290
289
|
def draw(self):
|
291
290
|
"""Integrate the spectrogram over frequency and plot the result."""
|
292
|
-
I = self._spectrogram.integrate_over_frequency(
|
293
|
-
|
294
|
-
|
295
|
-
|
291
|
+
I = self._spectrogram.integrate_over_frequency(
|
292
|
+
correct_background=self._background_subtract,
|
293
|
+
peak_normalise=self._peak_normalise,
|
294
|
+
)
|
295
|
+
self._get_ax().step(
|
296
|
+
self.times, I, where="mid", color=self.get_panel_format().line_color
|
297
|
+
)
|
296
298
|
|
297
299
|
def annotate_yaxis(self):
|
298
300
|
"""This panel does not annotate the y-axis."""
|
299
|
-
|
300
|
-
|
301
|
+
|
302
|
+
|
301
303
|
class SpectrogramPanel(BaseTimeSeriesPanel):
|
302
304
|
"""
|
303
305
|
Panel for visualising the full spectrogram.
|
304
306
|
|
305
|
-
This panel plots the spectrogram as a colormap, with optional log normalization or
|
307
|
+
This panel plots the spectrogram as a colormap, with optional log normalization or
|
306
308
|
in units of decibels above the background.
|
307
309
|
"""
|
310
|
+
|
308
311
|
def __init__(
|
309
|
-
self,
|
310
|
-
spectrogram: Spectrogram,
|
312
|
+
self,
|
313
|
+
spectrogram: Spectrogram,
|
311
314
|
log_norm: bool = False,
|
312
315
|
dBb: bool = False,
|
313
316
|
vmin: Optional[float] = None,
|
@@ -321,67 +324,65 @@ class SpectrogramPanel(BaseTimeSeriesPanel):
|
|
321
324
|
:param vmin: Minimum value for the colormap. Only applies if `dBb` is True. Defaults to None.
|
322
325
|
:param vmax: Maximum value for the colormap. Only applies if `dBb` is True. Defaults to None.
|
323
326
|
"""
|
324
|
-
super().__init__(PanelName.SPECTROGRAM,
|
325
|
-
spectrogram)
|
327
|
+
super().__init__(PanelName.SPECTROGRAM, spectrogram)
|
326
328
|
self._log_norm = log_norm
|
327
329
|
self._dBb = dBb
|
328
330
|
self._vmin = vmin
|
329
331
|
self._vmax = vmax
|
330
332
|
|
331
|
-
|
332
333
|
def _draw_dBb(self) -> None:
|
333
334
|
"""Plot the spectrogram in decibels above the background (dBb).
|
334
335
|
|
335
|
-
This method handles plotting the spectrogram with dBb scaling, applying
|
336
|
+
This method handles plotting the spectrogram with dBb scaling, applying
|
336
337
|
colormap bounds (`vmin` and `vmax`) and adding a colorbar to the panel.
|
337
338
|
"""
|
338
339
|
dynamic_spectra = self._spectrogram.compute_dynamic_spectra_dBb()
|
339
|
-
|
340
|
+
|
340
341
|
# use defaults if neither vmin or vmax is specified
|
341
342
|
vmin = self._vmin or -1
|
342
|
-
vmax = self._vmax or
|
343
|
+
vmax = self._vmax or 2
|
343
344
|
|
345
|
+
ax = self._get_ax()
|
344
346
|
# Plot the spectrogram
|
345
|
-
pcm =
|
347
|
+
pcm = ax.pcolormesh(
|
346
348
|
self.times,
|
347
349
|
self._spectrogram.frequencies,
|
348
350
|
dynamic_spectra,
|
349
351
|
vmin=vmin,
|
350
352
|
vmax=vmax,
|
351
|
-
cmap=self.
|
353
|
+
cmap=self.get_panel_format().spectrogram_cmap,
|
352
354
|
)
|
353
355
|
|
354
356
|
# Add colorbar
|
355
357
|
cbar_ticks = np.linspace(vmin, vmax, 6)
|
356
|
-
cbar = self.
|
358
|
+
cbar = self._get_fig().colorbar(pcm, ax=ax, ticks=cbar_ticks)
|
357
359
|
cbar.set_label("dBb")
|
358
360
|
|
359
|
-
|
360
361
|
def _draw_normal(self) -> None:
|
361
362
|
"""Plot the spectrogram with optional logarithmic normalization.
|
362
363
|
|
363
|
-
This method handles plotting the spectrogram without dBb scaling, using
|
364
|
+
This method handles plotting the spectrogram without dBb scaling, using
|
364
365
|
linear or log normalization based on the `log_norm` attribute.
|
365
366
|
"""
|
366
367
|
dynamic_spectra = self._spectrogram.dynamic_spectra
|
367
|
-
|
368
368
|
|
369
369
|
if self._log_norm:
|
370
|
-
norm = LogNorm(
|
371
|
-
|
370
|
+
norm = LogNorm(
|
371
|
+
vmin=np.nanmin(dynamic_spectra[dynamic_spectra > 0]),
|
372
|
+
vmax=np.nanmax(dynamic_spectra),
|
373
|
+
)
|
372
374
|
else:
|
373
375
|
norm = None
|
374
376
|
|
375
377
|
# Plot the spectrogram
|
376
|
-
self.
|
378
|
+
self._get_ax().pcolormesh(
|
377
379
|
self.times,
|
378
380
|
self._spectrogram.frequencies,
|
379
381
|
dynamic_spectra,
|
380
|
-
cmap=self.
|
382
|
+
cmap=self.get_panel_format().spectrogram_cmap,
|
381
383
|
norm=norm,
|
382
384
|
)
|
383
385
|
|
384
|
-
|
385
386
|
def draw(self) -> None:
|
386
387
|
"""Draw the spectrogram onto the panel."""
|
387
388
|
if self._dBb:
|
@@ -389,46 +390,35 @@ class SpectrogramPanel(BaseTimeSeriesPanel):
|
|
389
390
|
else:
|
390
391
|
self._draw_normal()
|
391
392
|
|
392
|
-
|
393
|
-
def annotate_yaxis(
|
394
|
-
self
|
395
|
-
) -> None:
|
393
|
+
def annotate_yaxis(self) -> None:
|
396
394
|
"""Annotate the yaxis, assuming units of Hz."""
|
397
|
-
self.
|
395
|
+
self._get_ax().set_ylabel("Frequency [Hz]")
|
398
396
|
return
|
399
|
-
|
400
|
-
|
401
|
-
def overlay_time_cuts(
|
402
|
-
self,
|
403
|
-
cuts_panel: TimeCutsPanel
|
404
|
-
) -> None:
|
397
|
+
|
398
|
+
def overlay_time_cuts(self, cuts_panel: TimeCutsPanel) -> None:
|
405
399
|
"""
|
406
400
|
Overlay horizontal lines on the spectrogram to indicate time cuts.
|
407
401
|
|
408
|
-
The lines correspond to the frequencies of the cuts on a `TimeCutsPanel`.
|
402
|
+
The lines correspond to the frequencies of the cuts on a `TimeCutsPanel`.
|
409
403
|
Colors are matched to the lines on the `TimeCutsPanel`.
|
410
404
|
|
411
405
|
:param cuts_panel: The `TimeCutsPanel` containing the cut frequencies to overlay.
|
412
406
|
"""
|
413
407
|
for frequency, color in cuts_panel.bind_to_colors():
|
414
|
-
self.
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
def overlay_frequency_cuts(
|
420
|
-
self,
|
421
|
-
cuts_panel: FrequencyCutsPanel
|
422
|
-
) -> None:
|
408
|
+
self._get_ax().axhline(
|
409
|
+
frequency, color=color, linewidth=self.get_panel_format().line_width
|
410
|
+
)
|
411
|
+
|
412
|
+
def overlay_frequency_cuts(self, cuts_panel: FrequencyCutsPanel) -> None:
|
423
413
|
"""
|
424
414
|
Overlay vertical lines on the spectrogram to indicate frequency cuts.
|
425
415
|
|
426
|
-
The lines correspond to the times of the cuts on a `FrequencyCutsPanel`.
|
416
|
+
The lines correspond to the times of the cuts on a `FrequencyCutsPanel`.
|
427
417
|
Colors are matched to the lines on the `FrequencyCutsPanel`.
|
428
418
|
|
429
419
|
:param cuts_panel: The `FrequencyCutsPanel` containing the cut times to overlay.
|
430
420
|
"""
|
431
421
|
for time, color in cuts_panel.bind_to_colors():
|
432
|
-
self.
|
433
|
-
|
434
|
-
|
422
|
+
self._get_ax().axvline(
|
423
|
+
time, color=color, linewidth=self.get_panel_format().line_width
|
424
|
+
)
|
@@ -11,6 +11,9 @@ from ._factory import get_event_handler, get_event_handler_cls_from_tag
|
|
11
11
|
from ._post_processor import start_post_processor
|
12
12
|
|
13
13
|
__all__ = [
|
14
|
-
"FixedEventHandler",
|
15
|
-
"
|
16
|
-
|
14
|
+
"FixedEventHandler",
|
15
|
+
"SweptEventHandler",
|
16
|
+
"start_post_processor",
|
17
|
+
"get_event_handler",
|
18
|
+
"get_event_handler_cls_from_tag",
|
19
|
+
]
|