spectre-core 0.0.7__py3-none-any.whl → 0.0.9__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 (26) hide show
  1. spectre_core/plotting/panel_stack.py +3 -1
  2. spectre_core/{watchdog → post_processing}/__init__.py +1 -1
  3. spectre_core/post_processing/base.py +132 -0
  4. spectre_core/{watchdog → post_processing}/factory.py +4 -4
  5. spectre_core/{watchdog → post_processing}/library/fixed/event_handler.py +8 -9
  6. spectre_core/{watchdog → post_processing}/library/sweep/event_handler.py +9 -10
  7. spectre_core/post_processing/post_processor.py +40 -0
  8. spectre_core/receivers/base.py +11 -4
  9. spectre_core/receivers/library/rspduo/gr/tuner_1_fixed.py +6 -2
  10. spectre_core/receivers/library/rspduo/gr/tuner_1_sweep.py +8 -12
  11. spectre_core/receivers/library/rspduo/gr/tuner_2_fixed.py +120 -0
  12. spectre_core/receivers/library/rspduo/gr/tuner_2_sweep.py +119 -0
  13. spectre_core/receivers/library/rspduo/receiver.py +34 -5
  14. spectre_core/receivers/library/test/receiver.py +45 -20
  15. spectre_core/receivers/validators.py +67 -29
  16. spectre_core/web_fetch/callisto.py +1 -1
  17. {spectre_core-0.0.7.dist-info → spectre_core-0.0.9.dist-info}/METADATA +2 -2
  18. {spectre_core-0.0.7.dist-info → spectre_core-0.0.9.dist-info}/RECORD +24 -22
  19. spectre_core/watchdog/base.py +0 -105
  20. spectre_core/watchdog/post_processor.py +0 -50
  21. /spectre_core/{watchdog → post_processing}/event_handler_register.py +0 -0
  22. /spectre_core/{watchdog → post_processing}/library/__init__.py +0 -0
  23. /spectre_core/{watchdog → post_processing}/library/fixed/__init__.py +0 -0
  24. {spectre_core-0.0.7.dist-info → spectre_core-0.0.9.dist-info}/LICENSE +0 -0
  25. {spectre_core-0.0.7.dist-info → spectre_core-0.0.9.dist-info}/WHEEL +0 -0
  26. {spectre_core-0.0.7.dist-info → spectre_core-0.0.9.dist-info}/top_level.txt +0 -0
@@ -15,7 +15,9 @@ from spectre_core.plotting.factory import get_panel
15
15
  from spectre_core.plotting.library.spectrogram.panel import Panel as SpectrogramPanel
16
16
 
17
17
  class PanelStack:
18
- def __init__(self, time_type: str, figsize: Tuple[int, int] = (10, 10)):
18
+ def __init__(self,
19
+ time_type: str = "seconds",
20
+ figsize: Tuple[int, int] = (10, 10)):
19
21
  self._time_type = time_type
20
22
  self._figsize = figsize
21
23
  self._panels: List[BasePanel] = []
@@ -3,4 +3,4 @@
3
3
  # SPDX-License-Identifier: GPL-3.0-or-later
4
4
 
5
5
  # dynamically import all event handlers
