fix(perception): isolate recorded decode from realtime admission

This commit is contained in:
DCCONSTRUCTIONS
2026-08-25 21:58:12 +03:00
parent bab0bb2df2
commit fb5bf943c9
4 changed files with 416 additions and 15 deletions
+45
View File
@@ -62,9 +62,12 @@ from k1link.perception.providers import (
from k1link.perception.recorded_source import (
DecodedFrameTiming,
DecodedRecordedSource,
DecodePhase,
PrefetchedRecordedImageDecoder,
RecordedRavnoves00Source,
RecordedSourceError,
ReplayPacing,
SourcePacingTiming,
)
@@ -839,6 +842,7 @@ def test_recorded_source_target_rate_changes_only_wall_clock_pacing(tmp_path: Pa
camera_path, timeline_path = _write_recorded_fixture(tmp_path)
now = [1_000_000_000]
waits: list[float] = []
pacing: list[SourcePacingTiming] = []
def wait(stop_event: Event, seconds: float) -> bool:
waits.append(seconds)
@@ -852,6 +856,7 @@ def test_recorded_source_target_rate_changes_only_wall_clock_pacing(tmp_path: Pa
target_rate_hz=20.0,
expected_frame_count=2,
expected_source_pack_sha256=None,
pacing_observer=pacing.append,
clock_ns=lambda: now[0],
wait=wait,
)
@@ -859,6 +864,8 @@ def test_recorded_source_target_rate_changes_only_wall_clock_pacing(tmp_path: Pa
packets = list(source.packets(Event()))
assert waits == pytest.approx([0.05])
assert [sample.sequence for sample in pacing] == [0, 1]
assert [sample.lateness_ns for sample in pacing] == [0, 0]
assert (
packets[1].envelope.timestamps.source_ns
- packets[0].envelope.timestamps.source_ns
@@ -879,6 +886,44 @@ def test_recorded_source_rejects_target_rate_for_uncapped_replay(tmp_path: Path)
)
def test_prefetched_decoder_moves_cold_decode_before_source_admission() -> None:
observed: list[DecodedFrameTiming] = []
class Decoder:
def frames(self, stop_event: Event) -> Iterator[np.ndarray]:
for index in range(4):
if stop_event.is_set():
return
yield np.full((2, 3, 3), index, dtype=np.uint8)
decoder = PrefetchedRecordedImageDecoder(
Decoder(),
capacity_frames=2,
ready_frames=2,
timing_observer=observed.append,
)
stop_event = Event()
snapshot = decoder.prepare(stop_event)
assert snapshot.buffered_frames == 2
assert snapshot.capacity_frames == 2
assert [sample.phase for sample in observed] == [
DecodePhase.PREADMISSION,
DecodePhase.PREADMISSION,
]
decoder.mark_admission_started()
frames = list(decoder.frames(stop_event))
decoder.close()
assert [int(frame[0, 0, 0]) for frame in frames] == [0, 1, 2, 3]
assert [sample.sequence for sample in observed] == [0, 1, 2, 3]
assert [sample.phase for sample in observed[2:]] == [
DecodePhase.HOT_LOOP,
DecodePhase.HOT_LOOP,
]
def test_recorded_source_rejects_timeline_mismatch(tmp_path: Path) -> None:
camera_path, timeline_path = _write_recorded_fixture(tmp_path, mismatched=True)
source = RecordedRavnoves00Source(