This commit is contained in:
Your Name
2024-04-27 13:48:05 -05:00
parent 2fbe9dbea1
commit 931db76fc6
432 changed files with 12973 additions and 3300 deletions

View File

@@ -3,15 +3,18 @@ from openpilot.common.numpy_fast import clip, interp
from openpilot.common.params import Params
from openpilot.selfdrive.car import apply_meas_steer_torque_limits, apply_std_steer_angle_limits, common_fault_avoidance, \
create_gas_interceptor_command, make_can_msg
from openpilot.selfdrive.car.interfaces import CarControllerBase
from openpilot.selfdrive.car.toyota import toyotacan
from openpilot.selfdrive.car.toyota.values import CAR, STATIC_DSU_MSGS, NO_STOP_TIMER_CAR, TSS2_CAR, \
MIN_ACC_SPEED, PEDAL_TRANSITION, CarControllerParams, ToyotaFlags, \
UNSUPPORTED_DSU_CAR, STOP_AND_GO_CAR
from opendbc.can.packer import CANPacker
from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_functions import CRUISING_SPEED
LongCtrlState = car.CarControl.Actuators.LongControlState
SteerControlType = car.CarParams.SteerControlType
VisualAlert = car.CarControl.HUDControl.VisualAlert
LongCtrlState = car.CarControl.Actuators.LongControlState
# LKA limits
# EPS faults if you apply torque while the steering rate is above 100 deg/s for too long
@@ -35,10 +38,21 @@ COMPENSATORY_CALCULATION_THRESHOLD_BP = [0., 11., 23.] # m/s
# Lock / unlock door commands - Credit goes to AlexandreSato!
LOCK_CMD = b'\x40\x05\x30\x11\x00\x80\x00\x00'
UNLOCK_CMD = b'\x40\x05\x30\x11\x00\x40\x00\x00'
PARK = car.CarState.GearShifter.park
class CarController:
def compute_gb_toyota(accel, speed):
creep_brake = 0.0
creep_speed = 2.3
creep_brake_value = 0.15
if speed < creep_speed:
creep_brake = (creep_speed - speed) / creep_speed * creep_brake_value
gb = accel - creep_brake
return gb
class CarController(CarControllerBase):
def __init__(self, dbc_name, CP, VM):
self.CP = CP
self.params = CarControllerParams(self.CP)
@@ -47,9 +61,10 @@ class CarController:
self.last_angle = 0
self.alert_active = False
self.last_standstill = False
self.prohibit_neg_calculation = True
self.standstill_req = False
self.steer_rate_counter = 0
self.prohibit_neg_calculation = True
self.distance_button = 0
self.packer = CANPacker(dbc_name)
self.gas = 0
@@ -64,6 +79,8 @@ class CarController:
self.doors_locked = False
self.doors_unlocked = True
self.pcm_accel_comp = 0
def update(self, CC, CS, now_nanos, frogpilot_variables):
actuators = CC.actuators
hud_control = CC.hudControl
@@ -136,34 +153,56 @@ class CarController:
pedal_offset = interp(CS.out.vEgo, [0.0, 2.3, MIN_ACC_SPEED + PEDAL_TRANSITION], [-.4, 0.0, 0.2])
pedal_command = PEDAL_SCALE * (actuators.accel + pedal_offset)
interceptor_gas_cmd = clip(pedal_command, 0., MAX_INTERCEPTOR_GAS)
elif self.CP.enableGasInterceptor and CC.longActive and self.CP.carFingerprint in STOP_AND_GO_CAR and actuators.accel > 0.0 \
and CS.out.standstill:
interceptor_gas_cmd = 0.12
elif self.CP.enableGasInterceptor and CC.longActive and self.CP.carFingerprint in STOP_AND_GO_CAR and actuators.accel > 0.0:
interceptor_gas_cmd = 0.12 if CS.out.standstill else 0.
else:
interceptor_gas_cmd = 0.
# prohibit negative compensatory calculations when first activating long after accelerator depression or engagement
if not CC.longActive:
self.prohibit_neg_calculation = True
comp_thresh = interp(CS.out.vEgo, COMPENSATORY_CALCULATION_THRESHOLD_BP, COMPENSATORY_CALCULATION_THRESHOLD_V)
# don't reset until a reasonable compensatory value is reached
if CS.pcm_neutral_force > comp_thresh * self.CP.mass:
self.prohibit_neg_calculation = False
# NO_STOP_TIMER_CAR will creep if compensation is applied when stopping or stopped, don't compensate when stopped or stopping
should_compensate = True
if (self.CP.carFingerprint in NO_STOP_TIMER_CAR and actuators.accel < 1e-3 or stopping) or CS.out.vEgo < 1e-3:
should_compensate = False
# limit minimum to only positive until first positive is reached after engagement, don't calculate when long isn't active
if CC.longActive and should_compensate and not self.prohibit_neg_calculation and (self.cydia_tune or self.frogs_go_moo_tune):
if CC.longActive and not self.prohibit_neg_calculation and (self.cydia_tune or self.frogs_go_moo_tune):
accel_offset = CS.pcm_neutral_force / self.CP.mass
else:
accel_offset = 0.
# only calculate pcm_accel_cmd when long is active to prevent disengagement from accelerator depression
if CC.longActive:
if frogpilot_variables.sport_plus:
pcm_accel_cmd = clip(actuators.accel + accel_offset, self.params.ACCEL_MIN, self.params.ACCEL_MAX_PLUS)
if self.frogs_go_moo_tune:
wind_brake = interp(CS.out.vEgo, [0.0, 2.3, 35.0], [0.001, 0.002, 0.15])
gas_accel = compute_gb_toyota(actuators.accel, CS.out.vEgo) + wind_brake
self.pcm_accel_comp = clip(gas_accel - CS.pcm_accel_net, self.pcm_accel_comp - 0.03, self.pcm_accel_comp + 0.03)
pcm_accel_cmd = gas_accel + self.pcm_accel_comp
if not CC.longActive:
pcm_accel_cmd = 0.0
pcm_accel_cmd = clip(pcm_accel_cmd, self.params.ACCEL_MIN, self.params.ACCEL_MAX_PLUS)
else:
pcm_accel_cmd = clip(actuators.accel + accel_offset, self.params.ACCEL_MIN, self.params.ACCEL_MAX_PLUS)
else:
pcm_accel_cmd = clip(actuators.accel + accel_offset, self.params.ACCEL_MIN, self.params.ACCEL_MAX)
if self.frogs_go_moo_tune:
wind_brake = interp(CS.out.vEgo, [0.0, 2.3, 35.0], [0.001, 0.002, 0.15])
gas_accel = compute_gb_toyota(actuators.accel, CS.out.vEgo) + wind_brake
self.pcm_accel_comp = clip(gas_accel - CS.pcm_accel_net, self.pcm_accel_comp - 0.03, self.pcm_accel_comp + 0.03)
pcm_accel_cmd = gas_accel + self.pcm_accel_comp
if not CC.longActive:
pcm_accel_cmd = 0.0
pcm_accel_cmd = clip(pcm_accel_cmd, self.params.ACCEL_MIN, self.params.ACCEL_MAX)
else:
pcm_accel_cmd = clip(actuators.accel + accel_offset, self.params.ACCEL_MIN, self.params.ACCEL_MAX)
else:
pcm_accel_cmd = 0.
@@ -191,16 +230,23 @@ class CarController:
# when stopping, send -2.5 raw acceleration immediately to prevent vehicle from creeping, else send actuators.accel
accel_raw = -2.5 if stopping and (self.cydia_tune or self.frogs_go_moo_tune) else actuators.accel
# Press distance button until we are at the correct bar length. Only change while enabled to avoid skipping startup popup
if self.frame % 6 == 0 and self.CP.openpilotLongitudinalControl:
desired_distance = 4 - hud_control.leadDistanceBars
if CS.out.cruiseState.enabled and CS.pcm_follow_distance != desired_distance:
self.distance_button = not self.distance_button
else:
self.distance_button = 0
# Lexus IS uses a different cancellation message
if pcm_cancel_cmd and self.CP.carFingerprint in UNSUPPORTED_DSU_CAR:
can_sends.append(toyotacan.create_acc_cancel_command(self.packer))
elif self.CP.openpilotLongitudinalControl:
can_sends.append(toyotacan.create_accel_command(self.packer, pcm_accel_cmd, accel_raw, pcm_cancel_cmd, self.standstill_req, lead, CS.acc_type, fcw_alert,
CS.distance_button, frogpilot_variables))
self.distance_button, frogpilot_variables))
self.accel = pcm_accel_cmd
else:
can_sends.append(toyotacan.create_accel_command(self.packer, 0, 0, pcm_cancel_cmd, False, lead, CS.acc_type, False,
CS.distance_button, frogpilot_variables))
can_sends.append(toyotacan.create_accel_command(self.packer, 0, 0, pcm_cancel_cmd, False, lead, CS.acc_type, False, self.distance_button, frogpilot_variables))
if self.frame % 2 == 0 and self.CP.enableGasInterceptor and self.CP.openpilotLongitudinalControl:
# send exactly zero if gas cmd is zero. Interceptor will send the max between read value and gas cmd.
@@ -247,15 +293,17 @@ class CarController:
new_actuators.gas = self.gas
# Lock doors when in drive / unlock doors when in park
if frogpilot_variables.lock_doors:
if self.doors_unlocked and CS.out.gearShifter != PARK:
if self.doors_unlocked and CS.out.gearShifter != PARK and CS.out.vEgo >= CRUISING_SPEED:
if frogpilot_variables.lock_doors:
can_sends.append(make_can_msg(0x750, LOCK_CMD, 0))
self.doors_locked = True
self.doors_unlocked = False
elif self.doors_locked and CS.out.gearShifter == PARK:
self.doors_locked = True
self.doors_unlocked = False
elif self.doors_locked and CS.out.gearShifter == PARK:
if frogpilot_variables.unlock_doors:
can_sends.append(make_can_msg(0x750, UNLOCK_CMD, 0))
self.doors_locked = False
self.doors_unlocked = True
self.doors_locked = False
self.doors_unlocked = True
self.frame += 1
return new_actuators, can_sends

View File

