Adaptive amateur-radio decoding — the Tuna method on the ham bands.
Project site: felbs.software · Contact: E@felbs.software
Part of the Tuna family: TV Tuna → Radio Tuna → wxTuna → aeroTuna → hamTuna. Same thesis every time: every decoder secretly knows how well it's doing — surface that truth-dial and close the loop on it.
The ham bands are a mixed world — digital (APRS, CW, PSK31, FT8) and voice (FM repeaters, SSB, and digital voice like DMR/D-STAR). One repo, one tool per mode, shared plumbing.
git clone https://github.com/Felbs/hamTuna.git
cd hamTuna
python tools/aprs.py selftest # AX.25->AFSK->FM roundtrip, no radio needed
python tools/cw.py selftest # Morse synth -> noise -> decode, no radio neededDependencies: numpy, scipy, and the SoapySDR python bindings +
a driver for your SDR (live modes only — selftests run with numpy/scipy
alone). Easiest path is radioconda;
on Debian/Ubuntu: apt install python3-numpy python3-scipy python3-soapysdr soapysdr-module-all;
on Arch/Omarchy: pacman -S python-numpy python-scipy soapysdr soapyrtlsdr (SDRplay: yay -S libsdrplay soapysdrplay3-git).
Amateur radio's AIS: hams beacon callsign + position over AX.25 packets, AFSK 1200 baud on 144.390 MHz (North America). Same HDLC framing and CRC-16/X.25 truth dial we field-proved on maritime AIS; the Bell-202 modem underneath is new (non-coherent dual-tone detector).
python tools/aprs.py selftest # full AX.25->AFSK->FM roundtrip, no radio
python tools/aprs.py capture --secs 120 # live: callsigns + positions heard- CW (Morse) — on-off keying with a confidence plane; the oldest digital mode meets the newest rescue tricks
- Repeater activity scanner — the voice-side inventory: which 2m/70cm repeaters are alive, learned by hour (the Knob of Time on voice)
- PSK31 — narrowband keyboard chat
- FT8 — via WSJT-X as the decode oracle; our value is the band-opening
clock (see Radio Tuna's
hf_knob), not re-decoding
Any SoapySDR-supported SDR + a VHF antenna. APRS beacons are strong and bursty — patience beats gain.
MIT