#!/usr/bin/env python3 """Pure numerical contracts for matched H3 feedback and packet semantics.""" from pathlib import Path import sys import unittest import numpy as np CODE_ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(CODE_ROOT)) from core.feedback_protocol import ( # noqa: E402 ForwardPacket, MapPolicy, MapRegistry, MapSnapshot, MappingKind, PacketRejectReason, PacketState, ReturnPacket, map_return_feedback, normalized_actual_power_mismatch, ) from core.network_emulator import ( # noqa: E402 DeterministicChannel, NetworkTraceEntry, PacketReceiver, ) class FeedbackProtocolTest(unittest.TestCase): def make_packet(self, seq=3, map_id=1): wrench = np.array([1.0, -2.0, 0.5, 0.2, -0.3, 0.1]) Js = np.arange(42, dtype=float).reshape(6, 7) / 50.0 return ReturnPacket( seq=seq, source_index=10 + seq, source_time=0.02 * seq, echoed_map_id=map_id, residual=np.linspace(-1.0, 1.0, 7), wrench=wrench, js_t_wrench=Js.T @ wrench, qd_slave_actual=np.linspace(0.1, 0.7, 7), ) def test_matched_conditions_share_one_input_hash(self): registry = MapRegistry() A = np.eye(7) registry.add(MapSnapshot(1, 2, 0.04, A)) packet = self.make_packet() Jm = np.arange(42, dtype=float).reshape(6, 7) / 70.0 differential = map_return_feedback( kind=MappingKind.MATCHED_DIFFERENTIAL_WRENCH, packet=packet, master_jacobian=Jm, maps=registry, ) direct = map_return_feedback( kind=MappingKind.DIRECT_MASTER_JACOBIAN, packet=packet, master_jacobian=Jm, maps=registry, ) self.assertEqual( differential.matched_input_hash, direct.matched_input_hash ) np.testing.assert_allclose( differential.tau_master_raw, A.T @ packet.js_t_wrench ) np.testing.assert_allclose( direct.tau_master_raw, Jm.T @ packet.wrench ) def test_source_stamped_policy_uses_echoed_map(self): registry = MapRegistry() registry.add(MapSnapshot(1, 0, 0.0, np.eye(7))) registry.add(MapSnapshot(2, 1, 0.02, 2.0 * np.eye(7))) packet = self.make_packet(map_id=1) Jm = np.zeros((6, 7)) source = map_return_feedback( kind=MappingKind.MATCHED_DIFFERENTIAL_WRENCH, packet=packet, master_jacobian=Jm, maps=registry, map_policy=MapPolicy.SOURCE_STAMPED, ) current = map_return_feedback( kind=MappingKind.MATCHED_DIFFERENTIAL_WRENCH, packet=packet, master_jacobian=Jm, maps=registry, map_policy=MapPolicy.CURRENT, ) self.assertEqual(source.selected_map_id, 1) self.assertEqual(current.selected_map_id, 2) np.testing.assert_allclose( current.tau_master_raw, 2.0 * source.tau_master_raw ) def test_power_endpoint_is_zero_for_exact_source_alignment(self): tau_s = np.array([[1.0, 2.0], [-1.0, 0.5]]) qd_s = np.array([[0.3, -0.1], [0.2, 0.4]]) tau_m = tau_s.copy() qd_m = qd_s.copy() mismatch = normalized_actual_power_mismatch( tau_m, qd_m, tau_s, qd_s, np.array([0.01, 0.02]) ) self.assertAlmostEqual(mismatch, 0.0) def test_duplicate_out_of_order_and_timeout_are_explicit(self): packets = [ ForwardPacket(i, i, i * 0.01, i, np.zeros(2), np.zeros(2)) for i in range(3) ] # seq 0 arrives after seq 1, and seq 1 is duplicated. channel = DeterministicChannel( [ NetworkTraceEntry(0, 0.04), NetworkTraceEntry(1, 0.01, duplicate=True), NetworkTraceEntry(2, 0.02), ] ) for packet in packets: channel.send(packet, packet.source_time) receiver = PacketReceiver(timeout_s=0.05) receptions = [] for delivery in channel.poll(0.05): receptions.append(receiver.accept(delivery)) self.assertTrue(receptions[0].accepted) self.assertIn( PacketRejectReason.DUPLICATE_OR_STALE, [item.reason for item in receptions if not item.accepted], ) held = receiver.sample(0.051) self.assertIn(held.state, (PacketState.ACTIVE, PacketState.RECOVERING)) self.assertEqual(receiver.sample(0.2).state, PacketState.TIMED_OUT) if __name__ == "__main__": unittest.main()