Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
e3935c0
move can ignition to opendbc
elkoled May 1, 2026
05ba983
volkswagen: port ID.4 MK1 (MEB, lateral only)
dkoep May 11, 2026
2eca7f4
volkswagen: ID.4 MK1 ui torque bar + steering limit tests
elkoled May 12, 2026
81bea42
volkswagen: ID.4 MK1 use angle control framework
elkoled May 12, 2026
209ba12
volkswagen: ID.4 MK1 fixes from elkoled/2 reference
elkoled May 12, 2026
195867a
volkswagen: ID.4 MK1 add bsm
elkoled May 12, 2026
e8e32bd
volkswagen: ID.4 MK1 align angle ctrl with safety
elkoled May 12, 2026
98af8f2
volkswagen: ID.4 MK1 simplify
elkoled May 12, 2026
505db36
volkswagen: ID.4 MK1 torque override
elkoled May 12, 2026
cc47e66
volkswagen: ID.4 MK1 torque from sunnypilot
elkoled May 12, 2026
9d86a6c
VW MEB: do not enforce timeout on MEB_Side_Assist_01
elkoled May 12, 2026
4e3dce7
sideassist
elkoled May 12, 2026
336f7a6
VW MEB: ignore_alive for MEB_Side_Assist_01
elkoled May 12, 2026
2e3a9ef
VW MEB: parkingBrake from Gateway_73.EPB_Status, align to elkoled/10
elkoled May 12, 2026
c879aac
VW MEB: align curvature/steeringPressed with elkoled/2
elkoled May 12, 2026
7a98cd4
volkswagen: ID.4 MK1 restore safety-aligned rate limit, fix BSM bus
elkoled May 12, 2026
26ffec8
volkswagen: ID.4 MK1 trim comments
elkoled May 12, 2026
ceff1de
volkswagen: ID.4 MK1 emergency assist intervention
elkoled May 12, 2026
c21fa21
volkswagen: ID.4 MK1 match curvature stack rate limits and wind-down
elkoled May 12, 2026
b8fdba9
volkswagen: ID.4 MK1 paste curvature math from deprecated stack
elkoled May 12, 2026
1e30578
volkswagen: STOCK_HCA_PRESENT scoped to non-MEB branch
elkoled May 12, 2026
65f6198
volkswagen: align carcontroller structure with sunnypilot
elkoled May 12, 2026
ebbdabd
volkswagen: ID.4 MK1 align MEB parser with sunnypilot/upstream
elkoled May 12, 2026
9972ea8
volkswagen: ID.4 MK1 docs entry
elkoled May 12, 2026
e7deba9
volkswagen: ID.4 MK1 docs ID.4 only
elkoled May 12, 2026
626505c
volkswagen: ID.4 MK1 tighten safety rate to ISO jerk envelope
elkoled May 12, 2026
75221b4
volkswagen: ID.4 MK1 align lateral with safety via vm
elkoled May 12, 2026
d8c228c
volkswagen: ID.4 MK1 revert VM-helper lateral, restore std curvature …
elkoled May 12, 2026
787af0a
VW MEB: add KLR_01 checksum magic
elkoled May 12, 2026
e0a2f8a
Revert "volkswagen: ID.4 MK1 revert VM-helper lateral, restore std cu…
elkoled May 12, 2026
12f7b66
VW MEB: gate HCA fault on drive mode
elkoled May 12, 2026
492dc1e
VW MEB: gate FAULT perm path on drive mode
elkoled May 12, 2026
9a859bf
VW MEB: gate accFaulted on drive mode
elkoled May 12, 2026
0c4c3f1
add debounce
elkoled May 12, 2026
dd0839f
VW MEB: replicate sunnypilot accFault helper, revert perm gate
elkoled May 12, 2026
d9c08b0
VW MEB: stop sending KLR_01 to test EPS FAULT cause
elkoled May 12, 2026
312da35
VW MEB: revert curvature rate-limit to branch 7 std (was VM-stub)
elkoled May 13, 2026
18541c6
VW MEB: do not treat HCA DISABLED as permanent fault
elkoled May 13, 2026
9822c50
more tests
elkoled May 13, 2026
d2abb4a
init counters for deterministic test
elkoled May 13, 2026
9f521b9
Revert "init counters for deterministic test"
elkoled May 13, 2026
fa79024
rm
elkoled May 13, 2026
ede75b1
Revert "VW MEB: stop sending KLR_01 to test EPS FAULT cause"
elkoled May 13, 2026
adff1d9
use getter/setter
elkoled May 13, 2026
b825183
clean tests
elkoled May 13, 2026
89f3f77
fix test
elkoled May 13, 2026
a97e724
volkswagen: ID.4 MK1 align lateral with safety via vm
elkoled May 12, 2026
b1144b5
add loop
elkoled May 13, 2026
97615d3
VW MEB: debounce steeringPressed to stop torque bar flicker
elkoled May 13, 2026
c78ebcf
split into brand tests
elkoled May 13, 2026
5495bcd
add vw meb can ignition
elkoled May 13, 2026
491b0e6
smol
elkoled May 13, 2026
659ea06
add meb can ignition
elkoled May 13, 2026
36a3aff
acc_btn_control
elkoled May 13, 2026
8321eb7
add sound alert
elkoled May 13, 2026
5ce144f
VW MEB: port create_eps_update + init apply_torque for STOCK_HCA_PRESENT
elkoled May 13, 2026
77cfa93
add hca comment
elkoled May 13, 2026
a095dc1
VW MEB: init klr_counter_last + read EA_01 for carFaultedNonCritical
elkoled May 13, 2026
11d32f0
move meb
elkoled May 13, 2026
4fa985e
VW MEB: restore ALT_GEAR detection and gear source branching
elkoled May 13, 2026
ba3084a
Revert "volkswagen: ID.4 MK1 align lateral with safety via vm"
elkoled May 13, 2026
d0a4b8b
VW MEB: revert safety to non-VM rate-lookup envelope
elkoled May 13, 2026
e0105f6
VW MEB: safety rate-lookup with 2x margin over python ISO jerk envelope
elkoled May 13, 2026
89453e0
VW MEB: simplify safety to absolute curvature cap only
elkoled May 13, 2026
fd86d72
VW MEB: add ISO lateral accel cap to safety (middle ground, no rate c…
elkoled May 13, 2026
156b427
raise steer driver allowance to 100
elkoled May 13, 2026
cc721b8
steer driver allowance = 80
elkoled May 13, 2026
2635c6d
Revert "steer driver allowance = 80"
elkoled May 13, 2026
d84d732
speed
elkoled May 14, 2026
253ad15
aeb prevention
elkoled May 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/CARS.md
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@
|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Grand California 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|ID.4 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|Volkswagen|Jetta 2015-18|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|Volkswagen|Jetta 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Jetta GLI 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
Expand Down
48 changes: 47 additions & 1 deletion opendbc/car/lateral.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import math
import numpy as np
from dataclasses import dataclass
from opendbc.car import structs, rate_limit, DT_CTRL
from opendbc.car import structs, rate_limit, DT_CTRL, ACCELERATION_DUE_TO_GRAVITY
from opendbc.car.vehicle_model import VehicleModel
from typing import Tuple

FRICTION_THRESHOLD = 0.2

# ISO 11270
ISO_LATERAL_ACCEL = 3.0 # m/s^2
ISO_LATERAL_JERK = 5.0 # m/s^3
# Add extra tolerance for average banked road since safety doesn't have the roll
AVERAGE_ROAD_ROLL = 0.06 # ~3.4 degrees, 6% superelevation. higher actual roll lowers lateral acceleration
MAX_LATERAL_ACCEL = ISO_LATERAL_ACCEL + (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL) # ~3.6 m/s^2


@dataclass
class CurvatureSteeringLimits:
CURVATURE_MAX: float


@dataclass
Expand Down Expand Up @@ -128,6 +137,43 @@ def apply_steer_angle_limits_vm(apply_angle: float, apply_angle_last: float, v_e
return float(np.clip(new_apply_angle, -limits.ANGLE_LIMITS.STEER_ANGLE_MAX, limits.ANGLE_LIMITS.STEER_ANGLE_MAX))


def get_max_curvature_jerk(v_ego: float, steer_step: int) -> float:
# ISO 11270
# Lateral jerk
ts_elapsed = steer_step * DT_CTRL
curvature_rate_limit = ISO_LATERAL_JERK / (max(v_ego, 1.0) ** 2)
max_jerk = curvature_rate_limit * ts_elapsed
return max_jerk


def get_max_curvature_average(v_ego: float) -> Tuple[float, float]:
max_curvature = MAX_LATERAL_ACCEL / (max(v_ego, 1.0) ** 2)
return -max_curvature, max_curvature


def apply_std_curvature_limits(apply_curvature: float, apply_curvature_last: float, v_ego: float, curvature: float, override: bool,
steer_step: int, lat_active: bool, limits: CurvatureSteeringLimits) -> float:

new_apply_curvature = apply_curvature

# Lateral jerk
max_jerk = get_max_curvature_jerk(v_ego, steer_step)
curvature_up = apply_curvature_last + max_jerk
curvature_down = apply_curvature_last - max_jerk

new_apply_curvature = float(np.clip(new_apply_curvature, curvature_down, curvature_up))

# Lateral acceleration (average road roll tolerance for safety)
min_curvature, max_curvature = get_max_curvature_average(v_ego)
new_apply_curvature = float(np.clip(new_apply_curvature, min_curvature, max_curvature))

# set output curvature as current curvature (if otherwise set to 0 in car controller)
if not lat_active:
new_apply_curvature = curvature

return float(np.clip(new_apply_curvature, -limits.CURVATURE_MAX, limits.CURVATURE_MAX))


def common_fault_avoidance(fault_condition: bool, request: bool, above_limit_frames: int,
max_above_limit_frames: int, max_mismatching_frames: int = 1):
"""
Expand Down
1 change: 1 addition & 0 deletions opendbc/car/tests/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ class CarTestRoute(NamedTuple):
CarTestRoute("6719965b0e1d1737/2023-08-29--06-40-05", TOYOTA.TOYOTA_CHR_TSS2), # hybrid, openpilot longitudinal, radar disabled
CarTestRoute("14623aae37e549f3/2021-10-24--01-20-49", TOYOTA.TOYOTA_PRIUS_V),

CarTestRoute("aebd8f1d4ea16066|00000009--b31e222338", VOLKSWAGEN.VOLKSWAGEN_ID4_MK1),
CarTestRoute("202c40641158a6e5/2021-09-21--09-43-24", VOLKSWAGEN.VOLKSWAGEN_ARTEON_MK1),
CarTestRoute("2c68dda277d887ac/2021-05-11--15-22-20", VOLKSWAGEN.VOLKSWAGEN_ATLAS_MK1),
CarTestRoute("ffcd23abbbd02219/2024-02-28--14-59-38", VOLKSWAGEN.VOLKSWAGEN_CADDY_MK3),
Expand Down
1 change: 1 addition & 0 deletions opendbc/car/torque_data/override.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"TESLA_MODEL_X" = [nan, 2.5, nan]

# Guess
"VOLKSWAGEN_ID4_MK1" = [nan, 2.5, nan]
"FORD_BRONCO_SPORT_MK1" = [nan, 1.5, nan]
"FORD_ESCAPE_MK4" = [nan, 1.5, nan]
"FORD_ESCAPE_MK4_5" = [nan, 1.5, nan]
Expand Down
78 changes: 68 additions & 10 deletions opendbc/car/volkswagen/carcontroller.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import numpy as np
from opendbc.can import CANPacker
from opendbc.car import Bus, DT_CTRL, structs
from opendbc.car.lateral import apply_driver_steer_torque_limits
from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_std_curvature_limits
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.interfaces import CarControllerBase
from opendbc.car.volkswagen import mlbcan, mqbcan, pqcan
from opendbc.car.volkswagen import mebcan, mlbcan, mqbcan, pqcan
from opendbc.car.volkswagen.values import CanBus, CarControllerParams, VolkswagenFlags

VisualAlert = structs.CarControl.HUDControl.VisualAlert
Expand Down Expand Up @@ -45,11 +45,16 @@ def __init__(self, dbc_names, CP):
self.CCS = pqcan
elif CP.flags & VolkswagenFlags.MLB:
self.CCS = mlbcan
elif CP.flags & VolkswagenFlags.MEB:
self.CCS = mebcan
else:
self.CCS = mqbcan

self.apply_torque_last = 0
self.apply_curvature_last = 0.
self.steering_power_last = 0
self.gra_acc_counter_last = None
self.klr_counter_last = None
self.hca_mitigation = HCAMitigation(self.CCP)

def update(self, CC, CS, now_nanos):
Expand All @@ -61,14 +66,55 @@ def update(self, CC, CS, now_nanos):

if self.frame % self.CCP.STEER_STEP == 0:
apply_torque = 0
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)

apply_torque = self.hca_mitigation.update(apply_torque, self.apply_torque_last)
hca_enabled = apply_torque != 0
self.apply_torque_last = apply_torque
can_sends.append(self.CCS.create_steering_control(self.packer_pt, self.CAN.pt, apply_torque, hca_enabled))
if self.CP.flags & VolkswagenFlags.MEB:
if CC.latActive:
hca_enabled = True
apply_curvature = actuators.curvature + (CS.curvature_meas - CC.currentCurvature)
apply_curvature = apply_std_curvature_limits(apply_curvature, self.apply_curvature_last, CS.out.vEgoRaw,
CS.curvature_meas, False, self.CCP.STEER_STEP, CC.latActive,
self.CCP.CURVATURE_LIMITS)

min_power = max(self.steering_power_last - self.CCP.STEERING_POWER_STEP, self.CCP.STEERING_POWER_MIN)
max_power = min(self.steering_power_last + self.CCP.STEERING_POWER_STEP, self.CCP.STEERING_POWER_MAX)
target_power_driver = int(np.interp(CS.out.steeringTorque, [self.CCP.STEER_DRIVER_ALLOWANCE, self.CCP.STEER_DRIVER_MAX],
[self.CCP.STEERING_POWER_MAX, self.CCP.STEERING_POWER_MIN]))
target_power = int(np.interp(CS.out.vEgo, [0., 0.5], [self.CCP.STEERING_POWER_MIN, target_power_driver]))
steering_power = min(max(target_power, min_power), max_power)

else:
if self.steering_power_last > 0:
hca_enabled = True
apply_curvature = float(np.clip(CS.curvature_meas,
-self.CCP.CURVATURE_LIMITS.CURVATURE_MAX,
self.CCP.CURVATURE_LIMITS.CURVATURE_MAX))
steering_power = max(self.steering_power_last - self.CCP.STEERING_POWER_STEP, 0)
else:
hca_enabled = False
apply_curvature = 0.
steering_power = 0

can_sends.append(self.CCS.create_steering_control(self.packer_pt, self.CAN.pt, apply_curvature, hca_enabled, steering_power))
self.apply_curvature_last = apply_curvature
self.steering_power_last = steering_power

else:
# 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.
apply_torque = 0
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)

apply_torque = self.hca_mitigation.update(apply_torque, self.apply_torque_last)
hca_enabled = apply_torque != 0
self.apply_torque_last = apply_torque
can_sends.append(self.CCS.create_steering_control(self.packer_pt, self.CAN.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
Expand All @@ -79,6 +125,17 @@ def update(self, CC, CS, now_nanos):
ea_simulated_torque = CS.out.steeringTorque
can_sends.append(self.CCS.create_eps_update(self.packer_pt, self.CAN.cam, CS.eps_stock_values, ea_simulated_torque))

# Emergency Assist intervention
if self.CP.flags & VolkswagenFlags.MEB and self.CP.flags & VolkswagenFlags.STOCK_KLR_PRESENT:
# send capacitive steering wheel touched
# propably EA is stock activated only for cars equipped with capacitive steering wheel
# (also stock long does resume from stop as long as hands on is detected additionally to OP resume spam)
klr_send_ready = CS.klr_stock_values["COUNTER"] != self.klr_counter_last
if klr_send_ready:
can_sends.append(mebcan.create_capacitive_wheel_touch(self.packer_pt, self.CAN.cam, CC.latActive, CS.klr_stock_values))
can_sends.append(mebcan.create_capacitive_wheel_touch(self.packer_pt, self.CAN.pt, CC.latActive, CS.klr_stock_values))
self.klr_counter_last = CS.klr_stock_values["COUNTER"]

# **** Acceleration Controls ******************************************** #

if self.CP.openpilotLongitudinalControl:
Expand Down Expand Up @@ -126,6 +183,7 @@ def update(self, CC, CS, now_nanos):
new_actuators = actuators.as_builder()
new_actuators.torque = self.apply_torque_last / self.CCP.STEER_MAX
new_actuators.torqueOutputCan = self.apply_torque_last
new_actuators.curvature = float(self.apply_curvature_last)

self.gra_acc_counter_last = CS.gra_stock_values["COUNTER"]
self.frame += 1
Expand Down
103 changes: 101 additions & 2 deletions opendbc/car/volkswagen/carstate.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ def __init__(self, CP):
super().__init__(CP)
self.frame = 0
self.eps_init_complete = False
self.cruise_recovery_timer = 0
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
self.acc_type = 0
self.curvature_meas = 0.

def update_button_enable(self, buttonEvents: list[structs.CarState.ButtonEvent]):
if not self.CP.pcmCruise:
Expand Down Expand Up @@ -52,6 +54,8 @@ def update(self, can_parsers) -> structs.CarState:
return self.update_pq(pt_cp, cam_cp, ext_cp)
elif self.CP.flags & VolkswagenFlags.MLB:
return self.update_mlb(pt_cp, cam_cp, ext_cp, alt_cp)
elif self.CP.flags & VolkswagenFlags.MEB:
return self.update_meb(pt_cp, cam_cp, ext_cp)

ret = structs.CarState()

Expand Down Expand Up @@ -126,7 +130,8 @@ def update(self, can_parsers) -> structs.CarState:
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 = ext_cp.vl["MEB_ACC_01"]["ACC_Wunschgeschw_02"] * CV.KPH_TO_MS
if ret.cruiseState.speed > 90: # 255 kph in m/s == no current setpoint
ret.cruiseState.speed = 0

self.eps_stock_values = pt_cp.vl["LH_EPS_03"]
Expand All @@ -152,7 +157,7 @@ def update_pq(self, pt_cp, cam_cp, ext_cp) -> structs.CarState:
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"]["LW1_Lenk_Gesch"] * (1, -1)[int(pt_cp.vl["Lenkwinkel_1"]["LW1_Gesch_Sign"])]
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.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > self.CCP.STEER_DRIVER_ALLOWANCE, 5)
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)

Expand Down Expand Up @@ -231,6 +236,69 @@ def update_pq(self, pt_cp, cam_cp, ext_cp) -> structs.CarState:
self.frame += 1
return ret

def update_meb(self, pt_cp, cam_cp, ext_cp) -> structs.CarState:
ret = structs.CarState()

self.parse_wheel_speeds(ret,
pt_cp.vl["ESC_51"]["VL_Radgeschw"],
pt_cp.vl["ESC_51"]["VR_Radgeschw"],
pt_cp.vl["ESC_51"]["HL_Radgeschw"],
pt_cp.vl["ESC_51"]["HR_Radgeschw"],
)
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["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 = self.update_steering_pressed(abs(ret.steeringTorque) > self.CCP.STEER_DRIVER_ALLOWANCE, 5)
self.curvature_meas = -pt_cp.vl["QFK_01"]["Curvature"] * (1, -1)[int(pt_cp.vl["QFK_01"]["Curvature_VZ"])]

if self.CP.flags & VolkswagenFlags.ALT_GEAR:
ret.gearShifter = self.parse_gear_shifter(self.CCP.shifter_values.get(pt_cp.vl["Gateway_73"]["GE_Fahrstufe"], None))
else:
ret.gearShifter = self.parse_gear_shifter(self.CCP.shifter_values.get(pt_cp.vl["Getriebe_11"]["GE_Fahrstufe"], None))
drive_mode = ret.gearShifter == GearShifter.drive

hca_status = self.CCP.hca_status_values.get(pt_cp.vl["QFK_01"]["LatCon_HCA_Status"])
ret.steerFaultTemporary, ret.steerFaultPermanent = self.update_hca_state(hca_status, drive_mode)

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

ret.gasPressed = pt_cp.vl["Motor_51"]["Accel_Pedal_Pressure"] > 0
ret.brakePressed = bool(pt_cp.vl["Motor_14"]["MO_Fahrer_bremst"])
ret.parkingBrake = pt_cp.vl["Gateway_73"]["EPB_Status"] in (1, 4)
ret.seatbeltUnlatched = pt_cp.vl["Airbag_02"]["AB_Gurtschloss_FA"] != 3
ret.espDisabled = bool(pt_cp.vl["ESP_21"]["ESP_Tastung_passiv"])

ret.cruiseState.available = pt_cp.vl["Motor_51"]["TSK_Status"] in (2, 3, 4, 5)
ret.cruiseState.enabled = pt_cp.vl["Motor_51"]["TSK_Status"] in (3, 4, 5)
ret.cruiseState.standstill = self.CP.pcmCruise and self.esp_hold_confirmation
accFaulted = pt_cp.vl["Motor_51"]["TSK_Status"] in (6, 7)
ret.accFaulted = self.update_acc_fault(accFaulted, parking_brake=ret.parkingBrake, drive_mode=drive_mode)

ret.leftBlinker = bool(pt_cp.vl["Blinkmodi_02"]["BM_links"])
ret.rightBlinker = bool(pt_cp.vl["Blinkmodi_02"]["BM_rechts"])

if self.CP.enableBsm:
ret.leftBlindspot = (bool(ext_cp.vl["MEB_Side_Assist_01"]["Blind_Spot_Info_Left"]) or
bool(ext_cp.vl["MEB_Side_Assist_01"]["Blind_Spot_Warn_Left"]))
ret.rightBlindspot = (bool(ext_cp.vl["MEB_Side_Assist_01"]["Blind_Spot_Info_Right"]) or
bool(ext_cp.vl["MEB_Side_Assist_01"]["Blind_Spot_Warn_Right"]))


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"]
self.klr_stock_values = pt_cp.vl["KLR_01"] if self.CP.flags & VolkswagenFlags.STOCK_KLR_PRESENT else {}

ret.buttonEvents = self.create_button_events(pt_cp, self.CCP.BUTTONS)
ret.lowSpeedAlert = self.update_low_speed_alert(ret.vEgo)

self.frame += 1
return ret

def update_mlb(self, pt_cp, cam_cp, ext_cp, alt_cp) -> structs.CarState:
ret = structs.CarState()

Expand Down Expand Up @@ -299,6 +367,7 @@ def parse_mlb_mqb_steering_state(self, ret, pt_cp, drive_mode=True):
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.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > self.CCP.STEER_DRIVER_ALLOWANCE, 5)

hca_status = self.CCP.hca_status_values.get(pt_cp.vl["LH_EPS_03"]["EPS_HCA_Status"])
ret.steerFaultTemporary, ret.steerFaultPermanent = self.update_hca_state(hca_status, drive_mode)
Expand All @@ -312,10 +381,24 @@ def update_hca_state(self, hca_status, drive_mode=True):
temp_fault = drive_mode and hca_status in ("REJECTED", "PREEMPTED") or not self.eps_init_complete
return temp_fault, perm_fault

def update_acc_fault(self, acc_fault, parking_brake=False, drive_mode=True, recovery_frames_max=100):
# Ignore FAULT when not in drive mode and parked
# do not show misleading error during ignition in parked state
# grant a short time to recover a normal cruise state
fault = acc_fault
if parking_brake and not drive_mode:
fault = False
self.cruise_recovery_timer = self.frame
elif self.frame - self.cruise_recovery_timer < recovery_frames_max:
fault = False
return fault

@staticmethod
def get_can_parsers(CP):
if CP.flags & VolkswagenFlags.PQ:
return CarState.get_can_parsers_pq(CP)
elif CP.flags & VolkswagenFlags.MEB:
return CarState.get_can_parsers_meb(CP)

# manually configure some optional and variable-rate/edge-triggered messages
pt_messages, cam_messages, alt_messages = [], [], []
Expand All @@ -342,3 +425,19 @@ def get_can_parsers_pq(CP):
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).cam),
Bus.alt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).alt),
}

@staticmethod
def get_can_parsers_meb(CP):
pt_messages = [
("Blinkmodi_02", 1), # From J519 BCM (sent at 1Hz when no lights active, 50Hz when active)
("Gateway_73", 10), # Gear position (ALT_GEAR) and EPB status
]
cam_messages = []
if CP.enableBsm:
bsm_target = cam_messages if CP.networkLocation == NetworkLocation.gateway else pt_messages
bsm_target.append(("MEB_Side_Assist_01", float('nan')))
return {
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], pt_messages, CanBus(CP).pt),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], cam_messages, CanBus(CP).cam),
Bus.alt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).alt),
}
Loading
Loading