Skip to main content

RTL-SDR receiver driver for bench automation — IQ capture, spectrum, streaming, calibration

Project description

⚠️ UNTESTED — This implementation has not been verified against physical hardware. Code is complete but has not been bench-tested. Verify behavior before relying on output.

rf-bench-drivers-rtlsdr

RTL-SDR receiver driver for the rf-bench bench automation suite. Wraps pyrtlsdr (librtlsdr) with PPM calibration, consistent import patterns, workflow helpers, and a thread-safe streaming generator.

Supported hardware:

  • RTL-SDR Blog v4 (R828D tuner, 1 PPM TCXO, bias tee) — recommended
  • RTL-SDR Blog v3 (R820T2 tuner, bias tee)
  • Generic RTL2832U-based DVB-T dongles (no TCXO — calibrate PPM before use)

Installation

pip install rf-bench-drivers-rtlsdr
# or, for the full rf-bench suite:
pip install rf-bench

System dependency: librtlsdr must be installed.

# Arch Linux
pacman -S rtl-sdr

# Debian / Ubuntu
apt install rtl-sdr librtlsdr-dev

# macOS
brew install librtlsdr

Linux udev rule (required to access the device as a non-root user):

SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2838", \
    MODE="0664", GROUP="plugdev"

Save to /etc/udev/rules.d/99-rtlsdr.rules, then udevadm control --reload-rules and re-plug the dongle.


Quick start

from rf_bench.rtlsdr import RTLSDR

# Basic capture
with RTLSDR() as sdr:
    sdr.set_center_freq(144_390_000)   # 144.390 MHz — APRS
    sdr.set_sample_rate(2_400_000)
    sdr.set_gain(30)
    iq = sdr.capture_iq(262_144)
    print(f"Captured {len(iq)} samples, dtype={iq.dtype}")

# Power spectrum
with RTLSDR() as sdr:
    sdr.set_center_freq(433_920_000)
    sdr.set_sample_rate(2_400_000)
    iq = sdr.capture_iq(262_144)
    freq_hz, power_db = sdr.power_spectrum(iq, rbw_hz=1000)
    peak_idx = power_db.argmax()
    print(f"Strongest signal: {freq_hz[peak_idx]/1e6:.3f} MHz  ({power_db[peak_idx]:.1f} dB)")

# Streaming (ADS-B example)
with RTLSDR() as sdr:
    sdr.set_center_freq(1_090_000_000)
    sdr.set_sample_rate(2_000_000)
    sdr.set_gain(40)
    for block in sdr.stream_iq(block_size=65_536):
        process_modes_s(block)
        if should_stop:
            break
    sdr.stop_stream()

PPM calibration

The RTL-SDR v4 TCXO is nominally 1 PPM, but the actual offset should be measured against a known reference for best frequency accuracy:

  1. Connect the RTL-SDR to a signal of known frequency (e.g. the SDG1062X set to 10.000000 MHz, verified on the SSA).
  2. Tune the RTL-SDR to the same frequency.
  3. Compute the offset: ppm = (measured_hz - nominal_hz) / nominal_hz * 1e6
  4. Save it:
with RTLSDR() as sdr:
    sdr.save_calibration(ppm=0.8)    # adjust to your measured value

The correction is stored in ~/.rtlsdr_cal.json and loaded automatically on every subsequent RTLSDR() construction. Pass ppm_correction=0 explicitly to override.


Bias tee (RTL-SDR Blog v3/v4)

The bias tee supplies 5 V (~180 mA) on the SMA centre pin to power an inline LNA. Requires librtlsdr >= 2.0.0 (included in the Arch rtl-sdr package):

with RTLSDR() as sdr:
    sdr.set_bias_tee(True)    # power the LNA
    sdr.set_center_freq(137_620_000)
    sdr.set_sample_rate(2_400_000)
    iq = sdr.capture_iq(2_400_000 * 10)   # 10 s capture
    sdr.set_bias_tee(False)   # disable before disconnecting the LNA

Always disable the bias tee before removing the antenna or LNA to avoid back-feeding the RTL-SDR input with the bias voltage.


Device enumeration

If multiple dongles are attached, select by serial number for reproducibility:

# List all attached devices
for dev in RTLSDR.find_devices():
    print(dev)
# {'index': 0, 'serial': '00000001', 'name': 'Generic RTL2832U OEM'}

# Connect to a specific dongle
with RTLSDR(serial="00000001") as sdr:
    print(sdr.identify())

