Skip to content

Latest commit

 

History

667 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

iq_tool

A multi-threaded command-line tool for resampling, filtering, shifting, correcting, and demodulating I/Q data streams.

I originally built this tool for a very specific need: processing I/Q files with NRSC-5 (HD Radio) captures for use with the NRSC5 decoder. This means it's particularly good at handling the frequency shifts and metadata found in WAV files from SDR software, but it has grown into a more general-purpose utility.

The goal of this tool is to provide a command-line utility for converting I/Q data from files or SDRs to a specific sample rate and format, and then either piping it to other programs or writing it to a file. While it features several built-in demodulation modules (like FM, AM, and HD Radio) for quick listening and decoding, it is fundamentally an I/Q processor rather than a full replacement for a desktop GUI SDR application.


⚠️ A Quick Word of Warning: This is a fast moving AI-Assisted Project ⚠️

If you're looking for a stable platform, look at GNU Radio. This project is a playground and should not be relied upon. At least not yet.

Let's be upfront: a large language model (AI) helped write a significant portion of this code, if not most. I guided it, reviewed its output the best I could, and tested the result, but this project didn't evolve through the typical trial-and-error of a human-only endeavor. Even this README you're reading was drafted by the AI based on the source code, then edited and refined by me.

Second, it's worth knowing that this was a learning project for me. I chose to use C for its simplicity and how it keeps you close to the metal; I also figured it would be a better target for AI code and DSP versus a more complex language like C++. Since I was learning the language as I went, what you'll see in the code is my journey of tackling C, threading, and DSP all at once. The focus was always on getting a practical, working result, which means some of the solutions are probably not what you'd find in a textbook. However, I have made a effort to clean up and refactor the code to the best of my knowledge and research.

What does this mean?

  • It's Experimental. While it works, it hasn't been battle-tested across a wide variety of systems and edge cases nor has it had a security review.
  • Design choices not stable. You may see features, command line options, etc. suddenly appear and disappear. You may also see large commits of lots of changes. The mainline codebase may also be broken at times due to fast moving code and changes. Releases may be more stable.
  • Bugs are expected. The logic very likely has quirks that haven't been discovered yet. Other issues causing crashes likely exist too.
  • Use with caution! I wouldn't use this for anything mission-critical without a thorough personal review.

What It Can Do

  • Multi-Threaded Pipeline: Uses an Input -> Chunker -> Dynamic DSP -> Output architecture.
  • Flexible Inputs:
    • WAV Files: Reads standard 8-bit and 16-bit complex (I/Q) WAV files.
    • Raw I/Q Files: Just point it at a headerless file, but you have to tell it the sample rate and format.
    • SDR Hardware: Streams directly from RTL-SDR, SDRplay, HackRF, Airspy, Airspy HF+, BladeRF, and HydraSDR devices.
    • SpyServer Support: Connect to networked SpyServer instances.
  • WAV Metadata Parsing: Automatically reads metadata from SDR I/Q captures for automatic frequency correction.
    • auxi chunks from SDR Console, SDRconnect, and SDRuno.
    • SDR# style filenames (e.g., ..._20240520_181030Z_97300000Hz_...).
  • Processing Features:
    • Resampling to a new sample rate.
    • Frequency Shifting: Apply shifts before or after resampling.
    • Demodulation & Decoding:
      • Wideband FM (WFM): Demodulates FM Broadcast signals to audio with integrated RDS/RBDS decoding.
      • Narrowband FM (NFM): Demodulates voice signals with adjustable squelch and standard/narrow bandwidths
      • NOAAWX (SAME): Demodulates NOAA Weather Radio signals and decodes SAME (Specific Area Message Encoding) alerts.
      • AM: Demodulates AM signals using a Synchronous (PLL) detector, or standard Envelope detection.
      • NRSC5 (HD Radio): Integrated support for demodulating and playing HD Radio streams (via libnrsc5).
  • Automatic Gain Control (AGC): A Harris/LMS block-level tracker with a configurable deadband for digital (MER preservation) and analog signal leveling.
    • Filtering:
      • Apply low-pass, high-pass, band-pass, or notch FIR filters.
      • Offers two processing methods: a FIR (time-domain) method and an FFT (frequency-domain) method and will attempt to automatically default to the most suitable method.
    • Automatic I/Q Correction: Can optionally find and fix I/Q imbalance on the fly. This is very experimental and possibly could make it worse.
    • DC Blocking: A simple high-pass filter to remove the DC offset.
  • Versatile Outputs:
    • Container Formats: stdout (for piping), raw-file (headerless files), standard wav, and wav-rf64 (for files >4GB).
    • Sample Formats: Supports a variety of complex sample formats including cs16, cu8, cs8, and more.
    • Presets: Define your favorite settings in a config file for quick access.

