diff --git a/common/params_keys.h b/common/params_keys.h index 2c4df6140..9190d821b 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -42,7 +42,9 @@ inline static std::unordered_map keys = { {"ExperimentalLongitudinalEnabled", {PERSISTENT, BOOL}}, {"ExperimentalMode", {PERSISTENT, BOOL}}, {"ExperimentalModeConfirmed", {PERSISTENT, BOOL}}, + {"PersistChillState", {PERSISTENT, BOOL, "0", "0", 1}}, {"PersistExperimentalState", {PERSISTENT, BOOL, "0", "0", 1}}, + {"PersistedCCStatus", {PERSISTENT, INT, "0", "0"}}, {"PersistedCEStatus", {PERSISTENT, INT, "0", "0"}}, {"FirmwareQueryDone", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}}, {"ForcePowerDown", {PERSISTENT, BOOL}}, @@ -196,6 +198,11 @@ inline static std::unordered_map keys = { {"CESlowerLead", {PERSISTENT, BOOL, "1", "0", 1}}, {"CESpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 1}}, {"CESpeedLead", {PERSISTENT, FLOAT, "0.0", "0.0", 1}}, + {"CCMLead", {PERSISTENT, BOOL, "1", "0", 1}}, + {"CCMSetSpeedMargin", {PERSISTENT, FLOAT, "3.0", "0.0", 1}}, + {"CCMSpeed", {PERSISTENT, FLOAT, "45.0", "0.0", 1}}, + {"CCMSpeedLead", {PERSISTENT, FLOAT, "35.0", "0.0", 1}}, + {"CCStatus", {CLEAR_ON_OFFROAD_TRANSITION, INT, "0", "0"}}, {"CEStatus", {CLEAR_ON_OFFROAD_TRANSITION, INT, "0", "0"}}, {"CEStopLights", {PERSISTENT, BOOL, "1", "0", 1}}, {"CEStoppedLead", {PERSISTENT, BOOL, "0", "0", 1}}, @@ -205,6 +212,7 @@ inline static std::unordered_map keys = { {"BootLogoToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}}, {"Compass", {PERSISTENT, BOOL, "0", "0", 1}}, {"CommunityFavorites", {PERSISTENT, STRING, "", "", 1}}, + {"ConditionalChill", {PERSISTENT, BOOL, "0", "0", 1}}, {"ConditionalExperimental", {PERSISTENT, BOOL, "1", "0", 1}}, {"CurvatureData", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}}, {"CurveSpeedController", {PERSISTENT, BOOL, "1", "0", 1}}, @@ -487,6 +495,7 @@ inline static std::unordered_map keys = { {"SetSpeedLimit", {PERSISTENT, BOOL, "0", "0", 1}}, {"SetSpeedOffset", {PERSISTENT, FLOAT, "0.0", "0.0", 2}}, {"ShowCEMStatus", {PERSISTENT, BOOL, "1", "0", 2}}, + {"ShowCCMStatus", {PERSISTENT, BOOL, "0", "0", 2}}, {"ShowCPU", {PERSISTENT, BOOL, "1", "0", 3}}, {"ShowCSCStatus", {PERSISTENT, BOOL, "1", "0", 2}}, {"ShowGPU", {PERSISTENT, BOOL, "0", "0", 3}}, diff --git a/selfdrive/controls/tests/test_conditional_chill_mode.py b/selfdrive/controls/tests/test_conditional_chill_mode.py new file mode 100644 index 000000000..83c05f6ce --- /dev/null +++ b/selfdrive/controls/tests/test_conditional_chill_mode.py @@ -0,0 +1,226 @@ +from types import SimpleNamespace + +from openpilot.common.constants import CV +from openpilot.starpilot.common.experimental_state import CCStatus +from openpilot.starpilot.controls.lib.conditional_chill_mode import ConditionalChillMode + + +class FakeParams: + def __init__(self, bools=None, ints=None): + self.bools = dict(bools or {}) + self.ints = dict(ints or {}) + + def get_bool(self, key): + return bool(self.bools.get(key, False)) + + def put_bool(self, key, value): + self.bools[key] = bool(value) + + def get_int(self, key, default=0): + return int(self.ints.get(key, default)) + + def put_int(self, key, value): + self.ints[key] = int(value) + + +class FakeDetector: + def __init__(self): + self.curve_detected = False + self.slow_lead_detected = False + self.stop_light_detected = False + self.stop_light_model_detected = False + + def curve_detection(self, *_args, **_kwargs): + return None + + def slow_lead(self, *_args, **_kwargs): + return None + + def stop_sign_and_light(self, *_args, **_kwargs): + return None + + +def make_sm(): + return { + "carState": SimpleNamespace(standstill=False, leftBlinker=False, rightBlinker=False), + "starpilotCarState": SimpleNamespace(trafficModeEnabled=False), + "starpilotRadarState": SimpleNamespace( + leadLeft=SimpleNamespace(status=False, dRel=float("inf"), vLead=0.0), + leadRight=SimpleNamespace(status=False, dRel=float("inf"), vLead=0.0), + ), + } + + +def make_toggles(): + return SimpleNamespace( + conditional_chill_speed=45 * CV.MPH_TO_MS, + conditional_chill_speed_lead=35 * CV.MPH_TO_MS, + conditional_chill_speed_margin=3 * CV.MPH_TO_MS, + conditional_chill_lead=True, + ) + + +def make_ccm(): + planner = SimpleNamespace( + params=FakeParams(), + params_memory=FakeParams(), + starpilot_vcruise=SimpleNamespace( + stop_sign_confirmed=False, + forcing_stop=False, + slc=SimpleNamespace(experimental_mode=False), + ), + raw_model_stopped=False, + model_stopped=False, + tracking_lead=False, + lead_one=SimpleNamespace( + status=False, + dRel=float("inf"), + vLead=0.0, + aLeadK=0.0, + modelProb=0.0, + radar=False, + ), + ) + detector = FakeDetector() + return planner, detector, ConditionalChillMode(planner, detector) + + +def test_ccm_stays_experimental_when_no_chill_condition_matches(monkeypatch): + planner, _detector, ccm = make_ccm() + sm = make_sm() + toggles = make_toggles() + + monkeypatch.setattr("openpilot.starpilot.controls.lib.conditional_chill_mode.time.monotonic", lambda: 1.0) + ccm.update(20 * CV.MPH_TO_MS, 21 * CV.MPH_TO_MS, sm, toggles) + + assert ccm.experimental_mode + assert ccm.status_value == CCStatus["OFF"] + + +def test_ccm_enters_chill_for_open_road_speed_recovery(monkeypatch): + planner, _detector, ccm = make_ccm() + sm = make_sm() + toggles = make_toggles() + monotonic_values = iter([10.0, 10.5]) + monkeypatch.setattr("openpilot.starpilot.controls.lib.conditional_chill_mode.time.monotonic", lambda: next(monotonic_values)) + + v_ego = 55 * CV.MPH_TO_MS + v_cruise = v_ego + 5 * CV.MPH_TO_MS + ccm.update(v_ego, v_cruise, sm, toggles) + ccm.update(v_ego, v_cruise, sm, toggles) + + assert not ccm.experimental_mode + assert ccm.status_value == CCStatus["SPEED"] + assert planner.params_memory.get_int("CCStatus") == CCStatus["SPEED"] + + +def test_ccm_enters_chill_for_stable_lead_cruising(monkeypatch): + planner, _detector, ccm = make_ccm() + sm = make_sm() + toggles = make_toggles() + monotonic_values = iter([10.0, 10.5]) + monkeypatch.setattr("openpilot.starpilot.controls.lib.conditional_chill_mode.time.monotonic", lambda: next(monotonic_values)) + + planner.tracking_lead = True + planner.lead_one.status = True + planner.lead_one.dRel = 45.0 + planner.lead_one.vLead = 24.8 + planner.lead_one.radar = True + + v_ego = 58 * CV.MPH_TO_MS + ccm.update(v_ego, v_ego, sm, toggles) + ccm.update(v_ego, v_ego, sm, toggles) + + assert not ccm.experimental_mode + assert ccm.status_value == CCStatus["LEAD"] + + +def test_ccm_hard_vetoes_force_experimental(monkeypatch): + planner, detector, ccm = make_ccm() + sm = make_sm() + toggles = make_toggles() + v_ego = 60 * CV.MPH_TO_MS + v_cruise = v_ego + 5 * CV.MPH_TO_MS + + veto_scenes = [] + + detector.slow_lead_detected = True + veto_scenes.append(("slow_lead", make_sm())) + detector.slow_lead_detected = False + + traffic_sm = make_sm() + traffic_sm["starpilotCarState"].trafficModeEnabled = True + veto_scenes.append(("traffic_mode", traffic_sm)) + + adjacent_sm = make_sm() + adjacent_sm["starpilotRadarState"].leadLeft = SimpleNamespace(status=True, dRel=25.0, vLead=12.0) + veto_scenes.append(("adjacent_lead", adjacent_sm)) + + for index, (_name, scene_sm) in enumerate(veto_scenes, start=1): + monkeypatch.setattr("openpilot.starpilot.controls.lib.conditional_chill_mode.time.monotonic", lambda idx=index: float(idx)) + if _name == "slow_lead": + detector.slow_lead_detected = True + else: + detector.slow_lead_detected = False + ccm.update(v_ego, v_cruise, scene_sm, toggles) + assert ccm.experimental_mode + assert ccm.status_value == CCStatus["OFF"] + + planner.starpilot_vcruise.slc.experimental_mode = True + monkeypatch.setattr("openpilot.starpilot.controls.lib.conditional_chill_mode.time.monotonic", lambda: 10.0) + ccm.update(v_ego, v_cruise, sm, toggles) + assert ccm.experimental_mode + assert ccm.status_value == CCStatus["OFF"] + + +def test_ccm_immediately_exits_chill_when_scene_turns_into_slow_lead(monkeypatch): + planner, detector, ccm = make_ccm() + sm = make_sm() + toggles = make_toggles() + monotonic_values = iter([10.0, 10.5, 10.6]) + monkeypatch.setattr("openpilot.starpilot.controls.lib.conditional_chill_mode.time.monotonic", lambda: next(monotonic_values)) + + planner.tracking_lead = True + planner.lead_one.status = True + planner.lead_one.dRel = 40.0 + planner.lead_one.vLead = 24.9 + planner.lead_one.radar = True + + v_ego = 58 * CV.MPH_TO_MS + ccm.update(v_ego, v_ego, sm, toggles) + ccm.update(v_ego, v_ego, sm, toggles) + assert not ccm.experimental_mode + + detector.slow_lead_detected = True + ccm.update(v_ego, v_ego, sm, toggles) + + assert ccm.experimental_mode + assert ccm.status_value == CCStatus["OFF"] + + +def test_ccm_respects_manual_chill_override(monkeypatch): + planner, _detector, ccm = make_ccm() + sm = make_sm() + toggles = make_toggles() + planner.params_memory.put_int("CCStatus", CCStatus["USER_CHILL"]) + + monkeypatch.setattr("openpilot.starpilot.controls.lib.conditional_chill_mode.time.monotonic", lambda: 1.0) + ccm.update(55 * CV.MPH_TO_MS, 60 * CV.MPH_TO_MS, sm, toggles) + + assert not ccm.experimental_mode + assert ccm.status_value == CCStatus["USER_CHILL"] + + +def test_ccm_restores_persisted_manual_experimental_override(monkeypatch): + planner, _detector, ccm = make_ccm() + sm = make_sm() + toggles = make_toggles() + planner.params.put_bool("PersistChillState", True) + planner.params.put_int("PersistedCCStatus", CCStatus["USER_EXPERIMENTAL"]) + + monkeypatch.setattr("openpilot.starpilot.controls.lib.conditional_chill_mode.time.monotonic", lambda: 1.0) + ccm.update(55 * CV.MPH_TO_MS, 60 * CV.MPH_TO_MS, sm, toggles) + + assert ccm.experimental_mode + assert ccm.status_value == CCStatus["USER_EXPERIMENTAL"] + assert planner.params_memory.get_int("CCStatus") == CCStatus["USER_EXPERIMENTAL"] diff --git a/selfdrive/selfdrived/selfdrived.py b/selfdrive/selfdrived/selfdrived.py index 59deca910..502a26bd2 100644 --- a/selfdrive/selfdrived/selfdrived.py +++ b/selfdrive/selfdrived/selfdrived.py @@ -593,7 +593,7 @@ class SelfdriveD: self.starpilot_events.add_from_msg(self.sm['starpilotPlan'].starpilotEvents) - if self.starpilot_toggles.conditional_experimental_mode: + if self.starpilot_toggles.conditional_experimental_mode or getattr(self.starpilot_toggles, "conditional_chill_mode", False): self.experimental_mode = self.sm['starpilotPlan'].experimentalMode else: self.experimental_mode |= self.sm['starpilotPlan'].experimentalMode diff --git a/selfdrive/ui/qt/offroad/experimental_mode.cc b/selfdrive/ui/qt/offroad/experimental_mode.cc index 759cc611d..fcfc892e6 100644 --- a/selfdrive/ui/qt/offroad/experimental_mode.cc +++ b/selfdrive/ui/qt/offroad/experimental_mode.cc @@ -12,8 +12,13 @@ ExperimentalModeButton::ExperimentalModeButton(QWidget *parent) : QPushButton(pa chill_pixmap = QPixmap("../assets/icons/couch.svg").scaledToWidth(img_width, Qt::SmoothTransformation); experimental_pixmap = QPixmap("../assets/icons/experimental_grey.svg").scaledToWidth(img_width, Qt::SmoothTransformation); - // go to toggles and expand experimental mode description - connect(this, &QPushButton::clicked, [=]() { emit openSettings(2, "ExperimentalMode"); }); + // go to toggles and expand whichever mode control is actually active + connect(this, &QPushButton::clicked, [=]() { + const QString toggle = params.getBool("ConditionalExperimental") ? "ConditionalExperimental" : + params.getBool("ConditionalChill") ? "ConditionalChill" : + "ExperimentalMode"; + emit openSettings(2, toggle); + }); setFixedHeight(125); QHBoxLayout *main_layout = new QHBoxLayout; @@ -76,6 +81,12 @@ void ExperimentalModeButton::showEvent(QShowEvent *event) { status = params.getInt("PersistedCEStatus"); } experimental_mode = !params.getBool("SafeMode") && status == 2; + } else if (params.getBool("ConditionalChill")) { + int status = params_memory.getInt("CCStatus"); + if ((status != 1 && status != 2) && params.getBool("PersistChillState")) { + status = params.getInt("PersistedCCStatus"); + } + experimental_mode = !params.getBool("SafeMode") && (status == 0 || status == 1); } else { experimental_mode = params.getBool("ExperimentalMode") && !params.getBool("SafeMode"); } diff --git a/selfdrive/ui/qt/onroad/buttons.cc b/selfdrive/ui/qt/onroad/buttons.cc index efae2f690..da58f4d6c 100644 --- a/selfdrive/ui/qt/onroad/buttons.cc +++ b/selfdrive/ui/qt/onroad/buttons.cc @@ -4,6 +4,16 @@ #include "selfdrive/ui/qt/util.h" +namespace { +bool ccmManualOverride(int status) { + return status == 1 || status == 2; +} + +bool cemManualOverride(int status) { + return status == 1 || status == 2; +} +} // namespace + void drawIcon(QPainter &p, const QPoint ¢er, const QPixmap &img, const QBrush &bg, float opacity, const int &angle) { p.setRenderHint(QPainter::Antialiasing); p.setOpacity(1.0); // bg dictates opacity of ellipse @@ -38,9 +48,13 @@ void ExperimentalButton::changeMode() { bool can_change = hasLongitudinalControl(cp) && params.getBool("ExperimentalModeConfirmed"); if (can_change) { if (starpilot_toggles.value("conditional_experimental_mode").toBool()) { - int override_value = (starpilot_scene.conditional_status == 1 || starpilot_scene.conditional_status == 2) ? 0 : experimental_mode ? 1 : 2; + int override_value = cemManualOverride(starpilot_scene.conditional_status) ? 0 : experimental_mode ? 1 : 2; params_memory.putInt("CEStatus", override_value); params.putInt("PersistedCEStatus", params.getBool("PersistExperimentalState") ? override_value : 0); + } else if (starpilot_toggles.value("conditional_chill_mode").toBool()) { + int override_value = ccmManualOverride(starpilot_scene.conditional_status) ? 0 : experimental_mode ? 2 : 1; + params_memory.putInt("CCStatus", override_value); + params.putInt("PersistedCCStatus", params.getBool("PersistChillState") ? override_value : 0); } else { params.putBool("ExperimentalMode", !experimental_mode); } @@ -97,6 +111,11 @@ void ExperimentalButton::showEvent(QShowEvent *event) { } void ExperimentalButton::updateBackgroundColor() { + const bool conditional_experimental_mode = starpilot_toggles.value("conditional_experimental_mode").toBool(); + const bool conditional_chill_mode = starpilot_toggles.value("conditional_chill_mode").toBool(); + const bool highlight_override = + (conditional_experimental_mode && starpilot_scene.conditional_status == 1) || + (conditional_chill_mode && ccmManualOverride(starpilot_scene.conditional_status)); if (starpilot_toggles.value("simple_mode").toBool()) { background_color = QColor(0, 0, 0, 166); } else if (isDown() || !engageable) { @@ -105,7 +124,7 @@ void ExperimentalButton::updateBackgroundColor() { background_color = bg_colors[STATUS_SWITCHBACK_MODE_ENABLED]; } else if (starpilot_scene.always_on_lateral_active) { background_color = bg_colors[STATUS_ALWAYS_ON_LATERAL_ACTIVE]; - } else if (starpilot_scene.conditional_status == 1) { + } else if (highlight_override) { background_color = bg_colors[STATUS_CEM_DISABLED]; } else if (experimental_mode) { background_color = bg_colors[STATUS_EXPERIMENTAL_MODE_ENABLED]; diff --git a/selfdrive/ui/qt/onroad/onroad_home.cc b/selfdrive/ui/qt/onroad/onroad_home.cc index 44be2dfb1..d6493835a 100644 --- a/selfdrive/ui/qt/onroad/onroad_home.cc +++ b/selfdrive/ui/qt/onroad/onroad_home.cc @@ -59,13 +59,19 @@ void OnroadWindow::updateState(const UIState &s, const StarPilotUIState &fs) { nvg->updateState(s, fs); const StarPilotUIScene &starpilot_scene = fs.starpilot_scene; + const QJsonObject &starpilot_toggles = starpilot_scene.starpilot_toggles; const auto selfdriveState = (*s.sm)["selfdriveState"].getSelfdriveState(); QColor bgColor = bg_colors[s.status]; + const bool conditional_experimental_mode = starpilot_toggles.value("conditional_experimental_mode").toBool(); + const bool conditional_chill_mode = starpilot_toggles.value("conditional_chill_mode").toBool(); + const bool highlight_override = + (conditional_experimental_mode && starpilot_scene.conditional_status == 1) || + (conditional_chill_mode && (starpilot_scene.conditional_status == 1 || starpilot_scene.conditional_status == 2)); if (starpilot_scene.switchback_mode_enabled && (selfdriveState.getEnabled() || starpilot_scene.always_on_lateral_active)) { bgColor = bg_colors[STATUS_SWITCHBACK_MODE_ENABLED]; } else if (starpilot_scene.always_on_lateral_active) { bgColor = bg_colors[STATUS_ALWAYS_ON_LATERAL_ACTIVE]; - } else if (starpilot_scene.conditional_status == 1) { + } else if (highlight_override) { bgColor = bg_colors[STATUS_CEM_DISABLED]; } else if (selfdriveState.getExperimentalMode()) { bgColor = bg_colors[STATUS_EXPERIMENTAL_MODE_ENABLED]; @@ -78,9 +84,6 @@ void OnroadWindow::updateState(const UIState &s, const StarPilotUIState &fs) { bg = bgColor; update(); } - - const QJsonObject &starpilot_toggles = starpilot_scene.starpilot_toggles; - starpilot_nvg->alertHeight = alerts->alertHeight; starpilot_onroad->bg = bg; diff --git a/selfdrive/ui/qt/widgets/controls.cc b/selfdrive/ui/qt/widgets/controls.cc index ceb78e012..b74272d0c 100644 --- a/selfdrive/ui/qt/widgets/controls.cc +++ b/selfdrive/ui/qt/widgets/controls.cc @@ -139,6 +139,11 @@ void ParamControl::toggleClicked(bool state) { if (!confirm || confirmed || !state || do_confirm()) { if (store_confirm && state) params.putBool(key + "Confirmed", true); params.putBool(key, state); + if (state && key == "ConditionalExperimental") { + params.putBool("ConditionalChill", false); + } else if (state && key == "ConditionalChill") { + params.putBool("ConditionalExperimental", false); + } if (key == "PersistExperimentalState") { static Params params_memory{"", true}; int persisted_status = 0; @@ -147,6 +152,14 @@ void ParamControl::toggleClicked(bool state) { persisted_status = (current_status == 1 || current_status == 2) ? current_status : 0; } params.putInt("PersistedCEStatus", persisted_status); + } else if (key == "PersistChillState") { + static Params params_memory{"", true}; + int persisted_status = 0; + if (state) { + int current_status = params_memory.getInt("CCStatus"); + persisted_status = (current_status == 1 || current_status == 2) ? current_status : 0; + } + params.putInt("PersistedCCStatus", persisted_status); } setIcon(state); } else { diff --git a/starpilot/common/experimental_state.py b/starpilot/common/experimental_state.py index 590fca26f..df7b356ac 100644 --- a/starpilot/common/experimental_state.py +++ b/starpilot/common/experimental_state.py @@ -6,6 +6,9 @@ from openpilot.common.params import Params PERSIST_EXPERIMENTAL_STATE_PARAM = "PersistExperimentalState" PERSISTED_CE_STATUS_PARAM = "PersistedCEStatus" CE_STATUS_PARAM = "CEStatus" +PERSIST_CHILL_STATE_PARAM = "PersistChillState" +PERSISTED_CC_STATUS_PARAM = "PersistedCCStatus" +CC_STATUS_PARAM = "CCStatus" CEStatus = { "OFF": 0, @@ -19,35 +22,71 @@ CEStatus = { "STOP_LIGHT": 8, } +CCStatus = { + "OFF": 0, + "USER_EXPERIMENTAL": 1, + "USER_CHILL": 2, + "LEAD": 4, + "SPEED": 6, +} + MANUAL_CE_STATUSES = { CEStatus["USER_DISABLED"], CEStatus["USER_OVERRIDDEN"], } +MANUAL_CC_STATUSES = { + CCStatus["USER_EXPERIMENTAL"], + CCStatus["USER_CHILL"], +} + def is_manual_ce_status(status: int) -> bool: return int(status) in MANUAL_CE_STATUSES +def is_manual_cc_status(status: int) -> bool: + return int(status) in MANUAL_CC_STATUSES + + def normalize_persisted_ce_status(status: int) -> int: status = int(status) return status if status in MANUAL_CE_STATUSES else CEStatus["OFF"] +def normalize_persisted_cc_status(status: int) -> int: + status = int(status) + return status if status in MANUAL_CC_STATUSES else CCStatus["OFF"] + + def get_persisted_ce_status(params: Params) -> int: return normalize_persisted_ce_status(params.get_int(PERSISTED_CE_STATUS_PARAM, default=CEStatus["OFF"])) +def get_persisted_cc_status(params: Params) -> int: + return normalize_persisted_cc_status(params.get_int(PERSISTED_CC_STATUS_PARAM, default=CCStatus["OFF"])) + + def set_persisted_ce_status(params: Params, status: int) -> int: normalized = normalize_persisted_ce_status(status) params.put_int(PERSISTED_CE_STATUS_PARAM, normalized) return normalized +def set_persisted_cc_status(params: Params, status: int) -> int: + normalized = normalize_persisted_cc_status(status) + params.put_int(PERSISTED_CC_STATUS_PARAM, normalized) + return normalized + + def clear_persisted_ce_status(params: Params) -> None: params.put_int(PERSISTED_CE_STATUS_PARAM, CEStatus["OFF"]) +def clear_persisted_cc_status(params: Params) -> None: + params.put_int(PERSISTED_CC_STATUS_PARAM, CCStatus["OFF"]) + + def sync_persist_experimental_state(params: Params, params_memory: Params | None, enabled: bool) -> None: params.put_bool(PERSIST_EXPERIMENTAL_STATE_PARAM, enabled) if enabled: @@ -57,21 +96,45 @@ def sync_persist_experimental_state(params: Params, params_memory: Params | None clear_persisted_ce_status(params) +def sync_persist_chill_state(params: Params, params_memory: Params | None, enabled: bool) -> None: + params.put_bool(PERSIST_CHILL_STATE_PARAM, enabled) + if enabled: + current_status = params_memory.get_int(CC_STATUS_PARAM, default=CCStatus["OFF"]) if params_memory is not None else CCStatus["OFF"] + set_persisted_cc_status(params, current_status) + else: + clear_persisted_cc_status(params) + + def sync_manual_ce_state(params: Params, status: int) -> int: return set_persisted_ce_status(params, status) if params.get_bool(PERSIST_EXPERIMENTAL_STATE_PARAM) else clear_and_return_off(params) +def sync_manual_cc_state(params: Params, status: int) -> int: + return set_persisted_cc_status(params, status) if params.get_bool(PERSIST_CHILL_STATE_PARAM) else clear_cc_and_return_off(params) + + def clear_and_return_off(params: Params) -> int: clear_persisted_ce_status(params) return CEStatus["OFF"] +def clear_cc_and_return_off(params: Params) -> int: + clear_persisted_cc_status(params) + return CCStatus["OFF"] + + def next_manual_ce_status(current_status: int, experimental_mode: bool) -> int: if is_manual_ce_status(current_status): return CEStatus["OFF"] return CEStatus["USER_DISABLED"] if experimental_mode else CEStatus["USER_OVERRIDDEN"] +def next_manual_cc_status(current_status: int, experimental_mode: bool) -> int: + if is_manual_cc_status(current_status): + return CCStatus["OFF"] + return CCStatus["USER_CHILL"] if experimental_mode else CCStatus["USER_EXPERIMENTAL"] + + def requested_experimental_mode(params: Params, params_memory: Params | None = None) -> bool: if params.get_bool("SafeMode"): return False @@ -82,6 +145,14 @@ def requested_experimental_mode(params: Params, params_memory: Params | None = N status = get_persisted_ce_status(params) return status == CEStatus["USER_OVERRIDDEN"] + if params.get_bool("ConditionalChill"): + status = params_memory.get_int(CC_STATUS_PARAM, default=CCStatus["OFF"]) if params_memory is not None else CCStatus["OFF"] + if not is_manual_cc_status(status): + status = get_persisted_cc_status(params) + if not is_manual_cc_status(status): + return True + return status == CCStatus["USER_EXPERIMENTAL"] + return params.get_bool("ExperimentalMode") @@ -100,3 +171,20 @@ def restore_persisted_ce_state(params: Params, params_memory: Params) -> int: return restored_status return current_status + + +def restore_persisted_cc_state(params: Params, params_memory: Params) -> int: + current_status = params_memory.get_int(CC_STATUS_PARAM, default=CCStatus["OFF"]) + if is_manual_cc_status(current_status): + sync_manual_cc_state(params, current_status) + return current_status + + if not params.get_bool(PERSIST_CHILL_STATE_PARAM): + return current_status + + restored_status = get_persisted_cc_status(params) + if restored_status != CCStatus["OFF"]: + params_memory.put_int(CC_STATUS_PARAM, restored_status) + return restored_status + + return current_status diff --git a/starpilot/common/safe_mode.py b/starpilot/common/safe_mode.py index 63f02dd4c..6f4eafdc3 100644 --- a/starpilot/common/safe_mode.py +++ b/starpilot/common/safe_mode.py @@ -104,6 +104,7 @@ SAFE_MODE_MANAGED_KEYS = ( "ReduceLateralAccelerationRainStorm", "ReduceLateralAccelerationSnow", "ConditionalExperimental", + "ConditionalChill", "CECurves", "CECurvesLead", "CELead", @@ -111,10 +112,16 @@ SAFE_MODE_MANAGED_KEYS = ( "CEStoppedLead", "CESpeed", "CESpeedLead", + "CCMLead", + "CCMSetSpeedMargin", + "CCMSpeed", + "CCMSpeedLead", "CEModelStopTime", "CEStopLights", "CESignalSpeed", "CESignalLaneDetection", + "PersistChillState", + "ShowCCMStatus", "CurveSpeedController", "SpeedLimitController", "SetSpeedLimit", @@ -199,6 +206,7 @@ SAFE_MODE_STOCK_PARAM_MAP = { } SAFE_MODE_MEMORY_VALUES = { + "CCStatus": 0, "CEStatus": 0, } diff --git a/starpilot/common/starpilot_variables.py b/starpilot/common/starpilot_variables.py index 7788708bf..6d2f36e18 100644 --- a/starpilot/common/starpilot_variables.py +++ b/starpilot/common/starpilot_variables.py @@ -229,6 +229,7 @@ EXCLUDED_KEYS = { "openpilotMinutes", "OverpassRequests", "PandaSignatures", + "PersistedCCStatus", "PersistedCEStatus", "SpeedLimits", "SpeedLimitsFiltered", @@ -707,6 +708,7 @@ class StarPilotVariables: toggle.cluster_offset = self.get_value("ClusterOffset", cast=float, condition=toggle.car_make == "toyota") toggle.conditional_experimental_mode = toggle.openpilot_longitudinal and self.get_value("ConditionalExperimental") + toggle.conditional_chill_mode = toggle.openpilot_longitudinal and not toggle.conditional_experimental_mode and self.get_value("ConditionalChill") toggle.conditional_curves = self.get_value("CECurves", condition=toggle.conditional_experimental_mode) toggle.conditional_curves_lead = self.get_value("CECurvesLead", condition=toggle.conditional_curves) toggle.conditional_lead = self.get_value("CELead", condition=toggle.conditional_experimental_mode) @@ -717,7 +719,15 @@ class StarPilotVariables: toggle.conditional_model_stop_time = self.get_value("CEModelStopTime", cast=float, condition=toggle.conditional_experimental_mode and self.get_value("CEStopLights")) toggle.conditional_signal = self.get_value("CESignalSpeed", cast=float, condition=toggle.conditional_experimental_mode, conversion=speed_conversion) toggle.conditional_signal_lane_detection = self.get_value("CESignalLaneDetection", condition=toggle.conditional_signal != 0) - toggle.cem_status = self.get_value("ShowCEMStatus", condition=toggle.conditional_experimental_mode) or toggle.debug_mode + toggle.conditional_chill_speed = self.get_value("CCMSpeed", cast=float, condition=toggle.conditional_chill_mode, conversion=speed_conversion) + toggle.conditional_chill_speed_lead = self.get_value("CCMSpeedLead", cast=float, condition=toggle.conditional_chill_mode, conversion=speed_conversion) + toggle.conditional_chill_speed_margin = self.get_value("CCMSetSpeedMargin", cast=float, condition=toggle.conditional_chill_mode, conversion=speed_conversion) + toggle.conditional_chill_lead = self.get_value("CCMLead", condition=toggle.conditional_chill_mode) + toggle.cem_status = ( + self.get_value("ShowCEMStatus", condition=toggle.conditional_experimental_mode) or + self.get_value("ShowCCMStatus", condition=toggle.conditional_chill_mode) or + toggle.debug_mode + ) toggle.curve_speed_controller = toggle.openpilot_longitudinal and self.get_value("CurveSpeedController") toggle.csc_status = self.get_value("ShowCSCStatus", condition=toggle.curve_speed_controller) or toggle.debug_mode diff --git a/starpilot/common/tests/test_experimental_state.py b/starpilot/common/tests/test_experimental_state.py new file mode 100644 index 000000000..b1ae1939d --- /dev/null +++ b/starpilot/common/tests/test_experimental_state.py @@ -0,0 +1,79 @@ +from openpilot.starpilot.common.experimental_state import ( + CC_STATUS_PARAM, + CCStatus, + CE_STATUS_PARAM, + CEStatus, + requested_experimental_mode, + restore_persisted_cc_state, +) + + +class FakeParams: + def __init__(self, bools=None, ints=None): + self.bools = dict(bools or {}) + self.ints = dict(ints or {}) + + def get_bool(self, key): + return bool(self.bools.get(key, False)) + + def put_bool(self, key, value): + self.bools[key] = bool(value) + + def get_int(self, key, default=0): + return int(self.ints.get(key, default)) + + def put_int(self, key, value): + self.ints[key] = int(value) + + +def test_requested_experimental_mode_defaults_to_experimental_in_ccm_auto(): + params = FakeParams(bools={"ConditionalChill": True}) + params_memory = FakeParams(ints={CC_STATUS_PARAM: CCStatus["OFF"]}) + + assert requested_experimental_mode(params, params_memory) is True + + +def test_requested_experimental_mode_respects_ccm_manual_override(): + params = FakeParams(bools={"ConditionalChill": True}) + + params_memory = FakeParams(ints={CC_STATUS_PARAM: CCStatus["USER_CHILL"]}) + assert requested_experimental_mode(params, params_memory) is False + + params_memory = FakeParams(ints={CC_STATUS_PARAM: CCStatus["USER_EXPERIMENTAL"]}) + assert requested_experimental_mode(params, params_memory) is True + + +def test_requested_experimental_mode_prefers_cem_if_both_conditional_modes_are_enabled(): + params = FakeParams(bools={"ConditionalExperimental": True, "ConditionalChill": True}) + + params_memory = FakeParams(ints={ + CE_STATUS_PARAM: CEStatus["OFF"], + CC_STATUS_PARAM: CCStatus["USER_EXPERIMENTAL"], + }) + assert requested_experimental_mode(params, params_memory) is False + + params_memory = FakeParams(ints={ + CE_STATUS_PARAM: CEStatus["USER_OVERRIDDEN"], + CC_STATUS_PARAM: CCStatus["USER_CHILL"], + }) + assert requested_experimental_mode(params, params_memory) is True + + +def test_requested_experimental_mode_safe_mode_overrides_ccm(): + params = FakeParams(bools={"SafeMode": True, "ConditionalChill": True}) + params_memory = FakeParams(ints={CC_STATUS_PARAM: CCStatus["USER_EXPERIMENTAL"]}) + + assert requested_experimental_mode(params, params_memory) is False + + +def test_restore_persisted_cc_state_rehydrates_manual_override(): + params = FakeParams( + bools={"PersistChillState": True}, + ints={"PersistedCCStatus": CCStatus["USER_CHILL"]}, + ) + params_memory = FakeParams(ints={CC_STATUS_PARAM: CCStatus["OFF"]}) + + restored_status = restore_persisted_cc_state(params, params_memory) + + assert restored_status == CCStatus["USER_CHILL"] + assert params_memory.get_int(CC_STATUS_PARAM) == CCStatus["USER_CHILL"] diff --git a/starpilot/controls/lib/conditional_chill_mode.py b/starpilot/controls/lib/conditional_chill_mode.py new file mode 100644 index 000000000..4e3386e0a --- /dev/null +++ b/starpilot/controls/lib/conditional_chill_mode.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python3 +import time + +from openpilot.common.constants import CV + +from openpilot.starpilot.common.experimental_state import ( + CCStatus, + is_manual_cc_status, + restore_persisted_cc_state, +) + + +class ConditionalChillMode: + CCM_STOP_MODEL_TIME = 7.0 + CHILL_ENTRY_CONFIRM_TIME = 0.35 + CHILL_EXIT_BUFFER_TIME = 0.35 + CHILL_MIN_DWELL_TIME = 1.2 + + STABLE_LEAD_MIN_MODEL_PROB = 0.9 + STABLE_LEAD_MAX_BRAKE = 0.2 + STABLE_LEAD_MIN_SPEED = 1.5 + STABLE_LEAD_MAX_DISTANCE = 90.0 + STABLE_LEAD_MAX_DISTANCE_TIME = 4.5 + STABLE_LEAD_MAX_CLOSING_SPEED = 0.75 + STABLE_LEAD_MAX_CLOSING_RATIO = 0.03 + + ADJACENT_LEAD_VETO_MIN_SPEED = 1.0 + ADJACENT_LEAD_VETO_MAX_DISTANCE = 65.0 + ADJACENT_LEAD_VETO_MAX_DISTANCE_TIME = 3.5 + + LOW_SPEED_STOP_SCENE_MAX_SPEED = 18 * CV.MPH_TO_MS + + def __init__(self, StarPilotPlanner, detector): + self.starpilot_planner = StarPilotPlanner + self.detector = detector + self.params = self.starpilot_planner.params + self.params_memory = self.starpilot_planner.params_memory + + self.experimental_mode = True + self.status_value = CCStatus["OFF"] + self._active_auto_status = CCStatus["OFF"] + self._candidate_since = 0.0 + self._soft_exit_since = 0.0 + self._chill_hold_until = 0.0 + self._prev_cc_status = None + + def update(self, v_ego, v_cruise, sm, starpilot_toggles): + now = time.monotonic() + safe_mode = self.params.get_bool("SafeMode") + + self.status_value = CCStatus["OFF"] if safe_mode else restore_persisted_cc_state(self.params, self.params_memory) + + if is_manual_cc_status(self.status_value): + self._reset_timers() + self.experimental_mode = self.status_value == CCStatus["USER_EXPERIMENTAL"] + self._write_status(self.status_value) + return + + self._refresh_detector(v_ego, sm) + + if safe_mode or self._has_hard_veto(v_ego, sm): + self._reset_timers() + self.experimental_mode = False if safe_mode else True + self.status_value = CCStatus["OFF"] + self._write_status(CCStatus["OFF"]) + return + + auto_status = self._get_chill_status(v_ego, v_cruise, sm, starpilot_toggles) + chill_candidate = auto_status != CCStatus["OFF"] + + if chill_candidate: + if self._candidate_since == 0.0: + self._candidate_since = now + self._soft_exit_since = 0.0 + + if not self.experimental_mode or (now - self._candidate_since) >= self.CHILL_ENTRY_CONFIRM_TIME: + self.experimental_mode = False + self._active_auto_status = auto_status + self._chill_hold_until = max(self._chill_hold_until, now + self.CHILL_MIN_DWELL_TIME) + self.status_value = self._active_auto_status if not self.experimental_mode else CCStatus["OFF"] + else: + self._candidate_since = 0.0 + if not self.experimental_mode: + if self._soft_exit_since == 0.0: + self._soft_exit_since = now + + hold_active = now < self._chill_hold_until + exit_buffer_active = (now - self._soft_exit_since) < self.CHILL_EXIT_BUFFER_TIME + if hold_active or exit_buffer_active: + self.status_value = self._active_auto_status + else: + self.experimental_mode = True + self._active_auto_status = CCStatus["OFF"] + self.status_value = CCStatus["OFF"] + else: + self._soft_exit_since = 0.0 + self.status_value = CCStatus["OFF"] + + self._write_status(self.status_value if not self.experimental_mode else CCStatus["OFF"]) + + def _reset_timers(self): + self._active_auto_status = CCStatus["OFF"] + self._candidate_since = 0.0 + self._soft_exit_since = 0.0 + self._chill_hold_until = 0.0 + + def _refresh_detector(self, v_ego, sm): + detector_toggles = type("DetectorToggles", (), { + "conditional_curves": True, + "conditional_curves_lead": True, + "conditional_lead": True, + "conditional_slower_lead": True, + "conditional_stopped_lead": True, + })() + self.detector.curve_detection(v_ego, detector_toggles) + self.detector.slow_lead(detector_toggles, v_ego) + self.detector.stop_sign_and_light(v_ego, sm, self.CCM_STOP_MODEL_TIME) + + def _has_hard_veto(self, v_ego, sm): + if sm["carState"].standstill: + return True + + if sm["carState"].leftBlinker or sm["carState"].rightBlinker: + return True + + if sm["starpilotCarState"].trafficModeEnabled: + return True + + if self.starpilot_planner.starpilot_vcruise.slc.experimental_mode: + return True + + if self.detector.curve_detected or self.detector.slow_lead_detected or self.detector.stop_light_detected: + return True + + if self.starpilot_planner.starpilot_vcruise.stop_sign_confirmed or self.starpilot_planner.starpilot_vcruise.forcing_stop: + return True + + if self._adjacent_lead_ambiguous(sm, v_ego): + return True + + return self._low_speed_stop_scene(v_ego) + + def _low_speed_stop_scene(self, v_ego): + if v_ego >= self.LOW_SPEED_STOP_SCENE_MAX_SPEED: + return False + + if self.starpilot_planner.raw_model_stopped or self.starpilot_planner.model_stopped or self.detector.stop_light_model_detected: + return True + + lead = self.starpilot_planner.lead_one + if not getattr(lead, "status", False): + return False + + lead_distance = float(getattr(lead, "dRel", float("inf"))) + lead_speed = float(getattr(lead, "vLead", float("inf"))) + lead_distance_limit = max(40.0, v_ego * self.STABLE_LEAD_MAX_DISTANCE_TIME) + return lead_distance < lead_distance_limit and lead_speed < max(6.0, v_ego + 0.5) + + def _get_chill_status(self, v_ego, v_cruise, sm, starpilot_toggles): + lead = self.starpilot_planner.lead_one + lead_status = bool(getattr(lead, "status", False)) + tracking_lead = bool(getattr(self.starpilot_planner, "tracking_lead", False)) + set_speed_error = max(0.0, v_cruise - v_ego) + + if (not lead_status and not tracking_lead and + v_ego >= starpilot_toggles.conditional_chill_speed and + set_speed_error >= starpilot_toggles.conditional_chill_speed_margin): + return CCStatus["SPEED"] + + if not starpilot_toggles.conditional_chill_lead: + return CCStatus["OFF"] + + if v_ego < starpilot_toggles.conditional_chill_speed_lead or not lead_status or not tracking_lead: + return CCStatus["OFF"] + + lead_distance = float(getattr(lead, "dRel", float("inf"))) + lead_speed = float(getattr(lead, "vLead", 0.0)) + lead_brake = max(0.0, -float(getattr(lead, "aLeadK", 0.0))) + lead_prob = float(getattr(lead, "modelProb", 0.0)) + closing_speed = max(0.0, v_ego - lead_speed) + max_closing_speed = max(self.STABLE_LEAD_MAX_CLOSING_SPEED, self.STABLE_LEAD_MAX_CLOSING_RATIO * v_ego) + max_distance = min(self.STABLE_LEAD_MAX_DISTANCE, max(35.0, v_ego * self.STABLE_LEAD_MAX_DISTANCE_TIME)) + lead_confident = bool(getattr(lead, "radar", False)) or lead_prob >= self.STABLE_LEAD_MIN_MODEL_PROB + + if not lead_confident: + return CCStatus["OFF"] + + if lead_distance >= max_distance or lead_speed <= self.STABLE_LEAD_MIN_SPEED: + return CCStatus["OFF"] + + if lead_brake > self.STABLE_LEAD_MAX_BRAKE or closing_speed > max_closing_speed: + return CCStatus["OFF"] + + return CCStatus["LEAD"] + + def _adjacent_lead_ambiguous(self, sm, v_ego): + radar_state = sm.get("starpilotRadarState") + if radar_state is None: + return False + + max_distance = min(self.ADJACENT_LEAD_VETO_MAX_DISTANCE, max(25.0, v_ego * self.ADJACENT_LEAD_VETO_MAX_DISTANCE_TIME)) + for lead in (getattr(radar_state, "leadLeft", None), getattr(radar_state, "leadRight", None)): + if lead is None or not getattr(lead, "status", False): + continue + + if float(getattr(lead, "dRel", float("inf"))) < max_distance and float(getattr(lead, "vLead", 0.0)) > self.ADJACENT_LEAD_VETO_MIN_SPEED: + return True + + return False + + def _write_status(self, status_value): + if status_value != self._prev_cc_status: + self.params_memory.put_int("CCStatus", status_value) + self._prev_cc_status = status_value diff --git a/starpilot/controls/starpilot_card.py b/starpilot/controls/starpilot_card.py index 3ba44d93a..d0db301a1 100644 --- a/starpilot/controls/starpilot_card.py +++ b/starpilot/controls/starpilot_card.py @@ -6,8 +6,11 @@ from openpilot.selfdrive.car.cruise import CRUISE_LONG_PRESS, ButtonType from openpilot.selfdrive.selfdrived.events import ET from openpilot.starpilot.common.experimental_state import ( + CCStatus, CEStatus, + next_manual_cc_status, next_manual_ce_status, + sync_manual_cc_state, sync_manual_ce_state, ) from openpilot.starpilot.common.starpilot_utilities import is_FrogsGoMoo @@ -80,6 +83,11 @@ class StarPilotCard: override_value = next_manual_ce_status(current_status, sm["selfdriveState"].experimentalMode) self.params_memory.put_int("CEStatus", override_value) sync_manual_ce_state(self.params, override_value) + elif getattr(starpilot_toggles, "conditional_chill_mode", False): + current_status = self.params_memory.get_int("CCStatus", default=CCStatus["OFF"]) + override_value = next_manual_cc_status(current_status, sm["selfdriveState"].experimentalMode) + self.params_memory.put_int("CCStatus", override_value) + sync_manual_cc_state(self.params, override_value) else: self.params.put_bool_nonblocking("ExperimentalMode", not sm["selfdriveState"].experimentalMode) diff --git a/starpilot/controls/starpilot_planner.py b/starpilot/controls/starpilot_planner.py index 9bedb8e8f..ffdeaeed4 100644 --- a/starpilot/controls/starpilot_planner.py +++ b/starpilot/controls/starpilot_planner.py @@ -16,6 +16,7 @@ from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import A_CHA from openpilot.starpilot.common.starpilot_utilities import calculate_lane_width, calculate_road_curvature from openpilot.starpilot.common.starpilot_variables import CRUISING_SPEED, MINIMUM_LATERAL_ACCELERATION, PLANNER_TIME, THRESHOLD +from openpilot.starpilot.controls.lib.conditional_chill_mode import ConditionalChillMode from openpilot.starpilot.controls.lib.conditional_experimental_mode import ConditionalExperimentalMode from openpilot.starpilot.controls.lib.starpilot_acceleration import StarPilotAcceleration from openpilot.starpilot.controls.lib.starpilot_events import StarPilotEvents @@ -47,6 +48,7 @@ class StarPilotPlanner: self.starpilot_acceleration = StarPilotAcceleration(self) self.starpilot_cem = ConditionalExperimentalMode(self) + self.starpilot_ccm = ConditionalChillMode(self, self.starpilot_cem) self.starpilot_events = StarPilotEvents(self, error_log, ThemeManager) self.starpilot_following = StarPilotFollowing(self) self.starpilot_vcruise = StarPilotVCruise(self) @@ -194,11 +196,17 @@ class StarPilotPlanner: self.starpilot_following.update(controls_enabled, v_ego, sm, starpilot_toggles) - cem_tracking_active = controls_enabled or sm["starpilotCarState"].alwaysOnLateralEnabled - if cem_tracking_active and starpilot_toggles.conditional_experimental_mode: + conditional_tracking_active = controls_enabled or sm["starpilotCarState"].alwaysOnLateralEnabled + if conditional_tracking_active and starpilot_toggles.conditional_experimental_mode: # Keep CEM's filters warm in AOL so engagement can inherit the current scene. self.starpilot_cem.update(v_ego, sm, starpilot_toggles) + self.starpilot_ccm.experimental_mode = True + elif conditional_tracking_active and starpilot_toggles.conditional_chill_mode: + self.starpilot_ccm.update(v_ego, v_cruise, sm, starpilot_toggles) + self.starpilot_cem.experimental_mode = False else: + self.starpilot_ccm.experimental_mode = True + self.starpilot_cem.experimental_mode = False self.starpilot_cem.curve_detected = False self.starpilot_cem.stop_sign_and_light(v_ego, sm, PLANNER_TIME - 2) @@ -263,7 +271,13 @@ class StarPilotPlanner: starpilotPlan.disableThrottle = self.starpilot_following.disable_throttle starpilotPlan.trackingLead = self.tracking_lead - starpilotPlan.experimentalMode = self.starpilot_cem.experimental_mode or self.starpilot_vcruise.slc.experimental_mode + conditional_experimental_mode = False + if starpilot_toggles.conditional_experimental_mode: + conditional_experimental_mode = self.starpilot_cem.experimental_mode + elif starpilot_toggles.conditional_chill_mode: + conditional_experimental_mode = self.starpilot_ccm.experimental_mode + + starpilotPlan.experimentalMode = conditional_experimental_mode or self.starpilot_vcruise.slc.experimental_mode starpilotPlan.forcingStop = self.starpilot_vcruise.forcing_stop starpilotPlan.forcingStopLength = self.starpilot_vcruise.tracked_model_length diff --git a/starpilot/controls/tests/test_starpilot_card.py b/starpilot/controls/tests/test_starpilot_card.py index ea5d25f06..d823a409c 100644 --- a/starpilot/controls/tests/test_starpilot_card.py +++ b/starpilot/controls/tests/test_starpilot_card.py @@ -235,6 +235,23 @@ def test_pacifica_hybrid_main_aol_waits_for_set_press(monkeypatch, tmp_path): assert ret.alwaysOnLateralEnabled is False +def test_conditional_chill_wheel_override_cycles_manual_state(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(conditional_chill_mode=True) + + sm["selfdriveState"].experimentalMode = True + card.handle_experimental_mode(sm, toggles) + assert card.params_memory.get_int("CCStatus") == spc.CCStatus["USER_CHILL"] + + card.handle_experimental_mode(sm, toggles) + assert card.params_memory.get_int("CCStatus") == spc.CCStatus["OFF"] + + 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) diff --git a/starpilot/starpilot_process.py b/starpilot/starpilot_process.py index 908889401..63fa7f0fb 100644 --- a/starpilot/starpilot_process.py +++ b/starpilot/starpilot_process.py @@ -199,7 +199,7 @@ def starpilot_thread(): sm = messaging.SubMaster(["carControl", "carState", "controlsState", "deviceState", "driverMonitoringState", "gpsLocation", "gpsLocationExternal", "liveParameters", "managerState", "modelV2", "onroadEvents", "pandaStates", "radarState", "selfdriveState", "starpilotCarState", - "starpilotSelfdriveState", "starpilotModelV2", "starpilotOnroadEvents", "mapdOut"], + "starpilotRadarState", "starpilotSelfdriveState", "starpilotModelV2", "starpilotOnroadEvents", "mapdOut"], poll="modelV2") params = Params(return_defaults=True) diff --git a/starpilot/system/the_pond/assets/components/tools/device_settings_layout.json b/starpilot/system/the_pond/assets/components/tools/device_settings_layout.json index 9699cf86e..3be299090 100644 --- a/starpilot/system/the_pond/assets/components/tools/device_settings_layout.json +++ b/starpilot/system/the_pond/assets/components/tools/device_settings_layout.json @@ -235,24 +235,16 @@ "step": 1.0, "parent_key": "QOLLateral" }, - { - "key": "PauseLateralOnSignal", - "label": "Turn Signal Only", - "description": "Only pause steering when the turn signal is active.", - "data_type": "bool", - "ui_type": "toggle", - "parent_key": "QOLLateral" - }, { "key": "LateralResumeDelay", "label": "Lateral Resume Delay", - "description": "Delay before steering resumes after the turn signal turns off. Only applies when vehicle speed dropped below half the pause speed during the signal. Set to 0 to disable.", + "description": "Delay before lateral control resumes after the turn signal is turned off. Only applies when the vehicle speed dropped below half the \"Pause Steering Below\" speed during the turn signal. Set to 0 to disable.", "data_type": "float", "ui_type": "numeric", "min": 0.0, "max": 5.0, "step": 0.1, - "parent_key": "PauseLateralOnSignal" + "parent_key": "QOLLateral" } ] }, @@ -284,99 +276,6 @@ "ui_type": "toggle", "parent_key": "AdvancedLongitudinalTune" }, - { - "key": "CustomAccelProfile", - "label": "Custom Accel Profile", - "description": "Replace the built-in acceleration profile with your own per-speed max-acceleration values. Breakpoint speeds stay fixed, and the starting defaults mirror the currently selected acceleration profile plus EV or Truck tuning.", - "data_type": "bool", - "ui_type": "toggle", - "is_parent_toggle": true, - "parent_key": "AdvancedLongitudinalTune" - }, - { - "key": "CustomAccelProfile0MPH", - "label": "0 mph", - "description": "Max acceleration in m/s² at the fixed 0 mph breakpoint.", - "data_type": "float", - "ui_type": "numeric", - "min": 0.0, - "max": 6.0, - "step": 0.01, - "precision": 2, - "parent_key": "CustomAccelProfile" - }, - { - "key": "CustomAccelProfile11MPH", - "label": "11 mph", - "description": "Max acceleration in m/s² at the fixed 11 mph breakpoint.", - "data_type": "float", - "ui_type": "numeric", - "min": 0.0, - "max": 6.0, - "step": 0.01, - "precision": 2, - "parent_key": "CustomAccelProfile" - }, - { - "key": "CustomAccelProfile22MPH", - "label": "22 mph", - "description": "Max acceleration in m/s² at the fixed 22 mph breakpoint.", - "data_type": "float", - "ui_type": "numeric", - "min": 0.0, - "max": 6.0, - "step": 0.01, - "precision": 2, - "parent_key": "CustomAccelProfile" - }, - { - "key": "CustomAccelProfile34MPH", - "label": "34 mph", - "description": "Max acceleration in m/s² at the fixed 34 mph breakpoint.", - "data_type": "float", - "ui_type": "numeric", - "min": 0.0, - "max": 6.0, - "step": 0.01, - "precision": 2, - "parent_key": "CustomAccelProfile" - }, - { - "key": "CustomAccelProfile45MPH", - "label": "45 mph", - "description": "Max acceleration in m/s² at the fixed 45 mph breakpoint.", - "data_type": "float", - "ui_type": "numeric", - "min": 0.0, - "max": 6.0, - "step": 0.01, - "precision": 2, - "parent_key": "CustomAccelProfile" - }, - { - "key": "CustomAccelProfile56MPH", - "label": "56 mph", - "description": "Max acceleration in m/s² at the fixed 56 mph breakpoint.", - "data_type": "float", - "ui_type": "numeric", - "min": 0.0, - "max": 6.0, - "step": 0.01, - "precision": 2, - "parent_key": "CustomAccelProfile" - }, - { - "key": "CustomAccelProfile89MPH", - "label": "89 mph", - "description": "Max acceleration in m/s² at the fixed 89 mph breakpoint.", - "data_type": "float", - "ui_type": "numeric", - "min": 0.0, - "max": 6.0, - "step": 0.01, - "precision": 2, - "parent_key": "CustomAccelProfile" - }, { "key": "LongitudinalActuatorDelay", "label": "Actuator Delay", @@ -568,6 +467,71 @@ "ui_type": "toggle", "parent_key": "ConditionalExperimental" }, + { + "key": "ConditionalChill", + "label": "Conditional Chill Mode", + "description": "Keep \"Experimental Mode\" on by default, but temporarily switch to \"Chill Mode\" in simple cruising scenes where speed holding is usually better.", + "data_type": "bool", + "ui_type": "toggle", + "is_parent_toggle": true + }, + { + "key": "PersistChillState", + "label": "Persist Chill State", + "description": "Keep your manual Conditional Chill override through reboots until you manually clear it.", + "data_type": "bool", + "ui_type": "toggle", + "parent_key": "ConditionalChill" + }, + { + "key": "CCMSpeed", + "label": "Above", + "description": "Switch to \"Chill Mode\" on open roads above this speed when no lead is detected and the car is still below the set speed.", + "data_type": "int", + "ui_type": "numeric", + "min": 0.0, + "max": 99.0, + "step": 1.0, + "parent_key": "ConditionalChill" + }, + { + "key": "CCMSpeedLead", + "label": "Above (With Lead)", + "description": "Switch to \"Chill Mode\" when following a stable lead above this speed.", + "data_type": "int", + "ui_type": "numeric", + "min": 0.0, + "max": 99.0, + "step": 1.0, + "parent_key": "ConditionalChill" + }, + { + "key": "CCMLead", + "label": "Stable Lead Ahead", + "description": "Switch to \"Chill Mode\" when following a steady, well-tracked lead vehicle at cruising speeds.", + "data_type": "bool", + "ui_type": "toggle", + "parent_key": "ConditionalChill" + }, + { + "key": "CCMSetSpeedMargin", + "label": "Set Speed Margin", + "description": "How far below the set speed the car must be before open-road Conditional Chill can engage.", + "data_type": "int", + "ui_type": "numeric", + "min": 0.0, + "max": 15.0, + "step": 1.0, + "parent_key": "ConditionalChill" + }, + { + "key": "ShowCCMStatus", + "label": "Status Widget", + "description": "Show which condition triggered \"Chill Mode\" on the driving screen.", + "data_type": "bool", + "ui_type": "toggle", + "parent_key": "ConditionalChill" + }, { "key": "CurveSpeedController", "label": "Curve Speed Controller", @@ -598,35 +562,8 @@ "description": "Customize the \"Traffic Mode\" personality profile. Designed for stop-and-go driving.", "data_type": "bool", "ui_type": "toggle", - "is_parent_toggle": true, - "parent_key": "CustomPersonalities" - }, - { - "key": "AggressivePersonalityProfile", - "label": "Aggressive", - "description": "Customize the \"Aggressive\" personality profile. Designed for assertive driving with tighter gaps.", - "data_type": "bool", - "ui_type": "toggle", - "is_parent_toggle": true, - "parent_key": "CustomPersonalities" - }, - { - "key": "StandardPersonalityProfile", - "label": "Standard", - "description": "Customize the \"Standard\" personality profile. Designed for balanced driving with moderate gaps.", - "data_type": "bool", - "ui_type": "toggle", - "is_parent_toggle": true, - "parent_key": "CustomPersonalities" - }, - { - "key": "RelaxedPersonalityProfile", - "label": "Relaxed", - "description": "Customize the \"Relaxed\" personality profile. Designed for smoother, more comfortable driving with larger gaps.", - "data_type": "bool", - "ui_type": "toggle", - "is_parent_toggle": true, - "parent_key": "CustomPersonalities" + "parent_key": "CustomPersonalities", + "is_parent_toggle": true }, { "key": "TrafficFollow", @@ -694,6 +631,15 @@ "step": 0.01, "parent_key": "TrafficPersonalityProfile" }, + { + "key": "AggressivePersonalityProfile", + "label": "Aggressive", + "description": "Customize the \"Aggressive\" personality profile. Designed for assertive driving with tighter gaps.", + "data_type": "bool", + "ui_type": "toggle", + "parent_key": "CustomPersonalities", + "is_parent_toggle": true + }, { "key": "AggressiveFollow", "label": "Following Distance", @@ -771,6 +717,15 @@ "step": 0.01, "parent_key": "AggressivePersonalityProfile" }, + { + "key": "StandardPersonalityProfile", + "label": "Standard", + "description": "Customize the \"Standard\" personality profile. Designed for balanced driving with moderate gaps.", + "data_type": "bool", + "ui_type": "toggle", + "parent_key": "CustomPersonalities", + "is_parent_toggle": true + }, { "key": "StandardFollow", "label": "Following Distance", @@ -848,6 +803,15 @@ "step": 0.01, "parent_key": "StandardPersonalityProfile" }, + { + "key": "RelaxedPersonalityProfile", + "label": "Relaxed", + "description": "Customize the \"Relaxed\" personality profile. Designed for smoother, more comfortable driving with larger gaps.", + "data_type": "bool", + "ui_type": "toggle", + "parent_key": "CustomPersonalities", + "is_parent_toggle": true + }, { "key": "RelaxedFollow", "label": "Following Distance", @@ -933,54 +897,6 @@ "ui_type": "toggle", "is_parent_toggle": true }, - { - "key": "AccelerationProfile", - "label": "Acceleration Profile", - "description": "How quickly openpilot speeds up. \"Eco\" is gentle and efficient, \"Sport\" is firmer and more responsive, and \"Sport+\" accelerates at the maximum rate allowed.", - "data_type": "int", - "ui_type": "dropdown", - "options": [ - { - "value": 0, - "label": "Standard" - }, - { - "value": 1, - "label": "Eco" - }, - { - "value": 2, - "label": "Sport" - }, - { - "value": 3, - "label": "Sport+" - } - ], - "parent_key": "LongitudinalTune" - }, - { - "key": "DecelerationProfile", - "label": "Deceleration Profile", - "description": "How firmly openpilot slows down. \"Eco\" favors coasting, \"Sport\" applies stronger braking.", - "data_type": "int", - "ui_type": "dropdown", - "options": [ - { - "value": 0, - "label": "Standard" - }, - { - "value": 1, - "label": "Eco" - }, - { - "value": 2, - "label": "Sport" - } - ], - "parent_key": "LongitudinalTune" - }, { "key": "HumanAcceleration", "label": "Human-Like Acceleration", @@ -992,7 +908,7 @@ { "key": "CoastUpToLeads", "label": "Coast Up To Leads", - "description": "Keep the optional far-lead comfort logic enabled, including gentle coasting and matched-follow smoothing for distant leads. Recommended unless your car shows lead-follow stutter or springy gas/brake behavior.", + "description": "Keep the optional far-lead comfort logic enabled, including gentle coasting and matched-follow smoothing for distant leads. Recommended to leave on unless your vehicle shows lead-follow stutter or springy gas/brake behavior.", "data_type": "bool", "ui_type": "toggle", "parent_key": "LongitudinalTune" @@ -1069,7 +985,7 @@ }, { "key": "ForceStopDistanceOffset", - "label": "Force Stop Distance Offset (ft)", + "label": "Force Stop Distance Offset", "description": "Tune where Force Stops bring the car to rest. Positive values let the car roll further before stopping (longer stop, closer to the line). Negative values stop the car sooner (more buffer before the line).", "data_type": "int", "ui_type": "numeric", @@ -1121,14 +1037,6 @@ "max": 99.0, "parent_key": "QOLLongitudinal" }, - { - "key": "RedneckCruise", - "label": "Redneck Cruise", - "description": "On supported Hyundai stock-long cars, use RES/SET button presses to match the cluster set speed to StarPilot's target.", - "data_type": "bool", - "ui_type": "toggle", - "parent_key": "QOLLongitudinal" - }, { "key": "ReverseCruise", "label": "Reverse Cruise Increase", @@ -1143,17 +1051,8 @@ "description": "Automatically adjust driving behavior based on real-time weather. Helps maintain comfort and safety in low visibility, rain, or snow.", "data_type": "bool", "ui_type": "toggle", - "is_parent_toggle": true, "parent_key": "QOLLongitudinal" }, - { - "key": "LowVisibilityOffsets", - "label": "Low Visibility (fog)", - "description": "Customise settings for driving in low visibility situations", - "ui_type": "group", - "is_parent_toggle": true, - "parent_key": "WeatherPresets" - }, { "key": "IncreaseFollowingLowVisibility", "label": "Increase Following Distance by:", @@ -1162,8 +1061,7 @@ "ui_type": "numeric", "min": 0.0, "max": 3.0, - "step": 0.01, - "parent_key": "LowVisibilityOffsets" + "step": 0.01 }, { "key": "IncreasedStoppedDistanceLowVisibility", @@ -1172,8 +1070,7 @@ "data_type": "float", "ui_type": "numeric", "min": 0.0, - "max": 10.0, - "parent_key": "LowVisibilityOffsets" + "max": 10.0 }, { "key": "ReduceAccelerationLowVisibility", @@ -1183,8 +1080,7 @@ "ui_type": "numeric", "min": 0.0, "max": 99.0, - "step": 1.0, - "parent_key": "LowVisibilityOffsets" + "step": 1.0 }, { "key": "ReduceLateralAccelerationLowVisibility", @@ -1194,16 +1090,7 @@ "ui_type": "numeric", "min": 0.0, "max": 99.0, - "step": 1.0, - "parent_key": "LowVisibilityOffsets" - }, - { - "key": "RainOffsets", - "label": "Light/Medium Rain", - "description": "Customise settings for driving in light & medium rain", - "ui_type": "group", - "is_parent_toggle": true, - "parent_key": "WeatherPresets" + "step": 1.0 }, { "key": "IncreaseFollowingRain", @@ -1213,8 +1100,7 @@ "ui_type": "numeric", "min": 0.0, "max": 3.0, - "step": 0.01, - "parent_key": "RainOffsets" + "step": 0.01 }, { "key": "IncreasedStoppedDistanceRain", @@ -1223,8 +1109,7 @@ "data_type": "float", "ui_type": "numeric", "min": 0.0, - "max": 10.0, - "parent_key": "RainOffsets" + "max": 10.0 }, { "key": "ReduceAccelerationRain", @@ -1234,8 +1119,7 @@ "ui_type": "numeric", "min": 0.0, "max": 99.0, - "step": 1.0, - "parent_key": "RainOffsets" + "step": 1.0 }, { "key": "ReduceLateralAccelerationRain", @@ -1245,16 +1129,7 @@ "ui_type": "numeric", "min": 0.0, "max": 99.0, - "step": 1.0, - "parent_key": "RainOffsets" - }, - { - "key": "RainStormOffsets", - "label": "Heavy Rain", - "description": "Customise settings for driving in heavy rain", - "ui_type": "group", - "is_parent_toggle": true, - "parent_key": "WeatherPresets" + "step": 1.0 }, { "key": "IncreaseFollowingRainStorm", @@ -1264,8 +1139,7 @@ "ui_type": "numeric", "min": 0.0, "max": 3.0, - "step": 0.01, - "parent_key": "RainStormOffsets" + "step": 0.01 }, { "key": "IncreasedStoppedDistanceRainStorm", @@ -1274,8 +1148,7 @@ "data_type": "float", "ui_type": "numeric", "min": 0.0, - "max": 10.0, - "parent_key": "RainStormOffsets" + "max": 10.0 }, { "key": "ReduceAccelerationRainStorm", @@ -1285,8 +1158,7 @@ "ui_type": "numeric", "min": 0.0, "max": 99.0, - "step": 1.0, - "parent_key": "RainStormOffsets" + "step": 1.0 }, { "key": "ReduceLateralAccelerationRainStorm", @@ -1296,16 +1168,7 @@ "ui_type": "numeric", "min": 0.0, "max": 99.0, - "step": 1.0, - "parent_key": "RainStormOffsets" - }, - { - "key": "SnowOffsets", - "label": "Snow", - "description": "Customise settings for driving in snow", - "ui_type": "group", - "is_parent_toggle": true, - "parent_key": "WeatherPresets" + "step": 1.0 }, { "key": "IncreaseFollowingSnow", @@ -1315,8 +1178,7 @@ "ui_type": "numeric", "min": 0.0, "max": 3.0, - "step": 0.01, - "parent_key": "SnowOffsets" + "step": 0.01 }, { "key": "IncreasedStoppedDistanceSnow", @@ -1325,8 +1187,7 @@ "data_type": "float", "ui_type": "numeric", "min": 0.0, - "max": 10.0, - "parent_key": "SnowOffsets" + "max": 10.0 }, { "key": "ReduceAccelerationSnow", @@ -1336,8 +1197,7 @@ "ui_type": "numeric", "min": 0.0, "max": 99.0, - "step": 1.0, - "parent_key": "SnowOffsets" + "step": 1.0 }, { "key": "ReduceLateralAccelerationSnow", @@ -1347,75 +1207,24 @@ "ui_type": "numeric", "min": 0.0, "max": 99.0, - "step": 1.0, - "parent_key": "SnowOffsets" + "step": 1.0 }, { "key": "SpeedLimitController", "label": "Speed Limit Controller", - "description": "Limit openpilot's maximum driving speed to the current speed limit obtained from downloaded maps, Mapbox, or the dashboard for supported vehicles (Ford, Genesis, Hyundai, Kia, Lexus, Toyota).", + "description": "Limit openpilot's maximum driving speed to the current speed limit obtained from downloaded maps, Mapbox, the dashboard, or vision-detected signs.", "data_type": "bool", "ui_type": "toggle", "is_parent_toggle": true }, - { - "key": "VisionSpeedLimitDetection", - "label": "Vision Speed Limit Detection", - "description": "Use the road camera to detect speed limit signs for SLC and speed limit filling.", - "data_type": "bool", - "ui_type": "toggle", - "is_parent_toggle": true, - "parent_key": "SpeedLimitController" - }, - { - "key": "VisionSpeedLimitAutoBookmark", - "label": "Auto-Bookmark Vision Signs", - "description": "Automatically save confirmed vision-detected speed limit signs into the speed-limit debug session so they can be imported into the training set later.", - "data_type": "bool", - "ui_type": "toggle", - "parent_key": "VisionSpeedLimitDetection" - }, - { - "key": "VisionSpeedLimitTrainingCollector", - "label": "Collect Extra Vision Training Samples", - "description": "Save lower-threshold vision sign candidates into the debug session for later training import without showing or applying them live. Leave this on if you want to help improve the model.", - "data_type": "bool", - "ui_type": "toggle", - "parent_key": "VisionSpeedLimitDetection" - }, - { - "key": "VisionSpeedLimitAutoPreserveSegment", - "label": "Preserve Auto-Bookmarked Segments", - "description": "Also send a real bookmark for confirmed auto-bookmarks so loggerd preserves the route segment. Leave this off unless you specifically want the extra storage usage.", - "data_type": "bool", - "ui_type": "toggle", - "parent_key": "VisionSpeedLimitAutoBookmark" - }, { "key": "SLCConfirmation", "label": "Confirm New Speed Limits", "description": "Ask before changing to a new speed limit. To accept, tap the flashing on-screen widget or press the Cruise Increase button. To deny, press the Cruise Decrease button or ignore the prompt for 30 seconds.", "data_type": "bool", "ui_type": "toggle", - "is_parent_toggle": true, "parent_key": "SpeedLimitController" }, - { - "key": "SLCConfirmationLower", - "label": "Confirm Lower Limits", - "description": "Require confirmation before applying a newly detected lower speed limit.", - "data_type": "bool", - "ui_type": "toggle", - "parent_key": "SLCConfirmation" - }, - { - "key": "SLCConfirmationHigher", - "label": "Confirm Higher Limits", - "description": "Require confirmation before applying a newly detected higher speed limit.", - "data_type": "bool", - "ui_type": "toggle", - "parent_key": "SLCConfirmation" - }, { "key": "SLCLookaheadHigher", "label": "Higher Limit Lookahead Time", @@ -1424,7 +1233,6 @@ "ui_type": "numeric", "min": 0.0, "max": 30.0, - "step": 0.1, "parent_key": "SpeedLimitController" }, { @@ -1435,7 +1243,6 @@ "ui_type": "numeric", "min": 0.0, "max": 30.0, - "step": 0.1, "parent_key": "SpeedLimitController" }, { @@ -1446,50 +1253,6 @@ "ui_type": "toggle", "parent_key": "SpeedLimitController" }, - { - "key": "SLCFallback", - "label": "Fallback Speed", - "description": "Choose the speed to use when no speed limit source is currently available.", - "data_type": "int", - "ui_type": "dropdown", - "options": [ - { - "value": 0, - "label": "Set Speed" - }, - { - "value": 1, - "label": "Experimental Mode" - }, - { - "value": 2, - "label": "Previous Limit" - } - ], - "parent_key": "SpeedLimitController" - }, - { - "key": "SLCOverride", - "label": "Override Speed", - "description": "Choose how SLC behaves after you manually drive faster than the posted speed limit.", - "data_type": "int", - "ui_type": "dropdown", - "options": [ - { - "value": 0, - "label": "None" - }, - { - "value": 1, - "label": "Set With Gas Pedal" - }, - { - "value": 2, - "label": "Max Set Speed" - } - ], - "parent_key": "SpeedLimitController" - }, { "key": "SLCMapboxFiller", "label": "Use Mapbox as Fallback", @@ -1499,59 +1262,11 @@ "parent_key": "SpeedLimitController" }, { - "key": "SLCPriority1", - "label": "Primary Speed Limit Source", - "description": "Choose the first speed-limit source to trust when more than one is available.", - "data_type": "string", - "ui_type": "dropdown", - "options": [ - { - "value": "Dashboard", - "label": "Dashboard" - }, - { - "value": "Map Data", - "label": "Map Data" - }, - { - "value": "Vision", - "label": "Vision" - }, - { - "value": "Highest", - "label": "Highest" - }, - { - "value": "Lowest", - "label": "Lowest" - } - ], - "parent_key": "SpeedLimitController" - }, - { - "key": "SLCPriority2", - "label": "Secondary Speed Limit Source", - "description": "Choose the backup speed-limit source when the primary one is unavailable.", - "data_type": "string", - "ui_type": "dropdown", - "options": [ - { - "value": "None", - "label": "None" - }, - { - "value": "Dashboard", - "label": "Dashboard" - }, - { - "value": "Map Data", - "label": "Map Data" - }, - { - "value": "Vision", - "label": "Vision" - } - ], + "key": "VisionSpeedLimitDetection", + "label": "Vision Speed Limit Detection", + "description": "Use the road camera to detect speed limit signs for SLC and speed limit filling.", + "data_type": "bool", + "ui_type": "toggle", "parent_key": "SpeedLimitController" }, { @@ -1562,7 +1277,6 @@ "ui_type": "numeric", "min": -99.0, "max": 99.0, - "step": 1.0, "parent_key": "SpeedLimitController" }, { @@ -1573,7 +1287,6 @@ "ui_type": "numeric", "min": -99.0, "max": 99.0, - "step": 1.0, "parent_key": "SpeedLimitController" }, { @@ -1584,7 +1297,6 @@ "ui_type": "numeric", "min": -99.0, "max": 99.0, - "step": 1.0, "parent_key": "SpeedLimitController" }, { @@ -1595,7 +1307,6 @@ "ui_type": "numeric", "min": -99.0, "max": 99.0, - "step": 1.0, "parent_key": "SpeedLimitController" }, { @@ -1606,7 +1317,6 @@ "ui_type": "numeric", "min": -99.0, "max": 99.0, - "step": 1.0, "parent_key": "SpeedLimitController" }, { @@ -1617,7 +1327,6 @@ "ui_type": "numeric", "min": -99.0, "max": 99.0, - "step": 1.0, "parent_key": "SpeedLimitController" }, { @@ -1628,7 +1337,6 @@ "ui_type": "numeric", "min": -99.0, "max": 99.0, - "step": 1.0, "parent_key": "SpeedLimitController" }, { @@ -1646,6 +1354,22 @@ "data_type": "bool", "ui_type": "toggle", "parent_key": "SpeedLimitController" + }, + { + "key": "SLCAbbreviatedSources", + "label": "Show Abbreviated Icon Sources", + "description": "Render the speed-limit sources as compact text labels (e.g. \"Dash-45\", \"MapD-30\") without icons.", + "data_type": "bool", + "ui_type": "toggle", + "parent_key": "SpeedLimitController" + }, + { + "key": "SLCActiveSourcesOnly", + "label": "Only Show Sources With Speed Limits", + "description": "Hide source rows that have no current speed limit reading. Works with both abbreviated and full display.", + "data_type": "bool", + "ui_type": "toggle", + "parent_key": "SpeedLimitController" } ] }, @@ -1693,6 +1417,38 @@ "ui_type": "toggle", "parent_key": "AdvancedCustomUI" }, + { + "key": "HideChangingLanesBanner", + "label": "Hide Changing Lanes Banner", + "description": "Hide the 'Changing Lanes' banner from the driving screen.", + "data_type": "bool", + "ui_type": "toggle", + "parent_key": "AdvancedCustomUI" + }, + { + "key": "HideDistanceProfileBanner", + "label": "Hide Distance Profile Banner", + "description": "Hide the driving personality banner when changing distance profiles.", + "data_type": "bool", + "ui_type": "toggle", + "parent_key": "AdvancedCustomUI" + }, + { + "key": "HideDMIcon", + "label": "Hide Driver Monitoring Icon", + "description": "Hide the driver monitoring icon from the driving screen.", + "data_type": "bool", + "ui_type": "toggle", + "parent_key": "AdvancedCustomUI" + }, + { + "key": "HideTurningBanner", + "label": "Hide Turning Banner", + "description": "Hide the 'Turning Left/Right' banner from the driving screen.", + "data_type": "bool", + "ui_type": "toggle", + "parent_key": "AdvancedCustomUI" + }, { "key": "HideSpeedLimit", "label": "Hide Speed Limits", @@ -1709,13 +1465,6 @@ "ui_type": "toggle", "parent_key": "AdvancedCustomUI" }, - { - "key": "HolidayThemes", - "label": "Holiday Themes", - "description": "Automatically apply seasonal theme assets during holiday windows. Turn this off to keep your normal theme year-round.", - "data_type": "bool", - "ui_type": "toggle" - }, { "key": "CustomUI", "label": "Driving Screen Widgets", @@ -1732,22 +1481,6 @@ "ui_type": "toggle", "parent_key": "CustomUI" }, - { - "key": "RainbowPath", - "label": "Rainbow Road", - "description": "Color the driving path like a rainbow road.\n\nOn the Python UIs this overrides acceleration and braking path coloring.", - "data_type": "bool", - "ui_type": "toggle", - "parent_key": "CustomUI" - }, - { - "key": "ShowModeStatusBanner", - "label": "Show Mode Status Banner", - "description": "Show a popup banner when the Python driving UIs switch into chill, experimental, switchback, or override mode.", - "data_type": "bool", - "ui_type": "toggle", - "parent_key": "CustomUI" - }, { "key": "AdjacentPath", "label": "Adjacent Lanes", @@ -1822,26 +1555,6 @@ "max": 24.0, "parent_key": "ModelUI" }, - { - "key": "LaneLinesColor", - "label": "Lane Line Color", - "description": "Set a custom lane-line color for the Python UIs.\n\nUse Stock to follow the default line color behavior.", - "data_type": "string", - "ui_type": "color", - "default_color": "#00FF00", - "parent_key": "ModelUI" - }, - { - "key": "BorderWidth", - "label": "Border Width", - "description": "Scale the driving-screen border thickness.\n\n100% matches the stock border width.", - "data_type": "float", - "ui_type": "numeric", - "min": 25.0, - "max": 250.0, - "step": 5.0, - "parent_key": "ModelUI" - }, { "key": "PathEdgeWidth", "label": "Path Edges Width", @@ -1852,15 +1565,6 @@ "max": 100.0, "parent_key": "ModelUI" }, - { - "key": "PathEdgesColor", - "label": "Path Edge Color", - "description": "Set a custom path-edge color for the Python UIs.\n\nUse Stock to follow the default line color behavior.", - "data_type": "string", - "ui_type": "color", - "default_color": "#00FF00", - "parent_key": "ModelUI" - }, { "key": "PathWidth", "label": "Path Width", @@ -1872,15 +1576,6 @@ "step": 0.1, "parent_key": "ModelUI" }, - { - "key": "PathColor", - "label": "Path Color", - "description": "Set a custom filled-path color for the Python UIs.\n\nUse Stock to follow the default path color behavior.", - "data_type": "string", - "ui_type": "color", - "default_color": "#30FF9C", - "parent_key": "ModelUI" - }, { "key": "RoadEdgesWidth", "label": "Road Edges Width", @@ -1939,32 +1634,6 @@ "ui_type": "toggle", "is_parent_toggle": true }, - { - "key": "CameraView", - "label": "Camera View", - "description": "Choose which camera feed the driving screen uses: Auto, Driver, Standard, or Wide.", - "data_type": "int", - "ui_type": "dropdown", - "options": [ - { - "value": 0, - "label": "Auto" - }, - { - "value": 1, - "label": "Driver" - }, - { - "value": 2, - "label": "Standard" - }, - { - "value": 3, - "label": "Wide" - } - ], - "parent_key": "QOLVisuals" - }, { "key": "DriverCamera", "label": "Show Driver Camera When In Reverse", @@ -1982,12 +1651,11 @@ "parent_key": "QOLVisuals" }, { - "key": "SimpleMode", - "label": "Simple Mode", - "description": "Use a more stock-like presentation by hiding most branch-specific UI, theme, sound, and alert styling. This only changes presentation and does not change driving behavior.", + "key": "DisableWideRoad", + "label": "Disable Wide Road Camera", + "description": "Only enable this if the wide camera is broken or for development!\n\nDisabling the wide camera may degrade driving performance and cause instability.\n\nRequires a reboot to take effect.", "data_type": "bool", - "ui_type": "toggle", - "parent_key": "QOLVisuals" + "ui_type": "toggle" } ] }, @@ -2003,21 +1671,10 @@ "ui_type": "toggle", "is_parent_toggle": true }, - { - "key": "SwitchbackModeCooldown", - "label": "Switchback Mode Cooldown", - "description": "Set the minimum time between repeated steering-limit and minimum-steer-speed alerts while \"Switchback Mode\" is active. Useful on winding roads where \"Turn Exceeds Steering Limit\" and \"Steer Unavailable Under\" can repeat frequently. Set to Off to disable the cooldown even when the mode is on.", - "data_type": "int", - "ui_type": "numeric", - "min": 0.0, - "max": 30.0, - "step": 1.0, - "parent_key": "AlertVolumeControl" - }, { "key": "BelowSteerSpeedVolume", "label": "Min Steer Speed Alert Volume", - "description": "Set the volume for the \"Steer Unavailable Under\" alert shown below the car's minimum steering speed. Set to Muted to silence only this alert.", + "description": "Set the volume for the \"Steer Unavailable Under\" alert shown below the car's minimum steering speed.\n\nSet to Muted to silence only this alert.", "data_type": "int", "ui_type": "numeric", "min": 0.0, @@ -2194,107 +1851,6 @@ "data_type": "bool", "ui_type": "toggle" }, - { - "key": "DisableOpenpilotLongitudinal", - "label": "Disable openpilot Longitudinal", - "description": "Use the vehicle's stock longitudinal control instead of openpilot longitudinal control. This can stop openpilot from sending cruise-control acceleration and braking commands on supported vehicles.", - "data_type": "bool", - "ui_type": "toggle" - }, - { - "key": "NAPRadarEnabled", - "label": "Tesla Pre-AP Bosch Radar", - "description": "Use the stock Bosch radar path on pre-Autopilot Teslas.", - "data_type": "bool", - "ui_type": "toggle", - "is_parent_toggle": true - }, - { - "key": "NAPRadarBehindNosecone", - "label": "Radar Behind Nosecone", - "description": "Enable this if the Bosch radar is mounted behind the nosecone instead of being exposed.", - "data_type": "bool", - "ui_type": "toggle", - "parent_key": "NAPRadarEnabled" - }, - { - "key": "NAPRadarOffset", - "label": "Radar Offset", - "description": "Adjust the radar position offset in meters if the radar alignment needs a small correction.", - "data_type": "float", - "ui_type": "numeric", - "min": -2.0, - "max": 2.0, - "step": 0.01, - "precision": 2, - "parent_key": "NAPRadarEnabled" - }, - { - "key": "NAPPedalEnabled", - "label": "Tesla Pre-AP comma Pedal Longitudinal", - "description": "Use a comma pedal interceptor for acceleration and regenerative braking on pre-Autopilot Teslas.", - "data_type": "bool", - "ui_type": "toggle", - "is_parent_toggle": true - }, - { - "key": "NAPPedalCanBus", - "label": "Pedal CAN Bus", - "description": "Select the CAN bus used by the Tesla pedal interceptor.", - "data_type": "int", - "ui_type": "dropdown", - "parent_key": "NAPPedalEnabled", - "options": [ - { - "value": 0, - "label": "CAN 0" - }, - { - "value": 2, - "label": "CAN 2" - } - ] - }, - { - "key": "NAPAdaptiveAccel", - "label": "Adaptive Acceleration Limit", - "description": "Reduce maximum acceleration as you close in on a lead vehicle when Tesla pedal-long is active.", - "data_type": "bool", - "ui_type": "toggle", - "parent_key": "NAPPedalEnabled" - }, - { - "key": "NAPPedalCalibDone", - "label": "Pedal Calibration Complete", - "description": "Enable only after entering valid Tesla pedal calibration values. Pedal-long stays inactive until this is on.", - "data_type": "bool", - "ui_type": "toggle", - "parent_key": "NAPPedalEnabled" - }, - { - "key": "NAPPedalCalibFactor", - "label": "Pedal Calibration Factor", - "description": "Scaling factor used to convert Tesla pedal interceptor output into the internal DI range.", - "data_type": "float", - "ui_type": "numeric", - "min": 0.01, - "max": 10.0, - "step": 0.01, - "precision": 2, - "parent_key": "NAPPedalEnabled" - }, - { - "key": "NAPPedalCalibZero", - "label": "Pedal Calibration Zero", - "description": "Zero point used to convert Tesla pedal interceptor output into the internal DI range.", - "data_type": "float", - "ui_type": "numeric", - "min": -50.0, - "max": 50.0, - "step": 0.01, - "precision": 2, - "parent_key": "NAPPedalEnabled" - }, { "key": "GMPedalLongitudinal", "label": "Use Pedal For Longitudinal", @@ -2324,16 +1880,16 @@ "ui_type": "toggle" }, { - "key": "VoltSNG", - "label": "Stop-and-Go Hack", - "description": "Force stop-and-go on the 2017 Chevy Volt.", + "key": "RemapCancelToDistance", + "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" }, { - "key": "GMAutoHold", - "label": "Volt Auto Hold", - "description": "Hold the car at a stop on supported non-CC-only Chevy Volts until the gas pedal is pressed.", + "key": "VoltSNG", + "label": "Stop-and-Go Hack", + "description": "Force stop-and-go on the 2017 Chevy Volt.", "data_type": "bool", "ui_type": "toggle" }, @@ -2363,636 +1919,6 @@ } ] }, - { - "name": "Wheel Controls", - "icon": "bi-controller", - "params": [ - { - "key": "RemapCancelToDistance", - "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" - }, - { - "key": "NostalgiaMode", - "label": "Nostalgia Mode", - "description": "Use the left paddle to pause openpilot acceleration and braking while Always On Lateral stays active on supported Hyundai CAN-FD cars.", - "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)", - "description": "Action performed when the \"Distance\" 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": "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)", - "description": "Action performed when the \"Distance\" 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": "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)", - "description": "Action performed when the \"Distance\" 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": "LKASButtonControl", - "label": "LKAS Button", - "description": "Action performed when the \"LKAS\" 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" - }, - { - "value": 9, - "label": "Toggle Always On Lateral" - } - ] - }, - { - "key": "MainCruiseButtonControl", - "label": "CC Main Button", - "description": "Action performed when the cruise control main button is pressed.", - "data_type": "int", - "ui_type": "dropdown", - "options": [ - { - "value": 0, - "label": "No Action" - }, - { - "value": 9, - "label": "Toggle Always On Lateral" - }, - { - "value": 10, - "label": "Adopt Current Speed Limit" - } - ] - }, - { - "key": "ModeButtonControl", - "label": "Mode Button (Short Press)", - "description": "Action performed when the \"Mode\" button is pressed. Hyundai CAN-FD only.", - "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": "LongModeButtonControl", - "label": "Mode Button (Long Press)", - "description": "Action performed when the \"Mode\" button is pressed for more than 0.5 seconds. Hyundai CAN-FD only.", - "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": "VeryLongModeButtonControl", - "label": "Mode Button (Extra Long Press)", - "description": "Action performed when the \"Mode\" button is pressed for more than 2.5 seconds. Hyundai CAN-FD only.", - "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": "StarButtonControl", - "label": "Star Button (Short Press)", - "description": "Action performed when the \"Star\" button is pressed. Hyundai CAN-FD only.", - "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": "LongStarButtonControl", - "label": "Star Button (Long Press)", - "description": "Action performed when the \"Star\" button is pressed for more than 0.5 seconds. Hyundai CAN-FD only.", - "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": "VeryLongStarButtonControl", - "label": "Star Button (Extra Long Press)", - "description": "Action performed when the \"Star\" button is pressed for more than 2.5 seconds. Hyundai CAN-FD only.", - "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" - } - ] - } - ] - }, { "name": "Device & Data", "icon": "bi-hdd", @@ -3015,6 +1941,14 @@ "max": 33.0, "parent_key": "DeviceManagement" }, + { + "key": "DisableWideRoad", + "label": "Disable Wide Road Camera", + "description": "WARNING: Only use this if the wide camera is malfunctioning or for development purposes. This may cause instability!\n\nRequires a reboot to take effect.", + "data_type": "bool", + "ui_type": "toggle", + "parent_key": "DeviceManagement" + }, { "key": "NoLogging", "label": "Disable Logging", @@ -3029,25 +1963,8 @@ "description": "WARNING: This will prevent your drives from being uploaded to comma connect which will impact debugging and official support from comma!\n\nPrevent the device from uploading driving data.", "data_type": "bool", "ui_type": "toggle", - "is_parent_toggle": true, "parent_key": "DeviceManagement" }, - { - "key": "DisableOnroadUploads", - "label": "Disable Onroad Uploads", - "description": "When \"Disable Uploads\" is enabled, allow uploads only while parked (offroad).", - "data_type": "bool", - "ui_type": "toggle", - "parent_key": "NoUploads" - }, - { - "key": "AlwaysAllowUploads", - "label": "Always Allow Uploads", - "description": "Override upload blocks and always keep uploader enabled. Advanced use only.", - "data_type": "bool", - "ui_type": "toggle", - "parent_key": "NoUploads" - }, { "key": "HigherBitrate", "label": "High-Quality Recording", @@ -3056,14 +1973,6 @@ "ui_type": "toggle", "parent_key": "DeviceManagement" }, - { - "key": "GsmMetered", - "label": "Cellular Metered", - "description": "Prevent large uploads on cellular. Turn this off to allow full/unfiltered uploads over mobile data.", - "data_type": "bool", - "ui_type": "toggle", - "parent_key": "DeviceManagement" - }, { "key": "LowVoltageShutdown", "label": "Low-Voltage Cutoff", @@ -3105,8 +2014,6 @@ "description": "The screen brightness while not driving.", "data_type": "int", "ui_type": "numeric", - "min": 1.0, - "max": 101.0, "step": 1.0, "parent_key": "ScreenManagement" }, @@ -3116,8 +2023,6 @@ "description": "The screen brightness while driving.", "data_type": "int", "ui_type": "numeric", - "min": 1.0, - "max": 101.0, "step": 1.0, "parent_key": "ScreenManagement" }, @@ -3208,25 +2113,5 @@ "options_endpoint": "/api/models/installed" } ] - }, - { - "name": "Developer", - "icon": "bi-exclamation-triangle", - "params": [ - { - "key": "DisableWideRoad", - "label": "Disable Wide Road Camera", - "description": "Only enable this if the wide camera is broken or for development!\n\nDisabling the wide camera may degrade driving performance and cause instability.\n\nRequires a reboot to take effect.", - "data_type": "bool", - "ui_type": "toggle" - }, - { - "key": "AllowImpossibleAcceleration", - "label": "Allow Impossible Acceleration", - "description": "WARNING: This suppresses openpilot's excessive longitudinal actuation diagnostic when measured acceleration looks impossible for the requested gas/brake command.\n\nLeave this OFF unless you are intentionally testing edge cases like shifting to neutral and understand that it can hide a real actuation problem.", - "data_type": "bool", - "ui_type": "toggle" - } - ] } -] +] \ No newline at end of file diff --git a/starpilot/system/the_pond/the_pond.py b/starpilot/system/the_pond/the_pond.py index 60874fb3c..1b7c9b3a1 100644 --- a/starpilot/system/the_pond/the_pond.py +++ b/starpilot/system/the_pond/the_pond.py @@ -65,7 +65,7 @@ from openpilot.starpilot.common.model_versions import ( uses_combined_driving_artifacts, uses_split_off_policy_artifacts, ) -from openpilot.starpilot.common.experimental_state import sync_persist_experimental_state +from openpilot.starpilot.common.experimental_state import sync_persist_chill_state, 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, migrate_cancel_button_controls, update_starpilot_toggles @@ -3794,6 +3794,22 @@ def setup(app): "updated": updated, }), 200 + if key in {"ConditionalExperimental", "ConditionalChill"}: + enabled = str_val.strip() in ("1", "true", "True") + params.put_bool(key, enabled) + + updated = {key: enabled} + if enabled: + other_key = "ConditionalChill" if key == "ConditionalExperimental" else "ConditionalExperimental" + params.put_bool(other_key, False) + updated[other_key] = False + + update_starpilot_toggles() + return jsonify({ + "message": f"Parameter '{key}' updated successfully.", + "updated": updated, + }), 200 + if key == "CustomAccelProfile": enabled = str_val.strip() in ("1", "true", "True") params.put_bool(key, enabled) @@ -3825,6 +3841,18 @@ def setup(app): }, }), 200 + if key == "PersistChillState": + enabled = str_val.strip() in ("1", "true", "True") + sync_persist_chill_state(params, params_memory, enabled) + update_starpilot_toggles() + return jsonify({ + "message": f"Parameter '{key}' updated successfully.", + "updated": { + "PersistChillState": enabled, + "PersistedCCStatus": params.get_int("PersistedCCStatus", default=0), + }, + }), 200 + if key == "IsRHD": enabled = str_val.strip() in ("1", "true", "True") params.put_bool("IsRHD", enabled) diff --git a/starpilot/ui/qt/offroad/longitudinal_settings.cc b/starpilot/ui/qt/offroad/longitudinal_settings.cc index 555acef29..4585ec614 100644 --- a/starpilot/ui/qt/offroad/longitudinal_settings.cc +++ b/starpilot/ui/qt/offroad/longitudinal_settings.cc @@ -16,6 +16,7 @@ StarPilotLongitudinalPanel::StarPilotLongitudinalPanel(StarPilotSettingsWindow * StarPilotListWidget *advancedLongitudinalTuneList = new StarPilotListWidget(this); StarPilotListWidget *aggressivePersonalityList = new StarPilotListWidget(this); + StarPilotListWidget *conditionalChillList = new StarPilotListWidget(this); StarPilotListWidget *conditionalExperimentalList = new StarPilotListWidget(this); StarPilotListWidget *curveSpeedList = new StarPilotListWidget(this); StarPilotListWidget *customDrivingPersonalityList = new StarPilotListWidget(this); @@ -36,6 +37,7 @@ StarPilotLongitudinalPanel::StarPilotLongitudinalPanel(StarPilotSettingsWindow * ScrollView *advancedLongitudinalTunePanel = new ScrollView(advancedLongitudinalTuneList, this); ScrollView *aggressivePersonalityPanel = new ScrollView(aggressivePersonalityList, this); + ScrollView *conditionalChillPanel = new ScrollView(conditionalChillList, this); ScrollView *conditionalExperimentalPanel = new ScrollView(conditionalExperimentalList, this); ScrollView *curveSpeedPanel = new ScrollView(curveSpeedList, this); ScrollView *customDrivingPersonalityPanel = new ScrollView(customDrivingPersonalityList, this); @@ -56,6 +58,7 @@ StarPilotLongitudinalPanel::StarPilotLongitudinalPanel(StarPilotSettingsWindow * longitudinalLayout->addWidget(advancedLongitudinalTunePanel); longitudinalLayout->addWidget(aggressivePersonalityPanel); + longitudinalLayout->addWidget(conditionalChillPanel); longitudinalLayout->addWidget(conditionalExperimentalPanel); longitudinalLayout->addWidget(curveSpeedPanel); longitudinalLayout->addWidget(customDrivingPersonalityPanel); @@ -95,6 +98,12 @@ StarPilotLongitudinalPanel::StarPilotLongitudinalPanel(StarPilotSettingsWindow * {"CEModelStopTime", tr("Predicted Stop In"), tr("Switch to \"Experimental Mode\" when openpilot predicts a stop within the set time. This is usually triggered when the model \"sees\" a red light or stop sign ahead.

Disclaimer: openpilot does not explicitly detect traffic lights or stop signs. In \"Experimental Mode\", openpilot makes end-to-end driving decisions from camera input, which means it may stop even when there's no clear reason!"), ""}, {"CESignalSpeed", tr("Turn Signal Below"), tr("Switch to \"Experimental Mode\" when using a turn signal below the set speed to allow the model to choose an appropriate speed for smoother left and right turns."), ""}, {"ShowCEMStatus", tr("Status Widget"), tr("Show which condition triggered \"Experimental Mode\" on the driving screen."), ""}, + {"ConditionalChill", tr("Conditional Chill Mode"), tr("Keep \"Experimental Mode\" on by default, but temporarily switch to \"Chill Mode\" in simple cruising scenes where speed holding is usually better."), "../../starpilot/assets/toggle_icons/icon_conditional.png"}, + {"PersistChillState", tr("Persist Chill State"), tr("Keep your manual Conditional Chill override through reboots until you manually clear it."), ""}, + {"CCMSpeed", tr("Above"), tr("Switch to \"Chill Mode\" on open roads above this speed when no lead is detected and the car is still below the set speed."), ""}, + {"CCMLead", tr("Stable Lead Ahead"), tr("Switch to \"Chill Mode\" when following a steady, well-tracked lead vehicle at cruising speeds."), ""}, + {"CCMSetSpeedMargin", tr("Set Speed Margin"), tr("How far below the set speed the car must be before open-road Conditional Chill can engage."), ""}, + {"ShowCCMStatus", tr("Status Widget"), tr("Show which condition triggered \"Chill Mode\" on the driving screen."), ""}, {"CurveSpeedController", tr("Curve Speed Controller"), tr("Automatically slow down for upcoming curves using data learned from your driving style, adapting to curves as you would."), "../../starpilot/assets/toggle_icons/icon_speed_map.png"}, {"CalibratedLateralAcceleration", tr("Calibrated Lateral Acceleration"), tr("The learned lateral acceleration from collected driving data. This sets how fast openpilot will take curves. Higher values allow faster cornering; lower values slow the vehicle for gentler turns."), ""}, @@ -253,11 +262,22 @@ StarPilotLongitudinalPanel::StarPilotLongitudinalPanel(StarPilotSettingsWindow * longitudinalLayout->setCurrentWidget(conditionalExperimentalPanel); }); longitudinalToggle = conditionalExperimentalToggle; + } else if (param == "ConditionalChill") { + StarPilotManageControl *conditionalChillToggle = new StarPilotManageControl(param, title, desc, icon); + QObject::connect(conditionalChillToggle, &StarPilotManageControl::manageButtonClicked, [longitudinalLayout, conditionalChillPanel]() { + longitudinalLayout->setCurrentWidget(conditionalChillPanel); + }); + longitudinalToggle = conditionalChillToggle; } else if (param == "CESpeed") { StarPilotParamValueControl *CESpeed = new StarPilotParamValueControl(param, title, desc, icon, 0, 99, tr(" mph"), std::map(), 1, true, 175); StarPilotParamValueControl *CESpeedLead = new StarPilotParamValueControl("CESpeedLead", tr("With Lead"), tr("Switch to \"Experimental Mode\" when driving below this speed with a lead to help openpilot handle low-speed situations more smoothly."), icon, 0, 99, tr(" mph"), std::map(), 1, true, 175); StarPilotDualParamValueControl *conditionalSpeeds = new StarPilotDualParamValueControl(CESpeed, CESpeedLead); longitudinalToggle = reinterpret_cast(conditionalSpeeds); + } else if (param == "CCMSpeed") { + StarPilotParamValueControl *CCMSpeed = new StarPilotParamValueControl(param, title, desc, icon, 0, 99, tr(" mph"), std::map(), 1, true, 175); + StarPilotParamValueControl *CCMSpeedLead = new StarPilotParamValueControl("CCMSpeedLead", tr("With Lead"), tr("Switch to \"Chill Mode\" when a stable lead is being followed above this speed."), icon, 0, 99, tr(" mph"), std::map(), 1, true, 175); + StarPilotDualParamValueControl *conditionalSpeeds = new StarPilotDualParamValueControl(CCMSpeed, CCMSpeedLead); + longitudinalToggle = reinterpret_cast(conditionalSpeeds); } else if (param == "CECurves") { std::vector curveToggles{"CECurvesLead"}; std::vector curveToggleNames{tr("With Lead")}; @@ -266,6 +286,8 @@ StarPilotLongitudinalPanel::StarPilotLongitudinalPanel(StarPilotSettingsWindow * std::vector leadToggles{"CESlowerLead", "CEStoppedLead"}; std::vector leadToggleNames{tr("Slower Lead"), tr("Stopped Lead")}; longitudinalToggle = new StarPilotButtonToggleControl(param, title, desc, icon, leadToggles, leadToggleNames); + } else if (param == "CCMSetSpeedMargin") { + longitudinalToggle = new StarPilotParamValueControl(param, title, desc, icon, 0, 15, tr(" mph"), std::map(), 1, true, 175); } else if (param == "CEModelStopTime") { std::map stopTimeLabels; for (int i = 0; i <= 10; ++i) { @@ -586,6 +608,8 @@ StarPilotLongitudinalPanel::StarPilotLongitudinalPanel(StarPilotSettingsWindow * advancedLongitudinalTuneList->addItem(longitudinalToggle); } else if (aggressivePersonalityKeys.contains(param)) { aggressivePersonalityList->addItem(longitudinalToggle); + } else if (conditionalChillKeys.contains(param)) { + conditionalChillList->addItem(longitudinalToggle); } else if (conditionalExperimentalKeys.contains(param)) { conditionalExperimentalList->addItem(longitudinalToggle); } else if (curveSpeedKeys.contains(param)) { @@ -658,6 +682,20 @@ StarPilotLongitudinalPanel::StarPilotLongitudinalPanel(StarPilotSettingsWindow * } updateToggles(); }); + QObject::connect(static_cast(toggles["ConditionalExperimental"]), &ToggleControl::toggleFlipped, this, [this]() { + if (params.getBool("ConditionalExperimental")) { + params.putBool("ConditionalChill", false); + static_cast(toggles["ConditionalChill"])->refresh(); + } + updateToggles(); + }); + QObject::connect(static_cast(toggles["ConditionalChill"]), &ToggleControl::toggleFlipped, this, [this]() { + if (params.getBool("ConditionalChill")) { + params.putBool("ConditionalExperimental", false); + static_cast(toggles["ConditionalExperimental"])->refresh(); + } + updateToggles(); + }); StarPilotParamValueControl *trafficFollowToggle = static_cast(toggles["TrafficFollow"]); StarPilotParamValueControl *trafficAccelerationToggle = static_cast(toggles["TrafficJerkAcceleration"]); @@ -831,6 +869,9 @@ void StarPilotLongitudinalPanel::updateMetric(bool metric, bool bootRun) { params.putIntNonBlocking("IncreasedStoppedDistanceRainStorm", params.getInt("IncreasedStoppedDistanceRainStorm") * distanceConversion); params.putIntNonBlocking("IncreasedStoppedDistanceSnow", params.getInt("IncreasedStoppedDistanceSnow") * distanceConversion); + params.putIntNonBlocking("CCMSpeed", params.getInt("CCMSpeed") * speedConversion); + params.putIntNonBlocking("CCMSpeedLead", params.getInt("CCMSpeedLead") * speedConversion); + params.putIntNonBlocking("CCMSetSpeedMargin", params.getInt("CCMSetSpeedMargin") * speedConversion); params.putIntNonBlocking("CESignalSpeed", params.getInt("CESignalSpeed") * speedConversion); params.putIntNonBlocking("CESpeed", params.getInt("CESpeed") * speedConversion); params.putIntNonBlocking("CESpeedLead", params.getInt("CESpeedLead") * speedConversion); @@ -881,7 +922,9 @@ void StarPilotLongitudinalPanel::updateMetric(bool metric, bool bootRun) { labelsInitialized = true; } + StarPilotDualParamValueControl *ccmSpeedToggle = reinterpret_cast(toggles["CCMSpeed"]); StarPilotDualParamValueControl *ceSpeedToggle = reinterpret_cast(toggles["CESpeed"]); + StarPilotParamValueControl *ccmSetSpeedMarginToggle = static_cast(toggles["CCMSetSpeedMargin"]); StarPilotParamValueButtonControl *ceSignal = static_cast(toggles["CESignalSpeed"]); StarPilotParamValueControl *customCruiseToggle = static_cast(toggles["CustomCruise"]); StarPilotParamValueControl *customCruiseLongToggle = static_cast(toggles["CustomCruiseLong"]); @@ -922,6 +965,8 @@ void StarPilotLongitudinalPanel::updateMetric(bool metric, bool bootRun) { increasedStoppedDistanceRainStormToggle->updateControl(0, 3, metricDistanceLabels); increasedStoppedDistanceSnowToggle->updateControl(0, 3, metricDistanceLabels); + ccmSpeedToggle->updateControl(0, 150, metricSpeedLabels); + ccmSetSpeedMarginToggle->updateControl(0, 25, metricSpeedLabels); ceSignal->updateControl(0, 150, metricSpeedLabels); ceSpeedToggle->updateControl(0, 150, metricSpeedLabels); customCruiseToggle->updateControl(1, 150, metricSpeedLabels); @@ -957,6 +1002,8 @@ void StarPilotLongitudinalPanel::updateMetric(bool metric, bool bootRun) { increasedStoppedDistanceRainStormToggle->updateControl(0, 10, imperialDistanceLabels); increasedStoppedDistanceSnowToggle->updateControl(0, 10, imperialDistanceLabels); + ccmSpeedToggle->updateControl(0, 99, imperialSpeedLabels); + ccmSetSpeedMarginToggle->updateControl(0, 15, imperialSpeedLabels); ceSignal->updateControl(0, 99, imperialSpeedLabels); ceSpeedToggle->updateControl(0, 99, imperialSpeedLabels); customCruiseToggle->updateControl(1, 99, imperialSpeedLabels); @@ -1041,6 +1088,8 @@ void StarPilotLongitudinalPanel::updateToggles() { toggles["AdvancedLongitudinalTune"]->setVisible(true); } else if (aggressivePersonalityKeys.contains(key)) { toggles["AggressivePersonalityProfile"]->setVisible(true); + } else if (conditionalChillKeys.contains(key)) { + toggles["ConditionalChill"]->setVisible(true); } else if (conditionalExperimentalKeys.contains(key)) { toggles["ConditionalExperimental"]->setVisible(true); } else if (curveSpeedKeys.contains(key)) { diff --git a/starpilot/ui/qt/offroad/longitudinal_settings.h b/starpilot/ui/qt/offroad/longitudinal_settings.h index 7a12c5c5a..5719b5a1e 100644 --- a/starpilot/ui/qt/offroad/longitudinal_settings.h +++ b/starpilot/ui/qt/offroad/longitudinal_settings.h @@ -30,6 +30,7 @@ private: QSet advancedLongitudinalTuneKeys = {"EVTuning", "TruckTuning", "LongitudinalActuatorDelay", "MaxDesiredAcceleration", "StartAccel", "StopAccel", "StoppingDecelRate", "VEgoStarting", "VEgoStopping"}; QSet aggressivePersonalityKeys = {"AggressiveFollow", "AggressiveFollowHigh", "AggressiveJerkAcceleration", "AggressiveJerkDeceleration", "AggressiveJerkDanger", "AggressiveJerkSpeed", "AggressiveJerkSpeedDecrease", "ResetAggressivePersonality"}; + QSet conditionalChillKeys = {"PersistChillState", "CCMSpeed", "CCMSpeedLead", "CCMLead", "CCMSetSpeedMargin", "ShowCCMStatus"}; QSet conditionalExperimentalKeys = {"PersistExperimentalState", "CESpeed", "CESpeedLead", "CECurves", "CELead", "CEModelStopTime", "CESignalSpeed", "CEStopLights", "ShowCEMStatus"}; QSet curveSpeedKeys = {"CalibratedLateralAcceleration", "CalibrationProgress", "ResetCurveData", "ShowCSCStatus"}; QSet customDrivingPersonalityKeys = {"AggressivePersonalityProfile", "RelaxedPersonalityProfile", "StandardPersonalityProfile", "TrafficPersonalityProfile"}; diff --git a/starpilot/ui/qt/onroad/starpilot_annotated_camera.cc b/starpilot/ui/qt/onroad/starpilot_annotated_camera.cc index af0f1a2db..a8578bdc7 100644 --- a/starpilot/ui/qt/onroad/starpilot_annotated_camera.cc +++ b/starpilot/ui/qt/onroad/starpilot_annotated_camera.cc @@ -468,9 +468,14 @@ void StarPilotAnnotatedCameraWidget::paintCEMStatus(QPainter &p) { cemStatusPosition.setY(dmIconPosition.y() - widget_size / 2); QRect cemWidget(cemStatusPosition, QSize(widget_size, widget_size)); + const bool conditionalExperimentalMode = starpilot_toggles.value("conditional_experimental_mode").toBool(); + const bool conditionalChillMode = starpilot_toggles.value("conditional_chill_mode").toBool(); + const bool manualOverride = + (conditionalExperimentalMode && starpilot_scene.conditional_status == 1) || + (conditionalChillMode && (starpilot_scene.conditional_status == 1 || starpilot_scene.conditional_status == 2)); p.setBrush(blackColor(166)); - if (starpilot_scene.conditional_status == 1) { + if (manualOverride) { p.setPen(QPen(QColor(bg_colors[STATUS_CEM_DISABLED]), 10)); } else if (experimentalMode) { p.setPen(QPen(QColor(bg_colors[STATUS_EXPERIMENTAL_MODE_ENABLED]), 10)); @@ -480,7 +485,19 @@ void StarPilotAnnotatedCameraWidget::paintCEMStatus(QPainter &p) { p.drawRoundedRect(cemWidget, 24, 24); QSharedPointer icon = chillModeIcon; - if (experimentalMode) { + if (conditionalChillMode && !conditionalExperimentalMode) { + if (starpilot_scene.conditional_status == 1) { + icon = experimentalModeIcon; + } else if (starpilot_scene.conditional_status == 2) { + icon = chillModeIcon; + } else if (starpilot_scene.conditional_status == 4) { + icon = cemLeadIcon; + } else if (starpilot_scene.conditional_status == 6) { + icon = cemSpeedIcon; + } else { + icon = experimentalMode ? experimentalModeIcon : chillModeIcon; + } + } else if (experimentalMode) { if (starpilot_scene.conditional_status == 1) { icon = chillModeIcon; } else if (starpilot_scene.conditional_status == 2) { @@ -786,12 +803,17 @@ void StarPilotAnnotatedCameraWidget::paintPathEdges(QPainter &p, int height) { gradient.setColorAt(0.5f, QColor(baseColor.red(), baseColor.green(), baseColor.blue(), 255.0f * 0.35f)); gradient.setColorAt(1.0f, QColor(baseColor.red(), baseColor.green(), baseColor.blue(), 255.0f * 0.0f)); }; + const bool conditionalExperimentalMode = starpilot_toggles.value("conditional_experimental_mode").toBool(); + const bool conditionalChillMode = starpilot_toggles.value("conditional_chill_mode").toBool(); + const bool highlightOverride = + (conditionalExperimentalMode && starpilot_scene.conditional_status == 1) || + (conditionalChillMode && (starpilot_scene.conditional_status == 1 || starpilot_scene.conditional_status == 2)); if (starpilot_scene.switchback_mode_enabled) { setPathEdgeColors(bg_colors[STATUS_SWITCHBACK_MODE_ENABLED]); } else if (starpilot_scene.always_on_lateral_active) { setPathEdgeColors(bg_colors[STATUS_ALWAYS_ON_LATERAL_ACTIVE]); - } else if (starpilot_scene.conditional_status == 1) { + } else if (highlightOverride) { setPathEdgeColors(bg_colors[STATUS_CEM_DISABLED]); } else if (experimentalMode) { setPathEdgeColors(bg_colors[STATUS_EXPERIMENTAL_MODE_ENABLED]); diff --git a/starpilot/ui/qt/widgets/starpilot_controls.cc b/starpilot/ui/qt/widgets/starpilot_controls.cc index 21ff31b13..01b340b72 100644 --- a/starpilot/ui/qt/widgets/starpilot_controls.cc +++ b/starpilot/ui/qt/widgets/starpilot_controls.cc @@ -128,7 +128,7 @@ void loadImage(const QString &basePath, QPixmap &pixmap, QSharedPointer void openDescriptions(bool forceOpenDescriptions, std::map toggles) { if (forceOpenDescriptions) { for (auto &[key, toggle] : toggles) { - if (key != "CESpeed") { + if (key != "CESpeed" && key != "CCMSpeed") { toggle->showDescription(); } } diff --git a/starpilot/ui/starpilot_ui.cc b/starpilot/ui/starpilot_ui.cc index c7ef16a06..643c8ed0c 100644 --- a/starpilot/ui/starpilot_ui.cc +++ b/starpilot/ui/starpilot_ui.cc @@ -114,6 +114,11 @@ StarPilotUIState *starpilotUIState() { void StarPilotUIState::update() { update_state(this); - starpilot_scene.conditional_status = starpilot_scene.enabled ? params_memory.getInt("CEStatus") : 0; + if (starpilot_scene.enabled && starpilot_scene.starpilot_toggles.value("conditional_chill_mode").toBool() && + !starpilot_scene.starpilot_toggles.value("conditional_experimental_mode").toBool()) { + starpilot_scene.conditional_status = params_memory.getInt("CCStatus"); + } else { + starpilot_scene.conditional_status = starpilot_scene.enabled ? params_memory.getInt("CEStatus") : 0; + } starpilot_scene.driver_camera_timer = starpilot_scene.reverse && starpilot_scene.starpilot_toggles.value("driver_camera_in_reverse").toBool() ? starpilot_scene.driver_camera_timer + 1 : 0; } diff --git a/tools/StarPilot/derive_feasible_params.py b/tools/StarPilot/derive_feasible_params.py index 9c1fe20b2..89c71f2a7 100755 --- a/tools/StarPilot/derive_feasible_params.py +++ b/tools/StarPilot/derive_feasible_params.py @@ -17,7 +17,7 @@ def get_repo_root() -> str: # Constants REPO_ROOT = get_repo_root() -PARAMS_CC_PATH = os.path.join(REPO_ROOT, 'common/params.cc') +PARAMS_KEYS_PATH = os.path.join(REPO_ROOT, 'common/params_keys.h') UI_DIRECTORIES = [ os.path.join(REPO_ROOT, 'selfdrive/ui'), os.path.join(REPO_ROOT, 'starpilot/ui') @@ -27,6 +27,7 @@ UI_DIRECTORIES = [ # rather than user-toggled configurations. KNOWN_READ_ONLY = { "ApiCache_Device", "ApiCache_DriveStats", "ApiCache_NavDestinations", + "CCStatus", "CEStatus", "CarMake", "CarModel", "CarModelName", "CarParamsPersistent", "CarVin", "ClusterOffset", "Compass", "DeveloperSidebarMetric1", "DeveloperSidebarMetric2", "DeveloperSidebarMetric3", "DeveloperSidebarMetric4", "DeveloperSidebarMetric5", @@ -36,25 +37,26 @@ KNOWN_READ_ONLY = { "GitRemote", "GithubSshKeys", "GithubUsername", "HardwareSerial", "IMEI", "InstallDate", "IsRhdDetected", "KonikMinutes", "LastGPSPosition", "LastMapsUpdate", "LastUpdateTime", "ModelDrivesAndScores", "ModelReleasedDates", - "ModelVersions", "PrimeType", "TermsVersion", "TrainingVersion", "Version", + "ModelVersions", "PersistedCCStatus", "PersistedCEStatus", "PrimeType", + "TermsVersion", "TrainingVersion", "Version", "openpilotMinutes", "CompletedTrainingVersion" } def extract_registered_keys(params_path: str) -> set: - """Extracts all legally registered parameter keys from common/params.cc""" + """Extracts all legally registered parameter keys from common/params_keys.h""" registered_keys = set() try: with open(params_path, 'r', encoding='utf-8') as f: content = f.read() - # Isolate the keys `unordered_map` block - keys_block_match = re.search(r'unordered_map keys = \{(.*?)\};', content, re.DOTALL) + # Isolate the registered keys block. + keys_block_match = re.search(r'keys\s*=\s*\{(.*?)\n\};', content, re.DOTALL) if not keys_block_match: - print("Error: Could not locate 'keys' map in params.cc") + print("Error: Could not locate 'keys' map in params_keys.h") return registered_keys - # Extract {"KeyName", FLAG} entries - for match in re.finditer(r'\{"([A-Za-z0-9_]+)",\s*([^}]+)\}', keys_block_match.group(1)): + # Extract {"KeyName", {FLAGS...}} entries. + for match in re.finditer(r'\{"([A-Za-z0-9_]+)",\s*\{([^}]*)\}\s*\}', keys_block_match.group(1)): key, flag = match.group(1), match.group(2) # Remove keys that are strictly internal ephemeral states if 'CLEAR_ON_MANAGER_START' not in flag: @@ -90,7 +92,7 @@ def main(): print(f"Starting parameter derivation inside {REPO_ROOT}...") # 1. Fetch - registered_keys = extract_registered_keys(PARAMS_CC_PATH) + registered_keys = extract_registered_keys(PARAMS_KEYS_PATH) ui_strings = extract_ui_string_literals(UI_DIRECTORIES) # 2. Intersect diff --git a/tools/StarPilot/feasibleparams.txt b/tools/StarPilot/feasibleparams.txt index 077769551..cdbef6660 100644 --- a/tools/StarPilot/feasibleparams.txt +++ b/tools/StarPilot/feasibleparams.txt @@ -1,14 +1,13 @@ Dynamically Derived Feasible Param Candidates (The Golden List) =============================================================== -Total globally registered C++ keys: 417 -Total explicit UI string references: 369 -Total Editable/Toggleable targets: 331 +Total globally registered C++ keys: 507 +Total explicit UI string references: 415 +Total Editable/Toggleable targets: 378 -AMapKey1 -AMapKey2 AccelerationPath AccelerationProfile +AdbEnabled AdjacentLeadsUI AdjacentPath AdjacentPathMetrics @@ -24,55 +23,52 @@ AggressiveJerkSpeed AggressiveJerkSpeedDecrease AggressivePersonalityProfile AlertVolumeControl +AlphaLongitudinalEnabled +AlwaysOnDM AlwaysOnLateral -AlwaysOnLateralLKAS -AlwaysOnLateralMain AutomaticUpdates AutomaticallyDownloadModels AvailableModelNames AvailableModelSeries AvailableModels -BigMap +BelowSteerSpeedVolume BlacklistedModels BlindSpotMetrics BlindSpotPath BootLogo BorderMetrics -BorderWidth +CCMLead +CCMSetSpeedMargin +CCMSpeed +CCMSpeedLead CECurves CECurvesLead CELead CEModelStopTime -CENavigation -CENavigationIntersections -CENavigationLead -CENavigationTurns CESignalLaneDetection CESignalSpeed CESlowerLead CESpeed CESpeedLead -CEStatus +CEStopLights CEStoppedLead CalibratedLateralAcceleration CalibrationParams CalibrationProgress CameraView +CancelButtonControl +CoastUpToLeads +ColorScheme CommunityFavorites +ConditionalChill ConditionalExperimental -CurvatureData CurveSpeedController CustomAlerts -CustomColors CustomCruise CustomCruiseLong -CustomDistanceIcons -CustomIcons CustomPersonalities -CustomSignals -CustomSounds +CustomThemes CustomUI -CancelButtonControl DebugMode DecelerationProfile DeveloperMetrics @@ -83,12 +79,12 @@ DeviceManagement DeviceShutdown DisableOnroadUploads DisableOpenpilotLongitudinal +DisableWideRoad DiscordUsername DisengageOnAccelerator DisengageVolume DistanceButtonControl -DoToggleReset -DoToggleResetStock +DistanceIconPack DownloadableBootLogos DownloadableColors DownloadableDistanceIcons @@ -97,98 +93,118 @@ DownloadableSignals DownloadableSounds DownloadableWheels DriverCamera +DrivingModel +DrivingModelName +DrivingModelVersion DynamicPathWidth DynamicPedalsOnUI EVTuning EngageVolume -ExperimentalLongitudinalEnabled ExperimentalMode ExperimentalModeConfirmed FPSCounter Fahrenheit -FavoriteDestinations ForceAutoTune ForceAutoTuneOff ForceFingerprint -ForceMPHDashboard +ForceStandstill +ForceStopDistanceOffset ForceStops ForceTorqueController -StarPilotStats FrogsGoMoosTweak -FullMap +GMDashSpoofOffsets GMPedalLongitudinal GoatScream +GoatScreamCriticalAlerts GreenLightAlert GsmApn GsmMetered GsmRoaming HasAcceptedTerms HideAlerts +HideChangingLanesBanner +HideDMIcon +HideDistanceProfileBanner HideLeadMarker -HideMap -HideMapIcon HideMaxSpeed HideSpeed HideSpeedLimit +HideTurningBanner HigherBitrate HolidayThemes HumanAcceleration +HumanLaneChanges +IconPack +IncreaseFollowingLowVisibility +IncreaseFollowingRain +IncreaseFollowingRainStorm +IncreaseFollowingSnow IncreaseThermalLimits IncreasedStoppedDistance -IsDriverViewEnabled +IncreasedStoppedDistanceLowVisibility +IncreasedStoppedDistanceRain +IncreasedStoppedDistanceRainStorm +IncreasedStoppedDistanceSnow IsLdwEnabled IsMetric +IsRHD +IsRHDOverride +KonikDongleId LKASButtonControl +LaneChangeSmoothing LaneChangeTime LaneChanges LaneDetectionWidth -LaneLinesColor LaneLinesWidth LanguageSetting +LateralResumeDelay LateralTune LeadDepartingAlert LeadDetectionThreshold -LeadIndicator LeadInfo LiveDelay LiveParameters +LiveParametersV2 LiveTorqueParameters LockDoors LockDoorsTimer LongCancelButtonControl LongDistanceButtonControl +LongModeButtonControl LongPitch +LongStarButtonControl LongitudinalActuatorDelay LongitudinalActuatorDelayStock LongitudinalPersonality LongitudinalTune LoudBlindspotAlert LowVoltageShutdown +MainCruiseButtonControl MapAcceleration MapDeceleration MapGears -MapStyle MapboxPublicKey MapboxSecretKey MapsSelected MaxDesiredAcceleration MinimumLaneChangeSpeed +ModeButtonControl Model ModelRandomizer +ModelSortMode ModelUI ModelVersion NNFF NNFFLite -NavPastDestinations -NavSettingLeftSide -NavSettingTime24h +NavDesiresAllowed +NavLongitudinalAllowed NavigationUI -NewLongAPI NoLogging NoUploads +NostalgiaMode NudgelessLaneChange NumericalTemp -OSMDownloadLocations +Offroad_ExcessiveActuation Offset1 Offset2 Offset3 @@ -199,30 +215,38 @@ Offset7 OneLaneChange OnroadDistanceButton OpenpilotEnabledToggle -OverpassRequests -PathColor -PathEdgesColor PathEdgeWidth PathWidth PauseAOLOnBrake PauseLateralOnSignal PauseLateralSpeed PedalsOnUI -PersonalizeOpenpilot +PersistChillState +PersistExperimentalState PreferredSchedule PromptDistractedVolume PromptVolume QOLLateral QOLLongitudinal QOLVisuals +RadarTakeoffs RadarTracksUI RainbowPath RandomEvents RandomThemes +RandomThemesHolidays +RecordAudio RecordFront RecordFrontLock RecoveryPower -RedPanda +ReduceAccelerationLowVisibility +ReduceAccelerationRain +ReduceAccelerationRainStorm +ReduceAccelerationSnow +ReduceLateralAccelerationLowVisibility +ReduceLateralAccelerationRain +ReduceLateralAccelerationRainStorm +ReduceLateralAccelerationSnow RefuseVolume RelaxedFollow RelaxedFollowHigh @@ -232,11 +256,14 @@ RelaxedJerkDeceleration RelaxedJerkSpeed RelaxedJerkSpeedDecrease RelaxedPersonalityProfile +RemapCancelToDistance RemoteStartBootsComma ReverseCruise RoadEdgesWidth RoadNameUI RotatingWheel +SLCAbbreviatedSources +SLCActiveSourcesOnly SLCConfirmation SLCConfirmationHigher SLCConfirmationLower @@ -245,23 +272,26 @@ SLCLookaheadHigher SLCLookaheadLower SLCMapboxFiller SLCOverride +SLCPriority +SLCPriority2 SNGHack +SafeMode ScreenBrightness ScreenBrightnessOnroad ScreenManagement ScreenRecorder ScreenTimeout ScreenTimeoutOnroad -SearchInput SetSpeedLimit SetSpeedOffset +ShowAllToggles +ShowCCMStatus ShowCEMStatus ShowCPU ShowCSCStatus ShowGPU ShowIP ShowMemoryUsage -ShowModeStatusBanner ShowSLCOffset ShowSpeedLimits ShowSteering @@ -269,13 +299,18 @@ ShowStoppingPoint ShowStoppingPointMetrics ShowStorageLeft ShowStorageUsed -ShownToggleDescriptions -Sidebar +SidebarMetrics +SidebarOpen +SignalAnimation SignalMetrics +SimpleMode +SoundPack SpeedLimitChangedAlert SpeedLimitController SpeedLimitFiller SpeedLimitSources +SpeedLimits +SpeedLimitsFiltered SshEnabled StandardFollow StandardFollowHigh @@ -286,6 +321,7 @@ StandardJerkSpeed StandardJerkSpeedDecrease StandardPersonalityProfile StandbyMode +StarButtonControl StartAccel StartAccelStock StartupMessageBottom @@ -299,8 +335,6 @@ SteerKP SteerKPStock SteerLatAccel SteerLatAccelStock -SteerOffset -SteerOffsetStock SteerRatio SteerRatioStock StopAccel @@ -309,6 +343,9 @@ StopDistance StoppedTimer StoppingDecelRate StoppingDecelRateStock +SubaruSNG +SwitchbackModeCooldown +SwitchbackModeEnabled TacoTune TetheringEnabled ToyotaDoors @@ -319,15 +356,14 @@ TrafficJerkDeceleration TrafficJerkSpeed TrafficJerkSpeedDecrease TrafficPersonalityProfile -TrailerLoad TruckTuning TuningLevel TuningLevelConfirmed TurnDesires -UnlimitedLength UnlockDoors UpdaterAvailableBranches UseKonikServer +UsePrebuilt UseSI UseVienna UserFavorites @@ -337,8 +373,13 @@ VEgoStopping VEgoStoppingStock VeryLongCancelButtonControl VeryLongDistanceButtonControl +VeryLongModeButtonControl +VeryLongStarButtonControl +VisionSpeedLimitDetection VoltSNG WarningImmediateVolume WarningSoftVolume +WeatherPresets +WheelControls WheelIcon WheelSpeed diff --git a/tools/StarPilot/generate_pond_layout.py b/tools/StarPilot/generate_pond_layout.py index ed15e7c71..2b1b5a0c6 100755 --- a/tools/StarPilot/generate_pond_layout.py +++ b/tools/StarPilot/generate_pond_layout.py @@ -116,6 +116,7 @@ PARENT_KEYS_MAPPING = { "longitudinal_settings.cc": { "advancedLongitudinalTuneKeys": "AdvancedLongitudinalTune", "aggressivePersonalityKeys": "AggressivePersonalityProfile", + "conditionalChillKeys": "ConditionalChill", "conditionalExperimentalKeys": "ConditionalExperimental", "curveSpeedKeys": "CurveSpeedController", "customDrivingPersonalityKeys": "CustomPersonalities", @@ -453,9 +454,9 @@ def parse_cpp_file(filename): if step_match: step = step_match.group(1) - # CESpeed is rendered in Qt with a dual numeric control (CESpeed + CESpeedLead), + # CESpeed/CCMSpeed are rendered in Qt with dual numeric controls, # so the generic assignment matcher cannot infer it reliably. - if key == "CESpeed": + if key in {"CESpeed", "CCMSpeed"}: widget_type = "numeric" data_type = "int" min_val, max_val, step = "0", "99", "1" @@ -519,7 +520,7 @@ def parse_cpp_file(filename): }, ]) - # Mirror CESpeed's dual slider (with-lead variant) from Qt. + # Mirror CESpeed/CCMSpeed's dual sliders (with-lead variants) from Qt. if key == "CESpeed": items.append({ "key": "CESpeedLead", @@ -532,6 +533,18 @@ def parse_cpp_file(filename): "step": 1.0, "parent_key": "ConditionalExperimental", }) + elif key == "CCMSpeed": + items.append({ + "key": "CCMSpeedLead", + "label": "Above (With Lead)", + "description": "Switch to \"Chill Mode\" when following a stable lead above this speed.", + "data_type": "int", + "ui_type": "numeric", + "min": 0.0, + "max": 99.0, + "step": 1.0, + "parent_key": "ConditionalChill", + }) return items