cli #15
|
|
@ -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
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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():
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user