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.
 
 
 
 
 
 

291 lines
8.0 KiB

// board enforces
// in-state
// accel set/resume
// out-state
// cancel button
// regen paddle
// accel rising edge
// brake rising edge
// brake > 0mph
//
int fmax_limit_check(float val, const float MAX, const float MIN) {
return (val > MAX) || (val < MIN);
}
// 2m/s are added to be less restrictive
const struct lookup_t TESLA_LOOKUP_ANGLE_RATE_UP = {
{2., 7., 17.},
{5., .8, .25}};
const struct lookup_t TESLA_LOOKUP_ANGLE_RATE_DOWN = {
{2., 7., 17.},
{5., 3.5, .8}};
const struct lookup_t TESLA_LOOKUP_MAX_ANGLE = {
{2., 29., 38.},
{410., 92., 36.}};
const int TESLA_RT_INTERVAL = 250000; // 250ms between real time checks
// state of angle limits
float tesla_desired_angle_last = 0; // last desired steer angle
float tesla_rt_angle_last = 0.; // last real time angle
float tesla_ts_angle_last = 0;
int tesla_controls_allowed_last = 0;
int tesla_brake_prev = 0;
int tesla_gas_prev = 0;
int tesla_speed = 0;
int eac_status = 0;
int tesla_ignition_started = 0;
void set_gmlan_digital_output(int to_set);
void reset_gmlan_switch_timeout(void);
void gmlan_switch_init(int timeout_enable);
static void tesla_rx_hook(CAN_FIFOMailBox_TypeDef *to_push)
{
set_gmlan_digital_output(0); // #define GMLAN_HIGH 0
reset_gmlan_switch_timeout(); //we're still in tesla safety mode, reset the timeout counter and make sure our output is enabled
//int bus_number = (to_push->RDTR >> 4) & 0xFF;
uint32_t addr;
if (to_push->RIR & 4)
{
// Extended
// Not looked at, but have to be separated
// to avoid address collision
addr = to_push->RIR >> 3;
}
else
{
// Normal
addr = to_push->RIR >> 21;
}
if (addr == 0x45)
{
// 6 bits starting at position 0
int lever_position = (to_push->RDLR & 0x3F);
if (lever_position == 2)
{ // pull forward
// activate openpilot
controls_allowed = 1;
//}
}
else if (lever_position == 1)
{ // push towards the back
// deactivate openpilot
controls_allowed = 0;
}
}
// Detect drive rail on (ignition) (start recording)
if (addr == 0x348)
{
// GTW_status
int drive_rail_on = (to_push->RDLR & 0x0001);
tesla_ignition_started = drive_rail_on == 1;
}
// exit controls on brake press
// DI_torque2::DI_brakePedal 0x118
if (addr == 0x118)
{
// 1 bit at position 16
if (((to_push->RDLR & 0x8000)) >> 15 == 1)
{
//disable break cancel by commenting line below
controls_allowed = 0;
}
//get vehicle speed in m/s. Tesla gives MPH
tesla_speed = ((((((to_push->RDLR >> 24) & 0x0F) << 8) + ((to_push->RDLR >> 16) & 0xFF)) * 0.05 - 25) * 1.609 / 3.6);
if (tesla_speed < 0)
{
tesla_speed = 0;
}
}
// exit controls on EPAS error
// EPAS_sysStatus::EPAS_eacStatus 0x370
if (addr == 0x370)
{
// if EPAS_eacStatus is not 1 or 2, disable control
eac_status = ((to_push->RDHR >> 21)) & 0x7;
// For human steering override we must not disable controls when eac_status == 0
// Additional safety: we could only allow eac_status == 0 when we have human steering allowed
if ((controls_allowed == 1) && (eac_status != 0) && (eac_status != 1) && (eac_status != 2))
{
controls_allowed = 0;
//puts("EPAS error! \n");
}
}
//get latest steering wheel angle
if (addr == 0x00E)
{
float angle_meas_now = (int)((((to_push->RDLR & 0x3F) << 8) + ((to_push->RDLR >> 8) & 0xFF)) * 0.1 - 819.2);
uint32_t ts = TIM2->CNT;
uint32_t ts_elapsed = get_ts_elapsed(ts, tesla_ts_angle_last);
// *** angle real time check
// add 1 to not false trigger the violation and multiply by 25 since the check is done every 250 ms and steer angle is updated at 100Hz
float rt_delta_angle_up = interpolate(TESLA_LOOKUP_ANGLE_RATE_UP, tesla_speed) * 25. + 1.;
float rt_delta_angle_down = interpolate(TESLA_LOOKUP_ANGLE_RATE_DOWN, tesla_speed) * 25. + 1.;
float highest_rt_angle = tesla_rt_angle_last + (tesla_rt_angle_last > 0 ? rt_delta_angle_up : rt_delta_angle_down);
float lowest_rt_angle = tesla_rt_angle_last - (tesla_rt_angle_last > 0 ? rt_delta_angle_down : rt_delta_angle_up);
if ((ts_elapsed > TESLA_RT_INTERVAL) || (controls_allowed && !tesla_controls_allowed_last))
{
tesla_rt_angle_last = angle_meas_now;
tesla_ts_angle_last = ts;
}
// check for violation;
if (fmax_limit_check(angle_meas_now, highest_rt_angle, lowest_rt_angle))
{
// We should not be able to STEER under these conditions
// Other sending is fine (to allow human override)
controls_allowed = 0;
//puts("WARN: RT Angle - No steer allowed! \n");
}
else
{
controls_allowed = 1;
}
tesla_controls_allowed_last = controls_allowed;
}
}
// 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 tesla_tx_hook(CAN_FIFOMailBox_TypeDef *to_send)
{
uint32_t addr;
float angle_raw;
float desired_angle;
addr = to_send->RIR >> 21;
// do not transmit CAN message if steering angle too high
// DAS_steeringControl::DAS_steeringAngleRequest
if (addr == 0x488)
{
angle_raw = ((to_send->RDLR & 0x7F) << 8) + ((to_send->RDLR & 0xFF00) >> 8);
desired_angle = angle_raw * 0.1 - 1638.35;
int16_t violation = 0;
int st_enabled = (to_send->RDLR & 0x400000) >> 22;
if (st_enabled == 0) {
//steering is not enabled, do not check angles and do send
tesla_desired_angle_last = desired_angle;
return true;
}
if (controls_allowed)
{
// add 1 to not false trigger the violation
float delta_angle_up = interpolate(TESLA_LOOKUP_ANGLE_RATE_UP, tesla_speed) + 1.;
float delta_angle_down = interpolate(TESLA_LOOKUP_ANGLE_RATE_DOWN, tesla_speed) + 1.;
float highest_desired_angle = tesla_desired_angle_last + (tesla_desired_angle_last > 0 ? delta_angle_up : delta_angle_down);
float lowest_desired_angle = tesla_desired_angle_last - (tesla_desired_angle_last > 0 ? delta_angle_down : delta_angle_up);
float TESLA_MAX_ANGLE = interpolate(TESLA_LOOKUP_MAX_ANGLE, tesla_speed) + 1.;
//check for max angles
violation |= fmax_limit_check(desired_angle, TESLA_MAX_ANGLE, -TESLA_MAX_ANGLE);
//check for angle delta changes
violation |= fmax_limit_check(desired_angle, highest_desired_angle, lowest_desired_angle);
if (violation)
{
controls_allowed = 0;
return false;
}
tesla_desired_angle_last = desired_angle;
return true;
}
return false;
}
return true;
}
static int tesla_tx_lin_hook(int lin_num, uint8_t *data, int len)
{
// LIN is not used on the Tesla
return false;
}
static void tesla_init(int16_t param)
{
controls_allowed = 0;
tesla_ignition_started = 0;
gmlan_switch_init(1); //init the gmlan switch with 1s timeout enabled
#ifdef PANDA
lline_relay_release();
#endif
}
static int tesla_ign_hook()
{
return tesla_ignition_started;
}
static int tesla_fwd_hook(int bus_num, CAN_FIFOMailBox_TypeDef *to_fwd)
{
int32_t addr = to_fwd->RIR >> 21;
if (bus_num == 0)
{
// change inhibit of GTW_epasControl
if (addr == 0x101)
{
to_fwd->RDLR = to_fwd->RDLR | 0x4000; // 0x4000: WITH_ANGLE, 0xC000: WITH_BOTH (angle and torque)
int checksum = (((to_fwd->RDLR & 0xFF00) >> 8) + (to_fwd->RDLR & 0xFF) + 2) & 0xFF;
to_fwd->RDLR = to_fwd->RDLR & 0xFFFF;
to_fwd->RDLR = to_fwd->RDLR + (checksum << 16);
return 2;
}
// remove EPB_epasControl
if (addr == 0x214)
{
return -1;
}
return 2; // Custom EPAS bus
}
if (bus_num == 2)
{
// remove GTW_epasControl in forwards
if (addr == 0x101)
{
return -1;
}
return 0; // Chassis CAN
}
return -1;
}
const safety_hooks tesla_hooks = {
.init = tesla_init,
.rx = tesla_rx_hook,
.tx = tesla_tx_hook,
.tx_lin = tesla_tx_lin_hook,
.ignition = tesla_ign_hook,
.fwd = tesla_fwd_hook,
.relay = nooutput_relay_hook,
};