Release 260111
This commit is contained in:
0
opendbc_repo/opendbc/car/volkswagen/__init__.py
Normal file
0
opendbc_repo/opendbc/car/volkswagen/__init__.py
Normal file
126
opendbc_repo/opendbc/car/volkswagen/carcontroller.py
Normal file
126
opendbc_repo/opendbc/car/volkswagen/carcontroller.py
Normal file
@@ -0,0 +1,126 @@
|
||||
import numpy as np
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus, DT_CTRL, apply_driver_steer_torque_limits, structs
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
from opendbc.car.volkswagen import mqbcan, pqcan
|
||||
from opendbc.car.volkswagen.values import CANBUS, CarControllerParams, VolkswagenFlags
|
||||
|
||||
VisualAlert = structs.CarControl.HUDControl.VisualAlert
|
||||
LongCtrlState = structs.CarControl.Actuators.LongControlState
|
||||
|
||||
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_names, CP):
|
||||
super().__init__(dbc_names, CP)
|
||||
self.CCP = CarControllerParams(CP)
|
||||
self.CCS = pqcan if CP.flags & VolkswagenFlags.PQ else mqbcan
|
||||
self.packer_pt = CANPacker(dbc_names[Bus.pt])
|
||||
self.ext_bus = CANBUS.pt if CP.networkLocation == structs.CarParams.NetworkLocation.fwdCamera else CANBUS.cam
|
||||
self.aeb_available = not CP.flags & VolkswagenFlags.PQ
|
||||
|
||||
self.apply_torque_last = 0
|
||||
self.gra_acc_counter_last = None
|
||||
self.eps_timer_soft_disable_alert = False
|
||||
self.hca_frame_timer_running = 0
|
||||
self.hca_frame_same_torque = 0
|
||||
|
||||
def update(self, CC, CS, now_nanos):
|
||||
actuators = CC.actuators
|
||||
hud_control = CC.hudControl
|
||||
can_sends = []
|
||||
|
||||
# **** Steering Controls ************************************************ #
|
||||
|
||||
if self.frame % self.CCP.STEER_STEP == 0:
|
||||
# Logic to avoid HCA state 4 "refused":
|
||||
# * Don't steer unless HCA is in state 3 "ready" or 5 "active"
|
||||
# * Don't steer at standstill
|
||||
# * Don't send > 3.00 Newton-meters torque
|
||||
# * Don't send the same torque for > 6 seconds
|
||||
# * Don't send uninterrupted steering for > 360 seconds
|
||||
# MQB racks reset the uninterrupted steering timer after a single frame
|
||||
# of HCA disabled; this is done whenever output happens to be zero.
|
||||
|
||||
if CC.latActive:
|
||||
new_torque = int(round(actuators.torque * self.CCP.STEER_MAX))
|
||||
apply_torque = apply_driver_steer_torque_limits(new_torque, self.apply_torque_last, CS.out.steeringTorque, self.CCP)
|
||||
self.hca_frame_timer_running += self.CCP.STEER_STEP
|
||||
if self.apply_torque_last == apply_torque:
|
||||
self.hca_frame_same_torque += self.CCP.STEER_STEP
|
||||
if self.hca_frame_same_torque > self.CCP.STEER_TIME_STUCK_TORQUE / DT_CTRL:
|
||||
apply_torque -= (1, -1)[apply_torque < 0]
|
||||
self.hca_frame_same_torque = 0
|
||||
else:
|
||||
self.hca_frame_same_torque = 0
|
||||
hca_enabled = abs(apply_torque) > 0
|
||||
else:
|
||||
hca_enabled = False
|
||||
apply_torque = 0
|
||||
|
||||
if not hca_enabled:
|
||||
self.hca_frame_timer_running = 0
|
||||
|
||||
self.eps_timer_soft_disable_alert = self.hca_frame_timer_running > self.CCP.STEER_TIME_ALERT / DT_CTRL
|
||||
self.apply_torque_last = apply_torque
|
||||
can_sends.append(self.CCS.create_steering_control(self.packer_pt, CANBUS.pt, apply_torque, hca_enabled))
|
||||
|
||||
if self.CP.flags & VolkswagenFlags.STOCK_HCA_PRESENT:
|
||||
# Pacify VW Emergency Assist driver inactivity detection by changing its view of driver steering input torque
|
||||
# to the greatest of actual driver input or 2x openpilot's output (1x openpilot output is not enough to
|
||||
# consistently reset inactivity detection on straight level roads). See commaai/openpilot#23274 for background.
|
||||
ea_simulated_torque = float(np.clip(apply_torque * 2, -self.CCP.STEER_MAX, self.CCP.STEER_MAX))
|
||||
if abs(CS.out.steeringTorque) > abs(ea_simulated_torque):
|
||||
ea_simulated_torque = CS.out.steeringTorque
|
||||
can_sends.append(self.CCS.create_eps_update(self.packer_pt, CANBUS.cam, CS.eps_stock_values, ea_simulated_torque))
|
||||
|
||||
# **** Acceleration Controls ******************************************** #
|
||||
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
if self.frame % self.CCP.ACC_CONTROL_STEP == 0:
|
||||
acc_control = self.CCS.acc_control_value(CS.out.cruiseState.available, CS.out.accFaulted, CC.longActive)
|
||||
accel = float(np.clip(actuators.accel, self.CCP.ACCEL_MIN, self.CCP.ACCEL_MAX) if CC.longActive else 0)
|
||||
stopping = actuators.longControlState == LongCtrlState.stopping
|
||||
starting = actuators.longControlState == LongCtrlState.pid and (CS.esp_hold_confirmation or CS.out.vEgo < self.CP.vEgoStopping)
|
||||
can_sends.extend(self.CCS.create_acc_accel_control(self.packer_pt, CANBUS.pt, CS.acc_type, CC.longActive, accel,
|
||||
acc_control, stopping, starting, CS.esp_hold_confirmation))
|
||||
|
||||
#if self.aeb_available:
|
||||
# if self.frame % self.CCP.AEB_CONTROL_STEP == 0:
|
||||
# can_sends.append(self.CCS.create_aeb_control(self.packer_pt, False, False, 0.0))
|
||||
# if self.frame % self.CCP.AEB_HUD_STEP == 0:
|
||||
# can_sends.append(self.CCS.create_aeb_hud(self.packer_pt, False, False))
|
||||
|
||||
# **** HUD Controls ***************************************************** #
|
||||
|
||||
if self.frame % self.CCP.LDW_STEP == 0:
|
||||
hud_alert = 0
|
||||
if hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw):
|
||||
hud_alert = self.CCP.LDW_MESSAGES["laneAssistTakeOver"]
|
||||
can_sends.append(self.CCS.create_lka_hud_control(self.packer_pt, CANBUS.pt, CS.ldw_stock_values, CC.latActive,
|
||||
CS.out.steeringPressed, hud_alert, hud_control))
|
||||
|
||||
if self.frame % self.CCP.ACC_HUD_STEP == 0 and self.CP.openpilotLongitudinalControl:
|
||||
lead_distance = 0
|
||||
if hud_control.leadVisible and self.frame * DT_CTRL > 1.0: # Don't display lead until we know the scaling factor
|
||||
lead_distance = 512 if CS.upscale_lead_car_signal else 8
|
||||
acc_hud_status = self.CCS.acc_hud_status_value(CS.out.cruiseState.available, CS.out.accFaulted, CC.longActive)
|
||||
# FIXME: follow the recent displayed-speed updates, also use mph_kmh toggle to fix display rounding problem?
|
||||
set_speed = hud_control.setSpeed * CV.MS_TO_KPH
|
||||
can_sends.append(self.CCS.create_acc_hud_control(self.packer_pt, CANBUS.pt, acc_hud_status, set_speed,
|
||||
lead_distance, hud_control.leadDistanceBars))
|
||||
|
||||
# **** Stock ACC Button Controls **************************************** #
|
||||
|
||||
gra_send_ready = self.CP.pcmCruise and CS.gra_stock_values["COUNTER"] != self.gra_acc_counter_last
|
||||
if gra_send_ready and (CC.cruiseControl.cancel or CC.cruiseControl.resume):
|
||||
can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, self.ext_bus, CS.gra_stock_values,
|
||||
cancel=CC.cruiseControl.cancel, resume=CC.cruiseControl.resume))
|
||||
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.torque = self.apply_torque_last / self.CCP.STEER_MAX
|
||||
new_actuators.torqueOutputCan = self.apply_torque_last
|
||||
|
||||
self.gra_acc_counter_last = CS.gra_stock_values["COUNTER"]
|
||||
self.frame += 1
|
||||
return new_actuators, can_sends
|
||||
280
opendbc_repo/opendbc/car/volkswagen/carstate.py
Normal file
280
opendbc_repo/opendbc/car/volkswagen/carstate.py
Normal file
@@ -0,0 +1,280 @@
|
||||
from opendbc.can import CANParser
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.volkswagen.values import DBC, CANBUS, NetworkLocation, TransmissionType, GearShifter, \
|
||||
CarControllerParams, VolkswagenFlags
|
||||
|
||||
ButtonType = structs.CarState.ButtonEvent.Type
|
||||
|
||||
|
||||
class CarState(CarStateBase):
|
||||
def __init__(self, CP):
|
||||
super().__init__(CP)
|
||||
self.frame = 0
|
||||
self.eps_init_complete = False
|
||||
self.CCP = CarControllerParams(CP)
|
||||
self.button_states = {button.event_type: False for button in self.CCP.BUTTONS}
|
||||
self.esp_hold_confirmation = False
|
||||
self.upscale_lead_car_signal = False
|
||||
self.eps_stock_values = False
|
||||
|
||||
def update_button_enable(self, buttonEvents: list[structs.CarState.ButtonEvent]):
|
||||
if not self.CP.pcmCruise:
|
||||
for b in buttonEvents:
|
||||
# Enable OP long on falling edge of enable buttons
|
||||
if b.type in (ButtonType.setCruise, ButtonType.resumeCruise) and not b.pressed:
|
||||
return True
|
||||
return False
|
||||
|
||||
def create_button_events(self, pt_cp, buttons):
|
||||
button_events = []
|
||||
|
||||
for button in buttons:
|
||||
state = pt_cp.vl[button.can_addr][button.can_msg] in button.values
|
||||
if self.button_states[button.event_type] != state:
|
||||
event = structs.CarState.ButtonEvent()
|
||||
event.type = button.event_type
|
||||
event.pressed = state
|
||||
button_events.append(event)
|
||||
self.button_states[button.event_type] = state
|
||||
|
||||
return button_events
|
||||
|
||||
def update(self, can_parsers) -> structs.CarState:
|
||||
pt_cp = can_parsers[Bus.pt]
|
||||
cam_cp = can_parsers[Bus.cam]
|
||||
ext_cp = pt_cp if self.CP.networkLocation == NetworkLocation.fwdCamera else cam_cp
|
||||
|
||||
if self.CP.flags & VolkswagenFlags.PQ:
|
||||
return self.update_pq(pt_cp, cam_cp, ext_cp)
|
||||
|
||||
ret = structs.CarState()
|
||||
|
||||
if self.CP.transmissionType == TransmissionType.direct:
|
||||
ret.gearShifter = self.parse_gear_shifter(self.CCP.shifter_values.get(pt_cp.vl["Motor_EV_01"]["MO_Waehlpos"], None))
|
||||
elif self.CP.transmissionType == TransmissionType.manual:
|
||||
ret.clutchPressed = not pt_cp.vl["Motor_14"]["MO_Kuppl_schalter"]
|
||||
if bool(pt_cp.vl["Gateway_72"]["BCM1_Rueckfahrlicht_Schalter"]):
|
||||
ret.gearShifter = GearShifter.reverse
|
||||
else:
|
||||
ret.gearShifter = GearShifter.drive
|
||||
else:
|
||||
ret.gearShifter = self.parse_gear_shifter(self.CCP.shifter_values.get(pt_cp.vl["Gateway_73"]["GE_Fahrstufe"], None))
|
||||
|
||||
if True:
|
||||
# MQB-specific
|
||||
self.upscale_lead_car_signal = bool(pt_cp.vl["Kombi_03"]["KBI_Variante"]) # Analog vs digital instrument cluster
|
||||
|
||||
ret.wheelSpeeds = self.get_wheel_speeds(
|
||||
pt_cp.vl["ESP_19"]["ESP_VL_Radgeschw_02"],
|
||||
pt_cp.vl["ESP_19"]["ESP_VR_Radgeschw_02"],
|
||||
pt_cp.vl["ESP_19"]["ESP_HL_Radgeschw_02"],
|
||||
pt_cp.vl["ESP_19"]["ESP_HR_Radgeschw_02"],
|
||||
)
|
||||
|
||||
ret.yawRate = pt_cp.vl["ESP_02"]["ESP_Gierrate"] * (1, -1)[int(pt_cp.vl["ESP_02"]["ESP_VZ_Gierrate"])] * CV.DEG_TO_RAD
|
||||
hca_status = self.CCP.hca_status_values.get(pt_cp.vl["LH_EPS_03"]["EPS_HCA_Status"])
|
||||
if self.CP.flags & VolkswagenFlags.STOCK_HCA_PRESENT:
|
||||
ret.carFaultedNonCritical = bool(cam_cp.vl["HCA_01"]["EA_Ruckfreigabe"]) or cam_cp.vl["HCA_01"]["EA_ACC_Sollstatus"] > 0 # EA
|
||||
|
||||
drive_mode = True
|
||||
ret.gas = pt_cp.vl["Motor_20"]["MO_Fahrpedalrohwert_01"] / 100.0
|
||||
ret.brake = pt_cp.vl["ESP_05"]["ESP_Bremsdruck"] / 250.0 # FIXME: this is pressure in Bar, not sure what OP expects
|
||||
brake_pedal_pressed = bool(pt_cp.vl["Motor_14"]["MO_Fahrer_bremst"])
|
||||
brake_pressure_detected = bool(pt_cp.vl["ESP_05"]["ESP_Fahrer_bremst"])
|
||||
ret.brakePressed = brake_pedal_pressed or brake_pressure_detected
|
||||
ret.parkingBrake = bool(pt_cp.vl["Kombi_01"]["KBI_Handbremse"]) # FIXME: need to include an EPB check as well
|
||||
|
||||
ret.doorOpen = any([pt_cp.vl["Gateway_72"]["ZV_FT_offen"],
|
||||
pt_cp.vl["Gateway_72"]["ZV_BT_offen"],
|
||||
pt_cp.vl["Gateway_72"]["ZV_HFS_offen"],
|
||||
pt_cp.vl["Gateway_72"]["ZV_HBFS_offen"],
|
||||
pt_cp.vl["Gateway_72"]["ZV_HD_offen"]])
|
||||
|
||||
if self.CP.enableBsm:
|
||||
# Infostufe: BSM LED on, Warnung: BSM LED flashing
|
||||
ret.leftBlindspot = bool(ext_cp.vl["SWA_01"]["SWA_Infostufe_SWA_li"]) or bool(ext_cp.vl["SWA_01"]["SWA_Warnung_SWA_li"])
|
||||
ret.rightBlindspot = bool(ext_cp.vl["SWA_01"]["SWA_Infostufe_SWA_re"]) or bool(ext_cp.vl["SWA_01"]["SWA_Warnung_SWA_re"])
|
||||
|
||||
ret.stockFcw = bool(ext_cp.vl["ACC_10"]["AWV2_Freigabe"])
|
||||
ret.stockAeb = bool(ext_cp.vl["ACC_10"]["ANB_Teilbremsung_Freigabe"]) or bool(ext_cp.vl["ACC_10"]["ANB_Zielbremsung_Freigabe"])
|
||||
|
||||
self.acc_type = ext_cp.vl["ACC_06"]["ACC_Typ"]
|
||||
self.esp_hold_confirmation = bool(pt_cp.vl["ESP_21"]["ESP_Haltebestaetigung"])
|
||||
acc_limiter_mode = ext_cp.vl["ACC_02"]["ACC_Gesetzte_Zeitluecke"] == 0
|
||||
speed_limiter_mode = bool(pt_cp.vl["TSK_06"]["TSK_Limiter_ausgewaehlt"])
|
||||
|
||||
ret.cruiseState.available = pt_cp.vl["TSK_06"]["TSK_Status"] in (2, 3, 4, 5)
|
||||
ret.cruiseState.enabled = pt_cp.vl["TSK_06"]["TSK_Status"] in (3, 4, 5)
|
||||
ret.cruiseState.speed = ext_cp.vl["ACC_02"]["ACC_Wunschgeschw_02"] * CV.KPH_TO_MS if self.CP.pcmCruise else 0
|
||||
ret.accFaulted = pt_cp.vl["TSK_06"]["TSK_Status"] in (6, 7)
|
||||
|
||||
ret.leftBlinker = bool(pt_cp.vl["Blinkmodi_02"]["Comfort_Signal_Left"])
|
||||
ret.rightBlinker = bool(pt_cp.vl["Blinkmodi_02"]["Comfort_Signal_Right"])
|
||||
|
||||
# Shared logic
|
||||
|
||||
ret.vEgoRaw = float(np.mean([ret.wheelSpeeds.fl, ret.wheelSpeeds.fr, ret.wheelSpeeds.rl, ret.wheelSpeeds.rr]))
|
||||
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
|
||||
|
||||
ret.steeringAngleDeg = pt_cp.vl["LWI_01"]["LWI_Lenkradwinkel"] * (1, -1)[int(pt_cp.vl["LWI_01"]["LWI_VZ_Lenkradwinkel"])]
|
||||
ret.steeringRateDeg = pt_cp.vl["LWI_01"]["LWI_Lenkradw_Geschw"] * (1, -1)[int(pt_cp.vl["LWI_01"]["LWI_VZ_Lenkradw_Geschw"])]
|
||||
ret.steeringTorque = pt_cp.vl["LH_EPS_03"]["EPS_Lenkmoment"] * (1, -1)[int(pt_cp.vl["LH_EPS_03"]["EPS_VZ_Lenkmoment"])]
|
||||
ret.steeringPressed = abs(ret.steeringTorque) > self.CCP.STEER_DRIVER_ALLOWANCE
|
||||
ret.steerFaultTemporary, ret.steerFaultPermanent = self.update_hca_state(hca_status, drive_mode)
|
||||
|
||||
ret.gasPressed = ret.gas > 0
|
||||
ret.espActive = bool(pt_cp.vl["ESP_21"]["ESP_Eingriff"])
|
||||
ret.espDisabled = pt_cp.vl["ESP_21"]["ESP_Tastung_passiv"] != 0
|
||||
ret.seatbeltUnlatched = pt_cp.vl["Airbag_02"]["AB_Gurtschloss_FA"] != 3
|
||||
|
||||
ret.standstill = ret.vEgoRaw == 0
|
||||
ret.cruiseState.standstill = self.CP.pcmCruise and self.esp_hold_confirmation
|
||||
ret.cruiseState.nonAdaptive = acc_limiter_mode or speed_limiter_mode
|
||||
if ret.cruiseState.speed > 90:
|
||||
ret.cruiseState.speed = 0
|
||||
|
||||
self.eps_stock_values = pt_cp.vl["LH_EPS_03"]
|
||||
self.ldw_stock_values = cam_cp.vl["LDW_02"] if self.CP.networkLocation == NetworkLocation.fwdCamera else {}
|
||||
self.gra_stock_values = pt_cp.vl["GRA_ACC_01"]
|
||||
|
||||
ret.buttonEvents = self.create_button_events(pt_cp, self.CCP.BUTTONS)
|
||||
|
||||
self.frame += 1
|
||||
return ret
|
||||
|
||||
def update_pq(self, pt_cp, cam_cp, ext_cp) -> structs.CarState:
|
||||
ret = structs.CarState()
|
||||
# Update vehicle speed and acceleration from ABS wheel speeds.
|
||||
ret.wheelSpeeds = self.get_wheel_speeds(
|
||||
pt_cp.vl["Bremse_3"]["Radgeschw__VL_4_1"],
|
||||
pt_cp.vl["Bremse_3"]["Radgeschw__VR_4_1"],
|
||||
pt_cp.vl["Bremse_3"]["Radgeschw__HL_4_1"],
|
||||
pt_cp.vl["Bremse_3"]["Radgeschw__HR_4_1"],
|
||||
)
|
||||
|
||||
# vEgo obtained from Bremse_1 vehicle speed rather than Bremse_3 wheel speeds because Bremse_3 isn't present on NSF
|
||||
ret.vEgoRaw = pt_cp.vl["Bremse_1"]["Geschwindigkeit_neu__Bremse_1_"] * CV.KPH_TO_MS
|
||||
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
|
||||
ret.standstill = ret.vEgoRaw == 0
|
||||
|
||||
# Update EPS position and state info. For signed values, VW sends the sign in a separate signal.
|
||||
ret.steeringAngleDeg = pt_cp.vl["Lenkhilfe_3"]["LH3_BLW"] * (1, -1)[int(pt_cp.vl["Lenkhilfe_3"]["LH3_BLWSign"])]
|
||||
ret.steeringRateDeg = pt_cp.vl["Lenkwinkel_1"]["Lenkradwinkel_Geschwindigkeit"] * (1, -1)[int(pt_cp.vl["Lenkwinkel_1"]["Lenkradwinkel_Geschwindigkeit_S"])]
|
||||
ret.steeringTorque = pt_cp.vl["Lenkhilfe_3"]["LH3_LM"] * (1, -1)[int(pt_cp.vl["Lenkhilfe_3"]["LH3_LMSign"])]
|
||||
ret.steeringPressed = abs(ret.steeringTorque) > self.CCP.STEER_DRIVER_ALLOWANCE
|
||||
ret.yawRate = pt_cp.vl["Bremse_5"]["Giergeschwindigkeit"] * (1, -1)[int(pt_cp.vl["Bremse_5"]["Vorzeichen_der_Giergeschwindigk"])] * CV.DEG_TO_RAD
|
||||
hca_status = self.CCP.hca_status_values.get(pt_cp.vl["Lenkhilfe_2"]["LH2_Sta_HCA"])
|
||||
ret.steerFaultTemporary, ret.steerFaultPermanent = self.update_hca_state(hca_status)
|
||||
|
||||
# Update gas, brakes, and gearshift.
|
||||
ret.gas = pt_cp.vl["Motor_3"]["Fahrpedal_Rohsignal"] / 100.0
|
||||
ret.gasPressed = ret.gas > 0
|
||||
ret.brake = pt_cp.vl["Bremse_5"]["Bremsdruck"] / 250.0 # FIXME: this is pressure in Bar, not sure what OP expects
|
||||
ret.brakePressed = bool(pt_cp.vl["Motor_2"]["Bremslichtschalter"])
|
||||
ret.parkingBrake = bool(pt_cp.vl["Kombi_1"]["Bremsinfo"])
|
||||
|
||||
# Update gear and/or clutch position data.
|
||||
if self.CP.transmissionType == TransmissionType.automatic:
|
||||
ret.gearShifter = self.parse_gear_shifter(self.CCP.shifter_values.get(pt_cp.vl["Getriebe_1"]["Waehlhebelposition__Getriebe_1_"], None))
|
||||
elif self.CP.transmissionType == TransmissionType.manual:
|
||||
ret.clutchPressed = not pt_cp.vl["Motor_1"]["Kupplungsschalter"]
|
||||
reverse_light = bool(pt_cp.vl["Gate_Komf_1"]["GK1_Rueckfahr"])
|
||||
if reverse_light:
|
||||
ret.gearShifter = GearShifter.reverse
|
||||
else:
|
||||
ret.gearShifter = GearShifter.drive
|
||||
|
||||
# Update door and trunk/hatch lid open status.
|
||||
ret.doorOpen = any([pt_cp.vl["Gate_Komf_1"]["GK1_Fa_Tuerkont"],
|
||||
pt_cp.vl["Gate_Komf_1"]["BSK_BT_geoeffnet"],
|
||||
pt_cp.vl["Gate_Komf_1"]["BSK_HL_geoeffnet"],
|
||||
pt_cp.vl["Gate_Komf_1"]["BSK_HR_geoeffnet"],
|
||||
pt_cp.vl["Gate_Komf_1"]["BSK_HD_Hauptraste"]])
|
||||
|
||||
# Update seatbelt fastened status.
|
||||
ret.seatbeltUnlatched = not bool(pt_cp.vl["Airbag_1"]["Gurtschalter_Fahrer"])
|
||||
|
||||
# Consume blind-spot monitoring info/warning LED states, if available.
|
||||
# Infostufe: BSM LED on, Warnung: BSM LED flashing
|
||||
if self.CP.enableBsm:
|
||||
ret.leftBlindspot = bool(ext_cp.vl["SWA_1"]["SWA_Infostufe_SWA_li"]) or bool(ext_cp.vl["SWA_1"]["SWA_Warnung_SWA_li"])
|
||||
ret.rightBlindspot = bool(ext_cp.vl["SWA_1"]["SWA_Infostufe_SWA_re"]) or bool(ext_cp.vl["SWA_1"]["SWA_Warnung_SWA_re"])
|
||||
|
||||
# Consume factory LDW data relevant for factory SWA (Lane Change Assist)
|
||||
# and capture it for forwarding to the blind spot radar controller
|
||||
self.ldw_stock_values = cam_cp.vl["LDW_Status"] if self.CP.networkLocation == NetworkLocation.fwdCamera else {}
|
||||
|
||||
# Stock FCW is considered active if the release bit for brake-jerk warning
|
||||
# is set. Stock AEB considered active if the partial braking or target
|
||||
# braking release bits are set.
|
||||
# Refer to VW Self Study Program 890253: Volkswagen Driver Assistance
|
||||
# Systems, chapters on Front Assist with Braking and City Emergency
|
||||
# Braking for the 2016 Passat NMS
|
||||
# TODO: deferred until we can collect data on pre-MY2016 behavior, AWV message may be shorter with fewer signals
|
||||
ret.stockFcw = False
|
||||
ret.stockAeb = False
|
||||
|
||||
# Update ACC radar status.
|
||||
self.acc_type = ext_cp.vl["ACC_System"]["ACS_Typ_ACC"]
|
||||
ret.cruiseState.available = bool(pt_cp.vl["Motor_5"]["GRA_Hauptschalter"])
|
||||
ret.cruiseState.enabled = pt_cp.vl["Motor_2"]["GRA_Status"] in (1, 2)
|
||||
if self.CP.pcmCruise:
|
||||
ret.accFaulted = ext_cp.vl["ACC_GRA_Anzeige"]["ACA_StaACC"] in (6, 7)
|
||||
else:
|
||||
ret.accFaulted = pt_cp.vl["Motor_2"]["GRA_Status"] == 3
|
||||
|
||||
# Update ACC setpoint. When the setpoint reads as 255, the driver has not
|
||||
# yet established an ACC setpoint, so treat it as zero.
|
||||
ret.cruiseState.speed = ext_cp.vl["ACC_GRA_Anzeige"]["ACA_V_Wunsch"] * CV.KPH_TO_MS
|
||||
if ret.cruiseState.speed > 70: # 255 kph in m/s == no current setpoint
|
||||
ret.cruiseState.speed = 0
|
||||
|
||||
# Update button states for turn signals and ACC controls, capture all ACC button state/config for passthrough
|
||||
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_stalk(300, pt_cp.vl["Gate_Komf_1"]["GK1_Blinker_li"],
|
||||
pt_cp.vl["Gate_Komf_1"]["GK1_Blinker_re"])
|
||||
ret.buttonEvents = self.create_button_events(pt_cp, self.CCP.BUTTONS)
|
||||
self.gra_stock_values = pt_cp.vl["GRA_Neu"]
|
||||
|
||||
# Additional safety checks performed in CarInterface.
|
||||
ret.espDisabled = bool(pt_cp.vl["Bremse_1"]["ESP_Passiv_getastet"])
|
||||
|
||||
self.frame += 1
|
||||
return ret
|
||||
|
||||
def update_hca_state(self, hca_status, drive_mode=True):
|
||||
# Treat FAULT as temporary for worst likely EPS recovery time, for cars without factory Lane Assist
|
||||
# DISABLED means the EPS hasn't been configured to support Lane Assist
|
||||
self.eps_init_complete = self.eps_init_complete or (hca_status in ("DISABLED", "READY", "ACTIVE") or self.frame > 600)
|
||||
perm_fault = drive_mode and hca_status == "DISABLED" or (self.eps_init_complete and hca_status == "FAULT")
|
||||
temp_fault = drive_mode and hca_status in ("REJECTED", "PREEMPTED") or not self.eps_init_complete
|
||||
return temp_fault, perm_fault
|
||||
|
||||
@staticmethod
|
||||
def get_can_parsers(CP):
|
||||
if CP.flags & VolkswagenFlags.PQ:
|
||||
return CarState.get_can_parsers_pq(CP)
|
||||
|
||||
# another case of the 1-50Hz
|
||||
cam_messages = []
|
||||
if CP.flags & VolkswagenFlags.STOCK_HCA_PRESENT:
|
||||
cam_messages += [
|
||||
("HCA_01", 1), # From R242 Driver assistance camera, 50Hz if steering/1Hz if not
|
||||
]
|
||||
|
||||
return {
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [
|
||||
# the 50->1Hz is currently too much for the CANParser to figure out
|
||||
("Blinkmodi_02", 1), # From J519 BCM (sent at 1Hz when no lights active, 50Hz when active)
|
||||
], CANBUS.pt),
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], cam_messages, CANBUS.cam),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get_can_parsers_pq(CP):
|
||||
return {
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CANBUS.pt),
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CANBUS.cam),
|
||||
}
|
||||
|
||||
1247
opendbc_repo/opendbc/car/volkswagen/fingerprints.py
Normal file
1247
opendbc_repo/opendbc/car/volkswagen/fingerprints.py
Normal file
File diff suppressed because it is too large
Load Diff
90
opendbc_repo/opendbc/car/volkswagen/interface.py
Normal file
90
opendbc_repo/opendbc/car/volkswagen/interface.py
Normal file
@@ -0,0 +1,90 @@
|
||||
from opendbc.car import get_safety_config, structs
|
||||
from opendbc.car.interfaces import CarInterfaceBase
|
||||
from opendbc.car.volkswagen.carcontroller import CarController
|
||||
from opendbc.car.volkswagen.carstate import CarState
|
||||
from opendbc.car.volkswagen.values import CAR, NetworkLocation, TransmissionType, VolkswagenFlags, VolkswagenSafetyFlags
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
CarState = CarState
|
||||
CarController = CarController
|
||||
|
||||
@staticmethod
|
||||
def _get_params(ret: structs.CarParams, candidate: CAR, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
|
||||
ret.brand = "volkswagen"
|
||||
ret.radarUnavailable = True
|
||||
|
||||
if ret.flags & VolkswagenFlags.PQ:
|
||||
# Set global PQ35/PQ46/NMS parameters
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.volkswagenPq)]
|
||||
ret.enableBsm = 0x3BA in fingerprint[0] # SWA_1
|
||||
|
||||
if 0x440 in fingerprint[0] or docs: # Getriebe_1
|
||||
ret.transmissionType = TransmissionType.automatic
|
||||
else:
|
||||
ret.transmissionType = TransmissionType.manual
|
||||
|
||||
if any(msg in fingerprint[1] for msg in (0x1A0, 0xC2)): # Bremse_1, Lenkwinkel_1
|
||||
ret.networkLocation = NetworkLocation.gateway
|
||||
else:
|
||||
ret.networkLocation = NetworkLocation.fwdCamera
|
||||
|
||||
# The PQ port is in dashcam-only mode due to a fixed six-minute maximum timer on HCA steering. An unsupported
|
||||
# EPS flash update to work around this timer, and enable steering down to zero, is available from:
|
||||
# https://github.com/pd0wm/pq-flasher
|
||||
# It is documented in a four-part blog series:
|
||||
# https://blog.willemmelching.nl/carhacking/2022/01/02/vw-part1/
|
||||
# Panda ALLOW_DEBUG firmware required.
|
||||
ret.dashcamOnly = True
|
||||
|
||||
else:
|
||||
# Set global MQB parameters
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.volkswagen)]
|
||||
ret.enableBsm = 0x30F in fingerprint[0] # SWA_01
|
||||
|
||||
if 0xAD in fingerprint[0] or docs: # Getriebe_11
|
||||
ret.transmissionType = TransmissionType.automatic
|
||||
elif 0x187 in fingerprint[0]: # Motor_EV_01
|
||||
ret.transmissionType = TransmissionType.direct
|
||||
else:
|
||||
ret.transmissionType = TransmissionType.manual
|
||||
|
||||
if any(msg in fingerprint[1] for msg in (0x40, 0x86, 0xB2, 0xFD)): # Airbag_01, LWI_01, ESP_19, ESP_21
|
||||
ret.networkLocation = NetworkLocation.gateway
|
||||
else:
|
||||
ret.networkLocation = NetworkLocation.fwdCamera
|
||||
|
||||
if 0x126 in fingerprint[2]: # HCA_01
|
||||
ret.flags |= VolkswagenFlags.STOCK_HCA_PRESENT.value
|
||||
|
||||
# Global lateral tuning defaults, can be overridden per-vehicle
|
||||
|
||||
ret.steerLimitTimer = 0.4
|
||||
if ret.flags & VolkswagenFlags.PQ:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
else:
|
||||
ret.steerActuatorDelay = 0.1
|
||||
ret.lateralTuning.pid.kpBP = [0.]
|
||||
ret.lateralTuning.pid.kiBP = [0.]
|
||||
ret.lateralTuning.pid.kf = 0.00006
|
||||
ret.lateralTuning.pid.kpV = [0.6]
|
||||
ret.lateralTuning.pid.kiV = [0.2]
|
||||
|
||||
# Global longitudinal tuning defaults, can be overridden per-vehicle
|
||||
|
||||
ret.alphaLongitudinalAvailable = ret.networkLocation == NetworkLocation.gateway or docs
|
||||
if alpha_long:
|
||||
# Proof-of-concept, prep for E2E only. No radar points available. Panda ALLOW_DEBUG firmware required.
|
||||
ret.openpilotLongitudinalControl = True
|
||||
ret.safetyConfigs[0].safetyParam |= VolkswagenSafetyFlags.LONG_CONTROL.value
|
||||
if ret.transmissionType == TransmissionType.manual:
|
||||
ret.minEnableSpeed = 4.5
|
||||
|
||||
ret.pcmCruise = not ret.openpilotLongitudinalControl
|
||||
ret.stopAccel = -0.55
|
||||
ret.vEgoStarting = 0.1
|
||||
ret.vEgoStopping = 0.5
|
||||
ret.autoResumeSng = ret.minEnableSpeed == -1
|
||||
|
||||
return ret
|
||||
258
opendbc_repo/opendbc/car/volkswagen/mqbcan.py
Normal file
258
opendbc_repo/opendbc/car/volkswagen/mqbcan.py
Normal file
@@ -0,0 +1,258 @@
|
||||
from opendbc.car.crc import CRC8H2F
|
||||
|
||||
|
||||
def create_steering_control(packer, bus, apply_torque, lkas_enabled):
|
||||
values = {
|
||||
"HCA_01_Status_HCA": 5 if lkas_enabled else 3,
|
||||
"HCA_01_LM_Offset": abs(apply_torque),
|
||||
"HCA_01_LM_OffSign": 1 if apply_torque < 0 else 0,
|
||||
"HCA_01_Vib_Freq": 18,
|
||||
"HCA_01_Sendestatus": 1 if lkas_enabled else 0,
|
||||
"EA_ACC_Wunschgeschwindigkeit": 327.36,
|
||||
}
|
||||
return packer.make_can_msg("HCA_01", bus, values)
|
||||
|
||||
|
||||
def create_eps_update(packer, bus, eps_stock_values, ea_simulated_torque):
|
||||
values = {s: eps_stock_values[s] for s in [
|
||||
"COUNTER", # Sync counter value to EPS output
|
||||
"EPS_Lenkungstyp", # EPS rack type
|
||||
"EPS_Berechneter_LW", # Absolute raw steering angle
|
||||
"EPS_VZ_BLW", # Raw steering angle sign
|
||||
"EPS_HCA_Status", # EPS HCA control status
|
||||
]}
|
||||
|
||||
values.update({
|
||||
# Absolute driver torque input and sign, with EA inactivity mitigation
|
||||
"EPS_Lenkmoment": abs(ea_simulated_torque),
|
||||
"EPS_VZ_Lenkmoment": 1 if ea_simulated_torque < 0 else 0,
|
||||
})
|
||||
|
||||
return packer.make_can_msg("LH_EPS_03", bus, values)
|
||||
|
||||
|
||||
def create_lka_hud_control(packer, bus, ldw_stock_values, lat_active, steering_pressed, hud_alert, hud_control):
|
||||
values = {}
|
||||
if len(ldw_stock_values):
|
||||
values = {s: ldw_stock_values[s] for s in [
|
||||
"LDW_SW_Warnung_links", # Blind spot in warning mode on left side due to lane departure
|
||||
"LDW_SW_Warnung_rechts", # Blind spot in warning mode on right side due to lane departure
|
||||
"LDW_Seite_DLCTLC", # Direction of most likely lane departure (left or right)
|
||||
"LDW_DLC", # Lane departure, distance to line crossing
|
||||
"LDW_TLC", # Lane departure, time to line crossing
|
||||
]}
|
||||
|
||||
values.update({
|
||||
"LDW_Status_LED_gelb": 1 if lat_active and steering_pressed else 0,
|
||||
"LDW_Status_LED_gruen": 1 if lat_active and not steering_pressed else 0,
|
||||
"LDW_Lernmodus_links": 3 if hud_control.leftLaneDepart else 1 + hud_control.leftLaneVisible,
|
||||
"LDW_Lernmodus_rechts": 3 if hud_control.rightLaneDepart else 1 + hud_control.rightLaneVisible,
|
||||
"LDW_Texte": hud_alert,
|
||||
})
|
||||
return packer.make_can_msg("LDW_02", bus, values)
|
||||
|
||||
|
||||
def create_acc_buttons_control(packer, bus, gra_stock_values, cancel=False, resume=False):
|
||||
values = {s: gra_stock_values[s] for s in [
|
||||
"GRA_Hauptschalter", # ACC button, on/off
|
||||
"GRA_Typ_Hauptschalter", # ACC main button type
|
||||
"GRA_Codierung", # ACC button configuration/coding
|
||||
"GRA_Tip_Stufe_2", # unknown related to stalk type
|
||||
"GRA_ButtonTypeInfo", # unknown related to stalk type
|
||||
]}
|
||||
|
||||
values.update({
|
||||
"COUNTER": (gra_stock_values["COUNTER"] + 1) % 16,
|
||||
"GRA_Abbrechen": cancel,
|
||||
"GRA_Tip_Wiederaufnahme": resume,
|
||||
})
|
||||
|
||||
return packer.make_can_msg("GRA_ACC_01", bus, values)
|
||||
|
||||
|
||||
def acc_control_value(main_switch_on, acc_faulted, long_active):
|
||||
if acc_faulted:
|
||||
acc_control = 6
|
||||
elif long_active:
|
||||
acc_control = 3
|
||||
elif main_switch_on:
|
||||
acc_control = 2
|
||||
else:
|
||||
acc_control = 0
|
||||
|
||||
return acc_control
|
||||
|
||||
|
||||
def acc_hud_status_value(main_switch_on, acc_faulted, long_active):
|
||||
# TODO: happens to resemble the ACC control value for now, but extend this for init/gas override later
|
||||
return acc_control_value(main_switch_on, acc_faulted, long_active)
|
||||
|
||||
|
||||
def create_acc_accel_control(packer, bus, acc_type, acc_enabled, accel, acc_control, stopping, starting, esp_hold):
|
||||
commands = []
|
||||
|
||||
acc_06_values = {
|
||||
"ACC_Typ": acc_type,
|
||||
"ACC_Status_ACC": acc_control,
|
||||
"ACC_StartStopp_Info": acc_enabled,
|
||||
"ACC_Sollbeschleunigung_02": accel if acc_enabled else 3.01,
|
||||
"ACC_zul_Regelabw_unten": 0.2, # TODO: dynamic adjustment of comfort-band
|
||||
"ACC_zul_Regelabw_oben": 0.2, # TODO: dynamic adjustment of comfort-band
|
||||
"ACC_neg_Sollbeschl_Grad_02": 4.0 if acc_enabled else 0, # TODO: dynamic adjustment of jerk limits
|
||||
"ACC_pos_Sollbeschl_Grad_02": 4.0 if acc_enabled else 0, # TODO: dynamic adjustment of jerk limits
|
||||
"ACC_Anfahren": starting,
|
||||
"ACC_Anhalten": stopping,
|
||||
}
|
||||
commands.append(packer.make_can_msg("ACC_06", bus, acc_06_values))
|
||||
|
||||
if starting:
|
||||
acc_hold_type = 4 # hold release / startup
|
||||
elif esp_hold:
|
||||
acc_hold_type = 3 # hold standby
|
||||
elif stopping:
|
||||
acc_hold_type = 1 # hold request
|
||||
else:
|
||||
acc_hold_type = 0
|
||||
|
||||
acc_07_values = {
|
||||
"ACC_Anhalteweg": 0.3 if stopping else 20.46, # Distance to stop (stopping coordinator handles terminal roll-out)
|
||||
"ACC_Freilauf_Info": 2 if acc_enabled else 0,
|
||||
"ACC_Folgebeschl": 3.02, # Not using secondary controller accel unless and until we understand its impact
|
||||
"ACC_Sollbeschleunigung_02": accel if acc_enabled else 3.01,
|
||||
"ACC_Anforderung_HMS": acc_hold_type,
|
||||
"ACC_Anfahren": starting,
|
||||
"ACC_Anhalten": stopping,
|
||||
}
|
||||
commands.append(packer.make_can_msg("ACC_07", bus, acc_07_values))
|
||||
|
||||
return commands
|
||||
|
||||
|
||||
def create_acc_hud_control(packer, bus, acc_hud_status, set_speed, lead_distance, distance):
|
||||
values = {
|
||||
"ACC_Status_Anzeige": acc_hud_status,
|
||||
"ACC_Wunschgeschw_02": set_speed if set_speed < 250 else 327.36,
|
||||
"ACC_Gesetzte_Zeitluecke": distance + 2,
|
||||
"ACC_Display_Prio": 3,
|
||||
"ACC_Abstandsindex": lead_distance,
|
||||
}
|
||||
|
||||
return packer.make_can_msg("ACC_02", bus, values)
|
||||
|
||||
|
||||
# AWV = Stopping Distance Reduction
|
||||
# Refer to Self Study Program 890253: Volkswagen Driver Assistance Systems, Design and Function
|
||||
|
||||
|
||||
def create_aeb_control(packer, fcw_active, aeb_active, accel):
|
||||
values = {
|
||||
"AWV_Vorstufe": 0, # Preliminary stage
|
||||
"AWV1_Anf_Prefill": 0, # Brake pre-fill request
|
||||
"AWV1_HBA_Param": 0, # Brake pre-fill level
|
||||
"AWV2_Freigabe": 0, # Stage 2 braking release
|
||||
"AWV2_Ruckprofil": 0, # Brake jerk level
|
||||
"AWV2_Priowarnung": 0, # Suppress lane departure warning in favor of FCW
|
||||
"ANB_Notfallblinken": 0, # Hazard flashers request
|
||||
"ANB_Teilbremsung_Freigabe": 0, # Target braking release
|
||||
"ANB_Zielbremsung_Freigabe": 0, # Partial braking release
|
||||
"ANB_Zielbrems_Teilbrems_Verz_Anf": 0.0, # Acceleration requirement for target/partial braking, m/s/s
|
||||
"AWV_Halten": 0, # Vehicle standstill request
|
||||
"PCF_Time_to_collision": 0xFF, # Pre Crash Front, populated only with a target, might be used on Audi only
|
||||
}
|
||||
|
||||
return packer.make_can_msg("ACC_10", 0, values)
|
||||
|
||||
|
||||
def create_aeb_hud(packer, aeb_supported, fcw_active):
|
||||
values = {
|
||||
"AWV_Texte": 5 if aeb_supported else 7, # FCW/AEB system status, display text (from menu in VAL)
|
||||
"AWV_Status_Anzeige": 1 if aeb_supported else 2, # FCW/AEB system status, available or disabled
|
||||
}
|
||||
|
||||
return packer.make_can_msg("ACC_15", 0, values)
|
||||
|
||||
|
||||
def volkswagen_mqb_meb_checksum(address: int, sig, d: bytearray) -> int:
|
||||
crc = 0xFF
|
||||
for i in range(1, len(d)):
|
||||
crc ^= d[i]
|
||||
crc = CRC8H2F[crc]
|
||||
counter = d[1] & 0x0F
|
||||
const = VOLKSWAGEN_MQB_MEB_CONSTANTS.get(address)
|
||||
if const:
|
||||
crc ^= const[counter]
|
||||
crc = CRC8H2F[crc]
|
||||
return crc ^ 0xFF
|
||||
|
||||
|
||||
def xor_checksum(address: int, sig, d: bytearray) -> int:
|
||||
checksum = 0
|
||||
checksum_byte = sig.start_bit // 8
|
||||
for i in range(len(d)):
|
||||
if i != checksum_byte:
|
||||
checksum ^= d[i]
|
||||
return checksum
|
||||
|
||||
|
||||
VOLKSWAGEN_MQB_MEB_CONSTANTS: dict[int, list[int]] = {
|
||||
0x40: [0x40] * 16, # Airbag_01
|
||||
0x86: [0x86] * 16, # LWI_01
|
||||
0x9F: [0xF5] * 16, # LH_EPS_03
|
||||
0xAD: [0x3F, 0x69, 0x39, 0xDC, 0x94, 0xF9, 0x14, 0x64,
|
||||
0xD8, 0x6A, 0x34, 0xCE, 0xA2, 0x55, 0xB5, 0x2C], # Getriebe_11
|
||||
0x0DB: [0x09, 0xFA, 0xCA, 0x8E, 0x62, 0xD5, 0xD1, 0xF0,
|
||||
0x31, 0xA0, 0xAF, 0xDA, 0x4D, 0x1A, 0x0A, 0x97], # AWV_03
|
||||
0xFC: [0x77, 0x5C, 0xA0, 0x89, 0x4B, 0x7C, 0xBB, 0xD6,
|
||||
0x1F, 0x6C, 0x4F, 0xF6, 0x20, 0x2B, 0x43, 0xDD], # ESC_51
|
||||
0xFD: [0xB4, 0xEF, 0xF8, 0x49, 0x1E, 0xE5, 0xC2, 0xC0,
|
||||
0x97, 0x19, 0x3C, 0xC9, 0xF1, 0x98, 0xD6, 0x61], # ESP_21
|
||||
0x101: [0xAA] * 16, # ESP_02
|
||||
0x102: [0xD7, 0x12, 0x85, 0x7E, 0x0B, 0x34, 0xFA, 0x16,
|
||||
0x7A, 0x25, 0x2D, 0x8F, 0x04, 0x8E, 0x5D, 0x35], # ESC_50
|
||||
0x106: [0x07] * 16, # ESP_05
|
||||
0x10B: [0x77, 0x5C, 0xA0, 0x89, 0x4B, 0x7C, 0xBB, 0xD6,
|
||||
0x1F, 0x6C, 0x4F, 0xF6, 0x20, 0x2B, 0x43, 0xDD], # Motor_51
|
||||
0x116: [0xAC] * 16, # ESP_10
|
||||
0x117: [0x16] * 16, # ACC_10
|
||||
0x120: [0xC4, 0xE2, 0x4F, 0xE4, 0xF8, 0x2F, 0x56, 0x81,
|
||||
0x9F, 0xE5, 0x83, 0x44, 0x05, 0x3F, 0x97, 0xDF], # TSK_06
|
||||
0x121: [0xE9, 0x65, 0xAE, 0x6B, 0x7B, 0x35, 0xE5, 0x5F,
|
||||
0x4E, 0xC7, 0x86, 0xA2, 0xBB, 0xDD, 0xEB, 0xB4], # Motor_20
|
||||
0x122: [0x37, 0x7D, 0xF3, 0xA9, 0x18, 0x46, 0x6D, 0x4D,
|
||||
0x3D, 0x71, 0x92, 0x9C, 0xE5, 0x32, 0x10, 0xB9], # ACC_06
|
||||
0x126: [0xDA] * 16, # HCA_01
|
||||
0x12B: [0x6A, 0x38, 0xB4, 0x27, 0x22, 0xEF, 0xE1, 0xBB,
|
||||
0xF8, 0x80, 0x84, 0x49, 0xC7, 0x9E, 0x1E, 0x2B], # GRA_ACC_01
|
||||
0x12E: [0xF8, 0xE5, 0x97, 0xC9, 0xD6, 0x07, 0x47, 0x21,
|
||||
0x66, 0xDD, 0xCF, 0x6F, 0xA1, 0x94, 0x74, 0x63], # ACC_07
|
||||
0x139: [0xED, 0x03, 0x1C, 0x13, 0xC6, 0x23, 0x78, 0x7A,
|
||||
0x8B, 0x40, 0x14, 0x51, 0xBF, 0x68, 0x32, 0xBA], # VMM_02
|
||||
0x13D: [0x20, 0xCA, 0x68, 0xD5, 0x1B, 0x31, 0xE2, 0xDA,
|
||||
0x08, 0x0A, 0xD4, 0xDE, 0x9C, 0xE4, 0x35, 0x5B], # QFK_01
|
||||
0x14C: [0x16, 0x35, 0x59, 0x15, 0x9A, 0x2A, 0x97, 0xB8,
|
||||
0x0E, 0x4E, 0x30, 0xCC, 0xB3, 0x07, 0x01, 0xAD], # Motor_54
|
||||
0x14D: [0x1A, 0x65, 0x81, 0x96, 0xC0, 0xDF, 0x11, 0x92,
|
||||
0xD3, 0x61, 0xC6, 0x95, 0x8C, 0x29, 0x21, 0xB5], # ACC_18
|
||||
0x187: [0x7F, 0xED, 0x17, 0xC2, 0x7C, 0xEB, 0x44, 0x21,
|
||||
0x01, 0xFA, 0xDB, 0x15, 0x4A, 0x6B, 0x23, 0x05], # Motor_EV_01
|
||||
0x1A4: [0x69, 0xBB, 0x54, 0xE6, 0x4E, 0x46, 0x8D, 0x7B,
|
||||
0xEA, 0x87, 0xE9, 0xB3, 0x63, 0xCE, 0xF8, 0xBF], # EA_01
|
||||
0x1AB: [0x13, 0x21, 0x9B, 0x6A, 0x9A, 0x62, 0xD4, 0x65,
|
||||
0x18, 0xF1, 0xAB, 0x16, 0x32, 0x89, 0xE7, 0x26], # ESP_33
|
||||
0x1F0: [0x2F, 0x3C, 0x22, 0x60, 0x18, 0xEB, 0x63, 0x76,
|
||||
0xC5, 0x91, 0x0F, 0x27, 0x34, 0x04, 0x7F, 0x02], # EA_02
|
||||
0x20A: [0x9D, 0xE8, 0x36, 0xA1, 0xCA, 0x3B, 0x1D, 0x33,
|
||||
0xE0, 0xD5, 0xBB, 0x5F, 0xAE, 0x3C, 0x31, 0x9F], # EML_06
|
||||
0x26B: [0xCE, 0xCC, 0xBD, 0x69, 0xA1, 0x3C, 0x18, 0x76,
|
||||
0x0F, 0x04, 0xF2, 0x3A, 0x93, 0x24, 0x19, 0x51], # TA_01
|
||||
0x30C: [0x0F] * 16, # ACC_02
|
||||
0x30F: [0x0C] * 16, # SWA_01
|
||||
0x324: [0x27] * 16, # ACC_04
|
||||
0x3BE: [0x1F, 0x28, 0xC6, 0x85, 0xE6, 0xF8, 0xB0, 0x19,
|
||||
0x5B, 0x64, 0x35, 0x21, 0xE4, 0xF7, 0x9C, 0x24], # Motor_14
|
||||
0x3C0: [0xC3] * 16, # Klemmen_Status_01
|
||||
0x3D5: [0xC5, 0x39, 0xC7, 0xF9, 0x92, 0xD8, 0x24, 0xCE,
|
||||
0xF1, 0xB5, 0x7A, 0xC4, 0xBC, 0x60, 0xE3, 0xD1], # Licht_Anf_01
|
||||
0x65D: [0xAC, 0xB3, 0xAB, 0xEB, 0x7A, 0xE1, 0x3B, 0xF7,
|
||||
0x73, 0xBA, 0x7C, 0x9E, 0x06, 0x5F, 0x02, 0xD9], # ESP_20
|
||||
}
|
||||
105
opendbc_repo/opendbc/car/volkswagen/pqcan.py
Normal file
105
opendbc_repo/opendbc/car/volkswagen/pqcan.py
Normal file
@@ -0,0 +1,105 @@
|
||||
def create_steering_control(packer, bus, apply_torque, lkas_enabled):
|
||||
values = {
|
||||
"LM_Offset": abs(apply_torque),
|
||||
"LM_OffSign": 1 if apply_torque < 0 else 0,
|
||||
"HCA_Status": 5 if (lkas_enabled and apply_torque != 0) else 3,
|
||||
"Vib_Freq": 16,
|
||||
}
|
||||
|
||||
return packer.make_can_msg("HCA_1", bus, values)
|
||||
|
||||
|
||||
def create_lka_hud_control(packer, bus, ldw_stock_values, lat_active, steering_pressed, hud_alert, hud_control):
|
||||
values = {}
|
||||
if len(ldw_stock_values):
|
||||
values = {s: ldw_stock_values[s] for s in [
|
||||
"LDW_SW_Warnung_links", # Blind spot in warning mode on left side due to lane departure
|
||||
"LDW_SW_Warnung_rechts", # Blind spot in warning mode on right side due to lane departure
|
||||
"LDW_Seite_DLCTLC", # Direction of most likely lane departure (left or right)
|
||||
"LDW_DLC", # Lane departure, distance to line crossing
|
||||
"LDW_TLC", # Lane departure, time to line crossing
|
||||
]}
|
||||
|
||||
values.update({
|
||||
"LDW_Lampe_gelb": 1 if lat_active and steering_pressed else 0,
|
||||
"LDW_Lampe_gruen": 1 if lat_active and not steering_pressed else 0,
|
||||
"LDW_Lernmodus_links": 3 if hud_control.leftLaneDepart else 1 + hud_control.leftLaneVisible,
|
||||
"LDW_Lernmodus_rechts": 3 if hud_control.rightLaneDepart else 1 + hud_control.rightLaneVisible,
|
||||
"LDW_Textbits": hud_alert,
|
||||
})
|
||||
|
||||
return packer.make_can_msg("LDW_Status", bus, values)
|
||||
|
||||
|
||||
def create_acc_buttons_control(packer, bus, gra_stock_values, cancel=False, resume=False):
|
||||
values = {s: gra_stock_values[s] for s in [
|
||||
"GRA_Hauptschalt", # ACC button, on/off
|
||||
"GRA_Typ_Hauptschalt", # ACC button, momentary vs latching
|
||||
"GRA_Kodierinfo", # ACC button, configuration
|
||||
"GRA_Sender", # ACC button, CAN message originator
|
||||
]}
|
||||
|
||||
values.update({
|
||||
"COUNTER": (gra_stock_values["COUNTER"] + 1) % 16,
|
||||
"GRA_Abbrechen": cancel,
|
||||
"GRA_Recall": resume,
|
||||
})
|
||||
|
||||
return packer.make_can_msg("GRA_Neu", bus, values)
|
||||
|
||||
|
||||
def acc_control_value(main_switch_on, acc_faulted, long_active):
|
||||
if long_active:
|
||||
acc_control = 1
|
||||
elif main_switch_on:
|
||||
acc_control = 2
|
||||
else:
|
||||
acc_control = 0
|
||||
|
||||
return acc_control
|
||||
|
||||
|
||||
def acc_hud_status_value(main_switch_on, acc_faulted, long_active):
|
||||
if acc_faulted:
|
||||
hud_status = 6
|
||||
elif long_active:
|
||||
hud_status = 3
|
||||
elif main_switch_on:
|
||||
hud_status = 2
|
||||
else:
|
||||
hud_status = 0
|
||||
|
||||
return hud_status
|
||||
|
||||
|
||||
def create_acc_accel_control(packer, bus, acc_type, acc_enabled, accel, acc_control, stopping, starting, esp_hold):
|
||||
commands = []
|
||||
|
||||
values = {
|
||||
"ACS_Sta_ADR": acc_control,
|
||||
"ACS_StSt_Info": acc_enabled,
|
||||
"ACS_Typ_ACC": acc_type,
|
||||
"ACS_Anhaltewunsch": acc_type == 1 and stopping,
|
||||
"ACS_FreigSollB": acc_enabled,
|
||||
"ACS_Sollbeschl": accel if acc_enabled else 3.01,
|
||||
"ACS_zul_Regelabw": 0.2 if acc_enabled else 1.27,
|
||||
"ACS_max_AendGrad": 3.0 if acc_enabled else 5.08,
|
||||
}
|
||||
|
||||
commands.append(packer.make_can_msg("ACC_System", bus, values))
|
||||
|
||||
return commands
|
||||
|
||||
|
||||
def create_acc_hud_control(packer, bus, acc_hud_status, set_speed, lead_distance, distance):
|
||||
values = {
|
||||
"ACA_StaACC": acc_hud_status,
|
||||
"ACA_Zeitluecke": distance + 2,
|
||||
"ACA_V_Wunsch": set_speed,
|
||||
"ACA_gemZeitl": lead_distance,
|
||||
"ACA_PrioDisp": 3,
|
||||
# TODO: restore dynamic pop-to-foreground/highlight behavior with ACA_PrioDisp and ACA_AnzDisplay
|
||||
# TODO: ACA_kmh_mph handling probably needed to resolve rounding errors in displayed setpoint
|
||||
}
|
||||
|
||||
return packer.make_can_msg("ACC_GRA_Anzeige", bus, values)
|
||||
60
opendbc_repo/opendbc/car/volkswagen/tests/test_volkswagen.py
Normal file
60
opendbc_repo/opendbc/car/volkswagen/tests/test_volkswagen.py
Normal file
@@ -0,0 +1,60 @@
|
||||
import random
|
||||
import re
|
||||
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.volkswagen.values import CAR, FW_QUERY_CONFIG, WMI
|
||||
from opendbc.car.volkswagen.fingerprints import FW_VERSIONS
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
CHASSIS_CODE_PATTERN = re.compile('[A-Z0-9]{2}')
|
||||
# TODO: determine the unknown groups
|
||||
SPARE_PART_FW_PATTERN = re.compile(b'\xf1\x87(?P<gateway>[0-9][0-9A-Z]{2})(?P<unknown>[0-9][0-9A-Z][0-9])(?P<unknown2>[0-9A-Z]{2}[0-9])([A-Z0-9]| )')
|
||||
|
||||
|
||||
class TestVolkswagenPlatformConfigs:
|
||||
def test_spare_part_fw_pattern(self, subtests):
|
||||
# Relied on for determining if a FW is likely VW
|
||||
for platform, ecus in FW_VERSIONS.items():
|
||||
with subtests.test(platform=platform.value):
|
||||
for fws in ecus.values():
|
||||
for fw in fws:
|
||||
assert SPARE_PART_FW_PATTERN.match(fw) is not None, f"Bad FW: {fw}"
|
||||
|
||||
def test_chassis_codes(self, subtests):
|
||||
for platform in CAR:
|
||||
with subtests.test(platform=platform.value):
|
||||
assert len(platform.config.wmis) > 0, "WMIs not set"
|
||||
assert len(platform.config.chassis_codes) > 0, "Chassis codes not set"
|
||||
assert all(CHASSIS_CODE_PATTERN.match(cc) for cc in \
|
||||
platform.config.chassis_codes), "Bad chassis codes"
|
||||
|
||||
# No two platforms should share chassis codes
|
||||
for comp in CAR:
|
||||
if platform == comp:
|
||||
continue
|
||||
assert set() == platform.config.chassis_codes & comp.config.chassis_codes, \
|
||||
f"Shared chassis codes: {comp}"
|
||||
|
||||
def test_custom_fuzzy_fingerprinting(self, subtests):
|
||||
all_radar_fw = list({fw for ecus in FW_VERSIONS.values() for fw in ecus[Ecu.fwdRadar, 0x757, None]})
|
||||
|
||||
for platform in CAR:
|
||||
with subtests.test(platform=platform.name):
|
||||
for wmi in WMI:
|
||||
for chassis_code in platform.config.chassis_codes | {"00"}:
|
||||
vin = ["0"] * 17
|
||||
vin[0:3] = wmi
|
||||
vin[6:8] = chassis_code
|
||||
vin = "".join(vin)
|
||||
|
||||
# Check a few FW cases - expected, unexpected
|
||||
for radar_fw in random.sample(all_radar_fw, 5) + [b'\xf1\x875Q0907572G \xf1\x890571', b'\xf1\x877H9907572AA\xf1\x890396']:
|
||||
should_match = ((wmi in platform.config.wmis and chassis_code in platform.config.chassis_codes) and
|
||||
radar_fw in all_radar_fw)
|
||||
|
||||
live_fws = {(0x757, None): [radar_fw]}
|
||||
matches = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(live_fws, vin, FW_VERSIONS)
|
||||
|
||||
expected_matches = {platform} if should_match else set()
|
||||
assert expected_matches == matches, "Bad match"
|
||||
528
opendbc_repo/opendbc/car/volkswagen/values.py
Normal file
528
opendbc_repo/opendbc/car/volkswagen/values.py
Normal file
@@ -0,0 +1,528 @@
|
||||
from collections import defaultdict, namedtuple
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum, IntFlag, StrEnum
|
||||
|
||||
from opendbc.car import Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, structs, uds
|
||||
from opendbc.can import CANDefine
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column, \
|
||||
Device
|
||||
from opendbc.car.fw_query_definitions import EcuAddrSubAddr, FwQueryConfig, Request, p16
|
||||
from opendbc.car.vin import Vin
|
||||
|
||||
Ecu = structs.CarParams.Ecu
|
||||
NetworkLocation = structs.CarParams.NetworkLocation
|
||||
TransmissionType = structs.CarParams.TransmissionType
|
||||
GearShifter = structs.CarState.GearShifter
|
||||
Button = namedtuple('Button', ['event_type', 'can_addr', 'can_msg', 'values'])
|
||||
|
||||
|
||||
class CarControllerParams:
|
||||
STEER_STEP = 2 # HCA_01/HCA_1 message frequency 50Hz
|
||||
ACC_CONTROL_STEP = 2 # ACC_06/ACC_07/ACC_System frequency 50Hz
|
||||
AEB_CONTROL_STEP = 2 # ACC_10 frequency 50Hz
|
||||
AEB_HUD_STEP = 20 # ACC_15 frequency 5Hz
|
||||
|
||||
# Documented lateral limits: 3.00 Nm max, rate of change 5.00 Nm/sec.
|
||||
# MQB vs PQ maximums are shared, but rate-of-change limited differently
|
||||
# based on safety requirements driven by lateral accel testing.
|
||||
|
||||
STEER_MAX = 300 # Max heading control assist torque 3.00 Nm
|
||||
STEER_DRIVER_MULTIPLIER = 3 # weight driver torque heavily
|
||||
STEER_DRIVER_FACTOR = 1 # from dbc
|
||||
|
||||
STEER_TIME_MAX = 360 # Max time that EPS allows uninterrupted HCA steering control
|
||||
STEER_TIME_ALERT = STEER_TIME_MAX - 10 # If mitigation fails, time to soft disengage before EPS timer expires
|
||||
STEER_TIME_STUCK_TORQUE = 1.9 # EPS limits same torque to 6 seconds, reset timer 3x within that period
|
||||
|
||||
DEFAULT_MIN_STEER_SPEED = 0.4 # m/s, newer EPS racks fault below this speed, don't show a low speed alert
|
||||
|
||||
ACCEL_MAX = 2.0 # 2.0 m/s max acceleration
|
||||
ACCEL_MIN = -3.5 # 3.5 m/s max deceleration
|
||||
|
||||
def __init__(self, CP):
|
||||
can_define = CANDefine(DBC[CP.carFingerprint][Bus.pt])
|
||||
|
||||
if CP.flags & VolkswagenFlags.PQ:
|
||||
self.LDW_STEP = 5 # LDW_1 message frequency 20Hz
|
||||
self.ACC_HUD_STEP = 4 # ACC_GRA_Anzeige frequency 25Hz
|
||||
self.STEER_DRIVER_ALLOWANCE = 80 # Driver intervention threshold 0.8 Nm
|
||||
self.STEER_DELTA_UP = 6 # Max HCA reached in 1.00s (STEER_MAX / (50Hz * 1.00))
|
||||
self.STEER_DELTA_DOWN = 10 # Min HCA reached in 0.60s (STEER_MAX / (50Hz * 0.60))
|
||||
|
||||
if CP.transmissionType == TransmissionType.automatic:
|
||||
self.shifter_values = can_define.dv["Getriebe_1"]["Waehlhebelposition__Getriebe_1_"]
|
||||
self.hca_status_values = can_define.dv["Lenkhilfe_2"]["LH2_Sta_HCA"]
|
||||
|
||||
self.BUTTONS = [
|
||||
Button(structs.CarState.ButtonEvent.Type.setCruise, "GRA_Neu", "GRA_Neu_Setzen", [1]),
|
||||
Button(structs.CarState.ButtonEvent.Type.resumeCruise, "GRA_Neu", "GRA_Recall", [1]),
|
||||
Button(structs.CarState.ButtonEvent.Type.accelCruise, "GRA_Neu", "GRA_Up_kurz", [1]),
|
||||
Button(structs.CarState.ButtonEvent.Type.decelCruise, "GRA_Neu", "GRA_Down_kurz", [1]),
|
||||
Button(structs.CarState.ButtonEvent.Type.cancel, "GRA_Neu", "GRA_Abbrechen", [1]),
|
||||
Button(structs.CarState.ButtonEvent.Type.gapAdjustCruise, "GRA_Neu", "GRA_Zeitluecke", [1]),
|
||||
]
|
||||
|
||||
self.LDW_MESSAGES = {
|
||||
"none": 0, # Nothing to display
|
||||
"laneAssistUnavail": 1, # "Lane Assist currently not available."
|
||||
"laneAssistUnavailSysError": 2, # "Lane Assist system error"
|
||||
"laneAssistUnavailNoSensorView": 3, # "Lane Assist not available. No sensor view."
|
||||
"laneAssistTakeOver": 4, # "Lane Assist: Please Take Over Steering"
|
||||
"laneAssistDeactivTrailer": 5, # "Lane Assist: no function with trailer"
|
||||
}
|
||||
|
||||
else:
|
||||
self.LDW_STEP = 10 # LDW_02 message frequency 10Hz
|
||||
self.ACC_HUD_STEP = 6 # ACC_02 message frequency 16Hz
|
||||
self.STEER_DRIVER_ALLOWANCE = 80 # Driver intervention threshold 0.8 Nm
|
||||
self.STEER_DELTA_UP = 4 # Max HCA reached in 1.50s (STEER_MAX / (50Hz * 1.50))
|
||||
self.STEER_DELTA_DOWN = 10 # Min HCA reached in 0.60s (STEER_MAX / (50Hz * 0.60))
|
||||
|
||||
if CP.transmissionType == TransmissionType.automatic:
|
||||
self.shifter_values = can_define.dv["Gateway_73"]["GE_Fahrstufe"]
|
||||
elif CP.transmissionType == TransmissionType.direct:
|
||||
self.shifter_values = can_define.dv["Motor_EV_01"]["MO_Waehlpos"]
|
||||
self.hca_status_values = can_define.dv["LH_EPS_03"]["EPS_HCA_Status"]
|
||||
|
||||
self.BUTTONS = [
|
||||
Button(structs.CarState.ButtonEvent.Type.setCruise, "GRA_ACC_01", "GRA_Tip_Setzen", [1]),
|
||||
Button(structs.CarState.ButtonEvent.Type.resumeCruise, "GRA_ACC_01", "GRA_Tip_Wiederaufnahme", [1]),
|
||||
Button(structs.CarState.ButtonEvent.Type.accelCruise, "GRA_ACC_01", "GRA_Tip_Hoch", [1]),
|
||||
Button(structs.CarState.ButtonEvent.Type.decelCruise, "GRA_ACC_01", "GRA_Tip_Runter", [1]),
|
||||
Button(structs.CarState.ButtonEvent.Type.cancel, "GRA_ACC_01", "GRA_Abbrechen", [1]),
|
||||
Button(structs.CarState.ButtonEvent.Type.gapAdjustCruise, "GRA_ACC_01", "GRA_Verstellung_Zeitluecke", [1]),
|
||||
]
|
||||
|
||||
self.LDW_MESSAGES = {
|
||||
"none": 0, # Nothing to display
|
||||
"laneAssistUnavailChime": 1, # "Lane Assist currently not available." with chime
|
||||
"laneAssistUnavailNoSensorChime": 3, # "Lane Assist not available. No sensor view." with chime
|
||||
"laneAssistTakeOverUrgent": 4, # "Lane Assist: Please Take Over Steering" with urgent beep
|
||||
"emergencyAssistUrgent": 6, # "Emergency Assist: Please Take Over Steering" with urgent beep
|
||||
"laneAssistTakeOverChime": 7, # "Lane Assist: Please Take Over Steering" with chime
|
||||
"laneAssistTakeOver": 8, # "Lane Assist: Please Take Over Steering" silent
|
||||
"emergencyAssistChangingLanes": 9, # "Emergency Assist: Changing lanes..." with urgent beep
|
||||
"laneAssistDeactivated": 10, # "Lane Assist deactivated." silent with persistent icon afterward
|
||||
}
|
||||
|
||||
|
||||
class CANBUS:
|
||||
pt = 0
|
||||
cam = 2
|
||||
|
||||
|
||||
class WMI(StrEnum):
|
||||
VOLKSWAGEN_USA_SUV = "1V2"
|
||||
VOLKSWAGEN_USA_CAR = "1VW"
|
||||
VOLKSWAGEN_MEXICO_SUV = "3VV"
|
||||
VOLKSWAGEN_MEXICO_CAR = "3VW"
|
||||
VOLKSWAGEN_ARGENTINA = "8AW"
|
||||
VOLKSWAGEN_BRASIL = "9BW"
|
||||
SAIC_VOLKSWAGEN = "LSV"
|
||||
SKODA = "TMB"
|
||||
SEAT = "VSS"
|
||||
AUDI_EUROPE_MPV = "WA1"
|
||||
AUDI_GERMANY_CAR = "WAU"
|
||||
MAN = "WMA"
|
||||
AUDI_SPORT = "WUA"
|
||||
VOLKSWAGEN_COMMERCIAL = "WV1"
|
||||
VOLKSWAGEN_COMMERCIAL_BUS_VAN = "WV2"
|
||||
VOLKSWAGEN_EUROPE_SUV = "WVG"
|
||||
VOLKSWAGEN_EUROPE_CAR = "WVW"
|
||||
VOLKSWAGEN_GROUP_RUS = "XW8"
|
||||
|
||||
|
||||
class VolkswagenSafetyFlags(IntFlag):
|
||||
LONG_CONTROL = 1
|
||||
|
||||
|
||||
class VolkswagenFlags(IntFlag):
|
||||
# Detected flags
|
||||
STOCK_HCA_PRESENT = 1
|
||||
|
||||
# Static flags
|
||||
PQ = 2
|
||||
|
||||
|
||||
@dataclass
|
||||
class VolkswagenMQBPlatformConfig(PlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: 'vw_mqb'})
|
||||
# Volkswagen uses the VIN WMI and chassis code to match in the absence of the comma power
|
||||
# on camera-integrated cars, as we lose too many ECUs to reliably identify the vehicle
|
||||
chassis_codes: set[str] = field(default_factory=set)
|
||||
wmis: set[WMI] = field(default_factory=set)
|
||||
|
||||
|
||||
@dataclass
|
||||
class VolkswagenPQPlatformConfig(VolkswagenMQBPlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: 'vw_pq'})
|
||||
|
||||
def init(self):
|
||||
self.flags |= VolkswagenFlags.PQ
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class VolkswagenCarSpecs(CarSpecs):
|
||||
centerToFrontRatio: float = 0.45
|
||||
steerRatio: float = 15.6
|
||||
minSteerSpeed: float = CarControllerParams.DEFAULT_MIN_STEER_SPEED
|
||||
|
||||
|
||||
class Footnote(Enum):
|
||||
KAMIQ = CarFootnote(
|
||||
"Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform.",
|
||||
Column.MODEL)
|
||||
PASSAT = CarFootnote(
|
||||
"Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets.",
|
||||
Column.MODEL)
|
||||
SKODA_HEATED_WINDSHIELD = CarFootnote(
|
||||
"Some Škoda vehicles are equipped with heated windshields, which are known " +
|
||||
"to block GPS signal needed for some comma 3X functionality.",
|
||||
Column.MODEL)
|
||||
VW_EXP_LONG = CarFootnote(
|
||||
"Only available for vehicles using a gateway (J533) harness. At this time, vehicles using a camera harness " +
|
||||
"are limited to using stock ACC.",
|
||||
Column.LONGITUDINAL, docs_only=True)
|
||||
VW_MQB_A0 = CarFootnote(
|
||||
"Model-years 2022 and beyond may have a combined CAN gateway and BCM, which is supported by openpilot " +
|
||||
"in software, but doesn't yet have a harness available from the comma store.",
|
||||
Column.HARDWARE)
|
||||
|
||||
|
||||
@dataclass
|
||||
class VWCarDocs(CarDocs):
|
||||
package: str = "Adaptive Cruise Control (ACC) & Lane Assist"
|
||||
car_parts: CarParts = field(default_factory=CarParts.common([CarHarness.vw_j533]))
|
||||
|
||||
def init_make(self, CP: structs.CarParams):
|
||||
self.footnotes.append(Footnote.VW_EXP_LONG)
|
||||
if "SKODA" in CP.carFingerprint:
|
||||
self.footnotes.append(Footnote.SKODA_HEATED_WINDSHIELD)
|
||||
|
||||
if CP.carFingerprint in (CAR.VOLKSWAGEN_CRAFTER_MK2, CAR.VOLKSWAGEN_TRANSPORTER_T61):
|
||||
self.car_parts = CarParts([Device.threex_angled_mount, CarHarness.vw_j533])
|
||||
|
||||
if abs(CP.minSteerSpeed - CarControllerParams.DEFAULT_MIN_STEER_SPEED) < 1e-3:
|
||||
self.min_steer_speed = 0
|
||||
|
||||
|
||||
# Check the 7th and 8th characters of the VIN before adding a new CAR. If the
|
||||
# chassis code is already listed below, don't add a new CAR, just add to the
|
||||
# FW_VERSIONS for that existing CAR.
|
||||
|
||||
class CAR(Platforms):
|
||||
config: VolkswagenMQBPlatformConfig | VolkswagenPQPlatformConfig
|
||||
|
||||
VOLKSWAGEN_ARTEON_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Arteon 2018-23", video="https://youtu.be/FAomFKPFlDA"),
|
||||
VWCarDocs("Volkswagen Arteon R 2020-23", video="https://youtu.be/FAomFKPFlDA"),
|
||||
VWCarDocs("Volkswagen Arteon eHybrid 2020-23", video="https://youtu.be/FAomFKPFlDA"),
|
||||
VWCarDocs("Volkswagen Arteon Shooting Brake 2020-23", video="https://youtu.be/FAomFKPFlDA"),
|
||||
VWCarDocs("Volkswagen CC 2018-22", video="https://youtu.be/FAomFKPFlDA"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1733, wheelbase=2.84),
|
||||
chassis_codes={"AN", "3H"},
|
||||
wmis={WMI.VOLKSWAGEN_EUROPE_CAR},
|
||||
)
|
||||
VOLKSWAGEN_ATLAS_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Atlas 2018-23"),
|
||||
VWCarDocs("Volkswagen Atlas Cross Sport 2020-22"),
|
||||
VWCarDocs("Volkswagen Teramont 2018-22"),
|
||||
VWCarDocs("Volkswagen Teramont Cross Sport 2021-22"),
|
||||
VWCarDocs("Volkswagen Teramont X 2021-22"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=2011, wheelbase=2.98),
|
||||
chassis_codes={"CA"},
|
||||
wmis={WMI.VOLKSWAGEN_USA_SUV, WMI.VOLKSWAGEN_EUROPE_SUV},
|
||||
)
|
||||
VOLKSWAGEN_CADDY_MK3 = VolkswagenPQPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Caddy 2019"),
|
||||
VWCarDocs("Volkswagen Caddy Maxi 2019"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1613, wheelbase=2.6, minSteerSpeed=21 * CV.KPH_TO_MS),
|
||||
chassis_codes={"2K"},
|
||||
wmis={WMI.VOLKSWAGEN_COMMERCIAL_BUS_VAN},
|
||||
)
|
||||
VOLKSWAGEN_CRAFTER_MK2 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Crafter 2017-24", video="https://youtu.be/4100gLeabmo"),
|
||||
VWCarDocs("Volkswagen e-Crafter 2018-24", video="https://youtu.be/4100gLeabmo"),
|
||||
VWCarDocs("Volkswagen Grand California 2019-24", video="https://youtu.be/4100gLeabmo"),
|
||||
VWCarDocs("MAN TGE 2017-24", video="https://youtu.be/4100gLeabmo"),
|
||||
VWCarDocs("MAN eTGE 2020-24", video="https://youtu.be/4100gLeabmo"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=2100, wheelbase=3.64, minSteerSpeed=50 * CV.KPH_TO_MS),
|
||||
chassis_codes={"SY", "SZ", "UY", "UZ"},
|
||||
wmis={WMI.VOLKSWAGEN_COMMERCIAL, WMI.MAN},
|
||||
)
|
||||
VOLKSWAGEN_GOLF_MK7 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen e-Golf 2014-20"),
|
||||
VWCarDocs("Volkswagen Golf 2015-20", auto_resume=False),
|
||||
VWCarDocs("Volkswagen Golf Alltrack 2015-19", auto_resume=False),
|
||||
VWCarDocs("Volkswagen Golf GTD 2015-20"),
|
||||
VWCarDocs("Volkswagen Golf GTE 2015-20"),
|
||||
VWCarDocs("Volkswagen Golf GTI 2015-21", auto_resume=False),
|
||||
VWCarDocs("Volkswagen Golf R 2015-19"),
|
||||
VWCarDocs("Volkswagen Golf SportsVan 2015-20"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1397, wheelbase=2.62),
|
||||
chassis_codes={"5G", "AU", "BA", "BE"},
|
||||
wmis={WMI.VOLKSWAGEN_MEXICO_CAR, WMI.VOLKSWAGEN_EUROPE_CAR},
|
||||
)
|
||||
VOLKSWAGEN_JETTA_MK6 = VolkswagenPQPlatformConfig(
|
||||
[VWCarDocs("Volkswagen Jetta 2015-18")],
|
||||
VolkswagenCarSpecs(mass=1518, wheelbase=2.65, minSteerSpeed=50 * CV.KPH_TO_MS, minEnableSpeed=20 * CV.KPH_TO_MS),
|
||||
chassis_codes={"5K", "AJ"},
|
||||
wmis={WMI.VOLKSWAGEN_MEXICO_CAR},
|
||||
)
|
||||
VOLKSWAGEN_JETTA_MK7 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Jetta 2018-23"),
|
||||
VWCarDocs("Volkswagen Jetta GLI 2021-23"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1328, wheelbase=2.71),
|
||||
chassis_codes={"BU"},
|
||||
wmis={WMI.VOLKSWAGEN_MEXICO_CAR, WMI.VOLKSWAGEN_EUROPE_CAR},
|
||||
)
|
||||
VOLKSWAGEN_PASSAT_MK8 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Passat 2015-22", footnotes=[Footnote.PASSAT]),
|
||||
VWCarDocs("Volkswagen Passat Alltrack 2015-22"),
|
||||
VWCarDocs("Volkswagen Passat GTE 2015-22"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1551, wheelbase=2.79),
|
||||
chassis_codes={"3C", "3G"},
|
||||
wmis={WMI.VOLKSWAGEN_EUROPE_CAR},
|
||||
)
|
||||
VOLKSWAGEN_PASSAT_NMS = VolkswagenPQPlatformConfig(
|
||||
[VWCarDocs("Volkswagen Passat NMS 2017-22")],
|
||||
VolkswagenCarSpecs(mass=1503, wheelbase=2.80, minSteerSpeed=50 * CV.KPH_TO_MS, minEnableSpeed=20 * CV.KPH_TO_MS),
|
||||
chassis_codes={"A3"},
|
||||
wmis={WMI.VOLKSWAGEN_USA_CAR},
|
||||
)
|
||||
VOLKSWAGEN_POLO_MK6 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Polo 2018-23", footnotes=[Footnote.VW_MQB_A0]),
|
||||
VWCarDocs("Volkswagen Polo GTI 2018-23", footnotes=[Footnote.VW_MQB_A0]),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1230, wheelbase=2.55),
|
||||
chassis_codes={"AW"},
|
||||
wmis={WMI.VOLKSWAGEN_EUROPE_CAR},
|
||||
)
|
||||
VOLKSWAGEN_SHARAN_MK2 = VolkswagenPQPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Sharan 2018-22"),
|
||||
VWCarDocs("SEAT Alhambra 2018-20"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1639, wheelbase=2.92, minSteerSpeed=50 * CV.KPH_TO_MS),
|
||||
chassis_codes={"7N"},
|
||||
wmis={WMI.VOLKSWAGEN_EUROPE_CAR},
|
||||
)
|
||||
VOLKSWAGEN_TAOS_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Volkswagen Taos 2022-23")],
|
||||
VolkswagenCarSpecs(mass=1498, wheelbase=2.69),
|
||||
chassis_codes={"B2"},
|
||||
wmis={WMI.VOLKSWAGEN_MEXICO_SUV, WMI.VOLKSWAGEN_ARGENTINA},
|
||||
)
|
||||
VOLKSWAGEN_TCROSS_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Volkswagen T-Cross 2021", footnotes=[Footnote.VW_MQB_A0])],
|
||||
VolkswagenCarSpecs(mass=1150, wheelbase=2.60),
|
||||
chassis_codes={"C1"},
|
||||
wmis={WMI.VOLKSWAGEN_EUROPE_SUV},
|
||||
)
|
||||
VOLKSWAGEN_TIGUAN_MK2 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Tiguan 2018-23"),
|
||||
VWCarDocs("Volkswagen Tiguan eHybrid 2021-23"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1715, wheelbase=2.74),
|
||||
chassis_codes={"5N", "AD", "AX", "BW"},
|
||||
wmis={WMI.VOLKSWAGEN_EUROPE_SUV, WMI.VOLKSWAGEN_MEXICO_SUV},
|
||||
)
|
||||
VOLKSWAGEN_TOURAN_MK2 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Volkswagen Touran 2016-23")],
|
||||
VolkswagenCarSpecs(mass=1516, wheelbase=2.79),
|
||||
chassis_codes={"1T"},
|
||||
wmis={WMI.VOLKSWAGEN_EUROPE_SUV},
|
||||
)
|
||||
VOLKSWAGEN_TRANSPORTER_T61 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Volkswagen Caravelle 2020"),
|
||||
VWCarDocs("Volkswagen California 2021-23"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1926, wheelbase=3.00, minSteerSpeed=14.0),
|
||||
chassis_codes={"7H", "7L"},
|
||||
wmis={WMI.VOLKSWAGEN_COMMERCIAL_BUS_VAN},
|
||||
)
|
||||
VOLKSWAGEN_TROC_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Volkswagen T-Roc 2018-23")],
|
||||
VolkswagenCarSpecs(mass=1413, wheelbase=2.63),
|
||||
chassis_codes={"A1"},
|
||||
wmis={WMI.VOLKSWAGEN_EUROPE_SUV},
|
||||
)
|
||||
AUDI_A3_MK3 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Audi A3 2014-19"),
|
||||
VWCarDocs("Audi A3 Sportback e-tron 2017-18"),
|
||||
VWCarDocs("Audi RS3 2018"),
|
||||
VWCarDocs("Audi S3 2015-17"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1335, wheelbase=2.61),
|
||||
chassis_codes={"8V", "FF"},
|
||||
wmis={WMI.AUDI_GERMANY_CAR, WMI.AUDI_SPORT},
|
||||
)
|
||||
AUDI_Q2_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Audi Q2 2018")],
|
||||
VolkswagenCarSpecs(mass=1205, wheelbase=2.61),
|
||||
chassis_codes={"GA"},
|
||||
wmis={WMI.AUDI_GERMANY_CAR},
|
||||
)
|
||||
AUDI_Q3_MK2 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Audi Q3 2019-23")],
|
||||
VolkswagenCarSpecs(mass=1623, wheelbase=2.68),
|
||||
chassis_codes={"8U", "F3", "FS"},
|
||||
wmis={WMI.AUDI_EUROPE_MPV, WMI.AUDI_GERMANY_CAR},
|
||||
)
|
||||
SEAT_ATECA_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("CUPRA Ateca 2018-23"),
|
||||
VWCarDocs("SEAT Ateca 2016-23"),
|
||||
VWCarDocs("SEAT Leon 2014-20"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1300, wheelbase=2.64),
|
||||
chassis_codes={"5F"},
|
||||
wmis={WMI.SEAT},
|
||||
)
|
||||
SKODA_FABIA_MK4 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Škoda Fabia 2022-23", footnotes=[Footnote.VW_MQB_A0])],
|
||||
VolkswagenCarSpecs(mass=1266, wheelbase=2.56),
|
||||
chassis_codes={"PJ"},
|
||||
wmis={WMI.SKODA},
|
||||
)
|
||||
SKODA_KAMIQ_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Škoda Kamiq 2021-23", footnotes=[Footnote.VW_MQB_A0, Footnote.KAMIQ]),
|
||||
VWCarDocs("Škoda Scala 2020-23", footnotes=[Footnote.VW_MQB_A0]),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1230, wheelbase=2.66),
|
||||
chassis_codes={"NW"},
|
||||
wmis={WMI.SKODA},
|
||||
)
|
||||
SKODA_KAROQ_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Škoda Karoq 2019-23")],
|
||||
VolkswagenCarSpecs(mass=1278, wheelbase=2.66),
|
||||
chassis_codes={"NU"},
|
||||
wmis={WMI.SKODA},
|
||||
)
|
||||
SKODA_KODIAQ_MK1 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Škoda Kodiaq 2017-23")],
|
||||
VolkswagenCarSpecs(mass=1569, wheelbase=2.79),
|
||||
chassis_codes={"NS"},
|
||||
wmis={WMI.SKODA, WMI.VOLKSWAGEN_GROUP_RUS},
|
||||
)
|
||||
SKODA_OCTAVIA_MK3 = VolkswagenMQBPlatformConfig(
|
||||
[
|
||||
VWCarDocs("Škoda Octavia 2015-19"),
|
||||
VWCarDocs("Škoda Octavia RS 2016"),
|
||||
VWCarDocs("Škoda Octavia Scout 2017-19"),
|
||||
],
|
||||
VolkswagenCarSpecs(mass=1388, wheelbase=2.68),
|
||||
chassis_codes={"NE"},
|
||||
wmis={WMI.SKODA},
|
||||
)
|
||||
SKODA_SUPERB_MK3 = VolkswagenMQBPlatformConfig(
|
||||
[VWCarDocs("Škoda Superb 2015-22")],
|
||||
VolkswagenCarSpecs(mass=1505, wheelbase=2.84),
|
||||
chassis_codes={"3V", "NP"},
|
||||
wmis={WMI.SKODA},
|
||||
)
|
||||
|
||||
|
||||
def match_fw_to_car_fuzzy(live_fw_versions, vin, offline_fw_versions) -> set[str]:
|
||||
candidates = set()
|
||||
|
||||
# Compile all FW versions for each ECU
|
||||
all_ecu_versions: dict[EcuAddrSubAddr, set[str]] = defaultdict(set)
|
||||
for ecus in offline_fw_versions.values():
|
||||
for ecu, versions in ecus.items():
|
||||
all_ecu_versions[ecu] |= set(versions)
|
||||
|
||||
# Check the WMI and chassis code to determine the platform
|
||||
# https://www.clubvw.org.au/vwreference/vwvin
|
||||
vin_obj = Vin(vin)
|
||||
chassis_code = vin_obj.vds[3:5]
|
||||
|
||||
for platform in CAR:
|
||||
valid_ecus = set()
|
||||
for ecu in offline_fw_versions[platform]:
|
||||
addr = ecu[1:]
|
||||
if ecu[0] not in CHECK_FUZZY_ECUS:
|
||||
continue
|
||||
|
||||
# Sanity check that live FW is in the superset of all FW, Volkswagen ECU part numbers are commonly shared
|
||||
found_versions = live_fw_versions.get(addr, [])
|
||||
expected_versions = all_ecu_versions[ecu]
|
||||
if not any(found_version in expected_versions for found_version in found_versions):
|
||||
break
|
||||
|
||||
valid_ecus.add(ecu[0])
|
||||
|
||||
if valid_ecus != CHECK_FUZZY_ECUS:
|
||||
continue
|
||||
|
||||
if vin_obj.wmi in platform.config.wmis and chassis_code in platform.config.chassis_codes:
|
||||
candidates.add(platform)
|
||||
|
||||
return {str(c) for c in candidates}
|
||||
|
||||
|
||||
# These ECUs are required to match to gain a VIN match
|
||||
# TODO: do we want to check camera when we add its FW?
|
||||
CHECK_FUZZY_ECUS = {Ecu.fwdRadar}
|
||||
|
||||
# All supported cars should return FW from the engine, srs, eps, and fwdRadar. Cars
|
||||
# with a manual trans won't return transmission firmware, but all other cars will.
|
||||
#
|
||||
# The 0xF187 SW part number query should return in the form of N[NX][NX] NNN NNN [X[X]],
|
||||
# where N=number, X=letter, and the trailing two letters are optional. Performance
|
||||
# tuners sometimes tamper with that field (e.g. 8V0 9C0 BB0 1 from COBB/EQT). Tampered
|
||||
# ECU SW part numbers are invalid for vehicle ID and compatibility checks. Try to have
|
||||
# them repaired by the tuner before including them in openpilot.
|
||||
|
||||
VOLKSWAGEN_VERSION_REQUEST_MULTI = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
|
||||
p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_SPARE_PART_NUMBER) + \
|
||||
p16(uds.DATA_IDENTIFIER_TYPE.VEHICLE_MANUFACTURER_ECU_SOFTWARE_VERSION_NUMBER) + \
|
||||
p16(uds.DATA_IDENTIFIER_TYPE.APPLICATION_DATA_IDENTIFICATION)
|
||||
VOLKSWAGEN_VERSION_RESPONSE = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40])
|
||||
|
||||
VOLKSWAGEN_RX_OFFSET = 0x6a
|
||||
|
||||
FW_QUERY_CONFIG = FwQueryConfig(
|
||||
requests=[request for bus, obd_multiplexing in [(1, True), (1, False), (0, False)] for request in [
|
||||
Request(
|
||||
[VOLKSWAGEN_VERSION_REQUEST_MULTI],
|
||||
[VOLKSWAGEN_VERSION_RESPONSE],
|
||||
whitelist_ecus=[Ecu.srs, Ecu.eps, Ecu.fwdRadar, Ecu.fwdCamera],
|
||||
rx_offset=VOLKSWAGEN_RX_OFFSET,
|
||||
bus=bus,
|
||||
obd_multiplexing=obd_multiplexing,
|
||||
),
|
||||
Request(
|
||||
[VOLKSWAGEN_VERSION_REQUEST_MULTI],
|
||||
[VOLKSWAGEN_VERSION_RESPONSE],
|
||||
whitelist_ecus=[Ecu.engine, Ecu.transmission],
|
||||
bus=bus,
|
||||
obd_multiplexing=obd_multiplexing,
|
||||
),
|
||||
]],
|
||||
non_essential_ecus={Ecu.eps: list(CAR)},
|
||||
extra_ecus=[(Ecu.fwdCamera, 0x74f, None)],
|
||||
match_fw_to_car_fuzzy=match_fw_to_car_fuzzy,
|
||||
)
|
||||
|
||||
DBC = CAR.create_dbc_map()
|
||||
Reference in New Issue
Block a user