Measuring Antenna Gain Patterns with Discovery Drive

Our Discovery Drive campaign is currently being crowd-funded on Crowd Supply. Please consider ordering a unit if you are interested in a high-quality, low-power, and portable antenna rotator. Below is an update from the campaign exploring a potential use-case for measuring antenna gain patterns:


In this update, we’ll examine an alternative use case: measuring antenna gain radiation patterns.

One interesting use of a capable Az/El rotator is to measure the radiation pattern of various antennas. This is normally done in an anechoic chamber, but if you have a large enough open space, it can be done cheaply with a rotator and signal source.

To test this as a proof of concept, we used Claude code to very quickly create a tool that could help us create an antenna pattern plot. The software tool simply rotates the antenna on the Discovery Drive one step at a time, measures the SNR using an RTL-SDR, and plots the reading on a graph. To be clear, this simple setup is not providing any sort of calibrated readings, but it will at least give you an idea of what the radiation pattern and performance of an antenna looks like.

In our test, we mounted a TV Yagi on the Discovery Drive and used our software to plot the radiation pattern at 433 MHz. As expected from a Yagi, we see higher gain at the front and lower gain at the rear.

Antenna Gain Results
Antenna Gain Results

Due to a lack of a suitable open area, this test was performed in a small backyard and, hence, the radiation pattern is a little lopsided due to multipath. In this test, we also used a simple omnidirectional antenna for the signal source, which exacerbated the multipath. A way to improve this test would be to use a directional antenna on the transmit side, too.

We will release this open-source tool for others to play with, but please be aware that it was only created for proof of concept. However, if there is interest, we can continue to refine it.

Below is a photo of the physical setup. A HackRF with Portapack and whip antenna are mounted on a tripod a few meters away, while the Discovery Drive carries a Yagi antenna. As the Discovery Drive rotates the Yagi through 0 to 360° in azimuth and -30 to 90° in elevation, it measures the received power at each step.

Antenna Gain Measurement Backyard Setup
Antenna Gain Measurement Backyard Setup

RTLSDR-NEXT: A Ground-Up Rust Rewrite of the RTL-SDR Driver

Thank you to Matthew Delashaw, who has written in and shared a guest post with us. Matthew has rewritten the 2013 librtlsdr library from the ground up in Rust. His motivations for doing so and the results are explained in the post below:


I actually started down this path as an "interest". There was a Ham radio Technical Interest Group I was planning on attending a meeting. I had already wanted to convert my Raspberry Pi into a fallback radio receiver for potential internet outages and listening to storm chasers on SKYWARN. Now I have the "v4" dongle, and a full end-to-end SDR solution. !Spoilers, I'm releasing a native smart phone client soon.

The RTL2832U chipset has powered affordable software-defined radio for over a decade. The reference driver, librtlsdr, was written in C around 2013 and follows the same architectural pattern it always has: a blocking callback loop, manual buffer management, and a programming model that predates modern async runtimes by years.

rtlsdr-next is a ground-up Rust rewrite. It exposes SDR data as a native Tokio Stream, ships a zero-allocation DSP pipeline, and has first-class support for the RTL-SDR Blog V4 — a newer hardware variant the upstream driver handles correctly but never cleanly abstracted. The result is faster, safer, and substantially easier to build applications on top of.

1.49 GiB/s IQ conversion on Pi 5  ·  ~45ms frequency switching (was ~270ms with 20 I2C toggles)  ·  0 allocations in the streaming hot path


Why rewrite it at all?

The C driver works. Millions of people run it daily via OpenWebRX, GQRX, SDR++, and friends. But its architecture creates friction at every layer: the callback-based stream makes backpressure impossible to reason about, the I2C bus is hammered with redundant open/close cycles, and the conversion routine uses a 256-entry lookup table whose cache pressure eats into throughput on modern out-of-order cores.

More practically: trying to integrate librtlsdr into a modern async Rust application means spawning a dedicated thread, wrapping callbacks in channels, and handling all the lifetime gymnastics manually. For every project that does this, someone reinvents the same boilerplate. There are plenty of Rust "wrappers" out there That exemplifies this.


The stream architecture

The primary interface is a standard async stream. A SampleStream wraps a background USB reader thread that feeds raw IQ bytes into a tokio::mpsc channel. The F32Stream layer sits on top and handles conversion, decimation, DC removal, and AGC — all in a single pipeline with no intermediate heap allocations.

let mut stream = driver.stream_f32(8)   // ÷8 → 256 kSPS
    .with_dc_removal(0.01)
    .with_agc(1.0, 0.01, 0.01);

while let Some(Ok(iq)) = stream.next().await {
    // interleaved f32 I/Q, ready to demodulate
}

The blocking USB read thread never touches the async runtime. Sample delivery to async consumers happens entirely through the channel, and the PooledBuffer type ensures the backing buffers are returned to the pool via Drop — no explicit lifecycle management needed at the call site.

  • SampleStream — Blocking USB thread → tokio::mpsc channel. Pre-allocated buffer pool. Flush-on-tune via broadcast::Sender.
  • F32Stream — Convert → decimate (FIR) → DC remove → AGC. Processes split I/Q in-place. No per-block allocation.
  • PooledBuffer — Returns buffer to pool on Drop. try_send with blocking fallback thread — the pool never silently starves.
  • BoardOrchestratorV4Orchestrator / GenericOrchestrator produce a TuningPlan. Board logic never leaks into chip drivers.

The I2C repeater optimization

Every register write to the R828D tuner chip goes through an I2C bridge in the RTL2832U. The bridge must be explicitly opened and closed around each transaction. In a naive implementation — which is what the reference driver does — every call to set_frequency independently opens and closes the repeater for each register write.

A full frequency switch involves setting the PLL, MUX, filter coefficients, and various control registers. That adds up to roughly 20 open/close cycles, and each one costs ~13ms of USB round-trip time.

The fix: a single with_repeater(|| { ... }) closure that holds the bridge open for the entire mux + PLL sequence. One open, one close, all the work done in between.

// Before: ~20 repeater toggles ≈ 270ms
self.set_mux(hz)?;   // 10 writes, each with open/close
self.set_pll(hz)?;   // 10 writes, each with open/close

// After: 1 repeater toggle ≈ 45ms
self.with_repeater(|| {
    self.set_mux_raw(hz)?;
    self.set_pll_raw(hz)?;
    Ok(())
})?;

The distinction between write_reg_mask (opens and closes the repeater itself) and write_reg_mask_raw (no repeater toggle, must be inside a bracket) is enforced by convention throughout the codebase. Any raw variant called outside a bracket is a bug that surfaces immediately as a timeout rather than silently returning stale data.


Converter throughput

librtlsdr converts raw IQ bytes to float via a static 256-entry lookup table. It is a reasonable approach from an era when float math was expensive and cache was plentiful. On the Cortex-A76 inside the Pi 5, the situation is inverted: the NEON FPU is underutilized and random-access table reads create cache pressure that limits throughput.

The arithmetic equivalent — (x as f32 - 127.5) / 127.5 — is computed in two instructions per sample and is trivially auto-vectorized by LLVM. The compiler emits NEON FMLA instructions without any manual intrinsics.

Operation librtlsdr (C) rtlsdr-next (Rust)
Standard conversion (256KB) 172.32 µs · 1.42 GiB/s 164.35 µs · 1.49 GiB/s
V4 inverted conversion 256.07 µs · 976 MiB/s 170.81 µs · 1.43 GiB/s
FIR decimation ÷8 N/A 615 µs · 426 MSa/s

The V4 inversion case is a particularly notable optimization. librtlsdr implements it as a two-pass operation: first a full LUT conversion, then a second pass to negate every Q sample. The Rust implementation folds both into a single pass, processing I and Q pairs together and avoiding a complete re-read of the output buffer.


RTL-SDR Blog V4 specifics

The V4 is a substantial hardware revision. It ships with an R828D tuner (not R820T), adds an HF upconverter and a GPIO-switched triplexer, and has several initialization quirks that librtlsdr discovered through usbmon traces and EEPROM string detection.

The board logic is isolated entirely in V4Orchestrator. Given a target frequency, it returns a TuningPlan — the actual tuner frequency, whether spectral inversion is needed, which triplexer path to select, and whether the frequency falls inside a notch band. The R828D chip driver never touches a GPIO.

Notable quirks baked into the driver: the R828D responds at I2C address 0x74 rather than the R820T's 0x34; frequencies below 28.8 MHz are upconverted by adding the crystal frequency, and the resulting spectrum is inverted (Q = –Q). Every demodulator register write must be followed by a dummy read of page 0x0a register 0x01 — the hardware requires this as a flush sync, and omitting it causes subsequent control transfers to stall with a pipe error.


Built-in DSP pipeline

The dsp module ships a complete demodulation stack. The decimator uses a windowed-sinc FIR with NEON acceleration on aarch64, with a scalar fallback that LLVM auto-vectorizes on x86_64. The FM demodulator is a quadrature discriminator with configurable de-emphasis. AM uses a two-stage DC-subtraction envelope detector. SSB uses the phasing method with a 65-tap Hilbert transformer windowed with Blackman-Harris for high sideband rejection.

