This commit is contained in:
firestar5683
2026-05-10 11:25:58 -05:00
parent 2bc5247dc0
commit 9872c5fb4b
19 changed files with 403 additions and 244 deletions
+3
View File
@@ -86,6 +86,9 @@ struct StarPilotCarState @0xf35cc4560bbf6ec2 {
customPressed @17 :Bool;
alwaysOnLateralAllowed @18 :Bool;
dashboardStopSign @19 :UInt8; # 0 = no signal / platform doesn't publish
cancelPressed @20 :Bool;
cancelLongPressed @21 :Bool;
cancelVeryLongPressed @22 :Bool;
}
struct StarPilotDeviceState @0xda96579883444c35 {
+4
View File
@@ -217,6 +217,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"CustomCruise", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
{"CustomCruiseLong", {PERSISTENT, FLOAT, "5.0", "5.0", 2}},
{"CustomPersonalities", {PERSISTENT, BOOL, "0", "0", 2}},
{"CancelButtonControl", {PERSISTENT, INT, "1", "0", 2}},
{"CancelButtonControlsMigrated", {PERSISTENT, BOOL, "0", "0"}},
{"TrafficPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
{"AggressivePersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
{"StandardPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
@@ -349,6 +351,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"LKASButtonControl", {PERSISTENT, INT, "5", "0", 2}},
{"LockDoors", {PERSISTENT, BOOL, "1", "0", 0}},
{"LockDoorsTimer", {PERSISTENT, INT, "0", "0", 0}},
{"LongCancelButtonControl", {PERSISTENT, INT, "5", "0", 2}},
{"LongDistanceButtonControl", {PERSISTENT, INT, "5", "0", 2}},
{"LongModeButtonControl", {PERSISTENT, INT, "0", "0", 2}},
{"LongStarButtonControl", {PERSISTENT, INT, "0", "0", 2}},
@@ -583,6 +586,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"VEgoStartingStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"VEgoStopping", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"VEgoStoppingStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"VeryLongCancelButtonControl", {PERSISTENT, INT, "6", "0", 2}},
{"VeryLongDistanceButtonControl", {PERSISTENT, INT, "6", "0", 2}},
{"VeryLongModeButtonControl", {PERSISTENT, INT, "0", "0", 2}},
{"VeryLongStarButtonControl", {PERSISTENT, INT, "0", "0", 2}},
+12 -16
View File
@@ -39,6 +39,10 @@ BOLT_GEN1_CANCEL_PERSONALITY_CARS = {
CAR.CHEVROLET_BOLT_CC_2017,
CAR.CHEVROLET_BOLT_CC_2018_2021,
}
BOLT_CANCEL_BUTTON_CARS = BOLT_GEN1_CANCEL_PERSONALITY_CARS | {
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
CAR.CHEVROLET_BOLT_CC_2022_2023,
}
class CarState(CarStateBase):
@@ -317,7 +321,7 @@ class CarState(CarStateBase):
getattr(starpilot_toggles, "remap_cancel_to_distance", False) and
self.CP.openpilotLongitudinalControl and
bool(self.CP.flags & GMFlags.PEDAL_LONG.value) and
self.CP.carFingerprint in (BOLT_GEN1_CANCEL_PERSONALITY_CARS | {CAR.CHEVROLET_MALIBU_HYBRID_CC})
self.CP.carFingerprint in (BOLT_CANCEL_BUTTON_CARS | {CAR.CHEVROLET_MALIBU_HYBRID_CC})
)
malibu_cancel_passthrough = (
remap_cancel_to_distance and
@@ -325,32 +329,26 @@ class CarState(CarStateBase):
self.CP.openpilotLongitudinalControl and
bool(self.CP.flags & GMFlags.PEDAL_LONG.value)
)
bolt_cancel_personality = (
bolt_cancel_button = (
remap_cancel_to_distance and
self.CP.carFingerprint in BOLT_GEN1_CANCEL_PERSONALITY_CARS and
self.CP.carFingerprint in BOLT_CANCEL_BUTTON_CARS and
self.CP.openpilotLongitudinalControl and
bool(self.CP.flags & GMFlags.PEDAL_LONG.value)
)
bolt_cancel_lkas_conflict = bolt_cancel_button and self.CP.carFingerprint in BOLT_GEN1_CANCEL_PERSONALITY_CARS
cruise_button_map = BUTTONS_DICT
if malibu_cancel_passthrough or bolt_cancel_personality:
if malibu_cancel_passthrough or bolt_cancel_button:
cruise_button_map = {k: v for k, v in BUTTONS_DICT.items() if k != CruiseButtons.CANCEL}
cruise_events = create_button_events(
self.cruise_buttons, prev_cruise_buttons, cruise_button_map, unpressed_btn=CruiseButtons.UNPRESS
)
cancel_gap_events = []
if bolt_cancel_personality and self.cruise_buttons != prev_cruise_buttons:
if prev_cruise_buttons == CruiseButtons.CANCEL:
cancel_gap_events.append(structs.CarState.ButtonEvent(pressed=False, type=ButtonType.gapAdjustCruise))
if self.cruise_buttons == CruiseButtons.CANCEL:
cancel_gap_events.append(structs.CarState.ButtonEvent(pressed=True, type=ButtonType.gapAdjustCruise))
suppress_malibu_side_buttons = malibu_cancel_passthrough and (
self.cruise_buttons in (CruiseButtons.CANCEL, CruiseButtons.MAIN) or
prev_cruise_buttons in (CruiseButtons.CANCEL, CruiseButtons.MAIN)
)
suppress_bolt_cancel_lkas = bolt_cancel_personality and (
suppress_bolt_cancel_lkas = bolt_cancel_lkas_conflict and (
self.cruise_buttons == CruiseButtons.CANCEL or
prev_cruise_buttons == CruiseButtons.CANCEL
)
@@ -365,7 +363,6 @@ class CarState(CarStateBase):
if self.cruise_buttons != CruiseButtons.UNPRESS or prev_cruise_buttons != CruiseButtons.INIT:
ret.buttonEvents = [
*cruise_events,
*cancel_gap_events,
*distance_events,
*lkas_events,
]
@@ -374,9 +371,8 @@ class CarState(CarStateBase):
ret.lowSpeedAlert = True
fp_ret = custom.StarPilotCarState.new_message()
if bolt_cancel_personality and self.cruise_buttons == CruiseButtons.CANCEL:
# Feed long-press personality logic as if distance is held while CANCEL is held.
fp_ret.distancePressed = True
if bolt_cancel_button and self.cruise_buttons == CruiseButtons.CANCEL:
fp_ret.cancelPressed = True
fp_ret.sportGear = pt_cp.vl["SportMode"]["SportMode"] == 1
return ret, fp_ret
+6 -2
View File
@@ -93,12 +93,16 @@ BOLT_PEDAL_LONG_CARS = {
CAR.CHEVROLET_MALIBU_HYBRID_CC,
}
# Cancel-to-personality mapping target: gen1 Bolt pedal-long paths only.
# Cancel-button remap support uses the same safety path on all pedal-long Bolts,
# but only gen1 needs extra LKAS suppression in CarState.
BOLT_GEN1_CANCEL_PERSONALITY_CARS = {
CAR.CHEVROLET_BOLT_CC_2017,
CAR.CHEVROLET_BOLT_CC_2018_2021,
}
CANCEL_REMAP_DISTANCE_CARS = BOLT_GEN1_CANCEL_PERSONALITY_CARS
CANCEL_REMAP_DISTANCE_CARS = BOLT_GEN1_CANCEL_PERSONALITY_CARS | {
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
CAR.CHEVROLET_BOLT_CC_2022_2023,
}
class CarInterface(CarInterfaceBase):
@@ -63,82 +63,6 @@ IONIQ_6_IPEDAL_REGEN_STATE = 0x50
IONIQ_6_IPEDAL_REGEN_STATE_2_PENDING = 0x01
IONIQ_6_IPEDAL_PROGRESS_RETRY_WAIT_FRAMES = 10
IONIQ_6_IPEDAL_RETRY_WAIT_FRAMES = 30
ANGLE_SAFETY_BASELINE_MODEL = "KIA_SPORTAGE_HEV_2026"
def get_baseline_safety_cp():
from opendbc.car.hyundai.interface import CarInterface
return CarInterface.get_non_essential_params(ANGLE_SAFETY_BASELINE_MODEL)
def calculate_angle_torque_reduction_gain(params, CS, apply_torque_last, target_torque_reduction_gain):
target_gain = max(target_torque_reduction_gain, params.ANGLE_ACTIVE_TORQUE_REDUCTION_GAIN)
driver_torque = abs(CS.out.steeringTorque)
alpha = np.interp(driver_torque, [params.STEER_THRESHOLD * 0.8, params.STEER_THRESHOLD * 2.0], [0.02, 0.1])
if CS.out.steeringPressed:
scale = 100.0
clamped_torque_gain = max(apply_torque_last, params.ANGLE_ACTIVE_TORQUE_REDUCTION_GAIN)
target_gain = params.ANGLE_MIN_TORQUE_REDUCTION_GAIN + (clamped_torque_gain - params.ANGLE_MIN_TORQUE_REDUCTION_GAIN) * \
np.exp(-(driver_torque - params.STEER_THRESHOLD) / scale)
new_gain = apply_torque_last + alpha * (target_gain - apply_torque_last)
return float(np.clip(new_gain, params.ANGLE_MIN_TORQUE_REDUCTION_GAIN, params.ANGLE_MAX_TORQUE_REDUCTION_GAIN))
def sp_smooth_angle(v_ego_raw: float, apply_angle: float, apply_angle_last: float) -> float:
if abs(apply_angle - apply_angle_last) <= 0.1:
return apply_angle
adjusted_alpha = np.interp(v_ego_raw, CarControllerParams.SMOOTHING_ANGLE_VEGO_MATRIX,
CarControllerParams.SMOOTHING_ANGLE_ALPHA_MATRIX)
adjusted_alpha_limited = float(min(float(adjusted_alpha), 1.0))
return (apply_angle * adjusted_alpha_limited) + (apply_angle_last * (1.0 - adjusted_alpha_limited))
class TorqueReductionGainController:
def __init__(self, angle_threshold=3.0, debounce_time=0.5, min_gain=0.0, max_gain=1.0,
ramp_up_rate=0.1, ramp_down_rate=0.05):
self.angle_threshold = angle_threshold
self.debounce_time = debounce_time
self.min_gain = min_gain
self.max_gain = max_gain
self.ramp_up_rate = ramp_up_rate
self.ramp_down_rate = ramp_down_rate
self.saturated_since = None
self.gain = min_gain
self.last_update_time = 0.0
def update(self, last_requested_angle, actual_angle, lat_active):
now = self.last_update_time + DT_CTRL if self.last_update_time > 0.0 else DT_CTRL
dt = now - self.last_update_time if self.last_update_time > 0.0 else DT_CTRL
self.last_update_time = now
angle_error = abs(last_requested_angle - actual_angle)
saturated = lat_active and angle_error > self.angle_threshold
if saturated:
if self.saturated_since is None:
self.saturated_since = now
elif (now - self.saturated_since) > self.debounce_time:
self.gain = min(self.gain + self.ramp_up_rate * dt, self.max_gain)
else:
self.saturated_since = None
self.gain = max(self.gain - self.ramp_down_rate * dt, self.min_gain)
if not lat_active:
self.gain = self.min_gain
self.saturated_since = None
return self.gain
def reset(self):
self.gain = self.min_gain
self.saturated_since = None
self.last_update_time = 0.0
@dataclass
class Ioniq6LongitudinalTuningState:
desired_accel: float = 0.0
@@ -300,12 +224,10 @@ class CarController(CarControllerBase):
self.packer = CANPacker(dbc_names[Bus.pt])
self.angle_limit_counter = 0
self.VM = VehicleModel(CP)
self.BASELINE_VM = VehicleModel(get_baseline_safety_cp())
self.accel_last = 0
self.apply_torque_last = 0
self.apply_angle_last = 0.0
self.angle_enable_smoothing_factor = True
self.car_fingerprint = CP.carFingerprint
self.last_button_frame = 0
self.ecu_disable_failed = False
@@ -326,14 +248,6 @@ class CarController(CarControllerBase):
self._ioniq_6_regen_request_sent = False
self._ioniq_6_last_gear = structs.CarState.GearShifter.unknown
self._genesis_g90_long_tuning = GenesisG90LongitudinalTuningState()
self.angle_torque_reduction_gain_controller = TorqueReductionGainController(
angle_threshold=0.3,
debounce_time=0.1,
min_gain=self.params.ANGLE_ACTIVE_TORQUE_REDUCTION_GAIN,
max_gain=self.params.ANGLE_MAX_TORQUE_REDUCTION_GAIN,
ramp_up_rate=self.params.ANGLE_RAMP_UP_TORQUE_REDUCTION_RATE,
ramp_down_rate=self.params.ANGLE_RAMP_DOWN_TORQUE_REDUCTION_RATE,
)
def _reset_ioniq_6_always_ipedal(self) -> None:
self._ioniq_6_always_ipedal_pending = False
@@ -442,23 +356,17 @@ class CarController(CarControllerBase):
desired_angle = float(np.clip(actuators.steeringAngleDeg,
-self.params.ANGLE_LIMITS.STEER_ANGLE_MAX,
self.params.ANGLE_LIMITS.STEER_ANGLE_MAX))
if self.angle_enable_smoothing_factor and abs(CS.out.vEgoRaw) < CarControllerParams.SMOOTHING_ANGLE_MAX_VEGO:
desired_angle = sp_smooth_angle(CS.out.vEgoRaw, desired_angle, self.apply_angle_last)
apply_angle = apply_steer_angle_limits_vm(desired_angle, self.apply_angle_last, CS.out.vEgoRaw,
CS.out.steeringAngleDeg, CC.latActive, self.params, self.VM)
if self.CP.carFingerprint != ANGLE_SAFETY_BASELINE_MODEL:
apply_angle = apply_steer_angle_limits_vm(apply_angle or desired_angle, self.apply_angle_last, CS.out.vEgoRaw,
CS.out.steeringAngleDeg, CC.latActive, self.params, self.BASELINE_VM)
if CS.out.steeringPressed and abs(CS.out.steeringTorque) > self.params.STEER_THRESHOLD:
apply_torque = self.params.ANGLE_MIN_TORQUE_REDUCTION_GAIN
elif CC.latActive and CS.out.vEgoRaw < 0.3:
apply_torque = self.params.ANGLE_ACTIVE_TORQUE_REDUCTION_GAIN
else:
apply_torque = self.params.ANGLE_MAX_TORQUE_REDUCTION_GAIN if CC.latActive else 0.0
target_torque_reduction_gain = self.angle_torque_reduction_gain_controller.update(
last_requested_angle=self.apply_angle_last,
actual_angle=CS.out.steeringAngleDeg,
lat_active=CC.latActive,
)
apply_torque = calculate_angle_torque_reduction_gain(self.params, CS, self.apply_torque_last, target_torque_reduction_gain)
apply_steer_req = CC.latActive and apply_torque != 0
apply_steer_req = CC.latActive and apply_torque > 0.0
torque_fault = False
if apply_angle is None:
@@ -8,10 +8,9 @@ from opendbc.car import Bus, ButtonType, gen_empty_fingerprint, structs
from opendbc.car.structs import CarControl, CarParams
from opendbc.car.fw_versions import build_fw_dict, match_fw_to_car
from opendbc.car.hyundai.carcontroller import CarController, Ioniq6LongitudinalTuningState, GenesisG90LongitudinalTuningState, \
IONIQ_6_IPEDAL_PADDLE_BURST_COUNT, IONIQ_6_IPEDAL_NEXT_COUNTER_BURST_COUNT, \
calculate_angle_torque_reduction_gain, sp_smooth_angle, \
update_ioniq_6_longitudinal_tuning, \
update_genesis_g90_longitudinal_tuning
IONIQ_6_IPEDAL_PADDLE_BURST_COUNT, IONIQ_6_IPEDAL_NEXT_COUNTER_BURST_COUNT, \
update_ioniq_6_longitudinal_tuning, \
update_genesis_g90_longitudinal_tuning
from opendbc.car.hyundai.carstate import CarState, decode_ioniq_6_blindspot_radar_state, decode_ioniq_6_ipedal_intermediate_state, \
decode_ioniq_6_ipedal_state, decode_ioniq_6_max_regen_state
from opendbc.car.hyundai.interface import CarInterface
@@ -1075,32 +1074,6 @@ class TestHyundaiFingerprint:
assert sportage_params.ANGLE_LIMITS.MAX_ANGLE_RATE > comparison_params.ANGLE_LIMITS.MAX_ANGLE_RATE
assert comparison_params.ANGLE_LIMITS.MAX_LATERAL_JERK == ioniq6_params.ANGLE_LIMITS.MAX_LATERAL_JERK
def test_angle_smoothing_is_speed_dependent(self):
target_angle = 12.0
last_angle = 0.0
low_speed_smoothed = sp_smooth_angle(0.0, target_angle, last_angle)
high_speed_smoothed = sp_smooth_angle(25.0, target_angle, last_angle)
assert last_angle < low_speed_smoothed < target_angle
assert high_speed_smoothed == pytest.approx(target_angle)
assert low_speed_smoothed < high_speed_smoothed
def test_angle_torque_reduction_gain_gives_up_on_driver_override(self):
sportage = CarParams.new_message()
sportage.carFingerprint = CAR.KIA_SPORTAGE_HEV_2026
sportage.flags = int(HyundaiFlags.CANFD | HyundaiFlags.CANFD_ANGLE_STEERING)
params = CarControllerParams(sportage)
no_override_cs = SimpleNamespace(out=SimpleNamespace(steeringTorque=0.0, steeringPressed=False))
override_cs = SimpleNamespace(out=SimpleNamespace(steeringTorque=float(params.STEER_THRESHOLD * 2), steeringPressed=True))
no_override_gain = calculate_angle_torque_reduction_gain(params, no_override_cs, params.ANGLE_ACTIVE_TORQUE_REDUCTION_GAIN, 1.0)
override_gain = calculate_angle_torque_reduction_gain(params, override_cs, 1.0, 1.0)
assert params.ANGLE_ACTIVE_TORQUE_REDUCTION_GAIN <= no_override_gain <= params.ANGLE_MAX_TORQUE_REDUCTION_GAIN
assert params.ANGLE_MIN_TORQUE_REDUCTION_GAIN <= override_gain < no_override_gain
def test_ioniq_5_canfd_aux_messages_are_optional(self):
toggles = get_test_toggles()
fingerprint = gen_empty_fingerprint()
+1 -7
View File
@@ -32,14 +32,8 @@ class CarControllerParams:
MAX_ANGLE_RATE=5,
)
ANGLE_MAX_TORQUE_REDUCTION_GAIN = 1.0
ANGLE_MIN_TORQUE_REDUCTION_GAIN = 0.1
ANGLE_MIN_TORQUE_REDUCTION_GAIN = 0.6
ANGLE_ACTIVE_TORQUE_REDUCTION_GAIN = 0.6
ANGLE_RAMP_UP_TORQUE_REDUCTION_RATE = 0.008
ANGLE_RAMP_DOWN_TORQUE_REDUCTION_RATE = 0.0012
ANGLE_TORQUE_OVERRIDE_CYCLES = 17
SMOOTHING_ANGLE_VEGO_MATRIX = [0.0, 8.5, 11.0, 13.8, 22.22]
SMOOTHING_ANGLE_ALPHA_MATRIX = [0.05, 0.1, 0.3, 0.6, 1.0]
SMOOTHING_ANGLE_MAX_VEGO = SMOOTHING_ANGLE_VEGO_MATRIX[-1]
def __init__(self, CP, vEgoRaw=100.):
self.ANGLE_LIMITS = self.ANGLE_LIMITS
+21 -4
View File
@@ -11,7 +11,6 @@ from msgq.visionipc import VisionIpcClient, VisionStreamType
from opendbc.car.chrysler.values import pacifica_hybrid_aol_stock_acc_mode
from opendbc.car.gm.values import GMFlags
from opendbc.safety import ALTERNATIVE_EXPERIENCE
from openpilot.common.params import Params
from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper, DT_CTRL
@@ -160,6 +159,7 @@ class SelfdriveD:
self.starpilot_AM = AlertManager()
self.starpilot_events = Events(starpilot=True)
self.cancel_pressed_previously = False
self.distance_pressed_previously = False
self.display_timer = 0
@@ -168,8 +168,7 @@ class SelfdriveD:
self.starpilot_events_prev = []
remap_cancel_to_distance = bool(self.CP.alternativeExperience & ALTERNATIVE_EXPERIENCE.GM_REMAP_CANCEL_TO_DISTANCE)
self.has_menu = self.CP.brand == "gm" and not (self.CP.flags & GMFlags.NO_CAMERA.value) and not remap_cancel_to_distance
self.has_menu = self.CP.brand == "gm" and not (self.CP.flags & GMFlags.NO_CAMERA.value)
self.FPCP = messaging.log_from_bytes(self.params.get("StarPilotCarParams", block=True), custom.StarPilotCarParams)
@@ -516,10 +515,22 @@ class SelfdriveD:
if self.sm['modelV2'].frameDropPerc > 20:
self.events.add(EventName.modeldLagging)
# Decrement personality on distance button press
# Decrement personality on configured steering-wheel button presses
if self.CP.openpilotLongitudinalControl:
cancel_pressed = False
distance_pressed = False
if self.starpilot_toggles.personality_profile_via_cancel:
cancel_pressed |= bool(getattr(self.sm['starpilotCarState'], "cancelPressed", False))
cancel_pressed &= not (
self.sm['starpilotCarState'].cancelLongPressed or
self.sm['starpilotCarState'].cancelVeryLongPressed
)
if self.starpilot_toggles.personality_profile_via_cancel_long:
cancel_pressed |= self.sm['starpilotCarState'].cancelLongPressed
if self.starpilot_toggles.personality_profile_via_cancel_very_long:
cancel_pressed |= self.sm['starpilotCarState'].cancelVeryLongPressed
if self.starpilot_toggles.personality_profile_via_distance:
distance_pressed |= any(not be.pressed and be.type == ButtonType.gapAdjustCruise for be in CS.buttonEvents)
distance_pressed &= not (self.sm['starpilotCarState'].distanceLongPressed or self.sm['starpilotCarState'].distanceVeryLongPressed)
@@ -530,6 +541,11 @@ class SelfdriveD:
if self.starpilot_toggles.personality_profile_via_lkas:
distance_pressed |= any(not be.pressed and be.type == ButtonType.lkas for be in CS.buttonEvents)
if not cancel_pressed and self.cancel_pressed_previously and not self.safe_mode:
self.personality = (self.personality - 1) % 3
self.params.put_nonblocking('LongitudinalPersonality', self.personality)
self.events.add(EventName.personalityChanged)
if not distance_pressed and self.distance_pressed_previously and not self.safe_mode:
if self.display_timer > 0 or not self.has_menu:
self.personality = (self.personality - 1) % 3
@@ -537,6 +553,7 @@ class SelfdriveD:
self.events.add(EventName.personalityChanged)
self.display_timer = 350
self.cancel_pressed_previously = cancel_pressed
self.distance_pressed_previously = distance_pressed
self.display_timer -= 1
@@ -35,6 +35,7 @@ from openpilot.selfdrive.ui.mici.layouts.settings.fingerprint_catalog import (
get_fingerprint_catalog,
shorten_model_label,
)
from openpilot.starpilot.common.starpilot_variables import migrate_cancel_button_controls
ACTION_OPTIONS = [
@@ -466,9 +467,10 @@ class VehicleSettingsManagerView(Widget):
rows.append({"target_id": "toggle:FrogsGoMoosTweak", "type": "toggle",
"title": tr("FrogsGoMoo Tweak"), "get_state": lambda: self._controller._params.get_bool("FrogsGoMoosTweak")})
rows.append({"target_id": "toggle:RemapCancelToDistance", "type": "toggle",
"title": tr("Remap Cancel Button"), "subtitle": tr("Remap the Cancel button to act as the Distance button."),
"get_state": lambda: self._controller._params.get_bool("RemapCancelToDistance")})
if cs.isBolt and cs.hasPedal:
rows.append({"target_id": "toggle:RemapCancelToDistance", "type": "toggle",
"title": tr("Remap Cancel Button"), "subtitle": tr("Treat the Cancel button as an extra mappable steering-wheel button."),
"get_state": lambda: self._controller._params.get_bool("RemapCancelToDistance")})
if cs.isHKGCanFd:
rows.append({"target_id": "toggle:AlwaysIPedal", "type": "toggle",
"title": tr("Always I-Pedal"),
@@ -487,6 +489,10 @@ class VehicleSettingsManagerView(Widget):
for key in ("DistanceButtonControl", "LongDistanceButtonControl", "VeryLongDistanceButtonControl"):
rows.append({"target_id": f"select:{key}", "type": "select", "title": tr(self._controller._action_title(key)),
"get_value": lambda k=key: self._controller._get_action_name(k), "pill_width": 140})
if cs.isBolt and cs.hasPedal and self._controller._params.get_bool("RemapCancelToDistance"):
for key in ("CancelButtonControl", "LongCancelButtonControl", "VeryLongCancelButtonControl"):
rows.append({"target_id": f"select:{key}", "type": "select", "title": tr(self._controller._action_title(key)),
"get_value": lambda k=key: self._controller._get_action_name(k), "pill_width": 140})
if not cs.isSubaru and not (cs.lkasAllowedForAOL and self._controller._params.get_bool("AlwaysOnLateral") and self._controller._params.get_bool("AlwaysOnLateralLKAS")):
rows.append({"target_id": "select:LKASButtonControl", "type": "select", "title": tr("LKAS Button"),
"get_value": lambda: self._controller._get_action_name("LKASButtonControl"), "pill_width": 140})
@@ -507,8 +513,11 @@ class StarPilotVehicleSettingsLayout(_SettingsPage):
def _action_title(self, key: str) -> str:
titles = {
"CancelButtonControl": "Cancel Button",
"DistanceButtonControl": "Distance Button",
"LongCancelButtonControl": "Cancel (Long Press)",
"LongDistanceButtonControl": "Distance (Long Press)",
"VeryLongCancelButtonControl": "Cancel (Very Long)",
"VeryLongDistanceButtonControl": "Distance (Very Long)",
"LKASButtonControl": "LKAS Button",
"ModeButtonControl": "Mode Button",
@@ -521,16 +530,10 @@ class StarPilotVehicleSettingsLayout(_SettingsPage):
return titles.get(key, key)
def _get_action_name(self, key: str) -> str:
if key == "LKASButtonControl" and self._params.get_bool("RemapCancelToDistance"):
if self._params.get_int("LKASButtonControl") != 0:
self._params.put_int("LKASButtonControl", 0)
return tr(ACTION_NAME_BY_ID[0])
idx = self._params.get_int(key)
return tr(ACTION_NAME_BY_ID.get(idx, ACTION_NAMES[0]))
def _get_available_actions(self, key: str | None = None) -> list[str]:
if key == "LKASButtonControl" and self._params.get_bool("RemapCancelToDistance"):
return [tr(ACTION_NAME_BY_ID[0])]
cs = starpilot_state.car_state
return [tr(o["name"]) for o in ACTION_OPTIONS if cs.hasOpenpilotLongitudinal or not o.get("requires_longitudinal", False)]
@@ -550,8 +553,8 @@ class StarPilotVehicleSettingsLayout(_SettingsPage):
if param_key == "RemapCancelToDistance":
new_state = not self._params.get_bool("RemapCancelToDistance")
self._params.put_bool("RemapCancelToDistance", new_state)
if new_state and self._params.get_int("LKASButtonControl") != 0:
self._params.put_int("LKASButtonControl", 0)
if new_state:
migrate_cancel_button_controls(self._params)
return
current = self._params.get_bool(param_key) if self._params.get(param_key, encoding="utf-8") is not None else False
self._params.put_bool(param_key, not current)
@@ -616,10 +619,6 @@ class StarPilotVehicleSettingsLayout(_SettingsPage):
gui_app.push_widget(dialog)
def _show_action_picker(self, key: str):
if key == "LKASButtonControl" and self._params.get_bool("RemapCancelToDistance"):
if self._params.get_int("LKASButtonControl") != 0:
self._params.put_int("LKASButtonControl", 0)
return
actions = self._get_available_actions(key)
current = self._get_action_name(key)
if current not in actions:
+70 -4
View File
@@ -136,6 +136,13 @@ BUTTON_FUNCTIONS = {
"BOOKMARK": 8,
}
CANCEL_BUTTON_MIGRATION_KEY = "CancelButtonControlsMigrated"
CANCEL_BUTTON_MAPPINGS = (
("DistanceButtonControl", "CancelButtonControl"),
("LongDistanceButtonControl", "LongCancelButtonControl"),
("VeryLongDistanceButtonControl", "VeryLongCancelButtonControl"),
)
DEVELOPER_SIDEBAR_METRICS = {
"NONE": 0,
"ACCELERATION_CURRENT": 1,
@@ -315,16 +322,30 @@ def process_starpilot_toggles(toggles):
return StarPilotVariables().starpilot_toggles
def update_starpilot_toggles():
migrate_cancel_button_controls()
if not hasattr(update_starpilot_toggles, "_params_memory"):
update_starpilot_toggles._params_memory = Params(memory=True)
update_starpilot_toggles._params_memory.put_bool("StarPilotTogglesUpdated", True)
def migrate_cancel_button_controls(params: Params | None = None) -> bool:
params = params or Params(return_defaults=True)
if params.get_bool(CANCEL_BUTTON_MIGRATION_KEY) or not params.get_bool("RemapCancelToDistance"):
return False
for source_key, target_key in CANCEL_BUTTON_MAPPINGS:
params.put_int(target_key, params.get_int(source_key))
params.put_bool(CANCEL_BUTTON_MIGRATION_KEY, True)
return True
class StarPilotVariables:
def __init__(self):
self.params = Params(return_defaults=True)
self.params_raw = Params()
self.params_memory = Params(memory=True)
migrate_cancel_button_controls(self.params)
self.starpilot_toggles = SimpleNamespace()
toggle = self.starpilot_toggles
@@ -781,6 +802,10 @@ class StarPilotVariables:
toggle.always_ipedal = self.get_value("AlwaysIPedal", condition=toggle.car_model == HYUNDAI_CAR.HYUNDAI_IONIQ_6)
toggle.nostalgia_mode = self.get_value("NostalgiaMode", condition=toggle.openpilot_longitudinal and toggle.car_model == HYUNDAI_CAR.HYUNDAI_IONIQ_6)
toggle.remap_cancel_to_distance = self.get_value(
"RemapCancelToDistance",
condition=toggle.car_make == "gm" and toggle.has_pedal and "BOLT" in toggle.car_model,
)
distance_button_control = self.get_value("DistanceButtonControl", cast=float)
toggle.experimental_mode_via_distance = toggle.openpilot_longitudinal and distance_button_control == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
@@ -815,6 +840,51 @@ class StarPilotVariables:
toggle.traffic_mode_via_distance_very_long = toggle.openpilot_longitudinal and distance_button_control_very_long == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
toggle.bookmark_via_distance_very_long = distance_button_control_very_long == BUTTON_FUNCTIONS["BOOKMARK"]
cancel_button_control = self.get_value(
"CancelButtonControl",
cast=float,
condition=toggle.remap_cancel_to_distance,
)
toggle.experimental_mode_via_cancel = toggle.openpilot_longitudinal and cancel_button_control == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
toggle.experimental_mode_via_press |= toggle.experimental_mode_via_cancel
toggle.force_coast_via_cancel = toggle.openpilot_longitudinal and cancel_button_control == BUTTON_FUNCTIONS["FORCE_COAST"]
toggle.pause_lateral_via_cancel = cancel_button_control == BUTTON_FUNCTIONS["PAUSE_LATERAL"]
toggle.pause_longitudinal_via_cancel = toggle.openpilot_longitudinal and cancel_button_control == BUTTON_FUNCTIONS["PAUSE_LONGITUDINAL"]
toggle.personality_profile_via_cancel = toggle.openpilot_longitudinal and cancel_button_control == BUTTON_FUNCTIONS["PERSONALITY_PROFILE"]
toggle.switchback_mode_via_cancel = cancel_button_control == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
toggle.traffic_mode_via_cancel = toggle.openpilot_longitudinal and cancel_button_control == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
toggle.bookmark_via_cancel = cancel_button_control == BUTTON_FUNCTIONS["BOOKMARK"]
cancel_button_control_long = self.get_value(
"LongCancelButtonControl",
cast=float,
condition=toggle.remap_cancel_to_distance,
)
toggle.experimental_mode_via_cancel_long = toggle.openpilot_longitudinal and cancel_button_control_long == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
toggle.experimental_mode_via_press |= toggle.experimental_mode_via_cancel_long
toggle.force_coast_via_cancel_long = toggle.openpilot_longitudinal and cancel_button_control_long == BUTTON_FUNCTIONS["FORCE_COAST"]
toggle.pause_lateral_via_cancel_long = cancel_button_control_long == BUTTON_FUNCTIONS["PAUSE_LATERAL"]
toggle.pause_longitudinal_via_cancel_long = toggle.openpilot_longitudinal and cancel_button_control_long == BUTTON_FUNCTIONS["PAUSE_LONGITUDINAL"]
toggle.personality_profile_via_cancel_long = toggle.openpilot_longitudinal and cancel_button_control_long == BUTTON_FUNCTIONS["PERSONALITY_PROFILE"]
toggle.switchback_mode_via_cancel_long = cancel_button_control_long == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
toggle.traffic_mode_via_cancel_long = toggle.openpilot_longitudinal and cancel_button_control_long == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
toggle.bookmark_via_cancel_long = cancel_button_control_long == BUTTON_FUNCTIONS["BOOKMARK"]
cancel_button_control_very_long = self.get_value(
"VeryLongCancelButtonControl",
cast=float,
condition=toggle.remap_cancel_to_distance,
)
toggle.experimental_mode_via_cancel_very_long = toggle.openpilot_longitudinal and cancel_button_control_very_long == BUTTON_FUNCTIONS["EXPERIMENTAL_MODE"]
toggle.experimental_mode_via_press |= toggle.experimental_mode_via_cancel_very_long
toggle.force_coast_via_cancel_very_long = toggle.openpilot_longitudinal and cancel_button_control_very_long == BUTTON_FUNCTIONS["FORCE_COAST"]
toggle.pause_lateral_via_cancel_very_long = cancel_button_control_very_long == BUTTON_FUNCTIONS["PAUSE_LATERAL"]
toggle.pause_longitudinal_via_cancel_very_long = toggle.openpilot_longitudinal and cancel_button_control_very_long == BUTTON_FUNCTIONS["PAUSE_LONGITUDINAL"]
toggle.personality_profile_via_cancel_very_long = toggle.openpilot_longitudinal and cancel_button_control_very_long == BUTTON_FUNCTIONS["PERSONALITY_PROFILE"]
toggle.switchback_mode_via_cancel_very_long = cancel_button_control_very_long == BUTTON_FUNCTIONS["SWITCHBACK_MODE"]
toggle.traffic_mode_via_cancel_very_long = toggle.openpilot_longitudinal and cancel_button_control_very_long == BUTTON_FUNCTIONS["TRAFFIC_MODE"]
toggle.bookmark_via_cancel_very_long = cancel_button_control_very_long == BUTTON_FUNCTIONS["BOOKMARK"]
toggle.frogsgomoo_tweak = self.get_value("FrogsGoMoosTweak", condition=toggle.openpilot_longitudinal and toggle.car_make == "toyota")
toggle.stoppingDecelRate = 0.01 if toggle.frogsgomoo_tweak else toggle.stoppingDecelRate
toggle.vEgoStarting = 0.1 if toggle.frogsgomoo_tweak else toggle.vEgoStarting
@@ -1210,10 +1280,6 @@ class StarPilotVariables:
condition=toggle.openpilot_longitudinal and toggle.car_make == "gm",
)
toggle.remote_start_boots_comma = self.get_value("RemoteStartBootsComma", condition=toggle.car_make == "gm")
toggle.remap_cancel_to_distance = self.get_value(
"RemapCancelToDistance",
condition=toggle.car_make == "gm" and toggle.has_pedal and "BOLT" in toggle.car_model,
)
gm_auto_hold_supported = toggle.car_model in LEGACY_VOLT_STOCK_ACC_CARS
toggle.gm_auto_hold = self.get_value("GMAutoHold", condition=gm_auto_hold_supported)
@@ -30,17 +30,33 @@ def test_get_starpilot_toggles_uses_last_non_empty_broadcast(monkeypatch):
class _FakeParams:
def __init__(self, floats=None):
def __init__(self, floats=None, ints=None, bools=None):
self.floats = dict(floats or {})
self.ints = dict(ints or {})
self.bools = dict(bools or {})
def get_float(self, key):
return float(self.floats.get(key, 0.0))
def get_int(self, key):
return int(self.ints.get(key, 0))
def get_bool(self, key):
return bool(self.bools.get(key, False))
def put_float(self, key, value):
self.floats[key] = float(value)
def put_int(self, key, value):
self.ints[key] = int(value)
def put_bool(self, key, value):
self.bools[key] = bool(value)
def remove(self, key):
self.floats.pop(key, None)
self.ints.pop(key, None)
self.bools.pop(key, None)
def test_sync_stock_param_does_not_stomp_existing_custom_value_when_stock_missing():
@@ -52,3 +68,26 @@ def test_sync_stock_param_does_not_stomp_existing_custom_value_when_stock_missin
assert params.get_float("SteerDelay") == 0.35
assert params.get_float("SteerDelayStock") == 0.10
def test_cancel_button_migration_copies_distance_actions_once():
params = _FakeParams(
ints={
"DistanceButtonControl": 8,
"LongDistanceButtonControl": 4,
"VeryLongDistanceButtonControl": 7,
},
bools={"RemapCancelToDistance": True},
)
assert spv.migrate_cancel_button_controls(params) is True
assert params.get_int("CancelButtonControl") == 8
assert params.get_int("LongCancelButtonControl") == 4
assert params.get_int("VeryLongCancelButtonControl") == 7
assert params.get_bool(spv.CANCEL_BUTTON_MIGRATION_KEY) is True
params.put_int("DistanceButtonControl", 1)
params.put_int("CancelButtonControl", 3)
assert spv.migrate_cancel_button_controls(params) is False
assert params.get_int("CancelButtonControl") == 3
+20
View File
@@ -25,6 +25,7 @@ class StarPilotCard:
self.prev_active = False
self.prev_cruise_enabled = False
self.decel_pressed = False
self.cancelPressed_previously = False
self.distancePressed_previously = False
self.force_coast = False
self.modePressed_previously = False
@@ -37,6 +38,7 @@ class StarPilotCard:
self.traffic_mode_enabled = False
self.gap_counter = 0
self.cancel_counter = 0
self._distance_poll_counter = 0
self._onroad_distance_pressed = False
@@ -149,6 +151,22 @@ class StarPilotCard:
self.handle_button_event("distance_long", sm, starpilot_toggles)
self.handle_button_event("distance_very_long", sm, starpilot_toggles)
cancel_pressed = bool(getattr(starpilotCarState, "cancelPressed", False))
if cancel_pressed:
self.cancel_counter += 1
elif not self.cancelPressed_previously:
self.cancel_counter = 0
self.cancelPressed_previously = cancel_pressed
if not cancel_pressed and 1 <= self.cancel_counter < self.long_press_threshold:
self.handle_button_event("cancel", sm, starpilot_toggles)
elif self.cancel_counter == self.long_press_threshold:
self.handle_button_event("cancel_long", sm, starpilot_toggles)
elif self.cancel_counter == self.very_long_press_threshold:
self.handle_button_event("cancel_long", sm, starpilot_toggles)
self.handle_button_event("cancel_very_long", sm, starpilot_toggles)
if any(be.pressed and be.type == ButtonType.lkas for be in carState.buttonEvents):
self.handle_button_event("lkas", sm, starpilot_toggles)
@@ -186,6 +204,8 @@ class StarPilotCard:
starpilotCarState.accelPressed = self.accel_pressed
starpilotCarState.alwaysOnLateralAllowed = self.always_on_lateral_allowed
starpilotCarState.alwaysOnLateralEnabled = self.always_on_lateral_enabled
starpilotCarState.cancelLongPressed = self.very_long_press_threshold > self.cancel_counter >= self.long_press_threshold
starpilotCarState.cancelVeryLongPressed = self.cancel_counter >= self.very_long_press_threshold
starpilotCarState.decelPressed = self.decel_pressed
starpilotCarState.distanceLongPressed = self.very_long_press_threshold > self.gap_counter >= self.long_press_threshold
starpilotCarState.distanceVeryLongPressed = self.gap_counter >= self.very_long_press_threshold
@@ -46,6 +46,9 @@ def make_toggles(**overrides):
"always_on_lateral_lkas": False,
"always_on_lateral_main": False,
"always_on_lateral_pause_speed": 0.0,
"bookmark_via_cancel": False,
"bookmark_via_cancel_long": False,
"bookmark_via_cancel_very_long": False,
"bookmark_via_lkas": False,
"conditional_experimental_mode": False,
"experimental_mode_via_lkas": False,
@@ -167,3 +170,29 @@ def test_pacifica_hybrid_main_aol_waits_for_set_press(monkeypatch, tmp_path):
ret = card.update(car_state, starpilot_car_state, sm, toggles)
assert ret.alwaysOnLateralAllowed is False
assert ret.alwaysOnLateralEnabled is False
def test_cancel_button_short_press_can_run_independent_mapping(monkeypatch, tmp_path):
monkeypatch.setattr(spc, "Params", FakeParams)
monkeypatch.setattr(spc, "is_FrogsGoMoo", lambda: False)
monkeypatch.setattr(spc, "ERROR_LOGS_PATH", tmp_path)
card = spc.StarPilotCard(SimpleNamespace(brand="gm"), SimpleNamespace(alternativeExperience=0))
sm = make_sm()
toggles = make_toggles(bookmark_via_cancel=True)
starpilot_car_state = SimpleNamespace(distancePressed=False, cancelPressed=False)
card.update(make_car_state(), starpilot_car_state, sm, toggles)
assert card.params_memory.get_int("WheelButtonBookmarkCounter") == 0
starpilot_car_state.cancelPressed = True
ret = card.update(make_car_state(), starpilot_car_state, sm, toggles)
assert ret.cancelLongPressed is False
assert ret.cancelVeryLongPressed is False
assert card.params_memory.get_int("WheelButtonBookmarkCounter") == 0
starpilot_car_state.cancelPressed = False
ret = card.update(make_car_state(), starpilot_car_state, sm, toggles)
assert ret.cancelLongPressed is False
assert ret.cancelVeryLongPressed is False
assert card.params_memory.get_int("WheelButtonBookmarkCounter") == 1
@@ -719,10 +719,9 @@ function clearSearchFilter() {
scheduleSyncInputs()
}
const cancelButtonKeys = new Set(["CancelButtonControl", "LongCancelButtonControl", "VeryLongCancelButtonControl"])
function getSettingLockReason(param) {
if (param?.key === "LKASButtonControl" && !!state.values.RemapCancelToDistance) {
return "Cancel remap requires the LKAS button to stay on No Action."
}
return ""
}
@@ -748,6 +747,9 @@ function renderSettingRow(p) {
if (!isParamEnabledForChildren(p.parent_key)) return ""
if (!state.expanded[p.parent_key]) return ""
}
if (cancelButtonKeys.has(p?.key) && !state.values.RemapCancelToDistance) {
return ""
}
const isNumeric = p.ui_type === "numeric"
const isColor = p.ui_type === "color"
@@ -2318,8 +2318,8 @@
"params": [
{
"key": "RemapCancelToDistance",
"label": "Remap Cancel To Distance",
"description": "On pedal-interceptor Bolts, remap the steering-wheel CANCEL button to distance/personality input.",
"label": "Remap Cancel Button",
"description": "On pedal-interceptor Bolts, treat the steering-wheel CANCEL button as an extra mappable button.",
"data_type": "bool",
"ui_type": "toggle"
},
@@ -2337,6 +2337,51 @@
"data_type": "bool",
"ui_type": "toggle"
},
{
"key": "CancelButtonControl",
"label": "Cancel Button (Short Press)",
"description": "Action performed when the remapped \"Cancel\" button is pressed.",
"data_type": "int",
"ui_type": "dropdown",
"options": [
{
"value": 0,
"label": "No Action"
},
{
"value": 1,
"label": "Change Personality"
},
{
"value": 2,
"label": "Force Coast"
},
{
"value": 3,
"label": "Pause Steering"
},
{
"value": 4,
"label": "Pause Accel/Brake"
},
{
"value": 5,
"label": "Toggle Experimental"
},
{
"value": 6,
"label": "Toggle Traffic"
},
{
"value": 7,
"label": "Toggle Switchback"
},
{
"value": 8,
"label": "Create Bookmark"
}
]
},
{
"key": "DistanceButtonControl",
"label": "Distance Button (Short Press)",
@@ -2382,6 +2427,51 @@
}
]
},
{
"key": "LongCancelButtonControl",
"label": "Cancel Button (Long Press)",
"description": "Action performed when the remapped \"Cancel\" button is pressed for more than 0.5 seconds.",
"data_type": "int",
"ui_type": "dropdown",
"options": [
{
"value": 0,
"label": "No Action"
},
{
"value": 1,
"label": "Change Personality"
},
{
"value": 2,
"label": "Force Coast"
},
{
"value": 3,
"label": "Pause Steering"
},
{
"value": 4,
"label": "Pause Accel/Brake"
},
{
"value": 5,
"label": "Toggle Experimental"
},
{
"value": 6,
"label": "Toggle Traffic"
},
{
"value": 7,
"label": "Toggle Switchback"
},
{
"value": 8,
"label": "Create Bookmark"
}
]
},
{
"key": "LongDistanceButtonControl",
"label": "Distance Button (Long Press)",
@@ -2427,6 +2517,51 @@
}
]
},
{
"key": "VeryLongCancelButtonControl",
"label": "Cancel Button (Extra Long Press)",
"description": "Action performed when the remapped \"Cancel\" button is pressed for more than 2.5 seconds.",
"data_type": "int",
"ui_type": "dropdown",
"options": [
{
"value": 0,
"label": "No Action"
},
{
"value": 1,
"label": "Change Personality"
},
{
"value": 2,
"label": "Force Coast"
},
{
"value": 3,
"label": "Pause Steering"
},
{
"value": 4,
"label": "Pause Accel/Brake"
},
{
"value": 5,
"label": "Toggle Experimental"
},
{
"value": 6,
"label": "Toggle Traffic"
},
{
"value": 7,
"label": "Toggle Switchback"
},
{
"value": 8,
"label": "Create Bookmark"
}
]
},
{
"key": "VeryLongDistanceButtonControl",
"label": "Distance Button (Extra Long Press)",
+13 -22
View File
@@ -63,7 +63,7 @@ from openpilot.starpilot.common.maps_catalog import (
from openpilot.starpilot.common.experimental_state import sync_persist_experimental_state
from openpilot.starpilot.common.starpilot_utilities import delete_file, get_lock_status, run_cmd
from openpilot.starpilot.common.starpilot_variables import ACTIVE_THEME_PATH, ERROR_LOGS_PATH, EXCLUDED_KEYS, LEGACY_STARPILOT_PARAM_RENAMES, MAPS_PATH, MODELS_PATH, RESOURCES_REPO, SCREEN_RECORDINGS_PATH, STOCK_THEME_PATH, THEME_SAVE_PATH,\
default_ev_tuning_enabled, update_starpilot_toggles
default_ev_tuning_enabled, migrate_cancel_button_controls, update_starpilot_toggles
from openpilot.starpilot.common.testing_grounds import (
DEFAULT_TESTING_GROUND_VARIANT as SHARED_DEFAULT_TESTING_GROUND_VARIANT,
TESTING_GROUND_VARIANT_LABELS as SHARED_TESTING_GROUND_VARIANT_LABELS,
@@ -784,17 +784,6 @@ def _get_param_int_value(key, default=0):
except Exception:
return int(default)
def _enforce_cancel_remap_lkas_lock():
if not params.get_bool("RemapCancelToDistance"):
return False
if _get_param_int_value("LKASButtonControl", 0) == 0:
return False
params.put("LKASButtonControl", "0")
update_starpilot_toggles()
return True
def _get_system_uptime_seconds():
try:
with open("/proc/uptime", "r", encoding="utf-8") as uptime_file:
@@ -3822,21 +3811,23 @@ def setup(app):
metered_enabled = str_val.strip() in ("1", "true", "True")
gsm_metered_apply_result = _apply_cellular_metered_setting(metered_enabled)
locked_lkas = _enforce_cancel_remap_lkas_lock()
migrated_cancel_buttons = migrate_cancel_button_controls(params)
update_starpilot_toggles()
response = {"message": f"Parameter '{key}' updated successfully."}
updated = {}
if key == "RemapCancelToDistance" and params.get_bool("RemapCancelToDistance"):
updated["RemapCancelToDistance"] = True
updated["LKASButtonControl"] = 0
response["message"] = "Remap Cancel To Distance enabled. LKAS Button has been locked to No Action."
elif key == "LKASButtonControl" and params.get_bool("RemapCancelToDistance"):
updated["LKASButtonControl"] = 0
updated["RemapCancelToDistance"] = True
response["message"] = "LKAS Button is locked to No Action while Remap Cancel To Distance is enabled."
elif locked_lkas:
updated["LKASButtonControl"] = 0
response["message"] = "Remap Cancel Button enabled."
if migrated_cancel_buttons:
if key == "RemapCancelToDistance" and params.get_bool("RemapCancelToDistance"):
response["message"] = "Remap Cancel Button enabled. Existing distance mappings were copied to the new cancel button."
for source_key, target_key in (
("DistanceButtonControl", "CancelButtonControl"),
("LongDistanceButtonControl", "LongCancelButtonControl"),
("VeryLongDistanceButtonControl", "VeryLongCancelButtonControl"),
):
updated[target_key] = _get_param_int_value(target_key, _get_param_int_value(source_key, 0))
if gsm_metered_apply_result is not None:
updated["GsmMetered"] = str_val.strip() in ("1", "true", "True")
@@ -3864,7 +3855,7 @@ def setup(app):
@app.route("/api/params/all", methods=["GET"])
def get_all_params():
_enforce_cancel_remap_lkas_lock()
migrate_cancel_button_controls(params)
allowed_keys, types = _get_param_type_info()
defaults_lookup = _get_default_param_values()
+1 -6
View File
@@ -178,7 +178,7 @@ StarPilotVehiclesPanel::StarPilotVehiclesPanel(StarPilotSettingsWindow *parent,
{"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."), ""},
{"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 To Distance"), tr("<b>On pedal-interceptor Bolts, remap the steering-wheel CANCEL button to distance/personality input.</b>"), ""},
{"RemapCancelToDistance", tr("Remap Cancel Button"), tr("<b>On pedal-interceptor Bolts, treat the steering-wheel CANCEL button as an extra mappable button.</b>"), ""},
{"VoltSNG", tr("Stop-and-Go Hack"), tr("<b>Force stop-and-go</b> on the 2017 Chevy Volt."), ""},
{"HKGToggles", tr("Hyundai/Kia/Genesis Settings"), tr("<b>StarPilot features for Genesis, Hyundai, and Kia vehicles.</b>"), ""},
@@ -306,11 +306,6 @@ StarPilotVehiclesPanel::StarPilotVehiclesPanel(StarPilotSettingsWindow *parent,
QSet<QString> rebootKeys = {"RemapCancelToDistance", "TacoTuneHacks"};
for (const QString &key : rebootKeys) {
QObject::connect(static_cast<ToggleControl*>(toggles[key]), &ToggleControl::toggleFlipped, [key, this](bool state) {
if (key == "RemapCancelToDistance" && state && params.getInt("LKASButtonControl") != 0) {
params.putInt("LKASButtonControl", 0);
updateStarPilotToggles();
}
if (started) {
if (key == "TacoTuneHacks" && state) {
if (StarPilotConfirmationDialog::toggleReboot(this)) {
+14 -33
View File
@@ -35,19 +35,6 @@ QString getWheelFunctionLabel(Params &params, const QString &key) {
return functionsMap.value(params.getInt(key.toStdString()), QObject::tr("No Action"));
}
bool lockLkasButtonIfNeeded(Params &params) {
if (!params.getBool("RemapCancelToDistance")) {
return false;
}
if (params.getInt("LKASButtonControl") != 0) {
params.putInt("LKASButtonControl", 0);
updateStarPilotToggles();
}
return true;
}
} // namespace
StarPilotWheelPanel::StarPilotWheelPanel(StarPilotSettingsWindow *parent, bool forceOpen) : StarPilotListWidget(parent), parent(parent) {
@@ -84,8 +71,11 @@ StarPilotWheelPanel::StarPilotWheelPanel(StarPilotSettingsWindow *parent, bool f
});
const std::vector<std::tuple<QString, QString, QString, QString>> wheelToggles {
{"CancelButtonControl", tr("Cancel Button"), tr("<b>Action performed when the remapped \"Cancel\" button is pressed.</b>"), "../../starpilot/assets/toggle_icons/icon_mute.png"},
{"DistanceButtonControl", tr("Distance Button"), tr("<b>Action performed when the \"Distance\" button is pressed.</b>"), "../../starpilot/assets/toggle_icons/icon_mute.png"},
{"LongCancelButtonControl", tr("Cancel Button (Long Press)"), tr("<b>Action performed when the remapped \"Cancel\" button is pressed for more than 0.5 seconds.</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"},
{"VeryLongCancelButtonControl", tr("Cancel Button (Very Long Press)"), tr("<b>Action performed when the remapped \"Cancel\" button is pressed for more than 2.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"},
{"ModeButtonControl", tr("Mode Button"), tr("<b>Action performed when the \"Mode\" button is pressed.</b>"), "../../starpilot/assets/toggle_icons/icon_mute.png"},
@@ -99,12 +89,6 @@ StarPilotWheelPanel::StarPilotWheelPanel(StarPilotSettingsWindow *parent, bool f
for (const auto &[param, title, desc, icon] : wheelToggles) {
ButtonControl *wheelToggle = new ButtonControl(title, tr("SELECT"), desc);
QObject::connect(wheelToggle, &ButtonControl::clicked, [key = param, parent, wheelToggle, this]() {
if (key == "LKASButtonControl" && lockLkasButtonIfNeeded(params)) {
wheelToggle->setValue(tr("No Action"));
wheelToggle->setEnabled(false);
return;
}
QMap<int, QString> functionsMap = getWheelFunctionsMap();
if (parent->hasOpenpilotLongitudinal) {
const QMap<int, QString> longitudinalFunctionsMap = getLongitudinalWheelFunctionsMap();
@@ -120,13 +104,7 @@ StarPilotWheelPanel::StarPilotWheelPanel(StarPilotSettingsWindow *parent, bool f
updateStarPilotToggles();
}
});
if (param == "LKASButtonControl" && lockLkasButtonIfNeeded(params)) {
wheelToggle->setValue(tr("No Action"));
wheelToggle->setEnabled(false);
} else {
wheelToggle->setValue(getWheelFunctionLabel(params, param));
}
wheelToggle->setValue(getWheelFunctionLabel(params, param));
toggles[param] = wheelToggle;
@@ -158,6 +136,15 @@ void StarPilotWheelPanel::updateToggles() {
setVisible &= !parent->lkasAllowedForAOL || !(params.getBool("AlwaysOnLateral") && params.getBool("AlwaysOnLateralLKAS"));
}
if (!showAllToggles && (
key == "CancelButtonControl" ||
key == "LongCancelButtonControl" ||
key == "VeryLongCancelButtonControl")) {
setVisible &= parent->isBolt;
setVisible &= parent->hasPedal;
setVisible &= params.getBool("RemapCancelToDistance");
}
if (!showAllToggles && key == "NostalgiaMode") {
setVisible &= parent->isHKGCanFd;
setVisible &= parent->hasOpenpilotLongitudinal;
@@ -178,13 +165,7 @@ void StarPilotWheelPanel::updateToggles() {
}
if (ButtonControl *wheelToggle = qobject_cast<ButtonControl*>(toggle)) {
if (key == "LKASButtonControl") {
const bool lkasLocked = lockLkasButtonIfNeeded(params);
wheelToggle->setEnabled(!lkasLocked);
wheelToggle->setValue(lkasLocked ? tr("No Action") : getWheelFunctionLabel(params, key));
} else {
wheelToggle->setValue(getWheelFunctionLabel(params, key));
}
wheelToggle->setValue(getWheelFunctionLabel(params, key));
}
toggle->setVisible(setVisible);
+3
View File
@@ -72,6 +72,7 @@ CustomPersonalities
CustomSignals
CustomSounds
CustomUI
CancelButtonControl
DebugMode
DecelerationProfile
DeveloperMetrics
@@ -154,6 +155,7 @@ LiveParameters
LiveTorqueParameters
LockDoors
LockDoorsTimer
LongCancelButtonControl
LongDistanceButtonControl
LongPitch
LongitudinalActuatorDelay
@@ -333,6 +335,7 @@ VEgoStarting
VEgoStartingStock
VEgoStopping
VEgoStoppingStock
VeryLongCancelButtonControl
VeryLongDistanceButtonControl
VoltSNG
WarningImmediateVolume