nopenpilot/panda/board/safety/safety_gm.h

229 lines
6.7 KiB
C

// board enforces
// in-state
// accel set/resume
// out-state
// cancel button
// regen paddle
// accel rising edge
// brake rising edge
// brake > 0mph
const int GM_MAX_STEER = 300;
const int GM_MAX_RT_DELTA = 128; // max delta torque allowed for real time checks
const uint32_t GM_RT_INTERVAL = 250000; // 250ms between real time checks
const int GM_MAX_RATE_UP = 7;
const int GM_MAX_RATE_DOWN = 17;
const int GM_DRIVER_TORQUE_ALLOWANCE = 50;
const int GM_DRIVER_TORQUE_FACTOR = 4;
const int GM_MAX_GAS = 3072;
const int GM_MAX_REGEN = 1404;
const int GM_MAX_BRAKE = 350;
const CanMsg GM_TX_MSGS[] = {{384, 0, 4}, {1033, 0, 7}, {1034, 0, 7}, {715, 0, 8}, {880, 0, 6}, // pt bus
{161, 1, 7}, {774, 1, 8}, {776, 1, 7}, {784, 1, 2}, // obs bus
{789, 2, 5}, // ch bus
{0x104c006c, 3, 3}, {0x10400060, 3, 5}}; // gmlan
// TODO: do checksum and counter checks. Add correct timestep, 0.1s for now.
AddrCheckStruct gm_addr_checks[] = {
{.msg = {{388, 0, 8, .expected_timestep = 100000U}, { 0 }, { 0 }}},
{.msg = {{842, 0, 5, .expected_timestep = 100000U}, { 0 }, { 0 }}},
{.msg = {{481, 0, 7, .expected_timestep = 100000U}, { 0 }, { 0 }}},
{.msg = {{201, 0, 8, .expected_timestep = 100000U}, { 0 }, { 0 }}},
{.msg = {{452, 0, 8, .expected_timestep = 100000U}, { 0 }, { 0 }}},
};
#define GM_RX_CHECK_LEN (sizeof(gm_addr_checks) / sizeof(gm_addr_checks[0]))
addr_checks gm_rx_checks = {gm_addr_checks, GM_RX_CHECK_LEN};
enum {
GM_BTN_UNPRESS = 1,
GM_BTN_RESUME = 2,
GM_BTN_SET = 3,
GM_BTN_CANCEL = 6,
};
static int gm_rx_hook(CANPacket_t *to_push) {
bool valid = addr_safety_check(to_push, &gm_rx_checks, NULL, NULL, NULL);
if (valid && (GET_BUS(to_push) == 0U)) {
int addr = GET_ADDR(to_push);
if (addr == 388) {
int torque_driver_new = ((GET_BYTE(to_push, 6) & 0x7U) << 8) | GET_BYTE(to_push, 7);
torque_driver_new = to_signed(torque_driver_new, 11);
// update array of samples
update_sample(&torque_driver, torque_driver_new);
}
// sample speed, really only care if car is moving or not
// rear left wheel speed
if (addr == 842) {
vehicle_moving = GET_BYTE(to_push, 0) | GET_BYTE(to_push, 1);
}
// ACC steering wheel buttons
if (addr == 481) {
int button = (GET_BYTE(to_push, 5) & 0x70U) >> 4;
// exit controls on cancel press
if (button == GM_BTN_CANCEL) {
controls_allowed = 0;
}
// enter controls on falling edge of set or resume
bool set = (button == GM_BTN_UNPRESS) && (cruise_button_prev == GM_BTN_SET);
bool res = (button == GM_BTN_UNPRESS) && (cruise_button_prev == GM_BTN_RESUME);
if (set || res) {
controls_allowed = 1;
}
cruise_button_prev = button;
}
if (addr == 201) {
brake_pressed = GET_BIT(to_push, 40U) != 0U;
}
if (addr == 452) {
gas_pressed = GET_BYTE(to_push, 5) != 0U;
}
// exit controls on regen paddle
if (addr == 189) {
bool regen = GET_BYTE(to_push, 0) & 0x20U;
if (regen) {
controls_allowed = 0;
}
}
// Check if ASCM or LKA camera are online
// on powertrain bus.
// 384 = ASCMLKASteeringCmd
// 715 = ASCMGasRegenCmd
generic_rx_checks(((addr == 384) || (addr == 715)));
}
return valid;
}
// all commands: gas/regen, friction brake and steering
// if controls_allowed and no pedals pressed
// allow all commands up to limit
// else
// block all commands that produce actuation
static int gm_tx_hook(CANPacket_t *to_send) {
int tx = 1;
int addr = GET_ADDR(to_send);
if (!msg_allowed(to_send, GM_TX_MSGS, sizeof(GM_TX_MSGS)/sizeof(GM_TX_MSGS[0]))) {
tx = 0;
}
// disallow actuator commands if gas or brake (with vehicle moving) are pressed
// and the the latching controls_allowed flag is True
int pedal_pressed = brake_pressed_prev && vehicle_moving;
bool alt_exp_allow_gas = alternative_experience & ALT_EXP_DISABLE_DISENGAGE_ON_GAS;
if (!alt_exp_allow_gas) {
pedal_pressed = pedal_pressed || gas_pressed_prev;
}
bool current_controls_allowed = controls_allowed && !pedal_pressed;
// BRAKE: safety check
if (addr == 789) {
int brake = ((GET_BYTE(to_send, 0) & 0xFU) << 8) + GET_BYTE(to_send, 1);
brake = (0x1000 - brake) & 0xFFF;
if (!current_controls_allowed) {
if (brake != 0) {
tx = 0;
}
}
if (brake > GM_MAX_BRAKE) {
tx = 0;
}
}
// LKA STEER: safety check
if (addr == 384) {
int desired_torque = ((GET_BYTE(to_send, 0) & 0x7U) << 8) + GET_BYTE(to_send, 1);
uint32_t ts = microsecond_timer_get();
bool violation = 0;
desired_torque = to_signed(desired_torque, 11);
if (current_controls_allowed) {
// *** global torque limit check ***
violation |= max_limit_check(desired_torque, GM_MAX_STEER, -GM_MAX_STEER);
// *** torque rate limit check ***
violation |= driver_limit_check(desired_torque, desired_torque_last, &torque_driver,
GM_MAX_STEER, GM_MAX_RATE_UP, GM_MAX_RATE_DOWN,
GM_DRIVER_TORQUE_ALLOWANCE, GM_DRIVER_TORQUE_FACTOR);
// used next time
desired_torque_last = desired_torque;
// *** torque real time rate limit check ***
violation |= rt_rate_limit_check(desired_torque, rt_torque_last, GM_MAX_RT_DELTA);
// every RT_INTERVAL set the new limits
uint32_t ts_elapsed = get_ts_elapsed(ts, ts_last);
if (ts_elapsed > GM_RT_INTERVAL) {
rt_torque_last = desired_torque;
ts_last = ts;
}
}
// no torque if controls is not allowed
if (!current_controls_allowed && (desired_torque != 0)) {
violation = 1;
}
// reset to 0 if either controls is not allowed or there's a violation
if (violation || !current_controls_allowed) {
desired_torque_last = 0;
rt_torque_last = 0;
ts_last = ts;
}
if (violation) {
tx = 0;
}
}
// GAS/REGEN: safety check
if (addr == 715) {
int gas_regen = ((GET_BYTE(to_send, 2) & 0x7FU) << 5) + ((GET_BYTE(to_send, 3) & 0xF8U) >> 3);
// Disabled message is !engaged with gas
// value that corresponds to max regen.
if (!current_controls_allowed) {
bool apply = GET_BYTE(to_send, 0) & 1U;
if (apply || (gas_regen != GM_MAX_REGEN)) {
tx = 0;
}
}
if (gas_regen > GM_MAX_GAS) {
tx = 0;
}
}
// 1 allows the message through
return tx;
}
static const addr_checks* gm_init(int16_t param) {
UNUSED(param);
controls_allowed = false;
relay_malfunction_reset();
return &gm_rx_checks;
}
const safety_hooks gm_hooks = {
.init = gm_init,
.rx = gm_rx_hook,
.tx = gm_tx_hook,
.tx_lin = nooutput_tx_lin_hook,
.fwd = default_fwd_hook,
};