cli #15

Merged
madrigal merged 28 commits from cli into main 2025-12-22 10:42:57 -05:00
14 changed files with 60 additions and 1651 deletions
Showing only changes of commit 14539d9269 - Show all commits

View File

@ -101,6 +101,7 @@ pylint = "^3.2.6" # For pyreverse, to automate the creation of UML diagrams
[tool.poetry.scripts] [tool.poetry.scripts]
ria = "ria_toolkit_oss.ria_toolkit_oss_cli.cli:cli" ria = "ria_toolkit_oss.ria_toolkit_oss_cli.cli:cli"
ria-tools = "ria_toolkit_oss.ria_toolkit_oss_cli.cli:cli"
[tool.black] [tool.black]
line-length = 119 line-length = 119

View File

@ -655,7 +655,6 @@ class Recording:
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)
def trim(self, num_samples: int, start_sample: Optional[int] = 0) -> Recording: def trim(self, num_samples: int, start_sample: Optional[int] = 0) -> Recording:
"""Trim Recording samples to a desired length, shifting annotations to maintain alignment. """Trim Recording samples to a desired length, shifting annotations to maintain alignment.

View File

@ -9,7 +9,6 @@ import os
import re import re
import struct import struct
from datetime import timezone from datetime import timezone
from typing import Optional
from typing import Any, List, Optional from typing import Any, List, Optional
import numpy as np import numpy as np
@ -21,7 +20,6 @@ from sigmf.utils import get_data_type_str
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
_BLUE_META_PREFIX = "META_" _BLUE_META_PREFIX = "META_"
_BLUE_META_TAG_MAX_LEN = 60 _BLUE_META_TAG_MAX_LEN = 60
_BLUE_SKIP_METADATA_KEYS = {"blue_data_format", "blue_endian", "blue_keywords"} _BLUE_SKIP_METADATA_KEYS = {"blue_data_format", "blue_endian", "blue_keywords"}

View File

