Files
panda-meb/tests/safety/test_ford.py
DevTekVE 4c2ccd7fe5 Modular Assistive Driving System (MADS) (#40)
* 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>
2024-12-05 19:39:36 -05:00

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()