diff --git a/common/params_keys.h b/common/params_keys.h index 575dcc2adc..97f23d5108 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -164,6 +164,7 @@ inline static std::unordered_map keys = { {"BackupManager_RestoreVersion", PERSISTENT}, // sunnypilot car specific params + {"HyundaiLongitudinalTuning", PERSISTENT}, {"HyundaiRadarTracks", PERSISTENT}, {"HyundaiRadarTracksConfirmed", PERSISTENT}, {"HyundaiRadarTracksPersistent", PERSISTENT}, diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc index 693a185cde..5f0010ac33 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc @@ -8,7 +8,50 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h" HyundaiSettings::HyundaiSettings(QWidget *parent) : BrandSettingsInterface(parent) { + std::vector tuning_texts{ tr("Off"), tr("Dynamic"), tr("Predictive") }; + longitudinalTuningToggle = new ButtonParamControl( + "HyundaiLongitudinalTuning", + tr("Custom Longitudinal Tuning"), + "", + "", + tuning_texts, + 500 + ); + QObject::connect(longitudinalTuningToggle, &ButtonParamControlSP::buttonToggled, this, &HyundaiSettings::updateSettings); + list->addItem(longitudinalTuningToggle); + longitudinalTuningToggle->showDescription(); } void HyundaiSettings::updateSettings() { + auto longitudinal_tuning_param = std::atoi(params.get("HyundaiLongitudinalTuning").c_str()); + + auto cp_bytes = params.get("CarParamsPersistent"); + if (!cp_bytes.empty()) { + AlignedBuffer aligned_buf; + capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size())); + cereal::CarParams::Reader CP = cmsg.getRoot(); + + has_longitudinal_control = hasLongitudinalControl(CP); + } else { + has_longitudinal_control = false; + } + + LongitudinalTuningOption longitudinal_tuning_option; + if (longitudinal_tuning_param == int(LongitudinalTuningOption::PREDICTIVE)) { + longitudinal_tuning_option = LongitudinalTuningOption::PREDICTIVE; + } else if (longitudinal_tuning_param == int(LongitudinalTuningOption::DYNAMIC)) { + longitudinal_tuning_option = LongitudinalTuningOption::DYNAMIC; + } else { + longitudinal_tuning_option = LongitudinalTuningOption::OFF; + } + + bool longitudinal_tuning_disabled = !offroad || !has_longitudinal_control; + QString longitudinal_tuning_description = longitudinalTuningDescription(longitudinal_tuning_option); + if (longitudinal_tuning_disabled) { + longitudinal_tuning_description = toggleDisableMsg(offroad, has_longitudinal_control); + } + + longitudinalTuningToggle->setEnabled(!longitudinal_tuning_disabled); + longitudinalTuningToggle->setDescription(longitudinal_tuning_description); + longitudinalTuningToggle->showDescription(); } diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h index edf495844d..c94d40cfde 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h @@ -14,10 +14,52 @@ #include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h" #include "selfdrive/ui/sunnypilot/qt/widgets/controls.h" +enum class LongitudinalTuningOption { + OFF, + DYNAMIC, + PREDICTIVE, +}; + class HyundaiSettings : public BrandSettingsInterface { Q_OBJECT public: explicit HyundaiSettings(QWidget *parent = nullptr); void updateSettings() override; + +private: + bool has_longitudinal_control = false; + ButtonParamControl *longitudinalTuningToggle = nullptr; + + static QString toggleDisableMsg(bool _offroad, bool _has_longitudinal_control) { + if (!_has_longitudinal_control) { + return tr("This feature can only be used with openpilot longitudinal control enabled."); + } + + if (!_offroad) { + return tr("Enable \"Always Offroad\" in Device panel, or turn vehicle off to select an option."); + } + + return QString(); + } + + static QString longitudinalTuningDescription(LongitudinalTuningOption option = LongitudinalTuningOption::OFF) { + QString off_str = tr("Off: Uses default tuning"); + QString dynamic_str = tr("Dynamic: Adjusts acceleration limits based on current speed"); + QString predictive_str = tr("Predictive: Uses future trajectory data to anticipate needed adjustments"); + + if (option == LongitudinalTuningOption::PREDICTIVE) { + predictive_str = "" + predictive_str + ""; + } else if (option == LongitudinalTuningOption::DYNAMIC) { + dynamic_str = "" + dynamic_str + ""; + } else { + off_str = "" + off_str + ""; + } + + return QString("%1

%2
%3
%4
") + .arg(tr("Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control.")) + .arg(off_str) + .arg(dynamic_str) + .arg(predictive_str); + } }; diff --git a/sunnypilot/selfdrive/car/interfaces.py b/sunnypilot/selfdrive/car/interfaces.py index 2c27e960e9..f2019f5ed7 100644 --- a/sunnypilot/selfdrive/car/interfaces.py +++ b/sunnypilot/selfdrive/car/interfaces.py @@ -11,6 +11,7 @@ from opendbc.car.car_helpers import can_fingerprint from opendbc.car.interfaces import CarInterfaceBase from opendbc.car.hyundai.radar_interface import RADAR_START_ADDR from opendbc.car.hyundai.values import HyundaiFlags, DBC as HYUNDAI_DBC +from opendbc.sunnypilot.car.hyundai.longitudinal.helpers import LongitudinalTuningType from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog @@ -25,6 +26,21 @@ def log_fingerprint(CP: structs.CarParams) -> None: else: sentry.capture_fingerprint(CP.carFingerprint, CP.brand) +def _initialize_custom_longitudinal_tuning(CI: CarInterfaceBase, CP: structs.CarParams, CP_SP: structs.CarParamsSP, + params: Params = None) -> None: + if params is None: + params = Params() + + # Hyundai Custom Longitudinal Tuning + if CP.brand == 'hyundai': + hyundai_longitudinal_tuning = int(params.get("HyundaiLongitudinalTuning", encoding="utf8") or 0) + if hyundai_longitudinal_tuning == LongitudinalTuningType.DYNAMIC: + CP_SP.flags |= HyundaiFlagsSP.LONG_TUNING_DYNAMIC.value + if hyundai_longitudinal_tuning == LongitudinalTuningType.PREDICTIVE: + CP_SP.flags |= HyundaiFlagsSP.LONG_TUNING_PREDICTIVE.value + + CP_SP = CI.get_longitudinal_tuning_sp(CP, CP_SP) + def _initialize_neural_network_lateral_control(CI: CarInterfaceBase, CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params = None, enabled: bool = False) -> None: @@ -61,10 +77,11 @@ def _initialize_radar_tracks(CP: structs.CarParams, CP_SP: structs.CarParamsSP, CP.radarUnavailable = False -def setup_interfaces(CI: CarInterfaceBase, params: Params = None): +def setup_interfaces(CI: CarInterfaceBase, params: Params = None) -> None: CP = CI.CP CP_SP = CI.CP_SP + _initialize_custom_longitudinal_tuning(CI, CP, CP_SP, params) _initialize_neural_network_lateral_control(CI, CP, CP_SP, params) _initialize_radar_tracks(CP, CP_SP, params) diff --git a/system/manager/manager.py b/system/manager/manager.py index fde85f0f7c..90df415a8c 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -45,6 +45,7 @@ def manager_init() -> None: ("AutoLaneChangeTimer", "0"), ("AutoLaneChangeBsmDelay", "0"), ("DynamicExperimentalControl", "0"), + ("HyundaiLongitudinalTuning", "0"), ("Mads", "1"), ("MadsMainCruiseAllowed", "1"), ("MadsPauseLateralOnBrake", "0"),