spectre-core 0.0.22__py3-none-any.whl → 0.0.23__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 +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 +13 -8
- spectre_core/config/_paths.py +18 -35
- 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 +13 -3
- spectre_core/plotting/_base.py +64 -138
- spectre_core/plotting/_format.py +10 -8
- spectre_core/plotting/_panel_names.py +7 -5
- spectre_core/plotting/_panel_stack.py +82 -115
- spectre_core/plotting/_panels.py +120 -155
- 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 +289 -322
- 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.23.dist-info}/METADATA +9 -23
- spectre_core-0.0.23.dist-info/RECORD +79 -0
- {spectre_core-0.0.22.dist-info → spectre_core-0.0.23.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.23.dist-info}/licenses/LICENSE +0 -0
- {spectre_core-0.0.22.dist-info → spectre_core-0.0.23.dist-info}/top_level.txt +0 -0
spectre_core/logs/_logs.py
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
4
|
|
5
5
|
from logging import getLogger
|
6
|
+
|
6
7
|
_LOGGER = getLogger(__name__)
|
7
8
|
|
8
9
|
import os
|
@@ -15,11 +16,8 @@ from spectre_core.config import get_logs_dir_path, TimeFormat
|
|
15
16
|
from ._process_types import ProcessType
|
16
17
|
|
17
18
|
|
18
|
-
def parse_log_base_file_name(
|
19
|
-
|
20
|
-
) -> Tuple[str, str, str]:
|
21
|
-
"""Parse the base file name of a log into a start time, process ID and process type.
|
22
|
-
"""
|
19
|
+
def parse_log_base_file_name(base_file_name: str) -> Tuple[str, str, str]:
|
20
|
+
"""Parse the base file name of a log into a start time, process ID and process type."""
|
23
21
|
file_name, _ = os.path.splitext(base_file_name)
|
24
22
|
log_start_time, pid, process_type = file_name.split("_")
|
25
23
|
return log_start_time, pid, process_type
|
@@ -27,12 +25,8 @@ def parse_log_base_file_name(
|
|
27
25
|
|
28
26
|
class Log(TextHandler):
|
29
27
|
"""Interface to read log files generated by `spectre`."""
|
30
|
-
|
31
|
-
|
32
|
-
start_time: str,
|
33
|
-
pid: str,
|
34
|
-
process_type: ProcessType
|
35
|
-
) -> None:
|
28
|
+
|
29
|
+
def __init__(self, start_time: str, pid: str, process_type: ProcessType) -> None:
|
36
30
|
"""Initialise a `Log` instance.
|
37
31
|
|
38
32
|
:param start_time: The timestamp when the log file was created.
|
@@ -48,40 +42,32 @@ class Log(TextHandler):
|
|
48
42
|
base_file_name = f"{start_time}_{pid}_{process_type.value}"
|
49
43
|
|
50
44
|
super().__init__(parent_path, base_file_name, "log")
|
51
|
-
|
52
45
|
|
53
46
|
@property
|
54
|
-
def start_time(
|
55
|
-
self
|
56
|
-
) -> str:
|
47
|
+
def start_time(self) -> str:
|
57
48
|
"""The system time when the log was created."""
|
58
49
|
return self._start_time
|
59
|
-
|
60
50
|
|
61
51
|
@property
|
62
|
-
def pid(
|
63
|
-
self
|
64
|
-
) -> str:
|
52
|
+
def pid(self) -> str:
|
65
53
|
"""The ID of the process writing to the log file."""
|
66
54
|
return self._pid
|
67
|
-
|
68
55
|
|
69
56
|
@property
|
70
|
-
def process_type(
|
71
|
-
self
|
72
|
-
) -> str:
|
57
|
+
def process_type(self) -> str:
|
73
58
|
"""Indicates the type of process, as defined by `ProcessType`."""
|
74
59
|
return self._process_type
|
75
60
|
|
76
61
|
|
77
62
|
class Logs:
|
78
63
|
"""Filter and read a collection of logs generated by `spectre`."""
|
64
|
+
|
79
65
|
def __init__(
|
80
|
-
self,
|
81
|
-
process_type: Optional[ProcessType] = None,
|
82
|
-
year: Optional[int] = None,
|
83
|
-
month: Optional[int] = None,
|
84
|
-
day: Optional[int] = None
|
66
|
+
self,
|
67
|
+
process_type: Optional[ProcessType] = None,
|
68
|
+
year: Optional[int] = None,
|
69
|
+
month: Optional[int] = None,
|
70
|
+
day: Optional[int] = None,
|
85
71
|
) -> None:
|
86
72
|
"""Initialise a `Logs` instance.
|
87
73
|
|
@@ -91,81 +77,53 @@ class Logs:
|
|
91
77
|
:param day: Filter by the numeric day. Defaults to None.
|
92
78
|
"""
|
93
79
|
self._process_type = process_type.value if process_type is not None else None
|
94
|
-
|
80
|
+
|
95
81
|
self._log_map: dict[str, Log] = OrderedDict()
|
96
82
|
self.set_date(year, month, day)
|
97
83
|
|
98
|
-
|
99
84
|
@property
|
100
|
-
def process_type(
|
101
|
-
self
|
102
|
-
) -> Optional[str]:
|
85
|
+
def process_type(self) -> Optional[str]:
|
103
86
|
"""Indicates the type of process, as defined by `ProcessType`."""
|
104
87
|
return self._process_type
|
105
|
-
|
106
88
|
|
107
89
|
@property
|
108
|
-
def year(
|
109
|
-
self
|
110
|
-
) -> Optional[int]:
|
90
|
+
def year(self) -> Optional[int]:
|
111
91
|
"""Filter by the numeric year."""
|
112
92
|
return self._year
|
113
93
|
|
114
|
-
|
115
|
-
|
116
|
-
def month(
|
117
|
-
self
|
118
|
-
) -> Optional[int]:
|
94
|
+
@property
|
95
|
+
def month(self) -> Optional[int]:
|
119
96
|
"""Filter by the numeric month."""
|
120
97
|
return self._month
|
121
|
-
|
122
98
|
|
123
99
|
@property
|
124
|
-
def day(
|
125
|
-
self
|
126
|
-
) -> Optional[int]:
|
100
|
+
def day(self) -> Optional[int]:
|
127
101
|
"""Filter by the numeric day."""
|
128
102
|
return self._day
|
129
103
|
|
130
|
-
|
131
104
|
@property
|
132
|
-
def logs_dir_path(
|
133
|
-
self
|
134
|
-
) -> str:
|
105
|
+
def logs_dir_path(self) -> str:
|
135
106
|
"""The shared ancestral path for all the log files. `Logs` recursively searches
|
136
107
|
this directory to find all log files according to the date and process type."""
|
137
108
|
return get_logs_dir_path(self.year, self.month, self.day)
|
138
|
-
|
139
109
|
|
140
110
|
@property
|
141
|
-
def log_list(
|
142
|
-
self
|
143
|
-
) -> list[Log]:
|
111
|
+
def log_list(self) -> list[Log]:
|
144
112
|
"""A list of all log handlers representing files found within `logs_dir_path`."""
|
145
113
|
return list(self._log_map.values())
|
146
114
|
|
147
|
-
|
148
115
|
@property
|
149
|
-
def num_logs(
|
150
|
-
self
|
151
|
-
) -> int:
|
116
|
+
def num_logs(self) -> int:
|
152
117
|
"""The number of log files found within `logs_dir_path`."""
|
153
|
-
return len(self.log_list)
|
154
|
-
|
118
|
+
return len(self.log_list)
|
155
119
|
|
156
120
|
@property
|
157
|
-
def file_names(
|
158
|
-
self
|
159
|
-
) -> list[str]:
|
121
|
+
def file_names(self) -> list[str]:
|
160
122
|
"""A list of all log file names found within `logs_dir_path`."""
|
161
123
|
return list(self._log_map.keys())
|
162
124
|
|
163
|
-
|
164
125
|
def set_date(
|
165
|
-
self,
|
166
|
-
year: Optional[int],
|
167
|
-
month: Optional[int],
|
168
|
-
day: Optional[int]
|
126
|
+
self, year: Optional[int], month: Optional[int], day: Optional[int]
|
169
127
|
) -> None:
|
170
128
|
"""Reset `logs_dir_path` according to the numeric date, and refresh the list
|
171
129
|
of available log files.
|
@@ -179,36 +137,28 @@ class Logs:
|
|
179
137
|
self._day = day
|
180
138
|
self.update()
|
181
139
|
|
182
|
-
|
183
|
-
def update(
|
184
|
-
self
|
185
|
-
) -> None:
|
140
|
+
def update(self) -> None:
|
186
141
|
"""Perform a fresh search of all files in `logs_dir_path` for log files
|
187
142
|
according to the date and process type."""
|
188
143
|
log_files = [f for (_, _, files) in os.walk(self.logs_dir_path) for f in files]
|
189
144
|
|
190
145
|
for log_file in log_files:
|
191
|
-
|
146
|
+
|
192
147
|
log_start_time, pid, process_type = parse_log_base_file_name(log_file)
|
193
148
|
|
194
149
|
if self.process_type and process_type != self.process_type:
|
195
150
|
continue
|
196
151
|
|
197
|
-
self._log_map[log_file] = Log(
|
152
|
+
self._log_map[log_file] = Log(
|
153
|
+
log_start_time, pid, ProcessType(process_type)
|
154
|
+
)
|
198
155
|
|
199
156
|
self._log_map = OrderedDict(sorted(self._log_map.items()))
|
200
157
|
|
201
|
-
|
202
|
-
def __iter__(
|
203
|
-
self
|
204
|
-
) -> Iterator[Log]:
|
158
|
+
def __iter__(self) -> Iterator[Log]:
|
205
159
|
yield from self.log_list
|
206
160
|
|
207
|
-
|
208
|
-
def get_from_file_name(
|
209
|
-
self,
|
210
|
-
file_name: str
|
211
|
-
) -> Log:
|
161
|
+
def get_from_file_name(self, file_name: str) -> Log:
|
212
162
|
"""Retrieve a `Log` instance based on the log file name.
|
213
163
|
|
214
164
|
:param file_name: The name of the log file (with or without extension).
|
@@ -220,13 +170,11 @@ class Logs:
|
|
220
170
|
try:
|
221
171
|
return self._log_map[file_name]
|
222
172
|
except KeyError:
|
223
|
-
raise FileNotFoundError(
|
224
|
-
|
173
|
+
raise FileNotFoundError(
|
174
|
+
f"Log handler for file name '{file_name}' not found in log map"
|
175
|
+
)
|
225
176
|
|
226
|
-
def get_from_pid(
|
227
|
-
self,
|
228
|
-
pid: str
|
229
|
-
) -> Log:
|
177
|
+
def get_from_pid(self, pid: str) -> Log:
|
230
178
|
"""Retrieve a `Log` instance based on the process ID.
|
231
179
|
|
232
180
|
:param pid: The process ID to search for.
|
@@ -4,11 +4,13 @@
|
|
4
4
|
|
5
5
|
from enum import Enum
|
6
6
|
|
7
|
+
|
7
8
|
class ProcessType(Enum):
|
8
9
|
"""The origin of a `spectre` process.
|
9
|
-
|
10
|
+
|
10
11
|
:ivar USER: A process is one initiated directly by the user, or part of the main user session.
|
11
12
|
:ivar WORKER: A process is one which is created and managed internally by `spectre`.
|
12
13
|
"""
|
13
|
-
|
14
|
-
|
14
|
+
|
15
|
+
USER = "user"
|
16
|
+
WORKER = "worker"
|
@@ -5,9 +5,19 @@
|
|
5
5
|
"""An intuitive API for plotting spectrogram data."""
|
6
6
|
|
7
7
|
from ._format import PanelFormat
|
8
|
-
from ._panels import
|
8
|
+
from ._panels import (
|
9
|
+
SpectrogramPanel,
|
10
|
+
FrequencyCutsPanel,
|
11
|
+
TimeCutsPanel,
|
12
|
+
IntegralOverFrequencyPanel,
|
13
|
+
)
|
9
14
|
from ._panel_stack import PanelStack
|
10
15
|
|
11
16
|
__all__ = [
|
12
|
-
"PanelFormat",
|
13
|
-
|
17
|
+
"PanelFormat",
|
18
|
+
"PanelStack",
|
19
|
+
"SpectrogramPanel",
|
20
|
+
"FrequencyCutsPanel",
|
21
|
+
"TimeCutsPanel",
|
22
|
+
"IntegralOverFrequencyPanel",
|
23
|
+
]
|
spectre_core/plotting/_base.py
CHANGED
@@ -21,13 +21,14 @@ from ._panel_names import PanelName
|
|
21
21
|
|
22
22
|
class XAxisType(Enum):
|
23
23
|
"""The x-axis type for a panel.
|
24
|
-
|
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
|
-
|
30
|
+
|
31
|
+
TIME = "time"
|
31
32
|
FREQUENCY = "frequency"
|
32
33
|
|
33
34
|
|
@@ -38,11 +39,8 @@ class BasePanel(ABC):
|
|
38
39
|
panels contribute to a composite plot. Subclasses must implement methods to define
|
39
40
|
how the panel is drawn and annotated, and specify its x-axis type.
|
40
41
|
"""
|
41
|
-
|
42
|
-
|
43
|
-
name: PanelName,
|
44
|
-
spectrogram: Spectrogram
|
45
|
-
) -> None:
|
42
|
+
|
43
|
+
def __init__(self, name: PanelName, spectrogram: Spectrogram) -> None:
|
46
44
|
"""Initialize an instance of `BasePanel`.
|
47
45
|
|
48
46
|
:param name: The name of the panel.
|
@@ -53,61 +51,40 @@ class BasePanel(ABC):
|
|
53
51
|
|
54
52
|
# internal attributes set by `PanelStack` during stacking.
|
55
53
|
self._panel_format: Optional[PanelFormat] = None
|
56
|
-
self._time_type
|
57
|
-
self._ax
|
58
|
-
self._fig
|
59
|
-
self._identifier
|
60
|
-
|
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
|
61
58
|
|
62
59
|
@abstractmethod
|
63
|
-
def draw(
|
64
|
-
self
|
65
|
-
) -> None:
|
60
|
+
def draw(self) -> None:
|
66
61
|
"""Modify the `ax` attribute to draw the panel contents."""
|
67
62
|
|
68
|
-
|
69
63
|
@abstractmethod
|
70
|
-
def annotate_xaxis(
|
71
|
-
self
|
72
|
-
) -> None:
|
64
|
+
def annotate_xaxis(self) -> None:
|
73
65
|
"""Modify the `ax` attribute to annotate the x-axis of the panel."""
|
74
66
|
|
75
|
-
|
76
67
|
@abstractmethod
|
77
|
-
def annotate_yaxis(
|
78
|
-
self
|
79
|
-
) -> None:
|
68
|
+
def annotate_yaxis(self) -> None:
|
80
69
|
"""Modify the `ax` attribute to annotate the y-axis of the panel."""
|
81
70
|
|
82
|
-
|
83
71
|
@property
|
84
72
|
@abstractmethod
|
85
|
-
def xaxis_type(
|
86
|
-
|
87
|
-
) -> XAxisType:
|
88
|
-
"""Specify the x-axis type for the panel."""
|
89
|
-
|
73
|
+
def xaxis_type(self) -> XAxisType:
|
74
|
+
"""Specify the x-axis type for the panel."""
|
90
75
|
|
91
76
|
@property
|
92
|
-
def spectrogram(
|
93
|
-
self
|
94
|
-
) -> Spectrogram:
|
77
|
+
def spectrogram(self) -> Spectrogram:
|
95
78
|
"""The spectrogram being visualised on this panel."""
|
96
79
|
return self._spectrogram
|
97
|
-
|
98
80
|
|
99
81
|
@property
|
100
|
-
def tag(
|
101
|
-
self
|
102
|
-
) -> str:
|
82
|
+
def tag(self) -> str:
|
103
83
|
"""The tag of the spectrogram being visualised."""
|
104
84
|
return self._spectrogram.tag
|
105
|
-
|
106
85
|
|
107
86
|
@property
|
108
|
-
def time_type(
|
109
|
-
self
|
110
|
-
) -> TimeType:
|
87
|
+
def time_type(self) -> TimeType:
|
111
88
|
"""The time type of the spectrogram.
|
112
89
|
|
113
90
|
:raises ValueError: If the `time_type` has not been set.
|
@@ -115,13 +92,9 @@ class BasePanel(ABC):
|
|
115
92
|
if self._time_type is None:
|
116
93
|
raise ValueError(f"`time_type` for the panel '{self.name}' must be set.")
|
117
94
|
return self._time_type
|
118
|
-
|
119
95
|
|
120
96
|
@time_type.setter
|
121
|
-
def time_type(
|
122
|
-
self,
|
123
|
-
value: TimeType
|
124
|
-
) -> None:
|
97
|
+
def time_type(self, value: TimeType) -> None:
|
125
98
|
"""Set the `TimeType` for the spectrogram.
|
126
99
|
|
127
100
|
This controls how time is represented and annotated on the panel.
|
@@ -129,20 +102,14 @@ class BasePanel(ABC):
|
|
129
102
|
:param value: The `TimeType` to assign to the spectrogram.
|
130
103
|
"""
|
131
104
|
self._time_type = value
|
132
|
-
|
133
105
|
|
134
106
|
@property
|
135
|
-
def name(
|
136
|
-
self
|
137
|
-
) -> PanelName:
|
107
|
+
def name(self) -> PanelName:
|
138
108
|
"""The name of the panel."""
|
139
109
|
return self._name
|
140
|
-
|
141
|
-
|
110
|
+
|
142
111
|
@property
|
143
|
-
def panel_format(
|
144
|
-
self
|
145
|
-
) -> PanelFormat:
|
112
|
+
def panel_format(self) -> PanelFormat:
|
146
113
|
"""Retrieve the panel format, which controls the style of the panel.
|
147
114
|
|
148
115
|
:raises ValueError: If the `panel_format` has not been set.
|
@@ -151,23 +118,16 @@ class BasePanel(ABC):
|
|
151
118
|
raise ValueError(f"`panel_format` for the panel '{self.name}' must be set.")
|
152
119
|
return self._panel_format
|
153
120
|
|
154
|
-
|
155
121
|
@panel_format.setter
|
156
|
-
def panel_format(
|
157
|
-
self,
|
158
|
-
value: PanelFormat
|
159
|
-
) -> None:
|
122
|
+
def panel_format(self, value: PanelFormat) -> None:
|
160
123
|
"""Set the panel format to control the style of the panel.
|
161
|
-
|
124
|
+
|
162
125
|
:param value: The `PanelFormat` to assign to the panel.
|
163
126
|
"""
|
164
127
|
self._panel_format = value
|
165
128
|
|
166
|
-
|
167
129
|
@property
|
168
|
-
def ax(
|
169
|
-
self
|
170
|
-
) -> Axes:
|
130
|
+
def ax(self) -> Axes:
|
171
131
|
"""The `Axes` object bound to this panel.
|
172
132
|
|
173
133
|
:raises AttributeError: If the `Axes` object has not been set.
|
@@ -175,13 +135,9 @@ class BasePanel(ABC):
|
|
175
135
|
if self._ax is None:
|
176
136
|
raise AttributeError(f"`ax` must be set for the panel `{self.name}`")
|
177
137
|
return self._ax
|
178
|
-
|
179
138
|
|
180
139
|
@ax.setter
|
181
|
-
def ax(
|
182
|
-
self,
|
183
|
-
value: Axes
|
184
|
-
) -> None:
|
140
|
+
def ax(self, value: Axes) -> None:
|
185
141
|
"""Assign a Matplotlib `Axes` object to this panel.
|
186
142
|
|
187
143
|
This `Axes` will be used for drawing and annotations.
|
@@ -190,11 +146,8 @@ class BasePanel(ABC):
|
|
190
146
|
"""
|
191
147
|
self._ax = value
|
192
148
|
|
193
|
-
|
194
149
|
@property
|
195
|
-
def fig(
|
196
|
-
self
|
197
|
-
) -> Figure:
|
150
|
+
def fig(self) -> Figure:
|
198
151
|
"""
|
199
152
|
The `Figure` object bound to this panel.
|
200
153
|
|
@@ -203,13 +156,9 @@ class BasePanel(ABC):
|
|
203
156
|
if self._fig is None:
|
204
157
|
raise AttributeError(f"`fig` must be set for the panel `{self.name}`")
|
205
158
|
return self._fig
|
206
|
-
|
207
159
|
|
208
160
|
@fig.setter
|
209
|
-
def fig(
|
210
|
-
self,
|
211
|
-
value: Figure
|
212
|
-
) -> None:
|
161
|
+
def fig(self, value: Figure) -> None:
|
213
162
|
"""
|
214
163
|
Assign a Matplotlib `Figure` object to this panel.
|
215
164
|
|
@@ -218,104 +167,81 @@ class BasePanel(ABC):
|
|
218
167
|
:param value: The Matplotlib `Figure` to assign to the panel.
|
219
168
|
"""
|
220
169
|
self._fig = value
|
221
|
-
|
222
|
-
|
170
|
+
|
223
171
|
@property
|
224
|
-
def identifier(
|
225
|
-
self
|
226
|
-
) -> Optional[str]:
|
172
|
+
def identifier(self) -> Optional[str]:
|
227
173
|
"""Optional identifier for the panel.
|
228
174
|
|
229
|
-
This identifier can be used to distinguish panels or aid in superimposing
|
175
|
+
This identifier can be used to distinguish panels or aid in superimposing
|
230
176
|
panels in a stack.
|
231
177
|
"""
|
232
178
|
return self._identifier
|
233
|
-
|
234
|
-
|
179
|
+
|
235
180
|
@identifier.setter
|
236
|
-
def identifier(
|
237
|
-
self,
|
238
|
-
value: str
|
239
|
-
) -> None:
|
181
|
+
def 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:
|
188
|
+
def hide_xaxis_labels(self) -> None:
|
250
189
|
"""Hide the x-axis labels for this panel."""
|
251
|
-
self.ax.tick_params(axis=
|
252
|
-
|
190
|
+
self.ax.tick_params(axis="x", labelbottom=False)
|
253
191
|
|
254
|
-
def hide_yaxis_labels(
|
255
|
-
self
|
256
|
-
) -> None:
|
192
|
+
def hide_yaxis_labels(self) -> None:
|
257
193
|
"""Hide the y-axis labels for this panel."""
|
258
|
-
self.ax.tick_params(axis=
|
259
|
-
|
194
|
+
self.ax.tick_params(axis="y", labelbottom=False)
|
195
|
+
|
260
196
|
|
261
197
|
class BaseTimeSeriesPanel(BasePanel):
|
262
198
|
"""
|
263
199
|
Abstract subclass of `BasePanel` designed for visualising time series data.
|
264
200
|
|
265
201
|
Subclasses must implement any remaining abstract methods from `BasePanel`.
|
266
|
-
"""
|
202
|
+
"""
|
203
|
+
|
267
204
|
@property
|
268
|
-
def xaxis_type(
|
269
|
-
self
|
270
|
-
) -> Literal[XAxisType.TIME]:
|
205
|
+
def xaxis_type(self) -> Literal[XAxisType.TIME]:
|
271
206
|
return XAxisType.TIME
|
272
|
-
|
273
|
-
|
207
|
+
|
274
208
|
@property
|
275
|
-
def times(
|
276
|
-
self
|
277
|
-
) -> npt.NDArray[np.float32 | np.datetime64]:
|
209
|
+
def times(self) -> npt.NDArray[np.float32 | np.datetime64]:
|
278
210
|
"""The times assigned to each spectrum according to the `TimeType`."""
|
279
|
-
return
|
280
|
-
|
211
|
+
return (
|
212
|
+
self.spectrogram.times
|
213
|
+
if self.time_type == TimeType.RELATIVE
|
214
|
+
else self.spectrogram.datetimes
|
215
|
+
)
|
281
216
|
|
282
|
-
def annotate_xaxis(
|
283
|
-
self
|
284
|
-
) -> None:
|
217
|
+
def annotate_xaxis(self) -> None:
|
285
218
|
"""Annotate the x-axis according to the specified `TimeType`."""
|
286
219
|
if self.time_type == TimeType.RELATIVE:
|
287
|
-
self.ax.set_xlabel(
|
220
|
+
self.ax.set_xlabel("Time [s]")
|
288
221
|
else:
|
289
|
-
#TODO: Adapt for time ranges greater than one day
|
290
|
-
start_date = datetime.strftime(
|
291
|
-
|
292
|
-
|
222
|
+
# TODO: Adapt for time ranges greater than one day
|
223
|
+
start_date = datetime.strftime(
|
224
|
+
self.spectrogram.start_datetime.astype(datetime), TimeFormat.DATE
|
225
|
+
)
|
226
|
+
self.ax.set_xlabel(f"Time [UTC] (Start Date: {start_date})")
|
293
227
|
self.ax.xaxis.set_major_formatter(mdates.DateFormatter(TimeFormat.TIME))
|
294
228
|
|
295
229
|
|
296
|
-
|
297
230
|
class BaseSpectrumPanel(BasePanel):
|
298
231
|
"""An abstract subclass of `BasePanel` tailored for visualising spectrum data.
|
299
|
-
|
232
|
+
|
300
233
|
Subclasses must implement any remaining abstract methods as described by `BasePanel`.
|
301
|
-
"""
|
234
|
+
"""
|
235
|
+
|
302
236
|
@property
|
303
|
-
def xaxis_type(
|
304
|
-
self
|
305
|
-
) -> Literal[XAxisType.FREQUENCY]:
|
237
|
+
def xaxis_type(self) -> Literal[XAxisType.FREQUENCY]:
|
306
238
|
return XAxisType.FREQUENCY
|
307
|
-
|
308
|
-
|
239
|
+
|
309
240
|
@property
|
310
|
-
def frequencies(
|
311
|
-
self
|
312
|
-
) -> npt.NDArray[np.float32]:
|
241
|
+
def frequencies(self) -> npt.NDArray[np.float32]:
|
313
242
|
"""The physical frequencies assigned to each spectral component."""
|
314
243
|
return self._spectrogram.frequencies
|
315
244
|
|
316
|
-
|
317
|
-
def annotate_xaxis(
|
318
|
-
self
|
319
|
-
) -> None:
|
245
|
+
def annotate_xaxis(self) -> None:
|
320
246
|
"""Annotate the x-axis assuming frequency in units of Hz."""
|
321
|
-
self.ax.set_xlabel(
|
247
|
+
self.ax.set_xlabel("Frequency [Hz]")
|
spectre_core/plotting/_format.py
CHANGED
@@ -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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
|
17
|
+
SPECTROGRAM = "spectrogram"
|
18
|
+
FREQUENCY_CUTS = "frequency_cuts"
|
19
|
+
TIME_CUTS = "time_cuts"
|
20
|
+
INTEGRAL_OVER_FREQUENCY = "integral_over_frequency"
|