6
- import spectre_core.watchdog.library
6
+ import spectre_core.post_processing.library
@@ -0,0 +1,132 @@
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 logging import getLogger
6
+ _LOGGER = getLogger(__name__)
7
+
8
+ from queue import Queue
9
+ from typing import Optional
10
+ from abc import ABC, abstractmethod
11
+ from math import floor
12
+
13
+ from watchdog.events import (
14
+ FileSystemEventHandler,
15
+ FileCreatedEvent,
16
+ )
17
+
18
+ from spectre_core.chunks.factory import get_chunk_from_tag
19
+ from spectre_core.file_handlers.configs import CaptureConfig
20
+ from spectre_core.spectrograms.spectrogram import Spectrogram
21
+ from spectre_core.spectrograms.transform import join_spectrograms
22
+ from spectre_core.spectrograms.transform import (
23
+ time_average,
24
+ frequency_average
25
+ )
26
+
27
+
28
+ class BaseEventHandler(ABC, FileSystemEventHandler):
29
+ def __init__(self,
30
+ tag: str):
31
+ self._tag = tag
32
+
33
+ self._Chunk = get_chunk_from_tag(tag)
34
+
35
+ self._capture_config = CaptureConfig(tag)
36
+
37
+ self._watch_extension = self._capture_config.get("watch_extension")
38
+ if self._watch_extension is None:
39
+ raise KeyError("The watch extension has not been specified in the capture config")
40
+
41
+ # attribute to store the next file to be processed
42
+ # (specifically, the absolute file path of the file)
43
+ self._queued_file: Optional[str] = None
44
+
45
+ # spectrogram cache stores spectrograms in memory
46
+ # such that they can be periodically written to files
47
+ # according to the joining time.
48
+ self._spectrogram: Optional[Spectrogram] = None
49
+
50
+
51
+
52
+ @abstractmethod
53
+ def process(self,
54
+ absolute_file_path: str) -> None:
55
+ """Process the file stored at the input absolute file path.
56
+
57
+ To be implemented by derived classes.
58
+ """
59
+
60
+
61
+ def on_created(self,
62
+ event: FileCreatedEvent):
63
+ """Process a newly created batch file, only once the next batch is created.
64
+
65
+ Since we assume that the batches are non-overlapping in time, this guarantees
66
+ we avoid post processing a file while it is being written to. Files are processed
67
+ sequentially, in the order they are created.
68
+ """
69
+
70
+ # the 'src_path' attribute holds the absolute path of the newly created file
71
+ absolute_file_path = event.src_path
72
+
73
+ # only 'notice' a file if it ends with the appropriate extension
74
+ # as defined in the capture config
75
+ if absolute_file_path.endswith(self._watch_extension):
76
+ _LOGGER.info(f"Noticed {absolute_file_path}")
77
+
78
+ # If there exists a queued file, try and process it
79
+ if self._queued_file is not None:
80
+ try:
81
+ self.process(self._queued_file)
82
+ except Exception:
83
+ _LOGGER.error(f"An error has occured while processing {self._queued_file}",
84
+ exc_info=True)
85
+ # flush any internally stored spectrogram on error to avoid lost data
86
+ self._flush_spectrogram()
87
+ # re-raise the exception to the main thread
88
+ raise
89
+
90
+ # Queue the current file for processing next
91
+ _LOGGER.info(f"Queueing {absolute_file_path} for post processing")
92
+ self._queued_file = absolute_file_path
93
+
94
+
95
+ def _average_in_time(self,
96
+ spectrogram: Spectrogram) -> Spectrogram:
97
+ _LOGGER.info("Averaging spectrogram in time")
98
+ requested_time_resolution = self._capture_config['time_resolution'] # [s]
99
+ if requested_time_resolution is None:
100
+ raise KeyError(f"Time resolution has not been specified in the capture config")
101
+ average_over = floor(requested_time_resolution/spectrogram.time_resolution) if requested_time_resolution > spectrogram.time_resolution else 1
102
+ return time_average(spectrogram, average_over)
103
+
104
+
105
+ def _average_in_frequency(self,
106
+ spectrogram: Spectrogram) -> Spectrogram:
107
+ _LOGGER.info("Averaging spectrogram in frequency")
108
+ frequency_resolution = self._capture_config['frequency_resolution'] # [Hz]
109
+ if frequency_resolution is None:
110
+ raise KeyError(f"Frequency resolution has not been specified in the capture config")
111
+ average_over = floor(frequency_resolution/spectrogram.frequency_resolution) if frequency_resolution > spectrogram.frequency_resolution else 1
112
+ return frequency_average(spectrogram, average_over)
113
+
114
+
115
+ def _join_spectrogram(self,
116
+ spectrogram: Spectrogram) -> None:
117
+ _LOGGER.info("Joining spectrogram")
118
+ if self._spectrogram is None:
119
+ self._spectrogram = spectrogram
120
+ else:
121
+ self._spectrogram = join_spectrograms([self._spectrogram, spectrogram])
122
+
123
+ if self._spectrogram.time_range >= self._capture_config['joining_time']:
124
+ self._flush_spectrogram()
125
+
126
+
127
+ def _flush_spectrogram(self) -> None:
128
+ if self._spectrogram:
129
+ _LOGGER.info(f"Flushing spectrogram to file with chunk start time {self._spectrogram.chunk_start_time}")
130
+ self._spectrogram.save()
131
+ _LOGGER.info("Flush successful, resetting spectrogram cache")
132
+ self._spectrogram = None # reset the cache
@@ -2,17 +2,17 @@
2
2
  # This file is part of SPECTRE