Getting Started: Building from Source

Dependencies:

  • A C11 compiler: (GCC, Clang)
  • CMake: (version 3.10 or higher)
  • libsndfile
  • liquid-dsp
  • libexpat
  • pthreads: This is a standard system component on Linux/macOS. On Windows, a compatible version is typically included with the MinGW-w64 toolchain.
  • (Optional) libfftw3: For a performance boost with FFT-based filtering, install (libfftw3-dev) before building or installing liquid-dsp.
  • (Optional) RTL-SDR Library (librtlsdr): For RTL-SDR support (e.g., librtlsdr-dev).
  • (Optional) BladeRF Library (libbladeRF): For BladeRF support (e.g., libbladerf-dev). Windows installers found here.
  • (Optional) HackRF Library (libhackrf): For HackRF support (e.g., libhackrf-dev).
  • (Optional) Airspy Library (libairspy): For Airspy support (e.g., libairspy-dev).
  • (Optional) Airspy HF+ Library (libairspyhf): For Airspy HF+ support (e.g., libairspyhf-dev).
  • (Optional) HydraSDR Library (libhydrasdr): For HydraSDR support. GitHub repo found here. (Please make sure latest firmware is installed otherwise the HydraSDR module will not work).
  • (Optional) SDRplay API Library: To build with SDRplay support, you must first download and install the official API from the SDRplay website.
  • (Optional) NRSC5 Library (libnrsc5): For NRSC5 (HD Radio) playback support. GitHub repo found here.

On Linux (Debian/Ubuntu Example for all modules)

  1. Install the dependencies:

    sudo apt-get update
    sudo apt-get install build-essential cmake libsndfile1-dev libliquid-dev libexpat1-dev librtlsdr-dev libhackrf-dev libairspy-dev libairspyhf-dev libbladerf-dev libusb-1.0-0-dev libfftw3-dev
  2. Build HydraSDR if enabling support:

    git clone https://github.com/hydrasdr/hydrasdr-host
    cd hydrasdr-host/libhydrasdr
    mkdir build
    cd build
    cmake ..
    make or make -j N (Replace N with number of threads)
    sudo make install
    sudo ldconfig
  3. Build NRSC5 if enabling support:

    git clone https://github.com/theori-io/nrsc5
    cd nrsc5
    mkdir build
    cd build
    cmake .. (Add -DUSE_SSE=ON for x86 or -DUSE_NEON=ON for ARM for higher performance)
    make or make -j N (Replace N with number of threads)
    sudo make install
    sudo ldconfig
  4. Build the tool:

    git clone https://github.com/pclov3r/iq_tool.git
    cd iq_tool
    mkdir build
    cd build
    
    # Build without any optional SDR support besides Spyserver support
    cmake ..
    
    # Or, build with everything enabled
    cmake -DWITH_RTLSDR=ON -DWITH_SDRPLAY=ON -DWITH_HACKRF=ON -DWITH_AIRSPY=ON -DWITH_AIRSPYHF=ON -DWITH_BLADERF=ON -DWITH_HYDRASDR=ON -DWITH_NRSC5=ON ..
    
    make or make -j N (Replace N with number of threads)
    make install 

You'll find the iq_tool executable in the build directory.

On Windows

Using Pre-compiled Binaries

Pre-compiled binaries for Windows are available on the project's releases page.

NRSC5 Support: To use the NRSC5 output module on Windows, you must source the libnrsc5.dll library yourself or compile it yourself following the Windows build instructions, and copy it into the same folder as the iq_tool.exe executable. If the DLL is not found, the program will return an error saying the library is missing.

Note: Not every commit will result in a new release and Windows binary. Releases are typically made after significant changes.

  1. Download and Extract: Download the .zip archive. You must extract all files (the .exe and all .dll files) into the same folder for the program to work.
  2. Choose the Correct Build: Pay close attention to the AVX vs. AVX2 builds available.
    • Note: Always attempt to use the AVX2 build first for maximum performance. If your processor does not support it, the program will notify you to download the standard AVX version instead.

