cli #15

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

View File

@ -60,7 +60,7 @@ def auto_select_device(quiet: bool = False) -> str:
all_devices = list_all_devices() all_devices = list_all_devices()
if len(all_devices) == 0: if len(all_devices) == 0:
raise click.ClickException("No SDR devices found.\n" "Run 'utils discover' to see available devices.") raise click.ClickException("No SDR devices found.\n" "Run 'ria discover' to see available devices.")
elif len(all_devices) == 1: elif len(all_devices) == 1:
device = all_devices[0] device = all_devices[0]
@ -96,7 +96,7 @@ def auto_select_device(quiet: bool = False) -> str:
raise click.ClickException( raise click.ClickException(
f"Multiple devices found. Specify with --device\n\n" f"Multiple devices found. Specify with --device\n\n"
f"Available devices:\n{device_list}\n\n" f"Available devices:\n{device_list}\n\n"
f"Run 'utils discover' for more details." f"Run 'ria discover' for more details."
) )
@ -301,8 +301,8 @@ def capture(
\b \b
Examples: Examples:
utils capture -d hackrf -s 2e6 -f 2.44e6 -b 2e6 ria capture -d hackrf -s 2e6 -f 2.44e6 -b 2e6
utils capture -d pluto -s 1e6 -f 2e9 -b 2e6 -n 50 ria capture -d pluto -s 1e6 -f 2e9 -b 2e6 -n 50
""" """

View File

@ -394,21 +394,21 @@ def combine(
\b \b
Examples: Examples:
# Concatenate recordings # Concatenate recordings
utils combine chunk1.npy chunk2.npy chunk3.npy full.npy ria 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 ria 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 ria combine long.npy short.npy output.npy --mode add --align-mode pad-center\n
\b \b
# Repeat pattern with spacing # Repeat pattern with spacing
utils combine signal.npy pattern.npy output.npy --mode add --align-mode repeat-spaced --repeat-spacing 10000 ria combine signal.npy pattern.npy output.npy --mode add --align-mode repeat-spaced --repeat-spacing 10000
""" """
# Validate inputs # Validate inputs
if len(inputs) < 2: if len(inputs) < 2:
raise click.ClickException( raise click.ClickException(
"Error: At least 2 input files required\n" "Usage: utils combine INPUT1 INPUT2 [INPUT3 ...] OUTPUT" "Error: At least 2 input files required\n" "Usage: ria combine INPUT1 INPUT2 [INPUT3 ...] OUTPUT"
) )
# Special case: single input (though we require 2+ above, this handles edge case) # Special case: single input (though we require 2+ above, this handles edge case)

View File

@ -1,6 +1,6 @@
# flake8: noqa: F401 # flake8: noqa: F401
""" """
This module contains all the CLI bindings for the utils package. This module contains all the CLI bindings for the ria package.
""" """
from .capture import capture from .capture import capture

View File

@ -1,4 +1,4 @@
"""Configuration file utilities for Utils CLI. """Configuration file utilities for ria_toolkit_oss CLI.
This module provides utilities for managing the user configuration file. This module provides utilities for managing the user configuration file.
The core integration (actually using these configs) is TODO for the core team. The core integration (actually using these configs) is TODO for the core team.
@ -26,10 +26,10 @@ def get_config_path(config_path: Optional[str] = None) -> Path:
# Try XDG_CONFIG_HOME first (Linux standard) # Try XDG_CONFIG_HOME first (Linux standard)
xdg_config = os.environ.get("XDG_CONFIG_HOME") xdg_config = os.environ.get("XDG_CONFIG_HOME")
if xdg_config: if xdg_config:
return Path(xdg_config) / "utils" / "config.yaml" return Path(xdg_config) / "ria" / "config.yaml"
# Fall back to ~/.utils/config.yaml # Fall back to ~/.ria/config.yaml
return Path.home() / ".utils" / "config.yaml" return Path.home() / ".ria" / "config.yaml"
def load_user_config(config_path: Optional[str] = None) -> Optional[dict]: def load_user_config(config_path: Optional[str] = None) -> Optional[dict]:
@ -73,9 +73,9 @@ def save_user_config(config: dict, config_path: Optional[str] = None) -> Path:
# Write config # Write config
with open(path, "w") as f: with open(path, "w") as f:
f.write("# Utils SDR CLI Configuration\n") f.write("# Ria SDR CLI Configuration\n")
f.write("# Auto-generated by 'utils init'\n") f.write("# Auto-generated by 'ria init'\n")
f.write("# Edit with 'utils init' or modify this file directly\n\n") f.write("# Edit with 'ria init' or modify this file directly\n\n")
yaml.dump(config, f, default_flow_style=False, sort_keys=False) yaml.dump(config, f, default_flow_style=False, sort_keys=False)
# Set secure permissions (user read/write only) # Set secure permissions (user read/write only)
@ -162,7 +162,7 @@ def format_config_display(config: dict) -> str:
# TODO for core team: Integration functions # TODO for core team: Integration functions
# These will be implemented when wiring config into core utils logic # These will be implemented when wiring config into core ria logic
def merge_config(user_config: dict, cli_args: dict) -> dict: def merge_config(user_config: dict, cli_args: dict) -> dict:

View File

@ -101,22 +101,22 @@ def convert( # noqa: C901
\b \b
Examples: Examples:
# SigMF to NumPy (explicit output) # SigMF to NumPy (explicit output)
utils convert recording.sigmf-data output.npy ria convert recording.sigmf-data output.npy
\b \b
# Auto-generate output filename # Auto-generate output filename
utils convert recording.npy --format sigmf ria convert recording.npy --format sigmf
\b \b
# Convert to specific directory # Convert to specific directory
utils convert long_path/recording.npy --format sigmf --output-dir converted ria convert long_path/recording.npy --format sigmf --output-dir converted
\b \b
# NumPy to WAV with decimation # NumPy to WAV with decimation
utils convert high_rate.npy audio.wav --wav-sample-rate 48000 ria convert high_rate.npy audio.wav --wav-sample-rate 48000
\b \b
# Legacy NPY to SigMF # Legacy NPY to SigMF
utils convert old.npy --format sigmf --legacy --overwrite ria convert old.npy --format sigmf --legacy --overwrite
\b \b
# Add metadata during conversion # Add metadata during conversion
utils convert raw.npy --format sigmf --metadata "location=lab" --metadata "antenna=dipole" ria convert raw.npy --format sigmf --metadata "location=lab" --metadata "antenna=dipole"
""" """
# Generate output filename if not provided # Generate output filename if not provided
@ -125,8 +125,8 @@ def convert( # noqa: C901
raise click.ClickException( raise click.ClickException(
"Either OUTPUT or --format must be specified\n" "Either OUTPUT or --format must be specified\n"
"Examples:\n" "Examples:\n"
" utils convert input.npy output.sigmf\n" " ria convert input.npy output.sigmf\n"
" utils convert input.npy --format sigmf" " ria convert input.npy --format sigmf"
) )
# Get input filename without extension # Get input filename without extension

View File

@ -31,12 +31,12 @@ def load_sdr_drivers(verbose: bool = False) -> Tuple[List[str], List[str], Dict[
# Try to import each SDR driver # Try to import each SDR driver
drivers = { drivers = {
"pluto": "utils.sdr.pluto", "pluto": "ria_toolkit_oss.sdr.pluto",
"hackrf": "utils.sdr.hackrf", "hackrf": "ria_toolkit_oss.sdr.hackrf",
"bladerf": "utils.sdr.bladerf", "bladerf": "ria_toolkit_oss.sdr.bladerf",
"usrp": "utils.sdr.usrp", "usrp": "ria_toolkit_oss.sdr.usrp",
"rtlsdr": "utils.sdr.rtlsdr", "rtlsdr": "ria_toolkit_oss.sdr.rtlsdr",
"thinkrf": "utils.sdr.thinkrf", "thinkrf": "ria_toolkit_oss.sdr.thinkrf",
} }
for driver_name, module_path in drivers.items(): for driver_name, module_path in drivers.items():

View File

@ -33,7 +33,7 @@ def prompt_with_default(text: str, default: str = "") -> str:
def init_show(config_file_path, config_path): def init_show(config_file_path, config_path):
if not config_file_path.exists(): if not config_file_path.exists():
click.echo(f"No configuration file found at: {config_file_path}") click.echo(f"No configuration file found at: {config_file_path}")
click.echo("\nRun 'utils init' to create a configuration.") click.echo("\nRun 'ria init' to create a configuration.")
return return
try: try:
@ -43,11 +43,11 @@ def init_show(config_file_path, config_path):
click.echo() click.echo()
click.echo(format_config_display(config)) click.echo(format_config_display(config))
click.echo() click.echo()
click.echo("To update: utils init") click.echo("To update: ria init")
click.echo("To reset: utils init --reset") click.echo("To reset: ria init --reset")
except Exception as e: except Exception as e:
click.echo(f"Error reading configuration: {e}", err=True) click.echo(f"Error reading configuration: {e}", err=True)
click.echo("\nRun 'utils init --reset' to recreate the configuration.") click.echo("\nRun 'ria init --reset' to recreate the configuration.")
def init_reset(config_file_path, config_path, yes): def init_reset(config_file_path, config_path, yes):
@ -78,7 +78,7 @@ def init_reset(config_file_path, config_path, yes):
try: try:
config_file_path.unlink() config_file_path.unlink()
click.echo("\n✓ Configuration deleted.") click.echo("\n✓ Configuration deleted.")
click.echo("\nRun 'utils init' to create a new configuration.") click.echo("\nRun 'ria init' to create a new configuration.")
except Exception as e: except Exception as e:
click.echo(f"Error deleting configuration: {e}", err=True) click.echo(f"Error deleting configuration: {e}", err=True)
@ -123,8 +123,8 @@ def save_config(config, config_path, use_interactive, warnings):
if use_interactive: if use_interactive:
click.echo() click.echo()
click.echo("You can view your config anytime with: utils init --show") click.echo("You can view your config anytime with: ria init --show")
click.echo("You can update values by running: utils init") click.echo("You can update values by running: ria init")
# Show warnings in non-interactive mode # Show warnings in non-interactive mode
elif warnings: elif warnings:
@ -176,26 +176,26 @@ def init(
): ):
"""Initialize user configuration. """Initialize user configuration.
Creates a configuration file at ~/.utils/config.yaml with default metadata Creates a configuration file at ~/.ria/config.yaml with default metadata
values that will be used across CLI commands. values that will be used across CLI commands.
Examples: Examples:
\b \b
# Interactive setup # Interactive setup
utils init ria init
\b \b
# Non-interactive setup # Non-interactive setup
utils init --author "Jane Doe" --project "RF_Analysis" --location "Lab_A" ria init --author "Jane Doe" --project "RF_Analysis" --location "Lab_A"
\b \b
# Show current configuration # Show current configuration
utils init --show ria init --show
\b \b
# Reset configuration # Reset configuration
utils init --reset ria init --reset
""" """
config_file_path = get_config_path(config_path) config_file_path = get_config_path(config_path)
@ -232,13 +232,13 @@ def init(
# Interactive mode # Interactive mode
if use_interactive: if use_interactive:
click.echo() click.echo()
click.echo("Welcome to Utils SDR CLI Configuration!") click.echo("Welcome to RIA Toolkit Oss SDR CLI Configuration!")
click.echo("=" * 60) click.echo("=" * 60)
click.echo() click.echo()
click.echo(f"This will create a configuration file at: {config_file_path}") click.echo(f"This will create a configuration file at: {config_file_path}")
click.echo() click.echo()
click.echo("These values will be automatically added to recordings and conversions.") click.echo("These values will be automatically added to recordings and conversions.")
click.echo("You can always change these later by running 'utils init' again.") click.echo("You can always change these later by running 'ria init' again.")
click.echo() click.echo()
click.echo("Press Enter to skip optional fields.") click.echo("Press Enter to skip optional fields.")
click.echo() click.echo()

View File

@ -101,35 +101,35 @@ def split( # noqa: C901
\b \b
Examples: Examples:
# Split at specific sample # Split at specific sample
utils split recording.sigmf --split-at 500000 --output-dir split_output ria split recording.sigmf --split-at 500000 --output-dir split_output
\b \b
# Split into equal chunks # Split into equal chunks
utils split capture.npy --split-every 100000 --output-dir chunks ria split capture.npy --split-every 100000 --output-dir chunks
\b \b
# Split by duration (requires sample_rate in metadata) # Split by duration (requires sample_rate in metadata)
utils split recording.sigmf --split-duration 1.0 --output-dir segments ria split recording.sigmf --split-duration 1.0 --output-dir segments
\b \b
# Trim recording # Trim recording
utils split signal.npy --trim --start 1000 --length 5000 --output-dir trimmed ria split signal.npy --trim --start 1000 --length 5000 --output-dir trimmed
\b \b
# Trim with end index # Trim with end index
utils split signal.npy --trim --start 1000 --end 6000 --output-dir trimmed ria split signal.npy --trim --start 1000 --end 6000 --output-dir trimmed
\b \b
# Extract all annotated regions # Extract all annotated regions
utils split annotated.sigmf --extract-annotations --output-dir annotations ria split annotated.sigmf --extract-annotations --output-dir annotations
\b \b
# Extract specific annotation label # Extract specific annotation label
utils split annotated.sigmf --extract-annotations --annotation-label "payload" ria split annotated.sigmf --extract-annotations --annotation-label "payload"
\b \b
# Extract specific annotation by index # Extract specific annotation by index
utils split annotated.sigmf --extract-annotations --annotation-index 1 ria split annotated.sigmf --extract-annotations --annotation-index 1
""" """
# Validate operation selection # Validate operation selection
@ -342,7 +342,7 @@ def split( # noqa: C901
# Extract annotated regions # Extract annotated regions
if not recording.annotations: if not recording.annotations:
raise click.ClickException( raise click.ClickException(
"No annotations found in recording\n" "Use 'utils annotate' to add annotations first" "No annotations found in recording\n" "Use 'ria annotate' to add annotations first"
) )
# Filter annotations # Filter annotations

View File

@ -57,7 +57,7 @@ def auto_select_tx_device(quiet: bool = False) -> str:
raise click.ClickException( raise click.ClickException(
"No TX-capable SDR devices found.\n" "No TX-capable SDR devices found.\n"
"TX-capable devices: PlutoSDR, HackRF, BladeRF, USRP\n" "TX-capable devices: PlutoSDR, HackRF, BladeRF, USRP\n"
"Run 'utils discover' to see all devices." "Run 'ria discover' to see all devices."
) )
elif len(tx_devices) == 1: elif len(tx_devices) == 1:
@ -92,7 +92,7 @@ def auto_select_tx_device(quiet: bool = False) -> str:
raise click.ClickException( raise click.ClickException(
f"Multiple TX-capable devices found. Specify with --device\n\n" f"Multiple TX-capable devices found. Specify with --device\n\n"
f"Available TX devices:\n{device_list}\n\n" f"Available TX devices:\n{device_list}\n\n"
f"Run 'utils discover' for more details." f"Run 'ria discover' for more details."
) )
@ -377,8 +377,8 @@ def transmit(
\b \b
Examples: Examples:
utils transmit -d hackrf --generate lfm --continuous ria 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 ria transmit -d pluto -f 2.44G -g -10 -in recordings/rec_HackRF_2MHz_2025-12-01_15-36-21_80fc33f.sigmf-data
""" """

View File

@ -277,25 +277,25 @@ def view(
\b \b
Examples: Examples:
# Basic visualization (saves to recording.png) # Basic visualization (saves to recording.png)
utils view recording.sigmf ria view recording.sigmf
\b \b
# Spectrogram with custom output # Spectrogram with custom output
utils view capture.npy --output spec.png ria view capture.npy --output spec.png
\b \b
# Interactive display # Interactive display
utils view signal.npy --show --no-save ria view signal.npy --show --no-save
\b \b
# High-resolution PDF # High-resolution PDF
utils view recording.blue --format pdf --dpi 600 ria view recording.blue --format pdf --dpi 600
\b \b
# Simple mode with constellation # Simple mode with constellation
utils view qam.wav --type simple --constellation --labels ria view qam.wav --type simple --constellation --labels
\b \b
# Full-featured plot # Full-featured plot
utils view capture.sigmf --type full --title "Lab Test" ria view capture.sigmf --type full --title "Lab Test"
\b \b
# Legacy NPY file # Legacy NPY file
utils view old_capture.npy --legacy --type simple ria view old_capture.npy --legacy --type simple
""" """
# Load config file if specified # Load config file if specified
if config: if config: