Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SDR radio code on the Pi (/opt/pi/sdr/)
Stdlib only, Python 3.14 (numpy for classify/discover). Every Python entry script is started with
python3 -I and puts its parent dir (the package root) on sys.path itself, so import radio works.
| dir | what | systemd unit (user) | ExecStart |
|---|---|---|---|
radio/ |
shared package: config.py (all paths, env vars, constants), channels.py (channels.json cache), recordings.py (DAY_RE/FILE_RE, per-day indexes, durations, scan/list/counts), mp3.py, util.py (JSON helpers) |
- | - |
web/ |
radio.home recordings UI | sdr-recordings (sdrweb) |
python3 -I /opt/pi/sdr/web/server.py |
scanner/gen_config.py |
writes rtl_airband.conf + channels.json from one frequency table | sdr-scanner ExecStartPre (root) |
python3 -I /opt/pi/sdr/scanner/gen_config.py /etc/sdr-scanner |
scanner/watchdog.sh |
restarts the scanner when the HackRF stream died | sdr-scanner-watchdog (timer, root) |
/opt/pi/sdr/scanner/watchdog.sh |
classify/classify.py |
voice/noise post-filter loop (+ audio.py DSP, adsb.py aircraft context, guard.py stuck-channel squelch raise) |
sdr-classify (sdr) |
python3 -I /opt/pi/sdr/classify/classify.py |
discover/discover.py |
hackrf_sweep source discovery (stops the scanner via the unit) | sdr-discover (timer, root) |
python3 -I /opt/pi/sdr/discover/discover.py |
archive/archive.py |
rsync old days to the home server, verify, delete local | sdr-archive (timer, sdr) |
python3 -I /opt/pi/sdr/archive/archive.py |
archive/retention.sh |
delete noise/blips, emergency cleanup | sdr-retention (timer, sdr) |
/opt/pi/sdr/archive/retention.sh |
transcribe/transcribe.py |
queue of voice recordings (since SDR_TR_START) -> Whisper on the owner desktop via https://whisper.home (Caddy, connects to 192.168.88.221); waits while the desktop is off; newest first | sdr-transcribe (sdr) |
python3 -I /opt/pi/sdr/transcribe/transcribe.py |
speaker/speaker.py |
plays new voice recordings on the Pi jack (no radio import; own env names SPEAKER_*) |
sdr-speaker (DynamicUser) |
python3 -I /opt/pi/sdr/speaker/speaker.py |
patches/, readsb/ |
rtl_airband local patch, readsb notes | - | - |
Data paths (all in radio/config.py, env override in brackets)
- recordings
/srv/sdr/recordings/YYYY-MM-DD/*.mp3[SDR_REC_DIR]; per day.cls.json(classifier),.archived.json(archive),noise/ - channels
/etc/sdr-scanner/channels.json[SDR_CHANNELS] (written by gen_config) - discovery
/srv/sdr/discovery/[SDR_DISC_DIR]:sources.json,promoted.json,adsb_latest.json,auto_snr.json,runs.jsonl - speaker state
/var/lib/sdr-speaker/state.json(web: SDR_SPEAKER_STATE, speaker.py: SPEAKER_STATE - different names on purpose, kept as before) - shares
/var/lib/sdr-web/shares.json[SDR_SHARES] - transcripts
/srv/sdr/transcripts/YYYY-MM-DD.json+status.json[SDR_TR_DIR] (web:trfield,/api/transcribe200/503 for the dashboard)