new support

This commit is contained in:
firestar5683
2026-09-13 16:24:10 -05:00
parent 3506c2561b
commit 151b07462c
11 changed files with 190 additions and 13 deletions
@@ -212,6 +212,7 @@ FINGERPRINTS.update({
CAR.CADILLAC_ESCALADE_ASCM: FINGERPRINTS[CAR.CADILLAC_ESCALADE],
CAR.CADILLAC_ESCALADE_ESV_2019_ASCM: [{**fp, SASCM_ADDRESS: 8} for fp in FINGERPRINTS[CAR.CADILLAC_ESCALADE_ESV_2019]],
CAR.CHEVROLET_SUBURBAN: FINGERPRINTS[CAR.CHEVROLET_SUBURBAN_CC],
CAR.CHEVROLET_SUBURBAN_ASCM: FINGERPRINTS[CAR.CHEVROLET_SUBURBAN_CC],
CAR.GMC_YUKON_CC: FINGERPRINTS[CAR.GMC_YUKON],
CAR.CADILLAC_XT6: FINGERPRINTS[CAR.CHEVROLET_TRAVERSE],
CAR.CADILLAC_XT5: FINGERPRINTS[CAR.CHEVROLET_TRAVERSE],
+1 -1
View File
@@ -522,7 +522,7 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate in (CAR.CHEVROLET_SUBURBAN, CAR.CHEVROLET_SUBURBAN_CC):
elif candidate in (CAR.CHEVROLET_SUBURBAN, CAR.CHEVROLET_SUBURBAN_ASCM, CAR.CHEVROLET_SUBURBAN_CC):
ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
@@ -268,6 +268,46 @@ class TestGMCarState:
class TestGMInterface:
def test_suburban_obd_and_ascm_integrations_remain_separate(self):
fingerprint = _empty_fingerprint()
fingerprint[0] = FINGERPRINTS[CAR.CHEVROLET_SUBURBAN][0].copy()
assert CAR.CHEVROLET_SUBURBAN_ASCM in ASCM_INT
assert FINGERPRINTS[CAR.CHEVROLET_SUBURBAN_ASCM] == FINGERPRINTS[CAR.CHEVROLET_SUBURBAN]
obd_params = interfaces[CAR.CHEVROLET_SUBURBAN].get_params(
CAR.CHEVROLET_SUBURBAN,
fingerprint,
[],
alpha_long=False,
is_release=False,
docs=False,
starpilot_toggles=_test_starpilot_toggles(),
)
ascm_params = interfaces[CAR.CHEVROLET_SUBURBAN_ASCM].get_params(
CAR.CHEVROLET_SUBURBAN_ASCM,
fingerprint,
[],
alpha_long=True,
is_release=False,
docs=False,
starpilot_toggles=_test_starpilot_toggles(),
)
assert obd_params.networkLocation == structs.CarParams.NetworkLocation.gateway
assert obd_params.openpilotLongitudinalControl
assert not obd_params.pcmCruise
assert obd_params.safetyConfigs[0].safetyParam == 0
assert ascm_params.networkLocation == structs.CarParams.NetworkLocation.fwdCamera
assert not ascm_params.flags & GMFlags.SASCM.value
assert not ascm_params.alphaLongitudinalAvailable
assert not ascm_params.openpilotLongitudinalControl
assert ascm_params.pcmCruise
assert ascm_params.safetyConfigs[0].safetyParam == GMSafetyFlags.HW_CAM.value | GMSafetyFlags.HW_ASCM_INT.value
assert ascm_params.lateralTuning.torque.latAccelFactor == pytest.approx(obd_params.lateralTuning.torque.latAccelFactor)
assert ascm_params.lateralTuning.torque.friction == pytest.approx(obd_params.lateralTuning.torque.friction)
def test_lacrosse_obd_and_ascm_integrations_remain_separate(self):
obd_params = interfaces[CAR.BUICK_LACROSSE].get_params(
CAR.BUICK_LACROSSE,
+6 -1
View File
@@ -357,6 +357,10 @@ class CAR(Platforms):
[GMCarDocs("Chevrolet Suburban Premier 2016-20")],
CarSpecs(mass=2731, wheelbase=3.302, steerRatio=17.3, centerToFrontRatio=0.49),
)
CHEVROLET_SUBURBAN_ASCM = GMPlatformConfig(
[GMCarDocs("Chevrolet Suburban Premier ASCM Harness 2016-20", "Adaptive Cruise Control (ACC) & LKAS")],
CHEVROLET_SUBURBAN.specs,
)
GMC_YUKON_CC = GMPlatformConfig(
[GMCarDocs("GMC Yukon No-ACC 2019-20")],
CarSpecs(mass=2541, wheelbase=2.95, steerRatio=16.3, centerToFrontRatio=0.4),
@@ -593,9 +597,10 @@ CC_REGEN_PADDLE_CAR = {
}
CAMERA_ACC_CAR.update(CC_ONLY_CAR)
# ASCM-INT paths are only enabled when SASCM (0x2FF) is detected at runtime
# ASCM-intercept variants preserve stock ACC. SASCM (0x2FF) enables alpha-long where supported.
ASCM_INT = {
CAR.CHEVROLET_VOLT_ASCM,
CAR.CHEVROLET_SUBURBAN_ASCM,
CAR.GMC_ACADIA_ASCM,
CAR.CHEVROLET_MALIBU_ASCM,
CAR.CADILLAC_ESCALADE_ASCM,
@@ -10,6 +10,7 @@ from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_steer_an
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.hyundai import hyundaicanfd, hyundaican
from opendbc.car.hyundai.hyundaicanfd import CanBus
from opendbc.car.hyundai.lead_data import CanLeadDataState
from opendbc.car.hyundai.values import HyundaiFlags, HyundaiSafetyFlags, HyundaiStarPilotFlags, Buttons, CarControllerParams, CAR, CANFD_ANGLE_LONGITUDINAL_CAR, \
CANFD_RADAR_ECU_KEEPALIVE_CAR, CANFD_ALT_BUTTONS_RESUME_CAR, kia_ev6_gt_line_longitudinal_tuning, \
KIA_EV6_GT_LINE_LONG_TUNING_TESTING_GROUND_ID
@@ -474,6 +475,7 @@ class CarController(CarControllerBase):
self._ioniq_6_lane_change_ui_frames = 0
self._ioniq_6_long_tuning = Ioniq6LongitudinalTuningState()
self._genesis_g90_long_tuning = GenesisG90LongitudinalTuningState()
self._can_lead_data = CanLeadDataState()
self._dash_lat_disengage_blink_frame = 0
self._dash_lat_disengage_init = False
self._dash_prev_lat_active = False
@@ -503,7 +505,9 @@ class CarController(CarControllerBase):
return lka_icon, lfa_icon
def _get_canfd_scc_lead_state(self, CC, CS, now_nanos):
openpilot_lead_visible = bool(getattr(CS, "openpilot_lead_visible", False) or CC.hudControl.leadVisible)
openpilot_lead_visible = bool(
getattr(CS, "openpilot_lead_visible", False) or getattr(CC.hudControl, "leadVisible", False)
)
openpilot_lead_distance = float(np.clip(getattr(CS, "openpilot_lead_distance", 0.0), 0.0, 204.7))
openpilot_lead_rel_speed = float(np.clip(getattr(CS, "openpilot_lead_rel_speed", 0.0), -16.4, 34.7))
stock_camera_lead_fresh = now_nanos - getattr(CS, "stock_camera_lead_ts", 0) <= CANFD_CAMERA_LEAD_STALE_NS
@@ -757,6 +761,13 @@ class CarController(CarControllerBase):
can_canfd_blended = bool(self.CP.flags & HyundaiFlags.CAN_CANFD_BLENDED)
blended_hda2 = can_canfd_blended and bool(self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING)
longitudinal_active = bool(self.long_active_ecu and getattr(CC, "longActive", False))
lead_visible = bool(getattr(CS, "openpilot_lead_visible", False) or getattr(hud_control, "leadVisible", False))
lead_distance = float(np.clip(getattr(CS, "openpilot_lead_distance", 0.0), 0.0, 204.7))
lead_rel_speed = float(np.clip(getattr(CS, "openpilot_lead_rel_speed", 0.0), -170.0, 239.5))
if lead_visible and lead_distance <= CANFD_LEAD_MIN_DISTANCE:
lead_distance = CANFD_FALLBACK_LEAD_DISTANCE
lead_rel_speed = 0.0
lead_data = self._can_lead_data.update(lead_distance, lead_rel_speed, lead_visible)
# HUD messages
sys_warning, sys_state, left_lane_warning, right_lane_warning = process_hud_alert(CC.enabled, self.car_fingerprint,
@@ -836,7 +847,7 @@ class CarController(CarControllerBase):
can_sends.extend(hyundaican.create_acc_commands(self.packer, CC.enabled, accel, jerk, int(self.frame / 2),
hud_control, set_speed_in_units, stopping,
CC.cruiseControl.override, use_fca, self.CP,
main_cruise_enabled))
main_cruise_enabled, lead_data))
# 20 Hz LFA MFA message
if self.frame % 5 == 0 and (self.CP.flags & HyundaiFlags.SEND_LFA.value or (self.long_active_ecu and blended_hda2)):
@@ -1022,14 +1033,17 @@ class CarController(CarControllerBase):
CC.leftBlinker,
CC.rightBlinker))
if self.frame % 2 == 0:
lead_visible, lead_distance, lead_rel_speed = self._get_canfd_scc_lead_state(CC, CS, now_nanos)
if self.CP.carFingerprint == CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN:
scc_jerk_limits = get_hyundai_canfd_scc_jerk_limits(self.CP)
acc_kwargs = {
"jerk_upper": scc_jerk_limits[0],
"jerk_lower": scc_jerk_limits[1],
"lead_distance": lead_distance,
"lead_rel_speed": lead_rel_speed,
"lead_visible": lead_visible,
}
else:
lead_visible, lead_distance, lead_rel_speed = self._get_canfd_scc_lead_state(CC, CS, now_nanos)
acc_kwargs = {
"main_mode_acc": int(CS.out.cruiseState.available),
"direct_accel": True,
@@ -1,5 +1,6 @@
import crcmod
from opendbc.car.hyundai.hyundaicanfd import CanBus
from opendbc.car.hyundai.lead_data import CanLeadData
from opendbc.car.hyundai.values import CAR, HyundaiFlags
hyundai_checksum = crcmod.mkCrcFun(0x11D, initCrc=0xFD, rev=False, xorOut=0xdf)
@@ -317,19 +318,20 @@ def create_acc_commands_can_canfd_blended_hda2(packer, enabled, accel, accel_las
def create_acc_commands(packer, enabled, accel, upper_jerk, idx, hud_control, set_speed, stopping, long_override, use_fca, CP,
main_cruise_enabled=True):
main_cruise_enabled=True, lead_data: CanLeadData | None = None):
commands = []
lead_data = lead_data or CanLeadData()
scc11_values = {
"MainMode_ACC": int(bool(main_cruise_enabled)),
"TauGapSet": hud_control.leadDistanceBars,
"VSetDis": set_speed if enabled else 0,
"AliveCounterACC": idx % 0x10,
"ObjValid": 1, # close lead makes controls tighter
"ACC_ObjStatus": 1, # close lead makes controls tighter
"ObjValid": int(lead_data.lead_visible),
"ACC_ObjStatus": int(lead_data.lead_visible),
"ACC_ObjLatPos": 0,
"ACC_ObjRelSpd": 0,
"ACC_ObjDist": 1, # close lead makes controls tighter
"ACC_ObjRelSpd": lead_data.lead_rel_speed,
"ACC_ObjDist": int(lead_data.lead_distance),
}
commands.append(packer.make_can_msg("SCC11", 0, scc11_values))
@@ -357,7 +359,8 @@ def create_acc_commands(packer, enabled, accel, upper_jerk, idx, hud_control, se
"JerkUpperLimit": upper_jerk, # stock usually is 1.0 but sometimes uses higher values
"JerkLowerLimit": 5.0, # stock usually is 0.5 but sometimes uses higher values
"ACCMode": 2 if enabled and long_override else 1 if enabled else 4, # stock will always be 4 instead of 0 after first disengage
"ObjGap": 2 if hud_control.leadVisible else 0, # 5: >30, m, 4: 25-30 m, 3: 20-25 m, 2: < 20 m, 0: no lead
"ObjGap": lead_data.object_gap, # 5: >30 m, 4: 25-30 m, 3: 20-25 m, 2: <20 m, 0: no lead
"ObjDistStat": lead_data.object_rel_gap,
}
commands.append(packer.make_can_msg("SCC14", 0, scc14_values))
@@ -0,0 +1,63 @@
from dataclasses import dataclass
@dataclass(frozen=True)
class CanLeadData:
object_gap: int = 0
lead_distance: float = 0.0
lead_rel_speed: float = 0.0
lead_visible: bool = False
@property
def object_rel_gap(self) -> int:
return 0 if self.lead_distance == 0 else 2 if self.lead_rel_speed < -0.2 else 1
def _hysteresis_update(current, new_value, counter, threshold):
if new_value == current:
return current, 0
counter += 1
return (new_value, 0) if counter >= threshold else (current, counter)
class CanLeadDataState:
LEAD_HYSTERESIS_FRAMES = 50
def __init__(self):
self._lead_on_counter = 0
self._lead_off_counter = 0
self._gap_counter = 0
self._lead_visible = False
self._object_gap = 0
@staticmethod
def _get_object_gap(lead_distance: float) -> int:
if lead_distance == 0:
return 0
if lead_distance < 20:
return 2
if lead_distance < 25:
return 3
if lead_distance < 30:
return 4
return 5
def update(self, lead_distance: float, lead_rel_speed: float, lead_visible: bool) -> CanLeadData:
counter = self._lead_on_counter if lead_visible else self._lead_off_counter
self._lead_visible, counter = _hysteresis_update(
self._lead_visible, lead_visible, counter, self.LEAD_HYSTERESIS_FRAMES,
)
if lead_visible:
self._lead_on_counter = counter
self._lead_off_counter = 0
else:
self._lead_off_counter = counter
self._lead_on_counter = 0
object_gap = self._get_object_gap(lead_distance)
self._object_gap, self._gap_counter = _hysteresis_update(
self._object_gap, object_gap, self._gap_counter, self.LEAD_HYSTERESIS_FRAMES,
)
return CanLeadData(self._object_gap, lead_distance, lead_rel_speed, self._lead_visible)
@@ -27,6 +27,7 @@ from opendbc.car.hyundai.carstate import CarState, decode_canfd_camera_lead, dec
from opendbc.car.hyundai.interface import CarInterface, KIA_EV9_ACCEL_MAX, get_communication_control_request
from opendbc.car.hyundai import hyundaican, hyundaicanfd
from opendbc.car.hyundai.hyundaicanfd import CanBus, hkg_can_fd_checksum
from opendbc.car.hyundai.lead_data import CanLeadData, CanLeadDataState
from opendbc.car.hyundai.radar_interface import MRREVO14F_RADAR_START_ADDR, MRR30_RADAR_START_ADDR, MRR35_RADAR_START_ADDR, \
RADAR_START_ADDR, RadarInterface, get_radar_track_config, radar_tracks_available
from opendbc.car.hyundai.values import CAMERA_SCC_CAR, CANFD_CAR, CAN_GEARS, CAR, CHECKSUM, DATE_FW_ECUS, DATELESS_FUZZY_CARS, \
@@ -2705,10 +2706,11 @@ class TestHyundaiFingerprint:
actuators=SimpleNamespace(longControlState=LongCtrlState.pid),
cruiseControl=SimpleNamespace(override=False, cancel=False, resume=False),
leftBlinker=False, rightBlinker=False,
hudControl=SimpleNamespace(leadDistanceBars=3),
hudControl=SimpleNamespace(leadDistanceBars=3, leadVisible=True),
)
cs = SimpleNamespace(
stock_lfa_msg=None, stock_lkas_msg=None,
openpilot_lead_visible=True, openpilot_lead_distance=37.5, openpilot_lead_rel_speed=-1.3,
out=SimpleNamespace(steeringAngleDeg=0.0, gearShifter=structs.CarState.GearShifter.drive),
)
@@ -2721,7 +2723,8 @@ class TestHyundaiFingerprint:
parser.update([(1, scc_msgs)])
assert parser.can_valid
assert parser.vl["SCC_CONTROL"]["MainMode_ACC"] == 1
assert parser.vl["SCC_CONTROL"]["ACC_ObjDist"] == pytest.approx(1.0)
assert parser.vl["SCC_CONTROL"]["ACC_ObjDist"] == pytest.approx(37.5)
assert parser.vl["SCC_CONTROL"]["ACC_ObjRelSpd"] == pytest.approx(-1.3)
assert parser.vl["SCC_CONTROL"]["ObjValid"] == 0
assert parser.vl["SCC_CONTROL"]["aReqValue"] == pytest.approx(-0.1)
assert parser.vl["SCC_CONTROL"]["aReqRaw"] == pytest.approx(-1.0)
@@ -3016,6 +3019,50 @@ class TestHyundaiFingerprint:
assert parser.vl["SCC14"]["ComfortBandUpper"] == pytest.approx(0.0)
assert parser.vl["SCC14"]["ComfortBandLower"] == pytest.approx(0.0)
assert parser.vl["SCC14"]["JerkLowerLimit"] == pytest.approx(5.0)
assert parser.vl["SCC11"]["ObjValid"] == 0
assert parser.vl["SCC11"]["ACC_ObjDist"] == 0
assert parser.vl["SCC14"]["ObjGap"] == 0
assert parser.vl["SCC14"]["ObjDistStat"] == 0
def test_can_acc_commands_show_approaching_lead(self):
CP = CarParams.new_message()
CP.carFingerprint = CAR.HYUNDAI_ELANTRA_2021
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("SCC11", 0), ("SCC14", 0)], 0)
lead_data = CanLeadData(object_gap=4, lead_distance=27.5, lead_rel_speed=-1.3, lead_visible=True)
msgs = hyundaican.create_acc_commands(packer, enabled=True, accel=0.0, upper_jerk=1.0, idx=3,
hud_control=SimpleNamespace(leadDistanceBars=3), set_speed=42,
stopping=False, long_override=False, use_fca=False, CP=CP,
lead_data=lead_data)
parser.update([(1, msgs)])
assert parser.can_valid
assert parser.vl["SCC11"]["ObjValid"] == 1
assert parser.vl["SCC11"]["ACC_ObjStatus"] == 1
assert parser.vl["SCC11"]["ACC_ObjDist"] == pytest.approx(27.0)
assert parser.vl["SCC11"]["ACC_ObjRelSpd"] == pytest.approx(-1.3)
assert parser.vl["SCC14"]["ObjGap"] == 4
assert parser.vl["SCC14"]["ObjDistStat"] == 2
def test_can_lead_data_hysteresis_and_distance_bands(self):
state = CanLeadDataState()
for _ in range(state.LEAD_HYSTERESIS_FRAMES - 1):
lead_data = state.update(18.0, -0.5, True)
assert not lead_data.lead_visible
assert lead_data.object_gap == 0
lead_data = state.update(18.0, -0.5, True)
assert lead_data.lead_visible
assert lead_data.object_gap == 2
assert lead_data.object_rel_gap == 2
for _ in range(state.LEAD_HYSTERESIS_FRAMES):
lead_data = state.update(32.0, 0.5, True)
assert lead_data.object_gap == 5
assert lead_data.object_rel_gap == 1
def test_can_acc_commands_follow_sonata_main_cruise_state(self):
CP = CarParams.new_message()
+1
View File
@@ -35,6 +35,7 @@ non_tested_cars = [
GM.CHEVROLET_MALIBU_ASCM,
GM.CHEVROLET_MALIBU_SDGM,
GM.CHEVROLET_SUBURBAN,
GM.CHEVROLET_SUBURBAN_ASCM,
GM.CHEVROLET_TRAX,
GM.CHEVROLET_VOLT_ASCM,
GM.CHEVROLET_VOLT_CAMERA,
@@ -90,6 +90,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"CHEVROLET_MALIBU" = "CHEVROLET_VOLT"
"CHEVROLET_MALIBU_ASCM" = "CHEVROLET_VOLT"
"CHEVROLET_MALIBU_SDGM" = "CHEVROLET_VOLT"
"CHEVROLET_SUBURBAN_ASCM" = "CHEVROLET_SUBURBAN"
"HOLDEN_ASTRA" = "CHEVROLET_VOLT"
"CHEVROLET_VOLT_CC" = "CHEVROLET_VOLT"
"CHEVROLET_VOLT_CAMERA" = "CHEVROLET_VOLT"
@@ -1480,6 +1480,7 @@ BO_ 905 SCC14: 8 SCC
SG_ JerkLowerLimit : 19|7@1+ (0.1,0) [0|12.7] "m/s^3" ESC
SG_ ACCMode : 32|3@1+ (1,0) [0|7] "" CLU,HUD,LDWS_LKAS,ESC
SG_ ObjGap : 56|8@1+ (1,0) [0|255] "" CLU,HUD,ESC
SG_ ObjDistStat : 42|2@1+ (1,0) [0|3] "" XXX
BO_ 1157 LFAHDA_MFC: 4 XXX
SG_ HDA_USM : 0|2@1+ (1,0) [0|3] "" XXX
@@ -1671,6 +1672,7 @@ VAL_ 871 CF_Lvr_IsgState 0 "enabled" 1 "activated" 2 "unknown" 3 "disabled";
VAL_ 871 CF_Lvr_Gear 12 "T" 5 "D" 8 "S" 6 "N" 7 "R" 0 "P";
VAL_ 882 Elect_Gear_Shifter 4 "S" 5 "D" 8 "S" 6 "N" 7 "R" 0 "P";
VAL_ 905 ACCMode 0 "off" 1 "enabled" 2 "driver_override" 3 "off_maybe_fault" 4 "cancelled";
VAL_ 905 ObjDistStat 0 "no_object" 1 "receding" 2 "approaching";
VAL_ 909 CF_VSM_Warn 2 "FCW" 3 "AEB";
VAL_ 916 ACCEnable 0 "SCC ready" 1 "SCC temp fault" 2 "SCC permanent fault" 3 "SCC permanent fault, communication issue";
VAL_ 1056 SCCInfoDisplay 0 "No Message" 2 "Cruise Control" 3 "Lost Lead" 4 "Standstill";