const int SUBARU_MAX_STEER = 2047; // 1s // real time torque limit to prevent controls spamming // the real time limit is 1500/sec const int SUBARU_MAX_RT_DELTA = 940; // max delta torque allowed for real time checks const int32_t SUBARU_RT_INTERVAL = 250000; // 250ms between real time checks const int SUBARU_MAX_RATE_UP = 50; const int SUBARU_MAX_RATE_DOWN = 70; const int SUBARU_DRIVER_TORQUE_ALLOWANCE = 60; const int SUBARU_DRIVER_TORQUE_FACTOR = 10; int subaru_cruise_engaged_last = 0; int subaru_rt_torque_last = 0; int subaru_desired_torque_last = 0; uint32_t subaru_ts_last = 0; struct sample_t subaru_torque_driver; // last few driver torques measured static void subaru_init(int16_t param) { #ifdef PANDA lline_relay_init(); #endif } static void subaru_rx_hook(CAN_FIFOMailBox_TypeDef *to_push) { int bus_number = (to_push->RDTR >> 4) & 0xFF; uint32_t addr = to_push->RIR >> 21; if ((addr == 0x119) && (bus_number == 0)){ int torque_driver_new = ((to_push->RDLR >> 16) & 0x7FF); torque_driver_new = to_signed(torque_driver_new, 11); // update array of samples update_sample(&subaru_torque_driver, torque_driver_new); } // enter controls on rising edge of ACC, exit controls on ACC off if ((addr == 0x240) && (bus_number == 0)) { int cruise_engaged = (to_push->RDHR >> 9) & 1; if (cruise_engaged && !subaru_cruise_engaged_last) { controls_allowed = 1; } else if (!cruise_engaged) { controls_allowed = 0; } subaru_cruise_engaged_last = cruise_engaged; } } static int subaru_tx_hook(CAN_FIFOMailBox_TypeDef *to_send) { uint32_t addr = to_send->RIR >> 21; // steer cmd checks if (addr == 0x122) { int desired_torque = ((to_send->RDLR >> 16) & 0x1FFF); int violation = 0; uint32_t ts = TIM2->CNT; desired_torque = to_signed(desired_torque, 13); if (controls_allowed) { // *** global torque limit check *** violation |= max_limit_check(desired_torque, SUBARU_MAX_STEER, -SUBARU_MAX_STEER); // *** torque rate limit check *** int desired_torque_last = subaru_desired_torque_last; violation |= driver_limit_check(desired_torque, desired_torque_last, &subaru_torque_driver, SUBARU_MAX_STEER, SUBARU_MAX_RATE_UP, SUBARU_MAX_RATE_DOWN, SUBARU_DRIVER_TORQUE_ALLOWANCE, SUBARU_DRIVER_TORQUE_FACTOR); // used next time subaru_desired_torque_last = desired_torque; // *** torque real time rate limit check *** violation |= rt_rate_limit_check(desired_torque, subaru_rt_torque_last, SUBARU_MAX_RT_DELTA); // every RT_INTERVAL set the new limits uint32_t ts_elapsed = get_ts_elapsed(ts, subaru_ts_last); if (ts_elapsed > SUBARU_RT_INTERVAL) { subaru_rt_torque_last = desired_torque; subaru_ts_last = ts; } } // no torque if controls is not allowed if (!controls_allowed && (desired_torque != 0)) { violation = 1; } // reset to 0 if either controls is not allowed or there's a violation if (violation || !controls_allowed) { subaru_desired_torque_last = 0; subaru_rt_torque_last = 0; subaru_ts_last = ts; } if (violation) { return false; } } return true; } static int subaru_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd) { // shifts bits 29 > 11 int32_t addr = to_fwd->RIR >> 21; // forward CAN 0 > 1 if (bus_num == 0) { return 2; // ES CAN } // forward CAN 1 > 0, except ES_LKAS else if (bus_num == 2) { // outback 2015 if (addr == 0x164) { return -1; } // global platform if (addr == 0x122) { return -1; } // ES Distance if (addr == 545) { return -1; } // ES LKAS if (addr == 802) { return -1; } return 0; // Main CAN } // fallback to do not forward return -1; } const safety_hooks subaru_hooks = { .init = subaru_init, .rx = subaru_rx_hook, .tx = subaru_tx_hook, .tx_lin = nooutput_tx_lin_hook, .ignition = default_ign_hook, .fwd = subaru_fwd_hook, .relay = alloutput_relay_hook, };