Building from Source

  1. Environment: Your best bet is to use a MinGW-w64 toolchain. Building within an MSYS2 environment may also work but has not been tested.
  2. Build Script: The repository includes a Windows build script (support/win-cross-compile) that can be used as a starting point.
    • Note: The script contains hard-coded paths to dependencies. You will need to edit this script and adjust the paths to match your own build environment or build the dependencies matching the layout.

How to Use It

The best way to see all options is to run iq_tool --help.

Note on Command-Line Options: The help message is dynamic. It intelligently shows only the options relevant to the features you enabled during compilation. For example, if you build the tool without SDRplay support, you won't see the --sdrplay-* options. The example below shows the output with all SDR modules enabled.

Command-Line Options

Usage: iq_tool -i <in_type> [in_file] -o <out_type> [out_file] [options]

Required Input & Output
    -i, --input=<str>                         Specifies the input module.
    -o, --output=<str>                        Specifies the output module and optional file path

Audio Output Options
    --audio-writer=<str>                      Save demodulated audio to a WAV file.
    --audio-writer-rf64                       Use RF64 format for the audio writer (supports >4GB files).
    --mute-audio                              Disable speaker playback

Input Processing Options
    --input-gain-multiplier=<flt>             Apply a linear gain multiplier to INPUT samples (before processing).

Baseband Processing Options
    --baseband-sample-rate=<flt>              Baseband sample rate feeding into a demodulator in Hz.
    --baseband-sample-format=<str>            Baseband sample format feeding into a demodulator {cf32|cs16|...}.
    --baseband-gain-multiplier=<flt>          Apply a linear gain multiplier to baseband samples before demodulation.

I/Q Output Options
    --output-sample-rate=<flt>                Output sample rate in Hz.
    --output-sample-format=<str>              Sample format for output data {cs8|cu8|cs16|...}.
    --output-gain-multiplier=<flt>            Apply a linear gain multiplier to OUTPUT samples before saving.

General Options
    --raw-passthrough                         Bypass all processing. Copies raw input bytes directly to output.
    --preset=<str>                            Use a preset for a common target.

SDR General Options
    --sdr-rf-freq=<flt>                       (Required for SDR) Tuner center frequency in Hz
    --sdr-freq-offset=<flt>                   Frequency offset in Hz (e.g. 125e6 for HamItUp).
    --sdr-sample-rate=<flt>                   Set sample rate in Hz. (Device-specific default)
    --sdr-bias-t                              (Optional) Enable Bias-T power.

DC Block Options
    --dc-block                                (Optional) Enable DC offset removal (high-pass filter).

I/Q Correction Options
    --iq-correction                           (Optional) Enable automatic I/Q imbalance correction.

Frequency Shift Options
    --freq-shift=<flt>                        Apply a direct frequency shift in Hz (e.g., -100e3)
    --shift-after-resample                    Apply frequency shift AFTER resampling (default is before)

Filtering Options (Chain up to 5 by combining options or adding suffixes -2, -3, etc. e.g., --lowpass --stopband --lowpass-2 --pass-range --pass-range-2)
    --lowpass=<flt>                           Isolate signal at DC. Keeps freqs from -<hz> to +<hz>.
    --highpass=<flt>                          Remove signal at DC. Rejects freqs from -<hz> to +<hz>.
    --pass-range=<str>                        Isolate a specific band. Format: 'start_freq:end_freq'.
    --stopband=<str>                          Remove a specific band (notch). Format: 'start_freq:end_freq'.

Filter Quality Options
    --transition-width=<flt>                  Set filter sharpness by transition width in Hz. (Default: Auto).
    --filter-taps=<int>                       Set exact filter length. Overrides --transition-width.
    --attenuation=<flt>                       Set global filter & resampler stop-band attenuation in dB. (Default: Auto).

Filter Implementation Options
    --filter-type=<str>                       Set filter implementation {fir|fft}. (Default: auto).
    --filter-fft-size=<int>                   Set FFT size for 'fft' filter type. Must be a power of 2.

AGC Options
    --baseband-agc                            Enable automatic gain control on the baseband signal before demodulation.
    --baseband-agc-target=<flt>               AGC target magnitude (0.0 - 1.0). (Default: 0.12)
    --output-agc                              Enable automatic gain control on the output signal before saving.
    --output-agc-target=<flt>                 AGC target magnitude (0.0 - 1.0). (Default: 0.12)

