mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-28 09:03:46 +08:00
122 lines
5.8 KiB
Python
122 lines
5.8 KiB
Python
"""The real optional observer retains original pose receipts until state brackets arrive."""
|
|
|
|
from types import SimpleNamespace
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
|
|
from openpilot.cereal import messaging
|
|
from openpilot.starpilot.flm.runtime_adapter_registry import physical_observer
|
|
from openpilot.starpilot.flm.tests.test_live_calibration_paths import PhysicalSources
|
|
|
|
BASE = 20_000_000_000
|
|
|
|
|
|
def command(stamp, *, speed=20., angle=0., **changes):
|
|
values = {'identity': 'a' * 64, 'generation': 1, 'source_epoch': 'b' * 64,
|
|
'baseline_revision': 'baseline', 'controller_revision': 'controller', 'native_identity': 'native',
|
|
'state_ns': stamp, 'replay_inputs': (('vm.sR', 14.), ('vm.cF', 100000.), ('vm.cR', 100000.),
|
|
('vEgo', speed), ('steeringAngleDeg', angle))}
|
|
values.update(changes)
|
|
return SimpleNamespace(**values)
|
|
|
|
|
|
def sources(stamp):
|
|
dm = messaging.new_message('deviceMotion').deviceMotion
|
|
dm.timestamp = stamp
|
|
dm.inputsOK = dm.sensorsOK = dm.posenetOK = True
|
|
for name, xyz in (('angularVelocityDevice', (.05, .1, .02)), ('orientationNED', (.01, .02, .03))):
|
|
value = getattr(dm, name)
|
|
value.valid = True
|
|
value.x, value.y, value.z = xyz
|
|
cal = messaging.new_message('extrinsicsCalibration').extrinsicsCalibration
|
|
cal.calStatus, cal.validBlocks = 'calibrated', 50
|
|
cal.rpyCalib, cal.rpyCalibSpread = (0., 0., 0.), (0., .001, .001)
|
|
cs = SimpleNamespace(steeringPressed=False, brakePressed=False, gasPressed=False, regenBraking=False,
|
|
buttonEvents=(), canTimeout=False, canValid=True, steerFaultTemporary=False,
|
|
steerFaultPermanent=False, accFaulted=False)
|
|
return PhysicalSources({'deviceMotion': dm, 'extrinsicsCalibration': cal, 'carState': cs,
|
|
'lateralDelay': SimpleNamespace(lateralDelay=.1),
|
|
'driverMonitoringState': SimpleNamespace(noResponseForceDecel=False)}, stamp)
|
|
|
|
|
|
def fixture():
|
|
collector = SimpleNamespace(session='session', model_block_sequence=0, _calibration_generation='c' * 32,
|
|
_learning_current_native_allowed=True, ready=[], adapter=SimpleNamespace(command_domain='normalized_torque'))
|
|
collector.split = lambda *args, **kwargs: collector.ready.clear()
|
|
cp = SimpleNamespace(steerRatio=14., wheelbase=2.9, centerToFront=1.2, mass=2000.)
|
|
return collector, cp, sources(BASE)
|
|
|
|
|
|
def observe(collector, cp, sm, now):
|
|
with mock.patch('openpilot.starpilot.flm.runtime_adapter_registry.time.monotonic_ns', return_value=now), \
|
|
mock.patch('openpilot.starpilot.flm.runtime_adapter_registry.time.clock_gettime_ns', return_value=now):
|
|
return physical_observer(sm, cp, collector, now)
|
|
|
|
|
|
def add_state(collector, sm, stamp, **changes):
|
|
collector.ready.append((command(stamp, **changes), SimpleNamespace(native_allowed=True, apply_ns=stamp)))
|
|
sm.logMonoTime['carState'] = stamp
|
|
|
|
|
|
def test_actual_observer_retries_original_pose_after_later_right_bracket():
|
|
collector, cp, sm = fixture()
|
|
add_state(collector, sm, BASE - 10_000_000, speed=20., angle=0.)
|
|
assert observe(collector, cp, sm, BASE) is None
|
|
add_state(collector, sm, BASE + 10_000_000, speed=22., angle=2.)
|
|
sm.recv_time['deviceMotion'] = (BASE + 9_000_000) / 1e9 # A poll cannot renew the original receipt.
|
|
result = observe(collector, cp, sm, BASE + 10_000_000)
|
|
assert result is not None
|
|
assert result.mono_ns == result.source_boot_ns == result.received_ns == BASE
|
|
assert result.clock_offset_ns == result.clock_uncertainty_ns == 0
|
|
assert result.speed_mps == 21. and result.steering_angle_deg == 1.
|
|
assert result.yaw_rate == pytest.approx(.02)
|
|
assert observe(collector, cp, sm, BASE + 10_000_001) is None
|
|
|
|
|
|
@pytest.mark.parametrize('transition', ['native', 'source', 'reset', 'baseline', 'calibration', 'vm', 'override', 'fault', 'invalid', 'expired'])
|
|
def test_pending_pose_cannot_cross_health_or_identity_transition(transition):
|
|
collector, cp, sm = fixture()
|
|
add_state(collector, sm, BASE - 10_000_000)
|
|
assert observe(collector, cp, sm, BASE) is None
|
|
changes = {}
|
|
if transition == 'native':
|
|
changes['native_identity'] = 'new-native'
|
|
elif transition == 'source':
|
|
changes['source_epoch'] = 'd' * 64
|
|
elif transition == 'reset':
|
|
collector.model_block_sequence += 1
|
|
elif transition == 'baseline':
|
|
changes['baseline_revision'] = 'changed'
|
|
elif transition == 'calibration':
|
|
sm['extrinsicsCalibration'].rpyCalib = (0., .001, .001)
|
|
elif transition == 'vm':
|
|
changes['replay_inputs'] = (('vm.sR', 16.), ('vm.cF', 100000.), ('vm.cR', 100000.), ('vEgo', 20.), ('steeringAngleDeg', 0.))
|
|
elif transition == 'override':
|
|
sm['carState'].brakePressed = True
|
|
elif transition == 'fault':
|
|
sm['carState'].canValid = False
|
|
elif transition == 'invalid':
|
|
sm['deviceMotion'].inputsOK = False
|
|
add_state(collector, sm, BASE + 10_000_000, **changes)
|
|
now = BASE + (100_000_001 if transition == 'expired' else 10_000_000)
|
|
assert observe(collector, cp, sm, now) is None
|
|
assert not collector._physical_pose_queue
|
|
|
|
|
|
def test_newer_pose_does_not_replace_queued_original_or_its_receipt():
|
|
collector, cp, sm = fixture()
|
|
add_state(collector, sm, BASE - 10_000_000)
|
|
assert observe(collector, cp, sm, BASE) is None
|
|
sm['deviceMotion'].timestamp = BASE + 50_000_000
|
|
sm['deviceMotion'].angularVelocityDevice.z = .08
|
|
sm.recv_time['deviceMotion'] = (BASE + 50_000_000) / 1e9
|
|
add_state(collector, sm, BASE + 10_000_000)
|
|
result = observe(collector, cp, sm, BASE + 50_000_000)
|
|
assert result is not None and result.mono_ns == result.received_ns == BASE
|
|
assert result.yaw_rate == pytest.approx(.02)
|
|
add_state(collector, sm, BASE + 60_000_000)
|
|
result = observe(collector, cp, sm, BASE + 60_000_000)
|
|
assert result is not None and result.mono_ns == result.received_ns == BASE + 50_000_000
|
|
assert result.yaw_rate == pytest.approx(.08)
|