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
@@ -0,0 +1,67 @@
|
|
1
|
+
# SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
|
2
|
+
# This file is part of SPECTRE
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
|
+
|
5
|
+
from dataclasses import dataclass
|
6
|
+
|
7
|
+
from ._receiver_names import ReceiverName
|
8
|
+
from .gr._rspduo import CaptureMethod
|
9
|
+
from .._spec_names import SpecName
|
10
|
+
from ._sdrplay_receiver import SDRPlayReceiver
|
11
|
+
from .._register import register_receiver
|
12
|
+
|
13
|
+
|
14
|
+
@dataclass
|
15
|
+
class Mode:
|
16
|
+
"""An operating mode for the `RSPduo` receiver."""
|
17
|
+
TUNER_1_FIXED_CENTER_FREQUENCY = f"tuner_1_fixed_center_frequency"
|
18
|
+
TUNER_2_FIXED_CENTER_FREQUENCY = f"tuner_2_fixed_center_frequency"
|
19
|
+
TUNER_1_SWEPT_CENTER_FREQUENCY = f"tuner_1_swept_center_frequency"
|
20
|
+
|
21
|
+
|
22
|
+
@register_receiver(ReceiverName.RSPDUO)
|
23
|
+
class RSPduo(SDRPlayReceiver):
|
24
|
+
"""Receiver implementation for the SDRPlay RSPduo (https://www.sdrplay.com/rspduo/)"""
|
25
|
+
def _add_specs(self) -> None:
|
26
|
+
self.add_spec( SpecName.SAMPLE_RATE_LOWER_BOUND, 200e3 )
|
27
|
+
self.add_spec( SpecName.SAMPLE_RATE_UPPER_BOUND, 10e6 )
|
28
|
+
self.add_spec( SpecName.FREQUENCY_LOWER_BOUND , 1e3 )
|
29
|
+
self.add_spec( SpecName.FREQUENCY_UPPER_BOUND , 2e9 )
|
30
|
+
self.add_spec( SpecName.IF_GAIN_UPPER_BOUND , -20 )
|
31
|
+
self.add_spec( SpecName.RF_GAIN_UPPER_BOUND , 0 )
|
32
|
+
self.add_spec( SpecName.API_RETUNING_LATENCY , 50 * 1e-3 )
|
33
|
+
self.add_spec( SpecName.BANDWIDTH_OPTIONS,
|
34
|
+
[200000, 300000, 600000, 1536000, 5000000, 6000000, 7000000, 8000000])
|
35
|
+
|
36
|
+
|
37
|
+
def _add_capture_methods(
|
38
|
+
self
|
39
|
+
) -> None:
|
40
|
+
self.add_capture_method(Mode.TUNER_1_FIXED_CENTER_FREQUENCY,
|
41
|
+
CaptureMethod.tuner_1_fixed_center_frequency)
|
42
|
+
self.add_capture_method(Mode.TUNER_2_FIXED_CENTER_FREQUENCY,
|
43
|
+
CaptureMethod.tuner_2_fixed_center_frequency)
|
44
|
+
self.add_capture_method(Mode.TUNER_1_SWEPT_CENTER_FREQUENCY,
|
45
|
+
CaptureMethod.tuner_1_swept_center_frequency)
|
46
|
+
|
47
|
+
|
48
|
+
def _add_capture_templates(
|
49
|
+
self
|
50
|
+
) -> None:
|
51
|
+
self.add_capture_template(Mode.TUNER_1_FIXED_CENTER_FREQUENCY,
|
52
|
+
self._get_capture_template_fixed_center_frequency())
|
53
|
+
self.add_capture_template(Mode.TUNER_2_FIXED_CENTER_FREQUENCY,
|
54
|
+
self._get_capture_template_fixed_center_frequency())
|
55
|
+
self.add_capture_template(Mode.TUNER_1_SWEPT_CENTER_FREQUENCY,
|
56
|
+
self._get_capture_template_swept_center_frequency())
|
57
|
+
|
58
|
+
|
59
|
+
def _add_pvalidators(
|
60
|
+
self
|
61
|
+
) -> None:
|
62
|
+
self.add_pvalidator(Mode.TUNER_1_FIXED_CENTER_FREQUENCY,
|
63
|
+
self._get_pvalidator_fixed_center_frequency())
|
64
|
+
self.add_pvalidator(Mode.TUNER_2_FIXED_CENTER_FREQUENCY,
|
65
|
+
self._get_pvalidator_fixed_center_frequency())
|
66
|
+
self.add_pvalidator(Mode.TUNER_1_SWEPT_CENTER_FREQUENCY,
|
67
|
+
self._get_pvalidator_swept_center_frequency())
|
@@ -0,0 +1,190 @@
|
|
1
|
+
# SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
|
2
|
+
# This file is part of SPECTRE
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
|
+
|
5
|
+
from typing import Callable, overload
|
6
|
+
|
7
|
+
from spectre_core.capture_configs import (
|
8
|
+
CaptureTemplate, CaptureMode, Parameters, Bound, PName,
|
9
|
+
get_base_capture_template, get_base_ptemplate, OneOf,
|
10
|
+
validate_fixed_center_frequency, validate_swept_center_frequency
|
11
|
+
)
|
12
|
+
from .._base import BaseReceiver
|
13
|
+
from .._spec_names import SpecName
|
14
|
+
|
15
|
+
class SDRPlayReceiver(BaseReceiver):
|
16
|
+
"""An abstract base class for SDRPlay receivers.
|
17
|
+
|
18
|
+
Includes ready-to-go pvalidators and capture templates which are shared by all subclasses.
|
19
|
+
Each subclasses must define the following hardware specifications:
|
20
|
+
|
21
|
+
.. code-block:: python
|
22
|
+
def _add_specs(self) -> None:
|
23
|
+
self.add_spec( SpecName.SAMPLE_RATE_LOWER_BOUND, <TBD> )
|
24
|
+
self.add_spec( SpecName.SAMPLE_RATE_UPPER_BOUND, <TBD> )
|
25
|
+
self.add_spec( SpecName.FREQUENCY_LOWER_BOUND , <TBD> )
|
26
|
+
self.add_spec( SpecName.FREQUENCY_UPPER_BOUND , <TBD> )
|
27
|
+
self.add_spec( SpecName.IF_GAIN_UPPER_BOUND , <TBD> )
|
28
|
+
self.add_spec( SpecName.RF_GAIN_UPPER_BOUND , <TBD> )
|
29
|
+
self.add_spec( SpecName.API_RETUNING_LATENCY , <TBD> )
|
30
|
+
self.add_spec( SpecName.BANDWIDTH_OPTIONS , <TBD> )
|
31
|
+
"""
|
32
|
+
def _get_pvalidator_fixed_center_frequency(
|
33
|
+
self
|
34
|
+
) -> Callable[[Parameters], None]:
|
35
|
+
def pvalidator(parameters: Parameters):
|
36
|
+
validate_fixed_center_frequency(parameters)
|
37
|
+
return pvalidator
|
38
|
+
|
39
|
+
|
40
|
+
def _get_pvalidator_swept_center_frequency(
|
41
|
+
self
|
42
|
+
) -> Callable[[Parameters], None]:
|
43
|
+
def pvalidator(parameters: Parameters):
|
44
|
+
validate_swept_center_frequency(parameters,
|
45
|
+
self.get_spec(SpecName.API_RETUNING_LATENCY))
|
46
|
+
return pvalidator
|
47
|
+
|
48
|
+
|
49
|
+
def _get_capture_template_fixed_center_frequency(
|
50
|
+
self
|
51
|
+
) -> CaptureTemplate:
|
52
|
+
|
53
|
+
capture_template = get_base_capture_template( CaptureMode.FIXED_CENTER_FREQUENCY )
|
54
|
+
capture_template.add_ptemplate( get_base_ptemplate(PName.BANDWIDTH) )
|
55
|
+
capture_template.add_ptemplate( get_base_ptemplate(PName.IF_GAIN) )
|
56
|
+
capture_template.add_ptemplate( get_base_ptemplate(PName.RF_GAIN) )
|
57
|
+
|
58
|
+
capture_template.set_defaults(
|
59
|
+
(PName.BATCH_SIZE, 3.0),
|
60
|
+
(PName.CENTER_FREQUENCY, 95800000),
|
61
|
+
(PName.SAMPLE_RATE, 600000),
|
62
|
+
(PName.BANDWIDTH, 600000),
|
63
|
+
(PName.WINDOW_HOP, 512),
|
64
|
+
(PName.WINDOW_SIZE, 1024),
|
65
|
+
(PName.WINDOW_TYPE, "blackman"),
|
66
|
+
(PName.RF_GAIN, -30),
|
67
|
+
(PName.IF_GAIN, -30)
|
68
|
+
)
|
69
|
+
|
70
|
+
capture_template.add_pconstraint(
|
71
|
+
PName.CENTER_FREQUENCY,
|
72
|
+
[
|
73
|
+
Bound(
|
74
|
+
lower_bound=self.get_spec(SpecName.FREQUENCY_LOWER_BOUND),
|
75
|
+
upper_bound=self.get_spec(SpecName.FREQUENCY_UPPER_BOUND)
|
76
|
+
)
|
77
|
+
]
|
78
|
+
)
|
79
|
+
capture_template.add_pconstraint(
|
80
|
+
PName.SAMPLE_RATE,
|
81
|
+
[
|
82
|
+
Bound(
|
83
|
+
lower_bound=self.get_spec(SpecName.SAMPLE_RATE_LOWER_BOUND),
|
84
|
+
upper_bound=self.get_spec(SpecName.SAMPLE_RATE_UPPER_BOUND)
|
85
|
+
)
|
86
|
+
]
|
87
|
+
)
|
88
|
+
capture_template.add_pconstraint(
|
89
|
+
PName.BANDWIDTH,
|
90
|
+
[
|
91
|
+
OneOf(
|
92
|
+
self.get_spec( SpecName.BANDWIDTH_OPTIONS )
|
93
|
+
)
|
94
|
+
]
|
95
|
+
)
|
96
|
+
capture_template.add_pconstraint(
|
97
|
+
PName.IF_GAIN,
|
98
|
+
[
|
99
|
+
Bound(
|
100
|
+
upper_bound=self.get_spec(SpecName.IF_GAIN_UPPER_BOUND)
|
101
|
+
)
|
102
|
+
]
|
103
|
+
)
|
104
|
+
capture_template.add_pconstraint(
|
105
|
+
PName.RF_GAIN,
|
106
|
+
[
|
107
|
+
Bound(
|
108
|
+
upper_bound=self.get_spec(SpecName.RF_GAIN_UPPER_BOUND)
|
109
|
+
)
|
110
|
+
]
|
111
|
+
)
|
112
|
+
return capture_template
|
113
|
+
|
114
|
+
|
115
|
+
def _get_capture_template_swept_center_frequency(
|
116
|
+
self
|
117
|
+
) -> CaptureTemplate:
|
118
|
+
|
119
|
+
capture_template = get_base_capture_template( CaptureMode.SWEPT_CENTER_FREQUENCY )
|
120
|
+
capture_template.add_ptemplate( get_base_ptemplate(PName.BANDWIDTH) )
|
121
|
+
capture_template.add_ptemplate( get_base_ptemplate(PName.IF_GAIN) )
|
122
|
+
capture_template.add_ptemplate( get_base_ptemplate(PName.RF_GAIN) )
|
123
|
+
|
124
|
+
capture_template.set_defaults(
|
125
|
+
(PName.BATCH_SIZE, 4.0),
|
126
|
+
(PName.MIN_FREQUENCY, 95000000),
|
127
|
+
(PName.MAX_FREQUENCY, 100000000),
|
128
|
+
(PName.SAMPLES_PER_STEP, 80000),
|
129
|
+
(PName.FREQUENCY_STEP, 1536000),
|
130
|
+
(PName.SAMPLE_RATE, 1536000),
|
131
|
+
(PName.BANDWIDTH, 1536000),
|
132
|
+
(PName.WINDOW_HOP, 512),
|
133
|
+
(PName.WINDOW_SIZE, 1024),
|
134
|
+
(PName.WINDOW_TYPE, "blackman"),
|
135
|
+
(PName.RF_GAIN, -30),
|
136
|
+
(PName.IF_GAIN, -30)
|
137
|
+
)
|
138
|
+
|
139
|
+
capture_template.add_pconstraint(
|
140
|
+
PName.MIN_FREQUENCY,
|
141
|
+
[
|
142
|
+
Bound(
|
143
|
+
lower_bound=self.get_spec(SpecName.FREQUENCY_LOWER_BOUND),
|
144
|
+
upper_bound=self.get_spec(SpecName.FREQUENCY_UPPER_BOUND)
|
145
|
+
)
|
146
|
+
]
|
147
|
+
)
|
148
|
+
capture_template.add_pconstraint(
|
149
|
+
PName.MAX_FREQUENCY,
|
150
|
+
[
|
151
|
+
Bound(
|
152
|
+
lower_bound=self.get_spec(SpecName.FREQUENCY_LOWER_BOUND),
|
153
|
+
upper_bound=self.get_spec(SpecName.FREQUENCY_UPPER_BOUND)
|
154
|
+
)
|
155
|
+
]
|
156
|
+
)
|
157
|
+
capture_template.add_pconstraint(
|
158
|
+
PName.SAMPLE_RATE,
|
159
|
+
[
|
160
|
+
Bound(
|
161
|
+
lower_bound=self.get_spec(SpecName.SAMPLE_RATE_LOWER_BOUND),
|
162
|
+
upper_bound=self.get_spec(SpecName.SAMPLE_RATE_UPPER_BOUND)
|
163
|
+
)
|
164
|
+
]
|
165
|
+
)
|
166
|
+
capture_template.add_pconstraint(
|
167
|
+
PName.BANDWIDTH,
|
168
|
+
[
|
169
|
+
OneOf(
|
170
|
+
self.get_spec( SpecName.BANDWIDTH_OPTIONS )
|
171
|
+
)
|
172
|
+
]
|
173
|
+
)
|
174
|
+
capture_template.add_pconstraint(
|
175
|
+
PName.IF_GAIN,
|
176
|
+
[
|
177
|
+
Bound(
|
178
|
+
upper_bound=self.get_spec(SpecName.IF_GAIN_UPPER_BOUND)
|
179
|
+
)
|
180
|
+
]
|
181
|
+
)
|
182
|
+
capture_template.add_pconstraint(
|
183
|
+
PName.RF_GAIN,
|
184
|
+
[
|
185
|
+
Bound(
|
186
|
+
upper_bound=self.get_spec(SpecName.RF_GAIN_UPPER_BOUND)
|
187
|
+
)
|
188
|
+
]
|
189
|
+
)
|
190
|
+
return capture_template
|
@@ -0,0 +1,218 @@
|
|
1
|
+
# SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
|
2
|
+
# This file is part of SPECTRE
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
|
+
|
5
|
+
from dataclasses import dataclass
|
6
|
+
from typing import Callable, cast
|
7
|
+
|
8
|
+
from spectre_core.capture_configs import (
|
9
|
+
CaptureTemplate, CaptureMode, Parameters, Bound, PName,
|
10
|
+
get_base_capture_template, make_base_capture_template, get_base_ptemplate,
|
11
|
+
validate_window
|
12
|
+
)
|
13
|
+
from .gr._test import CaptureMethod
|
14
|
+
from ._receiver_names import ReceiverName
|
15
|
+
from .._spec_names import SpecName
|
16
|
+
from .._base import BaseReceiver
|
17
|
+
from .._register import register_receiver
|
18
|
+
|
19
|
+
|
20
|
+
@dataclass(frozen=True)
|
21
|
+
class Mode:
|
22
|
+
"""An operating mode for the `Test` receiver."""
|
23
|
+
COSINE_SIGNAL_1 = "cosine_signal_1"
|
24
|
+
TAGGED_STAIRCASE = "tagged_staircase"
|
25
|
+
|
26
|
+
|
27
|
+
@register_receiver(ReceiverName.TEST)
|
28
|
+
class Test(BaseReceiver):
|
29
|
+
"""An entirely software-defined receiver, which generates synthetic signals."""
|
30
|
+
def _add_specs(
|
31
|
+
self
|
32
|
+
) -> None:
|
33
|
+
self.add_spec( SpecName.SAMPLE_RATE_LOWER_BOUND, 64000 )
|
34
|
+
self.add_spec( SpecName.SAMPLE_RATE_UPPER_BOUND, 640000 )
|
35
|
+
self.add_spec( SpecName.FREQUENCY_LOWER_BOUND , 16000 )
|
36
|
+
self.add_spec( SpecName.FREQUENCY_UPPER_BOUND , 160000 )
|
37
|
+
|
38
|
+
|
39
|
+
def _add_capture_methods(
|
40
|
+
self
|
41
|
+
) -> None:
|
42
|
+
self.add_capture_method(Mode.COSINE_SIGNAL_1 ,
|
43
|
+
CaptureMethod.cosine_signal_1 )
|
44
|
+
self.add_capture_method(Mode.TAGGED_STAIRCASE,
|
45
|
+
CaptureMethod.tagged_staircase)
|
46
|
+
|
47
|
+
|
48
|
+
def _add_pvalidators(
|
49
|
+
self
|
50
|
+
) -> None:
|
51
|
+
self.add_pvalidator(Mode.COSINE_SIGNAL_1 ,
|
52
|
+
self.__get_pvalidator_cosine_signal_1() )
|
53
|
+
self.add_pvalidator(Mode.TAGGED_STAIRCASE,
|
54
|
+
self.__get_pvalidator_tagged_staircase() )
|
55
|
+
|
56
|
+
|
57
|
+
def _add_capture_templates(
|
58
|
+
self
|
59
|
+
) -> None:
|
60
|
+
self.add_capture_template(Mode.COSINE_SIGNAL_1 ,
|
61
|
+
self.__get_capture_template_cosine_signal_1() )
|
62
|
+
self.add_capture_template(Mode.TAGGED_STAIRCASE,
|
63
|
+
self.__get_capture_template_tagged_staircase() )
|
64
|
+
|
65
|
+
|
66
|
+
def __get_capture_template_cosine_signal_1(
|
67
|
+
self
|
68
|
+
) -> CaptureTemplate:
|
69
|
+
capture_template = get_base_capture_template( CaptureMode.FIXED_CENTER_FREQUENCY )
|
70
|
+
capture_template.add_ptemplate( get_base_ptemplate(PName.AMPLITUDE) )
|
71
|
+
capture_template.add_ptemplate( get_base_ptemplate(PName.FREQUENCY) )
|
72
|
+
|
73
|
+
capture_template.set_defaults(
|
74
|
+
(PName.BATCH_SIZE, 3.0),
|
75
|
+
(PName.CENTER_FREQUENCY, 16000),
|
76
|
+
(PName.AMPLITUDE, 2.0),
|
77
|
+
(PName.FREQUENCY, 32000),
|
78
|
+
(PName.SAMPLE_RATE, 128000),
|
79
|
+
(PName.WINDOW_HOP, 512),
|
80
|
+
(PName.WINDOW_SIZE, 512),
|
81
|
+
(PName.WINDOW_TYPE, "boxcar")
|
82
|
+
)
|
83
|
+
|
84
|
+
capture_template.enforce_defaults(
|
85
|
+
PName.TIME_RESOLUTION,
|
86
|
+
PName.TIME_RANGE,
|
87
|
+
PName.FREQUENCY_RESOLUTION,
|
88
|
+
PName.WINDOW_TYPE
|
89
|
+
)
|
90
|
+
|
91
|
+
|
92
|
+
capture_template.add_pconstraint(
|
93
|
+
PName.SAMPLE_RATE,
|
94
|
+
[
|
95
|
+
Bound(
|
96
|
+
lower_bound=self.get_spec(SpecName.SAMPLE_RATE_LOWER_BOUND),
|
97
|
+
upper_bound=self.get_spec(SpecName.SAMPLE_RATE_UPPER_BOUND)
|
98
|
+
)
|
99
|
+
]
|
100
|
+
)
|
101
|
+
capture_template.add_pconstraint(
|
102
|
+
PName.FREQUENCY,
|
103
|
+
[
|
104
|
+
Bound(
|
105
|
+
lower_bound=self.get_spec(SpecName.FREQUENCY_LOWER_BOUND),
|
106
|
+
upper_bound=self.get_spec(SpecName.FREQUENCY_UPPER_BOUND)
|
107
|
+
)
|
108
|
+
]
|
109
|
+
)
|
110
|
+
return capture_template
|
111
|
+
|
112
|
+
|
113
|
+
def __get_capture_template_tagged_staircase(
|
114
|
+
self
|
115
|
+
) -> CaptureTemplate:
|
116
|
+
capture_template = make_base_capture_template(
|
117
|
+
PName.TIME_RESOLUTION,
|
118
|
+
PName.FREQUENCY_RESOLUTION,
|
119
|
+
PName.TIME_RANGE,
|
120
|
+
PName.SAMPLE_RATE,
|
121
|
+
PName.BATCH_SIZE,
|
122
|
+
PName.WINDOW_TYPE,
|
123
|
+
PName.WINDOW_HOP,
|
124
|
+
PName.WINDOW_SIZE,
|
125
|
+
PName.EVENT_HANDLER_KEY,
|
126
|
+
PName.BATCH_KEY,
|
127
|
+
PName.WATCH_EXTENSION,
|
128
|
+
PName.MIN_SAMPLES_PER_STEP,
|
129
|
+
PName.MAX_SAMPLES_PER_STEP,
|
130
|
+
PName.FREQUENCY_STEP,
|
131
|
+
PName.STEP_INCREMENT,
|
132
|
+
PName.OBS_ALT,
|
133
|
+
PName.OBS_LAT,
|
134
|
+
PName.OBS_LON,
|
135
|
+
PName.OBJECT,
|
136
|
+
PName.ORIGIN,
|
137
|
+
PName.TELESCOPE,
|
138
|
+
PName.INSTRUMENT
|
139
|
+
)
|
140
|
+
|
141
|
+
capture_template.set_defaults(
|
142
|
+
(PName.BATCH_SIZE, 3.0),
|
143
|
+
(PName.FREQUENCY_STEP, 128000),
|
144
|
+
(PName.MAX_SAMPLES_PER_STEP, 5000),
|
145
|
+
(PName.MIN_SAMPLES_PER_STEP, 4000),
|
146
|
+
(PName.SAMPLE_RATE, 128000),
|
147
|
+
(PName.STEP_INCREMENT, 200),
|
148
|
+
(PName.WINDOW_HOP, 512),
|
149
|
+
(PName.WINDOW_SIZE, 512),
|
150
|
+
(PName.WINDOW_TYPE, "boxcar"),
|
151
|
+
(PName.EVENT_HANDLER_KEY, "swept_center_frequency"),
|
152
|
+
(PName.BATCH_KEY, "iq_stream"),
|
153
|
+
(PName.WATCH_EXTENSION, "bin")
|
154
|
+
)
|
155
|
+
|
156
|
+
capture_template.enforce_defaults(
|
157
|
+
PName.TIME_RESOLUTION,
|
158
|
+
PName.TIME_RANGE,
|
159
|
+
PName.FREQUENCY_RESOLUTION,
|
160
|
+
PName.WINDOW_TYPE,
|
161
|
+
PName.EVENT_HANDLER_KEY,
|
162
|
+
PName.BATCH_KEY,
|
163
|
+
PName.WATCH_EXTENSION
|
164
|
+
)
|
165
|
+
|
166
|
+
return capture_template
|
167
|
+
|
168
|
+
|
169
|
+
def __get_pvalidator_cosine_signal_1(
|
170
|
+
self
|
171
|
+
) -> Callable[[Parameters], None]:
|
172
|
+
def pvalidator(parameters: Parameters) -> None:
|
173
|
+
validate_window(parameters)
|
174
|
+
|
175
|
+
sample_rate = cast(int, parameters.get_parameter_value(PName.SAMPLE_RATE))
|
176
|
+
window_size = cast(int, parameters.get_parameter_value(PName.WINDOW_SIZE))
|
177
|
+
frequency = cast(float, parameters.get_parameter_value(PName.FREQUENCY))
|
178
|
+
|
179
|
+
# check that the sample rate is an integer multiple of the underlying signal frequency
|
180
|
+
if sample_rate % frequency != 0:
|
181
|
+
raise ValueError("The sampling rate must be some integer multiple of frequency")
|
182
|
+
|
183
|
+
a = sample_rate/frequency
|
184
|
+
if a < 2:
|
185
|
+
raise ValueError((f"The ratio of sampling rate over frequency must be greater than two. "
|
186
|
+
f"Got {a}"))
|
187
|
+
|
188
|
+
# analytical requirement
|
189
|
+
# if p is the number of sampled cycles, we can find that p = window_size / a
|
190
|
+
# the number of sampled cycles must be a positive natural number.
|
191
|
+
p = window_size / a
|
192
|
+
if window_size % a != 0:
|
193
|
+
raise ValueError((f"The number of sampled cycles must be a positive natural number. "
|
194
|
+
f"Computed that p={p}"))
|
195
|
+
return pvalidator
|
196
|
+
|
197
|
+
|
198
|
+
def __get_pvalidator_tagged_staircase(
|
199
|
+
self
|
200
|
+
) -> Callable[[Parameters], None]:
|
201
|
+
def pvalidator(parameters: Parameters) -> None:
|
202
|
+
validate_window(parameters)
|
203
|
+
|
204
|
+
freq_step = cast(float, parameters.get_parameter_value(PName.FREQUENCY_STEP))
|
205
|
+
sample_rate = cast(int, parameters.get_parameter_value(PName.SAMPLE_RATE))
|
206
|
+
min_samples_per_step = cast(int, parameters.get_parameter_value(PName.MIN_SAMPLES_PER_STEP))
|
207
|
+
max_samples_per_step = cast(int, parameters.get_parameter_value(PName.MAX_SAMPLES_PER_STEP))
|
208
|
+
|
209
|
+
if freq_step != sample_rate:
|
210
|
+
raise ValueError(f"The frequency step must be equal to the sampling rate")
|
211
|
+
|
212
|
+
|
213
|
+
if min_samples_per_step > max_samples_per_step:
|
214
|
+
raise ValueError((f"Minimum samples per step cannot be greater than the maximum samples per step. "
|
215
|
+
f"Got {min_samples_per_step}, which is greater than {max_samples_per_step}"))
|
216
|
+
|
217
|
+
return pvalidator
|
218
|
+
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
|
2
|
+
# This file is part of SPECTRE
|
3
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
4
|
+
|
5
|
+
import sys
|
6
|
+
import signal
|
7
|
+
from typing import Type, TypeVar
|
8
|
+
|
9
|
+
from gnuradio import gr
|
10
|
+
|
11
|
+
from spectre_core.capture_configs import Parameters
|
12
|
+
|
13
|
+
class spectre_top_block(gr.top_block):
|
14
|
+
"""A thin wrapper around GNU Radio's `gr.top_block` class.
|
15
|
+
|
16
|
+
Propagate through the input tag and parameters to `flowgraph` in the constructor,
|
17
|
+
to make the GNU Radio flow graphs easily configurable.
|
18
|
+
"""
|
19
|
+
def flowgraph(
|
20
|
+
self,
|
21
|
+
tag: str,
|
22
|
+
parameters: Parameters
|
23
|
+
) -> None:
|
24
|
+
"""Define the flowgraph for the block.
|
25
|
+
|
26
|
+
This method uses inline imports to allow the `spectre_core.receivers`
|
27
|
+
module to work without requiring all Out-Of-Tree (OOT) modules to be installed.
|
28
|
+
Only import the necessary OOT modules when implementing this method.
|
29
|
+
"""
|
30
|
+
raise NotImplementedError()
|
31
|
+
|
32
|
+
|
33
|
+
def __init__(
|
34
|
+
self,
|
35
|
+
tag: str,
|
36
|
+
parameters: Parameters
|
37
|
+
) -> None:
|
38
|
+
"""Create an instance of `spectre_top_block`.
|
39
|
+
|
40
|
+
:param tag: The capture config tag.
|
41
|
+
:param parameters: The capture config parameters
|
42
|
+
"""
|
43
|
+
gr.top_block.__init__(self)
|
44
|
+
self.flowgraph(tag, parameters)
|
45
|
+
|
46
|
+
|
47
|
+
def capture(
|
48
|
+
tag: str,
|
49
|
+
parameters: Parameters,
|
50
|
+
top_block_cls: Type[spectre_top_block],
|
51
|
+
max_noutput_items: int = 10000000
|
52
|
+
) -> None:
|
53
|
+
"""
|
54
|
+
Run a GNU Radio flowgraph with the given number of output items.
|
55
|
+
|
56
|
+
Typically, this should be used with `partial` from `functools` to create
|
57
|
+
a capture method. For example,
|
58
|
+
|
59
|
+
.. code-block:: python
|
60
|
+
capture_method = partial(capture, top_block_cls=<your GNU Radio top block>)
|
61
|
+
|
62
|
+
:param tag: The capture config tag
|
63
|
+
:param parameters: The parameters stored in the capture config
|
64
|
+
:param top_block_cls: The subclass of `spectre_top_block`, defining the GNURadio flowgraph.
|
65
|
+
:param max_noutput_items: The number of max `noutput_items` in the flowgraph. This controls
|
66
|
+
the maximum number of output items any block will handle during a call to work. Defaults to 10000000
|
67
|
+
(which has been chosen as per GNU Radio source code).
|
68
|
+
"""
|
69
|
+
tb = top_block_cls(tag,
|
70
|
+
parameters)
|
71
|
+
|
72
|
+
def sig_handler(sig=None, frame=None):
|
73
|
+
tb.stop()
|
74
|
+
tb.wait()
|
75
|
+
sys.exit(0)
|
76
|
+
|
77
|
+
signal.signal(signal.SIGINT, sig_handler)
|
78
|
+
signal.signal(signal.SIGTERM, sig_handler)
|
79
|
+
|
80
|
+
tb.run(max_noutput_items)
|
@@ -19,34 +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
|
-
class _fixed_center_frequency(
|
31
|
-
def
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
24
|
+
from ._base import capture, spectre_top_block
|
25
|
+
|
26
|
+
class _fixed_center_frequency(spectre_top_block):
|
27
|
+
def flowgraph(
|
28
|
+
self,
|
29
|
+
tag: str,
|
30
|
+
parameters: Parameters
|
31
|
+
) -> None:
|
32
|
+
# OOT Module inline imports
|
33
|
+
from gnuradio import spectre
|
34
|
+
from gnuradio import sdrplay3
|
35
|
+
|
36
|
+
# Unpack the capture config parameters
|
37
|
+
sample_rate = parameters.get_parameter_value(PName.SAMPLE_RATE)
|
38
|
+
batch_size = parameters.get_parameter_value(PName.BATCH_SIZE)
|
39
|
+
center_freq = parameters.get_parameter_value(PName.CENTER_FREQUENCY)
|
40
|
+
bandwidth = parameters.get_parameter_value(PName.BANDWIDTH)
|
41
|
+
if_gain = parameters.get_parameter_value(PName.IF_GAIN)
|
42
|
+
rf_gain = parameters.get_parameter_value(PName.RF_GAIN)
|
45
43
|
|
46
44
|
|
47
|
-
##################################################
|
48
45
|
# Blocks
|
49
|
-
##################################################
|
50
46
|
self.spectre_batched_file_sink_0 = spectre.batched_file_sink(get_batches_dir_path(),
|
51
47
|
tag,
|
52
48
|
batch_size,
|
@@ -75,36 +71,33 @@ class _fixed_center_frequency(gr.top_block):
|
|
75
71
|
self.sdrplay3_rsp1a_0.set_sample_sequence_gaps_check(False)
|
76
72
|
self.sdrplay3_rsp1a_0.set_show_gain_changes(False)
|
77
73
|
|
78
|
-
|
79
|
-
##################################################
|
80
74
|
# Connections
|
81
|
-
##################################################
|
82
75
|
self.connect((self.sdrplay3_rsp1a_0, 0), (self.spectre_batched_file_sink_0, 0))
|
83
76
|
|
84
77
|
|
85
|
-
class _swept_center_frequency(
|
86
|
-
def
|
87
|
-
|
88
|
-
|
89
|
-
|
78
|
+
class _swept_center_frequency(spectre_top_block):
|
79
|
+
def flowgraph(
|
80
|
+
self,
|
81
|
+
tag: str,
|
82
|
+
parameters: Parameters
|
83
|
+
) -> None:
|
84
|
+
# OOT Module inline imports
|
85
|
+
from gnuradio import spectre
|
86
|
+
from gnuradio import sdrplay3
|
87
|
+
|
88
|
+
# Unpack the capture config parameters
|
89
|
+
sample_rate = parameters.get_parameter_value(PName.SAMPLE_RATE)
|
90
|
+
bandwidth = parameters.get_parameter_value(PName.BANDWIDTH)
|
91
|
+
min_frequency = parameters.get_parameter_value(PName.MIN_FREQUENCY)
|
92
|
+
max_frequency = parameters.get_parameter_value(PName.MAX_FREQUENCY)
|
93
|
+
frequency_step = parameters.get_parameter_value(PName.FREQUENCY_STEP)
|
94
|
+
samples_per_step = parameters.get_parameter_value(PName.SAMPLES_PER_STEP)
|
95
|
+
if_gain = parameters.get_parameter_value(PName.IF_GAIN)
|
96
|
+
rf_gain = parameters.get_parameter_value(PName.RF_GAIN)
|
97
|
+
batch_size = parameters.get_parameter_value(PName.BATCH_SIZE)
|
90
98
|
|
91
|
-
##################################################
|
92
|
-
# Unpack capture config
|
93
|
-
##################################################
|
94
|
-
sample_rate = parameters.get_parameter_value(PNames.SAMPLE_RATE)
|
95
|
-
bandwidth = parameters.get_parameter_value(PNames.BANDWIDTH)
|
96
|
-
min_frequency = parameters.get_parameter_value(PNames.MIN_FREQUENCY)
|
97
|
-
max_frequency = parameters.get_parameter_value(PNames.MAX_FREQUENCY)
|
98
|
-
frequency_step = parameters.get_parameter_value(PNames.FREQUENCY_STEP)
|
99
|
-
samples_per_step = parameters.get_parameter_value(PNames.SAMPLES_PER_STEP)
|
100
|
-
if_gain = parameters.get_parameter_value(PNames.IF_GAIN)
|
101
|
-
rf_gain = parameters.get_parameter_value(PNames.RF_GAIN)
|
102
|
-
batch_size = parameters.get_parameter_value(PNames.BATCH_SIZE)
|
103
99
|
|
104
|
-
|
105
|
-
##################################################
|
106
100
|
# Blocks
|
107
|
-
##################################################
|
108
101
|
self.spectre_sweep_driver_0 = spectre.sweep_driver(min_frequency,
|
109
102
|
max_frequency,
|
110
103
|
frequency_step,
|
@@ -143,16 +136,13 @@ class _swept_center_frequency(gr.top_block):
|
|
143
136
|
self.sdrplay3_rsp1a_0.set_sample_sequence_gaps_check(False)
|
144
137
|
self.sdrplay3_rsp1a_0.set_show_gain_changes(False)
|
145
138
|
|
146
|
-
|
147
|
-
##################################################
|
148
139
|
# Connections
|
149
|
-
##################################################
|
150
140
|
self.msg_connect((self.spectre_sweep_driver_0, 'freq'), (self.sdrplay3_rsp1a_0, 'freq'))
|
151
141
|
self.connect((self.sdrplay3_rsp1a_0, 0), (self.spectre_batched_file_sink_0, 0))
|
152
142
|
self.connect((self.sdrplay3_rsp1a_0, 0), (self.spectre_sweep_driver_0, 0))
|
153
143
|
|
154
144
|
|
155
145
|
@dataclass(frozen=True)
|
156
|
-
class
|
146
|
+
class CaptureMethod:
|
157
147
|
fixed_center_frequency = partial(capture, top_block_cls=_fixed_center_frequency)
|
158
|
-
swept_center_frequency = partial(capture, top_block_cls=_swept_center_frequency)
|
148
|
+
swept_center_frequency = partial(capture, top_block_cls=_swept_center_frequency, max_noutput_items=1024)
|