WAV Input (wav)
    --wav-center-target-freq=<flt>            Shift signal to a new target center frequency (e.g., 97.3e6)
    --wav-read-split-files                    Enable sequential reading of split WAV files.
    --wav-repeat                              Loop the WAV input file or sequence.

Raw File Input (rawfile)
    --rawfile-input-sample-rate=<flt>         (Required) The sample rate of the RAW input file.
    --rawfile-input-sample-format=<str>       (Required) The sample format of the RAW input file.
    --rawfile-repeat                          Loop the RAW input file.

Standard Input (stdin)
    --stdin-input-sample-rate=<flt>           (Required) The sample rate of the stdin stream in Hz.
    --stdin-input-sample-format=<str>         (Required) The sample format of the stdin stream.

RTL-SDR Input (rtlsdr)
    --rtlsdr-device-index=<int>               Select specific RTL-SDR device by index (0-indexed). (Default: 0)
    --rtlsdr-gain=<flt>                       Set manual tuner gain in dB (e.g., 28.0, 49.6). Disables AGC.
    --rtlsdr-ppm=<int>                        Set frequency correction in parts-per-million. (Optional, Default: 0)
    --rtlsdr-direct-sampling=<int>            Enable direct sampling mode for HF reception (1=I-branch, 2=Q-branch)

SDRplay Input (sdrplay)
    --sdrplay-bandwidth=<flt>                 Set analog bandwidth in Hz. (Optional, Default: 1.536e6)
    --sdrplay-device-index=<int>              Select specific SDRplay device by index (0-indexed). (Default: 0)
    --sdrplay-lna-state=<int>                 Set LNA state (0=min gain). Disables AGC.
    --sdrplay-if-gain=<int>                   Set IF gain in dB (fine gain, e.g., -20, -35, -59). (Default: -50 if a manual LNA state is specified.) Disables AGC.
    --sdrplay-lna-state-target=<flt>          Target a percentage (0-100) of the maximum LNA state. Automatically rounds down to the nearest LNA state.
    --sdrplay-lna-round-up                    Force LNA state target to round UP instead of down.
    --sdrplay-antenna=<str>                   Select antenna port (device-specific).
    --sdrplay-notch-fm                        Enable FM Broadcast Notch Filter.
    --sdrplay-notch-dab                       Enable DAB Broadcast Notch Filter.
    --sdrplay-notch-am                        Enable MW/AM Notch Filter (RSPduo Tuner A only).

HackRF Input (hackrf)
    --hackrf-lna-gain=<int>                   Set LNA (IF) gain in dB. (Optional, Default: 16)
    --hackrf-vga-gain=<int>                   Set VGA (Baseband) gain in dB. (Optional, Default: 0)
    --hackrf-amp-enable                       Enable the front-end RF amplifier (+14 dB).

Airspy Input (airspy)
    --airspy-gain-mode=<str>                  Gain mode: 'linearity', 'sensitivity', or 'manual'. (Default: AGC)
    --airspy-gain-value=<int>                 Gain value for linearity/sensitivity modes (0-21). (Default: 10)
    --airspy-lna-gain=<int>                   Manual LNA gain (0-14). Only with manual mode. (Default: 5)
    --airspy-mixer-gain=<int>                 Manual Mixer gain (0-15). Only with manual mode. (Default: 5)
    --airspy-vga-gain=<int>                   Manual VGA gain (0-15). Only with manual mode. (Default: 5)
    --airspy-sample-format=<str>              Sample format: 'cf32' or 'cs16'. (Default: cs16)
    --airspy-serial=<int>                     Select device by serial number (hex, e.g., 0x123456789ABCDEF0).
    --airspy-packing                          Enable bit-packing mode (12-bit samples).

Airspy HF+ Input (airspyhf)
    --airspyhf-agc=<str>                      AGC mode: 'off', 'low', or 'high'. (Default: high)
    --airspyhf-attn=<flt>                     Attenuation in dB (0.0 to 48.0). (Default: 0.0)
    --airspyhf-preamp                         Enable LNA/PreAmp.
    --airspyhf-serial=<int>                   Select device by serial number (hex, e.g., 0x123456789ABCDEF0).
    --airspyhf-no-lib-dsp                     Disable library DSP processing (IQ correction, DC removal, etc).

