spectre-core 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- spectre_core/__init__.py +3 -0
- spectre_core/cfg.py +116 -0
- spectre_core/chunks/__init__.py +206 -0
- spectre_core/chunks/base.py +160 -0
- spectre_core/chunks/chunk_register.py +15 -0
- spectre_core/chunks/factory.py +26 -0
- spectre_core/chunks/library/__init__.py +8 -0
- spectre_core/chunks/library/callisto/__init__.py +0 -0
- spectre_core/chunks/library/callisto/chunk.py +101 -0
- spectre_core/chunks/library/fixed/__init__.py +0 -0
- spectre_core/chunks/library/fixed/chunk.py +185 -0
- spectre_core/chunks/library/sweep/__init__.py +0 -0
- spectre_core/chunks/library/sweep/chunk.py +400 -0
- spectre_core/dynamic_imports.py +22 -0
- spectre_core/exceptions.py +17 -0
- spectre_core/file_handlers/base.py +94 -0
- spectre_core/file_handlers/configs.py +269 -0
- spectre_core/file_handlers/json.py +36 -0
- spectre_core/file_handlers/text.py +21 -0
- spectre_core/logging.py +222 -0
- spectre_core/plotting/__init__.py +5 -0
- spectre_core/plotting/base.py +194 -0
- spectre_core/plotting/factory.py +26 -0
- spectre_core/plotting/format.py +19 -0
- spectre_core/plotting/library/__init__.py +7 -0
- spectre_core/plotting/library/frequency_cuts/panel.py +74 -0
- spectre_core/plotting/library/integral_over_frequency/panel.py +34 -0
- spectre_core/plotting/library/spectrogram/panel.py +92 -0
- spectre_core/plotting/library/time_cuts/panel.py +77 -0
- spectre_core/plotting/panel_register.py +13 -0
- spectre_core/plotting/panel_stack.py +148 -0
- spectre_core/receivers/__init__.py +6 -0
- spectre_core/receivers/base.py +415 -0
- spectre_core/receivers/factory.py +19 -0
- spectre_core/receivers/library/__init__.py +7 -0
- spectre_core/receivers/library/rsp1a/__init__.py +0 -0
- spectre_core/receivers/library/rsp1a/gr/__init__.py +0 -0
- spectre_core/receivers/library/rsp1a/gr/fixed.py +104 -0
- spectre_core/receivers/library/rsp1a/gr/sweep.py +129 -0
- spectre_core/receivers/library/rsp1a/receiver.py +68 -0
- spectre_core/receivers/library/rspduo/__init__.py +0 -0
- spectre_core/receivers/library/rspduo/gr/__init__.py +0 -0
- spectre_core/receivers/library/rspduo/gr/tuner_1_fixed.py +110 -0
- spectre_core/receivers/library/rspduo/gr/tuner_1_sweep.py +135 -0
- spectre_core/receivers/library/rspduo/receiver.py +68 -0
- spectre_core/receivers/library/test/__init__.py +0 -0
- spectre_core/receivers/library/test/gr/__init__.py +0 -0
- spectre_core/receivers/library/test/gr/cosine_signal_1.py +83 -0
- spectre_core/receivers/library/test/gr/tagged_staircase.py +93 -0
- spectre_core/receivers/library/test/receiver.py +174 -0
- spectre_core/receivers/receiver_register.py +22 -0
- spectre_core/receivers/validators.py +205 -0
- spectre_core/spectrograms/__init__.py +3 -0
- spectre_core/spectrograms/analytical.py +205 -0
- spectre_core/spectrograms/array_operations.py +77 -0
- spectre_core/spectrograms/spectrogram.py +461 -0
- spectre_core/spectrograms/transform.py +267 -0
- spectre_core/watchdog/__init__.py +6 -0
- spectre_core/watchdog/base.py +105 -0
- spectre_core/watchdog/event_handler_register.py +15 -0
- spectre_core/watchdog/factory.py +22 -0
- spectre_core/watchdog/library/__init__.py +10 -0
- spectre_core/watchdog/library/fixed/__init__.py +0 -0
- spectre_core/watchdog/library/fixed/event_handler.py +41 -0
- spectre_core/watchdog/library/sweep/event_handler.py +55 -0
- spectre_core/watchdog/watcher.py +50 -0
- spectre_core/web_fetch/callisto.py +101 -0
- spectre_core-0.0.1.dist-info/LICENSE +674 -0
- spectre_core-0.0.1.dist-info/METADATA +40 -0
- spectre_core-0.0.1.dist-info/RECORD +72 -0
- spectre_core-0.0.1.dist-info/WHEEL +5 -0
- spectre_core-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,415 @@
|
|
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 abc import ABC, abstractmethod
|
6
|
+
from typing import Callable, Any, Optional
|
7
|
+
|
8
|
+
from spectre_core.receivers import validators
|
9
|
+
from spectre_core.file_handlers.configs import (
|
10
|
+
CaptureConfig,
|
11
|
+
validate_against_type_template,
|
12
|
+
type_cast_params
|
13
|
+
)
|
14
|
+
from spectre_core.exceptions import (
|
15
|
+
TemplateNotFoundError,
|
16
|
+
ModeNotFoundError,
|
17
|
+
SpecificationNotFoundError
|
18
|
+
)
|
19
|
+
|
20
|
+
|
21
|
+
class BaseReceiver(ABC):
|
22
|
+
def __init__(self, name: str, mode: Optional[str] = None):
|
23
|
+
self._name = name
|
24
|
+
|
25
|
+
self._capture_methods: dict[str, Callable] = None
|
26
|
+
self._set_capture_methods()
|
27
|
+
|
28
|
+
self._type_templates: dict[str, dict[str, Any]] = None
|
29
|
+
self._set_type_templates()
|
30
|
+
|
31
|
+
self._validators: dict[str, Callable] = None
|
32
|
+
self._set_validators()
|
33
|
+
|
34
|
+
self._specifications: dict[str, Any] = None
|
35
|
+
self._set_specifications()
|
36
|
+
|
37
|
+
self.mode = mode
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
@abstractmethod
|
42
|
+
def _set_capture_methods(self) -> None:
|
43
|
+
pass
|
44
|
+
|
45
|
+
|
46
|
+
@abstractmethod
|
47
|
+
def _set_validators(self) -> None:
|
48
|
+
pass
|
49
|
+
|
50
|
+
@abstractmethod
|
51
|
+
def _set_type_templates(self) -> None:
|
52
|
+
pass
|
53
|
+
|
54
|
+
|
55
|
+
@abstractmethod
|
56
|
+
def _set_specifications(self) -> None:
|
57
|
+
pass
|
58
|
+
|
59
|
+
|
60
|
+
@property
|
61
|
+
def name(self) -> str:
|
62
|
+
return self._name
|
63
|
+
|
64
|
+
|
65
|
+
@property
|
66
|
+
def capture_methods(self) -> dict[str, Callable]:
|
67
|
+
return self._capture_methods
|
68
|
+
|
69
|
+
|
70
|
+
@property
|
71
|
+
def validators(self) -> dict[str, Callable]:
|
72
|
+
return self._validators
|
73
|
+
|
74
|
+
|
75
|
+
@property
|
76
|
+
def type_templates(self) -> dict[str, dict[str, Any]]:
|
77
|
+
return self._type_templates
|
78
|
+
|
79
|
+
|
80
|
+
@property
|
81
|
+
def specifications(self) -> dict[dict, Any]:
|
82
|
+
return self._specifications
|
83
|
+
|
84
|
+
|
85
|
+
@property
|
86
|
+
def valid_modes(self) -> None:
|
87
|
+
capture_method_modes = list(self.capture_methods.keys())
|
88
|
+
validator_modes = list(self.validators.keys())
|
89
|
+
type_template_modes = list(self.type_templates.keys())
|
90
|
+
|
91
|
+
if capture_method_modes == validator_modes == type_template_modes:
|
92
|
+
return capture_method_modes
|
93
|
+
else:
|
94
|
+
raise ValueError(f"Mode mismatch for the receiver {self.name}. Could not define valid modes")
|
95
|
+
|
96
|
+
@property
|
97
|
+
def mode(self) -> str:
|
98
|
+
return self._mode
|
99
|
+
|
100
|
+
|
101
|
+
@mode.setter
|
102
|
+
def mode(self, value: Optional[str]) -> None:
|
103
|
+
if (value is not None) and value not in self.valid_modes:
|
104
|
+
raise ModeNotFoundError((f"{value} is not a defined mode for the receiver {self.name}. "
|
105
|
+
f"Expected one of {self.valid_modes}"))
|
106
|
+
self._mode = value
|
107
|
+
|
108
|
+
|
109
|
+
@property
|
110
|
+
def mode_is_set(self) -> bool:
|
111
|
+
return (self._mode is not None)
|
112
|
+
|
113
|
+
|
114
|
+
@property
|
115
|
+
def capture_method(self) -> Callable:
|
116
|
+
return self.capture_methods[self.mode]
|
117
|
+
|
118
|
+
|
119
|
+
@property
|
120
|
+
def validator(self) -> Callable:
|
121
|
+
return self.validators[self.mode]
|
122
|
+
|
123
|
+
|
124
|
+
@property
|
125
|
+
def type_template(self) -> dict[str, Any]:
|
126
|
+
return self._type_templates[self.mode]
|
127
|
+
|
128
|
+
|
129
|
+
def get_specification(self,
|
130
|
+
specification_key: str) -> Any:
|
131
|
+
specification = self.specifications.get(specification_key)
|
132
|
+
if specification is None:
|
133
|
+
expected_specifications = list(self.specifications.keys())
|
134
|
+
raise SpecificationNotFoundError(f"Invalid specification '{specification_key}'. Expected one of {expected_specifications}")
|
135
|
+
return specification
|
136
|
+
|
137
|
+
|
138
|
+
def validate_capture_config(self,
|
139
|
+
capture_config: CaptureConfig) -> None:
|
140
|
+
# validate against the active type template
|
141
|
+
validate_against_type_template(capture_config,
|
142
|
+
self.type_template,
|
143
|
+
ignore_keys=["receiver", "mode", "tag"])
|
144
|
+
# validate against receiver-specific constraints
|
145
|
+
self.validator(capture_config)
|
146
|
+
|
147
|
+
|
148
|
+
|
149
|
+
def start_capture(self,
|
150
|
+
tag: str) -> None:
|
151
|
+
capture_config = self.load_capture_config(tag)
|
152
|
+
self.capture_method(capture_config)
|
153
|
+
|
154
|
+
|
155
|
+
def save_params(self,
|
156
|
+
params: list[str],
|
157
|
+
tag: str,
|
158
|
+
doublecheck_overwrite: bool = True) -> None:
|
159
|
+
d = type_cast_params(params,
|
160
|
+
self.type_template)
|
161
|
+
|
162
|
+
validate_against_type_template(d,
|
163
|
+
self.type_template)
|
164
|
+
|
165
|
+
self.save_capture_config(d,
|
166
|
+
tag,
|
167
|
+
doublecheck_overwrite=doublecheck_overwrite)
|
168
|
+
|
169
|
+
|
170
|
+
def save_capture_config(self,
|
171
|
+
d: dict[str, Any],
|
172
|
+
tag: str,
|
173
|
+
doublecheck_overwrite: bool = True) -> None:
|
174
|
+
|
175
|
+
self.validate_capture_config(d)
|
176
|
+
|
177
|
+
d.update({"receiver": self.name,
|
178
|
+
"mode": self.mode,
|
179
|
+
"tag": tag})
|
180
|
+
|
181
|
+
capture_config = CaptureConfig(tag)
|
182
|
+
capture_config.save(d,
|
183
|
+
doublecheck_overwrite = doublecheck_overwrite)
|
184
|
+
|
185
|
+
|
186
|
+
def load_capture_config(self,
|
187
|
+
tag: str) -> CaptureConfig:
|
188
|
+
|
189
|
+
capture_config = CaptureConfig(tag)
|
190
|
+
|
191
|
+
if capture_config["receiver"] != self.name:
|
192
|
+
raise ValueError(f"Capture config receiver mismatch for tag {tag}. Expected {self.name}, got {capture_config['receiver']}")
|
193
|
+
|
194
|
+
if capture_config["mode"] != self.mode:
|
195
|
+
raise ValueError(f"Mode mismatch for the tag {tag}. Expected {self.mode}, got {capture_config['mode']}")
|
196
|
+
|
197
|
+
self.validate_capture_config(capture_config)
|
198
|
+
return capture_config
|
199
|
+
|
200
|
+
|
201
|
+
def get_create_capture_config_cmd(self,
|
202
|
+
tag: str,
|
203
|
+
as_string: bool = False) -> str:
|
204
|
+
"""Get a command which can be used to create a capture config with the SPECTRE CLI."""
|
205
|
+
command_as_list = ["spectre", "create", "capture-config",
|
206
|
+
"--tag", tag,
|
207
|
+
"--receiver", self.name,
|
208
|
+
"--mode", self.mode]
|
209
|
+
for key, value in self.type_template.items():
|
210
|
+
command_as_list.extend(["-p", f"{key}={value.__name__}"])
|
211
|
+
|
212
|
+
return " ".join(command_as_list) if as_string else command_as_list
|
213
|
+
|
214
|
+
|
215
|
+
# optional parent class which provides default templates and validators
|
216
|
+
class SPECTREReceiver(BaseReceiver):
|
217
|
+
def __init__(self, *args, **kwargs):
|
218
|
+
self.__set_default_type_templates()
|
219
|
+
super().__init__(*args, **kwargs)
|
220
|
+
|
221
|
+
|
222
|
+
@property
|
223
|
+
def default_type_templates(self) -> dict[str, dict[str, Any]]:
|
224
|
+
return self._default_type_templates
|
225
|
+
|
226
|
+
|
227
|
+
def __set_default_type_templates(self) -> None:
|
228
|
+
self._default_type_templates = {
|
229
|
+
"fixed": {
|
230
|
+
"center_freq": float, # [Hz]
|
231
|
+
"bandwidth": float, # [Hz]
|
232
|
+
"samp_rate": int, # [Hz]
|
233
|
+
"IF_gain": int, # [dB]
|
234
|
+
"RF_gain": int, # [dB]
|
235
|
+
"chunk_size": int, # [s]
|
236
|
+
"joining_time": int, # [s]
|
237
|
+
"time_resolution": float, # [s]
|
238
|
+
"frequency_resolution": float, # [Hz]
|
239
|
+
"window_type": str, # window type for STFFT
|
240
|
+
"window_kwargs": dict, # keyword arguments for window function, must be in order as in scipy documentation.
|
241
|
+
"window_size": int, # number of samples in STFFT window
|
242
|
+
"STFFT_kwargs": dict, # keyword arguments for the scipy STFFT class
|
243
|
+
"chunk_key": str, # tag will map to the chunk with this key
|
244
|
+
"event_handler_key": str, # tag will map to event handler with this key during post processing
|
245
|
+
},
|
246
|
+
"sweep": {
|
247
|
+
"min_freq": float, # [Hz]
|
248
|
+
"max_freq": float, # [Hz]
|
249
|
+
"samples_per_step": int,
|
250
|
+
"freq_step": float, # [Hz]
|
251
|
+
"bandwidth": float, # [Hz]
|
252
|
+
"samp_rate": int, # [Hz]
|
253
|
+
"IF_gain": int, # [dB]
|
254
|
+
"RF_gain": int, # [dB]
|
255
|
+
"chunk_size": int, # [s]
|
256
|
+
"joining_time": int, # [s]
|
257
|
+
"time_resolution": float, # [s]
|
258
|
+
"frequency_resolution": float, # [Hz]
|
259
|
+
"window_type": str, # window type for STFFT
|
260
|
+
"window_kwargs": dict, # keyword arguments for window function, must be in order as in scipy documentation.
|
261
|
+
"window_size": int, # number of samples in STFFT window
|
262
|
+
"STFFT_kwargs": dict, # keyword arguments for the scipy STFFT class
|
263
|
+
"chunk_key": str, # tag will map to the chunk with this key
|
264
|
+
"event_handler_key": str, # tag will map to event handler with this key during post processing
|
265
|
+
}
|
266
|
+
}
|
267
|
+
|
268
|
+
|
269
|
+
def _get_default_type_template(self,
|
270
|
+
mode: str) -> dict:
|
271
|
+
default_type_template = self.default_type_templates[mode]
|
272
|
+
if default_type_template is None:
|
273
|
+
raise TemplateNotFoundError(f"No default template found for the mode {mode}")
|
274
|
+
return default_type_template
|
275
|
+
|
276
|
+
|
277
|
+
def _default_sweep_validator(self,
|
278
|
+
capture_config: CaptureConfig) -> None:
|
279
|
+
min_freq = capture_config["min_freq"]
|
280
|
+
max_freq = capture_config["max_freq"]
|
281
|
+
samples_per_step = capture_config["samples_per_step"]
|
282
|
+
freq_step = capture_config["freq_step"]
|
283
|
+
bandwidth = capture_config["bandwidth"]
|
284
|
+
samp_rate = capture_config["samp_rate"]
|
285
|
+
IF_gain = capture_config["IF_gain"]
|
286
|
+
RF_gain = capture_config["RF_gain"]
|
287
|
+
chunk_size = capture_config["chunk_size"]
|
288
|
+
time_resolution = capture_config["time_resolution"]
|
289
|
+
window_type = capture_config["window_type"]
|
290
|
+
window_kwargs = capture_config["window_kwargs"]
|
291
|
+
window_size = capture_config["window_size"]
|
292
|
+
STFFT_kwargs = capture_config["STFFT_kwargs"]
|
293
|
+
chunk_key = capture_config["chunk_key"]
|
294
|
+
event_handler_key = capture_config[ "event_handler_key"]
|
295
|
+
|
296
|
+
validators.center_freq_strictly_positive(min_freq)
|
297
|
+
validators.center_freq_strictly_positive(max_freq)
|
298
|
+
validators.samp_rate_strictly_positive(samp_rate)
|
299
|
+
validators.bandwidth_strictly_positive(bandwidth)
|
300
|
+
validators.nyquist_criterion(samp_rate,
|
301
|
+
bandwidth)
|
302
|
+
validators.chunk_size_strictly_positive(chunk_size)
|
303
|
+
validators.time_resolution(time_resolution,
|
304
|
+
chunk_size)
|
305
|
+
validators.window(window_type,
|
306
|
+
window_kwargs,
|
307
|
+
window_size,
|
308
|
+
chunk_size,
|
309
|
+
samp_rate)
|
310
|
+
validators.STFFT_kwargs(STFFT_kwargs)
|
311
|
+
validators.chunk_key(chunk_key, "sweep")
|
312
|
+
validators.event_handler_key(event_handler_key, "sweep")
|
313
|
+
validators.gain_is_negative(IF_gain)
|
314
|
+
validators.gain_is_negative(RF_gain)
|
315
|
+
validators.num_steps_per_sweep(min_freq,
|
316
|
+
max_freq,
|
317
|
+
samp_rate,
|
318
|
+
freq_step)
|
319
|
+
validators.sweep_interval(min_freq,
|
320
|
+
max_freq,
|
321
|
+
samp_rate,
|
322
|
+
freq_step,
|
323
|
+
samples_per_step,
|
324
|
+
chunk_size)
|
325
|
+
validators.non_overlapping_steps(freq_step,
|
326
|
+
samp_rate)
|
327
|
+
validators.num_samples_per_step(samples_per_step,
|
328
|
+
window_size)
|
329
|
+
|
330
|
+
# if the api latency is defined, raise a warning if the step interval is of the same order
|
331
|
+
api_latency = self.specifications.get("api_latency")
|
332
|
+
if api_latency:
|
333
|
+
validators.step_interval(samples_per_step,
|
334
|
+
samp_rate,
|
335
|
+
api_latency)
|
336
|
+
|
337
|
+
|
338
|
+
def _default_fixed_validator(self,
|
339
|
+
capture_config: CaptureConfig) -> None:
|
340
|
+
center_freq = capture_config["center_freq"]
|
341
|
+
bandwidth = capture_config["bandwidth"]
|
342
|
+
samp_rate = capture_config["samp_rate"]
|
343
|
+
IF_gain = capture_config["IF_gain"]
|
344
|
+
RF_gain = capture_config["RF_gain"]
|
345
|
+
chunk_size = capture_config["chunk_size"]
|
346
|
+
time_resolution = capture_config["time_resolution"]
|
347
|
+
window_type = capture_config["window_type"]
|
348
|
+
window_kwargs = capture_config["window_kwargs"]
|
349
|
+
window_size = capture_config["window_size"]
|
350
|
+
STFFT_kwargs = capture_config["STFFT_kwargs"]
|
351
|
+
chunk_key = capture_config["chunk_key"]
|
352
|
+
event_handler_key = capture_config["event_handler_key"]
|
353
|
+
|
354
|
+
validators.center_freq_strictly_positive(center_freq)
|
355
|
+
validators.samp_rate_strictly_positive(samp_rate)
|
356
|
+
validators.bandwidth_strictly_positive(bandwidth)
|
357
|
+
validators.nyquist_criterion(samp_rate, bandwidth)
|
358
|
+
validators.chunk_size_strictly_positive(chunk_size)
|
359
|
+
validators.time_resolution(time_resolution, chunk_size)
|
360
|
+
validators.window(window_type,
|
361
|
+
window_kwargs,
|
362
|
+
window_size,
|
363
|
+
chunk_size,
|
364
|
+
samp_rate)
|
365
|
+
validators.STFFT_kwargs(STFFT_kwargs)
|
366
|
+
validators.chunk_key(chunk_key,
|
367
|
+
"fixed")
|
368
|
+
validators.event_handler_key(event_handler_key,
|
369
|
+
"fixed")
|
370
|
+
validators.gain_is_negative(IF_gain)
|
371
|
+
validators.gain_is_negative(RF_gain)
|
372
|
+
|
373
|
+
|
374
|
+
# parent class for shared methods and attributes of SDRPlay receivers
|
375
|
+
class SDRPlayReceiver(SPECTREReceiver):
|
376
|
+
def __init__(self, *args, **kwargs):
|
377
|
+
super().__init__(*args, **kwargs)
|
378
|
+
|
379
|
+
def _sdrplay_validator(self,
|
380
|
+
capture_config: CaptureConfig) -> None:
|
381
|
+
# RSPduo specific validations in single tuner mode
|
382
|
+
center_freq_lower_bound = self.get_specification("center_freq_lower_bound")
|
383
|
+
center_freq_upper_bound = self.get_specification("center_freq_upper_bound")
|
384
|
+
center_freq = capture_config.get("center_freq")
|
385
|
+
min_freq = capture_config.get("min_freq")
|
386
|
+
max_freq = capture_config.get("max_freq")
|
387
|
+
|
388
|
+
if center_freq:
|
389
|
+
validators.closed_confine_center_freq(center_freq,
|
390
|
+
center_freq_lower_bound,
|
391
|
+
center_freq_upper_bound)
|
392
|
+
|
393
|
+
if min_freq:
|
394
|
+
validators.closed_confine_center_freq(min_freq,
|
395
|
+
center_freq_lower_bound,
|
396
|
+
center_freq_upper_bound)
|
397
|
+
if max_freq:
|
398
|
+
validators.closed_confine_center_freq(max_freq,
|
399
|
+
center_freq_lower_bound,
|
400
|
+
center_freq_upper_bound)
|
401
|
+
|
402
|
+
validators.closed_confine_samp_rate(capture_config["samp_rate"],
|
403
|
+
self.get_specification("samp_rate_lower_bound"),
|
404
|
+
self.get_specification("samp_rate_upper_bound"))
|
405
|
+
|
406
|
+
|
407
|
+
validators.closed_confine_bandwidth(capture_config["bandwidth"],
|
408
|
+
self.get_specification("bandwidth_lower_bound"),
|
409
|
+
self.get_specification("bandwidth_upper_bound"))
|
410
|
+
|
411
|
+
validators.closed_upper_bound_IF_gain(capture_config["IF_gain"],
|
412
|
+
self.get_specification("IF_gain_upper_bound"))
|
413
|
+
|
414
|
+
validators.closed_upper_bound_RF_gain(capture_config["RF_gain"],
|
415
|
+
self.get_specification("RF_gain_upper_bound"))
|
@@ -0,0 +1,19 @@
|
|
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 Optional
|
6
|
+
|
7
|
+
from spectre_core.receivers.receiver_register import receivers
|
8
|
+
from spectre_core.receivers.base import BaseReceiver
|
9
|
+
from spectre_core.exceptions import ReceiverNotFoundError
|
10
|
+
|
11
|
+
# used to fetch an instance of the receiver class
|
12
|
+
def get_receiver(receiver_name: str, mode: Optional[str] = None) -> BaseReceiver:
|
13
|
+
Receiver = receivers.get(receiver_name)
|
14
|
+
if Receiver is None:
|
15
|
+
valid_receivers = list(receivers.keys())
|
16
|
+
raise ReceiverNotFoundError(f"No class found for the receiver: {receiver_name}. "
|
17
|
+
f"Please specify one of the following receivers {valid_receivers}")
|
18
|
+
return Receiver(receiver_name,
|
19
|
+
mode = mode)
|
@@ -0,0 +1,7 @@
|
|
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 spectre_core.dynamic_imports import import_target_modules
|
6
|
+
|
7
|
+
import_target_modules(__file__, __name__, "receiver")
|
File without changes
|
File without changes
|
@@ -0,0 +1,104 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
# SPDX-License-Identifier: GPL-3.0
|
5
|
+
#
|
6
|
+
# GNU Radio Python Flow Graph
|
7
|
+
# Title: Not titled yet
|
8
|
+
# GNU Radio version: 3.10.1.1
|
9
|
+
|
10
|
+
# SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
|
11
|
+
# This file is part of SPECTRE
|
12
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
13
|
+
|
14
|
+
import sys
|
15
|
+
import signal
|
16
|
+
from argparse import ArgumentParser
|
17
|
+
from typing import Any
|
18
|
+
|
19
|
+
from gnuradio import gr
|
20
|
+
from gnuradio.filter import firdes
|
21
|
+
from gnuradio.fft import window
|
22
|
+
from gnuradio.eng_arg import eng_float, intx
|
23
|
+
from gnuradio import eng_notation
|
24
|
+
from gnuradio import sdrplay3
|
25
|
+
from gnuradio import spectre
|
26
|
+
|
27
|
+
from spectre_core.cfg import CHUNKS_DIR_PATH
|
28
|
+
from spectre_core.file_handlers.configs import CaptureConfig
|
29
|
+
|
30
|
+
class fixed(gr.top_block):
|
31
|
+
|
32
|
+
def __init__(self,
|
33
|
+
capture_config: CaptureConfig):
|
34
|
+
gr.top_block.__init__(self, "fixed", catch_exceptions=True)
|
35
|
+
|
36
|
+
##################################################
|
37
|
+
# Unpack capture config
|
38
|
+
##################################################
|
39
|
+
samp_rate = capture_config['samp_rate']
|
40
|
+
tag = capture_config['tag']
|
41
|
+
chunk_size = capture_config['chunk_size']
|
42
|
+
center_freq = capture_config['center_freq']
|
43
|
+
bandwidth = capture_config['bandwidth']
|
44
|
+
IF_gain = capture_config['IF_gain']
|
45
|
+
RF_gain = capture_config['RF_gain']
|
46
|
+
is_sweeping = False
|
47
|
+
|
48
|
+
##################################################
|
49
|
+
# Blocks
|
50
|
+
##################################################
|
51
|
+
self.spectre_batched_file_sink_0 = spectre.batched_file_sink(CHUNKS_DIR_PATH, tag, chunk_size, samp_rate, is_sweeping)
|
52
|
+
self.sdrplay3_rsp1a_0 = sdrplay3.rsp1a(
|
53
|
+
'',
|
54
|
+
stream_args=sdrplay3.stream_args(
|
55
|
+
output_type='fc32',
|
56
|
+
channels_size=1
|
57
|
+
),
|
58
|
+
)
|
59
|
+
self.sdrplay3_rsp1a_0.set_sample_rate(samp_rate)
|
60
|
+
self.sdrplay3_rsp1a_0.set_center_freq(center_freq)
|
61
|
+
self.sdrplay3_rsp1a_0.set_bandwidth(bandwidth)
|
62
|
+
self.sdrplay3_rsp1a_0.set_gain_mode(False)
|
63
|
+
self.sdrplay3_rsp1a_0.set_gain(IF_gain, 'IF')
|
64
|
+
self.sdrplay3_rsp1a_0.set_gain(RF_gain, 'RF')
|
65
|
+
self.sdrplay3_rsp1a_0.set_freq_corr(0)
|
66
|
+
self.sdrplay3_rsp1a_0.set_dc_offset_mode(False)
|
67
|
+
self.sdrplay3_rsp1a_0.set_iq_balance_mode(False)
|
68
|
+
self.sdrplay3_rsp1a_0.set_agc_setpoint(-30)
|
69
|
+
self.sdrplay3_rsp1a_0.set_rf_notch_filter(False)
|
70
|
+
self.sdrplay3_rsp1a_0.set_dab_notch_filter(False)
|
71
|
+
self.sdrplay3_rsp1a_0.set_biasT(False)
|
72
|
+
self.sdrplay3_rsp1a_0.set_debug_mode(False)
|
73
|
+
self.sdrplay3_rsp1a_0.set_sample_sequence_gaps_check(False)
|
74
|
+
self.sdrplay3_rsp1a_0.set_show_gain_changes(False)
|
75
|
+
|
76
|
+
|
77
|
+
##################################################
|
78
|
+
# Connections
|
79
|
+
##################################################
|
80
|
+
self.connect((self.sdrplay3_rsp1a_0, 0), (self.spectre_batched_file_sink_0, 0))
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
def main(capture_config: CaptureConfig,
|
87
|
+
top_block_cls=fixed,
|
88
|
+
options=None):
|
89
|
+
|
90
|
+
tb = top_block_cls(capture_config)
|
91
|
+
|
92
|
+
def sig_handler(sig=None, frame=None):
|
93
|
+
tb.stop()
|
94
|
+
tb.wait()
|
95
|
+
|
96
|
+
sys.exit(0)
|
97
|
+
|
98
|
+
signal.signal(signal.SIGINT, sig_handler)
|
99
|
+
signal.signal(signal.SIGTERM, sig_handler)
|
100
|
+
|
101
|
+
tb.start()
|
102
|
+
|
103
|
+
tb.wait()
|
104
|
+
|
@@ -0,0 +1,129 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
#
|
5
|
+
# SPDX-License-Identifier: GPL-3.0
|
6
|
+
#
|
7
|
+
# GNU Radio Python Flow Graph
|
8
|
+
# Title: Not titled yet
|
9
|
+
# GNU Radio version: 3.10.1.1
|
10
|
+
|
11
|
+
import sys
|
12
|
+
import signal
|
13
|
+
from argparse import ArgumentParser
|
14
|
+
from typing import Any
|
15
|
+
|
16
|
+
from gnuradio import gr
|
17
|
+
from gnuradio.filter import firdes
|
18
|
+
from gnuradio.fft import window
|
19
|
+
from gnuradio.eng_arg import eng_float, intx
|
20
|
+
from gnuradio import eng_notation
|
21
|
+
from gnuradio import sdrplay3
|
22
|
+
from gnuradio import spectre
|
23
|
+
|
24
|
+
from spectre_core.cfg import CHUNKS_DIR_PATH
|
25
|
+
from spectre_core.file_handlers.configs import CaptureConfig
|
26
|
+
|
27
|
+
|
28
|
+
class sweep(gr.top_block):
|
29
|
+
def __init__(self,
|
30
|
+
capture_config: CaptureConfig):
|
31
|
+
gr.top_block.__init__(self, "sweep", catch_exceptions=True)
|
32
|
+
|
33
|
+
##################################################
|
34
|
+
# Unpack capture config
|
35
|
+
##################################################
|
36
|
+
samp_rate = capture_config['samp_rate']
|
37
|
+
bandwidth = capture_config['bandwidth']
|
38
|
+
min_freq = capture_config['min_freq']
|
39
|
+
max_freq = capture_config['max_freq']
|
40
|
+
freq_step = capture_config['freq_step']
|
41
|
+
samples_per_step = capture_config['samples_per_step']
|
42
|
+
IF_gain = capture_config['IF_gain']
|
43
|
+
RF_gain = capture_config['RF_gain']
|
44
|
+
chunk_size = capture_config['chunk_size']
|
45
|
+
start_freq = min_freq + samp_rate/2
|
46
|
+
tag = capture_config['tag']
|
47
|
+
|
48
|
+
##################################################
|
49
|
+
# Blocks
|
50
|
+
##################################################
|
51
|
+
self.spectre_sweep_driver_0 = spectre.sweep_driver(min_freq,
|
52
|
+
max_freq,
|
53
|
+
freq_step,
|
54
|
+
samp_rate,
|
55
|
+
samples_per_step,
|
56
|
+
'freq')
|
57
|
+
self.spectre_batched_file_sink_0 = spectre.batched_file_sink(CHUNKS_DIR_PATH,
|
58
|
+
tag,
|
59
|
+
chunk_size,
|
60
|
+
samp_rate,
|
61
|
+
True,
|
62
|
+
'freq',
|
63
|
+
start_freq)
|
64
|
+
self.sdrplay3_rsp1a_0 = sdrplay3.rsp1a(
|
65
|
+
'',
|
66
|
+
stream_args=sdrplay3.stream_args(
|
67
|
+
output_type='fc32',
|
68
|
+
channels_size=1
|
69
|
+
),
|
70
|
+
)
|
71
|
+
self.sdrplay3_rsp1a_0.set_sample_rate(samp_rate, True)
|
72
|
+
self.sdrplay3_rsp1a_0.set_center_freq(start_freq, True)
|
73
|
+
self.sdrplay3_rsp1a_0.set_bandwidth(bandwidth)
|
74
|
+
self.sdrplay3_rsp1a_0.set_gain_mode(False)
|
75
|
+
self.sdrplay3_rsp1a_0.set_gain(IF_gain, 'IF', True)
|
76
|
+
self.sdrplay3_rsp1a_0.set_gain(RF_gain, 'RF', True)
|
77
|
+
self.sdrplay3_rsp1a_0.set_freq_corr(0)
|
78
|
+
self.sdrplay3_rsp1a_0.set_dc_offset_mode(False)
|
79
|
+
self.sdrplay3_rsp1a_0.set_iq_balance_mode(False)
|
80
|
+
self.sdrplay3_rsp1a_0.set_agc_setpoint(-30)
|
81
|
+
self.sdrplay3_rsp1a_0.set_rf_notch_filter(False)
|
82
|
+
self.sdrplay3_rsp1a_0.set_dab_notch_filter(False)
|
83
|
+
self.sdrplay3_rsp1a_0.set_biasT(False)
|
84
|
+
self.sdrplay3_rsp1a_0.set_stream_tags(True)
|
85
|
+
self.sdrplay3_rsp1a_0.set_debug_mode(False)
|
86
|
+
self.sdrplay3_rsp1a_0.set_sample_sequence_gaps_check(False)
|
87
|
+
self.sdrplay3_rsp1a_0.set_show_gain_changes(False)
|
88
|
+
|
89
|
+
|
90
|
+
##################################################
|
91
|
+
# Connections
|
92
|
+
##################################################
|
93
|
+
self.msg_connect((self.spectre_sweep_driver_0, 'freq'), (self.sdrplay3_rsp1a_0, 'freq'))
|
94
|
+
self.connect((self.sdrplay3_rsp1a_0, 0), (self.spectre_batched_file_sink_0, 0))
|
95
|
+
self.connect((self.sdrplay3_rsp1a_0, 0), (self.spectre_sweep_driver_0, 0))
|
96
|
+
|
97
|
+
|
98
|
+
def get_samp_rate(self):
|
99
|
+
return self.samp_rate
|
100
|
+
|
101
|
+
def set_samp_rate(self, samp_rate):
|
102
|
+
self.samp_rate = samp_rate
|
103
|
+
self.sdrplay3_rsp1a_0.set_sample_rate(self.samp_rate, True)
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
|
108
|
+
def main(capture_config: CaptureConfig,
|
109
|
+
top_block_cls=sweep,
|
110
|
+
options=None):
|
111
|
+
|
112
|
+
tb = top_block_cls(capture_config)
|
113
|
+
|
114
|
+
def sig_handler(sig=None, frame=None):
|
115
|
+
tb.stop()
|
116
|
+
tb.wait()
|
117
|
+
|
118
|
+
sys.exit(0)
|
119
|
+
|
120
|
+
signal.signal(signal.SIGINT, sig_handler)
|
121
|
+
signal.signal(signal.SIGTERM, sig_handler)
|
122
|
+
|
123
|
+
tb.start(512)
|
124
|
+
|
125
|
+
tb.wait()
|
126
|
+
|
127
|
+
|
128
|
+
if __name__ == '__main__':
|
129
|
+
main()
|