desperado 0.2.1

Iterate and stream I/Q samples from stdin, files, TCP streams and SDR devices
Documentation

Desperado

A unified Rust library for reading I/Q samples from files, SDR devices, and streams

Crates.io Documentation License: MIT CI

Desperado is a library designed to factorize and reuse code for reading I/Q samples from files, SDR devices, and other sources.

Desperado provides a unified interface for iterating (synchronously) and streaming (asynchronously) complex I/Q samples in Complex<f32> format.

The library intentionally does not include demodulation, focusing instead on providing a consistent interface over various sources.

The name "Desperado" is a playful nod to DSP (Digital Signal Processing), and tips its hat to https://www.youtube.com/watch?v=-q93wc3-deU.

What are I/Q Samples?

I/Q (In-phase/Quadrature) samples are the fundamental representation of radio signals in software-defined radio (SDR). They represent complex numbers where:

  • I (In-phase): The real component, representing the signal along the cosine axis
  • Q (Quadrature): The imaginary component, representing the signal along the sine axis

Together, I and Q samples capture both the amplitude and phase information of a radio signal, allowing software to process, demodulate, and analyze RF signals that have been digitized by SDR hardware.

Desperado abstracts away the complexity of reading these samples from various sources (files, devices, network streams), providing a consistent interface regardless of the source or format (8-bit, 16-bit, float, etc.).

Installation

Add Desperado to your Cargo.toml:

[dependencies]
desperado = "0.1"

With SDR device support

To use hardware SDR devices, enable the appropriate feature flags:

[dependencies]
desperado = { version = "0.1", features = ["rtlsdr"] }  # For RTL-SDR devices
# or
desperado = { version = "0.1", features = ["soapy"] }   # For SoapySDR-compatible devices
# or
desperado = { version = "0.1", features = ["pluto"] }   # For Adalm-Pluto devices

Available features

  • rtlsdr: RTL-SDR device support (DVB-T dongles)
  • soapy: SoapySDR device support (HackRF, LimeSDR, etc.)
  • pluto: Adalm-Pluto SDR support

The following features are only needed for examples:

  • clap: Command-line argument parsing for examples
  • waterfall: Waterfall plot visualization example
  • audio: FM demodulation examples with audio output

Projects using desperado

  • jet1090 - Real-time ADS-B decoder for tracking aircraft
  • ship162 - AIS decoder for tracking maritime vessels

If you're using Desperado in your project, feel free to open a PR to add it here!

Usage

Basic example (synchronous version)

use desperado::{IqFormat, IqSource};

fn main() -> desperado::Result<()> {
    // Create an IQ source from a binary file
    let path = "sample.iq";
    let sample_rate = 96_000;
    let center_freq = 162_000_000;
    let chunk_size = 8136;
    let iq_format = IqFormat::Cu8;

    for samples in IqSource::from_file(path, center_freq, sample_rate, chunk_size, iq_format)? {
        for s in samples? {  // samples is a Result<Vec<Complex<f32>>, _>
            println!("  I: {}, Q: {}", s.re, s.im);
        }
    }
    Ok(())
}

RTL-SDR (async version)

Access to RTL-SDR devices is provided with the rtlsdr feature enabled.

use desperado::IqAsyncSource;
use futures::StreamExt;

#[tokio::main]
async fn main() -> desperado::Result<()> {
    let device_index = 0;
    let sample_rate = 2_400_000;
    let center_freq = 1_090_000_000;
    let gain = Some(496);

    let reader = IqAsyncSource::from_rtlsdr(device_index, center_freq, sample_rate, gain).await?;

    while let Some(samples) = reader.next().await {
        // Process samples...
    }

    Ok(())
}

More data sources

Desperado supports various data sources: the following table summarizes the available sources.

Methods are available in both synchronous (IqSource) and asynchronous (AsyncIqSource) versions.

Frontend Method name Optional feature Identifier
I/Q File [Async]IqSource::from_file file name
Standard Input [Async]IqSource::from_stdin
TCP socket [Async]IqSource::from_tcp address and port
RTL-SDR [Async]IqSource::from_rtlsdr rtlsdr device index
Soapy [Async]IqSource::from_soapy soapy device arguments
Adalm-Pluto [Async]IqSource::from_pluto pluto URI

All samples are returned as Complex<f32> values, regardless of the source.

  • The rtlsdr feature enables support for RTL-SDR devices (DVB-T dongles). It is based on the rtl-sdr-rs crate which is a pure Rust implementaiton of the RTL-SDR driver.
  • The soapy feature enables support for SoapySDR-compatible devices (HackRF, LimeSDR, BladeRF, etc.). It is based on the soapysdr crate which provides Rust bindings to the SoapySDR C++ library. The SoapySDR library must be installed separately.
  • The pluto feature enables support for Adalm-Pluto devices. It is based on the pluto-sdr crate which provides Rust bindings to the libiio C library. The libiio library must be installed separately.

Contributions to include more SDR frontends (LimeSDR, HackRF, etc.) or to port existing ones to pure Rust implementations are welcome.