@@ -10,9 +10,6 @@ from opendbc.can.parser import CANParser
from openpilot.selfdrive.car.interfaces import CarStateBase
from openpilot.selfdrive.car.toyota.values import ToyotaFlags, CAR, DBC, STEER_THRESHOLD, NO_STOP_TIMER_CAR, \
TSS2_CAR, RADAR_ACC_CAR, EPS_SCALE, UNSUPPORTED_DSU_CAR
from openpilot.selfdrive.controls.lib.drive_helpers import CRUISE_LONG_PRESS
from openpilot.selfdrive.frogpilot.functions.speed_limit_controller import SpeedLimitController
SteerControlType = car.CarParams.SteerControlType
@@ -45,19 +42,38 @@ class CarState(CarStateBase):
self.accurate_steer_angle_seen = False
self.angle_offset = FirstOrderFilter(None, 60.0, DT_CTRL, initialized=False)
self.prev_distance_button = 0
self.distance_button = 0
self.pcm_follow_distance = 0
self.low_speed_lockout = False
self.acc_type = 1
self.lkas_hud = {}
# FrogPilot variables
self.profile_restored = False
self.zss_compute = False
self.zss_cruise_active_last = False
self.pcm_accel_net = 0.0
self.pcm_neutral_force = 0.0
self.zss_angle_offset = 0
self.zss_threshold_count = 0
self.traffic_signals = {}
# Traffic signals for Speed Limit Controller - Credit goes to the DragonPilot team!
def calculate_speed_limit(self, cp_cam, frogpilot_variables):
signals = ["TSGN1", "SPDVAL1", "SPLSGN1", "TSGN2", "SPLSGN2", "TSGN3", "SPLSGN3", "TSGN4", "SPLSGN4"]
traffic_signals = {signal: cp_cam.vl["RSA1"].get(signal, cp_cam.vl["RSA2"].get(signal)) for signal in signals}
tsgn1 = traffic_signals.get("TSGN1", None)
spdval1 = traffic_signals.get("SPDVAL1", None)
if tsgn1 == 1 and not frogpilot_variables.force_mph_dashboard:
return spdval1 * CV.KPH_TO_MS
elif tsgn1 == 36 or frogpilot_variables.force_mph_dashboard:
return spdval1 * CV.MPH_TO_MS
else:
return 0
def update(self, cp, cp_cam, frogpilot_variables):
ret = car.CarState.new_message()
@@ -182,88 +198,37 @@ class CarState(CarStateBase):
if self.CP.carFingerprint != CAR.PRIUS_V:
self.lkas_hud = copy.copy(cp_cam.vl["LKAS_HUD"])
# FrogPilot functions
# Switch the current state of Experimental Mode if the LKAS button is double pressed
if frogpilot_variables.experimental_mode_via_lkas and ret.cruiseState.available and self.CP.carFingerprint != CAR.PRIUS_V:
message_keys = ["LDA_ON_MESSAGE", "SET_ME_X02"]
lkas_pressed = any(self.lkas_hud.get(key) == 1 for key in message_keys)
if lkas_pressed and not self.lkas_previously_pressed:
if frogpilot_variables.conditional_experimental_mode:
self.fpf.update_cestatus_lkas()
else:
self.fpf.update_experimental_mode()
self.lkas_previously_pressed = lkas_pressed
if self.CP.carFingerprint not in UNSUPPORTED_DSU_CAR:
# Need to subtract by 1 to comply with the personality profiles of "0", "1", and "2"
self.personality_profile = cp.vl["PCM_CRUISE_SM"]["DISTANCE_LINES"] - 1
self.pcm_follow_distance = cp.vl["PCM_CRUISE_2"]["PCM_FOLLOW_DISTANCE"]
if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR) or self.CP.flags & ToyotaFlags.SMART_DSU:
if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR) or (self.CP.flags & ToyotaFlags.SMART_DSU and not self.CP.flags & ToyotaFlags.RADAR_CAN_FILTER):
# distance button is wired to the ACC module (camera or radar)
self.prev_distance_button = self.distance_button
if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR):
distance_pressed = cp_acc.vl["ACC_CONTROL"]["DISTANCE"]
self.distance_button = cp_acc.vl["ACC_CONTROL"]["DISTANCE"]
else:
distance_pressed = cp.vl["SDSU"]["FD_BUTTON"]
else:
distance_pressed = False
self.distance_button = cp.vl["SDSU"]["FD_BUTTON"]
# Distance button functions
if ret.cruiseState.available:
if distance_pressed:
self.distance_pressed_counter += 1
elif self.distance_previously_pressed:
# Set the distance lines on the dash to match the new personality if the button was held down for less than 0.5 seconds
if self.distance_pressed_counter < CRUISE_LONG_PRESS:
self.previous_personality_profile = (self.personality_profile + 2) % 3
self.fpf.distance_button_function(self.previous_personality_profile)
self.profile_restored = False
self.distance_pressed_counter = 0
if self.CP.carFingerprint != CAR.PRIUS_V:
self.lkas_previously_enabled = self.lkas_enabled
message_keys = ["LDA_ON_MESSAGE", "SET_ME_X02"]
self.lkas_enabled = any(self.lkas_hud.get(key) == 1 for key in message_keys)
# Switch the current state of Experimental Mode if the button is held down for 0.5 seconds
if self.distance_pressed_counter == CRUISE_LONG_PRESS and frogpilot_variables.experimental_mode_via_distance:
if frogpilot_variables.conditional_experimental_mode:
self.fpf.update_cestatus_distance()
else:
self.fpf.update_experimental_mode()
self.params_memory.put_float("CarSpeedLimit", self.calculate_speed_limit(cp_cam, frogpilot_variables))
# Switch the current state of Traffic Mode if the button is held down for 2.5 seconds
if self.distance_pressed_counter == CRUISE_LONG_PRESS * 5 and frogpilot_variables.traffic_mode:
self.fpf.update_traffic_mode()
self.cruise_decreased_previously = self.cruise_decreased
self.cruise_increased_previously = self.cruise_increased
# Revert the previous changes to Experimental Mode
if frogpilot_variables.experimental_mode_via_distance:
if frogpilot_variables.conditional_experimental_mode:
self.fpf.update_cestatus_distance()
else:
self.fpf.update_experimental_mode()
self.cruise_decreased = self.pcm_acc_status == 10
self.cruise_increased = self.pcm_acc_status == 9
self.distance_previously_pressed = distance_pressed
# Update the distance lines on the dash upon ignition/onroad UI button clicked
if frogpilot_variables.personalities_via_wheel and ret.cruiseState.available:
# Sync with the onroad UI button
if self.fpf.personality_changed_via_ui:
self.profile_restored = False
self.previous_personality_profile = self.fpf.current_personality
self.fpf.reset_personality_changed_param()
# Set personality to the previous drive's personality or when the user changes it via the UI
if self.personality_profile == self.previous_personality_profile:
self.profile_restored = True
if not self.profile_restored:
self.distance_button = not self.distance_button
# Traffic signals for Speed Limit Controller - Credit goes to the DragonPilot team!
self.update_traffic_signals(cp_cam)
SpeedLimitController.car_speed_limit = self.calculate_speed_limit(frogpilot_variables)
SpeedLimitController.write_car_state()
self.pcm_accel_net = cp.vl["PCM_CRUISE"]["ACCEL_NET"]
self.pcm_neutral_force = cp.vl["PCM_CRUISE"]["NEUTRAL_FORCE"]
# ZSS Support - Credit goes to the DragonPilot team!
if self.CP.flags & ToyotaFlags.ZSS and self.zss_threshold_count < ZSS_THRESHOLD_COUNT:
zorro_steer = cp.vl["SECONDARY_STEER_ANGLE"]["ZORRO_STEER"]
# Only compute ZSS offset when acc is active
zss_cruise_active = ret.cruiseState.available
if zss_cruise_active and not self.zss_cruise_active_last:
@@ -287,24 +252,6 @@ class CarState(CarStateBase):
return ret
def update_traffic_signals(self, cp_cam):
signals = ["TSGN1", "SPDVAL1", "SPLSGN1", "TSGN2", "SPLSGN2", "TSGN3", "SPLSGN3", "TSGN4", "SPLSGN4"]
new_values = {signal: cp_cam.vl["RSA1"].get(signal, cp_cam.vl["RSA2"].get(signal)) for signal in signals}
if new_values != self.traffic_signals:
self.traffic_signals.update(new_values)
def calculate_speed_limit(self, frogpilot_variables):
tsgn1 = self.traffic_signals.get("TSGN1", None)
spdval1 = self.traffic_signals.get("SPDVAL1", None)
if tsgn1 == 1 and not frogpilot_variables.force_mph_dashboard:
return spdval1 * CV.KPH_TO_MS
elif tsgn1 == 36 or frogpilot_variables.force_mph_dashboard:
return spdval1 * CV.MPH_TO_MS
else:
return 0
@staticmethod
def get_can_parser(CP):
messages = [
@@ -353,7 +300,7 @@ class CarState(CarStateBase):
("PRE_COLLISION", 33),
]
if CP.flags & ToyotaFlags.SMART_DSU:
if CP.flags & ToyotaFlags.SMART_DSU and not CP.flags & ToyotaFlags.RADAR_CAN_FILTER:
messages += [
("SDSU", 100),
]

View File

