337 lines
12 KiB
Python
Executable File
337 lines
12 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import unittest
|
|
import numpy as np
|
|
from panda import Panda
|
|
from panda.tests.safety import libpandasafety_py
|
|
from panda.tests.safety.common import test_relay_malfunction, make_msg, \
|
|
test_manually_enable_controls_allowed, \
|
|
test_spam_can_buses, MAX_WRONG_COUNTERS
|
|
|
|
MAX_BRAKE = 255
|
|
|
|
INTERCEPTOR_THRESHOLD = 328
|
|
TX_MSGS = [[0xE4, 0], [0x194, 0], [0x1FA, 0], [0x200, 0], [0x30C, 0], [0x33D, 0], [0x39F, 0]]
|
|
|
|
HONDA_N_HW = 0
|
|
HONDA_BG_HW = 1
|
|
HONDA_BH_HW = 2
|
|
|
|
def honda_checksum(msg, addr, len_msg):
|
|
checksum = 0
|
|
while addr > 0:
|
|
checksum += addr
|
|
addr >>= 4
|
|
for i in range (0, 2*len_msg):
|
|
if i < 8:
|
|
checksum += (msg.RDLR >> (4 * i))
|
|
else:
|
|
checksum += (msg.RDHR >> (4 * (i - 8)))
|
|
return (8 - checksum) & 0xF
|
|
|
|
|
|
class TestHondaSafety(unittest.TestCase):
|
|
@classmethod
|
|
def setUp(cls):
|
|
cls.safety = libpandasafety_py.libpandasafety
|
|
cls.safety.set_safety_hooks(Panda.SAFETY_HONDA_NIDEC, 0)
|
|
cls.safety.init_tests_honda()
|
|
cls.cnt_speed = 0
|
|
cls.cnt_gas = 0
|
|
cls.cnt_button = 0
|
|
|
|
def _speed_msg(self, speed):
|
|
to_send = make_msg(0, 0x158)
|
|
to_send[0].RDLR = speed
|
|
to_send[0].RDHR |= (self.cnt_speed % 4) << 28
|
|
to_send[0].RDHR |= honda_checksum(to_send[0], 0x158, 8) << 24
|
|
self.cnt_speed += 1
|
|
return to_send
|
|
|
|
def _button_msg(self, buttons, addr):
|
|
honda_hw = self.safety.get_honda_hw()
|
|
bus = 1 if honda_hw == HONDA_BH_HW else 0
|
|
to_send = make_msg(bus, addr)
|
|
to_send[0].RDLR = buttons << 5
|
|
to_send[0].RDHR |= (self.cnt_button % 4) << 28
|
|
to_send[0].RDHR |= honda_checksum(to_send[0], addr, 8) << 24
|
|
self.cnt_button += 1
|
|
return to_send
|
|
|
|
def _brake_msg(self, brake):
|
|
to_send = make_msg(0, 0x17C)
|
|
to_send[0].RDHR = 0x200000 if brake else 0
|
|
to_send[0].RDHR |= (self.cnt_gas % 4) << 28
|
|
to_send[0].RDHR |= honda_checksum(to_send[0], 0x17C, 8) << 24
|
|
self.cnt_gas += 1
|
|
return to_send
|
|
|
|
def _alt_brake_msg(self, brake):
|
|
to_send = make_msg(0, 0x1BE)
|
|
to_send[0].RDLR = 0x10 if brake else 0
|
|
return to_send
|
|
|
|
def _gas_msg(self, gas):
|
|
to_send = make_msg(0, 0x17C)
|
|
to_send[0].RDLR = 1 if gas else 0
|
|
to_send[0].RDHR |= (self.cnt_gas % 4) << 28
|
|
to_send[0].RDHR |= honda_checksum(to_send[0], 0x17C, 8) << 24
|
|
self.cnt_gas += 1
|
|
return to_send
|
|
|
|
def _send_brake_msg(self, brake):
|
|
to_send = make_msg(0, 0x1FA)
|
|
to_send[0].RDLR = ((brake & 0x3) << 14) | ((brake & 0x3FF) >> 2)
|
|
return to_send
|
|
|
|
def _send_interceptor_msg(self, gas, addr):
|
|
to_send = make_msg(0, addr, 6)
|
|
gas2 = gas * 2
|
|
to_send[0].RDLR = ((gas & 0xff) << 8) | ((gas & 0xff00) >> 8) | \
|
|
((gas2 & 0xff) << 24) | ((gas2 & 0xff00) << 8)
|
|
return to_send
|
|
|
|
def _send_steer_msg(self, steer):
|
|
to_send = make_msg(0, 0xE4, 6)
|
|
to_send[0].RDLR = steer
|
|
return to_send
|
|
|
|
def test_spam_can_buses(self):
|
|
self.safety.set_honda_hw(HONDA_N_HW)
|
|
test_spam_can_buses(self, TX_MSGS)
|
|
|
|
def test_relay_malfunction(self):
|
|
test_relay_malfunction(self, 0xE4)
|
|
|
|
def test_default_controls_not_allowed(self):
|
|
self.assertFalse(self.safety.get_controls_allowed())
|
|
|
|
def test_manually_enable_controls_allowed(self):
|
|
test_manually_enable_controls_allowed(self)
|
|
|
|
def test_resume_button(self):
|
|
RESUME_BTN = 4
|
|
self.safety.set_controls_allowed(0)
|
|
self.safety.safety_rx_hook(self._button_msg(RESUME_BTN, 0x1A6))
|
|
self.assertTrue(self.safety.get_controls_allowed())
|
|
|
|
def test_set_button(self):
|
|
SET_BTN = 3
|
|
self.safety.set_controls_allowed(0)
|
|
self.safety.safety_rx_hook(self._button_msg(SET_BTN, 0x1A6))
|
|
self.assertTrue(self.safety.get_controls_allowed())
|
|
|
|
def test_cancel_button(self):
|
|
CANCEL_BTN = 2
|
|
self.safety.set_controls_allowed(1)
|
|
self.safety.safety_rx_hook(self._button_msg(CANCEL_BTN, 0x1A6))
|
|
self.assertFalse(self.safety.get_controls_allowed())
|
|
|
|
def test_sample_speed(self):
|
|
self.assertEqual(0, self.safety.get_honda_moving())
|
|
self.safety.safety_rx_hook(self._speed_msg(100))
|
|
self.assertEqual(1, self.safety.get_honda_moving())
|
|
|
|
def test_prev_brake(self):
|
|
self.assertFalse(self.safety.get_honda_brake_pressed_prev())
|
|
self.safety.safety_rx_hook(self._brake_msg(True))
|
|
self.assertTrue(self.safety.get_honda_brake_pressed_prev())
|
|
|
|
def test_disengage_on_brake(self):
|
|
self.safety.set_controls_allowed(1)
|
|
self.safety.safety_rx_hook(self._brake_msg(1))
|
|
self.assertFalse(self.safety.get_controls_allowed())
|
|
|
|
def test_alt_disengage_on_brake(self):
|
|
self.safety.set_honda_alt_brake_msg(1)
|
|
self.safety.set_controls_allowed(1)
|
|
self.safety.safety_rx_hook(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.safety.safety_rx_hook(self._alt_brake_msg(1))
|
|
self.assertTrue(self.safety.get_controls_allowed())
|
|
|
|
def test_allow_brake_at_zero_speed(self):
|
|
# Brake was already pressed
|
|
self.safety.safety_rx_hook(self._brake_msg(True))
|
|
self.safety.set_controls_allowed(1)
|
|
|
|
self.safety.safety_rx_hook(self._brake_msg(True))
|
|
self.assertTrue(self.safety.get_controls_allowed())
|
|
self.safety.safety_rx_hook(self._brake_msg(False)) # reset no brakes
|
|
|
|
def test_not_allow_brake_when_moving(self):
|
|
# Brake was already pressed
|
|
self.safety.safety_rx_hook(self._brake_msg(True))
|
|
self.safety.safety_rx_hook(self._speed_msg(100))
|
|
self.safety.set_controls_allowed(1)
|
|
|
|
self.safety.safety_rx_hook(self._brake_msg(True))
|
|
self.assertFalse(self.safety.get_controls_allowed())
|
|
|
|
def test_prev_gas(self):
|
|
self.safety.safety_rx_hook(self._gas_msg(False))
|
|
self.assertFalse(self.safety.get_honda_gas_prev())
|
|
self.safety.safety_rx_hook(self._gas_msg(True))
|
|
self.assertTrue(self.safety.get_honda_gas_prev())
|
|
|
|
def test_prev_gas_interceptor(self):
|
|
self.safety.safety_rx_hook(self._send_interceptor_msg(0x0, 0x201))
|
|
self.assertFalse(self.safety.get_gas_interceptor_prev())
|
|
self.safety.safety_rx_hook(self._send_interceptor_msg(0x1000, 0x201))
|
|
self.assertTrue(self.safety.get_gas_interceptor_prev())
|
|
self.safety.safety_rx_hook(self._send_interceptor_msg(0x0, 0x201))
|
|
self.safety.set_gas_interceptor_detected(False)
|
|
|
|
def test_disengage_on_gas(self):
|
|
self.safety.safety_rx_hook(self._gas_msg(0))
|
|
self.safety.set_controls_allowed(1)
|
|
self.safety.safety_rx_hook(self._gas_msg(1))
|
|
self.assertFalse(self.safety.get_controls_allowed())
|
|
|
|
def test_allow_engage_with_gas_pressed(self):
|
|
self.safety.safety_rx_hook(self._gas_msg(1))
|
|
self.safety.set_controls_allowed(1)
|
|
self.safety.safety_rx_hook(self._gas_msg(1))
|
|
self.assertTrue(self.safety.get_controls_allowed())
|
|
|
|
def test_disengage_on_gas_interceptor(self):
|
|
for g in range(0, 0x1000):
|
|
self.safety.safety_rx_hook(self._send_interceptor_msg(0, 0x201))
|
|
self.safety.set_controls_allowed(True)
|
|
self.safety.safety_rx_hook(self._send_interceptor_msg(g, 0x201))
|
|
remain_enabled = g <= INTERCEPTOR_THRESHOLD
|
|
self.assertEqual(remain_enabled, self.safety.get_controls_allowed())
|
|
self.safety.safety_rx_hook(self._send_interceptor_msg(0, 0x201))
|
|
self.safety.set_gas_interceptor_detected(False)
|
|
|
|
def test_allow_engage_with_gas_interceptor_pressed(self):
|
|
self.safety.safety_rx_hook(self._send_interceptor_msg(0x1000, 0x201))
|
|
self.safety.set_controls_allowed(1)
|
|
self.safety.safety_rx_hook(self._send_interceptor_msg(0x1000, 0x201))
|
|
self.assertTrue(self.safety.get_controls_allowed())
|
|
self.safety.safety_rx_hook(self._send_interceptor_msg(0, 0x201))
|
|
self.safety.set_gas_interceptor_detected(False)
|
|
|
|
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, 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 = MAX_BRAKE >= brake >= 0
|
|
else:
|
|
send = brake == 0
|
|
self.assertEqual(send, self.safety.safety_tx_hook(self._send_brake_msg(brake)))
|
|
self.safety.set_honda_fwd_brake(False)
|
|
|
|
def test_gas_interceptor_safety_check(self):
|
|
for gas in np.arange(0, 4000, 100):
|
|
for controls_allowed in [True, False]:
|
|
self.safety.set_controls_allowed(controls_allowed)
|
|
if controls_allowed:
|
|
send = True
|
|
else:
|
|
send = gas == 0
|
|
self.assertEqual(send, self.safety.safety_tx_hook(self._send_interceptor_msg(gas, 0x200)))
|
|
|
|
def test_steer_safety_check(self):
|
|
self.safety.set_controls_allowed(0)
|
|
self.assertTrue(self.safety.safety_tx_hook(self._send_steer_msg(0x0000)))
|
|
self.assertFalse(self.safety.safety_tx_hook(self._send_steer_msg(0x1000)))
|
|
|
|
def test_spam_cancel_safety_check(self):
|
|
RESUME_BTN = 4
|
|
SET_BTN = 3
|
|
CANCEL_BTN = 2
|
|
BUTTON_MSG = 0x296
|
|
for hw in [HONDA_BG_HW, HONDA_BH_HW]:
|
|
self.safety.set_honda_hw(hw)
|
|
self.safety.set_controls_allowed(0)
|
|
self.assertTrue(self.safety.safety_tx_hook(self._button_msg(CANCEL_BTN, BUTTON_MSG)))
|
|
self.assertFalse(self.safety.safety_tx_hook(self._button_msg(RESUME_BTN, BUTTON_MSG)))
|
|
self.assertFalse(self.safety.safety_tx_hook(self._button_msg(SET_BTN, BUTTON_MSG)))
|
|
# do not block resume if we are engaged already
|
|
self.safety.set_controls_allowed(1)
|
|
self.assertTrue(self.safety.safety_tx_hook(self._button_msg(RESUME_BTN, BUTTON_MSG)))
|
|
|
|
def test_rx_hook(self):
|
|
# checksum checks
|
|
SET_BTN = 3
|
|
for msg in ["btn1", "btn2", "gas", "speed"]:
|
|
self.safety.set_controls_allowed(1)
|
|
if msg == "btn1":
|
|
to_push = self._button_msg(SET_BTN, 0x1A6)
|
|
if msg == "btn2":
|
|
to_push = self._button_msg(SET_BTN, 0x296)
|
|
if msg == "gas":
|
|
to_push = self._gas_msg(0)
|
|
if msg == "speed":
|
|
to_push = self._speed_msg(0)
|
|
self.assertTrue(self.safety.safety_rx_hook(to_push))
|
|
to_push[0].RDHR = 0
|
|
self.assertFalse(self.safety.safety_rx_hook(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.cnt_speed = 0
|
|
self.cnt_gas = 0
|
|
self.cnt_button = 0
|
|
if i < MAX_WRONG_COUNTERS:
|
|
self.safety.set_controls_allowed(1)
|
|
self.safety.safety_rx_hook(self._button_msg(SET_BTN, 0x1A6))
|
|
self.safety.safety_rx_hook(self._speed_msg(0))
|
|
self.safety.safety_rx_hook(self._gas_msg(0))
|
|
else:
|
|
self.assertFalse(self.safety.safety_rx_hook(self._button_msg(SET_BTN, 0x1A6)))
|
|
self.assertFalse(self.safety.safety_rx_hook(self._speed_msg(0)))
|
|
self.assertFalse(self.safety.safety_rx_hook(self._gas_msg(0)))
|
|
self.assertFalse(self.safety.get_controls_allowed())
|
|
|
|
# restore counters for future tests with a couple of good messages
|
|
for i in range(2):
|
|
self.safety.set_controls_allowed(1)
|
|
self.safety.safety_rx_hook(self._button_msg(SET_BTN, 0x1A6))
|
|
self.safety.safety_rx_hook(self._speed_msg(0))
|
|
self.safety.safety_rx_hook(self._gas_msg(0))
|
|
self.safety.safety_rx_hook(self._button_msg(SET_BTN, 0x1A6))
|
|
self.assertTrue(self.safety.get_controls_allowed())
|
|
|
|
|
|
def test_fwd_hook(self):
|
|
buss = list(range(0x0, 0x3))
|
|
msgs = list(range(0x1, 0x800))
|
|
fwd_brake = [False, True]
|
|
|
|
self.safety.set_honda_hw(HONDA_N_HW)
|
|
|
|
for f in fwd_brake:
|
|
self.safety.set_honda_fwd_brake(f)
|
|
blocked_msgs = [0xE4, 0x194, 0x33D]
|
|
blocked_msgs += [0x30C, 0x39F]
|
|
if not f:
|
|
blocked_msgs += [0x1FA]
|
|
for b in buss:
|
|
for m in msgs:
|
|
if b == 0:
|
|
fwd_bus = 2
|
|
elif b == 1:
|
|
fwd_bus = -1
|
|
elif b == 2:
|
|
fwd_bus = -1 if m in blocked_msgs else 0
|
|
|
|
# assume len 8
|
|
self.assertEqual(fwd_bus, self.safety.safety_fwd_hook(b, make_msg(b, m, 8)))
|
|
|
|
self.safety.set_honda_fwd_brake(False)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|