mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 22:59:59 -07:00
Implement HF radiofax decoding with custom Python DSP pipeline (rtl_fm USB → Goertzel/Hilbert demodulation), 33-station database with broadcast schedules, audio waveform scope, live image preview, and decoded image gallery. Amber/gold UI theme for HF distinction. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
90 lines
2.6 KiB
Python
90 lines
2.6 KiB
Python
"""WeFax station database loader.
|
|
|
|
Loads and caches station data from data/wefax_stations.json. Provides
|
|
lookup by callsign and current-broadcast filtering based on UTC time.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
|
|
_stations_cache: list[dict] | None = None
|
|
_stations_by_callsign: dict[str, dict] = {}
|
|
|
|
_STATIONS_PATH = Path(__file__).resolve().parent.parent / 'data' / 'wefax_stations.json'
|
|
|
|
|
|
def load_stations() -> list[dict]:
|
|
"""Load all WeFax stations from JSON, caching on first call."""
|
|
global _stations_cache, _stations_by_callsign
|
|
|
|
if _stations_cache is not None:
|
|
return _stations_cache
|
|
|
|
with open(_STATIONS_PATH) as f:
|
|
data = json.load(f)
|
|
|
|
_stations_cache = data.get('stations', [])
|
|
_stations_by_callsign = {s['callsign']: s for s in _stations_cache}
|
|
return _stations_cache
|
|
|
|
|
|
def get_station(callsign: str) -> dict | None:
|
|
"""Get a single station by callsign."""
|
|
load_stations()
|
|
return _stations_by_callsign.get(callsign.upper())
|
|
|
|
|
|
def get_current_broadcasts(callsign: str) -> list[dict]:
|
|
"""Return schedule entries closest to the current UTC time.
|
|
|
|
Returns up to 3 entries: the most recent past broadcast and the
|
|
next two upcoming ones, annotated with ``minutes_until`` or
|
|
``minutes_ago`` relative to now.
|
|
"""
|
|
station = get_station(callsign)
|
|
if not station:
|
|
return []
|
|
|
|
now = datetime.now(timezone.utc)
|
|
current_minutes = now.hour * 60 + now.minute
|
|
|
|
schedule = station.get('schedule', [])
|
|
if not schedule:
|
|
return []
|
|
|
|
# Convert schedule times to minutes-since-midnight for comparison
|
|
entries: list[tuple[int, dict]] = []
|
|
for entry in schedule:
|
|
parts = entry['utc'].split(':')
|
|
mins = int(parts[0]) * 60 + int(parts[1])
|
|
entries.append((mins, entry))
|
|
entries.sort(key=lambda x: x[0])
|
|
|
|
# Find closest entries relative to now
|
|
results = []
|
|
for mins, entry in entries:
|
|
diff = mins - current_minutes
|
|
# Wrap around midnight
|
|
if diff < -720:
|
|
diff += 1440
|
|
elif diff > 720:
|
|
diff -= 1440
|
|
|
|
annotated = dict(entry)
|
|
if diff >= 0:
|
|
annotated['minutes_until'] = diff
|
|
else:
|
|
annotated['minutes_ago'] = abs(diff)
|
|
annotated['_sort_key'] = abs(diff)
|
|
results.append(annotated)
|
|
|
|
results.sort(key=lambda x: x['_sort_key'])
|
|
|
|
# Return 3 nearest entries, clean up sort key
|
|
for r in results:
|
|
r.pop('_sort_key', None)
|
|
return results[:3]
|