mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -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:
@@ -235,20 +235,36 @@ class SSTVImageDecoder:
|
||||
pos += self._porch_samples
|
||||
else:
|
||||
# Scottie: sync + porch between B and R.
|
||||
# Search for the actual sync pulse to correct for
|
||||
# SDR clock drift — without this, any timing error
|
||||
# accumulates line-by-line producing a visible slant.
|
||||
search_margin = max(100, self._line_samples // 10)
|
||||
sync_search_start = max(0, pos - search_margin)
|
||||
sync_search_end = min(
|
||||
len(self._buffer),
|
||||
pos + self._sync_samples + search_margin,
|
||||
)
|
||||
sync_region = self._buffer[sync_search_start:sync_search_end]
|
||||
sync_found = self._find_sync(sync_region)
|
||||
if sync_found is not None:
|
||||
pos = (sync_search_start + sync_found
|
||||
+ self._sync_samples + self._porch_samples)
|
||||
# Search for the actual sync pulse to correct per-line
|
||||
# SDR clock drift — without this, timing errors
|
||||
# accumulate line-by-line producing a visible slant.
|
||||
#
|
||||
# Constraints:
|
||||
# - Backward margin is small (50 samples ≈ 4.5 ms)
|
||||
# so we don't stray deep into B pixel data.
|
||||
# - Forward margin is bounded by available buffer so
|
||||
# the R channel decode never overflows the buffer.
|
||||
# - The candidate position is validated before use.
|
||||
r_samples = self._channel_samples[-1]
|
||||
bwd = min(50, pos)
|
||||
fwd = max(0, len(self._buffer) - pos
|
||||
- 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:
|
||||
pos += self._sync_samples + self._porch_samples
|
||||
elif self._separator_samples > 0:
|
||||
|
||||
Reference in New Issue
Block a user