HydraSDR Input (hydrasdr)
    --hydrasdr-gain-mode=<str>                Gain mode: 'linearity', 'sensitivity', or 'manual'. (Default: AGC)
    --hydrasdr-gain-value=<int>               Gain value for linearity/sensitivity modes (0-21). (Default: 10)
    --hydrasdr-lna-gain=<int>                 Manual LNA gain (0-14). Only with manual mode. (Default: 5)
    --hydrasdr-mixer-gain=<int>               Manual Mixer gain (0-15). Only with manual mode. (Default: 5)
    --hydrasdr-vga-gain=<int>                 Manual VGA gain (0-15). Only with manual mode. (Default: 5)
    --hydrasdr-sample-format=<str>            Sample format: 'cf32' or 'cs16'. (Default: cs16)
    --hydrasdr-serial=<int>                   Select device by serial number (hex, e.g., 0x123456789ABCDEF0).
    --hydrasdr-packing                        Enable bit-packing mode (12-bit samples).

BladeRF Input (bladerf)
    --bladerf-device-index=<int>              Select specific BladeRF device by index (0-indexed). (Default: 0)
    --bladerf-load-fpga=<str>                 Load an FPGA bitstream from the specified file.
    --bladerf-bandwidth=<flt>                 Set analog bandwidth in Hz. (Not applicable in 8-bit high-speed mode)
    --bladerf-gain=<int>                      Set overall manual gain in dB. Disables AGC.
    --bladerf-channel=<int>                   For BladeRF 2.0: Select RX channel 0 (RXA) or 1 (RXB). (Default: 0)
    --bladerf-bit-depth=<int>                 Set capture bit depth {8|12}. 8-bit mode is for BladeRF 2.0 only. (Default: 12, auto-switches to 8 for rates > 61.44 MHz on BladeRF 2.0)

SpyServer Client Input (spyserver-client)
    --spyserver-client-host=<str>             Hostname or IP of the spyserver instance (Required).
    --spyserver-client-port=<int>             Port number of the spyserver instance (Required).
    --spyserver-client-gain=<int>             Set manual gain. Disables AGC. (Ignored on servers without gain control)
    --spyserver-client-sample-format=<str>    Select sample format {cu8|cs16|cs24|cf32}. Default is cu8.

RAW File Output (rawfile)

    (No module-specific options)

WAV Output (wav)

    (No module-specific options)

WAV RF64 Output (wav-rf64)

    (No module-specific options)

Standard Output (stdout)

    (No module-specific options)

Direct Pipe Output (directpipe)

    (Pure data pipe bypassing stdio
     Route with '10>' or '10> >(...)')

    (No module-specific options)

NRSC5 Output (nrsc5)
    --nrsc5-band=<str>                        Set radio band {fm|am}. (Default: auto-detected)
    --nrsc5-program=<int>                     Select initial HD program/subchannel (0-7). (Required) Press keys 0-7 during playback to switch.
    --nrsc5-aas-dir=<str>                     Directory to dump AAS files (logos, maps, etc).

WFM Output (wfm)
    --wfm-de-emphasis-time=<flt>              Set FM de-emphasis time constant in microseconds (default: 75.0).
    --wfm-gain=<flt>                          Set audio output gain (linear).
    --wfm-force-stereo                        Force stereo decoding regardless of signal quality.
    --wfm-force-mono                          Force mono output.
    --wfm-raw-mpx-stdout                      Pipe raw MPX data (S16 Mono) to stdout while playing audio.
    --wfm-no-rds                              Disable RDS decoding (Enabled by default).
    --wfm-rds                                 Use World RDS standard (Default is US RBDS).

NFM Output (nfm)
    --nfm-gain=<flt>                          Audio gain (default: 1.0)
    --nfm-squelch=<flt>                       Squelch SNR threshold in dB (default: 10.0)
    --nfm-narrow                              Enable Narrow mode (2.5kHz dev). Default is Standard (5kHz dev).
    --nfm-no-squelch                          Disable squelch (force open audio)
    --nfm-no-discriminator-filter             Disable the discriminator filter.

NOAAWX Output (noaawx)
    --noaawx-gain=<flt>                       Audio gain (default: 1.0)
    --noaawx-no-nfm                           Bypass FM Demodulator and process input as direct audio.
    --noaawx-no-alert-tone                    Disable the alert tone on SAME alert received.
    --noaawx-standby                          Mute audio until a SAME alert is received, then mute again after EOM.

