sdrtop
A bench instrument for software defined radios, living in your terminal.
Spectrum, waterfall, and the measurements a plot cannot give you.
This project is supported by
Airspy · SDRplay
Via SoapySDR
Grey means written from the API, not from owning one. It should work. Nobody has told me either way yet, which is exactly as reassuring as it sounds. The honest version.
Install · Keys · Config · Roadmap · Hardware · Changelog · Full guide
Hey there! This is my take on a terminal monitor for SDR hardware. I wanted something that could hunt down every bit of diagnostic data from your radio and stream it straight to your terminal. Not a lazy *-info clone: raw, real-time metrics with a spectrum, a waterfall and a set of bench instruments, in a tmux pane, an SSH session, or the postage-stamp screen of a cyberdeck. I set out to print a few numbers. There is now a Friis noise figure model in here. I'm not entirely sure how that happened.
It's a hobby project built in my spare time, and honestly, I made it for you ❤️. Use it however you like, beat on it, and don't be shy: open issues, dig through the code, and if you've got a good idea, send it my way as a pull request or just a message. This is an open table, not my private garage.
demo.mp4
It's a terminal app, so brace yourself for the visual spectacle of monospace text in color. The only special effects are honest dBFS numbers.
Important
Early development. New features keep coming, and each one goes the same way: built, measured against the specification, then polished until its screen reads at a glance. Between features the work is sharper radio math and bug fixing.
Known issues: plenty 😄 If something looks broken, it's either a bug or an undocumented feature. Flip a coin, then open an issue.
Gallery
Piconet follow · classic Bluetooth
One piconet, found by the access code at the start of every packet and read packet by packet. How it works
piconet_follow.mp4
RDS demodulation · FM broadcast
Station name, programme type and RadioText, read off the 57 kHz subcarrier. No audio, on purpose. How it works
lab_rds_demod.mp4
The clips are snapshots of the day I recorded them; the screens keep moving.
📻 More screenshots: lab benches, RTL-SDR, the Command Rail cards
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Got a clean capture on your hardware? Drop it in user_docs/pics/ and send a PR. Shots from radios I don't own are the most useful ones there are.
What it does
Everything your radio knows about itself, in real time, without leaving the terminal. It's more than I planned. Every one of these started as "that'd be a nice little readout".
- The Command Rail, the default cockpit: a big frequency hero, an analog S-meter, mode cards that follow what you're doing, and SNR, power, noise figure and saturation each on its own live trace. All dials, no autopilot. It's a radio, not a self-driving car.
- Spectrum and waterfall with peak hold, noise floor tracking, zoom, a band-plan overlay and markers.
- Four lab benches for the questions a plot can't answer: is the quadrature clean, is the front end staged right, is my computer keeping up, and what is that signal. The details.
- A demodulator that doesn't play audio, deliberately: FM deviation, the MPX baseband and its pilot, stereo, CTCSS, AM depth and RDS. It reads radios, it doesn't play them. How it works.
- IQ correction, not just measurement: take the DC spike and the mirror image out of the stream and watch them actually go away.
- A band sweep wider than one window, stitched into one curve;
Enteron a peak tunes straight to it. - NET: the 2.4 GHz band, measured. Bluetooth (BLE, LE Coded and classic) and Wi-Fi, decoded and then treated like any other transmitter on the bench, each number with its uncertainty and against the specification's limits. Connections are followed from the sidelines: sdrtop never transmits and never joins anything. (First thing it taught me: the biggest airtime hog on my channel isn't Wi-Fi 6, it's a 1 Mb/s 802.11b beacon that should have retired around the time flip phones did.) The details.
- Recording:
Ctrl+Rwrites the raw IQ stream as SigMF, every lost sample marked where it happened. The details. - Small screens and shared radios: micro views that strip each concern down to one big number, and an observer mode that waits politely when another app holds the radio.
- Focus modes, themes and layouts: press a panel's highlighted letter and it's yours; six themes; presets you can rearrange or write yourself. Nothing needs a mouse.
Measured the awkward way rather than the easy way. Bandwidth about the carrier, not across whatever span you happened to capture, so a mistuned radio confesses instead of faking a good number. The noise floor as a density, so the same receiver reads as the same receiver whatever the sample rate. And every lab panel marks itself [STALE] the moment RX stops, so a frozen number is never mistaken for a live one.
Because the only thing worse than no data is confidently wrong data.
Documentation
→ Full user guide. This page is the short version; everything below is covered properly over there.
| Getting started: install & run | Keyboard shortcuts: every key | What's on screen: panels explained |
| The Lab presets: the bench-engineer views | Configuration: config.toml, every field | Advanced features: workflows & limits |
| Tips & tricks: gain, markers, workflows | Troubleshooting: when things go sideways | Supported hardware: what works today |
| Themes: the six palettes | What's new: the checkpoint log | The demodulator: how it was built |
| The NET section: Bluetooth and Wi-Fi, measured | Layout presets: the built-ins and your own | Recording: the IQ stream as SigMF |
| Changelog: version by version |
Install
Requirements: Linux · a HackRF One, an RTL-SDR, a tinySA, or anything SoapySDR speaks to
The one-liner
curl -fsSL https://raw.githubusercontent.com/musithang/sdrtop/main/packaging/install.sh | shThe installer tries the prebuilt binary on your machine. It compiles from source if the binary cannot run there. Native SDR libraries are optional runtime dependencies.
Add --hackrf or --rtlsdr to install the runtime for your radio. --soapy adds SoapySDR and its driver modules. A plain install adds none of these libraries.
Every flag it takes, and installing without root
sh install.sh --prefix ~/.local # install under a directory, no root anywhere sh install.sh --version v0.6.8 # a specific release instead of the latest sh install.sh --from-source # skip the prebuilt binary, always compile sh install.sh --git # compile the main branch, live dangerously sh install.sh --no-verify # skip the checksum check (say why first) sh install.sh --hackrf # add the HackRF runtime sh install.sh --rtlsdr # add the RTL-SDR runtime sh install.sh --soapy # add SoapySDR and its driver modules sh install.sh --deps-only --hackrf # only the selected runtime, no sdrtop sh install.sh --uninstall # remove what a previous run installed sh install.sh --help # this list, from the script itself
Piped straight into a shell they go after sh -s --. --no-verify turns off the checksum check on a download, which is the one thing standing between you and a tarball that isn't the one I published, so have a reason. The rest are explained in Getting started.
--deps-only requires a runtime flag. It never installs build tools.
Piping a script into sh means running code you haven't read. You should read it: packaging/install.sh. I'd want to.
Or cargo, the boring one that always works
sdrtop is on crates.io. Cargo compiles it for your machine.
sudo apt install build-essential # Debian / Ubuntu / Mint sudo pacman -S base-devel # Arch / Manjaro sudo dnf install gcc # Fedora cargo install sdrtop --locked
Building needs Rust 1.88+ and a C compiler/linker. Install Rust with rustup. No SDR libraries, development headers or pkg-config are required. At runtime, HackRF needs libhackrf 2023.01.1+ and RTL-SDR needs librtlsdr. A missing or incompatible library disables only its own backend, so install the runtimes for the radios you have (a tinySA needs neither).
Then go make coffee: a few minutes on a laptop, considerably more on a Raspberry Pi. It's not frozen, it's just Rust.
For SoapySDR devices
Beta, see Hardware. Neither of these is needed to build or run sdrtop; it looks for them at startup and shrugs if they aren't there.
sudo apt install libsoapysdr0.8 soapysdr-tools soapysdr-module-all
SoapySDRUtil --find # if this can't see your radio, sdrtop can't eitherThat last command is the whole diagnostic. If your radio isn't in that list, the missing piece is a driver module, and no amount of shouting at sdrtop will conjure one up.
Getting started covers the release tarball, checksums and provenance attestation. The tarball needs x86_64 Linux with glibc 2.36+. Runtime loading supports both Debian's librtlsdr.so.0 and Ubuntu's librtlsdr.so.2.
First run: sdrtop opens on its menu. Enter takes a layout, Space starts receiving, Esc brings the menu back, q quits and saves.
Keys
Layouts are grouped into sections and each section has its own numbers: Command Rail for the general views, Lab for the benches, Sweep for the band scan, Micro for the field views, and on a radio that reaches 2.4 GHz, Bluetooth (Band, LE, Classic, LE Coded) and Wi-Fi (its Channel views, on a radio that samples 20 Msps). So 2 is the RF bench inside Lab and the spectrum inside Command Rail. Esc opens the menu, which shows you the sections, the layouts in the one you're on, and the number that opens each. Nine keys per section, rather than one long row to memorise.
The ones that get you everywhere:
| Key | Action |
|---|---|
Esc |
Open the menu, or leave a focused panel |
Enter |
Take the highlighted layout |
Space |
Start / stop RX |
1–9 |
The nth layout of the section you're in |
↑ / ↓ |
Primary gain |
f / s |
Type a frequency / a sample rate |
c / e / l |
Focus the Command Rail / spectrum / waterfall |
Ctrl+R |
Record the IQ stream, or stop |
q |
Quit and save |
Capitals work everywhere: C and c do the same thing, so you never have to think about whether Shift is down. The gain keys relabel themselves per device, and a control your radio doesn't have simply isn't offered rather than sitting there doing nothing.
Forgotten one mid-session? Esc, then Tab to Keys. That reference is generated from the same table the app dispatches on, so it cannot go stale. Full list including every focus mode: Keyboard shortcuts.
Config
Saved to ~/.config/sdrtop/config.toml when you quit. Hand-editing is safe: a missing or broken file just falls back to defaults. Go ahead, mangle it; the parser has seen worse.
Frequency, gains, sample rate, markers, the sweep band, your theme and your layout all live there, plus any layouts of your own. Give a custom preset a section and a slot and it gets a place in the menu and a number key. Annotated example and every field: Configuration and Layout presets.
Themes: sdr (default) · nord · dracula · gruvbox · catppuccin · solarized, with per-field overrides. See Themes.
Roadmap
Features, then polish. New features keep landing, and when one is done its next step is always the same: polish, and a UI that reads at a glance. Here's where things stand. ✨
✅ Done
Native support for the HackRF One, the RTL-SDR, the PortaPack H4M and the tinySA family (that last one thanks to @AlCalzone). Spectrum, waterfall and the four lab benches. The FM demodulator with RDS. IQ correction, band sweeps, observer mode and SigMF recording. Bluetooth decoded and measured against the Core Specification, and the first Wi-Fi views. Release by release, it's all in the changelog.
🔧 In progress
- Wi-Fi: the channel survey and the Frames view are in. Next up is who and what is actually spending a channel's airtime
- SoapySDR feature parity: every panel and every measurement working through a SoapySDR device the way it does on a HackRF. Most of it is there; what's left needs datasheets nobody publishes
🔜 Next
- Native backends for the radios now sitting on my desk: the SDRplay RSP1B, the Airspy R2 and the Airspy HF+ Discovery
- Digital signals done properly, the way Bluetooth is now: LoRa, DMR, GSM, ADS-B, AIS. Each gets its own screens. Still no audio, still just data
- Editing the config from inside the app. Until then:
$EDITORand a little courage
Supported hardware
| Device | Status | Notes |
|---|---|---|
| HackRF One | ✅ Full support | All diagnostics, gain stages, ADC metrics |
| PortaPack H4M (Mayhem) | ✅ Full support | HackRF mode: all HackRF diagnostics apply |
| RTL-SDR (R820T, E4000, R828D) | ✅ Full support | Single tuner gain + AGC; no VGA, no BB filter, no Friis NF |
| tinySA / Ultra / Ultra+ | ✅ Spectrum support | ZS405 verified; calibrated dBm spectrum, waterfall and native band sweeps |
| Anything with a SoapySDR driver | 🧪 Beta | Airspy, SDRplay, Pluto, Lime, bladeRF, USRP, SoapyRemote. Unconfirmed on anything but a HackRF |
| SDRplay RSP1B | 🔲 Planned | On my desk; native backend in the queue |
| Airspy R2 | 🔲 Planned | On my desk; native backend in the queue |
| Airspy HF+ Discovery | 🔲 Planned | On my desk; native backend in the queue |
| HackRF Pro | 🔲 Planned | Needs hardware |
Hardware from the people who make it: the RSP1B came from SDRplay, the R2 and the HF+ Discovery from Airspy. Thank you both.
Native support is added only after physical testing on real devices. No guessing from datasheets. Translation: that list moves at exactly the speed of my hobby budget.
SoapySDR is the deliberate exception, because "I don't own one" was a bad answer to give every week, and buying one of everything is not a plan, it's a fantasy with a shipping address. Nothing about your radio is hardcoded: sdrtop asks the driver for the frequency range, the gain range, whether there's an AGC, the sample format and how many of its bits mean anything. What it can't ask, it refuses rather than invents. Verified against a HackRF through SoapyHackRF: the loader, enumeration, opening, capabilities and streaming. Unverified: literally every other radio. The honest version has the full reckoning, including the gotchas I already hit.
It took me a while to stop wincing at that. Before this, every row above was green and "tested on real hardware" applied to everything I shipped, which is another way of saying it separated nothing. The grey badges are what make the green ones mean something, and a rule nobody has ever had a reason to test is not a rule, it's a habit. This one now has one marked edge on it, on purpose.
📡 (pst… you scrolled this far, might as well)
.
/=\
( |#| )
(( |#| ))
((( |#| )))
(( |#| ))
( |#| )
|#|
/|#|\
/ |#| \
/ |#| \
/___|#|___\
/ |#| \
'''''''''''''''
Yep, it's a radio tower. I'm a simple man, I see free time, I make ASCII art.
73 de sdrtop: ham-speak for "catch you later." 📻
Credits · Contributing · License: GPL-3.0-or-later






