openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

416 lines
16 KiB

#pragma once
#include "opendbc/safety/safety_declarations.h"
// Stock longitudinal
#define TOYOTA_BASE_TX_MSGS \
{0x191, 0, 8, .check_relay = true}, {0x412, 0, 8, .check_relay = true}, {0x1D2, 0, 8, .check_relay = false}, /* LKAS + LTA + PCM cancel cmd */ \
#define TOYOTA_COMMON_TX_MSGS \
TOYOTA_BASE_TX_MSGS \
{0x2E4, 0, 5, .check_relay = true}, \
{0x343, 0, 8, .check_relay = false}, /* ACC cancel cmd */ \
#define TOYOTA_COMMON_SECOC_TX_MSGS \
TOYOTA_BASE_TX_MSGS \
{0x2E4, 0, 8, .check_relay = true}, {0x131, 0, 8, .check_relay = true}, \
{0x343, 0, 8, .check_relay = false}, /* ACC cancel cmd */ \
#define TOYOTA_COMMON_LONG_TX_MSGS \
TOYOTA_COMMON_TX_MSGS \
/* DSU bus 0 */ \
{0x283, 0, 7, .check_relay = false}, {0x2E6, 0, 8, .check_relay = false}, {0x2E7, 0, 8, .check_relay = false}, {0x33E, 0, 7, .check_relay = false}, \
{0x344, 0, 8, .check_relay = false}, {0x365, 0, 7, .check_relay = false}, {0x366, 0, 7, .check_relay = false}, {0x4CB, 0, 8, .check_relay = false}, \
/* DSU bus 1 */ \
{0x128, 1, 6, .check_relay = false}, {0x141, 1, 4, .check_relay = false}, {0x160, 1, 8, .check_relay = false}, {0x161, 1, 7, .check_relay = false}, \
{0x470, 1, 4, .check_relay = false}, \
/* PCS_HUD */ \
{0x411, 0, 8, .check_relay = false}, \
/* radar diagnostic address */ \
{0x750, 0, 8, .check_relay = false}, \
/* ACC */ \
{0x343, 0, 8, .check_relay = true}, \
#define TOYOTA_COMMON_RX_CHECKS(lta) \
{.msg = {{ 0xaa, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true, .frequency = 83U}, { 0 }, { 0 }}}, \
{.msg = {{0x260, 0, 8, .ignore_counter = true, .ignore_quality_flag=!(lta), .frequency = 50U}, { 0 }, { 0 }}}, \
#define TOYOTA_RX_CHECKS(lta) \
TOYOTA_COMMON_RX_CHECKS(lta) \
{.msg = {{0x1D2, 0, 8, .ignore_counter = true, .ignore_quality_flag = true, .frequency = 33U}, { 0 }, { 0 }}}, \
{.msg = {{0x226, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true, .frequency = 40U}, { 0 }, { 0 }}}, \
#define TOYOTA_ALT_BRAKE_RX_CHECKS(lta) \
TOYOTA_COMMON_RX_CHECKS(lta) \
{.msg = {{0x1D2, 0, 8, .ignore_counter = true, .ignore_quality_flag = true, .frequency = 33U}, { 0 }, { 0 }}}, \
{.msg = {{0x224, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true, .frequency = 40U}, { 0 }, { 0 }}}, \
#define TOYOTA_SECOC_RX_CHECKS \
TOYOTA_COMMON_RX_CHECKS(false) \
{.msg = {{0x176, 0, 8, .ignore_counter = true, .ignore_quality_flag = true, .frequency = 32U}, { 0 }, { 0 }}}, \
{.msg = {{0x116, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true, .frequency = 42U}, { 0 }, { 0 }}}, \
{.msg = {{0x101, 0, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true, .frequency = 50U}, { 0 }, { 0 }}}, \
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) {
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 = to_signed(angle_meas_new, 16);
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 * KPH_TO_MS);
}
}
}
static bool toyota_tx_hook(const CANPacket_t *to_send) {
const TorqueSteeringLimits TOYOTA_TORQUE_STEERING_LIMITS = {
.max_torque = 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
.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 = 171000, // 171ms; a ~10% buffer on cutting every 19 frames
.has_steer_req_tolerance = true,
};
static const AngleSteeringLimits TOYOTA_ANGLE_STEERING_LIMITS = {
// LTA angle limits
// factor for STEER_TORQUE_SENSOR->STEER_ANGLE and STEERING_LTA->STEER_ANGLE_CMD (1 / 0.0573)
.max_angle = 1657, // EPS only accepts up to 94.9461
.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_ANGLE_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_TORQUE_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_secoc) {
static RxCheck toyota_secoc_rx_checks[] = {
TOYOTA_SECOC_RX_CHECKS
};
SET_RX_CHECKS(toyota_secoc_rx_checks, ret);
} else 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_RX_CHECKS(true)
};
SET_RX_CHECKS(toyota_lta_rx_checks, ret);
} else {
static RxCheck toyota_lka_rx_checks[] = {
TOYOTA_RX_CHECKS(false)
};
static RxCheck toyota_lka_alt_brake_rx_checks[] = {
TOYOTA_ALT_BRAKE_RX_CHECKS(false)
};
if (!toyota_alt_brake) {
SET_RX_CHECKS(toyota_lka_rx_checks, ret);
} else {
SET_RX_CHECKS(toyota_lka_alt_brake_rx_checks, ret);
}
}
return ret;
}
const safety_hooks toyota_hooks = {
.init = toyota_init,
.rx = toyota_rx_hook,
.tx = toyota_tx_hook,
.get_checksum = toyota_get_checksum,
.compute_checksum = toyota_compute_checksum,
.get_quality_flag_valid = toyota_get_quality_flag_valid,
};