Files
..

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: tr field, /api/transcribe 200/503 for the dashboard)