mirror of
https://github.com/smittix/intercept.git
synced 2026-07-24 00:48:11 -07:00
fix: Correct Scottie sync search to prevent decoder stall
The previous sync search used search_margin = line_samples/10 (~306 samples for Scottie2), reaching deep into B channel pixel data behind pos and well past the expected sync end ahead of pos. When _find_sync returned a position in the late portion of that wide region, pos + R_channel_samples exceeded the buffer length. The buffer-too-short guard in _decode_line then returned early without consuming data or advancing the line counter, causing the stall guard in feed() to permanently break the decode loop. Fix: use a 50-sample backward margin (covers >130 ppm SDR drift) and a forward margin capped to whatever the current buffer can safely support for the R channel. A final candidate-position check before committing pos ensures no overflow is possible. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+30
-14
@@ -235,20 +235,36 @@ class SSTVImageDecoder:
|
|||||||
pos += self._porch_samples
|
pos += self._porch_samples
|
||||||
else:
|
else:
|
||||||
# Scottie: sync + porch between B and R.
|
# Scottie: sync + porch between B and R.
|
||||||
# Search for the actual sync pulse to correct for
|
# Search for the actual sync pulse to correct per-line
|
||||||
# SDR clock drift — without this, any timing error
|
# SDR clock drift — without this, timing errors
|
||||||
# accumulates line-by-line producing a visible slant.
|
# accumulate line-by-line producing a visible slant.
|
||||||
search_margin = max(100, self._line_samples // 10)
|
#
|
||||||
sync_search_start = max(0, pos - search_margin)
|
# Constraints:
|
||||||
sync_search_end = min(
|
# - Backward margin is small (50 samples ≈ 4.5 ms)
|
||||||
len(self._buffer),
|
# so we don't stray deep into B pixel data.
|
||||||
pos + self._sync_samples + search_margin,
|
# - Forward margin is bounded by available buffer so
|
||||||
)
|
# the R channel decode never overflows the buffer.
|
||||||
sync_region = self._buffer[sync_search_start:sync_search_end]
|
# - The candidate position is validated before use.
|
||||||
sync_found = self._find_sync(sync_region)
|
r_samples = self._channel_samples[-1]
|
||||||
if sync_found is not None:
|
bwd = min(50, pos)
|
||||||
pos = (sync_search_start + sync_found
|
fwd = max(0, len(self._buffer) - pos
|
||||||
+ self._sync_samples + self._porch_samples)
|
- self._sync_samples - self._porch_samples
|
||||||
|
- r_samples)
|
||||||
|
fwd = min(fwd, self._sync_samples)
|
||||||
|
if bwd + fwd > 0:
|
||||||
|
sync_region = self._buffer[
|
||||||
|
pos - bwd: pos + self._sync_samples + fwd]
|
||||||
|
sync_found = self._find_sync(sync_region)
|
||||||
|
if sync_found is not None:
|
||||||
|
candidate = (pos - bwd + sync_found
|
||||||
|
+ self._sync_samples
|
||||||
|
+ self._porch_samples)
|
||||||
|
if candidate + r_samples <= len(self._buffer):
|
||||||
|
pos = candidate
|
||||||
|
else:
|
||||||
|
pos += self._sync_samples + self._porch_samples
|
||||||
|
else:
|
||||||
|
pos += self._sync_samples + self._porch_samples
|
||||||
else:
|
else:
|
||||||
pos += self._sync_samples + self._porch_samples
|
pos += self._sync_samples + self._porch_samples
|
||||||
elif self._separator_samples > 0:
|
elif self._separator_samples > 0:
|
||||||
|
|||||||
Reference in New Issue
Block a user