forked from IQ.Lvbs/IQ.Pilot
234 lines
9.9 KiB
C
234 lines
9.9 KiB
C
#pragma once
|
|
|
|
#include "iqdbc/safety/declarations.h"
|
|
|
|
// BYD Sealion 7 (e-Platform 3.0), Konik BYD-6 relay harness.
|
|
// bus 0 = chassis CAN, bus 2 = camera / ADAS side.
|
|
#define BYD_STEER_MODULE_2 0x11FU // RX from EPS, steering angle
|
|
#define BYD_STEERING_TORQUE 0x1FCU // RX from EPS, driver torque + EPS state
|
|
#define BYD_WHEEL_SPEEDS 0x1F0U // RX from ESP, vehicle speed
|
|
#define BYD_DRIVE_STATE 0x242U // RX from VCU, gear + brake pressed
|
|
#define BYD_PEDAL 0x342U // RX from VCU, accelerator pedal
|
|
#define BYD_ACC_HUD_ADAS 0x32DU // RX from ADAS(b0), cruise state
|
|
#define BYD_STEERING_MODULE_ADAS 0x1E2U // TX to EPS, angle command
|
|
#define BYD_LKAS_HUD_ADAS 0x316U // TX to cluster, LKAS HUD
|
|
#define BYD_ACC_CMD 0x32EU // TX to IPB, accel command
|
|
#define BYD_PCM_BUTTONS 0x3B0U // TX, cruise cancel
|
|
|
|
// WHEEL_SPEEDS scale, kph per LSB. PROVISIONAL - keep in lockstep with byd_sealion_7.dbc.
|
|
#define BYD_WHEEL_SPEED_SCALE 0.0725f
|
|
|
|
// STEERING_TORQUE.DRIVER_TORQUE counts (0.1 Nm/LSB). MUST equal CarControllerParams
|
|
// .STEER_DRIVER_OVERRIDE * 10 in values.py: carstate.py latches on the same instantaneous
|
|
// sample, and if the two sides disagree openpilot and the panda desync into controlsMismatch.
|
|
#define BYD_DRIVER_TORQUE_OVERRIDE 120
|
|
|
|
// A steering override disengages and stays disengaged until the driver deliberately re-arms,
|
|
// either by cycling stock cruise or pressing the LKAS/ICC button (0x3B0 bit 6, confirmed
|
|
// on-car). Mirrors the override latch in carstate.py.
|
|
static bool byd_override_latched = false;
|
|
static bool byd_lkas_btn_prev = false;
|
|
|
|
// ACC_CMD.ACCEL_CMD is an 8-bit field at 0.05 m/s^2 per LSB with a -5 m/s^2 offset, so raw 100
|
|
// is 0.0 m/s^2. Limits below are in offset-corrected LSBs.
|
|
#define BYD_ACCEL_OFFSET 100
|
|
|
|
static uint8_t byd_get_counter(const CANPacket_t *msg) {
|
|
uint8_t cnt = 0U;
|
|
if ((msg->addr == BYD_STEERING_TORQUE) || (msg->addr == BYD_WHEEL_SPEEDS) || (msg->addr == BYD_PEDAL)) {
|
|
cnt = (msg->data[6] >> 4) & 0xFU;
|
|
} else if (msg->addr == BYD_ACC_HUD_ADAS) {
|
|
cnt = msg->data[6] & 0xFU;
|
|
} else if (msg->addr == BYD_STEER_MODULE_2) {
|
|
cnt = msg->data[4] & 0xFU;
|
|
} else {
|
|
}
|
|
return cnt;
|
|
}
|
|
|
|
static uint32_t byd_get_checksum(const CANPacket_t *msg) {
|
|
return msg->data[7];
|
|
}
|
|
|
|
static uint32_t byd_compute_checksum(const CANPacket_t *msg) {
|
|
// Every 8-byte BYD frame: the last byte is the inverted sum of the first seven.
|
|
uint8_t sum = 0U;
|
|
for (int i = 0; i < 7; i++) {
|
|
sum = (uint8_t)(sum + msg->data[i]);
|
|
}
|
|
return (uint32_t)((uint8_t)(~sum));
|
|
}
|
|
|
|
static void byd_rx_hook(const CANPacket_t *msg) {
|
|
if (msg->bus == 0U) {
|
|
// Steering angle: STEER_ANGLE_2, 0.1 deg/LSB, signed, little endian
|
|
if (msg->addr == BYD_STEER_MODULE_2) {
|
|
int angle_meas_new = to_signed((msg->data[1] << 8) | msg->data[0], 16);
|
|
update_sample(&angle_meas, angle_meas_new);
|
|
}
|
|
|
|
// Vehicle speed. NOTE: on the Sealion 7 this address carries four 12-bit wheel speeds, not
|
|
// the Atto 3's single 16-bit WHEELSPEED_CLEAN. Decoding it the Atto 3 way yields garbage,
|
|
// and vehicle speed feeds the angle rate limits.
|
|
// FL 0|12, FR 16|12, RL 28|12, RR 40|12
|
|
if (msg->addr == BYD_WHEEL_SPEEDS) {
|
|
uint32_t fl = ((uint32_t)msg->data[0]) | ((uint32_t)(msg->data[1] & 0xFU) << 8);
|
|
uint32_t fr = ((uint32_t)msg->data[2]) | ((uint32_t)(msg->data[3] & 0xFU) << 8);
|
|
uint32_t rl = ((uint32_t)(msg->data[3] >> 4)) | ((uint32_t)msg->data[4] << 4);
|
|
uint32_t rr = ((uint32_t)msg->data[5]) | ((uint32_t)(msg->data[6] & 0xFU) << 8);
|
|
|
|
float speed = ((float)(fl + fr + rl + rr) / 4.0f) * BYD_WHEEL_SPEED_SCALE;
|
|
vehicle_moving = speed > 0.0f;
|
|
UPDATE_VEHICLE_SPEED(speed * KPH_TO_MS);
|
|
}
|
|
|
|
// Brake pressed. This MUST stay the same bit that carstate.py reads (DRIVE_STATE bit 37);
|
|
// if the two latches read different sources a light brake graze clears only one of them and
|
|
// controlsd raises "Controls Mismatch".
|
|
if (msg->addr == BYD_DRIVE_STATE) {
|
|
brake_pressed = ((msg->data[4] >> 5) & 0x1U) != 0U;
|
|
}
|
|
|
|
// Gas pressed, from the real accelerator pedal (GAS_PEDAL, 0.01/LSB). NOT
|
|
// DRIVE_STATE.RAW_THROTTLE, which is powertrain torque demand and pulses on its own while
|
|
// accelerating.
|
|
if (msg->addr == BYD_PEDAL) {
|
|
gas_pressed = msg->data[0] > 10U;
|
|
}
|
|
|
|
// Driver torque, and the override latch. DRIVER_TORQUE is 4|12 signed.
|
|
if (msg->addr == BYD_STEERING_TORQUE) {
|
|
int torque_driver_new = to_signed(((msg->data[1] & 0xFFU) << 4) | (msg->data[0] >> 4), 12);
|
|
update_sample(&torque_driver, torque_driver_new);
|
|
if (SAFETY_ABS(torque_driver_new) > BYD_DRIVER_TORQUE_OVERRIDE) {
|
|
byd_override_latched = true;
|
|
}
|
|
}
|
|
|
|
// LKAS/ICC button (0x3B0 bit 6) re-arms after an override
|
|
if (msg->addr == BYD_PCM_BUTTONS) {
|
|
bool lkas_btn = ((msg->data[0] >> 6) & 0x1U) != 0U;
|
|
if (lkas_btn && !byd_lkas_btn_prev) {
|
|
byd_override_latched = false;
|
|
}
|
|
byd_lkas_btn_prev = lkas_btn;
|
|
}
|
|
|
|
// Cruise state. The ADAS/ACC ECU is on the chassis bus, not behind the camera relay.
|
|
// CRUISE_STATE is the high nibble of byte 5: 0=off, 1=available, 2=engaged,
|
|
// 3=engaged and commanding accel. PR #3337/#3352 read ACC_STATE from byte 2, which is a
|
|
// constant 0x3c here and can only ever report 7 (ERROR).
|
|
if (msg->addr == BYD_ACC_HUD_ADAS) {
|
|
uint8_t cruise_state = msg->data[5] >> 4;
|
|
if (cruise_state < 2U) {
|
|
byd_override_latched = false;
|
|
}
|
|
bool acc_on = (cruise_state >= 2U) && !byd_override_latched;
|
|
pcm_cruise_check(acc_on);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool byd_tx_hook(const CANPacket_t *msg) {
|
|
const AngleSteeringLimits BYD_STEERING_LIMITS = {
|
|
.max_angle = 3900, // 390 deg
|
|
.angle_deg_to_can = 10,
|
|
.frequency = 50U,
|
|
};
|
|
|
|
const AngleSteeringParams BYD_STEERING_PARAMS = {
|
|
.slip_factor = -0.000572451189655154, // calc_slip_factor(VM) for BYD_SEALION_7
|
|
.steer_ratio = 16.0,
|
|
.wheelbase = 2.93,
|
|
};
|
|
|
|
// ACCEL_CMD in offset-corrected LSBs of 0.05 m/s^2
|
|
const LongitudinalLimits BYD_LONG_LIMITS = {
|
|
.max_accel = 40, // 2.0 m/s^2
|
|
.min_accel = -70, // -3.5 m/s^2
|
|
.inactive_accel = 0,
|
|
.zero_accel = 0,
|
|
};
|
|
|
|
bool tx = true;
|
|
|
|
if (msg->bus == 0U) {
|
|
// Steering angle command: STEER_ANGLE 24|16, 0.1 deg/LSB signed; STEER_REQ is bit 21
|
|
if (msg->addr == BYD_STEERING_MODULE_ADAS) {
|
|
int desired_angle = to_signed((msg->data[4] << 8) | msg->data[3], 16);
|
|
bool steer_req = ((msg->data[2] >> 5) & 0x1U) != 0U;
|
|
|
|
if (steer_angle_cmd_checks_vm(desired_angle, steer_req, BYD_STEERING_LIMITS, BYD_STEERING_PARAMS)) {
|
|
tx = false;
|
|
}
|
|
}
|
|
|
|
// Longitudinal command
|
|
if (msg->addr == BYD_ACC_CMD) {
|
|
int desired_accel = (int)msg->data[0] - BYD_ACCEL_OFFSET;
|
|
if (longitudinal_accel_checks(desired_accel, BYD_LONG_LIMITS)) {
|
|
tx = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return tx;
|
|
}
|
|
|
|
static safety_config byd_init(uint16_t param) {
|
|
// 0x1E2 and 0x316 are transmitted continuously, gated only by STEER_REQ. check_relay blocks
|
|
// the camera's own copies, so openpilot is the only source of both while installed. The EPS
|
|
// latches a fault if the 0x1E2 stream stops while it is actuating.
|
|
static const CanMsg BYD_TX_MSGS[] = {
|
|
{BYD_STEERING_MODULE_ADAS, 0, 8, .check_relay = true},
|
|
{BYD_LKAS_HUD_ADAS, 0, 8, .check_relay = true},
|
|
{BYD_PCM_BUTTONS, 0, 8, .check_relay = false},
|
|
};
|
|
|
|
// Longitudinal is only offered on a gateway harness, where the ACC ECU is behind the relay
|
|
// and 0x32E is genuinely filterable. On a camera harness the ACC ECU is in front of the
|
|
// relay, so alphaLongitudinalAvailable is false there and this list is never selected -
|
|
// check_relay would otherwise fire on every stock ACC frame.
|
|
static const CanMsg BYD_LONG_TX_MSGS[] = {
|
|
{BYD_STEERING_MODULE_ADAS, 0, 8, .check_relay = true},
|
|
{BYD_LKAS_HUD_ADAS, 0, 8, .check_relay = true},
|
|
{BYD_ACC_CMD, 0, 8, .check_relay = true},
|
|
{BYD_PCM_BUTTONS, 0, 8, .check_relay = false},
|
|
};
|
|
|
|
// 4-bit rolling counters, so max_counter is 15. Leaving it 0 does not "skip" the check, it
|
|
// pins wrong_counters at the failure threshold and every frame is rejected.
|
|
static RxCheck byd_rx_checks[] = {
|
|
{.msg = {{BYD_STEER_MODULE_2, 0, 5, 100U, .ignore_checksum = true, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // steering angle (4-bit checksum, not the byte-7 one)
|
|
{.msg = {{BYD_STEERING_TORQUE, 0, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // driver torque + EPS state
|
|
{.msg = {{BYD_WHEEL_SPEEDS, 0, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // vehicle speed
|
|
{.msg = {{BYD_DRIVE_STATE, 0, 8, 50U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // gear + brake (no counter/checksum)
|
|
{.msg = {{BYD_PEDAL, 0, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // accelerator pedal
|
|
{.msg = {{BYD_ACC_HUD_ADAS, 0, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, // cruise state (chassis bus, not behind the relay)
|
|
};
|
|
|
|
byd_override_latched = false;
|
|
byd_lkas_btn_prev = false;
|
|
|
|
bool byd_longitudinal = false;
|
|
|
|
SAFETY_UNUSED(param);
|
|
#ifdef ALLOW_DEBUG
|
|
const int FLAG_BYD_LONG_CONTROL = 1;
|
|
byd_longitudinal = GET_FLAG(param, FLAG_BYD_LONG_CONTROL);
|
|
#endif
|
|
|
|
// cppcheck-suppress knownConditionTrueFalse
|
|
return byd_longitudinal ? BUILD_SAFETY_CFG(byd_rx_checks, BYD_LONG_TX_MSGS) : \
|
|
BUILD_SAFETY_CFG(byd_rx_checks, BYD_TX_MSGS);
|
|
}
|
|
|
|
const safety_hooks byd_hooks = {
|
|
.init = byd_init,
|
|
.rx = byd_rx_hook,
|
|
.tx = byd_tx_hook,
|
|
.get_counter = byd_get_counter,
|
|
.get_checksum = byd_get_checksum,
|
|
.compute_checksum = byd_compute_checksum,
|
|
};
|