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,28 +3,38 @@
|
|
3
3
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
4
|
|
5
5
|
import numpy as np
|
6
|
+
import numpy.typing as npt
|
6
7
|
from datetime import datetime, timedelta
|
7
8
|
from typing import Optional
|
8
9
|
from math import floor
|
9
10
|
|
10
|
-
from spectre_core.config import
|
11
|
-
from ._array_operations import find_closest_index, average_array
|
11
|
+
from spectre_core.config import TimeFormat
|
12
|
+
from ._array_operations import find_closest_index, average_array, time_elapsed
|
12
13
|
from ._spectrogram import Spectrogram
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
15
|
+
def frequency_chop(
|
16
|
+
spectrogram: Spectrogram,
|
17
|
+
start_frequency : float,
|
18
|
+
end_frequency : float
|
19
|
+
) -> Spectrogram:
|
20
|
+
"""
|
21
|
+
Extracts a portion of the spectrogram within the specified frequency range.
|
22
|
+
|
23
|
+
:param spectrogram: The input spectrogram to process.
|
24
|
+
:param start_frequency: The starting frequency of the desired range (Hz).
|
25
|
+
:param end_frequency: The ending frequency of the desired range (Hz).
|
26
|
+
:raises ValueError: If the specified frequency range is entirely outside the spectrogram's frequency range.
|
27
|
+
:raises ValueError: If the start and end indices for the frequency range are identical.
|
28
|
+
:return: A new spectrogram containing only the specified frequency range.
|
29
|
+
"""
|
30
|
+
is_entirely_below_frequency_range = (start_frequency < spectrogram.frequencies[0] and end_frequency < spectrogram.frequencies[0])
|
31
|
+
is_entirely_above_frequency_range = (start_frequency > spectrogram.frequencies[-1] and end_frequency > spectrogram.frequencies[-1])
|
22
32
|
if is_entirely_below_frequency_range or is_entirely_above_frequency_range:
|
23
|
-
|
33
|
+
raise ValueError(f"The requested frequency interval is entirely out of range of the input spectrogram.")
|
24
34
|
|
25
|
-
#find the index of the nearest matching frequency bins in the spectrogram
|
26
|
-
start_index = find_closest_index(start_frequency,
|
27
|
-
end_index
|
35
|
+
# find the index of the nearest matching frequency bins in the spectrogram
|
36
|
+
start_index = find_closest_index(np.float32(start_frequency), spectrogram.frequencies)
|
37
|
+
end_index = find_closest_index(np.float32(end_frequency) , spectrogram.frequencies)
|
28
38
|
|
29
39
|
# enforce distinct start and end indices
|
30
40
|
if start_index == end_index:
|
@@ -35,163 +45,193 @@ def frequency_chop(input_spectrogram: Spectrogram,
|
|
35
45
|
start_index, end_index = end_index, start_index
|
36
46
|
|
37
47
|
# chop the spectrogram accordingly
|
38
|
-
transformed_dynamic_spectra =
|
39
|
-
transformed_frequencies
|
48
|
+
transformed_dynamic_spectra = spectrogram.dynamic_spectra[start_index:end_index+1, :]
|
49
|
+
transformed_frequencies = spectrogram.frequencies[start_index:end_index+1]
|
40
50
|
|
41
|
-
# return the spectrogram instance
|
42
51
|
return Spectrogram(transformed_dynamic_spectra,
|
43
|
-
|
52
|
+
spectrogram.times,
|
44
53
|
transformed_frequencies,
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
def time_chop(
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
54
|
+
spectrogram.tag,
|
55
|
+
spectrogram.spectrum_unit,
|
56
|
+
spectrogram.start_datetime)
|
57
|
+
|
58
|
+
|
59
|
+
def time_chop(
|
60
|
+
spectrogram: Spectrogram,
|
61
|
+
start_time: str,
|
62
|
+
end_time: str
|
63
|
+
) -> Spectrogram:
|
64
|
+
"""
|
65
|
+
Extracts a portion of the spectrogram within the specified time range.
|
66
|
+
|
67
|
+
:param spectrogram: The input spectrogram to process.
|
68
|
+
:param start_time: The starting time of the desired range.
|
69
|
+
:param end_time: The ending time of the desired range.
|
70
|
+
:raises ValueError: If the specified time range is entirely outside the spectrogram's time range.
|
71
|
+
:raises ValueError: If the start and end indices for the time range are identical.
|
72
|
+
:return: A new spectrogram containing only the specified time range.
|
73
|
+
"""
|
74
|
+
start_datetime = np.datetime64( datetime.strptime(start_time, TimeFormat.DATETIME) )
|
75
|
+
end_datetime = np.datetime64( datetime.strptime(end_time , TimeFormat.DATETIME) )
|
76
|
+
|
77
|
+
is_entirely_below_time_range = (start_datetime < spectrogram.datetimes[0] and end_datetime < spectrogram.datetimes[0])
|
78
|
+
is_entirely_above_time_range = (start_datetime > spectrogram.datetimes[-1] and end_datetime > spectrogram.datetimes[-1])
|
62
79
|
if is_entirely_below_time_range or is_entirely_above_time_range:
|
63
|
-
|
80
|
+
raise ValueError(f"Requested time interval is entirely out of range of the input spectrogram.")
|
64
81
|
|
65
|
-
|
66
|
-
|
82
|
+
# find the index of the nearest matching spectrums in the spectrogram.
|
83
|
+
start_index = find_closest_index(start_datetime, spectrogram.datetimes)
|
84
|
+
end_index = find_closest_index(end_datetime, spectrogram.datetimes)
|
67
85
|
|
86
|
+
# enforce distinct start and end indices
|
68
87
|
if start_index == end_index:
|
69
88
|
raise ValueError(f"Start and end indices are equal! Got start_index: {start_index} and end_index: {end_index}")
|
70
89
|
|
90
|
+
# if start index is more than end index, swap the ordering so to enforce start_index <= end_index
|
71
91
|
if start_index > end_index:
|
72
92
|
start_index, end_index = end_index, start_index
|
73
93
|
|
74
|
-
# chop the spectrogram
|
75
|
-
transformed_dynamic_spectra =
|
76
|
-
|
77
|
-
# compute the new start datetime following the time chop
|
78
|
-
transformed_start_datetime = input_spectrogram.datetimes[start_index]
|
94
|
+
# chop the spectrogram accordingly
|
95
|
+
transformed_dynamic_spectra = spectrogram.dynamic_spectra[:, start_index:end_index+1]
|
96
|
+
transformed_start_datetime = spectrogram.datetimes[start_index]
|
79
97
|
|
80
|
-
# chop the times array
|
81
|
-
transformed_times
|
82
|
-
# assign the first spectrum to t=0 [s]
|
98
|
+
# chop the times array and translate such that the first spectrum to t=0 [s]
|
99
|
+
transformed_times = spectrogram.times[start_index:end_index+1]
|
83
100
|
transformed_times -= transformed_times[0]
|
84
101
|
|
85
102
|
return Spectrogram(transformed_dynamic_spectra,
|
86
103
|
transformed_times,
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
104
|
+
spectrogram.frequencies,
|
105
|
+
spectrogram.tag,
|
106
|
+
spectrogram.spectrum_unit,
|
107
|
+
transformed_start_datetime)
|
108
|
+
|
109
|
+
|
110
|
+
def _validate_and_compute_average_over(
|
111
|
+
original_resolution: float,
|
112
|
+
resolution: Optional[float],
|
113
|
+
average_over: int
|
114
|
+
) -> int:
|
115
|
+
"""
|
116
|
+
Validates the input parameters and computes `average_over` if `resolution` is specified.
|
117
|
+
|
118
|
+
:param resolution: The desired resolution for averaging. Mutually exclusive with `average_over`.
|
119
|
+
:param average_over: The number of consecutive spectrums to average over. Mutually exclusive with `resolution`.
|
120
|
+
:param original_resolution: The original resolution (e.g., time or frequency).
|
121
|
+
:raises ValueError: If neither or both `resolution` and `average_over` are specified.
|
122
|
+
:return: The computed or validated `average_over` value.
|
123
|
+
"""
|
124
|
+
if (resolution is None) and (average_over == 1):
|
125
|
+
return average_over
|
126
|
+
|
127
|
+
if not (resolution is not None) ^ (average_over != 1):
|
128
|
+
raise ValueError("Exactly one of 'resolution' or 'average_over' must be specified.")
|
91
129
|
|
92
|
-
|
93
|
-
def time_average(input_spectrogram: Spectrogram,
|
94
|
-
resolution: Optional[float] = None,
|
95
|
-
average_over: Optional[int] = None) -> Spectrogram:
|
96
|
-
|
97
|
-
# spectre does not currently support averaging of non-datetime assigned spectrograms
|
98
|
-
if not input_spectrogram.start_datetime_is_set:
|
99
|
-
raise NotImplementedError(f"Time averaging is not yet supported for spectrograms without an assigned datetime.")
|
100
|
-
|
101
|
-
# if nothing is specified, do nothing
|
102
|
-
if (resolution is None) and (average_over is None):
|
103
|
-
average_over = 1
|
104
|
-
|
105
|
-
if not (resolution is not None) ^ (average_over is not None):
|
106
|
-
raise ValueError(f"Exactly one of 'resolution' or 'average_over' "
|
107
|
-
f"must be specified.")
|
108
|
-
|
109
|
-
# if the resolution is specified, compute the appropriate number of spectrums to average over
|
110
|
-
# and recall the same function
|
111
130
|
if resolution is not None:
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
131
|
+
return max(1, floor(resolution / original_resolution))
|
132
|
+
|
133
|
+
else:
|
134
|
+
return average_over
|
135
|
+
|
136
|
+
|
137
|
+
def time_average(
|
138
|
+
spectrogram: Spectrogram,
|
139
|
+
resolution: Optional[float] = None,
|
140
|
+
average_over: int = 1,
|
141
|
+
) -> Spectrogram:
|
142
|
+
"""
|
143
|
+
Performs time averaging on the spectrogram data.
|
144
|
+
|
145
|
+
:param spectrogram: The input spectrogram to process.
|
146
|
+
:param resolution: The desired time resolution for averaging (seconds). Mutually exclusive with `average_over`.
|
147
|
+
:param average_over: The number of consecutive time points to average. Mutually exclusive with `resolution`.
|
148
|
+
:raises NotImplementedError: If the spectrogram lacks a defined start datetime.
|
149
|
+
:raises ValueError: If neither or both `resolution` and `average_over` are specified.
|
150
|
+
:return: A new spectrogram with time-averaged data.
|
151
|
+
"""
|
152
|
+
if not spectrogram.start_datetime_is_set:
|
153
|
+
raise NotImplementedError(
|
154
|
+
"Time averaging is not supported for spectrograms without an assigned start datetime."
|
155
|
+
)
|
156
|
+
|
157
|
+
average_over = _validate_and_compute_average_over(
|
158
|
+
spectrogram.time_resolution,
|
159
|
+
resolution,
|
160
|
+
average_over
|
161
|
+
)
|
162
|
+
|
163
|
+
# Perform averaging
|
164
|
+
transformed_dynamic_spectra = average_array(
|
165
|
+
spectrogram.dynamic_spectra, average_over, axis=1
|
166
|
+
)
|
167
|
+
transformed_times = average_array(spectrogram.times, average_over)
|
168
|
+
|
169
|
+
# Update start datetime and adjust times to start at t=0
|
170
|
+
transformed_start_datetime = (
|
171
|
+
spectrogram.datetimes[0] + (transformed_times[0]*1e6).astype("timedelta64[us]")
|
172
|
+
)
|
133
173
|
transformed_times -= transformed_times[0]
|
134
|
-
|
135
|
-
return Spectrogram(transformed_dynamic_spectra,
|
136
|
-
transformed_times,
|
137
|
-
input_spectrogram.frequencies,
|
138
|
-
input_spectrogram.tag,
|
139
|
-
transformed_start_datetime,
|
140
|
-
input_spectrogram.spectrum_type)
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
def frequency_average(input_spectrogram: Spectrogram,
|
145
|
-
resolution: Optional[float] = None,
|
146
|
-
average_over: Optional[int] = None) -> Spectrogram:
|
147
|
-
|
148
|
-
# if nothing is specified, do nothing
|
149
|
-
if (resolution is None) and (average_over is None):
|
150
|
-
average_over = 1
|
151
|
-
|
152
|
-
if not (resolution is not None) ^ (average_over is not None):
|
153
|
-
raise ValueError(f"Exactly one of 'resolution' or 'average_over' "
|
154
|
-
f"must be specified.")
|
155
|
-
|
156
|
-
# if the resolution is specified, compute the appropriate number of spectrums to average over
|
157
|
-
# and recall the same function
|
158
|
-
if resolution is not None:
|
159
|
-
average_over = max(1, floor(resolution / input_spectrogram.frequency_resolution))
|
160
|
-
return frequency_average(input_spectrogram, average_over=average_over)
|
161
|
-
|
162
|
-
# No averaging is required, if we have to average over every one spectrum
|
163
|
-
if average_over == 1:
|
164
|
-
return input_spectrogram
|
165
|
-
|
166
|
-
# We need to assign physical frequencies to the averaged spectrums in the spectrograms.
|
167
|
-
# is to assign the i'th averaged spectral component to the i'th averaged frequency.
|
168
|
-
# average the dynamic spectra array
|
169
|
-
transformed_dynamic_spectra = average_array(input_spectrogram.dynamic_spectra,
|
170
|
-
average_over,
|
171
|
-
axis=0)
|
172
|
-
transformed_frequencies = average_array(input_spectrogram.frequencies, average_over)
|
173
|
-
|
174
|
-
return Spectrogram(transformed_dynamic_spectra,
|
175
|
-
input_spectrogram.times,
|
176
|
-
transformed_frequencies,
|
177
|
-
input_spectrogram.tag,
|
178
|
-
input_spectrogram.start_datetime,
|
179
|
-
input_spectrogram.spectrum_type)
|
180
|
-
|
181
|
-
|
182
|
-
def _time_elapsed(datetimes: np.ndarray) -> np.ndarray:
|
183
|
-
# Extract the first datetime to use as the reference point
|
184
|
-
base_time = datetimes[0]
|
185
|
-
# Calculate elapsed time in seconds for each datetime in the list
|
186
|
-
elapsed_time = [(dt - base_time).total_seconds() for dt in datetimes]
|
187
|
-
# Convert the list of seconds to a NumPy array of type float32
|
188
|
-
return np.array(elapsed_time, dtype=np.float32)
|
189
|
-
|
190
|
-
|
191
|
-
# we assume that the spectrogram list is ordered chronologically
|
192
|
-
# we assume there is no time overlap in any of the spectrograms in the list
|
193
|
-
def join_spectrograms(spectrograms: list[Spectrogram]) -> Spectrogram:
|
194
174
|
|
175
|
+
return Spectrogram(
|
176
|
+
transformed_dynamic_spectra,
|
177
|
+
transformed_times,
|
178
|
+
spectrogram.frequencies,
|
179
|
+
spectrogram.tag,
|
180
|
+
spectrogram.spectrum_unit,
|
181
|
+
transformed_start_datetime,
|
182
|
+
)
|
183
|
+
|
184
|
+
|
185
|
+
def frequency_average(
|
186
|
+
spectrogram: Spectrogram,
|
187
|
+
resolution: Optional[float] = None,
|
188
|
+
average_over: int = 1,
|
189
|
+
) -> Spectrogram:
|
190
|
+
"""
|
191
|
+
Performs frequency averaging on the spectrogram data.
|
192
|
+
|
193
|
+
:param spectrogram: The input spectrogram to process.
|
194
|
+
:param resolution: The desired frequency resolution for averaging (Hz). Mutually exclusive with `average_over`.
|
195
|
+
:param average_over: The number of consecutive frequency bins to average. Mutually exclusive with `resolution`.
|
196
|
+
:raises ValueError: If neither or both `resolution` and `average_over` are specified.
|
197
|
+
:return: A new spectrogram with frequency-averaged data.
|
198
|
+
"""
|
199
|
+
average_over = _validate_and_compute_average_over(
|
200
|
+
spectrogram.frequency_resolution,
|
201
|
+
resolution,
|
202
|
+
average_over
|
203
|
+
)
|
204
|
+
|
205
|
+
# Perform averaging
|
206
|
+
transformed_dynamic_spectra = average_array(
|
207
|
+
spectrogram.dynamic_spectra, average_over, axis=0
|
208
|
+
)
|
209
|
+
transformed_frequencies = average_array(spectrogram.frequencies, average_over)
|
210
|
+
|
211
|
+
return Spectrogram(
|
212
|
+
transformed_dynamic_spectra,
|
213
|
+
spectrogram.times,
|
214
|
+
transformed_frequencies,
|
215
|
+
spectrogram.tag,
|
216
|
+
spectrogram.spectrum_unit,
|
217
|
+
spectrogram.start_datetime,
|
218
|
+
)
|
219
|
+
|
220
|
+
|
221
|
+
def join_spectrograms(
|
222
|
+
spectrograms: list[Spectrogram]
|
223
|
+
) -> Spectrogram:
|
224
|
+
"""
|
225
|
+
Joins multiple spectrograms into a single spectrogram along the time axis.
|
226
|
+
|
227
|
+
:param spectrograms: A list of spectrograms to combine.
|
228
|
+
:raises ValueError: If the input list is empty.
|
229
|
+
:raises ValueError: If spectrograms have mismatched frequency ranges.
|
230
|
+
:raises ValueError: If spectrograms have different tags.
|
231
|
+
:raises ValueError: If spectrograms have differing spectrum units.
|
232
|
+
:raises ValueError: If any spectrogram lacks a defined start datetime.
|
233
|
+
:return: A new spectrogram combining all input spectrograms along the time axis.
|
234
|
+
"""
|
195
235
|
# check that the length of the list is non-zero
|
196
236
|
num_spectrograms = len(spectrograms)
|
197
237
|
if num_spectrograms == 0:
|
@@ -207,31 +247,19 @@ def join_spectrograms(spectrograms: list[Spectrogram]) -> Spectrogram:
|
|
207
247
|
raise ValueError(f"All spectrograms must have identical frequency ranges")
|
208
248
|
if spectrogram.tag != reference_spectrogram.tag:
|
209
249
|
raise ValueError(f"All tags must be equal for each spectrogram in the input list!")
|
210
|
-
if spectrogram.
|
250
|
+
if spectrogram.spectrum_unit != reference_spectrogram.spectrum_unit:
|
211
251
|
raise ValueError(f"All units must be equal for each spectrogram in the input list!")
|
212
252
|
if not spectrogram.start_datetime_is_set:
|
213
253
|
raise ValueError(f"All spectrograms must have their start datetime set.")
|
214
254
|
|
215
|
-
#
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
# find the total number of frequency bins (we can safely now use the first)
|
220
|
-
num_total_freq_bins = len(reference_spectrogram.frequencies)
|
221
|
-
# create an empty numpy array to hold the joined spectrograms
|
222
|
-
transformed_dynamic_spectra = np.empty((num_total_freq_bins, num_total_time_bins))
|
223
|
-
|
224
|
-
start_index = 0
|
225
|
-
for spectrogram in spectrograms:
|
226
|
-
end_index = start_index + len(spectrogram.times)
|
227
|
-
transformed_dynamic_spectra[:, start_index:end_index] = spectrogram.dynamic_spectra
|
228
|
-
start_index = end_index
|
229
|
-
|
230
|
-
transformed_times = _time_elapsed(conc_datetimes)
|
255
|
+
# Concatenate all dynamic spectra directly along the time axis
|
256
|
+
transformed_dynamic_spectra = np.hstack([spectrogram.dynamic_spectra for spectrogram in spectrograms])
|
257
|
+
|
258
|
+
transformed_times = time_elapsed( np.concatenate([s.datetimes for s in spectrograms]) )
|
231
259
|
|
232
260
|
return Spectrogram(transformed_dynamic_spectra,
|
233
261
|
transformed_times,
|
234
262
|
reference_spectrogram.frequencies,
|
235
263
|
reference_spectrogram.tag,
|
236
|
-
reference_spectrogram.
|
237
|
-
reference_spectrogram.
|
264
|
+
reference_spectrogram.spectrum_unit,
|
265
|
+
reference_spectrogram.start_datetime)
|
@@ -2,8 +2,10 @@
|
|
2
2
|
# This file is part of SPECTRE
|
3
3
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
4
|
|
5
|
-
|
5
|
+
"""Download third-party spectrogram data."""
|
6
|
+
|
7
|
+
from ._callisto import download_callisto_data, CallistoInstrumentCode
|
6
8
|
|
7
9
|
__all__ = [
|
8
|
-
"download_callisto_data", "
|
10
|
+
"download_callisto_data", "CallistoInstrumentCode"
|
9
11
|
]
|