AM Output (am)
    --am-gain=<flt>                           Set audio output gain (linear).
    --am-cutoff=<flt>                         Set audio lowpass filter cutoff in Hz (default: 5000).
    --am-envelope                             Disable Synchronous AM (PLL) and use Magnitude Envelope Detection.

Available Presets
    nrsc5-usb                                 Isolates the Upper Sideband (USB) of an HD Radio signal.
    nrsc5-lsb                                 Isolates the Lower Sideband (LSB) of an HD Radio signal.

Help & Version
    -v, --version                             show program's version number and exit
    -h, --help                                show this help message and exit

Examples

Example 1: Basic File Resampling Resample a WAV file to a 16-bit RF64 (large WAV) file with a custom output rate.

iq_tool --input wav my_capture.wav --output wav-rf64 processed.wav --output-sample-format cs16 --output-sample-rate 240000

Example 2: Channel Selection (FFT Filter) Isolate a specific range of frequencies from a live SDR stream. The tool will automatically select the fft filter because this is an asymmetric (offset) filter.

iq_tool --input rtlsdr --sdr-rf-freq 98.5e6 --pass-range 50e3:250e3 --output stdout --output-sample-rate 240000 --output-sample-format cs16 | ...

Example 3: Decoding HD Radio (WAV Input) Decode an HD Radio capture. (Assumes the WAV has frequency metadata).

iq_tool --input wav my_capture.wav --wav-center-target-freq 97.3e6 --output nrsc5 --nrsc5-program 0

Example 4: Streaming HD Radio from an SDRplay Device Tune an SDRplay RSPdx to 102.5 MHz, set a manual gain level (using LNA state) and select an antenna port before outputting to nrsc5.

iq_tool --input sdrplay --sdr-rf-freq 102.5e6 --sdrplay-lna-state 4 --sdrplay-antenna B --output nrsc5 --nrsc5-program 0

Example 5: Piping to rtl_433 (ISM Band Decoding) Tune an Airspy to 433.92 MHz with linearity gain, downsample to 250 ksps, and pipe the cu8 output to rtl_433 for decoding weather stations and sensors.

iq_tool --input airspy --sdr-rf-freq 433.92e6 --airspy-gain-mode linearity --airspy-gain-value 15 --output stdout --output-sample-rate 250000 --output-sample-format cu8 | rtl_433 -r -

Configuration via Presets

iq_tool supports presets to save you from repeatedly typing the same output formatting options. A default iq_tool_presets.conf is included in the repository, which you can use as a starting point for your own configurations. A preset bundles common settings like target_rate, sample_format_name, and output_type into a single flag (--preset <name>), which is utilized for common piping configurations.

Pro Tip: If the tool finds config files in multiple locations, it will print a warning and load none of them to avoid confusion. Just delete the duplicates to fix it.

Search Locations:

  • Windows: The executable's directory, %APPDATA%\\iq_tool\\, %PROGRAMDATA%\\iq_tool\\
  • Linux: The current directory, $XDG_CONFIG_HOME/iq_tool/, /etc/iq_tool/ , /usr/local/etc/iq_tool

Current State & Future Plans

This tool is a work in progress.

  • Known Issues:

    • It's experimental. Expect bugs.
    • Windows builds are 64-bit only. I see no reason to post 32-bit ones given Windows 10 is end of life soon and Windows 11 is 64-bit only. If I'm wrong and it's required open an issue.
    • As mentioned, IQ correction may not be functioning correctly.
    • Log verbosity levels are not yet refined. The output is noisy, and many messages are not be at the appropriate level (e.g., ERROR vs. FATAL).
  • Roadmap:

    • Add RTL-SDR support.
    • Add BladeRF support.
    • Refactor configuration system for full modularity.
    • Implement FFT-based filtering option.
    • SpyServer Support
    • Implement Output AGC (DX, Local, and Digital profiles).
    • Airspy Support
    • Airspy HF+ support
    • SpyServer Support
    • Add HydraSDR support
    • Improve I/Q correction algorithm stability.
    • Refine and standardize log levels throughout the application.
    • Add unit tests.
    • General code cleanup and comment refactoring.
    • Improve the README.

For Developers: Architecture & Design

This section provides a high-level overview of the tool's internal design for those looking to understand, modify, or extend the codebase.

