Frequency Recorder GUI - ruchirguitar/RTLSDR-Airband GitHub Wiki
Frequency Recorder GUI
A local web app for running RTLSDR-Airband as a frequency recorder: add any AM/NFM frequency as a card instead of editing libconfig, listen to it live in your browser, browse and manage recorded clips, and control the engine process — all from http://127.0.0.1:5050. Works with any frequency your receiver's bandwidth covers (amateur radio, aviation, marine, simplex, ...) — see "Responsible & legal use" below.
The engine itself (src/) is unmodified. This is a layer on top: it reads and writes the same .conf files RTLSDR-Airband has always used, and starts/stops the same rtl_airband binary. Source: gui/.
Requirements
- Python 3.9+
rtl_airbandbuilt with-DNFM=ON— required for FM demodulation, which is the whole point of this tool. Without it, any channel set to NFM makes the engine refuse to start ("unknown modulation"). See Troubleshooting.sudo apt-get install build-essential cmake pkg-config libmp3lame-dev libshout3-dev 'libconfig++-dev' libfftw3-dev mkdir build && cd build && cmake -DNFM=ON ../ && make && sudo make install
Run it
cd gui
pip install -r requirements.txt
python3 app.py
Open http://127.0.0.1:5050.
Tabs
Frequencies
The main view. One "Receiver" panel (device index, gain, center frequency, correction — assumes a single SDR), with an inline reminder that every frequency below must fall within the receiver's tuned bandwidth around Center frequency (roughly ±1.2 MHz by default — see the in-app Help / FAQ tab for detail), and a card per frequency ("Frequency 1", "Frequency 2", ...):
-
Frequency and a modulation dropdown (NFM / AM). A channel with no modulation set at all shows as AM (the engine's real default) rather than silently picking one for you — the field is only written once you actually change it.
-
Squelch level, 0–9, like the SQL knob on a handheld radio:
0= always open (hears everything, including noise)5≈ the engine's own built-in auto default (9.54 dB above the noise floor)9= only the strongest signals open it
This maps to the engine's
squelch_snr_threshold(2 dB per level) under the hood. A channel already using the older absolutesquelch_threshold(dBFS) instead shows a note pointing at the Advanced tab rather than fighting with it — the two settings are different units and shouldn't both be set. -
Record to file and/or Stream to Icecast — independent toggles, not either/or; click either button to turn that output on or off, each shows its own fields below it, and both can be on at once. New file outputs default to a per-frequency folder plus a frequency-first filename (see "Recordings, in detail" below); a "↻ Use frequency-based directory + filename" button recomputes both on demand for a card created before this existed.
-
Enable live listening — adds a
udp_streamoutput on a fixed local port. Requires Save + Restart to take effect (see below), after which a Listen/volume/mute control appears.
Recordings
Lists clips from a configurable recordings folder (set on this tab), grouped by date, newest first, with inline playback, download, Open folder, and Delete. Any .mp3/.wav/.ogg found recursively under that folder is picked up, so dated_subdirectories = true output layouts work. Serving is restricted to that folder only.
Open folder opens the recordings directory in the file manager of whichever machine is running this GUI — only useful when that's the machine you're physically looking at, not over an SSH tunnel from elsewhere. Delete permanently removes a clip; there's a confirmation dialog, and there is no undo.
Process Control
The binary path, the live-audio sample rate setting, and a live tailing log of the engine's output. Start/Stop/Restart themselves are not on this tab — see below.
Start / Restart / Stop
These live in a bar directly under the header, visible on every tab — not tucked into Process Control — since it's the action you'll reach for constantly. Start and Restart save the current config first: RTLSDR-Airband reads its config once at startup, so restarting against a stale on-disk file would defeat the point, and saving unchanged content back is harmless. Stop just stops. The header's own Save button is there for saving without also (re)starting anything.
Buttons disable to match reality: Start is disabled while running, Stop/Restart are disabled while stopped.
Advanced
The full config tree — every field RTLSDR-Airband supports (multiple devices, mixers, scan mode, ...) — for anything the Frequencies tab doesn't model. Editing here or in Frequencies updates the same in-memory config either way; switching tabs always shows the latest state.
Raw Config
The config file as plain text, with syntax validation before saving.
Help / FAQ tab
An in-app reference for every setting above — device index, gain, center frequency and the bandwidth constraint in detail, modulation, squelch level, output types, live listening and its sample rate setting, recordings management, and why there's no waterfall. No need to leave the app to look any of it up.
Theme
The 🌙/☀️ button in the header switches between dark and light. The choice is remembered per-browser (localStorage), not shared between devices or with the server.
Live listening, in detail
Each channel with live listening enabled gets its own fixed UDP port (127.0.0.1, base port 17300 + channel index) carrying the engine's raw demodulated audio (see src/udp_stream.cpp upstream). The GUI's Python server listens on that port, buffers incoming audio, and the browser polls for new bytes and schedules them through the Web Audio API — so there's a short buffering delay, not zero-latency audio.
One setting to get right: the sample rate. RTLSDR-Airband demodulates at 16 kHz if built with -DNFM=ON, or 8 kHz otherwise. The GUI can't detect this from the running binary, so it's a setting on the Process Control tab (default 16000) — if audio sounds sped up or slowed down, that's the first thing to check.
Recordings, in detail
New file outputs lay clips out frequency-first, then by date:
airband-recordings/
145.400/
2026/09/30/
145.400_MyStation_20260930_143012.mp3
120.225/
2026/09/30/
120.225_Ground_20260930_091500.mp3
The 2026/09/30 nesting is the engine's own dated_subdirectories behavior (make_dated_subdirs in src/helper_functions.cpp upstream); the 145.400/120.225 folder above it is this GUI's own default directory value, one per frequency. Grouping by frequency first, then date, is meant to make something like "process today's clips for frequency X as a batch" (a transcription/summarization pass, say) straightforward to script without cross-referencing filenames.
Responsible & legal use
This tool can record or stream whatever your receiver picks up on the frequencies you configure. Radio laws vary by country and by the type of transmission — publicly broadcast, unencrypted traffic (like amateur/ham radio) is treated very differently in most places than private, licensed commercial, encrypted, or cellular traffic. This project provides the tool only; it's on you to know and follow the rules that apply where you are and to what you're monitoring. When in doubt, check with your local telecommunications regulator. (Also in the app itself: Help / FAQ tab.)
Known limitations
- The Frequencies tab assumes a single SDR device (
devices[0]). Multiple devices, mixers, and scan mode work, but only through the Advanced or Raw Config tabs. - No waterfall/spectrum display. The engine has no output that exposes spectrum data today (only demodulated audio), and a single RTL-SDR dongle can't be shared with a second process to get one elsewhere — a waterfall would need a new feature added to the C++ engine itself (e.g. a spectrum-data output alongside the existing
udp_streamaudio one), not just the GUI. Left out for now. - This is a development server (Flask's built-in one), meant for local/LAN use on the same machine as the SDR — not hardened for exposing over the open internet.
Tests
cd gui
python3 -m unittest discover -s . -p "test_*.py" -v