Sync vs Async: Which should I use?

Desperado provides both synchronous (IqSource) and asynchronous (AsyncIqSource) interfaces. Here's how to choose:

Use Synchronous (IqSource) when:

  • Processing files offline: Reading recorded I/Q files for analysis
  • Simple applications: You don't need concurrent operations
  • Blocking is acceptable: Your application can wait for I/O
  • Easier to reason about: Simpler control flow

Use Asynchronous (AsyncIqSource) when:

  • Real-time processing: Working with live SDR devices
  • Concurrent operations: Processing multiple streams simultaneously
  • Non-blocking required: Your application must remain responsive
  • Integrating with async ecosystem: Using tokio, async-std, etc.

Performance note: For single-threaded file processing, synchronous can be faster due to less overhead. For real-time SDR applications, async is typically better.

Performance Tuning

Chunk Size Selection

The chunk_size parameter determines how many I/Q samples are read in each iteration. Choosing the right size affects both performance and latency:

General guidelines:

  • Small chunks (1K-4K samples): Lower latency, more overhead, good for interactive applications
  • Medium chunks (8K-16K samples): Balanced performance, recommended for most applications
  • Large chunks (32K-64K samples): Better throughput, higher latency, good for batch processing

Example for different use cases:

// Real-time ADS-B decoding (low latency needed)
let source = IqSource::from_file(path, freq, rate, 4096, format)?;

// General SDR processing (balanced)
let source = IqSource::from_file(path, freq, rate, 16384, format)?;

// Batch file processing (maximum throughput)
let source = IqSource::from_file(path, freq, rate, 65536, format)?;

Hardware SDR Performance

RTL-SDR tips:

  • Sample rates above 2.4 MS/s may cause USB bandwidth issues
  • Use manual gain instead of AGC for better performance
  • On Linux, consider increasing USB buffer size: sudo modprobe rtl2832_sdr buffering=1

SoapySDR tips:

  • Check device-specific documentation for optimal buffer sizes
  • Some devices benefit from specific stream arguments
  • Monitor for dropped samples with verbose logging

PlutoSDR tips:

  • Buffer sizes should match your processing requirements
  • Network latency affects performance for IP-connected devices
  • Use USB 3.0 connections when possible

Format Considerations

Different I/Q formats have different performance characteristics:

Format Bandwidth Precision Use Case
Cu8 Lowest 8-bit RTL-SDR, bandwidth-limited scenarios
Cs8 Low 8-bit Signed 8-bit devices
Cs16 Medium 16-bit Higher dynamic range, HackRF
Cf32 Highest 32-bit Pre-processed files, maximum precision

Recommendation: Use the native format of your source when possible to avoid unnecessary conversions.

Troubleshooting

RTL-SDR kernel modules (Linux)

If the RTL kernel modules are installed you will need to temporarily unload them before using this library as follows:

sudo rmmod rtl2832_sdr
sudo rmmod dvb_usb_rtl28xxu
sudo rmmod rtl2832
sudo rmmod rtl8xxxu

Failure to do so will result in the following USB error:

thread 'main' panicked at 'Unable to open SDR device!: Usb(Busy)'

RTL-SDR Device Detection

To list available RTL-SDR devices:

rtl_test

SoapySDR Device Detection

To list available SoapySDR devices:

SoapySDRUtil --find

If your device isn't detected, ensure the appropriate SoapySDR module is installed (e.g., SoapyRTLSDR, SoapyHackRF, SoapyLimeSDR).

Adalm-Pluto Device Detection

To list available Adalm-Pluto devices:

iio_info -s

Finding I/Q Sample Files

If you need I/Q samples for testing or development:

Public Datasets

Capturing Your Own

With RTL-SDR:

rtl_sdr -f 1090000000 -s 2400000 -n 24000000 adsb_sample.iq

With SoapySDR:

SoapySDRUtil --rate=2.4e6 --freq=1090e6 --output=adsb_sample.iq

File Formats

Desperado supports raw I/Q files in various formats:

  • Cu8 (Complex unsigned 8-bit): Most common for RTL-SDR, 2 bytes per sample
  • Cs8 (Complex signed 8-bit): 2 bytes per sample
  • Cs16 (Complex signed 16-bit): 4 bytes per sample, common for SoapySDR/Pluto
  • Cf32 (Complex 32-bit float): 8 bytes per sample, high precision

Contributing

Contributions are welcome! Here are some ways you can help:

  • Add SDR device support: HackRF, LimeSDR, BladeRF, etc.
    Consider pure Rust implementations where possible!
  • Improve documentation: Fix typos, add examples, clarify explanations
  • Report bugs: Open an issue with details and reproduction steps
  • Add tests: Help improve test coverage
  • Share your project: Using Desperado? Add it to the "Projects Using Desperado" section!

Please ensure:

  • Code follows Rust conventions (cargo fmt, cargo clippy)
  • All tests pass (cargo test --all-features)
  • New features include documentation and tests

For major changes, please open an issue first to discuss the approach.

License

This project is licensed under the MIT License. See the license.md file for details.