feat: add frame synchronization prototype with tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
cah
2026-02-24 04:40:07 -07:00
parent af6055242e
commit c427dfdd3d
2 changed files with 573 additions and 0 deletions

108
model/test_frame_sync.py Normal file
View File

@@ -0,0 +1,108 @@
#!/usr/bin/env python3
"""
Tests for the frame synchronization prototype (frame_sync.py).
Tests cover stream generation (length, offsets) and syndrome screening
(correct offset identification, wrong offset rejection).
Run:
python3 -m pytest model/test_frame_sync.py -v
"""
import numpy as np
import pytest
from ldpc_sim import N, build_full_h_matrix
from frame_sync import generate_stream, syndrome_screen
# =============================================================================
# Fixtures
# =============================================================================
@pytest.fixture(scope="module")
def H():
"""Full expanded H matrix, built once per module."""
return build_full_h_matrix()
# =============================================================================
# TestStreamGeneration
# =============================================================================
class TestStreamGeneration:
"""Validate continuous LLR stream generation."""
def test_stream_length(self, H):
"""Stream should be n_frames * N + offset long."""
np.random.seed(100)
n_frames = 5
offset = 37
stream_llr, true_offset, info_list = generate_stream(
H, n_frames=n_frames, lam_s=5.0, lam_b=0.1, offset=offset
)
assert len(stream_llr) == n_frames * N + offset
assert true_offset == offset
assert len(info_list) == n_frames
def test_stream_zero_offset(self, H):
"""offset=0 should work, producing stream of length n_frames * N."""
np.random.seed(101)
n_frames = 3
stream_llr, true_offset, info_list = generate_stream(
H, n_frames=n_frames, lam_s=5.0, lam_b=0.1, offset=0
)
assert len(stream_llr) == n_frames * N
assert true_offset == 0
assert len(info_list) == n_frames
def test_stream_random_offset(self, H):
"""offset=None should pick a random offset in [0, N-1]."""
np.random.seed(102)
stream_llr, true_offset, info_list = generate_stream(
H, n_frames=4, lam_s=5.0, lam_b=0.1, offset=None
)
assert 0 <= true_offset < N
assert len(stream_llr) == 4 * N + true_offset
# =============================================================================
# TestSyndromeScreen
# =============================================================================
class TestSyndromeScreen:
"""Validate syndrome-based offset screening."""
def test_correct_offset_low_syndrome(self, H):
"""At high SNR (lam_s=10), the best offset should match the true offset."""
np.random.seed(200)
n_frames = 3
offset = 42
stream_llr, true_offset, _ = generate_stream(
H, n_frames=n_frames, lam_s=10.0, lam_b=0.1, offset=offset
)
scores = syndrome_screen(stream_llr, n_offsets=N)
# The true offset should have the lowest (or near-lowest) syndrome weight
best_offset = min(scores, key=scores.get)
assert best_offset == true_offset, (
f"Best offset {best_offset} (sw={scores[best_offset]}) "
f"!= true offset {true_offset} (sw={scores[true_offset]})"
)
def test_wrong_offsets_high_syndrome(self, H):
"""Wrong offsets should have average syndrome weight > 80."""
np.random.seed(201)
n_frames = 3
offset = 10
stream_llr, true_offset, _ = generate_stream(
H, n_frames=n_frames, lam_s=10.0, lam_b=0.1, offset=offset
)
scores = syndrome_screen(stream_llr, n_offsets=N)
# Collect syndrome weights for wrong offsets
wrong_weights = [
scores[o] for o in scores if o != true_offset
]
avg_wrong = np.mean(wrong_weights)
assert avg_wrong > 80, (
f"Average wrong-offset syndrome weight {avg_wrong:.1f} is not > 80"
)