@ -238,6 +238,7 @@ def determine_output_format(output, output_format, output_dir):
# Main command # Main command
# ============================================================================ # ============================================================================
@click.command() @click.command()
@click.argument("inputs", nargs=-1, required=True, type=click.Path(exists=True)) @click.argument("inputs", nargs=-1, required=True, type=click.Path(exists=True))
@click.argument("output", nargs=1, required=True, type=click.Path()) @click.argument("output", nargs=1, required=True, type=click.Path())
@ -302,7 +303,7 @@ def capture(
Examples: Examples:
utils capture -d hackrf -s 2e6 -f 2.44e6 -b 2e6 utils capture -d hackrf -s 2e6 -f 2.44e6 -b 2e6
utils capture -d pluto -s 1e6 -f 2e9 -b 2e6 -n 50 utils capture -d pluto -s 1e6 -f 2e9 -b 2e6 -n 50
""" """
# Load config file if specified # Load config file if specified

View File

@ -6,16 +6,16 @@ from pathlib import Path
import click import click
import numpy as np import numpy as np
from ria_toolkit_oss_cli.ria_toolkit_oss.common import (
from utils.data import Recording
from utils.io import from_npy_legacy, load_recording
from utils_cli.utils.common import (
echo_progress, echo_progress,
echo_verbose, echo_verbose,
format_sample_count, format_sample_count,
save_recording, save_recording,
) )
from ria_toolkit_oss.datatypes import Recording
from ria_toolkit_oss.io import from_npy_legacy, load_recording
def load_recording_list(inputs, legacy, verbose, quiet): def load_recording_list(inputs, legacy, verbose, quiet):
recordings = [] recordings = []
@ -395,10 +395,10 @@ def combine(
Examples: Examples:
# Concatenate recordings # Concatenate recordings
utils combine chunk1.npy chunk2.npy chunk3.npy full.npy utils combine chunk1.npy chunk2.npy chunk3.npy full.npy
\b \b
# Add signal and noise # Add signal and noise
utils combine signal.npy noise.npy noisy.npy --mode add\n utils combine signal.npy noise.npy noisy.npy --mode add\n
\b \b
# Add with center alignment # Add with center alignment
utils combine long.npy short.npy output.npy --mode add --align-mode pad-center\n utils combine long.npy short.npy output.npy --mode add --align-mode pad-center\n
\b \b

View File

@ -9,7 +9,8 @@ from .convert import convert
# Import all command functions # Import all command functions
from .discover import discover from .discover import discover
from .generate import generate
# from .generate import generate
from .init import init from .init import init
from .split import split from .split import split
from .transform import transform from .transform import transform
@ -17,9 +18,7 @@ from .transmit import transmit
from .view import view from .view import view
# Aliases # Aliases
synth = generate # synth = generate
# All commands will be automatically registered by cli.py # All commands will be automatically registered by cli.py
# Commands must be click.Command instances # Commands must be click.Command instances

View File

@ -8,7 +8,7 @@ import click
import yaml import yaml
from ria_toolkit_oss.datatypes.recording import Recording from ria_toolkit_oss.datatypes.recording import Recording
from src.ria_toolkit_oss.io.recording import to_blue, to_npy, to_sigmf, to_wav from ria_toolkit_oss.io.recording import to_blue, to_npy, to_sigmf, to_wav
def load_yaml_config(config_file: str) -> Dict[str, Any]: def load_yaml_config(config_file: str) -> Dict[str, Any]:

View File

@ -4,8 +4,15 @@ import os
from pathlib import Path from pathlib import Path
import click import click
from ria_toolkit_oss_cli.ria_toolkit_oss.common import (
check_for_overwriting,
detect_file_format,
echo_progress,
echo_verbose,
format_sample_count,
)
from utils.io.recording import ( from ria_toolkit_oss.io.recording import (
from_npy, from_npy,
load_recording, load_recording,
to_blue, to_blue,
@ -13,13 +20,6 @@ from utils.io.recording import (
to_sigmf, to_sigmf,
to_wav, to_wav,
) )
from utils_cli.utils.common import (
check_for_overwriting,
detect_file_format,
echo_progress,
echo_verbose,
format_sample_count,
)
from .config import load_user_config from .config import load_user_config
@ -97,7 +97,7 @@ def convert( # noqa: C901
If OUTPUT is not specified, the input filename is used with a new extension If OUTPUT is not specified, the input filename is used with a new extension
based on the --format option. based on the --format option.
\b \b
Examples: Examples:
# SigMF to NumPy (explicit output) # SigMF to NumPy (explicit output)

View File

@ -4,9 +4,7 @@ from pathlib import Path
import click import click
import numpy as np import numpy as np
from ria_toolkit_oss_cli.ria_toolkit_oss.common import (
from utils.io import from_npy_legacy, load_recording
from utils_cli.utils.common import (
detect_file_format, detect_file_format,
echo_progress, echo_progress,
echo_verbose, echo_verbose,
@ -14,6 +12,8 @@ from utils_cli.utils.common import (
save_recording, save_recording,
) )
from ria_toolkit_oss.io import from_npy_legacy, load_recording
def get_output_extension(format_name): def get_output_extension(format_name):
"""Get file extension for format name.""" """Get file extension for format name."""

View File

@ -7,17 +7,17 @@ import os
from pathlib import Path from pathlib import Path
import click import click
from ria_toolkit_oss_cli.ria_toolkit_oss.common import (
from utils.data.recording import Recording
from utils.io.recording import load_recording
from utils.transforms import iq_augmentations, iq_channel_models, iq_impairments
from utils_cli.utils.common import (
echo_progress, echo_progress,
echo_verbose, echo_verbose,
format_sample_count, format_sample_count,
save_recording, save_recording,
) )
from ria_toolkit_oss.datatypes.recording import Recording
from ria_toolkit_oss.io.recording import load_recording
from ria_toolkit_oss.transforms import iq_augmentations, iq_impairments
def get_available_transforms(module): def get_available_transforms(module):
"""Get list of public transform functions from a module. """Get list of public transform functions from a module.
@ -89,7 +89,7 @@ def show_transform_help(transform_name, func):
def quick_view_transform(recording, output_path, title="Transform Result"): def quick_view_transform(recording, output_path, title="Transform Result"):
"""Create a quick PNG visualization of transformed recording using constellation plot.""" """Create a quick PNG visualization of transformed recording using constellation plot."""
try: try:
from utils.view.view_signal_simple import view_simple_sig from ria_toolkit_oss.view.view_signal_simple import view_simple_sig
# Create PNG in same directory as output # Create PNG in same directory as output
output_dir = Path(output_path).parent output_dir = Path(output_path).parent
@ -249,6 +249,7 @@ def load_input(input, verbose):
echo_verbose(f"Loaded {format_sample_count(recording.data.shape[-1])} samples", verbose) echo_verbose(f"Loaded {format_sample_count(recording.data.shape[-1])} samples", verbose)
return recording return recording
@click.group() @click.group()
def transform(): def transform():
"""Apply signal transformations to recordings. """Apply signal transformations to recordings.
@ -260,20 +261,20 @@ def transform():
Each operation is applied independently. Chain multiple transforms by Each operation is applied independently. Chain multiple transforms by
running this command multiple times. running this command multiple times.
\b \b
Examples: Examples:
# List available augmentations # List available augmentations
utils transform augment --list ria_toolkit_oss transform augment --list
\b \b
# Apply channel swap # Apply channel swap
utils transform augment channel_swap input.npy ria_toolkit_oss transform augment channel_swap input.npy
\b \b
# Apply AWGN impairment # Apply AWGN impairment
utils transform impair awgn input.npy --snr-db 15 ria_toolkit_oss transform impair awgn input.npy --snr-db 15
\b \b
# Apply Rayleigh fading channel # Apply Rayleigh fading channel
utils transform apply_channel rayleigh input.npy --num-paths 5 ria_toolkit_oss transform apply_channel rayleigh input.npy --num-paths 5
""" """
pass pass
@ -299,19 +300,19 @@ def augment(augmentation, input, output, list_transforms, help_transform, params
# List all augmentations # List all augmentations
\b \b
utils transform augment --list ria_toolkit_oss transform augment --list
# Show parameters for an augmentation # Show parameters for an augmentation
\b \b
utils transform augment channel_swap --help-transform ria_toolkit_oss transform augment channel_swap --help-transform
# Apply augmentation # Apply augmentation
\b \b
utils transform augment channel_swap input.npy ria_toolkit_oss transform augment channel_swap input.npy
# Apply with parameters and save visualization # Apply with parameters and save visualization
\b \b
utils transform augment drop_samples input.npy --params max_section_size=5 --view ria_toolkit_oss transform augment drop_samples input.npy --params max_section_size=5 --view
""" """
available = get_available_transforms(iq_augmentations) available = get_available_transforms(iq_augmentations)
@ -406,19 +407,19 @@ def impair(impairment, input, output, list_transforms, help_transform, params, v
# List all impairments # List all impairments
\b \b
utils transform impair --list ria_toolkit_oss transform impair --list
# Show parameters for an impairment # Show parameters for an impairment
\b \b
utils transform impair add_awgn_to_signal --help-transform ria_toolkit_oss transform impair add_awgn_to_signal --help-transform
# Apply impairment # Apply impairment
\b \b
utils transform impair add_awgn_to_signal input.npy --params snr=10 ria_toolkit_oss transform impair add_awgn_to_signal input.npy --params snr=10
# Apply with visualization # Apply with visualization
\b \b
utils transform impair add_phase_noise input.npy --params phase_variance=0.001 --view ria_toolkit_oss transform impair add_phase_noise input.npy --params phase_variance=0.001 --view
""" """
available = get_available_transforms(iq_impairments) available = get_available_transforms(iq_impairments)
@ -514,10 +515,10 @@ def apply_channel(
\b \b
Examples: Examples:
utils transform apply_channel rayleigh_fading_channel input.npy --params num_paths=3 snr_db=15 ria_toolkit_oss transform apply_channel rayleigh_fading_channel input.npy --params num_paths=3 snr_db=15
\b \b
utils transform apply_channel doppler_channel recordings/input.npy \\ ria_toolkit_oss transform apply_channel doppler_channel recordings/input.npy \\
--params satellite_velocity=7500 \\ --params satellite_velocity=7500 \\
--params satellite_initial_distance=400000 \\ --params satellite_initial_distance=400000 \\
--params frequency=1e9 \\ --params frequency=1e9 \\
@ -637,19 +638,19 @@ def custom(
# List all custom transforms in directory # List all custom transforms in directory
\b \b
utils transform custom --transform-dir ~/my_transforms --list ria_toolkit_oss transform custom --transform-dir ~/my_transforms --list
# Show parameters for a transform # Show parameters for a transform
\b \b
utils transform custom my_filter --transform-dir ~/my_transforms --help-transform ria_toolkit_oss transform custom my_filter --transform-dir ~/my_transforms --help-transform
# Apply custom transform # Apply custom transform
\b \b
utils transform custom my_filter input.npy --transform-dir ~/my_transforms ria_toolkit_oss transform custom my_filter input.npy --transform-dir ~/my_transforms
# With parameters and visualization # With parameters and visualization
\b \b
utils transform custom my_filter input.npy --transform-dir ~/my_transforms \\ ria_toolkit_oss transform custom my_filter input.npy --transform-dir ~/my_transforms \\
--params cutoff_freq=5000 order=4 --view --params cutoff_freq=5000 order=4 --view
""" """
try: try:

View File

@ -6,8 +6,8 @@ import time
import click import click
from utils.data import Recording from ria_toolkit_oss.datatypes import Recording
from utils.io import from_npy_legacy, load_recording from ria_toolkit_oss.io import from_npy_legacy, load_recording
from .common import ( from .common import (
echo_progress, echo_progress,
@ -374,12 +374,12 @@ def transmit(
quiet, quiet,
): ):
"""Transmit IQ samples from file using SDR device. """Transmit IQ samples from file using SDR device.
\b \b
Examples: Examples:
utils transmit -d hackrf --generate lfm --continuous utils transmit -d hackrf --generate lfm --continuous
utils transmit -d pluto -f 2.44G -g -10 -in recordings/rec_HackRF_2MHz_2025-12-01_15-36-21_80fc33f.sigmf-data utils transmit -d pluto -f 2.44G -g -10 -in recordings/rec_HackRF_2MHz_2025-12-01_15-36-21_80fc33f.sigmf-data
""" """
# Load config file if specified # Load config file if specified

View File

@ -6,9 +6,9 @@ from typing import Optional
import click import click
from utils.io.recording import from_npy, load_recording from ria_toolkit_oss.io.recording import from_npy, load_recording
from utils.view.view_signal import view_annotations, view_channels, view_sig from ria_toolkit_oss.view.view_signal import view_channels, view_sig
from utils.view.view_signal_simple import view_simple_sig from ria_toolkit_oss.view.view_signal_simple import view_simple_sig
from .common import echo_progress, echo_verbose, load_yaml_config from .common import echo_progress, echo_verbose, load_yaml_config
@ -34,11 +34,6 @@ VISUALIZATION_TYPES = {
"spines", "spines",
], ],
}, },
"annotations": {
"function": view_annotations,
"description": "Annotation-focused spectrogram view",
"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": []},
} }

View File

@ -38,6 +38,7 @@ def set_spines(ax, spines):
ax.spines["bottom"].set_visible(False) ax.spines["bottom"].set_visible(False)
ax.spines["left"].set_visible(False) ax.spines["left"].set_visible(False)
def view_channels( def view_channels(
recording: Recording, recording: Recording,
output_path: Optional[str] = "images/signal.png", output_path: Optional[str] = "images/signal.png",