mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-30 19:33:45 +08:00
button
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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}},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)",
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -35,19 +35,6 @@ QString getWheelFunctionLabel(Params ¶ms, const QString &key) {
|
||||
return functionsMap.value(params.getInt(key.toStdString()), QObject::tr("No Action"));
|
||||
}
|
||||
|
||||
bool lockLkasButtonIfNeeded(Params ¶ms) {
|
||||
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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user