All demodulators maintain state across block boundaries — the history overlap buffer in the decimator ensures the FIR convolution is correct at every chunk edge, which is essential for continuous streaming.


Standalone servers

Two installable binaries ship alongside the library. rtl_tcp implements the standard RTL-TCP protocol and is compatible with OpenWebRX+, GQRX, and SDR++. websdr is a self-contained WebSocket SDR server with a full spectrum and waterfall UI embedded as a compiled-in HTML file — no separate web server needed. Both support TLS. The WebSDR binary accepts --cert and --key flags for wss:// connections, which are required by iOS App Transport Security when using a public domain.

  • OpenWebRX+ — confirmed working
  • GQRX — confirmed working
  • SDR++ — confirmed working
  • Corona SDR (iOS) — confirmed working

Getting started

cargo install rtlsdr-next

# Smoke test — run this first
RUST_LOG=info cargo run --release --example hw_probe

# Start an rtl_tcp server
rtl_tcp --address 0.0.0.0 --port 1234

# Start the WebSDR UI
websdr --address 0.0.0.0 --port 8080

On Linux, set up a udev rule for persistent USB access without sudo. On Windows, Zadig is required to swap the DVB-T driver to WinUSB — build works without it, but the USB runtime requires it at runtime.


Source on GitHub at github.com/mattdelashaw/rtlsdr-next. Licensed Apache 2.0. Benchmarks measured on Raspberry Pi 5 (aarch64) and AMD Ryzen 7600X (x86_64) with cargo build --release, no target-cpu=native.

Keep and eye out for the smart phone app release here: Spectral Bands

rtlsdr-next running with GQRX
rtlsdr-next running with GQRX

SPECTRAL-GSM: A Web-Based GSM Interception Platform Built on OsmocomBB

OsmocomBB is an open-source project that replaces the stock baseband firmware on old Motorola phones (C118, C139, etc.) that use the Texas Instruments Calypso chipset. By flashing custom "layer23" firmware over serial, these cheap legacy handsets become capable of accessing raw GSM radio data at the baseband level, enabling cell scanning, burst capture, and passive subscriber identity harvesting.

SPECTRAL-GSM builds on this by wrapping OsmocomBB into a full GSM intelligence suite controlled from a single browser tab. The system supports up to five phones simultaneously and provides a structured pipeline: scan local GSM cells, capture raw bursts on a target channel, crack the A5/1 encryption using rainbow tables on a 2 TB SSD, and then use the recovered session key for real-time voice and SMS decryption. Additional modules handle passive IMSI catching, targeted single-IMSI surveillance, silent SMS location probing via a USB modem, and OpenCellID cell tower mapping.

The developer notes that the platform is intended for authorized research, law enforcement, and educational use. At the moment, Mini0com has not provided a link or website to the software, only providing a PDF file, and video demonstrations of the system on their YouTube channel. Contact details for Mini0com can be found in the description on the YouTube videos below.

Spectral-GSM OsmocomBB

OTP Capture Demonstration Using Spectral-GSM OsmocomBB



 
 

Echo: KiwiSDR, OpenWebRX, WebSDR and FM-DX iOS Browser App now Officially Released

Back in February, we posted about the beta release of Echo, an iOS app designed for browsing global web-based KiwiSDR, OpenWebRX, WebSDR, and FM-DX software-defined radios. Mark, the developer of Echo, has now officially released the app on the Apple App Store for free.

Echo turns your iPhone and iPad into a global radio receiver. Browse 2,000+ KiwiSDR, OpenWebRX, WebSDR, and FM-DX servers to hear shortwave, aviation, numbers stations, and distant FM in real time.

More information can also be found on the new echosdr.com website.

Echo iOS KiwiSDR, OpenWebRX, WebSDR and FM-DX Browser App
Echo iOS KiwiSDR, OpenWebRX, WebSDR and FM-DX Browser App

TRNXSDR-Carrier: A Modular Baseboard for SDR Modules

Over on GitHub, user acruxcz has released the TRNXSDR-carrier, an open hardware baseboard platform designed to host and interconnect multiple SDR modules. The board is built around a Xilinx XC7Z015 FPGA with 1 GB of DDR3 RAM at 1066 MHz, and features four SMA RF connectors, Gigabit Ethernet, an SFP optical port (with GTX support planned for up to 5 Gbit throughput), and USB-C with Power Delivery. It is not a standalone SDR, instead it acts as a central hub that requires external SDR modules connected via its expansion slots.

