The default 1 MHz sample rate was too low for SatDump's meteor_m2-x_lrpt
pipeline, causing NOSYNC and 0.000dB SNR. Bumped to 2.4 MHz (SatDump
recommendation) and wired up the WEATHER_SAT_SAMPLE_RATE config value
so it actually gets passed to decoder.start() from both the auto-scheduler
and manual start route.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Timer threads now log on fire and catch all exceptions so scheduled
captures never die silently. Frontend connects SSE when the scheduler
is enabled (not only on manual Start) and polls /wefax/status every 10s
as a fallback so the UI stays in sync with auto-fired captures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace window.open() with a fullscreen modal matching the SSTV
pattern: toolbar with download/delete SVG buttons, close button,
click-outside-to-close, and confirmation before delete.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
stop() was returning before the decode thread could save any partial
image to disk, so the frontend loadImages() call found nothing new.
Join the decode thread (2s timeout) before returning — with select()-
based reads the thread exits within ~0.5s so this stays responsive.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace blocking stdout.read() with select()-based non-blocking reads
so the decode thread responds to stop within 0.5s
- Make stop() non-blocking by releasing the lock before terminating the
process and removing the redundant wait()
- Move initial scanning SSE event from start() into the decode thread so
it fires after the frontend EventSource connects
- Update frontend stop() to give immediate UI feedback before the fetch
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
rtl_fm subprocess failures (missing tool, no SDR hardware) were silent —
add tool-path check and post-spawn health check in _start_pipeline(),
show errors prominently in the strip status bar (red text + red dot),
and include error detail in scheduler skip events.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Flash the Start button itself with amber pulse when clicked without a
station selected, and show "Select Station" in the strip status text
right next to the button so the error is immediately visible.
Add a 24-hour timeline bar with broadcast window markers, red UTC time
cursor, and countdown boxes (HRS/MIN/SEC) that tick down to the next
broadcast. Broadcasts show as amber blocks on the timeline track with
imminent/active visual states matching the weather satellite pattern.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix silent failure when starting without station/frequency selected by
flashing amber on status text and dropdowns. Add auto-capture scheduler
that uses fixed UTC broadcast schedules from station data to
automatically start/stop WeFax decoding at broadcast times.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
- Remove profiles: [basic] from intercept service so docker compose up -d
works without --profile flag (fixes breaking change for existing deployments)
- Add missing Any import to routes/acars.py and routes/vdl2.py
- Reset last_message_time to None in ACARS and VDL2 clear endpoints
- Restore 131.725 and 131.825 to default ACARS frequencies (major US carriers)
- Copy VDL2 ACARS enrichment fields to top-level data dict instead of mutating
nested acars_payload (consistent with ACARS route pattern)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The shared audio queue (maxsize reduced from 80 to 20) was not flushed
when the monitor frequency changed — only when the monitor was disabled.
This caused up to 4 seconds of stale old-frequency audio to play after
clicking to tune, making click-to-tune appear non-functional.
Now flushes the queue whenever the VFO frequency changes, so audio at
the new frequency begins within ~50ms (one FFT frame).
1. Stop Monitor button was disabled during shared monitor retunes
because _syncMonitorButtons disabled the button whenever
_startingMonitor was true, even if the monitor was already active.
Now only disables during initial start (not retunes).
2. Click-to-tune was inconsistent because the shared monitor retune
(rearm after capture restart) captured the center frequency early
in _startMonitorInternal, then sent it via POST to /audio/start.
If the user clicked a new frequency during the async reconnect,
the POST carried the stale frequency and could override the click.
Now retunes use the live _monitorFreqMhz and send a WS tune sync
after reconnecting to ensure the backend has the latest VFO.
Two root causes for the waterfall/monitor lockup when scrolling past the
2.4 MHz RTL-SDR span:
1. safe_terminate() sent SIGKILL but never called wait(), leaving a
zombie process that kept the USB device handle open. The subsequent
capture restart failed the USB probe and the monitor could not use
the shared IQ path, falling back to a process-based monitor that
stole the SDR from the waterfall.
2. When the frontend created a new WebSocket after a failure, the old
handler's finally block called _set_shared_capture_state(running=False)
which could race with the new handler's running=True, making the
shared monitor path unavailable. Added a generation counter so only
the owning handler can clear the shared state.
When restarting capture for a new frequency, the USB handle from the
just-killed process may not be released by the kernel in time for the
rtl_test probe inside claim_sdr_device. Add retry logic (up to 4
attempts with 0.4s backoff) matching the pattern already used by the
audio start endpoint.
Also clean up stale shared-monitor state in the frontend error handler
so the monitor button is not left disabled when the capture restart
fails.
When changing frequency with shared monitor active, the monitor retune
could be silently dropped if a previous retune was still in-flight,
leaving the UI stuck on "Starting <freq>". After stopping and restarting
the waterfall, the monitor button could remain disabled because
_startingMonitor was never reset and _monitorRetuneTimer was not cleared.
- Cancel in-flight monitor start when queuing a new retune
- Always clear _pendingSharedMonitorRearm in started handler
- Clear _monitorRetuneTimer and reset _startingMonitor in stop()