3
3
  # SPDX-License-Identifier: GPL-3.0-or-later
4
4
 
5
- from spectre_core.watchdog.event_handler_register import event_handler_map
6
- from spectre_core.watchdog.base import BaseEventHandler
5
+ from spectre_core.post_processing.event_handler_register import event_handler_map
6
+ from spectre_core.post_processing.base import BaseEventHandler
7
7
  from spectre_core.file_handlers.configs import CaptureConfig
8
8
  from spectre_core.exceptions import EventHandlerNotFoundError
9
9
 
10
10
  def get_event_handler(event_handler_key: str) -> BaseEventHandler:
11
- # try and fetch the capture config mount
12
11
  EventHandler = event_handler_map.get(event_handler_key)
13
12
  if EventHandler is None:
14
13
  valid_event_handler_keys = list(event_handler_map.keys())
15
- raise EventHandlerNotFoundError(f"No event handler found for the event handler key: {event_handler_key}. Please specify one of the following event handler keys {valid_event_handler_keys}")
14
+ raise EventHandlerNotFoundError((f"No event handler found for the event handler key '{event_handler_key}'. "
15
+ f"Please specify one of the following event handler keys: {valid_event_handler_keys}"))
16
16
  return EventHandler
17
17
 
18
18
 
@@ -7,8 +7,8 @@ _LOGGER = getLogger(__name__)
7
7
 
8
8
  import os
9
9
 
10
- from spectre_core.watchdog.base import BaseEventHandler
11
- from spectre_core.watchdog.event_handler_register import register_event_handler
10
+ from spectre_core.post_processing.base import BaseEventHandler
11
+ from spectre_core.post_processing.event_handler_register import register_event_handler
12
12
 
13
13
  @register_event_handler("fixed")
14
14
  class EventHandler(BaseEventHandler):
@@ -16,20 +16,19 @@ class EventHandler(BaseEventHandler):
16
16
  super().__init__(*args, **kwargs)
17
17
 
18
18
 
19
- def process(self, file_path: str):
20
- _LOGGER.info(f"Processing: {file_path}")
21
- file_name = os.path.basename(file_path)
22
- chunk_start_time, _ = os.path.splitext(file_name)[0].split('_')
19
+ def process(self,
20
+ absolute_file_path: str):
21
+ _LOGGER.info(f"Processing: {absolute_file_path}")
22
+ file_name = os.path.basename(absolute_file_path)
23
+ base_file_name, _ = os.path.splitext(file_name)
24
+ chunk_start_time, _ = base_file_name.split('_')
23
25
  chunk = self._Chunk(chunk_start_time, self._tag)
24
26
 
25
27
  _LOGGER.info("Creating spectrogram")
26
28
  spectrogram = chunk.build_spectrogram()
27
29
 
28
- _LOGGER.info("Averaging spectrogram")
29
30
  spectrogram = self._average_in_time(spectrogram)
30
31
  spectrogram = self._average_in_frequency(spectrogram)
31
-
32
- _LOGGER.info("Joining spectrogram")
33
32
  self._join_spectrogram(spectrogram)
34
33
 
35
34
  bin_chunk = chunk.get_file('bin')
@@ -8,8 +8,8 @@ _LOGGER = getLogger(__name__)
8
8
  import os