The slot system is a defining feature. The baseboard provides two high-speed primary slots (one with JESD204B support via GTX), two lower-speed primary slots, and room for an expansion board adding a further six slots and ten module positions in total. Planned RF modules include Lime Microsystems LMS6002D and LMS7002M chips, as well as Analog Devices AD9361/AD9363/AD9364 transceivers. A custom GNU Radio OOT source block is already functional, and initial RX testing at 433 MHz shows a clean signal with minimal noise floor. SoapySDR driver support, which would bring compatibility with SDR++, SDRangel, and other tools, is planned.

The hardware design is at Rev 1.0 with a Rev 2.0 in progress to address known bugs. The project is actively under development. It is not yet known if the developers plan to sell hardware, or leave it as open-source plans. 

The TRNXSDR-Carrier Board
The TRNXSDR-Carrier Board

Adding ACARS Decoding to an ADS-B Flight Tracker

Over on his blog, cynicalGSD has written a detailed post about how he extended his home ADS-B flight tracking setup to also decode ACARS. His existing system runs an RTL-SDR dongle on a Raspberry Pi feeding a database and Flask web app. Adding ACARS required a second RTL-SDR and a separate VHF dipole antenna tuned for 129–131 MHz.

ACARS (Aircraft Communications Addressing and Reporting System) is a text-based datalink that has been in use since 1978, carrying short messages between aircraft and ground stations. It includes messages such as OOOI events (Out of gate, Off ground, On ground, Into gate), pilot weather reports, maintenance fault codes, and gate and fuel data. The key feature of their implementation is cross-referencing ACARS messages with existing ADS-B records via aircraft registration and ICAO hex address, enriching flight records with precise departure and arrival timestamps from the airline's own reporting system.

The full write-up covers the database schema, Python integration using acarsdec, gain tuning tips, and the Flask web interface. cynicalGSD mentions that the code is available for anyone interested, but we didn't see a link, so please comment on his post if you are interested.

Technical Summary of cynicalGSD's ACARS + ADS-B implementation.
Technical Summary of cynicalGSD's ACARS + ADS-B implementation.

Using the NISAR Satellite as an Illuminator for Passive Radar

Over on GitHub, Jean-Michel Friedt has uploaded new code, results, and findings from one of his latest experiments with passive radar. A simple passive radar system uses two coherent receive channels and two antennas. One antenna receives a clean reference signal from an illuminator of opportunity, such as an FM or TV transmitter, while the other surveillance antenna receives echoes from the area containing targets. By correlating the surveillance signal with the reference signal over different delays and Doppler shifts, the system produces a range-Doppler map showing potential targets.

The novel thing about Friedt's recent work is that the illuminator is a moving L/S-Band satellite in space. The illuminator used is the polar-orbiting NISAR, a NASA-ISRO satellite designed for synthetic aperture radar (SAR). SAR satellites create detailed images of Earth by sending radar pulses to the ground and combining the returning echoes collected as the satellite moves, effectively simulating a much larger antenna.

Part of the trouble with using NISAR as an illuminator is predicting when it will be illuminating your current location. Friedt's GitHub readme explains how the software does illumination prediction.

NISAR emits chirp signals at 20 MHz bandwidth in the L and S-band, so a wideband SDR is required to get the full resolution. In his setup, Friedt used an Ettus B210 or Enjoy Digital M2SDR SDR, with two active GNSS antennas. 

The results show that he was able to successfully receive reflections of the satellite signal from the ground, transform the range-doppler data into map coordinates, and overlay them on a map.

[Also seen on Hackaday]

Passive Radar via the NISAR Satellite
Passive Radar via the NISAR Satellite

 

PhaseLatch: Using a 1970’s Microprocessor Chip with a Modern 20 MSPS ADC

Back in September 2025 we posted about Anders Nielsen's PhaseLoom, an SDR based on the MOS Technology 6502 chip - the chip behind the early age of home computing, powering iconic systems like the Apple I & II, Commodore 64, Atari, and Nintendo Entertainment System.

Anders has now moved on and created PhaseLatch, which combines the 6502 with a modern ADC that can be memory-mapped directly onto the 6502's data bus. Although achieving the theoretical max ADC bandwidth of 20 MSPS is not possible with the underpowered 6502, Ander's notes that when combined with some external RAM he was still able to perform some DSP on the 6502 such as tone detection.

The entire project is open source on GitHub, and Anders sells pre-made boards for experimentation.

6502 SDR with 20MHz ADC!