spectre-core 0.0.7__tar.gz → 0.0.9__tar.gz
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-0.0.7 → spectre_core-0.0.9}/PKG-INFO +2 -2
- {spectre_core-0.0.7 → spectre_core-0.0.9}/README.md +1 -1
- {spectre_core-0.0.7 → spectre_core-0.0.9}/pyproject.toml +1 -1
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/plotting/panel_stack.py +3 -1
- {spectre_core-0.0.7/src/spectre_core/watchdog → spectre_core-0.0.9/src/spectre_core/post_processing}/__init__.py +1 -1
- spectre_core-0.0.9/src/spectre_core/post_processing/base.py +132 -0
- {spectre_core-0.0.7/src/spectre_core/watchdog → spectre_core-0.0.9/src/spectre_core/post_processing}/factory.py +4 -4
- {spectre_core-0.0.7/src/spectre_core/watchdog → spectre_core-0.0.9/src/spectre_core/post_processing}/library/fixed/event_handler.py +8 -9
- {spectre_core-0.0.7/src/spectre_core/watchdog → spectre_core-0.0.9/src/spectre_core/post_processing}/library/sweep/event_handler.py +9 -10
- spectre_core-0.0.9/src/spectre_core/post_processing/post_processor.py +40 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/receivers/base.py +11 -4
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/receivers/library/rspduo/gr/tuner_1_fixed.py +6 -2
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/receivers/library/rspduo/gr/tuner_1_sweep.py +8 -12
- spectre_core-0.0.9/src/spectre_core/receivers/library/rspduo/gr/tuner_2_fixed.py +120 -0
- spectre_core-0.0.9/src/spectre_core/receivers/library/rspduo/gr/tuner_2_sweep.py +119 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/receivers/library/rspduo/receiver.py +34 -5
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/receivers/library/test/receiver.py +45 -20
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/receivers/validators.py +67 -29
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/web_fetch/callisto.py +1 -1
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core.egg-info/PKG-INFO +2 -2
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core.egg-info/SOURCES.txt +11 -9
- spectre_core-0.0.7/src/spectre_core/watchdog/base.py +0 -105
- spectre_core-0.0.7/src/spectre_core/watchdog/post_processor.py +0 -50
- {spectre_core-0.0.7 → spectre_core-0.0.9}/LICENSE +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/setup.cfg +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/__init__.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/cfg.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/chunks/__init__.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/chunks/base.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/chunks/chunk_register.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/chunks/factory.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/chunks/library/__init__.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/chunks/library/callisto/__init__.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/chunks/library/callisto/chunk.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/chunks/library/fixed/__init__.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/chunks/library/fixed/chunk.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/chunks/library/sweep/__init__.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/chunks/library/sweep/chunk.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/dynamic_imports.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/exceptions.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/file_handlers/base.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/file_handlers/configs.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/file_handlers/json.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/file_handlers/text.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/logging.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/plotting/__init__.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/plotting/base.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/plotting/factory.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/plotting/format.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/plotting/library/__init__.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/plotting/library/frequency_cuts/panel.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/plotting/library/integral_over_frequency/panel.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/plotting/library/spectrogram/panel.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/plotting/library/time_cuts/panel.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/plotting/panel_register.py +0 -0
- {spectre_core-0.0.7/src/spectre_core/watchdog → spectre_core-0.0.9/src/spectre_core/post_processing}/event_handler_register.py +0 -0
- {spectre_core-0.0.7/src/spectre_core/watchdog → spectre_core-0.0.9/src/spectre_core/post_processing}/library/__init__.py +0 -0
- {spectre_core-0.0.7/src/spectre_core/receivers/library/rsp1a → spectre_core-0.0.9/src/spectre_core/post_processing/library/fixed}/__init__.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/receivers/__init__.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/receivers/factory.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/receivers/library/__init__.py +0 -0
- {spectre_core-0.0.7/src/spectre_core/receivers/library/rsp1a/gr → spectre_core-0.0.9/src/spectre_core/receivers/library/rsp1a}/__init__.py +0 -0
- {spectre_core-0.0.7/src/spectre_core/receivers/library/rspduo → spectre_core-0.0.9/src/spectre_core/receivers/library/rsp1a/gr}/__init__.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/receivers/library/rsp1a/gr/fixed.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/receivers/library/rsp1a/gr/sweep.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/receivers/library/rsp1a/receiver.py +0 -0
- {spectre_core-0.0.7/src/spectre_core/receivers/library/rspduo/gr → spectre_core-0.0.9/src/spectre_core/receivers/library/rspduo}/__init__.py +0 -0
- {spectre_core-0.0.7/src/spectre_core/receivers/library/test → spectre_core-0.0.9/src/spectre_core/receivers/library/rspduo/gr}/__init__.py +0 -0
- {spectre_core-0.0.7/src/spectre_core/receivers/library/test/gr → spectre_core-0.0.9/src/spectre_core/receivers/library/test}/__init__.py +0 -0
- {spectre_core-0.0.7/src/spectre_core/watchdog/library/fixed → spectre_core-0.0.9/src/spectre_core/receivers/library/test/gr}/__init__.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/receivers/library/test/gr/cosine_signal_1.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/receivers/library/test/gr/tagged_staircase.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/receivers/receiver_register.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/spectrograms/__init__.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/spectrograms/analytical.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/spectrograms/array_operations.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/spectrograms/spectrogram.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core/spectrograms/transform.py +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core.egg-info/dependency_links.txt +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core.egg-info/requires.txt +0 -0
- {spectre_core-0.0.7 → spectre_core-0.0.9}/src/spectre_core.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: spectre-core
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.9
|
4
4
|
Summary: The core Python package used by the spectre program.
|
5
5
|
Maintainer-email: Jimmy Fitzpatrick <jcfitzpatrick12@gmail.com>
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
@@ -698,7 +698,7 @@ Requires-Dist: watchdog==4.0.0
|
|
698
698
|
|
699
699
|
:loudspeaker: **This project is under active development. Contributors welcome.** :loudspeaker:
|
700
700
|
|
701
|
-
```spectre-core``` provides a SDR receiver-agnostic digital signal processing framework. It is the core Python package used by the [`spectre`](https://github.com/jcfitzpatrick12/spectre.git) program.
|
701
|
+
```spectre-core``` provides a SDR receiver-agnostic digital signal processing framework. It is the core Python package used by the [`spectre`](https://github.com/jcfitzpatrick12/spectre.git) program.
|
702
702
|
|
703
703
|
|
704
704
|
## Installation
|
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
:loudspeaker: **This project is under active development. Contributors welcome.** :loudspeaker:
|
6
6
|
|
7
|
-
```spectre-core``` provides a SDR receiver-agnostic digital signal processing framework. It is the core Python package used by the [`spectre`](https://github.com/jcfitzpatrick12/spectre.git) program.
|
7
|
+
```spectre-core``` provides a SDR receiver-agnostic digital signal processing framework. It is the core Python package used by the [`spectre`](https://github.com/jcfitzpatrick12/spectre.git) program.
|
8
8
|
|
9
9
|
|
10
10
|
## Installation
|
@@ -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,
|
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] = []
|
@@ -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.
|
6
|
-
from spectre_core.
|
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
|
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.
|
11
|
-
from spectre_core.
|
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,
|
20
|
-
|
21
|
-
|
22
|
-
|
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.
|
12
|
-
from spectre_core.
|
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,
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
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[
|
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
|
-
|
304
|
+
bandwidth)
|
302
305
|
validators.chunk_size_strictly_positive(chunk_size)
|
303
306
|
validators.time_resolution(time_resolution,
|
304
|
-
|
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:
|
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,
|
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:
|
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, "
|
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
|
+
|