mirror of
https://github.com/infiniteCable2/panda.git
synced 2026-02-18 17:23:52 +08:00
* alternative experience * safety init * fix * more update * not really * misra * Add Custom MIT License (#38) * brake check was not handled * revert * alt -> lkas * explicit checks * support toyota and ford * rename * hyundai can-fd support * only allow lkas if enabled * hyundai: main button handling * revert * hyundai: main button heartbeat * add logging for controls allowed lateral * fix panda safety * ford btn * toyota btn * fca btn * honda btn * mads safety tests * more tests * safety misra * safety mutation * misra * mutation experiment * fix * ford test main button * ford test lkas button * more ford test * hyundai lkas and main * more ford * hyundai canfd * rename * rename * cleaner * more fixes * more hyundai tests * no longer needed * thanks for tests! * more tests for lat * more explicit * make sure to reset * try this out * probably needed * move * misra * not needed * move to safety_mads * not really needed * remove * MADS: Refactor MADS safety with improved state management (pull request #46) Refactor MADS safety with improved state management This commit introduces a major refactoring of the MADS safety module, improving state management and control flow. Key changes include: Core Changes: - Introduced a MADSState struct to centralize state management - Removed global state variables in favor of structured state - Implemented button transition handling with explicit state tracking (PRESSED/RELEASED/NO_CHANGE) - Added state flags for button availability detection - Simplified lateral control permission logic Button Handling: - Separated main button and LKAS button state tracking - Added independent engagement states for each button - Improved button press detection across multiple platforms - Added support for main and LKAS buttons on Hyundai platforms - Modified ACC main state handling Testing: - Added comprehensive test coverage for MADS state transitions - Added new MADS-specific test base class for consistent testing across platforms - Added mutation testing for state management - Extended timeout for mutation tests from 5 to 8 minutes - Added extensive button press validation tests - Enhanced debugging output in replay drive tests The refactored code provides a more organized implementation of MADS safety features while maintaining compatibility with existing safety checks. * adding note * adding ford (WIP) * adding honda (WIP) * adding toyota (WIP) * adding chrysler (WIP) * Standardize Button State Handling Across Platforms Refactor button state handling by replacing integer constants with an enumerated `ButtonState` type and updating logic to improve readability and maintainability. This change affects button press detection in Ford, Honda, Hyundai, and Toyota safety modules and aligns them with a unified MADS button state approach. Enums provide a clearer understanding of button states and transitions, facilitating easier maintenance and future enhancements. * Disable LKAS button press logic in Honda and Toyota safety. The code for processing LKAS button presses has been commented out in both Honda and Toyota safety implementations. This change aims to investigate or temporarily halt the button press effects without removing the logic altogether. It will be important to test for any impacts this may have on vehicle control functionality. * Remove commented out code in toyota_rx_hook function This commit cleans up the toyota_rx_hook function by removing unnecessary commented-out code that checks for LKAS button presses on bus 2. This helps improve code readability and maintainability without altering the existing functionality. * GM, mazda, nissan, subaru (global & preglobal) * Honda LKAS * Revert "Remove commented out code in toyota_rx_hook function" This reverts commit d6b012c01a08118d91fad56c9f6ac2f92b671968. * Toyota, Subaru Global LKAS * nissan fix * gm fix * use speed msg to force rx * im bored * misra * subaru/toyota/honda * nope * attempt * go through all buttons * try nissan * more nissan * nissan tests passed! * subaru lkas test (not sure why it's not passing 2 and 3 values) * Improved code organization in safety_subaru.h and test_subaru.py This commit includes a minor restructuring in safety_subaru.h and test_subaru.py for better readability and flow. The condition check in safety_subaru.h for lkas_hud now has explicit parentheses. With regard to test_subaru.py, an unnecessary import was removed, and the sequence of steps in the test was reordered - now enabling mads and cleaning up mads_states happens before each subtest. * Refactor tests to use _speed_msg instead of _user_brake_msg. Updated the MADS safety tests to utilize the _speed_msg(0) function call in place of _user_brake_msg(False). * Reworking the tests a little for clarity * disabling lkas again on toyota temporarily * fix mads condition to engage * hyundai and honda good with new tests * Redoing more tests * update for safety tick ensuring mads control is exited while lagging * Updating tests for toyota * cleaning up tests on hkg * commenting out temp_debug for future use * revert * constants * cleanup * format! * match yota * Apply suggestions from code review * force * explicit checks * revert --------- Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
492 lines
21 KiB
Python
Executable File
492 lines
21 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import numpy as np
|
|
import random
|
|
import unittest
|
|
|
|
import panda.tests.safety.common as common
|
|
|
|
from panda import Panda
|
|
from panda.tests.libpanda import libpanda_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 stock longitudinal
|
|
# * 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: libpanda_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)
|
|
|
|
def test_enable_control_from_main(self):
|
|
for enable_mads in (True, False):
|
|
with self.subTest("enable_mads", mads_enabled=enable_mads):
|
|
self.safety.set_enable_mads(enable_mads, False)
|
|
for main_button_msg_valid in (True, False):
|
|
with self.subTest("main_button_msg_valid", state_valid=main_button_msg_valid):
|
|
self._mads_states_cleanup()
|
|
self._rx(self._pcm_status_msg(main_button_msg_valid))
|
|
self.assertEqual(enable_mads and main_button_msg_valid, self.safety.get_controls_allowed_lat())
|
|
self._mads_states_cleanup()
|
|
|
|
# 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 TestFordStockSafety(TestFordSafetyBase):
|
|
STEER_MESSAGE = MSG_LateralMotionControl
|
|
|
|
TX_MSGS = [
|
|
[MSG_Steering_Data_FD1, 0], [MSG_Steering_Data_FD1, 2], [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 = libpanda_py.libpanda
|
|
self.safety.set_safety_hooks(Panda.SAFETY_FORD, 0)
|
|
self.safety.init_tests()
|
|
|
|
|
|
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 = libpanda_py.libpanda
|
|
self.safety.set_safety_hooks(Panda.SAFETY_FORD, Panda.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 = libpanda_py.libpanda
|
|
self.safety.set_safety_hooks(Panda.SAFETY_FORD, Panda.FLAG_FORD_LONG_CONTROL)
|
|
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 = libpanda_py.libpanda
|
|
self.safety.set_safety_hooks(Panda.SAFETY_FORD, Panda.FLAG_FORD_LONG_CONTROL | Panda.FLAG_FORD_CANFD)
|
|
self.safety.init_tests()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|