mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 22:59:59 -07:00
fix: Expand Scottie sync deviation search window to fix under-correction
The slant correction was severely under-correcting because bwd=50 caused the sync deviation measurements to saturate after only ~25 lines (for a 2-sample/line SDR clock drift). Lines 25-256 all reported deviation=-50, pulling the linear regression slope toward zero. Increase bwd and fwd to 800 samples each — sufficient to track cumulative drift from up to ~±200 ppm SDR clock offset across the full 256-line image. Also use a full-sync-length (432-sample) Goertzel window instead of 1/3 length, giving ~111 Hz frequency resolution to cleanly separate the 1200 Hz sync tone from 1500 Hz pixel data. Search is stepped at 5 samples (~0.1 ms) for efficiency, keeping the goertzel_batch batch size at ~320 windows/line. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -246,21 +246,33 @@ class SSTVImageDecoder:
|
||||
# slant correction without touching pos or consumed —
|
||||
# so a noisy/false measurement never corrupts the decode.
|
||||
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)
|
||||
# Large window to cover cumulative SDR clock drift over
|
||||
# the full image. bwd=50 saturates after ~25 lines for
|
||||
# a 2-sample/line drift; 800 samples covers ±200 ppm.
|
||||
bwd = min(800, pos)
|
||||
remaining = len(self._buffer) - pos
|
||||
fwd = min(800, max(
|
||||
0,
|
||||
remaining - self._sync_samples
|
||||
- self._porch_samples - r_samples))
|
||||
deviation: float | None = None
|
||||
if bwd + fwd > 0:
|
||||
region_start = pos - bwd
|
||||
sync_region = self._buffer[
|
||||
region_start: pos + self._sync_samples + fwd]
|
||||
win = max(20, self._sync_samples // 3)
|
||||
n_win = len(sync_region) - win + 1
|
||||
if n_win > 0:
|
||||
windows = np.lib.stride_tricks.sliding_window_view(
|
||||
sync_region, win)
|
||||
# Full-sync-length window gives best freq resolution
|
||||
# (~111 Hz at 48 kHz) to cleanly separate 1200 Hz
|
||||
# sync from 1500 Hz pixel data.
|
||||
win = self._sync_samples
|
||||
n_raw = len(sync_region) - win + 1
|
||||
if n_raw > 0:
|
||||
# Step 5 samples (~0.1 ms) — enough resolution
|
||||
# for pixel-level drift, keeps batch size small.
|
||||
step = 5
|
||||
all_windows = (
|
||||
np.lib.stride_tricks.sliding_window_view(
|
||||
sync_region, win))
|
||||
windows = all_windows[::step]
|
||||
energies = goertzel_batch(
|
||||
windows,
|
||||
np.array([FREQ_SYNC, FREQ_BLACK]),
|
||||
@@ -269,9 +281,10 @@ class SSTVImageDecoder:
|
||||
black_e = energies[:, 1]
|
||||
valid_mask = sync_e > black_e * 2
|
||||
if valid_mask.any():
|
||||
fine_best = int(
|
||||
np.argmax(np.where(valid_mask, sync_e, 0.0)))
|
||||
deviation = float(fine_best - bwd)
|
||||
fine_idx = int(
|
||||
np.argmax(
|
||||
np.where(valid_mask, sync_e, 0.0)))
|
||||
deviation = float(fine_idx * step - bwd)
|
||||
self._sync_deviations.append(deviation)
|
||||
pos += self._sync_samples + self._porch_samples
|
||||
elif self._separator_samples > 0:
|
||||
|
||||
Reference in New Issue
Block a user