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
@@ -19,35 +19,30 @@
|
|
19
19
|
from functools import partial
|
20
20
|
from dataclasses import dataclass
|
21
21
|
|
22
|
-
from
|
23
|
-
from gnuradio import spectre
|
24
|
-
from gnuradio import sdrplay3
|
25
|
-
|
26
|
-
from spectre_core.capture_configs import Parameters, PNames
|
22
|
+
from spectre_core.capture_configs import Parameters, PName
|
27
23
|
from spectre_core.config import get_batches_dir_path
|
28
|
-
from ._base import capture
|
29
|
-
|
30
|
-
|
31
|
-
class _tuner_1_fixed_center_frequency(
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
24
|
+
from ._base import capture, spectre_top_block
|
25
|
+
|
26
|
+
|
27
|
+
class _tuner_1_fixed_center_frequency(spectre_top_block):
|
28
|
+
def flowgraph(
|
29
|
+
self,
|
30
|
+
tag: str,
|
31
|
+
parameters: Parameters
|
32
|
+
) -> None:
|
33
|
+
# OOT Module inline imports
|
34
|
+
from gnuradio import spectre
|
35
|
+
from gnuradio import sdrplay3
|
37
36
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
rf_gain = parameters.get_parameter_value(PNames.RF_GAIN)
|
47
|
-
|
48
|
-
##################################################
|
37
|
+
# Unpack the capture config parameters
|
38
|
+
sample_rate = parameters.get_parameter_value(PName.SAMPLE_RATE)
|
39
|
+
batch_size = parameters.get_parameter_value(PName.BATCH_SIZE)
|
40
|
+
center_freq = parameters.get_parameter_value(PName.CENTER_FREQUENCY)
|
41
|
+
bandwidth = parameters.get_parameter_value(PName.BANDWIDTH)
|
42
|
+
if_gain = parameters.get_parameter_value(PName.IF_GAIN)
|
43
|
+
rf_gain = parameters.get_parameter_value(PName.RF_GAIN)
|
44
|
+
|
49
45
|
# Blocks
|
50
|
-
##################################################
|
51
46
|
self.spectre_batched_file_sink_0 = spectre.batched_file_sink(get_batches_dir_path(),
|
52
47
|
tag,
|
53
48
|
batch_size,
|
@@ -80,33 +75,29 @@ class _tuner_1_fixed_center_frequency(gr.top_block):
|
|
80
75
|
self.sdrplay3_rspduo_0.set_sample_sequence_gaps_check(False)
|
81
76
|
self.sdrplay3_rspduo_0.set_show_gain_changes(False)
|
82
77
|
|
83
|
-
|
84
|
-
##################################################
|
85
78
|
# Connections
|
86
|
-
##################################################
|
87
79
|
self.connect((self.sdrplay3_rspduo_0, 0), (self.spectre_batched_file_sink_0, 0))
|
88
80
|
|
89
81
|
|
90
|
-
class _tuner_2_fixed_center_frequency(
|
91
|
-
def
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
82
|
+
class _tuner_2_fixed_center_frequency(spectre_top_block):
|
83
|
+
def flowgraph(
|
84
|
+
self,
|
85
|
+
tag: str,
|
86
|
+
parameters: Parameters
|
87
|
+
) -> None:
|
88
|
+
# OOT Module inline imports
|
89
|
+
from gnuradio import spectre
|
90
|
+
from gnuradio import sdrplay3
|
96
91
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
rf_gain = parameters.get_parameter_value(PNames.RF_GAIN)
|
106
|
-
|
107
|
-
##################################################
|
92
|
+
# Unpack the capture config parameters
|
93
|
+
sample_rate = parameters.get_parameter_value(PName.SAMPLE_RATE)
|
94
|
+
batch_size = parameters.get_parameter_value(PName.BATCH_SIZE)
|
95
|
+
center_freq = parameters.get_parameter_value(PName.CENTER_FREQUENCY)
|
96
|
+
bandwidth = parameters.get_parameter_value(PName.BANDWIDTH)
|
97
|
+
if_gain = parameters.get_parameter_value(PName.IF_GAIN)
|
98
|
+
rf_gain = parameters.get_parameter_value(PName.RF_GAIN)
|
99
|
+
|
108
100
|
# Blocks
|
109
|
-
##################################################
|
110
101
|
self.spectre_batched_file_sink_0 = spectre.batched_file_sink(get_batches_dir_path(),
|
111
102
|
tag,
|
112
103
|
batch_size,
|
@@ -141,34 +132,32 @@ class _tuner_2_fixed_center_frequency(gr.top_block):
|
|
141
132
|
self.sdrplay3_rspduo_0.set_show_gain_changes(False)
|
142
133
|
|
143
134
|
|
144
|
-
##################################################
|
145
135
|
# Connections
|
146
|
-
##################################################
|
147
136
|
self.connect((self.sdrplay3_rspduo_0, 0), (self.spectre_batched_file_sink_0, 0))
|
148
137
|
|
149
138
|
|
150
|
-
class _tuner_1_swept_center_frequency(
|
151
|
-
def
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
139
|
+
class _tuner_1_swept_center_frequency(spectre_top_block):
|
140
|
+
def flowgraph(
|
141
|
+
self,
|
142
|
+
tag: str,
|
143
|
+
parameters: Parameters
|
144
|
+
) -> None:
|
145
|
+
# OOT Module inline imports
|
146
|
+
from gnuradio import spectre
|
147
|
+
from gnuradio import sdrplay3
|
148
|
+
|
149
|
+
# Unpack the capture config parameters
|
150
|
+
sample_rate = parameters.get_parameter_value(PName.SAMPLE_RATE)
|
151
|
+
bandwidth = parameters.get_parameter_value(PName.BANDWIDTH)
|
152
|
+
min_frequency = parameters.get_parameter_value(PName.MIN_FREQUENCY)
|
153
|
+
max_frequency = parameters.get_parameter_value(PName.MAX_FREQUENCY)
|
154
|
+
frequency_step = parameters.get_parameter_value(PName.FREQUENCY_STEP)
|
155
|
+
samples_per_step = parameters.get_parameter_value(PName.SAMPLES_PER_STEP)
|
156
|
+
if_gain = parameters.get_parameter_value(PName.IF_GAIN)
|
157
|
+
rf_gain = parameters.get_parameter_value(PName.RF_GAIN)
|
158
|
+
batch_size = parameters.get_parameter_value(PName.BATCH_SIZE)
|
159
|
+
|
170
160
|
# Blocks
|
171
|
-
##################################################
|
172
161
|
self.spectre_sweep_driver_0 = spectre.sweep_driver(min_frequency,
|
173
162
|
max_frequency,
|
174
163
|
frequency_step,
|
@@ -212,16 +201,14 @@ class _tuner_1_swept_center_frequency(gr.top_block):
|
|
212
201
|
self.sdrplay3_rspduo_0.set_show_gain_changes(False)
|
213
202
|
|
214
203
|
|
215
|
-
##################################################
|
216
204
|
# Connections
|
217
|
-
##################################################
|
218
205
|
self.msg_connect((self.spectre_sweep_driver_0, 'freq'), (self.sdrplay3_rspduo_0, 'freq'))
|
219
206
|
self.connect((self.sdrplay3_rspduo_0, 0), (self.spectre_batched_file_sink_0, 0))
|
220
207
|
self.connect((self.sdrplay3_rspduo_0, 0), (self.spectre_sweep_driver_0, 0))
|
221
208
|
|
222
209
|
|
223
210
|
@dataclass(frozen=True)
|
224
|
-
class
|
211
|
+
class CaptureMethod:
|
225
212
|
tuner_1_fixed_center_frequency = partial(capture, top_block_cls=_tuner_1_fixed_center_frequency)
|
226
213
|
tuner_2_fixed_center_frequency = partial(capture, top_block_cls=_tuner_2_fixed_center_frequency)
|
227
|
-
tuner_1_swept_center_frequency = partial(capture, top_block_cls=_tuner_1_swept_center_frequency)
|
214
|
+
tuner_1_swept_center_frequency = partial(capture, top_block_cls=_tuner_1_swept_center_frequency, max_noutput_items=1024)
|
@@ -17,35 +17,34 @@
|
|
17
17
|
#
|
18
18
|
|
19
19
|
from functools import partial
|
20
|
+
from typing import Callable
|
20
21
|
from dataclasses import dataclass
|
21
22
|
|
22
23
|
from gnuradio import gr
|
23
24
|
from gnuradio import blocks
|
24
|
-
from gnuradio import spectre
|
25
25
|
from gnuradio import analog
|
26
26
|
|
27
|
-
from spectre_core.capture_configs import Parameters,
|
27
|
+
from spectre_core.capture_configs import Parameters, PName
|
28
28
|
from spectre_core.config import get_batches_dir_path
|
29
|
-
from ._base import capture
|
29
|
+
from ._base import capture, spectre_top_block
|
30
30
|
|
31
31
|
|
32
|
-
class _cosine_signal_1(
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
|
32
|
+
class _cosine_signal_1(spectre_top_block):
|
33
|
+
def flowgraph(
|
34
|
+
self,
|
35
|
+
tag: str,
|
36
|
+
parameters: Parameters
|
37
|
+
) -> None:
|
38
|
+
# Inline imports
|
39
|
+
from gnuradio import spectre
|
37
40
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
frequency = parameters.get_parameter_value(PNames.FREQUENCY)
|
44
|
-
amplitude = parameters.get_parameter_value(PNames.AMPLITUDE)
|
41
|
+
# Unpack the capture config parameters
|
42
|
+
samp_rate = parameters.get_parameter_value(PName.SAMPLE_RATE)
|
43
|
+
batch_size = parameters.get_parameter_value(PName.BATCH_SIZE)
|
44
|
+
frequency = parameters.get_parameter_value(PName.FREQUENCY)
|
45
|
+
amplitude = parameters.get_parameter_value(PName.AMPLITUDE)
|
45
46
|
|
46
|
-
##################################################
|
47
47
|
# Blocks
|
48
|
-
##################################################
|
49
48
|
self.spectre_batched_file_sink_0 = spectre.batched_file_sink(get_batches_dir_path(),
|
50
49
|
tag,
|
51
50
|
batch_size,
|
@@ -76,21 +75,24 @@ class _cosine_signal_1(gr.top_block):
|
|
76
75
|
self.connect((self.blocks_throttle_0_1, 0), (self.blocks_float_to_complex_1, 1))
|
77
76
|
|
78
77
|
|
79
|
-
class _tagged_staircase(
|
80
|
-
def
|
81
|
-
|
82
|
-
|
83
|
-
|
78
|
+
class _tagged_staircase(spectre_top_block):
|
79
|
+
def flowgraph(
|
80
|
+
self,
|
81
|
+
tag: str,
|
82
|
+
parameters: Parameters
|
83
|
+
) -> None:
|
84
|
+
# Inline imports
|
85
|
+
from gnuradio import spectre
|
84
86
|
|
85
87
|
##################################################
|
86
88
|
# Unpack capture config
|
87
89
|
##################################################
|
88
|
-
step_increment = parameters.get_parameter_value(
|
89
|
-
samp_rate = parameters.get_parameter_value(
|
90
|
-
min_samples_per_step = parameters.get_parameter_value(
|
91
|
-
max_samples_per_step = parameters.get_parameter_value(
|
92
|
-
frequency_step = parameters.get_parameter_value(
|
93
|
-
batch_size = parameters.get_parameter_value(
|
90
|
+
step_increment = parameters.get_parameter_value(PName.STEP_INCREMENT)
|
91
|
+
samp_rate = parameters.get_parameter_value(PName.SAMPLE_RATE)
|
92
|
+
min_samples_per_step = parameters.get_parameter_value(PName.MIN_SAMPLES_PER_STEP)
|
93
|
+
max_samples_per_step = parameters.get_parameter_value(PName.MAX_SAMPLES_PER_STEP)
|
94
|
+
frequency_step = parameters.get_parameter_value(PName.FREQUENCY_STEP)
|
95
|
+
batch_size = parameters.get_parameter_value(PName.BATCH_SIZE)
|
94
96
|
|
95
97
|
##################################################
|
96
98
|
# Blocks
|
@@ -118,6 +120,6 @@ class _tagged_staircase(gr.top_block):
|
|
118
120
|
|
119
121
|
|
120
122
|
@dataclass(frozen=True)
|
121
|
-
class
|
122
|
-
cosine_signal_1
|
123
|
-
tagged_staircase = partial(capture, top_block_cls=_tagged_staircase)
|
123
|
+
class CaptureMethod:
|
124
|
+
cosine_signal_1 : Callable[[str, Parameters], None] = partial(capture, top_block_cls=_cosine_signal_1)
|
125
|
+
tagged_staircase: Callable[[str, Parameters], None] = partial(capture, top_block_cls=_tagged_staircase)
|
@@ -2,11 +2,13 @@
|
|
2
2
|
# This file is part of SPECTRE
|
3
3
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
4
|
|
5
|
+
"""Create and transform spectrogram data."""
|
6
|
+
|
5
7
|
from ._analytical import (
|
6
8
|
get_analytical_spectrogram, validate_analytically, TestResults
|
7
9
|
)
|
8
10
|
from ._spectrogram import (
|
9
|
-
Spectrogram, FrequencyCut, TimeCut,
|
11
|
+
Spectrogram, FrequencyCut, TimeCut, SpectrumUnit, TimeType
|
10
12
|
)
|
11
13
|
from ._transform import (
|
12
14
|
frequency_chop, time_chop, frequency_average, time_average,
|
@@ -15,7 +17,7 @@ from ._transform import (
|
|
15
17
|
|
16
18
|
__all__ = [
|
17
19
|
"get_analytical_spectrogram", "validate_analytically", "TestResults",
|
18
|
-
"Spectrogram", "FrequencyCut", "TimeCut", "
|
20
|
+
"Spectrogram", "FrequencyCut", "TimeCut", "SpectrumUnit", "frequency_chop",
|
19
21
|
"time_chop", "frequency_average","time_average", "join_spectrograms",
|
20
|
-
"
|
22
|
+
"TimeType"
|
21
23
|
]
|
@@ -1,96 +1,129 @@
|
|
1
|
+
|
1
2
|
# SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
|
2
3
|
# This file is part of SPECTRE
|
3
4
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
5
|
|
5
|
-
from typing import Callable
|
6
|
-
from dataclasses import dataclass
|
6
|
+
from typing import Callable, cast
|
7
|
+
from dataclasses import dataclass, field
|
7
8
|
|
8
9
|
import numpy as np
|
9
10
|
|
10
|
-
from spectre_core.capture_configs import CaptureConfig,
|
11
|
+
from spectre_core.capture_configs import CaptureConfig, PName
|
11
12
|
from spectre_core.exceptions import ModeNotFoundError
|
12
|
-
from ._spectrogram import Spectrogram
|
13
|
+
from ._spectrogram import Spectrogram, SpectrumUnit
|
13
14
|
from ._array_operations import is_close
|
14
15
|
|
15
16
|
@dataclass
|
16
17
|
class TestResults:
|
17
|
-
|
18
|
-
|
19
|
-
# Whether the frequencies array matches analytically
|
20
|
-
frequencies_validated: bool = False
|
21
|
-
# Maps each time to whether the corresponding spectrum matched analytically
|
22
|
-
spectrum_validated: dict[float, bool] = None
|
18
|
+
"""
|
19
|
+
Summarise the validation results when comparing two spectrograms.
|
23
20
|
|
21
|
+
:ivar times_validated: Whether the time arrays match.
|
22
|
+
:ivar frequencies_validated: Whether the frequency arrays match.
|
23
|
+
:ivar spectrum_validated: Maps the relative time of each spectrum to its match results.
|
24
|
+
"""
|
25
|
+
times_validated: bool = False
|
26
|
+
frequencies_validated: bool = False
|
27
|
+
spectrum_validated: dict[float, bool] = field(default_factory=dict)
|
24
28
|
|
25
29
|
@property
|
26
|
-
def num_validated_spectrums(
|
27
|
-
|
30
|
+
def num_validated_spectrums(
|
31
|
+
self
|
32
|
+
) -> int:
|
33
|
+
"""Returns the count of spectrums that successfully passed validation."""
|
28
34
|
return sum(is_validated for is_validated in self.spectrum_validated.values())
|
29
35
|
|
30
36
|
|
31
37
|
@property
|
32
|
-
def num_invalid_spectrums(
|
33
|
-
|
38
|
+
def num_invalid_spectrums(
|
39
|
+
self
|
40
|
+
) -> int:
|
41
|
+
"""Returns the count of spectrums that failed validation."""
|
34
42
|
return len(self.spectrum_validated) - self.num_validated_spectrums
|
35
43
|
|
36
|
-
|
37
|
-
def to_dict(
|
44
|
+
|
45
|
+
def to_dict(
|
46
|
+
self
|
47
|
+
) -> dict[str, bool | dict[float, bool]]:
|
48
|
+
"""Converts the instance into a serialisable dictionary."""
|
38
49
|
return {
|
39
|
-
"times_validated": self.times_validated,
|
50
|
+
"times_validated" : self.times_validated,
|
40
51
|
"frequencies_validated": self.frequencies_validated,
|
41
|
-
"spectrum_validated": self.spectrum_validated
|
52
|
+
"spectrum_validated" : self.spectrum_validated
|
42
53
|
}
|
43
54
|
|
44
55
|
|
45
56
|
class _AnalyticalFactory:
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
57
|
+
"""Factory for creating analytical spectrograms."""
|
58
|
+
def __init__(
|
59
|
+
self
|
60
|
+
) -> None:
|
61
|
+
"""Initialises an instance of the `_AnalyticalFactory` class."""
|
62
|
+
self._builders: dict[str, Callable[[int, CaptureConfig], Spectrogram]] = {
|
63
|
+
"cosine_signal_1" : self._cosine_signal_1,
|
64
|
+
"tagged_staircase": self._tagged_staircase
|
50
65
|
}
|
51
|
-
self._test_modes = list(self.builders.keys())
|
52
66
|
|
53
67
|
|
54
68
|
@property
|
55
|
-
def builders(
|
69
|
+
def builders(
|
70
|
+
self
|
71
|
+
) -> dict[str, Callable[[int, CaptureConfig], Spectrogram]]:
|
72
|
+
"""
|
73
|
+
Provides a mapping from each `Test` receiver mode to its corresponding builder method.
|
74
|
+
|
75
|
+
Each builder method generates the expected spectrograms for a session run in the associated mode.
|
76
|
+
"""
|
56
77
|
return self._builders
|
57
78
|
|
58
79
|
|
59
80
|
@property
|
60
|
-
def test_modes(
|
61
|
-
|
81
|
+
def test_modes(
|
82
|
+
self
|
83
|
+
) -> list[str]:
|
84
|
+
"""Returns the available modes for the `Test` receiver."""
|
85
|
+
return list(self.builders.keys())
|
62
86
|
|
63
87
|
|
64
|
-
def get_spectrogram(
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
88
|
+
def get_spectrogram(
|
89
|
+
self,
|
90
|
+
num_spectrums: int,
|
91
|
+
capture_config: CaptureConfig
|
92
|
+
) -> Spectrogram:
|
93
|
+
"""
|
94
|
+
Generates an analytical spectrogram based on the capture config for a `Test` receiver.
|
95
|
+
|
96
|
+
:param num_spectrums: The number of spectrums to include in the output spectrogram.
|
97
|
+
:param capture_config: The capture config used for `Test` receiver data capture.
|
98
|
+
:raises ValueError: Raised if the capture config is not associated with a `Test` receiver.
|
99
|
+
:raises ModeNotFoundError: Raised if the specified `Test` mode in the capture config lacks
|
100
|
+
a corresponding builder method.
|
101
|
+
:return: The expected spectrogram for running a session with the `Test` receiver in the specified mode.
|
72
102
|
"""
|
73
|
-
|
74
103
|
if capture_config.receiver_name != "test":
|
75
104
|
raise ValueError(f"Input capture config must correspond to the test receiver")
|
76
105
|
|
77
106
|
builder_method = self.builders.get(capture_config.receiver_mode)
|
78
107
|
if builder_method is None:
|
79
|
-
raise ModeNotFoundError(f"Test mode not found. Expected one of {self.test_modes}, but received {capture_config.receiver_mode}")
|
108
|
+
raise ModeNotFoundError(f"Test mode not found. Expected one of '{self.test_modes}', but received '{capture_config.receiver_mode}'")
|
80
109
|
return builder_method(num_spectrums,
|
81
110
|
capture_config)
|
82
111
|
|
83
112
|
|
84
|
-
def
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
113
|
+
def _cosine_signal_1(
|
114
|
+
self,
|
115
|
+
num_spectrums: int,
|
116
|
+
capture_config: CaptureConfig
|
117
|
+
) -> Spectrogram:
|
118
|
+
"""Creates the expected spectrogram for the `Test` receiver operating in the mode `cosine-signal-1`."""
|
119
|
+
# Extract necessary parameters from the capture config.
|
120
|
+
window_size = cast(int, capture_config.get_parameter_value(PName.WINDOW_SIZE))
|
121
|
+
sample_rate = cast(int, capture_config.get_parameter_value(PName.SAMPLE_RATE))
|
122
|
+
frequency = cast(int, capture_config.get_parameter_value(PName.FREQUENCY))
|
123
|
+
window_hop = cast(int, capture_config.get_parameter_value(PName.WINDOW_HOP))
|
124
|
+
amplitude = cast(float, capture_config.get_parameter_value(PName.AMPLITUDE))
|
125
|
+
center_frequency = cast(float, capture_config.get_parameter_value(PName.CENTER_FREQUENCY))
|
126
|
+
|
94
127
|
# Calculate derived parameters a (sampling rate ratio) and p (sampled periods).
|
95
128
|
a = int(sample_rate / frequency)
|
96
129
|
p = int(window_size / a)
|
@@ -119,18 +152,21 @@ class _AnalyticalFactory:
|
|
119
152
|
times,
|
120
153
|
frequencies,
|
121
154
|
'analytically-derived-spectrogram',
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
def
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
155
|
+
SpectrumUnit.AMPLITUDE)
|
156
|
+
|
157
|
+
|
158
|
+
def _tagged_staircase(
|
159
|
+
self,
|
160
|
+
num_spectrums: int,
|
161
|
+
capture_config: CaptureConfig
|
162
|
+
) -> Spectrogram:
|
163
|
+
"""Creates the expected spectrogram for the `Test` receiver operating in the mode `tagged-staircase`."""
|
164
|
+
# Extract necessary parameters from the capture config.
|
165
|
+
window_size = cast(int, capture_config.get_parameter_value(PName.WINDOW_SIZE))
|
166
|
+
min_samples_per_step = cast(int, capture_config.get_parameter_value(PName.MIN_SAMPLES_PER_STEP))
|
167
|
+
max_samples_per_step = cast(int, capture_config.get_parameter_value(PName.MAX_SAMPLES_PER_STEP))
|
168
|
+
step_increment = cast(int, capture_config.get_parameter_value(PName.STEP_INCREMENT))
|
169
|
+
samp_rate = cast(int, capture_config.get_parameter_value(PName.SAMPLE_RATE))
|
134
170
|
|
135
171
|
# Calculate step sizes and derived parameters.
|
136
172
|
num_samples_per_step = np.arange(min_samples_per_step, max_samples_per_step + 1, step_increment)
|
@@ -156,7 +192,7 @@ class _AnalyticalFactory:
|
|
156
192
|
|
157
193
|
# Compute the frequency array
|
158
194
|
baseband_frequencies = np.fft.fftshift(np.fft.fftfreq(window_size, sampling_interval))
|
159
|
-
frequencies = np.empty((window_size * num_steps))
|
195
|
+
frequencies = np.empty((window_size * num_steps), dtype=np.float32)
|
160
196
|
for i in range(num_steps):
|
161
197
|
lower_bound = i * window_size
|
162
198
|
upper_bound = (i + 1) * window_size
|
@@ -167,25 +203,44 @@ class _AnalyticalFactory:
|
|
167
203
|
times,
|
168
204
|
frequencies,
|
169
205
|
'analytically-derived-spectrogram',
|
170
|
-
|
206
|
+
SpectrumUnit.AMPLITUDE)
|
171
207
|
|
172
208
|
|
173
|
-
def get_analytical_spectrogram(
|
174
|
-
|
209
|
+
def get_analytical_spectrogram(
|
210
|
+
num_spectrums: int,
|
211
|
+
capture_config: CaptureConfig
|
212
|
+
) -> Spectrogram:
|
213
|
+
"""Each mode of the `Test` receiver generates a known synthetic signal. Based on this, we can
|
214
|
+
derive an analytical solution that predicts the expected spectrogram for a session in that mode.
|
175
215
|
|
216
|
+
This function constructs the analytical spectrogram using the capture config for a `Test`
|
217
|
+
receiver operating in a specific mode.
|
218
|
+
|
219
|
+
:param num_spectrums: The number of spectrums in the output spectrogram.
|
220
|
+
:param capture_config: The capture config used for data capture.
|
221
|
+
:return: The expected, analytically derived spectrogram for the specified mode of the `Test` receiver.
|
222
|
+
"""
|
176
223
|
factory = _AnalyticalFactory()
|
177
224
|
return factory.get_spectrogram(num_spectrums,
|
178
225
|
capture_config)
|
179
226
|
|
180
227
|
|
181
|
-
def validate_analytically(
|
182
|
-
|
183
|
-
|
228
|
+
def validate_analytically(
|
229
|
+
spectrogram: Spectrogram,
|
230
|
+
capture_config: CaptureConfig,
|
231
|
+
absolute_tolerance: float
|
232
|
+
) -> TestResults:
|
233
|
+
"""Validate a spectrogram generated during sessions with a `Test` receiver operating
|
234
|
+
in a particular mode.
|
184
235
|
|
236
|
+
:param spectrogram: The spectrogram to be validated.
|
237
|
+
:param capture_config: The capture config used for data capture.
|
238
|
+
:param absolute_tolerance: Tolerance level for numerical comparisons.
|
239
|
+
:return: A `TestResults` object summarising the validation outcome.
|
240
|
+
"""
|
185
241
|
analytical_spectrogram = get_analytical_spectrogram(spectrogram.num_times,
|
186
242
|
capture_config)
|
187
243
|
|
188
|
-
|
189
244
|
test_results = TestResults()
|
190
245
|
|
191
246
|
test_results.times_validated = bool(is_close(analytical_spectrogram.times,
|