forked from IQ.Lvbs/IQ.Pilot
940 lines
36 KiB
Python
940 lines
36 KiB
Python
from collections import defaultdict, namedtuple
|
|
from dataclasses import dataclass, field
|
|
from enum import Enum, IntFlag, StrEnum
|
|
|
|
from iqdbc.car import ACCELERATION_DUE_TO_GRAVITY, Bus, CanBusBase, CarSpecs, DbcDict, DT_CTRL, PlatformConfig, Platforms, structs, uds
|
|
from iqdbc.car.lateral import CurvatureSteeringLimits
|
|
from iqdbc.can import CANDefine
|
|
from iqdbc.car.common.conversions import Conversions as CV
|
|
from iqdbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column
|
|
from iqdbc.car.lateral import AngleSteeringLimits, ISO_LATERAL_ACCEL, ISO_LATERAL_JERK
|
|
from iqdbc.car.fw_query_definitions import EcuAddrSubAddr, FwQueryConfig, Request, p16
|
|
from iqdbc.car.vin import Vin
|
|
|
|
Ecu = structs.CarParams.Ecu
|
|
NetworkLocation = structs.CarParams.NetworkLocation
|
|
TransmissionType = structs.CarParams.TransmissionType
|
|
DashcamOnlyReason = structs.CarParams.DashcamOnlyReason
|
|
GearShifter = structs.CarState.GearShifter
|
|
Button = namedtuple('Button', ['event_type', 'can_addr', 'can_msg', 'values'])
|
|
|
|
|
|
class CanBus(CanBusBase):
|
|
def __init__(self, CP=None, fingerprint=None) -> None:
|
|
super().__init__(CP, fingerprint)
|
|
self.offset = 0
|
|
|
|
@property
|
|
def pt(self) -> int:
|
|
# ADAS / Extended CAN, gateway side of the relay
|
|
return 0
|
|
|
|
@property
|
|
def aux(self) -> int:
|
|
# NetworkLocation.fwdCamera: radar-camera object fusion CAN
|
|
# NetworkLocation.gateway: powertrain CAN
|
|
return 1
|
|
|
|
@property
|
|
def powertrain(self) -> int:
|
|
return 1
|
|
|
|
@property
|
|
def main(self) -> int:
|
|
return 1
|
|
|
|
@property
|
|
def cam(self) -> int:
|
|
# ADAS / Extended CAN, camera side of the relay
|
|
return 2
|
|
|
|
@property
|
|
def ext(self) -> int:
|
|
# ADAS / Extended CAN, side of the relay with the ACC radar
|
|
return 2
|
|
|
|
# Extra Tolerances For Road Variance
|
|
AVERAGE_ROAD_ROLL = 0.06
|
|
PQ_STOPPING_SPEED = 1.5 * CV.KPH_TO_MS
|
|
PASSAT_B7_STOPPING_SPEED = 0.55 * CV.KPH_TO_MS
|
|
PASSAT_B7_STOP_ACCEL = -0.55
|
|
|
|
|
|
class CarControllerParams:
|
|
ANGLE_LIMITS: AngleSteeringLimits = AngleSteeringLimits(
|
|
# Max Steering Angle Allowed
|
|
500, # deg
|
|
# Volkswagen uses a vehicle model
|
|
([], []),
|
|
([], []),
|
|
|
|
# Vehicle Model Angle Limits
|
|
# Add extra tolerance for average banked road since safety doesn't have the roll calculation
|
|
MAX_LATERAL_ACCEL=ISO_LATERAL_ACCEL + (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL), # ~3.6 m/s^2
|
|
MAX_LATERAL_JERK=ISO_LATERAL_JERK + (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL), # ~3.6 m/s^3
|
|
|
|
# Limit Angle Rate to both prevent a openpilot fault and for low speed comfort (~12 mph rate down to 0 mph)
|
|
MAX_ANGLE_RATE=5, # deg/20ms frame
|
|
)
|
|
|
|
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
|
|
VW_LOW_SPEED_STATE_SPEED = 0.5 # m/s; below this, always force starting or stopping in MQB legacy long
|
|
SNG_HANDOFF_SPEED = 5.0 * CV.KPH_TO_MS
|
|
SNG_HOLD_DECEL_MAX = -0.1
|
|
|
|
# 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_BM = STEER_TIME_MAX - 120 # Attempts to mitigate the EPS max steer timer begin
|
|
STEER_TIME_ALERT = STEER_TIME_MAX - 10 # If mitigation fails, time to soft disengage before EPS timer expires
|
|
STEER_LOW_TORQUE = int(STEER_MAX * 0.20) # Steer timer mitigation performed when torque output under 20%
|
|
STEER_TIME_LOW_TORQUE = 0.5 # Wait for this duration of STEER_LOW_TORQUE to begin mitigation
|
|
STEER_TIME_STUCK_TORQUE = 1.9 # EPS limits same torque to 6 seconds, reset timer 3x within that period
|
|
STEER_TIME_RESET = 1.1 # Duration of HCA disable needed for effective EPS timer reset'
|
|
IQ_PQ_UNAVAILABLE_HUD_FRAMES = 25
|
|
|
|
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
|
|
VW_LOW_SPEED_STATE_SPEED = 0.5 # Always send either starting or stopping below this speed
|
|
|
|
def __init__(self, CP):
|
|
can_define = CANDefine(DBC[CP.carFingerprint][Bus.pt])
|
|
self.STEER_TIME_RESET = self.__class__.STEER_TIME_RESET
|
|
|
|
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 = 150 # Max HCA reached in 0.60s (STEER_MAX / (50Hz * 0.60))
|
|
self.STEER_DELTA_DOWN = 300 # Min HCA reached in 0.60s (STEER_MAX / (50Hz * 0.60))
|
|
|
|
if CP.transmissionType == TransmissionType.automatic:
|
|
self.shifter_values = can_define.dv["Getriebe_1"]["GE1_Wahl_Pos"]
|
|
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, 2, 3]),
|
|
]
|
|
|
|
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"
|
|
}
|
|
|
|
elif CP.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO):
|
|
self.LDW_STEP = 10
|
|
self.ACC_HUD_STEP = 6
|
|
self.STEER_DRIVER_ALLOWANCE = 60
|
|
self.STEER_DRIVER_MAX = 300
|
|
self.STEERING_POWER_MAX = 90
|
|
self.STEERING_POWER_MIN = 4
|
|
self.STEERING_POWER_STEP = 2
|
|
|
|
self.CURVATURE_PID: structs.CarParams.LateralPIDTuning = structs.CarParams.LateralPIDTuning(
|
|
kpBP=[10., 40.],
|
|
kiBP=[10., 40.],
|
|
kf=1.,
|
|
kpV=[0., 1.45],
|
|
kiV=[0., 0.12],
|
|
)
|
|
|
|
self.CURVATURE_LIMITS: CurvatureSteeringLimits = CurvatureSteeringLimits(
|
|
0.195,
|
|
)
|
|
|
|
if CP.flags & VolkswagenFlags.ALT_GEAR:
|
|
self.shifter_values = can_define.dv["Gateway_73"]["GE_Fahrstufe"]
|
|
else:
|
|
self.shifter_values = can_define.dv["Getriebe_11"]["GE_Fahrstufe"]
|
|
|
|
self.hca_status_values = can_define.dv["QFK_01"]["LatCon_HCA_Status"]
|
|
|
|
BASE_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.gapAdjustCruise, "GRA_ACC_01", "GRA_Verstellung_Zeitluecke", [1, 2, 3]),
|
|
]
|
|
self.BUTTONS = BASE_BUTTONS + [
|
|
Button(structs.CarState.ButtonEvent.Type.cancel, "GRA_ACC_01", "GRA_Hauptschalter", [1]),
|
|
]
|
|
self.BUTTONS_ALT = BASE_BUTTONS + [
|
|
Button(structs.CarState.ButtonEvent.Type.cancel, "GRA_ACC_01", "GRA_Abbrechen", [1]),
|
|
]
|
|
|
|
self.LDW_MESSAGES = {
|
|
"none": 0,
|
|
"laneAssistTakeOverUrgent": 4,
|
|
"laneAssistTakeOver": 8,
|
|
}
|
|
self.LDW_SOUNDS = {
|
|
"None": 0,
|
|
"Chime": 1,
|
|
"Beep": 2,
|
|
}
|
|
|
|
else:
|
|
self.LDW_STEP = 10 # LDW_02 message frequency 10Hz
|
|
self.ACC_HUD_STEP = 6 # ACC_02 message frequency 16Hz
|
|
|
|
self.hca_status_values = can_define.dv["LH_EPS_03"]["EPS_HCA_Status"]
|
|
|
|
if CP.flags & VolkswagenFlags.MLB:
|
|
self.STEER_DRIVER_ALLOWANCE = 60 # Driver intervention threshold 0.6 Nm
|
|
self.STEER_DELTA_UP = 9 # Max HCA reached in 0.66s (STEER_MAX / (50Hz * 0.66))
|
|
self.STEER_DELTA_DOWN = 10 # Min HCA reached in 0.60s (STEER_MAX / (50Hz * 0.60))
|
|
self.ACC_HUD_TEXT_STEP = int(2.0 / DT_CTRL) # ACC_02 primary display text dwell time
|
|
|
|
if CP.carFingerprint == CAR.PORSCHE_MACAN_MK1:
|
|
self.shifter_values = can_define.dv["Getriebe_03"]["GE_Waehlhebel"]
|
|
else:
|
|
self.shifter_values = None
|
|
|
|
self.BUTTONS = [
|
|
Button(structs.CarState.ButtonEvent.Type.setCruise, "LS_01", "LS_Tip_Setzen", [1]),
|
|
Button(structs.CarState.ButtonEvent.Type.resumeCruise, "LS_01", "LS_Tip_Wiederaufnahme", [1]),
|
|
Button(structs.CarState.ButtonEvent.Type.accelCruise, "LS_01", "LS_Tip_Hoch", [1]),
|
|
Button(structs.CarState.ButtonEvent.Type.decelCruise, "LS_01", "LS_Tip_Runter", [1]),
|
|
Button(structs.CarState.ButtonEvent.Type.cancel, "LS_01", "LS_Abbrechen", [1]),
|
|
Button(structs.CarState.ButtonEvent.Type.gapAdjustCruise, "LS_01", "LS_Verstellung_Zeitluecke", [1, 2, 3]),
|
|
]
|
|
|
|
# ACC_02.ACC_Texte_Primaeranz, primary ACC display text at the bottom of the cluster
|
|
self.ACC_HUD_TEXTS = {
|
|
"none": 0,
|
|
"setSpeed": 21,
|
|
}
|
|
self.ACC_HUD_TEXT_DISTANCE = {1: 2, 2: 3, 3: 4, 4: 5} # follow distance bars to display text
|
|
|
|
else:
|
|
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.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, 2, 3]),
|
|
]
|
|
|
|
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
|
|
}
|
|
|
|
|
|
HOLD_MAX_FRAMES = 60
|
|
HOLD_TORQUE_DEADBAND_NM = 20
|
|
HOLD_ACCEL_KI = 0.00002
|
|
|
|
|
|
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"
|
|
PORSCHE_SUV = "WP1"
|
|
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
|
|
ALT_CRC_VARIANT_1 = 2
|
|
NO_GAS_OFFSET = 4
|
|
ALLOW_LONG_ACCEL_WITH_GAS_PRESSED = 8
|
|
DISABLE_RADAR = 16
|
|
PQ_ALC_MODULE = 32
|
|
PQ_LOWLINE = 64
|
|
PQ_NO_CAM_BUS = 128
|
|
PQ_ACC_FTS_EPB = 256
|
|
PQ_SNG_ECD = 512
|
|
|
|
|
|
class VolkswagenFlags(IntFlag):
|
|
# Detected flags
|
|
STOCK_HCA_PRESENT = 1
|
|
KOMBI_PRESENT = 4
|
|
|
|
# Static flags
|
|
PQ = 2
|
|
MLB = 8
|
|
MEB = 256
|
|
MEB_GEN2 = 512
|
|
MQB_EVO = 1024
|
|
|
|
# Detected flags (MEB/MQBevo)
|
|
STOCK_KLR_PRESENT = 2048
|
|
STOCK_PSD_PRESENT = 4096
|
|
STOCK_PSD_06_PRESENT = 8192
|
|
STOCK_DIAGNOSE_01_PRESENT = 16384
|
|
ALT_GEAR = 32768
|
|
DISABLE_RADAR = 65536
|
|
|
|
|
|
class VolkswagenFlagsIQ(IntFlag):
|
|
IQ_CC_ONLY = 1 << 5 # CC only mode with radar (has AEB)
|
|
IQ_CC_ONLY_NO_RADAR = 1 << 6 # CC only mode without radar
|
|
IQ_LVBS_ALC_MODULE = 1 << 7 # IQ.Lvbs VW ALC hardware module present / intended active path
|
|
IQ_PQ_LOWLINE = 1 << 17 # Non-ECAN lateral-only PQ: bus 0 dead, TX on bus 1 (ptCAN)
|
|
IQ_PQ_ACC_FTS_EPB = 1 << 18 # B7 TRW450: ACC FtS + EPB hold, Motor_1 resume spoof on bus 1
|
|
IQ_PQ_SNG_ECD = 1 << 19
|
|
IQ_PQ_TIMEBOMB = 1 << 20
|
|
|
|
|
|
RADAR_DISABLE_STATE = {"error": False}
|
|
|
|
|
|
@dataclass
|
|
class VolkswagenMLBPlatformConfig(PlatformConfig):
|
|
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: 'vw_mlb'})
|
|
chassis_codes: set[str] = field(default_factory=set)
|
|
wmis: set[WMI] = field(default_factory=set)
|
|
|
|
def init(self):
|
|
self.flags |= VolkswagenFlags.MLB
|
|
|
|
|
|
@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)
|
|
model_years: set[str] = 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
|
|
class VolkswagenMEBPlatformConfig(PlatformConfig):
|
|
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: 'vw_meb', Bus.radar: 'vw_meb'})
|
|
chassis_codes: set[str] = field(default_factory=set)
|
|
wmis: set[WMI] = field(default_factory=set)
|
|
model_years: set[str] = field(default_factory=set)
|
|
|
|
def init(self):
|
|
self.flags |= VolkswagenFlags.MEB
|
|
if self.flags & VolkswagenFlags.MEB_GEN2:
|
|
self.dbc_dict = {Bus.pt: 'vw_meb_2024', Bus.radar: 'vw_meb_2024'}
|
|
|
|
|
|
@dataclass
|
|
class VolkswagenMQBevoPlatformConfig(PlatformConfig):
|
|
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: 'vw_mqbevo', Bus.radar: 'vw_mqbevo'})
|
|
chassis_codes: set[str] = field(default_factory=set)
|
|
wmis: set[WMI] = field(default_factory=set)
|
|
|
|
def init(self):
|
|
self.flags |= VolkswagenFlags.MQB_EVO
|
|
|
|
|
|
@dataclass(frozen=True, kw_only=True)
|
|
class VolkswagenCarSpecs(CarSpecs):
|
|
centerToFrontRatio: float = 0.45
|
|
steerRatio: float = 18.4
|
|
minSteerSpeed: float = CarControllerParams.DEFAULT_MIN_STEER_SPEED
|
|
|
|
# FIXME: nuke this
|
|
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 four 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 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 | VolkswagenMLBPlatformConfig | VolkswagenMEBPlatformConfig | VolkswagenMQBevoPlatformConfig
|
|
|
|
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),
|
|
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, steerRatio=17.0),
|
|
chassis_codes={"5G", "AU", "BA", "BE"},
|
|
wmis={WMI.VOLKSWAGEN_MEXICO_CAR, WMI.VOLKSWAGEN_EUROPE_CAR},
|
|
)
|
|
VOLKSWAGEN_GOLF_MK8 = VolkswagenMQBevoPlatformConfig(
|
|
[VWCarDocs("Volkswagen Golf 2020-25")],
|
|
VolkswagenCarSpecs(mass=1397, wheelbase=2.62),
|
|
chassis_codes={"CD"},
|
|
wmis={WMI.VOLKSWAGEN_EUROPE_CAR},
|
|
)
|
|
VOLKSWAGEN_ID3_MK1 = VolkswagenMEBPlatformConfig(
|
|
[
|
|
VWCarDocs("Volkswagen ID.3 2020-23"),
|
|
VWCarDocs("Volkswagen MEB Gen 1 2020-23"),
|
|
],
|
|
VolkswagenCarSpecs(mass=1935, wheelbase=2.77),
|
|
chassis_codes={"E1"},
|
|
wmis={WMI.VOLKSWAGEN_USA_SUV, WMI.VOLKSWAGEN_EUROPE_CAR},
|
|
model_years={"L", "M", "N", "P"},
|
|
)
|
|
VOLKSWAGEN_ID3_MK2 = VolkswagenMEBPlatformConfig(
|
|
[
|
|
VWCarDocs("Volkswagen ID.3 2024-25"),
|
|
VWCarDocs("Volkswagen MEB Gen 2 2024-25"),
|
|
],
|
|
VolkswagenCarSpecs(mass=1935, wheelbase=2.77),
|
|
chassis_codes={"E1"},
|
|
wmis={WMI.VOLKSWAGEN_USA_SUV, WMI.VOLKSWAGEN_EUROPE_CAR},
|
|
model_years={"R", "S"},
|
|
flags=VolkswagenFlags.MEB_GEN2,
|
|
)
|
|
VOLKSWAGEN_ID4_MK1 = VolkswagenMEBPlatformConfig(
|
|
[
|
|
VWCarDocs("Volkswagen ID.4 2021-23"),
|
|
VWCarDocs("Volkswagen ID.5 2022-23"),
|
|
],
|
|
VolkswagenCarSpecs(mass=2224, wheelbase=2.77),
|
|
chassis_codes={"E2"},
|
|
wmis={WMI.VOLKSWAGEN_USA_SUV, WMI.VOLKSWAGEN_EUROPE_CAR, WMI.VOLKSWAGEN_EUROPE_SUV},
|
|
)
|
|
VOLKSWAGEN_ID4_MK2 = VolkswagenMEBPlatformConfig(
|
|
[VWCarDocs("Volkswagen ID.4 2024-25")],
|
|
VolkswagenCarSpecs(mass=2224, wheelbase=2.77),
|
|
chassis_codes={"E8"},
|
|
wmis={WMI.VOLKSWAGEN_USA_SUV, WMI.VOLKSWAGEN_EUROPE_CAR, WMI.VOLKSWAGEN_EUROPE_SUV},
|
|
flags=VolkswagenFlags.MEB_GEN2,
|
|
)
|
|
VOLKSWAGEN_JETTA_MK6 = VolkswagenPQPlatformConfig(
|
|
[VWCarDocs("Volkswagen Jetta 2015-18")],
|
|
VolkswagenCarSpecs(mass=1518, wheelbase=2.65),
|
|
chassis_codes={"5K", "AJ"},
|
|
wmis={WMI.VOLKSWAGEN_MEXICO_CAR},
|
|
)
|
|
VOLKSWAGEN_JETTA_MK7 = VolkswagenMQBPlatformConfig(
|
|
[
|
|
VWCarDocs("Volkswagen Jetta 2019-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},
|
|
model_years={"F", "G", "H", "J", "K", "L", "M", "N"}, # 2015-2022
|
|
)
|
|
VOLKSWAGEN_PASSAT_MK7 = VolkswagenPQPlatformConfig(
|
|
[VWCarDocs("Volkswagen Passat 2.0 TDI 2014")],
|
|
VolkswagenCarSpecs(mass=1836, wheelbase=2.70, steerRatio=13.0, minSteerSpeed=31 * CV.KPH_TO_MS),
|
|
chassis_codes={"3C"},
|
|
wmis={WMI.VOLKSWAGEN_EUROPE_CAR},
|
|
model_years={"E"}, # 2014
|
|
)
|
|
VOLKSWAGEN_PASSAT_NMS = VolkswagenPQPlatformConfig(
|
|
[VWCarDocs("Volkswagen Passat NMS 2015-17")],
|
|
VolkswagenCarSpecs(mass=1503, wheelbase=2.80),
|
|
chassis_codes={"A3"},
|
|
wmis={WMI.VOLKSWAGEN_USA_CAR},
|
|
# NMS and NMS+ share chassis code A3; disambiguate by model year
|
|
model_years={"F", "G", "H"}, # 2015-2017
|
|
)
|
|
VOLKSWAGEN_PASSAT_NMS_PLUS = VolkswagenPQPlatformConfig(
|
|
[VWCarDocs("Volkswagen Passat NMS 2018-22")],
|
|
VolkswagenCarSpecs(mass=1503, wheelbase=2.80, minEnableSpeed=20 * CV.KPH_TO_MS),
|
|
chassis_codes={"A3"},
|
|
wmis={WMI.VOLKSWAGEN_USA_CAR},
|
|
model_years={"J", "K", "L", "M", "N"}, # 2018-2022
|
|
)
|
|
VOLKSWAGEN_PASSAT_B7 = VolkswagenPQPlatformConfig(
|
|
[VWCarDocs("Volkswagen Passat B7 2008-2011")],
|
|
VolkswagenCarSpecs(mass=1503, wheelbase=2.712, steerRatio=16.4, minSteerSpeed=0),
|
|
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"),
|
|
],
|
|
VolkswagenCarSpecs(mass=1639, wheelbase=2.92),
|
|
chassis_codes={"7N"},
|
|
wmis={WMI.VOLKSWAGEN_EUROPE_CAR},
|
|
)
|
|
SEAT_ALHAMBRA_MK1 = VolkswagenPQPlatformConfig(
|
|
[
|
|
VWCarDocs("SEAT Alhambra 2018-20"),
|
|
],
|
|
VolkswagenCarSpecs(mass=1639, wheelbase=2.92, minSteerSpeed=50 * CV.KPH_TO_MS),
|
|
chassis_codes={"7N"},
|
|
wmis={WMI.SEAT},
|
|
)
|
|
VOLKSWAGEN_TAOS_MK1 = VolkswagenMQBPlatformConfig(
|
|
[VWCarDocs("Volkswagen Taos 2022-24")],
|
|
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-24"),
|
|
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-24")],
|
|
VolkswagenCarSpecs(mass=1623, wheelbase=2.68),
|
|
chassis_codes={"8U", "F3", "FS"},
|
|
wmis={WMI.AUDI_EUROPE_MPV, WMI.AUDI_GERMANY_CAR},
|
|
)
|
|
AUDI_Q4_MK1 = VolkswagenMEBPlatformConfig(
|
|
[VWCarDocs("Audi Q4 2021-23")],
|
|
VolkswagenCarSpecs(mass=1965, wheelbase=2.764),
|
|
chassis_codes={"FZ"},
|
|
wmis={WMI.AUDI_EUROPE_MPV},
|
|
model_years={"M", "N", "P"},
|
|
)
|
|
AUDI_Q4_MK2 = VolkswagenMEBPlatformConfig(
|
|
[VWCarDocs("Audi Q4 2024-25")],
|
|
VolkswagenCarSpecs(mass=1965, wheelbase=2.764),
|
|
chassis_codes={"FZ"},
|
|
wmis={WMI.AUDI_EUROPE_MPV},
|
|
model_years={"R", "S"},
|
|
flags=VolkswagenFlags.MEB_GEN2,
|
|
)
|
|
AUDI_Q5_MK1 = VolkswagenMLBPlatformConfig(
|
|
[VWCarDocs("Audi Q5 2013-17")],
|
|
VolkswagenCarSpecs(mass=1895, wheelbase=2.81),
|
|
chassis_codes={"8R"},
|
|
wmis={WMI.AUDI_EUROPE_MPV, WMI.AUDI_GERMANY_CAR},
|
|
)
|
|
PORSCHE_MACAN_MK1 = VolkswagenMLBPlatformConfig(
|
|
[VWCarDocs("Porsche Macan 2017-24")],
|
|
VolkswagenCarSpecs(mass=1895, wheelbase=2.81, steerRatio=16.2),
|
|
chassis_codes={"95", "A5"},
|
|
wmis={WMI.PORSCHE_SUV},
|
|
)
|
|
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},
|
|
)
|
|
SEAT_LEON_MK4 = VolkswagenMQBevoPlatformConfig(
|
|
[VWCarDocs("SEAT Leon 2020-25")],
|
|
VolkswagenCarSpecs(mass=1300, wheelbase=2.685),
|
|
chassis_codes={"KL"},
|
|
wmis={WMI.SEAT},
|
|
)
|
|
CUPRA_BORN_MK1 = VolkswagenMEBPlatformConfig(
|
|
[VWCarDocs("CUPRA Born 2022-23")],
|
|
VolkswagenCarSpecs(mass=1950, wheelbase=2.766, steerRatio=15.9),
|
|
chassis_codes={"K1"},
|
|
model_years={"N", "P"},
|
|
wmis={WMI.SEAT},
|
|
)
|
|
SKODA_ENYAQ_MK1 = VolkswagenMEBPlatformConfig(
|
|
[VWCarDocs("Škoda Enyaq 2021-23")],
|
|
VolkswagenCarSpecs(mass=1965, wheelbase=2.77),
|
|
chassis_codes={"NY"},
|
|
model_years={"M", "N", "P"},
|
|
wmis={WMI.SKODA},
|
|
)
|
|
SKODA_ENYAQ_MK2 = VolkswagenMEBPlatformConfig(
|
|
[VWCarDocs("Škoda Enyaq 2024-25")],
|
|
VolkswagenCarSpecs(mass=1965, wheelbase=2.77),
|
|
chassis_codes={"NY"},
|
|
model_years={"R", "S"},
|
|
wmis={WMI.SKODA},
|
|
flags=VolkswagenFlags.MEB_GEN2,
|
|
)
|
|
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)
|
|
vin_wmi = vin_obj.wmi if len(vin_obj.wmi) == 3 else None
|
|
chassis_code = vin_obj.vds[3:5] if len(vin_obj.vds) >= 5 else None
|
|
model_year_code = vin_obj.vis[0] if len(vin_obj.vis) > 0 else None
|
|
vin_available = vin_wmi is not None and chassis_code is not None
|
|
|
|
fallback_ecus = CHECK_FUZZY_ECUS | {Ecu.fwdCamera, Ecu.adas, Ecu.cornerRadar, Ecu.parkingAdas}
|
|
fallback_optional_ecus = fallback_ecus - CHECK_FUZZY_ECUS
|
|
|
|
for platform in CAR:
|
|
if not vin_available:
|
|
matched_ecus = set()
|
|
optional_ecu_seen = False
|
|
for ecu, versions in offline_fw_versions[platform].items():
|
|
if ecu[0] not in fallback_ecus:
|
|
continue
|
|
|
|
found_versions = live_fw_versions.get(ecu[1:], [])
|
|
if len(found_versions) == 0:
|
|
continue
|
|
|
|
if ecu[0] in fallback_optional_ecus:
|
|
optional_ecu_seen = True
|
|
|
|
if any(found_version in versions for found_version in found_versions):
|
|
matched_ecus.add(ecu[0])
|
|
else:
|
|
matched_ecus = set()
|
|
break
|
|
|
|
if Ecu.fwdRadar in matched_ecus and (len(matched_ecus) >= 2 or not optional_ecu_seen):
|
|
candidates.add(platform)
|
|
continue
|
|
|
|
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
|
|
|
|
model_years = getattr(platform.config, "model_years", set())
|
|
if vin_wmi in platform.config.wmis and chassis_code in platform.config.chassis_codes:
|
|
if len(model_years) > 0 and model_year_code is not None and model_year_code not in model_years:
|
|
continue
|
|
candidates.add(platform)
|
|
|
|
return {str(c) for c in candidates}
|
|
|
|
|
|
# These ECUs are required to match to gain a VIN match
|
|
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
|
|
VOLKSWAGEN_RX_OFFSET_CANFD = 0x20000
|
|
|
|
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, Ecu.parkingAdas, Ecu.cornerRadar, Ecu.adas],
|
|
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,
|
|
),
|
|
Request(
|
|
[VOLKSWAGEN_VERSION_REQUEST_MULTI],
|
|
[VOLKSWAGEN_VERSION_RESPONSE],
|
|
whitelist_ecus=[Ecu.engine, Ecu.inverter],
|
|
rx_offset=VOLKSWAGEN_RX_OFFSET_CANFD,
|
|
bus=bus,
|
|
obd_multiplexing=obd_multiplexing,
|
|
),
|
|
]],
|
|
non_essential_ecus={Ecu.eps: list(CAR)},
|
|
extra_ecus=[(Ecu.fwdCamera, 0x74f, None),
|
|
(Ecu.parkingAdas, 0x70a, None),
|
|
(Ecu.cornerRadar, 0x74e, None),
|
|
(Ecu.adas, 0x769, None)],
|
|
match_fw_to_car_fuzzy=match_fw_to_car_fuzzy,
|
|
)
|
|
|
|
MQB_A0_CARS = {
|
|
CAR.VOLKSWAGEN_POLO_MK6,
|
|
CAR.VOLKSWAGEN_TCROSS_MK1,
|
|
CAR.SKODA_FABIA_MK4,
|
|
CAR.SKODA_KAMIQ_MK1,
|
|
}
|
|
|
|
|
|
def get_longitudinal_stopping_speed_override(candidate: CAR, flags: int) -> float:
|
|
if candidate == CAR.VOLKSWAGEN_PASSAT_B7:
|
|
return PASSAT_B7_STOPPING_SPEED
|
|
if flags & VolkswagenFlags.PQ:
|
|
return PQ_STOPPING_SPEED
|
|
return 0.0
|
|
|
|
|
|
def apply_pq_stopping_accel(candidate: CAR, accel: float, stopping: bool) -> float:
|
|
return PASSAT_B7_STOP_ACCEL if candidate == CAR.VOLKSWAGEN_PASSAT_B7 and stopping else accel
|
|
|
|
|
|
DBC = CAR.create_dbc_map()
|