This commit is contained in:
firestar5683
2026-06-24 23:54:45 -05:00
parent cec9f090c4
commit f0601e27ec
29 changed files with 446 additions and 83 deletions
@@ -165,6 +165,10 @@ jobs:
"panda/board/obj/panda_h7.bin.signed"
"panda/board/obj/panda_remote.bin.signed"
"panda/board/obj/panda_h7_remote.bin.signed"
"panda/board/obj/panda_can_ignition_only.bin.signed"
"panda/board/obj/panda_h7_can_ignition_only.bin.signed"
"panda/board/obj/panda_remote_can_ignition_only.bin.signed"
"panda/board/obj/panda_h7_remote_can_ignition_only.bin.signed"
"panda/board/obj/panda_jungle_h7.bin.signed"
"panda/board/obj/body_h7.bin.signed"
)
+1
View File
@@ -286,6 +286,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"FlashPanda", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"GMDashSpoofOffsets", {PERSISTENT, BOOL, "0", "0", 2}},
{"GMPedalLongitudinal", {PERSISTENT, BOOL, "1", "1", 2}},
{"IgnoreIgnitionLine", {PERSISTENT, BOOL, "0", "0"}},
{"LongPitch", {PERSISTENT, BOOL, "1", "0", 2}},
{"RemoteStartBootsComma", {PERSISTENT, BOOL, "0", "0"}},
{"RemapCancelToDistance", {PERSISTENT, BOOL, "0", "0"}},
@@ -4,7 +4,7 @@ import numpy as np
from opendbc.can import CANPacker
from opendbc.car import Bus, DT_CTRL, make_tester_present_msg, rate_limit, structs
from opendbc.car.common.filter_simple import FirstOrderFilter
from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_steer_angle_limits_vm, common_fault_avoidance
from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_steer_angle_limits_vm, common_fault_avoidance, get_max_angle_delta_vm
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.hyundai import hyundaicanfd, hyundaican
from opendbc.car.hyundai.hyundaicanfd import CanBus
@@ -64,6 +64,10 @@ REDNECK_BUTTON_COPIES_TIME = 7
REDNECK_BUTTON_COPIES_TIME_IMPERIAL = [REDNECK_BUTTON_COPIES_TIME + 3, 70]
REDNECK_BUTTON_COPIES_TIME_METRIC = [REDNECK_BUTTON_COPIES_TIME, 40]
ANGLE_SAFETY_BASELINE_MODEL = str(CAR.KIA_SPORTAGE_HEV_2026)
DEFAULT_ANGLE_SMOOTHING_VEGO_BP = [5.0, 10.0, 20.0]
DEFAULT_ANGLE_SMOOTHING_ALPHA_V = [0.2, 0.1, 0.0]
KIA_EV9_ANGLE_SMOOTHING_VEGO_BP = [0.0, 8.5, 11.0, 13.8, 18.0]
KIA_EV9_ANGLE_SMOOTHING_ALPHA_V = [0.05, 0.1, 0.3, 0.6, 1.0]
def egmp_dynamic_longitudinal_tuning(CP) -> bool:
@@ -220,6 +224,22 @@ def get_baseline_safety_cp():
return CarInterface.get_non_essential_params(ANGLE_SAFETY_BASELINE_MODEL)
def get_angle_smoothing_alpha(CP, v_ego: float) -> float:
if CP.carFingerprint == CAR.KIA_EV9:
return float(np.interp(v_ego, KIA_EV9_ANGLE_SMOOTHING_VEGO_BP, KIA_EV9_ANGLE_SMOOTHING_ALPHA_V))
return float(np.interp(v_ego, DEFAULT_ANGLE_SMOOTHING_VEGO_BP, DEFAULT_ANGLE_SMOOTHING_ALPHA_V))
def apply_steer_angle_limits_vm_checked(apply_angle: float, apply_angle_last: float, v_ego_raw: float,
steering_angle: float, lat_active: bool, limits, VM: VehicleModel) -> float | None:
new_apply_angle = apply_steer_angle_limits_vm(apply_angle, apply_angle_last, v_ego_raw, steering_angle, lat_active, limits, VM)
v_ego_raw = max(v_ego_raw, 1.0)
max_angle_delta = min(get_max_angle_delta_vm(v_ego_raw, VM, limits), limits.ANGLE_LIMITS.MAX_ANGLE_RATE)
safety_violation = lat_active and not np.isclose(new_apply_angle,
rate_limit(new_apply_angle, apply_angle_last, -max_angle_delta, max_angle_delta))
return None if safety_violation else new_apply_angle
def compute_torque_reduction_gain(steering_torque, v_ego, lat_active, last_gain):
if lat_active:
ceiling = np.interp(v_ego, [0.5, 1.5], [1.0, 0.85])
@@ -382,15 +402,16 @@ class CarController(CarControllerBase):
-self.params.ANGLE_LIMITS.STEER_ANGLE_MAX,
self.params.ANGLE_LIMITS.STEER_ANGLE_MAX))
self.angle_filter.update_alpha(float(np.interp(CS.out.vEgo, [5.0, 10.0, 20.0], [0.2, 0.1, 0.0])))
self.angle_filter.update_alpha(get_angle_smoothing_alpha(self.CP, CS.out.vEgo))
desired_angle = self.angle_filter.update(desired_angle)
apply_angle = apply_steer_angle_limits_vm(desired_angle, self.apply_angle_last, v_ego_raw,
CS.out.steeringAngleDeg, CC.latActive, self.params, self.VM)
angle_limit_fn = apply_steer_angle_limits_vm_checked if self.CP.carFingerprint == CAR.KIA_EV9 else apply_steer_angle_limits_vm
apply_angle = angle_limit_fn(desired_angle, self.apply_angle_last, v_ego_raw,
CS.out.steeringAngleDeg, CC.latActive, self.params, self.VM)
if str(self.CP.carFingerprint) != ANGLE_SAFETY_BASELINE_MODEL:
apply_angle = apply_steer_angle_limits_vm(apply_angle or desired_angle, self.apply_angle_last, v_ego_raw,
CS.out.steeringAngleDeg, CC.latActive, self.params, self.BASELINE_VM)
apply_angle = angle_limit_fn(apply_angle or desired_angle, self.apply_angle_last, v_ego_raw,
CS.out.steeringAngleDeg, CC.latActive, self.params, self.BASELINE_VM)
apply_torque = compute_torque_reduction_gain(CS.out.steeringTorque, v_ego_raw, CC.latActive, self.apply_torque_last)
apply_steer_req = CC.latActive and apply_torque != 0.0
@@ -650,7 +671,8 @@ class CarController(CarControllerBase):
# and stops publishing object tracks when it disappears. Spoof it periodically on
# PT bus so the radar keeps tracking.
if self.CP.carFingerprint in CANFD_RADAR_LIVE_LONGITUDINAL_CAR and self.frame % 4 == 0:
can_sends.append(hyundaicanfd.create_accelerator_brake_alt_spoof(0, self.frame // 4, CS.out.brakePressed, CS.out.gasPressed))
can_sends.append(hyundaicanfd.create_accelerator_brake_alt_spoof(0, self.frame // 4, CS.out.brakePressed,
CS.out.gasPressed, self.CP.carFingerprint))
elif not ccnc_non_hda2:
can_sends.extend(hyundaicanfd.create_fca_warning_light(self.packer, self.CAN, self.frame))
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and self.frame % 5 == 0:
@@ -96,6 +96,8 @@ def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque,
lfa_base_values=None, lkas_base_values=None, lka_icon=None):
if lka_icon is None:
lka_icon = 2 if enabled else 1
ev9_angle_lkas_alt = str(CP.carFingerprint) == "KIA_EV9" and CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING and \
CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
control_values = {
"LKA_MODE": 2,
@@ -128,6 +130,24 @@ def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque,
lkas_values["ADAS_StrAnglReqVal"] = apply_angle
lkas_values["LKAS_ANGLE_ACTIVE"] = 2 if lat_active else 1
lkas_values["ADAS_ACIAnglTqRedcGainVal"] = apply_torque if lat_active else 0.0
if ev9_angle_lkas_alt:
lkas_values.update({
"LKA_MODE": 0,
"LKA_AVAILABLE": 3 if lat_active else 0,
"LKA_WARNING": 0,
"LKA_ICON": lka_icon,
"FCA_SYSWARN": 0,
"TORQUE_REQUEST": 0,
"STEER_REQ": 0,
"LFA_BUTTON": 0,
"LKA_ASSIST": 0,
"DAMP_FACTOR": 100,
"HAS_LANE_SAFETY": 0,
})
# These signals overlap DAMP_FACTOR in the local DBC naming; omitting them
# preserves the stock angle-steering damping byte expected by the ADAS ECU.
lkas_values.pop("STEER_MODE", None)
lkas_values.pop("NEW_SIGNAL_2", None)
ret = []
if CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
@@ -709,11 +729,15 @@ def hkg_can_fd_checksum(address: int, sig, d: bytearray) -> int:
# radar stops publishing real object tracks. Spoof this message ourselves with valid CRC
# and current pedal state so the radar keeps tracking.
# Length is 24 bytes on Ioniq 6 (DBC declares 32 for ICE Hyundais, but EV firmware uses 24).
# Byte template captured from a real ADAS broadcast; bytes 6-23 appear static / config.
# Byte templates captured from real ADAS broadcasts; only checksum, counter,
# brake, and accelerator bits are updated for the radar heartbeat.
_ACCEL_BRAKE_ALT_TEMPLATE = bytes.fromhex("000000020000fcff000000000020000055ff000068000000")
_KIA_EV9_ACCEL_BRAKE_ALT_TEMPLATE = bytes.fromhex("00000000ff006f00e80400001201030055ffff0000000000")
def create_accelerator_brake_alt_spoof(bus: int, counter: int, brake_pressed: bool, accelerator_pressed: bool) -> CanData:
d = bytearray(_ACCEL_BRAKE_ALT_TEMPLATE)
def create_accelerator_brake_alt_spoof(bus: int, counter: int, brake_pressed: bool, accelerator_pressed: bool,
car_fingerprint=None) -> CanData:
template = _KIA_EV9_ACCEL_BRAKE_ALT_TEMPLATE if str(car_fingerprint) == "KIA_EV9" else _ACCEL_BRAKE_ALT_TEMPLATE
d = bytearray(template)
d[2] = counter & 0xFF # COUNTER (bit 16, 8-bit)
d[4] = (d[4] & ~0x01) | (0x01 if brake_pressed else 0x00) # BRAKE_PRESSED (bit 32)
d[22] = (d[22] & ~0x01) | (0x01 if accelerator_pressed else 0x00) # ACCELERATOR_PEDAL_PRESSED (bit 176)
@@ -4,6 +4,7 @@ from opendbc.car.hyundai.hyundaicanfd import CanBus
from opendbc.car.hyundai.values import HyundaiFlags, CAR, CarControllerParams, \
CANFD_UNSUPPORTED_LONGITUDINAL_CAR, \
CANFD_SECURITYACCESS_CAR, \
CANFD_ANGLE_LONGITUDINAL_CAR, \
CANFD_RADAR_LIVE_LONGITUDINAL_CAR, \
RADAR_LIVE_LONGITUDINAL_CAR, \
UNSUPPORTED_LONGITUDINAL_CAR, HyundaiSafetyFlags, \
@@ -99,8 +100,8 @@ class CarInterface(CarInterfaceBase):
# this needs to be figured out for cars without an ADAS ECU
# Cars in CANFD_SECURITYACCESS_CAR are known to have ADAS ECUs that work with SecurityAccess
ret.alphaLongitudinalAvailable = False
if lka_steering and ret.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
# Angle-steering LKA platforms still need stock longitudinal validation.
if lka_steering and ret.flags & HyundaiFlags.CANFD_ANGLE_STEERING and candidate not in CANFD_ANGLE_LONGITUDINAL_CAR:
# Most angle-steering LKA platforms still need stock longitudinal validation.
ret.alphaLongitudinalAvailable = False
ret.enableBsm = 0x1ba in fingerprint[CAN.ECAN]
@@ -10,7 +10,8 @@ from opendbc.car.fw_versions import build_fw_dict, match_fw_to_car
from opendbc.car.hyundai.carcontroller import CarController, Ioniq6LongitudinalTuningState, GenesisG90LongitudinalTuningState, \
update_ioniq_6_longitudinal_tuning, \
update_genesis_g90_longitudinal_tuning, egmp_dynamic_longitudinal_tuning, \
should_reset_ev6_gt_line_longitudinal_tuning, reset_ev6_gt_line_longitudinal_tuning
should_reset_ev6_gt_line_longitudinal_tuning, reset_ev6_gt_line_longitudinal_tuning, \
get_angle_smoothing_alpha, apply_steer_angle_limits_vm_checked
from opendbc.car.hyundai.carstate import CarState, decode_canfd_camera_lead, decode_ioniq_6_blindspot_radar_state
from opendbc.car.hyundai.interface import CarInterface
from opendbc.car.hyundai import hyundaican, hyundaicanfd
@@ -22,6 +23,7 @@ from opendbc.car.hyundai.values import CAMERA_SCC_CAR, CANFD_CAR, CAN_GEARS, CAR
UNSUPPORTED_LONGITUDINAL_CAR, PLATFORM_CODE_ECUS, HYUNDAI_VERSION_REQUEST_LONG, \
LEGACY_LONGITUDINAL_CAR, CarControllerParams, DBC, HyundaiFlags, get_platform_codes, HyundaiSafetyFlags, \
HyundaiStarPilotSafetyFlags, Buttons, kia_ev6_gt_line_longitudinal_tuning
from opendbc.car.vehicle_model import VehicleModel
LongCtrlState = CarControl.Actuators.LongControlState
from opendbc.car.hyundai.fingerprints import FW_VERSIONS
@@ -168,6 +170,7 @@ class TestHyundaiFingerprint:
(CAR.GENESIS_GV60_EV_1ST_GEN, MRR30_RADAR_START_ADDR),
(CAR.HYUNDAI_KONA_EV_2ND_GEN, MRR35_RADAR_START_ADDR),
(CAR.HYUNDAI_IONIQ_9, MRR35_RADAR_START_ADDR),
(CAR.KIA_EV9, MRR35_RADAR_START_ADDR),
):
radar_config = get_radar_track_config(candidate)
assert radar_config.start_addr == radar_addr
@@ -226,6 +229,21 @@ class TestHyundaiFingerprint:
assert CP.openpilotLongitudinalControl
assert not CP.radarUnavailable
ev9_radar_config = get_radar_track_config(CAR.KIA_EV9)
fingerprint = gen_empty_fingerprint()
fingerprint[CanBus(None, fingerprint).CAM][0x110] = 32
fingerprint[ev9_radar_config.bus][ev9_radar_config.start_addr] = ev9_radar_config.expected_length
ev9_car_fw = [CarParams.CarFw(ecu=Ecu.adas, fwVersion=b"", address=0x730, brand="hyundai")]
CP = CarInterface.get_params(CAR.KIA_EV9, fingerprint, ev9_car_fw, True, False, False, None)
assert CP.openpilotLongitudinalControl
assert not CP.radarUnavailable
assert CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.LONG
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CANFD_ANGLE_STEERING
CP = CarInterface.get_params(CAR.KIA_EV9, fingerprint, [], True, False, False, None)
assert not CP.openpilotLongitudinalControl
for candidate in HYUNDAI_NON_SCC_CARS:
CP = CarInterface.get_params(candidate, gen_empty_fingerprint(), [], True, False, False, None)
assert bool(CP.flags & HyundaiFlags.NON_SCC)
@@ -260,6 +278,22 @@ class TestHyundaiFingerprint:
assert CP.steerControlType == CarParams.SteerControlType.angle
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CANFD_ANGLE_STEERING
def test_ev9_angle_smoothing_is_ev9_specific(self):
ev9_cp = SimpleNamespace(carFingerprint=CAR.KIA_EV9)
other_cp = SimpleNamespace(carFingerprint=CAR.KIA_SPORTAGE_HEV_2026)
assert get_angle_smoothing_alpha(ev9_cp, 0.0) == pytest.approx(0.05)
assert get_angle_smoothing_alpha(ev9_cp, 13.8) == pytest.approx(0.6)
assert get_angle_smoothing_alpha(ev9_cp, 20.0) == pytest.approx(1.0)
assert get_angle_smoothing_alpha(other_cp, 20.0) == pytest.approx(0.0)
def test_checked_angle_limiter_blocks_rate_accel_conflict(self):
CP = CarInterface.get_non_essential_params(CAR.KIA_EV9)
params = CarControllerParams(CP)
assert apply_steer_angle_limits_vm_checked(0.0, 100.0, 20.0, 100.0, True,
params, VehicleModel(CP)) is None
def test_ccnc_hda2_lka_layout_does_not_set_ccnc_safety_param(self):
fingerprint = gen_empty_fingerprint()
cam_can = CanBus(None, fingerprint).CAM
@@ -1554,6 +1588,67 @@ class TestHyundaiFingerprint:
assert parser.vl["LKAS_ALT"]["STEER_REQ"] == 1
assert parser.vl["LKAS_ALT"]["LKA_ICON"] == 2
def test_ev9_angle_lkas_alt_uses_angle_status_fields(self):
CP = CarParams.new_message()
CP.carFingerprint = CAR.KIA_EV9
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING |
HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT)
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
can_bus = CanBus(CP)
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("LKAS_ALT", 0)], can_bus.ACAN)
stock_lkas = {
"CHECKSUM": 1234,
"COUNTER": 42,
"LKA_MODE": 2,
"LKA_AVAILABLE": 0,
"LKA_WARNING": 1,
"LKA_ICON": 1,
"FCA_SYSWARN": 1,
"TORQUE_REQUEST": 17,
"STEER_REQ": 1,
"LFA_BUTTON": 1,
"LKA_ASSIST": 1,
"STEER_MODE": 5,
"NEW_SIGNAL_2": 0,
"LKAS_ANGLE_ACTIVE": 1,
"HAS_LANE_SAFETY": 1,
"ADAS_StrAnglReqVal": 12.3,
"ADAS_ACIAnglTqRedcGainVal": 0.42,
"DAMP_FACTOR": 0,
}
msgs = hyundaicanfd.create_steering_messages(packer, CP, can_bus, True, True, 0.44, -31.5,
lkas_base_values=stock_lkas, lka_icon=2)
lkas_msgs = [msg for msg in msgs if msg[0] == 0x110]
assert len(lkas_msgs) == 1
parser.update([(1, lkas_msgs)])
assert parser.can_valid
assert parser.vl["LKAS_ALT"]["LKA_MODE"] == 0
assert parser.vl["LKAS_ALT"]["LKA_AVAILABLE"] == 3
assert parser.vl["LKAS_ALT"]["LKA_WARNING"] == 0
assert parser.vl["LKAS_ALT"]["LKA_ICON"] == 2
assert parser.vl["LKAS_ALT"]["FCA_SYSWARN"] == 0
assert parser.vl["LKAS_ALT"]["TORQUE_REQUEST"] == 0
assert parser.vl["LKAS_ALT"]["STEER_REQ"] == 0
assert parser.vl["LKAS_ALT"]["LFA_BUTTON"] == 0
assert parser.vl["LKAS_ALT"]["LKA_ASSIST"] == 0
assert parser.vl["LKAS_ALT"]["DAMP_FACTOR"] == 100
assert parser.vl["LKAS_ALT"]["LKAS_ANGLE_ACTIVE"] == 2
assert parser.vl["LKAS_ALT"]["HAS_LANE_SAFETY"] == 0
assert parser.vl["LKAS_ALT"]["ADAS_StrAnglReqVal"] == pytest.approx(-31.5)
assert parser.vl["LKAS_ALT"]["ADAS_ACIAnglTqRedcGainVal"] == pytest.approx(0.44)
def test_ev9_accelerator_brake_alt_spoof_matches_route_template(self):
msg = hyundaicanfd.create_accelerator_brake_alt_spoof(0, 0x66, True, False, CAR.KIA_EV9)
assert msg.address == 0x100
assert msg.src == 0
assert msg.dat.hex() == "470c6600ff006f00e80400001201030055ffff0000000000"
def test_ioniq_6_lfahda_cluster_allows_lfa_icon_override(self):
CP = CarParams.new_message()
CP.carFingerprint = CAR.HYUNDAI_IONIQ_6
+3 -1
View File
@@ -1129,7 +1129,9 @@ CANFD_RADAR_SCC_CAR = CAR.with_flags(HyundaiFlags.RADAR_SCC) # TODO: merge with
# CAN-FD cars with ADAS ECUs that work with the communication-control path.
CANFD_SECURITYACCESS_CAR = {CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_6, CAR.HYUNDAI_KONA_EV_2ND_GEN}
CANFD_UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.CANFD_NO_RADAR_DISABLE) - CANFD_SECURITYACCESS_CAR # TODO: merge with UNSUPPORTED_LONGITUDINAL_CAR
CANFD_RADAR_LIVE_LONGITUDINAL_CAR = {CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_6, CAR.KIA_EV6, CAR.GENESIS_GV60_EV_1ST_GEN}
CANFD_ANGLE_LONGITUDINAL_CAR = {CAR.KIA_EV9}
CANFD_RADAR_LIVE_LONGITUDINAL_CAR = {CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_6, CAR.KIA_EV6, CAR.GENESIS_GV60_EV_1ST_GEN,
CAR.KIA_EV9}
RADAR_LIVE_LONGITUDINAL_CAR = CANFD_RADAR_LIVE_LONGITUDINAL_CAR | {
CAR.HYUNDAI_IONIQ,
CAR.HYUNDAI_KONA_EV_2022,
+5 -2
View File
@@ -68,10 +68,11 @@ class CarInterface(CarInterfaceBase):
if candidate == CAR.TOYOTA_PRIUS:
stop_and_go = True
ret.flags |= ToyotaFlags.HYBRID.value
# Only give steer angle deadzone to for bad angle sensor prius
for fw in car_fw:
if fw.ecu == "eps" and not fw.fwVersion == b'8965B47060\x00\x00\x00\x00\x00\x00':
ret.steerActuatorDelay = 0.25
ret.steerActuatorDelay = 0.14
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg=0.3)
elif candidate in (CAR.LEXUS_RX, CAR.LEXUS_RX_TSS2):
@@ -154,7 +155,9 @@ class CarInterface(CarInterfaceBase):
# to a negative value, so it won't matter.
ret.minEnableSpeed = -1. if (stop_and_go or ret.enableGasInterceptorDEPRECATED) else MIN_ACC_SPEED
if candidate in TSS2_CAR or ret.enableGasInterceptorDEPRECATED:
prius_long_defaults = candidate == CAR.TOYOTA_PRIUS and ret.openpilotLongitudinalControl
if candidate in TSS2_CAR or ret.enableGasInterceptorDEPRECATED or prius_long_defaults:
ret.flags |= ToyotaFlags.RAISED_ACCEL_LIMIT.value
ret.vEgoStopping = 0.25
@@ -61,6 +61,24 @@ class TestToyotaInterfaces:
assert car_params.flags & ToyotaFlags.AUTO_BRAKE_HOLD.value
assert car_params.alternativeExperience & ALTERNATIVE_EXPERIENCE.ALLOW_AEB
def test_prius_openpilot_long_uses_hybrid_long_defaults(self):
car_params = CarInterface.get_params(
CAR.TOYOTA_PRIUS,
{0: {0x2FF: 8}},
[],
alpha_long=False,
is_release=False,
docs=False,
starpilot_toggles=SimpleNamespace(),
)
assert car_params.openpilotLongitudinalControl
assert car_params.flags & ToyotaFlags.HYBRID.value
assert car_params.flags & ToyotaFlags.RAISED_ACCEL_LIMIT.value
assert abs(car_params.longitudinalActuatorDelay - 0.05) < 1e-6
assert abs(car_params.vEgoStopping - 0.25) < 1e-6
assert abs(car_params.vEgoStarting - 0.25) < 1e-6
def test_essential_ecus(self, subtests):
# Asserts standard ECUs exist for each platform
common_ecus = {Ecu.fwdRadar, Ecu.fwdCamera}
@@ -221,7 +221,8 @@ static bool hyundai_canfd_tx_hook(const CANPacket_t *msg) {
}
// steering
const unsigned int steer_addr = (hyundai_canfd_lka_steering && !hyundai_longitudinal) ? hyundai_canfd_get_lka_addr() : 0x12aU;
const unsigned int steer_addr = (hyundai_canfd_lka_steering && (hyundai_canfd_angle_steering || !hyundai_longitudinal)) ?
hyundai_canfd_get_lka_addr() : 0x12aU;
if (msg->addr == steer_addr) {
if (hyundai_canfd_angle_steering) {
const int lkas_angle_active = (msg->data[9] >> 4U) & 0x3U;
@@ -334,6 +335,21 @@ static safety_config hyundai_canfd_init(uint16_t param) {
{0x1DA, 1, 32, .check_relay = false}, // ADRV_0x1da
};
static const CanMsg HYUNDAI_CANFD_LKA_STEERING_ALT_LONG_TX_MSGS[] = {
HYUNDAI_CANFD_LKA_STEERING_ALT_COMMON_TX_MSGS(0, 1)
HYUNDAI_CANFD_LFA_STEERING_COMMON_TX_MSGS(1)
HYUNDAI_CANFD_SCC_CONTROL_COMMON_TX_MSGS(1, true)
HYUNDAI_CANFD_BLINDSPOT_DASH_TX_MSGS(1)
{0x51, 0, 32, .check_relay = false}, // ADRV_0x51
{0x100, 0, 24, .check_relay = false}, // ACCELERATOR_BRAKE_ALT radar heartbeat spoof
{0x730, 1, 8, .check_relay = false}, // tester present for ADAS ECU disable
{0x160, 1, 16, .check_relay = false}, // ADRV_0x160
{0x1EA, 1, 32, .check_relay = false}, // ADRV_0x1ea
{0x200, 1, 8, .check_relay = false}, // ADRV_0x200
{0x345, 1, 8, .check_relay = false}, // ADRV_0x345
{0x1DA, 1, 32, .check_relay = false}, // ADRV_0x1da
};
static const CanMsg HYUNDAI_CANFD_LFA_STEERING_TX_MSGS[] = {
HYUNDAI_CANFD_CRUISE_BUTTON_TX_MSGS(2)
HYUNDAI_CANFD_LFA_STEERING_COMMON_TX_MSGS(0)
@@ -382,7 +398,11 @@ static safety_config hyundai_canfd_init(uint16_t param) {
};
SET_RX_CHECKS(hyundai_canfd_lka_steering_long_rx_checks, ret);
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS, ret);
if (hyundai_canfd_lka_steering_alt) {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_LONG_TX_MSGS, ret);
} else {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS, ret);
}
} else {
// Longitudinal checks for LFA steering
@@ -559,6 +559,61 @@ class TestHyundaiCanfdLKASteeringLongEV(HyundaiLongitudinalBase, TestHyundaiCanf
return self.packer.make_can_msg_safety("SCC_CONTROL", 1, values)
class TestHyundaiCanfdLKASteeringAltAngleLongEV(HyundaiLongitudinalBase, TestHyundaiCanfdAngleSteering):
TX_MSGS = [[0x110, 0], [0x1CF, 1], [0x362, 0], [0x51, 0], [0x100, 0], [0x730, 1], [0x12a, 1], [0x160, 1],
[0x1ba, 1], [0x1e0, 1], [0x1e5, 1], [0x31a, 1], [0x3b5, 1], [0x3c1, 1],
[0x1a0, 1], [0x1ea, 1], [0x200, 1], [0x345, 1], [0x1da, 1]]
RELAY_MALFUNCTION_ADDRS = {0: (0x110, 0x362), 1: (0x1a0,)} # LKAS_ALT, CAM_0x362, SCC_CONTROL
FWD_BLACKLISTED_ADDRS = {0: MRR35_RADAR_TRACK_ADDRS, 2: [0x110, 0x362]}
DISABLED_ECU_UDS_MSG = (0x730, 1)
DISABLED_ECU_ACTUATION_MSG = (0x1a0, 1)
PT_BUS = 1
SCC_BUS = 1
BUTTONS_TX_BUS = 1
STEER_MSG = "LKAS_ALT"
GAS_MSG = ("ACCELERATOR", "ACCELERATOR_PEDAL")
SAFETY_PARAM = HyundaiSafetyFlags.CANFD_LKA_STEERING | HyundaiSafetyFlags.CANFD_LKA_STEERING_ALT | \
HyundaiSafetyFlags.CANFD_ANGLE_STEERING | HyundaiSafetyFlags.LONG | HyundaiSafetyFlags.EV_GAS
def _angle_cmd_msg(self, angle, enabled, increment_timer=True, gain_raw=250):
if increment_timer:
self.safety.set_timer(self.angle_cmd_cnt * int(1e6 / self.LATERAL_FREQUENCY))
self.angle_cmd_cnt += 1
values = {
"LKA_MODE": 0,
"LKA_AVAILABLE": 3 if enabled else 0,
"LKA_WARNING": 0,
"LKA_ICON": 2 if enabled else 1,
"FCA_SYSWARN": 0,
"TORQUE_REQUEST": 0,
"STEER_REQ": 0,
"LFA_BUTTON": 0,
"LKA_ASSIST": 0,
"DAMP_FACTOR": 100,
"LKAS_ANGLE_ACTIVE": 2 if enabled else 1,
"HAS_LANE_SAFETY": 0,
"ADAS_StrAnglReqVal": angle,
"ADAS_ACIAnglTqRedcGainVal": gain_raw * 0.004 if enabled or gain_raw != 250 else 0.0,
}
return self.packer.make_can_msg_safety("LKAS_ALT", 0, values)
def _accel_msg(self, accel, aeb_req=False, aeb_decel=0):
values = {
"aReqRaw": accel,
"aReqValue": accel,
}
return self.packer.make_can_msg_safety("SCC_CONTROL", 1, values)
def _tx_acc_state_msg(self, main_on):
values = {"MainMode_ACC": int(main_on), "ACCMode": 0}
return self.packer.make_can_msg_safety("SCC_CONTROL", 1, values)
# Tests longitudinal for ICE, hybrid, EV cars with LFA steering
class TestHyundaiCanfdLFASteeringLongBase(HyundaiLongitudinalBase, TestHyundaiCanfdLFASteeringBase):
+4
View File
@@ -169,6 +169,10 @@ build_project("panda", base_project_f4, "./board/main.c", [])
build_project("panda_h7", base_project_h7, "./board/main.c", [])
build_project("panda_remote", base_project_f4, "./board/main.c", ["-DPANDA_GM_REMOTE_START_C9"])
build_project("panda_h7_remote", base_project_h7, "./board/main.c", ["-DPANDA_GM_REMOTE_START_C9"])
build_project("panda_can_ignition_only", base_project_f4, "./board/main.c", ["-DPANDA_IGNORE_IGNITION_LINE"])
build_project("panda_h7_can_ignition_only", base_project_h7, "./board/main.c", ["-DPANDA_IGNORE_IGNITION_LINE"])
build_project("panda_remote_can_ignition_only", base_project_f4, "./board/main.c", ["-DPANDA_GM_REMOTE_START_C9", "-DPANDA_IGNORE_IGNITION_LINE"])
build_project("panda_h7_remote_can_ignition_only", base_project_h7, "./board/main.c", ["-DPANDA_GM_REMOTE_START_C9", "-DPANDA_IGNORE_IGNITION_LINE"])
# panda jungle fw
flags = [
+13 -1
View File
@@ -27,9 +27,21 @@
#include "board/obj/gitversion.h"
#include "board/can_comms.h"
static bool panda_ignition_line(void);
#include "board/main_comms.h"
static bool panda_ignition_line(void) {
#ifdef PANDA_IGNORE_IGNITION_LINE
return false;
#else
return harness_check_ignition();
#endif
}
// ********************* Serial debugging *********************
void debug_ring_callback(uart_ring *ring) {
@@ -176,7 +188,7 @@ static void tick_handler(void) {
const bool recent_heartbeat = heartbeat_counter == 0U;
// tick drivers at 1Hz
bool started = harness_check_ignition() || ignition_can;
bool started = panda_ignition_line() || ignition_can;
bootkick_tick(started, recent_heartbeat);
// increase heartbeat counter and cap it at the uint32 limit
+1 -1
View File
@@ -12,7 +12,7 @@ static int get_health_pkt(void *dat) {
health->voltage_pkt = current_board->read_voltage_mV();
health->current_pkt = current_board->read_current_mA();
health->ignition_line_pkt = (uint8_t)(harness_check_ignition());
health->ignition_line_pkt = (uint8_t)(panda_ignition_line());
health->ignition_can_pkt = ignition_can;
health->controls_allowed_pkt = controls_allowed;
Binary file not shown.
+19 -19
View File
@@ -649,28 +649,28 @@ VOLT_PLEXY_FRICTION_SCALE_MULT_RIGHT = 1.05
VOLT_PLEXY_FRICTION_THRESHOLD_MULT_LEFT = 1.00
VOLT_PLEXY_FRICTION_THRESHOLD_MULT_RIGHT = 0.96
VOLT_PLEXY_CENTER_TAPER_REDUCTION_MULT = 0.94
PRIUS_TRANSITION_SPEED = 10.0
PRIUS_TRANSITION_SPEED = 8.5
PRIUS_PHASE_SCALE = 0.09
PRIUS_FF_GAIN_LEFT = 0.12
PRIUS_FF_GAIN_RIGHT = 0.13
PRIUS_FF_ONSET = 0.12
PRIUS_FF_ONSET_WIDTH = 0.07
PRIUS_FF_CUTOFF = 1.45
PRIUS_FF_CUTOFF_WIDTH = 0.36
PRIUS_FF_GAIN_LEFT = 0.08
PRIUS_FF_GAIN_RIGHT = 0.09
PRIUS_FF_ONSET = 0.15
PRIUS_FF_ONSET_WIDTH = 0.08
PRIUS_FF_CUTOFF = 1.30
PRIUS_FF_CUTOFF_WIDTH = 0.32
PRIUS_FRICTION_LAT_RISE = 0.18
PRIUS_FRICTION_JERK_RISE = 0.22
PRIUS_TURN_IN_BOOST_LEFT = 0.60
PRIUS_TURN_IN_BOOST_RIGHT = 0.56
PRIUS_UNWIND_TAPER_LEFT = 0.58
PRIUS_UNWIND_TAPER_RIGHT = 0.84
PRIUS_TURN_IN_THRESHOLD_REDUCTION_LEFT = 0.22
PRIUS_TURN_IN_THRESHOLD_REDUCTION_RIGHT = 0.22
PRIUS_UNWIND_THRESHOLD_INCREASE_LEFT = 0.36
PRIUS_UNWIND_THRESHOLD_INCREASE_RIGHT = 0.52
PRIUS_TURN_IN_FRICTION_BOOST_LEFT = 0.10
PRIUS_TURN_IN_FRICTION_BOOST_RIGHT = 0.10
PRIUS_UNWIND_FRICTION_REDUCTION_LEFT = 0.22
PRIUS_UNWIND_FRICTION_REDUCTION_RIGHT = 0.32
PRIUS_TURN_IN_BOOST_LEFT = 0.28
PRIUS_TURN_IN_BOOST_RIGHT = 0.25
PRIUS_UNWIND_TAPER_LEFT = 0.22
PRIUS_UNWIND_TAPER_RIGHT = 0.34
PRIUS_TURN_IN_THRESHOLD_REDUCTION_LEFT = 0.16
PRIUS_TURN_IN_THRESHOLD_REDUCTION_RIGHT = 0.16
PRIUS_UNWIND_THRESHOLD_INCREASE_LEFT = 0.22
PRIUS_UNWIND_THRESHOLD_INCREASE_RIGHT = 0.32
PRIUS_TURN_IN_FRICTION_BOOST_LEFT = 0.07
PRIUS_TURN_IN_FRICTION_BOOST_RIGHT = 0.07
PRIUS_UNWIND_FRICTION_REDUCTION_LEFT = 0.12
PRIUS_UNWIND_FRICTION_REDUCTION_RIGHT = 0.18
TRAILER_LOAD_FULL_ASSIST_KG = 15000.0 * CV.LB_TO_KG
TRAILER_LATERAL_MIN_SPEED = 15.0 * CV.MPH_TO_MS
+10 -1
View File
@@ -137,7 +137,16 @@ std::optional<std::string> Panda::get_serial() {
bool Panda::up_to_date() {
if (auto fw_sig = get_firmware_version()) {
for (auto fn : { "panda.bin.signed", "panda_h7.bin.signed", "panda_remote.bin.signed", "panda_h7_remote.bin.signed" }) {
for (auto fn : {
"panda.bin.signed",
"panda_h7.bin.signed",
"panda_remote.bin.signed",
"panda_h7_remote.bin.signed",
"panda_can_ignition_only.bin.signed",
"panda_h7_can_ignition_only.bin.signed",
"panda_remote_can_ignition_only.bin.signed",
"panda_h7_remote_can_ignition_only.bin.signed",
}) {
auto content = util::read_file(std::string("../../panda/board/obj/") + fn);
if (content.size() >= fw_sig->size() &&
memcmp(content.data() + content.size() - fw_sig->size(), fw_sig->data(), fw_sig->size()) == 0) {
+13 -4
View File
@@ -217,7 +217,8 @@ void fill_panda_can_state(cereal::PandaState::PandaCanState::Builder &cs, const
cs.setCanCoreResetCnt(can_health.can_core_reset_cnt);
}
std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *> &pandas, bool is_onroad, bool spoofing_started) {
std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *> &pandas, bool is_onroad,
bool spoofing_started, bool ignore_ignition_line) {
bool ignition_local = false;
const uint32_t pandas_cnt = pandas.size();
@@ -265,6 +266,12 @@ std::optional<bool> send_panda_states(PubMaster *pm, const std::vector<Panda *>
health.ignition_line_pkt = 0;
}
// Work around harness-box false ignition by relying only on CAN ignition.
// This is only appropriate for vehicles with reliable panda CAN ignition support.
if (ignore_ignition_line) {
health.ignition_line_pkt = 0;
}
ignition_local |= ((health.ignition_line_pkt != 0) || (health.ignition_can_pkt != 0));
pandaStates.push_back(health);
@@ -353,14 +360,15 @@ void send_peripheral_state(Panda *panda, PubMaster *pm) {
pm->send("peripheralState", msg);
}
void process_panda_state(std::vector<Panda *> &pandas, PubMaster *pm, bool engaged, bool is_onroad, bool spoofing_started) {
void process_panda_state(std::vector<Panda *> &pandas, PubMaster *pm, bool engaged, bool is_onroad,
bool spoofing_started, bool ignore_ignition_line) {
std::vector<std::string> connected_serials;
for (Panda *p : pandas) {
connected_serials.push_back(p->hw_serial());
}
{
auto ignition_opt = send_panda_states(pm, pandas, is_onroad, spoofing_started);
auto ignition_opt = send_panda_states(pm, pandas, is_onroad, spoofing_started, ignore_ignition_line);
if (!ignition_opt) {
LOGE("Failed to get ignition_opt");
return;
@@ -494,7 +502,8 @@ void pandad_run(std::vector<Panda *> &pandas) {
sm["selfdriveState"].getSelfdriveState().getEnabled() || preap_aol_engaged
);
is_onroad = params.getBool("IsOnroad");
process_panda_state(pandas, &pm, engaged, is_onroad, spoofing_started);
const bool ignore_ignition_line = params.getBool("IgnoreIgnitionLine");
process_panda_state(pandas, &pm, engaged, is_onroad, spoofing_started, ignore_ignition_line);
panda_safety.configureSafetyMode(is_onroad);
}
+35 -14
View File
@@ -13,20 +13,33 @@ from openpilot.system.hardware import HARDWARE
from openpilot.common.swaglog import cloudlog
def get_expected_firmware_path(panda: Panda, remote_start: bool) -> str:
app_fn = panda.get_mcu_type().config.app_fn
def get_selected_firmware_name(app_fn: str, remote_start: bool, ignore_ignition_line: bool) -> str:
if not remote_start and not ignore_ignition_line:
return app_fn
h7 = app_fn == "panda_h7.bin.signed"
name_parts = ["panda_h7" if h7 else "panda"]
if remote_start:
remote_fn = "panda_h7_remote.bin.signed" if app_fn == "panda_h7.bin.signed" else "panda_remote.bin.signed"
remote_path = os.path.join(FW_PATH, remote_fn)
if os.path.isfile(remote_path):
return remote_path
cloudlog.warning(f"Remote-start panda firmware not found: {remote_path}, falling back to default")
name_parts.append("remote")
if ignore_ignition_line:
name_parts.append("can_ignition_only")
return "_".join(name_parts) + ".bin.signed"
def get_expected_firmware_path(panda: Panda, remote_start: bool, ignore_ignition_line: bool) -> str:
app_fn = panda.get_mcu_type().config.app_fn
selected_fn = get_selected_firmware_name(app_fn, remote_start, ignore_ignition_line)
if selected_fn != app_fn:
selected_path = os.path.join(FW_PATH, selected_fn)
if os.path.isfile(selected_path):
return selected_path
cloudlog.warning(f"Selected panda firmware not found: {selected_path}, falling back to default")
return os.path.join(FW_PATH, app_fn)
def get_expected_signature(panda: Panda, remote_start: bool) -> bytes:
def get_expected_signature(panda: Panda, remote_start: bool, ignore_ignition_line: bool) -> bytes:
try:
fn = get_expected_firmware_path(panda, remote_start)
fn = get_expected_firmware_path(panda, remote_start, ignore_ignition_line)
return Panda.get_signature_from_firmware(fn)
except Exception:
cloudlog.exception("Error computing expected signature")
@@ -40,7 +53,14 @@ def get_remote_start_boots_comma(params: Params) -> bool:
return False
def flash_panda(panda_serial: str, remote_start: bool) -> Panda:
def get_ignore_ignition_line(params: Params) -> bool:
try:
return params.get_bool("IgnoreIgnitionLine")
except UnknownKeyName:
return False
def flash_panda(panda_serial: str, remote_start: bool, ignore_ignition_line: bool) -> Panda:
try:
panda = Panda(panda_serial)
except PandaProtocolMismatch:
@@ -48,8 +68,8 @@ def flash_panda(panda_serial: str, remote_start: bool) -> Panda:
HARDWARE.recover_internal_panda()
raise
fw_path = get_expected_firmware_path(panda, remote_start)
fw_signature = get_expected_signature(panda, remote_start)
fw_path = get_expected_firmware_path(panda, remote_start, ignore_ignition_line)
fw_signature = get_expected_signature(panda, remote_start, ignore_ignition_line)
internal_panda = panda.is_internal()
panda_version = "bootstub" if panda.bootstub else panda.get_version()
@@ -133,8 +153,9 @@ def main() -> None:
# Flash pandas
pandas: list[Panda] = []
remote_start = get_remote_start_boots_comma(params)
ignore_ignition_line = get_ignore_ignition_line(params)
for serial in panda_serials:
pandas.append(flash_panda(serial, remote_start))
pandas.append(flash_panda(serial, remote_start, ignore_ignition_line))
# Ensure internal panda is present if expected
internal_pandas = [panda for panda in pandas if panda.is_internal()]
@@ -186,7 +207,7 @@ def main() -> None:
first_run = False
# run pandad with all connected serials as arguments
if get_remote_start_boots_comma(params):
if get_remote_start_boots_comma(params) or get_ignore_ignition_line(params):
os.environ["BOARDD_SKIP_FW_CHECK"] = "1"
else:
os.environ.pop("BOARDD_SKIP_FW_CHECK", None)
@@ -1,12 +1,15 @@
from __future__ import annotations
import threading
import time
import pyray as rl
from openpilot.system.hardware import HARDWARE
from openpilot.system.ui.lib.application import FontWeight, gui_app
from openpilot.system.ui.lib.multilang import tr, tr_noop
from openpilot.system.ui.widgets import DialogResult, Widget
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog, alert_dialog
from openpilot.system.ui.widgets.label import gui_label
from openpilot.system.ui.lib.text_measure import measure_text_cached
from openpilot.system.ui.widgets.option_dialog import MultiOptionDialog
@@ -143,10 +146,16 @@ class VehicleSettingsManagerView(PanelManagerView):
"set_state": lambda s: self._controller._on_toggle("GMDashSpoofOffsets"),
})
if cs.isGM and cs.hasOpenpilotLongitudinal:
toggles.append({
"title": tr("CAN Ignition Only"),
"subtitle": tr("Use Panda firmware that ignores the physical ignition line and starts only from CAN ignition."),
"get_state": lambda: self._controller._params.get_bool("IgnoreIgnitionLine"),
"set_state": lambda s: self._controller._on_panda_firmware_toggle("IgnoreIgnitionLine", tr("CAN Ignition Only requires a Panda firmware update.")),
})
toggles.append({
"title": tr("Remote Start Panda"),
"get_state": lambda: self._controller._params.get_bool("RemoteStartBootsComma"),
"set_state": lambda s: self._controller._on_toggle("RemoteStartBootsComma"),
"set_state": lambda s: self._controller._on_panda_firmware_toggle("RemoteStartBootsComma", tr("Remote Start requires a Panda firmware update.")),
})
if cs.isGM and cs.isVolt and not cs.hasSNG:
toggles.append({
@@ -559,6 +568,29 @@ class StarPilotVehicleSettingsLayout(_SettingsPage):
if param_key == "ForceFingerprint":
self._manager_view._rebuild_toggle_grid()
def _on_panda_firmware_toggle(self, param_key: str, prompt: str):
current = self._params.get_bool(param_key) if self._params.get(param_key) is not None else False
new_state = not current
def flash_and_reboot():
self._params_memory.put_bool("FlashPanda", True)
while self._params_memory.get_bool("FlashPanda"):
time.sleep(0.1)
HARDWARE.reboot()
def on_confirm(res):
if res != DialogResult.CONFIRM:
starpilot_state.update(force=True)
self._manager_view._rebuild_toggle_grid()
return
self._params.put_bool(param_key, new_state)
threading.Thread(target=flash_and_reboot, daemon=True).start()
starpilot_state.update(force=True)
self._manager_view._rebuild_toggle_grid()
gui_app.push_widget(alert_dialog(tr("Panda flashing started. Device will reboot when finished.")))
gui_app.push_widget(ConfirmDialog(prompt, tr("Flash"), callback=on_confirm))
def _on_select(self, key: str):
if key in ("CarMake", "CarModel") and not self._params.get_bool("ForceFingerprint"):
return
+22 -5
View File
@@ -173,26 +173,43 @@ def extract_zip(zip_file, extract_path):
print(f"Extraction completed!")
def get_selected_panda_firmware_name(app_fn, remote_start, ignore_ignition_line):
if not remote_start and not ignore_ignition_line:
return app_fn
h7 = app_fn == "panda_h7.bin.signed"
name_parts = ["panda_h7" if h7 else "panda"]
if remote_start:
name_parts.append("remote")
if ignore_ignition_line:
name_parts.append("can_ignition_only")
return "_".join(name_parts) + ".bin.signed"
def flash_panda(params_memory):
params = Params()
try:
remote_start = params.get_bool("RemoteStartBootsComma")
except Exception:
remote_start = False
try:
ignore_ignition_line = params.get_bool("IgnoreIgnitionLine")
except Exception:
ignore_ignition_line = False
for serial in Panda.list():
try:
with Panda(serial=serial) as panda:
print(f"Flashing Panda {serial}")
flash_fn = None
if remote_start:
app_fn = panda.get_mcu_type().config.app_fn
remote_fn = "panda_h7_remote.bin.signed" if app_fn == "panda_h7.bin.signed" else "panda_remote.bin.signed"
candidate = os.path.join(FW_PATH, remote_fn)
app_fn = panda.get_mcu_type().config.app_fn
selected_fn = get_selected_panda_firmware_name(app_fn, remote_start, ignore_ignition_line)
if selected_fn != app_fn:
candidate = os.path.join(FW_PATH, selected_fn)
if os.path.isfile(candidate):
flash_fn = candidate
else:
print(f"Remote-start panda firmware missing: {candidate}. Falling back to default firmware.")
print(f"Selected panda firmware missing: {candidate}. Falling back to default firmware.")
panda.flash(fn=flash_fn)
except Exception as exception:
print(f"Failed to flash Panda {serial}: {exception}")
+1
View File
@@ -1361,6 +1361,7 @@ class StarPilotVariables:
"GMDashSpoofOffsets",
condition=toggle.car_make == "gm" and toggle.has_pedal,
)
toggle.ignore_ignition_line = self.get_value("IgnoreIgnitionLine", condition=toggle.car_make == "gm")
toggle.long_pitch = self.get_value(
"LongPitch",
condition=toggle.openpilot_longitudinal and toggle.car_make == "gm",
@@ -2480,6 +2480,13 @@
"data_type": "bool",
"ui_type": "toggle"
},
{
"key": "IgnoreIgnitionLine",
"label": "Use CAN Ignition Only",
"description": "Use Panda firmware that ignores the physical ignition line and starts from CAN ignition only.\n\nRequires a Panda flash. Use this only on vehicles with reliable CAN ignition when the harness box reports false ignition.",
"data_type": "bool",
"ui_type": "toggle"
},
{
"key": "LongPitch",
"label": "Smooth Pedal Response on Hills",
+22 -17
View File
@@ -173,6 +173,7 @@ StarPilotVehiclesPanel::StarPilotVehiclesPanel(StarPilotSettingsWindow *parent,
{"GMToggles", tr("General Motors Settings"), tr("<b>StarPilot features for General Motors vehicles.</b>"), ""},
{"GMPedalLongitudinal", tr("Use Pedal For Longitudinal"), tr("<b>Use the pedal interceptor for full longitudinal control</b> on supported GM vehicles."), ""},
{"GMDashSpoofOffsets", tr("Apply Offsets To Dash Spoof"), tr("<b>On GM pedal-long cars, add the configured set-speed offset</b> to the spoofed dash set speed so it matches the on-screen set speed."), ""},
{"IgnoreIgnitionLine", tr("Use CAN Ignition Only"), tr("<b>Use CAN ignition only and ignore the physical ignition line in Panda firmware.</b><br><br>Requires a Panda flash. Use this only on vehicles with reliable CAN ignition when the harness box reports false ignition."), ""},
{"LongPitch", tr("Smooth Pedal Response on Hills"), tr("<b>Smoothen acceleration and braking</b> when driving downhill/uphill."), ""},
{"RemoteStartBootsComma", tr("Remote Start Boots comma"), tr("<b>Use the remote-start GM panda firmware at boot.</b><br><br>Required for GM remote-start startup signal behavior."), ""},
{"RemapCancelToDistance", tr("Remap Cancel Button"), tr("<b>On pedal-interceptor Bolts, treat the steering-wheel CANCEL button as an extra mappable button.</b>"), ""},
@@ -300,24 +301,28 @@ StarPilotVehiclesPanel::StarPilotVehiclesPanel(StarPilotSettingsWindow *parent,
});
}
ParamControl *remoteStartToggle = static_cast<ParamControl*>(toggles["RemoteStartBootsComma"]);
QObject::connect(remoteStartToggle, &ToggleControl::toggleFlipped, [parent, remoteStartToggle, this](bool state) {
const QString prompt = tr("Remote Start requires a Panda firmware update. Flash the Panda now?");
if (!StarPilotConfirmationDialog::yesorno(prompt, this)) {
params.putBool("RemoteStartBootsComma", !state);
remoteStartToggle->refresh();
return;
}
std::thread([parent, this]() {
parent->keepScreenOn = true;
params_memory.putBool("FlashPanda", true);
while (params_memory.getBool("FlashPanda")) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
auto connectPandaFlashToggle = [parent, this](const QString &key, const QString &prompt) {
ParamControl *toggle = static_cast<ParamControl*>(toggles[key]);
QObject::connect(toggle, &ToggleControl::toggleFlipped, [parent, key, prompt, toggle, this](bool state) {
if (!StarPilotConfirmationDialog::yesorno(prompt, this)) {
params.putBool(key.toStdString(), !state);
toggle->refresh();
return;
}
Hardware::reboot();
}).detach();
});
std::thread([parent, this]() {
parent->keepScreenOn = true;
params_memory.putBool("FlashPanda", true);
while (params_memory.getBool("FlashPanda")) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
Hardware::reboot();
}).detach();
});
};
connectPandaFlashToggle("IgnoreIgnitionLine", tr("CAN Ignition Only requires a Panda firmware update. Flash the Panda now?"));
connectPandaFlashToggle("RemoteStartBootsComma", tr("Remote Start requires a Panda firmware update. Flash the Panda now?"));
openDescriptions(forceOpenDescriptions, toggles);
+1 -1
View File
@@ -23,7 +23,7 @@ private:
std::map<QString, AbstractControl*> toggles;
QSet<QString> gmKeys = {"GMPedalLongitudinal", "GMDashSpoofOffsets", "LongPitch", "RemoteStartBootsComma", "RemapCancelToDistance", "VoltSNG"};
QSet<QString> gmKeys = {"GMPedalLongitudinal", "GMDashSpoofOffsets", "IgnoreIgnitionLine", "LongPitch", "RemoteStartBootsComma", "RemapCancelToDistance", "VoltSNG"};
QSet<QString> longitudinalKeys = {"FrogsGoMoosTweak", "GMDashSpoofOffsets", "LongPitch", "RemapCancelToDistance", "SNGHack", "VoltSNG"};
QSet<QString> subaruKeys = {"SubaruSNG", "SubaruSNGManualParkingBrake"};
QSet<QString> toyotaKeys = {"ClusterOffset", "FrogsGoMoosTweak", "LockDoorsTimer", "SNGHack", "ToyotaDoors"};
+1
View File
@@ -114,6 +114,7 @@ ForceTorqueController
FrogsGoMoosTweak
GMDashSpoofOffsets
GMPedalLongitudinal
IgnoreIgnitionLine
GoatScream
GoatScreamCriticalAlerts
GreenLightAlert