#!/usr/bin/env python2 import unittest import numpy as np import libpandasafety_py MAX_RATE_UP = 7 MAX_RATE_DOWN = 17 MAX_STEER = 300 MAX_BRAKE = 350 MAX_GAS = 3072 MAX_REGEN = 1404 MAX_RT_DELTA = 128 RT_INTERVAL = 250000 DRIVER_TORQUE_ALLOWANCE = 50; DRIVER_TORQUE_FACTOR = 4; def twos_comp(val, bits): if val >= 0: return val else: return (2**bits) + val def sign(a): if a > 0: return 1 else: return -1 class TestGmSafety(unittest.TestCase): @classmethod def setUp(cls): cls.safety = libpandasafety_py.libpandasafety cls.safety.gm_init(0) cls.safety.init_tests_gm() def _speed_msg(self, speed): to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send[0].RIR = 842 << 21 to_send[0].RDLR = speed return to_send def _button_msg(self, buttons): to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send[0].RIR = 481 << 21 to_send[0].RDHR = buttons << 12 return to_send def _brake_msg(self, brake): to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send[0].RIR = 241 << 21 to_send[0].RDLR = 0xa00 if brake else 0x900 return to_send def _gas_msg(self, gas): to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send[0].RIR = 417 << 21 to_send[0].RDHR = (1 << 16) if gas else 0 return to_send def _send_brake_msg(self, brake): to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send[0].RIR = 789 << 21 brake = (-brake) & 0xfff to_send[0].RDLR = (brake >> 8) | ((brake &0xff) << 8) return to_send def _send_gas_msg(self, gas): to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send[0].RIR = 715 << 21 to_send[0].RDLR = ((gas & 0x1f) << 27) | ((gas & 0xfe0) << 11) return to_send def _set_prev_torque(self, t): self.safety.set_gm_desired_torque_last(t) self.safety.set_gm_rt_torque_last(t) def _torque_driver_msg(self, torque): to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send[0].RIR = 388 << 21 t = twos_comp(torque, 11) to_send[0].RDHR = (((t >> 8) & 0x7) << 16) | ((t & 0xFF) << 24) return to_send def _torque_msg(self, torque): to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') to_send[0].RIR = 384 << 21 t = twos_comp(torque, 11) to_send[0].RDLR = ((t >> 8) & 0x7) | ((t & 0xFF) << 8) return to_send def test_default_controls_not_allowed(self): self.assertFalse(self.safety.get_controls_allowed()) def test_resume_button(self): RESUME_BTN = 2 self.safety.set_controls_allowed(0) self.safety.gm_rx_hook(self._button_msg(RESUME_BTN)) self.assertTrue(self.safety.get_controls_allowed()) def test_set_button(self): SET_BTN = 3 self.safety.set_controls_allowed(0) self.safety.gm_rx_hook(self._button_msg(SET_BTN)) self.assertTrue(self.safety.get_controls_allowed()) def test_cancel_button(self): CANCEL_BTN = 6 self.safety.set_controls_allowed(1) self.safety.gm_rx_hook(self._button_msg(CANCEL_BTN)) self.assertFalse(self.safety.get_controls_allowed()) def test_disengage_on_brake(self): self.safety.set_controls_allowed(1) self.safety.gm_rx_hook(self._brake_msg(True)) self.assertFalse(self.safety.get_controls_allowed()) def test_allow_brake_at_zero_speed(self): # Brake was already pressed self.safety.gm_rx_hook(self._brake_msg(True)) self.safety.set_controls_allowed(1) self.safety.gm_rx_hook(self._brake_msg(True)) self.assertTrue(self.safety.get_controls_allowed()) self.safety.gm_rx_hook(self._brake_msg(False)) def test_not_allow_brake_when_moving(self): # Brake was already pressed self.safety.gm_rx_hook(self._brake_msg(True)) self.safety.gm_rx_hook(self._speed_msg(100)) self.safety.set_controls_allowed(1) self.safety.gm_rx_hook(self._brake_msg(True)) self.assertFalse(self.safety.get_controls_allowed()) self.safety.gm_rx_hook(self._brake_msg(False)) def test_disengage_on_gas(self): self.safety.set_controls_allowed(1) self.safety.gm_rx_hook(self._gas_msg(True)) self.assertFalse(self.safety.get_controls_allowed()) self.safety.gm_rx_hook(self._gas_msg(False)) def test_allow_engage_with_gas_pressed(self): self.safety.gm_rx_hook(self._gas_msg(True)) self.safety.set_controls_allowed(1) self.safety.gm_rx_hook(self._gas_msg(True)) self.assertTrue(self.safety.get_controls_allowed()) self.safety.gm_rx_hook(self._gas_msg(False)) def test_brake_safety_check(self): for enabled in [0, 1]: for b in range(0, 500): self.safety.set_controls_allowed(enabled) if abs(b) > MAX_BRAKE or (not enabled and b != 0): self.assertFalse(self.safety.gm_tx_hook(self._send_brake_msg(b))) else: self.assertTrue(self.safety.gm_tx_hook(self._send_brake_msg(b))) def test_gas_safety_check(self): for enabled in [0, 1]: for g in range(0, 2**12-1): self.safety.set_controls_allowed(enabled) if abs(g) > MAX_GAS or (not enabled and g != MAX_REGEN): self.assertFalse(self.safety.gm_tx_hook(self._send_gas_msg(g))) else: self.assertTrue(self.safety.gm_tx_hook(self._send_gas_msg(g))) def test_steer_safety_check(self): for enabled in [0, 1]: for t in range(-0x200, 0x200): self.safety.set_controls_allowed(enabled) self._set_prev_torque(t) if abs(t) > MAX_STEER or (not enabled and abs(t) > 0): self.assertFalse(self.safety.gm_tx_hook(self._torque_msg(t))) else: self.assertTrue(self.safety.gm_tx_hook(self._torque_msg(t))) 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_non_realtime_limit_up(self): self.safety.set_gm_torque_driver(0, 0) self.safety.set_controls_allowed(True) self._set_prev_torque(0) self.assertTrue(self.safety.gm_tx_hook(self._torque_msg(MAX_RATE_UP))) self._set_prev_torque(0) self.assertTrue(self.safety.gm_tx_hook(self._torque_msg(-MAX_RATE_UP))) self._set_prev_torque(0) self.assertFalse(self.safety.gm_tx_hook(self._torque_msg(MAX_RATE_UP + 1))) self.safety.set_controls_allowed(True) self._set_prev_torque(0) self.assertFalse(self.safety.gm_tx_hook(self._torque_msg(-MAX_RATE_UP - 1))) def test_non_realtime_limit_down(self): self.safety.set_gm_torque_driver(0, 0) self.safety.set_controls_allowed(True) def test_against_torque_driver(self): self.safety.set_controls_allowed(True) for sign in [-1, 1]: for t in np.arange(0, DRIVER_TORQUE_ALLOWANCE + 1, 1): t *= -sign self.safety.set_gm_torque_driver(t, t) self._set_prev_torque(MAX_STEER * sign) self.assertTrue(self.safety.gm_tx_hook(self._torque_msg(MAX_STEER * sign))) self.safety.set_gm_torque_driver(DRIVER_TORQUE_ALLOWANCE + 1, DRIVER_TORQUE_ALLOWANCE + 1) self.assertFalse(self.safety.gm_tx_hook(self._torque_msg(-MAX_STEER))) # spot check some individual cases for sign in [-1, 1]: driver_torque = (DRIVER_TORQUE_ALLOWANCE + 10) * sign torque_desired = (MAX_STEER - 10 * DRIVER_TORQUE_FACTOR) * sign delta = 1 * sign self._set_prev_torque(torque_desired) self.safety.set_gm_torque_driver(-driver_torque, -driver_torque) self.assertTrue(self.safety.gm_tx_hook(self._torque_msg(torque_desired))) self._set_prev_torque(torque_desired + delta) self.safety.set_gm_torque_driver(-driver_torque, -driver_torque) self.assertFalse(self.safety.gm_tx_hook(self._torque_msg(torque_desired + delta))) self._set_prev_torque(MAX_STEER * sign) self.safety.set_gm_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign) self.assertTrue(self.safety.gm_tx_hook(self._torque_msg((MAX_STEER - MAX_RATE_DOWN) * sign))) self._set_prev_torque(MAX_STEER * sign) self.safety.set_gm_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign) self.assertTrue(self.safety.gm_tx_hook(self._torque_msg(0))) self._set_prev_torque(MAX_STEER * sign) self.safety.set_gm_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign) self.assertFalse(self.safety.gm_tx_hook(self._torque_msg((MAX_STEER - 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_gm() self._set_prev_torque(0) self.safety.set_gm_torque_driver(0, 0) for t in np.arange(0, MAX_RT_DELTA, 1): t *= sign self.assertTrue(self.safety.gm_tx_hook(self._torque_msg(t))) self.assertFalse(self.safety.gm_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1)))) self._set_prev_torque(0) for t in np.arange(0, MAX_RT_DELTA, 1): t *= sign self.assertTrue(self.safety.gm_tx_hook(self._torque_msg(t))) # Increase timer to update rt_torque_last self.safety.set_timer(RT_INTERVAL + 1) self.assertTrue(self.safety.gm_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA - 1)))) self.assertTrue(self.safety.gm_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1)))) if __name__ == "__main__": unittest.main()