Files
StarPilot/openpilot/starpilot/flm/tests/test_event_physical.py
T
2026-10-18 20:17:20 -05:00

86 lines
4.2 KiB
Python

"""Independent whole-episode response gates, separate from legacy sign blocks."""
from dataclasses import replace
import math
from openpilot.starpilot.flm.event_physical import fit_event_physical
from openpilot.starpilot.flm.physical_response import PhysicalRow, fit_physical
BASE = 1_000_000_000
def episodes():
result = []
for episode in range(8):
for sample in range(80):
stamp = BASE + episode * 5_000_000_000 + sample * 50_000_000
command = .15 * math.sin(2 * math.pi * (sample + .25) / 40)
result.append(PhysicalRow(str(episode), stamp, stamp + 50_000_000,
command, 1.98 * command + .002, 'physical-context', 'left' if command > 0 else 'right', True, False, False))
return tuple(result)
def test_whole_mixed_sign_episodes_are_not_split_into_fake_independent_turns():
rows = episodes()
now = rows[-1].response_ns
legacy = fit_physical('a' * 64, 'normalized_torque', 'physical-context', rows,
lag_ns=50_000_000, min_excitation=.05, max_rmse=.05, now_ns=now)
assert legacy is None # Legacy one-sign block policy remains unchanged.
model = fit_event_physical('a' * 64, 'physical-context', rows, lag_ns=50_000_000, now_ns=now)
assert model is not None and abs(model.slope - 1.98) < 1e-12
assert model.train_blocks == ('0', '1', '2', '3')
assert model.heldout_blocks == ('4', '5', '6', '7')
assert model.heldout_rmse < 1e-12
assert len({row.block_id for row in rows}) == 8
def test_whole_episode_model_preserves_fault_clip_context_and_latency_gates():
rows = episodes()
for changed in (
replace(rows[25], saturated=True), replace(rows[25], healthy=False),
replace(rows[25], overridden=True), replace(rows[25], context='foreign'),
replace(rows[25], response_ns=rows[25].response_ns + 30_000_001),
):
altered = rows[:25] + (changed,) + rows[26:]
assert fit_event_physical('a' * 64, 'physical-context', altered, lag_ns=50_000_000, now_ns=rows[-1].response_ns) is None
def test_one_whole_episode_cannot_be_relabelled_as_eight_independent_episodes():
rows = tuple(replace(row, block_id='same-turn') for row in episodes())
assert fit_event_physical('a' * 64, 'physical-context', rows, lag_ns=50_000_000, now_ns=rows[-1].response_ns) is None
def test_local_model_does_not_extrapolate_or_accept_quiet_unidentified_input():
rows = episodes()
quiet = tuple(replace(row, command=row.command / 100, physical_response=row.physical_response / 100) for row in rows)
assert fit_event_physical('a' * 64, 'physical-context', quiet, lag_ns=50_000_000, now_ns=quiet[-1].response_ns) is None
unsupported = tuple(replace(row, command=row.command * 2, physical_response=row.physical_response * 2)
if row.block_id in ('4', '5', '6', '7') else row for row in rows)
assert fit_event_physical('a' * 64, 'physical-context', unsupported, lag_ns=50_000_000, now_ns=unsupported[-1].response_ns) is None
def test_zero_and_negative_zero_do_not_supply_negative_direction_support():
for zero in (0.0, -0.0):
for change_train in (True, False):
rows = tuple(replace(row, command=zero, physical_response=.002, direction='right')
if row.command < 0 and (change_train or row.block_id in ('4', '5', '6', '7')) else row
for row in episodes())
assert fit_event_physical('a' * 64, 'physical-context', rows,
lag_ns=50_000_000, now_ns=rows[-1].response_ns) is None
def test_tiny_positive_has_no_negative_direction_support_and_no_epsilon_is_used():
for tiny, expected in ((1e-15, False), (-1e-15, True)):
rows = tuple(replace(row, command=tiny, physical_response=1.98 * tiny + .002,
direction='left' if tiny > 0 else 'right') if row.command < 0 else row
for row in episodes())
model = fit_event_physical('a' * 64, 'physical-context', rows,
lag_ns=50_000_000, now_ns=rows[-1].response_ns)
assert (model is not None) is expected
def test_true_bilateral_original_control_still_qualifies():
rows = episodes()
assert any(row.command > 0 for row in rows) and any(row.command < 0 for row in rows)
assert fit_event_physical('a' * 64, 'physical-context', rows,
lag_ns=50_000_000, now_ns=rows[-1].response_ns) is not None