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.
Files changed (72) hide show
  1. spectre_core/__init__.py +3 -0
  2. spectre_core/cfg.py +116 -0
  3. spectre_core/chunks/__init__.py +206 -0
  4. spectre_core/chunks/base.py +160 -0
  5. spectre_core/chunks/chunk_register.py +15 -0
  6. spectre_core/chunks/factory.py +26 -0
  7. spectre_core/chunks/library/__init__.py +8 -0
  8. spectre_core/chunks/library/callisto/__init__.py +0 -0
  9. spectre_core/chunks/library/callisto/chunk.py +101 -0
  10. spectre_core/chunks/library/fixed/__init__.py +0 -0
  11. spectre_core/chunks/library/fixed/chunk.py +185 -0
  12. spectre_core/chunks/library/sweep/__init__.py +0 -0
  13. spectre_core/chunks/library/sweep/chunk.py +400 -0
  14. spectre_core/dynamic_imports.py +22 -0
  15. spectre_core/exceptions.py +17 -0
  16. spectre_core/file_handlers/base.py +94 -0
  17. spectre_core/file_handlers/configs.py +269 -0
  18. spectre_core/file_handlers/json.py +36 -0
  19. spectre_core/file_handlers/text.py +21 -0
  20. spectre_core/logging.py +222 -0
  21. spectre_core/plotting/__init__.py +5 -0
  22. spectre_core/plotting/base.py +194 -0
  23. spectre_core/plotting/factory.py +26 -0
  24. spectre_core/plotting/format.py +19 -0
  25. spectre_core/plotting/library/__init__.py +7 -0
  26. spectre_core/plotting/library/frequency_cuts/panel.py +74 -0
  27. spectre_core/plotting/library/integral_over_frequency/panel.py +34 -0
  28. spectre_core/plotting/library/spectrogram/panel.py +92 -0
  29. spectre_core/plotting/library/time_cuts/panel.py +77 -0
  30. spectre_core/plotting/panel_register.py +13 -0
  31. spectre_core/plotting/panel_stack.py +148 -0
  32. spectre_core/receivers/__init__.py +6 -0
  33. spectre_core/receivers/base.py +415 -0
  34. spectre_core/receivers/factory.py +19 -0
  35. spectre_core/receivers/library/__init__.py +7 -0
  36. spectre_core/receivers/library/rsp1a/__init__.py +0 -0
  37. spectre_core/receivers/library/rsp1a/gr/__init__.py +0 -0
  38. spectre_core/receivers/library/rsp1a/gr/fixed.py +104 -0
  39. spectre_core/receivers/library/rsp1a/gr/sweep.py +129 -0
  40. spectre_core/receivers/library/rsp1a/receiver.py +68 -0
  41. spectre_core/receivers/library/rspduo/__init__.py +0 -0
  42. spectre_core/receivers/library/rspduo/gr/__init__.py +0 -0
  43. spectre_core/receivers/library/rspduo/gr/tuner_1_fixed.py +110 -0
  44. spectre_core/receivers/library/rspduo/gr/tuner_1_sweep.py +135 -0
  45. spectre_core/receivers/library/rspduo/receiver.py +68 -0
  46. spectre_core/receivers/library/test/__init__.py +0 -0
  47. spectre_core/receivers/library/test/gr/__init__.py +0 -0
  48. spectre_core/receivers/library/test/gr/cosine_signal_1.py +83 -0
  49. spectre_core/receivers/library/test/gr/tagged_staircase.py +93 -0
  50. spectre_core/receivers/library/test/receiver.py +174 -0
  51. spectre_core/receivers/receiver_register.py +22 -0
  52. spectre_core/receivers/validators.py +205 -0
  53. spectre_core/spectrograms/__init__.py +3 -0
  54. spectre_core/spectrograms/analytical.py +205 -0
  55. spectre_core/spectrograms/array_operations.py +77 -0
  56. spectre_core/spectrograms/spectrogram.py +461 -0
  57. spectre_core/spectrograms/transform.py +267 -0
  58. spectre_core/watchdog/__init__.py +6 -0
  59. spectre_core/watchdog/base.py +105 -0
  60. spectre_core/watchdog/event_handler_register.py +15 -0
  61. spectre_core/watchdog/factory.py +22 -0
  62. spectre_core/watchdog/library/__init__.py +10 -0
  63. spectre_core/watchdog/library/fixed/__init__.py +0 -0
  64. spectre_core/watchdog/library/fixed/event_handler.py +41 -0
  65. spectre_core/watchdog/library/sweep/event_handler.py +55 -0
  66. spectre_core/watchdog/watcher.py +50 -0
  67. spectre_core/web_fetch/callisto.py +101 -0
  68. spectre_core-0.0.1.dist-info/LICENSE +674 -0
  69. spectre_core-0.0.1.dist-info/METADATA +40 -0
  70. spectre_core-0.0.1.dist-info/RECORD +72 -0
  71. spectre_core-0.0.1.dist-info/WHEEL +5 -0
  72. 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()