diff --git a/utils/sstv/image_decoder.py b/utils/sstv/image_decoder.py index 13a9eda..c7f2391 100644 --- a/utils/sstv/image_decoder.py +++ b/utils/sstv/image_decoder.py @@ -138,9 +138,16 @@ class SSTVImageDecoder: self._buffer = np.concatenate([self._buffer, samples]) - # Process complete lines + # Process complete lines. + # Guard against stalls: if _decode_line() cannot consume data + # (e.g. sub-component samples exceed line_samples due to rounding), + # break out and wait for more audio. while not self._complete and len(self._buffer) >= self._line_samples: + prev_line = self._current_line + prev_len = len(self._buffer) self._decode_line() + if self._current_line == prev_line and len(self._buffer) == prev_len: + break # No progress — need more data # Prevent unbounded buffer growth - keep at most 2 lines worth max_buffer = self._line_samples * 2 diff --git a/utils/sstv/sstv_decoder.py b/utils/sstv/sstv_decoder.py index 9daa9ac..36834bc 100644 --- a/utils/sstv/sstv_decoder.py +++ b/utils/sstv/sstv_decoder.py @@ -487,9 +487,14 @@ class SSTVDecoder: sync_energy = goertzel_mag(samples, 1200.0, SAMPLE_RATE) noise_floor = max(rms * 0.5, 0.001) - if leader_energy > noise_floor * 5: + # Require the tone to both exceed the noise floor AND + # dominate the other tone by 2x to avoid false positives + # from broadband noise. + if (leader_energy > noise_floor * 5 + and leader_energy > sync_energy * 2): sstv_tone = 'leader' - elif sync_energy > noise_floor * 5: + elif (sync_energy > noise_floor * 5 + and sync_energy > leader_energy * 2): sstv_tone = 'sync' elif signal_level > 10: sstv_tone = 'noise'