{"id":47287882,"url":"https://github.com/mattdelashaw/rtlsdr-next","last_synced_at":"2026-04-02T00:41:35.967Z","repository":{"id":342739609,"uuid":"1174895452","full_name":"mattdelashaw/rtlsdr-next","owner":"mattdelashaw","description":"High-performance async Rust driver for RTL-SDR(RTL2832U) with NEON optimizations for the Pi 5.","archived":false,"fork":false,"pushed_at":"2026-03-14T03:27:57.000Z","size":974,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-14T13:49:12.238Z","etag":null,"topics":["e4000","neon-simd","r820d","r820t","raspberry-pi","rtl-sdr","rust","sdr","software-defined-radio","tokio"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mattdelashaw.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-07T00:48:33.000Z","updated_at":"2026-03-14T03:26:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mattdelashaw/rtlsdr-next","commit_stats":null,"previous_names":["mattdelashaw/rtlsdr-next"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mattdelashaw/rtlsdr-next","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattdelashaw%2Frtlsdr-next","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattdelashaw%2Frtlsdr-next/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattdelashaw%2Frtlsdr-next/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattdelashaw%2Frtlsdr-next/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mattdelashaw","download_url":"https://codeload.github.com/mattdelashaw/rtlsdr-next/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mattdelashaw%2Frtlsdr-next/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30570633,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-16T06:02:37.763Z","status":"ssl_error","status_checked_at":"2026-03-16T06:02:14.913Z","response_time":96,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["e4000","neon-simd","r820d","r820t","raspberry-pi","rtl-sdr","rust","sdr","software-defined-radio","tokio"],"created_at":"2026-03-16T06:22:12.068Z","updated_at":"2026-04-02T00:41:35.960Z","avatar_url":"https://github.com/mattdelashaw.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# rtlsdr-next 📡\n\n![Descriptive alt text for the image](assets/images/banner.webp)\n\n[![Rust CI](https://github.com/mattdelashaw/rtlsdr-next/actions/workflows/rust.yml/badge.svg)](https://github.com/mattdelashaw/rtlsdr-next/actions/workflows/rust.yml)\n\nA high-performance, asynchronous, and safety-first Rust driver for RTL2832U-based Software Defined Radios (SDR).\n\nDesigned for the modern era (2026+), this driver moves away from the legacy C callback model toward a **Tokio-native Stream** architecture, with specific optimizations for high-bandwidth ARM hosts like the **Raspberry Pi 5**.\n\n\u003e [!CAUTION]\n\u003e This a shameless \"vibe-code\" project. I wanted to dig into Rust and this seemed like something fun to explore when I realized the gold version C code drivers were circa 2013. Since starting, I realized there is a Rust implementation that has a more human touch here: https://github.com/ccostes/rtl-sdr-rs\n\n## ✅ Confirmed Working Hardware\n\n| Dongle | Tuner | Host | Clients Tested |\n|--------|-------|------|----------------|\n| RTL-SDR Blog V4 | R828D | Raspberry Pi 5 (Bookworm) | OpenWebRX+, GQRX, SDR++ |\n| RTL-SDR Blog V4 | R828D | Windows 11 x86_64 (AMD Ryzen 7600X) | Corona SDR (iOS) |\n| RTL-SDR Blog V4 | R828D | Raspberry Pi 5 (Bookworm) | SpectralBands (iOS, WebSDR protocol) |\n\nOther RTL2832U dongles with R820T/R820T2/E4000 tuners should work — hardware verification welcome.\n\n## 🚀 Key Features\n\n- **Async-First Architecture:** Built on Tokio. SDR data is a standard `Stream` with backpressure and graceful shutdown.\n- **Full RTL-SDR Blog V4 Support:** Correct R828D initialization sequence reverse-engineered from usbmon traces against librtlsdr.\n- **Zero-Allocation Pipeline:** Custom buffer pooling eliminates memory allocations in the hot path.\n- **Auto-Vectorized DSP:** LLVM auto-vectorizes to NEON on aarch64 and AVX-512 on Zen 4 x86_64 — no manual intrinsics needed.\n- **Automatic Tuner Probing:** I2C presence detection for Rafael Micro (R820T/R828D), Elonics (E4000), and Fitipower (FC0012/13).\n- **Zero-Copy Broadcasting:** Share a single hardware device across multiple local apps via `Arc`-based broadcasting.\n- **Precision Frequency Correction:** Integrated PPM correction for both tuner PLL and RTL2832U resampler.\n- **Standalone Tools:** Optimized `rtl_tcp` and `websdr` binaries for professional distribution.\n\n## 🛠 Hardware: The V4 Deep Dive\n\nThe RTL-SDR Blog V4 required several initialization steps discovered during reverse engineering via usbmon traces:\n\n1. **GPIO Reset Pulse (non-V4 only):** Standard RTL-SDR dongles receive a GPIO 4 reset pulse during init. The V4 skips this entirely — librtlsdr detects the V4 by EEPROM string match and branches before the GPIO code. This driver mirrors that behavior exactly.\n\n2. **R828D I2C Address:** The R828D responds at `0x74`, not `0x34` (which is the R820T address). Probing must try both.\n\n3. **Low-IF Mode:** The R828D uses low-IF (not Zero-IF). After tuner detection, Zero-IF mode must be explicitly disabled (`page1 reg 0xb1 = 0x1a`) and the In-phase ADC input enabled (`page0 reg 0x08 = 0x4d`).\n\n4. **I2C Chunk Size:** The RTL2832U I2C bridge has a maximum transfer size of 8 bytes (7 data + 1 register byte). The 27-byte tuner init array must be written in chunks or the USB endpoint stalls with a pipe error.\n\n5. **Demod Register Sync:** Every demodulator register write must be followed by a dummy read of `page0 reg 0x01` — this is a hardware flush/sync requirement. Omitting it causes subsequent control transfers to stall.\n\n6. **EEPROM Recovery:** If the dongle's EEPROM is corrupted (reverts to generic strings), restore it with the RTL-SDR Blog fork of rtl_eeprom:\n   ```bash\n   ~/rtl-sdr-blog/build/src/rtl_eeprom -m \"RTLSDRBlog\" -p \"Blog V4\" -s \"00000001\"\n   ```\n\n## 🚀 Performance\n\n| Operation | Pi 5 aarch64 | x86_64 (Ryzen 7600X) |\n|-----------|-------------|----------------------|\n| `u8` → `f32` converter | 1.49 GiB/s | 12.67 GiB/s (AVX-512) |\n| FIR decimator ÷8 | 426 MSa/s | 590 MSa/s |\n| Full pipeline ÷8 | 328 MiB/s | — |\n\nBoth results use `cargo build --release` without `target-cpu=native`. On x86_64, LLVM auto-vectorizes the converter to AVX-512 on supported CPUs. Setting `target-cpu=native` on Zen 4 can cause AVX-512 frequency throttling that hurts the decimator — the default build is better balanced.\n\nPi 5 is memory-bandwidth-bound on the converter. x86_64 desktop has enough DDR5 bandwidth that the ALU keeps up.\n\n## 🎛 Performance Tuning \u0026 Latency\n\n### Reducing Frequency Switching Lag\n\nThis driver implements a **\"Flush-on-Tune\"** mechanism. When you change frequency (e.g., click a bookmark), all stale data currently in the driver's buffers is immediately dropped. This makes tuning feel much snappier than standard `librtlsdr`.\n\nHowever, you may still experience some lag in applications like **Gqrx**. This is because Gqrx maintains its own large internal audio and DSP buffers which we cannot flush.\n\n**For Gqrx Users:**\n- Set **Bandwidth** to `0` (Auto) to avoid unnecessary I2C filter commands.\n- Reduce **Audio Buffer** size in Gqrx settings if audio lags behind the waterfall.\n- If you need ultra-low latency, you can programmatically reduce the driver's buffer count via `StreamConfig`.\n\n**For OpenWebRX Users:**\n- OpenWebRX generally feels snappier because it manages its own pipeline more aggressively and benefits directly from our driver's flush mechanism.\n\n### Advanced Configuration (`StreamConfig`)\n\nYou can tune the trade-off between latency and stability by modifying the `StreamConfig` struct when creating a stream:\n\n```rust\n// Adjust configuration on the driver instance\ndriver.stream_config = rtlsdr::StreamConfig {\n    num_buffers: 8,          // Default: 16. Lower = less latency, higher risk of drops.\n    buffer_size: 256 * 1024, // Default: 256KB.\n};\n\n// Create the stream with the new settings\nlet stream = driver.stream();\n```\n\n## 📋 Prerequisites\n\n- Rust toolchain (stable)\n- libusb development headers\n- USB access (see platform setup below)\n\n```bash\n# Ubuntu/Debian\nsudo apt-get install libusb-1.0-0-dev\n\n# macOS\nbrew install libusb\n\n# Windows — no libusb headers needed, but see USB driver setup below\n```\n\n## 🔌 USB Permissions\n\nThe recommended approach is a persistent udev rule rather than `chmod`:\n\n```bash\n# Create udev rule\necho 'SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"0bda\", ATTRS{idProduct}==\"2838\", MODE=\"0666\", GROUP=\"plugdev\"' \\\n  | sudo tee /etc/udev/rules.d/99-rtlsdr.rules\n\n# Reload and trigger\nsudo udevadm control --reload-rules\nsudo udevadm trigger\n\n# Add yourself to plugdev if needed\nsudo usermod -aG plugdev $USER\n```\n\nFor quick testing only: `sudo chmod 666 /dev/bus/usb/$(lsusb | grep RTL | awk '{print $2\"/\"$4}' | tr -d ':')`\n\n### Windows\n\nWindows requires a one-time driver swap via [Zadig](https://zadig.akeo.ie/) — the dongle enumerates as a DVB-T TV tuner by default and must be switched to WinUSB before libusb can claim it:\n\n1. Download and open Zadig\n2. Options → List All Devices\n3. Select the RTL-SDR entry (look for \"Bulk-In, Interface 0\")\n4. Select **WinUSB** in the driver dropdown\n5. Click \"Replace Driver\"\n\nThis survives reboots but may need to be repeated if you plug into a different USB port. The Unix socket sharing server (`SharingServer`) is not available on Windows — use the rtl_tcp server for local sharing instead.\n\n### Environment Variables (Windows)\n\nCMD: `set RUST_LOG=info` then run the command separately.\nPowerShell: `$env:RUST_LOG = \"info\"; cargo run --release --example rtl_tcp`\n\n## 🏗 Building \u0026 Installation\n\n```bash\ngit clone https://github.com/mattdelashaw/rtlsdr-next\ncd rtlsdr-next\n# Install the library and binaries globally\ncargo install --path .\n```\n\nFor maximum Pi 5 performance, set the target CPU explicitly:\n\n```bash\nRUSTFLAGS=\"-C target-cpu=native\" cargo build --release\n```\n\nOn x86_64, `target-cpu=native` is not recommended — LLVM already auto-vectorizes well, and on Zen 4 CPUs AVX-512 activation causes frequency throttling that hurts sustained DSP throughput.\n\n## ▶️ Usage\n\n### Standalone Tools\n\nOnce installed, you can run the primary tools directly from your terminal:\n\n| Command | Description |\n|---------|-------------|\n| `rtl_tcp` | Standard RTL-SDR TCP server. Compatible with OpenWebRX+, GQRX, SDR++. Supports `-a/--address` and `-p/--port`. |\n| `websdr` | WebSocket SDR server. Streams decoded audio (48kHz PCM) and waterfall data. Supports `-a/--address`, `-p/--port`, and **TLS/SSL** (`wss://`). |\n\n```bash\n# Start rtl_tcp server on all interfaces, port 1234\nrtl_tcp --address 0.0.0.0 --port 1234\n\n# Start WebSDR server (standard ws://)\nwebsdr --address 0.0.0.0 --port 8080\n\n# Start WebSDR server with TLS (wss://)\nwebsdr --address 0.0.0.0 --port 8080 --cert cert.pem --key key.pem\n```\n\n#### 🔒 Secure WebSDR (wss://)\n\nTo support secure connections (required by many modern browsers and iOS App Transport Security when using public domains), you can provide a PEM-encoded certificate and private key.\n\n**Using Let's Encrypt:**\nIf you have a domain pointing to your host, you can use `certbot` to generate a certificate and point `websdr` to the generated files:\n```bash\nwebsdr --port 8080 --cert /etc/letsencrypt/live/yourdomain.com/fullchain.pem --key /etc/letsencrypt/live/yourdomain.com/privkey.pem\n```\n\n**Using Self-Signed (for testing):**\n```bash\nopenssl req -x509 -newkey rsa:4048 -keyout key.pem -out cert.pem -days 365 -nodes\n```\n*Note: iOS clients will require you to manually trust the root certificate if using self-signed.*\n\n### Development Examples\n\nThese demonstrate library usage and provide quick functional tests. Run them with `cargo run --example`.\n\n| Example | Description |\n|---------|-------------|\n| `hw_probe` | **Start here.** Full driver smoke test — init, tune, stream 1s, report throughput. Clear PASS/FAIL output. |\n| `fm_radio` | FM receiver with built-in demodulator. Outputs audio via the system audio device. |\n| `monitor` | Continuous stream monitor — logs average signal magnitude and throughput every 10 blocks. |\n\n```bash\n# Smoke test — run this first\nRUST_LOG=info cargo run --release --example hw_probe\n\n# FM radio\nRUST_LOG=info cargo run --release --example fm_radio -- --freq 97.1e6\n```\n\n### Diagnostic Tools\n\nThese are raw USB tools used during driver development. They bypass the driver\nentirely and speak directly to the hardware via libusb. Run them when you need\nto determine whether a problem is in the driver or the hardware.\n\n| Example | Description |\n|---------|-------------|\n| `diag_write` | Scans all 8 USB control blocks for register responses. Use when debugging Pipe errors. |\n| `diag_i2c` | Probes demod read/write encoding patterns and validates the dummy-read flush requirement. |\n| `diag_demod` | Re-acquisition pulse — tries up to 5 times to reclaim a busy/crashed USB interface. |\n| `diag_sys` | Dumps registers from USB/SYS/DEMOD blocks using both encoding patterns. |\n| `diag_raw_clone` | Replays the exact V4 init sequence raw. The definitive \"hardware vs driver\" test. |\n\n```bash\n# Is it the hardware or the driver?\nRUST_LOG=debug cargo run --release --example diag_raw_clone\n\n# Device stuck as busy after a crash?\ncargo run --release --example diag_demod\n\n# Pipe errors on demod writes?\ncargo run --release --example diag_i2c\n```\n\n\n\n## 🧪 Testing\n\n```bash\n# Unit tests (no hardware required)\ncargo test\n\n# Release mode (also runs NEON/scalar agreement tests on aarch64)\ncargo test --release\n```\n\nHardware-in-the-loop tests require a connected dongle and are run manually via the examples.\n\n## ⚙️ Environment Variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `RUST_LOG` | `warn` | Log level: `error`, `warn`, `info`, `debug`, `trace` |\n| `RTLSDR_DEVICE_INDEX` | `0` | USB device index if multiple dongles are connected |\n\n-### Baseline Comparisons\n-Measurements taken on an ARM64 host comparing the `rtlsdr-next` Rust implementation against the `librtlsdr` (v4 branch) C baseline using 256KB blocks:\n\n| Benchmark Task | librtlsdr (C) | rtlsdr-next (Rust) |\n| :--- | :--- | :--- |\n| **Standard Converter** (256KB) | **172.32 µs** | **164.35 µs** |\n| *Throughput* | 1.4168 GiB/s | 1.4855 GiB/s |\n| *Range [min ... max]* | [172.29 µs ... 172.36 µs] | [164.13 µs ... 164.78 µs] |\n| | | |\n| **V4 Inverted Converter** | **256.07 µs** | **170.81 µs** |\n| *Throughput* | 976.30 MiB/s | 1.4293 GiB/s |\n| *Range [min ... max]* | [256.02 µs ... 256.14 µs] | [170.78 µs ... 170.84 µs] |\n| | | |\n| **Decimation (FIR/4)** | **N/A** | **778.40 µs** |\n| *Throughput* | N/A | 336.77 Melem/s |\n| | | |\n| **Decimation (FIR/8)** | **N/A** | **615.37 µs** |\n| *Throughput* | N/A | 425.99 Melem/s |\n| | | |\n| **Decimation (FIR/16)** | **N/A** | **534.04 µs** |\n| *Throughput* | N/A | 490.87 Melem/s |\n| | | |\n| **Full Pipeline (FIR/4)** | **N/A** | **925.26 µs** |\n| *Throughput* | N/A | 270.19 MiB/s |\n| | | |\n| **Full Pipeline (FIR/8)** | **N/A** | **761.17 µs** |\n| *Throughput* | N/A | 328.44 MiB/s |\n| | | |\n| **Full Pipeline (FIR/16)** | **N/A** | **678.84 µs** |\n| *Throughput* | N/A | 368.27 MiB/s |\n\n\n-*Note: The performance gain in conversion is primarily due to moving from cache-latency-bound lookup tables to instruction-parallel arithmetic, which better utilizes modern out-of-order CPU pipelines.*\n\n## 🗺 Roadmap - Phaseshifting\n\n- [x] **Phase 1: Hardware Bridge** — USB vendor requests, I2C bridge, control transfer encoding\n- [x] **Phase 2: R828D / V4 Support** — Full tuner initialization, PLL, gain tables\n- [x] **Phase 3: Async Stream** — Tokio-native stream with backpressure and graceful shutdown\n- [x] **Phase 4: DSP Pipeline** — NEON SIMD FIR decimation, FM demodulator, AGC, DC removal\n- [x] **Phase 5: Device Sharing** — Zero-copy Arc broadcasting, Unix socket server\n- [x] **Phase 6: Auto Probing** — I2C handshake-based tuner detection\n- [x] **Phase 7: Zero-Allocation** — Buffer pooling, in-place processing\n- [x] **Phase 8: rtl_tcp Server** — Compatible with OpenWebRX+, GQRX, SDR#\n- [x] **Phase 9: Elonics E4000** — Full Zero-IF driver with manual gain control (theoretically)\n- [x] **Phase 10: Fitipower Tuners** — FC0012/FC0013 register maps\n- [x] **Phase 11: Cross-Platform** — Windows confirmed working via Zadig/WinUSB; x86_64 LLVM auto-vectorization benchmarked, no manual SIMD needed\n- [ ] **Phase 12: Configurables** — Runtime buffer sizes, gain modes, bias-T persistence\n\n## 📝 Reference Material\n\n### Tuner Datasheets and Drivers\n\n**Another Rust Implementation**\n- [RTL-SDR-RS](https://github.com/ccostes/rtl-sdr-rs) - probably human community driven project\n\n**Rafael Micro R820T / R820T2 / R828D**\n- [R820T Datasheet (leaked draft)](https://github.com/josebury/R820T-Datasheet/blob/master/R820T_Datasheet-Non_Disclosure_Working_Draft.pdf) — gold standard for register maps\n- [RTL-SDR Blog V4 Guide](https://www.rtl-sdr.com/V4/) — V4-specific hardware details\n- [tuner_r82xx.c](https://github.com/rtlsdrblog/rtl-sdr-blog/blob/master/src/tuner_r82xx.c) — reference C implementation\n\n**Elonics E4000** (legacy, up to 2.2 GHz, gap ~1.1 GHz)\n- [Osmocom E4000 Wiki](https://osmocom.org/projects/rtl-sdr/wiki/E4000)\n- [tuner_e4k.c](https://github.com/osmocom/rtl-sdr/blob/master/src/tuner_e4k.c)\n\n**Fitipower FC0012 / FC0013** (budget nano dongles)\n- [tuner_fc0012.c](https://github.com/osmocom/rtl-sdr/blob/master/src/tuner_fc0012.c)\n\n### RTL2832U\n\n- [Osmocom RTL-SDR Wiki](https://osmocom.org/projects/rtl-sdr/wiki/Rtl-sdr) — the definitive reference\n- [librtlsdr.c](https://github.com/rtlsdrblog/rtl-sdr-blog/blob/master/src/librtlsdr.c) — comments are essentially the RTL2832U register manual\n\n## 📜 License\n\nLicensed under the Apache License, Version 2.0.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmattdelashaw%2Frtlsdr-next","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmattdelashaw%2Frtlsdr-next","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmattdelashaw%2Frtlsdr-next/lists"}