Fix merge conflicts and port all imports from utils to ria_toolkit_oss
Some checks failed
Build Sphinx Docs Set / Build Docs (pull_request) Failing after 1s
Build Project / Build Project (3.10) (pull_request) Failing after 1s
Build Project / Build Project (3.11) (pull_request) Failing after 1s
Build Project / Build Project (3.12) (pull_request) Failing after 1s
Test with tox / Test with tox (3.10) (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

Resolves unresolved merge conflict markers left in committed files across
the annotations, view, data, and CLI packages. Updates all remaining
imports from the old utils.* namespace to ria_toolkit_oss.datatypes,
ria_toolkit_oss.io, and ria_toolkit_oss.view equivalents.
This commit is contained in:
G fordg1 2026-03-31 15:16:32 -04:00
parent ee2ce3b1f4
commit 5cfced8855
15 changed files with 26 additions and 328 deletions

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
""" """
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.
@ -54,9 +53,3 @@ from .parallel_signal_separator import (
from .qualify_slice import qualify_slice_from_annotations from .qualify_slice import qualify_slice_from_annotations
from .signal_isolation import isolate_signal from .signal_isolation import isolate_signal
from .threshold_qualifier import threshold_qualifier from .threshold_qualifier import threshold_qualifier
=======
from .cusum_annotator import annotate_with_cusum
from .energy_detector import detect_signals_energy
from .parallel_signal_separator import split_recording_annotations
from .threshold_qualifier import threshold_qualifier
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4

View File

@ -1,8 +1,4 @@
<<<<<<< HEAD
from utils.data.annotation import Annotation
=======
from ria_toolkit_oss.datatypes.annotation import Annotation from ria_toolkit_oss.datatypes.annotation import Annotation
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
# TODO figure out how to transfer labels in the merge case # TODO figure out how to transfer labels in the merge case

View File

@ -3,11 +3,7 @@ from typing import Optional
import numpy as np import numpy as np
<<<<<<< HEAD
from utils.data import Annotation, Recording
=======
from ria_toolkit_oss.datatypes import Annotation, Recording from ria_toolkit_oss.datatypes import Annotation, Recording
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
def annotate_with_cusum( def annotate_with_cusum(
@ -28,11 +24,7 @@ def annotate_with_cusum(
changes between a low and high amplitude. changes between a low and high amplitude.
:param recording: A ``Recording`` object to annotate. :param recording: A ``Recording`` object to annotate.
<<<<<<< HEAD
:type recording: ``utils.data.Recording``
=======
:type recording: ``ria_toolkit_oss.datatypes.Recording`` :type recording: ``ria_toolkit_oss.datatypes.Recording``
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
:param label: Label for the detected segments. :param label: Label for the detected segments.
:type label: str :type label: str
:param window_size: The length (in samples) of the moving average window. :param window_size: The length (in samples) of the moving average window.

View File

@ -11,11 +11,7 @@ from typing import Tuple
import numpy as np import numpy as np
from scipy.signal import filtfilt from scipy.signal import filtfilt
<<<<<<< HEAD
from utils.data import Annotation, Recording
=======
from ria_toolkit_oss.datatypes import Annotation, Recording from ria_toolkit_oss.datatypes import Annotation, Recording
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
def detect_signals_energy( def detect_signals_energy(
@ -77,13 +73,8 @@ def detect_signals_energy(
**Example**:: **Example**::
<<<<<<< HEAD
>>> from utils.io import load_recording
>>> from utils.annotations import detect_signals_energy
=======
>>> from ria.io import load_recording >>> from ria.io import load_recording
>>> from ria_toolkit_oss.annotations import detect_signals_energy >>> from ria_toolkit_oss.annotations import detect_signals_energy
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
>>> recording = load_recording("capture.sigmf") >>> recording = load_recording("capture.sigmf")
>>> # Detect with NBW frequency bounds (default, best for real signals) >>> # Detect with NBW frequency bounds (default, best for real signals)
@ -239,7 +230,7 @@ def calculate_nominal_bandwidth(
**Example**:: **Example**::
>>> from utils.annotations import calculate_nominal_bandwidth >>> from ria_toolkit_oss.annotations import calculate_nominal_bandwidth
>>> nbw, center = calculate_nominal_bandwidth(signal, sampling_rate=10e6) >>> nbw, center = calculate_nominal_bandwidth(signal, sampling_rate=10e6)
>>> print(f"NBW: {nbw/1e6:.3f} MHz, Center: {center/1e6:.3f} MHz") >>> print(f"NBW: {nbw/1e6:.3f} MHz, Center: {center/1e6:.3f} MHz")
""" """
@ -356,11 +347,7 @@ def annotate_with_obw(
**Example**:: **Example**::
<<<<<<< HEAD
>>> from utils.annotations import annotate_with_obw
=======
>>> from ria_toolkit_oss.annotations import annotate_with_obw >>> from ria_toolkit_oss.annotations import annotate_with_obw
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
>>> annotated = annotate_with_obw(recording, label="signal_obw") >>> annotated = annotate_with_obw(recording, label="signal_obw")
""" """
signal = recording.data[0] signal = recording.data[0]

View File

@ -38,11 +38,7 @@ sub-annotations.
Example: Example:
Two WiFi channels captured simultaneously: Two WiFi channels captured simultaneously:
<<<<<<< HEAD
>>> from utils.annotations import find_spectral_components
=======
>>> from ria_toolkit_oss.annotations import find_spectral_components >>> from ria_toolkit_oss.annotations import find_spectral_components
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
>>> # Detect the two distinct channels (returns relative frequencies) >>> # Detect the two distinct channels (returns relative frequencies)
>>> components = find_spectral_components(signal, sampling_rate=20e6) >>> components = find_spectral_components(signal, sampling_rate=20e6)
>>> print(f"Found {len(components)} components") >>> print(f"Found {len(components)} components")
@ -59,11 +55,7 @@ import numpy as np
from scipy import ndimage from scipy import ndimage
from scipy import signal as scipy_signal from scipy import signal as scipy_signal
<<<<<<< HEAD
from utils.data import Annotation, Recording
=======
from ria_toolkit_oss.datatypes import Annotation, Recording from ria_toolkit_oss.datatypes import Annotation, Recording
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
def find_spectral_components( def find_spectral_components(
@ -119,13 +111,8 @@ def find_spectral_components(
**Example**:: **Example**::
<<<<<<< HEAD
>>> from utils.io import load_recording
>>> from utils.annotations import find_spectral_components
=======
>>> from ria.io import load_recording >>> from ria.io import load_recording
>>> from ria_toolkit_oss.annotations import find_spectral_components >>> from ria_toolkit_oss.annotations import find_spectral_components
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
>>> recording = load_recording("capture.sigmf") >>> recording = load_recording("capture.sigmf")
>>> segment = recording.data[0][start:end] >>> segment = recording.data[0][start:end]
>>> # Components in relative (baseband) frequency >>> # Components in relative (baseband) frequency
@ -254,13 +241,8 @@ def split_annotation_by_components(
**Example**:: **Example**::
<<<<<<< HEAD
>>> from utils.io import load_recording
>>> from utils.annotations import split_annotation_by_components
=======
>>> from ria.io import load_recording >>> from ria.io import load_recording
>>> from ria_toolkit_oss.annotations import split_annotation_by_components >>> from ria_toolkit_oss.annotations import split_annotation_by_components
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
>>> recording = load_recording("capture.sigmf") >>> recording = load_recording("capture.sigmf")
>>> # Original annotation spans multiple channels >>> # Original annotation spans multiple channels
>>> original = recording.annotations[0] >>> original = recording.annotations[0]
@ -387,13 +369,8 @@ def split_recording_annotations(
**Example**:: **Example**::
<<<<<<< HEAD
>>> from utils.io import load_recording
>>> from utils.annotations import split_recording_annotations
=======
>>> from ria.io import load_recording >>> from ria.io import load_recording
>>> from ria_toolkit_oss.annotations import split_recording_annotations >>> from ria_toolkit_oss.annotations import split_recording_annotations
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
>>> recording = load_recording("capture.sigmf") >>> recording = load_recording("capture.sigmf")
>>> # Split all annotations >>> # Split all annotations
>>> split_rec = split_recording_annotations(recording) >>> split_rec = split_recording_annotations(recording)

View File

@ -1,10 +1,6 @@
import numpy as np import numpy as np
<<<<<<< HEAD
from utils.data import Recording
=======
from ria_toolkit_oss.datatypes import Recording from ria_toolkit_oss.datatypes import Recording
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
def qualify_slice_from_annotations(recording: Recording, slice_length: int): def qualify_slice_from_annotations(recording: Recording, slice_length: int):

View File

@ -1,13 +1,8 @@
import numpy as np import numpy as np
from scipy.signal import butter, lfilter from scipy.signal import butter, lfilter
<<<<<<< HEAD
from utils.data.annotation import Annotation
from utils.data.recording import Recording
=======
from ria_toolkit_oss.datatypes.annotation import Annotation from ria_toolkit_oss.datatypes.annotation import Annotation
from ria_toolkit_oss.datatypes.recording import Recording from ria_toolkit_oss.datatypes.recording import Recording
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
def isolate_signal(recording: Recording, annotation: Annotation) -> Recording: def isolate_signal(recording: Recording, annotation: Annotation) -> Recording:

View File

@ -46,29 +46,17 @@ from typing import Optional
import numpy as np import numpy as np
<<<<<<< HEAD
from utils.data import Annotation, Recording
def _find_ranges(indices, window_size):
=======
from ria_toolkit_oss.datatypes import Annotation, Recording from ria_toolkit_oss.datatypes import Annotation, Recording
def _find_ranges(indices, max_gap): def _find_ranges(indices, max_gap):
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
""" """
Groups individual indices into continuous temporal ranges. Groups individual indices into continuous temporal ranges.
Args: Args:
indices: Array of indices where the signal exceeded a threshold. indices: Array of indices where the signal exceeded a threshold.
<<<<<<< HEAD
window_size: Maximum gap allowed between indices to consider them part
of the same range.
=======
max_gap: Maximum gap allowed between indices to consider them part max_gap: Maximum gap allowed between indices to consider them part
of the same range. of the same range.
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
Returns: Returns:
A list of (start, stop) tuples representing detected signal segments. A list of (start, stop) tuples representing detected signal segments.
@ -77,30 +65,6 @@ def _find_ranges(indices, max_gap):
if len(indices) == 0: if len(indices) == 0:
return [] return []
<<<<<<< HEAD
ranges = []
start = indices[0]
in_range = False
for i in range(1, len(indices)):
# If the gap between current and previous index is within window_size,
# keep the range alive.
if indices[i] - indices[i - 1] <= window_size:
if not in_range:
# Start a new range
start = indices[i - 1]
in_range = True
else:
# Gap is too large; close the current range if one was active.
if in_range:
ranges.append((start, indices[i - 1]))
in_range = False
# Ensure the final segment is captured if the loop ends while in_range.
if in_range:
ranges.append((start, indices[-1]))
=======
start = indices[0] start = indices[0]
prev = indices[0] prev = indices[0]
ranges = [] ranges = []
@ -112,19 +76,10 @@ def _find_ranges(indices, max_gap):
prev = indices[i] prev = indices[i]
ranges.append((start, prev)) ranges.append((start, prev))
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
return ranges return ranges
<<<<<<< HEAD
def threshold_qualifier(
recording: Recording,
threshold: float,
window_size: Optional[int] = 1024,
label: Optional[str] = None,
annotation_type: Optional[str] = "standalone",
=======
def _expand_and_filter_ranges( def _expand_and_filter_ranges(
smoothed_power: np.ndarray, smoothed_power: np.ndarray,
initial_ranges: list[tuple[int, int]], initial_ranges: list[tuple[int, int]],
@ -231,7 +186,6 @@ def threshold_qualifier(
label: Optional[str] = None, label: Optional[str] = None,
annotation_type: Optional[str] = "standalone", annotation_type: Optional[str] = "standalone",
channel: int = 0, channel: int = 0,
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
) -> Recording: ) -> Recording:
""" """
Annotate a recording with bounding boxes for regions above a threshold. Annotate a recording with bounding boxes for regions above a threshold.
@ -249,27 +203,15 @@ def threshold_qualifier(
Args: Args:
recording: The Recording object containing IQ or real signal data. recording: The Recording object containing IQ or real signal data.
threshold: Sensitivity multiplier (0.0 to 1.0) applied to max power. threshold: Sensitivity multiplier (0.0 to 1.0) applied to max power.
<<<<<<< HEAD
window_size: Size of the smoothing filter and max gap for merging hits.
label: Custom string label for annotations.
annotation_type: Metadata string for the 'type' field in the annotation.
=======
window_size: Size of the smoothing filter in samples. Defaults to 1ms worth of samples. window_size: Size of the smoothing filter in samples. Defaults to 1ms worth of samples.
label: Custom string label for annotations. label: Custom string label for annotations.
annotation_type: Metadata string for the 'type' field in the annotation. annotation_type: Metadata string for the 'type' field in the annotation.
channel: Index of the channel to annotate. Defaults to 0. channel: Index of the channel to annotate. Defaults to 0.
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
Returns: Returns:
A new Recording object populated with detected Annotations. A new Recording object populated with detected Annotations.
""" """
# Extract signal and metadata # Extract signal and metadata
<<<<<<< HEAD
sample_data = recording.data[0]
sample_rate = recording.metadata["sample_rate"]
center_frequency = recording.metadata.get("center_frequency", 0)
=======
sample_data = recording.data[channel] sample_data = recording.data[channel]
sample_rate = recording.metadata["sample_rate"] sample_rate = recording.metadata["sample_rate"]
center_frequency = recording.metadata.get("center_frequency", 0) center_frequency = recording.metadata.get("center_frequency", 0)
@ -277,69 +219,11 @@ def threshold_qualifier(
if window_size is None: if window_size is None:
window_size = max(64, int(sample_rate * 0.001)) window_size = max(64, int(sample_rate * 0.001))
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
# --- 1. SIGNAL CONDITIONING --- # --- 1. SIGNAL CONDITIONING ---
# Convert to power (Magnitude squared) # Convert to power (Magnitude squared)
power_data = np.abs(sample_data) ** 2 power_data = np.abs(sample_data) ** 2
smoothing_window = np.ones(window_size) / window_size smoothing_window = np.ones(window_size) / window_size
smoothed_power = np.convolve(power_data, smoothing_window, mode="same") smoothed_power = np.convolve(power_data, smoothing_window, mode="same")
<<<<<<< HEAD
# Define thresholds based on the global peak of the smoothed signal
max_power = np.max(smoothed_power)
trigger_val = threshold * max_power # High threshold to trigger detection
boundary_val = (threshold / 2) * max_power # Low threshold to define signal edges
# --- 2. INITIAL DETECTION ---
# Identify indices that strictly exceed the high trigger
indices = np.where(smoothed_power > trigger_val)[0]
initial_ranges = _find_ranges(indices=indices, window_size=window_size)
annotations = []
threshold_base = min(sample_rate, len(sample_data))
for start, stop in initial_ranges:
if (stop - start) < (threshold_base * 0.01):
continue
# --- 3. HYSTERESIS (Boundary Expansion) ---
# Search backward from 'start' until power drops below the low boundary_val
true_start = start
while true_start > 0 and smoothed_power[true_start] > boundary_val:
true_start -= 1
# Search forward from 'stop' until power drops below the low boundary_val
true_stop = stop
while true_stop < len(smoothed_power) - 1 and smoothed_power[true_stop] > boundary_val:
true_stop += 1
# --- 4. SPECTRAL ANALYSIS (Frequency Detection) ---
signal_segment = sample_data[true_start:true_stop]
if len(signal_segment) > 0:
fft_data = np.abs(np.fft.fftshift(np.fft.fft(signal_segment)))
fft_freqs = np.fft.fftshift(np.fft.fftfreq(len(signal_segment), 1 / sample_rate))
# Determine frequency bounds where spectral energy is > 15% of segment peak
spectral_thresh = np.max(fft_data) * 0.15
sig_indices = np.where(fft_data > spectral_thresh)[0]
# Ensure the signal has some spectral width before annotating
if len(sig_indices) < 5:
continue
if len(sig_indices) > 0:
f_min, f_max = fft_freqs[sig_indices[0]], fft_freqs[sig_indices[-1]]
else:
# Default to middle half of bandwidth if no clear peaks found
f_min, f_max = -sample_rate / 4, sample_rate / 4
else:
f_min, f_max = -sample_rate / 4, sample_rate / 4
# --- 5. ANNOTATION GENERATION ---
if label is None:
label = f"{int(threshold*100)}%"
=======
group_gap_samples = _estimate_group_gap(sample_rate) group_gap_samples = _estimate_group_gap(sample_rate)
# Define thresholds using peak relative to baseline. # Define thresholds using peak relative to baseline.
@ -442,7 +326,6 @@ def threshold_qualifier(
# --- 5. ANNOTATION GENERATION --- # --- 5. ANNOTATION GENERATION ---
ann_label = label if label is not None else f"{int(threshold*100)}%" ann_label = label if label is not None else f"{int(threshold*100)}%"
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
# Pack metadata for the UI/Downstream processing # Pack metadata for the UI/Downstream processing
comment_data = { comment_data = {
@ -459,11 +342,7 @@ def threshold_qualifier(
sample_count=true_stop - true_start, sample_count=true_stop - true_start,
freq_lower_edge=center_frequency + f_min, freq_lower_edge=center_frequency + f_min,
freq_upper_edge=center_frequency + f_max, freq_upper_edge=center_frequency + f_max,
<<<<<<< HEAD
label=label,
=======
label=ann_label, label=ann_label,
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
comment=json.dumps(comment_data), comment=json.dumps(comment_data),
detail={"generator": "hysteresis_qualifier"}, detail={"generator": "hysteresis_qualifier"},
) )

View File

@ -12,7 +12,7 @@ from typing import Any, Iterator, Optional
import numpy as np import numpy as np
from numpy.typing import ArrayLike from numpy.typing import ArrayLike
from utils.data.annotation import Annotation from ria_toolkit_oss.datatypes.annotation import Annotation
PROTECTED_KEYS = ["rec_id", "timestamp"] PROTECTED_KEYS = ["rec_id", "timestamp"]
@ -66,7 +66,7 @@ class Recording:
**Examples:** **Examples:**
>>> import numpy >>> import numpy
>>> from utils.data import Recording, Annotation >>> from ria_toolkit_oss.datatypes import Recording, Annotation
>>> # Create an array of complex samples, just 1s in this case. >>> # Create an array of complex samples, just 1s in this case.
>>> samples = numpy.ones(10000, dtype=numpy.complex64) >>> samples = numpy.ones(10000, dtype=numpy.complex64)
@ -305,7 +305,7 @@ class Recording:
Create a recording and add metadata: Create a recording and add metadata:
>>> import numpy >>> import numpy
>>> from utils.data import Recording >>> from ria_toolkit_oss.datatypes import Recording
>>> >>>
>>> samples = numpy.ones(10000, dtype=numpy.complex64) >>> samples = numpy.ones(10000, dtype=numpy.complex64)
>>> metadata = { >>> metadata = {
@ -360,7 +360,7 @@ class Recording:
Create a recording and update metadata: Create a recording and update metadata:
>>> import numpy >>> import numpy
>>> from utils.data import Recording >>> from ria_toolkit_oss.datatypes import Recording
>>> samples = numpy.ones(10000, dtype=numpy.complex64) >>> samples = numpy.ones(10000, dtype=numpy.complex64)
>>> metadata = { >>> metadata = {
@ -414,7 +414,7 @@ class Recording:
Create a recording and add metadata: Create a recording and add metadata:
>>> import numpy >>> import numpy
>>> from utils.data import Recording >>> from ria_toolkit_oss.datatypes import Recording
>>> samples = numpy.ones(10000, dtype=numpy.complex64) >>> samples = numpy.ones(10000, dtype=numpy.complex64)
>>> metadata = { >>> metadata = {
@ -455,7 +455,7 @@ class Recording:
Create a recording and view it as a plot in a .png image: Create a recording and view it as a plot in a .png image:
>>> import numpy >>> import numpy
>>> from utils.data import Recording >>> from ria_toolkit_oss.datatypes import Recording
>>> samples = numpy.ones(10000, dtype=numpy.complex64) >>> samples = numpy.ones(10000, dtype=numpy.complex64)
>>> metadata = { >>> metadata = {
@ -466,14 +466,14 @@ class Recording:
>>> recording = Recording(data=samples, metadata=metadata) >>> recording = Recording(data=samples, metadata=metadata)
>>> recording.view() >>> recording.view()
""" """
from utils.view import view_sig from ria_toolkit_oss.view import view_sig
view_sig(recording=self, output_path=output_path, **kwargs) view_sig(recording=self, output_path=output_path, **kwargs)
def simple_view(self, **kwargs) -> None: def simple_view(self, **kwargs) -> None:
"""Create a plot of various signal visualizations as a PNG or SVG image. """Create a plot of various signal visualizations as a PNG or SVG image.
:param kwargs: Keyword arguments passed on to utils.view.view_signal_simple.create_plots. :param kwargs: Keyword arguments passed on to ria_toolkit_oss.view.view_signal_simple.create_plots.
:type: dict of keyword arguments :type: dict of keyword arguments
**Examples:** **Examples:**
@ -481,7 +481,7 @@ class Recording:
Create a recording and view it as a plot in a .png image: Create a recording and view it as a plot in a .png image:
>>> import numpy >>> import numpy
>>> from utils.data import Recording >>> from ria_toolkit_oss.datatypes import Recording
>>> samples = numpy.ones(10000, dtype=numpy.complex64) >>> samples = numpy.ones(10000, dtype=numpy.complex64)
>>> metadata = { >>> metadata = {
@ -492,7 +492,7 @@ class Recording:
>>> recording = Recording(data=samples, metadata=metadata) >>> recording = Recording(data=samples, metadata=metadata)
>>> recording.simple_view() >>> recording.simple_view()
""" """
from utils.view.view_signal_simple import view_simple_sig from ria_toolkit_oss.view.view_signal_simple import view_simple_sig
view_simple_sig(recording=self, **kwargs) view_simple_sig(recording=self, **kwargs)
@ -530,7 +530,7 @@ class Recording:
>>> recording = Recording(data=samples, metadata=metadata) >>> recording = Recording(data=samples, metadata=metadata)
>>> recording.view() >>> recording.view()
""" """
from utils.io.recording import to_sigmf from ria_toolkit_oss.io.recording import to_sigmf
to_sigmf(filename=filename, path=path, recording=self, overwrite=overwrite) to_sigmf(filename=filename, path=path, recording=self, overwrite=overwrite)
@ -565,7 +565,7 @@ class Recording:
>>> recording = Recording(data=samples, metadata=metadata) >>> recording = Recording(data=samples, metadata=metadata)
>>> recording.to_npy() >>> recording.to_npy()
""" """
from utils.io.recording import to_npy from ria_toolkit_oss.io.recording import to_npy
to_npy(recording=self, filename=filename, path=path, overwrite=overwrite) to_npy(recording=self, filename=filename, path=path, overwrite=overwrite)
@ -611,7 +611,7 @@ class Recording:
>>> recording = Recording(data=samples, metadata=metadata) >>> recording = Recording(data=samples, metadata=metadata)
>>> recording.to_wav() >>> recording.to_wav()
""" """
from utils.io.recording import to_wav from ria_toolkit_oss.io.recording import to_wav
return to_wav( return to_wav(
recording=self, recording=self,
@ -661,7 +661,7 @@ class Recording:
>>> recording = Recording(data=samples, metadata=metadata) >>> recording = Recording(data=samples, metadata=metadata)
>>> recording.to_blue() >>> recording.to_blue()
""" """
from utils.io.recording import to_blue from ria_toolkit_oss.io.recording import to_blue
return to_blue(recording=self, filename=filename, path=path, data_format=data_format, overwrite=overwrite) return to_blue(recording=self, filename=filename, path=path, data_format=data_format, overwrite=overwrite)

View File

@ -1,6 +1,6 @@
import numpy as np import numpy as np
from utils.signal.block_generator.block import Block from ria_toolkit_oss.signal.block_generator.block import Block
from utils.signal.block_generator.data_types import DataType from ria_toolkit_oss.signal.block_generator.data_types import DataType
class FrequencyUpConversion(Block): class FrequencyUpConversion(Block):

View File

@ -11,7 +11,7 @@ def spectrogram(rec: Recording, thumbnail: bool = False) -> Figure:
"""Create a spectrogram for the recording. """Create a spectrogram for the recording.
:param rec: Signal to plot. :param rec: Signal to plot.
:type rec: utils.data.Recording :type rec: ria_toolkit_oss.datatypes.Recording
:param thumbnail: Whether to return a small thumbnail version or full plot. :param thumbnail: Whether to return a small thumbnail version or full plot.
:type thumbnail: bool :type thumbnail: bool
@ -95,7 +95,7 @@ def iq_time_series(rec: Recording) -> Figure:
"""Create a time series plot of the real and imaginary parts of signal. """Create a time series plot of the real and imaginary parts of signal.
:param rec: Signal to plot. :param rec: Signal to plot.
:type rec: utils.data.Recording :type rec: ria_toolkit_oss.datatypes.Recording
:return: Time series plot as a Plotly figure. :return: Time series plot as a Plotly figure.
""" """
@ -125,7 +125,7 @@ def frequency_spectrum(rec: Recording) -> Figure:
"""Create a frequency spectrum plot from the recording. """Create a frequency spectrum plot from the recording.
:param rec: Input signal to plot. :param rec: Input signal to plot.
:type rec: utils.data.Recording :type rec: ria_toolkit_oss.datatypes.Recording
:return: Frequency spectrum as a Plotly figure. :return: Frequency spectrum as a Plotly figure.
""" """
@ -160,7 +160,7 @@ def constellation(rec: Recording) -> Figure:
"""Create a constellation plot from the recording. """Create a constellation plot from the recording.
:param rec: Input signal to plot. :param rec: Input signal to plot.
:type rec: utils.data.Recording :type rec: ria_toolkit_oss.datatypes.Recording
:return: Constellation as a Plotly figure. :return: Constellation as a Plotly figure.
""" """

View File

@ -13,8 +13,8 @@ from scipy.fft import fft, fftshift
from scipy.signal import spectrogram from scipy.signal import spectrogram
from scipy.signal.windows import hann from scipy.signal.windows import hann
from utils.data.recording import Recording from ria_toolkit_oss.datatypes.recording import Recording
from utils.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):
@ -58,17 +58,6 @@ def view_annotations(
sample_rate, center_frequency, _ = extract_metadata_fields(recording.metadata) sample_rate, center_frequency, _ = extract_metadata_fields(recording.metadata)
annotations = recording.annotations annotations = recording.annotations
<<<<<<< HEAD
# 2. Setup Color Mapping (No more hardcoded yellow fallback!)
# available_colors = [
# COLORS.get("magenta", "magenta"),
# COLORS.get("accent", "cyan"),
# COLORS.get("light", "white"),
# "lime",
# ]
palette = ["#FF00FF", "#00FF00", "#00FFFF", "#FFFF00", "#FF8000"]
=======
# 2. Setup Color Mapping # 2. Setup Color Mapping
available_colors = [ available_colors = [
COLORS.get("magenta", "magenta"), COLORS.get("magenta", "magenta"),
@ -78,7 +67,6 @@ def view_annotations(
] ]
palette = ["#2196F3", "#9C27B0", "#64B5F6", "#7B1FA2", "#5C6BC0", "#CE93D8", "#1565C0", "#7C4DFF"] palette = ["#2196F3", "#9C27B0", "#64B5F6", "#7B1FA2", "#5C6BC0", "#CE93D8", "#1565C0", "#7C4DFF"]
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
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)}
@ -87,11 +75,6 @@ def view_annotations(
complex_signal, NFFT=256, Fs=sample_rate, Fc=center_frequency, noverlap=128, cmap="twilight" complex_signal, NFFT=256, Fs=sample_rate, Fc=center_frequency, noverlap=128, cmap="twilight"
) )
<<<<<<< HEAD
# 4. Draw Annotations
for annotation in annotations:
# --- DEFINING VARIABLES FIRST ---
=======
# 4. Draw Annotations (highest threshold % first so lower % renders on top) # 4. Draw Annotations (highest threshold % first so lower % renders on top)
def _threshold_sort_key(ann): def _threshold_sort_key(ann):
try: try:
@ -100,21 +83,13 @@ def view_annotations(
return 0 return 0
for annotation in sorted(annotations, key=_threshold_sort_key, reverse=True): for annotation in sorted(annotations, key=_threshold_sort_key, reverse=True):
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
t_start = annotation.sample_start / sample_rate t_start = annotation.sample_start / sample_rate
t_width = annotation.sample_count / sample_rate t_width = annotation.sample_count / sample_rate
f_start = annotation.freq_lower_edge f_start = annotation.freq_lower_edge
f_height = annotation.freq_upper_edge - annotation.freq_lower_edge f_height = annotation.freq_upper_edge - annotation.freq_lower_edge
<<<<<<< HEAD
# Look up the color for this specific label
ann_color = label_to_color.get(annotation.label, "gray") ann_color = label_to_color.get(annotation.label, "gray")
# Draw the Rectangle
=======
ann_color = label_to_color.get(annotation.label, "gray")
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
rect = plt.Rectangle( rect = plt.Rectangle(
(t_start, f_start), t_width, f_height, linewidth=1.5, edgecolor=ann_color, facecolor="none", alpha=0.8 (t_start, f_start), t_width, f_height, linewidth=1.5, edgecolor=ann_color, facecolor="none", alpha=0.8
) )
@ -130,11 +105,7 @@ def view_annotations(
ax.set_title(title, fontsize=title_fontsize, pad=20) ax.set_title(title, fontsize=title_fontsize, pad=20)
ax.set_xlabel("Time (s)", fontsize=12) ax.set_xlabel("Time (s)", fontsize=12)
ax.set_ylabel("Frequency (MHz)", fontsize=12) ax.set_ylabel("Frequency (MHz)", fontsize=12)
<<<<<<< HEAD
ax.grid(alpha=0.1) # Add faint grid
=======
ax.grid(alpha=0.1) ax.grid(alpha=0.1)
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
output_path, _ = set_path(output_path=output_path) output_path, _ = set_path(output_path=output_path)
plt.savefig(output_path, dpi=dpi, bbox_inches="tight") plt.savefig(output_path, dpi=dpi, bbox_inches="tight")

View File

@ -12,8 +12,8 @@ import numpy as np
from scipy.fft import fft, fftshift from scipy.fft import fft, fftshift
from scipy.signal.windows import hann from scipy.signal.windows import hann
from utils.data.recording import Recording from ria_toolkit_oss.datatypes.recording import Recording
from utils.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

@ -11,13 +11,8 @@ from ria_toolkit_oss.annotations import (
split_recording_annotations, split_recording_annotations,
threshold_qualifier, threshold_qualifier,
) )
<<<<<<< HEAD
from ria_toolkit_oss.data import Annotation
from ria_toolkit_oss.data.recording import Recording
=======
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
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
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
@ -55,15 +50,6 @@ def detect_input_format(filepath):
def determine_output_path(input_path, output_path, fmt, quiet, overwrite): def determine_output_path(input_path, output_path, fmt, quiet, overwrite):
input_path = Path(input_path) input_path = Path(input_path)
<<<<<<< HEAD
if output_path:
target = Path(output_path)
final_path = target
else:
annotated_name = f"{input_path.stem}_annotated"
target = input_path.with_name(f"{annotated_name}{input_path.suffix}")
=======
input_is_annotated = input_path.stem.endswith("_annotated") input_is_annotated = input_path.stem.endswith("_annotated")
if output_path: if output_path:
@ -73,7 +59,6 @@ def determine_output_path(input_path, output_path, fmt, quiet, overwrite):
target = input_path target = input_path
else: else:
target = input_path.with_name(f"{input_path.stem}_annotated{input_path.suffix}") target = input_path.with_name(f"{input_path.stem}_annotated{input_path.suffix}")
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
if fmt == "sigmf": if fmt == "sigmf":
final_path = normalize_sigmf_path(target) final_path = normalize_sigmf_path(target)
@ -84,15 +69,10 @@ def determine_output_path(input_path, output_path, fmt, quiet, overwrite):
if not quiet: if not quiet:
click.echo(f"Saving to: {final_path}") click.echo(f"Saving to: {final_path}")
<<<<<<< HEAD
if final_path.exists() and not overwrite and final_path != input_path:
click.echo(f"Error: {final_path} already exists. Use --overwrite to replace it.", err=True)
=======
# Always allow writing to _annotated files; guard against overwriting originals # Always allow writing to _annotated files; guard against overwriting originals
target_is_annotated = final_path.stem.endswith("_annotated") target_is_annotated = final_path.stem.endswith("_annotated")
if final_path.exists() and not target_is_annotated and final_path != input_path: if final_path.exists() and not target_is_annotated and final_path != input_path:
click.echo(f"Error: {final_path} is not an annotated file and cannot be overwritten.", err=True) click.echo(f"Error: {final_path} is not an annotated file and cannot be overwritten.", err=True)
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
return None return None
return final_path return final_path
@ -250,13 +230,8 @@ def list(input, verbose):
\b \b
Examples: Examples:
<<<<<<< HEAD
utils annotate list recording.sigmf-data
utils annotate list signal.npy --verbose
=======
ria annotate list recording.sigmf-data ria annotate list recording.sigmf-data
ria annotate list signal.npy --verbose ria annotate list signal.npy --verbose
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
""" """
try: try:
recording = load_recording(input) recording = load_recording(input)
@ -324,13 +299,8 @@ def add(input, start, count, label, freq_lower, freq_upper, comment, annotation_
\b \b
Examples: Examples:
<<<<<<< HEAD
utils annotate add file.npy --start 1000 --count 500 --label wifi
utils annotate add signal.sigmf-data --start 0 --count 1000 --label burst --comment "Strong signal"
=======
ria annotate add file.npy --start 1000 --count 500 --label wifi ria annotate add file.npy --start 1000 --count 500 --label wifi
ria annotate add signal.sigmf-data --start 0 --count 1000 --label burst --comment "Strong signal" ria annotate add signal.sigmf-data --start 0 --count 1000 --label burst --comment "Strong signal"
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
""" """
try: try:
recording = load_recording(input) recording = load_recording(input)
@ -412,21 +382,12 @@ def add(input, start, count, label, freq_lower, freq_upper, comment, annotation_
def remove(input, index, output, overwrite, quiet): def remove(input, index, output, overwrite, quiet):
"""Remove annotation by index. """Remove annotation by index.
<<<<<<< HEAD
Use 'utils annotate list' to see annotation indices.
\b
Examples:
utils annotate remove signal.sigmf-data 2
utils annotate remove file.npy 0
=======
Use 'ria annotate list' to see annotation indices. Use 'ria annotate list' to see annotation indices.
\b \b
Examples: Examples:
ria annotate remove signal.sigmf-data 2 ria annotate remove signal.sigmf-data 2
ria annotate remove file.npy 0 ria annotate remove file.npy 0
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
""" """
try: try:
recording = load_recording(input) recording = load_recording(input)
@ -475,13 +436,8 @@ def clear(input, output, overwrite, force, quiet):
\b \b
Examples: Examples:
<<<<<<< HEAD
utils annotate clear signal.sigmf-data
utils annotate clear file.npy --force
=======
ria annotate clear signal.sigmf-data ria annotate clear signal.sigmf-data
ria annotate clear file.npy --force ria annotate clear file.npy --force
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
""" """
try: try:
recording = load_recording(input) recording = load_recording(input)
@ -576,17 +532,10 @@ def energy(
\b \b
Examples: Examples:
<<<<<<< HEAD
utils annotate energy capture.sigmf-data --label burst
utils annotate energy signal.npy --threshold 1.5 --min-distance 10000
utils annotate energy signal.sigmf-data --freq-method obw
utils annotate energy signal.sigmf-data --freq-method full-detected
=======
ria annotate energy capture.sigmf-data --label burst ria annotate energy capture.sigmf-data --label burst
ria annotate energy signal.npy --threshold 1.5 --min-distance 10000 ria annotate energy signal.npy --threshold 1.5 --min-distance 10000
ria annotate energy signal.sigmf-data --freq-method obw ria annotate energy signal.sigmf-data --freq-method obw
ria annotate energy signal.sigmf-data --freq-method full-detected ria annotate energy signal.sigmf-data --freq-method full-detected
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
""" """
try: try:
@ -662,13 +611,8 @@ def cusum(input, label, min_duration, window_size, tolerance, annotation_type, o
\b \b
Examples: Examples:
<<<<<<< HEAD
utils annotate cusum signal.sigmf-data --min-duration 5.0
utils annotate cusum data.npy --min-duration 10.0 --label state
=======
ria annotate cusum signal.sigmf-data --min-duration 5.0 ria annotate cusum signal.sigmf-data --min-duration 5.0
ria annotate cusum data.npy --min-duration 10.0 --label state ria annotate cusum data.npy --min-duration 10.0 --label state
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
""" """
try: try:
recording = load_recording(input) recording = load_recording(input)
@ -714,11 +658,7 @@ 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")
<<<<<<< HEAD
@click.option("--window-size", type=int, default=1024, help="Smoothing window size")
=======
@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)")
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
@click.option( @click.option(
"--type", "--type",
"annotation_type", "annotation_type",
@ -726,18 +666,11 @@ def cusum(input, label, min_duration, window_size, tolerance, annotation_type, o
default="standalone", default="standalone",
help="Annotation type", help="Annotation type",
) )
<<<<<<< HEAD
@click.option("--output", "-o", type=click.Path(), help="Output file path")
@click.option("--overwrite", is_flag=True, help="Overwrite input file (non-SigMF only)")
@click.option("--quiet", is_flag=True, help="Quiet mode")
def threshold(input, threshold, label, window_size, annotation_type, output, overwrite, quiet):
=======
@click.option("--channel", type=int, default=0, help="Channel index to annotate (default: 0)") @click.option("--channel", type=int, default=0, help="Channel index to annotate (default: 0)")
@click.option("--output", "-o", type=click.Path(), help="Output file path") @click.option("--output", "-o", type=click.Path(), help="Output file path")
@click.option("--overwrite", is_flag=True, help="Overwrite input file (non-SigMF only)") @click.option("--overwrite", is_flag=True, help="Overwrite input file (non-SigMF only)")
@click.option("--quiet", is_flag=True, help="Quiet mode") @click.option("--quiet", is_flag=True, help="Quiet mode")
def threshold(input, threshold, label, window_size, annotation_type, channel, output, overwrite, quiet): def threshold(input, threshold, label, window_size, annotation_type, channel, output, overwrite, quiet):
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
"""Auto-detect signals using threshold method. """Auto-detect signals using threshold method.
Detects samples above a percentage of maximum magnitude. Best for simple Detects samples above a percentage of maximum magnitude. Best for simple
@ -745,13 +678,8 @@ def threshold(input, threshold, label, window_size, annotation_type, channel, ou
\b \b
Examples: Examples:
<<<<<<< HEAD
utils annotate threshold signal.sigmf-data --threshold 0.7 --label wifi
utils annotate threshold data.npy --threshold 0.5 --window-size 2048
=======
ria annotate threshold signal.sigmf-data --threshold 0.7 --label wifi ria annotate threshold signal.sigmf-data --threshold 0.7 --label wifi
ria annotate threshold data.npy --threshold 0.5 --window-size 2048 ria annotate threshold data.npy --threshold 0.5 --window-size 2048
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
""" """
if not (0.0 <= threshold <= 1.0): if not (0.0 <= threshold <= 1.0):
raise click.ClickException(f"--threshold must be between 0.0 and 1.0, got {threshold}") raise click.ClickException(f"--threshold must be between 0.0 and 1.0, got {threshold}")
@ -766,12 +694,8 @@ def threshold(input, threshold, label, window_size, annotation_type, channel, ou
if not quiet: if not quiet:
click.echo("\nDetecting signals using threshold qualifier...") click.echo("\nDetecting signals using threshold qualifier...")
click.echo(f" Threshold: {threshold * 100:.1f}% of max magnitude") click.echo(f" Threshold: {threshold * 100:.1f}% of max magnitude")
<<<<<<< HEAD
click.echo(f" Window size: {window_size} samples")
=======
click.echo(f" Window size: {'auto (1ms)' if window_size is None else f'{window_size} samples'}") click.echo(f" Window size: {'auto (1ms)' if window_size is None else f'{window_size} samples'}")
click.echo(f" Channel: {channel}") click.echo(f" Channel: {channel}")
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
try: try:
initial_count = len(recording.annotations) initial_count = len(recording.annotations)
@ -781,10 +705,7 @@ def threshold(input, threshold, label, window_size, annotation_type, channel, ou
window_size=window_size, window_size=window_size,
label=label, label=label,
annotation_type=annotation_type, annotation_type=annotation_type,
<<<<<<< HEAD
=======
channel=channel, channel=channel,
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
) )
added = len(recording.annotations) - initial_count added = len(recording.annotations) - initial_count
@ -833,17 +754,10 @@ def separate(input, indices, nfft, noise_threshold_db, min_component_bw, output,
\b \b
Examples: Examples:
<<<<<<< HEAD
utils annotate separate capture.sigmf-data
utils annotate separate signal.npy --indices 0,1,2
utils annotate separate data.sigmf-data --noise-threshold-db -70
utils annotate separate signal.npy --min-component-bw 100000
=======
ria annotate separate capture.sigmf-data ria annotate separate capture.sigmf-data
ria annotate separate signal.npy --indices 0,1,2 ria annotate separate signal.npy --indices 0,1,2
ria annotate separate data.sigmf-data --noise-threshold-db -70 ria annotate separate data.sigmf-data --noise-threshold-db -70
ria annotate separate signal.npy --min-component-bw 100000 ria annotate separate signal.npy --min-component-bw 100000
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
""" """
try: try:

View File

@ -2,10 +2,8 @@
""" """
This module contains all the CLI bindings for the ria package. This module contains all the CLI bindings for the ria package.
""" """
<<<<<<< HEAD
=======
>>>>>>> 2bb2d9d5a780dbc17172135a5a1f10eba14b1af4
from .annotate import annotate from .annotate import annotate
from .capture import capture from .capture import capture
from .combine import combine from .combine import combine