An SDR-based RF baseline and anomaly detector. riotduck learns what's normal on your spectrum — and tells you the moment something appears or disappears.
A new transmitter pops up where the band was quiet — a rogue beacon, a covert implant, an FPV drone, a wireless camera that wasn't there last week. riotduck flags it within sweeps.
The other half no one watches for. An emitter you've been tracking goes silent. A persistent signal loses power. A covert transmitter you placed gets removed. You hear about it.
On appearance, riotduck captures I/Q and runs it through rtl_433 (~250 protocols) → URH (file-mode demod on rtl_433 miss) → unknown-signal analyzer (CW/OOK/FSK/FM/BPSK/QPSK + symbol rate) → your curated library.
A synthetic SDR backend ships with the project — real complex64 I/Q, real detections, no hardware. The full scanner → baseline → dedup → capture → fingerprint pipeline runs end-to-end.
git clone https://github.com/haxorthematrix/riotduck.git
cd riotduck
pip install -e .
riotduck scan --fake 1 --config config/default.yaml
A real 433.92 MHz transmitter that rtl_433 can't decode. URH recovers the bits; the analyzer characterizes the signal independently. Add a library entry and the next burst publishes Identification(source="library") with your name on it. Repeat detections share an event id — the dedup tracker working, including across two SDRs sharing a range.
Async agents on an in-process pub/sub bus. Each stage is its own module, unit-tested without hardware. 253 tests, ~7 s.
median / MAD baseline; global P10 noise floor robust to strong emitters in-band.n_up / n_down hysteresis; coalescing; bin-cluster shadow suppression collapses sidelobes of a strong emitter..meta.json per file.rtl_433 in file mode; on a miss it routes the capture through URH (via URH's bundled Python) for second-stage demod.library_candidates.yaml after N physical presses regardless of which SDRs saw them.riotduck replay --plot for offline detection tuning with PSD heatmaps.EventTracker across all ScannerAgents → two SDRs sharing a range fold matching detections into one event.SoapySDR-first with a pyrtlsdr fallback. Two RTL-SDRs sweeping in parallel works (libusb darwin threading handled). The synthetic backend is built in for hardware-free testing. See the README for the BOM and wiring diagrams.
hackrf_sweep fast path for multi-GHz rangesevents