mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-29 19:03:59 +08:00
IONIQ
UI Build LKAS engage/disengage sound It will now make the same sound as op engage/disengage Wheel Control fix 2 build Feature: Model Name on Developer Sidebar Model Name port and correct DEVELOPER_SIDEBAR_METRICS slot numbering Wheel Controls button fixes Mode/Star buttons were not showing on desk LKAS option was showing even when car was using it for AOL, not it hides it if being used for AOL MapGears Sync Accel/Decel on UI MapGears will sync with "Longitudinal Tuning" UI to show chosen accel/decel profile to match with chosen drive mode. Drive mode -> accel/decel profile mapping. Eco → Eco Normal → Standard Sport → Sport+/Sport Writes on gear state change, gated on MapAcceleration/MapDeceleration toggles independently ECU Disable and Auto long/exp check ECU Disable and Auto long/exp check compile Star and Mode buttons to wheel control options cereal/custom.capnp — Added modePressed @16 and customPressed @17 fields to StarPilotCarState opendbc_repo/opendbc/car/hyundai/carstate.py — Added STEERING_WHEEL_MEDIA_BUTTONS (50Hz) to the CANFD CAN parser, initialized mode_button/custom_button state, and set fp_ret.modePressed/fp_ret.customPressed in update_canfd() common/params_keys.h — Added 6 new params: ModeButtonControl, LongModeButtonControl, VeryLongModeButtonControl, StarButtonControl, LongStarButtonControl, VeryLongStarButtonControl starpilot/common/starpilot_variables.py — Added full short/long/very-long toggle processing for both Mode and Star buttons (gated on HyundaiFlags.CANFD), with has_canfd_media_buttons flag propagated to toggles starpilot/controls/starpilot_card.py — Added Mode and Star button press counters and short/long/very-long press event handling, mirroring the existing distance button logic selfdrive/ui/layouts/settings/starpilot/wheel.py — Added 6 new tiles for Mode/Star buttons (short, long, very long each), visible only when cs.isHKGCanFd is true MapGears for HKG Add HKG "Drive Modes" button ability to map to eco/normal/sport accel profiles. Dashboard speed limit reading for CANFD Add FR_CMR_02_100ms to CAN parsers in get_can_parsers_canfd: on ECAN (freq=10) for LKA_STEERING cars, on CAM (freq=0, optional) for all others calculate_canfd_speed_limit and fp_ret.dashboardSpeedLimit assignment were already present CAM bus uses freq=0 to avoid breaking canValid on non-LKA cars that don't have this message CANFD steering limits Raises STEER_MAX to 409 Speed-dependent deltas (DELTA_UP=10/DOWN=8 below 15 m/s, UP=2/DOWN=3 above), Update panda safety ceiling and tests to match. Removes TacoTuneHacks toggle dependency. Ioniq 6 toml values update Updated toml values closer to learned values DBC Update HKG Signals Added 5 new messages to hyundai_canfd.dbc and hyundai_canfd_generated.dbc: DRIVE_MODE_EV (0x205): EV drive mode state with Normal/Eco/Sport (button) values. Can be used to change acceleration profiles in openpilot based on drive mode. CAM_0x361 (0x361): Camera sign recognition with SIGN_TYPE and SIGN_TYPE_2 signals. Dashboard Speed Limit. Can be used as source for SLC. ADAS_0x380 (0x380): ADAS stop sign detection bit. Dashboard stop sign alert. Triggers 80-90ft before stop sign typically and can be used to help stopping for stop signs. DOOR_LOCK (0x414): Not actual command to lock/unlock, but may be used to detect lock state changes. STEERING_WHEEL_MEDIA_BUTTONS (0x448): Steering wheel button inputs (voice, phone, mode, next/prev, menu, scroll, custom) Can be used to assign custom functions to steering wheel buttons in openpilot. Targets Ioniq 6 but may apply to other Hyundai CAN FD vehicles.
This commit is contained in:
@@ -8,6 +8,8 @@
|
||||
**StarPilot** is a custom fork of [comma.ai's openpilot](https://comma.ai/openpilot),
|
||||
an open source driver assistance system.
|
||||
|
||||
whoisdomi
|
||||
|
||||
Openpilot provides
|
||||
* Automated Lane Centering
|
||||
* Adaptive Cruise Control
|
||||
|
||||
@@ -82,6 +82,9 @@ struct StarPilotCarState @0xf35cc4560bbf6ec2 {
|
||||
sportGear @13 :Bool;
|
||||
trafficModeEnabled @14 :Bool;
|
||||
gasStack @15 :Bool; # Compatibility with older StarPilot payloads
|
||||
modePressed @16 :Bool;
|
||||
customPressed @17 :Bool;
|
||||
alwaysOnLateralAllowed @18 :Bool;
|
||||
}
|
||||
|
||||
struct StarPilotDeviceState @0xda96579883444c35 {
|
||||
@@ -147,6 +150,8 @@ struct StarPilotOnroadEvent @0xa5cd762cd951a455 {
|
||||
youveGotMail @28;
|
||||
switchbackModeActive @29;
|
||||
switchbackModeInactive @30;
|
||||
lkasEnable @31;
|
||||
lkasDisable @32;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"DoShutdown", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"DoUninstall", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"DriverTooDistracted", {CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON, BOOL}},
|
||||
{"EcuDisableFailed", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"AlphaLongitudinalEnabled", {PERSISTENT, BOOL}},
|
||||
{"ExperimentalLongitudinalEnabled", {PERSISTENT, BOOL}},
|
||||
{"ExperimentalMode", {PERSISTENT, BOOL}},
|
||||
@@ -329,6 +330,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"LockDoors", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"LockDoorsTimer", {PERSISTENT, INT, "0", "0", 0}},
|
||||
{"LongDistanceButtonControl", {PERSISTENT, INT, "5", "0", 2}},
|
||||
{"LongModeButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"LongStarButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"LongitudinalActuatorDelay", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"LongitudinalActuatorDelayStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"LateralManeuverStatus", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, JSON, "{}", "{}"}},
|
||||
@@ -358,6 +361,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"MaxDesiredAcceleration", {PERSISTENT, FLOAT, "4.0", "2.0", 2}},
|
||||
{"MinimumBackupSize", {PERSISTENT, INT, "0", "0"}},
|
||||
{"MinimumLaneChangeSpeed", {PERSISTENT, FLOAT, "20.0", "20.0", 2}},
|
||||
{"ModeButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"ModelDownloadProgress", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"ModelDrivesAndScores", {PERSISTENT, JSON, "{}", "{}"}},
|
||||
{"ModelReleasedDates", {PERSISTENT, STRING, "", "", 1}},
|
||||
@@ -517,6 +521,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"StopDistance", {PERSISTENT, FLOAT, "6.0", "6.0", 2}},
|
||||
{"StoppingDecelRate", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"StoppingDecelRateStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"StarButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"SwitchbackModeCooldown", {PERSISTENT, INT, "5", "0", 2}},
|
||||
{"SwitchbackModeEnabled", {CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0", "0"}},
|
||||
{"SubaruSNG", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
@@ -555,6 +560,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"VEgoStopping", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"VEgoStoppingStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"VeryLongDistanceButtonControl", {PERSISTENT, INT, "6", "0", 2}},
|
||||
{"VeryLongModeButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"VeryLongStarButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"VoltSNG", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"WarningImmediateVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"WarningSoftVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
|
||||
@@ -1,10 +1,24 @@
|
||||
import time
|
||||
import datetime
|
||||
from opendbc.car.carlog import carlog
|
||||
from opendbc.car.isotp_parallel_query import IsoTpParallelQuery
|
||||
|
||||
EXT_DIAG_REQUEST = b'\x10\x03'
|
||||
EXT_DIAG_RESPONSE = b'\x50\x03'
|
||||
|
||||
COM_CONT_RESPONSE = b''
|
||||
# File-based logging for debugging
|
||||
ECU_LOG_FILE = "/data/ecu_disable.log"
|
||||
|
||||
def ecu_log(msg):
|
||||
"""Write to both carlog and a dedicated log file for debugging."""
|
||||
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
||||
log_line = f"[{timestamp}] {msg}"
|
||||
carlog.warning(msg)
|
||||
try:
|
||||
with open(ECU_LOG_FILE, "a") as f:
|
||||
f.write(log_line + "\n")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def disable_ecu(can_recv, can_send, bus=0, addr=0x7d0, sub_addr=None, com_cont_req=b'\x28\x83\x01', timeout=0.1, retry=10):
|
||||
@@ -13,24 +27,64 @@ def disable_ecu(can_recv, can_send, bus=0, addr=0x7d0, sub_addr=None, com_cont_r
|
||||
|
||||
This is used to disable the radar in some cars. Openpilot will emulate the radar.
|
||||
WARNING: THIS DISABLES AEB!"""
|
||||
carlog.warning(f"ecu disable {hex(addr), sub_addr} ...")
|
||||
ecu_log(f"=== ECU DISABLE START === addr={hex(addr)}, bus={bus}")
|
||||
|
||||
# Try multiple times with different approaches
|
||||
for i in range(retry):
|
||||
try:
|
||||
# Enter extended diagnostic session
|
||||
ecu_log(f"attempt {i+1}/{retry}: diag session...")
|
||||
query = IsoTpParallelQuery(can_send, can_recv, bus, [(addr, sub_addr)], [EXT_DIAG_REQUEST], [EXT_DIAG_RESPONSE])
|
||||
|
||||
for _, _ in query.get_data(timeout).items():
|
||||
carlog.warning("communication control disable tx/rx ...")
|
||||
ecu_log("diag session OK")
|
||||
|
||||
query = IsoTpParallelQuery(can_send, can_recv, bus, [(addr, sub_addr)], [com_cont_req], [COM_CONT_RESPONSE])
|
||||
query.get_data(0)
|
||||
# Small delay to let ECU fully enter diagnostic mode
|
||||
time.sleep(0.05)
|
||||
|
||||
carlog.warning("ecu disabled")
|
||||
return True
|
||||
# Send CC command and log the response
|
||||
ecu_log("sending CC...")
|
||||
cc_query = IsoTpParallelQuery(can_send, can_recv, bus, [(addr, sub_addr)], [com_cont_req], [b''])
|
||||
cc_response = cc_query.get_data(timeout)
|
||||
|
||||
except Exception:
|
||||
carlog.exception("ecu disable exception")
|
||||
# Log what we got back
|
||||
cc_success = False
|
||||
cc_rejected = False
|
||||
for (rx_addr, _), data in cc_response.items():
|
||||
ecu_log(f"CC response: {data.hex() if data else 'empty'}")
|
||||
# Check for positive response (0x68 = 0x28 + 0x40)
|
||||
if len(data) >= 1 and data[0] == 0x68:
|
||||
ecu_log("=== ECU DISABLE CONFIRMED ===")
|
||||
cc_success = True
|
||||
# Check for negative response
|
||||
elif len(data) >= 3 and data[0] == 0x7F:
|
||||
nrc = data[2]
|
||||
nrc_meanings = {
|
||||
0x12: "subFunctionNotSupported",
|
||||
0x13: "incorrectMessageLengthOrInvalidFormat",
|
||||
0x22: "conditionsNotCorrect (car must be in IGN-ON, not READY)",
|
||||
0x31: "requestOutOfRange",
|
||||
0x33: "securityAccessDenied",
|
||||
}
|
||||
nrc_name = nrc_meanings.get(nrc, "unknown")
|
||||
ecu_log(f"CC rejected: NRC 0x{nrc:02x} = {nrc_name}")
|
||||
cc_rejected = True
|
||||
|
||||
carlog.error(f"ecu disable retry ({i + 1}) ...")
|
||||
carlog.error("ecu disable failed")
|
||||
if cc_success:
|
||||
return True
|
||||
elif cc_rejected:
|
||||
# ECU explicitly rejected - don't retry, it won't work
|
||||
ecu_log("=== ECU DISABLE REJECTED ===")
|
||||
return False
|
||||
else:
|
||||
# No response - consider it sent (ECU might have stopped responding)
|
||||
ecu_log("=== ECU DISABLE SENT (no response) ===")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
ecu_log(f"attempt {i+1} exception: {e}")
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
ecu_log("=== ECU DISABLE FAILED ===")
|
||||
return False
|
||||
|
||||
@@ -8,6 +8,7 @@ from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CAR
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
from openpilot.common.params import Params
|
||||
|
||||
VisualAlert = structs.CarControl.HUDControl.VisualAlert
|
||||
LongCtrlState = structs.CarControl.Actuators.LongControlState
|
||||
@@ -57,11 +58,16 @@ class CarController(CarControllerBase):
|
||||
self.apply_angle_last = 0.0
|
||||
self.car_fingerprint = CP.carFingerprint
|
||||
self.last_button_frame = 0
|
||||
self.ecu_disable_failed = False
|
||||
self._ecu_disable_checked = False
|
||||
self._params = Params()
|
||||
self.long_active_ecu = self.CP.openpilotLongitudinalControl
|
||||
|
||||
def update(self, CC, CS, now_nanos, starpilot_toggles):
|
||||
actuators = CC.actuators
|
||||
hud_control = CC.hudControl
|
||||
|
||||
self.params = CarControllerParams(self.CP, CS.out.vEgoRaw)
|
||||
apply_angle = CS.out.steeringAngleDeg
|
||||
|
||||
if self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
|
||||
@@ -107,10 +113,19 @@ class CarController(CarControllerBase):
|
||||
|
||||
can_sends = []
|
||||
|
||||
# Check EcuDisableFailed once after init() has run
|
||||
if not self._ecu_disable_checked and self.frame > 0:
|
||||
self.ecu_disable_failed = self._params.get_bool("EcuDisableFailed")
|
||||
self._ecu_disable_checked = True
|
||||
|
||||
# When ECU disable was skipped (car started in READY mode), don't send any
|
||||
# longitudinal messages - stock ECU is still active and these would conflict
|
||||
self.long_active_ecu = self.CP.openpilotLongitudinalControl and not self.ecu_disable_failed
|
||||
|
||||
# *** common hyundai stuff ***
|
||||
|
||||
# tester present - w/ no response (keeps relevant ECU disabled)
|
||||
if self.frame % 100 == 0 and not (self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC) and self.CP.openpilotLongitudinalControl:
|
||||
if self.frame % 100 == 0 and not (self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC) and self.long_active_ecu:
|
||||
# for longitudinal control, either radar or ADAS driving ECU
|
||||
addr, bus = 0x7d0, self.CAN.ECAN if self.CP.flags & HyundaiFlags.CANFD else 0
|
||||
if self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING.value:
|
||||
@@ -155,7 +170,7 @@ class CarController(CarControllerBase):
|
||||
left_lane_warning, right_lane_warning))
|
||||
|
||||
# Button messages
|
||||
if not self.CP.openpilotLongitudinalControl:
|
||||
if not self.long_active_ecu:
|
||||
if CC.cruiseControl.cancel:
|
||||
can_sends.append(hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.CANCEL, self.CP))
|
||||
elif CC.cruiseControl.resume:
|
||||
@@ -166,7 +181,7 @@ class CarController(CarControllerBase):
|
||||
if (self.frame - self.last_button_frame) * DT_CTRL >= 0.15:
|
||||
self.last_button_frame = self.frame
|
||||
|
||||
if self.frame % 2 == 0 and self.CP.openpilotLongitudinalControl:
|
||||
if self.frame % 2 == 0 and self.long_active_ecu:
|
||||
# TODO: unclear if this is needed
|
||||
jerk = 3.0 if actuators.longControlState == LongCtrlState.pid else 1.0
|
||||
use_fca = self.CP.flags & HyundaiFlags.USE_FCA.value
|
||||
@@ -179,11 +194,11 @@ class CarController(CarControllerBase):
|
||||
can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled))
|
||||
|
||||
# 5 Hz ACC options
|
||||
if self.frame % 20 == 0 and self.CP.openpilotLongitudinalControl:
|
||||
if self.frame % 20 == 0 and self.long_active_ecu:
|
||||
can_sends.extend(hyundaican.create_acc_opt(self.packer, self.CP))
|
||||
|
||||
# 2 Hz front radar options
|
||||
if self.frame % 50 == 0 and self.CP.openpilotLongitudinalControl:
|
||||
if self.frame % 50 == 0 and self.long_active_ecu:
|
||||
can_sends.append(hyundaican.create_frt_radar_opt(self.packer))
|
||||
|
||||
return can_sends
|
||||
@@ -192,7 +207,7 @@ class CarController(CarControllerBase):
|
||||
can_sends = []
|
||||
|
||||
lka_steering = self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING
|
||||
lka_steering_long = lka_steering and self.CP.openpilotLongitudinalControl
|
||||
lka_steering_long = lka_steering and self.long_active_ecu
|
||||
|
||||
# steering control
|
||||
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled,
|
||||
@@ -211,7 +226,7 @@ class CarController(CarControllerBase):
|
||||
if lka_steering and self.CP.flags & HyundaiFlags.ENABLE_BLINKERS:
|
||||
can_sends.extend(hyundaicanfd.create_spas_messages(self.packer, self.CAN, CC.leftBlinker, CC.rightBlinker))
|
||||
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
if self.long_active_ecu:
|
||||
if lka_steering:
|
||||
can_sends.extend(hyundaicanfd.create_adrv_messages(self.packer, self.CAN, self.frame))
|
||||
else:
|
||||
|
||||
@@ -42,6 +42,8 @@ class CarState(CarStateBase):
|
||||
self.cruise_buttons: deque = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
|
||||
self.main_buttons: deque = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
|
||||
self.lda_button = 0
|
||||
self.mode_button = 0
|
||||
self.custom_button = 0
|
||||
|
||||
self.gear_msg_canfd = "ACCELERATOR" if CP.flags & HyundaiFlags.EV else \
|
||||
"GEAR_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS else \
|
||||
@@ -287,7 +289,7 @@ class CarState(CarStateBase):
|
||||
# cruise state
|
||||
# CAN FD cars enable on main button press, set available if no TCS faults preventing engagement
|
||||
ret.cruiseState.available = cp.vl["TCS"]["ACCEnable"] == 0
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
if self.CP.openpilotLongitudinalControl and not self.CP.pcmCruise:
|
||||
# These are not used for engage/disengage since openpilot keeps track of state using the buttons
|
||||
ret.cruiseState.enabled = cp.vl["TCS"]["ACC_REQ"] == 1
|
||||
ret.cruiseState.standstill = False
|
||||
@@ -328,19 +330,37 @@ class CarState(CarStateBase):
|
||||
fp_ret = custom.StarPilotCarState.new_message()
|
||||
fp_ret.dashboardSpeedLimit = calculate_canfd_speed_limit(self.CP, self.FPCP, cp, cp_cam, speed_factor)
|
||||
|
||||
if self.CP.flags & HyundaiFlags.EV:
|
||||
drive_mode = cp.vl["DRIVE_MODE_EV"]["DRIVE_MODE"]
|
||||
fp_ret.ecoGear = (drive_mode == 4)
|
||||
fp_ret.sportGear = (drive_mode == 5)
|
||||
|
||||
self.mode_button = cp.vl["STEERING_WHEEL_MEDIA_BUTTONS"]["MODE_BUTTON"]
|
||||
self.custom_button = cp.vl["STEERING_WHEEL_MEDIA_BUTTONS"]["CUSTOM_BUTTON"]
|
||||
fp_ret.modePressed = bool(self.mode_button)
|
||||
fp_ret.customPressed = bool(self.custom_button)
|
||||
|
||||
return ret, fp_ret
|
||||
|
||||
def get_can_parsers_canfd(self, CP):
|
||||
msgs = []
|
||||
cam_msgs = []
|
||||
if not (CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS):
|
||||
# TODO: this can be removed once we add dynamic support to vl_all
|
||||
msgs += [
|
||||
# this message is 50Hz but the ECU frequently stops transmitting for ~0.5s
|
||||
("CRUISE_BUTTONS", 1)
|
||||
]
|
||||
if CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
|
||||
msgs.append(("FR_CMR_02_100ms", 10))
|
||||
else:
|
||||
cam_msgs.append(("FR_CMR_02_100ms", 0)) # optional: not all non-LKA CANFD cars have this on CAM bus
|
||||
if CP.flags & HyundaiFlags.EV:
|
||||
msgs.append(("DRIVE_MODE_EV", 10))
|
||||
msgs.append(("STEERING_WHEEL_MEDIA_BUTTONS", 50))
|
||||
return {
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], msgs, CanBus(CP).ECAN),
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).CAM),
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], cam_msgs, CanBus(CP).CAM),
|
||||
}
|
||||
|
||||
def get_can_parsers(self, CP):
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import time
|
||||
from opendbc.car import Bus, get_safety_config, structs, uds
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, CAR, DBC, \
|
||||
CANFD_UNSUPPORTED_LONGITUDINAL_CAR, \
|
||||
CANFD_SECURITYACCESS_CAR, \
|
||||
UNSUPPORTED_LONGITUDINAL_CAR, HyundaiSafetyFlags
|
||||
from opendbc.car.hyundai.radar_interface import RADAR_START_ADDR
|
||||
from opendbc.car.interfaces import CarInterfaceBase
|
||||
from opendbc.car.disable_ecu import disable_ecu
|
||||
from opendbc.car.disable_ecu import disable_ecu, ecu_log
|
||||
from opendbc.car.hyundai.carcontroller import CarController
|
||||
from opendbc.car.hyundai.carstate import CarState
|
||||
from opendbc.car.hyundai.radar_interface import RadarInterface
|
||||
@@ -16,6 +18,9 @@ Ecu = structs.CarParams.Ecu
|
||||
# Cancel button can sometimes be ACC pause/resume button, main button can also enable on some cars
|
||||
ENABLE_BUTTONS = (ButtonType.accelCruise, ButtonType.decelCruise, ButtonType.cancel, ButtonType.mainCruise)
|
||||
|
||||
# Track when ECU disable happened - used to permanently suppress CAN errors from disabled ECU
|
||||
ECU_DISABLE_TIMESTAMP = 0.0
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
CarState = CarState
|
||||
@@ -37,8 +42,9 @@ class CarInterface(CarInterfaceBase):
|
||||
if ret.flags & HyundaiFlags.CANFD:
|
||||
# Shared configuration for CAN-FD cars
|
||||
ret.alphaLongitudinalAvailable = candidate not in CANFD_UNSUPPORTED_LONGITUDINAL_CAR
|
||||
if lka_steering and Ecu.adas not in [fw.ecu for fw in car_fw]:
|
||||
if lka_steering and Ecu.adas not in [fw.ecu for fw in car_fw] and candidate not in CANFD_SECURITYACCESS_CAR:
|
||||
# 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
|
||||
|
||||
ret.enableBsm = 0x1ba in fingerprint[CAN.ECAN]
|
||||
@@ -59,10 +65,6 @@ class CarInterface(CarInterfaceBase):
|
||||
if not ret.flags & HyundaiFlags.RADAR_SCC:
|
||||
ret.flags |= HyundaiFlags.CANFD_CAMERA_SCC.value
|
||||
|
||||
# Ioniq 6 HDA2 uses the camera SCC path for stock ACC, even on LKA steering variants.
|
||||
if candidate == CAR.HYUNDAI_IONIQ_6:
|
||||
ret.flags |= HyundaiFlags.CANFD_CAMERA_SCC.value
|
||||
|
||||
# Some LKA steering cars have alternative messages for gear checks
|
||||
# ICE cars do not have 0x130; GEARS message on 0x40 or 0x70 instead
|
||||
if 0x130 not in fingerprint[CAN.ECAN]:
|
||||
@@ -143,6 +145,10 @@ class CarInterface(CarInterfaceBase):
|
||||
if ret.flags & HyundaiFlags.NON_SCC:
|
||||
ret.alphaLongitudinalAvailable = False
|
||||
ret.openpilotLongitudinalControl = alpha_long and ret.alphaLongitudinalAvailable
|
||||
# When longitudinal is enabled, we disable the ADAS ECU which stops radar messages
|
||||
# Force radarUnavailable to prevent CAN Error from missing radar messages
|
||||
if ret.openpilotLongitudinalControl:
|
||||
ret.radarUnavailable = True
|
||||
ret.pcmCruise = not ret.openpilotLongitudinalControl
|
||||
ret.startingState = True
|
||||
ret.vEgoStarting = 0.1
|
||||
@@ -172,15 +178,50 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
@staticmethod
|
||||
def init(CP, can_recv, can_send, communication_control=None):
|
||||
# 0x80 silences response
|
||||
global ECU_DISABLE_TIMESTAMP
|
||||
from openpilot.common.params import Params
|
||||
params = Params()
|
||||
|
||||
if communication_control is None:
|
||||
communication_control = bytes([uds.SERVICE_TYPE.COMMUNICATION_CONTROL, 0x80 | uds.CONTROL_TYPE.DISABLE_RX_DISABLE_TX, uds.MESSAGE_TYPE.NORMAL])
|
||||
if CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
|
||||
# Don't use 0x80 suppress bit so we can read the ECU response.
|
||||
# Use ENABLE_RX_DISABLE_TX (0x01) so the ECU can still receive from rear radars for BSM
|
||||
# while blocking SCC TX.
|
||||
communication_control = bytes([uds.SERVICE_TYPE.COMMUNICATION_CONTROL, uds.CONTROL_TYPE.ENABLE_RX_DISABLE_TX, uds.MESSAGE_TYPE.NORMAL])
|
||||
else:
|
||||
# 0x80 silences response for other cars (original behavior)
|
||||
communication_control = bytes([uds.SERVICE_TYPE.COMMUNICATION_CONTROL, 0x80 | uds.CONTROL_TYPE.DISABLE_RX_DISABLE_TX, uds.MESSAGE_TYPE.NORMAL])
|
||||
|
||||
ecu_log(f"=== init() called: opLong={CP.openpilotLongitudinalControl}, flags=0x{CP.flags:x}, safetyParam={CP.safetyConfigs[-1].safetyParam} ===")
|
||||
|
||||
if CP.openpilotLongitudinalControl and not (CP.flags & (HyundaiFlags.CANFD_CAMERA_SCC | HyundaiFlags.CAMERA_SCC)):
|
||||
addr, bus = 0x7d0, CanBus(CP).ECAN if CP.flags & HyundaiFlags.CANFD else 0
|
||||
if CP.flags & HyundaiFlags.CANFD_LKA_STEERING.value:
|
||||
addr, bus = 0x730, CanBus(CP).ECAN
|
||||
disable_ecu(can_recv, can_send, bus=bus, addr=addr, com_cont_req=communication_control)
|
||||
|
||||
# Try ECU disable. If it succeeds (IGN-ON mode), enable longitudinal.
|
||||
# If it fails (READY mode returns NRC 0x22, or timeout), strip LONG safety flag
|
||||
# so panda forwards stock SCC messages normally (lateral-only mode).
|
||||
ecu_log(f"=== ECU DISABLE attempt: addr=0x{addr:x}, bus={bus} ===")
|
||||
ecu_disabled = disable_ecu(can_recv, can_send, bus=bus, addr=addr, com_cont_req=communication_control)
|
||||
|
||||
if CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
|
||||
# Ioniq 6: track success/failure to auto-switch between openpilot long and stock ACC
|
||||
if ecu_disabled:
|
||||
ECU_DISABLE_TIMESTAMP = time.monotonic()
|
||||
params.put_bool("EcuDisableFailed", False)
|
||||
params.put_bool("ExperimentalMode", True)
|
||||
ecu_log("=== ECU DISABLE SUCCESS - Longitudinal + Experimental ENABLED ===")
|
||||
else:
|
||||
params.put_bool("EcuDisableFailed", True)
|
||||
CP.safetyConfigs[-1].safetyParam &= ~HyundaiSafetyFlags.LONG.value
|
||||
ecu_log(f"=== ECU DISABLE FAILED - safetyParam stripped to {CP.safetyConfigs[-1].safetyParam}, lateral-only mode ===")
|
||||
else:
|
||||
# Other cars: just log, don't change safety params or params store
|
||||
if ecu_disabled:
|
||||
ecu_log("=== ECU DISABLE SUCCESS ===")
|
||||
else:
|
||||
ecu_log("=== ECU DISABLE FAILED ===")
|
||||
|
||||
# for blinkers
|
||||
if CP.flags & HyundaiFlags.ENABLE_BLINKERS:
|
||||
@@ -190,3 +231,38 @@ class CarInterface(CarInterfaceBase):
|
||||
def deinit(CP, can_recv, can_send):
|
||||
communication_control = bytes([uds.SERVICE_TYPE.COMMUNICATION_CONTROL, 0x80 | uds.CONTROL_TYPE.ENABLE_RX_ENABLE_TX, uds.MESSAGE_TYPE.NORMAL])
|
||||
CarInterface.init(CP, can_recv, can_send, communication_control)
|
||||
|
||||
def update(self, can_packets, starpilot_toggles):
|
||||
ret, fp_ret = super().update(can_packets, starpilot_toggles)
|
||||
|
||||
# When ECU disable was skipped (READY mode boot) or failed, suppress CAN timeout errors.
|
||||
# Keep checking param until it's True (init() sets it AFTER first update() call),
|
||||
# then cache to avoid per-frame param reads.
|
||||
if not getattr(self, '_ecu_disable_failed_cached', False):
|
||||
from openpilot.common.params import Params
|
||||
self._ecu_disable_failed_cached = Params().get_bool("EcuDisableFailed")
|
||||
if self._ecu_disable_failed_cached and not ret.canValid:
|
||||
ret.canValid = True
|
||||
|
||||
global ECU_DISABLE_TIMESTAMP
|
||||
if ECU_DISABLE_TIMESTAMP > 0 and not ret.canValid:
|
||||
# Check if any parser has counter/checksum errors (real CAN issues)
|
||||
has_counter_errors = False
|
||||
for cp in self.can_parsers.values():
|
||||
if cp is not None:
|
||||
for state in cp.message_states.values():
|
||||
if state.counter_fail >= 5: # MAX_BAD_COUNTER from parser.py
|
||||
has_counter_errors = True
|
||||
ecu_log(f"REAL CAN ERROR: {state.name} counter_fail={state.counter_fail}")
|
||||
break
|
||||
if has_counter_errors:
|
||||
break
|
||||
|
||||
if has_counter_errors:
|
||||
# Real CAN error - don't suppress, let it through
|
||||
ecu_log("ECU_DISABLE: NOT suppressing canValid - counter errors detected")
|
||||
else:
|
||||
# Only timeout errors (expected after ECU disable) - suppress silently
|
||||
ret.canValid = True
|
||||
|
||||
return ret, fp_ret
|
||||
|
||||
@@ -28,7 +28,7 @@ class CarControllerParams:
|
||||
ANGLE_MIN_TORQUE_REDUCTION_GAIN = 0.1
|
||||
ANGLE_ACTIVE_TORQUE_REDUCTION_GAIN = 0.6
|
||||
|
||||
def __init__(self, CP):
|
||||
def __init__(self, CP, vEgoRaw=100.):
|
||||
self.STEER_DELTA_UP = 3
|
||||
self.STEER_DELTA_DOWN = 7
|
||||
self.STEER_DRIVER_ALLOWANCE = 50
|
||||
@@ -38,12 +38,16 @@ class CarControllerParams:
|
||||
self.STEER_STEP = 1 # 100 Hz
|
||||
|
||||
if CP.flags & HyundaiFlags.CANFD:
|
||||
self.STEER_MAX = 270
|
||||
self.STEER_DRIVER_ALLOWANCE = 250
|
||||
self.STEER_MAX = 409
|
||||
self.STEER_DRIVER_ALLOWANCE = 100
|
||||
self.STEER_DRIVER_MULTIPLIER = 2
|
||||
self.STEER_THRESHOLD = 250
|
||||
self.STEER_DELTA_UP = 2
|
||||
self.STEER_DELTA_DOWN = 3
|
||||
self.STEER_THRESHOLD = 100
|
||||
if vEgoRaw < 15.0: # below ~34 mph - more aggressive for tight turns
|
||||
self.STEER_DELTA_UP = 10
|
||||
self.STEER_DELTA_DOWN = 8
|
||||
else:
|
||||
self.STEER_DELTA_UP = 2
|
||||
self.STEER_DELTA_DOWN = 3
|
||||
|
||||
if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
|
||||
self.STEER_THRESHOLD = 175
|
||||
@@ -377,7 +381,7 @@ class CAR(Platforms):
|
||||
HYUNDAI_IONIQ_6 = HyundaiCanFDPlatformConfig(
|
||||
[HyundaiCarDocs("Hyundai Ioniq 6 (with HDA II) 2023-24", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p]))],
|
||||
HYUNDAI_IONIQ_5.specs,
|
||||
flags=HyundaiFlags.EV,
|
||||
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT | HyundaiFlags.CANFD_NO_RADAR_DISABLE,
|
||||
)
|
||||
HYUNDAI_TUCSON_4TH_GEN = HyundaiCanFDPlatformConfig(
|
||||
[
|
||||
@@ -817,7 +821,9 @@ CAN_GEARS = {
|
||||
CANFD_CAR = CAR.with_flags(HyundaiFlags.CANFD)
|
||||
CANFD_RADAR_SCC_CAR = CAR.with_flags(HyundaiFlags.RADAR_SCC) # TODO: merge with UNSUPPORTED_LONGITUDINAL_CAR
|
||||
|
||||
CANFD_UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.CANFD_NO_RADAR_DISABLE) # TODO: merge with UNSUPPORTED_LONGITUDINAL_CAR
|
||||
# Cars with CANFD_NO_RADAR_DISABLE that now work with SecurityAccess handshake
|
||||
CANFD_SECURITYACCESS_CAR = {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
|
||||
|
||||
CAMERA_SCC_CAR = CAR.with_flags(HyundaiFlags.CAMERA_SCC)
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"GMC_ACADIA" = [1.6, 1.6, 0.2]
|
||||
"LEXUS_IS_TSS2" = [2.0, 2.0, 0.1]
|
||||
"HYUNDAI_KONA_EV_2ND_GEN" = [2.5, 2.5, 0.1]
|
||||
"HYUNDAI_IONIQ_6" = [2.5, 2.5, 0.005]
|
||||
"HYUNDAI_IONIQ_6" = [3.0, 3.0, 0.09]
|
||||
"HYUNDAI_AZERA_6TH_GEN" = [1.8, 1.8, 0.1]
|
||||
"HYUNDAI_AZERA_HEV_6TH_GEN" = [1.8, 1.8, 0.1]
|
||||
"KIA_K8_HEV_1ST_GEN" = [2.5, 2.5, 0.1]
|
||||
|
||||
@@ -514,6 +514,11 @@ BO_ 512 ADRV_0x200: 8 ADRV
|
||||
SG_ SET_ME_E1 : 24|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ SET_ME_3A : 32|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 517 DRIVE_MODE_EV: 8 XXX
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ DRIVE_MODE : 24|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 593 RADAR_0x251: 16 FRONT_RADAR
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
@@ -574,6 +579,10 @@ BO_ 837 ADRV_0x345: 8 ADRV
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ SET_ME_15 : 24|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 865 CAM_0x361: 32 CAMERA
|
||||
SG_ SIGN_TYPE : 208|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ SIGN_TYPE_2 : 240|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 866 CAM_0x362: 32 CAMERA
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
@@ -614,6 +623,9 @@ BO_ 874 BLINDSPOTS_FRONT_CORNER_2: 16 XXX
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 896 ADAS_0x380: 24 XXX
|
||||
SG_ STOP_SIGN : 83|1@1+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 961 BLINKER_STALKS: 8 XXX
|
||||
SG_ CHECKSUM_MAYBE : 7|8@0+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER_ALT : 15|4@0+ (1,0) [0|15] "" XXX
|
||||
@@ -643,6 +655,23 @@ BO_ 1043 BLINKERS: 8 XXX
|
||||
SG_ RIGHT_LAMP_ALT : 61|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ USE_ALT_LAMP : 62|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 1044 DOOR_LOCK: 8 XXX
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ DOOR_LOCK_STATE : 37|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ DOOR_LOCK_ACTION : 40|1@1+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 1096 STEERING_WHEEL_MEDIA_BUTTONS: 8 XXX
|
||||
SG_ VOICE_BUTTON : 16|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ PHONE_BUTTON : 18|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ MODE_BUTTON : 22|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ RIGHT_SCROLL_PRESS : 24|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ NEXT_BUTTON : 26|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ PREVIOUS_BUTTON : 28|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ MENU_BUTTON : 34|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ LEFT_SCROLL_PRESS : 38|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ CUSTOM_BUTTON : 44|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 1144 DRIVE_MODE: 8 XXX
|
||||
SG_ DRIVE_MODE : 0|16@1+ (1,-61611) [0|61611] "" XXX
|
||||
SG_ DRIVE_MODE2 : 28|3@1+ (1,0) [1|3] "" XXX
|
||||
@@ -940,6 +969,7 @@ VAL_ 506 ISLA_AutoUsmSta 0 "None Auto Function (Delete Menu)" 1 "Auto Off" 2 "Au
|
||||
VAL_ 506 ISLA_Cntry 0 "Europe/Russia/Australia" 1 "Domestic" 2 "China" 3 "USA" 4 "Canada" 5 "Australia" 6 "Reserved" 7 "Reserved" 8 "Reserved" 9 "Reserved" 10 "Reserved" 11 "Reserved" 12 "Reserved" 13 "Reserved" 14 "Reserved" 15 "Initial Value (default)";
|
||||
VAL_ 506 ISLA_AddtnlSign 0 "No Recognition (default)" 1 "School Crossing" 16 "Do Not Pass" 17 "Reserved" 18 "Reserved" 19 "Reserved" 20 "Reserved" 21 "Reserved" 22 "Reserved" 23 "Reserved" 24 "Exit" 25 "Roundabout" 26 "Right Curve" 27 "Left Curve" 28 "Winding Road" 29 "Reserved" 30 "Reserved" 31 "Reserved" 2 "Pedestrian Crossing" 3 "Bicycle Crossing" 4 "Reserved" 5 "Reserved" 6 "Reserved" 7 "Reserved" 8 "Stop" 9 "Yield" 10 "Stop Ahead" 11 "Yield Ahead" 12 "Road Construction Ahead" 13 "Lane Reduction" 14 "Reserved" 15 "Reserved";
|
||||
VAL_ 506 ISLA_SchoolZone 0 "No School Zone" 1 "School Zone" 2 "Reserved" 3 "Reserved";
|
||||
VAL_ 517 DRIVE_MODE 2 "Normal" 4 "Eco" 5 "Sport";
|
||||
VAL_ 687 HOD_Dir_Status 0 "HANDS OFF" 1 "HAND TOUCH (SOFT)" 2 "HAND TOUCH (STRONG)" 3 "HAND GRIP (SOFT)" 4 "HAND GRIP (STRONG)" 5 "RESERVED" 6 "RESERVED";
|
||||
VAL_ 698 IFSref_FR_CMR_Sta 0 "None Option (Default)" 1 "Normal" 2 "Blockage Status" 3 "Error Indicator";
|
||||
VAL_ 698 IFSref_VehNumVal 0 "No vehicle" 1 "Number of vehicles" 2 "Number of vehicles" 3 "Number of vehicles" 4 "Number of vehicles" 5 "Number of vehicles" 6 "Number of vehicles" 7 "Number of vehicles" 8 "Number of vehicles" 9 "Number of vehicles" 10 "Number of vehicles" 11 "Over than 10 vehicles" 12 "Reserved" 13 "Reserved" 14 "Default" 15 "Error indicator";
|
||||
|
||||
@@ -751,6 +751,11 @@ BO_ 512 ADRV_0x200: 8 ADRV
|
||||
SG_ SET_ME_E1 : 24|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ SET_ME_3A : 32|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 517 DRIVE_MODE_EV: 8 XXX
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ DRIVE_MODE : 24|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 593 RADAR_0x251: 16 FRONT_RADAR
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
@@ -811,6 +816,10 @@ BO_ 837 ADRV_0x345: 8 ADRV
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ SET_ME_15 : 24|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 865 CAM_0x361: 32 CAMERA
|
||||
SG_ SIGN_TYPE : 208|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ SIGN_TYPE_2 : 240|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 866 CAM_0x362: 32 CAMERA
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
@@ -851,6 +860,9 @@ BO_ 874 BLINDSPOTS_FRONT_CORNER_2: 16 XXX
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
|
||||
BO_ 896 ADAS_0x380: 24 XXX
|
||||
SG_ STOP_SIGN : 83|1@1+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 961 BLINKER_STALKS: 8 XXX
|
||||
SG_ CHECKSUM_MAYBE : 7|8@0+ (1,0) [0|255] "" XXX
|
||||
SG_ COUNTER_ALT : 15|4@0+ (1,0) [0|15] "" XXX
|
||||
@@ -880,6 +892,23 @@ BO_ 1043 BLINKERS: 8 XXX
|
||||
SG_ RIGHT_LAMP_ALT : 61|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ USE_ALT_LAMP : 62|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 1044 DOOR_LOCK: 8 XXX
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ DOOR_LOCK_STATE : 37|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ DOOR_LOCK_ACTION : 40|1@1+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 1096 STEERING_WHEEL_MEDIA_BUTTONS: 8 XXX
|
||||
SG_ VOICE_BUTTON : 16|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ PHONE_BUTTON : 18|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ MODE_BUTTON : 22|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ RIGHT_SCROLL_PRESS : 24|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ NEXT_BUTTON : 26|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ PREVIOUS_BUTTON : 28|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ MENU_BUTTON : 34|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ LEFT_SCROLL_PRESS : 38|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ CUSTOM_BUTTON : 44|1@0+ (1,0) [0|1] "" XXX
|
||||
|
||||
BO_ 1144 DRIVE_MODE: 8 XXX
|
||||
SG_ DRIVE_MODE : 0|16@1+ (1,-61611) [0|61611] "" XXX
|
||||
SG_ DRIVE_MODE2 : 28|3@1+ (1,0) [1|3] "" XXX
|
||||
@@ -1177,6 +1206,7 @@ VAL_ 506 ISLA_AutoUsmSta 0 "None Auto Function (Delete Menu)" 1 "Auto Off" 2 "Au
|
||||
VAL_ 506 ISLA_Cntry 0 "Europe/Russia/Australia" 1 "Domestic" 2 "China" 3 "USA" 4 "Canada" 5 "Australia" 6 "Reserved" 7 "Reserved" 8 "Reserved" 9 "Reserved" 10 "Reserved" 11 "Reserved" 12 "Reserved" 13 "Reserved" 14 "Reserved" 15 "Initial Value (default)";
|
||||
VAL_ 506 ISLA_AddtnlSign 0 "No Recognition (default)" 1 "School Crossing" 16 "Do Not Pass" 17 "Reserved" 18 "Reserved" 19 "Reserved" 20 "Reserved" 21 "Reserved" 22 "Reserved" 23 "Reserved" 24 "Exit" 25 "Roundabout" 26 "Right Curve" 27 "Left Curve" 28 "Winding Road" 29 "Reserved" 30 "Reserved" 31 "Reserved" 2 "Pedestrian Crossing" 3 "Bicycle Crossing" 4 "Reserved" 5 "Reserved" 6 "Reserved" 7 "Reserved" 8 "Stop" 9 "Yield" 10 "Stop Ahead" 11 "Yield Ahead" 12 "Road Construction Ahead" 13 "Lane Reduction" 14 "Reserved" 15 "Reserved";
|
||||
VAL_ 506 ISLA_SchoolZone 0 "No School Zone" 1 "School Zone" 2 "Reserved" 3 "Reserved";
|
||||
VAL_ 517 DRIVE_MODE 2 "Normal" 4 "Eco" 5 "Sport";
|
||||
VAL_ 687 HOD_Dir_Status 0 "HANDS OFF" 1 "HAND TOUCH (SOFT)" 2 "HAND TOUCH (STRONG)" 3 "HAND GRIP (SOFT)" 4 "HAND GRIP (STRONG)" 5 "RESERVED" 6 "RESERVED";
|
||||
VAL_ 698 IFSref_FR_CMR_Sta 0 "None Option (Default)" 1 "Normal" 2 "Blockage Status" 3 "Error Indicator";
|
||||
VAL_ 698 IFSref_VehNumVal 0 "No vehicle" 1 "Number of vehicles" 2 "Number of vehicles" 3 "Number of vehicles" 4 "Number of vehicles" 5 "Number of vehicles" 6 "Number of vehicles" 7 "Number of vehicles" 8 "Number of vehicles" 9 "Number of vehicles" 10 "Number of vehicles" 11 "Over than 10 vehicles" 12 "Reserved" 13 "Reserved" 14 "Default" 15 "Error indicator";
|
||||
|
||||
@@ -146,10 +146,10 @@ static void hyundai_canfd_rx_hook(const CANPacket_t *msg) {
|
||||
|
||||
static bool hyundai_canfd_tx_hook(const CANPacket_t *msg) {
|
||||
const TorqueSteeringLimits HYUNDAI_CANFD_STEERING_LIMITS = {
|
||||
.max_torque = 270,
|
||||
.max_rt_delta = 112,
|
||||
.max_rate_up = 2,
|
||||
.max_rate_down = 3,
|
||||
.max_torque = 409,
|
||||
.max_rt_delta = 375,
|
||||
.max_rate_up = 10,
|
||||
.max_rate_down = 10,
|
||||
.driver_torque_allowance = 250,
|
||||
.driver_torque_multiplier = 2,
|
||||
.type = TorqueDriverLimited,
|
||||
|
||||
@@ -49,11 +49,11 @@ class TestHyundaiCanfdBase(HyundaiButtonBase, common.CarSafetyTest, common.Drive
|
||||
STANDSTILL_THRESHOLD = 12 # 0.375 kph
|
||||
FWD_BLACKLISTED_ADDRS = {2: [0x50, 0x2a4]}
|
||||
|
||||
MAX_RATE_UP = 2
|
||||
MAX_RATE_DOWN = 3
|
||||
MAX_TORQUE_LOOKUP = [0], [270]
|
||||
MAX_RATE_UP = 10
|
||||
MAX_RATE_DOWN = 10
|
||||
MAX_TORQUE_LOOKUP = [0], [409]
|
||||
|
||||
MAX_RT_DELTA = 112
|
||||
MAX_RT_DELTA = 375
|
||||
|
||||
DRIVER_TORQUE_ALLOWANCE = 250
|
||||
DRIVER_TORQUE_FACTOR = 2
|
||||
|
||||
@@ -291,6 +291,19 @@ class Car:
|
||||
# Initialize CarInterface, once controls are ready
|
||||
# TODO: this can make us miss at least a few cycles when doing an ECU knockout
|
||||
self.CI.init(self.CP, *self.can_callbacks)
|
||||
# If ECU disable was skipped/failed, strip LONG safety flag from BOTH CarParams
|
||||
# and StarPilotCarParams (pandad ORs both safetyParams together)
|
||||
if self.CP.openpilotLongitudinalControl and self.params.get_bool("EcuDisableFailed"):
|
||||
# ECU disable failed/rejected - switch to lateral-only mode with stock ACC
|
||||
LONG_FLAG = 4 # HyundaiSafetyFlags.LONG
|
||||
for cfg in self.CP.safetyConfigs:
|
||||
cfg.safetyParam &= ~LONG_FLAG
|
||||
for cfg in self.FPCP.safetyConfigs:
|
||||
cfg.safetyParam &= ~LONG_FLAG
|
||||
# Let stock ACC manage cruise (prevents "controls mismatch" error)
|
||||
self.CP.pcmCruise = True
|
||||
self.params.put("CarParams", self.CP.to_bytes())
|
||||
self.params.put("StarPilotCarParams", self.FPCP.to_bytes())
|
||||
# signal pandad to switch to car safety mode
|
||||
self.params.put_bool_nonblocking("ControlsReady", True)
|
||||
|
||||
|
||||
@@ -136,7 +136,10 @@ class Controls:
|
||||
CS.steerFaultTemporary, CS.steerFaultPermanent,
|
||||
standstill, self.CP.steerAtStandstill,
|
||||
self.sm['starpilotPlan'].lateralCheck)
|
||||
CC.longActive = CC.enabled and not any(e.overrideLongitudinal for e in self.sm['onroadEvents']) and not self.sm['starpilotCarState'].pauseLongitudinal and self.CP.openpilotLongitudinalControl
|
||||
# EcuDisableFailed is set when car started in READY mode (ECU disable was rejected)
|
||||
# Disable longitudinal so stock ACC works instead
|
||||
ecu_disable_failed = self.params.get_bool("EcuDisableFailed")
|
||||
CC.longActive = CC.enabled and not any(e.overrideLongitudinal for e in self.sm['onroadEvents']) and not self.sm['starpilotCarState'].pauseLongitudinal and self.CP.openpilotLongitudinalControl and not ecu_disable_failed
|
||||
|
||||
actuators = CC.actuators
|
||||
actuators.longControlState = self.LoC.long_control_state
|
||||
|
||||
@@ -1232,6 +1232,14 @@ STARPILOT_EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
|
||||
Priority.LOW, VisualAlert.none, AudibleAlert.prompt, 3.),
|
||||
},
|
||||
|
||||
StarPilotEventName.lkasEnable: {
|
||||
ET.WARNING: EngagementAlert(AudibleAlert.engage),
|
||||
},
|
||||
|
||||
StarPilotEventName.lkasDisable: {
|
||||
ET.PERMANENT: EngagementAlert(AudibleAlert.disengage),
|
||||
},
|
||||
|
||||
StarPilotEventName.turningLeft: {
|
||||
ET.WARNING: Alert(
|
||||
"Turning Left",
|
||||
|
||||
@@ -119,7 +119,7 @@ class StarPilotAlwaysOnLateralLayout(StarPilotPanel):
|
||||
super().__init__()
|
||||
self.CATEGORIES = [
|
||||
{"title": tr_noop("Always On Lateral"), "type": "toggle", "get_state": lambda: self._params.get_bool("AlwaysOnLateral"), "set_state": lambda x: self._on_reboot_toggle("AlwaysOnLateral", x), "icon": "toggle_icons/icon_always_on_lateral.png", "color": "#597497"},
|
||||
{"title": tr_noop("Enable With LKAS"), "type": "toggle", "get_state": lambda: self._params.get_bool("AlwaysOnLateralLKAS"), "set_state": lambda x: self._params.put_bool("AlwaysOnLateralLKAS", x), "icon": "toggle_icons/icon_always_on_lateral.png", "color": "#597497", "visible": lambda: self._params.get_bool("AlwaysOnLateral")},
|
||||
{"title": tr_noop("Enable With LKAS"), "type": "toggle", "get_state": lambda: self._params.get_bool("AlwaysOnLateralLKAS"), "set_state": lambda x: self._params.put_bool("AlwaysOnLateralLKAS", x), "icon": "toggle_icons/icon_always_on_lateral.png", "color": "#597497", "visible": lambda: self._params.get_bool("AlwaysOnLateral") and starpilot_state.car_state.lkasAllowedForAOL},
|
||||
{"title": tr_noop("Pause Below"), "type": "value", "get_value": lambda: f"{self._params.get_int('PauseAOLOnBrake')} mph", "on_click": lambda: self._show_speed_selector("PauseAOLOnBrake"), "icon": "toggle_icons/icon_always_on_lateral.png", "color": "#597497", "visible": lambda: self._params.get_bool("AlwaysOnLateral")},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
@@ -62,6 +62,54 @@ class StarPilotWheelLayout(StarPilotPanel):
|
||||
"key": "LKASButtonControl",
|
||||
"color": "#64748B",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Mode Button"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._get_action_name("ModeButtonControl"),
|
||||
"on_click": lambda: self._show_action_picker("ModeButtonControl"),
|
||||
"key": "ModeButtonControl",
|
||||
"color": "#64748B",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Mode (Long Press)"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._get_action_name("LongModeButtonControl"),
|
||||
"on_click": lambda: self._show_action_picker("LongModeButtonControl"),
|
||||
"key": "LongModeButtonControl",
|
||||
"color": "#64748B",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Mode (Very Long)"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._get_action_name("VeryLongModeButtonControl"),
|
||||
"on_click": lambda: self._show_action_picker("VeryLongModeButtonControl"),
|
||||
"key": "VeryLongModeButtonControl",
|
||||
"color": "#64748B",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Star Button"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._get_action_name("StarButtonControl"),
|
||||
"on_click": lambda: self._show_action_picker("StarButtonControl"),
|
||||
"key": "StarButtonControl",
|
||||
"color": "#64748B",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Star (Long Press)"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._get_action_name("LongStarButtonControl"),
|
||||
"on_click": lambda: self._show_action_picker("LongStarButtonControl"),
|
||||
"key": "LongStarButtonControl",
|
||||
"color": "#64748B",
|
||||
},
|
||||
{
|
||||
"title": tr_noop("Star (Very Long)"),
|
||||
"type": "value",
|
||||
"get_value": lambda: self._get_action_name("VeryLongStarButtonControl"),
|
||||
"on_click": lambda: self._show_action_picker("VeryLongStarButtonControl"),
|
||||
"key": "VeryLongStarButtonControl",
|
||||
"color": "#64748B",
|
||||
},
|
||||
]
|
||||
self._rebuild_grid()
|
||||
|
||||
@@ -131,6 +179,10 @@ class StarPilotWheelLayout(StarPilotPanel):
|
||||
visible = True
|
||||
if key == "LKASButtonControl":
|
||||
visible &= not cs.isSubaru
|
||||
visible &= not (cs.lkasAllowedForAOL and self._params.get_bool("AlwaysOnLateral") and self._params.get_bool("AlwaysOnLateralLKAS"))
|
||||
if key in ("ModeButtonControl", "LongModeButtonControl", "VeryLongModeButtonControl",
|
||||
"StarButtonControl", "LongStarButtonControl", "VeryLongStarButtonControl"):
|
||||
visible &= cs.hasModeStarButtons
|
||||
if not visible:
|
||||
continue
|
||||
tile_type = cat.get("type", "hub")
|
||||
|
||||
@@ -7,6 +7,7 @@ from openpilot.system.hardware import HARDWARE, PC
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from cereal import car, log, custom, messaging
|
||||
from opendbc.car.gm.values import GMFlags
|
||||
from opendbc.car.hyundai.values import HyundaiFlags
|
||||
|
||||
@dataclass
|
||||
class StarPilotCarState:
|
||||
@@ -39,6 +40,7 @@ class StarPilotCarState:
|
||||
# ========== Device/Car State ==========
|
||||
isFrogsGoMoo: bool = False
|
||||
hasPCMCruise: bool = False
|
||||
hasModeStarButtons: bool = False
|
||||
lkasAllowedForAOL: bool = False
|
||||
openpilotLongitudinalControlDisabled: bool = False
|
||||
hasAlphaLongitudinal: bool = False
|
||||
@@ -82,6 +84,7 @@ class StarPilotState:
|
||||
if not fallback_model:
|
||||
fallback_model = "CHEVROLET_BOLT_ACC_2022_2023"
|
||||
|
||||
self.car_state.hasModeStarButtons = self.car_state.isHKGCanFd
|
||||
self.car_state.hasPedal = starpilot_toggles.get("has_pedal", True)
|
||||
self.car_state.hasSASCM = starpilot_toggles.get("has_sascm", False)
|
||||
self.car_state.hasSDSU = starpilot_toggles.get("has_sdsu", False)
|
||||
@@ -157,7 +160,9 @@ class StarPilotState:
|
||||
self.car_state.isTSK = bool(self._safe_get(CP, "secOcRequired", False))
|
||||
self.car_state.isVolt = car_fingerprint.startswith("CHEVROLET_VOLT")
|
||||
|
||||
self.car_state.lkasAllowedForAOL = starpilot_toggles.get("lkas_allowed_for_aol", False)
|
||||
cp_flags = self._safe_get(CP, "flags", 0)
|
||||
self.car_state.hasModeStarButtons = car_make == "hyundai" and bool(cp_flags & HyundaiFlags.CANFD)
|
||||
self.car_state.lkasAllowedForAOL = car_make == "hyundai" and bool(cp_flags & (HyundaiFlags.CANFD | HyundaiFlags.HAS_LDA_BUTTON))
|
||||
self.car_state.longitudinalActuatorDelay = float(self._safe_get(CP, "longitudinalActuatorDelay", self.car_state.longitudinalActuatorDelay))
|
||||
self.car_state.startAccel = float(self._safe_get(CP, "startAccel", self.car_state.startAccel))
|
||||
self.car_state.steerActuatorDelay = float(self._safe_get(CP, "steerActuatorDelay", self.car_state.steerActuatorDelay))
|
||||
|
||||
@@ -215,6 +215,7 @@ DeveloperPanel::DeveloperPanel(SettingsWindow *parent) : QFrame(parent) {
|
||||
{14, tr("Longitudinal MPC Jerk: Acceleration")},
|
||||
{15, tr("Longitudinal MPC Jerk: Danger Zone")},
|
||||
{16, tr("Longitudinal MPC Jerk: Speed Control")},
|
||||
{17, tr("Model Name")},
|
||||
};
|
||||
|
||||
ButtonControl *metricToggle = new ButtonControl(title, tr("SELECT"), desc);
|
||||
|
||||
@@ -142,9 +142,11 @@ DEVELOPER_SIDEBAR_METRICS = {
|
||||
"LATERAL_STEERING_ANGLE": 10,
|
||||
"LATERAL_TORQUE_USED": 11,
|
||||
"LONGITUDINAL_ACTUATOR_ACCELERATION": 12,
|
||||
"LONGITUDINAL_MPC_JERK_ACCELERATION": 13,
|
||||
"LONGITUDINAL_MPC_JERK_DANGER_ZONE": 14,
|
||||
"LONGITUDINAL_MPC_JERK_SPEED_CONTROL": 15,
|
||||
"LONGITUDINAL_MPC_DANGER_FACTOR": 13,
|
||||
"LONGITUDINAL_MPC_JERK_ACCELERATION": 14,
|
||||
"LONGITUDINAL_MPC_JERK_DANGER_ZONE": 15,
|
||||
"LONGITUDINAL_MPC_JERK_SPEED_CONTROL": 16,
|
||||
"MODEL_NAME": 17,
|
||||
}
|
||||
|
||||
DEVICE_SHUTDOWN_TIMES = {
|
||||
@@ -823,6 +825,68 @@ class StarPilotVariables:
|
||||
toggle.switchback_mode_via_lkas = lkas_button_control == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
|
||||
toggle.traffic_mode_via_lkas = toggle.openpilot_longitudinal and lkas_button_control == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
|
||||
|
||||
has_canfd_media_buttons = toggle.car_make == "hyundai" and bool(CP.flags & HyundaiFlags.CANFD)
|
||||
mode_button_control = self.get_value("ModeButtonControl", cast=float, condition=has_canfd_media_buttons)
|
||||
toggle.experimental_mode_via_mode = toggle.openpilot_longitudinal and mode_button_control == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
|
||||
toggle.experimental_mode_via_press |= toggle.experimental_mode_via_mode
|
||||
toggle.force_coast_via_mode = toggle.openpilot_longitudinal and mode_button_control == BUTTON_FUNCTIONS["FORCE_COAST"]
|
||||
toggle.pause_lateral_via_mode = mode_button_control == BUTTON_FUNCTIONS["PAUSE_LATERAL"]
|
||||
toggle.pause_longitudinal_via_mode = toggle.openpilot_longitudinal and mode_button_control == BUTTON_FUNCTIONS["PAUSE_LONGITUDINAL"]
|
||||
toggle.personality_profile_via_mode = toggle.openpilot_longitudinal and mode_button_control == BUTTON_FUNCTIONS["PERSONALITY_PROFILE"]
|
||||
toggle.switchback_mode_via_mode = mode_button_control == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
|
||||
toggle.traffic_mode_via_mode = toggle.openpilot_longitudinal and mode_button_control == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
|
||||
|
||||
mode_button_control_long = self.get_value("LongModeButtonControl", cast=float, condition=has_canfd_media_buttons)
|
||||
toggle.experimental_mode_via_mode_long = toggle.openpilot_longitudinal and mode_button_control_long == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
|
||||
toggle.experimental_mode_via_press |= toggle.experimental_mode_via_mode_long
|
||||
toggle.force_coast_via_mode_long = toggle.openpilot_longitudinal and mode_button_control_long == BUTTON_FUNCTIONS["FORCE_COAST"]
|
||||
toggle.pause_lateral_via_mode_long = mode_button_control_long == BUTTON_FUNCTIONS["PAUSE_LATERAL"]
|
||||
toggle.pause_longitudinal_via_mode_long = toggle.openpilot_longitudinal and mode_button_control_long == BUTTON_FUNCTIONS["PAUSE_LONGITUDINAL"]
|
||||
toggle.personality_profile_via_mode_long = toggle.openpilot_longitudinal and mode_button_control_long == BUTTON_FUNCTIONS["PERSONALITY_PROFILE"]
|
||||
toggle.switchback_mode_via_mode_long = mode_button_control_long == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
|
||||
toggle.traffic_mode_via_mode_long = toggle.openpilot_longitudinal and mode_button_control_long == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
|
||||
|
||||
mode_button_control_very_long = self.get_value("VeryLongModeButtonControl", cast=float, condition=has_canfd_media_buttons)
|
||||
toggle.experimental_mode_via_mode_very_long = toggle.openpilot_longitudinal and mode_button_control_very_long == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
|
||||
toggle.experimental_mode_via_press |= toggle.experimental_mode_via_mode_very_long
|
||||
toggle.force_coast_via_mode_very_long = toggle.openpilot_longitudinal and mode_button_control_very_long == BUTTON_FUNCTIONS["FORCE_COAST"]
|
||||
toggle.pause_lateral_via_mode_very_long = mode_button_control_very_long == BUTTON_FUNCTIONS["PAUSE_LATERAL"]
|
||||
toggle.pause_longitudinal_via_mode_very_long = toggle.openpilot_longitudinal and mode_button_control_very_long == BUTTON_FUNCTIONS["PAUSE_LONGITUDINAL"]
|
||||
toggle.personality_profile_via_mode_very_long = toggle.openpilot_longitudinal and mode_button_control_very_long == BUTTON_FUNCTIONS["PERSONALITY_PROFILE"]
|
||||
toggle.switchback_mode_via_mode_very_long = mode_button_control_very_long == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
|
||||
toggle.traffic_mode_via_mode_very_long = toggle.openpilot_longitudinal and mode_button_control_very_long == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
|
||||
|
||||
star_button_control = self.get_value("StarButtonControl", cast=float, condition=has_canfd_media_buttons)
|
||||
toggle.experimental_mode_via_star = toggle.openpilot_longitudinal and star_button_control == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
|
||||
toggle.experimental_mode_via_press |= toggle.experimental_mode_via_star
|
||||
toggle.force_coast_via_star = toggle.openpilot_longitudinal and star_button_control == BUTTON_FUNCTIONS["FORCE_COAST"]
|
||||
toggle.pause_lateral_via_star = star_button_control == BUTTON_FUNCTIONS["PAUSE_LATERAL"]
|
||||
toggle.pause_longitudinal_via_star = toggle.openpilot_longitudinal and star_button_control == BUTTON_FUNCTIONS["PAUSE_LONGITUDINAL"]
|
||||
toggle.personality_profile_via_star = toggle.openpilot_longitudinal and star_button_control == BUTTON_FUNCTIONS["PERSONALITY_PROFILE"]
|
||||
toggle.switchback_mode_via_star = star_button_control == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
|
||||
toggle.traffic_mode_via_star = toggle.openpilot_longitudinal and star_button_control == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
|
||||
|
||||
star_button_control_long = self.get_value("LongStarButtonControl", cast=float, condition=has_canfd_media_buttons)
|
||||
toggle.experimental_mode_via_star_long = toggle.openpilot_longitudinal and star_button_control_long == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
|
||||
toggle.experimental_mode_via_press |= toggle.experimental_mode_via_star_long
|
||||
toggle.force_coast_via_star_long = toggle.openpilot_longitudinal and star_button_control_long == BUTTON_FUNCTIONS["FORCE_COAST"]
|
||||
toggle.pause_lateral_via_star_long = star_button_control_long == BUTTON_FUNCTIONS["PAUSE_LATERAL"]
|
||||
toggle.pause_longitudinal_via_star_long = toggle.openpilot_longitudinal and star_button_control_long == BUTTON_FUNCTIONS["PAUSE_LONGITUDINAL"]
|
||||
toggle.personality_profile_via_star_long = toggle.openpilot_longitudinal and star_button_control_long == BUTTON_FUNCTIONS["PERSONALITY_PROFILE"]
|
||||
toggle.switchback_mode_via_star_long = star_button_control_long == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
|
||||
toggle.traffic_mode_via_star_long = toggle.openpilot_longitudinal and star_button_control_long == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
|
||||
|
||||
star_button_control_very_long = self.get_value("VeryLongStarButtonControl", cast=float, condition=has_canfd_media_buttons)
|
||||
toggle.experimental_mode_via_star_very_long = toggle.openpilot_longitudinal and star_button_control_very_long == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
|
||||
toggle.experimental_mode_via_press |= toggle.experimental_mode_via_star_very_long
|
||||
toggle.force_coast_via_star_very_long = toggle.openpilot_longitudinal and star_button_control_very_long == BUTTON_FUNCTIONS["FORCE_COAST"]
|
||||
toggle.pause_lateral_via_star_very_long = star_button_control_very_long == BUTTON_FUNCTIONS["PAUSE_LATERAL"]
|
||||
toggle.pause_longitudinal_via_star_very_long = toggle.openpilot_longitudinal and star_button_control_very_long == BUTTON_FUNCTIONS["PAUSE_LONGITUDINAL"]
|
||||
toggle.personality_profile_via_star_very_long = toggle.openpilot_longitudinal and star_button_control_very_long == BUTTON_FUNCTIONS["PERSONALITY_PROFILE"]
|
||||
toggle.switchback_mode_via_star_very_long = star_button_control_very_long == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
|
||||
toggle.traffic_mode_via_star_very_long = toggle.openpilot_longitudinal and star_button_control_very_long == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
|
||||
toggle.has_canfd_media_buttons = has_canfd_media_buttons
|
||||
|
||||
toggle.lock_doors_timer = self.get_value("LockDoorsTimer", cast=float, condition=(toggle.car_make == "toyota"))
|
||||
|
||||
longitudinal_tuning = toggle.openpilot_longitudinal and self.get_value("LongitudinalTune")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
import numpy as np
|
||||
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.selfdrive.controls.lib.longitudinal_planner import A_CRUISE_MIN, get_max_accel
|
||||
|
||||
from openpilot.starpilot.common.accel_profile import (
|
||||
@@ -80,10 +81,13 @@ def get_max_allowed_accel(v_ego, ev_tuning=True, truck_tuning=False):
|
||||
class StarPilotAcceleration:
|
||||
def __init__(self, StarPilotPlanner):
|
||||
self.starpilot_planner = StarPilotPlanner
|
||||
self.params = Params()
|
||||
|
||||
self.max_accel = 0
|
||||
self.min_accel = 0
|
||||
|
||||
self.last_gear_state = "init"
|
||||
|
||||
def update(self, v_ego, sm, starpilot_toggles):
|
||||
eco_gear = sm["starpilotCarState"].ecoGear
|
||||
sport_gear = sm["starpilotCarState"].sportGear
|
||||
@@ -100,10 +104,7 @@ class StarPilotAcceleration:
|
||||
if eco_gear:
|
||||
self.max_accel = get_max_accel_eco(v_ego, ev_tuning, truck_tuning)
|
||||
else:
|
||||
if starpilot_toggles.acceleration_profile == ACCELERATION_PROFILES["SPORT"]:
|
||||
self.max_accel = get_max_accel_sport(v_ego, ev_tuning, truck_tuning)
|
||||
else:
|
||||
self.max_accel = get_max_allowed_accel(v_ego, ev_tuning, truck_tuning)
|
||||
self.max_accel = get_max_allowed_accel(v_ego, ev_tuning, truck_tuning)
|
||||
else:
|
||||
if starpilot_toggles.acceleration_profile == ACCELERATION_PROFILES["ECO"]:
|
||||
self.max_accel = get_max_accel_eco(v_ego, ev_tuning, truck_tuning)
|
||||
@@ -135,3 +136,24 @@ class StarPilotAcceleration:
|
||||
self.min_accel = A_CRUISE_MIN_SPORT
|
||||
else:
|
||||
self.min_accel = A_CRUISE_MIN
|
||||
|
||||
# Sync AccelerationProfile and DecelerationProfile params so the UI reflects the active drive mode
|
||||
# Eco → Eco, Normal → Standard, Sport → Sport+
|
||||
gear_state = "eco" if eco_gear else ("sport" if sport_gear else "normal")
|
||||
if gear_state != self.last_gear_state:
|
||||
self.last_gear_state = gear_state
|
||||
if gear_state == "eco":
|
||||
if starpilot_toggles.map_acceleration:
|
||||
self.params.put_nonblocking("AccelerationProfile", ACCELERATION_PROFILES["ECO"])
|
||||
if starpilot_toggles.map_deceleration:
|
||||
self.params.put_nonblocking("DecelerationProfile", DECELERATION_PROFILES["ECO"])
|
||||
elif gear_state == "sport":
|
||||
if starpilot_toggles.map_acceleration:
|
||||
self.params.put_nonblocking("AccelerationProfile", ACCELERATION_PROFILES["SPORT_PLUS"])
|
||||
if starpilot_toggles.map_deceleration:
|
||||
self.params.put_nonblocking("DecelerationProfile", DECELERATION_PROFILES["SPORT"])
|
||||
else:
|
||||
if starpilot_toggles.map_acceleration:
|
||||
self.params.put_nonblocking("AccelerationProfile", ACCELERATION_PROFILES["STANDARD"])
|
||||
if starpilot_toggles.map_deceleration:
|
||||
self.params.put_nonblocking("DecelerationProfile", DECELERATION_PROFILES["STANDARD"])
|
||||
|
||||
@@ -22,7 +22,7 @@ class StarPilotEvents:
|
||||
|
||||
self.events = Events(starpilot=True)
|
||||
|
||||
self.always_on_lateral_enabled_previously = False
|
||||
self.always_on_lateral_allowed_previously = False
|
||||
self.previous_traffic_mode = False
|
||||
self.previous_switchback_mode = False
|
||||
self.random_event_playing = False
|
||||
@@ -57,6 +57,12 @@ class StarPilotEvents:
|
||||
else:
|
||||
self.max_acceleration = 0
|
||||
|
||||
if sm["starpilotCarState"].alwaysOnLateralAllowed != self.always_on_lateral_allowed_previously:
|
||||
if sm["starpilotCarState"].alwaysOnLateralAllowed:
|
||||
self.events.add(StarPilotEventName.lkasEnable)
|
||||
else:
|
||||
self.events.add(StarPilotEventName.lkasDisable)
|
||||
|
||||
if self.starpilot_planner.starpilot_vcruise.forcing_stop:
|
||||
self.events.add(StarPilotEventName.forcingStop)
|
||||
|
||||
@@ -173,11 +179,10 @@ class StarPilotEvents:
|
||||
elif event_choice == "yourFrogTriedToKillMe":
|
||||
self.events.add(StarPilotEventName.yourFrogTriedToKillMe)
|
||||
|
||||
if "youveGotMail" not in self.played_events and sm["starpilotCarState"].alwaysOnLateralEnabled and not self.always_on_lateral_enabled_previously:
|
||||
if "youveGotMail" not in self.played_events and sm["starpilotCarState"].alwaysOnLateralAllowed and not self.always_on_lateral_allowed_previously:
|
||||
if random.random() < RANDOM_EVENTS_CHANCE / DT_MDL:
|
||||
self.events.add(StarPilotEventName.youveGotMail)
|
||||
|
||||
self.always_on_lateral_enabled_previously = sm["starpilotCarState"].alwaysOnLateralEnabled
|
||||
self.random_event_playing |= bool({event for event in self.events.names if RANDOM_EVENT_START <= event <= RANDOM_EVENT_END})
|
||||
|
||||
if self.error_log.is_file():
|
||||
@@ -212,4 +217,5 @@ class StarPilotEvents:
|
||||
elif sm["starpilotModelV2"].turnDirection == TurnDirection.turnRight:
|
||||
self.events.add(StarPilotEventName.turningRight)
|
||||
|
||||
self.always_on_lateral_allowed_previously = sm["starpilotCarState"].alwaysOnLateralAllowed
|
||||
self.played_events.update(STARPILOT_EVENT_NAME[event] for event in self.events.names)
|
||||
|
||||
@@ -25,6 +25,10 @@ class StarPilotCard:
|
||||
self.decel_pressed = False
|
||||
self.distancePressed_previously = False
|
||||
self.force_coast = False
|
||||
self.modePressed_previously = False
|
||||
self.mode_counter = 0
|
||||
self.customPressed_previously = False
|
||||
self.custom_counter = 0
|
||||
self.pause_lateral = False
|
||||
self.pause_longitudinal = False
|
||||
self.switchback_mode_enabled = self.params_memory.get_bool("SwitchbackModeEnabled")
|
||||
@@ -120,9 +124,39 @@ class StarPilotCard:
|
||||
if any(be.pressed and be.type == ButtonType.lkas for be in carState.buttonEvents):
|
||||
self.handle_button_event("lkas", sm, starpilot_toggles)
|
||||
|
||||
if getattr(starpilot_toggles, "has_canfd_media_buttons", False):
|
||||
if starpilotCarState.modePressed:
|
||||
self.mode_counter += 1
|
||||
elif not self.modePressed_previously:
|
||||
self.mode_counter = 0
|
||||
self.modePressed_previously = starpilotCarState.modePressed
|
||||
|
||||
if not starpilotCarState.modePressed and 1 <= self.mode_counter < self.long_press_threshold:
|
||||
self.handle_button_event("mode", sm, starpilot_toggles)
|
||||
elif self.mode_counter == self.long_press_threshold:
|
||||
self.handle_button_event("mode_long", sm, starpilot_toggles)
|
||||
elif self.mode_counter == self.very_long_press_threshold:
|
||||
self.handle_button_event("mode_long", sm, starpilot_toggles)
|
||||
self.handle_button_event("mode_very_long", sm, starpilot_toggles)
|
||||
|
||||
if starpilotCarState.customPressed:
|
||||
self.custom_counter += 1
|
||||
elif not self.customPressed_previously:
|
||||
self.custom_counter = 0
|
||||
self.customPressed_previously = starpilotCarState.customPressed
|
||||
|
||||
if not starpilotCarState.customPressed and 1 <= self.custom_counter < self.long_press_threshold:
|
||||
self.handle_button_event("star", sm, starpilot_toggles)
|
||||
elif self.custom_counter == self.long_press_threshold:
|
||||
self.handle_button_event("star_long", sm, starpilot_toggles)
|
||||
elif self.custom_counter == self.very_long_press_threshold:
|
||||
self.handle_button_event("star_long", sm, starpilot_toggles)
|
||||
self.handle_button_event("star_very_long", sm, starpilot_toggles)
|
||||
|
||||
self.force_coast &= not (carState.brakePressed or carState.gasPressed)
|
||||
|
||||
starpilotCarState.accelPressed = self.accel_pressed
|
||||
starpilotCarState.alwaysOnLateralAllowed = self.always_on_lateral_allowed
|
||||
starpilotCarState.alwaysOnLateralEnabled = self.always_on_lateral_enabled
|
||||
starpilotCarState.decelPressed = self.decel_pressed
|
||||
starpilotCarState.distanceLongPressed = self.very_long_press_threshold > self.gap_counter >= self.long_press_threshold
|
||||
|
||||
@@ -996,7 +996,7 @@ void StarPilotLongitudinalPanel::updateToggles() {
|
||||
}
|
||||
|
||||
else if (key == "MapGears") {
|
||||
setVisible &= parent->isToyota;
|
||||
setVisible &= parent->isToyota || parent->isHKG;
|
||||
setVisible &= !parent->isTSK;
|
||||
}
|
||||
|
||||
|
||||
@@ -335,11 +335,13 @@ void StarPilotSettingsWindow::updateVariables() {
|
||||
std::string fallbackFingerprint = fallbackModel.toStdString();
|
||||
|
||||
hasPedal = starpilot_toggles.value("has_pedal").toBool(true);
|
||||
hasModeStarButtons = starpilot_toggles.contains("has_canfd_media_buttons") && starpilot_toggles.value("has_canfd_media_buttons").toBool();
|
||||
hasSDSU = starpilot_toggles.value("has_sdsu").toBool();
|
||||
hasZSS = starpilot_toggles.value("has_zss").toBool();
|
||||
isBolt = fallbackFingerprint.rfind("CHEVROLET_BOLT", 0) == 0;
|
||||
isGM = carMake == "gm";
|
||||
isHKG = carMake == "hyundai";
|
||||
isHKGCanFd = isHKG && hasModeStarButtons;
|
||||
isSubaru = carMake == "subaru";
|
||||
isToyota = carMake == "toyota";
|
||||
isVolt = fallbackFingerprint.rfind("CHEVROLET_VOLT", 0) == 0;
|
||||
@@ -382,6 +384,7 @@ void StarPilotSettingsWindow::updateVariables() {
|
||||
isTSK = CP.getSecOcRequired();
|
||||
isVolt = carFingerprint.rfind("CHEVROLET_VOLT", 0) == 0;
|
||||
latAccelFactor = CP.getLateralTuning().getTorque().getLatAccelFactor();
|
||||
hasModeStarButtons = starpilot_toggles.contains("has_canfd_media_buttons") ? starpilot_toggles.value("has_canfd_media_buttons").toBool() : isHKGCanFd;
|
||||
lkasAllowedForAOL = starpilot_toggles.value("lkas_allowed_for_aol").toBool();
|
||||
longitudinalActuatorDelay = CP.getLongitudinalActuatorDelay();
|
||||
startAccel = CP.getStartAccel();
|
||||
|
||||
@@ -31,6 +31,7 @@ public:
|
||||
bool hasRadar = true;
|
||||
bool hasSDSU = false;
|
||||
bool hasSNG = false;
|
||||
bool hasModeStarButtons = false;
|
||||
bool hasZSS = false;
|
||||
bool isAngleCar = false;
|
||||
bool isBolt = false;
|
||||
|
||||
@@ -56,7 +56,13 @@ StarPilotWheelPanel::StarPilotWheelPanel(StarPilotSettingsWindow *parent, bool f
|
||||
{"DistanceButtonControl", tr("Distance Button"), tr("<b>Action performed when the \"Distance\" button is pressed.</b>"), "../../starpilot/assets/toggle_icons/icon_mute.png"},
|
||||
{"LongDistanceButtonControl", tr("Distance Button (Long Press)"), tr("<b>Action performed when the \"Distance\" button is pressed for more than 0.5 seconds.</b>"), "../../starpilot/assets/toggle_icons/icon_mute.png"},
|
||||
{"VeryLongDistanceButtonControl", tr("Distance Button (Very Long Press)"), tr("<b>Action performed when the \"Distance\" button is pressed for more than 2.5 seconds.</b>"), "../../starpilot/assets/toggle_icons/icon_mute.png"},
|
||||
{"LKASButtonControl", tr("LKAS Button"), tr("<b>Action performed when the \"LKAS\" button is pressed.</b>"), "../../starpilot/assets/toggle_icons/icon_mute.png"}
|
||||
{"LKASButtonControl", tr("LKAS Button"), tr("<b>Action performed when the \"LKAS\" button is pressed.</b>"), "../../starpilot/assets/toggle_icons/icon_mute.png"},
|
||||
{"ModeButtonControl", tr("Mode Button"), tr("<b>Action performed when the \"Mode\" button is pressed.</b>"), "../../starpilot/assets/toggle_icons/icon_mute.png"},
|
||||
{"LongModeButtonControl", tr("Mode Button (Long Press)"), tr("<b>Action performed when the \"Mode\" button is pressed for more than 0.5 seconds.</b>"), "../../starpilot/assets/toggle_icons/icon_mute.png"},
|
||||
{"VeryLongModeButtonControl", tr("Mode Button (Very Long Press)"), tr("<b>Action performed when the \"Mode\" button is pressed for more than 2.5 seconds.</b>"), "../../starpilot/assets/toggle_icons/icon_mute.png"},
|
||||
{"StarButtonControl", tr("Star Button"), tr("<b>Action performed when the \"Star\" button is pressed.</b>"), "../../starpilot/assets/toggle_icons/icon_mute.png"},
|
||||
{"LongStarButtonControl", tr("Star Button (Long Press)"), tr("<b>Action performed when the \"Star\" button is pressed for more than 0.5 seconds.</b>"), "../../starpilot/assets/toggle_icons/icon_mute.png"},
|
||||
{"VeryLongStarButtonControl", tr("Star Button (Very Long Press)"), tr("<b>Action performed when the \"Star\" button is pressed for more than 2.5 seconds.</b>"), "../../starpilot/assets/toggle_icons/icon_mute.png"}
|
||||
};
|
||||
|
||||
for (const auto &[param, title, desc, icon] : wheelToggles) {
|
||||
@@ -121,6 +127,16 @@ void StarPilotWheelPanel::updateToggles() {
|
||||
setVisible &= !parent->lkasAllowedForAOL || !(params.getBool("AlwaysOnLateral") && params.getBool("AlwaysOnLateralLKAS"));
|
||||
}
|
||||
|
||||
if (!showAllToggles && (
|
||||
key == "ModeButtonControl" ||
|
||||
key == "LongModeButtonControl" ||
|
||||
key == "VeryLongModeButtonControl" ||
|
||||
key == "StarButtonControl" ||
|
||||
key == "LongStarButtonControl" ||
|
||||
key == "VeryLongStarButtonControl")) {
|
||||
setVisible &= parent->hasModeStarButtons;
|
||||
}
|
||||
|
||||
if (ButtonControl *wheelToggle = qobject_cast<ButtonControl*>(toggle)) {
|
||||
if (key == "LKASButtonControl") {
|
||||
const bool lkasLocked = lockLkasButtonIfNeeded(params);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "starpilot/ui/qt/widgets/developer_sidebar.h"
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
void DeveloperSidebar::drawMetric(QPainter &p, const QPair<QString, QString> &label, QColor c, int y) {
|
||||
const QRect rect = {12, y, 275, 126};
|
||||
|
||||
@@ -17,7 +19,11 @@ void DeveloperSidebar::drawMetric(QPainter &p, const QPair<QString, QString> &la
|
||||
|
||||
p.setPen(QColor(0xff, 0xff, 0xff));
|
||||
p.setFont(InterFont(35, QFont::DemiBold));
|
||||
p.drawText(rect.adjusted(0, 0, -22, 0), Qt::AlignCenter, label.first + "\n" + label.second);
|
||||
if (label.second.isEmpty()) {
|
||||
p.drawText(rect.adjusted(8, 8, -22, -8), Qt::AlignCenter | Qt::TextWordWrap, label.first);
|
||||
} else {
|
||||
p.drawText(rect.adjusted(8, 8, -22, -8), Qt::AlignCenter, label.first + "\n" + label.second);
|
||||
}
|
||||
}
|
||||
|
||||
DeveloperSidebar::DeveloperSidebar(QWidget *parent) : QFrame(parent) {
|
||||
@@ -144,6 +150,11 @@ void DeveloperSidebar::updateState(const UIState &s, const StarPilotUIState &fs)
|
||||
stiffnessFactorStatus = ItemStatus(QPair<QString, QString>(tr("STEER STIFF"), QString::number(liveParameters.getStiffnessFactor(), 'f', 5)), metricColor);
|
||||
torqueStatus = ItemStatus(QPair<QString, QString>(tr("TORQUE %"), torqueLabel), metricColor);
|
||||
|
||||
QString modelName = starpilot_scene.starpilot_toggles.value("model_name").toString();
|
||||
modelName.remove(QRegularExpression("\\(.*\\)"));
|
||||
modelName.remove(QRegularExpression("[^a-zA-Z0-9 \\-\\.:]"));
|
||||
modelNameStatus = ItemStatus(QPair<QString, QString>(modelName.trimmed(), ""), metricColor);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -171,6 +182,7 @@ void DeveloperSidebar::paintEvent(QPaintEvent *event) {
|
||||
metricMap.insert(14, &accelerationJerkStatus);
|
||||
metricMap.insert(15, &dangerJerkStatus);
|
||||
metricMap.insert(16, &speedJerkStatus);
|
||||
metricMap.insert(17, &modelNameStatus);
|
||||
|
||||
int count = 0;
|
||||
for (size_t i = 0; i < metricAssignments.size(); ++i) {
|
||||
|
||||
@@ -37,6 +37,7 @@ private:
|
||||
ItemStatus lateralEngagementStatus;
|
||||
ItemStatus longitudinalEngagementStatus;
|
||||
ItemStatus maxAccelerationStatus;
|
||||
ItemStatus modelNameStatus;
|
||||
ItemStatus speedJerkStatus;
|
||||
ItemStatus steerAngleStatus;
|
||||
ItemStatus steerRatioStatus;
|
||||
|
||||
Reference in New Issue
Block a user