mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -07:00
Fix SSTV decoder thread lifecycle and VIS detection reliability
Three bugs preventing the live SSTV pipeline from working: 1. Race condition: self._running was set AFTER starting the decode thread, so the thread checked the flag, found it False, and exited immediately without ever processing audio. 2. Ghost running state: when the decode thread exited (e.g. rtl_fm died), self._running stayed True. The decoder reported as running but was dead, and subsequent start() calls returned without doing anything - permanently stuck until app restart. 3. VIS detection fragility: unclassifiable windows at tone transition boundaries (mixed energy from two tones) caused the state machine to reset from LEADER/BREAK states back to IDLE, dropping valid VIS headers on real signals. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -283,8 +283,10 @@ class SSTVDecoder:
|
||||
try:
|
||||
freq_hz = self._get_doppler_corrected_freq_hz()
|
||||
self._current_tuned_freq_hz = freq_hz
|
||||
self._start_pipeline(freq_hz)
|
||||
# Set _running BEFORE starting the pipeline so the decode
|
||||
# thread sees it as True on its first loop iteration.
|
||||
self._running = True
|
||||
self._start_pipeline(freq_hz)
|
||||
|
||||
# Start Doppler tracking thread if enabled
|
||||
if self._doppler_enabled:
|
||||
@@ -306,6 +308,7 @@ class SSTVDecoder:
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self._running = False
|
||||
logger.error(f"Failed to start SSTV decoder: {e}")
|
||||
self._emit_progress(DecodeProgress(
|
||||
status='error',
|
||||
@@ -433,7 +436,26 @@ class SSTVDecoder:
|
||||
break
|
||||
time.sleep(0.1)
|
||||
|
||||
logger.info("Audio decode thread stopped")
|
||||
# Clean up if the thread exits while we thought we were running.
|
||||
# This prevents a "ghost running" state where is_running is True
|
||||
# but the thread has already died (e.g. rtl_fm exited).
|
||||
with self._lock:
|
||||
was_running = self._running
|
||||
self._running = False
|
||||
if was_running and self._rtl_process:
|
||||
with contextlib.suppress(Exception):
|
||||
self._rtl_process.terminate()
|
||||
self._rtl_process.wait(timeout=2)
|
||||
self._rtl_process = None
|
||||
|
||||
if was_running:
|
||||
logger.warning("Audio decode thread stopped unexpectedly")
|
||||
self._emit_progress(DecodeProgress(
|
||||
status='error',
|
||||
message='Decode pipeline stopped unexpectedly'
|
||||
))
|
||||
else:
|
||||
logger.info("Audio decode thread stopped")
|
||||
|
||||
def _save_decoded_image(self, decoder: SSTVImageDecoder,
|
||||
mode_name: str | None) -> None:
|
||||
|
||||
@@ -193,6 +193,8 @@ class VISDetector:
|
||||
# Transition to BREAK; this window counts as break window 1
|
||||
self._tone_counter = 1
|
||||
self._state = VISState.BREAK
|
||||
elif tone is None:
|
||||
pass # Ambiguous window at tone boundary — stay in state
|
||||
else:
|
||||
self._tone_counter = 0
|
||||
self._state = VISState.IDLE
|
||||
@@ -207,6 +209,8 @@ class VISDetector:
|
||||
# Transition to LEADER_2; this window counts
|
||||
self._tone_counter = 1
|
||||
self._state = VISState.LEADER_2
|
||||
elif tone is None:
|
||||
pass # Ambiguous window at tone boundary — stay in state
|
||||
else:
|
||||
self._tone_counter = 0
|
||||
self._state = VISState.IDLE
|
||||
@@ -227,6 +231,8 @@ class VISDetector:
|
||||
self._data_bits = []
|
||||
self._bit_accumulator = []
|
||||
self._state = VISState.DATA_BITS
|
||||
elif tone is None:
|
||||
pass # Ambiguous window at tone boundary — stay in state
|
||||
else:
|
||||
self._tone_counter = 0
|
||||
self._state = VISState.IDLE
|
||||
|
||||
Reference in New Issue
Block a user