@@ -573,6 +573,7 @@ FW_VERSIONS = {
b'\x018821F6201400\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'\x028646F12010C0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00',
b'\x028646F12010D0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00',
b'\x028646F1201100\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00',
b'\x028646F1201200\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00',
@@ -638,6 +639,7 @@ FW_VERSIONS = {
(Ecu.dsu, 0x791, None): [
b'881510E01100\x00\x00\x00\x00',
b'881510E01200\x00\x00\x00\x00',
b'881510E02200\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'8821F4702100\x00\x00\x00\x00',
@@ -685,6 +687,7 @@ FW_VERSIONS = {
b'\x01896630EB1000\x00\x00\x00\x00',
b'\x01896630EB1100\x00\x00\x00\x00',
b'\x01896630EB1200\x00\x00\x00\x00',
b'\x01896630EB1300\x00\x00\x00\x00',
b'\x01896630EB2000\x00\x00\x00\x00',
b'\x01896630EB2100\x00\x00\x00\x00',
b'\x01896630EB2200\x00\x00\x00\x00',
@@ -771,16 +774,22 @@ FW_VERSIONS = {
b'\x018966353S1000\x00\x00\x00\x00',
b'\x018966353S2000\x00\x00\x00\x00',
],
(Ecu.engine, 0x7e0, None): [
b'\x02353U0000\x00\x00\x00\x00\x00\x00\x00\x0052422000\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x7b0, None): [
b'\x01F15265337200\x00\x00\x00\x00',
b'\x01F15265342000\x00\x00\x00\x00',
b'\x01F15265343000\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B53450\x00\x00\x00\x00\x00\x00',
b'8965B53800\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F6201200\x00\x00\x00\x00',
b'\x018821F6201300\x00\x00\x00\x00',
b'\x018821F6201400\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'\x028646F5303300\x00\x00\x00\x008646G5301200\x00\x00\x00\x00',
@@ -843,6 +852,7 @@ FW_VERSIONS = {
b'8965B47023\x00\x00\x00\x00\x00\x00',
b'8965B47050\x00\x00\x00\x00\x00\x00',
b'8965B47060\x00\x00\x00\x00\x00\x00',
b'8965B47070\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x7b0, None): [
b'F152647290\x00\x00\x00\x00\x00\x00',
@@ -1024,6 +1034,7 @@ FW_VERSIONS = {
b'\x02896634A13000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x02896634A13001\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',
b'\x02896634A13101\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',
b'\x02896634A13201\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',
b'\x02896634A14001\x00\x00\x00\x00897CF1203001\x00\x00\x00\x00',
b'\x02896634A14001\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',
b'\x02896634A14101\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00',
@@ -1132,6 +1143,7 @@ FW_VERSIONS = {
b'\x01F15264283300\x00\x00\x00\x00',
b'\x01F152642F1000\x00\x00\x00\x00',
b'\x01F152642F8000\x00\x00\x00\x00',
b'\x01F152642F8100\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'\x028965B0R11000\x00\x00\x00\x008965B0R12000\x00\x00\x00\x00',
@@ -1144,6 +1156,7 @@ FW_VERSIONS = {
b'\x01896634AF0000\x00\x00\x00\x00',
b'\x01896634AJ2000\x00\x00\x00\x00',
b'\x01896634AL5000\x00\x00\x00\x00',
b'\x01896634AL6000\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'\x018821F0R03100\x00\x00\x00\x00',
@@ -1263,6 +1276,7 @@ FW_VERSIONS = {
},
CAR.LEXUS_ES: {
(Ecu.engine, 0x7e0, None): [
b'\x02333M4100\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x02333M4200\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x02333R0000\x00\x00\x00\x00\x00\x00\x00\x00A0C01000\x00\x00\x00\x00\x00\x00\x00\x00',
],
@@ -1533,6 +1547,7 @@ FW_VERSIONS = {
b'\x018966348W5100\x00\x00\x00\x00',
b'\x018966348W9000\x00\x00\x00\x00',
b'\x018966348X0000\x00\x00\x00\x00',
b'\x01896634D11000\x00\x00\x00\x00',
b'\x01896634D12000\x00\x00\x00\x00',
b'\x01896634D12100\x00\x00\x00\x00',
b'\x01896634D43000\x00\x00\x00\x00',

View File

@@ -1,15 +1,16 @@
from cereal import car
from openpilot.common.conversions import Conversions as CV
from cereal import car, custom
from panda import Panda
from panda.python import uds
from openpilot.selfdrive.car.toyota.values import Ecu, CAR, DBC, ToyotaFlags, CarControllerParams, TSS2_CAR, RADAR_ACC_CAR, NO_DSU_CAR, \
MIN_ACC_SPEED, EPS_SCALE, UNSUPPORTED_DSU_CAR, NO_STOP_TIMER_CAR, ANGLE_CONTROL_CAR, STOP_AND_GO_CAR
from openpilot.selfdrive.car import get_safety_config
from openpilot.selfdrive.car import create_button_events, get_safety_config
from openpilot.selfdrive.car.disable_ecu import disable_ecu
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
ButtonType = car.CarState.ButtonEvent.Type
EventName = car.CarEvent.EventName
SteerControlType = car.CarParams.SteerControlType
FrogPilotButtonType = custom.FrogPilotCarState.ButtonEvent.Type
class CarInterface(CarInterfaceBase):
@@ -21,7 +22,7 @@ class CarInterface(CarInterfaceBase):
return CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX
@staticmethod
def _get_params(ret, params, candidate, fingerprint, car_fw, experimental_long, docs):
def _get_params(ret, params, candidate, fingerprint, car_fw, disable_openpilot_long, experimental_long, docs):
ret.carName = "toyota"
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.toyota)]
ret.safetyConfigs[0].safetyParam = EPS_SCALE[candidate]
@@ -48,8 +49,6 @@ class CarInterface(CarInterfaceBase):
ret.stoppingControl = False # Toyota starts braking more when it thinks you want to stop
stop_and_go = candidate in TSS2_CAR
# Detect smartDSU, which intercepts ACC_CMD from the DSU (or radar) allowing openpilot to send it
# 0x2AA is sent by a similar device which intercepts the radar instead of DSU on NO_DSU_CARs
if 0x2FF in fingerprint[0] or (0x2AA in fingerprint[0] and candidate in NO_DSU_CAR):
@@ -58,74 +57,22 @@ class CarInterface(CarInterfaceBase):
if 0x2AA in fingerprint[0] and candidate in NO_DSU_CAR:
ret.flags |= ToyotaFlags.RADAR_CAN_FILTER.value
# In TSS2 cars, the camera does long control
found_ecus = [fw.ecu for fw in car_fw]
ret.enableDsu = len(found_ecus) > 0 and Ecu.dsu not in found_ecus and candidate not in (NO_DSU_CAR | UNSUPPORTED_DSU_CAR) \
and not (ret.flags & ToyotaFlags.SMART_DSU)
if candidate == CAR.PRIUS:
ret.wheelbase = 2.70
ret.steerRatio = 15.74 # unknown end-to-end spec
ret.tireStiffnessFactor = 0.6371 # hand-tune
ret.mass = 3045. * CV.LB_TO_KG
# Only give steer angle deadzone to for bad angle sensor prius
for fw in car_fw:
if fw.ecu == "eps" and not fw.fwVersion == b'8965B47060\x00\x00\x00\x00\x00\x00':
ret.steerActuatorDelay = 0.25
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg=0.2)
elif candidate == CAR.PRIUS_V:
ret.wheelbase = 2.78
ret.steerRatio = 17.4
ret.tireStiffnessFactor = 0.5533
ret.mass = 3340. * CV.LB_TO_KG
elif candidate in (CAR.RAV4, CAR.RAV4H):
ret.wheelbase = 2.65
ret.steerRatio = 16.88 # 14.5 is spec end-to-end
ret.tireStiffnessFactor = 0.5533
ret.mass = 3650. * CV.LB_TO_KG # mean between normal and hybrid
elif candidate == CAR.COROLLA:
ret.wheelbase = 2.70
ret.steerRatio = 18.27
ret.tireStiffnessFactor = 0.444 # not optimized yet
ret.mass = 2860. * CV.LB_TO_KG # mean between normal and hybrid
elif candidate in (CAR.LEXUS_RX, CAR.LEXUS_RX_TSS2):
ret.wheelbase = 2.79
ret.steerRatio = 16. # 14.8 is spec end-to-end
ret.wheelSpeedFactor = 1.035
ret.tireStiffnessFactor = 0.5533
ret.mass = 4481. * CV.LB_TO_KG # mean between min and max
elif candidate in (CAR.CHR, CAR.CHR_TSS2):
ret.wheelbase = 2.63906
ret.steerRatio = 13.6
ret.tireStiffnessFactor = 0.7933
ret.mass = 3300. * CV.LB_TO_KG
elif candidate in (CAR.CAMRY, CAR.CAMRY_TSS2):
ret.wheelbase = 2.82448
ret.steerRatio = 13.7
ret.tireStiffnessFactor = 0.7933
ret.mass = 3400. * CV.LB_TO_KG # mean between normal and hybrid
elif candidate in (CAR.HIGHLANDER, CAR.HIGHLANDER_TSS2):
# TODO: TSS-P models can do stop and go, but unclear if it requires sDSU or unplugging DSU
ret.wheelbase = 2.8194 # average of 109.8 and 112.2 in
ret.steerRatio = 16.0
ret.tireStiffnessFactor = 0.8
ret.mass = 4516. * CV.LB_TO_KG # mean between normal and hybrid
elif candidate in (CAR.AVALON, CAR.AVALON_2019, CAR.AVALON_TSS2):
# starting from 2019, all Avalon variants have stop and go
# https://engage.toyota.com/static/images/toyota_safety_sense/TSS_Applicability_Chart.pdf
ret.wheelbase = 2.82
ret.steerRatio = 14.8 # Found at https://pressroom.toyota.com/releases/2016+avalon+product+specs.download
ret.tireStiffnessFactor = 0.7983
ret.mass = 3505. * CV.LB_TO_KG # mean between normal and hybrid
elif candidate in (CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.RAV4_TSS2_2023):
ret.wheelbase = 2.68986
ret.steerRatio = 14.3
ret.tireStiffnessFactor = 0.7933
ret.mass = 3585. * CV.LB_TO_KG # Average between ICE and Hybrid
ret.lateralTuning.init('pid')
ret.lateralTuning.pid.kiBP = [0.0]
ret.lateralTuning.pid.kpBP = [0.0]
@@ -142,92 +89,16 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.pid.kf = 0.00004
break
elif candidate == CAR.COROLLA_TSS2:
ret.wheelbase = 2.67 # Average between 2.70 for sedan and 2.64 for hatchback
ret.steerRatio = 13.9
ret.tireStiffnessFactor = 0.444 # not optimized yet
ret.mass = 3060. * CV.LB_TO_KG
elif candidate in (CAR.LEXUS_ES, CAR.LEXUS_ES_TSS2):
ret.wheelbase = 2.8702
ret.steerRatio = 16.0 # not optimized
ret.tireStiffnessFactor = 0.444 # not optimized yet
ret.mass = 3677. * CV.LB_TO_KG # mean between min and max
elif candidate == CAR.SIENNA:
ret.wheelbase = 3.03
ret.steerRatio = 15.5
ret.tireStiffnessFactor = 0.444
ret.mass = 4590. * CV.LB_TO_KG
elif candidate in (CAR.LEXUS_IS, CAR.LEXUS_IS_TSS2, CAR.LEXUS_RC):
ret.wheelbase = 2.79908
ret.steerRatio = 13.3
ret.tireStiffnessFactor = 0.444
ret.mass = 3736.8 * CV.LB_TO_KG
elif candidate == CAR.LEXUS_GS_F:
ret.wheelbase = 2.84988
ret.steerRatio = 13.3
ret.tireStiffnessFactor = 0.444
ret.mass = 4034. * CV.LB_TO_KG
elif candidate == CAR.LEXUS_CTH:
ret.wheelbase = 2.60
ret.steerRatio = 18.6
ret.tireStiffnessFactor = 0.517
ret.mass = 3108 * CV.LB_TO_KG # mean between min and max
elif candidate in (CAR.LEXUS_NX, CAR.LEXUS_NX_TSS2):
ret.wheelbase = 2.66
ret.steerRatio = 14.7
ret.tireStiffnessFactor = 0.444 # not optimized yet
ret.mass = 4070 * CV.LB_TO_KG
elif candidate == CAR.LEXUS_LC_TSS2:
ret.wheelbase = 2.87
ret.steerRatio = 13.0
ret.tireStiffnessFactor = 0.444 # not optimized yet
ret.mass = 4500 * CV.LB_TO_KG
elif candidate == CAR.PRIUS_TSS2:
ret.wheelbase = 2.70002 # from toyota online sepc.
ret.steerRatio = 13.4 # True steerRatio from older prius
ret.tireStiffnessFactor = 0.6371 # hand-tune
ret.mass = 3115. * CV.LB_TO_KG
elif candidate == CAR.MIRAI:
ret.wheelbase = 2.91
ret.steerRatio = 14.8
ret.tireStiffnessFactor = 0.8
ret.mass = 4300. * CV.LB_TO_KG
elif candidate == CAR.ALPHARD_TSS2:
ret.wheelbase = 3.00
ret.steerRatio = 14.2
ret.tireStiffnessFactor = 0.444
ret.mass = 4305. * CV.LB_TO_KG
ret.centerToFront = ret.wheelbase * 0.44
# TODO: Some TSS-P platforms have BSM, but are flipped based on region or driving direction.
# Detect flipped signals and enable for C-HR and others
ret.enableBsm = 0x3F6 in fingerprint[0] and candidate in TSS2_CAR
# Detect smartDSU, which intercepts ACC_CMD from the DSU (or radar) allowing openpilot to send it
# 0x2AA is sent by a similar device which intercepts the radar instead of DSU on NO_DSU_CARs
if 0x2FF in fingerprint[0] or (0x2AA in fingerprint[0] and candidate in NO_DSU_CAR):
ret.flags |= ToyotaFlags.SMART_DSU.value
# No radar dbc for cars without DSU which are not TSS 2.0
# TODO: make an adas dbc file for dsu-less models
ret.radarUnavailable = DBC[candidate]['radar'] is None or candidate in (NO_DSU_CAR - TSS2_CAR)
# In TSS2 cars, the camera does long control
found_ecus = [fw.ecu for fw in car_fw]
ret.enableDsu = len(found_ecus) > 0 and Ecu.dsu not in found_ecus and candidate not in (NO_DSU_CAR | UNSUPPORTED_DSU_CAR) \
and not (ret.flags & ToyotaFlags.SMART_DSU)
# if the smartDSU is detected, openpilot can send ACC_CONTROL and the smartDSU will block it from the DSU or radar.
# since we don't yet parse radar on TSS2/TSS-P radar-based ACC cars, gate longitudinal behind experimental toggle
use_sdsu = bool(ret.flags & ToyotaFlags.SMART_DSU)
@@ -250,7 +121,7 @@ class CarInterface(CarInterfaceBase):
# - TSS2 radar ACC cars w/o smartDSU installed (disables radar)
# - TSS-P DSU-less cars w/ CAN filter installed (no radar parser yet)
ret.openpilotLongitudinalControl = use_sdsu or ret.enableDsu or candidate in (TSS2_CAR - RADAR_ACC_CAR) or bool(ret.flags & ToyotaFlags.DISABLE_RADAR.value)
ret.openpilotLongitudinalControl &= not params.get_bool("DisableOpenpilotLongitudinal")
ret.openpilotLongitudinalControl &= not disable_openpilot_long
ret.autoResumeSng = ret.openpilotLongitudinalControl and candidate in NO_STOP_TIMER_CAR
ret.enableGasInterceptor = 0x201 in fingerprint[0] and ret.openpilotLongitudinalControl
@@ -271,35 +142,30 @@ class CarInterface(CarInterfaceBase):
# on stock Toyota this is -2.5
ret.stopAccel = -2.5
tune.deadzoneBP = [0., 16., 20., 30.]
tune.deadzoneV = [0., .03, .06, .15]
ret.stoppingDecelRate = 0.17 # This is okay for TSS-P
if candidate in TSS2_CAR:
ret.vEgoStopping = 0.25
ret.vEgoStarting = 0.25
ret.stoppingDecelRate = 0.009 # reach stopping target smoothly
tune.deadzoneV = [.04, .05, .08, .15]
ret.stoppingDecelRate = 0.17
tune.kpBP = [0., 5.]
tune.kpV = [0.8, 1.]
tune.kiBP = [0., 5.]
tune.kiV = [0.3, 1.]
elif params.get_bool("FrogsGoMooTune"):
# on stock Toyota this is -2.5
ret.stopAccel = -2.5
ret.stoppingDecelRate = 0.3 # reach stopping target smoothly
tune.deadzoneBP = [0., 16., 20., 30.]
tune.deadzoneV = [0., .03, .06, .15]
tune.kpBP = [0., 5., 20.]
tune.kpV = [1.3, 1.0, 0.7]
tune.deadzoneV = [0., .03, .06, .15]
# In MPH = [ 0, 27, 45, 60, 89]
tune.kiBP = [ 0., 12., 20., 27., 40.]
tune.kiV = [.35, .215, .195, .10, .01]
if candidate in TSS2_CAR:
ret.stopAccel = -2.5
ret.stoppingDecelRate = 0.009 # reach stopping target smoothly
else:
ret.stopAccel = -2.5 # on stock Toyota this is -2.5
ret.stoppingDecelRate = 0.3 # This is okay for TSS-P
# In MPH = [ 0, 11, 45]
tune.kpBP = [0., 5., 20.]
tune.kpV = [1.3, 1.0, 0.7]
ret.vEgoStarting = 0.1
ret.vEgoStopping = 0.1
ret.vEgoStopping = 0.15 # car is near 0.1 to 0.2 when car starts requesting stopping accel
ret.vEgoStarting = 0.15 # needs to be > or == vEgoStopping
elif (candidate in TSS2_CAR or ret.enableGasInterceptor) and params.get_bool("DragonPilotTune"):
# Credit goes to the DragonPilot team!
tune.deadzoneBP = [0., 16., 20., 30.]
@@ -342,8 +208,16 @@ class CarInterface(CarInterfaceBase):
def _update(self, c, frogpilot_variables):
ret = self.CS.update(self.cp, self.cp_cam, frogpilot_variables)
if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR) or (self.CP.flags & ToyotaFlags.SMART_DSU and not self.CP.flags & ToyotaFlags.RADAR_CAN_FILTER):
ret.buttonEvents = [
*create_button_events(self.CS.cruise_increased, self.CS.cruise_increased_previously, {1: ButtonType.accelCruise}),
*create_button_events(self.CS.cruise_decreased, self.CS.cruise_decreased_previously, {1: ButtonType.decelCruise}),
*create_button_events(self.CS.distance_button, self.CS.prev_distance_button, {1: ButtonType.gapAdjustCruise}),
*create_button_events(self.CS.lkas_enabled, self.CS.lkas_previously_enabled, {1: FrogPilotButtonType.lkas}),
]
# events
events = self.create_common_events(ret, frogpilot_variables)
events = self.create_common_events(ret)
# Lane Tracing Assist control is unavailable (EPS_STATUS->LTA_STATE=0) until
# the more accurate angle sensor signal is initialized

View File

View File

@@ -0,0 +1,35 @@
#!/usr/bin/env python3
from collections import defaultdict
from cereal import car
from openpilot.selfdrive.car.toyota.values import PLATFORM_CODE_ECUS, get_platform_codes
from openpilot.selfdrive.car.toyota.fingerprints import FW_VERSIONS
Ecu = car.CarParams.Ecu
ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()}
if __name__ == "__main__":
parts_for_ecu: dict = defaultdict(set)
cars_for_code: dict = defaultdict(lambda: defaultdict(set))
for car_model, ecus in FW_VERSIONS.items():
print()
print(car_model)
for ecu in sorted(ecus, key=lambda x: int(x[0])):
if ecu[0] not in PLATFORM_CODE_ECUS:
continue
platform_codes = get_platform_codes(ecus[ecu])
parts_for_ecu[ecu] |= {code.split(b'-')[0] for code in platform_codes if code.count(b'-') > 1}
for code in platform_codes:
cars_for_code[ecu][b'-'.join(code.split(b'-')[:2])] |= {car_model}
print(f' (Ecu.{ECU_NAME[ecu[0]]}, {hex(ecu[1])}, {ecu[2]}):')
print(f' Codes: {platform_codes}')
print('\nECU parts:')
for ecu, parts in parts_for_ecu.items():
print(f' (Ecu.{ECU_NAME[ecu[0]]}, {hex(ecu[1])}, {ecu[2]}): {parts}')
print('\nCar models vs. platform codes (no major versions):')
for ecu, codes in cars_for_code.items():
print(f' (Ecu.{ECU_NAME[ecu[0]]}, {hex(ecu[1])}, {ecu[2]}):')
for code, cars in codes.items():
print(f' {code!r}: {sorted(cars)}')

View File

@@ -0,0 +1,174 @@
#!/usr/bin/env python3
from hypothesis import given, settings, strategies as st
import unittest
from cereal import car
from openpilot.selfdrive.car.fw_versions import build_fw_dict
from openpilot.selfdrive.car.toyota.fingerprints import FW_VERSIONS
from openpilot.selfdrive.car.toyota.values import CAR, DBC, TSS2_CAR, ANGLE_CONTROL_CAR, RADAR_ACC_CAR, \
FW_QUERY_CONFIG, PLATFORM_CODE_ECUS, FUZZY_EXCLUDED_PLATFORMS, \
get_platform_codes
Ecu = car.CarParams.Ecu
ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()}
def check_fw_version(fw_version: bytes) -> bool:
return b'?' not in fw_version
class TestToyotaInterfaces(unittest.TestCase):
def test_car_sets(self):
self.assertTrue(len(ANGLE_CONTROL_CAR - TSS2_CAR) == 0)
self.assertTrue(len(RADAR_ACC_CAR - TSS2_CAR) == 0)
def test_lta_platforms(self):
# At this time, only RAV4 2023 is expected to use LTA/angle control
self.assertEqual(ANGLE_CONTROL_CAR, {CAR.RAV4_TSS2_2023})
def test_tss2_dbc(self):
# We make some assumptions about TSS2 platforms,
# like looking up certain signals only in this DBC
for car_model, dbc in DBC.items():
if car_model in TSS2_CAR:
self.assertEqual(dbc["pt"], "toyota_nodsu_pt_generated")
def test_essential_ecus(self):
# Asserts standard ECUs exist for each platform
common_ecus = {Ecu.fwdRadar, Ecu.fwdCamera}
for car_model, ecus in FW_VERSIONS.items():
with self.subTest(car_model=car_model.value):
present_ecus = {ecu[0] for ecu in ecus}
missing_ecus = common_ecus - present_ecus
self.assertEqual(len(missing_ecus), 0)
# Some exceptions for other common ECUs
if car_model not in (CAR.ALPHARD_TSS2,):
self.assertIn(Ecu.abs, present_ecus)
if car_model not in (CAR.MIRAI,):
self.assertIn(Ecu.engine, present_ecus)
if car_model not in (CAR.PRIUS_V, CAR.LEXUS_CTH):
self.assertIn(Ecu.eps, present_ecus)
class TestToyotaFingerprint(unittest.TestCase):
def test_non_essential_ecus(self):
# Ensures only the cars that have multiple engine ECUs are in the engine non-essential ECU list
for car_model, ecus in FW_VERSIONS.items():
with self.subTest(car_model=car_model.value):
engine_ecus = {ecu for ecu in ecus if ecu[0] == Ecu.engine}
self.assertEqual(len(engine_ecus) > 1,
car_model in FW_QUERY_CONFIG.non_essential_ecus[Ecu.engine],
f"Car model unexpectedly {'not ' if len(engine_ecus) > 1 else ''}in non-essential list")
def test_valid_fw_versions(self):
# Asserts all FW versions are valid
for car_model, ecus in FW_VERSIONS.items():
with self.subTest(car_model=car_model.value):
for fws in ecus.values():
for fw in fws:
self.assertTrue(check_fw_version(fw), fw)
# Tests for part numbers, platform codes, and sub-versions which Toyota will use to fuzzy
# fingerprint in the absence of full FW matches:
@settings(max_examples=100)
@given(data=st.data())
def test_platform_codes_fuzzy_fw(self, data):
fw_strategy = st.lists(st.binary())
fws = data.draw(fw_strategy)
get_platform_codes(fws)
def test_platform_code_ecus_available(self):
# Asserts ECU keys essential for fuzzy fingerprinting are available on all platforms
for car_model, ecus in FW_VERSIONS.items():
with self.subTest(car_model=car_model.value):
for platform_code_ecu in PLATFORM_CODE_ECUS:
if platform_code_ecu == Ecu.eps and car_model in (CAR.PRIUS_V, CAR.LEXUS_CTH,):
continue
if platform_code_ecu == Ecu.abs and car_model in (CAR.ALPHARD_TSS2,):
continue
self.assertIn(platform_code_ecu, [e[0] for e in ecus])
def test_fw_format(self):
# Asserts:
# - every supported ECU FW version returns one platform code
# - every supported ECU FW version has a part number
# - expected parsing of ECU sub-versions
for car_model, ecus in FW_VERSIONS.items():
with self.subTest(car_model=car_model.value):
for ecu, fws in ecus.items():
if ecu[0] not in PLATFORM_CODE_ECUS:
continue
codes = dict()
for fw in fws:
result = get_platform_codes([fw])
# Check only one platform code and sub-version
self.assertEqual(1, len(result), f"Unable to parse FW: {fw}")
self.assertEqual(1, len(list(result.values())[0]), f"Unable to parse FW: {fw}")
codes |= result
# Toyota places the ECU part number in their FW versions, assert all parsable
# Note that there is only one unique part number per ECU across the fleet, so this
# is not important for identification, just a sanity check.
self.assertTrue(all(code.count(b"-") > 1 for code in codes),
f"FW does not have part number: {fw} {codes}")
def test_platform_codes_spot_check(self):
# Asserts basic platform code parsing behavior for a few cases
results = get_platform_codes([
b"F152607140\x00\x00\x00\x00\x00\x00",
b"F152607171\x00\x00\x00\x00\x00\x00",
b"F152607110\x00\x00\x00\x00\x00\x00",
b"F152607180\x00\x00\x00\x00\x00\x00",
])
self.assertEqual(results, {b"F1526-07-1": {b"10", b"40", b"71", b"80"}})
results = get_platform_codes([
b"\x028646F4104100\x00\x00\x00\x008646G5301200\x00\x00\x00\x00",
b"\x028646F4104100\x00\x00\x00\x008646G3304000\x00\x00\x00\x00",
])
self.assertEqual(results, {b"8646F-41-04": {b"100"}})
# Short version has no part number
results = get_platform_codes([
b"\x0235870000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00",
b"\x0235883000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00",
])
self.assertEqual(results, {b"58-70": {b"000"}, b"58-83": {b"000"}})
results = get_platform_codes([
b"F152607110\x00\x00\x00\x00\x00\x00",
b"F152607140\x00\x00\x00\x00\x00\x00",
b"\x028646F4104100\x00\x00\x00\x008646G5301200\x00\x00\x00\x00",
b"\x0235879000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00",
])
self.assertEqual(results, {b"F1526-07-1": {b"10", b"40"}, b"8646F-41-04": {b"100"}, b"58-79": {b"000"}})
def test_fuzzy_excluded_platforms(self):
# Asserts a list of platforms that will not fuzzy fingerprint with platform codes due to them being shared.
platforms_with_shared_codes = set()
for platform, fw_by_addr in FW_VERSIONS.items():
car_fw = []
for ecu, fw_versions in fw_by_addr.items():
ecu_name, addr, sub_addr = ecu
for fw in fw_versions:
car_fw.append({"ecu": ecu_name, "fwVersion": fw, "address": addr,
"subAddress": 0 if sub_addr is None else sub_addr})
CP = car.CarParams.new_message(carFw=car_fw)
matches = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(build_fw_dict(CP.carFw), FW_VERSIONS)
if len(matches) == 1:
self.assertEqual(list(matches)[0], platform)
else:
# If a platform has multiple matches, add it and its matches
platforms_with_shared_codes |= {str(platform), *matches}
self.assertEqual(platforms_with_shared_codes, FUZZY_EXCLUDED_PLATFORMS, (len(platforms_with_shared_codes), len(FW_VERSIONS)))
if __name__ == "__main__":
unittest.main()

View File

@@ -33,19 +33,19 @@ def create_lta_steer_command(packer, steer_control_type, steer_angle, steer_req,
return packer.make_can_msg("STEERING_LTA", 0, values)
def create_accel_command(packer, accel, accel_raw, pcm_cancel, standstill_req, lead, acc_type, fcw_alert, distance_button, frogpilot_variables):
def create_accel_command(packer, accel, accel_raw, pcm_cancel, standstill_req, lead, acc_type, fcw_alert, distance, frogpilot_variables):
# TODO: find the exact canceling bit that does not create a chime
values = {
"ACCEL_CMD": accel, # compensated accel command
"ACC_TYPE": acc_type,
"DISTANCE": distance_button,
"DISTANCE": distance,
"MINI_CAR": lead,
"PERMIT_BRAKING": 1,
"RELEASE_STANDSTILL": not standstill_req,
"CANCEL_REQ": pcm_cancel,
"ALLOW_LONG_PRESS": 2 if frogpilot_variables.reverse_cruise_increase else 1,
"ACC_CUT_IN": fcw_alert, # only shown when ACC enabled
"ACCEL_CMD_ALT": accel_raw, # raw accel command, pcm uses this to calculate a compensatory force
"ACCEL_CMD_ALT": accel_raw, # raw accel command, pcm uses this to calculate a compensatory force
}
return packer.make_can_msg("ACC_CONTROL", 0, values)

View File

@@ -1,13 +1,13 @@
import re
from collections import defaultdict
from dataclasses import dataclass, field
from enum import Enum, IntFlag, StrEnum
from typing import Dict, List, Set, Union
from enum import Enum, IntFlag
from cereal import car
from openpilot.common.conversions import Conversions as CV
from openpilot.selfdrive.car import CarSpecs, PlatformConfig, Platforms
from openpilot.selfdrive.car import AngleRateLimit, dbc_dict
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarInfo, Column, CarParts, CarHarness
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarDocs, Column, CarParts, CarHarness
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
Ecu = car.CarParams.Ecu
@@ -43,52 +43,24 @@ class CarControllerParams:
class ToyotaFlags(IntFlag):
# Detected flags
HYBRID = 1
SMART_DSU = 2
DISABLE_RADAR = 4
ZSS = 8
RADAR_CAN_FILTER = 1024
# Static flags
TSS2 = 8
NO_DSU = 16
UNSUPPORTED_DSU = 32
RADAR_ACC = 64
# these cars use the Lane Tracing Assist (LTA) message for lateral control
ANGLE_CONTROL = 128
NO_STOP_TIMER = 256
# these cars are speculated to allow stop and go when the DSU is unplugged or disabled with sDSU
SNG_WITHOUT_DSU = 512
class CAR(StrEnum):
# Toyota
ALPHARD_TSS2 = "TOYOTA ALPHARD 2020"
AVALON = "TOYOTA AVALON 2016"
AVALON_2019 = "TOYOTA AVALON 2019"
AVALON_TSS2 = "TOYOTA AVALON 2022" # TSS 2.5
CAMRY = "TOYOTA CAMRY 2018"
CAMRY_TSS2 = "TOYOTA CAMRY 2021" # TSS 2.5
CHR = "TOYOTA C-HR 2018"
CHR_TSS2 = "TOYOTA C-HR 2021"
COROLLA = "TOYOTA COROLLA 2017"
# LSS2 Lexus UX Hybrid is same as a TSS2 Corolla Hybrid
COROLLA_TSS2 = "TOYOTA COROLLA TSS2 2019"
HIGHLANDER = "TOYOTA HIGHLANDER 2017"
HIGHLANDER_TSS2 = "TOYOTA HIGHLANDER 2020"
PRIUS = "TOYOTA PRIUS 2017"
PRIUS_V = "TOYOTA PRIUS v 2017"
PRIUS_TSS2 = "TOYOTA PRIUS TSS2 2021"
RAV4 = "TOYOTA RAV4 2017"
RAV4H = "TOYOTA RAV4 HYBRID 2017"
RAV4_TSS2 = "TOYOTA RAV4 2019"
RAV4_TSS2_2022 = "TOYOTA RAV4 2022"
RAV4_TSS2_2023 = "TOYOTA RAV4 2023"
MIRAI = "TOYOTA MIRAI 2021" # TSS 2.5
SIENNA = "TOYOTA SIENNA 2018"
# Lexus
LEXUS_CTH = "LEXUS CT HYBRID 2018"
LEXUS_ES = "LEXUS ES 2018"
LEXUS_ES_TSS2 = "LEXUS ES 2019"
LEXUS_IS = "LEXUS IS 2018"
LEXUS_IS_TSS2 = "LEXUS IS 2023"
LEXUS_NX = "LEXUS NX 2018"
LEXUS_NX_TSS2 = "LEXUS NX 2020"
LEXUS_LC_TSS2 = "LEXUS LC 2024"
LEXUS_RC = "LEXUS RC 2020"
LEXUS_RX = "LEXUS RX 2016"
LEXUS_RX_TSS2 = "LEXUS RX 2020"
LEXUS_GS_F = "LEXUS GS F 2016"
ZSS = 1024
class Footnote(Enum):
CAMRY = CarFootnote(
@@ -97,132 +69,310 @@ class Footnote(Enum):
@dataclass
class ToyotaCarInfo(CarInfo):
class ToyotaCarDocs(CarDocs):
package: str = "All"
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.toyota_a]))
CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = {
@dataclass
class ToyotaTSS2PlatformConfig(PlatformConfig):
dbc_dict: dict = field(default_factory=lambda: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'))
def init(self):
self.flags |= ToyotaFlags.TSS2 | ToyotaFlags.NO_STOP_TIMER | ToyotaFlags.NO_DSU
if self.flags & ToyotaFlags.RADAR_ACC:
self.dbc_dict = dbc_dict('toyota_nodsu_pt_generated', None)
class CAR(Platforms):
# Toyota
CAR.ALPHARD_TSS2: [
ToyotaCarInfo("Toyota Alphard 2019-20"),
ToyotaCarInfo("Toyota Alphard Hybrid 2021"),
],
CAR.AVALON: [
ToyotaCarInfo("Toyota Avalon 2016", "Toyota Safety Sense P"),
ToyotaCarInfo("Toyota Avalon 2017-18"),
],
CAR.AVALON_2019: [
ToyotaCarInfo("Toyota Avalon 2019-21"),
ToyotaCarInfo("Toyota Avalon Hybrid 2019-21"),
],
CAR.AVALON_TSS2: [
ToyotaCarInfo("Toyota Avalon 2022"),
ToyotaCarInfo("Toyota Avalon Hybrid 2022"),
],
CAR.CAMRY: [
ToyotaCarInfo("Toyota Camry 2018-20", video_link="https://www.youtube.com/watch?v=fkcjviZY9CM", footnotes=[Footnote.CAMRY]),
ToyotaCarInfo("Toyota Camry Hybrid 2018-20", video_link="https://www.youtube.com/watch?v=Q2DYY0AWKgk"),
],
CAR.CAMRY_TSS2: [
ToyotaCarInfo("Toyota Camry 2021-24", footnotes=[Footnote.CAMRY]),
ToyotaCarInfo("Toyota Camry Hybrid 2021-24"),
],
CAR.CHR: [
ToyotaCarInfo("Toyota C-HR 2017-20"),
ToyotaCarInfo("Toyota C-HR Hybrid 2017-20"),
],
CAR.CHR_TSS2: [
ToyotaCarInfo("Toyota C-HR 2021"),
ToyotaCarInfo("Toyota C-HR Hybrid 2021-22"),
],
CAR.COROLLA: ToyotaCarInfo("Toyota Corolla 2017-19"),
CAR.COROLLA_TSS2: [
ToyotaCarInfo("Toyota Corolla 2020-22", video_link="https://www.youtube.com/watch?v=_66pXk0CBYA"),
ToyotaCarInfo("Toyota Corolla Cross (Non-US only) 2020-23", min_enable_speed=7.5),
ToyotaCarInfo("Toyota Corolla Hatchback 2019-22", video_link="https://www.youtube.com/watch?v=_66pXk0CBYA"),
# Hybrid platforms
ToyotaCarInfo("Toyota Corolla Hybrid 2020-22"),
ToyotaCarInfo("Toyota Corolla Hybrid (Non-US only) 2020-23", min_enable_speed=7.5),
ToyotaCarInfo("Toyota Corolla Cross Hybrid (Non-US only) 2020-22", min_enable_speed=7.5),
ToyotaCarInfo("Lexus UX Hybrid 2019-23"),
],
CAR.HIGHLANDER: [
ToyotaCarInfo("Toyota Highlander 2017-19", video_link="https://www.youtube.com/watch?v=0wS0wXSLzoo"),
ToyotaCarInfo("Toyota Highlander Hybrid 2017-19"),
],
CAR.HIGHLANDER_TSS2: [
ToyotaCarInfo("Toyota Highlander 2020-23"),
ToyotaCarInfo("Toyota Highlander Hybrid 2020-23"),
],
CAR.PRIUS: [
ToyotaCarInfo("Toyota Prius 2016", "Toyota Safety Sense P", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"),
ToyotaCarInfo("Toyota Prius 2017-20", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"),
ToyotaCarInfo("Toyota Prius Prime 2017-20", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"),
],
CAR.PRIUS_V: ToyotaCarInfo("Toyota Prius v 2017", "Toyota Safety Sense P", min_enable_speed=MIN_ACC_SPEED),
CAR.PRIUS_TSS2: [
ToyotaCarInfo("Toyota Prius 2021-22", video_link="https://www.youtube.com/watch?v=J58TvCpUd4U"),
ToyotaCarInfo("Toyota Prius Prime 2021-22", video_link="https://www.youtube.com/watch?v=J58TvCpUd4U"),
],
CAR.RAV4: [
ToyotaCarInfo("Toyota RAV4 2016", "Toyota Safety Sense P"),
ToyotaCarInfo("Toyota RAV4 2017-18")
],
CAR.RAV4H: [
ToyotaCarInfo("Toyota RAV4 Hybrid 2016", "Toyota Safety Sense P", video_link="https://youtu.be/LhT5VzJVfNI?t=26"),
ToyotaCarInfo("Toyota RAV4 Hybrid 2017-18", video_link="https://youtu.be/LhT5VzJVfNI?t=26")
],
CAR.RAV4_TSS2: [
ToyotaCarInfo("Toyota RAV4 2019-21", video_link="https://www.youtube.com/watch?v=wJxjDd42gGA"),
ToyotaCarInfo("Toyota RAV4 Hybrid 2019-21"),
],
CAR.RAV4_TSS2_2022: [
ToyotaCarInfo("Toyota RAV4 2022"),
ToyotaCarInfo("Toyota RAV4 Hybrid 2022", video_link="https://youtu.be/U0nH9cnrFB0"),
],
CAR.RAV4_TSS2_2023: [
ToyotaCarInfo("Toyota RAV4 2023-24"),
ToyotaCarInfo("Toyota RAV4 Hybrid 2023-24"),
],
CAR.MIRAI: ToyotaCarInfo("Toyota Mirai 2021"),
CAR.SIENNA: ToyotaCarInfo("Toyota Sienna 2018-20", video_link="https://www.youtube.com/watch?v=q1UPOo4Sh68", min_enable_speed=MIN_ACC_SPEED),
ALPHARD_TSS2 = ToyotaTSS2PlatformConfig(
"TOYOTA ALPHARD 2020",
[
ToyotaCarDocs("Toyota Alphard 2019-20"),
ToyotaCarDocs("Toyota Alphard Hybrid 2021"),
],
CarSpecs(mass=4305. * CV.LB_TO_KG, wheelbase=3.0, steerRatio=14.2, tireStiffnessFactor=0.444),
)
AVALON = PlatformConfig(
"TOYOTA AVALON 2016",
[
ToyotaCarDocs("Toyota Avalon 2016", "Toyota Safety Sense P"),
ToyotaCarDocs("Toyota Avalon 2017-18"),
],
CarSpecs(mass=3505. * CV.LB_TO_KG, wheelbase=2.82, steerRatio=14.8, tireStiffnessFactor=0.7983),
dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
)
AVALON_2019 = PlatformConfig(
"TOYOTA AVALON 2019",
[
ToyotaCarDocs("Toyota Avalon 2019-21"),
ToyotaCarDocs("Toyota Avalon Hybrid 2019-21"),
],
AVALON.specs,
dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
)
AVALON_TSS2 = ToyotaTSS2PlatformConfig(
"TOYOTA AVALON 2022", # TSS 2.5
[
ToyotaCarDocs("Toyota Avalon 2022"),
ToyotaCarDocs("Toyota Avalon Hybrid 2022"),
],
AVALON.specs,
)
CAMRY = PlatformConfig(
"TOYOTA CAMRY 2018",
[
ToyotaCarDocs("Toyota Camry 2018-20", video_link="https://www.youtube.com/watch?v=fkcjviZY9CM", footnotes=[Footnote.CAMRY]),
ToyotaCarDocs("Toyota Camry Hybrid 2018-20", video_link="https://www.youtube.com/watch?v=Q2DYY0AWKgk"),
],
CarSpecs(mass=3400. * CV.LB_TO_KG, wheelbase=2.82448, steerRatio=13.7, tireStiffnessFactor=0.7933),
dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
flags=ToyotaFlags.NO_DSU,
)
CAMRY_TSS2 = ToyotaTSS2PlatformConfig(
"TOYOTA CAMRY 2021", # TSS 2.5
[
ToyotaCarDocs("Toyota Camry 2021-24", footnotes=[Footnote.CAMRY]),
ToyotaCarDocs("Toyota Camry Hybrid 2021-24"),
],
CAMRY.specs,
)
CHR = PlatformConfig(
"TOYOTA C-HR 2018",
[
ToyotaCarDocs("Toyota C-HR 2017-20"),
ToyotaCarDocs("Toyota C-HR Hybrid 2017-20"),
],
CarSpecs(mass=3300. * CV.LB_TO_KG, wheelbase=2.63906, steerRatio=13.6, tireStiffnessFactor=0.7933),
dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
flags=ToyotaFlags.NO_DSU,
)
CHR_TSS2 = ToyotaTSS2PlatformConfig(
"TOYOTA C-HR 2021",
[
ToyotaCarDocs("Toyota C-HR 2021"),
ToyotaCarDocs("Toyota C-HR Hybrid 2021-22"),
],
CHR.specs,
flags=ToyotaFlags.RADAR_ACC,
)
COROLLA = PlatformConfig(
"TOYOTA COROLLA 2017",
[ToyotaCarDocs("Toyota Corolla 2017-19")],
CarSpecs(mass=2860. * CV.LB_TO_KG, wheelbase=2.7, steerRatio=18.27, tireStiffnessFactor=0.444),
dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
)
# LSS2 Lexus UX Hybrid is same as a TSS2 Corolla Hybrid
COROLLA_TSS2 = ToyotaTSS2PlatformConfig(
"TOYOTA COROLLA TSS2 2019",
[
ToyotaCarDocs("Toyota Corolla 2020-22", video_link="https://www.youtube.com/watch?v=_66pXk0CBYA"),
ToyotaCarDocs("Toyota Corolla Cross (Non-US only) 2020-23", min_enable_speed=7.5),
ToyotaCarDocs("Toyota Corolla Hatchback 2019-22", video_link="https://www.youtube.com/watch?v=_66pXk0CBYA"),
# Hybrid platforms
ToyotaCarDocs("Toyota Corolla Hybrid 2020-22"),
ToyotaCarDocs("Toyota Corolla Hybrid (Non-US only) 2020-23", min_enable_speed=7.5),
ToyotaCarDocs("Toyota Corolla Cross Hybrid (Non-US only) 2020-22", min_enable_speed=7.5),
ToyotaCarDocs("Lexus UX Hybrid 2019-23"),
],
CarSpecs(mass=3060. * CV.LB_TO_KG, wheelbase=2.67, steerRatio=13.9, tireStiffnessFactor=0.444),
)
HIGHLANDER = PlatformConfig(
"TOYOTA HIGHLANDER 2017",
[
ToyotaCarDocs("Toyota Highlander 2017-19", video_link="https://www.youtube.com/watch?v=0wS0wXSLzoo"),
ToyotaCarDocs("Toyota Highlander Hybrid 2017-19"),
],
CarSpecs(mass=4516. * CV.LB_TO_KG, wheelbase=2.8194, steerRatio=16.0, tireStiffnessFactor=0.8),
dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
flags=ToyotaFlags.NO_STOP_TIMER | ToyotaFlags.SNG_WITHOUT_DSU,
)
HIGHLANDER_TSS2 = ToyotaTSS2PlatformConfig(
"TOYOTA HIGHLANDER 2020",
[
ToyotaCarDocs("Toyota Highlander 2020-23"),
ToyotaCarDocs("Toyota Highlander Hybrid 2020-23"),
],
HIGHLANDER.specs,
)
PRIUS = PlatformConfig(
"TOYOTA PRIUS 2017",
[
ToyotaCarDocs("Toyota Prius 2016", "Toyota Safety Sense P", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"),
ToyotaCarDocs("Toyota Prius 2017-20", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"),
ToyotaCarDocs("Toyota Prius Prime 2017-20", video_link="https://www.youtube.com/watch?v=8zopPJI8XQ0"),
],
CarSpecs(mass=3045. * CV.LB_TO_KG, wheelbase=2.7, steerRatio=15.74, tireStiffnessFactor=0.6371),
dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
)
PRIUS_V = PlatformConfig(
"TOYOTA PRIUS v 2017",
[ToyotaCarDocs("Toyota Prius v 2017", "Toyota Safety Sense P", min_enable_speed=MIN_ACC_SPEED)],
CarSpecs(mass=3340. * CV.LB_TO_KG, wheelbase=2.78, steerRatio=17.4, tireStiffnessFactor=0.5533),
dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
flags=ToyotaFlags.NO_STOP_TIMER | ToyotaFlags.SNG_WITHOUT_DSU,
)
PRIUS_TSS2 = ToyotaTSS2PlatformConfig(
"TOYOTA PRIUS TSS2 2021",
[
ToyotaCarDocs("Toyota Prius 2021-22", video_link="https://www.youtube.com/watch?v=J58TvCpUd4U"),
ToyotaCarDocs("Toyota Prius Prime 2021-22", video_link="https://www.youtube.com/watch?v=J58TvCpUd4U"),
],
CarSpecs(mass=3115. * CV.LB_TO_KG, wheelbase=2.70002, steerRatio=13.4, tireStiffnessFactor=0.6371),
)
RAV4 = PlatformConfig(
"TOYOTA RAV4 2017",
[
ToyotaCarDocs("Toyota RAV4 2016", "Toyota Safety Sense P"),
ToyotaCarDocs("Toyota RAV4 2017-18")
],
CarSpecs(mass=3650. * CV.LB_TO_KG, wheelbase=2.65, steerRatio=16.88, tireStiffnessFactor=0.5533),
dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
)
RAV4H = PlatformConfig(
"TOYOTA RAV4 HYBRID 2017",
[
ToyotaCarDocs("Toyota RAV4 Hybrid 2016", "Toyota Safety Sense P", video_link="https://youtu.be/LhT5VzJVfNI?t=26"),
ToyotaCarDocs("Toyota RAV4 Hybrid 2017-18", video_link="https://youtu.be/LhT5VzJVfNI?t=26")
],
RAV4.specs,
dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
flags=ToyotaFlags.NO_STOP_TIMER,
)
RAV4_TSS2 = ToyotaTSS2PlatformConfig(
"TOYOTA RAV4 2019",
[
ToyotaCarDocs("Toyota RAV4 2019-21", video_link="https://www.youtube.com/watch?v=wJxjDd42gGA"),
ToyotaCarDocs("Toyota RAV4 Hybrid 2019-21"),
],
CarSpecs(mass=3585. * CV.LB_TO_KG, wheelbase=2.68986, steerRatio=14.3, tireStiffnessFactor=0.7933),
)
RAV4_TSS2_2022 = ToyotaTSS2PlatformConfig(
"TOYOTA RAV4 2022",
[
ToyotaCarDocs("Toyota RAV4 2022"),
ToyotaCarDocs("Toyota RAV4 Hybrid 2022", video_link="https://youtu.be/U0nH9cnrFB0"),
],
RAV4_TSS2.specs,
flags=ToyotaFlags.RADAR_ACC,
)
RAV4_TSS2_2023 = ToyotaTSS2PlatformConfig(
"TOYOTA RAV4 2023",
[
ToyotaCarDocs("Toyota RAV4 2023-24"),
ToyotaCarDocs("Toyota RAV4 Hybrid 2023-24"),
],
RAV4_TSS2.specs,
flags=ToyotaFlags.RADAR_ACC | ToyotaFlags.ANGLE_CONTROL,
)
MIRAI = ToyotaTSS2PlatformConfig(
"TOYOTA MIRAI 2021", # TSS 2.5
[ToyotaCarDocs("Toyota Mirai 2021")],
CarSpecs(mass=4300. * CV.LB_TO_KG, wheelbase=2.91, steerRatio=14.8, tireStiffnessFactor=0.8),
)
SIENNA = PlatformConfig(
"TOYOTA SIENNA 2018",
[ToyotaCarDocs("Toyota Sienna 2018-20", video_link="https://www.youtube.com/watch?v=q1UPOo4Sh68", min_enable_speed=MIN_ACC_SPEED)],
CarSpecs(mass=4590. * CV.LB_TO_KG, wheelbase=3.03, steerRatio=15.5, tireStiffnessFactor=0.444),
dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
flags=ToyotaFlags.NO_STOP_TIMER,
)
# Lexus
CAR.LEXUS_CTH: ToyotaCarInfo("Lexus CT Hybrid 2017-18", "Lexus Safety System+"),
CAR.LEXUS_ES: [
ToyotaCarInfo("Lexus ES 2017-18"),
ToyotaCarInfo("Lexus ES Hybrid 2017-18"),
],
CAR.LEXUS_ES_TSS2: [
ToyotaCarInfo("Lexus ES 2019-24"),
ToyotaCarInfo("Lexus ES Hybrid 2019-24", video_link="https://youtu.be/BZ29osRVJeg?t=12"),
],
CAR.LEXUS_IS: ToyotaCarInfo("Lexus IS 2017-19"),
CAR.LEXUS_IS_TSS2: ToyotaCarInfo("Lexus IS 2022-23"),
CAR.LEXUS_GS_F: ToyotaCarInfo("Lexus GS F 2016"),
CAR.LEXUS_NX: [
ToyotaCarInfo("Lexus NX 2018-19"),
ToyotaCarInfo("Lexus NX Hybrid 2018-19"),
],
CAR.LEXUS_NX_TSS2: [
ToyotaCarInfo("Lexus NX 2020-21"),
ToyotaCarInfo("Lexus NX Hybrid 2020-21"),
],
CAR.LEXUS_LC_TSS2: ToyotaCarInfo("Lexus LC 2024"),
CAR.LEXUS_RC: ToyotaCarInfo("Lexus RC 2018-20"),
CAR.LEXUS_RX: [
ToyotaCarInfo("Lexus RX 2016", "Lexus Safety System+"),
ToyotaCarInfo("Lexus RX 2017-19"),
# Hybrid platforms
ToyotaCarInfo("Lexus RX Hybrid 2016", "Lexus Safety System+"),
ToyotaCarInfo("Lexus RX Hybrid 2017-19"),
],
CAR.LEXUS_RX_TSS2: [
ToyotaCarInfo("Lexus RX 2020-22"),
ToyotaCarInfo("Lexus RX Hybrid 2020-22"),
],
}
LEXUS_CTH = PlatformConfig(
"LEXUS CT HYBRID 2018",
[ToyotaCarDocs("Lexus CT Hybrid 2017-18", "Lexus Safety System+")],
CarSpecs(mass=3108. * CV.LB_TO_KG, wheelbase=2.6, steerRatio=18.6, tireStiffnessFactor=0.517),
dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
)
LEXUS_ES = PlatformConfig(
"LEXUS ES 2018",
[
ToyotaCarDocs("Lexus ES 2017-18"),
ToyotaCarDocs("Lexus ES Hybrid 2017-18"),
],
CarSpecs(mass=3677. * CV.LB_TO_KG, wheelbase=2.8702, steerRatio=16.0, tireStiffnessFactor=0.444),
dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
)
LEXUS_ES_TSS2 = ToyotaTSS2PlatformConfig(
"LEXUS ES 2019",
[
ToyotaCarDocs("Lexus ES 2019-24"),
ToyotaCarDocs("Lexus ES Hybrid 2019-24", video_link="https://youtu.be/BZ29osRVJeg?t=12"),
],
LEXUS_ES.specs,
)
LEXUS_IS = PlatformConfig(
"LEXUS IS 2018",
[ToyotaCarDocs("Lexus IS 2017-19")],
CarSpecs(mass=3736.8 * CV.LB_TO_KG, wheelbase=2.79908, steerRatio=13.3, tireStiffnessFactor=0.444),
dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
flags=ToyotaFlags.UNSUPPORTED_DSU,
)
LEXUS_IS_TSS2 = ToyotaTSS2PlatformConfig(
"LEXUS IS 2023",
[ToyotaCarDocs("Lexus IS 2022-23")],
LEXUS_IS.specs,
)
LEXUS_NX = PlatformConfig(
"LEXUS NX 2018",
[
ToyotaCarDocs("Lexus NX 2018-19"),
ToyotaCarDocs("Lexus NX Hybrid 2018-19"),
],
CarSpecs(mass=4070. * CV.LB_TO_KG, wheelbase=2.66, steerRatio=14.7, tireStiffnessFactor=0.444),
dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
)
LEXUS_NX_TSS2 = ToyotaTSS2PlatformConfig(
"LEXUS NX 2020",
[
ToyotaCarDocs("Lexus NX 2020-21"),
ToyotaCarDocs("Lexus NX Hybrid 2020-21"),
],
LEXUS_NX.specs,
)
LEXUS_LC_TSS2 = ToyotaTSS2PlatformConfig(
"LEXUS LC 2024",
[ToyotaCarDocs("Lexus LC 2024")],
CarSpecs(mass=4500. * CV.LB_TO_KG, wheelbase=2.87, steerRatio=13.0, tireStiffnessFactor=0.444),
)
LEXUS_RC = PlatformConfig(
"LEXUS RC 2020",
[ToyotaCarDocs("Lexus RC 2018-20")],
LEXUS_IS.specs,
dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
flags=ToyotaFlags.UNSUPPORTED_DSU,
)
LEXUS_RX = PlatformConfig(
"LEXUS RX 2016",
[
ToyotaCarDocs("Lexus RX 2016", "Lexus Safety System+"),
ToyotaCarDocs("Lexus RX 2017-19"),
# Hybrid platforms
ToyotaCarDocs("Lexus RX Hybrid 2016", "Lexus Safety System+"),
ToyotaCarDocs("Lexus RX Hybrid 2017-19"),
],
CarSpecs(mass=4481. * CV.LB_TO_KG, wheelbase=2.79, steerRatio=16., tireStiffnessFactor=0.5533),
dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
)
LEXUS_RX_TSS2 = ToyotaTSS2PlatformConfig(
"LEXUS RX 2020",
[
ToyotaCarDocs("Lexus RX 2020-22"),
ToyotaCarDocs("Lexus RX Hybrid 2020-22"),
],
LEXUS_RX.specs,
)
LEXUS_GS_F = PlatformConfig(
"LEXUS GS F 2016",
[ToyotaCarDocs("Lexus GS F 2016")],
CarSpecs(mass=4034. * CV.LB_TO_KG, wheelbase=2.84988, steerRatio=13.3, tireStiffnessFactor=0.444),
dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
flags=ToyotaFlags.UNSUPPORTED_DSU,
)
# (addr, cars, bus, 1/freq*100, vl)
STATIC_DSU_MSGS = [
@@ -255,7 +405,7 @@ STATIC_DSU_MSGS = [
]
def get_platform_codes(fw_versions: List[bytes]) -> Dict[bytes, Set[bytes]]:
def get_platform_codes(fw_versions: list[bytes]) -> dict[bytes, set[bytes]]:
# Returns sub versions in a dict so comparisons can be made within part-platform-major_version combos
codes = defaultdict(set) # Optional[part]-platform-major_version: set of sub_version
for fw in fw_versions:
@@ -299,7 +449,7 @@ def get_platform_codes(fw_versions: List[bytes]) -> Dict[bytes, Set[bytes]]:
return dict(codes)
def match_fw_to_car_fuzzy(live_fw_versions, offline_fw_versions) -> Set[str]:
def match_fw_to_car_fuzzy(live_fw_versions, offline_fw_versions) -> set[str]:
candidates = set()
for candidate, fws in offline_fw_versions.items():
@@ -402,7 +552,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
Ecu.abs: [CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.SIENNA, CAR.LEXUS_IS, CAR.ALPHARD_TSS2],
# On some models, the engine can show on two different addresses
Ecu.engine: [CAR.HIGHLANDER, CAR.CAMRY, CAR.COROLLA_TSS2, CAR.CHR, CAR.CHR_TSS2, CAR.LEXUS_IS,
CAR.LEXUS_RC, CAR.LEXUS_NX, CAR.LEXUS_NX_TSS2, CAR.LEXUS_RX, CAR.LEXUS_RX_TSS2],
CAR.LEXUS_IS_TSS2, CAR.LEXUS_RC, CAR.LEXUS_NX, CAR.LEXUS_NX_TSS2, CAR.LEXUS_RX, CAR.LEXUS_RX_TSS2],
},
extra_ecus=[
# All known ECUs on a late-model Toyota vehicle not queried here:
@@ -442,66 +592,26 @@ FW_QUERY_CONFIG = FwQueryConfig(
STEER_THRESHOLD = 100
DBC = {
CAR.RAV4H: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.RAV4: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
CAR.PRIUS: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
CAR.PRIUS_V: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
CAR.COROLLA: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
CAR.LEXUS_LC_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.LEXUS_RC: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.LEXUS_RX: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.LEXUS_RX_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.CHR: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
CAR.CHR_TSS2: dbc_dict('toyota_nodsu_pt_generated', None),
CAR.CAMRY: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
CAR.CAMRY_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.HIGHLANDER: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.HIGHLANDER_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.AVALON: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.AVALON_2019: dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
CAR.AVALON_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.RAV4_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.RAV4_TSS2_2022: dbc_dict('toyota_nodsu_pt_generated', None),
CAR.RAV4_TSS2_2023: dbc_dict('toyota_nodsu_pt_generated', None),
CAR.COROLLA_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.LEXUS_ES: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
CAR.LEXUS_ES_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.SIENNA: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.LEXUS_IS: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.LEXUS_IS_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.LEXUS_CTH: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
CAR.LEXUS_NX: dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'),
CAR.LEXUS_NX_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.PRIUS_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.MIRAI: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.ALPHARD_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'),
CAR.LEXUS_GS_F: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
}
# These cars have non-standard EPS torque scale factors. All others are 73
EPS_SCALE = defaultdict(lambda: 73, {CAR.PRIUS: 66, CAR.COROLLA: 88, CAR.LEXUS_IS: 77, CAR.LEXUS_RC: 77, CAR.LEXUS_CTH: 100, CAR.PRIUS_V: 100})
# Toyota/Lexus Safety Sense 2.0 and 2.5
TSS2_CAR = {CAR.RAV4_TSS2, CAR.RAV4_TSS2_2022, CAR.RAV4_TSS2_2023, CAR.COROLLA_TSS2, CAR.LEXUS_ES_TSS2,
CAR.LEXUS_RX_TSS2, CAR.HIGHLANDER_TSS2, CAR.PRIUS_TSS2, CAR.CAMRY_TSS2, CAR.LEXUS_IS_TSS2,
CAR.MIRAI, CAR.LEXUS_NX_TSS2, CAR.LEXUS_LC_TSS2, CAR.ALPHARD_TSS2, CAR.AVALON_TSS2,
CAR.CHR_TSS2}
TSS2_CAR = CAR.with_flags(ToyotaFlags.TSS2)
NO_DSU_CAR = TSS2_CAR | {CAR.CHR, CAR.CAMRY}
NO_DSU_CAR = CAR.with_flags(ToyotaFlags.NO_DSU)
# the DSU uses the AEB message for longitudinal on these cars
UNSUPPORTED_DSU_CAR = {CAR.LEXUS_IS, CAR.LEXUS_RC, CAR.LEXUS_GS_F}
UNSUPPORTED_DSU_CAR = CAR.with_flags(ToyotaFlags.UNSUPPORTED_DSU)
# these cars have a radar which sends ACC messages instead of the camera
RADAR_ACC_CAR = {CAR.RAV4_TSS2_2022, CAR.RAV4_TSS2_2023, CAR.CHR_TSS2}
RADAR_ACC_CAR = CAR.with_flags(ToyotaFlags.RADAR_ACC)
# these cars use the Lane Tracing Assist (LTA) message for lateral control
ANGLE_CONTROL_CAR = {CAR.RAV4_TSS2_2023}
ANGLE_CONTROL_CAR = CAR.with_flags(ToyotaFlags.ANGLE_CONTROL)
# no resume button press required
NO_STOP_TIMER_CAR = TSS2_CAR | {CAR.PRIUS_V, CAR.RAV4H, CAR.HIGHLANDER, CAR.SIENNA}
NO_STOP_TIMER_CAR = CAR.with_flags(ToyotaFlags.NO_STOP_TIMER)
# stop and go
STOP_AND_GO_CAR = TSS2_CAR | {CAR.PRIUS, CAR.PRIUS_V, CAR.RAV4H, CAR.LEXUS_RX, CAR.CHR, CAR.CAMRY, CAR.HIGHLANDER,
CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_NX, CAR.MIRAI, CAR.AVALON_2019}
DBC = CAR.create_dbc_map()