9
9
 
10
10
  from spectre_core.chunks.base import BaseChunk
11
- from spectre_core.watchdog.base import BaseEventHandler
12
- from spectre_core.watchdog.event_handler_register import register_event_handler
11
+ from spectre_core.post_processing.base import BaseEventHandler
12
+ from spectre_core.post_processing.event_handler_register import register_event_handler
13
13
 
14
14
  @register_event_handler("sweep")
15
15
  class EventHandler(BaseEventHandler):
@@ -19,23 +19,22 @@ class EventHandler(BaseEventHandler):
19
19
  self.previous_chunk: BaseChunk = None # cache for previous chunk
20
20
 
21
21
 
22
- def process(self, file_path: str):
23
- _LOGGER.info(f"Processing: {file_path}")
24
- file_name = os.path.basename(file_path)
25
- chunk_start_time, _ = os.path.splitext(file_name)[0].split('_')
22
+ def process(self,
23
+ absolute_file_path: str):
24
+ _LOGGER.info(f"Processing: {absolute_file_path}")
25
+ file_name = os.path.basename(absolute_file_path)
26
+ base_file_name, _ = os.path.splitext(file_name)
27
+ chunk_start_time, _ = base_file_name.split('_')
26
28
  chunk = self._Chunk(chunk_start_time, self._tag)
27
29
 
28
30
  _LOGGER.info("Creating spectrogram")
29
31
  spectrogram = chunk.build_spectrogram(previous_chunk = self.previous_chunk)
30
32
 
31
- _LOGGER.info("Averaging spectrogram")
32
33
  spectrogram = self._average_in_time(spectrogram)
33
34
  spectrogram = self._average_in_frequency(spectrogram)
34
-
35
- _LOGGER.info("Joining spectrogram")
36
35
  self._join_spectrogram(spectrogram)
37
36
 
38
- # if the previous chunk has not yet been set, it means we were processing the first chunk
37
+ # if the previous chunk has not yet been set, it means we are processing the first chunk
39
38
  # so we don't need to handle the previous chunk
40
39
  if self.previous_chunk is None:
41
40
  # instead, only set it for the next time this method is called
@@ -0,0 +1,40 @@
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 logging import getLogger
6
+ _LOGGER = getLogger(__name__)
7
+
8
+ from watchdog.observers import Observer
9
+ from watchdog.events import FileCreatedEvent
10
+
11
+ from spectre_core.post_processing.factory import get_event_handler_from_tag
12
+ from spectre_core.cfg import CHUNKS_DIR_PATH
13
+
14
+ class PostProcessor:
15
+ def __init__(self,
16
+ tag: str):
17
+
18
+ self._observer = Observer()
19
+
20
+ EventHandler = get_event_handler_from_tag(tag)
21
+ self._event_handler = EventHandler(tag)
22
+
23
+
24
+ def start(self):
25
+ """Start an observer to process newly created files in the chunks directory"""
26
+ self._observer.schedule(self._event_handler,
27
+ CHUNKS_DIR_PATH,
28
+ recursive=True,
29
+ event_filter=[FileCreatedEvent])
30
+
31
+ try:
32
+ _LOGGER.info("Starting the post processing thread...")
33
+ self._observer.start()
34
+ self._observer.join()
35
+ except KeyboardInterrupt:
36
+ _LOGGER.warning(("Keyboard interrupt detected. Signalling "
37
+ "the post processing thread to stop"))
38
+ self._observer.stop()
39
+ _LOGGER.warning(("Post processing thread has been successfully stopped"))
40
+
@@ -242,6 +242,7 @@ class SPECTREReceiver(BaseReceiver):
242
242
  "hop": int, # STFFT window hops by so many samples
243
243
  "chunk_key": str, # maps to the corresponding chunk class
244
244
  "event_handler_key": str, # maps to the event handler used in post processing
245
+ "watch_extension": str, # event handlers watch for files with this extension
245
246
  },
