Restore Volvo safety coverage and correct new test fixtures

This commit is contained in:
Dom
2026-10-17 19:20:08 -05:00
parent 7357d6a54c
commit ec84548e85
14 changed files with 426 additions and 22 deletions
@@ -7,6 +7,9 @@ from opendbc.car.fingerprints import FW_VERSIONS
from opendbc.car.fw_versions import FW_QUERY_CONFIGS
from opendbc.car.interfaces import CarInterfaceBase, get_interface_attr
from opendbc.car.values import PLATFORMS
from opendbc.car.volvo.cma_carcontroller import CarControllerCMA
from opendbc.car.volvo.cma_carstate import CarStateCMA
from opendbc.car.volvo.values import CAR as VOLVO
from opendbc.testing import Fuzzy, fuzzy_test
ALL_ECUS = tuple(sorted({ecu for ecus in FW_VERSIONS.values() for ecu in ecus} |
@@ -112,8 +115,12 @@ def _make_optional_hardware_test(car_name):
self.assertEqual(params.carFingerprint, car_name)
self.assertEqual(fingerprint, gen_empty_fingerprint())
interface = interface_type(params)
self.assertIsInstance(interface.CS, interface_type.CarState)
self.assertIsInstance(interface.CC, interface_type.CarController)
if car_name in (VOLVO.VOLVO_XC40_RECHARGE, VOLVO.VOLVO_S60_RECHARGE, VOLVO.POLESTAR_2):
self.assertIsInstance(interface.CS, CarStateCMA)
self.assertIsInstance(interface.CC, CarControllerCMA)
else:
self.assertIsInstance(interface.CS, interface_type.CarState)
self.assertIsInstance(interface.CC, interface_type.CarController)
return test
@@ -0,0 +1,373 @@
"""Native CMA/SPA contracts, also collected by the safety unittest coverage gate.
The fixtures use the same DBCs and checksum encoders as the existing Volvo car
integration tests. Permissions come from physical RX, never a forced active flag.
"""
import unittest
from opendbc.can import CANPacker
from opendbc.car.volvo import cma_can
from opendbc.car.volvo.helpers import (
checksum_lca_2_message, checksum_2_0x69_message, checksum_lca_5_message,
checksum_1_pscm_related_message, checksum_2_pscm_related_message,
)
from opendbc.safety.tests.libsafety import libsafety_py
RELAY_BUSES = {0x58: 2, 0x57: 2, 0x69: 2, 0x90: 2, 0x67: 2, 0x97: 2, 0x92: 2, 0x16: 0, 0x17: 0}
def checksummed(message):
address, payload, bus = message
data = bytearray(payload)
if address == 0x69:
data[6] = checksum_lca_2_message(data[0], data[5])
data[2] = checksum_2_0x69_message(data[0], data[1], data[3], data[4])
elif address == 0x67:
data[2] = checksum_lca_5_message(data[0], data[1], data[3], data[4], data[5])
elif address == 0x17:
data[0] = checksum_1_pscm_related_message(data[1], data[2])
data[3] = checksum_2_pscm_related_message(data[2])
return address, bytes(data), bus
class TestVolvoCMASafety(unittest.TestCase):
PARAM = 0
def setUp(self):
self.safety = libsafety_py.libsafety
self.mid = CANPacker('volvo_mid_1')
self.pt = CANPacker('volvo_front_1_spa' if self.PARAM else 'volvo_front_1_cma')
self.reset()
def reset(self):
self.assertEqual(self.safety.set_safety_hooks(36, self.PARAM), 0)
self.safety.init_tests()
self.safety.set_timer(10000)
def rx(self, message):
address, data, bus = message
return self.safety.safety_rx_hook(libsafety_py.make_CANPacket(address, bus, data))
def tx(self, message):
address, data, bus = message
return self.safety.safety_tx_hook(libsafety_py.make_CANPacket(address, bus, data))
def sources(self, cruise=True, gear=3, sample=0, parity=0):
definitions: list[tuple[str, int, dict[str, float]]] = [
('SPEED', 0, {'SPEED': 3000}), ('SPEED_2', 0, {}), ('GEAR_POSITION', 0, {'GEAR_POSITION': gear}),
('LCA', 0, {}), ('LCA_2', 0, {'COUNTER_1': (sample * 2 + parity) % 16, 'COUNTER_2': sample * 4 % 16}),
('LCA_3', 0, {}), ('LCA_4', 0, {}), ('LCA_5', 0, {}), ('LCA_6', 0, {}), ('LCA_7', 0, {}),
('PSCM', 2, {}), ('DRIVER_INPUT', 2, {}), ('EGSM', 2, {}),
('PSCM_RELATED', 2, {'NEW_SIGNAL_2': 8}), ('SAS', 2, {}),
]
messages = [self.mid.make_can_msg(name, bus, values) for name, bus, values in definitions]
messages.append(self.pt.make_can_msg('ECM_1', 1, {'ACCELERATOR_PEDAL_POS': 0 if self.PARAM else 20}))
cruise_signal = 'CRUISE_CONTROL_SPA_ENABLED' if self.PARAM else 'CRUISE_CONTROL_ENABLED'
messages.append(self.pt.make_can_msg('BUS1_CRUISE_CONTROL', 1, {cruise_signal: int(cruise)}))
return [checksummed(message) for message in messages]
def feed(self, **kwargs):
incoming = self.sources(**kwargs)
for message in incoming:
self.assertTrue(self.rx(message), message)
self.safety.safety_tick()
self.assertTrue(self.safety.safety_config_valid())
self.assertEqual(bool(self.safety.get_controls_allowed()), kwargs.get('cruise', True))
return {address: (address, payload, bus) for address, payload, bus in incoming}
def relay(self, source):
address, data, _ = source
return address, data, RELAY_BUSES[address]
def lca_command(self, positive=614, negative=-614):
return cma_can.create_lca_message(self.mid, True, 0, {
'LCA_STEER': 0, 'OPAQUE_BYTE_0': 0, 'OPAQUE_BYTE_2': 0, 'OPAQUE_BYTE_7': 0,
}, authority_pos=positive, authority_neg=negative)
def angle_command(self, raw):
# LCA_5_STEER is signed 15-bit, 0.05596 degrees/count, in bytes 6-7.
data = bytearray(8)
data[6:8] = (raw & 0x7FFF).to_bytes(2, 'big')
return checksummed((0x67, data, 2))
def test_rx_sensor_scales_and_profile_pedals(self):
self.feed()
self.assertAlmostEqual(self.safety.get_vehicle_speed_max(), 3000 * .003977, places=5)
for raw in (-100, 100):
for _ in range(6):
self.assertTrue(self.rx(self.mid.make_can_msg('PSCM', 2, {'PSCM_ANGLE_SENSOR': raw * .05596})))
self.assertEqual(self.safety.get_angle_meas_min(), raw)
self.assertEqual(self.safety.get_angle_meas_max(), raw)
for torque in (-10, 10):
for _ in range(6):
self.assertTrue(self.rx(self.mid.make_can_msg('DRIVER_INPUT', 2, {'STEERING_DRIVER_INPUT': torque})))
self.assertEqual(self.safety.get_torque_driver_min(), torque)
self.assertEqual(self.safety.get_torque_driver_max(), torque)
threshold = 1 if self.PARAM else 21
step = 1 / 256 if self.PARAM else 1
for value, pressed in ((threshold + step, True), (threshold, False)):
self.assertTrue(self.rx(self.pt.make_can_msg('ECM_1', 1, {'ACCELERATOR_PEDAL_POS': value})))
self.assertEqual(bool(self.safety.get_gas_pressed_prev()), pressed)
for sample, pressed in enumerate((True, False), 1):
message = self.mid.make_can_msg('LCA_2', 0, {'COUNTER_1': sample * 2, 'COUNTER_2': sample * 4,
'BRAKE_PEDAL_PRESSED_B': int(pressed)})
self.assertTrue(self.rx(checksummed(message)))
self.assertEqual(bool(self.safety.get_brake_pressed_prev()), pressed)
self.assertFalse(self.safety.get_controls_allowed())
def test_stock_cruise_withdrawal_and_reengagement(self):
for enabled in (False, True, False, True):
signal = 'CRUISE_CONTROL_SPA_ENABLED' if self.PARAM else 'CRUISE_CONTROL_ENABLED'
self.assertTrue(self.rx(self.pt.make_can_msg('BUS1_CRUISE_CONTROL', 1, {signal: int(enabled)})))
self.assertEqual(bool(self.safety.get_controls_allowed()), enabled)
if not self.PARAM:
self.assertTrue(self.rx(self.pt.make_can_msg('BUS1_CRUISE_CONTROL', 1, {})))
self.assertTrue(self.rx(self.pt.make_can_msg('BUS1_CRUISE_CONTROL', 1, {'CRUISE_CONTROL_ENABLED_IDLE_TRAFFIC': 1})))
self.assertTrue(self.safety.get_controls_allowed())
def test_stock_only_when_cruise_withdrawn_and_original_age_expires(self):
incoming = self.feed(cruise=False)
for address in RELAY_BUSES:
message = self.relay(incoming[address])
self.assertTrue(self.tx(message))
data = bytearray(message[1])
data[0] ^= 1
self.assertFalse(self.tx((address, data, message[2])))
self.safety.set_timer(110000)
for address in RELAY_BUSES:
self.assertTrue(self.tx(self.relay(incoming[address])))
self.safety.set_timer(110001)
for address in RELAY_BUSES:
self.assertFalse(self.tx(self.relay(incoming[address])))
def test_allowlist_pins_bus_and_length(self):
incoming = self.feed(cruise=False)
for address, expected_bus in RELAY_BUSES.items():
for bus in range(8):
self.assertEqual(bool(self.tx((address, incoming[address][1], bus))), bus == expected_bus)
for length in range(8):
self.assertFalse(self.tx((address, incoming[address][1][:length], bus)))
for address in (0x15, 0x55, 0x60, 0x68, 0x80, 0x45):
for bus in range(3):
self.assertFalse(self.tx((address, bytes(8), bus)))
def test_active_native_source_bound_commands(self):
incoming = self.feed()
for address in RELAY_BUSES:
self.assertTrue(self.tx(self.relay(incoming[address])))
self.assertTrue(self.tx(self.lca_command()))
self.assertTrue(self.tx(self.angle_command(1)))
# These two hands-on relay payloads are emitted by create_pscm_message.
for hands_a, hands_b in ((195, 186), (249, 154)):
message = self.mid.make_can_msg('PSCM', 0, {'HANDS_ON_STEERING_WHEEL_A': hands_a,
'HANDS_ON_STEERING_WHEEL_B': hands_b})
self.assertTrue(self.tx(message))
# Counter replication may advance, but its observed echo must be preserved or zeroed.
for counter in range(15):
message = self.mid.make_can_msg('PSCM_RELATED', 0, {'NEW_SIGNAL_2': 8,
'SIG1_BYTE_1_HI_NIBBLE': counter, 'SIG1_REPLICA_BYTE_2_LO_NIBLE': counter})
self.assertTrue(self.tx(checksummed(message)))
def test_opaque_stock_fields_cannot_be_forged_with_recomputed_checksums(self):
incoming = self.feed()
# Start from accepted active templates, so an opaque mutation cannot be
# rejected merely because the stock frame lacks the fixed active fields.
active = {
0x58: self.lca_command(),
0x57: cma_can.create_lca_3_message(self.mid, True, 0, {'SPEED_A': 0, 'SPEED_B': 0, 'NEW_SIGNAL_9': 0}, 0),
0x69: checksummed(self.mid.make_can_msg('LCA_2', 2, {'BYTE_0': 24, 'PILOT_ASSIST_ENGAGED': 1, 'NEW_SIGNAL_3': 3})),
0x90: self.mid.make_can_msg('LCA_4', 2, {'LCA_ENABLE': 3}),
0x67: self.angle_command(1),
0x16: self.mid.make_can_msg('PSCM', 0, {'HANDS_ON_STEERING_WHEEL_A': 195, 'HANDS_ON_STEERING_WHEEL_B': 186}),
0x17: checksummed(self.mid.make_can_msg('PSCM_RELATED', 0, {'NEW_SIGNAL_2': 8,
'SIG1_BYTE_1_HI_NIBBLE': 1, 'SIG1_REPLICA_BYTE_2_LO_NIBLE': 1})),
}
for address, byte, mask in ((0x58, 6, 1), (0x57, 2, 1), (0x69, 5, 0x40), (0x90, 4, 1),
(0x67, 0, 1), (0x97, 0, 1), (0x92, 0, 1), (0x16, 7, 1), (0x17, 6, 1)):
with self.subTest(address=address):
baseline = active.get(address, self.relay(incoming[address]))
self.assertTrue(self.tx(baseline))
data = bytearray(baseline[1])
data[byte] ^= mask
self.assertFalse(self.tx(checksummed((address, data, RELAY_BUSES[address]))))
def test_torque_authority_limits(self):
self.feed()
for positive, negative, allowed in ((0, 0, True), (614, -614, True), (615, -614, False),
(614, -615, False), (-1, 0, False), (0, 1, False)):
self.assertEqual(bool(self.tx(self.lca_command(positive, negative))), allowed)
def test_angle_absolute_and_rate_limits(self):
for raw in (-9651, 9651):
self.reset()
self.feed()
self.safety.set_desired_angle_last(raw)
self.assertFalse(self.tx(self.angle_command(raw)))
for raw in (-1, 1):
self.reset()
self.feed()
self.assertTrue(self.tx(self.angle_command(raw)))
self.reset()
self.feed()
self.assertFalse(self.tx(self.angle_command(1000)))
def test_pscm_relay_cannot_invent_measured_angle(self):
self.feed()
for raw in (-55, -54, 54, 55):
self.assertTrue(self.rx(self.mid.make_can_msg('PSCM', 2, {'PSCM_ANGLE_SENSOR': raw * .05596})))
# Keep the source-bound angle intact and isolate the measurement guard.
# The standard measurement fixture does not grant control permission.
self.safety.set_angle_meas(0, 0)
message = self.mid.make_can_msg('PSCM', 0, {'PSCM_ANGLE_SENSOR': raw * .05596,
'HANDS_ON_STEERING_WHEEL_A': 195, 'HANDS_ON_STEERING_WHEEL_B': 186})
self.assertEqual(bool(self.tx(message)), abs(raw) <= 54)
for hands_a, hands_b in ((195, 154), (249, 186), (0, 1)):
self.assertFalse(self.tx(self.mid.make_can_msg('PSCM', 0, {
'HANDS_ON_STEERING_WHEEL_A': hands_a, 'HANDS_ON_STEERING_WHEEL_B': hands_b})))
def test_gear_is_physical_current_and_drive_only(self):
for gear in range(8):
with self.subTest(gear=gear):
self.reset()
incoming = self.feed(gear=gear)
self.assertEqual(bool(self.tx(self.lca_command())), gear in (3, 4))
self.assertTrue(self.tx(self.relay(incoming[0x58])))
self.reset()
self.feed()
self.safety.set_timer(110001)
for message in self.sources(sample=1):
if message[0] != 0x80:
self.assertTrue(self.rx(message))
self.assertTrue(self.safety.get_controls_allowed())
self.assertFalse(self.tx(self.lca_command()))
def test_each_known_checksum_rejects_corrupt_rx_and_tx(self):
for address, index in ((0x69, 6), (0x69, 2), (0x67, 2), (0x17, 0), (0x17, 3)):
with self.subTest(address=address, index=index):
self.reset()
incoming = self.feed()
data = bytearray(incoming[address][1])
data[index] ^= 1
self.assertFalse(self.tx((address, data, RELAY_BUSES[address])))
self.assertFalse(self.rx((address, data, incoming[address][2])))
self.assertFalse(self.safety.get_controls_allowed())
self.assertFalse(self.tx(self.lca_command()))
self.assertTrue(self.tx(self.relay(incoming[0x58])))
self.safety.safety_tick()
self.assertFalse(self.safety.safety_config_valid())
def test_unknown_pscm_counter_domains_rejected_despite_correct_checksum(self):
for first, second in ((0x10, 0x80), (0xF0, 0x8F), (8, 0x80), (0, 0)):
self.reset()
self.feed()
data = bytearray(8)
data[1:3] = bytes((first, second))
message = checksummed((0x17, data, 0))
self.assertFalse(self.tx(message))
self.assertFalse(self.rx((message[0], message[1], 2)))
def test_documented_lca2_counter_wraps_but_frozen_counter_expires(self):
for parity in (0, 1):
self.reset()
self.feed(parity=parity)
for sample in range(1, 25):
self.safety.set_timer(10000 + sample * 20000)
self.feed(sample=sample, parity=parity)
frozen = next(message for message in self.sources(sample=24, parity=parity) if message[0] == 0x69)
for sample in range(25, 35):
self.safety.set_timer(10000 + sample * 20000)
for message in self.sources(sample=sample, parity=parity):
if message[0] == 0x69:
self.rx(frozen) # The shared wrong-counter tolerance is eventually exhausted.
else:
self.assertTrue(self.rx(message))
self.safety.safety_tick()
self.assertFalse(self.safety.safety_config_valid())
self.assertFalse(self.safety.get_controls_allowed())
def test_lca2_phase_must_come_from_checksum_valid_physical_rx(self):
for index, mask in ((1, 1), (5, 1), (5, 4)):
self.reset()
incoming = self.feed()
data = bytearray(incoming[0x69][1])
data[index] ^= mask
self.assertFalse(self.rx(checksummed((0x69, data, 0))))
self.assertFalse(self.safety.get_controls_allowed())
self.reset()
message = next(message for message in self.sources() if message[0] == 0x69)
data = bytearray(message[1])
data[6] ^= 1
self.assertFalse(self.rx((0x69, data, 0)))
self.assertFalse(self.tx(self.relay(message)))
self.feed(parity=1)
def test_existing_lca2_special_case_checksum(self):
message = self.mid.make_can_msg('LCA_2', 0, {'BRAKE_PEDAL_PRESSED_A': 0})
self.assertEqual(message[1][0], 0)
self.assertEqual(message[1][5], 128)
self.assertEqual(checksummed(message)[1][6], 0)
self.assertTrue(self.rx(checksummed(message)))
def test_every_required_receiver_needed_after_mode_reset(self):
for missing in range(17):
self.reset()
incoming = self.feed()
self.assertTrue(self.tx(self.lca_command()))
self.reset()
self.assertFalse(self.tx(self.relay(incoming[0x58])))
messages = self.sources()
for index, message in enumerate(messages):
if index != missing:
self.assertTrue(self.rx(message))
self.assertFalse(self.safety.safety_config_valid())
self.assertFalse(self.tx(self.lca_command()))
self.assertTrue(self.rx(messages[missing]))
self.assertTrue(self.safety.safety_config_valid())
self.assertTrue(self.tx(self.lca_command()))
def test_required_receiver_loss_rejects_modified_commands(self):
for omitted in range(17):
self.reset()
self.feed()
for sample in range(1, 57):
self.safety.set_timer(10000 + sample * 20000)
for index, message in enumerate(self.sources(sample=sample)):
if index != omitted:
self.assertTrue(self.rx(message))
self.safety.safety_tick()
self.assertFalse(self.safety.safety_config_valid())
self.assertFalse(self.safety.get_controls_allowed())
self.assertFalse(self.tx(self.lca_command()))
def test_stock_relay_history_does_not_cross_mode_reset(self):
incoming = self.feed(cruise=False)
first = incoming[0x97]
for value in range(1, 5):
message = (0x97, bytes([value]) + bytes(7), 0)
self.assertTrue(self.rx(message))
self.assertTrue(self.tx(self.relay(message)))
self.assertFalse(self.tx(self.relay(first)))
self.reset()
self.assertFalse(self.tx(self.relay(message)))
def test_relay_malfunction_and_forwarding(self):
self.feed()
for bus in (0, 2):
for address in (*RELAY_BUSES, 0x60):
blocked = RELAY_BUSES.get(address) == 2 - bus
self.assertEqual(self.safety.safety_fwd_hook(bus, address), -1 if blocked else 2 - bus)
for address, bus in RELAY_BUSES.items():
self.reset()
self.assertTrue(self.rx((address, bytes(8), bus)))
self.assertTrue(self.safety.get_relay_malfunction())
self.assertFalse(self.tx(self.lca_command()))
self.assertEqual(self.safety.safety_fwd_hook(0, 0x60), -1)
class TestVolvoSPASafety(TestVolvoCMASafety):
PARAM = 1
if __name__ == '__main__':
unittest.main()
@@ -49,9 +49,9 @@ def test_recorded_contract_is_exact_original_comparison_for_all_signs():
def test_bad_support_versions_and_unversioned_optin_fail_closed():
for position in range(3):
for bad in (math.nan, math.inf, True):
values = [-17, -18, -18]
values: list[float] = [-17, -18, -18]
values[position] = bad
assert not debt_is_admitted(*values, STATE_ALIGNED_DEBT)
assert not debt_is_admitted(values[0], values[1], values[2], STATE_ALIGNED_DEBT)
rejects(lambda: clipping_quality('unknown'))
rejects(lambda: event_clipping_quality({'event_version': 2, 'event_clipping_quality': STATE_ALIGNED_DEBT}))
rejects(lambda: event_clipping_quality({'event_version': 3, 'event_clipping_quality': 'unknown'}))
@@ -50,6 +50,7 @@ def test_real_driver_measured_unwind_relief_is_weaker_and_final_only(platform):
EncoderBranch(quality_contract=STATE_ALIGNED_DEBT).step(event, candidate)
branch = EncoderBranch(quality_contract=STATE_ALIGNED_RELIEF)
assert branch.step(event, candidate) is not None
assert branch.previous is not None
assert 0 < branch.previous <= witness.final_raw
# Actual recorded no-clipping status cannot be bypassed by relief.
with pytest.raises(ValueError, match='limiter/support'):
@@ -20,7 +20,7 @@ from openpilot.starpilot.flm.tests.test_event_fd_boundary import engine
def test_exact_unwind_witnesses_and_partial_order_negatives():
assert native_relief_is_admitted(-76, -76, -75, -80, -79, -79, 4, 3, 4)
assert native_relief_is_admitted(-19, -19, -18, -28, -28, -28, 9, 9, 10)
valid = [-76, -76, -75, -80, -79, -79, 4, 3, 4]
valid: list[float] = [-76, -76, -75, -80, -79, -79, 4, 3, 4]
for index, value in ((2, -77), (2, 0), (2, 1), (5, -81), (5, 79), (6, -4), (7, -3), (8, -4), (8, 0), (1, float('nan'))):
changed = valid.copy()
changed[index] = value
@@ -45,10 +45,13 @@ def payload():
def plan_for(restored):
model = fitted_model()
base = primitive_plan(model)
values = {**asdict(base), 'version': RESTORE_ACTIVATION_VERSION,
'baseline_revision': restored.selected_baseline_revision, 'source_epoch': restored.source_epoch,
'cutoff_ns': 2_005_000_000, 'windows': ((2_000_000_000, 2_020_000_000, 2_010_000_000, 2_010_000_000),)}
return WholeMapRestorePlan(**values, restore_envelope_sha256=restore_envelope_digest(restored)).validate()
return WholeMapRestorePlan(
version=RESTORE_ACTIVATION_VERSION, identity=base.identity,
baseline_revision=restored.selected_baseline_revision, source_epoch=restored.source_epoch, physical_context=base.physical_context,
calibration_train_blocks=base.calibration_train_blocks, calibration_heldout_blocks=base.calibration_heldout_blocks,
cutoff_ns=2_005_000_000, stage_blocks=base.stage_blocks,
windows=((2_000_000_000, 2_020_000_000, 2_010_000_000, 2_010_000_000),),
restore_envelope_sha256=restore_envelope_digest(restored)).validate()
def test_restore_only_payload_has_no_shadow_or_operation_fallback():
@@ -20,11 +20,11 @@ def test_response_after_command_is_fresh_only_at_a_trusted_later_decision():
@pytest.mark.parametrize('index', (0, 1, 2))
def test_each_original_stream_must_be_nonfuture_and_within_100ms(index):
valid = [NOW - 2_000_000, NOW - 1_000_000, NOW]
valid: list[int | float] = [NOW - 2_000_000, NOW - 1_000_000, NOW]
for stamp in (NOW + 1, NOW - 101_000_000, 0, -1, True, float(NOW)):
values = valid.copy()
values[index] = stamp
assert not _fresh_current_physical(*values, NOW)
assert not _fresh_current_physical(values[0], values[1], values[2], NOW)
assert not _fresh_current_physical(NOW, NOW - 1, NOW, NOW)
@@ -1,5 +1,6 @@
"""Exact original-frame join; no synthetic replay qualification."""
from types import SimpleNamespace
from typing import TypedDict
from unittest import mock
import pytest
@@ -8,6 +9,20 @@ from openpilot.starpilot.flm.event_raw_fit import _control_frame_index, fit_even
from openpilot.starpilot.flm.tests.test_event_activation import fitted_model
class StageSample(TypedDict):
control_ns: int
apply_ns: int
mono_ns: int
control: dict[str, int]
output: dict[str, int]
physical: dict[str, float]
class StageEvent(TypedDict):
block_id: str
samples: tuple[StageSample, ...]
def test_index_preserves_original_frame_identity_and_missing_timestamp():
first = SimpleNamespace(control_ns=10, desired_physical_accel=.02)
second = SimpleNamespace(control_ns=20, desired_physical_accel=.03)
@@ -29,7 +44,7 @@ def test_actual_fit_missing_stage_frame_is_fail_closed():
# Isolate the stage join after its independently checked upstream operations.
# The model comes from the real measured-row fitter, never a qualification flag.
model = fitted_model()
events = tuple({'block_id': str(index), 'samples': ({'control_ns': index * 1_000_000_000,
events: tuple[StageEvent, ...] = tuple({'block_id': str(index), 'samples': ({'control_ns': index * 1_000_000_000,
'apply_ns': index * 1_000_000_000 + 1, 'mono_ns': index * 1_000_000_000 + 2,
'control': {'stamp': index * 1_000_000_000}, 'output': {'stamp': index * 1_000_000_000 + 1},
'physical': {'lateral_accel': .02}},)} for index in range(1, 17))
@@ -1,5 +1,5 @@
"""Model noise remains ineligible even for explicitly retained controller chatter."""
from dataclasses import asdict
from dataclasses import replace
import pytest
@@ -35,4 +35,4 @@ def test_exact_canonical_objective_only_retains_expected_chatter(group):
def test_nonfriction_coordinates_cannot_claim_friction_contract():
objective = EffectObjective('friction.left', GROUPS['friction.left'], 'a' * 64, 'b' * 64)
with pytest.raises(ValueError, match='canonical event group'):
EffectObjective(**{**asdict(objective), 'names': GROUPS['ff.left']})
replace(objective, names=GROUPS['ff.left'])
@@ -17,7 +17,7 @@ from openpilot.starpilot.flm.learner_factory import TorqueCollectionAdapter, RAW
from openpilot.starpilot.flm.runtime_adapter_registry import baseline_values, baseline_revision, selected_descriptor, selected_identity
from openpilot.starpilot.flm.tests.native_controls_fixture import native_frame
from openpilot.starpilot.flm.tests.test_registered_controls_global import controls_for, published_cp
from openpilot.starpilot.lateral.controller_selection import DOCUMENT_KEY as SELECTION_KEY, replace_mode
from openpilot.starpilot.lateral.controller_selection import DOCUMENT_KEY as SELECTION_KEY, ControllerMode, replace_mode
from openpilot.starpilot.lateral.learning_preferences import KEY, replace_preference
from openpilot.starpilot.lateral.torque_settings import DOCUMENT_KEY, FieldChoice, PlatformProfile, serialize_document
@@ -104,14 +104,15 @@ def test_off_keeps_historical_controller_output_despite_qualified_native_packet(
@contextmanager
def manual_ioniq():
cp = cp_for(HYUNDAI.HYUNDAI_IONIQ_6)
basis = tuple(float(getattr(cp.lateralTuning.torque, name)) for name in ('latAccelFactor', 'latAccelOffset', 'friction'))
torque = cp.lateralTuning.torque
basis = (float(torque.latAccelFactor), float(torque.latAccelOffset), float(torque.friction))
document = serialize_document({str(cp.carFingerprint): PlatformProfile(basis, FieldChoice('custom', basis[0] * .95), FieldChoice())})
with OpenpilotPrefix(), mock.patch.dict(os.environ, {'REPLAY': '1', 'SIMULATION': '0', 'TORQUE_REPLAY_RUNTIME': '0',
'AOL_REPLAY_RUNTIME': '0', 'LANE_CENTERING_REPLAY_RUNTIME': '0'}), mock.patch('openpilot.selfdrive.controls.controlsd.messaging.PubMaster'):
params = Params()
params.put('CarParams', published_cp(cp, params).to_bytes(), block=True)
params.put(KEY, replace_preference(None, cp, True), block=True)
params.put(SELECTION_KEY, json.loads(replace_mode(None, cp, 'starpilot')), block=True)
params.put(SELECTION_KEY, json.loads(replace_mode(None, cp, ControllerMode.STARPILOT)), block=True)
params.put(DOCUMENT_KEY, json.loads(document), block=True)
yield Controls(), document, basis
@@ -102,7 +102,10 @@ def test_honda_normalized_pre_lookup_comparison_and_modified_filter_shadow_is_pu
assert observed_branch.civic == prior_civic # paired evaluation never advances filter
observed_shapes.append(shape)
return _shaped_input(row, request, shape)
candidate_shape = None if not modified else before + tuple(witness.shape[4:])
candidate_shape = None
if modified:
assert before is not None
candidate_shape = before + tuple(witness.shape[4:])
expected_source = _shaped_input(witness, witness.requested_torque, candidate_shape)
params = dict(witness.limits)
expected = rate_limit(expected_source, branch.previous,
@@ -23,12 +23,13 @@ def incoming(identity, *, frame=0):
if identity != 'VOLVO_V40':
return sources(identity, frame=frame)
packer = CANPacker('volvo_v40_2017_pt')
return [packer.make_can_msg(name, bus, values) for name, bus, values in [
messages: list[tuple[str, int, dict[str, float]]] = [
('VehicleSpeed1', 0, {'VehicleSpeed': 72}), ('CCButtons', 0, {}),
('PSCM1', 0, {'SteeringAngleServo': 12.5, 'LKATorque': 10}),
('PedalandBrake', 0, {}), ('TCM0', 0, {'GearShifter': 3}), ('ACC', 0, {'SpeedTargetACC': 100}),
('MiscCarInfo', 0, {}), ('FSM0', 2, {'ACCStatusOnOff': 1, 'ACCStatusActive': 1}), ('FSM1', 2, {}),
]]
]
return [packer.make_can_msg(name, bus, values) for name, bus, values in messages]
class TestVolvoActualStartup(unittest.TestCase):
@@ -184,7 +184,7 @@ def test_actual_selfdrived_fault_clear_does_not_reengage_without_pcm_edge(identi
if lib.safety_config_valid() and fresh_panda:
break
else:
pytest.fail('Native RX and a fresh healthy Panda receipt did not recover within one source period')
pytest.fail(reason='Native RX and a fresh healthy Panda receipt did not recover within one source period')
assert not sd.enabled
_, command = cycle(True)
assert sd.enabled and sd.active and command.latActive
+1 -1
View File
@@ -1 +1 @@
{"protected_inventory_sha256": "93618a5ca359682ede714b04c6e50d87c0cf63a50e5f5ab05d1d1729cc38a08d", "schema_version": 1}
{"protected_inventory_sha256": "961f82df6e41b06fefdea7ced62e8e635225d545d9a3f3419b6e96f4d410607c", "schema_version": 1}