From 7283d09e7164fed23ee74fcd85a067ae12095208 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 18 Feb 2025 19:49:18 -0800 Subject: [PATCH 01/18] move safety tests --- tests/safety/__init__.py | 0 tests/safety/common.py | 985 -------------------------- tests/safety/hyundai_common.py | 157 ---- tests/safety/install_mull.sh | 11 - tests/safety/mutation.sh | 22 - tests/safety/test.sh | 30 - tests/safety/test_body.py | 70 -- tests/safety/test_chrysler.py | 127 ---- tests/safety/test_defaults.py | 73 -- tests/safety/test_elm327.py | 49 -- tests/safety/test_ford.py | 466 ------------ tests/safety/test_gm.py | 229 ------ tests/safety/test_honda.py | 575 --------------- tests/safety/test_hyundai.py | 218 ------ tests/safety/test_hyundai_canfd.py | 283 -------- tests/safety/test_mazda.py | 86 --- tests/safety/test_nissan.py | 119 ---- tests/safety/test_subaru.py | 230 ------ tests/safety/test_subaru_preglobal.py | 72 -- tests/safety/test_tesla.py | 169 ----- tests/safety/test_toyota.py | 364 ---------- tests/safety/test_volkswagen_mqb.py | 226 ------ tests/safety/test_volkswagen_pq.py | 201 ------ 23 files changed, 4762 deletions(-) delete mode 100644 tests/safety/__init__.py delete mode 100644 tests/safety/common.py delete mode 100644 tests/safety/hyundai_common.py delete mode 100755 tests/safety/install_mull.sh delete mode 100755 tests/safety/mutation.sh delete mode 100755 tests/safety/test.sh delete mode 100755 tests/safety/test_body.py delete mode 100755 tests/safety/test_chrysler.py delete mode 100755 tests/safety/test_defaults.py delete mode 100755 tests/safety/test_elm327.py delete mode 100755 tests/safety/test_ford.py delete mode 100755 tests/safety/test_gm.py delete mode 100755 tests/safety/test_honda.py delete mode 100755 tests/safety/test_hyundai.py delete mode 100755 tests/safety/test_hyundai_canfd.py delete mode 100755 tests/safety/test_mazda.py delete mode 100755 tests/safety/test_nissan.py delete mode 100755 tests/safety/test_subaru.py delete mode 100755 tests/safety/test_subaru_preglobal.py delete mode 100755 tests/safety/test_tesla.py delete mode 100755 tests/safety/test_toyota.py delete mode 100755 tests/safety/test_volkswagen_mqb.py delete mode 100755 tests/safety/test_volkswagen_pq.py diff --git a/tests/safety/__init__.py b/tests/safety/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/safety/common.py b/tests/safety/common.py deleted file mode 100644 index aaec2fb971..0000000000 --- a/tests/safety/common.py +++ /dev/null @@ -1,985 +0,0 @@ -import os -import abc -import unittest -import importlib -import numpy as np -from collections.abc import Callable - -from opendbc.can.packer import CANPacker # pylint: disable=import-error -from opendbc.safety import ALTERNATIVE_EXPERIENCE -from panda.tests.libsafety import libsafety_py - -MAX_WRONG_COUNTERS = 5 -MAX_SAMPLE_VALS = 6 -VEHICLE_SPEED_FACTOR = 100 - -MessageFunction = Callable[[float], libsafety_py.CANPacket] - -def sign_of(a): - return 1 if a > 0 else -1 - - -def make_msg(bus, addr, length=8, dat=None): - if dat is None: - dat = b'\x00' * length - return libsafety_py.make_CANPacket(addr, bus, dat) - - -class CANPackerPanda(CANPacker): - def make_can_msg_panda(self, name_or_addr, bus, values, fix_checksum=None): - msg = self.make_can_msg(name_or_addr, bus, values) - if fix_checksum is not None: - msg = fix_checksum(msg) - addr, dat, bus = msg - return libsafety_py.make_CANPacket(addr, bus, dat) - - -def add_regen_tests(cls): - """Dynamically adds regen tests for all user brake tests.""" - - # only rx/user brake tests, not brake command - found_tests = [func for func in dir(cls) if func.startswith("test_") and "user_brake" in func] - assert len(found_tests) >= 3, "Failed to detect known brake tests" - - for test in found_tests: - def _make_regen_test(brake_func): - def _regen_test(self): - # only for safety modes with a regen message - if self._user_regen_msg(0) is None: - raise unittest.SkipTest("Safety mode implements no _user_regen_msg") - - getattr(self, brake_func)(self._user_regen_msg, self.safety.get_regen_braking_prev) - return _regen_test - - setattr(cls, test.replace("brake", "regen"), _make_regen_test(test)) - - return cls - - -class PandaSafetyTestBase(unittest.TestCase): - safety: libsafety_py.Panda - - @classmethod - def setUpClass(cls): - if cls.__name__ == "PandaSafetyTestBase": - cls.safety = None - raise unittest.SkipTest - - def _reset_safety_hooks(self): - self.safety.set_safety_hooks(self.safety.get_current_safety_mode(), - self.safety.get_current_safety_param()) - - def _rx(self, msg): - return self.safety.safety_rx_hook(msg) - - def _tx(self, msg): - return self.safety.safety_tx_hook(msg) - - def _generic_limit_safety_check(self, msg_function: MessageFunction, min_allowed_value: float, max_allowed_value: float, - min_possible_value: float, max_possible_value: float, test_delta: float = 1, inactive_value: float = 0, - msg_allowed = True, additional_setup: Callable[[float], None] | None = None): - """ - Enforces that a signal within a message is only allowed to be sent within a specific range, min_allowed_value -> max_allowed_value. - Tests the range of min_possible_value -> max_possible_value with a delta of test_delta. - Message is also only allowed to be sent when controls_allowed is true, unless the value is equal to inactive_value. - Message is never allowed if msg_allowed is false, for example when stock longitudinal is enabled and you are sending acceleration requests. - additional_setup is used for extra setup before each _tx, ex: for setting the previous torque for rate limits - """ - - # Ensure that we at least test the allowed_value range - self.assertGreater(max_possible_value, max_allowed_value) - self.assertLessEqual(min_possible_value, min_allowed_value) - - for controls_allowed in [False, True]: - # enforce we don't skip over 0 or inactive - for v in np.concatenate((np.arange(min_possible_value, max_possible_value, test_delta), np.array([0, inactive_value]))): - v = round(v, 2) # floats might not hit exact boundary conditions without rounding - self.safety.set_controls_allowed(controls_allowed) - if additional_setup is not None: - additional_setup(v) - should_tx = controls_allowed and min_allowed_value <= v <= max_allowed_value - should_tx = (should_tx or v == inactive_value) and msg_allowed - self.assertEqual(self._tx(msg_function(v)), should_tx, (controls_allowed, should_tx, v)) - - def _common_measurement_test(self, msg_func: Callable, min_value: float, max_value: float, factor: float, - meas_min_func: Callable[[], int], meas_max_func: Callable[[], int]): - """Tests accurate measurement parsing, and that the struct is reset on safety mode init""" - for val in np.arange(min_value, max_value, 0.5): - for i in range(MAX_SAMPLE_VALS): - self.assertTrue(self._rx(msg_func(val + i * 0.1))) - - # assert close by one decimal place - self.assertAlmostEqual(meas_min_func() / factor, val, delta=0.1) - self.assertAlmostEqual(meas_max_func() / factor - 0.5, val, delta=0.1) - - # ensure sample_t is reset on safety init - self._reset_safety_hooks() - self.assertEqual(meas_min_func(), 0) - self.assertEqual(meas_max_func(), 0) - - -class LongitudinalAccelSafetyTest(PandaSafetyTestBase, abc.ABC): - - MAX_ACCEL: float = 2.0 - MIN_ACCEL: float = -3.5 - INACTIVE_ACCEL: float = 0.0 - - @classmethod - def setUpClass(cls): - if cls.__name__ == "LongitudinalAccelSafetyTest": - cls.safety = None - raise unittest.SkipTest - - @abc.abstractmethod - def _accel_msg(self, accel: float): - pass - - def test_accel_limits_correct(self): - self.assertGreater(self.MAX_ACCEL, 0) - self.assertLess(self.MIN_ACCEL, 0) - - def test_accel_actuation_limits(self, stock_longitudinal=False): - limits = ((self.MIN_ACCEL, self.MAX_ACCEL, ALTERNATIVE_EXPERIENCE.DEFAULT), - (self.MIN_ACCEL, self.MAX_ACCEL, ALTERNATIVE_EXPERIENCE.RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX)) - - for min_accel, max_accel, alternative_experience in limits: - # enforce we don't skip over 0 or inactive accel - for accel in np.concatenate((np.arange(min_accel - 1, max_accel + 1, 0.05), [0, self.INACTIVE_ACCEL])): - accel = round(accel, 2) # floats might not hit exact boundary conditions without rounding - for controls_allowed in [True, False]: - self.safety.set_controls_allowed(controls_allowed) - self.safety.set_alternative_experience(alternative_experience) - if stock_longitudinal: - should_tx = False - else: - should_tx = controls_allowed and min_accel <= accel <= max_accel - should_tx = should_tx or accel == self.INACTIVE_ACCEL - self.assertEqual(should_tx, self._tx(self._accel_msg(accel))) - - -class LongitudinalGasBrakeSafetyTest(PandaSafetyTestBase, abc.ABC): - - MIN_BRAKE: int = 0 - MAX_BRAKE: int | None = None - MAX_POSSIBLE_BRAKE: int | None = None - - MIN_GAS: int = 0 - MAX_GAS: int | None = None - INACTIVE_GAS = 0 - MAX_POSSIBLE_GAS: int | None = None - - def test_gas_brake_limits_correct(self): - self.assertIsNotNone(self.MAX_POSSIBLE_BRAKE) - self.assertIsNotNone(self.MAX_POSSIBLE_GAS) - - self.assertGreater(self.MAX_BRAKE, self.MIN_BRAKE) - self.assertGreater(self.MAX_GAS, self.MIN_GAS) - - @abc.abstractmethod - def _send_gas_msg(self, gas: int): - pass - - @abc.abstractmethod - def _send_brake_msg(self, brake: int): - pass - - def test_brake_safety_check(self): - self._generic_limit_safety_check(self._send_brake_msg, self.MIN_BRAKE, self.MAX_BRAKE, 0, self.MAX_POSSIBLE_BRAKE, 1) - - def test_gas_safety_check(self): - self._generic_limit_safety_check(self._send_gas_msg, self.MIN_GAS, self.MAX_GAS, 0, self.MAX_POSSIBLE_GAS, 1, self.INACTIVE_GAS) - - -class TorqueSteeringSafetyTestBase(PandaSafetyTestBase, abc.ABC): - - MAX_RATE_UP = 0 - MAX_RATE_DOWN = 0 - MAX_TORQUE = 0 - MAX_RT_DELTA = 0 - RT_INTERVAL = 0 - - NO_STEER_REQ_BIT = False - - @classmethod - def setUpClass(cls): - if cls.__name__ == "TorqueSteeringSafetyTestBase": - cls.safety = None - raise unittest.SkipTest - - @abc.abstractmethod - def _torque_cmd_msg(self, torque, steer_req=1): - pass - - def _set_prev_torque(self, t): - self.safety.set_desired_torque_last(t) - self.safety.set_rt_torque_last(t) - - def test_steer_safety_check(self): - for enabled in [0, 1]: - for t in range(int(-self.MAX_TORQUE * 1.5), int(self.MAX_TORQUE * 1.5)): - self.safety.set_controls_allowed(enabled) - self._set_prev_torque(t) - if abs(t) > self.MAX_TORQUE or (not enabled and abs(t) > 0): - self.assertFalse(self._tx(self._torque_cmd_msg(t))) - else: - self.assertTrue(self._tx(self._torque_cmd_msg(t))) - - def test_non_realtime_limit_up(self): - self.safety.set_controls_allowed(True) - - self._set_prev_torque(0) - self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_RATE_UP))) - self._set_prev_torque(0) - self.assertTrue(self._tx(self._torque_cmd_msg(-self.MAX_RATE_UP))) - - self._set_prev_torque(0) - self.assertFalse(self._tx(self._torque_cmd_msg(self.MAX_RATE_UP + 1))) - self.safety.set_controls_allowed(True) - self._set_prev_torque(0) - self.assertFalse(self._tx(self._torque_cmd_msg(-self.MAX_RATE_UP - 1))) - - def test_steer_req_bit(self): - """Asserts all torque safety modes check the steering request bit""" - if self.NO_STEER_REQ_BIT: - raise unittest.SkipTest("No steering request bit") - - self.safety.set_controls_allowed(True) - self._set_prev_torque(self.MAX_TORQUE) - - # Send torque successfully, then only drop the request bit and ensure it stays blocked - for _ in range(10): - self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, 1))) - - self.assertFalse(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, 0))) - for _ in range(10): - self.assertFalse(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, 1))) - - -class SteerRequestCutSafetyTest(TorqueSteeringSafetyTestBase, abc.ABC): - - @classmethod - def setUpClass(cls): - if cls.__name__ == "SteerRequestCutSafetyTest": - cls.safety = None - raise unittest.SkipTest - - # Safety around steering request bit mismatch tolerance - MIN_VALID_STEERING_FRAMES: int - MAX_INVALID_STEERING_FRAMES: int - MIN_VALID_STEERING_RT_INTERVAL: int - - def test_steer_req_bit_frames(self): - """ - Certain safety modes implement some tolerance on their steer request bits matching the - requested torque to avoid a steering fault or lockout and maintain torque. This tests: - - We can't cut torque for more than one frame - - We can't cut torque until at least the minimum number of matching steer_req messages - - We can always recover from violations if steer_req=1 - """ - - for min_valid_steer_frames in range(self.MIN_VALID_STEERING_FRAMES * 2): - # Reset match count and rt timer to allow cut (valid_steer_req_count, ts_steer_req_mismatch_last) - self.safety.init_tests() - self.safety.set_timer(self.MIN_VALID_STEERING_RT_INTERVAL) - - # Allow torque cut - self.safety.set_controls_allowed(True) - self._set_prev_torque(self.MAX_TORQUE) - for _ in range(min_valid_steer_frames): - self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=1))) - - # should tx if we've sent enough valid frames, and we're not cutting torque for too many frames consecutively - should_tx = min_valid_steer_frames >= self.MIN_VALID_STEERING_FRAMES - for idx in range(self.MAX_INVALID_STEERING_FRAMES * 2): - tx = self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=0)) - self.assertEqual(should_tx and idx < self.MAX_INVALID_STEERING_FRAMES, tx) - - # Keep blocking after one steer_req mismatch - for _ in range(100): - self.assertFalse(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=0))) - - # Make sure we can recover - self.assertTrue(self._tx(self._torque_cmd_msg(0, steer_req=1))) - self._set_prev_torque(self.MAX_TORQUE) - self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=1))) - - def test_steer_req_bit_multi_invalid(self): - """ - For safety modes allowing multiple consecutive invalid frames, this ensures that once a valid frame - is sent after an invalid frame (even without sending the max number of allowed invalid frames), - all counters are reset. - """ - for max_invalid_steer_frames in range(1, self.MAX_INVALID_STEERING_FRAMES * 2): - self.safety.init_tests() - self.safety.set_timer(self.MIN_VALID_STEERING_RT_INTERVAL) - - # Allow torque cut - self.safety.set_controls_allowed(True) - self._set_prev_torque(self.MAX_TORQUE) - for _ in range(self.MIN_VALID_STEERING_FRAMES): - self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=1))) - - # Send partial amount of allowed invalid frames - for idx in range(max_invalid_steer_frames): - should_tx = idx < self.MAX_INVALID_STEERING_FRAMES - self.assertEqual(should_tx, self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=0))) - - # Send one valid frame, and subsequent invalid should now be blocked - self._set_prev_torque(self.MAX_TORQUE) - self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=1))) - for _ in range(self.MIN_VALID_STEERING_FRAMES + 1): - self.assertFalse(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=0))) - - def test_steer_req_bit_realtime(self): - """ - Realtime safety for cutting steer request bit. This tests: - - That we allow messages with mismatching steer request bit if time from last is >= MIN_VALID_STEERING_RT_INTERVAL - - That frame mismatch safety does not interfere with this test - """ - for rt_us in np.arange(self.MIN_VALID_STEERING_RT_INTERVAL - 50000, self.MIN_VALID_STEERING_RT_INTERVAL + 50000, 10000): - # Reset match count and rt timer (valid_steer_req_count, ts_steer_req_mismatch_last) - self.safety.init_tests() - - # Make sure valid_steer_req_count doesn't affect this test - self.safety.set_controls_allowed(True) - self._set_prev_torque(self.MAX_TORQUE) - for _ in range(self.MIN_VALID_STEERING_FRAMES): - self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=1))) - - # Normally, sending MIN_VALID_STEERING_FRAMES valid frames should always allow - self.safety.set_timer(max(rt_us, 0)) - should_tx = rt_us >= self.MIN_VALID_STEERING_RT_INTERVAL - for _ in range(self.MAX_INVALID_STEERING_FRAMES): - self.assertEqual(should_tx, self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=0))) - - # Keep blocking after one steer_req mismatch - for _ in range(100): - self.assertFalse(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=0))) - - # Make sure we can recover - self.assertTrue(self._tx(self._torque_cmd_msg(0, steer_req=1))) - self._set_prev_torque(self.MAX_TORQUE) - self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_TORQUE, steer_req=1))) - - -class DriverTorqueSteeringSafetyTest(TorqueSteeringSafetyTestBase, abc.ABC): - - DRIVER_TORQUE_ALLOWANCE = 0 - DRIVER_TORQUE_FACTOR = 0 - - @classmethod - def setUpClass(cls): - if cls.__name__ == "DriverTorqueSteeringSafetyTest": - cls.safety = None - raise unittest.SkipTest - - @abc.abstractmethod - def _torque_driver_msg(self, torque): - pass - - def _reset_torque_driver_measurement(self, torque): - for _ in range(MAX_SAMPLE_VALS): - self._rx(self._torque_driver_msg(torque)) - - def test_non_realtime_limit_up(self): - self._reset_torque_driver_measurement(0) - super().test_non_realtime_limit_up() - - def test_against_torque_driver(self): - # Tests down limits and driver torque blending - self.safety.set_controls_allowed(True) - - # Cannot stay at MAX_TORQUE if above DRIVER_TORQUE_ALLOWANCE - for sign in [-1, 1]: - for driver_torque in np.arange(0, self.DRIVER_TORQUE_ALLOWANCE * 2, 1): - self._reset_torque_driver_measurement(-driver_torque * sign) - self._set_prev_torque(self.MAX_TORQUE * sign) - should_tx = abs(driver_torque) <= self.DRIVER_TORQUE_ALLOWANCE - self.assertEqual(should_tx, self._tx(self._torque_cmd_msg(self.MAX_TORQUE * sign))) - - # arbitrary high driver torque to ensure max steer torque is allowed - max_driver_torque = int(self.MAX_TORQUE / self.DRIVER_TORQUE_FACTOR + self.DRIVER_TORQUE_ALLOWANCE + 1) - - # spot check some individual cases - for sign in [-1, 1]: - # Ensure we wind down factor units for every unit above allowance - driver_torque = (self.DRIVER_TORQUE_ALLOWANCE + 10) * sign - torque_desired = (self.MAX_TORQUE - 10 * self.DRIVER_TORQUE_FACTOR) * sign - delta = 1 * sign - self._set_prev_torque(torque_desired) - self._reset_torque_driver_measurement(-driver_torque) - self.assertTrue(self._tx(self._torque_cmd_msg(torque_desired))) - self._set_prev_torque(torque_desired + delta) - self._reset_torque_driver_measurement(-driver_torque) - self.assertFalse(self._tx(self._torque_cmd_msg(torque_desired + delta))) - - # If we're well past the allowance, minimum wind down is MAX_RATE_DOWN - self._set_prev_torque(self.MAX_TORQUE * sign) - self._reset_torque_driver_measurement(-max_driver_torque * sign) - self.assertTrue(self._tx(self._torque_cmd_msg((self.MAX_TORQUE - self.MAX_RATE_DOWN) * sign))) - self._set_prev_torque(self.MAX_TORQUE * sign) - self._reset_torque_driver_measurement(-max_driver_torque * sign) - self.assertTrue(self._tx(self._torque_cmd_msg(0))) - self._set_prev_torque(self.MAX_TORQUE * sign) - self._reset_torque_driver_measurement(-max_driver_torque * sign) - self.assertFalse(self._tx(self._torque_cmd_msg((self.MAX_TORQUE - self.MAX_RATE_DOWN + 1) * sign))) - - def test_realtime_limits(self): - self.safety.set_controls_allowed(True) - - for sign in [-1, 1]: - self.safety.init_tests() - self._set_prev_torque(0) - self._reset_torque_driver_measurement(0) - for t in np.arange(0, self.MAX_RT_DELTA, 1): - t *= sign - self.assertTrue(self._tx(self._torque_cmd_msg(t))) - self.assertFalse(self._tx(self._torque_cmd_msg(sign * (self.MAX_RT_DELTA + 1)))) - - self._set_prev_torque(0) - for t in np.arange(0, self.MAX_RT_DELTA, 1): - t *= sign - self.assertTrue(self._tx(self._torque_cmd_msg(t))) - - # Increase timer to update rt_torque_last - self.safety.set_timer(self.RT_INTERVAL + 1) - self.assertTrue(self._tx(self._torque_cmd_msg(sign * (self.MAX_RT_DELTA - 1)))) - self.assertTrue(self._tx(self._torque_cmd_msg(sign * (self.MAX_RT_DELTA + 1)))) - - def test_reset_driver_torque_measurements(self): - # Tests that the driver torque measurement sample_t is reset on safety mode init - for t in np.linspace(-self.MAX_TORQUE, self.MAX_TORQUE, MAX_SAMPLE_VALS): - self.assertTrue(self._rx(self._torque_driver_msg(t))) - - self.assertNotEqual(self.safety.get_torque_driver_min(), 0) - self.assertNotEqual(self.safety.get_torque_driver_max(), 0) - - self._reset_safety_hooks() - self.assertEqual(self.safety.get_torque_driver_min(), 0) - self.assertEqual(self.safety.get_torque_driver_max(), 0) - - -class MotorTorqueSteeringSafetyTest(TorqueSteeringSafetyTestBase, abc.ABC): - - MAX_TORQUE_ERROR = 0 - TORQUE_MEAS_TOLERANCE = 0 - - @classmethod - def setUpClass(cls): - if cls.__name__ == "MotorTorqueSteeringSafetyTest": - cls.safety = None - raise unittest.SkipTest - - @abc.abstractmethod - def _torque_meas_msg(self, torque): - pass - - def _set_prev_torque(self, t): - super()._set_prev_torque(t) - self.safety.set_torque_meas(t, t) - - def test_torque_absolute_limits(self): - for controls_allowed in [True, False]: - for torque in np.arange(-self.MAX_TORQUE - 1000, self.MAX_TORQUE + 1000, self.MAX_RATE_UP): - self.safety.set_controls_allowed(controls_allowed) - self.safety.set_rt_torque_last(torque) - self.safety.set_torque_meas(torque, torque) - self.safety.set_desired_torque_last(torque - self.MAX_RATE_UP) - - if controls_allowed: - send = (-self.MAX_TORQUE <= torque <= self.MAX_TORQUE) - else: - send = torque == 0 - - self.assertEqual(send, self._tx(self._torque_cmd_msg(torque))) - - def test_non_realtime_limit_down(self): - self.safety.set_controls_allowed(True) - - torque_meas = self.MAX_TORQUE - self.MAX_TORQUE_ERROR - 50 - - self.safety.set_rt_torque_last(self.MAX_TORQUE) - self.safety.set_torque_meas(torque_meas, torque_meas) - self.safety.set_desired_torque_last(self.MAX_TORQUE) - self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_TORQUE - self.MAX_RATE_DOWN))) - - self.safety.set_rt_torque_last(self.MAX_TORQUE) - self.safety.set_torque_meas(torque_meas, torque_meas) - self.safety.set_desired_torque_last(self.MAX_TORQUE) - self.assertFalse(self._tx(self._torque_cmd_msg(self.MAX_TORQUE - self.MAX_RATE_DOWN + 1))) - - def test_exceed_torque_sensor(self): - self.safety.set_controls_allowed(True) - - for sign in [-1, 1]: - self._set_prev_torque(0) - for t in np.arange(0, self.MAX_TORQUE_ERROR + 2, 2): # step needs to be smaller than MAX_TORQUE_ERROR - t *= sign - self.assertTrue(self._tx(self._torque_cmd_msg(t))) - - self.assertFalse(self._tx(self._torque_cmd_msg(sign * (self.MAX_TORQUE_ERROR + 2)))) - - def test_realtime_limit_up(self): - self.safety.set_controls_allowed(True) - - for sign in [-1, 1]: - self.safety.init_tests() - self._set_prev_torque(0) - for t in np.arange(0, self.MAX_RT_DELTA + 1, 1): - t *= sign - self.safety.set_torque_meas(t, t) - self.assertTrue(self._tx(self._torque_cmd_msg(t))) - self.assertFalse(self._tx(self._torque_cmd_msg(sign * (self.MAX_RT_DELTA + 1)))) - - self._set_prev_torque(0) - for t in np.arange(0, self.MAX_RT_DELTA + 1, 1): - t *= sign - self.safety.set_torque_meas(t, t) - self.assertTrue(self._tx(self._torque_cmd_msg(t))) - - # Increase timer to update rt_torque_last - self.safety.set_timer(self.RT_INTERVAL + 1) - self.assertTrue(self._tx(self._torque_cmd_msg(sign * self.MAX_RT_DELTA))) - self.assertTrue(self._tx(self._torque_cmd_msg(sign * (self.MAX_RT_DELTA + 1)))) - - def test_torque_measurements(self): - trq = 50 - for t in [trq, -trq, 0, 0, 0, 0]: - self._rx(self._torque_meas_msg(t)) - - max_range = range(trq, trq + self.TORQUE_MEAS_TOLERANCE + 1) - min_range = range(-(trq + self.TORQUE_MEAS_TOLERANCE), -trq + 1) - self.assertTrue(self.safety.get_torque_meas_min() in min_range) - self.assertTrue(self.safety.get_torque_meas_max() in max_range) - - max_range = range(self.TORQUE_MEAS_TOLERANCE + 1) - min_range = range(-(trq + self.TORQUE_MEAS_TOLERANCE), -trq + 1) - self._rx(self._torque_meas_msg(0)) - self.assertTrue(self.safety.get_torque_meas_min() in min_range) - self.assertTrue(self.safety.get_torque_meas_max() in max_range) - - max_range = range(self.TORQUE_MEAS_TOLERANCE + 1) - min_range = range(-self.TORQUE_MEAS_TOLERANCE, 0 + 1) - self._rx(self._torque_meas_msg(0)) - self.assertTrue(self.safety.get_torque_meas_min() in min_range) - self.assertTrue(self.safety.get_torque_meas_max() in max_range) - - def test_reset_torque_measurements(self): - # Tests that the torque measurement sample_t is reset on safety mode init - for t in np.linspace(-self.MAX_TORQUE, self.MAX_TORQUE, MAX_SAMPLE_VALS): - self.assertTrue(self._rx(self._torque_meas_msg(t))) - - self.assertNotEqual(self.safety.get_torque_meas_min(), 0) - self.assertNotEqual(self.safety.get_torque_meas_max(), 0) - - self._reset_safety_hooks() - self.assertEqual(self.safety.get_torque_meas_min(), 0) - self.assertEqual(self.safety.get_torque_meas_max(), 0) - - -class AngleSteeringSafetyTest(PandaSafetyTestBase): - - DEG_TO_CAN: float - ANGLE_RATE_BP: list[float] - ANGLE_RATE_UP: list[float] # windup limit - ANGLE_RATE_DOWN: list[float] # unwind limit - - @classmethod - def setUpClass(cls): - if cls.__name__ == "AngleSteeringSafetyTest": - cls.safety = None - raise unittest.SkipTest - - @abc.abstractmethod - def _speed_msg(self, speed): - pass - - @abc.abstractmethod - def _angle_cmd_msg(self, angle: float, enabled: bool): - pass - - @abc.abstractmethod - def _angle_meas_msg(self, angle: float): - pass - - def _set_prev_desired_angle(self, t): - t = round(t * self.DEG_TO_CAN) - self.safety.set_desired_angle_last(t) - - def _reset_angle_measurement(self, angle): - for _ in range(MAX_SAMPLE_VALS): - self._rx(self._angle_meas_msg(angle)) - - def _reset_speed_measurement(self, speed): - for _ in range(MAX_SAMPLE_VALS): - self._rx(self._speed_msg(speed)) - - def test_vehicle_speed_measurements(self): - self._common_measurement_test(self._speed_msg, 0, 80, VEHICLE_SPEED_FACTOR, self.safety.get_vehicle_speed_min, self.safety.get_vehicle_speed_max) - - def test_steering_angle_measurements(self, max_angle=300): - self._common_measurement_test(self._angle_meas_msg, -max_angle, max_angle, self.DEG_TO_CAN, self.safety.get_angle_meas_min, self.safety.get_angle_meas_max) - - def test_angle_cmd_when_enabled(self, max_angle=300): - # when controls are allowed, angle cmd rate limit is enforced - speeds = [0., 1., 5., 10., 15., 50.] - angles = np.concatenate((np.arange(-max_angle, max_angle, 5), [0])) - for a in angles: - for s in speeds: - max_delta_up = np.interp(s, self.ANGLE_RATE_BP, self.ANGLE_RATE_UP) - max_delta_down = np.interp(s, self.ANGLE_RATE_BP, self.ANGLE_RATE_DOWN) - - # first test against false positives - self._reset_angle_measurement(a) - self._reset_speed_measurement(s) - - self._set_prev_desired_angle(a) - self.safety.set_controls_allowed(1) - - # Stay within limits - # Up - self.assertTrue(self._tx(self._angle_cmd_msg(a + sign_of(a) * max_delta_up, True))) - self.assertTrue(self.safety.get_controls_allowed()) - - # Don't change - self.assertTrue(self._tx(self._angle_cmd_msg(a, True))) - self.assertTrue(self.safety.get_controls_allowed()) - - # Down - self.assertTrue(self._tx(self._angle_cmd_msg(a - sign_of(a) * max_delta_down, True))) - self.assertTrue(self.safety.get_controls_allowed()) - - # Inject too high rates - # Up - self.assertFalse(self._tx(self._angle_cmd_msg(a + sign_of(a) * (max_delta_up + 1.1), True))) - - # Don't change - self.safety.set_controls_allowed(1) - self._set_prev_desired_angle(a) - self.assertTrue(self.safety.get_controls_allowed()) - self.assertTrue(self._tx(self._angle_cmd_msg(a, True))) - self.assertTrue(self.safety.get_controls_allowed()) - - # Down - self.assertFalse(self._tx(self._angle_cmd_msg(a - sign_of(a) * (max_delta_down + 1.1), True))) - - # Check desired steer should be the same as steer angle when controls are off - self.safety.set_controls_allowed(0) - self.assertTrue(self._tx(self._angle_cmd_msg(a, False))) - - def test_angle_cmd_when_disabled(self): - # Tests that only angles close to the meas are allowed while - # steer actuation bit is 0, regardless of controls allowed. - for controls_allowed in (True, False): - self.safety.set_controls_allowed(controls_allowed) - - for steer_control_enabled in (True, False): - for angle_meas in np.arange(-90, 91, 10): - self._reset_angle_measurement(angle_meas) - - for angle_cmd in np.arange(-90, 91, 10): - self._set_prev_desired_angle(angle_cmd) - - # controls_allowed is checked if actuation bit is 1, else the angle must be close to meas (inactive) - should_tx = controls_allowed if steer_control_enabled else angle_cmd == angle_meas - self.assertEqual(should_tx, self._tx(self._angle_cmd_msg(angle_cmd, steer_control_enabled))) - - -class PandaSafetyTest(PandaSafetyTestBase): - TX_MSGS: list[list[int]] | None = None - SCANNED_ADDRS = [*range(0x800), # Entire 11-bit CAN address space - *range(0x18DA00F1, 0x18DB00F1, 0x100), # 29-bit UDS physical addressing - *range(0x18DB00F1, 0x18DC00F1, 0x100), # 29-bit UDS functional addressing - *range(0x3300, 0x3400)] # Honda - FWD_BLACKLISTED_ADDRS: dict[int, list[int]] = {} # {bus: [addr]} - FWD_BUS_LOOKUP: dict[int, int] = {} - - @classmethod - def setUpClass(cls): - if cls.__name__ == "PandaSafetyTest" or cls.__name__.endswith('Base'): - cls.safety = None - raise unittest.SkipTest - - # ***** standard tests for all safety modes ***** - - def test_tx_msg_in_scanned_range(self): - # the relay malfunction, fwd hook, and spam can tests don't exhaustively - # scan the entire 29-bit address space, only some known important ranges - # make sure SCANNED_ADDRS stays up to date with car port TX_MSGS; new - # model ports should expand the range if needed - for msg in self.TX_MSGS: - self.assertTrue(msg[0] in self.SCANNED_ADDRS, f"{msg[0]=:#x}") - - def test_fwd_hook(self): - # some safety modes don't forward anything, while others blacklist msgs - for bus in range(3): - for addr in self.SCANNED_ADDRS: - # assume len 8 - fwd_bus = self.FWD_BUS_LOOKUP.get(bus, -1) - if bus in self.FWD_BLACKLISTED_ADDRS and addr in self.FWD_BLACKLISTED_ADDRS[bus]: - fwd_bus = -1 - self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(bus, addr), f"{addr=:#x} from {bus=} to {fwd_bus=}") - - def test_spam_can_buses(self): - for bus in range(4): - for addr in self.SCANNED_ADDRS: - if [addr, bus] not in self.TX_MSGS: - self.assertFalse(self._tx(make_msg(bus, addr, 8)), f"allowed TX {addr=} {bus=}") - - def test_default_controls_not_allowed(self): - self.assertFalse(self.safety.get_controls_allowed()) - - def test_manually_enable_controls_allowed(self): - self.safety.set_controls_allowed(1) - self.assertTrue(self.safety.get_controls_allowed()) - self.safety.set_controls_allowed(0) - self.assertFalse(self.safety.get_controls_allowed()) - - def test_tx_hook_on_wrong_safety_mode(self): - files = os.listdir(os.path.dirname(os.path.realpath(__file__))) - test_files = [f for f in files if f.startswith("test_") and f.endswith(".py")] - - current_test = self.__class__.__name__ - - all_tx = [] - for tf in test_files: - test = importlib.import_module("panda.tests.safety."+tf[:-3]) - for attr in dir(test): - if attr.startswith("Test") and attr != current_test: - tc = getattr(test, attr) - tx = tc.TX_MSGS - if tx is not None and not attr.endswith('Base'): - # No point in comparing different Tesla safety modes - if 'Tesla' in attr and 'Tesla' in current_test: - continue - # No point in comparing to ALLOUTPUT which allows all messages - if attr.startswith('TestAllOutput'): - continue - if attr.startswith('TestToyota') and current_test.startswith('TestToyota'): - continue - if attr.startswith('TestSubaruGen') and current_test.startswith('TestSubaruGen'): - continue - if attr.startswith('TestSubaruPreglobal') and current_test.startswith('TestSubaruPreglobal'): - continue - if {attr, current_test}.issubset({'TestVolkswagenPqSafety', 'TestVolkswagenPqStockSafety', 'TestVolkswagenPqLongSafety'}): - continue - if {attr, current_test}.issubset({'TestGmCameraSafety', 'TestGmCameraLongitudinalSafety'}): - continue - if attr.startswith('TestFord') and current_test.startswith('TestFord'): - continue - if attr.startswith('TestHyundaiCanfd') and current_test.startswith('TestHyundaiCanfd'): - continue - if {attr, current_test}.issubset({'TestVolkswagenMqbSafety', 'TestVolkswagenMqbStockSafety', 'TestVolkswagenMqbLongSafety'}): - continue - - # overlapping TX addrs, but they're not actuating messages for either car - if attr == 'TestHyundaiCanfdHDA2LongEV' and current_test.startswith('TestToyota'): - tx = list(filter(lambda m: m[0] not in [0x160, ], tx)) - - # Volkswagen MQB longitudinal actuating message overlaps with the Subaru lateral actuating message - if attr == 'TestVolkswagenMqbLongSafety' and current_test.startswith('TestSubaru'): - tx = list(filter(lambda m: m[0] not in [0x122, ], tx)) - - # Volkswagen MQB and Honda Nidec ACC HUD messages overlap - if attr == 'TestVolkswagenMqbLongSafety' and current_test.startswith('TestHondaNidec'): - tx = list(filter(lambda m: m[0] not in [0x30c, ], tx)) - - # Volkswagen MQB and Honda Bosch Radarless ACC HUD messages overlap - if attr == 'TestVolkswagenMqbLongSafety' and current_test.startswith('TestHondaBoschRadarless'): - tx = list(filter(lambda m: m[0] not in [0x30c, ], tx)) - - # TODO: Temporary, should be fixed in panda firmware, safety_honda.h - if attr.startswith('TestHonda'): - # exceptions for common msgs across different hondas - tx = list(filter(lambda m: m[0] not in [0x1FA, 0x30C, 0x33D, 0x33DB], tx)) - all_tx.append([[m[0], m[1], attr] for m in tx]) - - # make sure we got all the msgs - self.assertTrue(len(all_tx) >= len(test_files)-1) - - for tx_msgs in all_tx: - for addr, bus, test_name in tx_msgs: - msg = make_msg(bus, addr) - self.safety.set_controls_allowed(1) - # TODO: this should be blocked - if current_test in ["TestNissanSafety", "TestNissanSafetyAltEpsBus", "TestNissanLeafSafety"] and [addr, bus] in self.TX_MSGS: - continue - self.assertFalse(self._tx(msg), f"transmit of {addr=:#x} {bus=} from {test_name} during {current_test} was allowed") - - -@add_regen_tests -class PandaCarSafetyTest(PandaSafetyTest): - STANDSTILL_THRESHOLD: float | None = None - GAS_PRESSED_THRESHOLD = 0 - RELAY_MALFUNCTION_ADDRS: dict[int, tuple[int, ...]] | None = None - - @classmethod - def setUpClass(cls): - if cls.__name__ == "PandaCarSafetyTest" or cls.__name__.endswith('Base'): - cls.safety = None - raise unittest.SkipTest - - @abc.abstractmethod - def _user_brake_msg(self, brake): - pass - - def _user_regen_msg(self, regen): - pass - - @abc.abstractmethod - def _speed_msg(self, speed): - pass - - # Safety modes can override if vehicle_moving is driven by a different message - def _vehicle_moving_msg(self, speed: float): - return self._speed_msg(speed) - - @abc.abstractmethod - def _user_gas_msg(self, gas): - pass - - @abc.abstractmethod - def _pcm_status_msg(self, enable): - pass - - # ***** standard tests for all car-specific safety modes ***** - - def test_relay_malfunction(self): - # each car has an addr that is used to detect relay malfunction - # if that addr is seen on specified bus, triggers the relay malfunction - # protection logic: both tx_hook and fwd_hook are expected to return failure - self.assertFalse(self.safety.get_relay_malfunction()) - for bus in range(3): - for addr in self.SCANNED_ADDRS: - self.safety.set_relay_malfunction(False) - self._rx(make_msg(bus, addr, 8)) - should_relay_malfunction = addr in self.RELAY_MALFUNCTION_ADDRS.get(bus, ()) - self.assertEqual(should_relay_malfunction, self.safety.get_relay_malfunction(), (bus, addr)) - - # test relay malfunction protection logic - self.safety.set_relay_malfunction(True) - for bus in range(3): - for addr in self.SCANNED_ADDRS: - self.assertFalse(self._tx(make_msg(bus, addr, 8))) - self.assertEqual(-1, self.safety.safety_fwd_hook(bus, addr)) - - def test_prev_gas(self): - self.assertFalse(self.safety.get_gas_pressed_prev()) - for pressed in [self.GAS_PRESSED_THRESHOLD + 1, 0]: - self._rx(self._user_gas_msg(pressed)) - self.assertEqual(bool(pressed), self.safety.get_gas_pressed_prev()) - - def test_allow_engage_with_gas_pressed(self): - self._rx(self._user_gas_msg(1)) - self.safety.set_controls_allowed(True) - self._rx(self._user_gas_msg(1)) - self.assertTrue(self.safety.get_controls_allowed()) - self._rx(self._user_gas_msg(1)) - self.assertTrue(self.safety.get_controls_allowed()) - - def test_disengage_on_gas(self): - self._rx(self._user_gas_msg(0)) - self.safety.set_controls_allowed(True) - self._rx(self._user_gas_msg(self.GAS_PRESSED_THRESHOLD + 1)) - self.assertFalse(self.safety.get_controls_allowed()) - - def test_alternative_experience_no_disengage_on_gas(self): - self._rx(self._user_gas_msg(0)) - self.safety.set_controls_allowed(True) - self.safety.set_alternative_experience(ALTERNATIVE_EXPERIENCE.DISABLE_DISENGAGE_ON_GAS) - self._rx(self._user_gas_msg(self.GAS_PRESSED_THRESHOLD + 1)) - # Test we allow lateral, but not longitudinal - self.assertTrue(self.safety.get_controls_allowed()) - self.assertFalse(self.safety.get_longitudinal_allowed()) - # Make sure we can re-gain longitudinal actuation - self._rx(self._user_gas_msg(0)) - self.assertTrue(self.safety.get_longitudinal_allowed()) - - def test_prev_user_brake(self, _user_brake_msg=None, get_brake_pressed_prev=None): - if _user_brake_msg is None: - _user_brake_msg = self._user_brake_msg - get_brake_pressed_prev = self.safety.get_brake_pressed_prev - - self.assertFalse(get_brake_pressed_prev()) - for pressed in [True, False]: - self._rx(_user_brake_msg(not pressed)) - self.assertEqual(not pressed, get_brake_pressed_prev()) - self._rx(_user_brake_msg(pressed)) - self.assertEqual(pressed, get_brake_pressed_prev()) - - def test_enable_control_allowed_from_cruise(self): - self._rx(self._pcm_status_msg(False)) - self.assertFalse(self.safety.get_controls_allowed()) - self._rx(self._pcm_status_msg(True)) - self.assertTrue(self.safety.get_controls_allowed()) - - def test_disable_control_allowed_from_cruise(self): - self.safety.set_controls_allowed(1) - self._rx(self._pcm_status_msg(False)) - self.assertFalse(self.safety.get_controls_allowed()) - - def test_cruise_engaged_prev(self): - for engaged in [True, False]: - self._rx(self._pcm_status_msg(engaged)) - self.assertEqual(engaged, self.safety.get_cruise_engaged_prev()) - self._rx(self._pcm_status_msg(not engaged)) - self.assertEqual(not engaged, self.safety.get_cruise_engaged_prev()) - - def test_allow_user_brake_at_zero_speed(self, _user_brake_msg=None, get_brake_pressed_prev=None): - if _user_brake_msg is None: - _user_brake_msg = self._user_brake_msg - - # Brake was already pressed - self._rx(self._vehicle_moving_msg(0)) - self._rx(_user_brake_msg(1)) - self.safety.set_controls_allowed(1) - self._rx(_user_brake_msg(1)) - self.assertTrue(self.safety.get_controls_allowed()) - self.assertTrue(self.safety.get_longitudinal_allowed()) - self._rx(_user_brake_msg(0)) - self.assertTrue(self.safety.get_controls_allowed()) - self.assertTrue(self.safety.get_longitudinal_allowed()) - # rising edge of brake should disengage - self._rx(_user_brake_msg(1)) - self.assertFalse(self.safety.get_controls_allowed()) - self.assertFalse(self.safety.get_longitudinal_allowed()) - self._rx(_user_brake_msg(0)) # reset no brakes - - def test_not_allow_user_brake_when_moving(self, _user_brake_msg=None, get_brake_pressed_prev=None): - if _user_brake_msg is None: - _user_brake_msg = self._user_brake_msg - - # Brake was already pressed - self._rx(_user_brake_msg(1)) - self.safety.set_controls_allowed(1) - self._rx(self._vehicle_moving_msg(self.STANDSTILL_THRESHOLD)) - self._rx(_user_brake_msg(1)) - self.assertTrue(self.safety.get_controls_allowed()) - self.assertTrue(self.safety.get_longitudinal_allowed()) - self._rx(self._vehicle_moving_msg(self.STANDSTILL_THRESHOLD + 1)) - self._rx(_user_brake_msg(1)) - self.assertFalse(self.safety.get_controls_allowed()) - self.assertFalse(self.safety.get_longitudinal_allowed()) - self._rx(self._vehicle_moving_msg(0)) - - def test_vehicle_moving(self): - self.assertFalse(self.safety.get_vehicle_moving()) - - # not moving - self._rx(self._vehicle_moving_msg(0)) - self.assertFalse(self.safety.get_vehicle_moving()) - - # speed is at threshold - self._rx(self._vehicle_moving_msg(self.STANDSTILL_THRESHOLD)) - self.assertFalse(self.safety.get_vehicle_moving()) - - # past threshold - self._rx(self._vehicle_moving_msg(self.STANDSTILL_THRESHOLD + 1)) - self.assertTrue(self.safety.get_vehicle_moving()) - - def test_safety_tick(self): - self.safety.set_timer(int(2e6)) - self.safety.set_controls_allowed(True) - self.safety.safety_tick_current_safety_config() - self.assertFalse(self.safety.get_controls_allowed()) - self.assertFalse(self.safety.safety_config_valid()) diff --git a/tests/safety/hyundai_common.py b/tests/safety/hyundai_common.py deleted file mode 100644 index 7482d2fc6a..0000000000 --- a/tests/safety/hyundai_common.py +++ /dev/null @@ -1,157 +0,0 @@ -import unittest - -import panda.tests.safety.common as common -from panda.tests.libsafety import libsafety_py -from panda.tests.safety.common import make_msg - - -class Buttons: - NONE = 0 - RESUME = 1 - SET = 2 - CANCEL = 4 - - -PREV_BUTTON_SAMPLES = 8 -ENABLE_BUTTONS = (Buttons.RESUME, Buttons.SET, Buttons.CANCEL) - - -class HyundaiButtonBase: - # pylint: disable=no-member,abstract-method - BUTTONS_TX_BUS = 0 # tx on this bus, rx on 0 - SCC_BUS = 0 # rx on this bus - - def test_button_sends(self): - """ - Only RES and CANCEL buttons are allowed - - RES allowed while controls allowed - - CANCEL allowed while cruise is enabled - """ - self.safety.set_controls_allowed(0) - self.assertFalse(self._tx(self._button_msg(Buttons.RESUME, bus=self.BUTTONS_TX_BUS))) - self.assertFalse(self._tx(self._button_msg(Buttons.SET, bus=self.BUTTONS_TX_BUS))) - - self.safety.set_controls_allowed(1) - self.assertTrue(self._tx(self._button_msg(Buttons.RESUME, bus=self.BUTTONS_TX_BUS))) - self.assertFalse(self._tx(self._button_msg(Buttons.SET, bus=self.BUTTONS_TX_BUS))) - - for enabled in (True, False): - self._rx(self._pcm_status_msg(enabled)) - self.assertEqual(enabled, self._tx(self._button_msg(Buttons.CANCEL, bus=self.BUTTONS_TX_BUS))) - - def test_enable_control_allowed_from_cruise(self): - """ - Hyundai non-longitudinal only enables on PCM rising edge and recent button press. Tests PCM enabling with: - - disallowed: No buttons - - disallowed: Buttons that don't enable cruise - - allowed: Buttons that do enable cruise - - allowed: Main button with all above combinations - """ - for main_button in (0, 1): - for btn in range(8): - for _ in range(PREV_BUTTON_SAMPLES): # reset - self._rx(self._button_msg(Buttons.NONE)) - - self._rx(self._pcm_status_msg(False)) - self.assertFalse(self.safety.get_controls_allowed()) - self._rx(self._button_msg(btn, main_button=main_button)) - self._rx(self._pcm_status_msg(True)) - controls_allowed = btn in ENABLE_BUTTONS or main_button - self.assertEqual(controls_allowed, self.safety.get_controls_allowed()) - - def test_sampling_cruise_buttons(self): - """ - Test that we allow controls on recent button press, but not as button leaves sliding window - """ - self._rx(self._button_msg(Buttons.SET)) - for i in range(2 * PREV_BUTTON_SAMPLES): - self._rx(self._pcm_status_msg(False)) - self.assertFalse(self.safety.get_controls_allowed()) - self._rx(self._pcm_status_msg(True)) - controls_allowed = i < PREV_BUTTON_SAMPLES - self.assertEqual(controls_allowed, self.safety.get_controls_allowed()) - self._rx(self._button_msg(Buttons.NONE)) - - -class HyundaiLongitudinalBase(common.LongitudinalAccelSafetyTest): - # pylint: disable=no-member,abstract-method - - DISABLED_ECU_UDS_MSG: tuple[int, int] - DISABLED_ECU_ACTUATION_MSG: tuple[int, int] - - @classmethod - def setUpClass(cls): - if cls.__name__ == "HyundaiLongitudinalBase": - cls.safety = None - raise unittest.SkipTest - - # override these tests from PandaCarSafetyTest, hyundai longitudinal uses button enable - def test_disable_control_allowed_from_cruise(self): - pass - - def test_enable_control_allowed_from_cruise(self): - pass - - def test_sampling_cruise_buttons(self): - pass - - def test_cruise_engaged_prev(self): - pass - - def test_button_sends(self): - pass - - def _pcm_status_msg(self, enable): - raise Exception - - def _accel_msg(self, accel, aeb_req=False, aeb_decel=0): - raise NotImplementedError - - def test_set_resume_buttons(self): - """ - SET and RESUME enter controls allowed on their falling edge. - """ - for btn_prev in range(8): - for btn_cur in range(8): - self._rx(self._button_msg(Buttons.NONE)) - self.safety.set_controls_allowed(0) - for _ in range(10): - self._rx(self._button_msg(btn_prev)) - self.assertFalse(self.safety.get_controls_allowed()) - - # should enter controls allowed on falling edge and not transitioning to cancel - should_enable = btn_cur != btn_prev and \ - btn_cur != Buttons.CANCEL and \ - btn_prev in (Buttons.RESUME, Buttons.SET) - - self._rx(self._button_msg(btn_cur)) - self.assertEqual(should_enable, self.safety.get_controls_allowed()) - - def test_cancel_button(self): - self.safety.set_controls_allowed(1) - self._rx(self._button_msg(Buttons.CANCEL)) - self.assertFalse(self.safety.get_controls_allowed()) - - def test_tester_present_allowed(self): - """ - Ensure tester present diagnostic message is allowed to keep ECU knocked out - for longitudinal control. - """ - - addr, bus = self.DISABLED_ECU_UDS_MSG - tester_present = libsafety_py.make_CANPacket(addr, bus, b"\x02\x3E\x80\x00\x00\x00\x00\x00") - self.assertTrue(self._tx(tester_present)) - - not_tester_present = libsafety_py.make_CANPacket(addr, bus, b"\x03\xAA\xAA\x00\x00\x00\x00\x00") - self.assertFalse(self._tx(not_tester_present)) - - def test_disabled_ecu_alive(self): - """ - If the ECU knockout failed, make sure the relay malfunction is shown - """ - - addr, bus = self.DISABLED_ECU_ACTUATION_MSG - self.assertFalse(self.safety.get_relay_malfunction()) - self._rx(make_msg(bus, addr, 8)) - self.assertTrue(self.safety.get_relay_malfunction()) - diff --git a/tests/safety/install_mull.sh b/tests/safety/install_mull.sh deleted file mode 100755 index 75b1042ec3..0000000000 --- a/tests/safety/install_mull.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -set -e - -DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" -cd $DIR - -if ! command -v "mull-runner-17" > /dev/null 2>&1; then - sudo apt-get update && sudo apt-get install -y curl clang-17 - curl -1sLf 'https://dl.cloudsmith.io/public/mull-project/mull-stable/setup.deb.sh' | sudo -E bash - sudo apt-get update && sudo apt-get install -y mull-17 -fi diff --git a/tests/safety/mutation.sh b/tests/safety/mutation.sh deleted file mode 100755 index f208a151ad..0000000000 --- a/tests/safety/mutation.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash -set -e - -DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" -cd $DIR - -$DIR/install_mull.sh - -GIT_REF="${GIT_REF:-origin/master}" -GIT_ROOT=$(git rev-parse --show-toplevel) -MULL_OPS="mutators: [cxx_increment, cxx_decrement, cxx_comparison, cxx_boundary, cxx_bitwise_assignment, cxx_bitwise, cxx_arithmetic_assignment, cxx_arithmetic, cxx_remove_negation]" -echo -e "$MULL_OPS" > $GIT_ROOT/mull.yml -scons --mutation -j$(nproc) -D -echo -e "timeout: 10000\ngitDiffRef: $GIT_REF\ngitProjectRoot: $GIT_ROOT" >> $GIT_ROOT/mull.yml - -SAFETY_MODELS=$(find * | grep "^test_.*\.py") -for safety_model in ${SAFETY_MODELS[@]}; do - echo "" - echo "" - echo -e "Testing mutations on : $safety_model" - mull-runner-17 --ld-search-path /lib/x86_64-linux-gnu/ ../libpanda/libpanda.so -test-program=./$safety_model -done diff --git a/tests/safety/test.sh b/tests/safety/test.sh deleted file mode 100755 index 7e89462b6e..0000000000 --- a/tests/safety/test.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash -set -e - -DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" -cd $DIR - -# reset coverage data and generate gcc note file -rm -f ../libsafety/*.gcda -scons -j$(nproc) -D --coverage - -# run safety tests and generate coverage data -pytest test_*.py - -# generate and open report -if [ "$1" == "--report" ]; then - geninfo ../libsafety/ -o coverage.info - genhtml coverage.info -o coverage-out - sensible-browser coverage-out/index.html -fi - -# test coverage -GCOV_OUTPUT=$(gcov -n ../libsafety/safety.c) -INCOMPLETE_COVERAGE=$(echo "$GCOV_OUTPUT" | paste -s -d' \n' | grep -E "File.*(\/safety\/safety_.*)|(safety)\.h" | grep -v "100.00%" || true) -if [ -n "$INCOMPLETE_COVERAGE" ]; then - echo "FAILED: Some files have less than 100% coverage:" - echo "$INCOMPLETE_COVERAGE" - exit 1 -else - echo "SUCCESS: All checked files have 100% coverage!" -fi diff --git a/tests/safety/test_body.py b/tests/safety/test_body.py deleted file mode 100755 index 99a8e0ccd1..0000000000 --- a/tests/safety/test_body.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python3 -import unittest - -import panda.tests.safety.common as common - -from opendbc.safety import Safety -from panda.tests.libsafety import libsafety_py -from panda.tests.safety.common import CANPackerPanda - - -class TestBody(common.PandaSafetyTest): - TX_MSGS = [[0x250, 0], [0x251, 0], [0x350, 0], [0x351, 0], - [0x1, 0], [0x1, 1], [0x1, 2], [0x1, 3]] - - def setUp(self): - self.packer = CANPackerPanda("comma_body") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_BODY, 0) - self.safety.init_tests() - - def _motors_data_msg(self, speed_l, speed_r): - values = {"SPEED_L": speed_l, "SPEED_R": speed_r} - return self.packer.make_can_msg_panda("MOTORS_DATA", 0, values) - - def _torque_cmd_msg(self, torque_l, torque_r): - values = {"TORQUE_L": torque_l, "TORQUE_R": torque_r} - return self.packer.make_can_msg_panda("TORQUE_CMD", 0, values) - - def _knee_torque_cmd_msg(self, torque_l, torque_r): - values = {"TORQUE_L": torque_l, "TORQUE_R": torque_r} - return self.packer.make_can_msg_panda("KNEE_TORQUE_CMD", 0, values) - - def _max_motor_rpm_cmd_msg(self, max_rpm_l, max_rpm_r): - values = {"MAX_RPM_L": max_rpm_l, "MAX_RPM_R": max_rpm_r} - return self.packer.make_can_msg_panda("MAX_MOTOR_RPM_CMD", 0, values) - - def test_rx_hook(self): - self.assertFalse(self.safety.get_controls_allowed()) - self.assertFalse(self.safety.get_vehicle_moving()) - - # controls allowed when we get MOTORS_DATA message - self.assertTrue(self._rx(self._torque_cmd_msg(0, 0))) - self.assertTrue(self.safety.get_vehicle_moving()) # always moving - self.assertFalse(self.safety.get_controls_allowed()) - - self.assertTrue(self._rx(self._motors_data_msg(0, 0))) - self.assertTrue(self.safety.get_vehicle_moving()) # always moving - self.assertTrue(self.safety.get_controls_allowed()) - - def test_tx_hook(self): - self.assertFalse(self._tx(self._torque_cmd_msg(0, 0))) - self.assertFalse(self._tx(self._knee_torque_cmd_msg(0, 0))) - self.safety.set_controls_allowed(True) - self.assertTrue(self._tx(self._torque_cmd_msg(0, 0))) - self.assertTrue(self._tx(self._knee_torque_cmd_msg(0, 0))) - - def test_can_flasher(self): - # CAN flasher always allowed - self.safety.set_controls_allowed(False) - self.assertTrue(self._tx(common.make_msg(0, 0x1, 8))) - - # 0xdeadfaceU enters CAN flashing mode for base & knee - for addr in (0x250, 0x350): - self.assertTrue(self._tx(common.make_msg(0, addr, dat=b'\xce\xfa\xad\xde\x1e\x0b\xb0\x0a'))) - self.assertFalse(self._tx(common.make_msg(0, addr, dat=b'\xce\xfa\xad\xde\x1e\x0b\xb0'))) # not correct data/len - self.assertFalse(self._tx(common.make_msg(0, addr + 1, dat=b'\xce\xfa\xad\xde\x1e\x0b\xb0\x0a'))) # wrong address - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/safety/test_chrysler.py b/tests/safety/test_chrysler.py deleted file mode 100755 index 54c89c40d8..0000000000 --- a/tests/safety/test_chrysler.py +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env python3 -import unittest - -from opendbc.car.chrysler.values import ChryslerSafetyFlags -from opendbc.safety import Safety -from panda.tests.libsafety import libsafety_py -import panda.tests.safety.common as common -from panda.tests.safety.common import CANPackerPanda - - -class TestChryslerSafety(common.PandaCarSafetyTest, common.MotorTorqueSteeringSafetyTest): - TX_MSGS = [[0x23B, 0], [0x292, 0], [0x2A6, 0]] - STANDSTILL_THRESHOLD = 0 - RELAY_MALFUNCTION_ADDRS = {0: (0x292,)} - FWD_BLACKLISTED_ADDRS = {2: [0x292, 0x2A6]} - FWD_BUS_LOOKUP = {0: 2, 2: 0} - - MAX_RATE_UP = 3 - MAX_RATE_DOWN = 3 - MAX_TORQUE = 261 - MAX_RT_DELTA = 112 - RT_INTERVAL = 250000 - MAX_TORQUE_ERROR = 80 - - LKAS_ACTIVE_VALUE = 1 - - DAS_BUS = 0 - - def setUp(self): - self.packer = CANPackerPanda("chrysler_pacifica_2017_hybrid_generated") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_CHRYSLER, 0) - self.safety.init_tests() - - def _button_msg(self, cancel=False, resume=False): - values = {"ACC_Cancel": cancel, "ACC_Resume": resume} - return self.packer.make_can_msg_panda("CRUISE_BUTTONS", self.DAS_BUS, values) - - def _pcm_status_msg(self, enable): - values = {"ACC_ACTIVE": enable} - return self.packer.make_can_msg_panda("DAS_3", self.DAS_BUS, values) - - def _speed_msg(self, speed): - values = {"SPEED_LEFT": speed, "SPEED_RIGHT": speed} - return self.packer.make_can_msg_panda("SPEED_1", 0, values) - - def _user_gas_msg(self, gas): - values = {"Accelerator_Position": gas} - return self.packer.make_can_msg_panda("ECM_5", 0, values) - - def _user_brake_msg(self, brake): - values = {"Brake_Pedal_State": 1 if brake else 0} - return self.packer.make_can_msg_panda("ESP_1", 0, values) - - def _torque_meas_msg(self, torque): - values = {"EPS_TORQUE_MOTOR": torque} - return self.packer.make_can_msg_panda("EPS_2", 0, values) - - def _torque_cmd_msg(self, torque, steer_req=1): - values = {"STEERING_TORQUE": torque, "LKAS_CONTROL_BIT": self.LKAS_ACTIVE_VALUE if steer_req else 0} - return self.packer.make_can_msg_panda("LKAS_COMMAND", 0, values) - - def test_buttons(self): - for controls_allowed in (True, False): - self.safety.set_controls_allowed(controls_allowed) - - # resume only while controls allowed - self.assertEqual(controls_allowed, self._tx(self._button_msg(resume=True))) - - # can always cancel - self.assertTrue(self._tx(self._button_msg(cancel=True))) - - # only one button at a time - self.assertFalse(self._tx(self._button_msg(cancel=True, resume=True))) - self.assertFalse(self._tx(self._button_msg(cancel=False, resume=False))) - - -class TestChryslerRamDTSafety(TestChryslerSafety): - TX_MSGS = [[0xB1, 2], [0xA6, 0], [0xFA, 0]] - RELAY_MALFUNCTION_ADDRS = {0: (0xA6,)} - FWD_BLACKLISTED_ADDRS = {2: [0xA6, 0xFA]} - - MAX_RATE_UP = 6 - MAX_RATE_DOWN = 6 - MAX_TORQUE = 350 - - DAS_BUS = 2 - - LKAS_ACTIVE_VALUE = 2 - - def setUp(self): - self.packer = CANPackerPanda("chrysler_ram_dt_generated") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_CHRYSLER, ChryslerSafetyFlags.FLAG_CHRYSLER_RAM_DT) - self.safety.init_tests() - - def _speed_msg(self, speed): - values = {"Vehicle_Speed": speed} - return self.packer.make_can_msg_panda("ESP_8", 0, values) - -class TestChryslerRamHDSafety(TestChryslerSafety): - TX_MSGS = [[0x275, 0], [0x276, 0], [0x23A, 2]] - RELAY_MALFUNCTION_ADDRS = {0: (0x276,)} - FWD_BLACKLISTED_ADDRS = {2: [0x275, 0x276]} - - MAX_TORQUE = 361 - MAX_RATE_UP = 14 - MAX_RATE_DOWN = 14 - MAX_RT_DELTA = 182 - - DAS_BUS = 2 - - LKAS_ACTIVE_VALUE = 2 - - def setUp(self): - self.packer = CANPackerPanda("chrysler_ram_hd_generated") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_CHRYSLER, ChryslerSafetyFlags.FLAG_CHRYSLER_RAM_HD) - self.safety.init_tests() - - def _speed_msg(self, speed): - values = {"Vehicle_Speed": speed} - return self.packer.make_can_msg_panda("ESP_8", 0, values) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/safety/test_defaults.py b/tests/safety/test_defaults.py deleted file mode 100755 index d7627252cc..0000000000 --- a/tests/safety/test_defaults.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python3 -import unittest - -import panda.tests.safety.common as common - -from opendbc.safety import Safety -from panda.tests.libsafety import libsafety_py - - -class TestDefaultRxHookBase(common.PandaSafetyTest): - def test_rx_hook(self): - # default rx hook allows all msgs - for bus in range(4): - for addr in self.SCANNED_ADDRS: - self.assertTrue(self._rx(common.make_msg(bus, addr, 8)), f"failed RX {addr=}") - - -class TestNoOutput(TestDefaultRxHookBase): - TX_MSGS = [] - - def setUp(self): - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_NOOUTPUT, 0) - self.safety.init_tests() - - -class TestSilent(TestNoOutput): - """SILENT uses same hooks as NOOUTPUT""" - - def setUp(self): - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_SILENT, 0) - self.safety.init_tests() - - -class TestAllOutput(TestDefaultRxHookBase): - # Allow all messages - TX_MSGS = [[addr, bus] for addr in common.PandaSafetyTest.SCANNED_ADDRS - for bus in range(4)] - - def setUp(self): - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_ALLOUTPUT, 0) - self.safety.init_tests() - - def test_spam_can_buses(self): - # asserts tx allowed for all scanned addrs - for bus in range(4): - for addr in self.SCANNED_ADDRS: - should_tx = [addr, bus] in self.TX_MSGS - self.assertEqual(should_tx, self._tx(common.make_msg(bus, addr, 8)), f"allowed TX {addr=} {bus=}") - - def test_default_controls_not_allowed(self): - # controls always allowed - self.assertTrue(self.safety.get_controls_allowed()) - - def test_tx_hook_on_wrong_safety_mode(self): - # No point, since we allow all messages - pass - - -class TestAllOutputPassthrough(TestAllOutput): - FWD_BLACKLISTED_ADDRS = {} - FWD_BUS_LOOKUP = {0: 2, 2: 0} - - def setUp(self): - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_ALLOUTPUT, 1) - self.safety.init_tests() - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/safety/test_elm327.py b/tests/safety/test_elm327.py deleted file mode 100755 index 81b83776d5..0000000000 --- a/tests/safety/test_elm327.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python3 -import unittest - -import panda.tests.safety.common as common - -from opendbc.safety import Safety -from panda import DLC_TO_LEN -from panda.tests.libsafety import libsafety_py -from panda.tests.safety.test_defaults import TestDefaultRxHookBase - -GM_CAMERA_DIAG_ADDR = 0x24B - - -class TestElm327(TestDefaultRxHookBase): - TX_MSGS = [[addr, bus] for addr in [GM_CAMERA_DIAG_ADDR, *range(0x600, 0x800), - *range(0x18DA00F1, 0x18DB00F1, 0x100), # 29-bit UDS physical addressing - *[0x18DB33F1], # 29-bit UDS functional address - ] for bus in range(4)] - - def setUp(self): - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_ELM327, 0) - self.safety.init_tests() - - def test_tx_hook(self): - # ensure we can transmit arbitrary data on allowed addresses - for bus in range(4): - for addr in self.SCANNED_ADDRS: - should_tx = [addr, bus] in self.TX_MSGS - self.assertEqual(should_tx, self._tx(common.make_msg(bus, addr, 8))) - - # ELM only allows 8 byte UDS/KWP messages under ISO 15765-4 - for msg_len in DLC_TO_LEN: - should_tx = msg_len == 8 - self.assertEqual(should_tx, self._tx(common.make_msg(0, 0x700, msg_len))) - - # TODO: perform this check for all addresses - # 4 to 15 are reserved ISO-TP frame types (https://en.wikipedia.org/wiki/ISO_15765-2) - for byte in range(0xff): - should_tx = (byte >> 4) <= 3 - self.assertEqual(should_tx, self._tx(common.make_msg(0, GM_CAMERA_DIAG_ADDR, dat=bytes([byte] * 8)))) - - def test_tx_hook_on_wrong_safety_mode(self): - # No point, since we allow many diagnostic addresses - pass - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/safety/test_ford.py b/tests/safety/test_ford.py deleted file mode 100755 index 3bf54d467d..0000000000 --- a/tests/safety/test_ford.py +++ /dev/null @@ -1,466 +0,0 @@ -#!/usr/bin/env python3 -import numpy as np -import random -import unittest - -import panda.tests.safety.common as common -from opendbc.car.ford.values import FordSafetyFlags - -from opendbc.safety import Safety -from panda.tests.libsafety import libsafety_py -from panda.tests.safety.common import CANPackerPanda - -MSG_EngBrakeData = 0x165 # RX from PCM, for driver brake pedal and cruise state -MSG_EngVehicleSpThrottle = 0x204 # RX from PCM, for driver throttle input -MSG_BrakeSysFeatures = 0x415 # RX from ABS, for vehicle speed -MSG_EngVehicleSpThrottle2 = 0x202 # RX from PCM, for second vehicle speed -MSG_Yaw_Data_FD1 = 0x91 # RX from RCM, for yaw rate -MSG_Steering_Data_FD1 = 0x083 # TX by OP, various driver switches and LKAS/CC buttons -MSG_ACCDATA = 0x186 # TX by OP, ACC controls -MSG_ACCDATA_3 = 0x18A # TX by OP, ACC/TJA user interface -MSG_Lane_Assist_Data1 = 0x3CA # TX by OP, Lane Keep Assist -MSG_LateralMotionControl = 0x3D3 # TX by OP, Lateral Control message -MSG_LateralMotionControl2 = 0x3D6 # TX by OP, alternate Lateral Control message -MSG_IPMA_Data = 0x3D8 # TX by OP, IPMA and LKAS user interface - - -def checksum(msg): - addr, dat, bus = msg - ret = bytearray(dat) - - if addr == MSG_Yaw_Data_FD1: - chksum = dat[0] + dat[1] # VehRol_W_Actl - chksum += dat[2] + dat[3] # VehYaw_W_Actl - chksum += dat[5] # VehRollYaw_No_Cnt - chksum += dat[6] >> 6 # VehRolWActl_D_Qf - chksum += (dat[6] >> 4) & 0x3 # VehYawWActl_D_Qf - chksum = 0xff - (chksum & 0xff) - ret[4] = chksum - - elif addr == MSG_BrakeSysFeatures: - chksum = dat[0] + dat[1] # Veh_V_ActlBrk - chksum += (dat[2] >> 2) & 0xf # VehVActlBrk_No_Cnt - chksum += dat[2] >> 6 # VehVActlBrk_D_Qf - chksum = 0xff - (chksum & 0xff) - ret[3] = chksum - - elif addr == MSG_EngVehicleSpThrottle2: - chksum = (dat[2] >> 3) & 0xf # VehVActlEng_No_Cnt - chksum += (dat[4] >> 5) & 0x3 # VehVActlEng_D_Qf - chksum += dat[6] + dat[7] # Veh_V_ActlEng - chksum = 0xff - (chksum & 0xff) - ret[1] = chksum - - return addr, ret, bus - - -class Buttons: - CANCEL = 0 - RESUME = 1 - TJA_TOGGLE = 2 - - -# Ford safety has four different configurations tested here: -# * CAN with openpilot longitudinal -# * CAN FD with stock longitudinal -# * CAN FD with openpilot longitudinal - -class TestFordSafetyBase(common.PandaCarSafetyTest): - STANDSTILL_THRESHOLD = 1 - RELAY_MALFUNCTION_ADDRS = {0: (MSG_ACCDATA_3, MSG_Lane_Assist_Data1, MSG_LateralMotionControl, - MSG_LateralMotionControl2, MSG_IPMA_Data)} - - FWD_BLACKLISTED_ADDRS = {2: [MSG_ACCDATA_3, MSG_Lane_Assist_Data1, MSG_LateralMotionControl, - MSG_LateralMotionControl2, MSG_IPMA_Data]} - FWD_BUS_LOOKUP = {0: 2, 2: 0} - - # Max allowed delta between car speeds - MAX_SPEED_DELTA = 2.0 # m/s - - STEER_MESSAGE = 0 - - # Curvature control limits - DEG_TO_CAN = 50000 # 1 / (2e-5) rad to can - MAX_CURVATURE = 0.02 - MAX_CURVATURE_ERROR = 0.002 - CURVATURE_ERROR_MIN_SPEED = 10.0 # m/s - - ANGLE_RATE_BP = [5., 25., 25.] - ANGLE_RATE_UP = [0.00045, 0.0001, 0.0001] # windup limit - ANGLE_RATE_DOWN = [0.00045, 0.00015, 0.00015] # unwind limit - - cnt_speed = 0 - cnt_speed_2 = 0 - cnt_yaw_rate = 0 - - packer: CANPackerPanda - safety: libsafety_py.Panda - - @classmethod - def setUpClass(cls): - if cls.__name__ == "TestFordSafetyBase": - raise unittest.SkipTest - - def _set_prev_desired_angle(self, t): - t = round(t * self.DEG_TO_CAN) - self.safety.set_desired_angle_last(t) - - def _reset_curvature_measurement(self, curvature, speed): - for _ in range(6): - self._rx(self._speed_msg(speed)) - self._rx(self._yaw_rate_msg(curvature, speed)) - - # Driver brake pedal - def _user_brake_msg(self, brake: bool): - # brake pedal and cruise state share same message, so we have to send - # the other signal too - enable = self.safety.get_controls_allowed() - values = { - "BpedDrvAppl_D_Actl": 2 if brake else 1, - "CcStat_D_Actl": 5 if enable else 0, - } - return self.packer.make_can_msg_panda("EngBrakeData", 0, values) - - # ABS vehicle speed - def _speed_msg(self, speed: float, quality_flag=True): - values = {"Veh_V_ActlBrk": speed * 3.6, "VehVActlBrk_D_Qf": 3 if quality_flag else 0, "VehVActlBrk_No_Cnt": self.cnt_speed % 16} - self.__class__.cnt_speed += 1 - return self.packer.make_can_msg_panda("BrakeSysFeatures", 0, values, fix_checksum=checksum) - - # PCM vehicle speed - def _speed_msg_2(self, speed: float, quality_flag=True): - values = {"Veh_V_ActlEng": speed * 3.6, "VehVActlEng_D_Qf": 3 if quality_flag else 0, "VehVActlEng_No_Cnt": self.cnt_speed_2 % 16} - self.__class__.cnt_speed_2 += 1 - return self.packer.make_can_msg_panda("EngVehicleSpThrottle2", 0, values, fix_checksum=checksum) - - # Standstill state - def _vehicle_moving_msg(self, speed: float): - values = {"VehStop_D_Stat": 1 if speed <= self.STANDSTILL_THRESHOLD else random.choice((0, 2, 3))} - return self.packer.make_can_msg_panda("DesiredTorqBrk", 0, values) - - # Current curvature - def _yaw_rate_msg(self, curvature: float, speed: float, quality_flag=True): - values = {"VehYaw_W_Actl": curvature * speed, "VehYawWActl_D_Qf": 3 if quality_flag else 0, - "VehRollYaw_No_Cnt": self.cnt_yaw_rate % 256} - self.__class__.cnt_yaw_rate += 1 - return self.packer.make_can_msg_panda("Yaw_Data_FD1", 0, values, fix_checksum=checksum) - - # Drive throttle input - def _user_gas_msg(self, gas: float): - values = {"ApedPos_Pc_ActlArb": gas} - return self.packer.make_can_msg_panda("EngVehicleSpThrottle", 0, values) - - # Cruise status - def _pcm_status_msg(self, enable: bool): - # brake pedal and cruise state share same message, so we have to send - # the other signal too - brake = self.safety.get_brake_pressed_prev() - values = { - "BpedDrvAppl_D_Actl": 2 if brake else 1, - "CcStat_D_Actl": 5 if enable else 0, - } - return self.packer.make_can_msg_panda("EngBrakeData", 0, values) - - # LKAS command - def _lkas_command_msg(self, action: int): - values = { - "LkaActvStats_D2_Req": action, - } - return self.packer.make_can_msg_panda("Lane_Assist_Data1", 0, values) - - # LCA command - def _lat_ctl_msg(self, enabled: bool, path_offset: float, path_angle: float, curvature: float, curvature_rate: float): - if self.STEER_MESSAGE == MSG_LateralMotionControl: - values = { - "LatCtl_D_Rq": 1 if enabled else 0, - "LatCtlPathOffst_L_Actl": path_offset, # Path offset [-5.12|5.11] meter - "LatCtlPath_An_Actl": path_angle, # Path angle [-0.5|0.5235] radians - "LatCtlCurv_NoRate_Actl": curvature_rate, # Curvature rate [-0.001024|0.00102375] 1/meter^2 - "LatCtlCurv_No_Actl": curvature, # Curvature [-0.02|0.02094] 1/meter - } - return self.packer.make_can_msg_panda("LateralMotionControl", 0, values) - elif self.STEER_MESSAGE == MSG_LateralMotionControl2: - values = { - "LatCtl_D2_Rq": 1 if enabled else 0, - "LatCtlPathOffst_L_Actl": path_offset, # Path offset [-5.12|5.11] meter - "LatCtlPath_An_Actl": path_angle, # Path angle [-0.5|0.5235] radians - "LatCtlCrv_NoRate2_Actl": curvature_rate, # Curvature rate [-0.001024|0.001023] 1/meter^2 - "LatCtlCurv_No_Actl": curvature, # Curvature [-0.02|0.02094] 1/meter - } - return self.packer.make_can_msg_panda("LateralMotionControl2", 0, values) - - # Cruise control buttons - def _acc_button_msg(self, button: int, bus: int): - values = { - "CcAslButtnCnclPress": 1 if button == Buttons.CANCEL else 0, - "CcAsllButtnResPress": 1 if button == Buttons.RESUME else 0, - "TjaButtnOnOffPress": 1 if button == Buttons.TJA_TOGGLE else 0, - } - return self.packer.make_can_msg_panda("Steering_Data_FD1", bus, values) - - def test_rx_hook(self): - # checksum, counter, and quality flag checks - for quality_flag in [True, False]: - for msg in ["speed", "speed_2", "yaw"]: - self.safety.set_controls_allowed(True) - # send multiple times to verify counter checks - for _ in range(10): - if msg == "speed": - to_push = self._speed_msg(0, quality_flag=quality_flag) - elif msg == "speed_2": - to_push = self._speed_msg_2(0, quality_flag=quality_flag) - elif msg == "yaw": - to_push = self._yaw_rate_msg(0, 0, quality_flag=quality_flag) - - self.assertEqual(quality_flag, self._rx(to_push)) - self.assertEqual(quality_flag, self.safety.get_controls_allowed()) - - # Mess with checksum to make it fail, checksum is not checked for 2nd speed - to_push[0].data[3] = 0 # Speed checksum & half of yaw signal - should_rx = msg == "speed_2" and quality_flag - self.assertEqual(should_rx, self._rx(to_push)) - self.assertEqual(should_rx, self.safety.get_controls_allowed()) - - def test_rx_hook_speed_mismatch(self): - # Ford relies on speed for driver curvature limiting, so it checks two sources - for speed in np.arange(0, 40, 0.5): - for speed_delta in np.arange(-5, 5, 0.1): - speed_2 = round(max(speed + speed_delta, 0), 1) - # Set controls allowed in between rx since first message can reset it - self._rx(self._speed_msg(speed)) - self.safety.set_controls_allowed(True) - self._rx(self._speed_msg_2(speed_2)) - - within_delta = abs(speed - speed_2) <= self.MAX_SPEED_DELTA - self.assertEqual(self.safety.get_controls_allowed(), within_delta) - - def test_angle_measurements(self): - """Tests rx hook correctly parses the curvature measurement from the vehicle speed and yaw rate""" - for speed in np.arange(0.5, 40, 0.5): - for curvature in np.arange(0, self.MAX_CURVATURE * 2, 2e-3): - self._rx(self._speed_msg(speed)) - for c in (curvature, -curvature, 0, 0, 0, 0): - self._rx(self._yaw_rate_msg(c, speed)) - - self.assertEqual(self.safety.get_angle_meas_min(), round(-curvature * self.DEG_TO_CAN)) - self.assertEqual(self.safety.get_angle_meas_max(), round(curvature * self.DEG_TO_CAN)) - - self._rx(self._yaw_rate_msg(0, speed)) - self.assertEqual(self.safety.get_angle_meas_min(), round(-curvature * self.DEG_TO_CAN)) - self.assertEqual(self.safety.get_angle_meas_max(), 0) - - self._rx(self._yaw_rate_msg(0, speed)) - self.assertEqual(self.safety.get_angle_meas_min(), 0) - self.assertEqual(self.safety.get_angle_meas_max(), 0) - - def test_steer_allowed(self): - path_offsets = np.arange(-5.12, 5.11, 1).round() - path_angles = np.arange(-0.5, 0.5235, 0.1).round(1) - curvature_rates = np.arange(-0.001024, 0.00102375, 0.001).round(3) - curvatures = np.arange(-0.02, 0.02094, 0.01).round(2) - - for speed in (self.CURVATURE_ERROR_MIN_SPEED - 1, - self.CURVATURE_ERROR_MIN_SPEED + 1): - for controls_allowed in (True, False): - for steer_control_enabled in (True, False): - for path_offset in path_offsets: - for path_angle in path_angles: - for curvature_rate in curvature_rates: - for curvature in curvatures: - self.safety.set_controls_allowed(controls_allowed) - self._set_prev_desired_angle(curvature) - self._reset_curvature_measurement(curvature, speed) - - should_tx = path_offset == 0 and path_angle == 0 and curvature_rate == 0 - # when request bit is 0, only allow curvature of 0 since the signal range - # is not large enough to enforce it tracking measured - should_tx = should_tx and (controls_allowed if steer_control_enabled else curvature == 0) - with self.subTest(controls_allowed=controls_allowed, steer_control_enabled=steer_control_enabled, - path_offset=path_offset, path_angle=path_angle, curvature_rate=curvature_rate, - curvature=curvature): - self.assertEqual(should_tx, self._tx(self._lat_ctl_msg(steer_control_enabled, path_offset, path_angle, curvature, curvature_rate))) - - def test_curvature_rate_limit_up(self): - """ - When the curvature error is exceeded, commanded curvature must start moving towards meas respecting rate limits. - Since panda allows higher rate limits to avoid false positives, we need to allow a lower rate to move towards meas. - """ - self.safety.set_controls_allowed(True) - small_curvature = 2 / self.DEG_TO_CAN # significant small amount of curvature to cross boundary - - for speed in np.arange(0, 40, 0.5): - limit_command = speed > self.CURVATURE_ERROR_MIN_SPEED - max_delta_up = np.interp(speed - 1, self.ANGLE_RATE_BP, self.ANGLE_RATE_UP) - max_delta_up_lower = np.interp(speed + 1, self.ANGLE_RATE_BP, self.ANGLE_RATE_UP) - - cases = [ - (not limit_command, 0), - (not limit_command, max_delta_up_lower - small_curvature), - (True, max_delta_up_lower), - (True, max_delta_up), - (False, max_delta_up + small_curvature), - ] - - for sign in (-1, 1): - self._reset_curvature_measurement(sign * (self.MAX_CURVATURE_ERROR + 1e-3), speed) - for should_tx, curvature in cases: - self._set_prev_desired_angle(sign * small_curvature) - self.assertEqual(should_tx, self._tx(self._lat_ctl_msg(True, 0, 0, sign * (small_curvature + curvature), 0))) - - def test_curvature_rate_limit_down(self): - self.safety.set_controls_allowed(True) - small_curvature = 2 / self.DEG_TO_CAN # significant small amount of curvature to cross boundary - - for speed in np.arange(0, 40, 0.5): - limit_command = speed > self.CURVATURE_ERROR_MIN_SPEED - max_delta_down = np.interp(speed - 1, self.ANGLE_RATE_BP, self.ANGLE_RATE_DOWN) - max_delta_down_lower = np.interp(speed + 1, self.ANGLE_RATE_BP, self.ANGLE_RATE_DOWN) - - cases = [ - (not limit_command, self.MAX_CURVATURE), - (not limit_command, self.MAX_CURVATURE - max_delta_down_lower + small_curvature), - (True, self.MAX_CURVATURE - max_delta_down_lower), - (True, self.MAX_CURVATURE - max_delta_down), - (False, self.MAX_CURVATURE - max_delta_down - small_curvature), - ] - - for sign in (-1, 1): - self._reset_curvature_measurement(sign * (self.MAX_CURVATURE - self.MAX_CURVATURE_ERROR - 1e-3), speed) - for should_tx, curvature in cases: - self._set_prev_desired_angle(sign * self.MAX_CURVATURE) - self.assertEqual(should_tx, self._tx(self._lat_ctl_msg(True, 0, 0, sign * curvature, 0))) - - def test_prevent_lkas_action(self): - self.safety.set_controls_allowed(1) - self.assertFalse(self._tx(self._lkas_command_msg(1))) - - self.safety.set_controls_allowed(0) - self.assertFalse(self._tx(self._lkas_command_msg(1))) - - def test_acc_buttons(self): - for allowed in (0, 1): - self.safety.set_controls_allowed(allowed) - for enabled in (True, False): - self._rx(self._pcm_status_msg(enabled)) - self.assertTrue(self._tx(self._acc_button_msg(Buttons.TJA_TOGGLE, 2))) - - for allowed in (0, 1): - self.safety.set_controls_allowed(allowed) - for bus in (0, 2): - self.assertEqual(allowed, self._tx(self._acc_button_msg(Buttons.RESUME, bus))) - - for enabled in (True, False): - self._rx(self._pcm_status_msg(enabled)) - for bus in (0, 2): - self.assertEqual(enabled, self._tx(self._acc_button_msg(Buttons.CANCEL, bus))) - - -class TestFordCANFDStockSafety(TestFordSafetyBase): - STEER_MESSAGE = MSG_LateralMotionControl2 - - TX_MSGS = [ - [MSG_Steering_Data_FD1, 0], [MSG_Steering_Data_FD1, 2], [MSG_ACCDATA_3, 0], [MSG_Lane_Assist_Data1, 0], - [MSG_LateralMotionControl2, 0], [MSG_IPMA_Data, 0], - ] - - def setUp(self): - self.packer = CANPackerPanda("ford_lincoln_base_pt") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_FORD, FordSafetyFlags.FLAG_FORD_CANFD) - self.safety.init_tests() - - -class TestFordLongitudinalSafetyBase(TestFordSafetyBase): - RELAY_MALFUNCTION_ADDRS = {0: (MSG_ACCDATA, MSG_ACCDATA_3, MSG_Lane_Assist_Data1, MSG_LateralMotionControl, - MSG_LateralMotionControl2, MSG_IPMA_Data)} - - FWD_BLACKLISTED_ADDRS = {2: [MSG_ACCDATA, MSG_ACCDATA_3, MSG_Lane_Assist_Data1, MSG_LateralMotionControl, - MSG_LateralMotionControl2, MSG_IPMA_Data]} - - MAX_ACCEL = 2.0 # accel is used for brakes, but openpilot can set positive values - MIN_ACCEL = -3.5 - INACTIVE_ACCEL = 0.0 - - MAX_GAS = 2.0 - MIN_GAS = -0.5 - INACTIVE_GAS = -5.0 - - @classmethod - def setUpClass(cls): - if cls.__name__ == "TestFordLongitudinalSafetyBase": - raise unittest.SkipTest - - # ACC command - def _acc_command_msg(self, gas: float, brake: float, brake_actuation: bool, cmbb_deny: bool = False): - values = { - "AccPrpl_A_Rq": gas, # [-5|5.23] m/s^2 - "AccPrpl_A_Pred": gas, # [-5|5.23] m/s^2 - "AccBrkTot_A_Rq": brake, # [-20|11.9449] m/s^2 - "AccBrkPrchg_B_Rq": 1 if brake_actuation else 0, # Pre-charge brake request: 0=No, 1=Yes - "AccBrkDecel_B_Rq": 1 if brake_actuation else 0, # Deceleration request: 0=Inactive, 1=Active - "CmbbDeny_B_Actl": 1 if cmbb_deny else 0, # [0|1] deny AEB actuation - } - return self.packer.make_can_msg_panda("ACCDATA", 0, values) - - def test_stock_aeb(self): - # Test that CmbbDeny_B_Actl is never 1, it prevents the ABS module from actuating AEB requests from ACCDATA_2 - for controls_allowed in (True, False): - self.safety.set_controls_allowed(controls_allowed) - for cmbb_deny in (True, False): - should_tx = not cmbb_deny - self.assertEqual(should_tx, self._tx(self._acc_command_msg(self.INACTIVE_GAS, self.INACTIVE_ACCEL, controls_allowed, cmbb_deny))) - should_tx = controls_allowed and not cmbb_deny - self.assertEqual(should_tx, self._tx(self._acc_command_msg(self.MAX_GAS, self.MAX_ACCEL, controls_allowed, cmbb_deny))) - - def test_gas_safety_check(self): - for controls_allowed in (True, False): - self.safety.set_controls_allowed(controls_allowed) - for gas in np.concatenate((np.arange(self.MIN_GAS - 2, self.MAX_GAS + 2, 0.05), [self.INACTIVE_GAS])): - gas = round(gas, 2) # floats might not hit exact boundary conditions without rounding - should_tx = (controls_allowed and self.MIN_GAS <= gas <= self.MAX_GAS) or gas == self.INACTIVE_GAS - self.assertEqual(should_tx, self._tx(self._acc_command_msg(gas, self.INACTIVE_ACCEL, controls_allowed))) - - def test_brake_safety_check(self): - for controls_allowed in (True, False): - self.safety.set_controls_allowed(controls_allowed) - for brake_actuation in (True, False): - for brake in np.arange(self.MIN_ACCEL - 2, self.MAX_ACCEL + 2, 0.05): - brake = round(brake, 2) # floats might not hit exact boundary conditions without rounding - should_tx = (controls_allowed and self.MIN_ACCEL <= brake <= self.MAX_ACCEL) or brake == self.INACTIVE_ACCEL - should_tx = should_tx and (controls_allowed or not brake_actuation) - self.assertEqual(should_tx, self._tx(self._acc_command_msg(self.INACTIVE_GAS, brake, brake_actuation))) - - -class TestFordLongitudinalSafety(TestFordLongitudinalSafetyBase): - STEER_MESSAGE = MSG_LateralMotionControl - - TX_MSGS = [ - [MSG_Steering_Data_FD1, 0], [MSG_Steering_Data_FD1, 2], [MSG_ACCDATA, 0], [MSG_ACCDATA_3, 0], [MSG_Lane_Assist_Data1, 0], - [MSG_LateralMotionControl, 0], [MSG_IPMA_Data, 0], - ] - - def setUp(self): - self.packer = CANPackerPanda("ford_lincoln_base_pt") - self.safety = libsafety_py.libsafety - # Make sure we enforce long safety even without long flag for CAN - self.safety.set_safety_hooks(Safety.SAFETY_FORD, 0) - self.safety.init_tests() - - -class TestFordCANFDLongitudinalSafety(TestFordLongitudinalSafetyBase): - STEER_MESSAGE = MSG_LateralMotionControl2 - - TX_MSGS = [ - [MSG_Steering_Data_FD1, 0], [MSG_Steering_Data_FD1, 2], [MSG_ACCDATA, 0], [MSG_ACCDATA_3, 0], [MSG_Lane_Assist_Data1, 0], - [MSG_LateralMotionControl2, 0], [MSG_IPMA_Data, 0], - ] - - def setUp(self): - self.packer = CANPackerPanda("ford_lincoln_base_pt") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_FORD, FordSafetyFlags.FLAG_FORD_LONG_CONTROL | FordSafetyFlags.FLAG_FORD_CANFD) - self.safety.init_tests() - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/safety/test_gm.py b/tests/safety/test_gm.py deleted file mode 100755 index 4a8ee25137..0000000000 --- a/tests/safety/test_gm.py +++ /dev/null @@ -1,229 +0,0 @@ -#!/usr/bin/env python3 -import unittest - -from opendbc.car.gm.values import GMSafetyFlags -from opendbc.safety import Safety -from panda.tests.libsafety import libsafety_py -import panda.tests.safety.common as common -from panda.tests.safety.common import CANPackerPanda - - -class Buttons: - UNPRESS = 1 - RES_ACCEL = 2 - DECEL_SET = 3 - CANCEL = 6 - - -class GmLongitudinalBase(common.PandaCarSafetyTest, common.LongitudinalGasBrakeSafetyTest): - # pylint: disable=no-member,abstract-method - - RELAY_MALFUNCTION_ADDRS = {0: (0x180, 0x2CB)} # ASCMLKASteeringCmd, ASCMGasRegenCmd - - MAX_POSSIBLE_BRAKE = 2 ** 12 - MAX_BRAKE = 400 - - MAX_POSSIBLE_GAS = 2 ** 12 - - PCM_CRUISE = False # openpilot can control the PCM state if longitudinal - - def _send_brake_msg(self, brake): - values = {"FrictionBrakeCmd": -brake} - return self.packer_chassis.make_can_msg_panda("EBCMFrictionBrakeCmd", self.BRAKE_BUS, values) - - def _send_gas_msg(self, gas): - values = {"GasRegenCmd": gas} - return self.packer.make_can_msg_panda("ASCMGasRegenCmd", 0, values) - - # override these tests from PandaCarSafetyTest, GM longitudinal uses button enable - def _pcm_status_msg(self, enable): - raise NotImplementedError - - def test_disable_control_allowed_from_cruise(self): - pass - - def test_enable_control_allowed_from_cruise(self): - pass - - def test_cruise_engaged_prev(self): - pass - - def test_set_resume_buttons(self): - """ - SET and RESUME enter controls allowed on their falling and rising edges, respectively. - """ - for btn_prev in range(8): - for btn_cur in range(8): - with self.subTest(btn_prev=btn_prev, btn_cur=btn_cur): - self._rx(self._button_msg(btn_prev)) - self.safety.set_controls_allowed(0) - for _ in range(10): - self._rx(self._button_msg(btn_cur)) - - should_enable = btn_cur != Buttons.DECEL_SET and btn_prev == Buttons.DECEL_SET - should_enable = should_enable or (btn_cur == Buttons.RES_ACCEL and btn_prev != Buttons.RES_ACCEL) - should_enable = should_enable and btn_cur != Buttons.CANCEL - self.assertEqual(should_enable, self.safety.get_controls_allowed()) - - def test_cancel_button(self): - self.safety.set_controls_allowed(1) - self._rx(self._button_msg(Buttons.CANCEL)) - self.assertFalse(self.safety.get_controls_allowed()) - - -class TestGmSafetyBase(common.PandaCarSafetyTest, common.DriverTorqueSteeringSafetyTest): - STANDSTILL_THRESHOLD = 10 * 0.0311 - # Ensures ASCM is off on ASCM cars, and relay is not malfunctioning for camera-ACC cars - RELAY_MALFUNCTION_ADDRS = {0: (0x180,)} # ASCMLKASteeringCmd - BUTTONS_BUS = 0 # rx or tx - BRAKE_BUS = 0 # tx only - - MAX_RATE_UP = 10 - MAX_RATE_DOWN = 15 - MAX_TORQUE = 300 - MAX_RT_DELTA = 128 - RT_INTERVAL = 250000 - DRIVER_TORQUE_ALLOWANCE = 65 - DRIVER_TORQUE_FACTOR = 4 - - PCM_CRUISE = True # openpilot is tied to the PCM state if not longitudinal - - @classmethod - def setUpClass(cls): - if cls.__name__ == "TestGmSafetyBase": - cls.packer = None - cls.safety = None - raise unittest.SkipTest - - def setUp(self): - self.packer = CANPackerPanda("gm_global_a_powertrain_generated") - self.packer_chassis = CANPackerPanda("gm_global_a_chassis") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_GM, 0) - self.safety.init_tests() - - def _pcm_status_msg(self, enable): - if self.PCM_CRUISE: - values = {"CruiseState": enable} - return self.packer.make_can_msg_panda("AcceleratorPedal2", 0, values) - else: - raise NotImplementedError - - def _speed_msg(self, speed): - values = {"%sWheelSpd" % s: speed for s in ["RL", "RR"]} - return self.packer.make_can_msg_panda("EBCMWheelSpdRear", 0, values) - - def _user_brake_msg(self, brake): - # GM safety has a brake threshold of 8 - values = {"BrakePedalPos": 8 if brake else 0} - return self.packer.make_can_msg_panda("ECMAcceleratorPos", 0, values) - - def _user_regen_msg(self, regen): - values = {"RegenPaddle": 2 if regen else 0} - return self.packer.make_can_msg_panda("EBCMRegenPaddle", 0, values) - - def _user_gas_msg(self, gas): - values = {"AcceleratorPedal2": 1 if gas else 0} - if self.PCM_CRUISE: - # Fill CruiseState with expected value if the safety mode reads cruise state from gas msg - values["CruiseState"] = self.safety.get_controls_allowed() - return self.packer.make_can_msg_panda("AcceleratorPedal2", 0, values) - - def _torque_driver_msg(self, torque): - # Safety tests assume driver torque is an int, use DBC factor - values = {"LKADriverAppldTrq": torque * 0.01} - return self.packer.make_can_msg_panda("PSCMStatus", 0, values) - - def _torque_cmd_msg(self, torque, steer_req=1): - values = {"LKASteeringCmd": torque, "LKASteeringCmdActive": steer_req} - return self.packer.make_can_msg_panda("ASCMLKASteeringCmd", 0, values) - - def _button_msg(self, buttons): - values = {"ACCButtons": buttons} - return self.packer.make_can_msg_panda("ASCMSteeringButton", self.BUTTONS_BUS, values) - - -class TestGmAscmSafety(GmLongitudinalBase, TestGmSafetyBase): - TX_MSGS = [[0x180, 0], [0x409, 0], [0x40A, 0], [0x2CB, 0], [0x370, 0], # pt bus - [0xA1, 1], [0x306, 1], [0x308, 1], [0x310, 1], # obs bus - [0x315, 2]] # ch bus - FWD_BLACKLISTED_ADDRS: dict[int, list[int]] = {} - FWD_BUS_LOOKUP: dict[int, int] = {} - BRAKE_BUS = 2 - - MAX_GAS = 3072 - MIN_GAS = 1404 # maximum regen - INACTIVE_GAS = 1404 - - def setUp(self): - self.packer = CANPackerPanda("gm_global_a_powertrain_generated") - self.packer_chassis = CANPackerPanda("gm_global_a_chassis") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_GM, 0) - self.safety.init_tests() - - -class TestGmCameraSafetyBase(TestGmSafetyBase): - - FWD_BUS_LOOKUP = {0: 2, 2: 0} - - @classmethod - def setUpClass(cls): - if cls.__name__ == "TestGmCameraSafetyBase": - cls.packer = None - cls.safety = None - raise unittest.SkipTest - - def _user_brake_msg(self, brake): - values = {"BrakePressed": brake} - return self.packer.make_can_msg_panda("ECMEngineStatus", 0, values) - - -class TestGmCameraSafety(TestGmCameraSafetyBase): - TX_MSGS = [[0x180, 0], # pt bus - [0x184, 2]] # camera bus - FWD_BLACKLISTED_ADDRS = {2: [0x180], 0: [0x184]} # block LKAS message and PSCMStatus - BUTTONS_BUS = 2 # tx only - - def setUp(self): - self.packer = CANPackerPanda("gm_global_a_powertrain_generated") - self.packer_chassis = CANPackerPanda("gm_global_a_chassis") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_GM, GMSafetyFlags.FLAG_GM_HW_CAM) - self.safety.init_tests() - - def test_buttons(self): - # Only CANCEL button is allowed while cruise is enabled - self.safety.set_controls_allowed(0) - for btn in range(8): - self.assertFalse(self._tx(self._button_msg(btn))) - - self.safety.set_controls_allowed(1) - for btn in range(8): - self.assertFalse(self._tx(self._button_msg(btn))) - - for enabled in (True, False): - self._rx(self._pcm_status_msg(enabled)) - self.assertEqual(enabled, self._tx(self._button_msg(Buttons.CANCEL))) - - -class TestGmCameraLongitudinalSafety(GmLongitudinalBase, TestGmCameraSafetyBase): - TX_MSGS = [[0x180, 0], [0x315, 0], [0x2CB, 0], [0x370, 0], # pt bus - [0x184, 2]] # camera bus - FWD_BLACKLISTED_ADDRS = {2: [0x180, 0x2CB, 0x370, 0x315], 0: [0x184]} # block LKAS, ACC messages and PSCMStatus - BUTTONS_BUS = 0 # rx only - - MAX_GAS = 3400 - MIN_GAS = 1514 # maximum regen - INACTIVE_GAS = 1554 - - def setUp(self): - self.packer = CANPackerPanda("gm_global_a_powertrain_generated") - self.packer_chassis = CANPackerPanda("gm_global_a_chassis") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_GM, GMSafetyFlags.FLAG_GM_HW_CAM | GMSafetyFlags.FLAG_GM_HW_CAM_LONG) - self.safety.init_tests() - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/safety/test_honda.py b/tests/safety/test_honda.py deleted file mode 100755 index b2c544a7aa..0000000000 --- a/tests/safety/test_honda.py +++ /dev/null @@ -1,575 +0,0 @@ -#!/usr/bin/env python3 -import unittest -import numpy as np - -from opendbc.car.honda.values import HondaSafetyFlags -from opendbc.safety import Safety -from panda.tests.libsafety import libsafety_py -import panda.tests.safety.common as common -from panda.tests.safety.common import CANPackerPanda, MAX_WRONG_COUNTERS - -HONDA_N_COMMON_TX_MSGS = [[0xE4, 0], [0x194, 0], [0x1FA, 0], [0x30C, 0], [0x33D, 0]] - -class Btn: - NONE = 0 - MAIN = 1 - CANCEL = 2 - SET = 3 - RESUME = 4 - -HONDA_NIDEC = 0 -HONDA_BOSCH = 1 - - -# Honda safety has several different configurations tested here: -# * Nidec -# * normal (PCM-enable) -# * alt SCM messages (PCM-enable) -# * Bosch -# * Bosch with Longitudinal Support -# * Bosch Radarless -# * Bosch Radarless with Longitudinal Support - - -class HondaButtonEnableBase(common.PandaCarSafetyTest): - # pylint: disable=no-member,abstract-method - - # override these inherited tests since we're using button enable - def test_disable_control_allowed_from_cruise(self): - pass - - def test_enable_control_allowed_from_cruise(self): - pass - - def test_cruise_engaged_prev(self): - pass - - def test_buttons_with_main_off(self): - for btn in (Btn.SET, Btn.RESUME, Btn.CANCEL): - self.safety.set_controls_allowed(1) - self._rx(self._acc_state_msg(False)) - self._rx(self._button_msg(btn, main_on=False)) - self.assertFalse(self.safety.get_controls_allowed()) - - def test_set_resume_buttons(self): - """ - Both SET and RES should enter controls allowed on their falling edge. - """ - for main_on in (True, False): - self._rx(self._acc_state_msg(main_on)) - for btn_prev in range(8): - for btn_cur in range(8): - self._rx(self._button_msg(Btn.NONE)) - self.safety.set_controls_allowed(0) - for _ in range(10): - self._rx(self._button_msg(btn_prev)) - self.assertFalse(self.safety.get_controls_allowed()) - - # should enter controls allowed on falling edge and not transitioning to cancel or main - should_enable = (main_on and - btn_cur != btn_prev and - btn_prev in (Btn.RESUME, Btn.SET) and - btn_cur not in (Btn.CANCEL, Btn.MAIN)) - - self._rx(self._button_msg(btn_cur, main_on=main_on)) - self.assertEqual(should_enable, self.safety.get_controls_allowed(), msg=f"{main_on=} {btn_prev=} {btn_cur=}") - - def test_main_cancel_buttons(self): - """ - Both MAIN and CANCEL should exit controls immediately. - """ - for btn in (Btn.MAIN, Btn.CANCEL): - self.safety.set_controls_allowed(1) - self._rx(self._button_msg(btn, main_on=True)) - self.assertFalse(self.safety.get_controls_allowed()) - - def test_disengage_on_main(self): - self.safety.set_controls_allowed(1) - self._rx(self._acc_state_msg(True)) - self.assertTrue(self.safety.get_controls_allowed()) - self._rx(self._acc_state_msg(False)) - self.assertFalse(self.safety.get_controls_allowed()) - - def test_rx_hook(self): - - # TODO: move this test to common - # checksum checks - for msg in ["btn", "gas", "speed"]: - self.safety.set_controls_allowed(1) - if msg == "btn": - to_push = self._button_msg(Btn.SET) - if msg == "gas": - to_push = self._user_gas_msg(0) - if msg == "speed": - to_push = self._speed_msg(0) - self.assertTrue(self._rx(to_push)) - if msg != "btn": - to_push[0].data[4] = 0 # invalidate checksum - to_push[0].data[5] = 0 - to_push[0].data[6] = 0 - to_push[0].data[7] = 0 - self.assertFalse(self._rx(to_push)) - self.assertFalse(self.safety.get_controls_allowed()) - - # counter - # reset wrong_counters to zero by sending valid messages - for i in range(MAX_WRONG_COUNTERS + 1): - self.__class__.cnt_speed += 1 - self.__class__.cnt_button += 1 - self.__class__.cnt_powertrain_data += 1 - if i < MAX_WRONG_COUNTERS: - self.safety.set_controls_allowed(1) - self._rx(self._button_msg(Btn.SET)) - self._rx(self._speed_msg(0)) - self._rx(self._user_gas_msg(0)) - else: - self.assertFalse(self._rx(self._button_msg(Btn.SET))) - self.assertFalse(self._rx(self._speed_msg(0))) - self.assertFalse(self._rx(self._user_gas_msg(0))) - self.assertFalse(self.safety.get_controls_allowed()) - - # restore counters for future tests with a couple of good messages - for _ in range(2): - self.safety.set_controls_allowed(1) - self._rx(self._button_msg(Btn.SET, main_on=True)) - self._rx(self._speed_msg(0)) - self._rx(self._user_gas_msg(0)) - self._rx(self._button_msg(Btn.SET, main_on=True)) - self.assertTrue(self.safety.get_controls_allowed()) - - -class HondaPcmEnableBase(common.PandaCarSafetyTest): - # pylint: disable=no-member,abstract-method - - def test_buttons(self): - """ - Buttons should only cancel in this configuration, - since our state is tied to the PCM's cruise state. - """ - for controls_allowed in (True, False): - for main_on in (True, False): - # not a valid state - if controls_allowed and not main_on: - continue - - for btn in (Btn.SET, Btn.RESUME, Btn.CANCEL): - self.safety.set_controls_allowed(controls_allowed) - self._rx(self._acc_state_msg(main_on)) - - # btn + none for falling edge - self._rx(self._button_msg(btn, main_on=main_on)) - self._rx(self._button_msg(Btn.NONE, main_on=main_on)) - - if btn == Btn.CANCEL: - self.assertFalse(self.safety.get_controls_allowed()) - else: - self.assertEqual(controls_allowed, self.safety.get_controls_allowed()) - - -class HondaBase(common.PandaCarSafetyTest): - MAX_BRAKE = 255 - PT_BUS: int | None = None # must be set when inherited - STEER_BUS: int | None = None # must be set when inherited - BUTTONS_BUS: int | None = None # must be set when inherited, tx on this bus, rx on PT_BUS - - STANDSTILL_THRESHOLD = 0 - RELAY_MALFUNCTION_ADDRS = {0: (0xE4, 0x194)} # STEERING_CONTROL - FWD_BUS_LOOKUP = {0: 2, 2: 0} - - cnt_speed = 0 - cnt_button = 0 - cnt_brake = 0 - cnt_powertrain_data = 0 - cnt_acc_state = 0 - - @classmethod - def setUpClass(cls): - if cls.__name__.endswith("Base"): - cls.packer = None - cls.safety = None - raise unittest.SkipTest - - def _powertrain_data_msg(self, cruise_on=None, brake_pressed=None, gas_pressed=None): - # preserve the state - if cruise_on is None: - # or'd with controls allowed since the tests use it to "enable" cruise - cruise_on = self.safety.get_cruise_engaged_prev() or self.safety.get_controls_allowed() - if brake_pressed is None: - brake_pressed = self.safety.get_brake_pressed_prev() - if gas_pressed is None: - gas_pressed = self.safety.get_gas_pressed_prev() - - values = { - "ACC_STATUS": cruise_on, - "BRAKE_PRESSED": brake_pressed, - "PEDAL_GAS": gas_pressed, - "COUNTER": self.cnt_powertrain_data % 4 - } - self.__class__.cnt_powertrain_data += 1 - return self.packer.make_can_msg_panda("POWERTRAIN_DATA", self.PT_BUS, values) - - def _pcm_status_msg(self, enable): - return self._powertrain_data_msg(cruise_on=enable) - - def _speed_msg(self, speed): - values = {"XMISSION_SPEED": speed, "COUNTER": self.cnt_speed % 4} - self.__class__.cnt_speed += 1 - return self.packer.make_can_msg_panda("ENGINE_DATA", self.PT_BUS, values) - - def _acc_state_msg(self, main_on): - values = {"MAIN_ON": main_on, "COUNTER": self.cnt_acc_state % 4} - self.__class__.cnt_acc_state += 1 - return self.packer.make_can_msg_panda("SCM_FEEDBACK", self.PT_BUS, values) - - def _button_msg(self, buttons, main_on=False, bus=None): - bus = self.PT_BUS if bus is None else bus - values = {"CRUISE_BUTTONS": buttons, "COUNTER": self.cnt_button % 4} - self.__class__.cnt_button += 1 - return self.packer.make_can_msg_panda("SCM_BUTTONS", bus, values) - - def _user_brake_msg(self, brake): - return self._powertrain_data_msg(brake_pressed=brake) - - def _user_gas_msg(self, gas): - return self._powertrain_data_msg(gas_pressed=gas) - - def _send_steer_msg(self, steer): - values = {"STEER_TORQUE": steer} - return self.packer.make_can_msg_panda("STEERING_CONTROL", self.STEER_BUS, values) - - def _send_brake_msg(self, brake): - # must be implemented when inherited - raise NotImplementedError - - def test_disengage_on_brake(self): - self.safety.set_controls_allowed(1) - self._rx(self._user_brake_msg(1)) - self.assertFalse(self.safety.get_controls_allowed()) - - def test_steer_safety_check(self): - self.safety.set_controls_allowed(0) - self.assertTrue(self._tx(self._send_steer_msg(0x0000))) - self.assertFalse(self._tx(self._send_steer_msg(0x1000))) - - -# ********************* Honda Nidec ********************** - - -class TestHondaNidecSafetyBase(HondaBase): - TX_MSGS = HONDA_N_COMMON_TX_MSGS - FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0x194, 0x33D, 0x30C]} - - PT_BUS = 0 - STEER_BUS = 0 - BUTTONS_BUS = 0 - - MAX_GAS = 198 - - def setUp(self): - self.packer = CANPackerPanda("honda_civic_touring_2016_can_generated") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_HONDA_NIDEC, 0) - self.safety.init_tests() - - def _send_brake_msg(self, brake, aeb_req=0, bus=0): - values = {"COMPUTER_BRAKE": brake, "AEB_REQ_1": aeb_req} - return self.packer.make_can_msg_panda("BRAKE_COMMAND", bus, values) - - def _rx_brake_msg(self, brake, aeb_req=0): - return self._send_brake_msg(brake, aeb_req, bus=2) - - def _send_acc_hud_msg(self, pcm_gas, pcm_speed): - # Used to control ACC on Nidec without pedal - values = {"PCM_GAS": pcm_gas, "PCM_SPEED": pcm_speed} - return self.packer.make_can_msg_panda("ACC_HUD", 0, values) - - def test_acc_hud_safety_check(self): - for controls_allowed in [True, False]: - self.safety.set_controls_allowed(controls_allowed) - for pcm_gas in range(255): - for pcm_speed in range(100): - send = (controls_allowed and pcm_gas <= self.MAX_GAS) or (pcm_gas == 0 and pcm_speed == 0) - self.assertEqual(send, self._tx(self._send_acc_hud_msg(pcm_gas, pcm_speed))) - - def test_fwd_hook(self): - # normal operation, not forwarding AEB - self.FWD_BLACKLISTED_ADDRS[2].append(0x1FA) - self.safety.set_honda_fwd_brake(False) - super().test_fwd_hook() - - # forwarding AEB brake signal - self.FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0x194, 0x33D, 0x30C]} - self.safety.set_honda_fwd_brake(True) - super().test_fwd_hook() - - def test_honda_fwd_brake_latching(self): - # Shouldn't fwd stock Honda requesting brake without AEB - self.assertTrue(self._rx(self._rx_brake_msg(self.MAX_BRAKE, aeb_req=0))) - self.assertFalse(self.safety.get_honda_fwd_brake()) - - # Now allow controls and request some brake - openpilot_brake = round(self.MAX_BRAKE / 2.0) - self.safety.set_controls_allowed(True) - self.assertTrue(self._tx(self._send_brake_msg(openpilot_brake))) - - # Still shouldn't fwd stock Honda brake until it's more than openpilot's - for stock_honda_brake in range(self.MAX_BRAKE + 1): - self.assertTrue(self._rx(self._rx_brake_msg(stock_honda_brake, aeb_req=1))) - should_fwd_brake = stock_honda_brake >= openpilot_brake - self.assertEqual(should_fwd_brake, self.safety.get_honda_fwd_brake()) - - # Shouldn't stop fwding until AEB event is over - for stock_honda_brake in range(self.MAX_BRAKE + 1)[::-1]: - self.assertTrue(self._rx(self._rx_brake_msg(stock_honda_brake, aeb_req=1))) - self.assertTrue(self.safety.get_honda_fwd_brake()) - - self.assertTrue(self._rx(self._rx_brake_msg(0, aeb_req=0))) - self.assertFalse(self.safety.get_honda_fwd_brake()) - - def test_brake_safety_check(self): - for fwd_brake in [False, True]: - self.safety.set_honda_fwd_brake(fwd_brake) - for brake in np.arange(0, self.MAX_BRAKE + 10, 1): - for controls_allowed in [True, False]: - self.safety.set_controls_allowed(controls_allowed) - if fwd_brake: - send = False # block openpilot brake msg when fwd'ing stock msg - elif controls_allowed: - send = self.MAX_BRAKE >= brake >= 0 - else: - send = brake == 0 - self.assertEqual(send, self._tx(self._send_brake_msg(brake))) - - -class TestHondaNidecPcmSafety(HondaPcmEnableBase, TestHondaNidecSafetyBase): - """ - Covers the Honda Nidec safety mode - """ - - # Nidec doesn't disengage on falling edge of cruise. See comment in safety_honda.h - def test_disable_control_allowed_from_cruise(self): - pass - - -class TestHondaNidecPcmAltSafety(TestHondaNidecPcmSafety): - """ - Covers the Honda Nidec safety mode with alt SCM messages - """ - def setUp(self): - self.packer = CANPackerPanda("acura_ilx_2016_can_generated") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_HONDA_NIDEC, HondaSafetyFlags.FLAG_HONDA_NIDEC_ALT) - self.safety.init_tests() - - def _acc_state_msg(self, main_on): - values = {"MAIN_ON": main_on, "COUNTER": self.cnt_acc_state % 4} - self.__class__.cnt_acc_state += 1 - return self.packer.make_can_msg_panda("SCM_BUTTONS", self.PT_BUS, values) - - def _button_msg(self, buttons, main_on=False, bus=None): - bus = self.PT_BUS if bus is None else bus - values = {"CRUISE_BUTTONS": buttons, "MAIN_ON": main_on, "COUNTER": self.cnt_button % 4} - self.__class__.cnt_button += 1 - return self.packer.make_can_msg_panda("SCM_BUTTONS", bus, values) - - -# ********************* Honda Bosch ********************** - - -class TestHondaBoschSafetyBase(HondaBase): - PT_BUS = 1 - STEER_BUS = 0 - BUTTONS_BUS = 1 - - TX_MSGS = [[0xE4, 0], [0xE5, 0], [0x296, 1], [0x33D, 0], [0x33DA, 0], [0x33DB, 0]] - FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0xE5, 0x33D, 0x33DA, 0x33DB]} - - def setUp(self): - self.packer = CANPackerPanda("honda_accord_2018_can_generated") - self.safety = libsafety_py.libsafety - - def _alt_brake_msg(self, brake): - values = {"BRAKE_PRESSED": brake, "COUNTER": self.cnt_brake % 4} - self.__class__.cnt_brake += 1 - return self.packer.make_can_msg_panda("BRAKE_MODULE", self.PT_BUS, values) - - def _send_brake_msg(self, brake): - pass - - def test_alt_disengage_on_brake(self): - self.safety.set_honda_alt_brake_msg(1) - self.safety.set_controls_allowed(1) - self._rx(self._alt_brake_msg(1)) - self.assertFalse(self.safety.get_controls_allowed()) - - self.safety.set_honda_alt_brake_msg(0) - self.safety.set_controls_allowed(1) - self._rx(self._alt_brake_msg(1)) - self.assertTrue(self.safety.get_controls_allowed()) - - def test_spam_cancel_safety_check(self): - self.safety.set_controls_allowed(0) - self.assertTrue(self._tx(self._button_msg(Btn.CANCEL, bus=self.BUTTONS_BUS))) - self.assertFalse(self._tx(self._button_msg(Btn.RESUME, bus=self.BUTTONS_BUS))) - self.assertFalse(self._tx(self._button_msg(Btn.SET, bus=self.BUTTONS_BUS))) - # do not block resume if we are engaged already - self.safety.set_controls_allowed(1) - self.assertTrue(self._tx(self._button_msg(Btn.RESUME, bus=self.BUTTONS_BUS))) - - -class TestHondaBoschAltBrakeSafetyBase(TestHondaBoschSafetyBase): - """ - Base Bosch safety test class with an alternate brake message - """ - def setUp(self): - super().setUp() - self.safety.set_safety_hooks(Safety.SAFETY_HONDA_BOSCH, HondaSafetyFlags.FLAG_HONDA_ALT_BRAKE) - self.safety.init_tests() - - def _user_brake_msg(self, brake): - return self._alt_brake_msg(brake) - - def test_alt_brake_rx_hook(self): - self.safety.set_honda_alt_brake_msg(1) - self.safety.set_controls_allowed(1) - to_push = self._alt_brake_msg(0) - self.assertTrue(self._rx(to_push)) - to_push[0].data[2] = to_push[0].data[2] & 0xF0 # invalidate checksum - self.assertFalse(self._rx(to_push)) - self.assertFalse(self.safety.get_controls_allowed()) - - -class TestHondaBoschSafety(HondaPcmEnableBase, TestHondaBoschSafetyBase): - """ - Covers the Honda Bosch safety mode with stock longitudinal - """ - def setUp(self): - super().setUp() - self.safety.set_safety_hooks(Safety.SAFETY_HONDA_BOSCH, 0) - self.safety.init_tests() - - -class TestHondaBoschAltBrakeSafety(HondaPcmEnableBase, TestHondaBoschAltBrakeSafetyBase): - """ - Covers the Honda Bosch safety mode with stock longitudinal and an alternate brake message - """ - - -class TestHondaBoschLongSafety(HondaButtonEnableBase, TestHondaBoschSafetyBase): - """ - Covers the Honda Bosch safety mode with longitudinal control - """ - NO_GAS = -30000 - MAX_GAS = 2000 - MAX_ACCEL = 2.0 # accel is used for brakes, but openpilot can set positive values - MIN_ACCEL = -3.5 - - STEER_BUS = 1 - TX_MSGS = [[0xE4, 1], [0x1DF, 1], [0x1EF, 1], [0x1FA, 1], [0x30C, 1], [0x33D, 1], [0x33DA, 1], [0x33DB, 1], [0x39F, 1], [0x18DAB0F1, 1]] - FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0xE5, 0x33D, 0x33DA, 0x33DB]} - # 0x1DF is to test that radar is disabled - RELAY_MALFUNCTION_ADDRS = {0: (0xE4, 0x194), 1: (0x1DF,)} # STEERING_CONTROL, ACC_CONTROL - - def setUp(self): - super().setUp() - self.safety.set_safety_hooks(Safety.SAFETY_HONDA_BOSCH, HondaSafetyFlags.FLAG_HONDA_BOSCH_LONG) - self.safety.init_tests() - - def _send_gas_brake_msg(self, gas, accel): - values = { - "GAS_COMMAND": gas, - "ACCEL_COMMAND": accel, - "BRAKE_REQUEST": accel < 0, - } - return self.packer.make_can_msg_panda("ACC_CONTROL", self.PT_BUS, values) - - # Longitudinal doesn't need to send buttons - def test_spam_cancel_safety_check(self): - pass - - def test_diagnostics(self): - tester_present = libsafety_py.make_CANPacket(0x18DAB0F1, self.PT_BUS, b"\x02\x3E\x80\x00\x00\x00\x00\x00") - self.assertTrue(self._tx(tester_present)) - - not_tester_present = libsafety_py.make_CANPacket(0x18DAB0F1, self.PT_BUS, b"\x03\xAA\xAA\x00\x00\x00\x00\x00") - self.assertFalse(self._tx(not_tester_present)) - - def test_gas_safety_check(self): - for controls_allowed in [True, False]: - for gas in np.arange(self.NO_GAS, self.MAX_GAS + 2000, 100): - accel = 0 if gas < 0 else gas / 1000 - self.safety.set_controls_allowed(controls_allowed) - send = (controls_allowed and 0 <= gas <= self.MAX_GAS) or gas == self.NO_GAS - self.assertEqual(send, self._tx(self._send_gas_brake_msg(gas, accel)), (controls_allowed, gas, accel)) - - def test_brake_safety_check(self): - for controls_allowed in [True, False]: - for accel in np.arange(self.MIN_ACCEL - 1, self.MAX_ACCEL + 1, 0.01): - accel = round(accel, 2) # floats might not hit exact boundary conditions without rounding - self.safety.set_controls_allowed(controls_allowed) - send = self.MIN_ACCEL <= accel <= self.MAX_ACCEL if controls_allowed else accel == 0 - self.assertEqual(send, self._tx(self._send_gas_brake_msg(self.NO_GAS, accel)), (controls_allowed, accel)) - - -class TestHondaBoschRadarlessSafetyBase(TestHondaBoschSafetyBase): - """Base class for radarless Honda Bosch""" - PT_BUS = 0 - STEER_BUS = 0 - BUTTONS_BUS = 2 # camera controls ACC, need to send buttons on bus 2 - - TX_MSGS = [[0xE4, 0], [0x296, 2], [0x33D, 0]] - FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0xE5, 0x33D, 0x33DA, 0x33DB]} - - def setUp(self): - self.packer = CANPackerPanda("honda_civic_ex_2022_can_generated") - self.safety = libsafety_py.libsafety - - -class TestHondaBoschRadarlessSafety(HondaPcmEnableBase, TestHondaBoschRadarlessSafetyBase): - """ - Covers the Honda Bosch Radarless safety mode with stock longitudinal - """ - - def setUp(self): - super().setUp() - self.safety.set_safety_hooks(Safety.SAFETY_HONDA_BOSCH, HondaSafetyFlags.FLAG_HONDA_RADARLESS) - self.safety.init_tests() - - -class TestHondaBoschRadarlessAltBrakeSafety(HondaPcmEnableBase, TestHondaBoschRadarlessSafetyBase, TestHondaBoschAltBrakeSafetyBase): - """ - Covers the Honda Bosch Radarless safety mode with stock longitudinal and an alternate brake message - """ - - def setUp(self): - super().setUp() - self.safety.set_safety_hooks(Safety.SAFETY_HONDA_BOSCH, HondaSafetyFlags.FLAG_HONDA_RADARLESS | HondaSafetyFlags.FLAG_HONDA_ALT_BRAKE) - self.safety.init_tests() - - -class TestHondaBoschRadarlessLongSafety(common.LongitudinalAccelSafetyTest, HondaButtonEnableBase, - TestHondaBoschRadarlessSafetyBase): - """ - Covers the Honda Bosch Radarless safety mode with longitudinal control - """ - TX_MSGS = [[0xE4, 0], [0x33D, 0], [0x1C8, 0], [0x30C, 0]] - FWD_BLACKLISTED_ADDRS = {2: [0xE4, 0xE5, 0x33D, 0x33DA, 0x33DB, 0x1C8, 0x30C]} - - def setUp(self): - super().setUp() - self.safety.set_safety_hooks(Safety.SAFETY_HONDA_BOSCH, HondaSafetyFlags.FLAG_HONDA_RADARLESS | HondaSafetyFlags.FLAG_HONDA_BOSCH_LONG) - self.safety.init_tests() - - def _accel_msg(self, accel): - values = { - "ACCEL_COMMAND": accel, - } - return self.packer.make_can_msg_panda("ACC_CONTROL", self.PT_BUS, values) - - # Longitudinal doesn't need to send buttons - def test_spam_cancel_safety_check(self): - pass - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/safety/test_hyundai.py b/tests/safety/test_hyundai.py deleted file mode 100755 index 0f313a21ef..0000000000 --- a/tests/safety/test_hyundai.py +++ /dev/null @@ -1,218 +0,0 @@ -#!/usr/bin/env python3 -import random -import unittest - -from opendbc.car.hyundai.values import HyundaiSafetyFlags -from opendbc.safety import Safety -from panda.tests.libsafety import libsafety_py -import panda.tests.safety.common as common -from panda.tests.safety.common import CANPackerPanda -from panda.tests.safety.hyundai_common import HyundaiButtonBase, HyundaiLongitudinalBase - - -# 4 bit checkusm used in some hyundai messages -# lives outside the can packer because we never send this msg -def checksum(msg): - addr, dat, bus = msg - - chksum = 0 - if addr == 0x386: - for i, b in enumerate(dat): - for j in range(8): - # exclude checksum and counter bits - if (i != 1 or j < 6) and (i != 3 or j < 6) and (i != 5 or j < 6) and (i != 7 or j < 6): - bit = (b >> j) & 1 - else: - bit = 0 - chksum += bit - chksum = (chksum ^ 9) & 0xF - ret = bytearray(dat) - ret[5] |= (chksum & 0x3) << 6 - ret[7] |= (chksum & 0xc) << 4 - else: - for i, b in enumerate(dat): - if addr in [0x260, 0x421] and i == 7: - b &= 0x0F if addr == 0x421 else 0xF0 - elif addr == 0x394 and i == 6: - b &= 0xF0 - elif addr == 0x394 and i == 7: - continue - chksum += sum(divmod(b, 16)) - chksum = (16 - chksum) % 16 - ret = bytearray(dat) - ret[6 if addr == 0x394 else 7] |= chksum << (4 if addr == 0x421 else 0) - - return addr, ret, bus - - -class TestHyundaiSafety(HyundaiButtonBase, common.PandaCarSafetyTest, common.DriverTorqueSteeringSafetyTest, common.SteerRequestCutSafetyTest): - TX_MSGS = [[0x340, 0], [0x4F1, 0], [0x485, 0]] - STANDSTILL_THRESHOLD = 12 # 0.375 kph - RELAY_MALFUNCTION_ADDRS = {0: (0x340,)} # LKAS11 - FWD_BLACKLISTED_ADDRS = {2: [0x340, 0x485]} - FWD_BUS_LOOKUP = {0: 2, 2: 0} - - MAX_RATE_UP = 3 - MAX_RATE_DOWN = 7 - MAX_TORQUE = 384 - MAX_RT_DELTA = 112 - RT_INTERVAL = 250000 - DRIVER_TORQUE_ALLOWANCE = 50 - DRIVER_TORQUE_FACTOR = 2 - - # Safety around steering req bit - MIN_VALID_STEERING_FRAMES = 89 - MAX_INVALID_STEERING_FRAMES = 2 - MIN_VALID_STEERING_RT_INTERVAL = 810000 # a ~10% buffer, can send steer up to 110Hz - - cnt_gas = 0 - cnt_speed = 0 - cnt_brake = 0 - cnt_cruise = 0 - cnt_button = 0 - - def setUp(self): - self.packer = CANPackerPanda("hyundai_kia_generic") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_HYUNDAI, 0) - self.safety.init_tests() - - def _button_msg(self, buttons, main_button=0, bus=0): - values = {"CF_Clu_CruiseSwState": buttons, "CF_Clu_CruiseSwMain": main_button, "CF_Clu_AliveCnt1": self.cnt_button} - self.__class__.cnt_button += 1 - return self.packer.make_can_msg_panda("CLU11", bus, values) - - def _user_gas_msg(self, gas): - values = {"CF_Ems_AclAct": gas, "AliveCounter": self.cnt_gas % 4} - self.__class__.cnt_gas += 1 - return self.packer.make_can_msg_panda("EMS16", 0, values, fix_checksum=checksum) - - def _user_brake_msg(self, brake): - values = {"DriverOverride": 2 if brake else random.choice((0, 1, 3)), - "AliveCounterTCS": self.cnt_brake % 8} - self.__class__.cnt_brake += 1 - return self.packer.make_can_msg_panda("TCS13", 0, values, fix_checksum=checksum) - - def _speed_msg(self, speed): - # panda safety doesn't scale, so undo the scaling - values = {"WHL_SPD_%s" % s: speed * 0.03125 for s in ["FL", "FR", "RL", "RR"]} - values["WHL_SPD_AliveCounter_LSB"] = (self.cnt_speed % 16) & 0x3 - values["WHL_SPD_AliveCounter_MSB"] = (self.cnt_speed % 16) >> 2 - self.__class__.cnt_speed += 1 - return self.packer.make_can_msg_panda("WHL_SPD11", 0, values, fix_checksum=checksum) - - def _pcm_status_msg(self, enable): - values = {"ACCMode": enable, "CR_VSM_Alive": self.cnt_cruise % 16} - self.__class__.cnt_cruise += 1 - return self.packer.make_can_msg_panda("SCC12", self.SCC_BUS, values, fix_checksum=checksum) - - def _torque_driver_msg(self, torque): - values = {"CR_Mdps_StrColTq": torque} - return self.packer.make_can_msg_panda("MDPS12", 0, values) - - def _torque_cmd_msg(self, torque, steer_req=1): - values = {"CR_Lkas_StrToqReq": torque, "CF_Lkas_ActToi": steer_req} - return self.packer.make_can_msg_panda("LKAS11", 0, values) - - -class TestHyundaiSafetyAltLimits(TestHyundaiSafety): - MAX_RATE_UP = 2 - MAX_RATE_DOWN = 3 - MAX_TORQUE = 270 - - def setUp(self): - self.packer = CANPackerPanda("hyundai_kia_generic") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_HYUNDAI, HyundaiSafetyFlags.FLAG_HYUNDAI_ALT_LIMITS) - self.safety.init_tests() - - -class TestHyundaiSafetyCameraSCC(TestHyundaiSafety): - BUTTONS_TX_BUS = 2 # tx on 2, rx on 0 - SCC_BUS = 2 # rx on 2 - - def setUp(self): - self.packer = CANPackerPanda("hyundai_kia_generic") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_HYUNDAI, HyundaiSafetyFlags.FLAG_HYUNDAI_CAMERA_SCC) - self.safety.init_tests() - - -class TestHyundaiLegacySafety(TestHyundaiSafety): - def setUp(self): - self.packer = CANPackerPanda("hyundai_kia_generic") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_HYUNDAI_LEGACY, 0) - self.safety.init_tests() - - -class TestHyundaiLegacySafetyEV(TestHyundaiSafety): - def setUp(self): - self.packer = CANPackerPanda("hyundai_kia_generic") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_HYUNDAI_LEGACY, 1) - self.safety.init_tests() - - def _user_gas_msg(self, gas): - values = {"Accel_Pedal_Pos": gas} - return self.packer.make_can_msg_panda("E_EMS11", 0, values, fix_checksum=checksum) - - -class TestHyundaiLegacySafetyHEV(TestHyundaiSafety): - def setUp(self): - self.packer = CANPackerPanda("hyundai_kia_generic") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_HYUNDAI_LEGACY, 2) - self.safety.init_tests() - - def _user_gas_msg(self, gas): - values = {"CR_Vcu_AccPedDep_Pos": gas} - return self.packer.make_can_msg_panda("E_EMS11", 0, values, fix_checksum=checksum) - -class TestHyundaiLongitudinalSafety(HyundaiLongitudinalBase, TestHyundaiSafety): - TX_MSGS = [[0x340, 0], [0x4F1, 0], [0x485, 0], [0x420, 0], [0x421, 0], [0x50A, 0], [0x389, 0], [0x4A2, 0], [0x38D, 0], [0x483, 0], [0x7D0, 0]] - - RELAY_MALFUNCTION_ADDRS = {0: (0x340, 0x421)} # LKAS11, SCC12 - - DISABLED_ECU_UDS_MSG = (0x7D0, 0) - DISABLED_ECU_ACTUATION_MSG = (0x421, 0) - - def setUp(self): - self.packer = CANPackerPanda("hyundai_kia_generic") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_HYUNDAI, HyundaiSafetyFlags.FLAG_HYUNDAI_LONG) - self.safety.init_tests() - - def _accel_msg(self, accel, aeb_req=False, aeb_decel=0): - values = { - "aReqRaw": accel, - "aReqValue": accel, - "AEB_CmdAct": int(aeb_req), - "CR_VSM_DecCmd": aeb_decel, - } - return self.packer.make_can_msg_panda("SCC12", self.SCC_BUS, values) - - def _fca11_msg(self, idx=0, vsm_aeb_req=False, fca_aeb_req=False, aeb_decel=0): - values = { - "CR_FCA_Alive": idx % 0xF, - "FCA_Status": 2, - "CR_VSM_DecCmd": aeb_decel, - "CF_VSM_DecCmdAct": int(vsm_aeb_req), - "FCA_CmdAct": int(fca_aeb_req), - } - return self.packer.make_can_msg_panda("FCA11", 0, values) - - def test_no_aeb_fca11(self): - self.assertTrue(self._tx(self._fca11_msg())) - self.assertFalse(self._tx(self._fca11_msg(vsm_aeb_req=True))) - self.assertFalse(self._tx(self._fca11_msg(fca_aeb_req=True))) - self.assertFalse(self._tx(self._fca11_msg(aeb_decel=1.0))) - - def test_no_aeb_scc12(self): - self.assertTrue(self._tx(self._accel_msg(0))) - self.assertFalse(self._tx(self._accel_msg(0, aeb_req=True))) - self.assertFalse(self._tx(self._accel_msg(0, aeb_decel=1.0))) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/safety/test_hyundai_canfd.py b/tests/safety/test_hyundai_canfd.py deleted file mode 100755 index b6fd4fdcaa..0000000000 --- a/tests/safety/test_hyundai_canfd.py +++ /dev/null @@ -1,283 +0,0 @@ -#!/usr/bin/env python3 -from parameterized import parameterized_class -import unittest - -from opendbc.car.hyundai.values import HyundaiSafetyFlags -from opendbc.safety import Safety -from panda.tests.libsafety import libsafety_py -import panda.tests.safety.common as common -from panda.tests.safety.common import CANPackerPanda -from panda.tests.safety.hyundai_common import HyundaiButtonBase, HyundaiLongitudinalBase - - -class TestHyundaiCanfdBase(HyundaiButtonBase, common.PandaCarSafetyTest, common.DriverTorqueSteeringSafetyTest, common.SteerRequestCutSafetyTest): - - TX_MSGS = [[0x50, 0], [0x1CF, 1], [0x2A4, 0]] - STANDSTILL_THRESHOLD = 12 # 0.375 kph - FWD_BLACKLISTED_ADDRS = {2: [0x50, 0x2a4]} - FWD_BUS_LOOKUP = {0: 2, 2: 0} - - MAX_RATE_UP = 2 - MAX_RATE_DOWN = 3 - MAX_TORQUE = 270 - - MAX_RT_DELTA = 112 - RT_INTERVAL = 250000 - - DRIVER_TORQUE_ALLOWANCE = 250 - DRIVER_TORQUE_FACTOR = 2 - - # Safety around steering req bit - MIN_VALID_STEERING_FRAMES = 89 - MAX_INVALID_STEERING_FRAMES = 2 - MIN_VALID_STEERING_RT_INTERVAL = 810000 # a ~10% buffer, can send steer up to 110Hz - - PT_BUS = 0 - SCC_BUS = 2 - STEER_BUS = 0 - STEER_MSG = "" - GAS_MSG = ("", "") - BUTTONS_TX_BUS = 1 - - @classmethod - def setUpClass(cls): - super().setUpClass() - if cls.__name__ == "TestHyundaiCanfdBase": - cls.packer = None - cls.safety = None - raise unittest.SkipTest - - def _torque_driver_msg(self, torque): - values = {"STEERING_COL_TORQUE": torque} - return self.packer.make_can_msg_panda("MDPS", self.PT_BUS, values) - - def _torque_cmd_msg(self, torque, steer_req=1): - values = {"TORQUE_REQUEST": torque, "STEER_REQ": steer_req} - return self.packer.make_can_msg_panda(self.STEER_MSG, self.STEER_BUS, values) - - def _speed_msg(self, speed): - values = {f"WHEEL_SPEED_{i}": speed * 0.03125 for i in range(1, 5)} - return self.packer.make_can_msg_panda("WHEEL_SPEEDS", self.PT_BUS, values) - - def _user_brake_msg(self, brake): - values = {"DriverBraking": brake} - return self.packer.make_can_msg_panda("TCS", self.PT_BUS, values) - - def _user_gas_msg(self, gas): - values = {self.GAS_MSG[1]: gas} - return self.packer.make_can_msg_panda(self.GAS_MSG[0], self.PT_BUS, values) - - def _pcm_status_msg(self, enable): - values = {"ACCMode": 1 if enable else 0} - return self.packer.make_can_msg_panda("SCC_CONTROL", self.SCC_BUS, values) - - def _button_msg(self, buttons, main_button=0, bus=None): - if bus is None: - bus = self.PT_BUS - values = { - "CRUISE_BUTTONS": buttons, - "ADAPTIVE_CRUISE_MAIN_BTN": main_button, - } - return self.packer.make_can_msg_panda("CRUISE_BUTTONS", bus, values) - - -class TestHyundaiCanfdHDA1Base(TestHyundaiCanfdBase): - - TX_MSGS = [[0x12A, 0], [0x1A0, 1], [0x1CF, 0], [0x1E0, 0]] - RELAY_MALFUNCTION_ADDRS = {0: (0x12A,)} # LFA - FWD_BLACKLISTED_ADDRS = {2: [0x12A, 0x1E0]} - FWD_BUS_LOOKUP = {0: 2, 2: 0} - - STEER_MSG = "LFA" - BUTTONS_TX_BUS = 2 - SAFETY_PARAM: int - - @classmethod - def setUpClass(cls): - super().setUpClass() - if cls.__name__ in ("TestHyundaiCanfdHDA1", "TestHyundaiCanfdHDA1AltButtons"): - cls.packer = None - cls.safety = None - raise unittest.SkipTest - - def setUp(self): - self.packer = CANPackerPanda("hyundai_canfd") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_HYUNDAI_CANFD, self.SAFETY_PARAM) - self.safety.init_tests() - - -@parameterized_class([ - # Radar SCC, test with long flag to ensure flag is not respected until it is supported - {"GAS_MSG": ("ACCELERATOR_BRAKE_ALT", "ACCELERATOR_PEDAL_PRESSED"), "SCC_BUS": 0, "SAFETY_PARAM": HyundaiSafetyFlags.FLAG_HYUNDAI_LONG}, - {"GAS_MSG": ("ACCELERATOR", "ACCELERATOR_PEDAL"), "SCC_BUS": 0, "SAFETY_PARAM": HyundaiSafetyFlags.FLAG_HYUNDAI_EV_GAS | - HyundaiSafetyFlags.FLAG_HYUNDAI_LONG}, - {"GAS_MSG": ("ACCELERATOR_ALT", "ACCELERATOR_PEDAL"), "SCC_BUS": 0, "SAFETY_PARAM": HyundaiSafetyFlags.FLAG_HYUNDAI_HYBRID_GAS | - HyundaiSafetyFlags.FLAG_HYUNDAI_LONG}, - # Camera SCC - {"GAS_MSG": ("ACCELERATOR_BRAKE_ALT", "ACCELERATOR_PEDAL_PRESSED"), "SCC_BUS": 2, "SAFETY_PARAM": HyundaiSafetyFlags.FLAG_HYUNDAI_CAMERA_SCC}, - {"GAS_MSG": ("ACCELERATOR", "ACCELERATOR_PEDAL"), "SCC_BUS": 2, "SAFETY_PARAM": HyundaiSafetyFlags.FLAG_HYUNDAI_EV_GAS | - HyundaiSafetyFlags.FLAG_HYUNDAI_CAMERA_SCC}, - {"GAS_MSG": ("ACCELERATOR_ALT", "ACCELERATOR_PEDAL"), "SCC_BUS": 2, "SAFETY_PARAM": HyundaiSafetyFlags.FLAG_HYUNDAI_HYBRID_GAS | - HyundaiSafetyFlags.FLAG_HYUNDAI_CAMERA_SCC}, -]) -class TestHyundaiCanfdHDA1(TestHyundaiCanfdHDA1Base): - pass - - -@parameterized_class([ - # Radar SCC, test with long flag to ensure flag is not respected until it is supported - {"GAS_MSG": ("ACCELERATOR_BRAKE_ALT", "ACCELERATOR_PEDAL_PRESSED"), "SCC_BUS": 0, "SAFETY_PARAM": HyundaiSafetyFlags.FLAG_HYUNDAI_LONG}, - {"GAS_MSG": ("ACCELERATOR", "ACCELERATOR_PEDAL"), "SCC_BUS": 0, "SAFETY_PARAM": HyundaiSafetyFlags.FLAG_HYUNDAI_EV_GAS | - HyundaiSafetyFlags.FLAG_HYUNDAI_LONG}, - {"GAS_MSG": ("ACCELERATOR_ALT", "ACCELERATOR_PEDAL"), "SCC_BUS": 0, "SAFETY_PARAM": HyundaiSafetyFlags.FLAG_HYUNDAI_HYBRID_GAS | - HyundaiSafetyFlags.FLAG_HYUNDAI_LONG}, - # Camera SCC - {"GAS_MSG": ("ACCELERATOR_BRAKE_ALT", "ACCELERATOR_PEDAL_PRESSED"), "SCC_BUS": 2, "SAFETY_PARAM": HyundaiSafetyFlags.FLAG_HYUNDAI_CAMERA_SCC}, - {"GAS_MSG": ("ACCELERATOR", "ACCELERATOR_PEDAL"), "SCC_BUS": 2, "SAFETY_PARAM": HyundaiSafetyFlags.FLAG_HYUNDAI_EV_GAS | - HyundaiSafetyFlags.FLAG_HYUNDAI_CAMERA_SCC}, - {"GAS_MSG": ("ACCELERATOR_ALT", "ACCELERATOR_PEDAL"), "SCC_BUS": 2, "SAFETY_PARAM": HyundaiSafetyFlags.FLAG_HYUNDAI_HYBRID_GAS | - HyundaiSafetyFlags.FLAG_HYUNDAI_CAMERA_SCC}, -]) -class TestHyundaiCanfdHDA1AltButtons(TestHyundaiCanfdHDA1Base): - - SAFETY_PARAM: int - - def setUp(self): - self.packer = CANPackerPanda("hyundai_canfd") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_HYUNDAI_CANFD, HyundaiSafetyFlags.FLAG_HYUNDAI_CANFD_ALT_BUTTONS | self.SAFETY_PARAM) - self.safety.init_tests() - - def _button_msg(self, buttons, main_button=0, bus=1): - values = { - "CRUISE_BUTTONS": buttons, - "ADAPTIVE_CRUISE_MAIN_BTN": main_button, - } - return self.packer.make_can_msg_panda("CRUISE_BUTTONS_ALT", self.PT_BUS, values) - - def test_button_sends(self): - """ - No button send allowed with alt buttons. - """ - for enabled in (True, False): - for btn in range(8): - self.safety.set_controls_allowed(enabled) - self.assertFalse(self._tx(self._button_msg(btn))) - - -class TestHyundaiCanfdHDA2EV(TestHyundaiCanfdBase): - - TX_MSGS = [[0x50, 0], [0x1CF, 1], [0x2A4, 0]] - RELAY_MALFUNCTION_ADDRS = {0: (0x50,)} # LKAS - FWD_BLACKLISTED_ADDRS = {2: [0x50, 0x2a4]} - FWD_BUS_LOOKUP = {0: 2, 2: 0} - - PT_BUS = 1 - SCC_BUS = 1 - STEER_MSG = "LKAS" - GAS_MSG = ("ACCELERATOR", "ACCELERATOR_PEDAL") - - def setUp(self): - self.packer = CANPackerPanda("hyundai_canfd") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_HYUNDAI_CANFD, HyundaiSafetyFlags.FLAG_HYUNDAI_CANFD_HDA2 | HyundaiSafetyFlags.FLAG_HYUNDAI_EV_GAS) - self.safety.init_tests() - - -# TODO: Handle ICE and HEV configurations once we see cars that use the new messages -class TestHyundaiCanfdHDA2EVAltSteering(TestHyundaiCanfdBase): - - TX_MSGS = [[0x110, 0], [0x1CF, 1], [0x362, 0]] - RELAY_MALFUNCTION_ADDRS = {0: (0x110,)} # LKAS_ALT - FWD_BLACKLISTED_ADDRS = {2: [0x110, 0x362]} - FWD_BUS_LOOKUP = {0: 2, 2: 0} - - PT_BUS = 1 - SCC_BUS = 1 - STEER_MSG = "LKAS_ALT" - GAS_MSG = ("ACCELERATOR", "ACCELERATOR_PEDAL") - - def setUp(self): - self.packer = CANPackerPanda("hyundai_canfd") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_HYUNDAI_CANFD, HyundaiSafetyFlags.FLAG_HYUNDAI_CANFD_HDA2 | HyundaiSafetyFlags.FLAG_HYUNDAI_EV_GAS | - HyundaiSafetyFlags.FLAG_HYUNDAI_CANFD_HDA2_ALT_STEERING) - self.safety.init_tests() - - -class TestHyundaiCanfdHDA2LongEV(HyundaiLongitudinalBase, TestHyundaiCanfdHDA2EV): - - TX_MSGS = [[0x50, 0], [0x1CF, 1], [0x2A4, 0], [0x51, 0], [0x730, 1], [0x12a, 1], [0x160, 1], - [0x1e0, 1], [0x1a0, 1], [0x1ea, 1], [0x200, 1], [0x345, 1], [0x1da, 1]] - - RELAY_MALFUNCTION_ADDRS = {0: (0x50,), 1: (0x1a0,)} # LKAS, SCC_CONTROL - - DISABLED_ECU_UDS_MSG = (0x730, 1) - DISABLED_ECU_ACTUATION_MSG = (0x1a0, 1) - - STEER_MSG = "LFA" - GAS_MSG = ("ACCELERATOR", "ACCELERATOR_PEDAL") - STEER_BUS = 1 - - def setUp(self): - self.packer = CANPackerPanda("hyundai_canfd") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_HYUNDAI_CANFD, HyundaiSafetyFlags.FLAG_HYUNDAI_CANFD_HDA2 | - HyundaiSafetyFlags.FLAG_HYUNDAI_LONG | HyundaiSafetyFlags.FLAG_HYUNDAI_EV_GAS) - self.safety.init_tests() - - def _accel_msg(self, accel, aeb_req=False, aeb_decel=0): - values = { - "aReqRaw": accel, - "aReqValue": accel, - } - return self.packer.make_can_msg_panda("SCC_CONTROL", 1, values) - - -# Tests HDA1 longitudinal for ICE, hybrid, EV -@parameterized_class([ - # Camera SCC is the only supported configuration for HDA1 longitudinal, TODO: allow radar SCC - {"GAS_MSG": ("ACCELERATOR_BRAKE_ALT", "ACCELERATOR_PEDAL_PRESSED"), "SAFETY_PARAM": HyundaiSafetyFlags.FLAG_HYUNDAI_LONG}, - {"GAS_MSG": ("ACCELERATOR", "ACCELERATOR_PEDAL"), "SAFETY_PARAM": HyundaiSafetyFlags.FLAG_HYUNDAI_LONG | HyundaiSafetyFlags.FLAG_HYUNDAI_EV_GAS}, - {"GAS_MSG": ("ACCELERATOR_ALT", "ACCELERATOR_PEDAL"), "SAFETY_PARAM": HyundaiSafetyFlags.FLAG_HYUNDAI_LONG | HyundaiSafetyFlags.FLAG_HYUNDAI_HYBRID_GAS}, -]) -class TestHyundaiCanfdHDA1Long(HyundaiLongitudinalBase, TestHyundaiCanfdHDA1Base): - - FWD_BLACKLISTED_ADDRS = {2: [0x12a, 0x1e0, 0x1a0]} - - RELAY_MALFUNCTION_ADDRS = {0: (0x12A, 0x1a0)} # LFA, SCC_CONTROL - - DISABLED_ECU_UDS_MSG = (0x730, 1) - DISABLED_ECU_ACTUATION_MSG = (0x1a0, 0) - - STEER_MSG = "LFA" - STEER_BUS = 0 - SCC_BUS = 2 - - @classmethod - def setUpClass(cls): - if cls.__name__ == "TestHyundaiCanfdHDA1Long": - cls.safety = None - raise unittest.SkipTest - - def setUp(self): - self.packer = CANPackerPanda("hyundai_canfd") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_HYUNDAI_CANFD, HyundaiSafetyFlags.FLAG_HYUNDAI_CAMERA_SCC | self.SAFETY_PARAM) - self.safety.init_tests() - - def _accel_msg(self, accel, aeb_req=False, aeb_decel=0): - values = { - "aReqRaw": accel, - "aReqValue": accel, - } - return self.packer.make_can_msg_panda("SCC_CONTROL", 0, values) - - # no knockout - def test_tester_present_allowed(self): - pass - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/safety/test_mazda.py b/tests/safety/test_mazda.py deleted file mode 100755 index 77624aa098..0000000000 --- a/tests/safety/test_mazda.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python3 -import unittest -from opendbc.safety import Safety -from panda.tests.libsafety import libsafety_py -import panda.tests.safety.common as common -from panda.tests.safety.common import CANPackerPanda - - -class TestMazdaSafety(common.PandaCarSafetyTest, common.DriverTorqueSteeringSafetyTest): - - TX_MSGS = [[0x243, 0], [0x09d, 0], [0x440, 0]] - STANDSTILL_THRESHOLD = .1 - RELAY_MALFUNCTION_ADDRS = {0: (0x243,)} - FWD_BLACKLISTED_ADDRS = {2: [0x243, 0x440]} - FWD_BUS_LOOKUP = {0: 2, 2: 0} - - MAX_RATE_UP = 10 - MAX_RATE_DOWN = 25 - MAX_TORQUE = 800 - - MAX_RT_DELTA = 300 - RT_INTERVAL = 250000 - - DRIVER_TORQUE_ALLOWANCE = 15 - DRIVER_TORQUE_FACTOR = 1 - - # Mazda actually does not set any bit when requesting torque - NO_STEER_REQ_BIT = True - - def setUp(self): - self.packer = CANPackerPanda("mazda_2017") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_MAZDA, 0) - self.safety.init_tests() - - def _torque_meas_msg(self, torque): - values = {"STEER_TORQUE_MOTOR": torque} - return self.packer.make_can_msg_panda("STEER_TORQUE", 0, values) - - def _torque_driver_msg(self, torque): - values = {"STEER_TORQUE_SENSOR": torque} - return self.packer.make_can_msg_panda("STEER_TORQUE", 0, values) - - def _torque_cmd_msg(self, torque, steer_req=1): - values = {"LKAS_REQUEST": torque} - return self.packer.make_can_msg_panda("CAM_LKAS", 0, values) - - def _speed_msg(self, speed): - values = {"SPEED": speed} - return self.packer.make_can_msg_panda("ENGINE_DATA", 0, values) - - def _user_brake_msg(self, brake): - values = {"BRAKE_ON": brake} - return self.packer.make_can_msg_panda("PEDALS", 0, values) - - def _user_gas_msg(self, gas): - values = {"PEDAL_GAS": gas} - return self.packer.make_can_msg_panda("ENGINE_DATA", 0, values) - - def _pcm_status_msg(self, enable): - values = {"CRZ_ACTIVE": enable} - return self.packer.make_can_msg_panda("CRZ_CTRL", 0, values) - - def _button_msg(self, resume=False, cancel=False): - values = { - "CAN_OFF": cancel, - "CAN_OFF_INV": (cancel + 1) % 2, - "RES": resume, - "RES_INV": (resume + 1) % 2, - } - return self.packer.make_can_msg_panda("CRZ_BTNS", 0, values) - - def test_buttons(self): - # only cancel allows while controls not allowed - self.safety.set_controls_allowed(0) - self.assertTrue(self._tx(self._button_msg(cancel=True))) - self.assertFalse(self._tx(self._button_msg(resume=True))) - - # do not block resume if we are engaged already - self.safety.set_controls_allowed(1) - self.assertTrue(self._tx(self._button_msg(cancel=True))) - self.assertTrue(self._tx(self._button_msg(resume=True))) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/safety/test_nissan.py b/tests/safety/test_nissan.py deleted file mode 100755 index 643d893d94..0000000000 --- a/tests/safety/test_nissan.py +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env python3 -import unittest - -from opendbc.car.nissan.values import NissanSafetyFlags -from opendbc.safety import Safety -from panda.tests.libsafety import libsafety_py -import panda.tests.safety.common as common -from panda.tests.safety.common import CANPackerPanda - - -class TestNissanSafety(common.PandaCarSafetyTest, common.AngleSteeringSafetyTest): - - TX_MSGS = [[0x169, 0], [0x2b1, 0], [0x4cc, 0], [0x20b, 2], [0x280, 2]] - STANDSTILL_THRESHOLD = 0 - GAS_PRESSED_THRESHOLD = 3 - RELAY_MALFUNCTION_ADDRS = {0: (0x169,)} - FWD_BLACKLISTED_ADDRS = {0: [0x280], 2: [0x169, 0x2b1, 0x4cc]} - FWD_BUS_LOOKUP = {0: 2, 2: 0} - - EPS_BUS = 0 - CRUISE_BUS = 2 - - # Angle control limits - DEG_TO_CAN = 100 - - ANGLE_RATE_BP = [0., 5., 15.] - ANGLE_RATE_UP = [5., .8, .15] # windup limit - ANGLE_RATE_DOWN = [5., 3.5, .4] # unwind limit - - def setUp(self): - self.packer = CANPackerPanda("nissan_x_trail_2017_generated") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_NISSAN, 0) - self.safety.init_tests() - - def _angle_cmd_msg(self, angle: float, enabled: bool): - values = {"DESIRED_ANGLE": angle, "LKA_ACTIVE": 1 if enabled else 0} - return self.packer.make_can_msg_panda("LKAS", 0, values) - - def _angle_meas_msg(self, angle: float): - values = {"STEER_ANGLE": angle} - return self.packer.make_can_msg_panda("STEER_ANGLE_SENSOR", self.EPS_BUS, values) - - def _pcm_status_msg(self, enable): - values = {"CRUISE_ENABLED": enable} - return self.packer.make_can_msg_panda("CRUISE_STATE", self.CRUISE_BUS, values) - - def _speed_msg(self, speed): - values = {"WHEEL_SPEED_%s" % s: speed * 3.6 for s in ["RR", "RL"]} - return self.packer.make_can_msg_panda("WHEEL_SPEEDS_REAR", self.EPS_BUS, values) - - def _user_brake_msg(self, brake): - values = {"USER_BRAKE_PRESSED": brake} - return self.packer.make_can_msg_panda("DOORS_LIGHTS", self.EPS_BUS, values) - - def _user_gas_msg(self, gas): - values = {"GAS_PEDAL": gas} - return self.packer.make_can_msg_panda("GAS_PEDAL", self.EPS_BUS, values) - - def _acc_button_cmd(self, cancel=0, propilot=0, flw_dist=0, _set=0, res=0): - no_button = not any([cancel, propilot, flw_dist, _set, res]) - values = {"CANCEL_BUTTON": cancel, "PROPILOT_BUTTON": propilot, - "FOLLOW_DISTANCE_BUTTON": flw_dist, "SET_BUTTON": _set, - "RES_BUTTON": res, "NO_BUTTON_PRESSED": no_button} - return self.packer.make_can_msg_panda("CRUISE_THROTTLE", 2, values) - - def test_acc_buttons(self): - btns = [ - ("cancel", True), - ("propilot", False), - ("flw_dist", False), - ("_set", False), - ("res", False), - (None, False), - ] - for controls_allowed in (True, False): - for btn, should_tx in btns: - self.safety.set_controls_allowed(controls_allowed) - args = {} if btn is None else {btn: 1} - tx = self._tx(self._acc_button_cmd(**args)) - self.assertEqual(tx, should_tx) - - -class TestNissanSafetyAltEpsBus(TestNissanSafety): - """Altima uses different buses""" - - EPS_BUS = 1 - CRUISE_BUS = 1 - - def setUp(self): - self.packer = CANPackerPanda("nissan_x_trail_2017_generated") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_NISSAN, NissanSafetyFlags.FLAG_NISSAN_ALT_EPS_BUS) - self.safety.init_tests() - - -class TestNissanLeafSafety(TestNissanSafety): - - def setUp(self): - self.packer = CANPackerPanda("nissan_leaf_2018_generated") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_NISSAN, 0) - self.safety.init_tests() - - def _user_brake_msg(self, brake): - values = {"USER_BRAKE_PRESSED": brake} - return self.packer.make_can_msg_panda("CRUISE_THROTTLE", 0, values) - - def _user_gas_msg(self, gas): - values = {"GAS_PEDAL": gas} - return self.packer.make_can_msg_panda("CRUISE_THROTTLE", 0, values) - - # TODO: leaf should use its own safety param - def test_acc_buttons(self): - pass - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/safety/test_subaru.py b/tests/safety/test_subaru.py deleted file mode 100755 index e004c00fba..0000000000 --- a/tests/safety/test_subaru.py +++ /dev/null @@ -1,230 +0,0 @@ -#!/usr/bin/env python3 -import enum -import unittest - -from opendbc.car.subaru.values import SubaruSafetyFlags -from opendbc.safety import Safety -from panda.tests.libsafety import libsafety_py -import panda.tests.safety.common as common -from panda.tests.safety.common import CANPackerPanda -from functools import partial - -class SubaruMsg(enum.IntEnum): - Brake_Status = 0x13c - CruiseControl = 0x240 - Throttle = 0x40 - Steering_Torque = 0x119 - Wheel_Speeds = 0x13a - ES_LKAS = 0x122 - ES_LKAS_ANGLE = 0x124 - ES_Brake = 0x220 - ES_Distance = 0x221 - ES_Status = 0x222 - ES_DashStatus = 0x321 - ES_LKAS_State = 0x322 - ES_Infotainment = 0x323 - ES_UDS_Request = 0x787 - ES_HighBeamAssist = 0x121 - ES_STATIC_1 = 0x22a - ES_STATIC_2 = 0x325 - - -SUBARU_MAIN_BUS = 0 -SUBARU_ALT_BUS = 1 -SUBARU_CAM_BUS = 2 - - -def lkas_tx_msgs(alt_bus, lkas_msg=SubaruMsg.ES_LKAS): - return [[lkas_msg, SUBARU_MAIN_BUS], - [SubaruMsg.ES_Distance, alt_bus], - [SubaruMsg.ES_DashStatus, SUBARU_MAIN_BUS], - [SubaruMsg.ES_LKAS_State, SUBARU_MAIN_BUS], - [SubaruMsg.ES_Infotainment, SUBARU_MAIN_BUS]] - -def long_tx_msgs(alt_bus): - return [[SubaruMsg.ES_Brake, alt_bus], - [SubaruMsg.ES_Status, alt_bus]] - -def gen2_long_additional_tx_msgs(): - return [[SubaruMsg.ES_UDS_Request, SUBARU_CAM_BUS], - [SubaruMsg.ES_HighBeamAssist, SUBARU_MAIN_BUS], - [SubaruMsg.ES_STATIC_1, SUBARU_MAIN_BUS], - [SubaruMsg.ES_STATIC_2, SUBARU_MAIN_BUS]] - -def fwd_blacklisted_addr(lkas_msg=SubaruMsg.ES_LKAS): - return {SUBARU_CAM_BUS: [lkas_msg, SubaruMsg.ES_DashStatus, SubaruMsg.ES_LKAS_State, SubaruMsg.ES_Infotainment]} - -class TestSubaruSafetyBase(common.PandaCarSafetyTest): - FLAGS = 0 - STANDSTILL_THRESHOLD = 0 # kph - RELAY_MALFUNCTION_ADDRS = {SUBARU_MAIN_BUS: (SubaruMsg.ES_LKAS,)} - FWD_BUS_LOOKUP = {SUBARU_MAIN_BUS: SUBARU_CAM_BUS, SUBARU_CAM_BUS: SUBARU_MAIN_BUS} - FWD_BLACKLISTED_ADDRS = fwd_blacklisted_addr() - - MAX_RT_DELTA = 940 - RT_INTERVAL = 250000 - - DRIVER_TORQUE_ALLOWANCE = 60 - DRIVER_TORQUE_FACTOR = 50 - - ALT_MAIN_BUS = SUBARU_MAIN_BUS - ALT_CAM_BUS = SUBARU_CAM_BUS - - DEG_TO_CAN = 100 - - INACTIVE_GAS = 1818 - - def setUp(self): - self.packer = CANPackerPanda("subaru_global_2017_generated") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_SUBARU, self.FLAGS) - self.safety.init_tests() - - def _set_prev_torque(self, t): - self.safety.set_desired_torque_last(t) - self.safety.set_rt_torque_last(t) - - def _torque_driver_msg(self, torque): - values = {"Steer_Torque_Sensor": torque} - return self.packer.make_can_msg_panda("Steering_Torque", 0, values) - - def _speed_msg(self, speed): - values = {s: speed for s in ["FR", "FL", "RR", "RL"]} - return self.packer.make_can_msg_panda("Wheel_Speeds", self.ALT_MAIN_BUS, values) - - def _angle_meas_msg(self, angle): - values = {"Steering_Angle": angle} - return self.packer.make_can_msg_panda("Steering_Torque", 0, values) - - def _user_brake_msg(self, brake): - values = {"Brake": brake} - return self.packer.make_can_msg_panda("Brake_Status", self.ALT_MAIN_BUS, values) - - def _user_gas_msg(self, gas): - values = {"Throttle_Pedal": gas} - return self.packer.make_can_msg_panda("Throttle", 0, values) - - def _pcm_status_msg(self, enable): - values = {"Cruise_Activated": enable} - return self.packer.make_can_msg_panda("CruiseControl", self.ALT_MAIN_BUS, values) - - -class TestSubaruStockLongitudinalSafetyBase(TestSubaruSafetyBase): - def _cancel_msg(self, cancel, cruise_throttle=0): - values = {"Cruise_Cancel": cancel, "Cruise_Throttle": cruise_throttle} - return self.packer.make_can_msg_panda("ES_Distance", self.ALT_MAIN_BUS, values) - - def test_cancel_message(self): - # test that we can only send the cancel message (ES_Distance) with inactive throttle (1818) and Cruise_Cancel=1 - for cancel in [True, False]: - self._generic_limit_safety_check(partial(self._cancel_msg, cancel), self.INACTIVE_GAS, self.INACTIVE_GAS, 0, 2**12, 1, self.INACTIVE_GAS, cancel) - - -class TestSubaruLongitudinalSafetyBase(TestSubaruSafetyBase, common.LongitudinalGasBrakeSafetyTest): - MIN_GAS = 808 - MAX_GAS = 3400 - INACTIVE_GAS = 1818 - MAX_POSSIBLE_GAS = 2**13 - - MIN_BRAKE = 0 - MAX_BRAKE = 600 - MAX_POSSIBLE_BRAKE = 2**16 - - MIN_RPM = 0 - MAX_RPM = 3600 - MAX_POSSIBLE_RPM = 2**13 - - FWD_BLACKLISTED_ADDRS = {2: [SubaruMsg.ES_LKAS, SubaruMsg.ES_Brake, SubaruMsg.ES_Distance, - SubaruMsg.ES_Status, SubaruMsg.ES_DashStatus, - SubaruMsg.ES_LKAS_State, SubaruMsg.ES_Infotainment]} - - def test_rpm_safety_check(self): - self._generic_limit_safety_check(self._send_rpm_msg, self.MIN_RPM, self.MAX_RPM, 0, self.MAX_POSSIBLE_RPM, 1) - - def _send_brake_msg(self, brake): - values = {"Brake_Pressure": brake} - return self.packer.make_can_msg_panda("ES_Brake", self.ALT_MAIN_BUS, values) - - def _send_gas_msg(self, gas): - values = {"Cruise_Throttle": gas} - return self.packer.make_can_msg_panda("ES_Distance", self.ALT_MAIN_BUS, values) - - def _send_rpm_msg(self, rpm): - values = {"Cruise_RPM": rpm} - return self.packer.make_can_msg_panda("ES_Status", self.ALT_MAIN_BUS, values) - - -class TestSubaruTorqueSafetyBase(TestSubaruSafetyBase, common.DriverTorqueSteeringSafetyTest, common.SteerRequestCutSafetyTest): - MAX_RATE_UP = 50 - MAX_RATE_DOWN = 70 - MAX_TORQUE = 2047 - - # Safety around steering req bit - MIN_VALID_STEERING_FRAMES = 7 - MAX_INVALID_STEERING_FRAMES = 1 - MIN_VALID_STEERING_RT_INTERVAL = 144000 - - def _torque_cmd_msg(self, torque, steer_req=1): - values = {"LKAS_Output": torque, "LKAS_Request": steer_req} - return self.packer.make_can_msg_panda("ES_LKAS", SUBARU_MAIN_BUS, values) - - -class TestSubaruGen1TorqueStockLongitudinalSafety(TestSubaruStockLongitudinalSafetyBase, TestSubaruTorqueSafetyBase): - FLAGS = 0 - TX_MSGS = lkas_tx_msgs(SUBARU_MAIN_BUS) - - -class TestSubaruGen2TorqueSafetyBase(TestSubaruTorqueSafetyBase): - ALT_MAIN_BUS = SUBARU_ALT_BUS - ALT_CAM_BUS = SUBARU_ALT_BUS - - MAX_RATE_UP = 40 - MAX_RATE_DOWN = 40 - MAX_TORQUE = 1000 - - -class TestSubaruGen2TorqueStockLongitudinalSafety(TestSubaruStockLongitudinalSafetyBase, TestSubaruGen2TorqueSafetyBase): - FLAGS = SubaruSafetyFlags.FLAG_SUBARU_GEN2 - TX_MSGS = lkas_tx_msgs(SUBARU_ALT_BUS) - - -class TestSubaruGen1LongitudinalSafety(TestSubaruLongitudinalSafetyBase, TestSubaruTorqueSafetyBase): - FLAGS = SubaruSafetyFlags.FLAG_SUBARU_LONG - TX_MSGS = lkas_tx_msgs(SUBARU_MAIN_BUS) + long_tx_msgs(SUBARU_MAIN_BUS) - - -class TestSubaruGen2LongitudinalSafety(TestSubaruLongitudinalSafetyBase, TestSubaruGen2TorqueSafetyBase): - FLAGS = SubaruSafetyFlags.FLAG_SUBARU_LONG | SubaruSafetyFlags.FLAG_SUBARU_GEN2 - TX_MSGS = lkas_tx_msgs(SUBARU_ALT_BUS) + long_tx_msgs(SUBARU_ALT_BUS) + gen2_long_additional_tx_msgs() - - def _rdbi_msg(self, did: int): - return b'\x03\x22' + did.to_bytes(2) + b'\x00\x00\x00\x00' - - def _es_uds_msg(self, msg: bytes): - return libsafety_py.make_CANPacket(SubaruMsg.ES_UDS_Request, 2, msg) - - def test_es_uds_message(self): - tester_present = b'\x02\x3E\x80\x00\x00\x00\x00\x00' - not_tester_present = b"\x03\xAA\xAA\x00\x00\x00\x00\x00" - - button_did = 0x1130 - - # Tester present is allowed for gen2 long to keep eyesight disabled - self.assertTrue(self._tx(self._es_uds_msg(tester_present))) - - # Non-Tester present is not allowed - self.assertFalse(self._tx(self._es_uds_msg(not_tester_present))) - - # Only button_did is allowed to be read via UDS - for did in range(0xFFFF): - should_tx = (did == button_did) - self.assertEqual(self._tx(self._es_uds_msg(self._rdbi_msg(did))), should_tx) - - # any other msg is not allowed - for sid in range(0xFF): - msg = b'\x03' + sid.to_bytes(1) + b'\x00' * 6 - self.assertFalse(self._tx(self._es_uds_msg(msg))) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/safety/test_subaru_preglobal.py b/tests/safety/test_subaru_preglobal.py deleted file mode 100755 index 577b43c26c..0000000000 --- a/tests/safety/test_subaru_preglobal.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python3 -import unittest - -from opendbc.car.subaru.values import SubaruSafetyFlags -from opendbc.safety import Safety -from panda.tests.libsafety import libsafety_py -import panda.tests.safety.common as common -from panda.tests.safety.common import CANPackerPanda - - -class TestSubaruPreglobalSafety(common.PandaCarSafetyTest, common.DriverTorqueSteeringSafetyTest): - FLAGS = 0 - DBC = "subaru_outback_2015_generated" - TX_MSGS = [[0x161, 0], [0x164, 0]] - STANDSTILL_THRESHOLD = 0 # kph - RELAY_MALFUNCTION_ADDRS = {0: (0x164,)} - FWD_BLACKLISTED_ADDRS = {2: [0x161, 0x164]} - FWD_BUS_LOOKUP = {0: 2, 2: 0} - - MAX_RATE_UP = 50 - MAX_RATE_DOWN = 70 - MAX_TORQUE = 2047 - - MAX_RT_DELTA = 940 - RT_INTERVAL = 250000 - - DRIVER_TORQUE_ALLOWANCE = 75 - DRIVER_TORQUE_FACTOR = 10 - - def setUp(self): - self.packer = CANPackerPanda(self.DBC) - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_SUBARU_PREGLOBAL, self.FLAGS) - self.safety.init_tests() - - def _set_prev_torque(self, t): - self.safety.set_desired_torque_last(t) - self.safety.set_rt_torque_last(t) - - def _torque_driver_msg(self, torque): - values = {"Steer_Torque_Sensor": torque} - return self.packer.make_can_msg_panda("Steering_Torque", 0, values) - - def _speed_msg(self, speed): - # subaru safety doesn't use the scaled value, so undo the scaling - values = {s: speed*0.0592 for s in ["FR", "FL", "RR", "RL"]} - return self.packer.make_can_msg_panda("Wheel_Speeds", 0, values) - - def _user_brake_msg(self, brake): - values = {"Brake_Pedal": brake} - return self.packer.make_can_msg_panda("Brake_Pedal", 0, values) - - def _torque_cmd_msg(self, torque, steer_req=1): - values = {"LKAS_Command": torque, "LKAS_Active": steer_req} - return self.packer.make_can_msg_panda("ES_LKAS", 0, values) - - def _user_gas_msg(self, gas): - values = {"Throttle_Pedal": gas} - return self.packer.make_can_msg_panda("Throttle", 0, values) - - def _pcm_status_msg(self, enable): - values = {"Cruise_Activated": enable} - return self.packer.make_can_msg_panda("CruiseControl", 0, values) - - -class TestSubaruPreglobalReversedDriverTorqueSafety(TestSubaruPreglobalSafety): - FLAGS = SubaruSafetyFlags.FLAG_SUBARU_PREGLOBAL_REVERSED_DRIVER_TORQUE - DBC = "subaru_outback_2019_generated" - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/safety/test_tesla.py b/tests/safety/test_tesla.py deleted file mode 100755 index aeab5b9ab1..0000000000 --- a/tests/safety/test_tesla.py +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/env python3 -import unittest - -from opendbc.car.tesla.values import TeslaSafetyFlags -from opendbc.safety import Safety -from panda.tests.libsafety import libsafety_py -import panda.tests.safety.common as common -from panda.tests.safety.common import CANPackerPanda - -MSG_DAS_steeringControl = 0x488 -MSG_APS_eacMonitor = 0x27d -MSG_DAS_Control = 0x2b9 - - -class TestTeslaSafetyBase(common.PandaCarSafetyTest, common.AngleSteeringSafetyTest, common.LongitudinalAccelSafetyTest): - RELAY_MALFUNCTION_ADDRS = {0: (MSG_DAS_steeringControl, MSG_APS_eacMonitor)} - FWD_BLACKLISTED_ADDRS = {2: [MSG_DAS_steeringControl, MSG_APS_eacMonitor]} - TX_MSGS = [[MSG_DAS_steeringControl, 0], [MSG_APS_eacMonitor, 0], [MSG_DAS_Control, 0]] - - STANDSTILL_THRESHOLD = 0.1 - GAS_PRESSED_THRESHOLD = 3 - FWD_BUS_LOOKUP = {0: 2, 2: 0} - - # Angle control limits - DEG_TO_CAN = 10 - - ANGLE_RATE_BP = [0., 5., 15.] - ANGLE_RATE_UP = [10., 1.6, .3] # windup limit - ANGLE_RATE_DOWN = [10., 7.0, .8] # unwind limit - - # Long control limits - MAX_ACCEL = 2.0 - MIN_ACCEL = -3.48 - INACTIVE_ACCEL = 0.0 - - packer: CANPackerPanda - - @classmethod - def setUpClass(cls): - if cls.__name__ == "TestTeslaSafetyBase": - raise unittest.SkipTest - - def _angle_cmd_msg(self, angle: float, enabled: bool): - values = {"DAS_steeringAngleRequest": angle, "DAS_steeringControlType": 1 if enabled else 0} - return self.packer.make_can_msg_panda("DAS_steeringControl", 0, values) - - def _angle_meas_msg(self, angle: float): - values = {"EPAS3S_internalSAS": angle} - return self.packer.make_can_msg_panda("EPAS3S_sysStatus", 0, values) - - def _user_brake_msg(self, brake): - values = {"IBST_driverBrakeApply": 2 if brake else 1} - return self.packer.make_can_msg_panda("IBST_status", 0, values) - - def _speed_msg(self, speed): - values = {"DI_vehicleSpeed": speed * 3.6} - return self.packer.make_can_msg_panda("DI_speed", 0, values) - - def _vehicle_moving_msg(self, speed: float): - values = {"DI_cruiseState": 3 if speed <= self.STANDSTILL_THRESHOLD else 2} - return self.packer.make_can_msg_panda("DI_state", 0, values) - - def _user_gas_msg(self, gas): - values = {"DI_accelPedalPos": gas} - return self.packer.make_can_msg_panda("DI_systemStatus", 0, values) - - def _pcm_status_msg(self, enable): - values = {"DI_cruiseState": 2 if enable else 0} - return self.packer.make_can_msg_panda("DI_state", 0, values) - - def _long_control_msg(self, set_speed, acc_val=0, jerk_limits=(0, 0), accel_limits=(0, 0), aeb_event=0, bus=0): - values = { - "DAS_setSpeed": set_speed, - "DAS_accState": acc_val, - "DAS_aebEvent": aeb_event, - "DAS_jerkMin": jerk_limits[0], - "DAS_jerkMax": jerk_limits[1], - "DAS_accelMin": accel_limits[0], - "DAS_accelMax": accel_limits[1], - } - return self.packer.make_can_msg_panda("DAS_control", bus, values) - - def _accel_msg(self, accel: float): - # For common.LongitudinalAccelSafetyTest - return self._long_control_msg(10, accel_limits=(accel, max(accel, 0))) - - def test_vehicle_speed_measurements(self): - # OVERRIDDEN: 79.1667 is the max speed in m/s - self._common_measurement_test(self._speed_msg, 0, 285 / 3.6, common.VEHICLE_SPEED_FACTOR, - self.safety.get_vehicle_speed_min, self.safety.get_vehicle_speed_max) - - -class TestTeslaStockSafety(TestTeslaSafetyBase): - - def setUp(self): - self.packer = CANPackerPanda("tesla_model3_party") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_TESLA, 0) - self.safety.init_tests() - - def test_accel_actuation_limits(self, stock_longitudinal=True): - super().test_accel_actuation_limits(stock_longitudinal) - - def test_cancel(self): - for accval in range(16): - self.safety.set_controls_allowed(True) - should_tx = accval == 13 # ACC_CANCEL_GENERIC_SILENT - self.assertFalse(self._tx(self._long_control_msg(0, acc_val=accval, accel_limits=(self.MIN_ACCEL, self.MAX_ACCEL)))) - self.assertEqual(should_tx, self._tx(self._long_control_msg(0, acc_val=accval))) - - def test_no_aeb(self): - for aeb_event in range(4): - self.assertEqual(self._tx(self._long_control_msg(10, acc_val=13, aeb_event=aeb_event)), aeb_event == 0) - - -class TestTeslaLongitudinalSafety(TestTeslaSafetyBase): - RELAY_MALFUNCTION_ADDRS = {0: (MSG_DAS_steeringControl, MSG_APS_eacMonitor, MSG_DAS_Control)} - FWD_BLACKLISTED_ADDRS = {2: [MSG_DAS_steeringControl, MSG_APS_eacMonitor, MSG_DAS_Control]} - - def setUp(self): - self.packer = CANPackerPanda("tesla_model3_party") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_TESLA, TeslaSafetyFlags.FLAG_TESLA_LONG_CONTROL) - self.safety.init_tests() - - def test_no_aeb(self): - for aeb_event in range(4): - self.assertEqual(self._tx(self._long_control_msg(10, aeb_event=aeb_event)), aeb_event == 0) - - def test_stock_aeb_passthrough(self): - no_aeb_msg = self._long_control_msg(10, aeb_event=0) - no_aeb_msg_cam = self._long_control_msg(10, aeb_event=0, bus=2) - aeb_msg_cam = self._long_control_msg(10, aeb_event=1, bus=2) - - # stock system sends no AEB -> no forwarding, and OP is allowed to TX - self.assertEqual(1, self._rx(no_aeb_msg_cam)) - self.assertEqual(-1, self.safety.safety_fwd_hook(2, no_aeb_msg_cam.addr)) - self.assertTrue(self._tx(no_aeb_msg)) - - # stock system sends AEB -> forwarding, and OP is not allowed to TX - self.assertEqual(1, self._rx(aeb_msg_cam)) - self.assertEqual(0, self.safety.safety_fwd_hook(2, aeb_msg_cam.addr)) - self.assertFalse(self._tx(no_aeb_msg)) - - def test_prevent_reverse(self): - # Note: Tesla can reverse while at a standstill if both accel_min and accel_max are negative. - self.safety.set_controls_allowed(True) - - # accel_min and accel_max are positive - self.assertTrue(self._tx(self._long_control_msg(set_speed=10, accel_limits=(1.1, 0.8)))) - self.assertTrue(self._tx(self._long_control_msg(set_speed=0, accel_limits=(1.1, 0.8)))) - - # accel_min and accel_max are both zero - self.assertTrue(self._tx(self._long_control_msg(set_speed=10, accel_limits=(0, 0)))) - self.assertTrue(self._tx(self._long_control_msg(set_speed=0, accel_limits=(0, 0)))) - - # accel_min and accel_max have opposing signs - self.assertTrue(self._tx(self._long_control_msg(set_speed=10, accel_limits=(-0.8, 1.3)))) - self.assertTrue(self._tx(self._long_control_msg(set_speed=0, accel_limits=(0.8, -1.3)))) - self.assertTrue(self._tx(self._long_control_msg(set_speed=0, accel_limits=(0, -1.3)))) - - # accel_min and accel_max are negative - self.assertFalse(self._tx(self._long_control_msg(set_speed=10, accel_limits=(-1.1, -0.6)))) - self.assertFalse(self._tx(self._long_control_msg(set_speed=0, accel_limits=(-0.6, -1.1)))) - self.assertFalse(self._tx(self._long_control_msg(set_speed=0, accel_limits=(-0.1, -0.1)))) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/safety/test_toyota.py b/tests/safety/test_toyota.py deleted file mode 100755 index 5a9ee37acd..0000000000 --- a/tests/safety/test_toyota.py +++ /dev/null @@ -1,364 +0,0 @@ -#!/usr/bin/env python3 -import numpy as np -import random -import unittest -import itertools - -from opendbc.car.toyota.values import ToyotaSafetyFlags -from opendbc.safety import Safety -from panda.tests.libsafety import libsafety_py -import panda.tests.safety.common as common -from panda.tests.safety.common import CANPackerPanda - -TOYOTA_COMMON_TX_MSGS = [[0x2E4, 0], [0x191, 0], [0x412, 0], [0x343, 0], [0x1D2, 0]] # LKAS + LTA + ACC & PCM cancel cmds -TOYOTA_SECOC_TX_MSGS = [[0x131, 0]] + TOYOTA_COMMON_TX_MSGS -TOYOTA_COMMON_LONG_TX_MSGS = [[0x283, 0], [0x2E6, 0], [0x2E7, 0], [0x33E, 0], [0x344, 0], [0x365, 0], [0x366, 0], [0x4CB, 0], # DSU bus 0 - [0x128, 1], [0x141, 1], [0x160, 1], [0x161, 1], [0x470, 1], # DSU bus 1 - [0x411, 0], # PCS_HUD - [0x750, 0]] # radar diagnostic address - - -class TestToyotaSafetyBase(common.PandaCarSafetyTest, common.LongitudinalAccelSafetyTest): - - TX_MSGS = TOYOTA_COMMON_TX_MSGS + TOYOTA_COMMON_LONG_TX_MSGS - STANDSTILL_THRESHOLD = 0 # kph - RELAY_MALFUNCTION_ADDRS = {0: (0x2E4, 0x343)} - FWD_BLACKLISTED_ADDRS = {2: [0x2E4, 0x412, 0x191, 0x343]} - FWD_BUS_LOOKUP = {0: 2, 2: 0} - EPS_SCALE = 73 - - packer: CANPackerPanda - safety: libsafety_py.Panda - - @classmethod - def setUpClass(cls): - if cls.__name__.endswith("Base"): - cls.packer = None - cls.safety = None - raise unittest.SkipTest - - def _torque_meas_msg(self, torque: int, driver_torque: int | None = None): - values = {"STEER_TORQUE_EPS": (torque / self.EPS_SCALE) * 100.} - if driver_torque is not None: - values["STEER_TORQUE_DRIVER"] = driver_torque - return self.packer.make_can_msg_panda("STEER_TORQUE_SENSOR", 0, values) - - # Both torque and angle safety modes test with each other's steering commands - def _torque_cmd_msg(self, torque, steer_req=1): - values = {"STEER_TORQUE_CMD": torque, "STEER_REQUEST": steer_req} - return self.packer.make_can_msg_panda("STEERING_LKA", 0, values) - - def _angle_meas_msg(self, angle: float, steer_angle_initializing: bool = False): - # This creates a steering torque angle message. Not set on all platforms, - # relative to init angle on some older TSS2 platforms. Only to be used with LTA - values = {"STEER_ANGLE": angle, "STEER_ANGLE_INITIALIZING": int(steer_angle_initializing)} - return self.packer.make_can_msg_panda("STEER_TORQUE_SENSOR", 0, values) - - def _angle_cmd_msg(self, angle: float, enabled: bool): - return self._lta_msg(int(enabled), int(enabled), angle, torque_wind_down=100 if enabled else 0) - - def _lta_msg(self, req, req2, angle_cmd, torque_wind_down=100): - values = {"STEER_REQUEST": req, "STEER_REQUEST_2": req2, "STEER_ANGLE_CMD": angle_cmd, "TORQUE_WIND_DOWN": torque_wind_down} - return self.packer.make_can_msg_panda("STEERING_LTA", 0, values) - - def _accel_msg(self, accel, cancel_req=0): - values = {"ACCEL_CMD": accel, "CANCEL_REQ": cancel_req} - return self.packer.make_can_msg_panda("ACC_CONTROL", 0, values) - - def _speed_msg(self, speed): - values = {("WHEEL_SPEED_%s" % n): speed * 3.6 for n in ["FR", "FL", "RR", "RL"]} - return self.packer.make_can_msg_panda("WHEEL_SPEEDS", 0, values) - - def _user_brake_msg(self, brake): - values = {"BRAKE_PRESSED": brake} - return self.packer.make_can_msg_panda("BRAKE_MODULE", 0, values) - - def _user_gas_msg(self, gas): - cruise_active = self.safety.get_controls_allowed() - values = {"GAS_RELEASED": not gas, "CRUISE_ACTIVE": cruise_active} - return self.packer.make_can_msg_panda("PCM_CRUISE", 0, values) - - def _pcm_status_msg(self, enable): - values = {"CRUISE_ACTIVE": enable} - return self.packer.make_can_msg_panda("PCM_CRUISE", 0, values) - - def test_diagnostics(self, stock_longitudinal: bool = False): - for should_tx, msg in ((False, b"\x6D\x02\x3E\x00\x00\x00\x00\x00"), # fwdCamera tester present - (False, b"\x0F\x03\xAA\xAA\x00\x00\x00\x00"), # non-tester present - (True, b"\x0F\x02\x3E\x00\x00\x00\x00\x00")): - tester_present = libsafety_py.make_CANPacket(0x750, 0, msg) - self.assertEqual(should_tx and not stock_longitudinal, self._tx(tester_present)) - - def test_block_aeb(self, stock_longitudinal: bool = False): - for controls_allowed in (True, False): - for bad in (True, False): - for _ in range(10): - self.safety.set_controls_allowed(controls_allowed) - dat = [random.randint(1, 255) for _ in range(7)] - if not bad: - dat = [0]*6 + dat[-1:] - msg = libsafety_py.make_CANPacket(0x283, 0, bytes(dat)) - self.assertEqual(not bad and not stock_longitudinal, self._tx(msg)) - - # Only allow LTA msgs with no actuation - def test_lta_steer_cmd(self): - for engaged, req, req2, torque_wind_down, angle in itertools.product([True, False], - [0, 1], [0, 1], - [0, 50, 100], - np.linspace(-20, 20, 5)): - self.safety.set_controls_allowed(engaged) - - should_tx = not req and not req2 and angle == 0 and torque_wind_down == 0 - self.assertEqual(should_tx, self._tx(self._lta_msg(req, req2, angle, torque_wind_down)), - f"{req=} {req2=} {angle=} {torque_wind_down=}") - - def test_rx_hook(self): - # checksum checks - for msg in ["trq", "pcm"]: - self.safety.set_controls_allowed(1) - if msg == "trq": - to_push = self._torque_meas_msg(0) - if msg == "pcm": - to_push = self._pcm_status_msg(True) - self.assertTrue(self._rx(to_push)) - to_push[0].data[4] = 0 - to_push[0].data[5] = 0 - to_push[0].data[6] = 0 - to_push[0].data[7] = 0 - self.assertFalse(self._rx(to_push)) - self.assertFalse(self.safety.get_controls_allowed()) - - -class TestToyotaSafetyTorque(TestToyotaSafetyBase, common.MotorTorqueSteeringSafetyTest, common.SteerRequestCutSafetyTest): - - MAX_RATE_UP = 15 - MAX_RATE_DOWN = 25 - MAX_TORQUE = 1500 - MAX_RT_DELTA = 450 - RT_INTERVAL = 250000 - MAX_TORQUE_ERROR = 350 - TORQUE_MEAS_TOLERANCE = 1 # toyota safety adds one to be conservative for rounding - - # Safety around steering req bit - MIN_VALID_STEERING_FRAMES = 18 - MAX_INVALID_STEERING_FRAMES = 1 - MIN_VALID_STEERING_RT_INTERVAL = 170000 # a ~10% buffer, can send steer up to 110Hz - - def setUp(self): - self.packer = CANPackerPanda("toyota_nodsu_pt_generated") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_TOYOTA, self.EPS_SCALE) - self.safety.init_tests() - - -class TestToyotaSafetyAngle(TestToyotaSafetyBase, common.AngleSteeringSafetyTest): - - # Angle control limits - DEG_TO_CAN = 17.452007 # 1 / 0.0573 deg to can - - ANGLE_RATE_BP = [5., 25., 25.] - ANGLE_RATE_UP = [0.3, 0.15, 0.15] # windup limit - ANGLE_RATE_DOWN = [0.36, 0.26, 0.26] # unwind limit - - MAX_LTA_ANGLE = 94.9461 # PCS faults if commanding above this, deg - MAX_MEAS_TORQUE = 1500 # max allowed measured EPS torque before wind down - MAX_LTA_DRIVER_TORQUE = 150 # max allowed driver torque before wind down - - def setUp(self): - self.packer = CANPackerPanda("toyota_nodsu_pt_generated") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_TOYOTA, self.EPS_SCALE | ToyotaSafetyFlags.FLAG_TOYOTA_LTA) - self.safety.init_tests() - - # Only allow LKA msgs with no actuation - def test_lka_steer_cmd(self): - for engaged, steer_req, torque in itertools.product([True, False], - [0, 1], - np.linspace(-1500, 1500, 7)): - self.safety.set_controls_allowed(engaged) - torque = int(torque) - self.safety.set_rt_torque_last(torque) - self.safety.set_torque_meas(torque, torque) - self.safety.set_desired_torque_last(torque) - - should_tx = not steer_req and torque == 0 - self.assertEqual(should_tx, self._tx(self._torque_cmd_msg(torque, steer_req))) - - def test_lta_steer_cmd(self): - """ - Tests the LTA steering command message - controls_allowed: - * STEER_REQUEST and STEER_REQUEST_2 do not mismatch - * TORQUE_WIND_DOWN is only set to 0 or 100 when STEER_REQUEST and STEER_REQUEST_2 are both 1 - * Full torque messages are blocked if either EPS torque or driver torque is above the threshold - - not controls_allowed: - * STEER_REQUEST, STEER_REQUEST_2, and TORQUE_WIND_DOWN are all 0 - """ - for controls_allowed in (True, False): - for angle in np.arange(-90, 90, 1): - self.safety.set_controls_allowed(controls_allowed) - self._reset_angle_measurement(angle) - self._set_prev_desired_angle(angle) - - self.assertTrue(self._tx(self._lta_msg(0, 0, angle, 0))) - if controls_allowed: - # Test the two steer request bits and TORQUE_WIND_DOWN torque wind down signal - for req, req2, torque_wind_down in itertools.product([0, 1], [0, 1], [0, 50, 100]): - mismatch = not (req or req2) and torque_wind_down != 0 - should_tx = req == req2 and (torque_wind_down in (0, 100)) and not mismatch - self.assertEqual(should_tx, self._tx(self._lta_msg(req, req2, angle, torque_wind_down))) - - # Test max EPS torque and driver override thresholds - cases = itertools.product( - (0, self.MAX_MEAS_TORQUE - 1, self.MAX_MEAS_TORQUE, self.MAX_MEAS_TORQUE + 1, self.MAX_MEAS_TORQUE * 2), - (0, self.MAX_LTA_DRIVER_TORQUE - 1, self.MAX_LTA_DRIVER_TORQUE, self.MAX_LTA_DRIVER_TORQUE + 1, self.MAX_LTA_DRIVER_TORQUE * 2) - ) - - for eps_torque, driver_torque in cases: - for sign in (-1, 1): - for _ in range(6): - self._rx(self._torque_meas_msg(sign * eps_torque, sign * driver_torque)) - - # Toyota adds 1 to EPS torque since it is rounded after EPS factor - should_tx = (eps_torque - 1) <= self.MAX_MEAS_TORQUE and driver_torque <= self.MAX_LTA_DRIVER_TORQUE - self.assertEqual(should_tx, self._tx(self._lta_msg(1, 1, angle, 100))) - self.assertTrue(self._tx(self._lta_msg(1, 1, angle, 0))) # should tx if we wind down torque - - else: - # Controls not allowed - for req, req2, torque_wind_down in itertools.product([0, 1], [0, 1], [0, 50, 100]): - should_tx = not (req or req2) and torque_wind_down == 0 - self.assertEqual(should_tx, self._tx(self._lta_msg(req, req2, angle, torque_wind_down))) - - def test_steering_angle_measurements(self, max_angle=None): - # Measurement test tests max angle + 0.5 which will fail - super().test_steering_angle_measurements(max_angle=self.MAX_LTA_ANGLE - 0.5) - - def test_angle_cmd_when_enabled(self, max_angle=None): - super().test_angle_cmd_when_enabled(max_angle=self.MAX_LTA_ANGLE) - - def test_angle_measurements(self): - """ - * Tests angle meas quality flag dictates whether angle measurement is parsed, and if rx is valid - * Tests rx hook correctly clips the angle measurement, since it is to be compared to LTA cmd when inactive - """ - for steer_angle_initializing in (True, False): - for angle in np.arange(0, self.MAX_LTA_ANGLE * 2, 1): - # If init flag is set, do not rx or parse any angle measurements - for a in (angle, -angle, 0, 0, 0, 0): - self.assertEqual(not steer_angle_initializing, - self._rx(self._angle_meas_msg(a, steer_angle_initializing))) - - final_angle = (0 if steer_angle_initializing else - round(min(angle, self.MAX_LTA_ANGLE) * self.DEG_TO_CAN)) - self.assertEqual(self.safety.get_angle_meas_min(), -final_angle) - self.assertEqual(self.safety.get_angle_meas_max(), final_angle) - - self._rx(self._angle_meas_msg(0)) - self.assertEqual(self.safety.get_angle_meas_min(), -final_angle) - self.assertEqual(self.safety.get_angle_meas_max(), 0) - - self._rx(self._angle_meas_msg(0)) - self.assertEqual(self.safety.get_angle_meas_min(), 0) - self.assertEqual(self.safety.get_angle_meas_max(), 0) - - -class TestToyotaAltBrakeSafety(TestToyotaSafetyTorque): - - def setUp(self): - self.packer = CANPackerPanda("toyota_new_mc_pt_generated") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_TOYOTA, self.EPS_SCALE | ToyotaSafetyFlags.FLAG_TOYOTA_ALT_BRAKE) - self.safety.init_tests() - - def _user_brake_msg(self, brake): - values = {"BRAKE_PRESSED": brake} - return self.packer.make_can_msg_panda("BRAKE_MODULE", 0, values) - - # No LTA message in the DBC - def test_lta_steer_cmd(self): - pass - - -class TestToyotaStockLongitudinalBase(TestToyotaSafetyBase): - - TX_MSGS = TOYOTA_COMMON_TX_MSGS - # Base addresses minus ACC_CONTROL (0x343) - RELAY_MALFUNCTION_ADDRS = {0: (0x2E4,)} - FWD_BLACKLISTED_ADDRS = {2: [0x2E4, 0x412, 0x191]} - - def test_diagnostics(self, stock_longitudinal: bool = True): - super().test_diagnostics(stock_longitudinal=stock_longitudinal) - - def test_block_aeb(self, stock_longitudinal: bool = True): - super().test_block_aeb(stock_longitudinal=stock_longitudinal) - - def test_accel_actuation_limits(self, stock_longitudinal=True): - super().test_accel_actuation_limits(stock_longitudinal=stock_longitudinal) - - def test_acc_cancel(self): - """ - Regardless of controls allowed, never allow ACC_CONTROL if cancel bit isn't set - """ - for controls_allowed in [True, False]: - self.safety.set_controls_allowed(controls_allowed) - for accel in np.arange(self.MIN_ACCEL - 1, self.MAX_ACCEL + 1, 0.1): - self.assertFalse(self._tx(self._accel_msg(accel))) - should_tx = np.isclose(accel, 0, atol=0.0001) - self.assertEqual(should_tx, self._tx(self._accel_msg(accel, cancel_req=1))) - - -class TestToyotaStockLongitudinalTorque(TestToyotaStockLongitudinalBase, TestToyotaSafetyTorque): - - def setUp(self): - self.packer = CANPackerPanda("toyota_nodsu_pt_generated") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_TOYOTA, self.EPS_SCALE | ToyotaSafetyFlags.FLAG_TOYOTA_STOCK_LONGITUDINAL) - self.safety.init_tests() - - -class TestToyotaStockLongitudinalAngle(TestToyotaStockLongitudinalBase, TestToyotaSafetyAngle): - - def setUp(self): - self.packer = CANPackerPanda("toyota_nodsu_pt_generated") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_TOYOTA, self.EPS_SCALE | ToyotaSafetyFlags.FLAG_TOYOTA_STOCK_LONGITUDINAL | ToyotaSafetyFlags.FLAG_TOYOTA_LTA) - self.safety.init_tests() - - -class TestToyotaSecOcSafety(TestToyotaStockLongitudinalBase): - - TX_MSGS = TOYOTA_SECOC_TX_MSGS - RELAY_MALFUNCTION_ADDRS = {0: (0x2E4,)} - FWD_BLACKLISTED_ADDRS = {2: [0x2E4, 0x412, 0x191, 0x131]} - - def setUp(self): - self.packer = CANPackerPanda("toyota_secoc_pt_generated") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_TOYOTA, self.EPS_SCALE | ToyotaSafetyFlags.FLAG_TOYOTA_STOCK_LONGITUDINAL | ToyotaSafetyFlags.FLAG_TOYOTA_SECOC) - self.safety.init_tests() - - # This platform also has alternate brake and PCM messages, but same naming in the DBC, so same packers work - - def _user_gas_msg(self, gas): - values = {"GAS_PEDAL_USER": gas} - return self.packer.make_can_msg_panda("GAS_PEDAL", 0, values) - - # This platform sends both STEERING_LTA (same as other Toyota) and STEERING_LTA_2 (SecOC signed) - # STEERING_LTA is checked for no-actuation by the base class, STEERING_LTA_2 is checked for no-actuation below - - def _lta_2_msg(self, req, req2, angle_cmd, torque_wind_down=100): - values = {"STEER_REQUEST": req, "STEER_REQUEST_2": req2, "STEER_ANGLE_CMD": angle_cmd} - return self.packer.make_can_msg_panda("STEERING_LTA_2", 0, values) - - def test_lta_2_steer_cmd(self): - for engaged, req, req2, angle in itertools.product([True, False], [0, 1], [0, 1], np.linspace(-20, 20, 5)): - self.safety.set_controls_allowed(engaged) - - should_tx = not req and not req2 and angle == 0 - self.assertEqual(should_tx, self._tx(self._lta_2_msg(req, req2, angle)), f"{req=} {req2=} {angle=}") - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/safety/test_volkswagen_mqb.py b/tests/safety/test_volkswagen_mqb.py deleted file mode 100755 index 0f09f340e5..0000000000 --- a/tests/safety/test_volkswagen_mqb.py +++ /dev/null @@ -1,226 +0,0 @@ -#!/usr/bin/env python3 -import unittest -import numpy as np -from opendbc.safety import Safety -from panda.tests.libsafety import libsafety_py -import panda.tests.safety.common as common -from panda.tests.safety.common import CANPackerPanda -from opendbc.car.volkswagen.values import VolkswagenSafetyFlags - -MAX_ACCEL = 2.0 -MIN_ACCEL = -3.5 - -MSG_ESP_19 = 0xB2 # RX from ABS, for wheel speeds -MSG_LH_EPS_03 = 0x9F # RX from EPS, for driver steering torque -MSG_ESP_05 = 0x106 # RX from ABS, for brake light state -MSG_TSK_06 = 0x120 # RX from ECU, for ACC status from drivetrain coordinator -MSG_MOTOR_20 = 0x121 # RX from ECU, for driver throttle input -MSG_ACC_06 = 0x122 # TX by OP, ACC control instructions to the drivetrain coordinator -MSG_HCA_01 = 0x126 # TX by OP, Heading Control Assist steering torque -MSG_GRA_ACC_01 = 0x12B # TX by OP, ACC control buttons for cancel/resume -MSG_ACC_07 = 0x12E # TX by OP, ACC control instructions to the drivetrain coordinator -MSG_ACC_02 = 0x30C # TX by OP, ACC HUD data to the instrument cluster -MSG_LDW_02 = 0x397 # TX by OP, Lane line recognition and text alerts - - -class TestVolkswagenMqbSafety(common.PandaCarSafetyTest, common.DriverTorqueSteeringSafetyTest): - STANDSTILL_THRESHOLD = 0 - RELAY_MALFUNCTION_ADDRS = {0: (MSG_HCA_01,)} - - MAX_RATE_UP = 4 - MAX_RATE_DOWN = 10 - MAX_TORQUE = 300 - MAX_RT_DELTA = 75 - RT_INTERVAL = 250000 - - DRIVER_TORQUE_ALLOWANCE = 80 - DRIVER_TORQUE_FACTOR = 3 - - @classmethod - def setUpClass(cls): - if cls.__name__ == "TestVolkswagenMqbSafety": - cls.packer = None - cls.safety = None - raise unittest.SkipTest - - # Wheel speeds _esp_19_msg - def _speed_msg(self, speed): - values = {"ESP_%s_Radgeschw_02" % s: speed for s in ["HL", "HR", "VL", "VR"]} - return self.packer.make_can_msg_panda("ESP_19", 0, values) - - # Driver brake pressure over threshold - def _esp_05_msg(self, brake): - values = {"ESP_Fahrer_bremst": brake} - return self.packer.make_can_msg_panda("ESP_05", 0, values) - - # Brake pedal switch - def _motor_14_msg(self, brake): - values = {"MO_Fahrer_bremst": brake} - return self.packer.make_can_msg_panda("Motor_14", 0, values) - - def _user_brake_msg(self, brake): - return self._motor_14_msg(brake) - - # Driver throttle input - def _user_gas_msg(self, gas): - values = {"MO_Fahrpedalrohwert_01": gas} - return self.packer.make_can_msg_panda("Motor_20", 0, values) - - # ACC engagement status - def _tsk_status_msg(self, enable, main_switch=True): - if main_switch: - tsk_status = 3 if enable else 2 - else: - tsk_status = 0 - values = {"TSK_Status": tsk_status} - return self.packer.make_can_msg_panda("TSK_06", 0, values) - - def _pcm_status_msg(self, enable): - return self._tsk_status_msg(enable) - - # Driver steering input torque - def _torque_driver_msg(self, torque): - values = {"EPS_Lenkmoment": abs(torque), "EPS_VZ_Lenkmoment": torque < 0} - return self.packer.make_can_msg_panda("LH_EPS_03", 0, values) - - # openpilot steering output torque - def _torque_cmd_msg(self, torque, steer_req=1): - values = {"HCA_01_LM_Offset": abs(torque), "HCA_01_LM_OffSign": torque < 0, "HCA_01_Sendestatus": steer_req} - return self.packer.make_can_msg_panda("HCA_01", 0, values) - - # Cruise control buttons - def _gra_acc_01_msg(self, cancel=0, resume=0, _set=0, bus=2): - values = {"GRA_Abbrechen": cancel, "GRA_Tip_Setzen": _set, "GRA_Tip_Wiederaufnahme": resume} - return self.packer.make_can_msg_panda("GRA_ACC_01", bus, values) - - # Acceleration request to drivetrain coordinator - def _acc_06_msg(self, accel): - values = {"ACC_Sollbeschleunigung_02": accel} - return self.packer.make_can_msg_panda("ACC_06", 0, values) - - # Acceleration request to drivetrain coordinator - def _acc_07_msg(self, accel, secondary_accel=3.02): - values = {"ACC_Sollbeschleunigung_02": accel, "ACC_Folgebeschl": secondary_accel} - return self.packer.make_can_msg_panda("ACC_07", 0, values) - - # Verify brake_pressed is true if either the switch or pressure threshold signals are true - def test_redundant_brake_signals(self): - test_combinations = [(True, True, True), (True, True, False), (True, False, True), (False, False, False)] - for brake_pressed, motor_14_signal, esp_05_signal in test_combinations: - self._rx(self._motor_14_msg(False)) - self._rx(self._esp_05_msg(False)) - self.assertFalse(self.safety.get_brake_pressed_prev()) - self._rx(self._motor_14_msg(motor_14_signal)) - self._rx(self._esp_05_msg(esp_05_signal)) - self.assertEqual(brake_pressed, self.safety.get_brake_pressed_prev(), - f"expected {brake_pressed=} with {motor_14_signal=} and {esp_05_signal=}") - - def test_torque_measurements(self): - # TODO: make this test work with all cars - self._rx(self._torque_driver_msg(50)) - self._rx(self._torque_driver_msg(-50)) - self._rx(self._torque_driver_msg(0)) - self._rx(self._torque_driver_msg(0)) - self._rx(self._torque_driver_msg(0)) - self._rx(self._torque_driver_msg(0)) - - self.assertEqual(-50, self.safety.get_torque_driver_min()) - self.assertEqual(50, self.safety.get_torque_driver_max()) - - self._rx(self._torque_driver_msg(0)) - self.assertEqual(0, self.safety.get_torque_driver_max()) - self.assertEqual(-50, self.safety.get_torque_driver_min()) - - self._rx(self._torque_driver_msg(0)) - self.assertEqual(0, self.safety.get_torque_driver_max()) - self.assertEqual(0, self.safety.get_torque_driver_min()) - - -class TestVolkswagenMqbStockSafety(TestVolkswagenMqbSafety): - TX_MSGS = [[MSG_HCA_01, 0], [MSG_LDW_02, 0], [MSG_LH_EPS_03, 2], [MSG_GRA_ACC_01, 0], [MSG_GRA_ACC_01, 2]] - FWD_BLACKLISTED_ADDRS = {0: [MSG_LH_EPS_03], 2: [MSG_HCA_01, MSG_LDW_02]} - FWD_BUS_LOOKUP = {0: 2, 2: 0} - - def setUp(self): - self.packer = CANPackerPanda("vw_mqb_2010") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_VOLKSWAGEN_MQB, 0) - self.safety.init_tests() - - def test_spam_cancel_safety_check(self): - self.safety.set_controls_allowed(0) - self.assertTrue(self._tx(self._gra_acc_01_msg(cancel=1))) - self.assertFalse(self._tx(self._gra_acc_01_msg(resume=1))) - self.assertFalse(self._tx(self._gra_acc_01_msg(_set=1))) - # do not block resume if we are engaged already - self.safety.set_controls_allowed(1) - self.assertTrue(self._tx(self._gra_acc_01_msg(resume=1))) - - -class TestVolkswagenMqbLongSafety(TestVolkswagenMqbSafety): - TX_MSGS = [[MSG_HCA_01, 0], [MSG_LDW_02, 0], [MSG_LH_EPS_03, 2], [MSG_ACC_02, 0], [MSG_ACC_06, 0], [MSG_ACC_07, 0]] - FWD_BLACKLISTED_ADDRS = {0: [MSG_LH_EPS_03], 2: [MSG_HCA_01, MSG_LDW_02, MSG_ACC_02, MSG_ACC_06, MSG_ACC_07]} - FWD_BUS_LOOKUP = {0: 2, 2: 0} - INACTIVE_ACCEL = 3.01 - - def setUp(self): - self.packer = CANPackerPanda("vw_mqb_2010") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_VOLKSWAGEN_MQB, VolkswagenSafetyFlags.FLAG_VOLKSWAGEN_LONG_CONTROL) - self.safety.init_tests() - - # stock cruise controls are entirely bypassed under openpilot longitudinal control - def test_disable_control_allowed_from_cruise(self): - pass - - def test_enable_control_allowed_from_cruise(self): - pass - - def test_cruise_engaged_prev(self): - pass - - def test_set_and_resume_buttons(self): - for button in ["set", "resume"]: - # ACC main switch must be on, engage on falling edge - self.safety.set_controls_allowed(0) - self._rx(self._tsk_status_msg(False, main_switch=False)) - self._rx(self._gra_acc_01_msg(_set=(button == "set"), resume=(button == "resume"), bus=0)) - self.assertFalse(self.safety.get_controls_allowed(), f"controls allowed on {button} with main switch off") - self._rx(self._tsk_status_msg(False, main_switch=True)) - self._rx(self._gra_acc_01_msg(_set=(button == "set"), resume=(button == "resume"), bus=0)) - self.assertFalse(self.safety.get_controls_allowed(), f"controls allowed on {button} rising edge") - self._rx(self._gra_acc_01_msg(bus=0)) - self.assertTrue(self.safety.get_controls_allowed(), f"controls not allowed on {button} falling edge") - - def test_cancel_button(self): - # Disable on rising edge of cancel button - self._rx(self._tsk_status_msg(False, main_switch=True)) - self.safety.set_controls_allowed(1) - self._rx(self._gra_acc_01_msg(cancel=True, bus=0)) - self.assertFalse(self.safety.get_controls_allowed(), "controls allowed after cancel") - - def test_main_switch(self): - # Disable as soon as main switch turns off - self._rx(self._tsk_status_msg(False, main_switch=True)) - self.safety.set_controls_allowed(1) - self._rx(self._tsk_status_msg(False, main_switch=False)) - self.assertFalse(self.safety.get_controls_allowed(), "controls allowed after ACC main switch off") - - def test_accel_safety_check(self): - for controls_allowed in [True, False]: - # enforce we don't skip over 0 or inactive accel - for accel in np.concatenate((np.arange(MIN_ACCEL - 2, MAX_ACCEL + 2, 0.03), [0, self.INACTIVE_ACCEL])): - accel = round(accel, 2) # floats might not hit exact boundary conditions without rounding - is_inactive_accel = accel == self.INACTIVE_ACCEL - send = (controls_allowed and MIN_ACCEL <= accel <= MAX_ACCEL) or is_inactive_accel - self.safety.set_controls_allowed(controls_allowed) - # primary accel request used by ECU - self.assertEqual(send, self._tx(self._acc_06_msg(accel)), (controls_allowed, accel)) - # additional accel request used by ABS/ESP - self.assertEqual(send, self._tx(self._acc_07_msg(accel)), (controls_allowed, accel)) - # ensure the optional secondary accel field remains inactive for now - self.assertEqual(is_inactive_accel, self._tx(self._acc_07_msg(accel, secondary_accel=accel)), (controls_allowed, accel)) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/safety/test_volkswagen_pq.py b/tests/safety/test_volkswagen_pq.py deleted file mode 100755 index e6bb56d377..0000000000 --- a/tests/safety/test_volkswagen_pq.py +++ /dev/null @@ -1,201 +0,0 @@ -#!/usr/bin/env python3 -import unittest - -from opendbc.car.volkswagen.values import VolkswagenSafetyFlags -from opendbc.safety import Safety -from panda.tests.libsafety import libsafety_py -import panda.tests.safety.common as common -from panda.tests.safety.common import CANPackerPanda - -MSG_LENKHILFE_3 = 0x0D0 # RX from EPS, for steering angle and driver steering torque -MSG_HCA_1 = 0x0D2 # TX by OP, Heading Control Assist steering torque -MSG_BREMSE_1 = 0x1A0 # RX from ABS, for ego speed -MSG_MOTOR_2 = 0x288 # RX from ECU, for CC state and brake switch state -MSG_ACC_SYSTEM = 0x368 # TX by OP, longitudinal acceleration controls -MSG_MOTOR_3 = 0x380 # RX from ECU, for driver throttle input -MSG_GRA_NEU = 0x38A # TX by OP, ACC control buttons for cancel/resume -MSG_MOTOR_5 = 0x480 # RX from ECU, for ACC main switch state -MSG_ACC_GRA_ANZEIGE = 0x56A # TX by OP, ACC HUD -MSG_LDW_1 = 0x5BE # TX by OP, Lane line recognition and text alerts - - -class TestVolkswagenPqSafety(common.PandaCarSafetyTest, common.DriverTorqueSteeringSafetyTest): - cruise_engaged = False - - STANDSTILL_THRESHOLD = 0 - RELAY_MALFUNCTION_ADDRS = {0: (MSG_HCA_1,)} - - MAX_RATE_UP = 6 - MAX_RATE_DOWN = 10 - MAX_TORQUE = 300 - MAX_RT_DELTA = 113 - RT_INTERVAL = 250000 - - DRIVER_TORQUE_ALLOWANCE = 80 - DRIVER_TORQUE_FACTOR = 3 - - @classmethod - def setUpClass(cls): - if cls.__name__ == "TestVolkswagenPqSafety": - cls.packer = None - cls.safety = None - raise unittest.SkipTest - - def _set_prev_torque(self, t): - self.safety.set_desired_torque_last(t) - self.safety.set_rt_torque_last(t) - - # Ego speed (Bremse_1) - def _speed_msg(self, speed): - values = {"Geschwindigkeit_neu__Bremse_1_": speed} - return self.packer.make_can_msg_panda("Bremse_1", 0, values) - - # Brake light switch (shared message Motor_2) - def _user_brake_msg(self, brake): - # since this signal is used for engagement status, preserve current state - return self._motor_2_msg(brake_pressed=brake, cruise_engaged=self.safety.get_controls_allowed()) - - # ACC engaged status (shared message Motor_2) - def _pcm_status_msg(self, enable): - self.__class__.cruise_engaged = enable - return self._motor_2_msg(cruise_engaged=enable) - - # Acceleration request to drivetrain coordinator - def _accel_msg(self, accel): - values = {"ACS_Sollbeschl": accel} - return self.packer.make_can_msg_panda("ACC_System", 0, values) - - # Driver steering input torque - def _torque_driver_msg(self, torque): - values = {"LH3_LM": abs(torque), "LH3_LMSign": torque < 0} - return self.packer.make_can_msg_panda("Lenkhilfe_3", 0, values) - - # openpilot steering output torque - def _torque_cmd_msg(self, torque, steer_req=1, hca_status=5): - values = {"LM_Offset": abs(torque), "LM_OffSign": torque < 0, "HCA_Status": hca_status if steer_req else 3} - return self.packer.make_can_msg_panda("HCA_1", 0, values) - - # ACC engagement and brake light switch status - # Called indirectly for compatibility with common.py tests - def _motor_2_msg(self, brake_pressed=False, cruise_engaged=False): - values = {"Bremslichtschalter": brake_pressed, - "GRA_Status": cruise_engaged} - return self.packer.make_can_msg_panda("Motor_2", 0, values) - - # ACC main switch status - def _motor_5_msg(self, main_switch=False): - values = {"GRA_Hauptschalter": main_switch} - return self.packer.make_can_msg_panda("Motor_5", 0, values) - - # Driver throttle input (Motor_3) - def _user_gas_msg(self, gas): - values = {"Fahrpedal_Rohsignal": gas} - return self.packer.make_can_msg_panda("Motor_3", 0, values) - - # Cruise control buttons (GRA_Neu) - def _button_msg(self, _set=False, resume=False, cancel=False, bus=2): - values = {"GRA_Neu_Setzen": _set, "GRA_Recall": resume, "GRA_Abbrechen": cancel} - return self.packer.make_can_msg_panda("GRA_Neu", bus, values) - - def test_torque_measurements(self): - # TODO: make this test work with all cars - self._rx(self._torque_driver_msg(50)) - self._rx(self._torque_driver_msg(-50)) - self._rx(self._torque_driver_msg(0)) - self._rx(self._torque_driver_msg(0)) - self._rx(self._torque_driver_msg(0)) - self._rx(self._torque_driver_msg(0)) - - self.assertEqual(-50, self.safety.get_torque_driver_min()) - self.assertEqual(50, self.safety.get_torque_driver_max()) - - self._rx(self._torque_driver_msg(0)) - self.assertEqual(0, self.safety.get_torque_driver_max()) - self.assertEqual(-50, self.safety.get_torque_driver_min()) - - self._rx(self._torque_driver_msg(0)) - self.assertEqual(0, self.safety.get_torque_driver_max()) - self.assertEqual(0, self.safety.get_torque_driver_min()) - - -class TestVolkswagenPqStockSafety(TestVolkswagenPqSafety): - # Transmit of GRA_Neu is allowed on bus 0 and 2 to keep compatibility with gateway and camera integration - TX_MSGS = [[MSG_HCA_1, 0], [MSG_GRA_NEU, 0], [MSG_GRA_NEU, 2], [MSG_LDW_1, 0]] - FWD_BLACKLISTED_ADDRS = {2: [MSG_HCA_1, MSG_LDW_1]} - FWD_BUS_LOOKUP = {0: 2, 2: 0} - - def setUp(self): - self.packer = CANPackerPanda("vw_golf_mk4") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_VOLKSWAGEN_PQ, 0) - self.safety.init_tests() - - def test_spam_cancel_safety_check(self): - self.safety.set_controls_allowed(0) - self.assertTrue(self._tx(self._button_msg(cancel=True))) - self.assertFalse(self._tx(self._button_msg(resume=True))) - self.assertFalse(self._tx(self._button_msg(_set=True))) - # do not block resume if we are engaged already - self.safety.set_controls_allowed(1) - self.assertTrue(self._tx(self._button_msg(resume=True))) - - -class TestVolkswagenPqLongSafety(TestVolkswagenPqSafety, common.LongitudinalAccelSafetyTest): - TX_MSGS = [[MSG_HCA_1, 0], [MSG_LDW_1, 0], [MSG_ACC_SYSTEM, 0], [MSG_ACC_GRA_ANZEIGE, 0]] - FWD_BLACKLISTED_ADDRS = {2: [MSG_HCA_1, MSG_LDW_1, MSG_ACC_SYSTEM, MSG_ACC_GRA_ANZEIGE]} - FWD_BUS_LOOKUP = {0: 2, 2: 0} - INACTIVE_ACCEL = 3.01 - - def setUp(self): - self.packer = CANPackerPanda("vw_golf_mk4") - self.safety = libsafety_py.libsafety - self.safety.set_safety_hooks(Safety.SAFETY_VOLKSWAGEN_PQ, VolkswagenSafetyFlags.FLAG_VOLKSWAGEN_LONG_CONTROL) - self.safety.init_tests() - - # stock cruise controls are entirely bypassed under openpilot longitudinal control - def test_disable_control_allowed_from_cruise(self): - pass - - def test_enable_control_allowed_from_cruise(self): - pass - - def test_cruise_engaged_prev(self): - pass - - def test_set_and_resume_buttons(self): - for button in ["set", "resume"]: - # ACC main switch must be on, engage on falling edge - self.safety.set_controls_allowed(0) - self._rx(self._motor_5_msg(main_switch=False)) - self._rx(self._button_msg(_set=(button == "set"), resume=(button == "resume"), bus=0)) - self._rx(self._button_msg(bus=0)) - self.assertFalse(self.safety.get_controls_allowed(), f"controls allowed on {button} with main switch off") - self._rx(self._motor_5_msg(main_switch=True)) - self._rx(self._button_msg(_set=(button == "set"), resume=(button == "resume"), bus=0)) - self.assertFalse(self.safety.get_controls_allowed(), f"controls allowed on {button} rising edge") - self._rx(self._button_msg(bus=0)) - self.assertTrue(self.safety.get_controls_allowed(), f"controls not allowed on {button} falling edge") - - def test_cancel_button(self): - # Disable on rising edge of cancel button - self._rx(self._motor_5_msg(main_switch=True)) - self.safety.set_controls_allowed(1) - self._rx(self._button_msg(cancel=True, bus=0)) - self.assertFalse(self.safety.get_controls_allowed(), "controls allowed after cancel") - - def test_main_switch(self): - # Disable as soon as main switch turns off - self._rx(self._motor_5_msg(main_switch=True)) - self.safety.set_controls_allowed(1) - self._rx(self._motor_5_msg(main_switch=False)) - self.assertFalse(self.safety.get_controls_allowed(), "controls allowed after ACC main switch off") - - def test_torque_cmd_enable_variants(self): - # The EPS rack accepts either 5 or 7 for an enabled status, with different low speed tuning behavior - self.safety.set_controls_allowed(1) - for enabled_status in (5, 7): - self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_RATE_UP, steer_req=1, hca_status=enabled_status)), - f"torque cmd rejected with {enabled_status=}") - -if __name__ == "__main__": - unittest.main() From 0abe334edb8ca0b21e2a733c227a0645f3b3bd2c Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 18 Feb 2025 19:51:16 -0800 Subject: [PATCH 02/18] move libsafety --- SConscript | 1 - tests/libsafety/SConscript | 54 --------- tests/libsafety/libsafety_py.py | 75 ------------ tests/libsafety/safety.c | 25 ---- tests/libsafety/safety_helpers.h | 187 ------------------------------ tests/libsafety/safety_helpers.py | 104 ----------------- 6 files changed, 446 deletions(-) delete mode 100644 tests/libsafety/SConscript delete mode 100644 tests/libsafety/libsafety_py.py delete mode 100644 tests/libsafety/safety.c delete mode 100644 tests/libsafety/safety_helpers.h delete mode 100644 tests/libsafety/safety_helpers.py diff --git a/SConscript b/SConscript index bc13c27833..f6501e88cc 100644 --- a/SConscript +++ b/SConscript @@ -188,4 +188,3 @@ SConscript('board/jungle/SConscript') # test files if GetOption('extras'): SConscript('tests/libpanda/SConscript') - SConscript('tests/libsafety/SConscript') diff --git a/tests/libsafety/SConscript b/tests/libsafety/SConscript deleted file mode 100644 index 09bb4c9637..0000000000 --- a/tests/libsafety/SConscript +++ /dev/null @@ -1,54 +0,0 @@ -import platform - -CC = 'gcc' -system = platform.system() -if system == 'Darwin': - # gcc installed by homebrew has version suffix (e.g. gcc-12) in order to be - # distinguishable from system one - which acts as a symlink to clang - CC += '-13' - -env = Environment( - CC=CC, - CFLAGS=[ - '-nostdlib', - '-fno-builtin', - '-std=gnu11', - '-Wfatal-errors', - '-Wno-pointer-to-int-cast', - ], - CPPPATH=[".", "../../board/"], -) -if system == "Darwin": - env.PrependENVPath('PATH', '/opt/homebrew/bin') - -if GetOption('mutation'): - env['CC'] = 'clang-17' - flags = [ - '-fprofile-instr-generate', - '-fcoverage-mapping', - '-fpass-plugin=/usr/lib/mull-ir-frontend-17', - '-g', - '-grecord-command-line', - ] - env['CFLAGS'] += flags - env['LINKFLAGS'] += flags - -if GetOption('ubsan'): - flags = [ - "-fsanitize=undefined", - "-fno-sanitize-recover=undefined", - ] - env['CFLAGS'] += flags - env['LINKFLAGS'] += flags - -safety = env.SharedObject("safety.os", "safety.c") -libsafety = env.SharedLibrary("libsafety.so", [safety]) - -if GetOption('coverage'): - env.Append( - CFLAGS=["-fprofile-arcs", "-ftest-coverage", "-fprofile-abs-path",], - LIBS=["gcov"], - ) - # GCC note file is generated by compiler, ensure we build it, and allow scons to clean it up - AlwaysBuild(safety) - env.SideEffect("safety.gcno", safety) diff --git a/tests/libsafety/libsafety_py.py b/tests/libsafety/libsafety_py.py deleted file mode 100644 index 83413bf320..0000000000 --- a/tests/libsafety/libsafety_py.py +++ /dev/null @@ -1,75 +0,0 @@ -import os -from cffi import FFI -from typing import Protocol - -from panda import LEN_TO_DLC -from panda.tests.libsafety.safety_helpers import PandaSafety, setup_safety_helpers - -libsafety_dir = os.path.dirname(os.path.abspath(__file__)) -libsafety_fn = os.path.join(libsafety_dir, "libsafety.so") - -ffi = FFI() - -ffi.cdef(""" -typedef struct { - unsigned char fd : 1; - unsigned char bus : 3; - unsigned char data_len_code : 4; - unsigned char rejected : 1; - unsigned char returned : 1; - unsigned char extended : 1; - unsigned int addr : 29; - unsigned char checksum; - unsigned char data[64]; -} CANPacket_t; -""", packed=True) - -ffi.cdef(""" -bool safety_rx_hook(CANPacket_t *to_send); -bool safety_tx_hook(CANPacket_t *to_push); -int safety_fwd_hook(int bus_num, int addr); -int set_safety_hooks(uint16_t mode, uint16_t param); -""") - -ffi.cdef(""" -void can_set_checksum(CANPacket_t *packet); -""") - -setup_safety_helpers(ffi) - -class CANPacket: - reserved: int - bus: int - data_len_code: int - rejected: int - returned: int - extended: int - addr: int - data: list[int] - -class Panda(PandaSafety, Protocol): - # CAN - def can_set_checksum(self, p: CANPacket) -> None: ... - - # safety - def safety_rx_hook(self, to_send: CANPacket) -> int: ... - def safety_tx_hook(self, to_push: CANPacket) -> int: ... - def safety_fwd_hook(self, bus_num: int, addr: int) -> int: ... - def set_safety_hooks(self, mode: int, param: int) -> int: ... - - -libsafety: Panda = ffi.dlopen(libsafety_fn) - - -# helpers - -def make_CANPacket(addr: int, bus: int, dat): - ret = ffi.new('CANPacket_t *') - ret[0].extended = 1 if addr >= 0x800 else 0 - ret[0].addr = addr - ret[0].data_len_code = LEN_TO_DLC[len(dat)] - ret[0].bus = bus - ret[0].data = bytes(dat) - libsafety.can_set_checksum(ret) - - return ret diff --git a/tests/libsafety/safety.c b/tests/libsafety/safety.c deleted file mode 100644 index acf49d030a..0000000000 --- a/tests/libsafety/safety.c +++ /dev/null @@ -1,25 +0,0 @@ -#include "fake_stm.h" -#include "config.h" -#include "can.h" - -bool can_init(uint8_t can_number) { return true; } -void process_can(uint8_t can_number) { } -//int safety_tx_hook(CANPacket_t *to_send) { return 1; } - -typedef struct harness_configuration harness_configuration; -void refresh_can_tx_slots_available(void); -void can_tx_comms_resume_usb(void) { }; -void can_tx_comms_resume_spi(void) { }; - -#include "health.h" -#include "faults.h" -#include "libc.h" -#include "boards/board_declarations.h" -#include "safety.h" -#include "main_definitions.h" -#include "drivers/can_common.h" - -#include "can_comms.h" - -// libpanda stuff -#include "safety_helpers.h" diff --git a/tests/libsafety/safety_helpers.h b/tests/libsafety/safety_helpers.h deleted file mode 100644 index 98a1ff18a5..0000000000 --- a/tests/libsafety/safety_helpers.h +++ /dev/null @@ -1,187 +0,0 @@ -void safety_tick_current_safety_config() { - safety_tick(¤t_safety_config); -} - -bool safety_config_valid() { - if (current_safety_config.rx_checks_len <= 0) { - printf("missing RX checks\n"); - return false; - } - - for (int i = 0; i < current_safety_config.rx_checks_len; i++) { - const RxCheck addr = current_safety_config.rx_checks[i]; - bool valid = addr.status.msg_seen && !addr.status.lagging && addr.status.valid_checksum && (addr.status.wrong_counters < MAX_WRONG_COUNTERS) && addr.status.valid_quality_flag; - if (!valid) { - // printf("i %d seen %d lagging %d valid checksum %d wrong counters %d valid quality flag %d\n", i, addr.status.msg_seen, addr.status.lagging, addr.status.valid_checksum, addr.status.wrong_counters, addr.status.valid_quality_flag); - return false; - } - } - return true; -} - -void set_controls_allowed(bool c){ - controls_allowed = c; -} - -void set_alternative_experience(int mode){ - alternative_experience = mode; -} - -void set_relay_malfunction(bool c){ - relay_malfunction = c; -} - -bool get_controls_allowed(void){ - return controls_allowed; -} - -int get_alternative_experience(void){ - return alternative_experience; -} - -bool get_relay_malfunction(void){ - return relay_malfunction; -} - -bool get_gas_pressed_prev(void){ - return gas_pressed_prev; -} - -void set_gas_pressed_prev(bool c){ - gas_pressed_prev = c; -} - -bool get_brake_pressed_prev(void){ - return brake_pressed_prev; -} - -bool get_regen_braking_prev(void){ - return regen_braking_prev; -} - -bool get_cruise_engaged_prev(void){ - return cruise_engaged_prev; -} - -void set_cruise_engaged_prev(bool engaged){ - cruise_engaged_prev = engaged; -} - -bool get_vehicle_moving(void){ - return vehicle_moving; -} - -bool get_acc_main_on(void){ - return acc_main_on; -} - -int get_vehicle_speed_min(void){ - return vehicle_speed.min; -} - -int get_vehicle_speed_max(void){ - return vehicle_speed.max; -} - -int get_vehicle_speed_last(void){ - return vehicle_speed.values[0]; -} - -int get_current_safety_mode(void){ - return current_safety_mode; -} - -int get_current_safety_param(void){ - return current_safety_param; -} - -void set_timer(uint32_t t){ - timer.CNT = t; -} - -void set_torque_meas(int min, int max){ - torque_meas.min = min; - torque_meas.max = max; -} - -int get_torque_meas_min(void){ - return torque_meas.min; -} - -int get_torque_meas_max(void){ - return torque_meas.max; -} - -void set_torque_driver(int min, int max){ - torque_driver.min = min; - torque_driver.max = max; -} - -int get_torque_driver_min(void){ - return torque_driver.min; -} - -int get_torque_driver_max(void){ - return torque_driver.max; -} - -void set_rt_torque_last(int t){ - rt_torque_last = t; -} - -void set_desired_torque_last(int t){ - desired_torque_last = t; -} - -void set_desired_angle_last(int t){ - desired_angle_last = t; -} - -int get_desired_angle_last(void){ - return desired_angle_last; -} - -void set_angle_meas(int min, int max){ - angle_meas.min = min; - angle_meas.max = max; -} - -int get_angle_meas_min(void){ - return angle_meas.min; -} - -int get_angle_meas_max(void){ - return angle_meas.max; -} - - -// ***** car specific helpers ***** - -void set_honda_alt_brake_msg(bool c){ - honda_alt_brake_msg = c; -} - -void set_honda_bosch_long(bool c){ - honda_bosch_long = c; -} - -int get_honda_hw(void) { - return honda_hw; -} - -void set_honda_fwd_brake(bool c){ - honda_fwd_brake = c; -} - -bool get_honda_fwd_brake(void){ - return honda_fwd_brake; -} - -void init_tests(void){ - safety_mode_cnt = 2U; // avoid ignoring relay_malfunction logic - alternative_experience = 0; - set_timer(0); - ts_steer_req_mismatch_last = 0; - valid_steer_req_count = 0; - invalid_steer_req_count = 0; -} diff --git a/tests/libsafety/safety_helpers.py b/tests/libsafety/safety_helpers.py deleted file mode 100644 index 789bad9719..0000000000 --- a/tests/libsafety/safety_helpers.py +++ /dev/null @@ -1,104 +0,0 @@ -# panda safety helpers, from safety_helpers.c -from typing import Protocol - -def setup_safety_helpers(ffi): - ffi.cdef(""" - void set_controls_allowed(bool c); - bool get_controls_allowed(void); - bool get_longitudinal_allowed(void); - void set_alternative_experience(int mode); - int get_alternative_experience(void); - void set_relay_malfunction(bool c); - bool get_relay_malfunction(void); - bool get_gas_pressed_prev(void); - void set_gas_pressed_prev(bool); - bool get_brake_pressed_prev(void); - bool get_regen_braking_prev(void); - bool get_acc_main_on(void); - int get_vehicle_speed_min(void); - int get_vehicle_speed_max(void); - int get_vehicle_speed_last(void); - int get_current_safety_mode(void); - int get_current_safety_param(void); - - void set_torque_meas(int min, int max); - int get_torque_meas_min(void); - int get_torque_meas_max(void); - void set_torque_driver(int min, int max); - int get_torque_driver_min(void); - int get_torque_driver_max(void); - void set_desired_torque_last(int t); - void set_rt_torque_last(int t); - void set_desired_angle_last(int t); - int get_desired_angle_last(); - void set_angle_meas(int min, int max); - int get_angle_meas_min(void); - int get_angle_meas_max(void); - - bool get_cruise_engaged_prev(void); - void set_cruise_engaged_prev(bool engaged); - bool get_vehicle_moving(void); - void set_timer(uint32_t t); - - void safety_tick_current_safety_config(); - bool safety_config_valid(); - - void init_tests(void); - - void set_honda_fwd_brake(bool c); - bool get_honda_fwd_brake(void); - void set_honda_alt_brake_msg(bool c); - void set_honda_bosch_long(bool c); - int get_honda_hw(void); - """) - -class PandaSafety(Protocol): - def set_controls_allowed(self, c: bool) -> None: ... - def get_controls_allowed(self) -> bool: ... - def get_longitudinal_allowed(self) -> bool: ... - def set_alternative_experience(self, mode: int) -> None: ... - def get_alternative_experience(self) -> int: ... - def set_relay_malfunction(self, c: bool) -> None: ... - def get_relay_malfunction(self) -> bool: ... - def get_gas_pressed_prev(self) -> bool: ... - def set_gas_pressed_prev(self, c: bool) -> None: ... - def get_brake_pressed_prev(self) -> bool: ... - def get_regen_braking_prev(self) -> bool: ... - def get_acc_main_on(self) -> bool: ... - def get_vehicle_speed_min(self) -> int: ... - def get_vehicle_speed_max(self) -> int: ... - def get_vehicle_speed_last(self) -> int: ... - def get_current_safety_mode(self) -> int: ... - def get_current_safety_param(self) -> int: ... - - def set_torque_meas(self, min: int, max: int) -> None: ... # noqa: A002 - def get_torque_meas_min(self) -> int: ... - def get_torque_meas_max(self) -> int: ... - def set_torque_driver(self, min: int, max: int) -> None: ... # noqa: A002 - def get_torque_driver_min(self) -> int: ... - def get_torque_driver_max(self) -> int: ... - def set_desired_torque_last(self, t: int) -> None: ... - def set_rt_torque_last(self, t: int) -> None: ... - def set_desired_angle_last(self, t: int) -> None: ... - def get_desired_angle_last(self) -> int: ... - def set_angle_meas(self, min: int, max: int) -> None: ... # noqa: A002 - def get_angle_meas_min(self) -> int: ... - def get_angle_meas_max(self) -> int: ... - - def get_cruise_engaged_prev(self) -> bool: ... - def set_cruise_engaged_prev(self, enabled: bool) -> None: ... - def get_vehicle_moving(self) -> bool: ... - def set_timer(self, t: int) -> None: ... - - def safety_tick_current_safety_config(self) -> None: ... - def safety_config_valid(self) -> bool: ... - - def init_tests(self) -> None: ... - - def set_honda_fwd_brake(self, c: bool) -> None: ... - def get_honda_fwd_brake(self) -> bool: ... - def set_honda_alt_brake_msg(self, c: bool) -> None: ... - def set_honda_bosch_long(self, c: bool) -> None: ... - def get_honda_hw(self) -> int: ... - - From 7bf73dd8eea761bb646db991d12ce0fc2511bd4f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 18 Feb 2025 19:55:48 -0800 Subject: [PATCH 03/18] move safety --- board/safety.h | 746 ------------------------ board/safety/safety_body.h | 50 -- board/safety/safety_chrysler.h | 304 ---------- board/safety/safety_defaults.h | 73 --- board/safety/safety_elm327.h | 42 -- board/safety/safety_ford.h | 430 -------------- board/safety/safety_gm.h | 259 -------- board/safety/safety_honda.h | 461 --------------- board/safety/safety_hyundai.h | 356 ----------- board/safety/safety_hyundai_canfd.h | 363 ------------ board/safety/safety_hyundai_common.h | 128 ---- board/safety/safety_mazda.h | 131 ----- board/safety/safety_nissan.h | 163 ------ board/safety/safety_subaru.h | 293 ---------- board/safety/safety_subaru_preglobal.h | 129 ---- board/safety/safety_tesla.h | 213 ------- board/safety/safety_toyota.h | 413 ------------- board/safety/safety_volkswagen_common.h | 13 - board/safety/safety_volkswagen_mqb.h | 302 ---------- board/safety/safety_volkswagen_pq.h | 259 -------- board/safety_declarations.h | 283 --------- 21 files changed, 5411 deletions(-) delete mode 100644 board/safety.h delete mode 100644 board/safety/safety_body.h delete mode 100644 board/safety/safety_chrysler.h delete mode 100644 board/safety/safety_defaults.h delete mode 100644 board/safety/safety_elm327.h delete mode 100644 board/safety/safety_ford.h delete mode 100644 board/safety/safety_gm.h delete mode 100644 board/safety/safety_honda.h delete mode 100644 board/safety/safety_hyundai.h delete mode 100644 board/safety/safety_hyundai_canfd.h delete mode 100644 board/safety/safety_hyundai_common.h delete mode 100644 board/safety/safety_mazda.h delete mode 100644 board/safety/safety_nissan.h delete mode 100644 board/safety/safety_subaru.h delete mode 100644 board/safety/safety_subaru_preglobal.h delete mode 100644 board/safety/safety_tesla.h delete mode 100644 board/safety/safety_toyota.h delete mode 100644 board/safety/safety_volkswagen_common.h delete mode 100644 board/safety/safety_volkswagen_mqb.h delete mode 100644 board/safety/safety_volkswagen_pq.h delete mode 100644 board/safety_declarations.h diff --git a/board/safety.h b/board/safety.h deleted file mode 100644 index 913109957c..0000000000 --- a/board/safety.h +++ /dev/null @@ -1,746 +0,0 @@ -#pragma once - -#include "safety_declarations.h" -#include "can.h" - -// include the safety policies. -#include "safety/safety_defaults.h" -#include "safety/safety_honda.h" -#include "safety/safety_toyota.h" -#include "safety/safety_tesla.h" -#include "safety/safety_gm.h" -#include "safety/safety_ford.h" -#include "safety/safety_hyundai.h" -#include "safety/safety_chrysler.h" -#include "safety/safety_subaru.h" -#include "safety/safety_subaru_preglobal.h" -#include "safety/safety_mazda.h" -#include "safety/safety_nissan.h" -#include "safety/safety_volkswagen_mqb.h" -#include "safety/safety_volkswagen_pq.h" -#include "safety/safety_elm327.h" -#include "safety/safety_body.h" - -// CAN-FD only safety modes -#ifdef CANFD -#include "safety/safety_hyundai_canfd.h" -#endif - -// from cereal.car.CarParams.SafetyModel -#define SAFETY_SILENT 0U -#define SAFETY_HONDA_NIDEC 1U -#define SAFETY_TOYOTA 2U -#define SAFETY_ELM327 3U -#define SAFETY_GM 4U -#define SAFETY_HONDA_BOSCH_GIRAFFE 5U -#define SAFETY_FORD 6U -#define SAFETY_HYUNDAI 8U -#define SAFETY_CHRYSLER 9U -#define SAFETY_TESLA 10U -#define SAFETY_SUBARU 11U -#define SAFETY_MAZDA 13U -#define SAFETY_NISSAN 14U -#define SAFETY_VOLKSWAGEN_MQB 15U -#define SAFETY_ALLOUTPUT 17U -#define SAFETY_GM_ASCM 18U -#define SAFETY_NOOUTPUT 19U -#define SAFETY_HONDA_BOSCH 20U -#define SAFETY_VOLKSWAGEN_PQ 21U -#define SAFETY_SUBARU_PREGLOBAL 22U -#define SAFETY_HYUNDAI_LEGACY 23U -#define SAFETY_HYUNDAI_COMMUNITY 24U -#define SAFETY_STELLANTIS 25U -#define SAFETY_FAW 26U -#define SAFETY_BODY 27U -#define SAFETY_HYUNDAI_CANFD 28U - -uint32_t GET_BYTES(const CANPacket_t *msg, int start, int len) { - uint32_t ret = 0U; - for (int i = 0; i < len; i++) { - const uint32_t shift = i * 8; - ret |= (((uint32_t)msg->data[start + i]) << shift); - } - return ret; -} - -const int MAX_WRONG_COUNTERS = 5; - -// This can be set by the safety hooks -bool controls_allowed = false; -bool relay_malfunction = false; -bool gas_pressed = false; -bool gas_pressed_prev = false; -bool brake_pressed = false; -bool brake_pressed_prev = false; -bool regen_braking = false; -bool regen_braking_prev = false; -bool cruise_engaged_prev = false; -struct sample_t vehicle_speed; -bool vehicle_moving = false; -bool acc_main_on = false; // referred to as "ACC off" in ISO 15622:2018 -int cruise_button_prev = 0; -bool safety_rx_checks_invalid = false; - -// for safety modes with torque steering control -int desired_torque_last = 0; // last desired steer torque -int rt_torque_last = 0; // last desired torque for real time check -int valid_steer_req_count = 0; // counter for steer request bit matching non-zero torque -int invalid_steer_req_count = 0; // counter to allow multiple frames of mismatching torque request bit -struct sample_t torque_meas; // last 6 motor torques produced by the eps -struct sample_t torque_driver; // last 6 driver torques measured -uint32_t ts_torque_check_last = 0; -uint32_t ts_steer_req_mismatch_last = 0; // last timestamp steer req was mismatched with torque - -// state for controls_allowed timeout logic -bool heartbeat_engaged = false; // openpilot enabled, passed in heartbeat USB command -uint32_t heartbeat_engaged_mismatches = 0; // count of mismatches between heartbeat_engaged and controls_allowed - -// for safety modes with angle steering control -uint32_t ts_angle_last = 0; -int desired_angle_last = 0; -struct sample_t angle_meas; // last 6 steer angles/curvatures - - -int alternative_experience = 0; - -// time since safety mode has been changed -uint32_t safety_mode_cnt = 0U; - -uint16_t current_safety_mode = SAFETY_SILENT; -uint16_t current_safety_param = 0; -static const safety_hooks *current_hooks = &nooutput_hooks; -safety_config current_safety_config; - -static bool is_msg_valid(RxCheck addr_list[], int index) { - bool valid = true; - if (index != -1) { - if (!addr_list[index].status.valid_checksum || !addr_list[index].status.valid_quality_flag || (addr_list[index].status.wrong_counters >= MAX_WRONG_COUNTERS)) { - valid = false; - controls_allowed = false; - } - } - return valid; -} - -static int get_addr_check_index(const CANPacket_t *to_push, RxCheck addr_list[], const int len) { - int bus = GET_BUS(to_push); - int addr = GET_ADDR(to_push); - int length = GET_LEN(to_push); - - int index = -1; - for (int i = 0; i < len; i++) { - // if multiple msgs are allowed, determine which one is present on the bus - if (!addr_list[i].status.msg_seen) { - for (uint8_t j = 0U; (j < MAX_ADDR_CHECK_MSGS) && (addr_list[i].msg[j].addr != 0); j++) { - if ((addr == addr_list[i].msg[j].addr) && (bus == addr_list[i].msg[j].bus) && - (length == addr_list[i].msg[j].len)) { - addr_list[i].status.index = j; - addr_list[i].status.msg_seen = true; - break; - } - } - } - - if (addr_list[i].status.msg_seen) { - int idx = addr_list[i].status.index; - if ((addr == addr_list[i].msg[idx].addr) && (bus == addr_list[i].msg[idx].bus) && - (length == addr_list[i].msg[idx].len)) { - index = i; - break; - } - } - } - return index; -} - -static void update_addr_timestamp(RxCheck addr_list[], int index) { - if (index != -1) { - uint32_t ts = microsecond_timer_get(); - addr_list[index].status.last_timestamp = ts; - } -} - -static void update_counter(RxCheck addr_list[], int index, uint8_t counter) { - if (index != -1) { - uint8_t expected_counter = (addr_list[index].status.last_counter + 1U) % (addr_list[index].msg[addr_list[index].status.index].max_counter + 1U); - addr_list[index].status.wrong_counters += (expected_counter == counter) ? -1 : 1; - addr_list[index].status.wrong_counters = CLAMP(addr_list[index].status.wrong_counters, 0, MAX_WRONG_COUNTERS); - addr_list[index].status.last_counter = counter; - } -} - -static bool rx_msg_safety_check(const CANPacket_t *to_push, - const safety_config *cfg, - const safety_hooks *safety_hooks) { - - int index = get_addr_check_index(to_push, cfg->rx_checks, cfg->rx_checks_len); - update_addr_timestamp(cfg->rx_checks, index); - - if (index != -1) { - // checksum check - if ((safety_hooks->get_checksum != NULL) && (safety_hooks->compute_checksum != NULL) && cfg->rx_checks[index].msg[cfg->rx_checks[index].status.index].check_checksum) { - uint32_t checksum = safety_hooks->get_checksum(to_push); - uint32_t checksum_comp = safety_hooks->compute_checksum(to_push); - cfg->rx_checks[index].status.valid_checksum = checksum_comp == checksum; - } else { - cfg->rx_checks[index].status.valid_checksum = true; - } - - // counter check (max_counter == 0 means skip check) - if ((safety_hooks->get_counter != NULL) && (cfg->rx_checks[index].msg[cfg->rx_checks[index].status.index].max_counter > 0U)) { - uint8_t counter = safety_hooks->get_counter(to_push); - update_counter(cfg->rx_checks, index, counter); - } else { - cfg->rx_checks[index].status.wrong_counters = 0U; - } - - // quality flag check - if ((safety_hooks->get_quality_flag_valid != NULL) && cfg->rx_checks[index].msg[cfg->rx_checks[index].status.index].quality_flag) { - cfg->rx_checks[index].status.valid_quality_flag = safety_hooks->get_quality_flag_valid(to_push); - } else { - cfg->rx_checks[index].status.valid_quality_flag = true; - } - } - return is_msg_valid(cfg->rx_checks, index); -} - -bool safety_rx_hook(const CANPacket_t *to_push) { - bool controls_allowed_prev = controls_allowed; - - bool valid = rx_msg_safety_check(to_push, ¤t_safety_config, current_hooks); - if (valid) { - current_hooks->rx(to_push); - } - - // reset mismatches on rising edge of controls_allowed to avoid rare race condition - if (controls_allowed && !controls_allowed_prev) { - heartbeat_engaged_mismatches = 0; - } - - return valid; -} - -static bool msg_allowed(const CANPacket_t *to_send, const CanMsg msg_list[], int len) { - int addr = GET_ADDR(to_send); - int bus = GET_BUS(to_send); - int length = GET_LEN(to_send); - - bool allowed = false; - for (int i = 0; i < len; i++) { - if ((addr == msg_list[i].addr) && (bus == msg_list[i].bus) && (length == msg_list[i].len)) { - allowed = true; - break; - } - } - return allowed; -} - -bool safety_tx_hook(CANPacket_t *to_send) { - bool whitelisted = msg_allowed(to_send, current_safety_config.tx_msgs, current_safety_config.tx_msgs_len); - if ((current_safety_mode == SAFETY_ALLOUTPUT) || (current_safety_mode == SAFETY_ELM327)) { - whitelisted = true; - } - - const bool safety_allowed = current_hooks->tx(to_send); - return !relay_malfunction && whitelisted && safety_allowed; -} - -int safety_fwd_hook(int bus_num, int addr) { - return (relay_malfunction ? -1 : current_hooks->fwd(bus_num, addr)); -} - -bool get_longitudinal_allowed(void) { - return controls_allowed && !gas_pressed_prev; -} - -// Given a CRC-8 poly, generate a static lookup table to use with a fast CRC-8 -// algorithm. Called at init time for safety modes using CRC-8. -void gen_crc_lookup_table_8(uint8_t poly, uint8_t crc_lut[]) { - for (uint16_t i = 0U; i <= 0xFFU; i++) { - uint8_t crc = (uint8_t)i; - for (int j = 0; j < 8; j++) { - if ((crc & 0x80U) != 0U) { - crc = (uint8_t)((crc << 1) ^ poly); - } else { - crc <<= 1; - } - } - crc_lut[i] = crc; - } -} - -#ifdef CANFD -void gen_crc_lookup_table_16(uint16_t poly, uint16_t crc_lut[]) { - for (uint16_t i = 0; i < 256U; i++) { - uint16_t crc = i << 8U; - for (uint16_t j = 0; j < 8U; j++) { - if ((crc & 0x8000U) != 0U) { - crc = (uint16_t)((crc << 1) ^ poly); - } else { - crc <<= 1; - } - } - crc_lut[i] = crc; - } -} -#endif - -// 1Hz safety function called by main. Now just a check for lagging safety messages -void safety_tick(const safety_config *cfg) { - const uint8_t MAX_MISSED_MSGS = 10U; - bool rx_checks_invalid = false; - uint32_t ts = microsecond_timer_get(); - if (cfg != NULL) { - for (int i=0; i < cfg->rx_checks_len; i++) { - uint32_t elapsed_time = get_ts_elapsed(ts, cfg->rx_checks[i].status.last_timestamp); - // lag threshold is max of: 1s and MAX_MISSED_MSGS * expected timestep. - // Quite conservative to not risk false triggers. - // 2s of lag is worse case, since the function is called at 1Hz - uint32_t timestep = 1e6 / cfg->rx_checks[i].msg[cfg->rx_checks[i].status.index].frequency; - bool lagging = elapsed_time > MAX(timestep * MAX_MISSED_MSGS, 1e6); - cfg->rx_checks[i].status.lagging = lagging; - if (lagging) { - controls_allowed = false; - } - - if (lagging || !is_msg_valid(cfg->rx_checks, i)) { - rx_checks_invalid = true; - } - } - } - - safety_rx_checks_invalid = rx_checks_invalid; -} - -static void relay_malfunction_set(void) { - relay_malfunction = true; - fault_occurred(FAULT_RELAY_MALFUNCTION); -} - -void generic_rx_checks(bool stock_ecu_detected) { - // allow 1s of transition timeout after relay changes state before assessing malfunctioning - const uint32_t RELAY_TRNS_TIMEOUT = 1U; - - // exit controls on rising edge of gas press - if (gas_pressed && !gas_pressed_prev && !(alternative_experience & ALT_EXP_DISABLE_DISENGAGE_ON_GAS)) { - controls_allowed = false; - } - gas_pressed_prev = gas_pressed; - - // exit controls on rising edge of brake press - if (brake_pressed && (!brake_pressed_prev || vehicle_moving)) { - controls_allowed = false; - } - brake_pressed_prev = brake_pressed; - - // exit controls on rising edge of regen paddle - if (regen_braking && (!regen_braking_prev || vehicle_moving)) { - controls_allowed = false; - } - regen_braking_prev = regen_braking; - - // check if stock ECU is on bus broken by car harness - if ((safety_mode_cnt > RELAY_TRNS_TIMEOUT) && stock_ecu_detected) { - relay_malfunction_set(); - } -} - -static void relay_malfunction_reset(void) { - relay_malfunction = false; - fault_recovered(FAULT_RELAY_MALFUNCTION); -} - -// resets values and min/max for sample_t struct -static void reset_sample(struct sample_t *sample) { - for (int i = 0; i < MAX_SAMPLE_VALS; i++) { - sample->values[i] = 0; - } - update_sample(sample, 0); -} - -int set_safety_hooks(uint16_t mode, uint16_t param) { - const safety_hook_config safety_hook_registry[] = { - {SAFETY_SILENT, &nooutput_hooks}, - {SAFETY_HONDA_NIDEC, &honda_nidec_hooks}, - {SAFETY_TOYOTA, &toyota_hooks}, - {SAFETY_ELM327, &elm327_hooks}, - {SAFETY_GM, &gm_hooks}, - {SAFETY_HONDA_BOSCH, &honda_bosch_hooks}, - {SAFETY_HYUNDAI, &hyundai_hooks}, - {SAFETY_CHRYSLER, &chrysler_hooks}, - {SAFETY_SUBARU, &subaru_hooks}, - {SAFETY_VOLKSWAGEN_MQB, &volkswagen_mqb_hooks}, - {SAFETY_NISSAN, &nissan_hooks}, - {SAFETY_NOOUTPUT, &nooutput_hooks}, - {SAFETY_HYUNDAI_LEGACY, &hyundai_legacy_hooks}, - {SAFETY_MAZDA, &mazda_hooks}, - {SAFETY_BODY, &body_hooks}, - {SAFETY_FORD, &ford_hooks}, -#ifdef CANFD - {SAFETY_HYUNDAI_CANFD, &hyundai_canfd_hooks}, -#endif -#ifdef ALLOW_DEBUG - {SAFETY_TESLA, &tesla_hooks}, - {SAFETY_SUBARU_PREGLOBAL, &subaru_preglobal_hooks}, - {SAFETY_VOLKSWAGEN_PQ, &volkswagen_pq_hooks}, - {SAFETY_ALLOUTPUT, &alloutput_hooks}, -#endif - }; - - // reset state set by safety mode - safety_mode_cnt = 0U; - relay_malfunction = false; - gas_pressed = false; - gas_pressed_prev = false; - brake_pressed = false; - brake_pressed_prev = false; - regen_braking = false; - regen_braking_prev = false; - cruise_engaged_prev = false; - vehicle_moving = false; - acc_main_on = false; - cruise_button_prev = 0; - desired_torque_last = 0; - rt_torque_last = 0; - ts_angle_last = 0; - desired_angle_last = 0; - ts_torque_check_last = 0; - ts_steer_req_mismatch_last = 0; - valid_steer_req_count = 0; - invalid_steer_req_count = 0; - - // reset samples - reset_sample(&vehicle_speed); - reset_sample(&torque_meas); - reset_sample(&torque_driver); - reset_sample(&angle_meas); - - controls_allowed = false; - relay_malfunction_reset(); - safety_rx_checks_invalid = false; - - current_safety_config.rx_checks = NULL; - current_safety_config.rx_checks_len = 0; - current_safety_config.tx_msgs = NULL; - current_safety_config.tx_msgs_len = 0; - - int set_status = -1; // not set - int hook_config_count = sizeof(safety_hook_registry) / sizeof(safety_hook_config); - for (int i = 0; i < hook_config_count; i++) { - if (safety_hook_registry[i].id == mode) { - current_hooks = safety_hook_registry[i].hooks; - current_safety_mode = mode; - current_safety_param = param; - set_status = 0; // set - } - } - if ((set_status == 0) && (current_hooks->init != NULL)) { - safety_config cfg = current_hooks->init(param); - current_safety_config.rx_checks = cfg.rx_checks; - current_safety_config.rx_checks_len = cfg.rx_checks_len; - current_safety_config.tx_msgs = cfg.tx_msgs; - current_safety_config.tx_msgs_len = cfg.tx_msgs_len; - // reset all dynamic fields in addr struct - for (int j = 0; j < current_safety_config.rx_checks_len; j++) { - current_safety_config.rx_checks[j].status = (RxStatus){0}; - } - } - return set_status; -} - -// convert a trimmed integer to signed 32 bit int -int to_signed(int d, int bits) { - int d_signed = d; - int max_value = (1 << MAX((bits - 1), 0)); - if (d >= max_value) { - d_signed = d - (1 << MAX(bits, 0)); - } - return d_signed; -} - -// given a new sample, update the sample_t struct -void update_sample(struct sample_t *sample, int sample_new) { - for (int i = MAX_SAMPLE_VALS - 1; i > 0; i--) { - sample->values[i] = sample->values[i-1]; - } - sample->values[0] = sample_new; - - // get the minimum and maximum measured samples - sample->min = sample->values[0]; - sample->max = sample->values[0]; - for (int i = 1; i < MAX_SAMPLE_VALS; i++) { - if (sample->values[i] < sample->min) { - sample->min = sample->values[i]; - } - if (sample->values[i] > sample->max) { - sample->max = sample->values[i]; - } - } -} - -static bool max_limit_check(int val, const int MAX_VAL, const int MIN_VAL) { - return (val > MAX_VAL) || (val < MIN_VAL); -} - -// check that commanded torque value isn't too far from measured -static bool dist_to_meas_check(int val, int val_last, struct sample_t *val_meas, - const int MAX_RATE_UP, const int MAX_RATE_DOWN, const int MAX_ERROR) { - - // *** val rate limit check *** - int highest_allowed_rl = MAX(val_last, 0) + MAX_RATE_UP; - int lowest_allowed_rl = MIN(val_last, 0) - MAX_RATE_UP; - - // if we've exceeded the meas val, we must start moving toward 0 - int highest_allowed = MIN(highest_allowed_rl, MAX(val_last - MAX_RATE_DOWN, MAX(val_meas->max, 0) + MAX_ERROR)); - int lowest_allowed = MAX(lowest_allowed_rl, MIN(val_last + MAX_RATE_DOWN, MIN(val_meas->min, 0) - MAX_ERROR)); - - // check for violation - return max_limit_check(val, highest_allowed, lowest_allowed); -} - -// check that commanded value isn't fighting against driver -static bool driver_limit_check(int val, int val_last, const struct sample_t *val_driver, - const int MAX_VAL, const int MAX_RATE_UP, const int MAX_RATE_DOWN, - const int MAX_ALLOWANCE, const int DRIVER_FACTOR) { - - // torque delta/rate limits - int highest_allowed_rl = MAX(val_last, 0) + MAX_RATE_UP; - int lowest_allowed_rl = MIN(val_last, 0) - MAX_RATE_UP; - - // driver - int driver_max_limit = MAX_VAL + (MAX_ALLOWANCE + val_driver->max) * DRIVER_FACTOR; - int driver_min_limit = -MAX_VAL + (-MAX_ALLOWANCE + val_driver->min) * DRIVER_FACTOR; - - // if we've exceeded the applied torque, we must start moving toward 0 - int highest_allowed = MIN(highest_allowed_rl, MAX(val_last - MAX_RATE_DOWN, - MAX(driver_max_limit, 0))); - int lowest_allowed = MAX(lowest_allowed_rl, MIN(val_last + MAX_RATE_DOWN, - MIN(driver_min_limit, 0))); - - // check for violation - return max_limit_check(val, highest_allowed, lowest_allowed); -} - - -// real time check, mainly used for steer torque rate limiter -static bool rt_rate_limit_check(int val, int val_last, const int MAX_RT_DELTA) { - - // *** torque real time rate limit check *** - int highest_val = MAX(val_last, 0) + MAX_RT_DELTA; - int lowest_val = MIN(val_last, 0) - MAX_RT_DELTA; - - // check for violation - return max_limit_check(val, highest_val, lowest_val); -} - - -// interp function that holds extreme values -static float interpolate(struct lookup_t xy, float x) { - - int size = sizeof(xy.x) / sizeof(xy.x[0]); - float ret = xy.y[size - 1]; // default output is last point - - // x is lower than the first point in the x array. Return the first point - if (x <= xy.x[0]) { - ret = xy.y[0]; - - } else { - // find the index such that (xy.x[i] <= x < xy.x[i+1]) and linearly interp - for (int i=0; i < (size - 1); i++) { - if (x < xy.x[i+1]) { - float x0 = xy.x[i]; - float y0 = xy.y[i]; - float dx = xy.x[i+1] - x0; - float dy = xy.y[i+1] - y0; - // dx should not be zero as xy.x is supposed to be monotonic - dx = MAX(dx, 0.0001); - ret = (dy * (x - x0) / dx) + y0; - break; - } - } - } - return ret; -} - -int ROUND(float val) { - return val + ((val > 0.0) ? 0.5 : -0.5); -} - -// Safety checks for longitudinal actuation -bool longitudinal_accel_checks(int desired_accel, const LongitudinalLimits limits) { - bool accel_valid = get_longitudinal_allowed() && !max_limit_check(desired_accel, limits.max_accel, limits.min_accel); - bool accel_inactive = desired_accel == limits.inactive_accel; - return !(accel_valid || accel_inactive); -} - -bool longitudinal_speed_checks(int desired_speed, const LongitudinalLimits limits) { - return !get_longitudinal_allowed() && (desired_speed != limits.inactive_speed); -} - -bool longitudinal_transmission_rpm_checks(int desired_transmission_rpm, const LongitudinalLimits limits) { - bool transmission_rpm_valid = get_longitudinal_allowed() && !max_limit_check(desired_transmission_rpm, limits.max_transmission_rpm, limits.min_transmission_rpm); - bool transmission_rpm_inactive = desired_transmission_rpm == limits.inactive_transmission_rpm; - return !(transmission_rpm_valid || transmission_rpm_inactive); -} - -bool longitudinal_gas_checks(int desired_gas, const LongitudinalLimits limits) { - bool gas_valid = get_longitudinal_allowed() && !max_limit_check(desired_gas, limits.max_gas, limits.min_gas); - bool gas_inactive = desired_gas == limits.inactive_gas; - return !(gas_valid || gas_inactive); -} - -bool longitudinal_brake_checks(int desired_brake, const LongitudinalLimits limits) { - bool violation = false; - violation |= !get_longitudinal_allowed() && (desired_brake != 0); - violation |= desired_brake > limits.max_brake; - return violation; -} - -// Safety checks for torque-based steering commands -bool steer_torque_cmd_checks(int desired_torque, int steer_req, const SteeringLimits limits) { - bool violation = false; - uint32_t ts = microsecond_timer_get(); - - if (controls_allowed) { - // *** global torque limit check *** - violation |= max_limit_check(desired_torque, limits.max_steer, -limits.max_steer); - - // *** torque rate limit check *** - if (limits.type == TorqueDriverLimited) { - violation |= driver_limit_check(desired_torque, desired_torque_last, &torque_driver, - limits.max_steer, limits.max_rate_up, limits.max_rate_down, - limits.driver_torque_allowance, limits.driver_torque_factor); - } else { - violation |= dist_to_meas_check(desired_torque, desired_torque_last, &torque_meas, - limits.max_rate_up, limits.max_rate_down, limits.max_torque_error); - } - desired_torque_last = desired_torque; - - // *** torque real time rate limit check *** - violation |= rt_rate_limit_check(desired_torque, rt_torque_last, limits.max_rt_delta); - - // every RT_INTERVAL set the new limits - uint32_t ts_elapsed = get_ts_elapsed(ts, ts_torque_check_last); - if (ts_elapsed > limits.max_rt_interval) { - rt_torque_last = desired_torque; - ts_torque_check_last = ts; - } - } - - // no torque if controls is not allowed - if (!controls_allowed && (desired_torque != 0)) { - violation = true; - } - - // certain safety modes set their steer request bit low for one or more frame at a - // predefined max frequency to avoid steering faults in certain situations - bool steer_req_mismatch = (steer_req == 0) && (desired_torque != 0); - if (!limits.has_steer_req_tolerance) { - if (steer_req_mismatch) { - violation = true; - } - - } else { - if (steer_req_mismatch) { - if (invalid_steer_req_count == 0) { - // disallow torque cut if not enough recent matching steer_req messages - if (valid_steer_req_count < limits.min_valid_request_frames) { - violation = true; - } - - // or we've cut torque too recently in time - uint32_t ts_elapsed = get_ts_elapsed(ts, ts_steer_req_mismatch_last); - if (ts_elapsed < limits.min_valid_request_rt_interval) { - violation = true; - } - } else { - // or we're cutting more frames consecutively than allowed - if (invalid_steer_req_count >= limits.max_invalid_request_frames) { - violation = true; - } - } - - valid_steer_req_count = 0; - ts_steer_req_mismatch_last = ts; - invalid_steer_req_count = MIN(invalid_steer_req_count + 1, limits.max_invalid_request_frames); - } else { - valid_steer_req_count = MIN(valid_steer_req_count + 1, limits.min_valid_request_frames); - invalid_steer_req_count = 0; - } - } - - // reset to 0 if either controls is not allowed or there's a violation - if (violation || !controls_allowed) { - valid_steer_req_count = 0; - invalid_steer_req_count = 0; - desired_torque_last = 0; - rt_torque_last = 0; - ts_torque_check_last = ts; - ts_steer_req_mismatch_last = ts; - } - - return violation; -} - -// Safety checks for angle-based steering commands -bool steer_angle_cmd_checks(int desired_angle, bool steer_control_enabled, const SteeringLimits limits) { - bool violation = false; - - if (controls_allowed && steer_control_enabled) { - // convert floating point angle rate limits to integers in the scale of the desired angle on CAN, - // add 1 to not false trigger the violation. also fudge the speed by 1 m/s so rate limits are - // always slightly above openpilot's in case we read an updated speed in between angle commands - // TODO: this speed fudge can be much lower, look at data to determine the lowest reasonable offset - int delta_angle_up = (interpolate(limits.angle_rate_up_lookup, (vehicle_speed.min / VEHICLE_SPEED_FACTOR) - 1.) * limits.angle_deg_to_can) + 1.; - int delta_angle_down = (interpolate(limits.angle_rate_down_lookup, (vehicle_speed.min / VEHICLE_SPEED_FACTOR) - 1.) * limits.angle_deg_to_can) + 1.; - - // allow down limits at zero since small floats will be rounded to 0 - int highest_desired_angle = desired_angle_last + ((desired_angle_last > 0) ? delta_angle_up : delta_angle_down); - int lowest_desired_angle = desired_angle_last - ((desired_angle_last >= 0) ? delta_angle_down : delta_angle_up); - - // check that commanded angle value isn't too far from measured, used to limit torque for some safety modes - // ensure we start moving in direction of meas while respecting rate limits if error is exceeded - if (limits.enforce_angle_error && ((vehicle_speed.values[0] / VEHICLE_SPEED_FACTOR) > limits.angle_error_min_speed)) { - // the rate limits above are liberally above openpilot's to avoid false positives. - // likewise, allow a lower rate for moving towards meas when error is exceeded - int delta_angle_up_lower = interpolate(limits.angle_rate_up_lookup, (vehicle_speed.max / VEHICLE_SPEED_FACTOR) + 1.) * limits.angle_deg_to_can; - int delta_angle_down_lower = interpolate(limits.angle_rate_down_lookup, (vehicle_speed.max / VEHICLE_SPEED_FACTOR) + 1.) * limits.angle_deg_to_can; - - int highest_desired_angle_lower = desired_angle_last + ((desired_angle_last > 0) ? delta_angle_up_lower : delta_angle_down_lower); - int lowest_desired_angle_lower = desired_angle_last - ((desired_angle_last >= 0) ? delta_angle_down_lower : delta_angle_up_lower); - - lowest_desired_angle = MIN(MAX(lowest_desired_angle, angle_meas.min - limits.max_angle_error - 1), highest_desired_angle_lower); - highest_desired_angle = MAX(MIN(highest_desired_angle, angle_meas.max + limits.max_angle_error + 1), lowest_desired_angle_lower); - - // don't enforce above the max steer - lowest_desired_angle = CLAMP(lowest_desired_angle, -limits.max_steer, limits.max_steer); - highest_desired_angle = CLAMP(highest_desired_angle, -limits.max_steer, limits.max_steer); - } - - // check for violation; - violation |= max_limit_check(desired_angle, highest_desired_angle, lowest_desired_angle); - } - desired_angle_last = desired_angle; - - // Angle should either be 0 or same as current angle while not steering - if (!steer_control_enabled) { - violation |= (limits.inactive_angle_is_zero ? (desired_angle != 0) : - max_limit_check(desired_angle, angle_meas.max + 1, angle_meas.min - 1)); - } - - // No angle control allowed when controls are not allowed - violation |= !controls_allowed && steer_control_enabled; - - return violation; -} - -void pcm_cruise_check(bool cruise_engaged) { - // Enter controls on rising edge of stock ACC, exit controls if stock ACC disengages - if (!cruise_engaged) { - controls_allowed = false; - } - if (cruise_engaged && !cruise_engaged_prev) { - controls_allowed = true; - } - cruise_engaged_prev = cruise_engaged; -} diff --git a/board/safety/safety_body.h b/board/safety/safety_body.h deleted file mode 100644 index fcba1b577a..0000000000 --- a/board/safety/safety_body.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include "safety_declarations.h" - -static void body_rx_hook(const CANPacket_t *to_push) { - // body is never at standstill - vehicle_moving = true; - - if (GET_ADDR(to_push) == 0x201U) { - controls_allowed = true; - } -} - -static bool body_tx_hook(const CANPacket_t *to_send) { - bool tx = true; - int addr = GET_ADDR(to_send); - int len = GET_LEN(to_send); - - if (!controls_allowed && (addr != 0x1)) { - tx = false; - } - - // Allow going into CAN flashing mode for base & knee even if controls are not allowed - bool flash_msg = ((addr == 0x250) || (addr == 0x350)) && (len == 8); - if (!controls_allowed && (GET_BYTES(to_send, 0, 4) == 0xdeadfaceU) && (GET_BYTES(to_send, 4, 4) == 0x0ab00b1eU) && flash_msg) { - tx = true; - } - - return tx; -} - -static safety_config body_init(uint16_t param) { - static RxCheck body_rx_checks[] = { - {.msg = {{0x201, 0, 8, .check_checksum = false, .max_counter = 0U, .frequency = 100U}, { 0 }, { 0 }}}, - }; - - static const CanMsg BODY_TX_MSGS[] = {{0x250, 0, 8}, {0x250, 0, 6}, {0x251, 0, 5}, // body - {0x350, 0, 8}, {0x350, 0, 6}, {0x351, 0, 5}, // knee - {0x1, 0, 8}}; // CAN flasher - - UNUSED(param); - return BUILD_SAFETY_CFG(body_rx_checks, BODY_TX_MSGS); -} - -const safety_hooks body_hooks = { - .init = body_init, - .rx = body_rx_hook, - .tx = body_tx_hook, - .fwd = default_fwd_hook, -}; diff --git a/board/safety/safety_chrysler.h b/board/safety/safety_chrysler.h deleted file mode 100644 index 2bbc942715..0000000000 --- a/board/safety/safety_chrysler.h +++ /dev/null @@ -1,304 +0,0 @@ -#pragma once - -#include "safety_declarations.h" - -typedef struct { - const int EPS_2; - const int ESP_1; - const int ESP_8; - const int ECM_5; - const int DAS_3; - const int DAS_6; - const int LKAS_COMMAND; - const int CRUISE_BUTTONS; -} ChryslerAddrs; - -typedef enum { - CHRYSLER_RAM_DT, - CHRYSLER_RAM_HD, - CHRYSLER_PACIFICA, // plus Jeep -} ChryslerPlatform; -static ChryslerPlatform chrysler_platform; -static const ChryslerAddrs *chrysler_addrs; - -static uint32_t chrysler_get_checksum(const CANPacket_t *to_push) { - int checksum_byte = GET_LEN(to_push) - 1U; - return (uint8_t)(GET_BYTE(to_push, checksum_byte)); -} - -static uint32_t chrysler_compute_checksum(const CANPacket_t *to_push) { - // TODO: clean this up - // http://illmatics.com/Remote%20Car%20Hacking.pdf - uint8_t checksum = 0xFFU; - int len = GET_LEN(to_push); - for (int j = 0; j < (len - 1); j++) { - uint8_t shift = 0x80U; - uint8_t curr = (uint8_t)GET_BYTE(to_push, j); - for (int i=0; i<8; i++) { - uint8_t bit_sum = curr & shift; - uint8_t temp_chk = checksum & 0x80U; - if (bit_sum != 0U) { - bit_sum = 0x1C; - if (temp_chk != 0U) { - bit_sum = 1; - } - checksum = checksum << 1; - temp_chk = checksum | 1U; - bit_sum ^= temp_chk; - } else { - if (temp_chk != 0U) { - bit_sum = 0x1D; - } - checksum = checksum << 1; - bit_sum ^= checksum; - } - checksum = bit_sum; - shift = shift >> 1; - } - } - return (uint8_t)(~checksum); -} - -static uint8_t chrysler_get_counter(const CANPacket_t *to_push) { - return (uint8_t)(GET_BYTE(to_push, 6) >> 4); -} - -static void chrysler_rx_hook(const CANPacket_t *to_push) { - const int bus = GET_BUS(to_push); - const int addr = GET_ADDR(to_push); - - // Measured EPS torque - if ((bus == 0) && (addr == chrysler_addrs->EPS_2)) { - int torque_meas_new = ((GET_BYTE(to_push, 4) & 0x7U) << 8) + GET_BYTE(to_push, 5) - 1024U; - update_sample(&torque_meas, torque_meas_new); - } - - // enter controls on rising edge of ACC, exit controls on ACC off - const int das_3_bus = (chrysler_platform == CHRYSLER_PACIFICA) ? 0 : 2; - if ((bus == das_3_bus) && (addr == chrysler_addrs->DAS_3)) { - bool cruise_engaged = GET_BIT(to_push, 21U); - pcm_cruise_check(cruise_engaged); - } - - // TODO: use the same message for both - // update vehicle moving - if ((chrysler_platform != CHRYSLER_PACIFICA) && (bus == 0) && (addr == chrysler_addrs->ESP_8)) { - vehicle_moving = ((GET_BYTE(to_push, 4) << 8) + GET_BYTE(to_push, 5)) != 0U; - } - if ((chrysler_platform == CHRYSLER_PACIFICA) && (bus == 0) && (addr == 514)) { - int speed_l = (GET_BYTE(to_push, 0) << 4) + (GET_BYTE(to_push, 1) >> 4); - int speed_r = (GET_BYTE(to_push, 2) << 4) + (GET_BYTE(to_push, 3) >> 4); - vehicle_moving = (speed_l != 0) || (speed_r != 0); - } - - // exit controls on rising edge of gas press - if ((bus == 0) && (addr == chrysler_addrs->ECM_5)) { - gas_pressed = GET_BYTE(to_push, 0U) != 0U; - } - - // exit controls on rising edge of brake press - if ((bus == 0) && (addr == chrysler_addrs->ESP_1)) { - brake_pressed = ((GET_BYTE(to_push, 0U) & 0xFU) >> 2U) == 1U; - } - - generic_rx_checks((bus == 0) && (addr == chrysler_addrs->LKAS_COMMAND)); -} - -static bool chrysler_tx_hook(const CANPacket_t *to_send) { - const SteeringLimits CHRYSLER_STEERING_LIMITS = { - .max_steer = 261, - .max_rt_delta = 112, - .max_rt_interval = 250000, - .max_rate_up = 3, - .max_rate_down = 3, - .max_torque_error = 80, - .type = TorqueMotorLimited, - }; - - const SteeringLimits CHRYSLER_RAM_DT_STEERING_LIMITS = { - .max_steer = 350, - .max_rt_delta = 112, - .max_rt_interval = 250000, - .max_rate_up = 6, - .max_rate_down = 6, - .max_torque_error = 80, - .type = TorqueMotorLimited, - }; - - const SteeringLimits CHRYSLER_RAM_HD_STEERING_LIMITS = { - .max_steer = 361, - .max_rt_delta = 182, - .max_rt_interval = 250000, - .max_rate_up = 14, - .max_rate_down = 14, - .max_torque_error = 80, - .type = TorqueMotorLimited, - }; - - bool tx = true; - int addr = GET_ADDR(to_send); - - // STEERING - if (addr == chrysler_addrs->LKAS_COMMAND) { - int start_byte = (chrysler_platform == CHRYSLER_PACIFICA) ? 0 : 1; - int desired_torque = ((GET_BYTE(to_send, start_byte) & 0x7U) << 8) | GET_BYTE(to_send, start_byte + 1); - desired_torque -= 1024; - - const SteeringLimits limits = (chrysler_platform == CHRYSLER_PACIFICA) ? CHRYSLER_STEERING_LIMITS : - (chrysler_platform == CHRYSLER_RAM_DT) ? CHRYSLER_RAM_DT_STEERING_LIMITS : CHRYSLER_RAM_HD_STEERING_LIMITS; - - bool steer_req = (chrysler_platform == CHRYSLER_PACIFICA) ? GET_BIT(to_send, 4U) : (GET_BYTE(to_send, 3) & 0x7U) == 2U; - if (steer_torque_cmd_checks(desired_torque, steer_req, limits)) { - tx = false; - } - } - - // FORCE CANCEL: only the cancel button press is allowed - if (addr == chrysler_addrs->CRUISE_BUTTONS) { - const bool is_cancel = GET_BYTE(to_send, 0) == 1U; - const bool is_resume = GET_BYTE(to_send, 0) == 0x10U; - const bool allowed = is_cancel || (is_resume && controls_allowed); - if (!allowed) { - tx = false; - } - } - - return tx; -} - -static int chrysler_fwd_hook(int bus_num, int addr) { - int bus_fwd = -1; - - // forward to camera - if (bus_num == 0) { - bus_fwd = 2; - } - - // forward all messages from camera except LKAS messages - const bool is_lkas = ((addr == chrysler_addrs->LKAS_COMMAND) || (addr == chrysler_addrs->DAS_6)); - if ((bus_num == 2) && !is_lkas){ - bus_fwd = 0; - } - - return bus_fwd; -} - -static safety_config chrysler_init(uint16_t param) { - - const uint32_t CHRYSLER_PARAM_RAM_DT = 1U; // set for Ram DT platform - - // CAN messages for Chrysler/Jeep platforms - static const ChryslerAddrs CHRYSLER_ADDRS = { - .EPS_2 = 0x220, // EPS driver input torque - .ESP_1 = 0x140, // Brake pedal and vehicle speed - .ESP_8 = 0x11C, // Brake pedal and vehicle speed - .ECM_5 = 0x22F, // Throttle position sensor - .DAS_3 = 0x1F4, // ACC engagement states from DASM - .DAS_6 = 0x2A6, // LKAS HUD and auto headlight control from DASM - .LKAS_COMMAND = 0x292, // LKAS controls from DASM - .CRUISE_BUTTONS = 0x23B, // Cruise control buttons - }; - - // CAN messages for the 5th gen RAM DT platform - static const ChryslerAddrs CHRYSLER_RAM_DT_ADDRS = { - .EPS_2 = 0x31, // EPS driver input torque - .ESP_1 = 0x83, // Brake pedal and vehicle speed - .ESP_8 = 0x79, // Brake pedal and vehicle speed - .ECM_5 = 0x9D, // Throttle position sensor - .DAS_3 = 0x99, // ACC engagement states from DASM - .DAS_6 = 0xFA, // LKAS HUD and auto headlight control from DASM - .LKAS_COMMAND = 0xA6, // LKAS controls from DASM - .CRUISE_BUTTONS = 0xB1, // Cruise control buttons - }; - - static RxCheck chrysler_ram_dt_rx_checks[] = { - {.msg = {{CHRYSLER_RAM_DT_ADDRS.EPS_2, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 100U}, { 0 }, { 0 }}}, - {.msg = {{CHRYSLER_RAM_DT_ADDRS.ESP_1, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{CHRYSLER_RAM_DT_ADDRS.ESP_8, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{CHRYSLER_RAM_DT_ADDRS.ECM_5, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{CHRYSLER_RAM_DT_ADDRS.DAS_3, 2, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, - }; - - static RxCheck chrysler_rx_checks[] = { - {.msg = {{CHRYSLER_ADDRS.EPS_2, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 100U}, { 0 }, { 0 }}}, - {.msg = {{CHRYSLER_ADDRS.ESP_1, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, - //{.msg = {{ESP_8, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}}}, - {.msg = {{514, 0, 8, .check_checksum = false, .max_counter = 0U, .frequency = 100U}, { 0 }, { 0 }}}, - {.msg = {{CHRYSLER_ADDRS.ECM_5, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{CHRYSLER_ADDRS.DAS_3, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, - }; - - static const CanMsg CHRYSLER_TX_MSGS[] = { - {CHRYSLER_ADDRS.CRUISE_BUTTONS, 0, 3}, - {CHRYSLER_ADDRS.LKAS_COMMAND, 0, 6}, - {CHRYSLER_ADDRS.DAS_6, 0, 8}, - }; - - static const CanMsg CHRYSLER_RAM_DT_TX_MSGS[] = { - {CHRYSLER_RAM_DT_ADDRS.CRUISE_BUTTONS, 2, 3}, - {CHRYSLER_RAM_DT_ADDRS.LKAS_COMMAND, 0, 8}, - {CHRYSLER_RAM_DT_ADDRS.DAS_6, 0, 8}, - }; - -#ifdef ALLOW_DEBUG - // CAN messages for the 5th gen RAM HD platform - static const ChryslerAddrs CHRYSLER_RAM_HD_ADDRS = { - .EPS_2 = 0x220, // EPS driver input torque - .ESP_1 = 0x140, // Brake pedal and vehicle speed - .ESP_8 = 0x11C, // Brake pedal and vehicle speed - .ECM_5 = 0x22F, // Throttle position sensor - .DAS_3 = 0x1F4, // ACC engagement states from DASM - .DAS_6 = 0x275, // LKAS HUD and auto headlight control from DASM - .LKAS_COMMAND = 0x276, // LKAS controls from DASM - .CRUISE_BUTTONS = 0x23A, // Cruise control buttons - }; - - static RxCheck chrysler_ram_hd_rx_checks[] = { - {.msg = {{CHRYSLER_RAM_HD_ADDRS.EPS_2, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 100U}, { 0 }, { 0 }}}, - {.msg = {{CHRYSLER_RAM_HD_ADDRS.ESP_1, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{CHRYSLER_RAM_HD_ADDRS.ESP_8, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{CHRYSLER_RAM_HD_ADDRS.ECM_5, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{CHRYSLER_RAM_HD_ADDRS.DAS_3, 2, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, - }; - - static const CanMsg CHRYSLER_RAM_HD_TX_MSGS[] = { - {CHRYSLER_RAM_HD_ADDRS.CRUISE_BUTTONS, 2, 3}, - {CHRYSLER_RAM_HD_ADDRS.LKAS_COMMAND, 0, 8}, - {CHRYSLER_RAM_HD_ADDRS.DAS_6, 0, 8}, - }; - - const uint32_t CHRYSLER_PARAM_RAM_HD = 2U; // set for Ram HD platform - bool enable_ram_hd = GET_FLAG(param, CHRYSLER_PARAM_RAM_HD); -#endif - - safety_config ret; - - bool enable_ram_dt = GET_FLAG(param, CHRYSLER_PARAM_RAM_DT); - - if (enable_ram_dt) { - chrysler_platform = CHRYSLER_RAM_DT; - chrysler_addrs = &CHRYSLER_RAM_DT_ADDRS; - ret = BUILD_SAFETY_CFG(chrysler_ram_dt_rx_checks, CHRYSLER_RAM_DT_TX_MSGS); -#ifdef ALLOW_DEBUG - } else if (enable_ram_hd) { - chrysler_platform = CHRYSLER_RAM_HD; - chrysler_addrs = &CHRYSLER_RAM_HD_ADDRS; - ret = BUILD_SAFETY_CFG(chrysler_ram_hd_rx_checks, CHRYSLER_RAM_HD_TX_MSGS); -#endif - } else { - chrysler_platform = CHRYSLER_PACIFICA; - chrysler_addrs = &CHRYSLER_ADDRS; - ret = BUILD_SAFETY_CFG(chrysler_rx_checks, CHRYSLER_TX_MSGS); - } - return ret; -} - -const safety_hooks chrysler_hooks = { - .init = chrysler_init, - .rx = chrysler_rx_hook, - .tx = chrysler_tx_hook, - .fwd = chrysler_fwd_hook, - .get_counter = chrysler_get_counter, - .get_checksum = chrysler_get_checksum, - .compute_checksum = chrysler_compute_checksum, -}; diff --git a/board/safety/safety_defaults.h b/board/safety/safety_defaults.h deleted file mode 100644 index 6716b57b3c..0000000000 --- a/board/safety/safety_defaults.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "safety_declarations.h" - -void default_rx_hook(const CANPacket_t *to_push) { - UNUSED(to_push); -} - -// *** no output safety mode *** - -static safety_config nooutput_init(uint16_t param) { - UNUSED(param); - return (safety_config){NULL, 0, NULL, 0}; -} - -static bool nooutput_tx_hook(const CANPacket_t *to_send) { - UNUSED(to_send); - return false; -} - -static int default_fwd_hook(int bus_num, int addr) { - UNUSED(bus_num); - UNUSED(addr); - return -1; -} - -const safety_hooks nooutput_hooks = { - .init = nooutput_init, - .rx = default_rx_hook, - .tx = nooutput_tx_hook, - .fwd = default_fwd_hook, -}; - -// *** all output safety mode *** - -// Enables passthrough mode where relay is open and bus 0 gets forwarded to bus 2 and vice versa -static bool alloutput_passthrough = false; - -static safety_config alloutput_init(uint16_t param) { - // Enables passthrough mode where relay is open and bus 0 gets forwarded to bus 2 and vice versa - const uint16_t ALLOUTPUT_PARAM_PASSTHROUGH = 1; - controls_allowed = true; - alloutput_passthrough = GET_FLAG(param, ALLOUTPUT_PARAM_PASSTHROUGH); - return (safety_config){NULL, 0, NULL, 0}; -} - -static bool alloutput_tx_hook(const CANPacket_t *to_send) { - UNUSED(to_send); - return true; -} - -static int alloutput_fwd_hook(int bus_num, int addr) { - int bus_fwd = -1; - UNUSED(addr); - - if (alloutput_passthrough) { - if (bus_num == 0) { - bus_fwd = 2; - } - if (bus_num == 2) { - bus_fwd = 0; - } - } - - return bus_fwd; -} - -const safety_hooks alloutput_hooks = { - .init = alloutput_init, - .rx = default_rx_hook, - .tx = alloutput_tx_hook, - .fwd = alloutput_fwd_hook, -}; diff --git a/board/safety/safety_elm327.h b/board/safety/safety_elm327.h deleted file mode 100644 index 83efd826b9..0000000000 --- a/board/safety/safety_elm327.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "safety_declarations.h" -#include "safety_defaults.h" - -static bool elm327_tx_hook(const CANPacket_t *to_send) { - const int GM_CAMERA_DIAG_ADDR = 0x24B; - - bool tx = true; - int addr = GET_ADDR(to_send); - int len = GET_LEN(to_send); - - // All ISO 15765-4 messages must be 8 bytes long - if (len != 8) { - tx = false; - } - - // Check valid 29 bit send addresses for ISO 15765-4 - // Check valid 11 bit send addresses for ISO 15765-4 - if ((addr != 0x18DB33F1) && ((addr & 0x1FFF00FF) != 0x18DA00F1) && - ((addr & 0x1FFFFF00) != 0x600) && ((addr & 0x1FFFFF00) != 0x700) && - (addr != GM_CAMERA_DIAG_ADDR)) { - tx = false; - } - - // GM camera uses non-standard diagnostic address, this has no control message address collisions - if ((addr == GM_CAMERA_DIAG_ADDR) && (len == 8)) { - // Only allow known frame types for ISO 15765-2 - if ((GET_BYTE(to_send, 0) & 0xF0U) > 0x30U) { - tx = false; - } - } - return tx; -} - -// If current_board->has_obd and safety_param == 0, bus 1 is multiplexed to the OBD-II port -const safety_hooks elm327_hooks = { - .init = nooutput_init, - .rx = default_rx_hook, - .tx = elm327_tx_hook, - .fwd = default_fwd_hook, -}; diff --git a/board/safety/safety_ford.h b/board/safety/safety_ford.h deleted file mode 100644 index 63ef9dbc45..0000000000 --- a/board/safety/safety_ford.h +++ /dev/null @@ -1,430 +0,0 @@ -#pragma once - -#include "safety_declarations.h" - -// Safety-relevant CAN messages for Ford vehicles. -#define FORD_EngBrakeData 0x165 // RX from PCM, for driver brake pedal and cruise state -#define FORD_EngVehicleSpThrottle 0x204 // RX from PCM, for driver throttle input -#define FORD_DesiredTorqBrk 0x213 // RX from ABS, for standstill state -#define FORD_BrakeSysFeatures 0x415 // RX from ABS, for vehicle speed -#define FORD_EngVehicleSpThrottle2 0x202 // RX from PCM, for second vehicle speed -#define FORD_Yaw_Data_FD1 0x91 // RX from RCM, for yaw rate -#define FORD_Steering_Data_FD1 0x083 // TX by OP, various driver switches and LKAS/CC buttons -#define FORD_ACCDATA 0x186 // TX by OP, ACC controls -#define FORD_ACCDATA_3 0x18A // TX by OP, ACC/TJA user interface -#define FORD_Lane_Assist_Data1 0x3CA // TX by OP, Lane Keep Assist -#define FORD_LateralMotionControl 0x3D3 // TX by OP, Lateral Control message -#define FORD_LateralMotionControl2 0x3D6 // TX by OP, alternate Lateral Control message -#define FORD_IPMA_Data 0x3D8 // TX by OP, IPMA and LKAS user interface - -// CAN bus numbers. -#define FORD_MAIN_BUS 0U -#define FORD_CAM_BUS 2U - -static uint8_t ford_get_counter(const CANPacket_t *to_push) { - int addr = GET_ADDR(to_push); - - uint8_t cnt = 0; - if (addr == FORD_BrakeSysFeatures) { - // Signal: VehVActlBrk_No_Cnt - cnt = (GET_BYTE(to_push, 2) >> 2) & 0xFU; - } else if (addr == FORD_Yaw_Data_FD1) { - // Signal: VehRollYaw_No_Cnt - cnt = GET_BYTE(to_push, 5); - } else { - } - return cnt; -} - -static uint32_t ford_get_checksum(const CANPacket_t *to_push) { - int addr = GET_ADDR(to_push); - - uint8_t chksum = 0; - if (addr == FORD_BrakeSysFeatures) { - // Signal: VehVActlBrk_No_Cs - chksum = GET_BYTE(to_push, 3); - } else if (addr == FORD_Yaw_Data_FD1) { - // Signal: VehRollYawW_No_Cs - chksum = GET_BYTE(to_push, 4); - } else { - } - return chksum; -} - -static uint32_t ford_compute_checksum(const CANPacket_t *to_push) { - int addr = GET_ADDR(to_push); - - uint8_t chksum = 0; - if (addr == FORD_BrakeSysFeatures) { - chksum += GET_BYTE(to_push, 0) + GET_BYTE(to_push, 1); // Veh_V_ActlBrk - chksum += GET_BYTE(to_push, 2) >> 6; // VehVActlBrk_D_Qf - chksum += (GET_BYTE(to_push, 2) >> 2) & 0xFU; // VehVActlBrk_No_Cnt - chksum = 0xFFU - chksum; - } else if (addr == FORD_Yaw_Data_FD1) { - chksum += GET_BYTE(to_push, 0) + GET_BYTE(to_push, 1); // VehRol_W_Actl - chksum += GET_BYTE(to_push, 2) + GET_BYTE(to_push, 3); // VehYaw_W_Actl - chksum += GET_BYTE(to_push, 5); // VehRollYaw_No_Cnt - chksum += GET_BYTE(to_push, 6) >> 6; // VehRolWActl_D_Qf - chksum += (GET_BYTE(to_push, 6) >> 4) & 0x3U; // VehYawWActl_D_Qf - chksum = 0xFFU - chksum; - } else { - } - - return chksum; -} - -static bool ford_get_quality_flag_valid(const CANPacket_t *to_push) { - int addr = GET_ADDR(to_push); - - bool valid = false; - if (addr == FORD_BrakeSysFeatures) { - valid = (GET_BYTE(to_push, 2) >> 6) == 0x3U; // VehVActlBrk_D_Qf - } else if (addr == FORD_EngVehicleSpThrottle2) { - valid = ((GET_BYTE(to_push, 4) >> 5) & 0x3U) == 0x3U; // VehVActlEng_D_Qf - } else if (addr == FORD_Yaw_Data_FD1) { - valid = ((GET_BYTE(to_push, 6) >> 4) & 0x3U) == 0x3U; // VehYawWActl_D_Qf - } else { - } - return valid; -} - -static bool ford_longitudinal = false; - -#define FORD_INACTIVE_CURVATURE 1000U -#define FORD_INACTIVE_CURVATURE_RATE 4096U -#define FORD_INACTIVE_PATH_OFFSET 512U -#define FORD_INACTIVE_PATH_ANGLE 1000U - -#define FORD_CANFD_INACTIVE_CURVATURE_RATE 1024U - -#define FORD_MAX_SPEED_DELTA 2.0 // m/s - -static bool ford_lkas_msg_check(int addr) { - return (addr == FORD_ACCDATA_3) - || (addr == FORD_Lane_Assist_Data1) - || (addr == FORD_LateralMotionControl) - || (addr == FORD_LateralMotionControl2) - || (addr == FORD_IPMA_Data); -} - -// Curvature rate limits -static const SteeringLimits FORD_STEERING_LIMITS = { - .max_steer = 1000, - .angle_deg_to_can = 50000, // 1 / (2e-5) rad to can - .max_angle_error = 100, // 0.002 * FORD_STEERING_LIMITS.angle_deg_to_can - .angle_rate_up_lookup = { - {5., 25., 25.}, - {0.00045, 0.0001, 0.0001} - }, - .angle_rate_down_lookup = { - {5., 25., 25.}, - {0.00045, 0.00015, 0.00015} - }, - - // no blending at low speed due to lack of torque wind-up and inaccurate current curvature - .angle_error_min_speed = 10.0, // m/s - - .enforce_angle_error = true, - .inactive_angle_is_zero = true, -}; - -static void ford_rx_hook(const CANPacket_t *to_push) { - if (GET_BUS(to_push) == FORD_MAIN_BUS) { - int addr = GET_ADDR(to_push); - - // Update in motion state from standstill signal - if (addr == FORD_DesiredTorqBrk) { - // Signal: VehStop_D_Stat - vehicle_moving = ((GET_BYTE(to_push, 3) >> 3) & 0x3U) != 1U; - } - - // Update vehicle speed - if (addr == FORD_BrakeSysFeatures) { - // Signal: Veh_V_ActlBrk - UPDATE_VEHICLE_SPEED(((GET_BYTE(to_push, 0) << 8) | GET_BYTE(to_push, 1)) * 0.01 / 3.6); - } - - // Check vehicle speed against a second source - if (addr == FORD_EngVehicleSpThrottle2) { - // Disable controls if speeds from ABS and PCM ECUs are too far apart. - // Signal: Veh_V_ActlEng - float filtered_pcm_speed = ((GET_BYTE(to_push, 6) << 8) | GET_BYTE(to_push, 7)) * 0.01 / 3.6; - bool is_invalid_speed = ABS(filtered_pcm_speed - ((float)vehicle_speed.values[0] / VEHICLE_SPEED_FACTOR)) > FORD_MAX_SPEED_DELTA; - if (is_invalid_speed) { - controls_allowed = false; - } - } - - // Update vehicle yaw rate - if (addr == FORD_Yaw_Data_FD1) { - // Signal: VehYaw_W_Actl - float ford_yaw_rate = (((GET_BYTE(to_push, 2) << 8U) | GET_BYTE(to_push, 3)) * 0.0002) - 6.5; - float current_curvature = ford_yaw_rate / MAX(vehicle_speed.values[0] / VEHICLE_SPEED_FACTOR, 0.1); - // convert current curvature into units on CAN for comparison with desired curvature - update_sample(&angle_meas, ROUND(current_curvature * FORD_STEERING_LIMITS.angle_deg_to_can)); - } - - // Update gas pedal - if (addr == FORD_EngVehicleSpThrottle) { - // Pedal position: (0.1 * val) in percent - // Signal: ApedPos_Pc_ActlArb - gas_pressed = (((GET_BYTE(to_push, 0) & 0x03U) << 8) | GET_BYTE(to_push, 1)) > 0U; - } - - // Update brake pedal and cruise state - if (addr == FORD_EngBrakeData) { - // Signal: BpedDrvAppl_D_Actl - brake_pressed = ((GET_BYTE(to_push, 0) >> 4) & 0x3U) == 2U; - - // Signal: CcStat_D_Actl - unsigned int cruise_state = GET_BYTE(to_push, 1) & 0x07U; - bool cruise_engaged = (cruise_state == 4U) || (cruise_state == 5U); - pcm_cruise_check(cruise_engaged); - } - - // If steering controls messages are received on the destination bus, it's an indication - // that the relay might be malfunctioning. - bool stock_ecu_detected = ford_lkas_msg_check(addr); - if (ford_longitudinal) { - stock_ecu_detected = stock_ecu_detected || (addr == FORD_ACCDATA); - } - generic_rx_checks(stock_ecu_detected); - } - -} - -static bool ford_tx_hook(const CANPacket_t *to_send) { - const LongitudinalLimits FORD_LONG_LIMITS = { - // acceleration cmd limits (used for brakes) - // Signal: AccBrkTot_A_Rq - .max_accel = 5641, // 1.9999 m/s^s - .min_accel = 4231, // -3.4991 m/s^2 - .inactive_accel = 5128, // -0.0008 m/s^2 - - // gas cmd limits - // Signal: AccPrpl_A_Rq & AccPrpl_A_Pred - .max_gas = 700, // 2.0 m/s^2 - .min_gas = 450, // -0.5 m/s^2 - .inactive_gas = 0, // -5.0 m/s^2 - }; - - bool tx = true; - - int addr = GET_ADDR(to_send); - - // Safety check for ACCDATA accel and brake requests - if (addr == FORD_ACCDATA) { - // Signal: AccPrpl_A_Rq - int gas = ((GET_BYTE(to_send, 6) & 0x3U) << 8) | GET_BYTE(to_send, 7); - // Signal: AccPrpl_A_Pred - int gas_pred = ((GET_BYTE(to_send, 2) & 0x3U) << 8) | GET_BYTE(to_send, 3); - // Signal: AccBrkTot_A_Rq - int accel = ((GET_BYTE(to_send, 0) & 0x1FU) << 8) | GET_BYTE(to_send, 1); - // Signal: CmbbDeny_B_Actl - bool cmbb_deny = GET_BIT(to_send, 37U); - - // Signal: AccBrkPrchg_B_Rq & AccBrkDecel_B_Rq - bool brake_actuation = GET_BIT(to_send, 54U) || GET_BIT(to_send, 55U); - - bool violation = false; - violation |= longitudinal_accel_checks(accel, FORD_LONG_LIMITS); - violation |= longitudinal_gas_checks(gas, FORD_LONG_LIMITS); - violation |= longitudinal_gas_checks(gas_pred, FORD_LONG_LIMITS); - - // Safety check for stock AEB - violation |= cmbb_deny; // do not prevent stock AEB actuation - - violation |= !get_longitudinal_allowed() && brake_actuation; - - if (violation) { - tx = false; - } - } - - // Safety check for Steering_Data_FD1 button signals - // Note: Many other signals in this message are not relevant to safety (e.g. blinkers, wiper switches, high beam) - // which we passthru in OP. - if (addr == FORD_Steering_Data_FD1) { - // Violation if resume button is pressed while controls not allowed, or - // if cancel button is pressed when cruise isn't engaged. - bool violation = false; - violation |= GET_BIT(to_send, 8U) && !cruise_engaged_prev; // Signal: CcAslButtnCnclPress (cancel) - violation |= GET_BIT(to_send, 25U) && !controls_allowed; // Signal: CcAsllButtnResPress (resume) - - if (violation) { - tx = false; - } - } - - // Safety check for Lane_Assist_Data1 action - if (addr == FORD_Lane_Assist_Data1) { - // Do not allow steering using Lane_Assist_Data1 (Lane-Departure Aid). - // This message must be sent for Lane Centering to work, and can include - // values such as the steering angle or lane curvature for debugging, - // but the action (LkaActvStats_D2_Req) must be set to zero. - unsigned int action = GET_BYTE(to_send, 0) >> 5; - if (action != 0U) { - tx = false; - } - } - - // Safety check for LateralMotionControl action - if (addr == FORD_LateralMotionControl) { - // Signal: LatCtl_D_Rq - bool steer_control_enabled = ((GET_BYTE(to_send, 4) >> 2) & 0x7U) != 0U; - unsigned int raw_curvature = (GET_BYTE(to_send, 0) << 3) | (GET_BYTE(to_send, 1) >> 5); - unsigned int raw_curvature_rate = ((GET_BYTE(to_send, 1) & 0x1FU) << 8) | GET_BYTE(to_send, 2); - unsigned int raw_path_angle = (GET_BYTE(to_send, 3) << 3) | (GET_BYTE(to_send, 4) >> 5); - unsigned int raw_path_offset = (GET_BYTE(to_send, 5) << 2) | (GET_BYTE(to_send, 6) >> 6); - - // These signals are not yet tested with the current safety limits - bool violation = (raw_curvature_rate != FORD_INACTIVE_CURVATURE_RATE) || (raw_path_angle != FORD_INACTIVE_PATH_ANGLE) || (raw_path_offset != FORD_INACTIVE_PATH_OFFSET); - - // Check angle error and steer_control_enabled - int desired_curvature = raw_curvature - FORD_INACTIVE_CURVATURE; // /FORD_STEERING_LIMITS.angle_deg_to_can to get real curvature - violation |= steer_angle_cmd_checks(desired_curvature, steer_control_enabled, FORD_STEERING_LIMITS); - - if (violation) { - tx = false; - } - } - - // Safety check for LateralMotionControl2 action - if (addr == FORD_LateralMotionControl2) { - // Signal: LatCtl_D2_Rq - bool steer_control_enabled = ((GET_BYTE(to_send, 0) >> 4) & 0x7U) != 0U; - unsigned int raw_curvature = (GET_BYTE(to_send, 2) << 3) | (GET_BYTE(to_send, 3) >> 5); - unsigned int raw_curvature_rate = (GET_BYTE(to_send, 6) << 3) | (GET_BYTE(to_send, 7) >> 5); - unsigned int raw_path_angle = ((GET_BYTE(to_send, 3) & 0x1FU) << 6) | (GET_BYTE(to_send, 4) >> 2); - unsigned int raw_path_offset = ((GET_BYTE(to_send, 4) & 0x3U) << 8) | GET_BYTE(to_send, 5); - - // These signals are not yet tested with the current safety limits - bool violation = (raw_curvature_rate != FORD_CANFD_INACTIVE_CURVATURE_RATE) || (raw_path_angle != FORD_INACTIVE_PATH_ANGLE) || (raw_path_offset != FORD_INACTIVE_PATH_OFFSET); - - // Check angle error and steer_control_enabled - int desired_curvature = raw_curvature - FORD_INACTIVE_CURVATURE; // /FORD_STEERING_LIMITS.angle_deg_to_can to get real curvature - violation |= steer_angle_cmd_checks(desired_curvature, steer_control_enabled, FORD_STEERING_LIMITS); - - if (violation) { - tx = false; - } - } - - return tx; -} - -static int ford_fwd_hook(int bus_num, int addr) { - int bus_fwd = -1; - - switch (bus_num) { - case FORD_MAIN_BUS: { - // Forward all traffic from bus 0 onward - bus_fwd = FORD_CAM_BUS; - break; - } - case FORD_CAM_BUS: { - if (ford_lkas_msg_check(addr)) { - // Block stock LKAS and UI messages - bus_fwd = -1; - } else if (ford_longitudinal && (addr == FORD_ACCDATA)) { - // Block stock ACC message - bus_fwd = -1; - } else { - // Forward remaining traffic - bus_fwd = FORD_MAIN_BUS; - } - break; - } - default: { - // No other buses should be in use; fallback to do-not-forward - bus_fwd = -1; - break; - } - } - - return bus_fwd; -} - -static safety_config ford_init(uint16_t param) { - bool ford_canfd = false; - - // warning: quality flags are not yet checked in openpilot's CAN parser, - // this may be the cause of blocked messages - static RxCheck ford_rx_checks[] = { - {.msg = {{FORD_BrakeSysFeatures, 0, 8, .check_checksum = true, .max_counter = 15U, .quality_flag=true, .frequency = 50U}, { 0 }, { 0 }}}, - // FORD_EngVehicleSpThrottle2 has a counter that either randomly skips or by 2, likely ECU bug - // Some hybrid models also experience a bug where this checksum mismatches for one or two frames under heavy acceleration with ACC - // It has been confirmed that the Bronco Sport's camera only disallows ACC for bad quality flags, not counters or checksums, so we match that - {.msg = {{FORD_EngVehicleSpThrottle2, 0, 8, .check_checksum = false, .quality_flag=true, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{FORD_Yaw_Data_FD1, 0, 8, .check_checksum = true, .max_counter = 255U, .quality_flag=true, .frequency = 100U}, { 0 }, { 0 }}}, - // These messages have no counter or checksum - {.msg = {{FORD_EngBrakeData, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, - {.msg = {{FORD_EngVehicleSpThrottle, 0, 8, .frequency = 100U}, { 0 }, { 0 }}}, - {.msg = {{FORD_DesiredTorqBrk, 0, 8, .frequency = 50U}, { 0 }, { 0 }}}, - }; - - #define FORD_COMMON_TX_MSGS \ - {FORD_Steering_Data_FD1, 0, 8}, \ - {FORD_Steering_Data_FD1, 2, 8}, \ - {FORD_ACCDATA_3, 0, 8}, \ - {FORD_Lane_Assist_Data1, 0, 8}, \ - {FORD_IPMA_Data, 0, 8}, \ - - static const CanMsg FORD_CANFD_LONG_TX_MSGS[] = { - FORD_COMMON_TX_MSGS - {FORD_ACCDATA, 0, 8}, - {FORD_LateralMotionControl2, 0, 8}, - }; - - static const CanMsg FORD_CANFD_STOCK_TX_MSGS[] = { - FORD_COMMON_TX_MSGS - {FORD_LateralMotionControl2, 0, 8}, - }; - - static const CanMsg FORD_STOCK_TX_MSGS[] = { - FORD_COMMON_TX_MSGS - {FORD_LateralMotionControl, 0, 8}, - }; - - static const CanMsg FORD_LONG_TX_MSGS[] = { - FORD_COMMON_TX_MSGS - {FORD_ACCDATA, 0, 8}, - {FORD_LateralMotionControl, 0, 8}, - }; - - UNUSED(param); -#ifdef ALLOW_DEBUG - const uint16_t FORD_PARAM_LONGITUDINAL = 1; - const uint16_t FORD_PARAM_CANFD = 2; - ford_longitudinal = GET_FLAG(param, FORD_PARAM_LONGITUDINAL); - ford_canfd = GET_FLAG(param, FORD_PARAM_CANFD); -#endif - - // Longitudinal is the default for CAN, and optional for CAN FD w/ ALLOW_DEBUG - ford_longitudinal = !ford_canfd || ford_longitudinal; - - safety_config ret; - // FIXME: cppcheck thinks that ford_canfd is always false. This is not true - // if ALLOW_DEBUG is defined but cppcheck is run without ALLOW_DEBUG - // cppcheck-suppress knownConditionTrueFalse - if (ford_canfd) { - ret = ford_longitudinal ? BUILD_SAFETY_CFG(ford_rx_checks, FORD_CANFD_LONG_TX_MSGS) : \ - BUILD_SAFETY_CFG(ford_rx_checks, FORD_CANFD_STOCK_TX_MSGS); - } else { - // cppcheck-suppress knownConditionTrueFalse - ret = ford_longitudinal ? BUILD_SAFETY_CFG(ford_rx_checks, FORD_LONG_TX_MSGS) : \ - BUILD_SAFETY_CFG(ford_rx_checks, FORD_STOCK_TX_MSGS); - } - return ret; -} - -const safety_hooks ford_hooks = { - .init = ford_init, - .rx = ford_rx_hook, - .tx = ford_tx_hook, - .fwd = ford_fwd_hook, - .get_counter = ford_get_counter, - .get_checksum = ford_get_checksum, - .compute_checksum = ford_compute_checksum, - .get_quality_flag_valid = ford_get_quality_flag_valid, -}; diff --git a/board/safety/safety_gm.h b/board/safety/safety_gm.h deleted file mode 100644 index f0902a921a..0000000000 --- a/board/safety/safety_gm.h +++ /dev/null @@ -1,259 +0,0 @@ -#pragma once - -#include "safety_declarations.h" - -static const LongitudinalLimits *gm_long_limits; - -enum { - GM_BTN_UNPRESS = 1, - GM_BTN_RESUME = 2, - GM_BTN_SET = 3, - GM_BTN_CANCEL = 6, -}; - -typedef enum { - GM_ASCM, - GM_CAM -} GmHardware; -static GmHardware gm_hw = GM_ASCM; -static bool gm_cam_long = false; -static bool gm_pcm_cruise = false; - -static void gm_rx_hook(const CANPacket_t *to_push) { - - const int GM_STANDSTILL_THRSLD = 10; // 0.311kph - - - - if (GET_BUS(to_push) == 0U) { - int addr = GET_ADDR(to_push); - - if (addr == 0x184) { - int torque_driver_new = ((GET_BYTE(to_push, 6) & 0x7U) << 8) | GET_BYTE(to_push, 7); - torque_driver_new = to_signed(torque_driver_new, 11); - // update array of samples - update_sample(&torque_driver, torque_driver_new); - } - - // sample rear wheel speeds - if (addr == 0x34A) { - int left_rear_speed = (GET_BYTE(to_push, 0) << 8) | GET_BYTE(to_push, 1); - int right_rear_speed = (GET_BYTE(to_push, 2) << 8) | GET_BYTE(to_push, 3); - vehicle_moving = (left_rear_speed > GM_STANDSTILL_THRSLD) || (right_rear_speed > GM_STANDSTILL_THRSLD); - } - - // ACC steering wheel buttons (GM_CAM is tied to the PCM) - if ((addr == 0x1E1) && !gm_pcm_cruise) { - int button = (GET_BYTE(to_push, 5) & 0x70U) >> 4; - - // enter controls on falling edge of set or rising edge of resume (avoids fault) - bool set = (button != GM_BTN_SET) && (cruise_button_prev == GM_BTN_SET); - bool res = (button == GM_BTN_RESUME) && (cruise_button_prev != GM_BTN_RESUME); - if (set || res) { - controls_allowed = true; - } - - // exit controls on cancel press - if (button == GM_BTN_CANCEL) { - controls_allowed = false; - } - - cruise_button_prev = button; - } - - // Reference for brake pressed signals: - // https://github.com/commaai/openpilot/blob/master/selfdrive/car/gm/carstate.py - if ((addr == 0xBE) && (gm_hw == GM_ASCM)) { - brake_pressed = GET_BYTE(to_push, 1) >= 8U; - } - - if ((addr == 0xC9) && (gm_hw == GM_CAM)) { - brake_pressed = GET_BIT(to_push, 40U); - } - - if (addr == 0x1C4) { - gas_pressed = GET_BYTE(to_push, 5) != 0U; - - // enter controls on rising edge of ACC, exit controls when ACC off - if (gm_pcm_cruise) { - bool cruise_engaged = (GET_BYTE(to_push, 1) >> 5) != 0U; - pcm_cruise_check(cruise_engaged); - } - } - - if (addr == 0xBD) { - regen_braking = (GET_BYTE(to_push, 0) >> 4) != 0U; - } - - bool stock_ecu_detected = (addr == 0x180); // ASCMLKASteeringCmd - - // Check ASCMGasRegenCmd only if we're blocking it - if (!gm_pcm_cruise && (addr == 0x2CB)) { - stock_ecu_detected = true; - } - generic_rx_checks(stock_ecu_detected); - } -} - -static bool gm_tx_hook(const CANPacket_t *to_send) { - const SteeringLimits GM_STEERING_LIMITS = { - .max_steer = 300, - .max_rate_up = 10, - .max_rate_down = 15, - .driver_torque_allowance = 65, - .driver_torque_factor = 4, - .max_rt_delta = 128, - .max_rt_interval = 250000, - .type = TorqueDriverLimited, - }; - - bool tx = true; - int addr = GET_ADDR(to_send); - - // BRAKE: safety check - if (addr == 0x315) { - int brake = ((GET_BYTE(to_send, 0) & 0xFU) << 8) + GET_BYTE(to_send, 1); - brake = (0x1000 - brake) & 0xFFF; - if (longitudinal_brake_checks(brake, *gm_long_limits)) { - tx = false; - } - } - - // LKA STEER: safety check - if (addr == 0x180) { - int desired_torque = ((GET_BYTE(to_send, 0) & 0x7U) << 8) + GET_BYTE(to_send, 1); - desired_torque = to_signed(desired_torque, 11); - - bool steer_req = GET_BIT(to_send, 3U); - - if (steer_torque_cmd_checks(desired_torque, steer_req, GM_STEERING_LIMITS)) { - tx = false; - } - } - - // GAS/REGEN: safety check - if (addr == 0x2CB) { - bool apply = GET_BIT(to_send, 0U); - int gas_regen = ((GET_BYTE(to_send, 2) & 0x7FU) << 5) + ((GET_BYTE(to_send, 3) & 0xF8U) >> 3); - - bool violation = false; - // Allow apply bit in pre-enabled and overriding states - violation |= !controls_allowed && apply; - violation |= longitudinal_gas_checks(gas_regen, *gm_long_limits); - - if (violation) { - tx = false; - } - } - - // BUTTONS: used for resume spamming and cruise cancellation with stock longitudinal - if ((addr == 0x1E1) && gm_pcm_cruise) { - int button = (GET_BYTE(to_send, 5) >> 4) & 0x7U; - - bool allowed_cancel = (button == 6) && cruise_engaged_prev; - if (!allowed_cancel) { - tx = false; - } - } - - return tx; -} - -static int gm_fwd_hook(int bus_num, int addr) { - int bus_fwd = -1; - - if (gm_hw == GM_CAM) { - if (bus_num == 0) { - // block PSCMStatus; forwarded through openpilot to hide an alert from the camera - bool is_pscm_msg = (addr == 0x184); - if (!is_pscm_msg) { - bus_fwd = 2; - } - } - - if (bus_num == 2) { - // block lkas message and acc messages if gm_cam_long, forward all others - bool is_lkas_msg = (addr == 0x180); - bool is_acc_msg = (addr == 0x315) || (addr == 0x2CB) || (addr == 0x370); - bool block_msg = is_lkas_msg || (is_acc_msg && gm_cam_long); - if (!block_msg) { - bus_fwd = 0; - } - } - } - - return bus_fwd; -} - -static safety_config gm_init(uint16_t param) { - const uint16_t GM_PARAM_HW_CAM = 1; - - static const LongitudinalLimits GM_ASCM_LONG_LIMITS = { - .max_gas = 3072, - .min_gas = 1404, - .inactive_gas = 1404, - .max_brake = 400, - }; - - static const CanMsg GM_ASCM_TX_MSGS[] = {{0x180, 0, 4}, {0x409, 0, 7}, {0x40A, 0, 7}, {0x2CB, 0, 8}, {0x370, 0, 6}, // pt bus - {0xA1, 1, 7}, {0x306, 1, 8}, {0x308, 1, 7}, {0x310, 1, 2}, // obs bus - {0x315, 2, 5}}; // ch bus - - - static const LongitudinalLimits GM_CAM_LONG_LIMITS = { - .max_gas = 3400, - .min_gas = 1514, - .inactive_gas = 1554, - .max_brake = 400, - }; - - static const CanMsg GM_CAM_LONG_TX_MSGS[] = {{0x180, 0, 4}, {0x315, 0, 5}, {0x2CB, 0, 8}, {0x370, 0, 6}, // pt bus - {0x184, 2, 8}}; // camera bus - - - // TODO: do checksum and counter checks. Add correct timestep, 0.1s for now. - static RxCheck gm_rx_checks[] = { - {.msg = {{0x184, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, - {.msg = {{0x34A, 0, 5, .frequency = 10U}, { 0 }, { 0 }}}, - {.msg = {{0x1E1, 0, 7, .frequency = 10U}, { 0 }, { 0 }}}, - {.msg = {{0xBE, 0, 6, .frequency = 10U}, // Volt, Silverado, Acadia Denali - {0xBE, 0, 7, .frequency = 10U}, // Bolt EUV - {0xBE, 0, 8, .frequency = 10U}}}, // Escalade - {.msg = {{0x1C4, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, - {.msg = {{0xC9, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, - }; - - static const CanMsg GM_CAM_TX_MSGS[] = {{0x180, 0, 4}, // pt bus - {0x1E1, 2, 7}, {0x184, 2, 8}}; // camera bus - - gm_hw = GET_FLAG(param, GM_PARAM_HW_CAM) ? GM_CAM : GM_ASCM; - - if (gm_hw == GM_ASCM) { - gm_long_limits = &GM_ASCM_LONG_LIMITS; - } else if (gm_hw == GM_CAM) { - gm_long_limits = &GM_CAM_LONG_LIMITS; - } else { - } - -#ifdef ALLOW_DEBUG - const uint16_t GM_PARAM_HW_CAM_LONG = 2; - gm_cam_long = GET_FLAG(param, GM_PARAM_HW_CAM_LONG); -#endif - gm_pcm_cruise = (gm_hw == GM_CAM) && !gm_cam_long; - - safety_config ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_ASCM_TX_MSGS); - if (gm_hw == GM_CAM) { - // FIXME: cppcheck thinks that gm_cam_long is always false. This is not true - // if ALLOW_DEBUG is defined but cppcheck is run without ALLOW_DEBUG - // cppcheck-suppress knownConditionTrueFalse - ret = gm_cam_long ? BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_LONG_TX_MSGS) : BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_TX_MSGS); - } - return ret; -} - -const safety_hooks gm_hooks = { - .init = gm_init, - .rx = gm_rx_hook, - .tx = gm_tx_hook, - .fwd = gm_fwd_hook, -}; diff --git a/board/safety/safety_honda.h b/board/safety/safety_honda.h deleted file mode 100644 index 60ccea1e16..0000000000 --- a/board/safety/safety_honda.h +++ /dev/null @@ -1,461 +0,0 @@ -#pragma once - -#include "safety_declarations.h" - -// All common address checks except SCM_BUTTONS which isn't on one Nidec safety configuration -#define HONDA_COMMON_NO_SCM_FEEDBACK_RX_CHECKS(pt_bus) \ - {.msg = {{0x1A6, (pt_bus), 8, .check_checksum = true, .max_counter = 3U, .frequency = 25U}, /* SCM_BUTTONS */ \ - {0x296, (pt_bus), 4, .check_checksum = true, .max_counter = 3U, .frequency = 25U}, { 0 }}}, \ - {.msg = {{0x158, (pt_bus), 8, .check_checksum = true, .max_counter = 3U, .frequency = 100U}, { 0 }, { 0 }}}, /* ENGINE_DATA */ \ - {.msg = {{0x17C, (pt_bus), 8, .check_checksum = true, .max_counter = 3U, .frequency = 100U}, { 0 }, { 0 }}}, /* POWERTRAIN_DATA */ \ - -#define HONDA_COMMON_RX_CHECKS(pt_bus) \ - HONDA_COMMON_NO_SCM_FEEDBACK_RX_CHECKS(pt_bus) \ - {.msg = {{0x326, (pt_bus), 8, .check_checksum = true, .max_counter = 3U, .frequency = 10U}, { 0 }, { 0 }}}, /* SCM_FEEDBACK */ \ - -// Alternate brake message is used on some Honda Bosch, and Honda Bosch radarless (where PT bus is 0) -#define HONDA_ALT_BRAKE_ADDR_CHECK(pt_bus) \ - {.msg = {{0x1BE, (pt_bus), 3, .check_checksum = true, .max_counter = 3U, .frequency = 50U}, { 0 }, { 0 }}}, /* BRAKE_MODULE */ \ - - -// Nidec and bosch radarless has the powertrain bus on bus 0 -static RxCheck honda_common_rx_checks[] = { - HONDA_COMMON_RX_CHECKS(0) -}; - -enum { - HONDA_BTN_NONE = 0, - HONDA_BTN_MAIN = 1, - HONDA_BTN_CANCEL = 2, - HONDA_BTN_SET = 3, - HONDA_BTN_RESUME = 4, -}; - -static int honda_brake = 0; -static bool honda_brake_switch_prev = false; -static bool honda_alt_brake_msg = false; -static bool honda_fwd_brake = false; -static bool honda_bosch_long = false; -static bool honda_bosch_radarless = false; -typedef enum {HONDA_NIDEC, HONDA_BOSCH} HondaHw; -static HondaHw honda_hw = HONDA_NIDEC; - - -static int honda_get_pt_bus(void) { - return ((honda_hw == HONDA_BOSCH) && !honda_bosch_radarless) ? 1 : 0; -} - -static uint32_t honda_get_checksum(const CANPacket_t *to_push) { - int checksum_byte = GET_LEN(to_push) - 1U; - return (uint8_t)(GET_BYTE(to_push, checksum_byte)) & 0xFU; -} - -static uint32_t honda_compute_checksum(const CANPacket_t *to_push) { - int len = GET_LEN(to_push); - uint8_t checksum = 0U; - unsigned int addr = GET_ADDR(to_push); - while (addr > 0U) { - checksum += (uint8_t)(addr & 0xFU); addr >>= 4; - } - for (int j = 0; j < len; j++) { - uint8_t byte = GET_BYTE(to_push, j); - checksum += (uint8_t)(byte & 0xFU) + (byte >> 4U); - if (j == (len - 1)) { - checksum -= (byte & 0xFU); // remove checksum in message - } - } - return (uint8_t)((8U - checksum) & 0xFU); -} - -static uint8_t honda_get_counter(const CANPacket_t *to_push) { - int counter_byte = GET_LEN(to_push) - 1U; - return (GET_BYTE(to_push, counter_byte) >> 4U) & 0x3U; -} - -static void honda_rx_hook(const CANPacket_t *to_push) { - const bool pcm_cruise = ((honda_hw == HONDA_BOSCH) && !honda_bosch_long) || (honda_hw == HONDA_NIDEC); - int pt_bus = honda_get_pt_bus(); - - int addr = GET_ADDR(to_push); - int bus = GET_BUS(to_push); - - // sample speed - if (addr == 0x158) { - // first 2 bytes - vehicle_moving = GET_BYTE(to_push, 0) | GET_BYTE(to_push, 1); - } - - // check ACC main state - // 0x326 for all Bosch and some Nidec, 0x1A6 for some Nidec - if ((addr == 0x326) || (addr == 0x1A6)) { - acc_main_on = GET_BIT(to_push, ((addr == 0x326) ? 28U : 47U)); - if (!acc_main_on) { - controls_allowed = false; - } - } - - // enter controls when PCM enters cruise state - if (pcm_cruise && (addr == 0x17C)) { - const bool cruise_engaged = GET_BIT(to_push, 38U); - // engage on rising edge - if (cruise_engaged && !cruise_engaged_prev) { - controls_allowed = true; - } - - // Since some Nidec cars can brake down to 0 after the PCM disengages, - // we don't disengage when the PCM does. - if (!cruise_engaged && (honda_hw != HONDA_NIDEC)) { - controls_allowed = false; - } - cruise_engaged_prev = cruise_engaged; - } - - // state machine to enter and exit controls for button enabling - // 0x1A6 for the ILX, 0x296 for the Civic Touring - if (((addr == 0x1A6) || (addr == 0x296)) && (bus == pt_bus)) { - int button = (GET_BYTE(to_push, 0) & 0xE0U) >> 5; - - // enter controls on the falling edge of set or resume - bool set = (button != HONDA_BTN_SET) && (cruise_button_prev == HONDA_BTN_SET); - bool res = (button != HONDA_BTN_RESUME) && (cruise_button_prev == HONDA_BTN_RESUME); - if (acc_main_on && !pcm_cruise && (set || res)) { - controls_allowed = true; - } - - // exit controls once main or cancel are pressed - if ((button == HONDA_BTN_MAIN) || (button == HONDA_BTN_CANCEL)) { - controls_allowed = false; - } - cruise_button_prev = button; - } - - // user brake signal on 0x17C reports applied brake from computer brake on accord - // and crv, which prevents the usual brake safety from working correctly. these - // cars have a signal on 0x1BE which only detects user's brake being applied so - // in these cases, this is used instead. - // most hondas: 0x17C - // accord, crv: 0x1BE - if (honda_alt_brake_msg) { - if (addr == 0x1BE) { - brake_pressed = GET_BIT(to_push, 4U); - } - } else { - if (addr == 0x17C) { - // also if brake switch is 1 for two CAN frames, as brake pressed is delayed - const bool brake_switch = GET_BIT(to_push, 32U); - brake_pressed = (GET_BIT(to_push, 53U)) || (brake_switch && honda_brake_switch_prev); - honda_brake_switch_prev = brake_switch; - } - } - - if (addr == 0x17C) { - gas_pressed = GET_BYTE(to_push, 0) != 0U; - } - - // disable stock Honda AEB in alternative experience - if (!(alternative_experience & ALT_EXP_DISABLE_STOCK_AEB)) { - if ((bus == 2) && (addr == 0x1FA)) { - bool honda_stock_aeb = GET_BIT(to_push, 29U); - int honda_stock_brake = (GET_BYTE(to_push, 0) << 2) | (GET_BYTE(to_push, 1) >> 6); - - // Forward AEB when stock braking is higher than openpilot braking - // only stop forwarding when AEB event is over - if (!honda_stock_aeb) { - honda_fwd_brake = false; - } else if (honda_stock_brake >= honda_brake) { - honda_fwd_brake = true; - } else { - // Leave Honda forward brake as is - } - } - } - - int bus_rdr_car = (honda_hw == HONDA_BOSCH) ? 0 : 2; // radar bus, car side - bool stock_ecu_detected = false; - - // If steering controls messages are received on the destination bus, it's an indication - // that the relay might be malfunctioning - if ((addr == 0xE4) || (addr == 0x194)) { - if (((honda_hw != HONDA_NIDEC) && (bus == bus_rdr_car)) || ((honda_hw == HONDA_NIDEC) && (bus == 0))) { - stock_ecu_detected = true; - } - } - // If Honda Bosch longitudinal mode is selected we need to ensure the radar is turned off - // Verify this by ensuring ACC_CONTROL (0x1DF) is not received on the PT bus - if (honda_bosch_long && !honda_bosch_radarless && (bus == pt_bus) && (addr == 0x1DF)) { - stock_ecu_detected = true; - } - - generic_rx_checks(stock_ecu_detected); - -} - -static bool honda_tx_hook(const CANPacket_t *to_send) { - - const LongitudinalLimits HONDA_BOSCH_LONG_LIMITS = { - .max_accel = 200, // accel is used for brakes - .min_accel = -350, - - .max_gas = 2000, - .inactive_gas = -30000, - }; - - const LongitudinalLimits HONDA_NIDEC_LONG_LIMITS = { - .max_gas = 198, // 0xc6 - .max_brake = 255, - - .inactive_speed = 0, - }; - - bool tx = true; - int addr = GET_ADDR(to_send); - int bus = GET_BUS(to_send); - - int bus_pt = honda_get_pt_bus(); - int bus_buttons = (honda_bosch_radarless) ? 2 : bus_pt; // the camera controls ACC on radarless Bosch cars - - // ACC_HUD: safety check (nidec w/o pedal) - if ((addr == 0x30C) && (bus == bus_pt)) { - int pcm_speed = (GET_BYTE(to_send, 0) << 8) | GET_BYTE(to_send, 1); - int pcm_gas = GET_BYTE(to_send, 2); - - bool violation = false; - violation |= longitudinal_speed_checks(pcm_speed, HONDA_NIDEC_LONG_LIMITS); - violation |= longitudinal_gas_checks(pcm_gas, HONDA_NIDEC_LONG_LIMITS); - if (violation) { - tx = false; - } - } - - // BRAKE: safety check (nidec) - if ((addr == 0x1FA) && (bus == bus_pt)) { - honda_brake = (GET_BYTE(to_send, 0) << 2) + ((GET_BYTE(to_send, 1) >> 6) & 0x3U); - if (longitudinal_brake_checks(honda_brake, HONDA_NIDEC_LONG_LIMITS)) { - tx = false; - } - if (honda_fwd_brake) { - tx = false; - } - } - - // BRAKE/GAS: safety check (bosch) - if ((addr == 0x1DF) && (bus == bus_pt)) { - int accel = (GET_BYTE(to_send, 3) << 3) | ((GET_BYTE(to_send, 4) >> 5) & 0x7U); - accel = to_signed(accel, 11); - - int gas = (GET_BYTE(to_send, 0) << 8) | GET_BYTE(to_send, 1); - gas = to_signed(gas, 16); - - bool violation = false; - violation |= longitudinal_accel_checks(accel, HONDA_BOSCH_LONG_LIMITS); - violation |= longitudinal_gas_checks(gas, HONDA_BOSCH_LONG_LIMITS); - if (violation) { - tx = false; - } - } - - // ACCEL: safety check (radarless) - if ((addr == 0x1C8) && (bus == bus_pt)) { - int accel = (GET_BYTE(to_send, 0) << 4) | (GET_BYTE(to_send, 1) >> 4); - accel = to_signed(accel, 12); - - bool violation = false; - violation |= longitudinal_accel_checks(accel, HONDA_BOSCH_LONG_LIMITS); - if (violation) { - tx = false; - } - } - - // STEER: safety check - if ((addr == 0xE4) || (addr == 0x194)) { - if (!controls_allowed) { - bool steer_applied = GET_BYTE(to_send, 0) | GET_BYTE(to_send, 1); - if (steer_applied) { - tx = false; - } - } - } - - // Bosch supplemental control check - if (addr == 0xE5) { - if ((GET_BYTES(to_send, 0, 4) != 0x10800004U) || ((GET_BYTES(to_send, 4, 4) & 0x00FFFFFFU) != 0x0U)) { - tx = false; - } - } - - // FORCE CANCEL: safety check only relevant when spamming the cancel button in Bosch HW - // ensuring that only the cancel button press is sent (VAL 2) when controls are off. - // This avoids unintended engagements while still allowing resume spam - if ((addr == 0x296) && !controls_allowed && (bus == bus_buttons)) { - if (((GET_BYTE(to_send, 0) >> 5) & 0x7U) != 2U) { - tx = false; - } - } - - // Only tester present ("\x02\x3E\x80\x00\x00\x00\x00\x00") allowed on diagnostics address - if (addr == 0x18DAB0F1) { - if ((GET_BYTES(to_send, 0, 4) != 0x00803E02U) || (GET_BYTES(to_send, 4, 4) != 0x0U)) { - tx = false; - } - } - - return tx; -} - -static safety_config honda_nidec_init(uint16_t param) { - static CanMsg HONDA_N_TX_MSGS[] = {{0xE4, 0, 5}, {0x194, 0, 4}, {0x1FA, 0, 8}, {0x30C, 0, 8}, {0x33D, 0, 5}}; - - const uint16_t HONDA_PARAM_NIDEC_ALT = 4; - - honda_hw = HONDA_NIDEC; - honda_brake = 0; - honda_brake_switch_prev = false; - honda_fwd_brake = false; - honda_alt_brake_msg = false; - honda_bosch_long = false; - honda_bosch_radarless = false; - - safety_config ret; - - bool enable_nidec_alt = GET_FLAG(param, HONDA_PARAM_NIDEC_ALT); - - if (enable_nidec_alt) { - // For Nidecs with main on signal on an alternate msg (missing 0x326) - static RxCheck honda_nidec_alt_rx_checks[] = { - HONDA_COMMON_NO_SCM_FEEDBACK_RX_CHECKS(0) - }; - - SET_RX_CHECKS(honda_nidec_alt_rx_checks, ret); - } else { - SET_RX_CHECKS(honda_common_rx_checks, ret); - } - - SET_TX_MSGS(HONDA_N_TX_MSGS, ret); - - return ret; -} - -static safety_config honda_bosch_init(uint16_t param) { - static CanMsg HONDA_BOSCH_TX_MSGS[] = {{0xE4, 0, 5}, {0xE5, 0, 8}, {0x296, 1, 4}, {0x33D, 0, 5}, {0x33DA, 0, 5}, {0x33DB, 0, 8}}; // Bosch - static CanMsg HONDA_BOSCH_LONG_TX_MSGS[] = {{0xE4, 1, 5}, {0x1DF, 1, 8}, {0x1EF, 1, 8}, {0x1FA, 1, 8}, {0x30C, 1, 8}, {0x33D, 1, 5}, {0x33DA, 1, 5}, {0x33DB, 1, 8}, {0x39F, 1, 8}, {0x18DAB0F1, 1, 8}}; // Bosch w/ gas and brakes - static CanMsg HONDA_RADARLESS_TX_MSGS[] = {{0xE4, 0, 5}, {0x296, 2, 4}, {0x33D, 0, 8}}; // Bosch radarless - static CanMsg HONDA_RADARLESS_LONG_TX_MSGS[] = {{0xE4, 0, 5}, {0x33D, 0, 8}, {0x1C8, 0, 8}, {0x30C, 0, 8}}; // Bosch radarless w/ gas and brakes - - const uint16_t HONDA_PARAM_ALT_BRAKE = 1; - const uint16_t HONDA_PARAM_RADARLESS = 8; - - static RxCheck honda_common_alt_brake_rx_checks[] = { - HONDA_COMMON_RX_CHECKS(0) - HONDA_ALT_BRAKE_ADDR_CHECK(0) - }; - - static RxCheck honda_bosch_alt_brake_rx_checks[] = { - HONDA_COMMON_RX_CHECKS(1) - HONDA_ALT_BRAKE_ADDR_CHECK(1) - }; - - // Bosch has pt on bus 1, verified 0x1A6 does not exist - static RxCheck honda_bosch_rx_checks[] = { - HONDA_COMMON_RX_CHECKS(1) - }; - - honda_hw = HONDA_BOSCH; - honda_brake_switch_prev = false; - honda_bosch_radarless = GET_FLAG(param, HONDA_PARAM_RADARLESS); - // Checking for alternate brake override from safety parameter - honda_alt_brake_msg = GET_FLAG(param, HONDA_PARAM_ALT_BRAKE); - - // radar disabled so allow gas/brakes -#ifdef ALLOW_DEBUG - const uint16_t HONDA_PARAM_BOSCH_LONG = 2; - honda_bosch_long = GET_FLAG(param, HONDA_PARAM_BOSCH_LONG); -#endif - - safety_config ret; - if (honda_bosch_radarless && honda_alt_brake_msg) { - SET_RX_CHECKS(honda_common_alt_brake_rx_checks, ret); - } else if (honda_bosch_radarless) { - SET_RX_CHECKS(honda_common_rx_checks, ret); - } else if (honda_alt_brake_msg) { - SET_RX_CHECKS(honda_bosch_alt_brake_rx_checks, ret); - } else { - SET_RX_CHECKS(honda_bosch_rx_checks, ret); - } - - if (honda_bosch_radarless) { - if (honda_bosch_long) { - SET_TX_MSGS(HONDA_RADARLESS_LONG_TX_MSGS, ret); - } else { - SET_TX_MSGS(HONDA_RADARLESS_TX_MSGS, ret); - } - } else { - if (honda_bosch_long) { - SET_TX_MSGS(HONDA_BOSCH_LONG_TX_MSGS, ret); - } else { - SET_TX_MSGS(HONDA_BOSCH_TX_MSGS, ret); - } - } - return ret; -} - -static int honda_nidec_fwd_hook(int bus_num, int addr) { - // fwd from car to camera. also fwd certain msgs from camera to car - // 0xE4 is steering on all cars except CRV and RDX, 0x194 for CRV and RDX, - // 0x1FA is brake control, 0x30C is acc hud, 0x33D is lkas hud - int bus_fwd = -1; - - if (bus_num == 0) { - bus_fwd = 2; - } - - if (bus_num == 2) { - // block stock lkas messages and stock acc messages (if OP is doing ACC) - bool is_lkas_msg = (addr == 0xE4) || (addr == 0x194) || (addr == 0x33D); - bool is_acc_hud_msg = addr == 0x30C; - bool is_brake_msg = addr == 0x1FA; - bool block_fwd = is_lkas_msg || is_acc_hud_msg || (is_brake_msg && !honda_fwd_brake); - if (!block_fwd) { - bus_fwd = 0; - } - } - - return bus_fwd; -} - -static int honda_bosch_fwd_hook(int bus_num, int addr) { - int bus_fwd = -1; - - if (bus_num == 0) { - bus_fwd = 2; - } - if (bus_num == 2) { - bool is_lkas_msg = (addr == 0xE4) || (addr == 0xE5) || (addr == 0x33D) || (addr == 0x33DA) || (addr == 0x33DB); - bool is_acc_msg = ((addr == 0x1C8) || (addr == 0x30C)) && honda_bosch_radarless && honda_bosch_long; - bool block_msg = is_lkas_msg || is_acc_msg; - if (!block_msg) { - bus_fwd = 0; - } - } - - return bus_fwd; -} - -const safety_hooks honda_nidec_hooks = { - .init = honda_nidec_init, - .rx = honda_rx_hook, - .tx = honda_tx_hook, - .fwd = honda_nidec_fwd_hook, - .get_counter = honda_get_counter, - .get_checksum = honda_get_checksum, - .compute_checksum = honda_compute_checksum, -}; - -const safety_hooks honda_bosch_hooks = { - .init = honda_bosch_init, - .rx = honda_rx_hook, - .tx = honda_tx_hook, - .fwd = honda_bosch_fwd_hook, - .get_counter = honda_get_counter, - .get_checksum = honda_get_checksum, - .compute_checksum = honda_compute_checksum, -}; diff --git a/board/safety/safety_hyundai.h b/board/safety/safety_hyundai.h deleted file mode 100644 index cf73afee62..0000000000 --- a/board/safety/safety_hyundai.h +++ /dev/null @@ -1,356 +0,0 @@ -#pragma once - -#include "safety_declarations.h" -#include "safety_hyundai_common.h" - -#define HYUNDAI_LIMITS(steer, rate_up, rate_down) { \ - .max_steer = (steer), \ - .max_rate_up = (rate_up), \ - .max_rate_down = (rate_down), \ - .max_rt_delta = 112, \ - .max_rt_interval = 250000, \ - .driver_torque_allowance = 50, \ - .driver_torque_factor = 2, \ - .type = TorqueDriverLimited, \ - /* the EPS faults when the steering angle is above a certain threshold for too long. to prevent this, */ \ - /* we allow setting CF_Lkas_ActToi bit to 0 while maintaining the requested torque value for two consecutive frames */ \ - .min_valid_request_frames = 89, \ - .max_invalid_request_frames = 2, \ - .min_valid_request_rt_interval = 810000, /* 810ms; a ~10% buffer on cutting every 90 frames */ \ - .has_steer_req_tolerance = true, \ -} - -extern const LongitudinalLimits HYUNDAI_LONG_LIMITS; -const LongitudinalLimits HYUNDAI_LONG_LIMITS = { - .max_accel = 200, // 1/100 m/s2 - .min_accel = -350, // 1/100 m/s2 -}; - -static const CanMsg HYUNDAI_TX_MSGS[] = { - {0x340, 0, 8}, // LKAS11 Bus 0 - {0x4F1, 0, 4}, // CLU11 Bus 0 - {0x485, 0, 4}, // LFAHDA_MFC Bus 0 -}; - -#define HYUNDAI_COMMON_RX_CHECKS(legacy) \ - {.msg = {{0x260, 0, 8, .check_checksum = true, .max_counter = 3U, .frequency = 100U}, \ - {0x371, 0, 8, .frequency = 100U}, { 0 }}}, \ - {.msg = {{0x386, 0, 8, .check_checksum = !(legacy), .max_counter = (legacy) ? 0U : 15U, .frequency = 100U}, { 0 }, { 0 }}}, \ - {.msg = {{0x394, 0, 8, .check_checksum = !(legacy), .max_counter = (legacy) ? 0U : 7U, .frequency = 100U}, { 0 }, { 0 }}}, \ - -#define HYUNDAI_SCC12_ADDR_CHECK(scc_bus) \ - {.msg = {{0x421, (scc_bus), 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, \ - -static bool hyundai_legacy = false; - -static uint8_t hyundai_get_counter(const CANPacket_t *to_push) { - int addr = GET_ADDR(to_push); - - uint8_t cnt = 0; - if (addr == 0x260) { - cnt = (GET_BYTE(to_push, 7) >> 4) & 0x3U; - } else if (addr == 0x386) { - cnt = ((GET_BYTE(to_push, 3) >> 6) << 2) | (GET_BYTE(to_push, 1) >> 6); - } else if (addr == 0x394) { - cnt = (GET_BYTE(to_push, 1) >> 5) & 0x7U; - } else if (addr == 0x421) { - cnt = GET_BYTE(to_push, 7) & 0xFU; - } else if (addr == 0x4F1) { - cnt = (GET_BYTE(to_push, 3) >> 4) & 0xFU; - } else { - } - return cnt; -} - -static uint32_t hyundai_get_checksum(const CANPacket_t *to_push) { - int addr = GET_ADDR(to_push); - - uint8_t chksum = 0; - if (addr == 0x260) { - chksum = GET_BYTE(to_push, 7) & 0xFU; - } else if (addr == 0x386) { - chksum = ((GET_BYTE(to_push, 7) >> 6) << 2) | (GET_BYTE(to_push, 5) >> 6); - } else if (addr == 0x394) { - chksum = GET_BYTE(to_push, 6) & 0xFU; - } else if (addr == 0x421) { - chksum = GET_BYTE(to_push, 7) >> 4; - } else { - } - return chksum; -} - -static uint32_t hyundai_compute_checksum(const CANPacket_t *to_push) { - int addr = GET_ADDR(to_push); - - uint8_t chksum = 0; - if (addr == 0x386) { - // count the bits - for (int i = 0; i < 8; i++) { - uint8_t b = GET_BYTE(to_push, i); - for (int j = 0; j < 8; j++) { - uint8_t bit = 0; - // exclude checksum and counter - if (((i != 1) || (j < 6)) && ((i != 3) || (j < 6)) && ((i != 5) || (j < 6)) && ((i != 7) || (j < 6))) { - bit = (b >> (uint8_t)j) & 1U; - } - chksum += bit; - } - } - chksum = (chksum ^ 9U) & 15U; - } else { - // sum of nibbles - for (int i = 0; i < 8; i++) { - if ((addr == 0x394) && (i == 7)) { - continue; // exclude - } - uint8_t b = GET_BYTE(to_push, i); - if (((addr == 0x260) && (i == 7)) || ((addr == 0x394) && (i == 6)) || ((addr == 0x421) && (i == 7))) { - b &= (addr == 0x421) ? 0x0FU : 0xF0U; // remove checksum - } - chksum += (b % 16U) + (b / 16U); - } - chksum = (16U - (chksum % 16U)) % 16U; - } - - return chksum; -} - -static void hyundai_rx_hook(const CANPacket_t *to_push) { - int bus = GET_BUS(to_push); - int addr = GET_ADDR(to_push); - - // SCC12 is on bus 2 for camera-based SCC cars, bus 0 on all others - if ((addr == 0x421) && (((bus == 0) && !hyundai_camera_scc) || ((bus == 2) && hyundai_camera_scc))) { - // 2 bits: 13-14 - int cruise_engaged = (GET_BYTES(to_push, 0, 4) >> 13) & 0x3U; - hyundai_common_cruise_state_check(cruise_engaged); - } - - if (bus == 0) { - if (addr == 0x251) { - int torque_driver_new = (GET_BYTES(to_push, 0, 2) & 0x7ffU) - 1024U; - // update array of samples - update_sample(&torque_driver, torque_driver_new); - } - - // ACC steering wheel buttons - if (addr == 0x4F1) { - int cruise_button = GET_BYTE(to_push, 0) & 0x7U; - bool main_button = GET_BIT(to_push, 3U); - hyundai_common_cruise_buttons_check(cruise_button, main_button); - } - - // gas press, different for EV, hybrid, and ICE models - if ((addr == 0x371) && hyundai_ev_gas_signal) { - gas_pressed = (((GET_BYTE(to_push, 4) & 0x7FU) << 1) | GET_BYTE(to_push, 3) >> 7) != 0U; - } else if ((addr == 0x371) && hyundai_hybrid_gas_signal) { - gas_pressed = GET_BYTE(to_push, 7) != 0U; - } else if ((addr == 0x260) && !hyundai_ev_gas_signal && !hyundai_hybrid_gas_signal) { - gas_pressed = (GET_BYTE(to_push, 7) >> 6) != 0U; - } else { - } - - // sample wheel speed, averaging opposite corners - if (addr == 0x386) { - uint32_t front_left_speed = GET_BYTES(to_push, 0, 2) & 0x3FFFU; - uint32_t rear_right_speed = GET_BYTES(to_push, 6, 2) & 0x3FFFU; - vehicle_moving = (front_left_speed > HYUNDAI_STANDSTILL_THRSLD) || (rear_right_speed > HYUNDAI_STANDSTILL_THRSLD); - } - - if (addr == 0x394) { - brake_pressed = ((GET_BYTE(to_push, 5) >> 5U) & 0x3U) == 0x2U; - } - - bool stock_ecu_detected = (addr == 0x340); - - // If openpilot is controlling longitudinal we need to ensure the radar is turned off - // Enforce by checking we don't see SCC12 - if (hyundai_longitudinal && (addr == 0x421)) { - stock_ecu_detected = true; - } - generic_rx_checks(stock_ecu_detected); - } -} - -static bool hyundai_tx_hook(const CANPacket_t *to_send) { - const SteeringLimits HYUNDAI_STEERING_LIMITS = HYUNDAI_LIMITS(384, 3, 7); - const SteeringLimits HYUNDAI_STEERING_LIMITS_ALT = HYUNDAI_LIMITS(270, 2, 3); - - bool tx = true; - int addr = GET_ADDR(to_send); - - // FCA11: Block any potential actuation - if (addr == 0x38D) { - int CR_VSM_DecCmd = GET_BYTE(to_send, 1); - bool FCA_CmdAct = GET_BIT(to_send, 20U); - bool CF_VSM_DecCmdAct = GET_BIT(to_send, 31U); - - if ((CR_VSM_DecCmd != 0) || FCA_CmdAct || CF_VSM_DecCmdAct) { - tx = false; - } - } - - // ACCEL: safety check - if (addr == 0x421) { - int desired_accel_raw = (((GET_BYTE(to_send, 4) & 0x7U) << 8) | GET_BYTE(to_send, 3)) - 1023U; - int desired_accel_val = ((GET_BYTE(to_send, 5) << 3) | (GET_BYTE(to_send, 4) >> 5)) - 1023U; - - int aeb_decel_cmd = GET_BYTE(to_send, 2); - bool aeb_req = GET_BIT(to_send, 54U); - - bool violation = false; - - violation |= longitudinal_accel_checks(desired_accel_raw, HYUNDAI_LONG_LIMITS); - violation |= longitudinal_accel_checks(desired_accel_val, HYUNDAI_LONG_LIMITS); - violation |= (aeb_decel_cmd != 0); - violation |= aeb_req; - - if (violation) { - tx = false; - } - } - - // LKA STEER: safety check - if (addr == 0x340) { - int desired_torque = ((GET_BYTES(to_send, 0, 4) >> 16) & 0x7ffU) - 1024U; - bool steer_req = GET_BIT(to_send, 27U); - - const SteeringLimits limits = hyundai_alt_limits ? HYUNDAI_STEERING_LIMITS_ALT : HYUNDAI_STEERING_LIMITS; - if (steer_torque_cmd_checks(desired_torque, steer_req, limits)) { - tx = false; - } - } - - // UDS: Only tester present ("\x02\x3E\x80\x00\x00\x00\x00\x00") allowed on diagnostics address - if (addr == 0x7D0) { - if ((GET_BYTES(to_send, 0, 4) != 0x00803E02U) || (GET_BYTES(to_send, 4, 4) != 0x0U)) { - tx = false; - } - } - - // BUTTONS: used for resume spamming and cruise cancellation - if ((addr == 0x4F1) && !hyundai_longitudinal) { - int button = GET_BYTE(to_send, 0) & 0x7U; - - bool allowed_resume = (button == 1) && controls_allowed; - bool allowed_cancel = (button == 4) && cruise_engaged_prev; - if (!(allowed_resume || allowed_cancel)) { - tx = false; - } - } - - return tx; -} - -static int hyundai_fwd_hook(int bus_num, int addr) { - - int bus_fwd = -1; - - // forward cam to ccan and viceversa, except lkas cmd - if (bus_num == 0) { - bus_fwd = 2; - } - - if (bus_num == 2) { - // Stock LKAS11 messages - bool is_lkas_11 = (addr == 0x340); - // LFA and HDA cluster icons - bool is_lfahda_mfc = (addr == 0x485); - - bool block_msg = is_lkas_11 || is_lfahda_mfc; - if (!block_msg) { - bus_fwd = 0; - } - } - - return bus_fwd; -} - -static safety_config hyundai_init(uint16_t param) { - static const CanMsg HYUNDAI_LONG_TX_MSGS[] = { - {0x340, 0, 8}, // LKAS11 Bus 0 - {0x4F1, 0, 4}, // CLU11 Bus 0 - {0x485, 0, 4}, // LFAHDA_MFC Bus 0 - {0x420, 0, 8}, // SCC11 Bus 0 - {0x421, 0, 8}, // SCC12 Bus 0 - {0x50A, 0, 8}, // SCC13 Bus 0 - {0x389, 0, 8}, // SCC14 Bus 0 - {0x4A2, 0, 2}, // FRT_RADAR11 Bus 0 - {0x38D, 0, 8}, // FCA11 Bus 0 - {0x483, 0, 8}, // FCA12 Bus 0 - {0x7D0, 0, 8}, // radar UDS TX addr Bus 0 (for radar disable) - }; - - static const CanMsg HYUNDAI_CAMERA_SCC_TX_MSGS[] = { - {0x340, 0, 8}, // LKAS11 Bus 0 - {0x4F1, 2, 4}, // CLU11 Bus 2 - {0x485, 0, 4}, // LFAHDA_MFC Bus 0 - }; - - hyundai_common_init(param); - hyundai_legacy = false; - - if (hyundai_camera_scc) { - hyundai_longitudinal = false; - } - - safety_config ret; - if (hyundai_longitudinal) { - static RxCheck hyundai_long_rx_checks[] = { - HYUNDAI_COMMON_RX_CHECKS(false) - // Use CLU11 (buttons) to manage controls allowed instead of SCC cruise state - {.msg = {{0x4F1, 0, 4, .check_checksum = false, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, - }; - - ret = BUILD_SAFETY_CFG(hyundai_long_rx_checks, HYUNDAI_LONG_TX_MSGS); - } else if (hyundai_camera_scc) { - static RxCheck hyundai_cam_scc_rx_checks[] = { - HYUNDAI_COMMON_RX_CHECKS(false) - HYUNDAI_SCC12_ADDR_CHECK(2) - }; - - ret = BUILD_SAFETY_CFG(hyundai_cam_scc_rx_checks, HYUNDAI_CAMERA_SCC_TX_MSGS); - } else { - static RxCheck hyundai_rx_checks[] = { - HYUNDAI_COMMON_RX_CHECKS(false) - HYUNDAI_SCC12_ADDR_CHECK(0) - }; - - ret = BUILD_SAFETY_CFG(hyundai_rx_checks, HYUNDAI_TX_MSGS); - } - return ret; -} - -static safety_config hyundai_legacy_init(uint16_t param) { - // older hyundai models have less checks due to missing counters and checksums - static RxCheck hyundai_legacy_rx_checks[] = { - HYUNDAI_COMMON_RX_CHECKS(true) - HYUNDAI_SCC12_ADDR_CHECK(0) - }; - - hyundai_common_init(param); - hyundai_legacy = true; - hyundai_longitudinal = false; - hyundai_camera_scc = false; - return BUILD_SAFETY_CFG(hyundai_legacy_rx_checks, HYUNDAI_TX_MSGS); -} - -const safety_hooks hyundai_hooks = { - .init = hyundai_init, - .rx = hyundai_rx_hook, - .tx = hyundai_tx_hook, - .fwd = hyundai_fwd_hook, - .get_counter = hyundai_get_counter, - .get_checksum = hyundai_get_checksum, - .compute_checksum = hyundai_compute_checksum, -}; - -const safety_hooks hyundai_legacy_hooks = { - .init = hyundai_legacy_init, - .rx = hyundai_rx_hook, - .tx = hyundai_tx_hook, - .fwd = hyundai_fwd_hook, - .get_counter = hyundai_get_counter, - .get_checksum = hyundai_get_checksum, - .compute_checksum = hyundai_compute_checksum, -}; diff --git a/board/safety/safety_hyundai_canfd.h b/board/safety/safety_hyundai_canfd.h deleted file mode 100644 index b42889bb0e..0000000000 --- a/board/safety/safety_hyundai_canfd.h +++ /dev/null @@ -1,363 +0,0 @@ -#pragma once - -#include "safety_declarations.h" -#include "safety_hyundai_common.h" - -// *** Addresses checked in rx hook *** -// EV, ICE, HYBRID: ACCELERATOR (0x35), ACCELERATOR_BRAKE_ALT (0x100), ACCELERATOR_ALT (0x105) -#define HYUNDAI_CANFD_COMMON_RX_CHECKS(pt_bus) \ - {.msg = {{0x35, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, \ - {0x100, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, \ - {0x105, (pt_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}}}, \ - {.msg = {{0x175, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, \ - {.msg = {{0xa0, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, \ - {.msg = {{0xea, (pt_bus), 24, .check_checksum = true, .max_counter = 0xffU, .frequency = 100U}, { 0 }, { 0 }}}, \ - -#define HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(pt_bus) \ - {.msg = {{0x1cf, (pt_bus), 8, .check_checksum = false, .max_counter = 0xfU, .frequency = 50U}, { 0 }, { 0 }}}, \ - -#define HYUNDAI_CANFD_ALT_BUTTONS_ADDR_CHECK(pt_bus) \ - {.msg = {{0x1aa, (pt_bus), 16, .check_checksum = false, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, \ - -// SCC_CONTROL (from ADAS unit or camera) -#define HYUNDAI_CANFD_SCC_ADDR_CHECK(scc_bus) \ - {.msg = {{0x1a0, (scc_bus), 32, .check_checksum = true, .max_counter = 0xffU, .frequency = 50U}, { 0 }, { 0 }}}, \ - -static bool hyundai_canfd_alt_buttons = false; -static bool hyundai_canfd_hda2_alt_steering = false; - -static int hyundai_canfd_hda2_get_lkas_addr(void) { - return hyundai_canfd_hda2_alt_steering ? 0x110 : 0x50; -} - -static uint8_t hyundai_canfd_get_counter(const CANPacket_t *to_push) { - uint8_t ret = 0; - if (GET_LEN(to_push) == 8U) { - ret = GET_BYTE(to_push, 1) >> 4; - } else { - ret = GET_BYTE(to_push, 2); - } - return ret; -} - -static uint32_t hyundai_canfd_get_checksum(const CANPacket_t *to_push) { - uint32_t chksum = GET_BYTE(to_push, 0) | (GET_BYTE(to_push, 1) << 8); - return chksum; -} - -static void hyundai_canfd_rx_hook(const CANPacket_t *to_push) { - int bus = GET_BUS(to_push); - int addr = GET_ADDR(to_push); - - const int pt_bus = hyundai_canfd_hda2 ? 1 : 0; - const int scc_bus = hyundai_camera_scc ? 2 : pt_bus; - - if (bus == pt_bus) { - // driver torque - if (addr == 0xea) { - int torque_driver_new = ((GET_BYTE(to_push, 11) & 0x1fU) << 8U) | GET_BYTE(to_push, 10); - torque_driver_new -= 4095; - update_sample(&torque_driver, torque_driver_new); - } - - // cruise buttons - const int button_addr = hyundai_canfd_alt_buttons ? 0x1aa : 0x1cf; - if (addr == button_addr) { - bool main_button = false; - int cruise_button = 0; - if (addr == 0x1cf) { - cruise_button = GET_BYTE(to_push, 2) & 0x7U; - main_button = GET_BIT(to_push, 19U); - } else { - cruise_button = (GET_BYTE(to_push, 4) >> 4) & 0x7U; - main_button = GET_BIT(to_push, 34U); - } - hyundai_common_cruise_buttons_check(cruise_button, main_button); - } - - // gas press, different for EV, hybrid, and ICE models - if ((addr == 0x35) && hyundai_ev_gas_signal) { - gas_pressed = GET_BYTE(to_push, 5) != 0U; - } else if ((addr == 0x105) && hyundai_hybrid_gas_signal) { - gas_pressed = GET_BIT(to_push, 103U) || (GET_BYTE(to_push, 13) != 0U) || GET_BIT(to_push, 112U); - } else if ((addr == 0x100) && !hyundai_ev_gas_signal && !hyundai_hybrid_gas_signal) { - gas_pressed = GET_BIT(to_push, 176U); - } else { - } - - // brake press - if (addr == 0x175) { - brake_pressed = GET_BIT(to_push, 81U); - } - - // vehicle moving - if (addr == 0xa0) { - uint32_t front_left_speed = GET_BYTES(to_push, 8, 2); - uint32_t rear_right_speed = GET_BYTES(to_push, 14, 2); - vehicle_moving = (front_left_speed > HYUNDAI_STANDSTILL_THRSLD) || (rear_right_speed > HYUNDAI_STANDSTILL_THRSLD); - } - } - - if (bus == scc_bus) { - // cruise state - if ((addr == 0x1a0) && !hyundai_longitudinal) { - // 1=enabled, 2=driver override - int cruise_status = ((GET_BYTE(to_push, 8) >> 4) & 0x7U); - bool cruise_engaged = (cruise_status == 1) || (cruise_status == 2); - hyundai_common_cruise_state_check(cruise_engaged); - } - } - - const int steer_addr = hyundai_canfd_hda2 ? hyundai_canfd_hda2_get_lkas_addr() : 0x12a; - bool stock_ecu_detected = (addr == steer_addr) && (bus == 0); - if (hyundai_longitudinal) { - // on HDA2, ensure ADRV ECU is still knocked out - // on others, ensure accel msg is blocked from camera - const int stock_scc_bus = hyundai_canfd_hda2 ? 1 : 0; - stock_ecu_detected = stock_ecu_detected || ((addr == 0x1a0) && (bus == stock_scc_bus)); - } - generic_rx_checks(stock_ecu_detected); - -} - -static bool hyundai_canfd_tx_hook(const CANPacket_t *to_send) { - const SteeringLimits HYUNDAI_CANFD_STEERING_LIMITS = { - .max_steer = 270, - .max_rt_delta = 112, - .max_rt_interval = 250000, - .max_rate_up = 2, - .max_rate_down = 3, - .driver_torque_allowance = 250, - .driver_torque_factor = 2, - .type = TorqueDriverLimited, - - // the EPS faults when the steering angle is above a certain threshold for too long. to prevent this, - // we allow setting torque actuation bit to 0 while maintaining the requested torque value for two consecutive frames - .min_valid_request_frames = 89, - .max_invalid_request_frames = 2, - .min_valid_request_rt_interval = 810000, // 810ms; a ~10% buffer on cutting every 90 frames - .has_steer_req_tolerance = true, - }; - - bool tx = true; - int addr = GET_ADDR(to_send); - - // steering - const int steer_addr = (hyundai_canfd_hda2 && !hyundai_longitudinal) ? hyundai_canfd_hda2_get_lkas_addr() : 0x12a; - if (addr == steer_addr) { - int desired_torque = (((GET_BYTE(to_send, 6) & 0xFU) << 7U) | (GET_BYTE(to_send, 5) >> 1U)) - 1024U; - bool steer_req = GET_BIT(to_send, 52U); - - if (steer_torque_cmd_checks(desired_torque, steer_req, HYUNDAI_CANFD_STEERING_LIMITS)) { - tx = false; - } - } - - // cruise buttons check - if (addr == 0x1cf) { - int button = GET_BYTE(to_send, 2) & 0x7U; - bool is_cancel = (button == HYUNDAI_BTN_CANCEL); - bool is_resume = (button == HYUNDAI_BTN_RESUME); - - bool allowed = (is_cancel && cruise_engaged_prev) || (is_resume && controls_allowed); - if (!allowed) { - tx = false; - } - } - - // UDS: only tester present ("\x02\x3E\x80\x00\x00\x00\x00\x00") allowed on diagnostics address - if ((addr == 0x730) && hyundai_canfd_hda2) { - if ((GET_BYTES(to_send, 0, 4) != 0x00803E02U) || (GET_BYTES(to_send, 4, 4) != 0x0U)) { - tx = false; - } - } - - // ACCEL: safety check - if (addr == 0x1a0) { - int desired_accel_raw = (((GET_BYTE(to_send, 17) & 0x7U) << 8) | GET_BYTE(to_send, 16)) - 1023U; - int desired_accel_val = ((GET_BYTE(to_send, 18) << 4) | (GET_BYTE(to_send, 17) >> 4)) - 1023U; - - bool violation = false; - - if (hyundai_longitudinal) { - violation |= longitudinal_accel_checks(desired_accel_raw, HYUNDAI_LONG_LIMITS); - violation |= longitudinal_accel_checks(desired_accel_val, HYUNDAI_LONG_LIMITS); - } else { - // only used to cancel on here - if ((desired_accel_raw != 0) || (desired_accel_val != 0)) { - violation = true; - } - } - - if (violation) { - tx = false; - } - } - - return tx; -} - -static int hyundai_canfd_fwd_hook(int bus_num, int addr) { - int bus_fwd = -1; - - if (bus_num == 0) { - bus_fwd = 2; - } - if (bus_num == 2) { - // LKAS for HDA2, LFA for HDA1 - int hda2_lfa_block_addr = hyundai_canfd_hda2_alt_steering ? 0x362 : 0x2a4; - bool is_lkas_msg = ((addr == hyundai_canfd_hda2_get_lkas_addr()) || (addr == hda2_lfa_block_addr)) && hyundai_canfd_hda2; - bool is_lfa_msg = ((addr == 0x12a) && !hyundai_canfd_hda2); - - // HUD icons - bool is_lfahda_msg = ((addr == 0x1e0) && !hyundai_canfd_hda2); - - // CRUISE_INFO for non-HDA2, we send our own longitudinal commands - bool is_scc_msg = ((addr == 0x1a0) && hyundai_longitudinal && !hyundai_canfd_hda2); - - bool block_msg = is_lkas_msg || is_lfa_msg || is_lfahda_msg || is_scc_msg; - if (!block_msg) { - bus_fwd = 0; - } - } - - return bus_fwd; -} - -static safety_config hyundai_canfd_init(uint16_t param) { - const int HYUNDAI_PARAM_CANFD_HDA2_ALT_STEERING = 128; - const int HYUNDAI_PARAM_CANFD_ALT_BUTTONS = 32; - - static const CanMsg HYUNDAI_CANFD_HDA2_TX_MSGS[] = { - {0x50, 0, 16}, // LKAS - {0x1CF, 1, 8}, // CRUISE_BUTTON - {0x2A4, 0, 24}, // CAM_0x2A4 - }; - - static const CanMsg HYUNDAI_CANFD_HDA2_ALT_STEERING_TX_MSGS[] = { - {0x110, 0, 32}, // LKAS_ALT - {0x1CF, 1, 8}, // CRUISE_BUTTON - {0x362, 0, 32}, // CAM_0x362 - }; - - static const CanMsg HYUNDAI_CANFD_HDA2_LONG_TX_MSGS[] = { - {0x50, 0, 16}, // LKAS - {0x1CF, 1, 8}, // CRUISE_BUTTON - {0x2A4, 0, 24}, // CAM_0x2A4 - {0x51, 0, 32}, // ADRV_0x51 - {0x730, 1, 8}, // tester present for ADAS ECU disable - {0x12A, 1, 16}, // LFA - {0x160, 1, 16}, // ADRV_0x160 - {0x1E0, 1, 16}, // LFAHDA_CLUSTER - {0x1A0, 1, 32}, // CRUISE_INFO - {0x1EA, 1, 32}, // ADRV_0x1ea - {0x200, 1, 8}, // ADRV_0x200 - {0x345, 1, 8}, // ADRV_0x345 - {0x1DA, 1, 32}, // ADRV_0x1da - }; - - static const CanMsg HYUNDAI_CANFD_HDA1_TX_MSGS[] = { - {0x12A, 0, 16}, // LFA - {0x1A0, 0, 32}, // CRUISE_INFO - {0x1CF, 2, 8}, // CRUISE_BUTTON - {0x1E0, 0, 16}, // LFAHDA_CLUSTER - }; - - - hyundai_common_init(param); - - gen_crc_lookup_table_16(0x1021, hyundai_canfd_crc_lut); - hyundai_canfd_alt_buttons = GET_FLAG(param, HYUNDAI_PARAM_CANFD_ALT_BUTTONS); - hyundai_canfd_hda2_alt_steering = GET_FLAG(param, HYUNDAI_PARAM_CANFD_HDA2_ALT_STEERING); - - // no long for radar-SCC HDA1 yet - if (!hyundai_canfd_hda2 && !hyundai_camera_scc) { - hyundai_longitudinal = false; - } - - safety_config ret; - if (hyundai_longitudinal) { - if (hyundai_canfd_hda2) { - static RxCheck hyundai_canfd_hda2_long_rx_checks[] = { - HYUNDAI_CANFD_COMMON_RX_CHECKS(1) - HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(1) - }; - - ret = BUILD_SAFETY_CFG(hyundai_canfd_hda2_long_rx_checks, HYUNDAI_CANFD_HDA2_LONG_TX_MSGS); - } else { - static RxCheck hyundai_canfd_long_alt_buttons_rx_checks[] = { - HYUNDAI_CANFD_COMMON_RX_CHECKS(0) - HYUNDAI_CANFD_ALT_BUTTONS_ADDR_CHECK(0) - }; - - // Longitudinal checks for HDA1 - static RxCheck hyundai_canfd_long_rx_checks[] = { - HYUNDAI_CANFD_COMMON_RX_CHECKS(0) - HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(0) - }; - - ret = hyundai_canfd_alt_buttons ? BUILD_SAFETY_CFG(hyundai_canfd_long_alt_buttons_rx_checks, HYUNDAI_CANFD_HDA1_TX_MSGS) : \ - BUILD_SAFETY_CFG(hyundai_canfd_long_rx_checks, HYUNDAI_CANFD_HDA1_TX_MSGS); - } - } else { - if (hyundai_canfd_hda2) { - // *** HDA2 checks *** - // E-CAN is on bus 1, ADAS unit sends SCC messages on HDA2. - // Does not use the alt buttons message - static RxCheck hyundai_canfd_hda2_rx_checks[] = { - HYUNDAI_CANFD_COMMON_RX_CHECKS(1) - HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(1) - HYUNDAI_CANFD_SCC_ADDR_CHECK(1) - }; - - ret = hyundai_canfd_hda2_alt_steering ? BUILD_SAFETY_CFG(hyundai_canfd_hda2_rx_checks, HYUNDAI_CANFD_HDA2_ALT_STEERING_TX_MSGS) : \ - BUILD_SAFETY_CFG(hyundai_canfd_hda2_rx_checks, HYUNDAI_CANFD_HDA2_TX_MSGS); - } else if (!hyundai_camera_scc) { - static RxCheck hyundai_canfd_radar_scc_alt_buttons_rx_checks[] = { - HYUNDAI_CANFD_COMMON_RX_CHECKS(0) - HYUNDAI_CANFD_ALT_BUTTONS_ADDR_CHECK(0) - HYUNDAI_CANFD_SCC_ADDR_CHECK(0) - }; - - // Radar sends SCC messages on these cars instead of camera - static RxCheck hyundai_canfd_radar_scc_rx_checks[] = { - HYUNDAI_CANFD_COMMON_RX_CHECKS(0) - HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(0) - HYUNDAI_CANFD_SCC_ADDR_CHECK(0) - }; - - ret = hyundai_canfd_alt_buttons ? BUILD_SAFETY_CFG(hyundai_canfd_radar_scc_alt_buttons_rx_checks, HYUNDAI_CANFD_HDA1_TX_MSGS) : \ - BUILD_SAFETY_CFG(hyundai_canfd_radar_scc_rx_checks, HYUNDAI_CANFD_HDA1_TX_MSGS); - } else { - // *** Non-HDA2 checks *** - static RxCheck hyundai_canfd_alt_buttons_rx_checks[] = { - HYUNDAI_CANFD_COMMON_RX_CHECKS(0) - HYUNDAI_CANFD_ALT_BUTTONS_ADDR_CHECK(0) - HYUNDAI_CANFD_SCC_ADDR_CHECK(2) - }; - - // Camera sends SCC messages on HDA1. - // Both button messages exist on some platforms, so we ensure we track the correct one using flag - static RxCheck hyundai_canfd_rx_checks[] = { - HYUNDAI_CANFD_COMMON_RX_CHECKS(0) - HYUNDAI_CANFD_BUTTONS_ADDR_CHECK(0) - HYUNDAI_CANFD_SCC_ADDR_CHECK(2) - }; - - ret = hyundai_canfd_alt_buttons ? BUILD_SAFETY_CFG(hyundai_canfd_alt_buttons_rx_checks, HYUNDAI_CANFD_HDA1_TX_MSGS) : \ - BUILD_SAFETY_CFG(hyundai_canfd_rx_checks, HYUNDAI_CANFD_HDA1_TX_MSGS); - } - } - - return ret; -} - -const safety_hooks hyundai_canfd_hooks = { - .init = hyundai_canfd_init, - .rx = hyundai_canfd_rx_hook, - .tx = hyundai_canfd_tx_hook, - .fwd = hyundai_canfd_fwd_hook, - .get_counter = hyundai_canfd_get_counter, - .get_checksum = hyundai_canfd_get_checksum, - .compute_checksum = hyundai_common_canfd_compute_checksum, -}; diff --git a/board/safety/safety_hyundai_common.h b/board/safety/safety_hyundai_common.h deleted file mode 100644 index d83b396401..0000000000 --- a/board/safety/safety_hyundai_common.h +++ /dev/null @@ -1,128 +0,0 @@ -#pragma once - -#include "safety_declarations.h" - -extern uint16_t hyundai_canfd_crc_lut[256]; -uint16_t hyundai_canfd_crc_lut[256]; - -static const uint8_t HYUNDAI_PREV_BUTTON_SAMPLES = 8; // roughly 160 ms - // -extern const uint32_t HYUNDAI_STANDSTILL_THRSLD; -const uint32_t HYUNDAI_STANDSTILL_THRSLD = 12; // 0.375 kph - -enum { - HYUNDAI_BTN_NONE = 0, - HYUNDAI_BTN_RESUME = 1, - HYUNDAI_BTN_SET = 2, - HYUNDAI_BTN_CANCEL = 4, -}; - -// common state -extern bool hyundai_ev_gas_signal; -bool hyundai_ev_gas_signal = false; - -extern bool hyundai_hybrid_gas_signal; -bool hyundai_hybrid_gas_signal = false; - -extern bool hyundai_longitudinal; -bool hyundai_longitudinal = false; - -extern bool hyundai_camera_scc; -bool hyundai_camera_scc = false; - -extern bool hyundai_canfd_hda2; -bool hyundai_canfd_hda2 = false; - -extern bool hyundai_alt_limits; -bool hyundai_alt_limits = false; - -static uint8_t hyundai_last_button_interaction; // button messages since the user pressed an enable button - -void hyundai_common_init(uint16_t param) { - const int HYUNDAI_PARAM_EV_GAS = 1; - const int HYUNDAI_PARAM_HYBRID_GAS = 2; - const int HYUNDAI_PARAM_CAMERA_SCC = 8; - const int HYUNDAI_PARAM_CANFD_HDA2 = 16; - const int HYUNDAI_PARAM_ALT_LIMITS = 64; // TODO: shift this down with the rest of the common flags - - hyundai_ev_gas_signal = GET_FLAG(param, HYUNDAI_PARAM_EV_GAS); - hyundai_hybrid_gas_signal = !hyundai_ev_gas_signal && GET_FLAG(param, HYUNDAI_PARAM_HYBRID_GAS); - hyundai_camera_scc = GET_FLAG(param, HYUNDAI_PARAM_CAMERA_SCC); - hyundai_canfd_hda2 = GET_FLAG(param, HYUNDAI_PARAM_CANFD_HDA2); - hyundai_alt_limits = GET_FLAG(param, HYUNDAI_PARAM_ALT_LIMITS); - - hyundai_last_button_interaction = HYUNDAI_PREV_BUTTON_SAMPLES; - -#ifdef ALLOW_DEBUG - const int HYUNDAI_PARAM_LONGITUDINAL = 4; - hyundai_longitudinal = GET_FLAG(param, HYUNDAI_PARAM_LONGITUDINAL); -#else - hyundai_longitudinal = false; -#endif -} - -void hyundai_common_cruise_state_check(const bool cruise_engaged) { - // some newer HKG models can re-enable after spamming cancel button, - // so keep track of user button presses to deny engagement if no interaction - - // enter controls on rising edge of ACC and recent user button press, exit controls when ACC off - if (!hyundai_longitudinal) { - if (cruise_engaged && !cruise_engaged_prev && (hyundai_last_button_interaction < HYUNDAI_PREV_BUTTON_SAMPLES)) { - controls_allowed = true; - } - - if (!cruise_engaged) { - controls_allowed = false; - } - cruise_engaged_prev = cruise_engaged; - } -} - -void hyundai_common_cruise_buttons_check(const int cruise_button, const bool main_button) { - if ((cruise_button == HYUNDAI_BTN_RESUME) || (cruise_button == HYUNDAI_BTN_SET) || (cruise_button == HYUNDAI_BTN_CANCEL) || main_button) { - hyundai_last_button_interaction = 0U; - } else { - hyundai_last_button_interaction = MIN(hyundai_last_button_interaction + 1U, HYUNDAI_PREV_BUTTON_SAMPLES); - } - - if (hyundai_longitudinal) { - // enter controls on falling edge of resume or set - bool set = (cruise_button != HYUNDAI_BTN_SET) && (cruise_button_prev == HYUNDAI_BTN_SET); - bool res = (cruise_button != HYUNDAI_BTN_RESUME) && (cruise_button_prev == HYUNDAI_BTN_RESUME); - if (set || res) { - controls_allowed = true; - } - - // exit controls on cancel press - if (cruise_button == HYUNDAI_BTN_CANCEL) { - controls_allowed = false; - } - - cruise_button_prev = cruise_button; - } -} - -uint32_t hyundai_common_canfd_compute_checksum(const CANPacket_t *to_push) { - int len = GET_LEN(to_push); - uint32_t address = GET_ADDR(to_push); - - uint16_t crc = 0; - - for (int i = 2; i < len; i++) { - crc = (crc << 8U) ^ hyundai_canfd_crc_lut[(crc >> 8U) ^ GET_BYTE(to_push, i)]; - } - - // Add address to crc - crc = (crc << 8U) ^ hyundai_canfd_crc_lut[(crc >> 8U) ^ ((address >> 0U) & 0xFFU)]; - crc = (crc << 8U) ^ hyundai_canfd_crc_lut[(crc >> 8U) ^ ((address >> 8U) & 0xFFU)]; - - if (len == 24) { - crc ^= 0x819dU; - } else if (len == 32) { - crc ^= 0x9f5bU; - } else { - - } - - return crc; -} diff --git a/board/safety/safety_mazda.h b/board/safety/safety_mazda.h deleted file mode 100644 index ed87451f77..0000000000 --- a/board/safety/safety_mazda.h +++ /dev/null @@ -1,131 +0,0 @@ -#pragma once - -#include "safety_declarations.h" - -// CAN msgs we care about -#define MAZDA_LKAS 0x243 -#define MAZDA_LKAS_HUD 0x440 -#define MAZDA_CRZ_CTRL 0x21c -#define MAZDA_CRZ_BTNS 0x09d -#define MAZDA_STEER_TORQUE 0x240 -#define MAZDA_ENGINE_DATA 0x202 -#define MAZDA_PEDALS 0x165 - -// CAN bus numbers -#define MAZDA_MAIN 0 -#define MAZDA_CAM 2 - -// track msgs coming from OP so that we know what CAM msgs to drop and what to forward -static void mazda_rx_hook(const CANPacket_t *to_push) { - if ((int)GET_BUS(to_push) == MAZDA_MAIN) { - int addr = GET_ADDR(to_push); - - if (addr == MAZDA_ENGINE_DATA) { - // sample speed: scale by 0.01 to get kph - int speed = (GET_BYTE(to_push, 2) << 8) | GET_BYTE(to_push, 3); - vehicle_moving = speed > 10; // moving when speed > 0.1 kph - } - - if (addr == MAZDA_STEER_TORQUE) { - int torque_driver_new = GET_BYTE(to_push, 0) - 127U; - // update array of samples - update_sample(&torque_driver, torque_driver_new); - } - - // enter controls on rising edge of ACC, exit controls on ACC off - if (addr == MAZDA_CRZ_CTRL) { - bool cruise_engaged = GET_BYTE(to_push, 0) & 0x8U; - pcm_cruise_check(cruise_engaged); - } - - if (addr == MAZDA_ENGINE_DATA) { - gas_pressed = (GET_BYTE(to_push, 4) || (GET_BYTE(to_push, 5) & 0xF0U)); - } - - if (addr == MAZDA_PEDALS) { - brake_pressed = (GET_BYTE(to_push, 0) & 0x10U); - } - - generic_rx_checks((addr == MAZDA_LKAS)); - } -} - -static bool mazda_tx_hook(const CANPacket_t *to_send) { - const SteeringLimits MAZDA_STEERING_LIMITS = { - .max_steer = 800, - .max_rate_up = 10, - .max_rate_down = 25, - .max_rt_delta = 300, - .max_rt_interval = 250000, - .driver_torque_factor = 1, - .driver_torque_allowance = 15, - .type = TorqueDriverLimited, - }; - - bool tx = true; - int bus = GET_BUS(to_send); - // Check if msg is sent on the main BUS - if (bus == MAZDA_MAIN) { - int addr = GET_ADDR(to_send); - - // steer cmd checks - if (addr == MAZDA_LKAS) { - int desired_torque = (((GET_BYTE(to_send, 0) & 0x0FU) << 8) | GET_BYTE(to_send, 1)) - 2048U; - - if (steer_torque_cmd_checks(desired_torque, -1, MAZDA_STEERING_LIMITS)) { - tx = false; - } - } - - // cruise buttons check - if (addr == MAZDA_CRZ_BTNS) { - // allow resume spamming while controls allowed, but - // only allow cancel while contrls not allowed - bool cancel_cmd = (GET_BYTE(to_send, 0) == 0x1U); - if (!controls_allowed && !cancel_cmd) { - tx = false; - } - } - } - - return tx; -} - -static int mazda_fwd_hook(int bus, int addr) { - int bus_fwd = -1; - - if (bus == MAZDA_MAIN) { - bus_fwd = MAZDA_CAM; - } else if (bus == MAZDA_CAM) { - bool block = (addr == MAZDA_LKAS) || (addr == MAZDA_LKAS_HUD); - if (!block) { - bus_fwd = MAZDA_MAIN; - } - } else { - // don't fwd - } - - return bus_fwd; -} - -static safety_config mazda_init(uint16_t param) { - static const CanMsg MAZDA_TX_MSGS[] = {{MAZDA_LKAS, 0, 8}, {MAZDA_CRZ_BTNS, 0, 8}, {MAZDA_LKAS_HUD, 0, 8}}; - - static RxCheck mazda_rx_checks[] = { - {.msg = {{MAZDA_CRZ_CTRL, 0, 8, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{MAZDA_CRZ_BTNS, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, - {.msg = {{MAZDA_STEER_TORQUE, 0, 8, .frequency = 83U}, { 0 }, { 0 }}}, - {.msg = {{MAZDA_ENGINE_DATA, 0, 8, .frequency = 100U}, { 0 }, { 0 }}}, - {.msg = {{MAZDA_PEDALS, 0, 8, .frequency = 50U}, { 0 }, { 0 }}}, - }; - - UNUSED(param); - return BUILD_SAFETY_CFG(mazda_rx_checks, MAZDA_TX_MSGS); -} - -const safety_hooks mazda_hooks = { - .init = mazda_init, - .rx = mazda_rx_hook, - .tx = mazda_tx_hook, - .fwd = mazda_fwd_hook, -}; diff --git a/board/safety/safety_nissan.h b/board/safety/safety_nissan.h deleted file mode 100644 index fd47e09448..0000000000 --- a/board/safety/safety_nissan.h +++ /dev/null @@ -1,163 +0,0 @@ -#pragma once - -#include "safety_declarations.h" - -static bool nissan_alt_eps = false; - -static void nissan_rx_hook(const CANPacket_t *to_push) { - int bus = GET_BUS(to_push); - int addr = GET_ADDR(to_push); - - if (bus == (nissan_alt_eps ? 1 : 0)) { - if (addr == 0x2) { - // Current steering angle - // Factor -0.1, little endian - int angle_meas_new = (GET_BYTES(to_push, 0, 4) & 0xFFFFU); - // Multiply by -10 to match scale of LKAS angle - angle_meas_new = to_signed(angle_meas_new, 16) * -10; - - // update array of samples - update_sample(&angle_meas, angle_meas_new); - } - - if (addr == 0x285) { - // Get current speed and standstill - uint16_t right_rear = (GET_BYTE(to_push, 0) << 8) | (GET_BYTE(to_push, 1)); - uint16_t left_rear = (GET_BYTE(to_push, 2) << 8) | (GET_BYTE(to_push, 3)); - vehicle_moving = (right_rear | left_rear) != 0U; - UPDATE_VEHICLE_SPEED((right_rear + left_rear) / 2.0 * 0.005 / 3.6); - } - - // X-Trail 0x15c, Leaf 0x239 - if ((addr == 0x15c) || (addr == 0x239)) { - if (addr == 0x15c){ - gas_pressed = ((GET_BYTE(to_push, 5) << 2) | ((GET_BYTE(to_push, 6) >> 6) & 0x3U)) > 3U; - } else { - gas_pressed = GET_BYTE(to_push, 0) > 3U; - } - } - - // X-trail 0x454, Leaf 0x239 - if ((addr == 0x454) || (addr == 0x239)) { - if (addr == 0x454){ - brake_pressed = (GET_BYTE(to_push, 2) & 0x80U) != 0U; - } else { - brake_pressed = ((GET_BYTE(to_push, 4) >> 5) & 1U) != 0U; - } - } - } - - // Handle cruise enabled - if ((addr == 0x30f) && (bus == (nissan_alt_eps ? 1 : 2))) { - bool cruise_engaged = (GET_BYTE(to_push, 0) >> 3) & 1U; - pcm_cruise_check(cruise_engaged); - } - - generic_rx_checks((addr == 0x169) && (bus == 0)); -} - - -static bool nissan_tx_hook(const CANPacket_t *to_send) { - const SteeringLimits NISSAN_STEERING_LIMITS = { - .angle_deg_to_can = 100, - .angle_rate_up_lookup = { - {0., 5., 15.}, - {5., .8, .15} - }, - .angle_rate_down_lookup = { - {0., 5., 15.}, - {5., 3.5, .4} - }, - }; - - bool tx = true; - int addr = GET_ADDR(to_send); - bool violation = false; - - // steer cmd checks - if (addr == 0x169) { - int desired_angle = ((GET_BYTE(to_send, 0) << 10) | (GET_BYTE(to_send, 1) << 2) | ((GET_BYTE(to_send, 2) >> 6) & 0x3U)); - bool lka_active = (GET_BYTE(to_send, 6) >> 4) & 1U; - - // Factor is -0.01, offset is 1310. Flip to correct sign, but keep units in CAN scale - desired_angle = -desired_angle + (1310.0f * NISSAN_STEERING_LIMITS.angle_deg_to_can); - - if (steer_angle_cmd_checks(desired_angle, lka_active, NISSAN_STEERING_LIMITS)) { - violation = true; - } - } - - // acc button check, only allow cancel button to be sent - if (addr == 0x20b) { - // Violation of any button other than cancel is pressed - violation |= ((GET_BYTE(to_send, 1) & 0x3dU) > 0U); - } - - if (violation) { - tx = false; - } - - return tx; -} - - -static int nissan_fwd_hook(int bus_num, int addr) { - int bus_fwd = -1; - - if (bus_num == 0) { - bool block_msg = (addr == 0x280); // CANCEL_MSG - if (!block_msg) { - bus_fwd = 2; // ADAS - } - } - - if (bus_num == 2) { - // 0x169 is LKAS, 0x2b1 LKAS_HUD, 0x4cc LKAS_HUD_INFO_MSG - bool block_msg = ((addr == 0x169) || (addr == 0x2b1) || (addr == 0x4cc)); - if (!block_msg) { - bus_fwd = 0; // V-CAN - } - } - - return bus_fwd; -} - -static safety_config nissan_init(uint16_t param) { - static const CanMsg NISSAN_TX_MSGS[] = { - {0x169, 0, 8}, // LKAS - {0x2b1, 0, 8}, // PROPILOT_HUD - {0x4cc, 0, 8}, // PROPILOT_HUD_INFO_MSG - {0x20b, 2, 6}, // CRUISE_THROTTLE (X-Trail) - {0x20b, 1, 6}, // CRUISE_THROTTLE (Altima) - {0x280, 2, 8} // CANCEL_MSG (Leaf) - }; - - // Signals duplicated below due to the fact that these messages can come in on either CAN bus, depending on car model. - static RxCheck nissan_rx_checks[] = { - {.msg = {{0x2, 0, 5, .frequency = 100U}, - {0x2, 1, 5, .frequency = 100U}, { 0 }}}, // STEER_ANGLE_SENSOR - {.msg = {{0x285, 0, 8, .frequency = 50U}, - {0x285, 1, 8, .frequency = 50U}, { 0 }}}, // WHEEL_SPEEDS_REAR - {.msg = {{0x30f, 2, 3, .frequency = 10U}, - {0x30f, 1, 3, .frequency = 10U}, { 0 }}}, // CRUISE_STATE - {.msg = {{0x15c, 0, 8, .frequency = 50U}, - {0x15c, 1, 8, .frequency = 50U}, - {0x239, 0, 8, .frequency = 50U}}}, // GAS_PEDAL - {.msg = {{0x454, 0, 8, .frequency = 10U}, - {0x454, 1, 8, .frequency = 10U}, - {0x1cc, 0, 4, .frequency = 100U}}}, // DOORS_LIGHTS / BRAKE - }; - - // EPS Location. false = V-CAN, true = C-CAN - const int NISSAN_PARAM_ALT_EPS_BUS = 1; - - nissan_alt_eps = GET_FLAG(param, NISSAN_PARAM_ALT_EPS_BUS); - return BUILD_SAFETY_CFG(nissan_rx_checks, NISSAN_TX_MSGS); -} - -const safety_hooks nissan_hooks = { - .init = nissan_init, - .rx = nissan_rx_hook, - .tx = nissan_tx_hook, - .fwd = nissan_fwd_hook, -}; diff --git a/board/safety/safety_subaru.h b/board/safety/safety_subaru.h deleted file mode 100644 index 290150657e..0000000000 --- a/board/safety/safety_subaru.h +++ /dev/null @@ -1,293 +0,0 @@ -#pragma once - -#include "safety_declarations.h" - -#define SUBARU_STEERING_LIMITS_GENERATOR(steer_max, rate_up, rate_down) \ - { \ - .max_steer = (steer_max), \ - .max_rt_delta = 940, \ - .max_rt_interval = 250000, \ - .max_rate_up = (rate_up), \ - .max_rate_down = (rate_down), \ - .driver_torque_factor = 50, \ - .driver_torque_allowance = 60, \ - .type = TorqueDriverLimited, \ - /* the EPS will temporary fault if the steering rate is too high, so we cut the \ - the steering torque every 7 frames for 1 frame if the steering rate is high */ \ - .min_valid_request_frames = 7, \ - .max_invalid_request_frames = 1, \ - .min_valid_request_rt_interval = 144000, /* 10% tolerance */ \ - .has_steer_req_tolerance = true, \ - } - -#define MSG_SUBARU_Brake_Status 0x13c -#define MSG_SUBARU_CruiseControl 0x240 -#define MSG_SUBARU_Throttle 0x40 -#define MSG_SUBARU_Steering_Torque 0x119 -#define MSG_SUBARU_Wheel_Speeds 0x13a - -#define MSG_SUBARU_ES_LKAS 0x122 -#define MSG_SUBARU_ES_Brake 0x220 -#define MSG_SUBARU_ES_Distance 0x221 -#define MSG_SUBARU_ES_Status 0x222 -#define MSG_SUBARU_ES_DashStatus 0x321 -#define MSG_SUBARU_ES_LKAS_State 0x322 -#define MSG_SUBARU_ES_Infotainment 0x323 - -#define MSG_SUBARU_ES_UDS_Request 0x787 - -#define MSG_SUBARU_ES_HighBeamAssist 0x121 -#define MSG_SUBARU_ES_STATIC_1 0x22a -#define MSG_SUBARU_ES_STATIC_2 0x325 - -#define SUBARU_MAIN_BUS 0 -#define SUBARU_ALT_BUS 1 -#define SUBARU_CAM_BUS 2 - -#define SUBARU_COMMON_TX_MSGS(alt_bus, lkas_msg) \ - {lkas_msg, SUBARU_MAIN_BUS, 8}, \ - {MSG_SUBARU_ES_Distance, alt_bus, 8}, \ - {MSG_SUBARU_ES_DashStatus, SUBARU_MAIN_BUS, 8}, \ - {MSG_SUBARU_ES_LKAS_State, SUBARU_MAIN_BUS, 8}, \ - {MSG_SUBARU_ES_Infotainment, SUBARU_MAIN_BUS, 8}, \ - -#define SUBARU_COMMON_LONG_TX_MSGS(alt_bus) \ - {MSG_SUBARU_ES_Brake, alt_bus, 8}, \ - {MSG_SUBARU_ES_Status, alt_bus, 8}, \ - -#define SUBARU_GEN2_LONG_ADDITIONAL_TX_MSGS() \ - {MSG_SUBARU_ES_UDS_Request, SUBARU_CAM_BUS, 8}, \ - {MSG_SUBARU_ES_HighBeamAssist, SUBARU_MAIN_BUS, 8}, \ - {MSG_SUBARU_ES_STATIC_1, SUBARU_MAIN_BUS, 8}, \ - {MSG_SUBARU_ES_STATIC_2, SUBARU_MAIN_BUS, 8}, \ - -#define SUBARU_COMMON_RX_CHECKS(alt_bus) \ - {.msg = {{MSG_SUBARU_Throttle, SUBARU_MAIN_BUS, 8, .check_checksum = true, .max_counter = 15U, .frequency = 100U}, { 0 }, { 0 }}}, \ - {.msg = {{MSG_SUBARU_Steering_Torque, SUBARU_MAIN_BUS, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, \ - {.msg = {{MSG_SUBARU_Wheel_Speeds, alt_bus, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, \ - {.msg = {{MSG_SUBARU_Brake_Status, alt_bus, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, \ - {.msg = {{MSG_SUBARU_CruiseControl, alt_bus, 8, .check_checksum = true, .max_counter = 15U, .frequency = 20U}, { 0 }, { 0 }}}, \ - -static bool subaru_gen2 = false; -static bool subaru_longitudinal = false; - -static uint32_t subaru_get_checksum(const CANPacket_t *to_push) { - return (uint8_t)GET_BYTE(to_push, 0); -} - -static uint8_t subaru_get_counter(const CANPacket_t *to_push) { - return (uint8_t)(GET_BYTE(to_push, 1) & 0xFU); -} - -static uint32_t subaru_compute_checksum(const CANPacket_t *to_push) { - int addr = GET_ADDR(to_push); - int len = GET_LEN(to_push); - uint8_t checksum = (uint8_t)(addr) + (uint8_t)((unsigned int)(addr) >> 8U); - for (int i = 1; i < len; i++) { - checksum += (uint8_t)GET_BYTE(to_push, i); - } - return checksum; -} - -static void subaru_rx_hook(const CANPacket_t *to_push) { - const int bus = GET_BUS(to_push); - const int alt_main_bus = subaru_gen2 ? SUBARU_ALT_BUS : SUBARU_MAIN_BUS; - - int addr = GET_ADDR(to_push); - if ((addr == MSG_SUBARU_Steering_Torque) && (bus == SUBARU_MAIN_BUS)) { - int torque_driver_new; - torque_driver_new = ((GET_BYTES(to_push, 0, 4) >> 16) & 0x7FFU); - torque_driver_new = -1 * to_signed(torque_driver_new, 11); - update_sample(&torque_driver, torque_driver_new); - - int angle_meas_new = (GET_BYTES(to_push, 4, 2) & 0xFFFFU); - // convert Steering_Torque -> Steering_Angle to centidegrees, to match the ES_LKAS_ANGLE angle request units - angle_meas_new = ROUND(to_signed(angle_meas_new, 16) * -2.17); - update_sample(&angle_meas, angle_meas_new); - } - - // enter controls on rising edge of ACC, exit controls on ACC off - if ((addr == MSG_SUBARU_CruiseControl) && (bus == alt_main_bus)) { - bool cruise_engaged = GET_BIT(to_push, 41U); - pcm_cruise_check(cruise_engaged); - } - - // update vehicle moving with any non-zero wheel speed - if ((addr == MSG_SUBARU_Wheel_Speeds) && (bus == alt_main_bus)) { - uint32_t fr = (GET_BYTES(to_push, 1, 3) >> 4) & 0x1FFFU; - uint32_t rr = (GET_BYTES(to_push, 3, 3) >> 1) & 0x1FFFU; - uint32_t rl = (GET_BYTES(to_push, 4, 3) >> 6) & 0x1FFFU; - uint32_t fl = (GET_BYTES(to_push, 6, 2) >> 3) & 0x1FFFU; - - vehicle_moving = (fr > 0U) || (rr > 0U) || (rl > 0U) || (fl > 0U); - - UPDATE_VEHICLE_SPEED((fr + rr + rl + fl) / 4U * 0.057); - } - - if ((addr == MSG_SUBARU_Brake_Status) && (bus == alt_main_bus)) { - brake_pressed = GET_BIT(to_push, 62U); - } - - if ((addr == MSG_SUBARU_Throttle) && (bus == SUBARU_MAIN_BUS)) { - gas_pressed = GET_BYTE(to_push, 4) != 0U; - } - - generic_rx_checks((addr == MSG_SUBARU_ES_LKAS) && (bus == SUBARU_MAIN_BUS)); -} - -static bool subaru_tx_hook(const CANPacket_t *to_send) { - const SteeringLimits SUBARU_STEERING_LIMITS = SUBARU_STEERING_LIMITS_GENERATOR(2047, 50, 70); - const SteeringLimits SUBARU_GEN2_STEERING_LIMITS = SUBARU_STEERING_LIMITS_GENERATOR(1000, 40, 40); - - const LongitudinalLimits SUBARU_LONG_LIMITS = { - .min_gas = 808, // appears to be engine braking - .max_gas = 3400, // approx 2 m/s^2 when maxing cruise_rpm and cruise_throttle - .inactive_gas = 1818, // this is zero acceleration - .max_brake = 600, // approx -3.5 m/s^2 - - .min_transmission_rpm = 0, - .max_transmission_rpm = 3600, - }; - - bool tx = true; - int addr = GET_ADDR(to_send); - bool violation = false; - - // steer cmd checks - if (addr == MSG_SUBARU_ES_LKAS) { - int desired_torque = ((GET_BYTES(to_send, 0, 4) >> 16) & 0x1FFFU); - desired_torque = -1 * to_signed(desired_torque, 13); - - bool steer_req = GET_BIT(to_send, 29U); - - const SteeringLimits limits = subaru_gen2 ? SUBARU_GEN2_STEERING_LIMITS : SUBARU_STEERING_LIMITS; - violation |= steer_torque_cmd_checks(desired_torque, steer_req, limits); - } - - // check es_brake brake_pressure limits - if (addr == MSG_SUBARU_ES_Brake) { - int es_brake_pressure = GET_BYTES(to_send, 2, 2); - violation |= longitudinal_brake_checks(es_brake_pressure, SUBARU_LONG_LIMITS); - } - - // check es_distance cruise_throttle limits - if (addr == MSG_SUBARU_ES_Distance) { - int cruise_throttle = (GET_BYTES(to_send, 2, 2) & 0x1FFFU); - bool cruise_cancel = GET_BIT(to_send, 56U); - - if (subaru_longitudinal) { - violation |= longitudinal_gas_checks(cruise_throttle, SUBARU_LONG_LIMITS); - } else { - // If openpilot is not controlling long, only allow ES_Distance for cruise cancel requests, - // (when Cruise_Cancel is true, and Cruise_Throttle is inactive) - violation |= (cruise_throttle != SUBARU_LONG_LIMITS.inactive_gas); - violation |= (!cruise_cancel); - } - } - - // check es_status transmission_rpm limits - if (addr == MSG_SUBARU_ES_Status) { - int transmission_rpm = (GET_BYTES(to_send, 2, 2) & 0x1FFFU); - violation |= longitudinal_transmission_rpm_checks(transmission_rpm, SUBARU_LONG_LIMITS); - } - - if (addr == MSG_SUBARU_ES_UDS_Request) { - // tester present ('\x02\x3E\x80\x00\x00\x00\x00\x00') is allowed for gen2 longitudinal to keep eyesight disabled - bool is_tester_present = (GET_BYTES(to_send, 0, 4) == 0x00803E02U) && (GET_BYTES(to_send, 4, 4) == 0x0U); - - // reading ES button data by identifier (b'\x03\x22\x11\x30\x00\x00\x00\x00') is also allowed (DID 0x1130) - bool is_button_rdbi = (GET_BYTES(to_send, 0, 4) == 0x30112203U) && (GET_BYTES(to_send, 4, 4) == 0x0U); - - violation |= !(is_tester_present || is_button_rdbi); - } - - if (violation){ - tx = false; - } - return tx; -} - -static int subaru_fwd_hook(int bus_num, int addr) { - int bus_fwd = -1; - - if (bus_num == SUBARU_MAIN_BUS) { - bus_fwd = SUBARU_CAM_BUS; // to the eyesight camera - } - - if (bus_num == SUBARU_CAM_BUS) { - // Global platform - bool block_lkas = ((addr == MSG_SUBARU_ES_LKAS) || - (addr == MSG_SUBARU_ES_DashStatus) || - (addr == MSG_SUBARU_ES_LKAS_State) || - (addr == MSG_SUBARU_ES_Infotainment)); - - bool block_long = ((addr == MSG_SUBARU_ES_Brake) || - (addr == MSG_SUBARU_ES_Distance) || - (addr == MSG_SUBARU_ES_Status)); - - bool block_msg = block_lkas || (subaru_longitudinal && block_long); - if (!block_msg) { - bus_fwd = SUBARU_MAIN_BUS; // Main CAN - } - } - - return bus_fwd; -} - -static safety_config subaru_init(uint16_t param) { - static const CanMsg SUBARU_TX_MSGS[] = { - SUBARU_COMMON_TX_MSGS(SUBARU_MAIN_BUS, MSG_SUBARU_ES_LKAS) - }; - - static const CanMsg SUBARU_LONG_TX_MSGS[] = { - SUBARU_COMMON_TX_MSGS(SUBARU_MAIN_BUS, MSG_SUBARU_ES_LKAS) - SUBARU_COMMON_LONG_TX_MSGS(SUBARU_MAIN_BUS) - }; - - static const CanMsg SUBARU_GEN2_TX_MSGS[] = { - SUBARU_COMMON_TX_MSGS(SUBARU_ALT_BUS, MSG_SUBARU_ES_LKAS) - }; - - static const CanMsg SUBARU_GEN2_LONG_TX_MSGS[] = { - SUBARU_COMMON_TX_MSGS(SUBARU_ALT_BUS, MSG_SUBARU_ES_LKAS) - SUBARU_COMMON_LONG_TX_MSGS(SUBARU_ALT_BUS) - SUBARU_GEN2_LONG_ADDITIONAL_TX_MSGS() - }; - - static RxCheck subaru_rx_checks[] = { - SUBARU_COMMON_RX_CHECKS(SUBARU_MAIN_BUS) - }; - - static RxCheck subaru_gen2_rx_checks[] = { - SUBARU_COMMON_RX_CHECKS(SUBARU_ALT_BUS) - }; - - const uint16_t SUBARU_PARAM_GEN2 = 1; - - subaru_gen2 = GET_FLAG(param, SUBARU_PARAM_GEN2); - -#ifdef ALLOW_DEBUG - const uint16_t SUBARU_PARAM_LONGITUDINAL = 2; - subaru_longitudinal = GET_FLAG(param, SUBARU_PARAM_LONGITUDINAL); -#endif - - safety_config ret; - if (subaru_gen2) { - ret = subaru_longitudinal ? BUILD_SAFETY_CFG(subaru_gen2_rx_checks, SUBARU_GEN2_LONG_TX_MSGS) : \ - BUILD_SAFETY_CFG(subaru_gen2_rx_checks, SUBARU_GEN2_TX_MSGS); - } else { - ret = subaru_longitudinal ? BUILD_SAFETY_CFG(subaru_rx_checks, SUBARU_LONG_TX_MSGS) : \ - BUILD_SAFETY_CFG(subaru_rx_checks, SUBARU_TX_MSGS); - } - return ret; -} - -const safety_hooks subaru_hooks = { - .init = subaru_init, - .rx = subaru_rx_hook, - .tx = subaru_tx_hook, - .fwd = subaru_fwd_hook, - .get_counter = subaru_get_counter, - .get_checksum = subaru_get_checksum, - .compute_checksum = subaru_compute_checksum, -}; diff --git a/board/safety/safety_subaru_preglobal.h b/board/safety/safety_subaru_preglobal.h deleted file mode 100644 index a1efdfe655..0000000000 --- a/board/safety/safety_subaru_preglobal.h +++ /dev/null @@ -1,129 +0,0 @@ -#pragma once - -#include "safety_declarations.h" - -// Preglobal platform -// 0x161 is ES_CruiseThrottle -// 0x164 is ES_LKAS - -#define MSG_SUBARU_PG_CruiseControl 0x144 -#define MSG_SUBARU_PG_Throttle 0x140 -#define MSG_SUBARU_PG_Wheel_Speeds 0xD4 -#define MSG_SUBARU_PG_Brake_Pedal 0xD1 -#define MSG_SUBARU_PG_ES_LKAS 0x164 -#define MSG_SUBARU_PG_ES_Distance 0x161 -#define MSG_SUBARU_PG_Steering_Torque 0x371 - -#define SUBARU_PG_MAIN_BUS 0 -#define SUBARU_PG_CAM_BUS 2 - -static bool subaru_pg_reversed_driver_torque = false; - -static void subaru_preglobal_rx_hook(const CANPacket_t *to_push) { - const int bus = GET_BUS(to_push); - - if (bus == SUBARU_PG_MAIN_BUS) { - int addr = GET_ADDR(to_push); - if (addr == MSG_SUBARU_PG_Steering_Torque) { - int torque_driver_new; - torque_driver_new = (GET_BYTE(to_push, 3) >> 5) + (GET_BYTE(to_push, 4) << 3); - torque_driver_new = to_signed(torque_driver_new, 11); - torque_driver_new = subaru_pg_reversed_driver_torque ? -torque_driver_new : torque_driver_new; - update_sample(&torque_driver, torque_driver_new); - } - - // enter controls on rising edge of ACC, exit controls on ACC off - if (addr == MSG_SUBARU_PG_CruiseControl) { - bool cruise_engaged = GET_BIT(to_push, 49U); - pcm_cruise_check(cruise_engaged); - } - - // update vehicle moving with any non-zero wheel speed - if (addr == MSG_SUBARU_PG_Wheel_Speeds) { - vehicle_moving = ((GET_BYTES(to_push, 0, 4) >> 12) != 0U) || (GET_BYTES(to_push, 4, 4) != 0U); - } - - if (addr == MSG_SUBARU_PG_Brake_Pedal) { - brake_pressed = ((GET_BYTES(to_push, 0, 4) >> 16) & 0xFFU) > 0U; - } - - if (addr == MSG_SUBARU_PG_Throttle) { - gas_pressed = GET_BYTE(to_push, 0) != 0U; - } - - generic_rx_checks((addr == MSG_SUBARU_PG_ES_LKAS)); - } -} - -static bool subaru_preglobal_tx_hook(const CANPacket_t *to_send) { - const SteeringLimits SUBARU_PG_STEERING_LIMITS = { - .max_steer = 2047, - .max_rt_delta = 940, - .max_rt_interval = 250000, - .max_rate_up = 50, - .max_rate_down = 70, - .driver_torque_factor = 10, - .driver_torque_allowance = 75, - .type = TorqueDriverLimited, - }; - - bool tx = true; - int addr = GET_ADDR(to_send); - - // steer cmd checks - if (addr == MSG_SUBARU_PG_ES_LKAS) { - int desired_torque = ((GET_BYTES(to_send, 0, 4) >> 8) & 0x1FFFU); - desired_torque = -1 * to_signed(desired_torque, 13); - - bool steer_req = GET_BIT(to_send, 24U); - - if (steer_torque_cmd_checks(desired_torque, steer_req, SUBARU_PG_STEERING_LIMITS)) { - tx = false; - } - - } - return tx; -} - -static int subaru_preglobal_fwd_hook(int bus_num, int addr) { - int bus_fwd = -1; - - if (bus_num == SUBARU_PG_MAIN_BUS) { - bus_fwd = SUBARU_PG_CAM_BUS; // Camera CAN - } - - if (bus_num == SUBARU_PG_CAM_BUS) { - bool block_msg = ((addr == MSG_SUBARU_PG_ES_Distance) || (addr == MSG_SUBARU_PG_ES_LKAS)); - if (!block_msg) { - bus_fwd = SUBARU_PG_MAIN_BUS; // Main CAN - } - } - - return bus_fwd; -} - -static safety_config subaru_preglobal_init(uint16_t param) { - static const CanMsg SUBARU_PG_TX_MSGS[] = { - {MSG_SUBARU_PG_ES_Distance, SUBARU_PG_MAIN_BUS, 8}, - {MSG_SUBARU_PG_ES_LKAS, SUBARU_PG_MAIN_BUS, 8} - }; - - // TODO: do checksum and counter checks after adding the signals to the outback dbc file - static RxCheck subaru_preglobal_rx_checks[] = { - {.msg = {{MSG_SUBARU_PG_Throttle, SUBARU_PG_MAIN_BUS, 8, .frequency = 100U}, { 0 }, { 0 }}}, - {.msg = {{MSG_SUBARU_PG_Steering_Torque, SUBARU_PG_MAIN_BUS, 8, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{MSG_SUBARU_PG_CruiseControl, SUBARU_PG_MAIN_BUS, 8, .frequency = 20U}, { 0 }, { 0 }}}, - }; - - const int SUBARU_PG_PARAM_REVERSED_DRIVER_TORQUE = 4; - - subaru_pg_reversed_driver_torque = GET_FLAG(param, SUBARU_PG_PARAM_REVERSED_DRIVER_TORQUE); - return BUILD_SAFETY_CFG(subaru_preglobal_rx_checks, SUBARU_PG_TX_MSGS); -} - -const safety_hooks subaru_preglobal_hooks = { - .init = subaru_preglobal_init, - .rx = subaru_preglobal_rx_hook, - .tx = subaru_preglobal_tx_hook, - .fwd = subaru_preglobal_fwd_hook, -}; diff --git a/board/safety/safety_tesla.h b/board/safety/safety_tesla.h deleted file mode 100644 index 29c35360e3..0000000000 --- a/board/safety/safety_tesla.h +++ /dev/null @@ -1,213 +0,0 @@ -#pragma once - -#include "safety_declarations.h" - -static bool tesla_longitudinal = false; -static bool tesla_stock_aeb = false; - -static void tesla_rx_hook(const CANPacket_t *to_push) { - int bus = GET_BUS(to_push); - int addr = GET_ADDR(to_push); - - if (bus == 0) { - // Steering angle: (0.1 * val) - 819.2 in deg. - if (addr == 0x370) { - // Store it 1/10 deg to match steering request - int angle_meas_new = (((GET_BYTE(to_push, 4) & 0x3FU) << 8) | GET_BYTE(to_push, 5)) - 8192U; - update_sample(&angle_meas, angle_meas_new); - } - - // Vehicle speed - if (addr == 0x257) { - // Vehicle speed: ((val * 0.08) - 40) / MS_TO_KPH - float speed = ((((GET_BYTE(to_push, 2) << 4) | (GET_BYTE(to_push, 1) >> 4)) * 0.08) - 40) / 3.6; - UPDATE_VEHICLE_SPEED(speed); - } - - // Gas pressed - if (addr == 0x118) { - gas_pressed = (GET_BYTE(to_push, 4) != 0U); - } - - // Brake pressed - if (addr == 0x39d) { - brake_pressed = (GET_BYTE(to_push, 2) & 0x03U) == 2U; - } - - // Cruise state - if (addr == 0x286) { - int cruise_state = (GET_BYTE(to_push, 1) >> 4) & 0x07U; - bool cruise_engaged = (cruise_state == 2) || // ENABLED - (cruise_state == 3) || // STANDSTILL - (cruise_state == 4) || // OVERRIDE - (cruise_state == 6) || // PRE_FAULT - (cruise_state == 7); // PRE_CANCEL - - vehicle_moving = cruise_state != 3; // STANDSTILL - pcm_cruise_check(cruise_engaged); - } - } - - if (bus == 2) { - if (tesla_longitudinal && (addr == 0x2b9)) { - // "AEB_ACTIVE" - tesla_stock_aeb = (GET_BYTE(to_push, 2) & 0x03U) == 1U; - } - } - - generic_rx_checks((addr == 0x488) && (bus == 0)); // DAS_steeringControl - generic_rx_checks((addr == 0x27d) && (bus == 0)); // APS_eacMonitor - - if (tesla_longitudinal) { - generic_rx_checks((addr == 0x2b9) && (bus == 0)); - } -} - - -static bool tesla_tx_hook(const CANPacket_t *to_send) { - const SteeringLimits TESLA_STEERING_LIMITS = { - .angle_deg_to_can = 10, - .angle_rate_up_lookup = { - {0., 5., 15.}, - {10., 1.6, .3} - }, - .angle_rate_down_lookup = { - {0., 5., 15.}, - {10., 7.0, .8} - }, - }; - - const LongitudinalLimits TESLA_LONG_LIMITS = { - .max_accel = 425, // 2 m/s^2 - .min_accel = 288, // -3.48 m/s^2 - .inactive_accel = 375, // 0. m/s^2 - }; - - bool tx = true; - int addr = GET_ADDR(to_send); - bool violation = false; - - // Steering control: (0.1 * val) - 1638.35 in deg. - if (addr == 0x488) { - // We use 1/10 deg as a unit here - int raw_angle_can = ((GET_BYTE(to_send, 0) & 0x7FU) << 8) | GET_BYTE(to_send, 1); - int desired_angle = raw_angle_can - 16384; - int steer_control_type = GET_BYTE(to_send, 2) >> 6; - bool steer_control_enabled = (steer_control_type != 0) && // NONE - (steer_control_type != 3); // DISABLED - - if (steer_angle_cmd_checks(desired_angle, steer_control_enabled, TESLA_STEERING_LIMITS)) { - violation = true; - } - } - - // DAS_control: longitudinal control message - if (addr == 0x2b9) { - // No AEB events may be sent by openpilot - int aeb_event = GET_BYTE(to_send, 2) & 0x03U; - if (aeb_event != 0) { - violation = true; - } - - int raw_accel_max = ((GET_BYTE(to_send, 6) & 0x1FU) << 4) | (GET_BYTE(to_send, 5) >> 4); - int raw_accel_min = ((GET_BYTE(to_send, 5) & 0x0FU) << 5) | (GET_BYTE(to_send, 4) >> 3); - int acc_state = GET_BYTE(to_send, 1) >> 4; - - if (tesla_longitudinal) { - // Don't send messages when the stock AEB system is active - if (tesla_stock_aeb) { - violation = true; - } - - // Prevent both acceleration from being negative, as this could cause the car to reverse after coming to standstill - if ((raw_accel_max < TESLA_LONG_LIMITS.inactive_accel) && (raw_accel_min < TESLA_LONG_LIMITS.inactive_accel)) { - violation = true; - } - - // Don't allow any acceleration limits above the safety limits - violation |= longitudinal_accel_checks(raw_accel_max, TESLA_LONG_LIMITS); - violation |= longitudinal_accel_checks(raw_accel_min, TESLA_LONG_LIMITS); - } else { - // does allowing cancel here disrupt stock AEB? TODO: find out and add safety or remove comment - // Can only send cancel longitudinal messages when not controlling longitudinal - if (acc_state != 13) { // ACC_CANCEL_GENERIC_SILENT - violation = true; - } - - // No actuation is allowed when not controlling longitudinal - if ((raw_accel_max != TESLA_LONG_LIMITS.inactive_accel) || (raw_accel_min != TESLA_LONG_LIMITS.inactive_accel)) { - violation = true; - } - } - } - - if (violation) { - tx = false; - } - - return tx; -} - -static int tesla_fwd_hook(int bus_num, int addr) { - int bus_fwd = -1; - - if (bus_num == 0) { - // Party to autopilot - bus_fwd = 2; - } - - if (bus_num == 2) { - bool block_msg = false; - // DAS_steeringControl, APS_eacMonitor - if ((addr == 0x488) || (addr == 0x27d)) { - block_msg = true; - } - - // DAS_control - if (tesla_longitudinal && (addr == 0x2b9) && !tesla_stock_aeb) { - block_msg = true; - } - - if (!block_msg) { - bus_fwd = 0; - } - } - - return bus_fwd; -} - -static safety_config tesla_init(uint16_t param) { - - static const CanMsg TESLA_M3_Y_TX_MSGS[] = { - {0x488, 0, 4}, // DAS_steeringControl - {0x2b9, 0, 8}, // DAS_control - {0x27D, 0, 3}, // APS_eacMonitor - }; - - UNUSED(param); -#ifdef ALLOW_DEBUG - const int TESLA_FLAG_LONGITUDINAL_CONTROL = 1; - tesla_longitudinal = GET_FLAG(param, TESLA_FLAG_LONGITUDINAL_CONTROL); -#endif - - tesla_stock_aeb = false; - - static RxCheck tesla_model3_y_rx_checks[] = { - {.msg = {{0x2b9, 2, 8, .frequency = 25U}, { 0 }, { 0 }}}, // DAS_control - {.msg = {{0x257, 0, 8, .frequency = 50U}, { 0 }, { 0 }}}, // DI_speed (speed in kph) - {.msg = {{0x370, 0, 8, .frequency = 100U}, { 0 }, { 0 }}}, // EPAS3S_internalSAS (steering angle) - {.msg = {{0x118, 0, 8, .frequency = 100U}, { 0 }, { 0 }}}, // DI_systemStatus (gas pedal) - {.msg = {{0x39d, 0, 5, .frequency = 25U}, { 0 }, { 0 }}}, // IBST_status (brakes) - {.msg = {{0x286, 0, 8, .frequency = 10U}, { 0 }, { 0 }}}, // DI_state (acc state) - {.msg = {{0x311, 0, 7, .frequency = 10U}, { 0 }, { 0 }}}, // UI_warning (blinkers, buckle switch & doors) - }; - - return BUILD_SAFETY_CFG(tesla_model3_y_rx_checks, TESLA_M3_Y_TX_MSGS); -} - -const safety_hooks tesla_hooks = { - .init = tesla_init, - .rx = tesla_rx_hook, - .tx = tesla_tx_hook, - .fwd = tesla_fwd_hook, -}; diff --git a/board/safety/safety_toyota.h b/board/safety/safety_toyota.h deleted file mode 100644 index 7008bf8419..0000000000 --- a/board/safety/safety_toyota.h +++ /dev/null @@ -1,413 +0,0 @@ -#pragma once - -#include "safety_declarations.h" - -// Stock longitudinal -#define TOYOTA_BASE_TX_MSGS \ - {0x191, 0, 8}, {0x412, 0, 8}, {0x343, 0, 8}, {0x1D2, 0, 8}, /* LKAS + LTA + ACC & PCM cancel cmds */ \ - -#define TOYOTA_COMMON_TX_MSGS \ - TOYOTA_BASE_TX_MSGS \ - {0x2E4, 0, 5}, \ - -#define TOYOTA_COMMON_SECOC_TX_MSGS \ - TOYOTA_BASE_TX_MSGS \ - {0x2E4, 0, 8}, {0x131, 0, 8}, \ - -#define TOYOTA_COMMON_LONG_TX_MSGS \ - TOYOTA_COMMON_TX_MSGS \ - {0x283, 0, 7}, {0x2E6, 0, 8}, {0x2E7, 0, 8}, {0x33E, 0, 7}, {0x344, 0, 8}, {0x365, 0, 7}, {0x366, 0, 7}, {0x4CB, 0, 8}, /* DSU bus 0 */ \ - {0x128, 1, 6}, {0x141, 1, 4}, {0x160, 1, 8}, {0x161, 1, 7}, {0x470, 1, 4}, /* DSU bus 1 */ \ - {0x411, 0, 8}, /* PCS_HUD */ \ - {0x750, 0, 8}, /* radar diagnostic address */ \ - -#define TOYOTA_COMMON_RX_CHECKS(lta) \ - {.msg = {{ 0xaa, 0, 8, .check_checksum = false, .frequency = 83U}, { 0 }, { 0 }}}, \ - {.msg = {{0x260, 0, 8, .check_checksum = true, .quality_flag = (lta), .frequency = 50U}, { 0 }, { 0 }}}, \ - {.msg = {{0x1D2, 0, 8, .check_checksum = true, .frequency = 33U}, \ - {0x176, 0, 8, .check_checksum = true, .frequency = 32U}, { 0 }}}, \ - {.msg = {{0x101, 0, 8, .check_checksum = false, .frequency = 50U}, \ - {0x224, 0, 8, .check_checksum = false, .frequency = 40U}, \ - {0x226, 0, 8, .check_checksum = false, .frequency = 40U}}}, \ - -static bool toyota_secoc = false; -static bool toyota_alt_brake = false; -static bool toyota_stock_longitudinal = false; -static bool toyota_lta = false; -static int toyota_dbc_eps_torque_factor = 100; // conversion factor for STEER_TORQUE_EPS in %: see dbc file - -static uint32_t toyota_compute_checksum(const CANPacket_t *to_push) { - int addr = GET_ADDR(to_push); - int len = GET_LEN(to_push); - uint8_t checksum = (uint8_t)(addr) + (uint8_t)((unsigned int)(addr) >> 8U) + (uint8_t)(len); - for (int i = 0; i < (len - 1); i++) { - checksum += (uint8_t)GET_BYTE(to_push, i); - } - return checksum; -} - -static uint32_t toyota_get_checksum(const CANPacket_t *to_push) { - int checksum_byte = GET_LEN(to_push) - 1U; - return (uint8_t)(GET_BYTE(to_push, checksum_byte)); -} - -static bool toyota_get_quality_flag_valid(const CANPacket_t *to_push) { - int addr = GET_ADDR(to_push); - - bool valid = false; - if (addr == 0x260) { - valid = !GET_BIT(to_push, 3U); // STEER_ANGLE_INITIALIZING - } - return valid; -} - -static void toyota_rx_hook(const CANPacket_t *to_push) { - const int TOYOTA_LTA_MAX_ANGLE = 1657; // EPS only accepts up to 94.9461 - - if (GET_BUS(to_push) == 0U) { - int addr = GET_ADDR(to_push); - - // get eps motor torque (0.66 factor in dbc) - if (addr == 0x260) { - int torque_meas_new = (GET_BYTE(to_push, 5) << 8) | GET_BYTE(to_push, 6); - torque_meas_new = to_signed(torque_meas_new, 16); - - // scale by dbc_factor - torque_meas_new = (torque_meas_new * toyota_dbc_eps_torque_factor) / 100; - - // update array of sample - update_sample(&torque_meas, torque_meas_new); - - // increase torque_meas by 1 to be conservative on rounding - torque_meas.min--; - torque_meas.max++; - - // driver torque for angle limiting - int torque_driver_new = (GET_BYTE(to_push, 1) << 8) | GET_BYTE(to_push, 2); - torque_driver_new = to_signed(torque_driver_new, 16); - update_sample(&torque_driver, torque_driver_new); - - // LTA request angle should match current angle while inactive, clipped to max accepted angle. - // note that angle can be relative to init angle on some TSS2 platforms, LTA has the same offset - bool steer_angle_initializing = GET_BIT(to_push, 3U); - if (!steer_angle_initializing) { - int angle_meas_new = (GET_BYTE(to_push, 3) << 8U) | GET_BYTE(to_push, 4); - angle_meas_new = CLAMP(to_signed(angle_meas_new, 16), -TOYOTA_LTA_MAX_ANGLE, TOYOTA_LTA_MAX_ANGLE); - update_sample(&angle_meas, angle_meas_new); - } - } - - // enter controls on rising edge of ACC, exit controls on ACC off - // exit controls on rising edge of gas press, if not alternative experience - // exit controls on rising edge of brake press - if (toyota_secoc) { - if (addr == 0x176) { - bool cruise_engaged = GET_BIT(to_push, 5U); // PCM_CRUISE.CRUISE_ACTIVE - pcm_cruise_check(cruise_engaged); - } - if (addr == 0x116) { - gas_pressed = GET_BYTE(to_push, 1) != 0U; // GAS_PEDAL.GAS_PEDAL_USER - } - if (addr == 0x101) { - brake_pressed = GET_BIT(to_push, 3U); // BRAKE_MODULE.BRAKE_PRESSED (toyota_rav4_prime_generated.dbc) - } - } else { - if (addr == 0x1D2) { - bool cruise_engaged = GET_BIT(to_push, 5U); // PCM_CRUISE.CRUISE_ACTIVE - pcm_cruise_check(cruise_engaged); - gas_pressed = !GET_BIT(to_push, 4U); // PCM_CRUISE.GAS_RELEASED - } - if (!toyota_alt_brake && (addr == 0x226)) { - brake_pressed = GET_BIT(to_push, 37U); // BRAKE_MODULE.BRAKE_PRESSED (toyota_nodsu_pt_generated.dbc) - } - if (toyota_alt_brake && (addr == 0x224)) { - brake_pressed = GET_BIT(to_push, 5U); // BRAKE_MODULE.BRAKE_PRESSED (toyota_new_mc_pt_generated.dbc) - } - } - - // sample speed - if (addr == 0xaa) { - int speed = 0; - // sum 4 wheel speeds. conversion: raw * 0.01 - 67.67 - for (uint8_t i = 0U; i < 8U; i += 2U) { - int wheel_speed = (GET_BYTE(to_push, i) << 8U) | GET_BYTE(to_push, (i + 1U)); - speed += wheel_speed - 6767; - } - // check that all wheel speeds are at zero value - vehicle_moving = speed != 0; - - UPDATE_VEHICLE_SPEED(speed / 4.0 * 0.01 / 3.6); - } - - bool stock_ecu_detected = addr == 0x2E4; // STEERING_LKA - if (!toyota_stock_longitudinal && (addr == 0x343)) { - stock_ecu_detected = true; // ACC_CONTROL - } - generic_rx_checks(stock_ecu_detected); - } -} - -static bool toyota_tx_hook(const CANPacket_t *to_send) { - const SteeringLimits TOYOTA_STEERING_LIMITS = { - .max_steer = 1500, - .max_rate_up = 15, // ramp up slow - .max_rate_down = 25, // ramp down fast - .max_torque_error = 350, // max torque cmd in excess of motor torque - .max_rt_delta = 450, // the real time limit is 1800/sec, a 20% buffer - .max_rt_interval = 250000, - .type = TorqueMotorLimited, - - // the EPS faults when the steering angle rate is above a certain threshold for too long. to prevent this, - // we allow setting STEER_REQUEST bit to 0 while maintaining the requested torque value for a single frame - .min_valid_request_frames = 18, - .max_invalid_request_frames = 1, - .min_valid_request_rt_interval = 170000, // 170ms; a ~10% buffer on cutting every 19 frames - .has_steer_req_tolerance = true, - - // LTA angle limits - // factor for STEER_TORQUE_SENSOR->STEER_ANGLE and STEERING_LTA->STEER_ANGLE_CMD (1 / 0.0573) - .angle_deg_to_can = 17.452007, - .angle_rate_up_lookup = { - {5., 25., 25.}, - {0.3, 0.15, 0.15} - }, - .angle_rate_down_lookup = { - {5., 25., 25.}, - {0.36, 0.26, 0.26} - }, - }; - - const int TOYOTA_LTA_MAX_MEAS_TORQUE = 1500; - const int TOYOTA_LTA_MAX_DRIVER_TORQUE = 150; - - // longitudinal limits - const LongitudinalLimits TOYOTA_LONG_LIMITS = { - .max_accel = 2000, // 2.0 m/s2 - .min_accel = -3500, // -3.5 m/s2 - }; - - bool tx = true; - int addr = GET_ADDR(to_send); - int bus = GET_BUS(to_send); - - // Check if msg is sent on BUS 0 - if (bus == 0) { - // ACCEL: safety check on byte 1-2 - if (addr == 0x343) { - int desired_accel = (GET_BYTE(to_send, 0) << 8) | GET_BYTE(to_send, 1); - desired_accel = to_signed(desired_accel, 16); - - bool violation = false; - violation |= longitudinal_accel_checks(desired_accel, TOYOTA_LONG_LIMITS); - - // only ACC messages that cancel are allowed when openpilot is not controlling longitudinal - if (toyota_stock_longitudinal) { - bool cancel_req = GET_BIT(to_send, 24U); - if (!cancel_req) { - violation = true; - } - if (desired_accel != TOYOTA_LONG_LIMITS.inactive_accel) { - violation = true; - } - } - - if (violation) { - tx = false; - } - } - - // AEB: block all actuation. only used when DSU is unplugged - if (addr == 0x283) { - // only allow the checksum, which is the last byte - bool block = (GET_BYTES(to_send, 0, 4) != 0U) || (GET_BYTE(to_send, 4) != 0U) || (GET_BYTE(to_send, 5) != 0U); - if (block) { - tx = false; - } - } - - // STEERING_LTA angle steering check - if (addr == 0x191) { - // check the STEER_REQUEST, STEER_REQUEST_2, TORQUE_WIND_DOWN, STEER_ANGLE_CMD signals - bool lta_request = GET_BIT(to_send, 0U); - bool lta_request2 = GET_BIT(to_send, 25U); - int torque_wind_down = GET_BYTE(to_send, 5); - int lta_angle = (GET_BYTE(to_send, 1) << 8) | GET_BYTE(to_send, 2); - lta_angle = to_signed(lta_angle, 16); - - bool steer_control_enabled = lta_request || lta_request2; - if (!toyota_lta) { - // using torque (LKA), block LTA msgs with actuation requests - if (steer_control_enabled || (lta_angle != 0) || (torque_wind_down != 0)) { - tx = false; - } - } else { - // check angle rate limits and inactive angle - if (steer_angle_cmd_checks(lta_angle, steer_control_enabled, TOYOTA_STEERING_LIMITS)) { - tx = false; - } - - if (lta_request != lta_request2) { - tx = false; - } - - // TORQUE_WIND_DOWN is gated on steer request - if (!steer_control_enabled && (torque_wind_down != 0)) { - tx = false; - } - - // TORQUE_WIND_DOWN can only be no or full torque - if ((torque_wind_down != 0) && (torque_wind_down != 100)) { - tx = false; - } - - // check if we should wind down torque - int driver_torque = MIN(ABS(torque_driver.min), ABS(torque_driver.max)); - if ((driver_torque > TOYOTA_LTA_MAX_DRIVER_TORQUE) && (torque_wind_down != 0)) { - tx = false; - } - - int eps_torque = MIN(ABS(torque_meas.min), ABS(torque_meas.max)); - if ((eps_torque > TOYOTA_LTA_MAX_MEAS_TORQUE) && (torque_wind_down != 0)) { - tx = false; - } - } - } - - // STEERING_LTA_2 angle steering check (SecOC) - if (toyota_secoc && (addr == 0x131)) { - // SecOC cars block any form of LTA actuation for now - bool lta_request = GET_BIT(to_send, 3U); // STEERING_LTA_2.STEER_REQUEST - bool lta_request2 = GET_BIT(to_send, 0U); // STEERING_LTA_2.STEER_REQUEST_2 - int lta_angle_msb = GET_BYTE(to_send, 2); // STEERING_LTA_2.STEER_ANGLE_CMD (MSB) - int lta_angle_lsb = GET_BYTE(to_send, 3); // STEERING_LTA_2.STEER_ANGLE_CMD (LSB) - - bool actuation = lta_request || lta_request2 || (lta_angle_msb != 0) || (lta_angle_lsb != 0); - if (actuation) { - tx = false; - } - } - - // STEER: safety check on bytes 2-3 - if (addr == 0x2E4) { - int desired_torque = (GET_BYTE(to_send, 1) << 8) | GET_BYTE(to_send, 2); - desired_torque = to_signed(desired_torque, 16); - bool steer_req = GET_BIT(to_send, 0U); - // When using LTA (angle control), assert no actuation on LKA message - if (!toyota_lta) { - if (steer_torque_cmd_checks(desired_torque, steer_req, TOYOTA_STEERING_LIMITS)) { - tx = false; - } - } else { - if ((desired_torque != 0) || steer_req) { - tx = false; - } - } - } - } - - // UDS: Only tester present ("\x0F\x02\x3E\x00\x00\x00\x00\x00") allowed on diagnostics address - if (addr == 0x750) { - // this address is sub-addressed. only allow tester present to radar (0xF) - bool invalid_uds_msg = (GET_BYTES(to_send, 0, 4) != 0x003E020FU) || (GET_BYTES(to_send, 4, 4) != 0x0U); - if (invalid_uds_msg) { - tx = 0; - } - } - - return tx; -} - -static safety_config toyota_init(uint16_t param) { - static const CanMsg TOYOTA_TX_MSGS[] = { - TOYOTA_COMMON_TX_MSGS - }; - - static const CanMsg TOYOTA_SECOC_TX_MSGS[] = { - TOYOTA_COMMON_SECOC_TX_MSGS - }; - - static const CanMsg TOYOTA_LONG_TX_MSGS[] = { - TOYOTA_COMMON_LONG_TX_MSGS - }; - - // safety param flags - // first byte is for EPS factor, second is for flags - const uint32_t TOYOTA_PARAM_OFFSET = 8U; - const uint32_t TOYOTA_EPS_FACTOR = (1UL << TOYOTA_PARAM_OFFSET) - 1U; - const uint32_t TOYOTA_PARAM_ALT_BRAKE = 1UL << TOYOTA_PARAM_OFFSET; - const uint32_t TOYOTA_PARAM_STOCK_LONGITUDINAL = 2UL << TOYOTA_PARAM_OFFSET; - const uint32_t TOYOTA_PARAM_LTA = 4UL << TOYOTA_PARAM_OFFSET; - -#ifdef ALLOW_DEBUG - const uint32_t TOYOTA_PARAM_SECOC = 8UL << TOYOTA_PARAM_OFFSET; - toyota_secoc = GET_FLAG(param, TOYOTA_PARAM_SECOC); -#endif - - toyota_alt_brake = GET_FLAG(param, TOYOTA_PARAM_ALT_BRAKE); - toyota_stock_longitudinal = GET_FLAG(param, TOYOTA_PARAM_STOCK_LONGITUDINAL); - toyota_lta = GET_FLAG(param, TOYOTA_PARAM_LTA); - toyota_dbc_eps_torque_factor = param & TOYOTA_EPS_FACTOR; - - safety_config ret; - if (toyota_stock_longitudinal) { - if (toyota_secoc) { - SET_TX_MSGS(TOYOTA_SECOC_TX_MSGS, ret); - } else { - SET_TX_MSGS(TOYOTA_TX_MSGS, ret); - } - } else { - SET_TX_MSGS(TOYOTA_LONG_TX_MSGS, ret); - } - - if (toyota_lta) { - // Check the quality flag for angle measurement when using LTA, since it's not set on TSS-P cars - static RxCheck toyota_lta_rx_checks[] = { - TOYOTA_COMMON_RX_CHECKS(true) - }; - - SET_RX_CHECKS(toyota_lta_rx_checks, ret); - } else { - static RxCheck toyota_lka_rx_checks[] = { - TOYOTA_COMMON_RX_CHECKS(false) - }; - - SET_RX_CHECKS(toyota_lka_rx_checks, ret); - } - - return ret; -} - -static int toyota_fwd_hook(int bus_num, int addr) { - - int bus_fwd = -1; - - if (bus_num == 0) { - bus_fwd = 2; - } - - if (bus_num == 2) { - // block stock lkas messages and stock acc messages (if OP is doing ACC) - // in TSS2, 0x191 is LTA which we need to block to avoid controls collision - bool is_lkas_msg = ((addr == 0x2E4) || (addr == 0x412) || (addr == 0x191)); - // on SecOC cars 0x131 is also LTA - is_lkas_msg |= toyota_secoc && (addr == 0x131); - // in TSS2 the camera does ACC as well, so filter 0x343 - bool is_acc_msg = (addr == 0x343); - bool block_msg = is_lkas_msg || (is_acc_msg && !toyota_stock_longitudinal); - if (!block_msg) { - bus_fwd = 0; - } - } - - return bus_fwd; -} - -const safety_hooks toyota_hooks = { - .init = toyota_init, - .rx = toyota_rx_hook, - .tx = toyota_tx_hook, - .fwd = toyota_fwd_hook, - .get_checksum = toyota_get_checksum, - .compute_checksum = toyota_compute_checksum, - .get_quality_flag_valid = toyota_get_quality_flag_valid, -}; diff --git a/board/safety/safety_volkswagen_common.h b/board/safety/safety_volkswagen_common.h deleted file mode 100644 index f94879212b..0000000000 --- a/board/safety/safety_volkswagen_common.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -extern const uint16_t FLAG_VOLKSWAGEN_LONG_CONTROL; -const uint16_t FLAG_VOLKSWAGEN_LONG_CONTROL = 1; - -extern bool volkswagen_longitudinal; -bool volkswagen_longitudinal = false; - -extern bool volkswagen_set_button_prev; -bool volkswagen_set_button_prev = false; - -extern bool volkswagen_resume_button_prev; -bool volkswagen_resume_button_prev = false; diff --git a/board/safety/safety_volkswagen_mqb.h b/board/safety/safety_volkswagen_mqb.h deleted file mode 100644 index 40f7cbccc9..0000000000 --- a/board/safety/safety_volkswagen_mqb.h +++ /dev/null @@ -1,302 +0,0 @@ -#pragma once - -#include "safety_declarations.h" -#include "safety_volkswagen_common.h" - -#define MSG_ESP_19 0x0B2 // RX from ABS, for wheel speeds -#define MSG_LH_EPS_03 0x09F // RX from EPS, for driver steering torque -#define MSG_ESP_05 0x106 // RX from ABS, for brake switch state -#define MSG_TSK_06 0x120 // RX from ECU, for ACC status from drivetrain coordinator -#define MSG_MOTOR_20 0x121 // RX from ECU, for driver throttle input -#define MSG_ACC_06 0x122 // TX by OP, ACC control instructions to the drivetrain coordinator -#define MSG_HCA_01 0x126 // TX by OP, Heading Control Assist steering torque -#define MSG_GRA_ACC_01 0x12B // TX by OP, ACC control buttons for cancel/resume -#define MSG_ACC_07 0x12E // TX by OP, ACC control instructions to the drivetrain coordinator -#define MSG_ACC_02 0x30C // TX by OP, ACC HUD data to the instrument cluster -#define MSG_MOTOR_14 0x3BE // RX from ECU, for brake switch status -#define MSG_LDW_02 0x397 // TX by OP, Lane line recognition and text alerts - -static uint8_t volkswagen_crc8_lut_8h2f[256]; // Static lookup table for CRC8 poly 0x2F, aka 8H2F/AUTOSAR -static bool volkswagen_mqb_brake_pedal_switch = false; -static bool volkswagen_mqb_brake_pressure_detected = false; - -static uint32_t volkswagen_mqb_get_checksum(const CANPacket_t *to_push) { - return (uint8_t)GET_BYTE(to_push, 0); -} - -static uint8_t volkswagen_mqb_get_counter(const CANPacket_t *to_push) { - // MQB message counters are consistently found at LSB 8. - return (uint8_t)GET_BYTE(to_push, 1) & 0xFU; -} - -static uint32_t volkswagen_mqb_compute_crc(const CANPacket_t *to_push) { - int addr = GET_ADDR(to_push); - int len = GET_LEN(to_push); - - // This is CRC-8H2F/AUTOSAR with a twist. See the OpenDBC implementation - // of this algorithm for a version with explanatory comments. - - uint8_t crc = 0xFFU; - for (int i = 1; i < len; i++) { - crc ^= (uint8_t)GET_BYTE(to_push, i); - crc = volkswagen_crc8_lut_8h2f[crc]; - } - - uint8_t counter = volkswagen_mqb_get_counter(to_push); - if (addr == MSG_LH_EPS_03) { - crc ^= (uint8_t[]){0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5,0xF5}[counter]; - } else if (addr == MSG_ESP_05) { - crc ^= (uint8_t[]){0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07}[counter]; - } else if (addr == MSG_TSK_06) { - crc ^= (uint8_t[]){0xC4,0xE2,0x4F,0xE4,0xF8,0x2F,0x56,0x81,0x9F,0xE5,0x83,0x44,0x05,0x3F,0x97,0xDF}[counter]; - } else if (addr == MSG_MOTOR_20) { - crc ^= (uint8_t[]){0xE9,0x65,0xAE,0x6B,0x7B,0x35,0xE5,0x5F,0x4E,0xC7,0x86,0xA2,0xBB,0xDD,0xEB,0xB4}[counter]; - } else if (addr == MSG_GRA_ACC_01) { - crc ^= (uint8_t[]){0x6A,0x38,0xB4,0x27,0x22,0xEF,0xE1,0xBB,0xF8,0x80,0x84,0x49,0xC7,0x9E,0x1E,0x2B}[counter]; - } else { - // Undefined CAN message, CRC check expected to fail - } - crc = volkswagen_crc8_lut_8h2f[crc]; - - return (uint8_t)(crc ^ 0xFFU); -} - -static safety_config volkswagen_mqb_init(uint16_t param) { - // Transmit of GRA_ACC_01 is allowed on bus 0 and 2 to keep compatibility with gateway and camera integration - static const CanMsg VOLKSWAGEN_MQB_STOCK_TX_MSGS[] = {{MSG_HCA_01, 0, 8}, {MSG_GRA_ACC_01, 0, 8}, {MSG_GRA_ACC_01, 2, 8}, - {MSG_LDW_02, 0, 8}, {MSG_LH_EPS_03, 2, 8}}; - - static const CanMsg VOLKSWAGEN_MQB_LONG_TX_MSGS[] = {{MSG_HCA_01, 0, 8}, {MSG_LDW_02, 0, 8}, {MSG_LH_EPS_03, 2, 8}, - {MSG_ACC_02, 0, 8}, {MSG_ACC_06, 0, 8}, {MSG_ACC_07, 0, 8}}; - - static RxCheck volkswagen_mqb_rx_checks[] = { - {.msg = {{MSG_ESP_19, 0, 8, .check_checksum = false, .max_counter = 0U, .frequency = 100U}, { 0 }, { 0 }}}, - {.msg = {{MSG_LH_EPS_03, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 100U}, { 0 }, { 0 }}}, - {.msg = {{MSG_ESP_05, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{MSG_TSK_06, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{MSG_MOTOR_20, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{MSG_MOTOR_14, 0, 8, .check_checksum = false, .max_counter = 0U, .frequency = 10U}, { 0 }, { 0 }}}, - {.msg = {{MSG_GRA_ACC_01, 0, 8, .check_checksum = true, .max_counter = 15U, .frequency = 33U}, { 0 }, { 0 }}}, - }; - - UNUSED(param); - - volkswagen_set_button_prev = false; - volkswagen_resume_button_prev = false; - volkswagen_mqb_brake_pedal_switch = false; - volkswagen_mqb_brake_pressure_detected = false; - -#ifdef ALLOW_DEBUG - volkswagen_longitudinal = GET_FLAG(param, FLAG_VOLKSWAGEN_LONG_CONTROL); -#endif - gen_crc_lookup_table_8(0x2F, volkswagen_crc8_lut_8h2f); - return volkswagen_longitudinal ? BUILD_SAFETY_CFG(volkswagen_mqb_rx_checks, VOLKSWAGEN_MQB_LONG_TX_MSGS) : \ - BUILD_SAFETY_CFG(volkswagen_mqb_rx_checks, VOLKSWAGEN_MQB_STOCK_TX_MSGS); -} - -static void volkswagen_mqb_rx_hook(const CANPacket_t *to_push) { - if (GET_BUS(to_push) == 0U) { - int addr = GET_ADDR(to_push); - - // Update in-motion state by sampling wheel speeds - if (addr == MSG_ESP_19) { - // sum 4 wheel speeds - int speed = 0; - for (uint8_t i = 0U; i < 8U; i += 2U) { - int wheel_speed = GET_BYTE(to_push, i) | (GET_BYTE(to_push, i + 1U) << 8); - speed += wheel_speed; - } - // Check all wheel speeds for any movement - vehicle_moving = speed > 0; - } - - // Update driver input torque samples - // Signal: LH_EPS_03.EPS_Lenkmoment (absolute torque) - // Signal: LH_EPS_03.EPS_VZ_Lenkmoment (direction) - if (addr == MSG_LH_EPS_03) { - int torque_driver_new = GET_BYTE(to_push, 5) | ((GET_BYTE(to_push, 6) & 0x1FU) << 8); - int sign = (GET_BYTE(to_push, 6) & 0x80U) >> 7; - if (sign == 1) { - torque_driver_new *= -1; - } - update_sample(&torque_driver, torque_driver_new); - } - - if (addr == MSG_TSK_06) { - // When using stock ACC, enter controls on rising edge of stock ACC engage, exit on disengage - // Always exit controls on main switch off - // Signal: TSK_06.TSK_Status - int acc_status = (GET_BYTE(to_push, 3) & 0x7U); - bool cruise_engaged = (acc_status == 3) || (acc_status == 4) || (acc_status == 5); - acc_main_on = cruise_engaged || (acc_status == 2); - - if (!volkswagen_longitudinal) { - pcm_cruise_check(cruise_engaged); - } - - if (!acc_main_on) { - controls_allowed = false; - } - } - - if (addr == MSG_GRA_ACC_01) { - // If using openpilot longitudinal, enter controls on falling edge of Set or Resume with main switch on - // Signal: GRA_ACC_01.GRA_Tip_Setzen - // Signal: GRA_ACC_01.GRA_Tip_Wiederaufnahme - if (volkswagen_longitudinal) { - bool set_button = GET_BIT(to_push, 16U); - bool resume_button = GET_BIT(to_push, 19U); - if ((volkswagen_set_button_prev && !set_button) || (volkswagen_resume_button_prev && !resume_button)) { - controls_allowed = acc_main_on; - } - volkswagen_set_button_prev = set_button; - volkswagen_resume_button_prev = resume_button; - } - // Always exit controls on rising edge of Cancel - // Signal: GRA_ACC_01.GRA_Abbrechen - if (GET_BIT(to_push, 13U)) { - controls_allowed = false; - } - } - - // Signal: Motor_20.MO_Fahrpedalrohwert_01 - if (addr == MSG_MOTOR_20) { - gas_pressed = ((GET_BYTES(to_push, 0, 4) >> 12) & 0xFFU) != 0U; - } - - // Signal: Motor_14.MO_Fahrer_bremst (ECU detected brake pedal switch F63) - if (addr == MSG_MOTOR_14) { - volkswagen_mqb_brake_pedal_switch = (GET_BYTE(to_push, 3) & 0x10U) >> 4; - } - - // Signal: ESP_05.ESP_Fahrer_bremst (ESP detected driver brake pressure above platform specified threshold) - if (addr == MSG_ESP_05) { - volkswagen_mqb_brake_pressure_detected = (GET_BYTE(to_push, 3) & 0x4U) >> 2; - } - - brake_pressed = volkswagen_mqb_brake_pedal_switch || volkswagen_mqb_brake_pressure_detected; - - generic_rx_checks((addr == MSG_HCA_01)); - } -} - -static bool volkswagen_mqb_tx_hook(const CANPacket_t *to_send) { - // lateral limits - const SteeringLimits VOLKSWAGEN_MQB_STEERING_LIMITS = { - .max_steer = 300, // 3.0 Nm (EPS side max of 3.0Nm with fault if violated) - .max_rt_delta = 75, // 4 max rate up * 50Hz send rate * 250000 RT interval / 1000000 = 50 ; 50 * 1.5 for safety pad = 75 - .max_rt_interval = 250000, // 250ms between real time checks - .max_rate_up = 4, // 2.0 Nm/s RoC limit (EPS rack has own soft-limit of 5.0 Nm/s) - .max_rate_down = 10, // 5.0 Nm/s RoC limit (EPS rack has own soft-limit of 5.0 Nm/s) - .driver_torque_allowance = 80, - .driver_torque_factor = 3, - .type = TorqueDriverLimited, - }; - - // longitudinal limits - // acceleration in m/s2 * 1000 to avoid floating point math - const LongitudinalLimits VOLKSWAGEN_MQB_LONG_LIMITS = { - .max_accel = 2000, - .min_accel = -3500, - .inactive_accel = 3010, // VW sends one increment above the max range when inactive - }; - - int addr = GET_ADDR(to_send); - bool tx = true; - - // Safety check for HCA_01 Heading Control Assist torque - // Signal: HCA_01.HCA_01_LM_Offset (absolute torque) - // Signal: HCA_01.HCA_01_LM_OffSign (direction) - if (addr == MSG_HCA_01) { - int desired_torque = GET_BYTE(to_send, 2) | ((GET_BYTE(to_send, 3) & 0x1U) << 8); - bool sign = GET_BIT(to_send, 31U); - if (sign) { - desired_torque *= -1; - } - - bool steer_req = GET_BIT(to_send, 30U); - - if (steer_torque_cmd_checks(desired_torque, steer_req, VOLKSWAGEN_MQB_STEERING_LIMITS)) { - tx = false; - } - } - - // Safety check for both ACC_06 and ACC_07 acceleration requests - // To avoid floating point math, scale upward and compare to pre-scaled safety m/s2 boundaries - if ((addr == MSG_ACC_06) || (addr == MSG_ACC_07)) { - bool violation = false; - int desired_accel = 0; - - if (addr == MSG_ACC_06) { - // Signal: ACC_06.ACC_Sollbeschleunigung_02 (acceleration in m/s2, scale 0.005, offset -7.22) - desired_accel = ((((GET_BYTE(to_send, 4) & 0x7U) << 8) | GET_BYTE(to_send, 3)) * 5U) - 7220U; - } else { - // Signal: ACC_07.ACC_Folgebeschl (acceleration in m/s2, scale 0.03, offset -4.6) - int secondary_accel = (GET_BYTE(to_send, 4) * 30U) - 4600U; - violation |= (secondary_accel != 3020); // enforce always inactive (one increment above max range) at this time - // Signal: ACC_07.ACC_Sollbeschleunigung_02 (acceleration in m/s2, scale 0.005, offset -7.22) - desired_accel = (((GET_BYTE(to_send, 7) << 3) | ((GET_BYTE(to_send, 6) & 0xE0U) >> 5)) * 5U) - 7220U; - } - - violation |= longitudinal_accel_checks(desired_accel, VOLKSWAGEN_MQB_LONG_LIMITS); - - if (violation) { - tx = false; - } - } - - // FORCE CANCEL: ensuring that only the cancel button press is sent when controls are off. - // This avoids unintended engagements while still allowing resume spam - if ((addr == MSG_GRA_ACC_01) && !controls_allowed) { - // disallow resume and set: bits 16 and 19 - if ((GET_BYTE(to_send, 2) & 0x9U) != 0U) { - tx = false; - } - } - - return tx; -} - -static int volkswagen_mqb_fwd_hook(int bus_num, int addr) { - int bus_fwd = -1; - - switch (bus_num) { - case 0: - if (addr == MSG_LH_EPS_03) { - // openpilot needs to replace apparent driver steering input torque to pacify VW Emergency Assist - bus_fwd = -1; - } else { - // Forward all remaining traffic from Extended CAN onward - bus_fwd = 2; - } - break; - case 2: - if ((addr == MSG_HCA_01) || (addr == MSG_LDW_02)) { - // openpilot takes over LKAS steering control and related HUD messages from the camera - bus_fwd = -1; - } else if (volkswagen_longitudinal && ((addr == MSG_ACC_02) || (addr == MSG_ACC_06) || (addr == MSG_ACC_07))) { - // openpilot takes over acceleration/braking control and related HUD messages from the stock ACC radar - bus_fwd = -1; - } else { - // Forward all remaining traffic from Extended CAN devices to J533 gateway - bus_fwd = 0; - } - break; - default: - // No other buses should be in use; fallback to do-not-forward - bus_fwd = -1; - break; - } - - return bus_fwd; -} - -const safety_hooks volkswagen_mqb_hooks = { - .init = volkswagen_mqb_init, - .rx = volkswagen_mqb_rx_hook, - .tx = volkswagen_mqb_tx_hook, - .fwd = volkswagen_mqb_fwd_hook, - .get_counter = volkswagen_mqb_get_counter, - .get_checksum = volkswagen_mqb_get_checksum, - .compute_checksum = volkswagen_mqb_compute_crc, -}; diff --git a/board/safety/safety_volkswagen_pq.h b/board/safety/safety_volkswagen_pq.h deleted file mode 100644 index 75979a5149..0000000000 --- a/board/safety/safety_volkswagen_pq.h +++ /dev/null @@ -1,259 +0,0 @@ -#pragma once - -#include "safety_declarations.h" -#include "safety_volkswagen_common.h" - -#define MSG_LENKHILFE_3 0x0D0 // RX from EPS, for steering angle and driver steering torque -#define MSG_HCA_1 0x0D2 // TX by OP, Heading Control Assist steering torque -#define MSG_BREMSE_1 0x1A0 // RX from ABS, for ego speed -#define MSG_MOTOR_2 0x288 // RX from ECU, for CC state and brake switch state -#define MSG_ACC_SYSTEM 0x368 // TX by OP, longitudinal acceleration controls -#define MSG_MOTOR_3 0x380 // RX from ECU, for driver throttle input -#define MSG_GRA_NEU 0x38A // TX by OP, ACC control buttons for cancel/resume -#define MSG_MOTOR_5 0x480 // RX from ECU, for ACC main switch state -#define MSG_ACC_GRA_ANZEIGE 0x56A // TX by OP, ACC HUD -#define MSG_LDW_1 0x5BE // TX by OP, Lane line recognition and text alerts - -static uint32_t volkswagen_pq_get_checksum(const CANPacket_t *to_push) { - int addr = GET_ADDR(to_push); - - return (uint32_t)GET_BYTE(to_push, (addr == MSG_MOTOR_5) ? 7 : 0); -} - -static uint8_t volkswagen_pq_get_counter(const CANPacket_t *to_push) { - int addr = GET_ADDR(to_push); - uint8_t counter = 0U; - - if (addr == MSG_LENKHILFE_3) { - counter = (uint8_t)(GET_BYTE(to_push, 1) & 0xF0U) >> 4; - } else if (addr == MSG_GRA_NEU) { - counter = (uint8_t)(GET_BYTE(to_push, 2) & 0xF0U) >> 4; - } else { - } - - return counter; -} - -static uint32_t volkswagen_pq_compute_checksum(const CANPacket_t *to_push) { - int addr = GET_ADDR(to_push); - int len = GET_LEN(to_push); - uint8_t checksum = 0U; - int checksum_byte = (addr == MSG_MOTOR_5) ? 7 : 0; - - // Simple XOR over the payload, except for the byte where the checksum lives. - for (int i = 0; i < len; i++) { - if (i != checksum_byte) { - checksum ^= (uint8_t)GET_BYTE(to_push, i); - } - } - - return checksum; -} - -static safety_config volkswagen_pq_init(uint16_t param) { - // Transmit of GRA_Neu is allowed on bus 0 and 2 to keep compatibility with gateway and camera integration - static const CanMsg VOLKSWAGEN_PQ_STOCK_TX_MSGS[] = {{MSG_HCA_1, 0, 5}, {MSG_LDW_1, 0, 8}, - {MSG_GRA_NEU, 0, 4}, {MSG_GRA_NEU, 2, 4}}; - - static const CanMsg VOLKSWAGEN_PQ_LONG_TX_MSGS[] = {{MSG_HCA_1, 0, 5}, {MSG_LDW_1, 0, 8}, - {MSG_ACC_SYSTEM, 0, 8}, {MSG_ACC_GRA_ANZEIGE, 0, 8}}; - - static RxCheck volkswagen_pq_rx_checks[] = { - {.msg = {{MSG_LENKHILFE_3, 0, 6, .check_checksum = true, .max_counter = 15U, .frequency = 100U}, { 0 }, { 0 }}}, - {.msg = {{MSG_BREMSE_1, 0, 8, .check_checksum = false, .max_counter = 0U, .frequency = 100U}, { 0 }, { 0 }}}, - {.msg = {{MSG_MOTOR_2, 0, 8, .check_checksum = false, .max_counter = 0U, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{MSG_MOTOR_3, 0, 8, .check_checksum = false, .max_counter = 0U, .frequency = 100U}, { 0 }, { 0 }}}, - {.msg = {{MSG_MOTOR_5, 0, 8, .check_checksum = true, .max_counter = 0U, .frequency = 50U}, { 0 }, { 0 }}}, - {.msg = {{MSG_GRA_NEU, 0, 4, .check_checksum = true, .max_counter = 15U, .frequency = 30U}, { 0 }, { 0 }}}, - }; - - UNUSED(param); - - volkswagen_set_button_prev = false; - volkswagen_resume_button_prev = false; - -#ifdef ALLOW_DEBUG - volkswagen_longitudinal = GET_FLAG(param, FLAG_VOLKSWAGEN_LONG_CONTROL); -#endif - return volkswagen_longitudinal ? BUILD_SAFETY_CFG(volkswagen_pq_rx_checks, VOLKSWAGEN_PQ_LONG_TX_MSGS) : \ - BUILD_SAFETY_CFG(volkswagen_pq_rx_checks, VOLKSWAGEN_PQ_STOCK_TX_MSGS); -} - -static void volkswagen_pq_rx_hook(const CANPacket_t *to_push) { - if (GET_BUS(to_push) == 0U) { - int addr = GET_ADDR(to_push); - - // Update in-motion state from speed value. - // Signal: Bremse_1.Geschwindigkeit_neu__Bremse_1_ - if (addr == MSG_BREMSE_1) { - int speed = ((GET_BYTE(to_push, 2) & 0xFEU) >> 1) | (GET_BYTE(to_push, 3) << 7); - vehicle_moving = speed > 0; - } - - // Update driver input torque samples - // Signal: Lenkhilfe_3.LH3_LM (absolute torque) - // Signal: Lenkhilfe_3.LH3_LMSign (direction) - if (addr == MSG_LENKHILFE_3) { - int torque_driver_new = GET_BYTE(to_push, 2) | ((GET_BYTE(to_push, 3) & 0x3U) << 8); - int sign = (GET_BYTE(to_push, 3) & 0x4U) >> 2; - if (sign == 1) { - torque_driver_new *= -1; - } - update_sample(&torque_driver, torque_driver_new); - } - - if (volkswagen_longitudinal) { - if (addr == MSG_MOTOR_5) { - // ACC main switch on is a prerequisite to enter controls, exit controls immediately on main switch off - // Signal: Motor_5.GRA_Hauptschalter - acc_main_on = GET_BIT(to_push, 50U); - if (!acc_main_on) { - controls_allowed = false; - } - } - - if (addr == MSG_GRA_NEU) { - // If ACC main switch is on, enter controls on falling edge of Set or Resume - // Signal: GRA_Neu.GRA_Neu_Setzen - // Signal: GRA_Neu.GRA_Neu_Recall - bool set_button = GET_BIT(to_push, 16U); - bool resume_button = GET_BIT(to_push, 17U); - if ((volkswagen_set_button_prev && !set_button) || (volkswagen_resume_button_prev && !resume_button)) { - controls_allowed = acc_main_on; - } - volkswagen_set_button_prev = set_button; - volkswagen_resume_button_prev = resume_button; - // Exit controls on rising edge of Cancel, override Set/Resume if present simultaneously - // Signal: GRA_ACC_01.GRA_Abbrechen - if (GET_BIT(to_push, 9U)) { - controls_allowed = false; - } - } - } else { - if (addr == MSG_MOTOR_2) { - // Enter controls on rising edge of stock ACC, exit controls if stock ACC disengages - // Signal: Motor_2.GRA_Status - int acc_status = (GET_BYTE(to_push, 2) & 0xC0U) >> 6; - bool cruise_engaged = (acc_status == 1) || (acc_status == 2); - pcm_cruise_check(cruise_engaged); - } - } - - // Signal: Motor_3.Fahrpedal_Rohsignal - if (addr == MSG_MOTOR_3) { - gas_pressed = (GET_BYTE(to_push, 2)); - } - - // Signal: Motor_2.Bremslichtschalter - if (addr == MSG_MOTOR_2) { - brake_pressed = (GET_BYTE(to_push, 2) & 0x1U); - } - - generic_rx_checks((addr == MSG_HCA_1)); - } -} - -static bool volkswagen_pq_tx_hook(const CANPacket_t *to_send) { - // lateral limits - const SteeringLimits VOLKSWAGEN_PQ_STEERING_LIMITS = { - .max_steer = 300, // 3.0 Nm (EPS side max of 3.0Nm with fault if violated) - .max_rt_delta = 113, // 6 max rate up * 50Hz send rate * 250000 RT interval / 1000000 = 75 ; 125 * 1.5 for safety pad = 113 - .max_rt_interval = 250000, // 250ms between real time checks - .max_rate_up = 6, // 3.0 Nm/s RoC limit (EPS rack has own soft-limit of 5.0 Nm/s) - .max_rate_down = 10, // 5.0 Nm/s RoC limit (EPS rack has own soft-limit of 5.0 Nm/s) - .driver_torque_factor = 3, - .driver_torque_allowance = 80, - .type = TorqueDriverLimited, - }; - - // longitudinal limits - // acceleration in m/s2 * 1000 to avoid floating point math - const LongitudinalLimits VOLKSWAGEN_PQ_LONG_LIMITS = { - .max_accel = 2000, - .min_accel = -3500, - .inactive_accel = 3010, // VW sends one increment above the max range when inactive - }; - - int addr = GET_ADDR(to_send); - bool tx = true; - - // Safety check for HCA_1 Heading Control Assist torque - // Signal: HCA_1.LM_Offset (absolute torque) - // Signal: HCA_1.LM_Offsign (direction) - if (addr == MSG_HCA_1) { - int desired_torque = GET_BYTE(to_send, 2) | ((GET_BYTE(to_send, 3) & 0x7FU) << 8); - desired_torque = desired_torque / 32; // DBC scale from PQ network to centi-Nm - int sign = (GET_BYTE(to_send, 3) & 0x80U) >> 7; - if (sign == 1) { - desired_torque *= -1; - } - - uint32_t hca_status = ((GET_BYTE(to_send, 1) >> 4) & 0xFU); - bool steer_req = ((hca_status == 5U) || (hca_status == 7U)); - - if (steer_torque_cmd_checks(desired_torque, steer_req, VOLKSWAGEN_PQ_STEERING_LIMITS)) { - tx = false; - } - } - - // Safety check for acceleration commands - // To avoid floating point math, scale upward and compare to pre-scaled safety m/s2 boundaries - if (addr == MSG_ACC_SYSTEM) { - // Signal: ACC_System.ACS_Sollbeschl (acceleration in m/s2, scale 0.005, offset -7.22) - int desired_accel = ((((GET_BYTE(to_send, 4) & 0x7U) << 8) | GET_BYTE(to_send, 3)) * 5U) - 7220U; - - if (longitudinal_accel_checks(desired_accel, VOLKSWAGEN_PQ_LONG_LIMITS)) { - tx = false; - } - } - - // FORCE CANCEL: ensuring that only the cancel button press is sent when controls are off. - // This avoids unintended engagements while still allowing resume spam - if ((addr == MSG_GRA_NEU) && !controls_allowed) { - // Signal: GRA_Neu.GRA_Neu_Setzen - // Signal: GRA_Neu.GRA_Neu_Recall - if (GET_BIT(to_send, 16U) || GET_BIT(to_send, 17U)) { - tx = false; - } - } - - return tx; -} - -static int volkswagen_pq_fwd_hook(int bus_num, int addr) { - int bus_fwd = -1; - - switch (bus_num) { - case 0: - // Forward all traffic from the Extended CAN onward - bus_fwd = 2; - break; - case 2: - if ((addr == MSG_HCA_1) || (addr == MSG_LDW_1)) { - // openpilot takes over LKAS steering control and related HUD messages from the camera - bus_fwd = -1; - } else if (volkswagen_longitudinal && ((addr == MSG_ACC_SYSTEM) || (addr == MSG_ACC_GRA_ANZEIGE))) { - // openpilot takes over acceleration/braking control and related HUD messages from the stock ACC radar - } else { - // Forward all remaining traffic from Extended CAN devices to J533 gateway - bus_fwd = 0; - } - break; - default: - // No other buses should be in use; fallback to do-not-forward - bus_fwd = -1; - break; - } - - return bus_fwd; -} - -const safety_hooks volkswagen_pq_hooks = { - .init = volkswagen_pq_init, - .rx = volkswagen_pq_rx_hook, - .tx = volkswagen_pq_tx_hook, - .fwd = volkswagen_pq_fwd_hook, - .get_counter = volkswagen_pq_get_counter, - .get_checksum = volkswagen_pq_get_checksum, - .compute_checksum = volkswagen_pq_compute_checksum, -}; diff --git a/board/safety_declarations.h b/board/safety_declarations.h deleted file mode 100644 index b0e9588e95..0000000000 --- a/board/safety_declarations.h +++ /dev/null @@ -1,283 +0,0 @@ -#pragma once - -#include -#include - -#define GET_BIT(msg, b) ((bool)!!(((msg)->data[((b) / 8U)] >> ((b) % 8U)) & 0x1U)) -#define GET_BYTE(msg, b) ((msg)->data[(b)]) -#define GET_FLAG(value, mask) (((__typeof__(mask))(value) & (mask)) == (mask)) // cppcheck-suppress misra-c2012-1.2; allow __typeof__ - -#define BUILD_SAFETY_CFG(rx, tx) ((safety_config){(rx), (sizeof((rx)) / sizeof((rx)[0])), \ - (tx), (sizeof((tx)) / sizeof((tx)[0]))}) -#define SET_RX_CHECKS(rx, config) \ - do { \ - (config).rx_checks = (rx); \ - (config).rx_checks_len = sizeof((rx)) / sizeof((rx)[0]); \ - } while (0); - -#define SET_TX_MSGS(tx, config) \ - do { \ - (config).tx_msgs = (tx); \ - (config).tx_msgs_len = sizeof((tx)) / sizeof((tx)[0]); \ - } while(0); - -#define UPDATE_VEHICLE_SPEED(val_ms) (update_sample(&vehicle_speed, ROUND((val_ms) * VEHICLE_SPEED_FACTOR))) - -uint32_t GET_BYTES(const CANPacket_t *msg, int start, int len); - -extern const int MAX_WRONG_COUNTERS; -#define MAX_ADDR_CHECK_MSGS 3U -#define MAX_SAMPLE_VALS 6 -// used to represent floating point vehicle speed in a sample_t -#define VEHICLE_SPEED_FACTOR 100.0 - - -// sample struct that keeps 6 samples in memory -struct sample_t { - int values[MAX_SAMPLE_VALS]; - int min; - int max; -}; - -// safety code requires floats -struct lookup_t { - float x[3]; - float y[3]; -}; - -typedef struct { - int addr; - int bus; - int len; -} CanMsg; - -typedef enum { - TorqueMotorLimited, // torque steering command, limited by EPS output torque - TorqueDriverLimited, // torque steering command, limited by driver's input torque -} SteeringControlType; - -typedef struct { - // torque cmd limits - const int max_steer; - const int max_rate_up; - const int max_rate_down; - const int max_rt_delta; - const uint32_t max_rt_interval; - - const SteeringControlType type; - - // driver torque limits - const int driver_torque_allowance; - const int driver_torque_factor; - - // motor torque limits - const int max_torque_error; - - // safety around steer req bit - const int min_valid_request_frames; - const int max_invalid_request_frames; - const uint32_t min_valid_request_rt_interval; - const bool has_steer_req_tolerance; - - // angle cmd limits - const float angle_deg_to_can; - const struct lookup_t angle_rate_up_lookup; - const struct lookup_t angle_rate_down_lookup; - const int max_angle_error; // used to limit error between meas and cmd while enabled - const float angle_error_min_speed; // minimum speed to start limiting angle error - - const bool enforce_angle_error; // enables max_angle_error check - const bool inactive_angle_is_zero; // if false, enforces angle near meas when disabled (default) -} SteeringLimits; - -typedef struct { - // acceleration cmd limits - const int max_accel; - const int min_accel; - const int inactive_accel; - - // gas & brake cmd limits - // inactive and min gas are 0 on most safety modes - const int max_gas; - const int min_gas; - const int inactive_gas; - const int max_brake; - - // transmission rpm limits - const int max_transmission_rpm; - const int min_transmission_rpm; - const int inactive_transmission_rpm; - - // speed cmd limits - const int inactive_speed; -} LongitudinalLimits; - -typedef struct { - const int addr; - const int bus; - const int len; - const bool check_checksum; // true is checksum check is performed - const uint8_t max_counter; // maximum value of the counter. 0 means that the counter check is skipped - const bool quality_flag; // true is quality flag check is performed - const uint32_t frequency; // expected frequency of the message [Hz] -} CanMsgCheck; - -typedef struct { - // dynamic flags, reset on safety mode init - bool msg_seen; - int index; // if multiple messages are allowed to be checked, this stores the index of the first one seen. only msg[msg_index] will be used - bool valid_checksum; // true if and only if checksum check is passed - int wrong_counters; // counter of wrong counters, saturated between 0 and MAX_WRONG_COUNTERS - bool valid_quality_flag; // true if the message's quality/health/status signals are valid - uint8_t last_counter; // last counter value - uint32_t last_timestamp; // micro-s - bool lagging; // true if and only if the time between updates is excessive -} RxStatus; - -// params and flags about checksum, counter and frequency checks for each monitored address -typedef struct { - const CanMsgCheck msg[MAX_ADDR_CHECK_MSGS]; // check either messages (e.g. honda steer) - RxStatus status; -} RxCheck; - -typedef struct { - RxCheck *rx_checks; - int rx_checks_len; - const CanMsg *tx_msgs; - int tx_msgs_len; -} safety_config; - -typedef uint32_t (*get_checksum_t)(const CANPacket_t *to_push); -typedef uint32_t (*compute_checksum_t)(const CANPacket_t *to_push); -typedef uint8_t (*get_counter_t)(const CANPacket_t *to_push); -typedef bool (*get_quality_flag_valid_t)(const CANPacket_t *to_push); - -typedef safety_config (*safety_hook_init)(uint16_t param); -typedef void (*rx_hook)(const CANPacket_t *to_push); -typedef bool (*tx_hook)(const CANPacket_t *to_send); -typedef int (*fwd_hook)(int bus_num, int addr); - -typedef struct { - safety_hook_init init; - rx_hook rx; - tx_hook tx; - fwd_hook fwd; - get_checksum_t get_checksum; - compute_checksum_t compute_checksum; - get_counter_t get_counter; - get_quality_flag_valid_t get_quality_flag_valid; -} safety_hooks; - -bool safety_rx_hook(const CANPacket_t *to_push); -bool safety_tx_hook(CANPacket_t *to_send); -uint32_t get_ts_elapsed(uint32_t ts, uint32_t ts_last); -int to_signed(int d, int bits); -void update_sample(struct sample_t *sample, int sample_new); -bool get_longitudinal_allowed(void); -int ROUND(float val); -void gen_crc_lookup_table_8(uint8_t poly, uint8_t crc_lut[]); -#ifdef CANFD -void gen_crc_lookup_table_16(uint16_t poly, uint16_t crc_lut[]); -#endif -void generic_rx_checks(bool stock_ecu_detected); -bool steer_torque_cmd_checks(int desired_torque, int steer_req, const SteeringLimits limits); -bool steer_angle_cmd_checks(int desired_angle, bool steer_control_enabled, const SteeringLimits limits); -bool longitudinal_accel_checks(int desired_accel, const LongitudinalLimits limits); -bool longitudinal_speed_checks(int desired_speed, const LongitudinalLimits limits); -bool longitudinal_gas_checks(int desired_gas, const LongitudinalLimits limits); -bool longitudinal_transmission_rpm_checks(int desired_transmission_rpm, const LongitudinalLimits limits); -bool longitudinal_brake_checks(int desired_brake, const LongitudinalLimits limits); -void pcm_cruise_check(bool cruise_engaged); - -void safety_tick(const safety_config *safety_config); - -// This can be set by the safety hooks -extern bool controls_allowed; -extern bool relay_malfunction; -extern bool gas_pressed; -extern bool gas_pressed_prev; -extern bool brake_pressed; -extern bool brake_pressed_prev; -extern bool regen_braking; -extern bool regen_braking_prev; -extern bool cruise_engaged_prev; -extern struct sample_t vehicle_speed; -extern bool vehicle_moving; -extern bool acc_main_on; // referred to as "ACC off" in ISO 15622:2018 -extern int cruise_button_prev; -extern bool safety_rx_checks_invalid; - -// for safety modes with torque steering control -extern int desired_torque_last; // last desired steer torque -extern int rt_torque_last; // last desired torque for real time check -extern int valid_steer_req_count; // counter for steer request bit matching non-zero torque -extern int invalid_steer_req_count; // counter to allow multiple frames of mismatching torque request bit -extern struct sample_t torque_meas; // last 6 motor torques produced by the eps -extern struct sample_t torque_driver; // last 6 driver torques measured -extern uint32_t ts_torque_check_last; -extern uint32_t ts_steer_req_mismatch_last; // last timestamp steer req was mismatched with torque - -// state for controls_allowed timeout logic -extern bool heartbeat_engaged; // openpilot enabled, passed in heartbeat USB command -extern uint32_t heartbeat_engaged_mismatches; // count of mismatches between heartbeat_engaged and controls_allowed - -// for safety modes with angle steering control -extern uint32_t ts_angle_last; -extern int desired_angle_last; -extern struct sample_t angle_meas; // last 6 steer angles/curvatures - -// This can be set with a USB command -// It enables features that allow alternative experiences, like not disengaging on gas press -// It is only either 0 or 1 on mainline comma.ai openpilot - -#define ALT_EXP_DISABLE_DISENGAGE_ON_GAS 1 - -// If using this flag, make sure to communicate to your users that a stock safety feature is now disabled. -#define ALT_EXP_DISABLE_STOCK_AEB 2 - -// If using this flag, be aware that harder braking is more likely to lead to rear endings, -// and that alone this flag doesn't make braking compliant because there's also a time element. -// Setting this flag is used for allowing the full -5.0 to +4.0 m/s^2 at lower speeds -// See ISO 15622:2018 for more information. -#define ALT_EXP_RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX 8 - -// This flag allows AEB to be commanded from openpilot. -#define ALT_EXP_ALLOW_AEB 16 - -extern int alternative_experience; - -// time since safety mode has been changed -extern uint32_t safety_mode_cnt; - -typedef struct { - uint16_t id; - const safety_hooks *hooks; -} safety_hook_config; - -extern uint16_t current_safety_mode; -extern uint16_t current_safety_param; -extern safety_config current_safety_config; - -int safety_fwd_hook(int bus_num, int addr); -int set_safety_hooks(uint16_t mode, uint16_t param); - -extern const safety_hooks body_hooks; -extern const safety_hooks chrysler_hooks; -extern const safety_hooks elm327_hooks; -extern const safety_hooks nooutput_hooks; -extern const safety_hooks alloutput_hooks; -extern const safety_hooks ford_hooks; -extern const safety_hooks gm_hooks; -extern const safety_hooks honda_nidec_hooks; -extern const safety_hooks honda_bosch_hooks; -extern const safety_hooks hyundai_canfd_hooks; -extern const safety_hooks hyundai_hooks; -extern const safety_hooks hyundai_legacy_hooks; -extern const safety_hooks mazda_hooks; -extern const safety_hooks nissan_hooks; -extern const safety_hooks subaru_hooks; -extern const safety_hooks subaru_preglobal_hooks; -extern const safety_hooks tesla_hooks; -extern const safety_hooks toyota_hooks; -extern const safety_hooks volkswagen_mqb_hooks; -extern const safety_hooks volkswagen_pq_hooks; From 175a600f42585d2a6561c53e9ad4df957d044176 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 18 Feb 2025 20:46:44 -0800 Subject: [PATCH 04/18] remove safety test job --- .github/workflows/test.yaml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 52c2a06103..a462225823 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -70,24 +70,6 @@ jobs: - name: Test communication protocols run: $RUN "cd tests/usbprotocol && ./test.sh" - safety: - name: safety - runs-on: ubuntu-latest - strategy: - matrix: - flags: ['', '--ubsan'] - timeout-minutes: 20 - steps: - - uses: actions/checkout@v2 - - name: Build Docker image - run: eval "$BUILD" - - name: Run safety tests - timeout-minutes: 5 - run: | - ${{ env.RUN }} "scons -c -j$(nproc) && \ - scons -j$(nproc) ${{ matrix.flags }} && \ - tests/safety/test.sh" - misra_linter: name: MISRA C:2012 Linter runs-on: ubuntu-latest From af365585f877390168b74bafdd620500b2f349b6 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Tue, 18 Feb 2025 21:57:31 -0800 Subject: [PATCH 05/18] fix compilation --- SConscript | 1 + board/jungle/main.c | 2 +- tests/libpanda/SConscript | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/SConscript b/SConscript index f6501e88cc..7a7e48b74c 100644 --- a/SConscript +++ b/SConscript @@ -84,6 +84,7 @@ def build_project(project_name, project, extra_flags): '..', panda_root, f"{panda_root}/board/", + f"{panda_root}/../opendbc_repo/opendbc/safety/", ] env = Environment( diff --git a/board/jungle/main.c b/board/jungle/main.c index 329ee98a1f..6cbc160c16 100644 --- a/board/jungle/main.c +++ b/board/jungle/main.c @@ -1,7 +1,7 @@ // ********************* Includes ********************* #include "board/config.h" -#include "board/safety.h" +#include "safety.h" #include "board/drivers/pwm.h" #include "board/drivers/usb.h" diff --git a/tests/libpanda/SConscript b/tests/libpanda/SConscript index 2ad405f5e2..15994f853e 100644 --- a/tests/libpanda/SConscript +++ b/tests/libpanda/SConscript @@ -16,7 +16,7 @@ env = Environment( '-Wfatal-errors', '-Wno-pointer-to-int-cast', ], - CPPPATH=[".", "../../board/"], + CPPPATH=[".", "../../board/", "../../../opendbc_repo/opendbc/safety/"], ) if system == "Darwin": env.PrependENVPath('PATH', '/opt/homebrew/bin') From c765bce03088b23a239e61183bd11eb35a709000 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 19 Feb 2025 01:20:32 -0800 Subject: [PATCH 06/18] update ref --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 5525adef1f..8b3274f87a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,7 +37,7 @@ RUN pip3 install --break-system-packages --no-cache-dir $PYTHONPATH/panda/[dev] # TODO: this should be a "pip install" or not even in this repo at all RUN git config --global --add safe.directory $PYTHONPATH/panda -ENV OPENDBC_REF="87a51e38b53d91075419f01b4cd2e625ee7d4516" +ENV OPENDBC_REF="39e10a045a4a5411a64de791ae463461f8a5f37b" RUN cd /tmp/ && \ git clone --depth 1 https://github.com/commaai/opendbc opendbc_repo && \ cd opendbc_repo && git fetch origin $OPENDBC_REF && git checkout FETCH_HEAD && rm -rf .git/ && \ From c22dd7b86a622d076ee4f1939ffca3b957648871 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 19 Feb 2025 01:20:47 -0800 Subject: [PATCH 07/18] rm safety mutation test --- .github/workflows/test.yaml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index a462225823..032596c9e6 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -98,20 +98,6 @@ jobs: timeout-minutes: 5 run: ${{ env.RUN }} "cd tests/misra && pytest -n8 test_mutation.py" - mutation: - name: Mutation tests - runs-on: ubuntu-latest - timeout-minutes: 20 - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 # need master to get diff - - name: Build Docker image - run: eval "$BUILD" - - name: Mutation tests - timeout-minutes: 5 - run: ${{ env.RUN }} "GIT_REF=${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.event.before || 'origin/master' }} cd tests/safety && ./mutation.sh" - static_analysis: name: static analysis runs-on: ubuntu-latest From a037aef79b4c396110c862da0cf4146db4aeae35 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 19 Feb 2025 01:22:57 -0800 Subject: [PATCH 08/18] fix misra test --- tests/misra/test_misra.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/misra/test_misra.sh b/tests/misra/test_misra.sh index 64a6bf27a8..c53d1bb6ae 100755 --- a/tests/misra/test_misra.sh +++ b/tests/misra/test_misra.sh @@ -47,6 +47,7 @@ cppcheck() { $CPPCHECK_DIR/cppcheck --inline-suppr -I $PANDA_DIR/board/ \ -I "$(arm-none-eabi-gcc -print-file-name=include)" \ -I $PANDA_DIR/board/stm32f4/inc/ -I $PANDA_DIR/board/stm32h7/inc/ \ + -I $PANDA_DIR/../opendbc_repo/opendbc/safety/ \ --suppressions-list=$DIR/suppressions.txt --suppress=*:*inc/* \ --suppress=*:*include/* --error-exitcode=2 --check-level=exhaustive --safety \ --platform=arm32-wchar_t4 $COMMON_DEFINES --checkers-report=$CHECKLIST.tmp \ From ec764151d7a754fe916c33a67e0178fe0e29a4bf Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 19 Feb 2025 01:39:12 -0800 Subject: [PATCH 09/18] no safety here --- tests/misra/test_mutation.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/misra/test_mutation.py b/tests/misra/test_mutation.py index 796b93dd27..7b4e1833fd 100755 --- a/tests/misra/test_mutation.py +++ b/tests/misra/test_mutation.py @@ -32,8 +32,6 @@ ("board/stm32f4/llbxcan.h", "s/1U/1/g", True), # H7 only ("board/stm32h7/llfdcan.h", "s/return ret;/if (true) { return ret; } else { return false; }/g", True), - # general safety - ("board/safety/safety_toyota.h", "s/is_lkas_msg =.*;/is_lkas_msg = addr == 1 || addr == 2;/g", True), ] patterns = [ @@ -61,7 +59,7 @@ all_files = glob.glob('board/**', root_dir=ROOT, recursive=True) files = [f for f in all_files if f.endswith(('.c', '.h')) and not f.startswith(IGNORED_PATHS)] -assert len(files) > 70, all(d in files for d in ('board/main.c', 'board/stm32f4/llbxcan.h', 'board/stm32h7/llfdcan.h', 'board/safety/safety_toyota.h')) +assert len(files) > 70, all(d in files for d in ('board/main.c', 'board/stm32f4/llbxcan.h', 'board/stm32h7/llfdcan.h')) for p in patterns: mutations.append((random.choice(files), p, True)) @@ -77,6 +75,6 @@ def test_misra_mutation(fn, patch, should_fail): assert r == 0 # run test - r = subprocess.run("SKIP_TABLES_DIFF=1 tests/misra/test_misra.sh", cwd=tmp, shell=True) + r = subprocess.run("SKIP_TABLES_DIFF=1 SKIP_BUILD=1 tests/misra/test_misra.sh", cwd=tmp, shell=True) failed = r.returncode != 0 assert failed == should_fail From 6d24f5f7273836651d8dff697bfb37142945ee8b Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 19 Feb 2025 01:57:42 -0800 Subject: [PATCH 10/18] fix build for both local and docker --- SConscript | 2 +- tests/libpanda/SConscript | 2 +- tests/misra/test_misra.sh | 2 +- tests/misra/test_mutation.py | 8 +++++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/SConscript b/SConscript index 7a7e48b74c..f617410b43 100644 --- a/SConscript +++ b/SConscript @@ -84,7 +84,7 @@ def build_project(project_name, project, extra_flags): '..', panda_root, f"{panda_root}/board/", - f"{panda_root}/../opendbc_repo/opendbc/safety/", + f"{panda_root}/../opendbc/safety/", ] env = Environment( diff --git a/tests/libpanda/SConscript b/tests/libpanda/SConscript index 15994f853e..a690ba7577 100644 --- a/tests/libpanda/SConscript +++ b/tests/libpanda/SConscript @@ -16,7 +16,7 @@ env = Environment( '-Wfatal-errors', '-Wno-pointer-to-int-cast', ], - CPPPATH=[".", "../../board/", "../../../opendbc_repo/opendbc/safety/"], + CPPPATH=[".", "../../board/", "../../../opendbc/safety/"], ) if system == "Darwin": env.PrependENVPath('PATH', '/opt/homebrew/bin') diff --git a/tests/misra/test_misra.sh b/tests/misra/test_misra.sh index c53d1bb6ae..aaa7b0c365 100755 --- a/tests/misra/test_misra.sh +++ b/tests/misra/test_misra.sh @@ -47,7 +47,7 @@ cppcheck() { $CPPCHECK_DIR/cppcheck --inline-suppr -I $PANDA_DIR/board/ \ -I "$(arm-none-eabi-gcc -print-file-name=include)" \ -I $PANDA_DIR/board/stm32f4/inc/ -I $PANDA_DIR/board/stm32h7/inc/ \ - -I $PANDA_DIR/../opendbc_repo/opendbc/safety/ \ + -I $PANDA_DIR/../opendbc/safety/ \ --suppressions-list=$DIR/suppressions.txt --suppress=*:*inc/* \ --suppress=*:*include/* --error-exitcode=2 --check-level=exhaustive --safety \ --platform=arm32-wchar_t4 $COMMON_DEFINES --checkers-report=$CHECKLIST.tmp \ diff --git a/tests/misra/test_mutation.py b/tests/misra/test_mutation.py index 7b4e1833fd..a01b1a3390 100755 --- a/tests/misra/test_mutation.py +++ b/tests/misra/test_mutation.py @@ -29,6 +29,7 @@ # default (None, None, False), # F4 only + ("board/stm32f4/llbxcan.h", "", False), # no change should pass ("board/stm32f4/llbxcan.h", "s/1U/1/g", True), # H7 only ("board/stm32h7/llfdcan.h", "s/return ret;/if (true) { return ret; } else { return false; }/g", True), @@ -67,14 +68,15 @@ @pytest.mark.parametrize("fn, patch, should_fail", mutations) def test_misra_mutation(fn, patch, should_fail): with tempfile.TemporaryDirectory() as tmp: - shutil.copytree(ROOT, tmp, dirs_exist_ok=True) + shutil.copytree(ROOT, tmp + "/panda", dirs_exist_ok=True) + shutil.copytree(ROOT + "../opendbc_repo/opendbc", tmp + "/opendbc", dirs_exist_ok=True) # apply patch if fn is not None: - r = os.system(f"cd {tmp} && sed -i '{patch}' {fn}") + r = os.system(f"cd {tmp}/panda && sed -i '{patch}' {fn}") assert r == 0 # run test - r = subprocess.run("SKIP_TABLES_DIFF=1 SKIP_BUILD=1 tests/misra/test_misra.sh", cwd=tmp, shell=True) + r = subprocess.run("SKIP_TABLES_DIFF=1 panda/tests/misra/test_misra.sh", cwd=tmp, shell=True) failed = r.returncode != 0 assert failed == should_fail From b8cb2adbbed9c3d309da37de96f08c7b77b4ad42 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 19 Feb 2025 01:59:32 -0800 Subject: [PATCH 11/18] already tested --- tests/misra/test_mutation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/misra/test_mutation.py b/tests/misra/test_mutation.py index a01b1a3390..e9084dca51 100755 --- a/tests/misra/test_mutation.py +++ b/tests/misra/test_mutation.py @@ -29,7 +29,6 @@ # default (None, None, False), # F4 only - ("board/stm32f4/llbxcan.h", "", False), # no change should pass ("board/stm32f4/llbxcan.h", "s/1U/1/g", True), # H7 only ("board/stm32h7/llfdcan.h", "s/return ret;/if (true) { return ret; } else { return false; }/g", True), From 060c54ad9e00b17685e7d4bd0c141d2d2d5bfe7d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 19 Feb 2025 02:05:39 -0800 Subject: [PATCH 12/18] fix misra mutation --- tests/misra/test_mutation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/misra/test_mutation.py b/tests/misra/test_mutation.py index e9084dca51..db955a8f44 100755 --- a/tests/misra/test_mutation.py +++ b/tests/misra/test_mutation.py @@ -68,7 +68,7 @@ def test_misra_mutation(fn, patch, should_fail): with tempfile.TemporaryDirectory() as tmp: shutil.copytree(ROOT, tmp + "/panda", dirs_exist_ok=True) - shutil.copytree(ROOT + "../opendbc_repo/opendbc", tmp + "/opendbc", dirs_exist_ok=True) + shutil.copytree(ROOT + "../opendbc", tmp + "/opendbc", dirs_exist_ok=True) # apply patch if fn is not None: From f76ea8e62173680226de73a695d3c212bca71654 Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 19 Feb 2025 15:57:19 -0800 Subject: [PATCH 13/18] move Safety Model readme section to opendbc --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index b906d03f82..781afc7979 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,7 @@ panda speaks CAN and CAN FD, and it runs on [STM32F413](https://www.st.com/resou ## Safety Model -When a panda powers up, by default it's in `SAFETY_SILENT` mode. While in `SAFETY_SILENT` mode, the CAN buses are forced to be silent. In order to send messages, you have to select a safety mode. Some of safety modes (for example `SAFETY_ALLOUTPUT`) are disabled in release firmwares. In order to use them, compile and flash your own build. - -Safety modes optionally support `controls_allowed`, which allows or blocks a subset of messages based on a customizable state in the board. +panda is compiled with safety firmware provided by [opendbc](https://github.com/commaai/opendbc). See details about the car safety models, safety testing, and code rigor on that repository. ## Code Rigor From e984eed5fa7354286bf12b3810cafce2e3571b6d Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 19 Feb 2025 15:58:52 -0800 Subject: [PATCH 14/18] and panda --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 781afc7979..4baf69233d 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ panda is compiled with safety firmware provided by [opendbc](https://github.com/ ## Code Rigor -The panda firmware is written for its use in conjunction with [openpilot](https://github.com/commaai/openpilot). The panda firmware, through its safety model, provides and enforces the +The panda firmware is written for its use in conjunction with [openpilot](https://github.com/commaai/openpilot) and [panda](https://github.com/commaai/panda). The panda firmware, through its safety model, provides and enforces the [openpilot safety](https://github.com/commaai/openpilot/blob/master/docs/SAFETY.md). Due to its critical function, it's important that the application code rigor within the `board` folder is held to high standards. These are the [CI regression tests](https://github.com/commaai/panda/actions) we have in place: From bea58c067cd4016485d9300c5feb679e8a25eb2f Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 19 Feb 2025 15:59:28 -0800 Subject: [PATCH 15/18] fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4baf69233d..781afc7979 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ panda is compiled with safety firmware provided by [opendbc](https://github.com/ ## Code Rigor -The panda firmware is written for its use in conjunction with [openpilot](https://github.com/commaai/openpilot) and [panda](https://github.com/commaai/panda). The panda firmware, through its safety model, provides and enforces the +The panda firmware is written for its use in conjunction with [openpilot](https://github.com/commaai/openpilot). The panda firmware, through its safety model, provides and enforces the [openpilot safety](https://github.com/commaai/openpilot/blob/master/docs/SAFETY.md). Due to its critical function, it's important that the application code rigor within the `board` folder is held to high standards. These are the [CI regression tests](https://github.com/commaai/panda/actions) we have in place: From 965c407a51789183b9f4982a6950db97a8f3475b Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 19 Feb 2025 16:01:07 -0800 Subject: [PATCH 16/18] in --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 781afc7979..71902ca1f0 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ panda speaks CAN and CAN FD, and it runs on [STM32F413](https://www.st.com/resou ## Safety Model -panda is compiled with safety firmware provided by [opendbc](https://github.com/commaai/opendbc). See details about the car safety models, safety testing, and code rigor on that repository. +panda is compiled with safety firmware provided by [opendbc](https://github.com/commaai/opendbc). See details about the car safety models, safety testing, and code rigor in that repository. ## Code Rigor From b658ebcc4a4c71f2b561fe98c0fc95386c00a17b Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 19 Feb 2025 16:01:54 -0800 Subject: [PATCH 17/18] not here --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 71902ca1f0..35fe59021e 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ These are the [CI regression tests](https://github.com/commaai/panda/actions) we * A generic static code analysis is performed by [cppcheck](https://github.com/danmar/cppcheck/). * In addition, [cppcheck](https://github.com/danmar/cppcheck/) has a specific addon to check for [MISRA C:2012](https://misra.org.uk/) violations. See [current coverage](https://github.com/commaai/panda/blob/master/tests/misra/coverage_table). * Compiler options are relatively strict: the flags `-Wall -Wextra -Wstrict-prototypes -Werror` are enforced. -* The [safety logic](https://github.com/commaai/panda/tree/master/board/safety) is tested and verified by [unit tests](https://github.com/commaai/panda/tree/master/tests/safety) for each supported car variant. +* The [safety logic](https://github.com/commaai/panda/tree/master/opendbc/safety) is tested and verified by [unit tests](https://github.com/commaai/panda/tree/master/tests/safety) for each supported car variant. to ensure that the behavior remains unchanged. * A hardware-in-the-loop test verifies panda's functionalities on all active panda variants, including: * additional safety model checks @@ -38,7 +38,6 @@ to ensure that the behavior remains unchanged. The above tests are themselves tested by: * a [mutation test](tests/misra/test_mutation.py) on the MISRA coverage -* 100% line coverage enforced on the safety unit tests In addition, we run the [ruff linter](https://github.com/astral-sh/ruff) and [mypy](https://mypy-lang.org/) on panda's Python library. From 9531e592df9107300593b6e0eb561ba951759acc Mon Sep 17 00:00:00 2001 From: Shane Smiskol Date: Wed, 19 Feb 2025 16:02:15 -0800 Subject: [PATCH 18/18] not here --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 35fe59021e..174a9cc432 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ These are the [CI regression tests](https://github.com/commaai/panda/actions) we * A generic static code analysis is performed by [cppcheck](https://github.com/danmar/cppcheck/). * In addition, [cppcheck](https://github.com/danmar/cppcheck/) has a specific addon to check for [MISRA C:2012](https://misra.org.uk/) violations. See [current coverage](https://github.com/commaai/panda/blob/master/tests/misra/coverage_table). * Compiler options are relatively strict: the flags `-Wall -Wextra -Wstrict-prototypes -Werror` are enforced. -* The [safety logic](https://github.com/commaai/panda/tree/master/opendbc/safety) is tested and verified by [unit tests](https://github.com/commaai/panda/tree/master/tests/safety) for each supported car variant. +* The [safety logic](https://github.com/commaai/panda/tree/master/opendbc/safety) is tested and verified by [unit tests](https://github.com/commaai/panda/tree/master/opendbc/safety/tests) for each supported car variant. to ensure that the behavior remains unchanged. * A hardware-in-the-loop test verifies panda's functionalities on all active panda variants, including: * additional safety model checks