246
247
  "sweep": {
247
248
  "min_freq": float, # [Hz]
@@ -262,6 +263,7 @@ class SPECTREReceiver(BaseReceiver):
262
263
  "hop": int, # keyword arguments for the scipy STFFT class
263
264
  "chunk_key": str, # maps to the corresponding chunk class
264
265
  "event_handler_key": str, # maps to the event handler used in post processing
266
+ "watch_extension": str, # event handlers watch for files with this extension
265
267
  }
266
268
  }
267
269
 
@@ -291,17 +293,18 @@ class SPECTREReceiver(BaseReceiver):
291
293
  window_size = capture_config["window_size"]
292
294
  hop = capture_config["hop"]
293
295
  chunk_key = capture_config["chunk_key"]
294
- event_handler_key = capture_config[ "event_handler_key"]
296
+ event_handler_key = capture_config["event_handler_key"]
297
+ watch_extension = capture_config["watch_extension"]
295
298
 
296
299
  validators.center_freq_strictly_positive(min_freq)
297
300
  validators.center_freq_strictly_positive(max_freq)
298
301
  validators.samp_rate_strictly_positive(samp_rate)
299
302
  validators.bandwidth_strictly_positive(bandwidth)
300
303
  validators.nyquist_criterion(samp_rate,
301
- bandwidth)
304
+ bandwidth)
302
305
  validators.chunk_size_strictly_positive(chunk_size)
303
306
  validators.time_resolution(time_resolution,
304
- chunk_size)
307
+ chunk_size)
305
308
  validators.window(window_type,
306
309
  window_kwargs,
307
310
  window_size,
@@ -326,6 +329,8 @@ class SPECTREReceiver(BaseReceiver):
326
329
  samp_rate)
327
330
  validators.num_samples_per_step(samples_per_step,
328
331
  window_size)
332
+ validators.watch_extension(watch_extension,
333
+ "bin")
329
334
 
330
335
  # if the api latency is defined, raise a warning if the step interval is of the same order
331
336
  api_latency = self.specifications.get("api_latency")
@@ -350,6 +355,7 @@ class SPECTREReceiver(BaseReceiver):
350
355
  hop = capture_config["hop"]
351
356
  chunk_key = capture_config["chunk_key"]
352
357
  event_handler_key = capture_config["event_handler_key"]
358
+ watch_extension = capture_config["watch_extension"]
353
359
 
354
360
  validators.center_freq_strictly_positive(center_freq)
355
361
  validators.samp_rate_strictly_positive(samp_rate)
@@ -369,7 +375,8 @@ class SPECTREReceiver(BaseReceiver):
369
375
  "fixed")
370
376
  validators.gain_is_negative(IF_gain)
371
377
  validators.gain_is_negative(RF_gain)
372
-
378
+ validators.watch_extension(watch_extension,
379
+ "bin")
373
380
 
374
381
  # parent class for shared methods and attributes of SDRPlay receivers
375
382
  class SDRPlayReceiver(SPECTREReceiver):
@@ -5,7 +5,7 @@
5
5
  # SPDX-License-Identifier: GPL-3.0
6
6
  #
7
7
  # GNU Radio Python Flow Graph
8
- # Title: Options 0
8
+ # Title: tuner_1_fixed
9
9
  # GNU Radio version: 3.10.1.1
10
10
 
11
11
  # SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
@@ -50,7 +50,11 @@ class tuner_1_fixed(gr.top_block):
50
50
  ##################################################
51
51
  # Blocks
52
52
  ##################################################