The Data Flow Pipeline

iq_tool processes data through a pipeline of concurrent, decoupled stages. Each major task runs in its own dedicated thread, using thread-safe lock-free ring buffers and queues to hand off data from one stage to the next. To maximize throughput and minimize latency, the pipeline utilizes a pre-allocated memory arena of SampleChunk structures, allowing for high-speed data flow without the overhead of dynamic memory allocation.

The sequence of threads and their responsibilities are as follows:

  1. Input Thread (Optional / Live Mode Only): This thread is active when using live inputs (SDR hardware, network streams, or OS pipes). Its sole responsibility is to acquire data from the hardware or protocol and write it into a lock-free ring buffer (input_ring_buffer). It uses a structured packet format (Header + Payload) which allows it to communicate stream events, such as hardware overruns or resets, to the rest of the pipeline. This thread runs at real-time priority to separate real-time data acquisition from downstream DSP processing.

  2. Chunker Thread: The bridge between raw data and the DSP chain.

    • In Live Mode: It "sips" data from the lock-free input ring buffer, parses packet headers, performs format conversion to complex float (cf32), and populates SampleChunk structures.
    • In File Mode: It reads raw data directly from the disk and formats chunks. Once a SampleChunk is filled, the Chunker thread pushes it into the DSP queue (chunker_output_queue).
  3. Dynamic DSP Thread(s): The core mathematical engine. Rather than a hardcoded pipeline, iq_tool uses a Dynamic DSP Process Chain. During initialization, the orchestrator evaluates the user's config and constructs an array of active DspModuleInterface blocks (e.g. AGC, DC Block, Resampler, Filter). The DSP thread rapidly passes the SampleChunk through this chain. Disabled modules are physically excluded from the chain, resulting in zero-overhead routing.

  4. Output Thread: The final stage in the pipeline. It dequeues formatted buffers from output_queue and delivers them to the destination.

    • Synchronous Output (File/Stdout): It manages backpressure; if the destination is slow, the output thread will block the pipeline to ensure no data is lost.
    • Real-time Output (Audio/Live): For demodulator and audio modules (AM, NFM, WFM, NOAAWX, NRSC5), the output thread interacts with the OS sound driver or decoders. If the pipeline runs faster than real-time, the output thread manages timing to synchronize playback.

The Modular System

The tool features a fully modular architecture designed to support additional data sources, DSP algorithms, and sinks. By strictly enforcing the Open-Closed Principle, hardware and format-specific logic is entirely decoupled from the core pipeline.

  • The Core Interfaces (module.h): All modules are built upon three primary virtual method tables (v-tables) defined in include/module.h:
    • InputModuleInterface: Standardizes how the pipeline initializes hardware and starts data acquisition.
    • DspModuleInterface: Standardizes how mathematical algorithms process chunks of samples, ensuring they maintain their own private memory states (encapsulated via void*).
    • OutputModuleInterface: Standardizes how data is delivered to a destination.
  • Encapsulation: Modules do not expose public header files. Module implementations, including state structures and function definitions, must remain strictly static to their respective translation units.
  • The Registry (module_registry.c): Serves as a dynamic factory and catalog of compiled-in modules, maintaining strict decoupling from module implementations.
  • Adding a New Module:
    1. Create the Source File: Add a .c file to the appropriate directory (src/input/, src/dsp/, src/output/).
    2. Implement the Interface: Fulfill the required function pointers in the matching *ModuleInterface struct (e.g., InputModuleInterface).
    3. Define CLI Options: Define any unique command-line flags within a static const struct argparse_option array and expose it via the get_cli_options interface method.
    4. Register the Module: Use the __attribute__((constructor)) compiler directive to dynamically register the module API with the registry during program initialization (e.g., via module_register_input()).
    5. Update the Build System: Append the source file to CMakeLists.txt using the register_module macro to handle optional dependencies and linking.

Acknowledgements

A massive thank you to Benjamin of HydraSDR for generously providing a HydraSDR unit.

Contributing

Contributions are welcome via pull requests and issues.

Since an AI had a heavy hand in writing this tool, AI-assisted contributions are accepted provided they meet project standards. All PRs will be carefully reviewed.

About

A tool to resample, filter, shift, correct, and demodulate I/Q data streams from files or SDRs.

Topics

Resources

Stars

22 stars

Watchers

4 watching

Forks

Releases

Used by

Contributors

Languages