Compare commits

..

2 Commits

Author SHA1 Message Date
cfa1da9f4d Linting
Some checks failed
Build Project / Build Project (3.12) (pull_request) Failing after 1s
Test with tox / Test with tox (3.11) (pull_request) Failing after 1s
Test with tox / Test with tox (3.12) (pull_request) Failing after 1s
Build Sphinx Docs Set / Build Docs (pull_request) Successful in 46s
Build Project / Build Project (3.10) (pull_request) Successful in 7m7s
Test with tox / Test with tox (3.10) (pull_request) Failing after 13m42s
Build Project / Build Project (3.11) (pull_request) Successful in 4m31s
2026-04-02 10:37:42 -04:00
0e3e022084 Updated poetry.lock 2026-04-02 10:35:21 -04:00
22 changed files with 1252 additions and 1090 deletions

2186
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,3 @@
""" """
The annotations package contains tools and utilities for creating, managing, and processing annotations. The annotations package contains tools and utilities for creating, managing, and processing annotations.

View File

@ -167,7 +167,10 @@ def _estimate_spectral_bounds(signal_segment: np.ndarray, sample_rate: float) ->
else: else:
runs = _find_ranges(sig_indices, max_gap=max(1, spectral_smooth_bins // 2)) runs = _find_ranges(sig_indices, max_gap=max(1, spectral_smooth_bins // 2))
peak_idx = int(np.argmax(smoothed_fft)) peak_idx = int(np.argmax(smoothed_fft))
lo_idx, hi_idx = min(runs, key=lambda run: 0 if run[0] <= peak_idx <= run[1] else min(abs(run[0] - peak_idx), abs(run[1] - peak_idx))) lo_idx, hi_idx = min(
runs,
key=lambda run: 0 if run[0] <= peak_idx <= run[1] else min(abs(run[0] - peak_idx), abs(run[1] - peak_idx)),
)
# Prevent extremely narrow tone boxes from collapsing to just a few bins. # Prevent extremely narrow tone boxes from collapsing to just a few bins.
min_total_bw_hz = 20_000.0 min_total_bw_hz = 20_000.0

View File

@ -956,8 +956,10 @@ def get_result_sizes( # noqa: C901 # TODO: Simplify function
# Check that each class that will be augmented does not already suffice target_size # Check that each class that will be augmented does not already suffice target_size
for cls_name, target_size_value in zip(classes_to_augment, target_size): for cls_name, target_size_value in zip(classes_to_augment, target_size):
if class_sizes[cls_name] >= target_size_value: if class_sizes[cls_name] >= target_size_value:
raise ValueError(f"""target_size of {target_size_value} is already sufficed for current size of raise ValueError(
{class_sizes[cls_name]} for class: {cls_name}""") f"""target_size of {target_size_value} is already sufficed for current size of
{class_sizes[cls_name]} for class: {cls_name}"""
)
for index, class_name in enumerate(classes_to_augment): for index, class_name in enumerate(classes_to_augment):
result_sizes[class_name] = target_size[index] result_sizes[class_name] = target_size[index]

View File

@ -136,9 +136,9 @@ def from_npy(file: os.PathLike | str, legacy: bool = False) -> Recording:
annotations = [] annotations = []
except ModuleNotFoundError: except ModuleNotFoundError:
# File was pickled with utils.data.Annotation — remap to ria_toolkit_oss # File was pickled with utils.data.Annotation — remap to ria_toolkit_oss
import pickle
import sys import sys
import types import types
import ria_toolkit_oss.datatypes.annotation as _ann_mod import ria_toolkit_oss.datatypes.annotation as _ann_mod
utils_shim = types.ModuleType("utils") utils_shim = types.ModuleType("utils")

View File

@ -474,8 +474,10 @@ class Blade(SDR):
if gain_mode == "relative": if gain_mode == "relative":
if gain > 0: if gain > 0:
raise SDRParameterError("When gain_mode = 'relative', gain must be < 0. This sets \ raise SDRParameterError(
the gain relative to the maximum possible gain.") "When gain_mode = 'relative', gain must be < 0. This sets \
the gain relative to the maximum possible gain."
)
else: else:
abs_gain = rx_gain_max + gain abs_gain = rx_gain_max + gain
else: else:
@ -548,8 +550,10 @@ class Blade(SDR):
if gain_mode == "relative": if gain_mode == "relative":
if gain > 0: if gain > 0:
raise SDRParameterError("When gain_mode = 'relative', gain must be < 0. This sets\ raise SDRParameterError(
the gain relative to the maximum possible gain.") "When gain_mode = 'relative', gain must be < 0. This sets\
the gain relative to the maximum possible gain."
)
else: else:
abs_gain = tx_gain_max + gain abs_gain = tx_gain_max + gain
else: else:

View File

@ -172,8 +172,10 @@ class HackRF(SDR):
tx_gain_max = 47 tx_gain_max = 47
if gain_mode == "relative": if gain_mode == "relative":
if gain > 0: if gain > 0:
raise SDRParameterError("When gain_mode = 'relative', gain must be < 0. This \ raise SDRParameterError(
sets the gain relative to the maximum possible gain.") "When gain_mode = 'relative', gain must be < 0. This \
sets the gain relative to the maximum possible gain."
)
else: else:
abs_gain = tx_gain_max + gain abs_gain = tx_gain_max + gain
else: else:

View File

@ -274,16 +274,20 @@ class Pluto(SDR):
data = [self._convert_tx_samples(samples), self._convert_tx_samples(samples)] data = [self._convert_tx_samples(samples), self._convert_tx_samples(samples)]
else: else:
if len(recording) > 2: if len(recording) > 2:
warnings.warn("More recordings were provided than channels in the Pluto. \ warnings.warn(
Only the first two recordings will be used") "More recordings were provided than channels in the Pluto. \
Only the first two recordings will be used"
)
sample0 = self._convert_tx_samples(recording.data[0]) sample0 = self._convert_tx_samples(recording.data[0])
sample1 = self._convert_tx_samples(recording.data[1]) sample1 = self._convert_tx_samples(recording.data[1])
data = [sample0, sample1] data = [sample0, sample1]
elif isinstance(recording, list): elif isinstance(recording, list):
if len(recording) > 2: if len(recording) > 2:
warnings.warn("More recordings were provided than channels in the Pluto. \ warnings.warn(
Only the first two recordings will be used") "More recordings were provided than channels in the Pluto. \
Only the first two recordings will be used"
)
if isinstance(recording[0], np.ndarray): if isinstance(recording[0], np.ndarray):
data = [self._convert_tx_samples(recording[0]), self._convert_tx_samples(recording[1])] data = [self._convert_tx_samples(recording[0]), self._convert_tx_samples(recording[1])]
@ -423,8 +427,10 @@ class Pluto(SDR):
if gain_mode == "relative": if gain_mode == "relative":
if gain > 0: if gain > 0:
raise SDRParameterError("When gain_mode = 'relative', gain must be < 0. This sets \ raise SDRParameterError(
the gain relative to the maximum possible gain.") "When gain_mode = 'relative', gain must be < 0. This sets \
the gain relative to the maximum possible gain."
)
else: else:
abs_gain = rx_gain_max + gain abs_gain = rx_gain_max + gain
else: else:
@ -534,8 +540,10 @@ class Pluto(SDR):
if gain_mode == "relative": if gain_mode == "relative":
if gain > 0: if gain > 0:
raise SDRParameterError("When gain_mode = 'relative', gain must be < 0. This sets\ raise SDRParameterError(
the gain relative to the maximum possible gain.") "When gain_mode = 'relative', gain must be < 0. This sets\
the gain relative to the maximum possible gain."
)
else: else:
abs_gain = tx_gain_max + gain abs_gain = tx_gain_max + gain
else: else:

View File

@ -131,15 +131,19 @@ class RTLSDR(SDR):
if gain_mode == "relative": if gain_mode == "relative":
if gain > 0: if gain > 0:
raise SDRParameterError("When gain_mode = 'relative', gain must be < 0. This sets\ raise SDRParameterError(
the gain relative to the maximum possible gain.") "When gain_mode = 'relative', gain must be < 0. This sets\
the gain relative to the maximum possible gain."
)
target_gain = max_gain + gain target_gain = max_gain + gain
else: else:
target_gain = gain target_gain = gain
if target_gain < min_gain or target_gain > max_gain: if target_gain < min_gain or target_gain > max_gain:
print(f"Requested gain {target_gain} dB out of range;\ print(
clamping to valid span {min_gain}-{max_gain} dB.") f"Requested gain {target_gain} dB out of range;\
clamping to valid span {min_gain}-{max_gain} dB."
)
target_gain = min(max(target_gain, min_gain), max_gain) target_gain = min(max(target_gain, min_gain), max_gain)
target_gain = min(available_gains, key=lambda g: abs(g - target_gain)) target_gain = min(available_gains, key=lambda g: abs(g - target_gain))

View File

@ -392,8 +392,10 @@ class ThinkRF(SDR):
actual_sample_rate = self.BASE_SAMPLE_RATE / decimation actual_sample_rate = self.BASE_SAMPLE_RATE / decimation
if abs(actual_sample_rate - requested_sample_rate) > 1e3: # More than 1 kHz difference if abs(actual_sample_rate - requested_sample_rate) > 1e3: # More than 1 kHz difference
print(f"ThinkRF: Requested {requested_sample_rate/1e6:.2f} MS/s → \ print(
Using decimation={decimation} ({actual_sample_rate/1e6:.2f} MS/s)") f"ThinkRF: Requested {requested_sample_rate/1e6:.2f} MS/s → \
Using decimation={decimation} ({actual_sample_rate/1e6:.2f} MS/s)"
)
return decimation, actual_sample_rate return decimation, actual_sample_rate

View File

@ -148,8 +148,10 @@ class USRP(SDR):
gain_range = self.usrp.get_rx_gain_range() gain_range = self.usrp.get_rx_gain_range()
if gain_mode == "relative": if gain_mode == "relative":
if gain > 0: if gain > 0:
raise SDRParameterError("When gain_mode = 'relative', gain must be < 0. This sets\ raise SDRParameterError(
the gain relative to the maximum possible gain.") "When gain_mode = 'relative', gain must be < 0. This sets\
the gain relative to the maximum possible gain."
)
else: else:
# set gain relative to max # set gain relative to max
abs_gain = gain_range.stop() + gain abs_gain = gain_range.stop() + gain
@ -354,8 +356,10 @@ class USRP(SDR):
gain_range = self.usrp.get_tx_gain_range() gain_range = self.usrp.get_tx_gain_range()
if gain_mode == "relative": if gain_mode == "relative":
if gain > 0: if gain > 0:
raise SDRParameterError("When gain_mode = 'relative', gain must be < 0. This sets\ raise SDRParameterError(
the gain relative to the maximum possible gain.") "When gain_mode = 'relative', gain must be < 0. This sets\
the gain relative to the maximum possible gain."
)
else: else:
# set gain relative to max # set gain relative to max
abs_gain = gain_range.stop() + gain abs_gain = gain_range.stop() + gain

View File

@ -37,8 +37,10 @@ class Add(RecordableBlock, ProcessBlock):
samples = block.get_samples(num_samples) samples = block.get_samples(num_samples)
if len(samples) != num_samples: if len(samples) != num_samples:
raise ValueError(f"Block {self.__class__.__name__} requested {num_samples} \ raise ValueError(
from block {block.__class__.__name__} but got {len(samples)}.") f"Block {self.__class__.__name__} requested {num_samples} \
from block {block.__class__.__name__} but got {len(samples)}."
)
return samples return samples

View File

@ -1,4 +1,5 @@
import numpy as np import numpy as np
from ria_toolkit_oss.signal.block_generator.block import Block from ria_toolkit_oss.signal.block_generator.block import Block
from ria_toolkit_oss.signal.block_generator.data_types import DataType from ria_toolkit_oss.signal.block_generator.data_types import DataType

View File

@ -23,9 +23,11 @@ class ProcessBlock(Block, ABC):
) )
elif not all(isinstance(item, Block) for item in input): elif not all(isinstance(item, Block) for item in input):
raise ValueError(f"Invalid input to block '{self.__class__.__name__}'. \ raise ValueError(
f"Invalid input to block '{self.__class__.__name__}'. \
Expected a list of Block objects but got \ Expected a list of Block objects but got \
{'[' + ',' .join(f'{item.__class__.__name__}({repr(item)})' for item in input) + ']'}") {'[' + ',' .join(f'{item.__class__.__name__}({repr(item)})' for item in input) + ']'}"
)
elif len(input) != len(self.input_type): elif len(input) != len(self.input_type):
raise ValueError( raise ValueError(

View File

@ -20,8 +20,10 @@ class RecordableBlock(Block, Recordable):
:raises ValueError: If the number of samples is incorrect.""" :raises ValueError: If the number of samples is incorrect."""
samples = self.get_samples(num_samples) samples = self.get_samples(num_samples)
if len(samples) != num_samples: if len(samples) != num_samples:
raise ValueError(f"Error in block {self.__class__.__name__} record(). \ raise ValueError(
Requested {num_samples} samples but got {len(samples)}") f"Error in block {self.__class__.__name__} record(). \
Requested {num_samples} samples but got {len(samples)}"
)
metadata = self._get_metadata() metadata = self._get_metadata()
return Recording(data=samples, metadata=metadata) return Recording(data=samples, metadata=metadata)

View File

@ -39,7 +39,9 @@ class RecordingSource(SourceBlock, RecordableBlock):
:raises ValueError: If num_samples is greater than the recording length. :raises ValueError: If num_samples is greater than the recording length.
""" """
if num_samples - 1 >= self.recording.data.shape[1]: if num_samples - 1 >= self.recording.data.shape[1]:
raise ValueError(f"{num_samples} samples requested from recording source with \ raise ValueError(
{self.recording.data.shape[1]} samples available.") f"{num_samples} samples requested from recording source with \
{self.recording.data.shape[1]} samples available."
)
return self.recording.data[0, 0:num_samples] return self.recording.data[0, 0:num_samples]

View File

@ -610,8 +610,10 @@ def cut_out( # noqa: C901 # TODO: Simplify function
raise ValueError("signal must be CxN complex.") raise ValueError("signal must be CxN complex.")
if fill_type not in {"zeros", "ones", "low-snr", "avg-snr", "high-snr"}: if fill_type not in {"zeros", "ones", "low-snr", "avg-snr", "high-snr"}:
raise UserWarning("""fill_type must be "zeros", "ones", "low-snr", "avg-snr", or "high-snr", raise UserWarning(
"ones" has been selected by default""") """fill_type must be "zeros", "ones", "low-snr", "avg-snr", or "high-snr",
"ones" has been selected by default"""
)
if max_section_size < 1 or max_section_size >= n: if max_section_size < 1 or max_section_size >= n:
raise ValueError("max_section_size must be at least 1 and must be less than the length of signal.") raise ValueError("max_section_size must be at least 1 and must be less than the length of signal.")

View File

@ -4,7 +4,6 @@ import textwrap
from typing import Optional from typing import Optional
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from matplotlib.patches import Patch
import numpy as np import numpy as np
from matplotlib import gridspec from matplotlib import gridspec
from matplotlib.patches import Patch from matplotlib.patches import Patch
@ -14,7 +13,12 @@ from scipy.signal import spectrogram
from scipy.signal.windows import hann from scipy.signal.windows import hann
from ria_toolkit_oss.datatypes.recording import Recording from ria_toolkit_oss.datatypes.recording import Recording
from ria_toolkit_oss.view.tools import COLORS, decimate, extract_metadata_fields, set_path from ria_toolkit_oss.view.tools import (
COLORS,
decimate,
extract_metadata_fields,
set_path,
)
def get_fft_size(plot_length): def get_fft_size(plot_length):
@ -59,13 +63,6 @@ def view_annotations(
annotations = recording.annotations annotations = recording.annotations
# 2. Setup Color Mapping # 2. Setup Color Mapping
available_colors = [
COLORS.get("magenta", "magenta"),
COLORS.get("accent", "cyan"),
COLORS.get("light", "white"),
"lime",
]
palette = ["#2196F3", "#9C27B0", "#64B5F6", "#7B1FA2", "#5C6BC0", "#CE93D8", "#1565C0", "#7C4DFF"] palette = ["#2196F3", "#9C27B0", "#64B5F6", "#7B1FA2", "#5C6BC0", "#CE93D8", "#1565C0", "#7C4DFF"]
unique_labels = sorted(list(set(ann.label for ann in annotations if ann.label))) unique_labels = sorted(list(set(ann.label for ann in annotations if ann.label)))
label_to_color = {label: palette[i % len(palette)] for i, label in enumerate(unique_labels)} label_to_color = {label: palette[i % len(palette)] for i, label in enumerate(unique_labels)}

View File

@ -13,7 +13,12 @@ from scipy.fft import fft, fftshift
from scipy.signal.windows import hann from scipy.signal.windows import hann
from ria_toolkit_oss.datatypes.recording import Recording from ria_toolkit_oss.datatypes.recording import Recording
from ria_toolkit_oss.view.tools import COLORS, decimate, extract_metadata_fields, set_path from ria_toolkit_oss.view.tools import (
COLORS,
decimate,
extract_metadata_fields,
set_path,
)
def _add_annotations(annotations, compact_mode, show_labels, sample_rate_hz, center_freq_hz, ax2): def _add_annotations(annotations, compact_mode, show_labels, sample_rate_hz, center_freq_hz, ax2):

View File

@ -14,7 +14,10 @@ from ria_toolkit_oss.annotations import (
from ria_toolkit_oss.datatypes import Annotation from ria_toolkit_oss.datatypes import Annotation
from ria_toolkit_oss.datatypes.recording import Recording from ria_toolkit_oss.datatypes.recording import Recording
from ria_toolkit_oss.io import load_recording, to_blue, to_npy, to_sigmf, to_wav from ria_toolkit_oss.io import load_recording, to_blue, to_npy, to_sigmf, to_wav
from ria_toolkit_oss_cli.ria_toolkit_oss.common import format_frequency, format_sample_count from ria_toolkit_oss_cli.ria_toolkit_oss.common import (
format_frequency,
format_sample_count,
)
def normalize_sigmf_path(filepath): def normalize_sigmf_path(filepath):
@ -658,7 +661,12 @@ def cusum(input, label, min_duration, window_size, tolerance, annotation_type, o
@click.argument("input", type=click.Path(exists=True)) @click.argument("input", type=click.Path(exists=True))
@click.option("--threshold", type=float, required=True, help="Threshold (0.0-1.0, fraction of max magnitude)") @click.option("--threshold", type=float, required=True, help="Threshold (0.0-1.0, fraction of max magnitude)")
@click.option("--label", type=str, default=None, help="Annotation label") @click.option("--label", type=str, default=None, help="Annotation label")
@click.option("--window-size", type=int, default=None, help="Smoothing window size in samples (default: 1ms at recording sample rate)") @click.option(
"--window-size",
type=int,
default=None,
help="Smoothing window size in samples (default: 1ms at recording sample rate)",
)
@click.option( @click.option(
"--type", "--type",
"annotation_type", "annotation_type",

View File

@ -34,13 +34,12 @@ VISUALIZATION_TYPES = {
"spines", "spines",
], ],
}, },
"annotations": { "annotations": {
"function": view_annotations, "function": view_annotations,
"description": "Annotation-focused spectrogram view", "description": "Annotation-focused spectrogram view",
"options": ["channel", "dark"], "options": ["channel", "dark"],
}, },
"channels": {"function": view_channels, "description": "Multi-channel IQ and spectrogram view", "options": []}, "channels": {"function": view_channels, "description": "Multi-channel IQ and spectrogram view", "options": []},
"annotations": {"function": view_annotations, "description": "Annotated spectrogram view", "options": ["channel", "dark"]},
} }