diff --git a/docs/source/intro/getting_started.rst b/docs/source/intro/getting_started.rst index fa8e87e..59de870 100644 --- a/docs/source/intro/getting_started.rst +++ b/docs/source/intro/getting_started.rst @@ -105,93 +105,99 @@ Top-level CLI follows this model: 3.1 Discover radios -------------------- -Run this first in any new environment to verify drivers and detect hardware before -attempting RX/TX commands. +Run this first to verify drivers and detect connected hardware. .. code-block:: bash - ria discover ria discover -v - ria discover --json-output + +See :ref:`discover ` for JSON output and troubleshooting options. 3.2 Initialize local metadata defaults --------------------------------------- -Set reusable metadata once so generated/captured files automatically include consistent -provenance fields. +Set reusable metadata once so captured files include consistent provenance fields. .. code-block:: bash ria init - ria init --author "Jane Doe" --project "rf-campaign-1" --location "Lab-A" # non-interactive - ria init --show # show config + +See :ref:`init ` for non-interactive and config path options. 3.3 Capture IQ --------------- -Capture baseband data from a connected SDR into a reusable file format. +Record baseband IQ from a connected SDR. .. code-block:: bash ria capture -d pluto -f 2.44G -s 2e6 -n 500000 -o capture.sigmf-data +See :ref:`capture ` for all device, format, and metadata options. + 3.4 Visualize and inspect -------------------------- -Render quick diagnostic plots to validate signal presence, quality, and rough structure. +Render a quick diagnostic plot to validate signal presence and quality. .. code-block:: bash ria view capture.sigmf-data --type simple - ria view capture.sigmf-data --type full --show --no-save + +See :ref:`view ` for full multi-panel plots and display options. 3.5 Auto-annotate and inspect annotations ------------------------------------------ -Create initial labels automatically, then inspect annotation objects before downstream use. +Detect signal regions automatically, then verify the results. .. code-block:: bash - ria annotate energy capture.sigmf-data --label signal --threshold 1.2 - ria annotate list capture.sigmf-data --verbose + ria annotate energy capture.sigmf-data --label signal + ria annotate list capture.sigmf-data + +See :ref:`annotate ` for threshold tuning and other detection methods. 3.6 Convert and split ---------------------- -Normalize file format and split large captures into manageable chunks for processing or -training. +Convert to a different format and split into fixed-size chunks. .. code-block:: bash ria convert capture.sigmf-data capture.npy ria split capture.sigmf-data --split-every 100000 --output-dir chunks +See :ref:`convert ` and :ref:`split ` for format and trim options. + 3.7 Apply transforms --------------------- -Augment or impair recordings to produce controlled variants. +Augment or impair a recording to produce controlled variants. .. code-block:: bash - ria transform augment channel_swap capture.npy ria transform impair add_awgn_to_signal capture.npy --params snr=10 +See :ref:`transform ` for available augmentations and custom transforms. + 3.8 Transmit (TX-capable radios only) -------------------------------------- -Replay recorded or synthesized IQ through a transmit-capable SDR. +Replay a recording through a transmit-capable SDR. .. code-block:: bash ria transmit -d hackrf -f 2.44G -s 2e6 --input capture.sigmf-data - ria transmit -d hackrf --generate lfm --continuous # generated waveform + +See :ref:`transmit ` for continuous mode and generated waveform options. 4) Command Reference @@ -227,6 +233,68 @@ Replay recorded or synthesized IQ through a transmit-capable SDR. * Use ``--verbose`` first when troubleshooting; it surfaces driver-level failures that are hidden in default output. +**Example output:** + +Run ``ria discover -v`` to see loaded drivers, failure reasons, and attached devices: + +.. code-block:: text + + $ ria discover -v + + ✅ Loaded drivers (3): + hackrf + pluto + bladerf + + ❌ Failed drivers (3): + usrp: ModuleNotFoundError: uhd + rtlsdr: ImportError: pyrtlsdr is required to use the RTLSDR class + thinkrf: ImportError: pyrf is required to use the ThinkRF integration. + Install with: pip install ria-toolkit-oss[thinkrf] + + ======================================== + Attached Devices + ======================================== + + 📡 USRP/UHD devices (1): + ✅ MyB200 (B200) - Serial: 30C51D5 + + 📱 PlutoSDR devices: None found + 🔧 HackRF devices: None found + + ======================================== + Discovery Summary + ======================================== + Loaded drivers: 3 + Failed drivers: 3 + Detected devices: 1 + +With ``--json-output`` (useful for scripting and automation): + +.. code-block:: json + + { + "loaded_drivers": ["hackrf"], + "failed_drivers": ["pluto", "bladerf", "usrp", "rtlsdr", "thinkrf"], + "devices": [ + { + "type": "BladeRF", + "Description": "Nuand bladeRF 2.0", + "Backend": "libusb", + "Serial": "8518b488d3e3443da979680f472bbb87", + "USB Bus": "4", + "USB Address": "2" + } + ], + "total_devices": 1 + } + +.. note:: + Driver load failures are normal on systems where only a subset of SDR backends are + installed. A failed driver just means that backend's Python library isn't present — it + does not prevent other drivers from working. Install only the packages for the hardware + you use. + .. _cmd-init: @@ -244,36 +312,47 @@ Replay recorded or synthesized IQ through a transmit-capable SDR. ria init [options] -**Key options:** +**Options:** -*Metadata defaults:* -``--author``, ``--organization``, ``--project``, ``--location``, ``--testbed``, -``--license``, ``--hw``, ``--dataset`` - -*Actions:* -``--show``, ``--reset`` - -*Control:* -``--config-path``, ``--interactive`` / ``--no-interactive``, ``-y`` / ``--yes`` - -**What each option category does:** - -* Metadata defaults (``--author``, ``--project``, etc.): stored once and reused for later - recordings so files have consistent provenance. -* SigMF-focused fields (``--license``, ``--hw``, ``--dataset``): populate metadata commonly - expected in shared datasets. +* ``--author``, ``--organization``, ``--project``, ``--location``, ``--testbed``, + ``--license``, ``--hw``, ``--dataset``: stored once and reused for later recordings so + files have consistent provenance. * ``--show``: read-only inspect of the current resolved config. * ``--reset``: remove config and start clean. -* ``--config-path``: use a non-default config location (useful for isolated environments or - CI). +* ``--config-path``: use a non-default config location (useful for isolated environments or CI). * ``--interactive`` / ``--no-interactive``: force prompts on or off regardless of terminal auto-detection. * ``--yes``: suppress confirmation prompts for scripted runs. +**Example output:** + +Set metadata fields non-interactively: + +.. code-block:: text + + $ ria init --author "Jane Doe" --project "rf-campaign-1" --location "Lab-A" --no-interactive + + ✓ Configuration saved to: /home/user/.ria/config.yaml + +Then verify what was saved with ``--show``: + +.. code-block:: text + + $ ria init --show + + Current Configuration (/home/user/.ria/config.yaml): + ============================================================ + + Author: Jane Doe + Project: rf-campaign-1 + Location: Lab-A + + To update: ria init + To reset: ria init --reset + .. note:: - Current command output includes a note that some config integration is still being - finalized. Config values are already consumed by multiple commands (capture, convert, - generate metadata, and YAML config loading paths). + Config integration is still being finalized. Config values are already consumed by + multiple commands (capture, convert, generate metadata, and YAML config loading paths). .. _cmd-capture: @@ -295,53 +374,25 @@ Replay recorded or synthesized IQ through a transmit-capable SDR. Device selection (``--device``) is optional if only one device is detected. Exactly one of ``--num-samples`` or ``--duration`` is required. -**Core options:** - -*Device/connection:* +**Options:** * ``-d, --device {pluto,hackrf,bladerf,usrp,rtlsdr,thinkrf}`` -* ``-i, --ident`` -* ``-c, --config `` - -*RF/capture:* - +* ``-i, --ident``: serial or IP selector when multiple devices of the same type are present. +* ``-c, --config ``: load options from a YAML file; CLI flags override loaded values. * ``-s, --sample-rate`` * ``-f, --center-frequency`` (supports values like ``915e6``, ``2.4G``) -* ``-g, --gain`` -* ``-b, --bandwidth`` -* ``-n, --num-samples`` -* ``-t, --duration`` - -*Output:* - -* ``-o, --output`` -* ``--output-dir`` -* ``--format {npy,sigmf,wav,blue}`` -* ``--save-image`` - -*Metadata/logging:* - -* ``-m, --metadata KEY=VALUE`` (repeatable) +* ``-g, --gain``, ``-b, --bandwidth`` +* ``-n, --num-samples`` or ``-t, --duration``: use sample count for deterministic datasets, + or duration for quick time-based acquisition. +* ``-o, --output``, ``--output-dir``: output path or directory. A timestamped filename is + generated if ``--output`` is omitted; defaults to ``recordings/`` if ``--output-dir`` is + omitted. +* ``--format {npy,sigmf,wav,blue}``: inferred from file extension if not set. ``sigmf`` is + best for annotation workflows. +* ``--save-image``: writes a quick visual summary alongside the capture file. +* ``-m, --metadata KEY=VALUE`` (repeatable): injects run-specific metadata. * ``-v, --verbose``, ``-q, --quiet`` -**How options work in practice:** - -* ``--device`` + ``--ident``: select both device class and target instance; ``--ident`` - takes serial/IP style selectors. -* ``--config``: load a YAML option set, then override specific fields on the CLI as needed. -* ``--num-samples`` vs ``--duration``: use exact sample count for deterministic datasets, - or time-based capture for quick acquisition. -* ``--format``: ``sigmf`` is best for metadata/annotation workflows. -* ``--save-image``: writes a quick visual summary alongside capture output. -* ``--metadata KEY=VALUE``: injects run-specific metadata (campaign ID, antenna, scenario - tag, etc.). - -**Output behavior:** - -* If ``--output`` is omitted, a timestamped filename is generated automatically. -* If ``--output-dir`` is omitted, captures default to ``recordings/``. -* Format is inferred from the ``--output`` extension when no explicit ``--format`` is given. - **Examples:** .. code-block:: bash @@ -350,6 +401,28 @@ Device selection (``--device``) is optional if only one device is detected. Exac ria capture -d pluto -f 915e6 -t 2 --format npy --output-dir recordings ria capture -c capture_config.yaml +**Example output:** + +.. note:: + ``capture`` requires a connected SDR. The following shows representative output for a + HackRF capture. + +.. code-block:: text + + $ ria capture -d hackrf -s 2e6 -f 2.44G -n 1000000 -o rf.sigmf-data + + Initializing HackRF... + Device: HackRF One + Serial: a74ad5e4e2a14b7d + Center frequency: 2.44 GHz + Sample rate: 2.00 MS/s + Gain: 20 dB + + Capturing 1,000,000 samples... + + Saved: rf.sigmf-data + rf.sigmf-meta + .. _cmd-view: @@ -397,13 +470,13 @@ Device selection (``--device``) is optional if only one device is detected. Exac **Mode-specific options:** -*simple:* ``--fast``, ``--compact``, ``--horizontal``, ``--constellation``, ``--labels``, +simple: ``--fast``, ``--compact``, ``--horizontal``, ``--constellation``, ``--labels``, ``--slice start:end[:step]`` -*full:* ``--plot-length``, ``--no-spectrogram``, ``--no-iq``, ``--no-frequency``, +full: ``--plot-length``, ``--no-spectrogram``, ``--no-iq``, ``--no-frequency``, ``--no-constellation``, ``--no-metadata``, ``--no-logo``, ``--spines`` -*annotations/channels:* ``--channel`` +annotations / channels: ``--channel`` **Examples:** @@ -416,15 +489,36 @@ Device selection (``--device``) is optional if only one device is detected. Exac ria view recordings\qam64_35.npy --type simple ria view recordings\qam64_35.npy --type full -.. figure:: ../images/recordings/qam64_35.png - :alt: Example output of ria view recordings\qam64_35.npy --type simple +**Example output:** - Output of ``ria view recordings\qam64_35.npy --type simple`` +.. code-block:: text + + $ ria view qam64_35.npy --type simple + + Loading recording: qam64_35.npy + + Recording Metadata: + ---------------------------------------- + modulation: qam64 + constellation: qam + bits_per_symbol: 6 + sps: 6 + beta: 0.35 + source: signal.block_generator + ---------------------------------------- + + Generating simple visualization... + Saved: qam64_35.png + +.. figure:: ../images/recordings/qam64_35.png + :alt: Example output of ria view qam64_35.npy --type simple + + Output of ``ria view qam64_35.npy --type simple`` .. figure:: ../images/recordings/qam64_35-full.png - :alt: Example output of ria view recordings\qam64_35.npy --type full + :alt: Example output of ria view qam64_35.npy --type full - Output of ``ria view recordings\qam64_35.npy --type full`` + Output of ``ria view qam64_35.npy --type full`` .. _cmd-annotate: @@ -438,7 +532,7 @@ Device selection (``--device``) is optional if only one device is detected. Exac * Build or refine label metadata directly in recordings for downstream training, QA, and filtering. -**Command shape:** +**Usage:** .. code-block:: bash @@ -469,12 +563,11 @@ Device selection (``--device``) is optional if only one device is detected. Exac * - ``separate`` - Decompose annotations into narrower spectral components -**General behavior:** +SigMF is the preferred format for durable annotation metadata. For non-SigMF input, most +operations write a new output artifact unless ``--overwrite`` is set. +``--type {standalone,parallel,intersection}`` controls annotation relation semantics. -* SigMF is the preferred format for durable annotation metadata. -* For non-SigMF input, many operations write a new output artifact unless overwrite behavior - is explicitly requested. -* ``--type {standalone,parallel,intersection}`` controls annotation relation semantics. +**Manual subcommands:** ``ria annotate list`` ~~~~~~~~~~~~~~~~~~~~~ @@ -519,6 +612,8 @@ index. Removes all annotations from the recording. ``--force`` bypasses the confirmation prompt. +**Automatic detection subcommands:** + ``ria annotate energy`` ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -541,6 +636,19 @@ Detects energetic regions above the estimated noise floor and writes them as ann * ``--nfft``, ``--obw-power`` * ``--type``, ``-o`` / ``--output``, ``--overwrite``, ``--quiet`` +``ria annotate threshold`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: bash + + ria annotate threshold --threshold <0.0..1.0> [options] + +Uses normalized magnitude thresholding to derive annotation spans. Use where a fixed +amplitude threshold is sufficient. + +* ``--label``, ``--window-size``, ``--type``, ``-o`` / ``--output``, ``--overwrite``, + ``--quiet`` + ``ria annotate cusum`` ~~~~~~~~~~~~~~~~~~~~~~~ @@ -557,19 +665,6 @@ contiguous segments. * ``--tolerance``: merges nearby boundaries when set above default. * ``--type``, ``-o`` / ``--output``, ``--overwrite``, ``--quiet`` -``ria annotate threshold`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block:: bash - - ria annotate threshold --threshold <0.0..1.0> [options] - -Uses normalized magnitude thresholding to derive annotation spans. Use where a fixed -amplitude threshold is sufficient. - -* ``--label``, ``--window-size``, ``--type``, ``-o`` / ``--output``, ``--overwrite``, - ``--quiet`` - ``ria annotate separate`` ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -577,12 +672,17 @@ amplitude threshold is sufficient. ria annotate separate [options] -Decomposes selected annotations into narrower spectral components and emits refined -annotations. +Decomposes existing annotations into narrower sub-band annotations by detecting distinct +frequency components within each annotated time window. It does not detect signal regions +from scratch — run ``energy``, ``threshold``, or ``cusum`` first to produce the input +annotations, then use ``separate`` to refine them spectrally. + +Use this when a single broad annotation covers multiple signals at different frequencies +and you want separate annotations per component. * ``--indices "0,1,2"``: limit operation to specific annotations; omit to process all. * ``--nfft``: larger FFT improves frequency resolution but increases compute time. -* ``--noise-threshold-db``: stabilizes detection across heterogeneous captures. +* ``--noise-threshold-db``: sets the noise floor in dB; auto-estimated if omitted. * ``--min-component-bw``: rejects narrow fragments likely to be noise artifacts. * ``-o`` / ``--output``, ``--overwrite``, ``--quiet``, ``--verbose`` @@ -593,14 +693,208 @@ annotations. ria annotate list capture.sigmf-data --verbose ria annotate add capture.sigmf-data --start 10000 --count 5000 --label burst ria annotate energy capture.sigmf-data --label signal --threshold 1.3 + ria annotate threshold capture.sigmf-data --threshold 0.5 --label signal ria annotate cusum capture.sigmf-data --min-duration 5 - ria annotate threshold recordings/sample_recording3.npy --threshold 0.7 --label 70% ria annotate separate capture.sigmf-data --indices 0,1 --verbose -.. figure:: ../images/recordings/sample_recording3_annotated.png - :alt: Example output of ria annotate threshold recordings/sample_recording3.npy --threshold 0.7 --label 70% +**Example output:** - Output of ``ria annotate threshold recordings/sample_recording3.npy --threshold 0.7 --label 70%`` +``ria annotate list`` +^^^^^^^^^^^^^^^^^^^^^ + +Inspect all annotations with ``--verbose``: + +.. code-block:: text + + $ ria annotate list sample_recording3_annotated.npy --verbose + + Annotations in sample_recording3_annotated.npy: + [0] Samples 170,599-171,116: signal + Type: standalone + Frequency: 3.41 GHz - 3.41 GHz + Detail: {'generator': 'energy_detector', 'freq_method': 'nbw'} + [1] Samples 182,310-182,841: signal + Type: standalone + Frequency: 3.41 GHz - 3.41 GHz + Detail: {'generator': 'energy_detector', 'freq_method': 'nbw'} + [2] Samples 1,133,165-1,133,706: signal + ... + [7] Samples 2,113,268-2,861,395: signal + Type: standalone + Frequency: 3.41 GHz - 3.41 GHz + Detail: {'generator': 'energy_detector', 'freq_method': 'nbw'} + + Total: 8 annotation(s) + +``ria annotate add`` +^^^^^^^^^^^^^^^^^^^^ + +Add a single annotation by sample index: + +.. code-block:: text + + $ ria annotate add sample_recording3_annotated.npy --start 50000 --count 10000 --label burst -o out.npy + + Loaded: sample_recording3_annotated.npy + + Adding annotation: + Start: 50,000 + Count: 10,000 samples + Frequency: full bandwidth + Label: burst + Type: standalone + Saving to: out.npy + ✓ Saved + +``ria annotate remove`` +^^^^^^^^^^^^^^^^^^^^^^^ + +Remove one annotation by its list index (run ``annotate list`` first to confirm): + +.. code-block:: text + + $ ria annotate remove sample_recording3_annotated.npy 0 -o out.npy + + Loaded: sample_recording3_annotated.npy + + Removing annotation [0]: + Removed: samples 170,599-171,116 (signal) + Saving to: out.npy + ✓ Saved + +``ria annotate clear`` +^^^^^^^^^^^^^^^^^^^^^^ + +Remove all annotations at once: + +.. code-block:: text + + $ ria annotate clear sample_recording3_annotated.npy --force --overwrite + + Loaded: sample_recording3_annotated.npy + + Cleared 8 annotation(s) + Saving to: sample_recording3_annotated.npy + ✓ Saved + +``ria annotate energy`` +^^^^^^^^^^^^^^^^^^^^^^^ + +Auto-detect signal regions above the noise floor: + +.. code-block:: text + + $ ria annotate energy sample_recording3.npy --label signal -o sample_recording3_annotated.npy + + Loaded: sample_recording3.npy + + Detecting signals using energy-based method... + Time detection: + Segments: 10 + Threshold: 1.2x noise floor + Window size: 200 samples + Min distance: 5000 samples + Frequency bounds: nbw + ✓ Added 8 annotation(s) + Saving to: sample_recording3_annotated.npy + ✓ Saved + +.. figure:: ../images/recordings/sample_recording3_annotated.png + :alt: Energy-detected annotations on sample_recording3.npy + + ``ria annotate energy sample_recording3.npy --label signal`` + +``ria annotate threshold`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Detect regions above a fixed fraction of peak magnitude: + +.. code-block:: text + + $ ria annotate threshold sample_recording3.npy --threshold 0.7 --label strong -o out.npy + + Loaded: sample_recording3.npy + + Detecting signals using threshold qualifier... + Threshold: 70.0% of max magnitude + Window size: auto (1ms) + Channel: 0 + ✓ Added 2 annotation(s) + Saving to: out.npy + ✓ Saved + +.. figure:: ../images/recordings/sample_recording3_threshold.png + :alt: Threshold annotations on sample_recording3.npy at 0.7 + + ``ria annotate threshold sample_recording3.npy --threshold 0.7 --label strong`` + +``ria annotate cusum`` +^^^^^^^^^^^^^^^^^^^^^^ + +Detect regime changes using change-point detection: + +.. code-block:: text + + $ ria annotate cusum sample_recording3.npy --label regime -o out.npy + + Loaded: sample_recording3.npy + + Detecting segments using CUSUM... + Min duration: 5.0 ms + ✓ Added 37 annotation(s) + Saving to: out.npy + ✓ Saved + +.. figure:: ../images/recordings/sample_recording3_cusum.png + :alt: CUSUM annotations on sample_recording3.npy + + ``ria annotate cusum sample_recording3.npy --label regime`` + +``ria annotate separate`` +^^^^^^^^^^^^^^^^^^^^^^^^^ + +``separate`` takes existing annotations as input and splits each one into narrower +sub-band annotations by finding distinct spectral peaks within the annotated time window. +The typical workflow is to first run ``threshold`` (or ``energy``) to mark signal regions, +then run ``separate`` to resolve the individual frequency components within them. + +Step 1 — create broad annotations with ``threshold``: + +.. code-block:: text + + $ ria annotate threshold sample_recording5.npy --threshold 0.5 --label signal -o annotated.npy + + Loaded: sample_recording5.npy + + Detecting signals using threshold qualifier... + Threshold: 50.0% of max magnitude + Window size: auto (1ms) + Channel: 0 + ✓ Added 3 annotation(s) + Saving to: annotated.npy + ✓ Saved + +Step 2 — run ``separate`` to split by frequency component: + +.. code-block:: text + + $ ria annotate separate annotated.npy -o separated.npy + + Loaded: annotated.npy + + Splitting annotations by frequency components... + Input annotations: 3 + FFT size: 65536 + Noise threshold: auto-estimated + Min component BW: 50.00 kHz + ✓ Output annotations: 6 (+3 change) + Saving to: separated.npy + ✓ Saved + +.. figure:: ../images/recordings/sample_recording5_after_separate.png + :alt: Annotations after separate — split into upper and lower sub-bands + + After ``separate`` — each annotation is resolved into upper and lower frequency components .. _cmd-convert: @@ -643,6 +937,18 @@ inferred from the output file extension. ria convert highrate.npy audio.wav --wav-sample-rate 48000 ria convert old.npy --format sigmf --legacy --overwrite +**Example output:** + +.. code-block:: text + + $ ria convert sample_recording3.npy sample_recording3.sigmf-data + + Converting: sample_recording3.npy → sample_recording3.sigmf-data + Input format: NPY + Output format: SIGMF + Samples: 3,000,000 + Conversion complete: sample_recording3.sigmf-data, sample_recording3.sigmf-meta + .. _cmd-split: @@ -686,6 +992,26 @@ Choose exactly one operation per invocation: ria split recording.npy --trim --start 1000 --length 5000 --output-dir trimmed ria split annotated.sigmf-data --extract-annotations --annotation-label payload +**Example output:** + +.. code-block:: text + + $ ria split sample_recording3.npy --split-every 500000 --output-dir chunks + + Loading: sample_recording3.npy + Total samples: 3,000,000 + + Splitting into chunks of 500,000 samples... + Creating 6 chunks... + Chunk 1/6: samples 0-499,999... + Chunk 2/6: samples 500,000-999,999... + Chunk 3/6: samples 1,000,000-1,499,999... + Chunk 4/6: samples 1,500,000-1,999,999... + Chunk 5/6: samples 2,000,000-2,499,999... + Chunk 6/6: samples 2,500,000-2,999,999... + + Created 6 chunks in chunks/ + .. _cmd-combine: @@ -713,12 +1039,9 @@ Choose exactly one operation per invocation: * ``--overwrite``, ``--metadata KEY=VALUE`` (repeatable) * ``--legacy``, ``--verbose``, ``--quiet`` -**Mode semantics:** - -* ``concat``: append inputs sequentially in time. -* ``add``: sample-wise summation — all inputs must be aligned to the same length. - -**Alignment options for** ``--mode add``: +``--mode concat`` appends inputs sequentially in time. ``--mode add`` performs sample-wise +summation and requires all inputs to be the same length, or an ``--align-mode`` to +reconcile length differences: * ``error``: fail if lengths differ. * ``truncate``: cut all to shortest length. @@ -735,6 +1058,15 @@ Choose exactly one operation per invocation: ria combine long.npy short.npy out.npy --mode add --align-mode pad-center ria combine signal.npy pattern.npy out.npy --mode add --align-mode repeat-spaced --repeat-spacing 10000 +**Example output:** + +.. code-block:: text + + $ ria combine sample_recording3.npy qam64_35.npy combined.npy + + Combining 2 recordings (concat mode)... + Saved to: combined.npy + .. _cmd-generate: @@ -778,76 +1110,45 @@ Choose exactly one operation per invocation: * - ``ook``, ``oqpsk``, ``gmsk`` - On-off keying and continuous-phase modulation schemes -**Common options shared across all generators:** +**Common options** (all subcommands): -* ``-s, --sample-rate`` (required) -* ``-n, --num-samples`` or ``-t, --duration`` -* ``--frequency-shift``, ``-fc`` / ``--center-frequency`` -* ``--add-noise``, ``--noise-power``, ``--path-gain`` -* ``-o, --output`` (required), ``-F`` / ``--format {npy,sigmf,wav,blue}`` -* ``--multipath-paths``, ``--multipath-max-delay`` -* ``--iq-amp-imbalance``, ``--iq-phase-imbalance``, ``--iq-dc-offset`` -* ``--config `` -* ``-w`` / ``--overwrite``, ``-m`` / ``--metadata KEY=VALUE`` (repeatable) -* ``-v`` / ``--verbose``, ``-q`` / ``--quiet`` +* ``-s, --sample-rate`` (required), ``-n, --num-samples`` or ``-t, --duration`` +* ``-o, --output`` (required), ``-F / --format {npy,sigmf,wav,blue}`` +* ``--frequency-shift``, ``--center-frequency``: separate baseband shape from RF metadata. +* ``--add-noise``, ``--noise-power``, ``--path-gain``: apply noise post-generation. +* ``--multipath-paths``, ``--multipath-max-delay``, ``--iq-amp-imbalance``, + ``--iq-phase-imbalance``, ``--iq-dc-offset``: channel and IQ impairments. +* ``--config ``, ``-w / --overwrite``, ``-m / --metadata KEY=VALUE``, + ``-v / --verbose``, ``-q / --quiet`` -``--frequency-shift`` and ``--center-frequency`` let you separate the baseband shape from -RF metadata context. ``--add-noise`` and ``--noise-power`` apply post-generation noise. -Multipath and IQ imbalance flags apply impairment-style post-processing during generation. +**Subcommand options:** -``tone`` -~~~~~~~~~ +.. list-table:: + :widths: 20 80 + :header-rows: 1 -Options: ``--frequency``, ``--amplitude``, ``--phase`` - -``noise`` -~~~~~~~~~~ - -Options: ``--noise-type {gaussian,uniform}``, ``--power`` - -``chirp`` -~~~~~~~~~~ - -Options: ``--bandwidth`` (required), ``--period`` (required), ``--type {up,down,up_down}`` - -``square`` -~~~~~~~~~~~ - -Options: ``--frequency``, ``--amplitude``, ``--duty-cycle``, ``--phase`` - -``sawtooth`` -~~~~~~~~~~~~~ - -Options: ``--frequency``, ``--amplitude``, ``--phase`` - -Digital modulation families: ``qam``, ``apsk``, ``pam``, ``psk`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -* ``--symbols``, ``--order`` -* ``--symbol-rate`` -* ``--filter {rrc,rc,gaussian,none}``, ``--filter-span``, ``--filter-beta`` -* ``--message-source {random,file,string}``, ``--message-content`` - -Use ``--message-source random`` for synthetic datasets, ``file`` for deterministic replay, -or ``string`` for small human-readable payload testing. Pulse-shaping filter options -(``--filter``, ``--filter-span``, ``--filter-beta``) control spectral occupancy and ISI. - -``fsk`` -~~~~~~~~ - -Options: ``--symbols``, ``--order``, ``--symbol-rate``, ``--freq-spacing``, -``--modulation-index``, ``--message-source``, ``--message-content`` - -``--freq-spacing`` and ``--modulation-index`` drive tone separation and spectral profile. - -``ook``, ``oqpsk``, ``gmsk`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Options: ``--symbol-rate`` (required), ``--message-source {random,file,string}``, -``--message-content``; ``gmsk`` also accepts ``--bt`` - -``gmsk --bt`` sets the Gaussian filter bandwidth-time product (spectral compactness vs -symbol transition sharpness). + * - Subcommand + - Unique options + * - ``tone`` + - ``--frequency``, ``--amplitude``, ``--phase`` + * - ``noise`` + - ``--noise-type {gaussian,uniform}``, ``--power`` + * - ``chirp`` + - ``--bandwidth`` (required), ``--period`` (required), ``--type {up,down,up_down}`` + * - ``square`` + - ``--frequency``, ``--amplitude``, ``--duty-cycle``, ``--phase`` + * - ``sawtooth`` + - ``--frequency``, ``--amplitude``, ``--phase`` + * - ``qam``, ``apsk``, ``pam``, ``psk`` + - ``--order``, ``--symbol-rate``, ``--filter {rrc,rc,gaussian,none}``, + ``--filter-span``, ``--filter-beta``, + ``--message-source {random,file,string}``, ``--message-content`` + * - ``fsk`` + - ``--order``, ``--symbol-rate``, ``--freq-spacing``, ``--modulation-index``, + ``--message-source``, ``--message-content`` + * - ``ook``, ``oqpsk``, ``gmsk`` + - ``--symbol-rate`` (required), ``--message-source``, ``--message-content``; + ``gmsk`` also accepts ``--bt`` (Gaussian filter bandwidth-time product) **Examples:** @@ -859,6 +1160,20 @@ symbol transition sharpness). ria generate qam -s 2e6 -r 100e3 -M 16 -N 5000 --message-source random -o qam16.npy ria synth psk -s 2e6 -r 100e3 -M 8 -N 8000 -o psk8.npy +**Example output:** + +.. code-block:: text + + $ ria generate tone -s 2e6 -n 100000 --frequency 50e3 -o tone.npy + + Generating tone: 50.00 kHz at 2.00 MS/s + +.. code-block:: text + + $ ria generate qam -s 2e6 -n 50000 --order 16 --symbol-rate 100e3 --message-source random -o qam16.npy + + Generating QAM-16 (2500 symbols)... + .. _cmd-transform: @@ -930,6 +1245,41 @@ inspect parameter hints. ``--view`` writes a PNG preview alongside transform out ria transform custom --transform-dir ./my_transforms --list ria transform custom my_filter in.npy out.npy --transform-dir ./my_transforms --params cutoff=0.2 +**Example output:** + +List available augmentations: + +.. code-block:: text + + $ ria transform augment --list + + Available augmentations: + amplitude_reversal Negates the amplitudes of both the I and Q data samples + channel_swap Switches the I (In-phase) with the Q (Quadrature) data samples + cut_out Cuts out random sections of IQ data and replaces them with zeros + drop_samples Randomly drops IQ data samples + generate_awgn Generates additive white gaussian noise relative to the SNR + magnitude_rescale Selects a random starting point and multiplies IQ data by a scalar + patch_shuffle Selects random patches and shuffles the data samples within them + quantize_parts Quantizes random parts of the IQ data by a few bits + quantize_tape Quantizes the IQ data by a few bits + spectral_inversion Negates the imaginary components (Q) of the data samples + time_reversal Reverses the order of I and Q data samples along the time axis + +Apply an impairment (AWGN at SNR=10 dB): + +.. code-block:: text + + $ ria transform impair add_awgn_to_signal sample_recording3.npy sample_recording3_awgn.npy --params snr=10 + + Impairing: sample_recording3.npy → sample_recording3_awgn.npy + Saved to: sample_recording3_awgn.npy + +.. figure:: ../images/recordings/sample_recording3_awgn.png + :alt: sample_recording3.npy after add_awgn_to_signal at SNR=10 dB + + ``sample_recording3.npy`` after ``add_awgn_to_signal --params snr=10`` + .. _cmd-transmit: @@ -948,30 +1298,21 @@ inspect parameter hints. ``--view`` writes a PNG preview alongside transform out ria transmit [options] -**Input source (choose one):** +If neither ``--input`` nor ``--generate`` is specified, the command defaults to a generated +LFM waveform. +**Options:** + +* ``-d`` / ``--device {pluto,hackrf,bladerf,usrp}``, ``-i`` / ``--ident``, ``-c`` / ``--config`` +* ``-s`` / ``--sample-rate``, ``-f`` / ``--center-frequency``, ``-g`` / ``--gain``, ``-b`` / ``--bandwidth`` * ``--input ``: transmit an existing recording. -* ``--generate {lfm,chirp,sine,pulse}``: synthesize a transmit signal on the fly. -* If neither is specified, the command defaults to a generated LFM waveform. - -**Core options:** - -*Device/radio:* ``-d`` / ``--device {pluto,hackrf,bladerf,usrp}``, ``-i`` / ``--ident``, -``-c`` / ``--config`` - -*RF:* ``-s`` / ``--sample-rate``, ``-f`` / ``--center-frequency``, ``-g`` / ``--gain``, -``-b`` / ``--bandwidth`` - -*Input/gen:* ``--input``, ``--legacy``, ``--generate {lfm,chirp,sine,pulse}`` - -*TX control:* - -* ``-r, --repeat`` +* ``--generate {lfm,chirp,sine,pulse}``: synthesize a signal on the fly. +* ``--legacy``: use older NPY loader for historical recordings. +* ``-r, --repeat``: transmit the input a fixed number of times. * ``--continuous``: transmit until interrupted (``Ctrl+C``). * ``--tx-delay``: pause between repeats when ``--repeat`` is used. * ``-y, --yes``: skip confirmation prompts; use carefully in scripted environments. - -*Logging:* ``-v`` / ``--verbose``, ``-q`` / ``--quiet`` +* ``-v`` / ``--verbose``, ``-q`` / ``--quiet`` .. warning:: ``--continuous`` transmits until manually interrupted. Validate gain settings, antenna @@ -985,6 +1326,25 @@ inspect parameter hints. ``--view`` writes a PNG preview alongside transform out ria transmit -d hackrf --generate lfm -f 2.44G --continuous ria transmit -d usrp --input msg.npy -r 3 --tx-delay 0.5 +**Example output:** + +.. note:: + ``transmit`` requires a TX-capable SDR. The following shows representative output for a + PlutoSDR playback. + +.. code-block:: text + + $ ria transmit -d pluto -f 915e6 -s 2e6 --input capture.sigmf-data + + Initializing PlutoSDR... + URI: ip:192.168.2.1 + Center frequency: 915.00 MHz + Sample rate: 2.00 MS/s + Gain: 0 dB + + Transmitting capture.sigmf-data (500,000 samples)... + Transmit complete. + 5) YAML Config Patterns ======================== @@ -1031,25 +1391,7 @@ experiment-specific fields on the CLI. ria generate noise --config generate.yaml -6) Version Notes -================= - -These notes are based on the current implementation and should be re-validated against future -releases. - -1. Some command docstrings and examples still mention ``utils`` or ``ria_toolkit_oss`` - command prefixes in text blocks. The operational command is ``ria ...``. -2. Command bindings currently import ``viewe`` instead of ``view`` in - ``src/ria_toolkit_oss_cli/ria_toolkit_oss/commands.py``. -3. Multiple non-CLI modules still import ``utils.*``, which can create runtime dependency - coupling when using only ``ria-toolkit-oss`` in isolation. - -.. tip:: - If you observe unexpected import errors after install, check the package version and - changelog, then test ``ria --help`` in a clean virtual environment. - - -7) Brief Scripting (Python) Preview +6) Brief Scripting (Python) Preview ===================================== For quick non-CLI use: