mirror of
https://github.com/ruvnet/RuView
synced 2026-07-26 18:01:48 +00:00
feat(adr-110): Python SyncPacket API parity with Rust (apply_to_local + interpolation)
Iter 26 — closes the ABI gap between the Python and Rust SyncPacket
decoders. Before this, Python could decode the wire but had no helpers
to apply offsets or recover per-frame mesh time; any Python-side tooling
(host scripts, replay analysers, notebooks) would have to re-implement
the math from scratch and could drift from Rust silently.
New methods on the Python SyncPacket dataclass:
local_minus_epoch_us() -> int
Signed local-vs-mesh offset. Matches Rust byte-for-byte.
apply_to_local(local_at_frame_us: int) -> int
offset = epoch_us - local_us
return local_at_frame_us + offset
Identity at local_at_frame_us == self.local_us returns epoch_us.
mesh_aligned_us_for_sequence(frame_seq: int, fps_hz: float) -> int
Sequence-based interpolation matching Rust's identical method.
Includes u32 wraparound handling via masked-subtract — verified
against Rust's iter 17 `mesh_aligned_for_sequence_handles_seq_wraparound`.
3 new Python tests (10 total in TestSyncPacketParser, all green in 0.24s):
test_apply_to_local_recovers_epoch_at_sync_point
Identity at the sync point. Also verifies local_minus_epoch_us()
matches §A0.10's measured 1,163,565 µs bench number.
test_apply_to_local_preserves_inter_frame_delta
Frame arriving 5 s after the sync on the follower's local clock
produces mesh time exactly 5 s after sync.epoch_us.
test_mesh_aligned_us_for_sequence_matches_rust
Cross-language parity with Rust's
`end_to_end_sync_decode_then_frame_mesh_recovery` (iter 20):
100 frames after sync.sequence at 20 fps = sync.epoch_us + 5 s.
Cross-checks via apply_to_local — both paths must agree.
Test count after iter 26:
Python TestSyncPacketParser: 10/10 (was 7/7)
Rust sync_packet::tests: 15/15
Combined: 25 unit tests defending the SyncPacket contract across
the two host language stacks.
Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -365,6 +365,38 @@ class TestSyncPacketParser:
|
||||
assert sync_magic == SyncPacketParser.MAGIC
|
||||
assert csi_magic != sync_magic
|
||||
|
||||
def test_apply_to_local_recovers_epoch_at_sync_point(self):
|
||||
"""ADR-110 iter 26 — Python parity with Rust's `apply_to_local`.
|
||||
At local_at_frame == sync.local_us, the recovered mesh time must
|
||||
equal sync.epoch_us exactly."""
|
||||
pkt = SyncPacketParser.parse(build_sync_packet(
|
||||
local_us=28_798_450, epoch_us=27_634_885, sequence=20,
|
||||
))
|
||||
assert pkt.apply_to_local(pkt.local_us) == pkt.epoch_us
|
||||
assert pkt.local_minus_epoch_us() == 1_163_565 # §A0.10's bench number
|
||||
|
||||
def test_apply_to_local_preserves_inter_frame_delta(self):
|
||||
"""A frame arriving 5 s after the sync packet on the follower's
|
||||
local clock must produce a mesh time exactly 5 s after sync.epoch_us."""
|
||||
pkt = SyncPacketParser.parse(build_sync_packet(
|
||||
local_us=28_798_450, epoch_us=27_634_885, sequence=20,
|
||||
))
|
||||
local_at_frame = pkt.local_us + 5_000_000
|
||||
assert pkt.apply_to_local(local_at_frame) == pkt.epoch_us + 5_000_000
|
||||
|
||||
def test_mesh_aligned_us_for_sequence_matches_rust(self):
|
||||
"""Cross-language parity with Rust's
|
||||
`end_to_end_sync_decode_then_frame_mesh_recovery` test —
|
||||
100 frames after sync.sequence at 20 fps = sync.epoch_us + 5 s."""
|
||||
pkt = SyncPacketParser.parse(build_sync_packet(
|
||||
local_us=28_798_450, epoch_us=27_634_885, sequence=20,
|
||||
))
|
||||
mesh = pkt.mesh_aligned_us_for_sequence(120, 20.0)
|
||||
assert mesh == pkt.epoch_us + 5_000_000
|
||||
# Both paths (apply_to_local + interpolation) must agree
|
||||
local_at = pkt.local_us + 5_000_000
|
||||
assert pkt.apply_to_local(local_at) == mesh
|
||||
|
||||
def test_canonical_wire_bytes_match_rust_decoder(self):
|
||||
"""ADR-110 iter 21 — cross-language wire-format conformance gate.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user