53
- self.spectre_batched_file_sink_0 = spectre.batched_file_sink(CHUNKS_DIR_PATH, tag, chunk_size, samp_rate, is_sweeping)
53
+ self.spectre_batched_file_sink_0 = spectre.batched_file_sink(CHUNKS_DIR_PATH,
54
+ tag,
55
+ chunk_size,
56
+ samp_rate,
57
+ is_sweeping)
54
58
  self.sdrplay3_rspduo_0 = sdrplay3.rspduo(
55
59
  '',
56
60
  rspduo_mode="Single Tuner",
@@ -5,9 +5,13 @@
5
5
  # SPDX-License-Identifier: GPL-3.0
6
6
  #
7
7
  # GNU Radio Python Flow Graph
8
- # Title: Not titled yet
8
+ # Title: tuner_1_sweep
9
9
  # GNU Radio version: 3.10.1.1
10
10
 
11
+ # SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
12
+ # This file is part of SPECTRE
13
+ # SPDX-License-Identifier: GPL-3.0-or-later
14
+
11
15
  import sys
12
16
  import signal
13
17
  from argparse import ArgumentParser
@@ -31,7 +35,7 @@ from spectre_core.file_handlers.configs import CaptureConfig
31
35
  class tuner_1_sweep(gr.top_block):
32
36
  def __init__(self,
33
37
  capture_config: CaptureConfig):
34
- gr.top_block.__init__(self, "tuner-1-sweep", catch_exceptions=True)
38
+ gr.top_block.__init__(self, "tuner_1_sweep", catch_exceptions=True)
35
39
 
36
40
  ##################################################
37
41
  # Unpack capture config
@@ -93,8 +97,8 @@ class tuner_1_sweep(gr.top_block):
93
97
  self.sdrplay3_rspduo_0.set_debug_mode(False)
94
98
  self.sdrplay3_rspduo_0.set_sample_sequence_gaps_check(False)
95
99
  self.sdrplay3_rspduo_0.set_show_gain_changes(False)
96
- self.blocks_tag_debug_0 = blocks.tag_debug(gr.sizeof_gr_complex*1, 'freq', "freq")
97
- self.blocks_tag_debug_0.set_display(True)
100
+ # self.blocks_tag_debug_0 = blocks.tag_debug(gr.sizeof_gr_complex*1, 'freq', "freq")
101
+ # self.blocks_tag_debug_0.set_display(True)
98
102
 
99
103
 
100
104
  ##################################################
@@ -105,14 +109,6 @@ class tuner_1_sweep(gr.top_block):
105
109
  self.connect((self.sdrplay3_rspduo_0, 0), (self.spectre_sweep_driver_0, 0))
106
110
 
107
111
 
108
- def get_samp_rate(self):
109
- return self.samp_rate
110
-
111
- def set_samp_rate(self, samp_rate):
112
- self.samp_rate = samp_rate
113
- self.sdrplay3_rspduo_0.set_sample_rate(self.samp_rate, True)
114
-
115
-
116
112
 
117
113
 
118
114
  def main(capture_config: CaptureConfig,
@@ -0,0 +1,120 @@
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
+ # SPDX-FileCopyrightText: © 2024 Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
12
+ # This file is part of SPECTRE
13
+ # SPDX-License-Identifier: GPL-3.0-or-later
14
+
15
+ from gnuradio import gr
16
+ from gnuradio.filter import firdes
17
+ from gnuradio.fft import window
18
+ import sys
19
+ import signal
20
+ from argparse import ArgumentParser
21
+ from gnuradio.eng_arg import eng_float, intx
22
+ from gnuradio import eng_notation
23
+ from gnuradio import sdrplay3
24
+ from gnuradio import spectre
25
+
26
+ from spectre_core.cfg import CHUNKS_DIR_PATH
27
+ from spectre_core.file_handlers.configs import CaptureConfig
28
+
29
+
30
+ class tuner_2_fixed(gr.top_block):
31
+
32
+ def __init__(self,
33
+ capture_config: CaptureConfig):
34
+ gr.top_block.__init__(self, "tuner_2_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,
52
+ tag,
53
+ chunk_size,
54
+ samp_rate,
55
+ is_sweeping)
56
+ self.sdrplay3_rspduo_0 = sdrplay3.rspduo(
57
+ '',
58
+ rspduo_mode="Single Tuner",
59
+ antenna="Tuner 2 50 ohm",
60
+ stream_args=sdrplay3.stream_args(
61
+ output_type='fc32',
62
+ channels_size=1
63
+ ),
64
+ )
65
+ self.sdrplay3_rspduo_0.set_sample_rate(samp_rate)
66
+ self.sdrplay3_rspduo_0.set_center_freq(center_freq)
67
+ self.sdrplay3_rspduo_0.set_bandwidth(bandwidth)
68
+ self.sdrplay3_rspduo_0.set_antenna("Tuner 2 50 ohm")
69
+ self.sdrplay3_rspduo_0.set_gain_mode(False)
70
+ self.sdrplay3_rspduo_0.set_gain(IF_gain, 'IF')
71
+ self.sdrplay3_rspduo_0.set_gain(RF_gain, 'RF', False)
72
+ self.sdrplay3_rspduo_0.set_freq_corr(0)
73
+ self.sdrplay3_rspduo_0.set_dc_offset_mode(False)
74
+ self.sdrplay3_rspduo_0.set_iq_balance_mode(False)
75
+ self.sdrplay3_rspduo_0.set_agc_setpoint(-30)
76
+ self.sdrplay3_rspduo_0.set_rf_notch_filter(False)
77
+ self.sdrplay3_rspduo_0.set_dab_notch_filter(False)
78
+ self.sdrplay3_rspduo_0.set_am_notch_filter(False)
79
+ self.sdrplay3_rspduo_0.set_biasT(False)
80
+ self.sdrplay3_rspduo_0.set_stream_tags(False)
81
+ self.sdrplay3_rspduo_0.set_debug_mode(False)
82
+ self.sdrplay3_rspduo_0.set_sample_sequence_gaps_check(False)
83
+ self.sdrplay3_rspduo_0.set_show_gain_changes(False)
84
+
85
+
86
+ ##################################################
87
+ # Connections
88
+ ##################################################
89
+ self.connect((self.sdrplay3_rspduo_0, 0), (self.spectre_batched_file_sink_0, 0))
90
+
91
+
92
+ def get_samp_rate(self):
93
+ return self.samp_rate
94
+
95
+ def set_samp_rate(self, samp_rate):
96
+ self.samp_rate = samp_rate
97
+ self.sdrplay3_rspduo_0.set_sample_rate(self.samp_rate, False)
98
+
99
+
100
+
101
+
102
+ def main(capture_config: CaptureConfig,
103
+ top_block_cls=tuner_2_fixed,
104
+ options=None):
105
+
106
+ tb = top_block_cls(capture_config)
107
+
108
+ def sig_handler(sig=None, frame=None):
109
+ tb.stop()
110
+ tb.wait()
111
+
112
+ sys.exit(0)
113
+
114
+ signal.signal(signal.SIGINT, sig_handler)
115
+ signal.signal(signal.SIGTERM, sig_handler)
116
+
117
+ tb.start()
118
+
119
+ tb.wait()
120
+
@@ -0,0 +1,119 @@
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
+ from gnuradio import gr
12
+ from gnuradio.filter import firdes
13
+ from gnuradio.fft import window
14
+ import sys
15
+ import signal
16
+ from argparse import ArgumentParser
17
+ from gnuradio.eng_arg import eng_float, intx
18
+ from gnuradio import eng_notation
19
+ from gnuradio import sdrplay3
20
+ from gnuradio import spectre
21
+
22
+ from spectre_core.cfg import CHUNKS_DIR_PATH
23
+ from spectre_core.file_handlers.configs import CaptureConfig
24
+
25
+
26
+ class tuner_2_sweep(gr.top_block):
27
+ def __init__(self,
28
+ capture_config: CaptureConfig):
29
+ gr.top_block.__init__(self, "tuner_2_sweep", catch_exceptions=True)
30
+
31
+ ##################################################
32
+ # Unpack capture config
33
+ ##################################################
34
+ samp_rate = capture_config['samp_rate']
35
+ bandwidth = capture_config['bandwidth']
36
+ min_freq = capture_config['min_freq']
37
+ max_freq = capture_config['max_freq']
38
+ freq_step = capture_config['freq_step']
39
+ samples_per_step = capture_config['samples_per_step']
40
+ IF_gain = capture_config['IF_gain']
41
+ RF_gain = capture_config['RF_gain']
42
+ chunk_size = capture_config['chunk_size']
43
+ start_freq = min_freq + samp_rate/2
44
+ tag = capture_config['tag']
45
+
46
+ ##################################################
47
+ # Blocks
48
+ ##################################################
49
+ self.spectre_sweep_driver_0 = spectre.sweep_driver(min_freq,
50
+ max_freq,
51
+ freq_step,
52
+ samp_rate,
53
+ samples_per_step,
54
+ 'freq')
55
+ self.spectre_batched_file_sink_0 = spectre.batched_file_sink(CHUNKS_DIR_PATH,
56
+ tag,
57
+ chunk_size,
58
+ samp_rate,
59
+ True,
60
+ 'freq',
61
+ start_freq)
62
+ self.sdrplay3_rspduo_0 = sdrplay3.rspduo(
63
+ '',
64
+ rspduo_mode="Single Tuner",
65
+ antenna="Tuner 2 50 ohm",
66
+ stream_args=sdrplay3.stream_args(
67
+ output_type='fc32',
68
+ channels_size=1
69
+ ),
70
+ )
71
+ self.sdrplay3_rspduo_0.set_sample_rate(samp_rate, True)
72
+ self.sdrplay3_rspduo_0.set_center_freq(start_freq, True)
73
+ self.sdrplay3_rspduo_0.set_bandwidth(bandwidth)
74
+ self.sdrplay3_rspduo_0.set_antenna("Tuner 2 50 ohm")
75
+ self.sdrplay3_rspduo_0.set_gain_mode(False)
76
+ self.sdrplay3_rspduo_0.set_gain(IF_gain, 'IF', True)
77
+ self.sdrplay3_rspduo_0.set_gain(RF_gain, 'RF', True)
78
+ self.sdrplay3_rspduo_0.set_freq_corr(0)
79
+ self.sdrplay3_rspduo_0.set_dc_offset_mode(False)
80
+ self.sdrplay3_rspduo_0.set_iq_balance_mode(False)
81
+ self.sdrplay3_rspduo_0.set_agc_setpoint(-30)
82
+ self.sdrplay3_rspduo_0.set_rf_notch_filter(False)
83
+ self.sdrplay3_rspduo_0.set_dab_notch_filter(True)
84
+ self.sdrplay3_rspduo_0.set_am_notch_filter(False)
85
+ self.sdrplay3_rspduo_0.set_biasT(False)
86
+ self.sdrplay3_rspduo_0.set_stream_tags(True)
87
+ self.sdrplay3_rspduo_0.set_debug_mode(False)
88
+ self.sdrplay3_rspduo_0.set_sample_sequence_gaps_check(False)
89
+ self.sdrplay3_rspduo_0.set_show_gain_changes(False)
90
+
91
+
92
+ ##################################################
93
+ # Connections
94
+ ##################################################
95
+ self.msg_connect((self.spectre_sweep_driver_0, 'freq'), (self.sdrplay3_rspduo_0, 'freq'))
96
+ self.connect((self.sdrplay3_rspduo_0, 0), (self.spectre_batched_file_sink_0, 0))
97
+ self.connect((self.sdrplay3_rspduo_0, 0), (self.spectre_sweep_driver_0, 0))
98
+
99
+
100
+ def main(capture_config: CaptureConfig,
101
+ top_block_cls=tuner_2_sweep,
102
+ options=None):
103
+ tb = top_block_cls(capture_config)
104
+
105
+ def sig_handler(sig=None, frame=None):
106
+ tb.stop()
107
+ tb.wait()
108
+
109
+ sys.exit(0)
110
+
111
+ signal.signal(signal.SIGINT, sig_handler)
112
+ signal.signal(signal.SIGTERM, sig_handler)
113
+
114
+ tb.start(1024)
115
+
116
+ tb.wait()
117
+
118
+ if __name__ == '__main__':
119
+ main()