IQ.Pilot Prebuilt Release @ 27f668a
This commit is contained in:
141
artifacts/package_runtime/iqdbc/car/tesla/teslacan.py
Normal file
141
artifacts/package_runtime/iqdbc/car/tesla/teslacan.py
Normal file
@@ -0,0 +1,141 @@
|
||||
from iqdbc.car.common.conversions import Conversions as CV
|
||||
from iqdbc.car.tesla.values import CANBUS, CarControllerParams, TeslaFlags
|
||||
from iqdbc.car import DT_CTRL
|
||||
|
||||
|
||||
class TeslaCAN:
|
||||
COAST_ENTRY_ACCEL = 0.03
|
||||
COAST_EXIT_ACCEL = 0.08
|
||||
IMMEDIATE_REGEN_ACCEL = -0.5
|
||||
COAST_JERK_LIMIT = 0.7
|
||||
|
||||
def __init__(self, CP, packer):
|
||||
self.CP = CP
|
||||
self.packer = packer
|
||||
self.l_jerk = 0.0
|
||||
self.longitudinal_mode = "coast"
|
||||
|
||||
def create_steering_control(self, angle, enabled, control_type):
|
||||
# control_type comes from torque_blend: ANGLE_CONTROL (1) normally, LANE_KEEP_ASSIST (2) when cooperative steering is enabled
|
||||
control_type = control_type if enabled else 0
|
||||
if self.CP.flags & TeslaFlags.LEGACY_DAS_STEERING:
|
||||
control_type <<= 1 # legacy firmware uses a 2-bit field, one bit up from the 3-bit signal
|
||||
|
||||
values = {
|
||||
"DAS_steeringAngleRequest": -angle,
|
||||
"DAS_steeringHapticRequest": 0,
|
||||
"DAS_steeringControlType": control_type,
|
||||
}
|
||||
|
||||
return self.packer.make_can_msg("DAS_steeringControl", CANBUS.party, values)
|
||||
|
||||
def _update_longitudinal_mode(self, accel, active, cruise_override, stopping, cancel):
|
||||
if not active or cruise_override or cancel:
|
||||
self.longitudinal_mode = "coast"
|
||||
elif stopping or accel <= self.IMMEDIATE_REGEN_ACCEL:
|
||||
self.longitudinal_mode = "regen"
|
||||
elif self.longitudinal_mode == "drive":
|
||||
if accel <= -self.COAST_EXIT_ACCEL:
|
||||
self.longitudinal_mode = "regen"
|
||||
elif accel <= self.COAST_ENTRY_ACCEL:
|
||||
self.longitudinal_mode = "coast"
|
||||
elif self.longitudinal_mode == "regen":
|
||||
if accel >= self.COAST_EXIT_ACCEL:
|
||||
self.longitudinal_mode = "drive"
|
||||
elif accel >= -self.COAST_ENTRY_ACCEL:
|
||||
self.longitudinal_mode = "coast"
|
||||
elif accel >= self.COAST_EXIT_ACCEL:
|
||||
self.longitudinal_mode = "drive"
|
||||
elif accel <= -self.COAST_EXIT_ACCEL:
|
||||
self.longitudinal_mode = "regen"
|
||||
|
||||
def create_longitudinal_command(self, acc_state, accel, counter, v_ego, active, cruise_override, set_speed_kph=None,
|
||||
comfort_mode=False, stopping=False, cancel=False):
|
||||
from iqdbc.car.interfaces import V_CRUISE_MAX
|
||||
|
||||
set_speed = max(v_ego * CV.MS_TO_KPH, 0)
|
||||
if active:
|
||||
self.l_jerk = 0.0 if cruise_override else min(
|
||||
self.l_jerk + CarControllerParams.JERK_UP * DT_CTRL * 4,
|
||||
CarControllerParams.JERK_LIMIT_MAX,
|
||||
)
|
||||
else:
|
||||
self.l_jerk = 0.0
|
||||
|
||||
accel_min = accel
|
||||
accel_max = max(accel, 0)
|
||||
jerk_min = CarControllerParams.JERK_LIMIT_MIN
|
||||
jerk_max = self.l_jerk
|
||||
|
||||
if active and comfort_mode:
|
||||
self._update_longitudinal_mode(accel, active, cruise_override, stopping, cancel)
|
||||
if self.longitudinal_mode == "drive":
|
||||
set_speed = V_CRUISE_MAX if set_speed_kph is None else max(0.0, min(V_CRUISE_MAX, float(set_speed_kph)))
|
||||
elif self.longitudinal_mode == "regen":
|
||||
set_speed = 0
|
||||
else:
|
||||
accel_min = min(accel, 0)
|
||||
accel_max = max(accel, 0)
|
||||
jerk_min = -self.COAST_JERK_LIMIT
|
||||
jerk_max = self.COAST_JERK_LIMIT
|
||||
elif active:
|
||||
self._update_longitudinal_mode(accel, False, cruise_override, stopping, cancel)
|
||||
set_speed = 0 if accel < 0 else V_CRUISE_MAX
|
||||
if set_speed_kph is not None and accel >= 0:
|
||||
set_speed = max(0.0, min(V_CRUISE_MAX, float(set_speed_kph)))
|
||||
else:
|
||||
self._update_longitudinal_mode(accel, active, cruise_override, stopping, cancel)
|
||||
|
||||
values = {
|
||||
"DAS_setSpeed": set_speed,
|
||||
"DAS_accState": acc_state,
|
||||
"DAS_aebEvent": 0,
|
||||
"DAS_jerkMin": jerk_min,
|
||||
"DAS_jerkMax": jerk_max,
|
||||
"DAS_accelMin": accel_min,
|
||||
"DAS_accelMax": accel_max,
|
||||
"DAS_controlCounter": counter,
|
||||
}
|
||||
return self.packer.make_can_msg("DAS_control", CANBUS.party, values)
|
||||
|
||||
def create_steering_allowed(self):
|
||||
values = {
|
||||
"APS_eacAllow": 1,
|
||||
}
|
||||
|
||||
return self.packer.make_can_msg("APS_eacMonitor", CANBUS.party, values)
|
||||
|
||||
def create_body_controls(self, stock_dat, left_blinker, right_blinker, cancel=False):
|
||||
# Ride alongside the car's native DAS_bodyControls: copy the raw frame, override only the
|
||||
# turn-indicator bits, and stamp counter + 1 so our frame supersedes the stock one.
|
||||
dat = bytearray(stock_dat)
|
||||
if len(dat) < 8:
|
||||
dat.extend(b"\x00" * (8 - len(dat)))
|
||||
|
||||
if left_blinker or right_blinker:
|
||||
turn_req = 1 if left_blinker else 2 # DAS_TURN_INDICATOR_LEFT / _RIGHT
|
||||
dat[1] = (dat[1] & ~0x07) | (turn_req & 0x07)
|
||||
dat[2] = (dat[2] & ~0x3C) | (1 << 2) # DAS_ACTIVE_NAV_LANE_CHANGE
|
||||
elif cancel:
|
||||
dat[1] = (dat[1] & ~0x07) | 0x03 # DAS_TURN_INDICATOR_CANCEL
|
||||
dat[2] = (dat[2] & ~0x3C) | (4 << 2) # DAS_CANCEL_LANE_CHANGE
|
||||
|
||||
counter = (((dat[6] >> 4) + 1) & 0x0F)
|
||||
dat[6] = (dat[6] & ~0xF0) | (counter << 4)
|
||||
|
||||
addr = 0x3E9
|
||||
checksum = (addr & 0xFF) + ((addr >> 8) & 0xFF)
|
||||
for i in range(7):
|
||||
checksum += dat[i]
|
||||
dat[7] = checksum & 0xFF
|
||||
|
||||
return addr, bytes(dat), CANBUS.vehicle
|
||||
|
||||
|
||||
def tesla_checksum(address: int, sig, d: bytearray) -> int:
|
||||
checksum = (address & 0xFF) + ((address >> 8) & 0xFF)
|
||||
checksum_byte = sig.start_bit // 8
|
||||
for i in range(len(d)):
|
||||
if i != checksum_byte:
|
||||
checksum += d[i]
|
||||
return checksum & 0xFF
|
||||
Reference in New Issue
Block a user