Set a permanent serial number with rtl_eeprom -s 00000001 (from the rtl-sdr package).


API reference

RTLSDR(serial=None, device_index=0, ppm_correction=None, sample_rate=2_400_000, gain='auto')

Opens the device. Raises RTLSDRError if the serial is not found. Raises RTLSDRBusyError if another process already has the device open.

Method Description
find_devices() Class method — list attached devices as [{'index', 'serial', 'name'}]
identify() Return device info dict: serial, tuner_type, valid_gains_db, sample_rate, center_freq, gain, ppm_correction
set_center_freq(freq_hz) Set tuning frequency; PPM correction applied automatically
set_sample_rate(rate) Set IQ sample rate in S/s (typical: 250_000 – 3_200_000)
set_gain(gain_db) Set gain in dB (snapped to nearest valid step) or 'auto'
set_bias_tee(enabled) Enable/disable bias tee (RTL-SDR Blog v3/v4 only)
capture_iq(num_samples) Capture a block; returns complex64 numpy array
power_spectrum(iq, rbw_hz) Welch PSD; returns (freq_hz, power_db) float32 arrays
scan_activity(threshold_db, num_samples) Quick scan; returns list of detected signals
stream_iq(block_size) Generator yielding complex64 blocks; call stop_stream() when done
stop_stream() Stop streaming and join the reader thread
save_calibration(ppm) Save PPM correction to ~/.rtlsdr_cal.json
close() Stop streaming and close device

Valid sample rates

The RTL2832U supports a continuous range from ~250 kS/s to 3.2 MS/s. Values outside 900 kS/s – 3.2 MS/s may have increased sample loss on some systems. Common choices:

Rate Bandwidth Typical use
250_000 200 kHz Narrow-band FM, single ISM channel
1_024_000 ~800 kHz ACARS, P25 channel bank
2_000_000 ~1.6 MHz ADS-B (1090 MHz)
2_048_000 ~1.6 MHz DVB-T, general use
2_400_000 ~2.0 MHz Weather satellites, wideband ISM
3_200_000 ~2.6 MHz Maximum reliable rate

Power spectrum note

power_spectrum() returns power values relative to the peak bin (0 dB = peak). The values are not calibrated absolute dBm — the RTL-SDR has no calibrated power reference. Use the SSA3032X Plus for absolute amplitude measurements; use the RTL-SDR for signal detection, classification, and protocol decode where relative power and frequency are sufficient.


Hardware connection notes

The RTL-SDR Blog v4 appears as a single USB device (RTL2832U, USB VID 0x0bda PID 0x2838 or variant). Only one process may open it at a time; RTLSDR() raises RTLSDRBusyError if it is already in use.

For ADS-B (1090 MHz), a dedicated 1090 MHz bandpass filter (FA 1090 MHz or similar) before the LNA significantly reduces out-of-band noise and improves decode rate.

For weather satellites (137 MHz), a V-dipole antenna (~54 cm elements, 120° spread) is optimal — omnidirectional in elevation, no tracking required for LEO passes.


License

GPL-3.0-or-later

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rf_bench_drivers_rtlsdr-0.1.1.tar.gz (24.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rf_bench_drivers_rtlsdr-0.1.1-py3-none-any.whl (23.5 kB view details)

Uploaded Python 3

File details

Details for the file rf_bench_drivers_rtlsdr-0.1.1.tar.gz.

File metadata

  • Download URL: rf_bench_drivers_rtlsdr-0.1.1.tar.gz
  • Upload date:
  • Size: 24.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.5

File hashes

Hashes for rf_bench_drivers_rtlsdr-0.1.1.tar.gz
Algorithm Hash digest
SHA256 39f508fa1d6bebfa5ca0a2210bbc488de0a2478f7fc13d65ada1b668bfa7790b
MD5 ac8c93f85b4d58acf813331a0e4d4082
BLAKE2b-256 431691c07851d3219910a016b908783b1412b80203a525b6c582800b1bf56b6b

See more details on using hashes here.

File details

Details for the file rf_bench_drivers_rtlsdr-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for rf_bench_drivers_rtlsdr-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 fae074cd36edc273c33929b6fee4521fb1dedb0e1a67575ace1be3481b4ac467
MD5 37b62d8edea70664897def634e4e5f48
BLAKE2b-256 bd9542d9ffea838eb7720ac7cdfca6b03145b36158528d87d72b6ff9c0749f5e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page