diff --git a/README.md b/README.md index 21928a8611ae77..596db65b15a94f 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ FrogPilot is a fully open-sourced fork of openpilot, featuring clear and concise ------ FrogPilot was last updated on: -**October 21st, 2024** +**November 1st, 2024** Features ------ diff --git a/cereal/car.capnp b/cereal/car.capnp index ec40d92c040f10..cd3f35a621113c 100644 --- a/cereal/car.capnp +++ b/cereal/car.capnp @@ -137,14 +137,15 @@ struct CarEvent @0x9b1657f34caf3ad3 { openpilotCrashedRandomEvent @137; pedalInterceptorNoBrake @138; speedLimitChanged @139; - torqueNNLoad @140; - trafficModeActive @141; - trafficModeInactive @142; - turningLeft @143; - turningRight @144; - vCruise69 @145; - yourFrogTriedToKillMe @146; - youveGotMail @147; + thisIsFineSteerSaturated @140; + torqueNNLoad @141; + trafficModeActive @142; + trafficModeInactive @143; + turningLeft @144; + turningRight @145; + vCruise69 @146; + yourFrogTriedToKillMe @147; + youveGotMail @148; radarCanErrorDEPRECATED @15; communityFeatureDisallowedDEPRECATED @62; @@ -453,7 +454,8 @@ struct CarControl { mail @16; nessie @17; noice @18; - uwu @19; + thisIsFine @19; + uwu @20; } } diff --git a/cereal/custom.capnp b/cereal/custom.capnp index 6b5554fe7b7c09..0fa098743f01b7 100644 --- a/cereal/custom.capnp +++ b/cereal/custom.capnp @@ -54,27 +54,29 @@ struct FrogPilotPlan @0x80ae746ee2596b11 { desiredFollowDistance @4 :Float32; experimentalMode @5 :Bool; forcingStop @6 :Bool; - frogpilotEvents @7 :List(Car.CarEvent); - lateralCheck @8 :Bool; - laneWidthLeft @9 :Float32; - laneWidthRight @10 :Float32; - maxAcceleration @11 :Float32; - minAcceleration @12 :Float32; - redLight @13 :Bool; - safeObstacleDistance @14 :Int32; - safeObstacleDistanceStock @15 :Int32; - slcOverridden @16 :Bool; - slcOverriddenSpeed @17 :Float32; - slcSpeedLimit @18 :Float32; - slcSpeedLimitOffset @19 :Float32; - speedJerk @20 :Float32; - speedJerkStock @21 :Float32; - speedLimitChanged @22 :Bool; - stoppedEquivalenceFactor @23 :Int32; - tFollow @24 :Float32; - unconfirmedSlcSpeedLimit @25 :Float32; - vCruise @26 :Float32; - vtscControllingCurve @27 :Bool; + forcingStopLength @7 :Float32; + frogpilotEvents @8 :List(Car.CarEvent); + lateralCheck @9 :Bool; + laneWidthLeft @10 :Float32; + laneWidthRight @11 :Float32; + maxAcceleration @12 :Float32; + minAcceleration @13 :Float32; + redLight @14 :Bool; + safeObstacleDistance @15 :Int64; + safeObstacleDistanceStock @16 :Int64; + slcOverridden @17 :Bool; + slcOverriddenSpeed @18 :Float32; + slcSpeedLimit @19 :Float32; + slcSpeedLimitOffset @20 :Float32; + speedJerk @21 :Float32; + speedJerkStock @22 :Float32; + speedLimitChanged @23 :Bool; + stoppedEquivalenceFactor @24 :Int64; + tFollow @25 :Float32; + togglesUpdated @26 :Bool; + unconfirmedSlcSpeedLimit @27 :Float32; + vCruise @28 :Float32; + vtscControllingCurve @29 :Bool; } struct CustomReserved5 @0xa5cd762cd951a455 { diff --git a/cereal/log.capnp b/cereal/log.capnp index 3b1df446a51ff5..9a944eac3429f6 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -618,6 +618,10 @@ struct RadarState @0x9a185389d6fdd05f { leadOne @3 :LeadData; leadTwo @4 :LeadData; + leadLeft @13 :LeadData; + leadRight @14 :LeadData; + leadLeftFar @15 :LeadData; + leadRightFar @16 :LeadData; cumLagMs @5 :Float32; struct LeadData { diff --git a/cereal/services.py b/cereal/services.py index 1ac0ae4760138c..6a15c9672c27ad 100755 --- a/cereal/services.py +++ b/cereal/services.py @@ -74,13 +74,6 @@ def __init__(self, should_log: bool, frequency: float, decimation: Optional[int] "userFlag": (True, 0., 1), "microphone": (True, 10., 10), - # FrogPilot - "frogpilotCarControl": (True, 100., 10), - "frogpilotCarState": (True, 100., 10), - "frogpilotDeviceState": (True, 2., 1), - "frogpilotNavigation": (True, 1., 10), - "frogpilotPlan": (True, 20., 5), - # debug "uiDebug": (True, 0., 1), "testJoystick": (True, 0.), @@ -97,6 +90,13 @@ def __init__(self, should_log: bool, frequency: float, decimation: Optional[int] "customReservedRawData0": (True, 0.), "customReservedRawData1": (True, 0.), "customReservedRawData2": (True, 0.), + + # FrogPilot + "frogpilotCarControl": (True, 100., 10), + "frogpilotCarState": (True, 100., 10), + "frogpilotDeviceState": (True, 2., 1), + "frogpilotNavigation": (True, 1., 10), + "frogpilotPlan": (True, 20., 5), } SERVICE_LIST = {name: Service(*vals) for idx, (name, vals) in enumerate(_services.items())} diff --git a/common/conversions.py b/common/conversions.py index 8554646ed0bb72..62c8debdb56e0e 100644 --- a/common/conversions.py +++ b/common/conversions.py @@ -12,8 +12,6 @@ class Conversions: KNOTS_TO_MS = 1. / MS_TO_KNOTS # Distance - KM_TO_MILES = KPH_TO_MPH - MILES_TO_KM = MPH_TO_KPH METER_TO_FOOT = 3.28084 FOOT_TO_METER = 1. / METER_TO_FOOT CM_TO_INCH = 1. / 2.54 diff --git a/common/params.cc b/common/params.cc index 918f714f3e3cf8..08eee2fe468295 100644 --- a/common/params.cc +++ b/common/params.cc @@ -211,12 +211,11 @@ std::unordered_map keys = { // FrogPilot parameters {"AccelerationPath", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"AccelerationProfile", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, + {"AdjacentLeadsUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"AdjacentPath", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"AdjacentPathMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"AdvancedCustomUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"AdvancedLateralTune", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"AdvancedLongitudinalTune", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"AdvancedQOLDriving", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"AggressiveFollow", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"AggressiveJerkAcceleration", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"AggressiveJerkDanger", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, @@ -240,7 +239,6 @@ std::unordered_map keys = { {"BlindSpotMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"BlindSpotPath", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"BorderMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"CameraFPS", PERSISTENT}, {"CameraView", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"CancelModelDownload", PERSISTENT}, {"CancelThemeDownload", PERSISTENT}, @@ -266,6 +264,7 @@ std::unordered_map keys = { {"CESpeedLead", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"CEStatus", CLEAR_ON_OFFROAD_TRANSITION}, {"CEStoppedLead", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, + {"ClassicModels", PERSISTENT}, {"ClusterOffset", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, {"ColorToDownload", PERSISTENT}, {"Compass", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, @@ -281,18 +280,24 @@ std::unordered_map keys = { {"CustomCruiseLong", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"CustomDistanceIcons", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"CustomIcons", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"CustomPaths", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, + {"CustomizationLevel", PERSISTENT}, + {"CustomizationLevelConfirmed", PERSISTENT}, {"CustomPersonalities", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"CustomSignals", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"CustomSounds", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"CustomUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"DecelerationProfile", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, + {"DefaultModelName", CLEAR_ON_MANAGER_START}, {"DeveloperUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"DeviceManagement", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"DeviceShutdown", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"DisableCurveSpeedSmoothing", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"DisableOnroadUploads", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"DisableOpenpilotLongitudinal", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, + {"DissolvedOxygenCalibrationParams", PERSISTENT}, + {"DissolvedOxygenDrives", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, + {"DissolvedOxygenLiveTorqueParameters", PERSISTENT}, + {"DissolvedOxygenScore", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"DistanceIconToDownload", PERSISTENT}, {"DisengageVolume", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"DoToggleReset", PERSISTENT}, @@ -303,10 +308,8 @@ std::unordered_map keys = { {"DownloadableSounds", PERSISTENT}, {"DownloadableWheels", PERSISTENT}, {"DownloadAllModels", PERSISTENT}, - {"DragonPilotTune", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, {"DriveRated", CLEAR_ON_ONROAD_TRANSITION}, {"DriverCamera", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"DrivingPersonalities", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"DuckAmigoCalibrationParams", PERSISTENT}, {"DuckAmigoDrives", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"DuckAmigoLiveTorqueParameters", PERSISTENT}, @@ -334,6 +337,7 @@ std::unordered_map keys = { {"FrogPilotDrives", PERSISTENT | FROGPILOT_TRACKING}, {"FrogPilotKilometers", PERSISTENT | FROGPILOT_TRACKING}, {"FrogPilotMinutes", PERSISTENT | FROGPILOT_TRACKING}, + {"FrogPilotToggles", PERSISTENT}, {"FrogPilotTogglesUpdated", PERSISTENT}, {"FrogsGoMoo", PERSISTENT}, {"FrogsGoMoosTweak", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, @@ -353,8 +357,6 @@ std::unordered_map keys = { {"HideMapIcon", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"HideMaxSpeed", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"HideSpeed", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"HideSpeedUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, - {"HideUIElements", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"HolidayThemes", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"HumanAcceleration", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"HumanFollowing", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, @@ -397,16 +399,15 @@ std::unordered_map keys = { {"MinimumLaneChangeSpeed", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"Model", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"ModelDownloadProgress", PERSISTENT}, - {"ModelManagement", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"ModelName", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"ModelRandomizer", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"ModelsDownloaded", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"ModelSelector", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"ModelToDownload", PERSISTENT}, {"ModelUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"MTSCCurvatureCheck", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"MTSCEnabled", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"NavigationModels", PERSISTENT}, + {"NavigationUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"NextMapSpeedLimit", PERSISTENT}, {"NewLongAPI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, {"NewLongAPIGM", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, @@ -434,6 +435,7 @@ std::unordered_map keys = { {"OneLaneChange", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"OnroadDistanceButton", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"OnroadDistanceButtonPressed", PERSISTENT}, + {"openpilotMinutes", PERSISTENT}, {"OSMDownloadBounds", PERSISTENT}, {"OSMDownloadLocations", PERSISTENT}, {"OSMDownloadProgress", CLEAR_ON_MANAGER_START}, @@ -493,7 +495,6 @@ std::unordered_map keys = { {"ShowIP", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"ShowMemoryUsage", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"ShowSLCOffset", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"ShowSLCOffsetUI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"ShowSteering", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"ShowStoppingPoint", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"ShowStoppingPointMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, @@ -503,6 +504,7 @@ std::unordered_map keys = { {"SidebarMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"SignalMetrics", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"SignalToDownload", PERSISTENT}, + {"SLCConfirmation", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"SLCConfirmationHigher", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"SLCConfirmationLower", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"SLCConfirmed", PERSISTENT}, @@ -541,6 +543,7 @@ std::unordered_map keys = { {"TacoTune", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"TetheringEnabled", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_OTHER}, {"ThemeDownloadProgress", PERSISTENT}, + {"ThemeUpdated", PERSISTENT}, {"TombRaiderCalibrationParams", PERSISTENT}, {"TombRaiderDrives", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"TombRaiderLiveTorqueParameters", PERSISTENT}, @@ -564,7 +567,6 @@ std::unordered_map keys = { {"UseSI", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, {"UseStockColors", CLEAR_ON_MANAGER_START}, {"UseVienna", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, - {"VelocityModels", PERSISTENT}, {"VisionTurnControl", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_CONTROLS}, {"VoltSNG", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VEHICLES}, {"WarningImmediateVolume", PERSISTENT | FROGPILOT_STORAGE | FROGPILOT_VISUALS}, diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py index 863c1c3996554f..1369007eb6bd82 100755 --- a/selfdrive/car/card.py +++ b/selfdrive/car/card.py @@ -16,7 +16,8 @@ from openpilot.selfdrive.car.interfaces import CarInterfaceBase from openpilot.selfdrive.controls.lib.events import Events -from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables +from openpilot.selfdrive.frogpilot.frogpilot_utilities import update_frogpilot_toggles +from openpilot.selfdrive.frogpilot.frogpilot_variables import get_frogpilot_toggles REPLAY = "REPLAY" in os.environ @@ -28,7 +29,7 @@ class Car: def __init__(self, CI=None): self.can_sock = messaging.sub_sock('can', timeout=20) - self.sm = messaging.SubMaster(['pandaStates', 'carControl', 'onroadEvents']) + self.sm = messaging.SubMaster(['pandaStates', 'carControl', 'onroadEvents', 'frogpilotPlan']) self.pm = messaging.PubMaster(['sendcan', 'carState', 'carParams', 'carOutput', 'frogpilotCarState']) self.can_rcv_cum_timeout_counter = 0 @@ -74,10 +75,7 @@ def __init__(self, CI=None): self.rk = Ratekeeper(100, print_delay_threshold=None) # FrogPilot variables - self.frogpilot_toggles = FrogPilotVariables.toggles - FrogPilotVariables.update_frogpilot_params() - - self.update_toggles = False + self.frogpilot_toggles = get_frogpilot_toggles(True) # set alternative experiences from parameters self.disengage_on_accelerator = self.params.get_bool("DisengageOnAccelerator") @@ -97,6 +95,8 @@ def __init__(self, CI=None): self.params.put_nonblocking("CarParamsCache", cp_bytes) self.params.put_nonblocking("CarParamsPersistent", cp_bytes) + update_frogpilot_toggles() + def state_update(self) -> car.CarState: """carState update loop, driven by can""" @@ -199,11 +199,8 @@ def card_thread(self): self.rk.monitor_time() # Update FrogPilot parameters - if FrogPilotVariables.toggles_updated: - self.update_toggles = True - elif self.update_toggles: - FrogPilotVariables.update_frogpilot_params() - self.update_toggles = False + if self.sm['frogpilotPlan'].togglesUpdated: + self.frogpilot_toggles = get_frogpilot_toggles() def main(): config_realtime_process(4, Priority.CTRL_HIGH) diff --git a/selfdrive/car/ford/carcontroller.py b/selfdrive/car/ford/carcontroller.py index 4103e5f4dbc7f8..fc0dd01257c072 100644 --- a/selfdrive/car/ford/carcontroller.py +++ b/selfdrive/car/ford/carcontroller.py @@ -97,9 +97,9 @@ def update(self, CC, CS, now_nanos, frogpilot_toggles): if self.CP.openpilotLongitudinalControl and (self.frame % CarControllerParams.ACC_CONTROL_STEP) == 0: # Both gas and accel are in m/s^2, accel is used solely for braking if frogpilot_toggles.sport_plus: - accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, get_max_allowed_accel(CS.out.vEgo))) + accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, get_max_allowed_accel(CS.out.vEgo))) else: - accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, CarControllerParams.ACCEL_MAX)) + accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, CarControllerParams.ACCEL_MAX)) gas = accel if not CC.longActive or gas < CarControllerParams.MIN_GAS: gas = CarControllerParams.INACTIVE_GAS diff --git a/selfdrive/car/honda/carcontroller.py b/selfdrive/car/honda/carcontroller.py index 153a6784871d5b..9dd120ec7dcc11 100644 --- a/selfdrive/car/honda/carcontroller.py +++ b/selfdrive/car/honda/carcontroller.py @@ -219,9 +219,9 @@ def update(self, CC, CS, now_nanos, frogpilot_toggles): if self.CP.carFingerprint in HONDA_BOSCH: if frogpilot_toggles.sport_plus: - self.accel = clip(accel, self.params.BOSCH_ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, get_max_allowed_accel(CS.out.vEgo))) + self.accel = clip(accel, self.params.BOSCH_ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, get_max_allowed_accel(CS.out.vEgo))) else: - self.accel = clip(accel, self.params.BOSCH_ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, self.params.BOSCH_ACCEL_MAX)) + self.accel = clip(accel, self.params.BOSCH_ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, self.params.BOSCH_ACCEL_MAX)) self.gas = interp(accel, self.params.BOSCH_GAS_LOOKUP_BP, self.params.BOSCH_GAS_LOOKUP_V) stopping = actuators.longControlState == LongCtrlState.stopping diff --git a/selfdrive/car/hyundai/carcontroller.py b/selfdrive/car/hyundai/carcontroller.py index 92c0f5e4905bbb..ff037c116e5c20 100644 --- a/selfdrive/car/hyundai/carcontroller.py +++ b/selfdrive/car/hyundai/carcontroller.py @@ -82,9 +82,9 @@ def update(self, CC, CS, now_nanos, frogpilot_toggles): # accel + longitudinal if frogpilot_toggles.sport_plus: - accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, get_max_allowed_accel(CS.out.vEgo))) + accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, get_max_allowed_accel(CS.out.vEgo))) else: - accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, CarControllerParams.ACCEL_MAX)) + accel = clip(actuators.accel, CarControllerParams.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, CarControllerParams.ACCEL_MAX)) stopping = actuators.longControlState == LongCtrlState.stopping set_speed_in_units = hud_control.setSpeed * (CV.MS_TO_KPH if CS.is_metric else CV.MS_TO_MPH) diff --git a/selfdrive/car/torque_data/lat_models/CHEVROLET_BOLT_EUV.json b/selfdrive/car/torque_data/lat_models/CHEVROLET_BOLT_EUV.json new file mode 100644 index 00000000000000..f228dcfa17a0f6 --- /dev/null +++ b/selfdrive/car/torque_data/lat_models/CHEVROLET_BOLT_EUV.json @@ -0,0 +1 @@ +{"input_std":[[9.281861],[1.8477924],[0.7977224],[0.047467366],[1.7969038],[1.8168812],[1.8351218],[1.785757],[1.7335743],[1.6658221],[1.5893887],[0.047346078],[0.0473731],[0.047383286],[0.047291175],[0.047300573],[0.04712479],[0.046799928]],"model_test_loss":0.027355343103408813,"input_size":18,"current_date_and_time":"2023-08-05_05-11-44","input_mean":[[21.655252],[-0.07694559],[-0.006081294],[-0.007598456],[-0.07309746],[-0.075890236],[-0.07804942],[-0.076106496],[-0.07184982],[-0.06528668],[-0.060404416],[-0.0077262702],[-0.0077046235],[-0.0076850485],[-0.007687444],[-0.007708868],[-0.0077959923],[-0.008017422]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.35776415],[-0.126288],[0.007988413],[0.03850029],[-0.056796823],[0.0072075897],[0.17376427]],"dense_1_W":[[0.010538558,6.7293744,-0.007391298,-1.035045,-0.47446147,1.0940393,0.08225446,-0.5795131,0.573115,1.82923,-0.16999252,0.8818023,0.03560901,-0.9470653,-1.042151,1.1532398,1.9009485,-1.2265956],[0.009010456,-1.3618345,0.039026346,0.22943486,-0.6669799,-0.768271,-0.16828564,-0.13406865,0.19760236,-0.18953702,-0.019137766,0.1468623,0.26367718,0.4732375,0.73679143,0.62450147,0.30198866,-0.93340015],[-0.044318866,2.2138615,9.620889,-0.66480356,-0.06849887,-0.038568456,-0.30580127,-0.088089585,0.31187677,0.5968372,-1.1462088,0.13234706,0.29166338,-0.69978577,-0.01790655,-0.097928315,-0.56950736,0.7560642],[1.3758256,0.8700429,-0.24295154,0.20832618,-0.7735097,1.0379355,-0.60753095,-0.5457418,-1.0892137,-0.6708325,0.24982774,0.09251328,0.08596103,-0.58034134,-0.3046114,-0.19754426,0.006461508,0.5989185],[0.06314328,-2.492993,-0.28200585,0.49902886,0.8513198,-0.5704965,1.0960953,-0.08426956,-0.76074624,0.10669376,0.02775254,-0.42194095,-0.34657434,-0.10863868,0.17019278,0.0963825,-0.10169116,0.21243806],[0.007984091,-1.9276509,-0.0013926353,0.04057924,1.4438432,1.3440262,-2.506722,1.700801,0.72410774,0.13471895,-0.18753268,0.7303607,0.018133093,-0.71643597,0.07050904,-0.30161944,0.085108586,0.061202843],[0.9413167,-0.9954305,-0.19510143,-0.4711845,-0.12398665,-0.45175523,1.0616771,0.28550953,-0.55543137,-0.21576759,-0.2364377,-0.012782618,-0.050565187,0.257694,-0.20975965,0.00022543047,-0.08760746,0.4983852]],"activation":"σ"},{"dense_2_W":[[-0.71804935,0.017023304,-0.099854074,-0.3262651,-0.402829,-0.12073083,0.13537839],[-0.44002575,-0.1071093,-0.58886194,-0.17319413,-0.21134079,0.23135148,0.0006880979],[-0.55711204,-0.8693639,-0.6443761,-0.7382846,0.18980889,-0.6082511,-0.63103676],[0.11338112,-0.5327033,0.2856197,0.32239884,-0.72682667,0.5302305,-0.45094746],[-0.35197002,-0.14440043,-0.025249843,-0.48697743,0.3340018,-0.25992322,0.0050561456],[0.34757507,0.15670523,-0.14263277,0.50704384,-0.020171141,0.6408679,-0.3074123],[0.40258837,-0.59363264,0.35948923,-0.060853776,-0.0072541097,0.89743376,-0.49789146],[-0.63476,0.29580453,0.1689314,-0.5061521,0.24901053,-0.23218995,0.57968193],[-0.66173035,0.50861925,-0.5845035,-0.6602214,0.8341883,-0.31437424,0.8046359],[0.038493533,0.15464582,-0.04848341,-0.57820857,-0.25891733,-0.47527292,0.21441916],[-0.15464531,-0.07454202,-0.8215851,-0.12614948,-0.5924209,0.00017916794,0.24154592],[0.6091424,-0.112086505,0.1144111,-0.31035227,-0.9237534,0.041003596,-0.3542808],[0.2989792,-0.23780549,0.116059326,-0.6056522,0.5499526,-0.9001413,0.5200723]],"activation":"σ","dense_2_b":[[-0.2638964],[-0.24607328],[-0.15493082],[-0.0071187904],[-0.23515384],[-0.09098133],[-0.034066215],[0.03609036],[-0.03842933],[-0.042302527],[-0.2439947],[-0.16342077],[-0.01030317]]},{"dense_3_W":[[0.45771673,-0.2084603,0.3570187,0.35835707,0.13684571,-0.582958,0.29889587,0.43543494,0.11841301,-0.26185623,-0.4988437,0.5752996,-0.28057483],[-0.19594137,0.050280698,-0.29057446,-0.2829161,-0.16987291,-0.21278952,-0.59946907,0.21295123,0.7040468,0.53549695,-0.52553934,-0.19560973,0.6233473],[-0.19091003,0.08669418,-0.5792192,0.57799137,-0.36263424,0.6037143,0.27898273,-0.30951327,-0.3572644,0.46720102,-0.5403428,0.32415462,-0.60570025]],"activation":"identity","dense_3_b":[[-0.0047377176],[0.023170695],[-0.021546118]]},{"dense_4_W":[[-0.12453941,-1.006034,0.96776205]],"dense_4_b":[[-0.02351544]],"activation":"identity"}]} \ No newline at end of file diff --git a/selfdrive/car/toyota/carcontroller.py b/selfdrive/car/toyota/carcontroller.py index 032860e6ffa42e..f1e1728a6c7ae0 100644 --- a/selfdrive/car/toyota/carcontroller.py +++ b/selfdrive/car/toyota/carcontroller.py @@ -54,7 +54,7 @@ def __init__(self, dbc_name, CP, VM): self.distance_button = 0 self.pcm_accel_compensation = 0.0 - self.permit_braking = 0.0 + self.permit_braking = True self.packer = CANPacker(dbc_name) self.accel = 0 @@ -141,10 +141,13 @@ def update(self, CC, CS, now_nanos, frogpilot_toggles): interceptor_gas_cmd = 0. # For cars where we allow a higher max acceleration of 2.0 m/s^2, compensate for PCM request overshoot and imprecise braking - # TODO: sometimes when switching from brake to gas quickly, CLUTCH->ACCEL_NET shows a slow unwind. make it go to 0 immediately if (self.CP.flags & ToyotaFlags.RAISED_ACCEL_LIMIT or self.CP.flags & ToyotaFlags.NEW_TOYOTA_TUNE) and CC.longActive and not CS.out.cruiseState.standstill: # calculate amount of acceleration PCM should apply to reach target, given pitch - accel_due_to_pitch = math.sin(CS.slope_angle) * ACCELERATION_DUE_TO_GRAVITY + if len(CC.orientationNED) == 3: + accel_due_to_pitch = math.sin(CC.orientationNED[1]) * ACCELERATION_DUE_TO_GRAVITY + else: + accel_due_to_pitch = 0.0 + net_acceleration_request = actuators.accel + accel_due_to_pitch # let PCM handle stopping for now @@ -171,9 +174,9 @@ def update(self, CC, CS, now_nanos, frogpilot_toggles): self.permit_braking = True if frogpilot_toggles.sport_plus: - pcm_accel_cmd = clip(pcm_accel_cmd, self.params.ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, get_max_allowed_accel(CS.out.vEgo))) + pcm_accel_cmd = clip(pcm_accel_cmd, self.params.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, get_max_allowed_accel(CS.out.vEgo))) else: - pcm_accel_cmd = clip(pcm_accel_cmd, self.params.ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, self.params.ACCEL_MAX)) + pcm_accel_cmd = clip(pcm_accel_cmd, self.params.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, self.params.ACCEL_MAX)) # on entering standstill, send standstill request if CS.out.standstill and not self.last_standstill and (self.CP.carFingerprint not in NO_STOP_TIMER_CAR or self.CP.enableGasInterceptor): diff --git a/selfdrive/car/toyota/carstate.py b/selfdrive/car/toyota/carstate.py index 75130c2fd9b039..6d162c84450e09 100644 --- a/selfdrive/car/toyota/carstate.py +++ b/selfdrive/car/toyota/carstate.py @@ -64,7 +64,6 @@ def __init__(self, CP): self.acc_type = 1 self.lkas_hud = {} self.pcm_accel_net = 0.0 - self.slope_angle = 0.0 # FrogPilot variables self.latActive_previous = False @@ -273,7 +272,6 @@ def get_can_parser(CP): ("BODY_CONTROL_STATE", 3), ("BODY_CONTROL_STATE_2", 2), ("ESP_CONTROL", 3), - ("VSC1S07", 20), ("EPS_STATUS", 25), ("BRAKE_MODULE", 40), ("WHEEL_SPEEDS", 80), diff --git a/selfdrive/car/toyota/fingerprints.py b/selfdrive/car/toyota/fingerprints.py index 292be96fc85e73..e3a918dac8eda0 100644 --- a/selfdrive/car/toyota/fingerprints.py +++ b/selfdrive/car/toyota/fingerprints.py @@ -127,6 +127,7 @@ b'\x018966333X0000\x00\x00\x00\x00', b'\x018966333X4000\x00\x00\x00\x00', b'\x01896633T16000\x00\x00\x00\x00', + b'\x01896633TA2000\x00\x00\x00\x00', b'\x028966306B2100\x00\x00\x00\x00897CF3302002\x00\x00\x00\x00', b'\x028966306B2300\x00\x00\x00\x00897CF3302002\x00\x00\x00\x00', b'\x028966306B2500\x00\x00\x00\x00897CF3302002\x00\x00\x00\x00', @@ -321,6 +322,7 @@ b'F1526F4073\x00\x00\x00\x00\x00\x00', b'F1526F4121\x00\x00\x00\x00\x00\x00', b'F1526F4122\x00\x00\x00\x00\x00\x00', + b'F1526F4190\x00\x00\x00\x00\x00\x00', ], (Ecu.eps, 0x7a1, None): [ b'8965B10011\x00\x00\x00\x00\x00\x00', @@ -337,6 +339,7 @@ b'\x033F401200\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203202\x00\x00\x00\x00', b'\x033F424000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203202\x00\x00\x00\x00', b'\x033F424000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203302\x00\x00\x00\x00', + b'\x033F435000\x00\x00\x00\x00\x00\x00\x00\x00A0202000\x00\x00\x00\x00\x00\x00\x00\x00895231203302\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x750, 0xf): [ b'8821F0W01000 ', @@ -431,6 +434,7 @@ CAR.TOYOTA_COROLLA_TSS2: { (Ecu.engine, 0x700, None): [ b'\x01896630A22000\x00\x00\x00\x00', + b'\x01896630A42000\x00\x00\x00\x00', b'\x01896630ZG2000\x00\x00\x00\x00', b'\x01896630ZG5000\x00\x00\x00\x00', b'\x01896630ZG5100\x00\x00\x00\x00', @@ -525,6 +529,7 @@ b'8965B16011\x00\x00\x00\x00\x00\x00', b'8965B16101\x00\x00\x00\x00\x00\x00', b'8965B16170\x00\x00\x00\x00\x00\x00', + b'8965B16260\x00\x00\x00\x00\x00\x00', b'8965B76012\x00\x00\x00\x00\x00\x00', b'8965B76050\x00\x00\x00\x00\x00\x00', b'8965B76091\x00\x00\x00\x00\x00\x00', @@ -539,6 +544,7 @@ b'\x01F15260A010\x00\x00\x00\x00\x00\x00', b'\x01F15260A050\x00\x00\x00\x00\x00\x00', b'\x01F15260A070\x00\x00\x00\x00\x00\x00', + b'\x01F15260A33000\x00\x00\x00\x00', b'\x01F152612641\x00\x00\x00\x00\x00\x00', b'\x01F152612651\x00\x00\x00\x00\x00\x00', b'\x01F152612862\x00\x00\x00\x00\x00\x00', @@ -598,6 +604,7 @@ b'\x028646F1601100\x00\x00\x00\x008646G2601400\x00\x00\x00\x00', b'\x028646F1601200\x00\x00\x00\x008646G2601400\x00\x00\x00\x00', b'\x028646F1601300\x00\x00\x00\x008646G2601400\x00\x00\x00\x00', + b'\x028646F1601500\x00\x00\x00\x008646G2601400\x00\x00\x00\x00', b'\x028646F4203400\x00\x00\x00\x008646G2601200\x00\x00\x00\x00', b'\x028646F76020C0\x00\x00\x00\x008646G26011A0\x00\x00\x00\x00', b'\x028646F7603100\x00\x00\x00\x008646G2601200\x00\x00\x00\x00', @@ -1005,6 +1012,33 @@ b'8646F4204000\x00\x00\x00\x00', ], }, + CAR.TOYOTA_RAV4_PRIME: { + (Ecu.engine, 0x700, None): [ + b'\x018966342S7000\x00\x00\x00\x00', + b'\x018966342Z1000\x00\x00\x00\x00', + b'\x018966342Z1100\x00\x00\x00\x00', + b'\x01896634AJ7000\x00\x00\x00\x00', + ], + (Ecu.abs, 0x7b0, None): [ + b'\x01F15264228300\x00\x00\x00\x00', + b'\x01F15264228500\x00\x00\x00\x00', + b'\x01F15264284100\x00\x00\x00\x00', + b'\x01F152642F3000\x00\x00\x00\x00', + ], + (Ecu.eps, 0x7a1, None): [ + b'\x018965B4209000\x00\x00\x00\x00', + b'\x018965B4233100\x00\x00\x00\x00', + ], + (Ecu.fwdRadar, 0x750, 0xf): [ + b'\x018821F3301400\x00\x00\x00\x00', + b'\x018821F6201400\x00\x00\x00\x00', + ], + (Ecu.fwdCamera, 0x750, 0x6d): [ + b'\x028646F4205200\x00\x00\x00\x008646G4202000\x00\x00\x00\x00', + b'\x028646F4205300\x00\x00\x00\x008646G4202100\x00\x00\x00\x00', + b'\x028646F4210100\x00\x00\x00\x008646G3305000\x00\x00\x00\x00', + ], + }, CAR.TOYOTA_RAV4_TSS2: { (Ecu.engine, 0x700, None): [ b'\x01896630R58000\x00\x00\x00\x00', @@ -1226,6 +1260,28 @@ b'8646F0801100\x00\x00\x00\x00', ], }, + CAR.TOYOTA_SIENNA_4TH_GEN: { + (Ecu.engine, 0x700, None): [ + b'\x01896630841000\x00\x00\x00\x00', + b'\x01896630857101\x00\x00\x00\x00', + b'\x01896630864000\x00\x00\x00\x00', + ], + (Ecu.abs, 0x7b0, None): [ + b'\x01F15260815100\x00\x00\x00\x00', + b'\x01F15260815300\x00\x00\x00\x00', + ], + (Ecu.eps, 0x7a1, None): [ + b'\x018965B4509100\x00\x00\x00\x00', + ], + (Ecu.fwdRadar, 0x750, 0xf): [ + b'\x018821F3301500\x00\x00\x00\x00', + ], + (Ecu.fwdCamera, 0x750, 0x6d): [ + b'\x028646F0802200\x00\x00\x00\x008646G4202100\x00\x00\x00\x00', + b'\x028646F0802300\x00\x00\x00\x008646G4202100\x00\x00\x00\x00', + b'\x028646F0802400\x00\x00\x00\x008646G4202100\x00\x00\x00\x00', + ], + }, CAR.LEXUS_CTH: { (Ecu.dsu, 0x791, None): [ b'881517601100\x00\x00\x00\x00', @@ -1644,6 +1700,7 @@ b'8965B47070\x00\x00\x00\x00\x00\x00', ], (Ecu.fwdRadar, 0x750, 0xf): [ + b'\x018821F3301300\x00\x00\x00\x00', b'\x018821F3301400\x00\x00\x00\x00', ], (Ecu.fwdCamera, 0x750, 0x6d): [ diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index 21c8670093be48..63e55166678b9d 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -156,9 +156,6 @@ def _get_params(ret, candidate, fingerprint, car_fw, disable_openpilot_long, exp tune.kiV = [3.6, 2.4, 1.5] if params.get_bool("FrogsGoMoosTweak"): - if ret.flags & ToyotaFlags.NEW_TOYOTA_TUNE or ret.flags & ToyotaFlags.RAISED_ACCEL_LIMIT: - tune.kiV = [0.3] - ret.stoppingDecelRate = 0.1 # reach stopping target smoothly ret.vEgoStopping = 0.15 ret.vEgoStarting = 0.15 diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 96bae1420bd5f6..7caa0efb053b7b 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -47,9 +47,7 @@ def __init__(self, CP): class ToyotaFlags(IntFlag): # Detected flags HYBRID = 1 - SMART_DSU = 2 DISABLE_RADAR = 4 - RADAR_CAN_FILTER = 2048 # Static flags TSS2 = 8 @@ -59,14 +57,17 @@ class ToyotaFlags(IntFlag): # these cars use the Lane Tracing Assist (LTA) message for lateral control ANGLE_CONTROL = 128 NO_STOP_TIMER = 256 - # these cars are speculated to allow stop and go when the DSU is unplugged or disabled with sDSU + # these cars are speculated to allow stop and go when the DSU is unplugged SNG_WITHOUT_DSU = 512 # these cars can utilize 2.0 m/s^2 RAISED_ACCEL_LIMIT = 1024 + SECOC = 2048 # FrogPilot Toyota flags NEW_TOYOTA_TUNE = 4096 - ZSS = 8192 + RADAR_CAN_FILTER = 8192 + SMART_DSU = 16384 + ZSS = 32768 class Footnote(Enum): CAMRY = CarFootnote( @@ -254,6 +255,14 @@ class CAR(Platforms): TOYOTA_RAV4_TSS2.specs, flags=ToyotaFlags.RADAR_ACC | ToyotaFlags.ANGLE_CONTROL, ) + TOYOTA_RAV4_PRIME = PlatformConfig( + # TODO: Enable this docs entry when it can be suppressed from openpilot CARS.md + # [ToyotaCarDocs("Toyota RAV4 Prime 2021-23", min_enable_speed=MIN_ACC_SPEED)], + [], + CarSpecs(mass=4372. * CV.LB_TO_KG, wheelbase=2.68, steerRatio=16.88, tireStiffnessFactor=0.5533), + dbc_dict('toyota_rav4_prime_generated', 'toyota_tss2_adas'), + flags=ToyotaFlags.TSS2 | ToyotaFlags.NO_STOP_TIMER | ToyotaFlags.NO_DSU | ToyotaFlags.SECOC, + ) TOYOTA_MIRAI = ToyotaTSS2PlatformConfig( # TSS 2.5 [ToyotaCarDocs("Toyota Mirai 2021")], CarSpecs(mass=4300. * CV.LB_TO_KG, wheelbase=2.91, steerRatio=14.8, tireStiffnessFactor=0.8), @@ -264,6 +273,14 @@ class CAR(Platforms): dbc_dict('toyota_tnga_k_pt_generated', 'toyota_adas'), flags=ToyotaFlags.NO_STOP_TIMER, ) + TOYOTA_SIENNA_4TH_GEN = PlatformConfig( + # TODO: Enable this docs entry when it can be suppressed from openpilot CARS.md + # [ToyotaCarDocs("Toyota Sienna 2021-23", min_enable_speed=MIN_ACC_SPEED)], + [], + CarSpecs(mass=4625. * CV.LB_TO_KG, wheelbase=3.06, steerRatio=17.8, tireStiffnessFactor=0.444), + dbc_dict('toyota_rav4_prime_generated', 'toyota_tss2_adas'), + flags=ToyotaFlags.TSS2 | ToyotaFlags.NO_STOP_TIMER | ToyotaFlags.NO_DSU | ToyotaFlags.SECOC, + ) # Lexus LEXUS_CTH = PlatformConfig( @@ -547,6 +564,7 @@ def match_fw_to_car_fuzzy(live_fw_versions, vin, offline_fw_versions) -> set[str # Hybrid control computer can be on 0x7e2 (KWP) or 0x7d2 (UDS) depending on platform (Ecu.hybrid, 0x7e2, None), # Hybrid Control Assembly & Computer + (Ecu.hybrid, 0x7d2, None), # Hybrid Control Assembly & Computer (Ecu.srs, 0x780, None), # SRS Airbag # Transmission is combined with engine on some platforms, such as TSS-P RAV4 (Ecu.transmission, 0x701, None), @@ -577,6 +595,8 @@ def match_fw_to_car_fuzzy(live_fw_versions, vin, offline_fw_versions) -> set[str ANGLE_CONTROL_CAR = CAR.with_flags(ToyotaFlags.ANGLE_CONTROL) +SECOC_CAR = CAR.with_flags(ToyotaFlags.SECOC) + # no resume button press required NO_STOP_TIMER_CAR = CAR.with_flags(ToyotaFlags.NO_STOP_TIMER) diff --git a/selfdrive/car/volkswagen/carcontroller.py b/selfdrive/car/volkswagen/carcontroller.py index e7e7ee1f77dca4..802a0ce4eea009 100644 --- a/selfdrive/car/volkswagen/carcontroller.py +++ b/selfdrive/car/volkswagen/carcontroller.py @@ -83,9 +83,9 @@ def update(self, CC, CS, now_nanos, frogpilot_toggles): if self.frame % self.CCP.ACC_CONTROL_STEP == 0 and self.CP.openpilotLongitudinalControl: acc_control = self.CCS.acc_control_value(CS.out.cruiseState.available, CS.out.accFaulted, CC.longActive) if frogpilot_toggles.sport_plus: - accel = clip(actuators.accel, self.CCP.ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, get_max_allowed_accel(CS.out.vEgo))) if CC.longActive else 0 + accel = clip(actuators.accel, self.CCP.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, get_max_allowed_accel(CS.out.vEgo))) if CC.longActive else 0 else: - accel = clip(actuators.accel, self.CCP.ACCEL_MIN, min(frogpilot_toggles.max_desired_accel, self.CCP.ACCEL_MAX)) if CC.longActive else 0 + accel = clip(actuators.accel, self.CCP.ACCEL_MIN, min(frogpilot_toggles.max_desired_acceleration, self.CCP.ACCEL_MAX)) if CC.longActive else 0 stopping = actuators.longControlState == LongCtrlState.stopping starting = actuators.longControlState == LongCtrlState.pid and (CS.esp_hold_confirmation or CS.out.vEgo < self.CP.vEgoStopping) can_sends.extend(self.CCS.create_acc_accel_control(self.packer_pt, CANBUS.pt, CS.acc_type, CC.longActive, accel, diff --git a/selfdrive/classic_modeld/classic_modeld.py b/selfdrive/classic_modeld/classic_modeld.py index 06c7cf8112c26a..c285acd636d20e 100755 --- a/selfdrive/classic_modeld/classic_modeld.py +++ b/selfdrive/classic_modeld/classic_modeld.py @@ -27,7 +27,7 @@ from openpilot.selfdrive.frogpilot.assets.model_manager import DEFAULT_MODEL from openpilot.selfdrive.frogpilot.frogpilot_functions import MODELS_PATH -from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables +from openpilot.selfdrive.frogpilot.frogpilot_variables import get_frogpilot_toggles PROCESS_NAME = "selfdrive.classic_modeld.modeld" SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') @@ -136,14 +136,11 @@ def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_ def main(demo=False): # FrogPilot variables - frogpilot_toggles = FrogPilotVariables.toggles - FrogPilotVariables.update_frogpilot_params() + frogpilot_toggles = get_frogpilot_toggles(True) enable_navigation = not frogpilot_toggles.navigationless_model radarless = frogpilot_toggles.radarless_model - update_toggles = False - cloudlog.warning("classic_modeld init") sentry.set_tag("daemon", PROCESS_NAME) @@ -347,11 +344,8 @@ def main(demo=False): last_vipc_frame_id = meta_main.frame_id # Update FrogPilot parameters - if FrogPilotVariables.toggles_updated: - update_toggles = True - elif update_toggles: - FrogPilotVariables.update_frogpilot_params() - update_toggles = False + if sm['frogpilotPlan'].togglesUpdated: + frogpilot_toggles = get_frogpilot_toggles() if __name__ == "__main__": try: diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 4ce4dfcb43359b..552bc90a4c0f98 100644 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -32,7 +32,7 @@ from openpilot.system.hardware import HARDWARE from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_acceleration import get_max_allowed_accel -from openpilot.selfdrive.frogpilot.frogpilot_variables import NON_DRIVING_GEARS, FrogPilotVariables +from openpilot.selfdrive.frogpilot.frogpilot_variables import CRUISING_SPEED, NON_DRIVING_GEARS, get_frogpilot_toggles SOFT_DISABLE_TIME = 3 # seconds LDW_MIN_SPEED = 31 * CV.MPH_TO_MS @@ -99,7 +99,7 @@ def __init__(self, CI=None): if REPLAY: # no vipc in replay will make them ignored anyways ignore += ['roadCameraState', 'wideRoadCameraState'] - if FrogPilotVariables.toggles.radarless_model: + if get_frogpilot_toggles(True).radarless_model: ignore += ['radarState'] self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration', 'carOutput', 'driverMonitoringState', 'longitudinalPlan', 'liveLocationKalman', @@ -164,7 +164,7 @@ def __init__(self, CI=None): self.can_log_mono_time = 0 - self.startup_event = get_startup_event(car_recognized, not self.CP.passive, len(self.CP.carFw) > 0, self.block_user, FrogPilotVariables.toggles) + self.startup_event = get_startup_event(car_recognized, not self.CP.passive, len(self.CP.carFw) > 0, self.block_user, get_frogpilot_toggles(True)) if not sounds_available: self.events.add(EventName.soundsUnavailable, static=True) @@ -181,8 +181,7 @@ def __init__(self, CI=None): self.rk = Ratekeeper(100, print_delay_threshold=None) # FrogPilot variables - self.frogpilot_toggles = FrogPilotVariables.toggles - FrogPilotVariables.update_frogpilot_params() + self.frogpilot_toggles = get_frogpilot_toggles(True) self.params_memory = Params("/dev/shm/params") @@ -191,11 +190,12 @@ def __init__(self, CI=None): self.fcw_event_triggered = False self.no_entry_alert_triggered = False self.onroad_distance_pressed = False - self.radarless_model = self.frogpilot_toggles.radarless_model self.resume_pressed = False self.resume_previously_pressed = False self.steer_saturated_event_triggered = False - self.update_toggles = False + + self.radarless_model = self.frogpilot_toggles.radarless_model + self.use_old_long = self.CP.carName == "hyundai" and not self.params.get_bool("NewLongAPI") self.use_old_long |= self.CP.carName == "gm" and not self.params.get_bool("NewLongAPIGM") @@ -626,7 +626,7 @@ def state_control(self, CS): t_since_plan = (self.sm.frame - self.sm.recv_frame['longitudinalPlan']) * DT_CTRL actuators.accel = self.LoC.update_old_long(CC.longActive, CS, long_plan, pid_accel_limits, t_since_plan) else: - actuators.accel = self.LoC.update(CC.longActive, CS, long_plan.aTarget, long_plan.shouldStop, pid_accel_limits) + actuators.accel = self.LoC.update(CC.longActive, CS, long_plan.aTarget, long_plan.shouldStop or self.sm['frogpilotPlan'].forcingStopLength <= 0, pid_accel_limits) if len(long_plan.speeds): actuators.speed = long_plan.speeds[-1] @@ -673,7 +673,7 @@ def state_control(self, CS): good_speed = CS.vEgo > 5 max_torque = abs(self.sm['carOutput'].actuatorsOutput.steer) > 0.99 if undershooting and turning and good_speed and max_torque: - lac_log.active and self.events.add(EventName.goatSteerSaturated if self.frogpilot_toggles.goat_scream else EventName.steerSaturated) + lac_log.active and self.events.add(EventName.goatSteerSaturated if self.frogpilot_toggles.goat_scream_alert else EventName.steerSaturated) self.steer_saturated_event_triggered = True else: self.steer_saturated_event_triggered = False @@ -730,7 +730,7 @@ def update_frogpilot_variables(self, CS): self.always_on_lateral_active &= not (CS.brakePressed and CS.vEgo < self.frogpilot_toggles.always_on_lateral_pause_speed) or CS.standstill self.always_on_lateral_active = bool(self.always_on_lateral_active) - if self.frogpilot_toggles.conditional_experimental_mode or self.frogpilot_toggles.slc_fallback_experimental: + if self.frogpilot_toggles.conditional_experimental_mode or self.frogpilot_toggles.slc_fallback_experimental_mode: self.experimental_mode = self.sm['frogpilotPlan'].experimentalMode if any(be.pressed and be.type == FrogPilotButtonType.lkas for be in CS.buttonEvents): @@ -814,7 +814,7 @@ def publish_logs(self, CS, start_time, CC, lac_log, FPCC): if self.enabled: clear_event_types.add(ET.NO_ENTRY) - alerts = self.events.create_alerts(self.current_alert_types, [self.CP, CS, self.sm, self.is_metric, self.soft_disable_timer]) + alerts = self.events.create_alerts(self.current_alert_types, [self.CP, CS, self.sm, self.is_metric, self.soft_disable_timer, self.frogpilot_toggles]) self.AM.add_many(self.sm.frame, alerts) current_alert = self.AM.process_alerts(self.sm.frame, clear_event_types) if current_alert: @@ -941,11 +941,8 @@ def params_thread(self, evt): time.sleep(0.1) # Update FrogPilot parameters - if FrogPilotVariables.toggles_updated: - self.update_toggles = True - elif self.update_toggles: - FrogPilotVariables.update_frogpilot_params() - self.update_toggles = False + if self.sm['frogpilotPlan'].togglesUpdated: + self.frogpilot_toggles = get_frogpilot_toggles() def controlsd_thread(self): e = threading.Event() diff --git a/selfdrive/controls/lib/desire_helper.py b/selfdrive/controls/lib/desire_helper.py index 2089bb55b5b4cf..0f3afa95ec64c8 100644 --- a/selfdrive/controls/lib/desire_helper.py +++ b/selfdrive/controls/lib/desire_helper.py @@ -68,7 +68,7 @@ def update(self, carstate, lateral_active, lane_change_prob, frogpilotPlan, frog self.lane_change_state = LaneChangeState.off self.lane_change_direction = LaneChangeDirection.none self.turn_direction = TurnDirection.none - elif one_blinker and below_lane_change_speed and not carstate.standstill and frogpilot_toggles.turn_desires: + elif one_blinker and below_lane_change_speed and not carstate.standstill and frogpilot_toggles.use_turn_desires: self.lane_change_state = LaneChangeState.off self.lane_change_direction = LaneChangeDirection.none self.turn_direction = TurnDirection.turnLeft if carstate.leftBlinker else TurnDirection.turnRight diff --git a/selfdrive/controls/lib/events.py b/selfdrive/controls/lib/events.py index b0ccd98c1535e2..eb9132035ab0cb 100755 --- a/selfdrive/controls/lib/events.py +++ b/selfdrive/controls/lib/events.py @@ -4,6 +4,7 @@ import os from enum import IntEnum from collections.abc import Callable +from types import SimpleNamespace from cereal import log, car import cereal.messaging as messaging @@ -212,31 +213,31 @@ def get_display_speed(speed_ms: float, metric: bool) -> str: def soft_disable_alert(alert_text_2: str) -> AlertCallbackType: - def func(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: + def func(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: if soft_disable_time < int(0.5 / DT_CTRL): return ImmediateDisableAlert(alert_text_2) return SoftDisableAlert(alert_text_2) return func def user_soft_disable_alert(alert_text_2: str) -> AlertCallbackType: - def func(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: + def func(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: if soft_disable_time < int(0.5 / DT_CTRL): return ImmediateDisableAlert(alert_text_2) return UserSoftDisableAlert(alert_text_2) return func -def startup_master_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: +def startup_master_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: branch = get_short_branch() # Ensure get_short_branch is cached to avoid lags on startup if "REPLAY" in os.environ: branch = "replay" return StartupAlert("WARNING: This branch is not tested", branch, alert_status=AlertStatus.userPrompt) -def below_engage_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: +def below_engage_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: return NoEntryAlert(f"Drive above {get_display_speed(CP.minEnableSpeed, metric)} to engage") -def below_steer_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: +def below_steer_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: return Alert( f"Steer Unavailable Below {get_display_speed(CP.minSteerSpeed, metric)}", "", @@ -244,7 +245,7 @@ def below_steer_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.S Priority.LOW, VisualAlert.steerRequired, AudibleAlert.prompt, 0.4) -def calibration_incomplete_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: +def calibration_incomplete_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: first_word = 'Recalibration' if sm['liveCalibration'].calStatus == log.LiveCalibrationData.Status.recalibrating else 'Calibration' return Alert( f"{first_word} in Progress: {sm['liveCalibration'].calPerc:.0f}%", @@ -253,54 +254,39 @@ def calibration_incomplete_alert(CP: car.CarParams, CS: car.CarState, sm: messag Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2) -def torque_nn_load_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: - model_name = Params().get("NNFFModelName", encoding='utf-8') - if model_name == "": - return Alert( - "NNFF Torque Controller not available", - "Donate logs to Twilsonco to get your car supported!", - AlertStatus.userPrompt, AlertSize.mid, - Priority.LOW, VisualAlert.none, AudibleAlert.prompt, 10.0) - else: - return Alert( - "NNFF Torque Controller loaded", - model_name, - AlertStatus.frogpilot, AlertSize.mid, - Priority.LOW, VisualAlert.none, AudibleAlert.engage, 5.0) - # *** debug alerts *** -def out_of_space_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: +def out_of_space_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: full_perc = round(100. - sm['deviceState'].freeSpacePercent) return NormalPermanentAlert("Out of Storage", f"{full_perc}% full") -def posenet_invalid_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: +def posenet_invalid_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: mdl = sm['modelV2'].velocity.x[0] if len(sm['modelV2'].velocity.x) else math.nan err = CS.vEgo - mdl msg = f"Speed Error: {err:.1f} m/s" return NoEntryAlert(msg, alert_text_1="Posenet Speed Invalid") -def process_not_running_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: +def process_not_running_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: not_running = [p.name for p in sm['managerState'].processes if not p.running and p.shouldBeRunning] msg = ', '.join(not_running) return NoEntryAlert(msg, alert_text_1="Process Not Running") -def comm_issue_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: +def comm_issue_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: bs = [s for s in sm.data.keys() if not sm.all_checks([s, ])] msg = ', '.join(bs[:4]) # can't fit too many on one line return NoEntryAlert(msg, alert_text_1="Communication Issue Between Processes") -def camera_malfunction_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: +def camera_malfunction_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: all_cams = ('roadCameraState', 'driverCameraState', 'wideRoadCameraState') bad_cams = [s.replace('State', '') for s in all_cams if s in sm.data.keys() and not sm.all_checks([s, ])] return NormalPermanentAlert("Camera Malfunction", ', '.join(bad_cams)) -def calibration_invalid_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: +def calibration_invalid_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: rpy = sm['liveCalibration'].rpyCalib yaw = math.degrees(rpy[2] if len(rpy) == 3 else math.nan) pitch = math.degrees(rpy[1] if len(rpy) == 3 else math.nan) @@ -308,34 +294,34 @@ def calibration_invalid_alert(CP: car.CarParams, CS: car.CarState, sm: messaging return NormalPermanentAlert("Calibration Invalid", angles) -def overheat_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: +def overheat_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: cpu = max(sm['deviceState'].cpuTempC, default=0.) gpu = max(sm['deviceState'].gpuTempC, default=0.) temp = max((cpu, gpu, sm['deviceState'].memoryTempC)) return NormalPermanentAlert("System Overheated", f"{temp:.0f} °C") -def low_memory_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: +def low_memory_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: return NormalPermanentAlert("Low Memory", f"{sm['deviceState'].memoryUsagePercent}% used") -def high_cpu_usage_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: +def high_cpu_usage_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: x = max(sm['deviceState'].cpuUsagePercent, default=0.) return NormalPermanentAlert("High CPU Usage", f"{x}% used") -def modeld_lagging_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: +def modeld_lagging_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: return NormalPermanentAlert("Driving Model Lagging", f"{sm['modelV2'].frameDropPerc:.1f}% frames dropped") -def wrong_car_mode_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: +def wrong_car_mode_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: text = "Enable Adaptive Cruise to Engage" if CP.carName == "honda": text = "Enable Main Switch to Engage" return NoEntryAlert(text) -def joystick_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: +def joystick_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: axes = sm['testJoystick'].axes gb, steer = list(axes)[:2] if len(axes) else (0., 0.) vals = f"Gas: {round(gb * 100.)}%, Steer: {round(steer * 100.)}%" @@ -343,11 +329,22 @@ def joystick_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, # FrogPilot Alerts -def custom_startup_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: - params = Params() - return StartupAlert(params.get("StartupMessageTop", encoding='utf-8') or "", params.get("StartupMessageBottom", encoding='utf-8') or "", alert_status=AlertStatus.frogpilot) +def custom_startup_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: + return StartupAlert(frogpilot_toggles.startup_alert_top, frogpilot_toggles.startup_alert_bottom, alert_status=AlertStatus.frogpilot) + + +def forcing_stop_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: + model_length = sm['frogpilotPlan'].forcingStopLength + model_length_msg = f"{model_length:.1f} meters" if metric else f"{model_length * CV.METER_TO_FOOT:.1f} feet" -def holiday_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: + return Alert( + f"Forcing the car to stop in {model_length_msg}", + "Press the gas pedal or 'Resume' button to override", + AlertStatus.frogpilot, AlertSize.mid, + Priority.MID, VisualAlert.none, AudibleAlert.prompt, 1.) + + +def holiday_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: holiday_messages = { "new_years": ("Happy New Year! 🎉", "newYearsDayAlert"), "valentines": ("Happy Valentine's Day! ❤️", "valentinesDayAlert"), @@ -362,7 +359,7 @@ def holiday_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, "christmas_week": ("Merry Christmas! 🎄", "christmasAlert") } - holiday_name = Params().get("CurrentHolidayTheme", encoding='utf-8') + holiday_name = frogpilot_toggles.current_holiday_theme message, alert_type = holiday_messages.get(holiday_name, ("", "")) return Alert( @@ -371,7 +368,8 @@ def holiday_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, AlertStatus.normal, AlertSize.small, Priority.LOWEST, VisualAlert.none, AudibleAlert.engage, 5.) -def no_lane_available_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int) -> Alert: + +def no_lane_available_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: lane_width = sm['frogpilotPlan'].laneWidthLeft if CS.leftBlinker else sm['frogpilotPlan'].laneWidthRight lane_width_msg = f"{lane_width:.1f} meters" if metric else f"{lane_width * CV.METER_TO_FOOT:.1f} feet" @@ -381,6 +379,23 @@ def no_lane_available_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.S AlertStatus.normal, AlertSize.mid, Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2) + +def torque_nn_load_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert: + model_name = Params().get("NNFFModelName", encoding='utf-8') + if model_name == "": + return Alert( + "NNFF Torque Controller not available", + "Donate logs to Twilsonco to get your car supported!", + AlertStatus.userPrompt, AlertSize.mid, + Priority.LOW, VisualAlert.none, AudibleAlert.prompt, 10.0) + else: + return Alert( + "NNFF Torque Controller loaded", + model_name, + AlertStatus.frogpilot, AlertSize.mid, + Priority.LOW, VisualAlert.none, AudibleAlert.none, 5.0) + + EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = { # ********** events with no alerts ********** @@ -1020,11 +1035,7 @@ def no_lane_available_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.S }, EventName.forcingStop: { - ET.WARNING: Alert( - "Forcing the car to stop", - "Press the gas pedal or 'Resume' button to override", - AlertStatus.frogpilot, AlertSize.mid, - Priority.MID, VisualAlert.none, AudibleAlert.prompt, 1.), + ET.WARNING: forcing_stop_alert, }, EventName.goatSteerSaturated: { @@ -1091,6 +1102,14 @@ def no_lane_available_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.S Priority.LOW, VisualAlert.none, AudibleAlert.prompt, 3.), }, + EventName.thisIsFineSteerSaturated: { + ET.WARNING: Alert( + "This is fine", + "☕", + AlertStatus.userPrompt, AlertSize.mid, + Priority.LOW, VisualAlert.steerRequired, AudibleAlert.thisIsFine, 2.), + }, + EventName.torqueNNLoad: { ET.PERMANENT: torque_nn_load_alert, }, diff --git a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py index b9cd1907433e9b..a144574304a7e2 100644 --- a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py +++ b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py @@ -326,10 +326,10 @@ def extrapolate_lead(x_lead, v_lead, a_lead, a_lead_tau): lead_xv = np.column_stack((x_lead_traj, v_lead_traj)) return lead_xv - def process_lead(self, lead, increased_distance=0): + def process_lead(self, lead): v_ego = self.x0[1] if lead is not None and lead.status: - x_lead = lead.dRel - increased_distance + x_lead = lead.dRel v_lead = lead.vLead a_lead = lead.aLeadK a_lead_tau = lead.aLeadTau @@ -355,12 +355,11 @@ def set_accel_limits(self, min_a, max_a): self.cruise_min_a = min_a self.max_a = max_a - def update(self, lead_one, lead_two, v_cruise, x, v, a, j, radarless_model, t_follow, trafficModeActive, frogpilot_toggles, personality=log.LongitudinalPersonality.standard): + def update(self, lead_one, lead_two, v_cruise, x, v, a, j, radarless_model, t_follow, trafficModeActive, personality=log.LongitudinalPersonality.standard): v_ego = self.x0[1] self.status = lead_one.status or lead_two.status - increased_distance = max(frogpilot_toggles.increase_stopped_distance + min(10 - v_ego, 0), 0) if not trafficModeActive else 0 - lead_xv_0 = self.process_lead(lead_one, increased_distance) + lead_xv_0 = self.process_lead(lead_one) lead_xv_1 = self.process_lead(lead_two) # To estimate a safe distance from a moving lead, we calculate how much stopping @@ -370,7 +369,8 @@ def update(self, lead_one, lead_two, v_cruise, x, v, a, j, radarless_model, t_fo lead_1_obstacle = lead_xv_1[:,0] + get_stopped_equivalence_factor(lead_xv_1[:,1]) self.params[:,0] = ACCEL_MIN - self.params[:,1] = self.max_a + # negative accel constraint causes problems because negative speed is not allowed + self.params[:,1] = max(0.0, self.max_a) # Update in ACC mode or ACC/e2e blend if self.mode == 'acc': @@ -379,6 +379,7 @@ def update(self, lead_one, lead_two, v_cruise, x, v, a, j, radarless_model, t_fo # Fake an obstacle for cruise, this ensures smooth acceleration to set speed # when the leads are no factor. v_lower = v_ego + (T_IDXS * self.cruise_min_a * 1.05) + # TODO does this make sense when max_a is negative? v_upper = v_ego + (T_IDXS * self.max_a * 1.05) v_cruise_clipped = np.clip(v_cruise * np.ones(N+1), v_lower, diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index c087005bb9fa24..0280f3e936bde8 100644 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -22,7 +22,7 @@ A_CRUISE_MAX_BP = [0., 10.0, 25., 40.] CONTROL_N_T_IDX = ModelConstants.T_IDXS[:CONTROL_N] ALLOW_THROTTLE_THRESHOLD = 0.5 -ACCEL_LIMIT_MARGIN = 0.05 +MIN_ALLOW_THROTTLE_SPEED = 2.5 # Lookup table for turns _A_TOTAL_MAX_V = [1.7, 3.2] @@ -58,7 +58,10 @@ def get_accel_from_plan(CP, speeds, accels): a_target_now = interp(DT_MDL, CONTROL_N_T_IDX, accels) v_target = interp(CP.longitudinalActuatorDelay + DT_MDL, CONTROL_N_T_IDX, speeds) - a_target = 2 * (v_target - v_target_now) / CP.longitudinalActuatorDelay - a_target_now + if v_target != v_target_now: + a_target = 2 * (v_target - v_target_now) / CP.longitudinalActuatorDelay - a_target_now + else: + a_target = a_target_now v_target_1sec = interp(CP.longitudinalActuatorDelay + DT_MDL + 1.0, CONTROL_N_T_IDX, speeds) else: @@ -183,7 +186,7 @@ def parse_model(model_msg, model_error, v_ego, taco_tune): throttle_prob = 1.0 return x, v, a, j, throttle_prob - def update(self, radarless_model, secretgoodopenpilot_model, sm, frogpilot_toggles): + def update(self, classic_model, radarless_model, sm, frogpilot_toggles): self.mpc.mode = 'blended' if sm['controlsState'].experimentalMode else 'acc' if len(sm['carControl'].orientationNED) == 3: @@ -209,7 +212,8 @@ def update(self, radarless_model, secretgoodopenpilot_model, sm, frogpilot_toggl accel_limits = [sm['frogpilotPlan'].minAcceleration, sm['frogpilotPlan'].maxAcceleration] if self.mpc.mode == 'acc': - accel_limits_turns = limit_accel_in_turns(v_ego, sm['carState'].steeringAngleDeg, accel_limits, self.CP) + steer_angle_without_offset = sm['carState'].steeringAngleDeg - sm['liveParameters'].angleOffsetDeg + accel_limits_turns = limit_accel_in_turns(v_ego, steer_angle_without_offset, accel_limits, self.CP) else: accel_limits_turns = [ACCEL_MIN, ACCEL_MAX] @@ -223,12 +227,13 @@ def update(self, radarless_model, secretgoodopenpilot_model, sm, frogpilot_toggl # Compute model v_ego error self.v_model_error = get_speed_error(sm['modelV2'], v_ego) x, v, a, j, throttle_prob = self.parse_model(sm['modelV2'], self.v_model_error, v_ego, frogpilot_toggles.taco_tune) - self.allow_throttle = throttle_prob > ALLOW_THROTTLE_THRESHOLD + # Don't clip at low speeds since throttle_prob doesn't account for creep + self.allow_throttle = throttle_prob > ALLOW_THROTTLE_THRESHOLD or v_ego <= MIN_ALLOW_THROTTLE_SPEED - if not self.allow_throttle and v_ego > 5.0 and secretgoodopenpilot_model: # Don't clip at low speeds since throttle_prob doesn't account for creep - # MPC breaks when accel limits would cause negative velocity within the MPC horizon, so we clip the max accel limit at vEgo/T_MAX plus a bit of margin - clipped_accel_coast = max(accel_coast, accel_limits_turns[0], -v_ego / T_IDXS_MPC[-1] + ACCEL_LIMIT_MARGIN) - accel_limits_turns[1] = min(accel_limits_turns[1], clipped_accel_coast) + if not self.allow_throttle and not classic_model: + clipped_accel_coast = max(accel_coast, accel_limits_turns[0]) + clipped_accel_coast_interp = interp(v_ego, [MIN_ALLOW_THROTTLE_SPEED, MIN_ALLOW_THROTTLE_SPEED*2], [accel_limits_turns[1], clipped_accel_coast]) + accel_limits_turns[1] = min(accel_limits_turns[1], clipped_accel_coast_interp) if force_slow_decel: v_cruise = 0.0 @@ -242,8 +247,9 @@ def update(self, radarless_model, secretgoodopenpilot_model, sm, frogpilot_toggl lead_states = [self.lead_one, self.lead_two] for index in range(len(lead_states)): if len(model_leads) > index: + distance_offset = max(frogpilot_toggles.increased_stopped_distance + min(10 - v_ego, 0), 0) if not sm['frogpilotCarState'].trafficModeActive else 0 model_lead = model_leads[index] - lead_states[index].update(model_lead.x[0], model_lead.y[0], model_lead.v[0], model_lead.a[0], model_lead.prob) + lead_states[index].update(model_lead.x[0] - distance_offset, model_lead.y[0], model_lead.v[0], model_lead.a[0], model_lead.prob) else: lead_states[index].reset() else: @@ -254,7 +260,7 @@ def update(self, radarless_model, secretgoodopenpilot_model, sm, frogpilot_toggl self.mpc.set_accel_limits(accel_limits_turns[0], accel_limits_turns[1]) self.mpc.set_cur_state(self.v_desired_filter.x, self.a_desired) self.mpc.update(self.lead_one, self.lead_two, sm['frogpilotPlan'].vCruise, x, v, a, j, radarless_model, sm['frogpilotPlan'].tFollow, - sm['frogpilotCarState'].trafficModeActive, frogpilot_toggles, personality=sm['controlsState'].personality) + sm['frogpilotCarState'].trafficModeActive, personality=sm['controlsState'].personality) self.a_desired_trajectory_full = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.a_solution) self.v_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.v_solution) diff --git a/selfdrive/controls/plannerd.py b/selfdrive/controls/plannerd.py index 3bae4ca0a12229..3c8f51ab253eb0 100644 --- a/selfdrive/controls/plannerd.py +++ b/selfdrive/controls/plannerd.py @@ -6,7 +6,7 @@ from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner import cereal.messaging as messaging -from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables +from openpilot.selfdrive.frogpilot.frogpilot_variables import get_frogpilot_toggles def publish_ui_plan(sm, pm, longitudinal_planner): ui_send = messaging.new_message('uiPlan') @@ -30,31 +30,26 @@ def plannerd_thread(): longitudinal_planner = LongitudinalPlanner(CP) pm = messaging.PubMaster(['longitudinalPlan', 'uiPlan']) - sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'radarState', 'modelV2', 'frogpilotCarControl', 'frogpilotCarState', 'frogpilotPlan'], + sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'liveParameters', 'radarState', 'modelV2', + 'frogpilotCarControl', 'frogpilotCarState', 'frogpilotPlan'], poll='modelV2', ignore_avg_freq=['radarState']) # FrogPilot variables - frogpilot_toggles = FrogPilotVariables.toggles - FrogPilotVariables.update_frogpilot_params() + frogpilot_toggles = get_frogpilot_toggles(True) + classic_model = frogpilot_toggles.classic_model radarless_model = frogpilot_toggles.radarless_model - secretgoodopenpilot_model = frogpilot_toggles.secretgoodopenpilot_model - - update_toggles = False while True: sm.update() if sm.updated['modelV2']: - longitudinal_planner.update(radarless_model, secretgoodopenpilot_model, sm, frogpilot_toggles) + longitudinal_planner.update(classic_model, radarless_model, sm, frogpilot_toggles) longitudinal_planner.publish(sm, pm) publish_ui_plan(sm, pm, longitudinal_planner) # Update FrogPilot parameters - if FrogPilotVariables.toggles_updated: - update_toggles = True - elif update_toggles: - FrogPilotVariables.update_frogpilot_params() - update_toggles = False + if sm['frogpilotPlan'].togglesUpdated: + frogpilot_toggles = get_frogpilot_toggles() def main(): plannerd_thread() diff --git a/selfdrive/controls/radard.py b/selfdrive/controls/radard.py index 2f44a8d6a032ca..9249d6231ef1af 100644 --- a/selfdrive/controls/radard.py +++ b/selfdrive/controls/radard.py @@ -2,6 +2,7 @@ import importlib import math from collections import deque +from types import SimpleNamespace from typing import Any import capnp @@ -13,7 +14,7 @@ from openpilot.common.simple_kalman import KF1D -from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables +from openpilot.selfdrive.frogpilot.frogpilot_variables import get_frogpilot_toggles # Default lead acceleration decay set to 50% at 1s _LEAD_ACCEL_TAU = 1.5 @@ -108,6 +109,19 @@ def get_RadarState(self, model_prob: float = 0.0): "radarTrackId": self.identifier, } + def potential_adjacent_lead(self, far: bool, lane_width: float, left: bool, model_data: capnp._DynamicStructReader): + adjacent_lane_max = float('inf') if far else lane_width * 1.5 + adjacent_lane_min = max(lane_width * 1.5, 4.5) if far else max(lane_width * 0.5, 1.5) + + y_delta = self.yRel + interp(self.dRel, model_data.position.x, model_data.position.y) + + if left and adjacent_lane_min < y_delta < adjacent_lane_max: + return True + elif not left and adjacent_lane_min < -y_delta < adjacent_lane_max: + return True + else: + return False + def potential_low_speed_lead(self, v_ego: float): # stop for stuff in front of you and low speed, even without model confirmation # Radar points closer than 0.75, are almost always glitches on toyota radars @@ -168,9 +182,9 @@ def get_RadarState_from_vision(lead_msg: capnp._DynamicStructReader, v_ego: floa def get_lead(v_ego: float, ready: bool, tracks: dict[int, Track], lead_msg: capnp._DynamicStructReader, - model_v_ego: float, lead_detection_threshold: float, low_speed_override: bool = True) -> dict[str, Any]: + model_v_ego: float, frogpilot_toggles: SimpleNamespace, frogpilotCarState: capnp._DynamicStructReader, low_speed_override: bool = True) -> dict[str, Any]: # Determine leads, this is where the essential logic happens - if len(tracks) > 0 and ready and lead_msg.prob > lead_detection_threshold: + if len(tracks) > 0 and ready and lead_msg.prob > frogpilot_toggles.lead_detection_probability: track = match_vision_to_track(v_ego, lead_msg, tracks) else: track = None @@ -178,7 +192,7 @@ def get_lead(v_ego: float, ready: bool, tracks: dict[int, Track], lead_msg: capn lead_dict = {'status': False} if track is not None: lead_dict = track.get_RadarState(lead_msg.prob) - elif (track is None) and ready and (lead_msg.prob > lead_detection_threshold): + elif (track is None) and ready and (lead_msg.prob > frogpilot_toggles.lead_detection_probability): lead_dict = get_RadarState_from_vision(lead_msg, v_ego, model_v_ego) if low_speed_override: @@ -190,11 +204,25 @@ def get_lead(v_ego: float, ready: bool, tracks: dict[int, Track], lead_msg: capn if (not lead_dict['status']) or (closest_track.dRel < lead_dict['dRel']): lead_dict = closest_track.get_RadarState() + if 'dRel' in lead_dict: + lead_dict['dRel'] -= max(frogpilot_toggles.increased_stopped_distance + min(10 - v_ego, 0), 0) if not frogpilotCarState.trafficModeActive else 0 + + return lead_dict + + +def get_lead_adjacent(tracks: dict[int, Track], model_data: capnp._DynamicStructReader, lane_width: float, left: bool = True, far: bool = False) -> dict[str, Any]: + lead_dict = {'status': False} + + adjacent_tracks = [c for c in tracks.values() if c.vLead > 1 and c.potential_adjacent_lead(far, lane_width, left, model_data)] + if len(adjacent_tracks) > 0: + closest_track = min(adjacent_tracks, key=lambda c: c.dRel) + lead_dict = closest_track.get_RadarState() + return lead_dict class RadarD: - def __init__(self, radar_ts: float, delay: int = 0): + def __init__(self, frogpilot_toggles, radar_ts: float, delay: int = 0): self.points: dict[int, tuple[float, float, float]] = {} self.current_time = 0.0 @@ -213,12 +241,9 @@ def __init__(self, radar_ts: float, delay: int = 0): self.ready = False # FrogPilot variables - self.frogpilot_toggles = FrogPilotVariables.toggles - FrogPilotVariables.update_frogpilot_params() - - self.velocity_model = self.frogpilot_toggles.velocity_model + self.frogpilot_toggles = frogpilot_toggles - self.update_toggles = False + self.classic_model = self.frogpilot_toggles.classic_model def update(self, sm: messaging.SubMaster, rr): self.ready = sm.seen['modelV2'] @@ -263,23 +288,26 @@ def update(self, sm: messaging.SubMaster, rr): self.radar_state.radarErrors = list(radar_errors) self.radar_state.carStateMonoTime = sm.logMonoTime['carState'] - if self.velocity_model and len(sm['modelV2'].velocity.x): - model_v_ego = sm['modelV2'].velocity.x[0] - elif len(sm['modelV2'].temporalPose.trans): + if self.classic_model and len(sm['modelV2'].temporalPose.trans): model_v_ego = sm['modelV2'].temporalPose.trans[0] + elif len(sm['modelV2'].velocity.x): + model_v_ego = sm['modelV2'].velocity.x[0] else: model_v_ego = self.v_ego leads_v3 = sm['modelV2'].leadsV3 if len(leads_v3) > 1: - self.radar_state.leadOne = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[0], model_v_ego, self.frogpilot_toggles.lead_detection_threshold, low_speed_override=True) - self.radar_state.leadTwo = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[1], model_v_ego, self.frogpilot_toggles.lead_detection_threshold, low_speed_override=False) + self.radar_state.leadOne = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[0], model_v_ego, self.frogpilot_toggles, sm['frogpilotCarState'], low_speed_override=True) + self.radar_state.leadTwo = get_lead(self.v_ego, self.ready, self.tracks, leads_v3[1], model_v_ego, self.frogpilot_toggles, sm['frogpilotCarState'], low_speed_override=False) + + if self.frogpilot_toggles.adjacent_lead_tracking and self.ready: + self.radar_state.leadLeft = get_lead_adjacent(self.tracks, sm['modelV2'], sm['frogpilotPlan'].laneWidthLeft, left=True) + self.radar_state.leadLeftFar = get_lead_adjacent(self.tracks, sm['modelV2'], sm['frogpilotPlan'].laneWidthLeft, left=True, far=True) + self.radar_state.leadRight = get_lead_adjacent(self.tracks, sm['modelV2'], sm['frogpilotPlan'].laneWidthRight, left=False) + self.radar_state.leadRightFar = get_lead_adjacent(self.tracks, sm['modelV2'], sm['frogpilotPlan'].laneWidthRight, left=False, far=True) # Update FrogPilot parameters - if FrogPilotVariables.toggles_updated: - self.update_toggles = True - elif self.update_toggles: - FrogPilotVariables.update_frogpilot_params() - self.update_toggles = False + if sm['frogpilotPlan'].togglesUpdated: + self.frogpilot_toggles = get_frogpilot_toggles() def publish(self, pm: messaging.PubMaster, lag_ms: float): assert self.radar_state is not None @@ -348,14 +376,14 @@ def main(): RI = RadarInterface(CP) rk = Ratekeeper(1.0 / CP.radarTimeStep, print_delay_threshold=None) - RD = RadarD(CP.radarTimeStep, RI.delay) # FrogPilot variables - frogpilot_toggles = FrogPilotVariables.toggles - FrogPilotVariables.update_frogpilot_params() + frogpilot_toggles = get_frogpilot_toggles(True) + + RD = RadarD(frogpilot_toggles, CP.radarTimeStep, RI.delay) if not frogpilot_toggles.radarless_model: - sm = messaging.SubMaster(['modelV2', 'carState'], frequency=int(1./DT_CTRL)) + sm = messaging.SubMaster(['modelV2', 'carState', 'frogpilotCarState', 'frogpilotPlan'], frequency=int(1./DT_CTRL)) pm = messaging.PubMaster(['radarState', 'liveTracks']) while 1: can_strings = messaging.drain_sock_raw(can_sock, wait_for_one=True) diff --git a/selfdrive/frogpilot/assets/active_theme/colors/colors.json b/selfdrive/frogpilot/assets/active_theme/colors/colors.json index 80e70f351c2180..bf527d51eb243e 100644 --- a/selfdrive/frogpilot/assets/active_theme/colors/colors.json +++ b/selfdrive/frogpilot/assets/active_theme/colors/colors.json @@ -23,12 +23,6 @@ "blue": 54, "alpha": 242 }, - "RoadEdges": { - "red": 23, - "green": 134, - "blue": 68, - "alpha": 242 - }, "Sidebar1": { "red": 23, "green": 134, diff --git a/selfdrive/frogpilot/assets/download_functions.py b/selfdrive/frogpilot/assets/download_functions.py index 9057c2dbce593d..0a642e53cc94d9 100644 --- a/selfdrive/frogpilot/assets/download_functions.py +++ b/selfdrive/frogpilot/assets/download_functions.py @@ -1,7 +1,7 @@ import os import requests -from openpilot.selfdrive.frogpilot.frogpilot_functions import delete_file, is_url_pingable +from openpilot.selfdrive.frogpilot.frogpilot_utilities import delete_file, is_url_pingable GITHUB_URL = "https://raw.githubusercontent.com/FrogAi/FrogPilot-Resources/" GITLAB_URL = "https://gitlab.com/FrogAi/FrogPilot-Resources/-/raw/" @@ -44,10 +44,10 @@ def handle_error(destination, error_message, error, download_param, progress_par def handle_request_error(error, destination, download_param, progress_param, params_memory): error_map = { - requests.HTTPError: lambda e: f"Server error ({e.response.status_code})" if e.response else "Server error.", requests.ConnectionError: "Connection dropped.", - requests.Timeout: "Download timed out.", - requests.RequestException: "Network request error. Check connection." + requests.HTTPError: lambda e: f"Server error ({e.response.status_code})" if e.response else "Server error.", + requests.RequestException: "Network request error. Check connection.", + requests.Timeout: "Download timed out." } error_message = error_map.get(type(error), "Unexpected error.") @@ -75,19 +75,6 @@ def get_repository_url(): return GITLAB_URL return None -def link_valid(url): - try: - response = requests.head(url, allow_redirects=True, timeout=5) - response.raise_for_status() - return True - except requests.HTTPError as e: - if e.response.status_code != 404: - handle_request_error(e, None, None, None, None) - return False - except Exception as e: - handle_request_error(e, None, None, None, None) - return False - def verify_download(file_path, url, initial_download=True): remote_file_size = get_remote_file_size(url) if remote_file_size is None: diff --git a/selfdrive/frogpilot/assets/holiday_themes/halloween_week/colors/colors.json b/selfdrive/frogpilot/assets/holiday_themes/halloween_week/colors/colors.json index 9fca352351cac6..91690ba0b21a35 100644 --- a/selfdrive/frogpilot/assets/holiday_themes/halloween_week/colors/colors.json +++ b/selfdrive/frogpilot/assets/holiday_themes/halloween_week/colors/colors.json @@ -23,12 +23,6 @@ "blue": 255, "alpha": 255 }, - "RoadEdges": { - "red": 242, - "green": 117, - "blue": 3, - "alpha": 255 - }, "Sidebar1": { "red": 134, "green": 47, diff --git a/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/colors/colors.json b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/colors/colors.json new file mode 100644 index 00000000000000..bf527d51eb243e --- /dev/null +++ b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/colors/colors.json @@ -0,0 +1,44 @@ +{ + "LaneLines": { + "red": 23, + "green": 134, + "blue": 68, + "alpha": 242 + }, + "LeadMarker": { + "red": 23, + "green": 134, + "blue": 68, + "alpha": 242 + }, + "Path": { + "red": 23, + "green": 134, + "blue": 68, + "alpha": 242 + }, + "PathEdge": { + "red": 18, + "green": 107, + "blue": 54, + "alpha": 242 + }, + "Sidebar1": { + "red": 23, + "green": 134, + "blue": 68, + "alpha": 242 + }, + "Sidebar2": { + "red": 23, + "green": 134, + "blue": 68, + "alpha": 242 + }, + "Sidebar3": { + "red": 23, + "green": 134, + "blue": 68, + "alpha": 242 + } +} diff --git a/selfdrive/frogpilot/assets/holiday_themes/halloween_week/distance_icons/aggressive.gif b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/distance_icons/aggressive.gif similarity index 100% rename from selfdrive/frogpilot/assets/holiday_themes/halloween_week/distance_icons/aggressive.gif rename to selfdrive/frogpilot/assets/holiday_themes/world_frog_day/distance_icons/aggressive.gif diff --git a/selfdrive/frogpilot/assets/holiday_themes/halloween_week/distance_icons/relaxed.gif b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/distance_icons/relaxed.gif similarity index 100% rename from selfdrive/frogpilot/assets/holiday_themes/halloween_week/distance_icons/relaxed.gif rename to selfdrive/frogpilot/assets/holiday_themes/world_frog_day/distance_icons/relaxed.gif diff --git a/selfdrive/frogpilot/assets/holiday_themes/halloween_week/distance_icons/standard.gif b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/distance_icons/standard.gif similarity index 100% rename from selfdrive/frogpilot/assets/holiday_themes/halloween_week/distance_icons/standard.gif rename to selfdrive/frogpilot/assets/holiday_themes/world_frog_day/distance_icons/standard.gif diff --git a/selfdrive/frogpilot/assets/holiday_themes/halloween_week/distance_icons/traffic.gif b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/distance_icons/traffic.gif similarity index 100% rename from selfdrive/frogpilot/assets/holiday_themes/halloween_week/distance_icons/traffic.gif rename to selfdrive/frogpilot/assets/holiday_themes/world_frog_day/distance_icons/traffic.gif diff --git a/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/icons/button_home.gif b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/icons/button_home.gif index 6b20a726e4ce2f..133d8d34ed1c00 100644 Binary files a/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/icons/button_home.gif and b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/icons/button_home.gif differ diff --git a/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/traditional_100 b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/traditional_100 new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_1.png b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_1.png index 43e0b446f8ea9d..8c2f7a54ed26a3 100644 Binary files a/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_1.png and b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_1.png differ diff --git a/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_2.png b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_2.png index e8e147976b7330..3c7d2d9eb8a0ad 100644 Binary files a/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_2.png and b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_2.png differ diff --git a/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_3.png b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_3.png index b59b003f95eb2f..b495eeae190cbc 100644 Binary files a/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_3.png and b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_3.png differ diff --git a/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_4.png b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_4.png index c3c1d204e68375..024a0b03ea6c08 100644 Binary files a/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_4.png and b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_4.png differ diff --git a/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_5.png b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_5.png new file mode 100644 index 00000000000000..91eb05f12f4579 Binary files /dev/null and b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_5.png differ diff --git a/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_6.png b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_6.png new file mode 100644 index 00000000000000..3c7d2d9eb8a0ad Binary files /dev/null and b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_6.png differ diff --git a/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_blindspot.png b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_blindspot.png new file mode 100644 index 00000000000000..b721266fb2f1b1 Binary files /dev/null and b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/signals/turn_signal_blindspot.png differ diff --git a/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/steering_wheel/wheel.png b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/steering_wheel/wheel.png new file mode 100644 index 00000000000000..712255984600c0 Binary files /dev/null and b/selfdrive/frogpilot/assets/holiday_themes/world_frog_day/steering_wheel/wheel.png differ diff --git a/selfdrive/frogpilot/assets/model_manager.py b/selfdrive/frogpilot/assets/model_manager.py index b4913ac61d347b..92de018d3e3bf4 100644 --- a/selfdrive/frogpilot/assets/model_manager.py +++ b/selfdrive/frogpilot/assets/model_manager.py @@ -1,26 +1,31 @@ import json import os import re +import requests import shutil import time +import urllib.parse import urllib.request from openpilot.common.basedir import BASEDIR -from openpilot.common.params import Params, UnknownKeyName +from openpilot.common.params import Params -from openpilot.selfdrive.frogpilot.assets.download_functions import GITHUB_URL, GITLAB_URL, download_file, get_remote_file_size, get_repository_url, handle_error, handle_request_error, verify_download -from openpilot.selfdrive.frogpilot.frogpilot_functions import MODELS_PATH, delete_file +from openpilot.selfdrive.frogpilot.assets.download_functions import GITHUB_URL, GITLAB_URL, download_file, get_repository_url, handle_error, handle_request_error, verify_download +from openpilot.selfdrive.frogpilot.frogpilot_functions import MODELS_PATH +from openpilot.selfdrive.frogpilot.frogpilot_utilities import delete_file -VERSION = "v9" +VERSION = "v10" DEFAULT_MODEL = "north-dakota" DEFAULT_MODEL_NAME = "North Dakota (Default)" + def process_model_name(model_name): cleaned_name = re.sub(r'[🗺️👀📡]', '', model_name) cleaned_name = re.sub(r'[^a-zA-Z0-9()-]', '', cleaned_name) return cleaned_name.replace(' ', '').replace('(Default)', '').replace('-', '') + class ModelManager: def __init__(self): self.params = Params() @@ -30,6 +35,63 @@ def __init__(self): self.download_param = "ModelToDownload" self.download_progress_param = "ModelDownloadProgress" + @staticmethod + def fetch_models(url): + try: + with urllib.request.urlopen(url, timeout=10) as response: + return json.loads(response.read().decode('utf-8'))['models'] + except Exception as error: + handle_request_error(error, None, None, None, None) + return [] + + @staticmethod + def fetch_all_model_sizes(repo_url): + project_path = "FrogAi/FrogPilot-Resources" + branch = "Models" + + if "github" in repo_url: + api_url = f"https://api.github.com/repos/{project_path}/contents?ref={branch}" + elif "gitlab" in repo_url: + api_url = f"https://gitlab.com/api/v4/projects/{urllib.parse.quote_plus(project_path)}/repository/tree?ref={branch}" + else: + raise ValueError(f"Unsupported repository URL format: {repo_url}. Supported formats are GitHub and GitLab URLs.") + + try: + response = requests.get(api_url) + response.raise_for_status() + thneed_files = [file for file in response.json() if file['name'].endswith('.thneed')] + + if "gitlab" in repo_url: + model_sizes = {} + for file in thneed_files: + file_path = file['path'] + metadata_url = f"https://gitlab.com/api/v4/projects/{urllib.parse.quote_plus(project_path)}/repository/files/{urllib.parse.quote_plus(file_path)}/raw?ref={branch}" + metadata_response = requests.head(metadata_url) + metadata_response.raise_for_status() + model_sizes[file['name'].replace('.thneed', '')] = int(metadata_response.headers.get('content-length', 0)) + return model_sizes + else: + return {file['name'].replace('.thneed', ''): file['size'] for file in thneed_files if 'size' in file} + + except requests.RequestException as e: + raise ConnectionError(f"Failed to fetch model sizes from {'GitHub' if 'github.com' in repo_url else 'GitLab'}: {e}") + + @staticmethod + def copy_default_model(): + classic_default_model_path = os.path.join(MODELS_PATH, f"{DEFAULT_MODEL}.thneed") + source_path = os.path.join(BASEDIR, "selfdrive", "classic_modeld", "models", "supercombo.thneed") + + if os.path.isfile(source_path): + shutil.copyfile(source_path, classic_default_model_path) + print(f"Copied the classic default model from {source_path} to {classic_default_model_path}") + + default_model_path = os.path.join(MODELS_PATH, "secret-good-openpilot.thneed") + source_path = os.path.join(BASEDIR, "selfdrive", "modeld", "models", "supercombo.thneed") + + if os.path.isfile(source_path): + shutil.copyfile(source_path, default_model_path) + print(f"Copied the default model from {source_path} to {default_model_path}") + def handle_verification_failure(self, model, model_path): if self.params_memory.get_bool(self.cancel_download_param): return @@ -65,65 +127,53 @@ def download_model(self, model_to_download): else: self.handle_verification_failure(model_to_download, model_path) - def fetch_models(self, url): - try: - with urllib.request.urlopen(url, timeout=10) as response: - return json.loads(response.read().decode('utf-8'))['models'] - except Exception as error: - handle_request_error(error, None, None, None, None) - return [] + def queue_model_download(self, model, model_name=None): + while self.params_memory.get(self.download_param, encoding='utf-8'): + time.sleep(1) - def update_model_params(self, model_info, repo_url): - available_models, available_model_names, experimental_models, navigation_models, radarless_models, velocity_models = [], [], [], [], [], [] + self.params_memory.put(self.download_param, model) + if model_name: + self.params_memory.put(self.download_progress_param, f"Downloading {model_name}...") + def update_model_params(self, model_info, repo_url): + available_models = [] for model in model_info: available_models.append(model['id']) - available_model_names.append(model['name']) - - if model.get("e2e_longitudinal", False): - e2e_longitudinal_models.append(model['id']) - if model.get("experimental", False): - experimental_models.append(model['id']) - if model.get("gas_brake", False): - gas_brake_models.append(model['id']) - if model.get("velocity", False): - velocity_models.append(model['id']) - if "🗺️" in model['name']: - navigation_models.append(model['id']) - if "📡" not in model['name']: - radarless_models.append(model['id']) self.params.put_nonblocking("AvailableModels", ','.join(available_models)) - self.params.put_nonblocking("AvailableModelsNames", ','.join(available_model_names)) - self.params.put_nonblocking("ExperimentalModels", ','.join(experimental_models)) - self.params.put_nonblocking("NavigationModels", ','.join(navigation_models)) - self.params.put_nonblocking("RadarlessModels", ','.join(radarless_models)) - self.params.put_nonblocking("VelocityModels", ','.join(velocity_models)) + self.params.put_nonblocking("AvailableModelsNames", ','.join([model['name'] for model in model_info])) + self.params.put_nonblocking("ClassicModels", ','.join([model['id'] for model in model_info if model.get("classic_model", False)])) + self.params.put_nonblocking("ExperimentalModels", ','.join([model['id'] for model in model_info if model.get("experimental", False)])) + self.params.put_nonblocking("NavigationModels", ','.join([model['id'] for model in model_info if "🗺️" in model['name']])) + self.params.put_nonblocking("RadarlessModels", ','.join([model['id'] for model in model_info if "📡" not in model['name']])) print("Models list updated successfully.") if available_models: - models_downloaded = self.are_all_models_downloaded(available_models, available_model_names, repo_url) + models_downloaded = self.are_all_models_downloaded(available_models, repo_url) self.params.put_bool_nonblocking("ModelsDownloaded", models_downloaded) - def are_all_models_downloaded(self, available_models, available_model_names, repo_url): + def are_all_models_downloaded(self, available_models, repo_url): automatically_update_models = self.params.get_bool("AutomaticallyUpdateModels") all_models_downloaded = True + model_sizes = self.fetch_all_model_sizes(repo_url) download_queue = [] + for model in available_models: model_path = os.path.join(MODELS_PATH, f"{model}.thneed") - model_url = f"{repo_url}Models/{model}.thneed" + expected_size = model_sizes.get(model) + + if expected_size is None: + print(f"Size data for {model} not available.") + continue if os.path.isfile(model_path): - if automatically_update_models: - verify_result = verify_download(model_path, model_url, False) - if verify_result is None: - all_models_downloaded = False - elif not verify_result: - print(f"Model {model} is outdated. Re-downloading...") - delete_file(model_path) - download_queue.append(model) - all_models_downloaded = False + local_size = os.path.getsize(model_path) + if automatically_update_models and local_size != expected_size: + print(f"Model {model} is outdated. Re-downloading...") + delete_file(model_path) + download_queue.append(model) + all_models_downloaded = False else: if automatically_update_models: print(f"Model {model} isn't downloaded. Downloading...") @@ -135,14 +185,6 @@ def are_all_models_downloaded(self, available_models, available_model_names, rep return all_models_downloaded - def queue_model_download(self, model, model_name=None): - while self.params_memory.get(self.download_param, encoding='utf-8'): - time.sleep(1) - - self.params_memory.put(self.download_param, model) - if model_name: - self.params_memory.put(self.download_progress_param, f"Downloading {model_name}...") - def validate_models(self): current_model = self.params.get("Model", encoding='utf-8') current_model_name = self.params.get("ModelName", encoding='utf-8') @@ -162,31 +204,11 @@ def validate_models(self): for model_file in os.listdir(MODELS_PATH): model_name = model_file.replace(".thneed", "") if model_name not in available_models.split(','): - reason = "Model is not in the list of available models" if model_name == current_model: self.params.put_nonblocking("Model", DEFAULT_MODEL) self.params.put_nonblocking("ModelName", DEFAULT_MODEL_NAME) delete_file(os.path.join(MODELS_PATH, model_file)) - print(f"Deleted model file: {model_file} - Reason: {reason}") - - def copy_default_model(self): - default_model_path = os.path.join(MODELS_PATH, f"{DEFAULT_MODEL}.thneed") - source_path = os.path.join(BASEDIR, "selfdrive", "classic_modeld", "models", "supercombo.thneed") - - if os.path.isfile(source_path) and not os.path.isfile(default_model_path): - shutil.copyfile(source_path, default_model_path) - print(f"Copied default model from {source_path} to {default_model_path}") - else: - print(f"Source default model not found at {source_path}. Exiting...") - - sgo_model_path = os.path.join(MODELS_PATH, "secret-good-openpilot.thneed") - source_path = os.path.join(BASEDIR, "selfdrive", "modeld", "models", "supercombo.thneed") - - if os.path.isfile(source_path) and not os.path.isfile(sgo_model_path): - shutil.copyfile(source_path, sgo_model_path) - print(f"Copied 'secret-good-openpilot' model from {source_path} to {sgo_model_path}") - else: - print(f"Source 'secret-good-openpilot' model not found at {source_path}. Exiting...") + print(f"Deleted model file: {model_file} - Reason: Model is not in the list of available models") def update_models(self, boot_run=False): if boot_run: @@ -197,12 +219,12 @@ def update_models(self, boot_run=False): print("GitHub and GitLab are offline...") return + model_info = self.fetch_models(f"{repo_url}Versions/model_names_{VERSION}.json") + if model_info: + self.update_model_params(model_info, repo_url) + if boot_run: self.validate_models() - else: - model_info = self.fetch_models(f"{repo_url}Versions/model_names_{VERSION}.json") - if model_info: - self.update_model_params(model_info, repo_url) def download_all_models(self): repo_url = get_repository_url() @@ -230,10 +252,8 @@ def download_all_models(self): if not os.path.isfile(os.path.join(MODELS_PATH, f"{model}.thneed")): model_index = available_models.index(model) model_name = available_model_names[model_index] - cleaned_model_name = re.sub(r'[🗺️👀📡]', '', model_name).strip() print(f"Downloading model: {cleaned_model_name}") - self.queue_model_download(model, cleaned_model_name) while self.params_memory.get(self.download_param, encoding='utf-8'): diff --git a/selfdrive/frogpilot/assets/other_images/original_bg.jpg b/selfdrive/frogpilot/assets/other_images/original_bg.jpg deleted file mode 100644 index 13c36a7e240359..00000000000000 Binary files a/selfdrive/frogpilot/assets/other_images/original_bg.jpg and /dev/null differ diff --git a/selfdrive/frogpilot/assets/random_events/icons/this_is_fine.gif b/selfdrive/frogpilot/assets/random_events/icons/this_is_fine.gif new file mode 100644 index 00000000000000..3cc427f760cb64 Binary files /dev/null and b/selfdrive/frogpilot/assets/random_events/icons/this_is_fine.gif differ diff --git a/selfdrive/frogpilot/assets/random_events/sounds/this_is_fine.wav b/selfdrive/frogpilot/assets/random_events/sounds/this_is_fine.wav new file mode 100644 index 00000000000000..e43f4e59ccf6a8 Binary files /dev/null and b/selfdrive/frogpilot/assets/random_events/sounds/this_is_fine.wav differ diff --git a/selfdrive/frogpilot/assets/theme_manager.py b/selfdrive/frogpilot/assets/theme_manager.py index dabf060885eb6d..1977a6ecdec5ab 100644 --- a/selfdrive/frogpilot/assets/theme_manager.py +++ b/selfdrive/frogpilot/assets/theme_manager.py @@ -1,4 +1,5 @@ import glob +import json import os import re import requests @@ -11,12 +12,14 @@ from openpilot.common.basedir import BASEDIR from openpilot.common.params import Params -from openpilot.selfdrive.frogpilot.assets.download_functions import GITHUB_URL, GITLAB_URL, download_file, get_repository_url, handle_error, handle_request_error, link_valid, verify_download -from openpilot.selfdrive.frogpilot.frogpilot_functions import ACTIVE_THEME_PATH, THEME_SAVE_PATH, update_frogpilot_toggles +from openpilot.selfdrive.frogpilot.assets.download_functions import GITHUB_URL, GITLAB_URL, download_file, get_repository_url, handle_error, handle_request_error, verify_download +from openpilot.selfdrive.frogpilot.frogpilot_functions import ACTIVE_THEME_PATH, THEME_SAVE_PATH +from openpilot.selfdrive.frogpilot.frogpilot_utilities import update_frogpilot_toggles CANCEL_DOWNLOAD_PARAM = "CancelThemeDownload" DOWNLOAD_PROGRESS_PARAM = "ThemeDownloadProgress" + def update_theme_asset(asset_type, theme, holiday_theme, params): save_location = os.path.join(ACTIVE_THEME_PATH, asset_type) @@ -49,6 +52,7 @@ def update_theme_asset(asset_type, theme, holiday_theme, params): shutil.copytree(asset_location, save_location) print(f"Copied {asset_location} to {save_location}") + def update_wheel_image(image, holiday_theme=None, random_event=True): wheel_save_location = os.path.join(ACTIVE_THEME_PATH, "steering_wheel") @@ -74,6 +78,7 @@ def update_wheel_image(image, holiday_theme=None, random_event=True): print(f"Copied {source_file} to {destination_file}") break + class ThemeManager: def __init__(self): self.params = Params() @@ -90,8 +95,83 @@ def calculate_thanksgiving(year): @staticmethod def is_within_week_of(target_date, now): start_of_week = target_date - timedelta(days=target_date.weekday()) - end_of_week = start_of_week + timedelta(days=6) - return start_of_week <= now <= end_of_week + return start_of_week <= now < target_date + + @staticmethod + def fetch_files(url): + try: + response = requests.get(url, timeout=10) + response.raise_for_status() + return [name for name in re.findall(r'href="[^"]*\/blob\/[^"]*\/([^"]*)"', response.text) if name.lower() != "license"] + except Exception as error: + handle_request_error(error, None, None, None, None) + return [] + + @staticmethod + def fetch_assets(repo_url): + repo = "FrogAi/FrogPilot-Resources" + branches = ["Themes", "Distance-Icons", "Steering-Wheels"] + + assets = { + "themes": {}, + "distance_icons": [], + "wheels": [] + } + + if "github" in repo_url: + base_api_url = "https://api.github.com/repos" + elif "gitlab" in repo_url: + base_api_url = "https://gitlab.com/api/v4/projects" + repo = repo.replace("/", "%2F") + else: + print(f"Unsupported repository URL: {repo_url}") + return assets + + for branch in branches: + if "github" in repo_url: + api_url = f"{base_api_url}/{repo}/git/trees/{branch}?recursive=1" + elif "gitlab" in repo_url: + api_url = f"{base_api_url}/{repo}/repository/tree?ref={branch}&recursive=true" + + try: + print(f"Fetching assets from branch '{branch}': {api_url}") + response = requests.get(api_url, timeout=10) + response.raise_for_status() + content = response.json() + + if "github" in repo_url: + items = content.get('tree', []) + elif "gitlab" in repo_url: + items = content + + for item in items: + if item["type"] != "blob": + continue + + item_path = item["path"].lower() + if branch == "Themes": + theme_name = item["path"].split('/')[0] + assets["themes"].setdefault(theme_name, set()) + if "icons" in item_path: + assets["themes"][theme_name].add("icons") + elif "signals" in item_path: + assets["themes"][theme_name].add("signals") + elif "sounds" in item_path: + assets["themes"][theme_name].add("sounds") + else: + assets["themes"][theme_name].add("colors") + + elif branch == "Distance-Icons": + assets["distance_icons"].append(item["path"]) + + elif branch == "Steering-Wheels": + assets["wheels"].append(item["path"]) + + except requests.exceptions.RequestException as error: + print(f"Error occurred when fetching from branch '{branch}': {error}") + + assets["themes"] = {k: list(v) for k, v in assets["themes"].items()} + return assets def update_holiday(self): now = date.today() @@ -150,6 +230,7 @@ def update_active_theme(self): "wheel_image": ("wheel_image", "stock") } + theme_changed = False for asset, (asset_type, current_value) in asset_mappings.items(): if current_value != self.previous_assets.get(asset) or current_holiday_theme != self.previous_assets.get("holiday_theme"): print(f"Updating {asset}: {asset_type} with value {current_holiday_theme if current_holiday_theme else current_value}") @@ -160,9 +241,13 @@ def update_active_theme(self): update_theme_asset(asset_type, current_value, current_holiday_theme, self.params) self.previous_assets[asset] = current_value + theme_changed = True - self.previous_assets["holiday_theme"] = current_holiday_theme - update_frogpilot_toggles() + if theme_changed: + if current_holiday_theme: + self.previous_assets["holiday_theme"] = current_holiday_theme + self.params_memory.put_bool("ThemeUpdated", True) + update_frogpilot_toggles() def extract_zip(self, zip_file, extract_path): print(f"Extracting {zip_file} to {extract_path}") @@ -242,26 +327,6 @@ def download_theme(self, theme_component, theme_name, theme_param): self.handle_verification_failure(extentions, theme_component, theme_name, theme_param, download_path) - @staticmethod - def fetch_files(url): - try: - response = requests.get(url, timeout=10) - response.raise_for_status() - return [name for name in re.findall(r'href="[^"]*\/blob\/[^"]*\/([^"]*)"', response.text) if name.lower() != "license"] - except Exception as error: - handle_request_error(error, None, None, None, None) - return [] - - @staticmethod - def fetch_folders(url): - try: - response = requests.get(url, timeout=10) - response.raise_for_status() - return re.findall(r'href="[^"]*\/tree\/[^"]*\/([^"]*)"', response.text) - except Exception as error: - handle_request_error(error, None, None, None, None) - return [] - def update_theme_params(self, downloadable_colors, downloadable_distance_icons, downloadable_icons, downloadable_signals, downloadable_sounds, downloadable_wheels): def filter_existing_assets(assets, subfolder): existing_themes = { @@ -333,6 +398,7 @@ def validate_themes(self): def update_themes(self, boot_run=False): if not os.path.exists(THEME_SAVE_PATH): + print(f"Theme save path does not exist: {THEME_SAVE_PATH}") return repo_url = get_repository_url() @@ -343,34 +409,34 @@ def update_themes(self, boot_run=False): if boot_run: self.validate_themes() - if repo_url == GITHUB_URL: - base_url = "https://github.com/FrogAi/FrogPilot-Resources/blob/Themes/" - distance_icons_files = self.fetch_files("https://github.com/FrogAi/FrogPilot-Resources/blob/Distance-Icons") - wheel_files = self.fetch_files("https://github.com/FrogAi/FrogPilot-Resources/blob/Steering-Wheels") - else: - base_url = "https://gitlab.com/FrogAi/FrogPilot-Resources/-/blob/Themes/" - distance_icons_files = self.fetch_files("https://github.com/FrogAi/FrogPilot-Resources/blob/Distance-Icons") - wheel_files = self.fetch_files("https://gitlab.com/FrogAi/FrogPilot-Resources/-/blob/Steering-Wheels") + assets = self.fetch_assets(repo_url) - theme_folders = self.fetch_folders(base_url) downloadable_colors = [] downloadable_icons = [] downloadable_signals = [] downloadable_sounds = [] - for theme in theme_folders: + for theme, available_assets in assets["themes"].items(): theme_name = theme.replace('_', ' ').split('.')[0].title() + print(f"Checking theme: {theme_name}") - if link_valid(f"{base_url}{theme}/colors.zip"): + if "colors" in available_assets: downloadable_colors.append(theme_name) - if link_valid(f"{base_url}{theme}/icons.zip"): + if "icons" in available_assets: downloadable_icons.append(theme_name) - if link_valid(f"{base_url}{theme}/signals.zip"): + if "signals" in available_assets: downloadable_signals.append(theme_name) - if link_valid(f"{base_url}{theme}/sounds.zip"): + if "sounds" in available_assets: downloadable_sounds.append(theme_name) - downloadable_distance_icons = [distance_icons.replace('_', ' ').split('.')[0].title() for distance_icons in distance_icons_files] - downloadable_wheels = [wheel.replace('_', ' ').split('.')[0].title() for wheel in wheel_files] + downloadable_distance_icons = [distance_icon.replace('_', ' ').split('.')[0].title() for distance_icon in assets["distance_icons"]] + downloadable_wheels = [wheel.replace('_', ' ').split('.')[0].title() for wheel in assets["wheels"]] + + print(f"Downloadable Colors: {downloadable_colors}") + print(f"Downloadable Icons: {downloadable_icons}") + print(f"Downloadable Signals: {downloadable_signals}") + print(f"Downloadable Sounds: {downloadable_sounds}") + print(f"Downloadable Distance Icons: {downloadable_distance_icons}") + print(f"Downloadable Wheels: {downloadable_wheels}") self.update_theme_params(downloadable_colors, downloadable_distance_icons, downloadable_icons, downloadable_signals, downloadable_sounds, downloadable_wheels) diff --git a/selfdrive/frogpilot/assets/toggle_icons/advanced_quality_of_life.png b/selfdrive/frogpilot/assets/toggle_icons/advanced_quality_of_life.png deleted file mode 100644 index e84f90ac0e939c..00000000000000 Binary files a/selfdrive/frogpilot/assets/toggle_icons/advanced_quality_of_life.png and /dev/null differ diff --git a/selfdrive/frogpilot/assets/toggle_icons/icon_accessibility.png b/selfdrive/frogpilot/assets/toggle_icons/icon_accessibility.png new file mode 100644 index 00000000000000..cf80f5ef96b4b9 Binary files /dev/null and b/selfdrive/frogpilot/assets/toggle_icons/icon_accessibility.png differ diff --git a/selfdrive/frogpilot/assets/toggle_icons/icon_advanced.png b/selfdrive/frogpilot/assets/toggle_icons/icon_advanced.png deleted file mode 100644 index 1b3c52390d8c6a..00000000000000 Binary files a/selfdrive/frogpilot/assets/toggle_icons/icon_advanced.png and /dev/null differ diff --git a/selfdrive/frogpilot/assets/toggle_icons/icon_advanced_calibration.png b/selfdrive/frogpilot/assets/toggle_icons/icon_advanced_calibration.png deleted file mode 100644 index 25e9f247c6e5e0..00000000000000 Binary files a/selfdrive/frogpilot/assets/toggle_icons/icon_advanced_calibration.png and /dev/null differ diff --git a/selfdrive/frogpilot/assets/toggle_icons/icon_advanced_device.png b/selfdrive/frogpilot/assets/toggle_icons/icon_advanced_device.png index 6d577060820f17..f849ec1324c716 100644 Binary files a/selfdrive/frogpilot/assets/toggle_icons/icon_advanced_device.png and b/selfdrive/frogpilot/assets/toggle_icons/icon_advanced_device.png differ diff --git a/selfdrive/frogpilot/assets/toggle_icons/icon_advanced_longitudinal_tune.png b/selfdrive/frogpilot/assets/toggle_icons/icon_advanced_longitudinal_tune.png deleted file mode 100644 index 72a7e4057ac14e..00000000000000 Binary files a/selfdrive/frogpilot/assets/toggle_icons/icon_advanced_longitudinal_tune.png and /dev/null differ diff --git a/selfdrive/frogpilot/assets/toggle_icons/icon_advanced_model.png b/selfdrive/frogpilot/assets/toggle_icons/icon_advanced_model.png deleted file mode 100644 index c299a1e793e24f..00000000000000 Binary files a/selfdrive/frogpilot/assets/toggle_icons/icon_advanced_model.png and /dev/null differ diff --git a/selfdrive/frogpilot/assets/toggle_icons/icon_advanced_personality.png b/selfdrive/frogpilot/assets/toggle_icons/icon_advanced_personality.png deleted file mode 100644 index 2ce09a567a1c7b..00000000000000 Binary files a/selfdrive/frogpilot/assets/toggle_icons/icon_advanced_personality.png and /dev/null differ diff --git a/selfdrive/frogpilot/assets/toggle_icons/icon_advanced_road.png b/selfdrive/frogpilot/assets/toggle_icons/icon_advanced_road.png deleted file mode 100644 index 76513344845127..00000000000000 Binary files a/selfdrive/frogpilot/assets/toggle_icons/icon_advanced_road.png and /dev/null differ diff --git a/selfdrive/frogpilot/assets/toggle_icons/icon_customization.png b/selfdrive/frogpilot/assets/toggle_icons/icon_customization.png new file mode 100644 index 00000000000000..8c2fa95ca96586 Binary files /dev/null and b/selfdrive/frogpilot/assets/toggle_icons/icon_customization.png differ diff --git a/selfdrive/frogpilot/assets/toggle_icons/icon_green_light.png b/selfdrive/frogpilot/assets/toggle_icons/icon_green_light.png index cb47d7a81d4d58..b0c06f82134287 100644 Binary files a/selfdrive/frogpilot/assets/toggle_icons/icon_green_light.png and b/selfdrive/frogpilot/assets/toggle_icons/icon_green_light.png differ diff --git a/selfdrive/frogpilot/assets/toggle_icons/icon_personality.png b/selfdrive/frogpilot/assets/toggle_icons/icon_personality.png new file mode 100644 index 00000000000000..4f552c59b4752b Binary files /dev/null and b/selfdrive/frogpilot/assets/toggle_icons/icon_personality.png differ diff --git a/selfdrive/frogpilot/controls/frogpilot_planner.py b/selfdrive/frogpilot/controls/frogpilot_planner.py index 53789076f6756b..ad586694599dfb 100644 --- a/selfdrive/frogpilot/controls/frogpilot_planner.py +++ b/selfdrive/frogpilot/controls/frogpilot_planner.py @@ -12,7 +12,7 @@ from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_events import FrogPilotEvents from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_following import FrogPilotFollowing from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_vcruise import FrogPilotVCruise -from openpilot.selfdrive.frogpilot.frogpilot_functions import MovingAverageCalculator, calculate_lane_width, calculate_road_curvature, update_frogpilot_toggles +from openpilot.selfdrive.frogpilot.frogpilot_utilities import MovingAverageCalculator, calculate_lane_width, calculate_road_curvature from openpilot.selfdrive.frogpilot.frogpilot_variables import CRUISING_SPEED, MODEL_LENGTH, NON_DRIVING_GEARS, PLANNER_TIME, THRESHOLD class FrogPilotPlanner: @@ -29,23 +29,21 @@ def __init__(self): self.tracking_lead_mac = MovingAverageCalculator() self.lateral_check = False - self.lead_departing = False self.model_stopped = False self.slower_lead = False - self.taking_curve_quickly = False self.tracking_lead = False self.model_length = 0 self.road_curvature = 1 - self.tracking_lead_distance = 0 self.v_cruise = 0 def update(self, carState, controlsState, frogpilotCarControl, frogpilotCarState, frogpilotNavigation, modelData, radarless_model, radarState, frogpilot_toggles): if radarless_model: model_leads = list(modelData.leadsV3) if len(model_leads) > 0: + distance_offset = max(frogpilot_toggles.increased_stopped_distance + min(10 - v_ego, 0), 0) if not frogpilotCarState.trafficModeActive else 0 model_lead = model_leads[0] - self.lead_one.update(model_lead.x[0], model_lead.y[0], model_lead.v[0], model_lead.a[0], model_lead.prob) + self.lead_one.update(model_lead.x[0] - distance_offset, model_lead.y[0], model_lead.v[0], model_lead.a[0], model_lead.prob) else: self.lead_one.reset() else: @@ -55,41 +53,25 @@ def update(self, carState, controlsState, frogpilotCarControl, frogpilotCarState v_ego = max(carState.vEgo, 0) v_lead = self.lead_one.vLead - driving_gear = carState.gearShifter not in NON_DRIVING_GEARS - - distance_offset = max(frogpilot_toggles.increase_stopped_distance + min(10 - v_ego, 0), 0) if not frogpilotCarState.trafficModeActive else 0 - lead_distance = self.lead_one.dRel - distance_offset - stopping_distance = STOP_DISTANCE + distance_offset - self.frogpilot_acceleration.update(controlsState, frogpilotCarState, v_cruise, v_ego, frogpilot_toggles) run_cem = frogpilot_toggles.conditional_experimental_mode or frogpilot_toggles.force_stops or frogpilot_toggles.green_light_alert or frogpilot_toggles.show_stopping_point - if run_cem and (controlsState.enabled or frogpilotCarControl.alwaysOnLateralActive) and driving_gear: - self.cem.update(carState, frogpilotNavigation, modelData, v_ego, v_lead, frogpilot_toggles) + if run_cem and (controlsState.enabled or frogpilotCarControl.alwaysOnLateralActive) and carState.gearShifter not in NON_DRIVING_GEARS: + self.cem.update(carState, frogpilotCarState, frogpilotNavigation, modelData, v_ego, v_lead, frogpilot_toggles) else: self.cem.stop_light_detected = False - self.frogpilot_events.update(carState, controlsState, frogpilotCarControl, frogpilotCarState, modelData, frogpilot_toggles) - self.frogpilot_following.update(carState.aEgo, controlsState, frogpilotCarState, lead_distance, stopping_distance, v_ego, v_lead, frogpilot_toggles) + self.frogpilot_events.update(carState, controlsState, frogpilotCarControl, frogpilotCarState, self.lead_one.dRel, modelData, v_lead, frogpilot_toggles) + self.frogpilot_following.update(carState.aEgo, controlsState, frogpilotCarState, self.lead_one.dRel, v_ego, v_lead, frogpilot_toggles) - check_lane_width = frogpilot_toggles.adjacent_lanes or frogpilot_toggles.adjacent_path_metrics or frogpilot_toggles.blind_spot_path or frogpilot_toggles.lane_detection - if check_lane_width and v_ego >= frogpilot_toggles.minimum_lane_change_speed: + check_lane_width = frogpilot_toggles.adjacent_paths or frogpilot_toggles.adjacent_path_metrics or frogpilot_toggles.blind_spot_path or frogpilot_toggles.lane_detection + if check_lane_width and v_ego >= frogpilot_toggles.minimum_lane_change_speed or frogpilot_toggles.adjacent_lead_tracking: self.lane_width_left = calculate_lane_width(modelData.laneLines[0], modelData.laneLines[1], modelData.roadEdges[0]) self.lane_width_right = calculate_lane_width(modelData.laneLines[3], modelData.laneLines[2], modelData.roadEdges[1]) else: self.lane_width_left = 0 self.lane_width_right = 0 - if frogpilot_toggles.lead_departing_alert and self.tracking_lead and driving_gear and carState.standstill: - if self.tracking_lead_distance == 0: - self.tracking_lead_distance = lead_distance - - self.lead_departing = lead_distance - self.tracking_lead_distance > 1 - self.lead_departing &= v_lead > 1 - else: - self.lead_departing = False - self.tracking_lead_distance = 0 - self.lateral_check = v_ego >= frogpilot_toggles.pause_lateral_below_speed self.lateral_check |= frogpilot_toggles.pause_lateral_below_signal and not (carState.leftBlinker or carState.rightBlinker) self.lateral_check |= carState.standstill @@ -100,26 +82,20 @@ def update(self, carState, controlsState, frogpilotCarControl, frogpilotCarState self.road_curvature = calculate_road_curvature(modelData, v_ego) if not carState.standstill else 1 - if frogpilot_toggles.random_events and v_ego > CRUISING_SPEED and driving_gear: - self.taking_curve_quickly = v_ego > (1 / self.road_curvature)**0.75 * 2 > CRUISING_SPEED * 2 and abs(carState.steeringAngleDeg) > 30 - else: - self.taking_curve_quickly = False - - self.tracking_lead = self.set_lead_status(lead_distance, stopping_distance, v_ego) + self.tracking_lead = self.set_lead_status(frogpilotCarState, v_ego, frogpilot_toggles) self.v_cruise = self.frogpilot_vcruise.update(carState, controlsState, frogpilotCarControl, frogpilotCarState, frogpilotNavigation, modelData, v_cruise, v_ego, frogpilot_toggles) - if self.frogpilot_events.frame == 1: # Force update to check the current state of "Always On Lateral" and holiday theme - update_frogpilot_toggles() + def set_lead_status(self, frogpilotCarState, v_ego, frogpilot_toggles): + distance_offset = max(frogpilot_toggles.increased_stopped_distance + min(10 - v_ego, 0), 0) if not frogpilotCarState.trafficModeActive else 0 - def set_lead_status(self, lead_distance, stopping_distance, v_ego): following_lead = self.lead_one.status - following_lead &= 1 < lead_distance < self.model_length + stopping_distance + following_lead &= 1 < self.lead_one.dRel - distance_offset < self.model_length + STOP_DISTANCE following_lead &= v_ego > CRUISING_SPEED or self.tracking_lead self.tracking_lead_mac.add_data(following_lead) return self.tracking_lead_mac.get_moving_average() >= THRESHOLD - def publish(self, sm, pm, frogpilot_toggles): + def publish(self, sm, pm, frogpilot_toggles, toggles_updated): frogpilot_plan_send = messaging.new_message('frogpilotPlan') frogpilot_plan_send.valid = sm.all_checks(service_list=['carState', 'controlsState']) frogpilotPlan = frogpilot_plan_send.frogpilotPlan @@ -142,6 +118,7 @@ def publish(self, sm, pm, frogpilot_toggles): frogpilotPlan.experimentalMode = self.cem.experimental_mode or self.frogpilot_vcruise.slc.experimental_mode frogpilotPlan.forcingStop = self.frogpilot_vcruise.forcing_stop + frogpilotPlan.forcingStopLength = self.frogpilot_vcruise.tracked_model_length frogpilotPlan.frogpilotEvents = self.frogpilot_events.events.to_msg() @@ -162,6 +139,8 @@ def publish(self, sm, pm, frogpilot_toggles): frogpilotPlan.speedLimitChanged = self.frogpilot_vcruise.speed_limit_changed frogpilotPlan.unconfirmedSlcSpeedLimit = self.frogpilot_vcruise.slc.desired_speed_limit + frogpilotPlan.togglesUpdated = toggles_updated + frogpilotPlan.vCruise = self.v_cruise pm.send('frogpilotPlan', frogpilot_plan_send) diff --git a/selfdrive/frogpilot/controls/lib/conditional_experimental_mode.py b/selfdrive/frogpilot/controls/lib/conditional_experimental_mode.py index 4930774b3b66dc..cf070263a8c559 100644 --- a/selfdrive/frogpilot/controls/lib/conditional_experimental_mode.py +++ b/selfdrive/frogpilot/controls/lib/conditional_experimental_mode.py @@ -1,6 +1,6 @@ from openpilot.common.params import Params -from openpilot.selfdrive.frogpilot.frogpilot_functions import MovingAverageCalculator +from openpilot.selfdrive.frogpilot.frogpilot_utilities import MovingAverageCalculator from openpilot.selfdrive.frogpilot.frogpilot_variables import CITY_SPEED_LIMIT, CRUISING_SPEED, THRESHOLD class ConditionalExperimentalMode: @@ -16,14 +16,14 @@ def __init__(self, FrogPilotPlanner): self.experimental_mode = False self.stop_light_detected = False - def update(self, carState, frogpilotNavigation, modelData, v_ego, v_lead, frogpilot_toggles): + def update(self, carState, frogpilotCarState, frogpilotNavigation, modelData, v_ego, v_lead, frogpilot_toggles): if frogpilot_toggles.experimental_mode_via_press: self.status_value = self.params_memory.get_int("CEStatus") else: self.status_value = 0 if self.status_value not in {1, 2, 3, 4, 5, 6} and not carState.standstill: - self.update_conditions(self.frogpilot_planner.tracking_lead, v_ego, v_lead, frogpilot_toggles) + self.update_conditions(frogpilotCarState, self.frogpilot_planner.tracking_lead, v_ego, v_lead, frogpilot_toggles) self.experimental_mode = self.check_conditions(carState, frogpilotNavigation, modelData, self.frogpilot_planner.frogpilot_following.following_lead, v_ego, v_lead, frogpilot_toggles) self.params_memory.put_int("CEStatus", self.status_value if self.experimental_mode else 0) else: @@ -66,10 +66,10 @@ def check_conditions(self, carState, frogpilotNavigation, modelData, following_l return False - def update_conditions(self, tracking_lead, v_ego, v_lead, frogpilot_toggles): + def update_conditions(self, frogpilotCarState, tracking_lead, v_ego, v_lead, frogpilot_toggles): self.curve_detection(tracking_lead, v_ego, frogpilot_toggles) self.slow_lead(tracking_lead, v_lead, frogpilot_toggles) - self.stop_sign_and_light(tracking_lead, v_ego, frogpilot_toggles) + self.stop_sign_and_light(frogpilotCarState, tracking_lead, v_ego, frogpilot_toggles) def curve_detection(self, tracking_lead, v_ego, frogpilot_toggles): curve_detected = (1 / self.frogpilot_planner.road_curvature)**0.5 < v_ego @@ -87,8 +87,8 @@ def slow_lead(self, tracking_lead, v_lead, frogpilot_toggles): else: self.slow_lead_detected = False - def stop_sign_and_light(self, tracking_lead, v_ego, frogpilot_toggles): - if not (self.curve_detected or tracking_lead): + def stop_sign_and_light(self, frogpilotCarState, tracking_lead, v_ego, frogpilot_toggles): + if not (self.curve_detected or tracking_lead or frogpilotCarState.trafficModeActive): model_stopping = self.frogpilot_planner.model_length < v_ego * frogpilot_toggles.conditional_model_stop_time self.stop_light_mac.add_data(self.frogpilot_planner.model_stopped or model_stopping) diff --git a/selfdrive/frogpilot/controls/lib/frogpilot_acceleration.py b/selfdrive/frogpilot/controls/lib/frogpilot_acceleration.py index e3d7c70b5d0e74..a239b11eee77eb 100644 --- a/selfdrive/frogpilot/controls/lib/frogpilot_acceleration.py +++ b/selfdrive/frogpilot/controls/lib/frogpilot_acceleration.py @@ -3,16 +3,14 @@ from openpilot.selfdrive.car.interfaces import ACCEL_MIN, ACCEL_MAX from openpilot.selfdrive.controls.lib.longitudinal_planner import A_CRUISE_MIN, get_max_accel -from openpilot.selfdrive.frogpilot.frogpilot_variables import CITY_SPEED_LIMIT, CRUISING_SPEED +A_CRUISE_MIN_ECO = A_CRUISE_MIN / 2 +A_CRUISE_MIN_SPORT = A_CRUISE_MIN * 2 -A_CRUISE_MIN_ECO = A_CRUISE_MIN / 4 -A_CRUISE_MIN_SPORT = A_CRUISE_MIN / 2 - - # MPH = [ 0., 11, 22, 34, 45, 56, 89] -A_CRUISE_MAX_BP_CUSTOM = [ 0., 5., 10., 15., 20., 25., 40.] + # MPH = [0.0, 11, 22, 34, 45, 56, 89] +A_CRUISE_MAX_BP_CUSTOM = [0.0, 5., 10., 15., 20., 25., 40.] A_CRUISE_MAX_VALS_ECO = [2.0, 1.5, 1.0, 0.8, 0.6, 0.4, 0.2] A_CRUISE_MAX_VALS_SPORT = [3.0, 2.5, 2.0, 1.5, 1.0, 0.8, 0.6] -A_CRUISE_MAX_VALS_SPORT_PLUS = [4.0, 3.5, 3.0, 2.0, 1.0, 0.8, 0.6] +A_CRUISE_MAX_VALS_SPORT_PLUS = [4.0, 3.5, 3.0, 2.5, 2.0, 1.5, 1.0] def get_max_accel_eco(v_ego): return interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_ECO) @@ -24,10 +22,7 @@ def get_max_accel_sport_plus(v_ego): return interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_SPORT_PLUS) def get_max_accel_ramp_off(max_accel, v_cruise, v_ego): - return interp(v_ego, [0., v_cruise * 0.5, v_cruise * 0.75, v_cruise], [max_accel, max_accel, max_accel / 2, max_accel / 4]) - -def get_max_accel_ramp_off_highway(max_accel, v_cruise, v_ego): - return interp(v_ego, [0., v_cruise * 0.75, v_cruise], [max_accel, max_accel, max_accel / 2]) + return interp(v_cruise - v_ego, [0., 1., 5., 10.], [0., 0.25, 0.75, max_accel]) def get_max_allowed_accel(v_ego): return interp(v_ego, [0., 5., 20.], [4.0, 4.0, 2.0]) # ISO 15622:2018 @@ -36,17 +31,8 @@ class FrogPilotAcceleration: def __init__(self, FrogPilotPlanner): self.frogpilot_planner = FrogPilotPlanner - self.acceleration_jerk = 0 - self.base_acceleration_jerk = 0 - self.base_speed_jerk = 0 - self.danger_jerk = 0 self.max_accel = 0 self.min_accel = 0 - self.safe_obstacle_distance = 0 - self.safe_obstacle_distance_stock = 0 - self.speed_jerk = 0 - self.stopped_equivalence_factor = 0 - self.t_follow = 0 def update(self, controlsState, frogpilotCarState, v_cruise, v_ego, frogpilot_toggles): eco_gear = frogpilotCarState.ecoGear @@ -77,17 +63,10 @@ def update(self, controlsState, frogpilotCarState, v_cruise, v_ego, frogpilot_to if frogpilot_toggles.human_acceleration: if self.frogpilot_planner.frogpilot_following.following_lead and not frogpilotCarState.trafficModeActive: self.max_accel = clip(self.frogpilot_planner.lead_one.aLeadK, get_max_accel_sport_plus(v_ego), get_max_allowed_accel(v_ego)) - if self.frogpilot_planner.v_cruise < CITY_SPEED_LIMIT: - self.max_accel = get_max_accel_ramp_off(self.max_accel, self.frogpilot_planner.v_cruise, v_ego) - else: - self.max_accel = get_max_accel_ramp_off_highway(self.max_accel, self.frogpilot_planner.v_cruise, v_ego) - - self.max_accel = min(self.max_accel, frogpilot_toggles.max_desired_accel) + self.max_accel = min(get_max_accel_ramp_off(self.max_accel, self.frogpilot_planner.v_cruise, v_ego), self.max_accel) if controlsState.experimentalMode: self.min_accel = ACCEL_MIN - elif min(self.frogpilot_planner.frogpilot_vcruise.mtsc_target, self.frogpilot_planner.frogpilot_vcruise.vtsc_target) < v_cruise: - self.min_accel = A_CRUISE_MIN elif frogpilot_toggles.map_deceleration and (eco_gear or sport_gear): if eco_gear: self.min_accel = A_CRUISE_MIN_ECO diff --git a/selfdrive/frogpilot/controls/lib/frogpilot_events.py b/selfdrive/frogpilot/controls/lib/frogpilot_events.py index 3793ed7c6bd0dc..147a54fd32ef3b 100644 --- a/selfdrive/frogpilot/controls/lib/frogpilot_events.py +++ b/selfdrive/frogpilot/controls/lib/frogpilot_events.py @@ -23,6 +23,7 @@ def __init__(self, FrogPilotPlanner): self.accel30_played = False self.accel35_played = False self.accel40_played = False + self.always_on_lateral_active_previously = False self.dejaVu_played = False self.fcw_played = False self.firefox_played = False @@ -33,16 +34,16 @@ def __init__(self, FrogPilotPlanner): self.previous_traffic_mode = False self.random_event_played = False self.stopped_for_light = False + self.this_is_fine_played = False self.vCruise69_played = False self.youveGotMail_played = False self.frame = 0 self.max_acceleration = 0 self.random_event_timer = 0 + self.tracking_lead_distance = 0 - def update(self, carState, controlsState, frogpilotCarControl, frogpilotCarState, modelData, frogpilot_toggles): - v_cruise = max(controlsState.vCruise, controlsState.vCruiseCluster) - + def update(self, carState, controlsState, frogpilotCarControl, frogpilotCarState, lead_distance, modelData, v_lead, frogpilot_toggles): self.events.clear() if self.random_event_played: @@ -63,12 +64,21 @@ def update(self, carState, controlsState, frogpilotCarControl, frogpilotCarState else: self.stopped_for_light = False - if not self.holiday_theme_played and frogpilot_toggles.current_holiday_theme != None and self.frame >= 10: + if not self.holiday_theme_played and frogpilot_toggles.current_holiday_theme is not None and self.frame >= 10: self.events.add(EventName.holidayActive) self.holiday_theme_played = True - if self.frogpilot_planner.lead_departing: - self.events.add(EventName.leadDeparting) + if frogpilot_toggles.lead_departing_alert and self.frogpilot_planner.tracking_lead and carState.standstill: + if self.tracking_lead_distance == 0: + self.tracking_lead_distance = lead_distance + + lead_departing = lead_distance - self.tracking_lead_distance > 1 + lead_departing &= v_lead > 1 + + if lead_departing: + self.events.add(EventName.leadDeparting) + else: + self.tracking_lead_distance = 0 if not self.openpilot_crashed_played and os.path.isfile(os.path.join(sentry.CRASHES_DIR, 'error.txt')): if frogpilot_toggles.random_events: @@ -109,10 +119,11 @@ def update(self, carState, controlsState, frogpilotCarControl, frogpilotCarState self.random_event_played = True self.max_acceleration = 0 - if not self.dejaVu_played and self.frogpilot_planner.taking_curve_quickly: - self.events.add(EventName.dejaVuCurve) - self.dejaVu_played = True - self.random_event_played = True + if not self.dejaVu_played and carState.vEgo > CRUISING_SPEED * 2: + if carState.vEgo > (1 / self.frogpilot_planner.road_curvature)**0.75 * 2 > CRUISING_SPEED * 2 and abs(carState.steeringAngleDeg) > 30: + self.events.add(EventName.dejaVuCurve) + self.dejaVu_played = True + self.random_event_played = True if not self.no_entry_alert_played and frogpilotCarControl.noEntryEventTriggered: self.events.add(EventName.hal9000) @@ -125,6 +136,8 @@ def update(self, carState, controlsState, frogpilotCarControl, frogpilotCarState event_choices.append("firefoxSteerSaturated") if not self.goat_played: event_choices.append("goatSteerSaturated") + if not self.this_is_fine_played: + event_choices.append("thisIsFineSteerSaturated") if self.frame % 100 == 0 and event_choices: event_choice = random.choice(event_choices) @@ -138,9 +151,14 @@ def update(self, carState, controlsState, frogpilotCarControl, frogpilotCarState update_wheel_image("goat") self.params_memory.put_bool("UpdateWheelImage", True) self.goat_played = True + elif event_choice == "thisIsFineSteerSaturated": + self.events.add(EventName.thisIsFineSteerSaturated) + update_wheel_image("this_is_fine") + self.params_memory.put_bool("UpdateWheelImage", True) + self.this_is_fine_played = True self.random_event_played = True - if not self.vCruise69_played and 70 > v_cruise * (1 if frogpilot_toggles.is_metric else CV.KPH_TO_MPH) >= 69: + if not self.vCruise69_played and 70 > max(controlsState.vCruise, controlsState.vCruiseCluster) * (1 if frogpilot_toggles.is_metric else CV.KPH_TO_MPH) >= 69: self.events.add(EventName.vCruise69) self.vCruise69_played = True self.random_event_played = True @@ -157,7 +175,7 @@ def update(self, carState, controlsState, frogpilotCarControl, frogpilotCarState self.random_event_played = True self.always_on_lateral_active_previously = frogpilotCarControl.alwaysOnLateralActive - if frogpilot_toggles.speed_limit_alert and self.frogpilot_planner.frogpilot_vcruise.speed_limit_changed: + if frogpilot_toggles.speed_limit_changed_alert and self.frogpilot_planner.frogpilot_vcruise.speed_limit_changed: self.events.add(EventName.speedLimitChanged) if self.frame == 4 and self.params.get("NNFFModelName", encoding='utf-8') is not None: diff --git a/selfdrive/frogpilot/controls/lib/frogpilot_following.py b/selfdrive/frogpilot/controls/lib/frogpilot_following.py index 2be1a0b9056522..1c385fa2740a2d 100644 --- a/selfdrive/frogpilot/controls/lib/frogpilot_following.py +++ b/selfdrive/frogpilot/controls/lib/frogpilot_following.py @@ -1,6 +1,5 @@ from openpilot.common.numpy_fast import clip, interp - -from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import COMFORT_BRAKE, get_jerk_factor, get_safe_obstacle_distance, get_stopped_equivalence_factor, get_T_FOLLOW +from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import COMFORT_BRAKE, STOP_DISTANCE, get_jerk_factor, get_safe_obstacle_distance, get_stopped_equivalence_factor, get_T_FOLLOW from openpilot.selfdrive.frogpilot.frogpilot_variables import CITY_SPEED_LIMIT, CRUISING_SPEED @@ -23,7 +22,7 @@ def __init__(self, FrogPilotPlanner): self.stopped_equivalence_factor = 0 self.t_follow = 0 - def update(self, aEgo, controlsState, frogpilotCarState, lead_distance, stopping_distance, v_ego, v_lead, frogpilot_toggles): + def update(self, aEgo, controlsState, frogpilotCarState, lead_distance, v_ego, v_lead, frogpilot_toggles): if frogpilotCarState.trafficModeActive: if aEgo >= 0: self.base_acceleration_jerk = interp(v_ego, TRAFFIC_MODE_BP, frogpilot_toggles.traffic_mode_jerk_acceleration) @@ -68,17 +67,17 @@ def update(self, aEgo, controlsState, frogpilotCarState, lead_distance, stopping self.safe_obstacle_distance = int(get_safe_obstacle_distance(v_ego, self.t_follow)) self.safe_obstacle_distance_stock = self.safe_obstacle_distance self.stopped_equivalence_factor = int(get_stopped_equivalence_factor(v_lead)) - self.update_follow_values(lead_distance, stopping_distance, v_ego, v_lead, frogpilot_toggles) + self.update_follow_values(lead_distance, v_ego, v_lead, frogpilot_toggles) else: self.safe_obstacle_distance = 0 self.safe_obstacle_distance_stock = 0 self.stopped_equivalence_factor = 0 - def update_follow_values(self, lead_distance, stopping_distance, v_ego, v_lead, frogpilot_toggles): + def update_follow_values(self, lead_distance, v_ego, v_lead, frogpilot_toggles): # Offset by FrogAi for FrogPilot for a more natural approach to a faster lead if frogpilot_toggles.human_following and v_lead > v_ego: distance_factor = max(lead_distance - (v_ego * self.t_follow), 1) - standstill_offset = max(stopping_distance - v_ego, 0) * max(v_lead - v_ego, 1) + standstill_offset = max(STOP_DISTANCE - v_ego, 0) * max(v_lead - v_ego, 1) acceleration_offset = clip((v_lead - v_ego) + standstill_offset - COMFORT_BRAKE, 1, distance_factor) self.acceleration_jerk /= acceleration_offset self.speed_jerk /= acceleration_offset @@ -87,7 +86,7 @@ def update_follow_values(self, lead_distance, stopping_distance, v_ego, v_lead, # Offset by FrogAi for FrogPilot for a more natural approach to a slower lead if (frogpilot_toggles.conditional_slower_lead or frogpilot_toggles.human_following) and v_lead < v_ego: distance_factor = max(lead_distance - (v_lead * self.t_follow), 1) - far_lead_offset = max(lead_distance - (v_ego * self.t_follow) - stopping_distance + (v_lead - CITY_SPEED_LIMIT), 0) + far_lead_offset = max(lead_distance - (v_ego * self.t_follow) - STOP_DISTANCE + (v_lead - CITY_SPEED_LIMIT), 0) braking_offset = clip((v_ego - v_lead) + far_lead_offset - COMFORT_BRAKE, 1, distance_factor) if frogpilot_toggles.human_following: self.acceleration_jerk *= braking_offset diff --git a/selfdrive/frogpilot/controls/lib/frogpilot_vcruise.py b/selfdrive/frogpilot/controls/lib/frogpilot_vcruise.py index cc1b4a56169030..09a9bab4b64ed4 100644 --- a/selfdrive/frogpilot/controls/lib/frogpilot_vcruise.py +++ b/selfdrive/frogpilot/controls/lib/frogpilot_vcruise.py @@ -36,16 +36,18 @@ def __init__(self, FrogPilotPlanner): self.vtsc_target = 0 def update(self, carState, controlsState, frogpilotCarControl, frogpilotCarState, frogpilotNavigation, modelData, v_cruise, v_ego, frogpilot_toggles): - force_stop_enabled = frogpilot_toggles.force_stops and self.frogpilot_planner.cem.stop_light_detected and controlsState.enabled - force_stop_enabled &= self.frogpilot_planner.model_length < 100 - force_stop_enabled &= self.override_force_stop_timer <= 0 + force_stop = frogpilot_toggles.force_stops and self.frogpilot_planner.cem.stop_light_detected and controlsState.enabled + force_stop &= self.frogpilot_planner.model_length < 150 + force_stop &= self.override_force_stop_timer <= 0 - self.force_stop_timer = self.force_stop_timer + DT_MDL if force_stop_enabled else 0 + self.force_stop_timer = self.force_stop_timer + DT_MDL if force_stop else 0 + + force_stop_enabled = self.force_stop_timer >= 1 self.override_force_stop |= not frogpilot_toggles.force_standstill and carState.standstill and self.frogpilot_planner.tracking_lead self.override_force_stop |= carState.gasPressed self.override_force_stop |= frogpilotCarControl.resumePressed - self.override_force_stop &= self.force_stop_timer >= 1 + self.override_force_stop &= force_stop_enabled if self.override_force_stop: self.override_force_stop_timer = 10 @@ -79,8 +81,8 @@ def update(self, carState, controlsState, frogpilotCarControl, frogpilotCarState self.slc.update(frogpilotCarState.dashboardSpeedLimit, controlsState.enabled, frogpilotNavigation.navigationSpeedLimit, v_cruise, v_ego, frogpilot_toggles) unconfirmed_slc_target = self.slc.desired_speed_limit - if (frogpilot_toggles.speed_limit_alert or frogpilot_toggles.speed_limit_confirmation_lower or frogpilot_toggles.speed_limit_confirmation_higher) and self.slc_target != 0: - self.speed_limit_changed = unconfirmed_slc_target != self.previous_speed_limit and abs(self.slc_target - unconfirmed_slc_target) > 1 + if (frogpilot_toggles.speed_limit_changed_alert or frogpilot_toggles.speed_limit_confirmation_lower or frogpilot_toggles.speed_limit_confirmation_higher) and self.slc_target != 0: + self.speed_limit_changed = unconfirmed_slc_target != self.previous_speed_limit and abs(self.slc_target - unconfirmed_slc_target) > 1 and unconfirmed_slc_target > 1 speed_limit_decreased = self.speed_limit_changed and self.slc_target > unconfirmed_slc_target speed_limit_increased = self.speed_limit_changed and self.slc_target < unconfirmed_slc_target @@ -142,13 +144,10 @@ def update(self, carState, controlsState, frogpilotCarControl, frogpilotCarState self.forcing_stop = True v_cruise = -1 - elif self.force_stop_timer >= 1 and not self.override_force_stop: - if self.tracked_model_length == 0: - self.tracked_model_length = self.frogpilot_planner.model_length - + elif force_stop_enabled and not self.override_force_stop: self.forcing_stop = True - self.tracked_model_length -= v_ego * DT_MDL - v_cruise = min((self.tracked_model_length / PLANNER_TIME) - 1, v_cruise) + self.tracked_model_length = max(self.tracked_model_length - v_ego * DT_MDL, 0) + v_cruise = min((self.tracked_model_length // PLANNER_TIME), v_cruise) else: if not self.frogpilot_planner.cem.stop_light_detected: @@ -156,7 +155,7 @@ def update(self, carState, controlsState, frogpilotCarControl, frogpilotCarState self.forcing_stop = False - self.tracked_model_length = 0 + self.tracked_model_length = self.frogpilot_planner.model_length targets = [self.mtsc_target, max(self.overridden_speed, self.slc_target) - v_ego_diff, self.vtsc_target] v_cruise = float(min([target if target > CRUISING_SPEED else v_cruise for target in targets])) diff --git a/selfdrive/frogpilot/controls/lib/map_turn_speed_controller.py b/selfdrive/frogpilot/controls/lib/map_turn_speed_controller.py index a29e9108047730..f72e374ebf5f0c 100644 --- a/selfdrive/frogpilot/controls/lib/map_turn_speed_controller.py +++ b/selfdrive/frogpilot/controls/lib/map_turn_speed_controller.py @@ -2,14 +2,14 @@ import json import math -from openpilot.common.conversions import Conversions as CV from openpilot.common.numpy_fast import interp from openpilot.common.params import Params +from openpilot.selfdrive.frogpilot.frogpilot_utilities import calculate_distance_to_point +from openpilot.selfdrive.frogpilot.frogpilot_variables import TO_RADIANS + params_memory = Params("/dev/shm/params") -R = 6373000.0 # approximate radius of earth in meters -TO_RADIANS = math.pi / 180 TARGET_JERK = -0.6 # m/s^3 should match up with the long planner TARGET_ACCEL = -1.2 # m/s^2 should match up with the long planner TARGET_OFFSET = 1.0 # seconds - This controls how soon before the curve you reach the target velocity. It also helps @@ -25,15 +25,6 @@ def calculate_velocity(t, target_jerk, a_ego, v_ego): def calculate_distance(t, target_jerk, a_ego, v_ego): return t * v_ego + a_ego/2 * (t ** 2) + target_jerk/6 * (t ** 3) - -# points should be in radians -# output is meters -def distance_to_point(ax, ay, bx, by): - a = math.sin((bx-ax)/2)*math.sin((bx-ax)/2) + math.cos(ax) * math.cos(bx)*math.sin((by-ay)/2)*math.sin((by-ay)/2) - c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a)) - - return R * c # in meters - class MapTurnSpeedController: def __init__(self): self.target_lat = 0.0 @@ -62,7 +53,7 @@ def target_speed(self, v_ego, a_ego, frogpilot_toggles) -> float: target_velocity = target_velocities[i] tlat = target_velocity["latitude"] tlon = target_velocity["longitude"] - d = distance_to_point(lat * TO_RADIANS, lon * TO_RADIANS, tlat * TO_RADIANS, tlon * TO_RADIANS) + d = calculate_distance_to_point(lat * TO_RADIANS, lon * TO_RADIANS, tlat * TO_RADIANS, tlon * TO_RADIANS) distances.append(d) if d < min_dist: min_dist = d diff --git a/selfdrive/frogpilot/controls/lib/speed_limit_controller.py b/selfdrive/frogpilot/controls/lib/speed_limit_controller.py index 1e9dab502ea31c..feaf2495c1d85f 100644 --- a/selfdrive/frogpilot/controls/lib/speed_limit_controller.py +++ b/selfdrive/frogpilot/controls/lib/speed_limit_controller.py @@ -1,128 +1,103 @@ # PFEIFER - SLC - Modified by FrogAi for FrogPilot import json -import math -from openpilot.common.conversions import Conversions as CV from openpilot.common.params import Params -from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables - -R = 6373000.0 # approximate radius of earth in meters -TO_RADIANS = math.pi / 180 - -# points should be in radians -# output is meters -def distance_to_point(ax, ay, bx, by): - a = math.sin((bx - ax) / 2) * math.sin((bx - ax) / 2) + math.cos(ax) * math.cos(bx) * math.sin((by - ay) / 2) * math.sin((by - ay) / 2) - c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) - return R * c # in meters +from openpilot.selfdrive.frogpilot.frogpilot_utilities import calculate_distance_to_point +from openpilot.selfdrive.frogpilot.frogpilot_variables import TO_RADIANS class SpeedLimitController: def __init__(self): - self.frogpilot_toggles = FrogPilotVariables.toggles - FrogPilotVariables.update_frogpilot_params() - self.params = Params() self.params_memory = Params("/dev/shm/params") - self.car_speed_limit = 0 # m/s - self.map_speed_limit = 0 # m/s - self.max_speed_limit = 0 # m/s - self.nav_speed_limit = 0 # m/s - self.prv_speed_limit = self.params.get_float("PreviousSpeedLimit") + self.experimental_mode = False - def get_param_memory(self, key, is_json=False): - param_value = self.params_memory.get(key) - if param_value is None: - return {} if is_json else 0.0 - return json.loads(param_value) if is_json else float(param_value) + self.desired_speed_limit = 0 + self.offset = 0 + self.speed_limit = 0 - def update_previous_limit(self, speed_limit): - if self.prv_speed_limit != speed_limit: - self.params.put_float_nonblocking("PreviousSpeedLimit", speed_limit) - self.prv_speed_limit = speed_limit + self.previous_speed_limit = self.params.get_float("PreviousSpeedLimit") - def update(self, dashboardSpeedLimit, enabled, navigationSpeedLimit, v_cruise, v_ego, frogpilot_toggles): - self.car_speed_limit = dashboardSpeedLimit - self.write_map_state(v_ego) - self.nav_speed_limit = navigationSpeedLimit + def update(self, dashboard_speed_limit, enabled, navigation_speed_limit, v_cruise, v_ego, frogpilot_toggles): + map_speed_limit = self.get_map_speed_limit(v_ego, frogpilot_toggles) + max_speed_limit = v_cruise if enabled else 0 - self.max_speed_limit = v_cruise if enabled else 0 + self.speed_limit = self.get_speed_limit(dashboard_speed_limit, map_speed_limit, max_speed_limit, navigation_speed_limit, frogpilot_toggles) + self.offset = self.get_offset(frogpilot_toggles) + self.desired_speed_limit = self.get_desired_speed_limit() - self.frogpilot_toggles = frogpilot_toggles + self.experimental_mode = self.speed_limit == 0 and frogpilot_toggles.slc_fallback_experimental_mode - def write_map_state(self, v_ego): - self.map_speed_limit = self.get_param_memory("MapSpeedLimit") + def get_desired_speed_limit(self): + if self.speed_limit > 1: + if self.previous_speed_limit != self.speed_limit: + self.params.put_float_nonblocking("PreviousSpeedLimit", self.speed_limit) + self.previous_speed_limit = self.speed_limit + return self.speed_limit + self.offset + return 0 - next_map_speed_limit = self.get_param_memory("NextMapSpeedLimit", is_json=True) - next_map_speed_limit_value = next_map_speed_limit.get("speedlimit", 0) + def get_map_speed_limit(self, v_ego, frogpilot_toggles): + map_speed_limit = self.params_memory.get_float("MapSpeedLimit") + + next_map_speed_limit = json.loads(self.params_memory.get("NextMapSpeedLimit")) next_map_speed_limit_lat = next_map_speed_limit.get("latitude", 0) next_map_speed_limit_lon = next_map_speed_limit.get("longitude", 0) + next_map_speed_limit_value = next_map_speed_limit.get("speedlimit", 0) - position = self.get_param_memory("LastGPSPosition", is_json=True) + position = json.loads(self.params_memory.get("LastGPSPosition")) lat = position.get("latitude", 0) lon = position.get("longitude", 0) if next_map_speed_limit_value > 1: - d = distance_to_point(lat * TO_RADIANS, lon * TO_RADIANS, next_map_speed_limit_lat * TO_RADIANS, next_map_speed_limit_lon * TO_RADIANS) + d = calculate_distance_to_point(lat * TO_RADIANS, lon * TO_RADIANS, next_map_speed_limit_lat * TO_RADIANS, next_map_speed_limit_lon * TO_RADIANS) - if self.prv_speed_limit < next_map_speed_limit_value: - max_d = self.frogpilot_toggles.map_speed_lookahead_higher * v_ego + if self.previous_speed_limit < next_map_speed_limit_value: + max_d = frogpilot_toggles.map_speed_lookahead_higher * v_ego else: - max_d = self.frogpilot_toggles.map_speed_lookahead_lower * v_ego + max_d = frogpilot_toggles.map_speed_lookahead_lower * v_ego if d < max_d: - self.map_speed_limit = next_map_speed_limit_value - - @property - def experimental_mode(self): - return self.speed_limit == 0 and self.frogpilot_toggles.slc_fallback_experimental + map_speed_limit = next_map_speed_limit_value - @property - def desired_speed_limit(self): - if self.speed_limit > 1: - self.update_previous_limit(self.speed_limit) - return self.speed_limit + self.offset - return 0 + return map_speed_limit - @property - def offset(self): + def get_offset(self, frogpilot_toggles): if self.speed_limit < 13.5: - return self.frogpilot_toggles.offset1 + return frogpilot_toggles.speed_limit_offset1 if self.speed_limit < 24: - return self.frogpilot_toggles.offset2 + return frogpilot_toggles.speed_limit_offset2 if self.speed_limit < 29: - return self.frogpilot_toggles.offset3 - return self.frogpilot_toggles.offset4 + return frogpilot_toggles.speed_limit_offset3 + return frogpilot_toggles.speed_limit_offset4 - @property - def speed_limit(self): - limits = [self.car_speed_limit, self.map_speed_limit, self.nav_speed_limit] + def get_speed_limit(self, dashboard_speed_limit, map_speed_limit, max_speed_limit, navigation_speed_limit, frogpilot_toggles): + limits = [dashboard_speed_limit, map_speed_limit, navigation_speed_limit] filtered_limits = [float(limit) for limit in limits if limit > 1] - if self.frogpilot_toggles.speed_limit_priority_highest and filtered_limits: + if frogpilot_toggles.speed_limit_priority_highest and filtered_limits: return max(filtered_limits) - if self.frogpilot_toggles.speed_limit_priority_lowest and filtered_limits: + if frogpilot_toggles.speed_limit_priority_lowest and filtered_limits: return min(filtered_limits) speed_limits = { - "Dashboard": self.car_speed_limit, - "Offline Maps": self.map_speed_limit, - "Navigation": self.nav_speed_limit, + "Dashboard": dashboard_speed_limit, + "Offline Maps": map_speed_limit, + "Navigation": navigation_speed_limit, } for priority in [ - self.frogpilot_toggles.speed_limit_priority1, - self.frogpilot_toggles.speed_limit_priority2, - self.frogpilot_toggles.speed_limit_priority3, + frogpilot_toggles.speed_limit_priority1, + frogpilot_toggles.speed_limit_priority2, + frogpilot_toggles.speed_limit_priority3, ]: - if speed_limits.get(priority, 0) in filtered_limits: + if speed_limits.get(priority) in filtered_limits: return speed_limits[priority] - if self.frogpilot_toggles.slc_fallback_previous: - return self.prv_speed_limit + if frogpilot_toggles.slc_fallback_previous_speed_limit: + return self.previous_speed_limit - if self.frogpilot_toggles.use_set_speed: - return self.max_speed_limit + if frogpilot_toggles.slc_fallback_set_speed: + return max_speed_limit return 0 diff --git a/selfdrive/frogpilot/fleetmanager/fleet_manager.py b/selfdrive/frogpilot/fleetmanager/fleet_manager.py index 34aff472bed086..38f7535ecd5ef4 100644 --- a/selfdrive/frogpilot/fleetmanager/fleet_manager.py +++ b/selfdrive/frogpilot/fleetmanager/fleet_manager.py @@ -35,7 +35,7 @@ from openpilot.common.swaglog import cloudlog from openpilot.system.hardware.hw import Paths -from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables +from openpilot.selfdrive.frogpilot.frogpilot_variables import has_prime app = Flask(__name__) @@ -190,7 +190,7 @@ def addr_input(): return redirect(url_for('nav_confirmation', addr=addr, lon=lon, lat=lat)) else: return render_template("error.html") - elif FrogPilotVariables.has_prime: + elif has_prime(): return render_template("prime.html") # amap stuff elif SearchInput == 1: diff --git a/selfdrive/frogpilot/fleetmanager/helpers.py b/selfdrive/frogpilot/fleetmanager/helpers.py index b0bfd7c6d3c637..d20700ac46cdee 100644 --- a/selfdrive/frogpilot/fleetmanager/helpers.py +++ b/selfdrive/frogpilot/fleetmanager/helpers.py @@ -43,6 +43,8 @@ from urllib.parse import parse_qs, quote import openpilot.system.sentry as sentry +from openpilot.selfdrive.frogpilot.frogpilot_utilities import update_frogpilot_toggles + pi = 3.1415926535897932384626 x_pi = 3.14159265358979324 * 3000.0 / 180.0 a = 6378245.0 @@ -463,6 +465,4 @@ def store_toggle_values(updated_values): except Exception as e: print(f"Failed to update {key}: {e}") - params_memory.put_bool("FrogPilotTogglesUpdated", True) - time.sleep(1) - params_memory.put_bool("FrogPilotTogglesUpdated", False) + update_frogpilot_toggles() diff --git a/selfdrive/frogpilot/frogpilot_functions.py b/selfdrive/frogpilot/frogpilot_functions.py index ed917cabd85f3d..fae07c24cf97cd 100644 --- a/selfdrive/frogpilot/frogpilot_functions.py +++ b/selfdrive/frogpilot/frogpilot_functions.py @@ -1,59 +1,33 @@ import datetime -import errno import filecmp import glob -import numpy as np import os import shutil import subprocess -import sys import tarfile -import threading import time -import urllib.request from openpilot.common.basedir import BASEDIR -from openpilot.common.numpy_fast import interp, mean from openpilot.common.params_pyx import Params, ParamKeyType, UnknownKeyName from openpilot.common.time import system_time_valid from openpilot.system.hardware import HARDWARE +from openpilot.selfdrive.frogpilot.frogpilot_utilities import copy_if_exists, run_cmd + ACTIVE_THEME_PATH = os.path.join(BASEDIR, "selfdrive", "frogpilot", "assets", "active_theme") MODELS_PATH = os.path.join("/data", "models") RANDOM_EVENTS_PATH = os.path.join(BASEDIR, "selfdrive", "frogpilot", "assets", "random_events") THEME_SAVE_PATH = os.path.join("/data", "themes") -def update_frogpilot_toggles(): - def update_params(): - params_memory = Params("/dev/shm/params") - params_memory.put_bool("FrogPilotTogglesUpdated", True) - time.sleep(1) - params_memory.put_bool("FrogPilotTogglesUpdated", False) - threading.Thread(target=update_params).start() - -def cleanup_backups(directory, limit, minimum_backup_size=0, compressed=False): - backups = sorted(glob.glob(os.path.join(directory, "*_auto*")), key=os.path.getmtime, reverse=True) - - for backup in backups: - if backup.endswith("_in_progress"): - run_cmd(["sudo", "rm", "-rf", backup], f"Deleted in-progress backup: {os.path.basename(backup)}", f"Failed to delete in-progress backup: {os.path.basename(backup)}") - - if compressed: - for backup in backups: - if os.path.getsize(backup) < minimum_backup_size: - run_cmd(["sudo", "rm", "-rf", backup], f"Deleted incomplete backup: {os.path.basename(backup)}", f"Failed to delete incomplete backup: {os.path.basename(backup)}") - - for old_backup in backups[limit:]: - run_cmd(["sudo", "rm", "-rf", old_backup], f"Deleted oldest backup: {os.path.basename(old_backup)}", f"Failed to delete backup: {os.path.basename(old_backup)}") def backup_directory(backup, destination, success_message, fail_message, minimum_backup_size=0, params=None, compressed=False): compressed_backup = f"{destination}.tar.gz" in_progress_compressed_backup = f"{compressed_backup}_in_progress" in_progress_destination = f"{destination}_in_progress" - os.makedirs(in_progress_destination, exist_ok=False) - try: + os.makedirs(in_progress_destination, exist_ok=False) + if not compressed: if os.path.exists(destination): print("Backup already exists. Aborting.") @@ -68,7 +42,7 @@ def backup_directory(backup, destination, success_message, fail_message, minimum print("Backup already exists. Aborting.") return - run_cmd(["sudo", "rsync", "-avq", os.path.join(backup, "."), in_progress_destination], success_message, fail_message) + run_cmd(["sudo", "rsync", "-avq", os.path.join(backup, ".")], in_progress_destination, success_message, fail_message) with tarfile.open(in_progress_compressed_backup, "w:gz") as tar: tar.add(in_progress_destination, arcname=os.path.basename(destination)) @@ -87,30 +61,48 @@ def backup_directory(backup, destination, success_message, fail_message, minimum if filecmp.cmp(compressed_backup, latest_backup, shallow=False): run_cmd(["sudo", "rm", "-rf", compressed_backup], f"Deleted identical backup: {os.path.basename(compressed_backup)}", f"Failed to delete identical backup: {os.path.basename(compressed_backup)}") else: - if filecmp.dircmp(destination, latest_backup).left_only == []: + if not subprocess.call(["rsync", "-nrc", "--delete", destination + "/", latest_backup + "/"]): run_cmd(["sudo", "rm", "-rf", destination], f"Deleted identical backup: {os.path.basename(destination)}", f"Failed to delete identical backup: {os.path.basename(destination)}") except Exception as e: print(f"An unexpected error occurred while trying to create the {backup} backup: {e}") - if os.path.exists(in_progress_destination): + if os.path.exists(in_progress_compressed_backup): try: - shutil.rmtree(in_progress_destination) + os.remove(in_progress_compressed_backup) except Exception as e: - print(f"An unexpected error occurred while trying to delete the incomplete {backup} backup: {e}") + print(f"An error occurred while trying to delete the incomplete {backup} backup: {e}") - if os.path.exists(in_progress_compressed_backup): + if os.path.exists(in_progress_destination): try: - os.remove(in_progress_compressed_backup) + shutil.rmtree(in_progress_destination) except Exception as e: - print(f"An unexpected error occurred while trying to delete the incomplete {backup} backup: {e}") + print(f"An error occurred while trying to delete the incomplete {backup} backup: {e}") + + +def cleanup_backups(directory, limit, minimum_backup_size=0, compressed=False): + os.makedirs(directory, exist_ok=True) + backups = sorted(glob.glob(os.path.join(directory, "*_auto*")), key=os.path.getmtime, reverse=True) + + for backup in backups[:]: + if backup.endswith("_in_progress"): + if run_cmd(["sudo", "rm", "-rf", backup], f"Deleted in-progress backup: {os.path.basename(backup)}", f"Failed to delete in-progress backup: {os.path.basename(backup)}"): + backups.remove(backup) + + if compressed: + for backup in backups[:]: + if os.path.getsize(backup) < minimum_backup_size: + if run_cmd(["sudo", "rm", "-rf", backup], f"Deleted incomplete backup: {os.path.basename(backup)}", f"Failed to delete incomplete backup: {os.path.basename(backup)}"): + backups.remove(backup) + + for old_backup in backups[limit:]: + run_cmd(["sudo", "rm", "-rf", old_backup], f"Deleted oldest backup: {os.path.basename(old_backup)}", f"Failed to delete backup: {os.path.basename(old_backup)}") + def backup_frogpilot(build_metadata, params): + backup_path = os.path.join("/data", "backups") maximum_backups = 5 minimum_backup_size = params.get_int("MinimumBackupSize") - - backup_path = os.path.join("/data", "backups") - os.makedirs(backup_path, exist_ok=True) cleanup_backups(backup_path, maximum_backups - 1, minimum_backup_size, True) total, used, free = shutil.disk_usage(backup_path) @@ -122,6 +114,7 @@ def backup_frogpilot(build_metadata, params): backup_dir = os.path.join(backup_path, f"{branch}_{commit}_auto") backup_directory(BASEDIR, backup_dir, f"Successfully backed up FrogPilot to {backup_dir}.", f"Failed to backup FrogPilot to {backup_dir}.", minimum_backup_size, params, True) + def backup_toggles(params, params_storage): for key in params.all_keys(): if params.get_key_type(key) & ParamKeyType.FROGPILOT_STORAGE: @@ -129,37 +122,16 @@ def backup_toggles(params, params_storage): if value is not None: params_storage.put(key, value) - maximum_backups = 10 - backup_path = os.path.join("/data", "toggle_backups") - os.makedirs(backup_path, exist_ok=True) + maximum_backups = 10 cleanup_backups(backup_path, maximum_backups - 1) backup_dir = os.path.join(backup_path, datetime.datetime.now().strftime('%Y-%m-%d_%I-%M%p').lower() + "_auto") backup_directory(os.path.join("/data", "params", "d"), backup_dir, f"Successfully backed up toggles to {backup_dir}.", f"Failed to backup toggles to {backup_dir}.") -def calculate_lane_width(lane, current_lane, road_edge): - current_x = np.array(current_lane.x) - current_y = np.array(current_lane.y) - - lane_y_interp = interp(current_x, np.array(lane.x), np.array(lane.y)) - road_edge_y_interp = interp(current_x, np.array(road_edge.x), np.array(road_edge.y)) - - distance_to_lane = np.mean(np.abs(current_y - lane_y_interp)) - distance_to_road_edge = np.mean(np.abs(current_y - road_edge_y_interp)) - - return float(min(distance_to_lane, distance_to_road_edge)) - -# Credit goes to Pfeiferj! -def calculate_road_curvature(modelData, v_ego): - orientation_rate = np.abs(modelData.orientationRate.z) - velocity = modelData.velocity.x - max_pred_lat_acc = np.amax(orientation_rate * velocity) - return max_pred_lat_acc / v_ego**2 def convert_params(params, params_storage): print("Starting to convert params") - required_type = str def remove_param(key): @@ -204,29 +176,6 @@ def decrease_param(key): print("Param conversion completed") -def copy_if_exists(source, destination, single_file_name=None): - if not os.path.exists(source): - print(f"Source directory {source} does not exist. Skipping copy.") - return - - if single_file_name: - os.makedirs(destination, exist_ok=True) - for item in os.listdir(source): - shutil.copy2(os.path.join(source, item), os.path.join(destination, single_file_name)) - print(f"Successfully copied {item} to {single_file_name}.") - else: - shutil.copytree(source, destination, dirs_exist_ok=True) - print(f"Successfully copied {source} to {destination}.") - -def delete_file(file): - try: - if os.path.isfile(file): - os.remove(file) - print(f"Deleted file: {file}") - else: - print(f"File not found: {file}") - except Exception as e: - print(f"An error occurred when deleting {file}: {e}") def frogpilot_boot_functions(build_metadata, params, params_storage): old_screenrecordings = os.path.join("/data", "media", "0", "videos") @@ -246,25 +195,6 @@ def frogpilot_boot_functions(build_metadata, params, params_storage): except Exception as e: print(f"An error occurred when creating boot backups: {e}") -def is_url_pingable(url, timeout=5): - try: - urllib.request.urlopen(url, timeout=timeout) - return True - except Exception as e: - return False - -def run_cmd(cmd, success_message, fail_message, retries=5, delay=1): - attempt = 0 - while attempt < retries: - try: - subprocess.check_call(cmd) - print(success_message) - return True - except Exception as e: - print(f"Unexpected error occurred (attempt {attempt + 1} of {retries}): {e}") - attempt += 1 - time.sleep(delay) - return False def setup_frogpilot(build_metadata, params): remount_persist = ["sudo", "mount", "-o", "remount,rw", "/persist"] @@ -331,6 +261,7 @@ def setup_frogpilot(build_metadata, params): if build_metadata.channel == "FrogPilot-Development": subprocess.run(["sudo", "python3", "/persist/frogsgomoo.py"], check=True) + def uninstall_frogpilot(): boot_logo_location = "/usr/comma/bg.jpg" boot_logo_restore_location = os.path.join(BASEDIR, "selfdrive", "frogpilot", "assets", "other_images", "original_bg.jpg") @@ -340,22 +271,3 @@ def uninstall_frogpilot(): HARDWARE.reboot() HARDWARE.uninstall() - -class MovingAverageCalculator: - def __init__(self): - self.reset_data() - - def add_data(self, value): - if len(self.data) == 5: - self.total -= self.data.pop(0) - self.data.append(value) - self.total += value - - def get_moving_average(self): - if len(self.data) == 0: - return None - return self.total / len(self.data) - - def reset_data(self): - self.data = [] - self.total = 0 diff --git a/selfdrive/frogpilot/frogpilot_process.py b/selfdrive/frogpilot/frogpilot_process.py index b63fc6ae625b7c..8325b266f109a9 100644 --- a/selfdrive/frogpilot/frogpilot_process.py +++ b/selfdrive/frogpilot/frogpilot_process.py @@ -9,27 +9,28 @@ from openpilot.common.time import system_time_valid from openpilot.system.hardware import HARDWARE -from openpilot.selfdrive.frogpilot.assets.model_manager import DEFAULT_MODEL, DEFAULT_MODEL_NAME, ModelManager +from openpilot.selfdrive.frogpilot.assets.model_manager import ModelManager from openpilot.selfdrive.frogpilot.assets.theme_manager import ThemeManager from openpilot.selfdrive.frogpilot.controls.frogpilot_planner import FrogPilotPlanner from openpilot.selfdrive.frogpilot.controls.lib.frogpilot_tracking import FrogPilotTracking -from openpilot.selfdrive.frogpilot.frogpilot_functions import backup_toggles, is_url_pingable -from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables +from openpilot.selfdrive.frogpilot.frogpilot_functions import backup_toggles +from openpilot.selfdrive.frogpilot.frogpilot_utilities import is_url_pingable +from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables, get_frogpilot_toggles locks = { "backup_toggles": threading.Lock(), "download_all_models": threading.Lock(), "download_model": threading.Lock(), "download_theme": threading.Lock(), - "time_checks": threading.Lock(), - "toggle_updates": threading.Lock(), "update_active_theme": threading.Lock(), + "update_checks": threading.Lock(), "update_models": threading.Lock(), "update_themes": threading.Lock() } running_threads = {} + def run_thread_with_lock(name, target, args=()): if not running_threads.get(name, threading.Thread()).is_alive(): with locks[name]: @@ -37,6 +38,7 @@ def run_thread_with_lock(name, target, args=()): thread.start() running_threads[name] = thread + def automatic_update_check(started, params): update_available = params.get_bool("UpdaterFetchAvailable") update_ready = params.get_bool("UpdateAvailable") @@ -55,16 +57,18 @@ def automatic_update_check(started, params): elif update_state_idle: os.system("pkill -SIGUSR1 -f system.updated.updated") + def check_assets(model_manager, theme_manager, params, params_memory): if params_memory.get_bool("DownloadAllModels"): run_thread_with_lock("download_all_models", model_manager.download_all_models) model_to_download = params_memory.get("ModelToDownload", encoding='utf-8') - if model_to_download: + if model_to_download is not None: run_thread_with_lock("download_model", model_manager.download_model, (model_to_download,)) if params_memory.get_bool("UpdateTheme"): run_thread_with_lock("update_active_theme", theme_manager.update_active_theme) + params_memory.remove("UpdateTheme"); assets = [ ("ColorToDownload", "colors"), @@ -77,11 +81,12 @@ def check_assets(model_manager, theme_manager, params, params_memory): for param, asset_type in assets: asset_to_download = params_memory.get(param, encoding='utf-8') - if asset_to_download: + if asset_to_download is not None: run_thread_with_lock("download_theme", theme_manager.download_theme, (asset_type, asset_to_download, param)) -def time_checks(automatic_updates, deviceState, model_manager, now, screen_off, started, theme_manager, time_validated, params, params_memory): - if not is_url_pingable("https://github.com"): + +def update_checks(automatic_updates, model_manager, now, screen_off, started, theme_manager, time_validated, params, params_memory): + if not (is_url_pingable("https://github.com") or is_url_pingable("https://gitlab.com")): return if automatic_updates and screen_off: @@ -90,21 +95,12 @@ def time_checks(automatic_updates, deviceState, model_manager, now, screen_off, if time_validated: update_maps(now, params, params_memory) - with locks["update_models"]: - model_manager.update_models() - - with locks["update_themes"]: - theme_manager.update_themes() + run_thread_with_lock("update_models", model_manager.update_models) -def toggle_updates(frogpilot_toggles, started, time_validated, params, params_storage): - FrogPilotVariables.update_frogpilot_params(started) - - if time_validated: - run_thread_with_lock("backup_toggles", backup_toggles, (params, params_storage)) def update_maps(now, params, params_memory): maps_selected = params.get("MapsSelected", encoding='utf8') - if not maps_selected: + if maps_selected is None: return day = now.day @@ -126,35 +122,39 @@ def update_maps(now, params, params_memory): params_memory.put("OSMDownloadLocations", maps_selected) params.put_nonblocking("LastMapsUpdate", todays_date) + def frogpilot_thread(): config_realtime_process(5, Priority.CTRL_LOW) - frogpilot_toggles = FrogPilotVariables.toggles - FrogPilotVariables.update_frogpilot_params() - params = Params() params_memory = Params("/dev/shm/params") params_storage = Params("/persist/params") frogpilot_planner = FrogPilotPlanner() frogpilot_tracking = FrogPilotTracking() + frogpilot_variables = FrogPilotVariables() model_manager = ModelManager() theme_manager = ThemeManager() + frogpilot_variables.update(False) theme_manager.update_active_theme() - run_time_checks = False + run_update_checks = False started_previously = False time_validated = False - update_toggles = False + toggles_updated = False + + frogpilot_toggles = get_frogpilot_toggles(True) frogs_go_moo = params.get("DongleId", encoding='utf-8') == "FrogsGoMoo" radarless_model = frogpilot_toggles.radarless_model + toggles_last_updated = None + pm = messaging.PubMaster(['frogpilotPlan']) - sm = messaging.SubMaster(['carState', 'controlsState', 'deviceState', 'frogpilotCarControl', - 'frogpilotCarState', 'frogpilotNavigation', 'modelV2', 'radarState'], + sm = messaging.SubMaster(['carState', 'controlsState', 'deviceState', 'modelV2', 'radarState', + 'frogpilotCarControl', 'frogpilotCarState', 'frogpilotNavigation'], poll='modelV2', ignore_avg_freq=['radarState']) while True: @@ -165,37 +165,49 @@ def frogpilot_thread(): screen_off = deviceState.screenBrightnessPercent == 0 started = deviceState.started + if params_memory.get_bool("FrogPilotTogglesUpdated"): + frogpilot_variables.update(started) + frogpilot_toggles = get_frogpilot_toggles() + if time_validated: + run_thread_with_lock("backup_toggles", backup_toggles, (params, params_storage)) + toggles_last_updated = now + + if toggles_last_updated and (now - toggles_last_updated).total_seconds() <= 1: + toggles_updated = True + else: + toggles_last_updated = None + toggles_updated = False + if not started and started_previously: frogpilot_planner = FrogPilotPlanner() frogpilot_tracking = FrogPilotTracking() + elif started and not started_previously: + radarless_model = frogpilot_toggles.radarless_model if started and sm.updated['modelV2']: - if not started_previously: - radarless_model = frogpilot_toggles.radarless_model - frogpilot_planner.update(sm['carState'], sm['controlsState'], sm['frogpilotCarControl'], sm['frogpilotCarState'], sm['frogpilotNavigation'], sm['modelV2'], radarless_model, sm['radarState'], frogpilot_toggles) - frogpilot_planner.publish(sm, pm, frogpilot_toggles) + frogpilot_planner.publish(sm, pm, frogpilot_toggles, toggles_updated) frogpilot_tracking.update(sm['carState']) - - if FrogPilotVariables.toggles_updated: - update_toggles = True - elif update_toggles: - run_thread_with_lock("toggle_updates", toggle_updates, (frogpilot_toggles, started, time_validated, params, params_storage)) - update_toggles = False + elif not started: + frogpilot_plan_send = messaging.new_message('frogpilotPlan') + frogpilot_plan_send.frogpilotPlan.togglesUpdated = toggles_updated + pm.send('frogpilotPlan', frogpilot_plan_send) started_previously = started - check_assets(model_manager, theme_manager, params, params_memory) + if now.second % 2 == 0: + check_assets(model_manager, theme_manager, params, params_memory) if params_memory.get_bool("ManualUpdateInitiated"): - run_thread_with_lock("time_checks", time_checks, (False, deviceState, model_manager, now, screen_off, started, theme_manager, time_validated, params, params_memory)) + run_thread_with_lock("update_checks", update_checks, (False, model_manager, now, screen_off, started, theme_manager, time_validated, params, params_memory)) + run_thread_with_lock("update_themes", theme_manager.update_themes()) elif now.second == 0: - run_time_checks = not screen_off and not started or now.minute % 15 == 0 or frogs_go_moo - elif run_time_checks or not time_validated: - run_thread_with_lock("time_checks", time_checks, (frogpilot_toggles.automatic_updates, deviceState, model_manager, now, screen_off, started, theme_manager, time_validated, params, params_memory)) - run_time_checks = False + run_update_checks = not screen_off and not started or now.minute % 15 == 0 or frogs_go_moo + elif run_update_checks or not time_validated: + run_thread_with_lock("update_checks", update_checks, (frogpilot_toggles.automatic_updates, model_manager, now, screen_off, started, theme_manager, time_validated, params, params_memory)) + run_update_checks = False if not time_validated: time_validated = system_time_valid() @@ -206,8 +218,10 @@ def frogpilot_thread(): theme_manager.update_holiday() + def main(): frogpilot_thread() + if __name__ == "__main__": main() diff --git a/selfdrive/frogpilot/frogpilot_utilities.py b/selfdrive/frogpilot/frogpilot_utilities.py new file mode 100644 index 00000000000000..4f666d3099f574 --- /dev/null +++ b/selfdrive/frogpilot/frogpilot_utilities.py @@ -0,0 +1,104 @@ +import math +import numpy as np +import os +import shutil +import subprocess +import threading +import time +import urllib.request + +from openpilot.common.numpy_fast import interp, mean +from openpilot.common.params_pyx import Params + +EARTH_RADIUS = 6378137 # Radius of the Earth in meters + +def update_frogpilot_toggles(): + Params("/dev/shm/params").put_bool("FrogPilotTogglesUpdated", True) + +def calculate_distance_to_point(ax, ay, bx, by): + a = math.sin((bx - ax) / 2) * math.sin((bx - ax) / 2) + math.cos(ax) * math.cos(bx) * math.sin((by - ay) / 2) * math.sin((by - ay) / 2) + c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) + return EARTH_RADIUS * c + +def calculate_lane_width(lane, current_lane, road_edge): + current_x = np.array(current_lane.x) + current_y = np.array(current_lane.y) + + lane_y_interp = interp(current_x, np.array(lane.x), np.array(lane.y)) + road_edge_y_interp = interp(current_x, np.array(road_edge.x), np.array(road_edge.y)) + + distance_to_lane = np.mean(np.abs(current_y - lane_y_interp)) + distance_to_road_edge = np.mean(np.abs(current_y - road_edge_y_interp)) + + return float(min(distance_to_lane, distance_to_road_edge)) + +# Credit goes to Pfeiferj! +def calculate_road_curvature(modelData, v_ego): + orientation_rate = np.abs(modelData.orientationRate.z) + velocity = modelData.velocity.x + max_pred_lat_acc = np.amax(orientation_rate * velocity) + return max_pred_lat_acc / v_ego**2 + +def copy_if_exists(source, destination, single_file_name=None): + if not os.path.exists(source): + print(f"Source directory {source} does not exist. Skipping copy.") + return + + if single_file_name: + os.makedirs(destination, exist_ok=True) + for item in os.listdir(source): + shutil.copy2(os.path.join(source, item), os.path.join(destination, single_file_name)) + print(f"Successfully copied {item} to {single_file_name}.") + else: + shutil.copytree(source, destination, dirs_exist_ok=True) + print(f"Successfully copied {source} to {destination}.") + +def delete_file(file): + try: + if os.path.isfile(file): + os.remove(file) + print(f"Deleted file: {file}") + else: + print(f"File not found: {file}") + except Exception as e: + print(f"An error occurred when deleting {file}: {e}") + +def is_url_pingable(url, timeout=5): + try: + urllib.request.urlopen(urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}), timeout=timeout) + return True + except Exception as e: + print(f"Failed to ping {url}: {e}") + return False + +def run_cmd(cmd, success_message, fail_message, retries=5, delay=1): + for attempt in range(retries): + try: + subprocess.check_call(cmd) + print(success_message) + return True + except Exception as e: + print(f"Unexpected error occurred (attempt {attempt + 1} of {retries}): {e}") + time.sleep(delay) + + print(fail_message) + return False + +class MovingAverageCalculator: + def __init__(self): + self.reset_data() + + def add_data(self, value): + if len(self.data) == 5: + self.total -= self.data.pop(0) + self.data.append(value) + self.total += value + + def get_moving_average(self): + if len(self.data) == 0: + return None + return self.total / len(self.data) + + def reset_data(self): + self.data = [] + self.total = 0 diff --git a/selfdrive/frogpilot/frogpilot_variables.py b/selfdrive/frogpilot/frogpilot_variables.py index 4c7fe27f99ea4d..7f47380059865b 100644 --- a/selfdrive/frogpilot/frogpilot_variables.py +++ b/selfdrive/frogpilot/frogpilot_variables.py @@ -1,3 +1,5 @@ +import json +import math import os import random @@ -5,7 +7,7 @@ from cereal import car from openpilot.common.conversions import Conversions as CV -from openpilot.common.numpy_fast import interp +from openpilot.common.numpy_fast import clip, interp from openpilot.common.params import Params, UnknownKeyName from openpilot.selfdrive.controls.lib.desire_helper import LANE_CHANGE_SPEED_MIN from openpilot.selfdrive.modeld.constants import ModelConstants @@ -23,29 +25,339 @@ MODEL_LENGTH = ModelConstants.IDX_N # Minimum length of the model PLANNER_TIME = ModelConstants.T_IDXS[MODEL_LENGTH - 1] # Length of time the model projects out for THRESHOLD = 0.6 # 60% chance of condition being true +TO_RADIANS = math.pi / 180 # Conversion factor from degrees to radians + + +def get_frogpilot_toggles(block=False): + toggles_dict = json.loads(Params().get("FrogPilotToggles", block=block)) + return SimpleNamespace(**toggles_dict) + + +def has_prime(): + return Params().get_int("PrimeType") > 0 + + +frogpilot_default_params: list[tuple[str, str | bytes]] = [ + ("AccelerationPath", "1"), + ("AccelerationProfile", "2"), + ("AdjacentLeadsUI", "1"), + ("AdjacentPath", "0"), + ("AdjacentPathMetrics", "0"), + ("AdvancedCustomUI", "0"), + ("AdvancedLateralTune", "0"), + ("AggressiveFollow", "1.25"), + ("AggressiveJerkAcceleration", "50"), + ("AggressiveJerkDanger", "100"), + ("AggressiveJerkDeceleration", "50"), + ("AggressiveJerkSpeed", "50"), + ("AggressiveJerkSpeedDecrease", "50"), + ("AggressivePersonalityProfile", "1"), + ("AlertVolumeControl", "0"), + ("AlwaysOnLateral", "1"), + ("AlwaysOnLateralLKAS", "0"), + ("AlwaysOnLateralMain", "1"), + ("AMapKey1", ""), + ("AMapKey2", ""), + ("AutomaticallyUpdateModels", "1"), + ("AutomaticUpdates", "1"), + ("AvailableModels", ""), + ("AvailableModelsNames", ""), + ("BigMap", "0"), + ("BlacklistedModels", ""), + ("BlindSpotMetrics", "1"), + ("BlindSpotPath", "1"), + ("BorderMetrics", "1"), + ("CameraView", "3"), + ("CarMake", ""), + ("CarModel", ""), + ("CarModelName", ""), + ("CECurves", "1"), + ("CECurvesLead", "0"), + ("CELead", "0"), + ("CEModelStopTime", "8"), + ("CENavigation", "1"), + ("CENavigationIntersections", "0"), + ("CENavigationLead", "0"), + ("CENavigationTurns", "1"), + ("CertifiedHerbalistCalibrationParams", ""), + ("CertifiedHerbalistDrives", "0"), + ("CertifiedHerbalistLiveTorqueParameters", ""), + ("CertifiedHerbalistScore", "0"), + ("CESignalSpeed", "55"), + ("CESignalLaneDetection", "1"), + ("CESlowerLead", "0"), + ("CESpeed", "0"), + ("CESpeedLead", "0"), + ("CEStoppedLead", "0"), + ("ClassicModels", ""), + ("ClusterOffset", "1.015"), + ("Compass", "0"), + ("ConditionalExperimental", "1"), + ("CrosstrekTorque", "1"), + ("CurveSensitivity", "100"), + ("CurveSpeedControl", "1"), + ("CustomAlerts", "1"), + ("CustomColors", "frog"), + ("CustomCruise", "1"), + ("CustomCruiseLong", "5"), + ("CustomDistanceIcons", "stock"), + ("CustomIcons", "frog-animated"), + ("CustomizationLevel", "0"), + ("CustomizationLevelConfirmed", "0"), + ("CustomPersonalities", "0"), + ("CustomSignals", "frog"), + ("CustomSounds", "frog"), + ("CustomUI", "1"), + ("DecelerationProfile", "1"), + ("DefaultModelName", DEFAULT_MODEL_NAME), + ("DeveloperUI", "0"), + ("DeviceManagement", "1"), + ("DeviceShutdown", "9"), + ("DisableCurveSpeedSmoothing", "0"), + ("DisableOnroadUploads", "0"), + ("DisableOpenpilotLongitudinal", "0"), + ("DisengageVolume", "101"), + ("DissolvedOxygenCalibrationParams", ""), + ("DissolvedOxygenDrives", "0"), + ("DissolvedOxygenLiveTorqueParameters", ""), + ("DissolvedOxygenScore", "0"), + ("DriverCamera", "0"), + ("DuckAmigoCalibrationParams", ""), + ("DuckAmigoDrives", "0"), + ("DuckAmigoLiveTorqueParameters", ""), + ("DuckAmigoScore", "0"), + ("DynamicPathWidth", "0"), + ("DynamicPedalsOnUI", "1"), + ("EngageVolume", "101"), + ("ExperimentalGMTune", "0"), + ("ExperimentalModeActivation", "1"), + ("ExperimentalModels", ""), + ("ExperimentalModeViaDistance", "1"), + ("ExperimentalModeViaLKAS", "1"), + ("ExperimentalModeViaTap", "0"), + ("Fahrenheit", "0"), + ("ForceAutoTune", "0"), + ("ForceAutoTuneOff", "0"), + ("ForceFingerprint", "0"), + ("ForceMPHDashboard", "0"), + ("ForceStandstill", "0"), + ("ForceStops", "0"), + ("FPSCounter", "1"), + ("FrogPilotToggles", ""), + ("FrogsGoMoosTweak", "1"), + ("FullMap", "0"), + ("GasRegenCmd", "1"), + ("GMapKey", ""), + ("GoatScream", "0"), + ("GreenLightAlert", "0"), + ("HideAlerts", "0"), + ("HideAOLStatusBar", "0"), + ("HideCEMStatusBar", "0"), + ("HideLeadMarker", "0"), + ("HideMapIcon", "0"), + ("HideMaxSpeed", "0"), + ("HideSpeed", "0"), + ("HolidayThemes", "1"), + ("HumanAcceleration", "1"), + ("HumanFollowing", "1"), + ("IncreasedStoppedDistance", "3"), + ("IncreaseThermalLimits", "0"), + ("JerkInfo", "1"), + ("LaneChangeCustomizations", "0"), + ("LaneChangeTime", "1.0"), + ("LaneDetectionWidth", "9"), + ("LaneLinesWidth", "4"), + ("LateralMetrics", "1"), + ("LateralTune", "1"), + ("LeadDepartingAlert", "0"), + ("LeadDetectionThreshold", "35"), + ("LeadInfo", "1"), + ("LockDoors", "1"), + ("LongitudinalMetrics", "1"), + ("LongitudinalTune", "1"), + ("LongPitch", "1"), + ("LosAngelesCalibrationParams", ""), + ("LosAngelesDrives", "0"), + ("LosAngelesLiveTorqueParameters", ""), + ("LosAngelesScore", "0"), + ("LoudBlindspotAlert", "0"), + ("LowVoltageShutdown", str(VBATT_PAUSE_CHARGING)), + ("MapAcceleration", "0"), + ("MapDeceleration", "0"), + ("MapGears", "0"), + ("MapboxPublicKey", ""), + ("MapboxSecretKey", ""), + ("MapsSelected", ""), + ("MapStyle", "0"), + ("MaxDesiredAcceleration", "4.0"), + ("MinimumLaneChangeSpeed", str(LANE_CHANGE_SPEED_MIN / CV.MPH_TO_MS)), + ("Model", DEFAULT_MODEL), + ("ModelName", DEFAULT_MODEL_NAME), + ("ModelRandomizer", "0"), + ("ModelUI", "1"), + ("MTSCCurvatureCheck", "1"), + ("MTSCEnabled", "1"), + ("NavigationModels", ""), + ("NavigationUI", "1"), + ("NewLongAPI", "0"), + ("NewLongAPIGM", "1"), + ("NewToyotaTune", "0"), + ("NNFF", "1"), + ("NNFFLite", "1"), + ("NoLogging", "0"), + ("NorthDakotaCalibrationParams", ""), + ("NorthDakotaDrives", "0"), + ("NorthDakotaLiveTorqueParameters", ""), + ("NorthDakotaScore", "0"), + ("NotreDameCalibrationParams", ""), + ("NotreDameDrives", "0"), + ("NotreDameLiveTorqueParameters", ""), + ("NotreDameScore", "0"), + ("NoUploads", "0"), + ("NudgelessLaneChange", "0"), + ("NumericalTemp", "1"), + ("OfflineMode", "1"), + ("Offset1", "5"), + ("Offset2", "5"), + ("Offset3", "5"), + ("Offset4", "10"), + ("OneLaneChange", "1"), + ("OnroadDistanceButton", "0"), + ("openpilotMinutes", "0"), + ("PathEdgeWidth", "20"), + ("PathWidth", "6.1"), + ("PauseAOLOnBrake", "0"), + ("PauseLateralOnSignal", "0"), + ("PauseLateralSpeed", "0"), + ("PedalsOnUI", "0"), + ("PersonalizeOpenpilot", "1"), + ("PreferredSchedule", "2"), + ("PromptDistractedVolume", "101"), + ("PromptVolume", "101"), + ("QOLLateral", "1"), + ("QOLLongitudinal", "1"), + ("QOLVisuals", "1"), + ("RadarlessModels", ""), + ("RadicalTurtleCalibrationParams", ""), + ("RadicalTurtleDrives", "0"), + ("RadicalTurtleLiveTorqueParameters", ""), + ("RadicalTurtleScore", "0"), + ("RandomEvents", "0"), + ("RecertifiedHerbalistCalibrationParams", ""), + ("RecertifiedHerbalistDrives", "0"), + ("RecertifiedHerbalistLiveTorqueParameters", ""), + ("RecertifiedHerbalistScore", "0"), + ("RefuseVolume", "101"), + ("RelaxedFollow", "1.75"), + ("RelaxedJerkAcceleration", "100"), + ("RelaxedJerkDanger", "100"), + ("RelaxedJerkDeceleration", "100"), + ("RelaxedJerkSpeed", "100"), + ("RelaxedJerkSpeedDecrease", "100"), + ("RelaxedPersonalityProfile", "1"), + ("ResetFrogTheme", "0"), + ("ReverseCruise", "0"), + ("RoadEdgesWidth", "2"), + ("RoadNameUI", "1"), + ("RotatingWheel", "1"), + ("ScreenBrightness", "101"), + ("ScreenBrightnessOnroad", "101"), + ("ScreenManagement", "1"), + ("ScreenRecorder", "1"), + ("ScreenTimeout", "30"), + ("ScreenTimeoutOnroad", "30"), + ("SearchInput", "0"), + ("SecretGoodOpenpilotCalibrationParams", ""), + ("SecretGoodOpenpilotDrives", "0"), + ("SecretGoodOpenpilotLiveTorqueParameters", ""), + ("SecretGoodOpenpilotScore", "0"), + ("SetSpeedLimit", "0"), + ("SetSpeedOffset", "0"), + ("ShowCPU", "1"), + ("ShowGPU", "0"), + ("ShowIP", "0"), + ("ShowMemoryUsage", "1"), + ("ShowSLCOffset", "1"), + ("ShowSteering", "1"), + ("ShowStoppingPoint", "1"), + ("ShowStoppingPointMetrics", "0"), + ("ShowStorageLeft", "0"), + ("ShowStorageUsed", "0"), + ("Sidebar", "0"), + ("SidebarMetrics", "1"), + ("SignalMetrics", "0"), + ("SLCConfirmation", "1"), + ("SLCConfirmationHigher", "1"), + ("SLCConfirmationLower", "1"), + ("SLCFallback", "2"), + ("SLCLookaheadHigher", "5"), + ("SLCLookaheadLower", "5"), + ("SLCOverride", "1"), + ("SLCPriority1", "Dashboard"), + ("SLCPriority2", "Navigation"), + ("SLCPriority3", "Offline Maps"), + ("SNGHack", "1"), + ("SpeedLimitChangedAlert", "1"), + ("SpeedLimitController", "1"), + ("StartupMessageBottom", "so I do what I want 🐸"), + ("StartupMessageTop", "Hippity hoppity this is my property"), + ("StandardFollow", "1.45"), + ("StandardJerkAcceleration", "100"), + ("StandardJerkDanger", "100"), + ("StandardJerkDeceleration", "100"), + ("StandardJerkSpeed", "100"), + ("StandardJerkSpeedDecrease", "100"), + ("StandardPersonalityProfile", "1"), + ("StandbyMode", "0"), + ("StaticPedalsOnUI", "0"), + ("SteerFriction", "0.1"), + ("SteerFrictionStock", "0.1"), + ("SteerLatAccel", "2.5"), + ("SteerLatAccelStock", "2.5"), + ("SteerKP", "1"), + ("SteerKPStock", "1"), + ("SteerRatio", "15"), + ("SteerRatioStock", "15"), + ("StoppedTimer", "0"), + ("TacoTune", "0"), + ("ToyotaDoors", "1"), + ("TrafficFollow", "0.5"), + ("TrafficJerkAcceleration", "50"), + ("TrafficJerkDanger", "100"), + ("TrafficJerkDeceleration", "50"), + ("TrafficJerkSpeed", "50"), + ("TrafficJerkSpeedDecrease", "50"), + ("TrafficPersonalityProfile", "1"), + ("TuningInfo", "1"), + ("TurnAggressiveness", "100"), + ("TurnDesires", "0"), + ("UnlimitedLength", "1"), + ("UnlockDoors", "1"), + ("UseSI", "1"), + ("UseVienna", "0"), + ("VisionTurnControl", "1"), + ("VoltSNG", "0"), + ("WarningImmediateVolume", "101"), + ("WarningSoftVolume", "101"), + ("WD40CalibrationParams", ""), + ("WD40Drives", "0"), + ("WD40LiveTorqueParameters", ""), + ("WD40Score", "0"), + ("WheelIcon", "frog"), + ("WheelSpeed", "0") +] + class FrogPilotVariables: def __init__(self): + self.default_frogpilot_toggles = SimpleNamespace(**dict(frogpilot_default_params)) self.frogpilot_toggles = SimpleNamespace() self.params = Params() self.params_memory = Params("/dev/shm/params") - self.has_prime = self.params.get_int("PrimeType") > 0 - - self.update_frogpilot_params(False) - - @property - def toggles(self): - return self.frogpilot_toggles - - @property - def toggles_updated(self): - return self.params_memory.get_bool("FrogPilotTogglesUpdated") - - def update_frogpilot_params(self, started=True): + def update(self, started): toggle = self.frogpilot_toggles - openpilot_installed = self.params.get_bool("HasAcceptedTerms") key = "CarParams" if started else "CarParamsPersistent" @@ -57,180 +369,222 @@ def update_frogpilot_params(self, started=True): car_make = CP.carName car_model = CP.carFingerprint has_auto_tune = (car_model == "hyundai" or car_model == "toyota") and CP.lateralTuning.which == "torque" + has_bsm = CP.enableBsm + has_radar = not CP.radarUnavailable is_pid_car = CP.lateralTuning.which == "pid" - max_acceleration_allowed = key == "CarParams" and CP.alternativeExperience & ALTERNATIVE_EXPERIENCE.RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX - toggle.openpilot_longitudinal = CP.openpilotLongitudinalControl + max_acceleration_enabled = key == "CarParams" and CP.alternativeExperience & ALTERNATIVE_EXPERIENCE.RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX + openpilot_longitudinal = CP.openpilotLongitudinalControl pcm_cruise = CP.pcmCruise else: always_on_lateral_set = False car_make = "mock" car_model = "mock" has_auto_tune = False + has_bsm = False + has_radar = False is_pid_car = False - max_acceleration_allowed = False - toggle.openpilot_longitudinal = False + max_acceleration_enabled = False + openpilot_longitudinal = False pcm_cruise = False toggle.is_metric = self.params.get_bool("IsMetric") - distance_conversion = 1. if toggle.is_metric else CV.FOOT_TO_METER + distance_conversion = 1 if toggle.is_metric else CV.FOOT_TO_METER + small_distance_conversion = 1 if toggle.is_metric else CV.INCH_TO_CM speed_conversion = CV.KPH_TO_MS if toggle.is_metric else CV.MPH_TO_MS - advanced_custom_onroad_ui = self.params.get_bool("AdvancedCustomUI") - toggle.show_stopping_point = advanced_custom_onroad_ui and self.params.get_bool("ShowStoppingPoint") + toggle.advanced_custom_onroad_ui = self.params.get_bool("AdvancedCustomUI") + toggle.hide_lead_marker = toggle.advanced_custom_onroad_ui and self.params.get_bool("HideLeadMarker") + toggle.hide_speed = toggle.advanced_custom_onroad_ui and self.params.get_bool("HideSpeed") + toggle.hide_map_icon = toggle.advanced_custom_onroad_ui and self.params.get_bool("HideMapIcon") + toggle.hide_max_speed = toggle.advanced_custom_onroad_ui and self.params.get_bool("HideMaxSpeed") + toggle.hide_alerts = toggle.advanced_custom_onroad_ui and self.params.get_bool("HideAlerts") + toggle.use_wheel_speed = toggle.advanced_custom_onroad_ui and self.params.get_bool("WheelSpeed") - advanced_lateral_tune = self.params.get_bool("AdvancedLateralTune") + toggle.advanced_lateral_tuning = self.params.get_bool("AdvancedLateralTune") stock_steer_friction = self.params.get_float("SteerFrictionStock") - toggle.steer_friction = self.params.get_float("SteerFriction") if advanced_lateral_tune else stock_steer_friction + toggle.steer_friction = self.params.get_float("SteerFriction") if toggle.advanced_lateral_tuning else stock_steer_friction toggle.use_custom_steer_friction = toggle.steer_friction != stock_steer_friction and not is_pid_car - stock_steer_lat_accel_factor = self.params.get_float("SteerLatAccelStock") - toggle.steer_lat_accel_factor = self.params.get_float("SteerLatAccel") if advanced_lateral_tune else stock_steer_lat_accel_factor - toggle.use_custom_lat_accel_factor = toggle.steer_lat_accel_factor != stock_steer_lat_accel_factor and not is_pid_car stock_steer_kp = self.params.get_float("SteerKPStock") - toggle.steer_kp = self.params.get_float("SteerKP") if advanced_lateral_tune else stock_steer_kp + toggle.steer_kp = self.params.get_float("SteerKP") if toggle.advanced_lateral_tuning else stock_steer_kp toggle.use_custom_kp = toggle.steer_kp != stock_steer_kp and not is_pid_car + stock_steer_lat_accel_factor = self.params.get_float("SteerLatAccelStock") + toggle.steer_lat_accel_factor = self.params.get_float("SteerLatAccel") if toggle.advanced_lateral_tuning else stock_steer_lat_accel_factor + toggle.use_custom_lat_accel_factor = toggle.steer_lat_accel_factor != stock_steer_lat_accel_factor and not is_pid_car stock_steer_ratio = self.params.get_float("SteerRatioStock") - toggle.steer_ratio = self.params.get_float("SteerRatio") if advanced_lateral_tune else stock_steer_ratio + toggle.steer_ratio = self.params.get_float("SteerRatio") if toggle.advanced_lateral_tuning else stock_steer_ratio toggle.use_custom_steer_ratio = toggle.steer_ratio != stock_steer_ratio - toggle.force_auto_tune = advanced_lateral_tune and not has_auto_tune and self.params.get_bool("ForceAutoTune") - toggle.force_auto_tune_off = advanced_lateral_tune and has_auto_tune and self.params.get_bool("ForceAutoTuneOff") - toggle.taco_tune = advanced_lateral_tune and self.params.get_bool("TacoTune") - toggle.turn_desires = advanced_lateral_tune and self.params.get_bool("TurnDesires") - - advanced_longitudinal_tune = toggle.openpilot_longitudinal and self.params.get_bool("LongitudinalTune") - toggle.lead_detection_threshold = self.params.get_int("LeadDetectionThreshold") / 100. if advanced_longitudinal_tune else 0.5 - toggle.max_desired_accel = self.params.get_float("MaxDesiredAcceleration") if advanced_longitudinal_tune else 4.0 - - advanced_quality_of_life_driving = self.params.get_bool("AdvancedQOLDriving") - toggle.force_standstill = advanced_quality_of_life_driving and self.params.get_bool("ForceStandstill") - toggle.force_stops = advanced_quality_of_life_driving and self.params.get_bool("ForceStops") - toggle.set_speed_offset = self.params.get_int("SetSpeedOffset") * (1. if toggle.is_metric else CV.MPH_TO_KPH) if advanced_quality_of_life_driving and not pcm_cruise else 0 + toggle.force_auto_tune = toggle.advanced_lateral_tuning and not has_auto_tune and not is_pid_car and self.params.get_bool("ForceAutoTune") + toggle.force_auto_tune_off = toggle.advanced_lateral_tuning and has_auto_tune and not is_pid_car and self.params.get_bool("ForceAutoTuneOff") toggle.alert_volume_control = self.params.get_bool("AlertVolumeControl") - toggle.disengage_volume = self.params.get_int("DisengageVolume") if toggle.alert_volume_control else 100 - toggle.engage_volume = self.params.get_int("EngageVolume") if toggle.alert_volume_control else 100 - toggle.prompt_volume = self.params.get_int("PromptVolume") if toggle.alert_volume_control else 100 - toggle.promptDistracted_volume = self.params.get_int("PromptDistractedVolume") if toggle.alert_volume_control else 100 - toggle.refuse_volume = self.params.get_int("RefuseVolume") if toggle.alert_volume_control else 100 - toggle.warningSoft_volume = self.params.get_int("WarningSoftVolume") if toggle.alert_volume_control else 100 - toggle.warningImmediate_volume = max(self.params.get_int("WarningImmediateVolume"), 25) if toggle.alert_volume_control else 100 + toggle.disengage_volume = self.params.get_int("DisengageVolume") if toggle.alert_volume_control else 101 + toggle.engage_volume = self.params.get_int("EngageVolume") if toggle.alert_volume_control else 101 + toggle.prompt_volume = self.params.get_int("PromptVolume") if toggle.alert_volume_control else 101 + toggle.promptDistracted_volume = self.params.get_int("PromptDistractedVolume") if toggle.alert_volume_control else 101 + toggle.refuse_volume = self.params.get_int("RefuseVolume") if toggle.alert_volume_control else 101 + toggle.warningSoft_volume = self.params.get_int("WarningSoftVolume") if toggle.alert_volume_control else 101 + toggle.warningImmediate_volume = max(self.params.get_int("WarningImmediateVolume"), 25) if toggle.alert_volume_control else 101 toggle.always_on_lateral = always_on_lateral_set and self.params.get_bool("AlwaysOnLateral") toggle.always_on_lateral_lkas = toggle.always_on_lateral and car_make != "subaru" and self.params.get_bool("AlwaysOnLateralLKAS") toggle.always_on_lateral_main = toggle.always_on_lateral and self.params.get_bool("AlwaysOnLateralMain") toggle.always_on_lateral_pause_speed = self.params.get_int("PauseAOLOnBrake") if toggle.always_on_lateral else 0 + toggle.always_on_lateral_status_bar = toggle.always_on_lateral and not self.params.get_bool("HideAOLStatusBar") toggle.automatic_updates = self.params.get_bool("AutomaticUpdates") toggle.cluster_offset = self.params.get_float("ClusterOffset") if car_make == "toyota" else 1 - toggle.conditional_experimental_mode = toggle.openpilot_longitudinal and self.params.get_bool("ConditionalExperimental") - toggle.conditional_limit = self.params.get_int("CESpeed") * speed_conversion if toggle.conditional_experimental_mode else 0 - toggle.conditional_limit_lead = self.params.get_int("CESpeedLead") * speed_conversion if toggle.conditional_experimental_mode else 0 + toggle.conditional_experimental_mode = openpilot_longitudinal and self.params.get_bool("ConditionalExperimental") toggle.conditional_curves = toggle.conditional_experimental_mode and self.params.get_bool("CECurves") toggle.conditional_curves_lead = toggle.conditional_curves and self.params.get_bool("CECurvesLead") toggle.conditional_lead = toggle.conditional_experimental_mode and self.params.get_bool("CELead") toggle.conditional_slower_lead = toggle.conditional_lead and self.params.get_bool("CESlowerLead") toggle.conditional_stopped_lead = toggle.conditional_lead and self.params.get_bool("CEStoppedLead") + toggle.conditional_limit = self.params.get_int("CESpeed") * speed_conversion if toggle.conditional_experimental_mode else 0 + toggle.conditional_limit_lead = self.params.get_int("CESpeedLead") * speed_conversion if toggle.conditional_experimental_mode else 0 toggle.conditional_navigation = toggle.conditional_experimental_mode and self.params.get_bool("CENavigation") toggle.conditional_navigation_intersections = toggle.conditional_navigation and self.params.get_bool("CENavigationIntersections") toggle.conditional_navigation_lead = toggle.conditional_navigation and self.params.get_bool("CENavigationLead") toggle.conditional_navigation_turns = toggle.conditional_navigation and self.params.get_bool("CENavigationTurns") toggle.conditional_model_stop_time = self.params.get_int("CEModelStopTime") if toggle.conditional_experimental_mode else 0 toggle.conditional_signal = self.params.get_int("CESignalSpeed") if toggle.conditional_experimental_mode else 0 - toggle.conditional_signal_lane_detection = toggle.conditional_signal and self.params.get_bool("CESignalLaneDetection") + toggle.conditional_signal_lane_detection = toggle.conditional_signal != 0 and self.params.get_bool("CESignalLaneDetection") + toggle.conditional_status_bar = toggle.conditional_experimental_mode and not self.params.get_bool("HideCEMStatusBar") if toggle.conditional_experimental_mode: self.params.put_bool("ExperimentalMode", True) + toggle.crosstrek_torque = car_model == "SUBARU_IMPREZA" and self.params.get_bool("CrosstrekTorque") + toggle.current_holiday_theme = self.params.get("CurrentHolidayTheme", encoding='utf-8') if self.params.get_bool("HolidayThemes") else None - curve_speed_controller = toggle.openpilot_longitudinal and self.params.get_bool("CurveSpeedControl") - toggle.map_turn_speed_controller = curve_speed_controller and self.params.get_bool("MTSCEnabled") + toggle.curve_speed_controller = openpilot_longitudinal and self.params.get_bool("CurveSpeedControl") + toggle.curve_sensitivity = self.params.get_int("CurveSensitivity") / 100 if toggle.curve_speed_controller else 1 + toggle.turn_aggressiveness = self.params.get_int("TurnAggressiveness") / 100 if toggle.curve_speed_controller else 1 + toggle.disable_curve_speed_smoothing = toggle.curve_speed_controller and self.params.get_bool("DisableCurveSpeedSmoothing") + toggle.map_turn_speed_controller = toggle.curve_speed_controller and self.params.get_bool("MTSCEnabled") toggle.mtsc_curvature_check = toggle.map_turn_speed_controller and self.params.get_bool("MTSCCurvatureCheck") - toggle.vision_turn_controller = curve_speed_controller and self.params.get_bool("VisionTurnControl") - toggle.curve_sensitivity = self.params.get_int("CurveSensitivity") / 100. if curve_speed_controller else 1 - toggle.turn_aggressiveness = self.params.get_int("TurnAggressiveness") / 100. if curve_speed_controller else 1 - - custom_alerts = self.params.get_bool("CustomAlerts") - toggle.goat_scream = toggle.current_holiday_theme is None and custom_alerts and self.params.get_bool("GoatScream") - toggle.green_light_alert = custom_alerts and self.params.get_bool("GreenLightAlert") - toggle.lead_departing_alert = custom_alerts and self.params.get_bool("LeadDepartingAlert") - toggle.loud_blindspot_alert = custom_alerts and self.params.get_bool("LoudBlindspotAlert") - - toggle.custom_personalities = toggle.openpilot_longitudinal and self.params.get_bool("CustomPersonalities") - aggressive_profile = toggle.custom_personalities and self.params.get_bool("AggressivePersonalityProfile") - toggle.aggressive_jerk_acceleration = self.params.get_int("AggressiveJerkAcceleration") / 100. if aggressive_profile else 0.5 - toggle.aggressive_jerk_deceleration = self.params.get_int("AggressiveJerkDeceleration") / 100. if aggressive_profile else 0.5 - toggle.aggressive_jerk_danger = self.params.get_int("AggressiveJerkDanger") / 100. if aggressive_profile else 0.5 - toggle.aggressive_jerk_speed = self.params.get_int("AggressiveJerkSpeed") / 100. if aggressive_profile else 0.5 - toggle.aggressive_jerk_speed_decrease = self.params.get_int("AggressiveJerkSpeedDecrease") / 100. if aggressive_profile else 0.5 - toggle.aggressive_follow = self.params.get_float("AggressiveFollow") if aggressive_profile else 1.25 - standard_profile = toggle.custom_personalities and self.params.get_bool("StandardPersonalityProfile") - toggle.standard_jerk_acceleration = self.params.get_int("StandardJerkAcceleration") / 100. if standard_profile else 1.0 - toggle.standard_jerk_deceleration = self.params.get_int("StandardJerkDeceleration") / 100. if standard_profile else 1.0 - toggle.standard_jerk_danger = self.params.get_int("StandardJerkDanger") / 100. if standard_profile else 0.5 - toggle.standard_jerk_speed = self.params.get_int("StandardJerkSpeed") / 100. if standard_profile else 1.0 - toggle.standard_jerk_speed_decrease = self.params.get_int("StandardJerkSpeedDecrease") / 100. if standard_profile else 1.0 - toggle.standard_follow = self.params.get_float("StandardFollow") if standard_profile else 1.45 - relaxed_profile = toggle.custom_personalities and self.params.get_bool("RelaxedPersonalityProfile") - toggle.relaxed_jerk_acceleration = self.params.get_int("RelaxedJerkAcceleration") / 100. if relaxed_profile else 1.0 - toggle.relaxed_jerk_deceleration = self.params.get_int("RelaxedJerkDeceleration") / 100. if relaxed_profile else 1.0 - toggle.relaxed_jerk_danger = self.params.get_int("RelaxedJerkDanger") / 100. if relaxed_profile else 0.5 - toggle.relaxed_jerk_speed = self.params.get_int("RelaxedJerkSpeed") / 100. if relaxed_profile else 1.0 - toggle.relaxed_jerk_speed_decrease = self.params.get_int("RelaxedJerkSpeedDecrease") / 100. if relaxed_profile else 1.0 - toggle.relaxed_follow = self.params.get_float("RelaxedFollow") if relaxed_profile else 1.75 - traffic_profile = toggle.custom_personalities and self.params.get_bool("TrafficPersonalityProfile") - toggle.traffic_mode_jerk_acceleration = [self.params.get_int("TrafficJerkAcceleration") / 100., toggle.aggressive_jerk_acceleration] if traffic_profile else [0.5, 0.5] - toggle.traffic_mode_jerk_deceleration = [self.params.get_int("TrafficJerkDeceleration") / 100., toggle.aggressive_jerk_deceleration] if traffic_profile else [0.5, 0.5] - toggle.traffic_mode_jerk_danger = [self.params.get_int("TrafficJerkDanger") / 100., toggle.aggressive_jerk_danger] if traffic_profile else [1.0, 1.0] - toggle.traffic_mode_jerk_speed = [self.params.get_int("TrafficJerkSpeed") / 100., toggle.aggressive_jerk_speed] if traffic_profile else [0.5, 0.5] - toggle.traffic_mode_jerk_speed_decrease = [self.params.get_int("TrafficJerkSpeedDecrease") / 100., toggle.aggressive_jerk_speed_decrease] if traffic_profile else [0.5, 0.5] - toggle.traffic_mode_t_follow = [self.params.get_float("TrafficFollow"), toggle.aggressive_follow] if traffic_profile else [0.5, 1.0] - - custom_ui = self.params.get_bool("CustomUI") - custom_paths = custom_ui and self.params.get_bool("CustomPaths") - toggle.adjacent_lanes = custom_paths and self.params.get_bool("AdjacentPath") - toggle.blind_spot_path = custom_paths and self.params.get_bool("BlindSpotPath") - - developer_ui = self.params.get_bool("DeveloperUI") - show_lateral = developer_ui and self.params.get_bool("LateralMetrics") - toggle.adjacent_path_metrics = show_lateral and self.params.get_bool("AdjacentPathMetrics") + toggle.vision_turn_controller = toggle.curve_speed_controller and self.params.get_bool("VisionTurnControl") + + toggle.custom_alerts = self.params.get_bool("CustomAlerts") + toggle.goat_scream_alert = toggle.current_holiday_theme is None and toggle.custom_alerts and self.params.get_bool("GoatScream") + toggle.green_light_alert = toggle.custom_alerts and self.params.get_bool("GreenLightAlert") + toggle.lead_departing_alert = toggle.custom_alerts and self.params.get_bool("LeadDepartingAlert") + toggle.loud_blindspot_alert = has_bsm and toggle.custom_alerts and self.params.get_bool("LoudBlindspotAlert") + toggle.speed_limit_changed_alert = toggle.custom_alerts and self.params.get_bool("SpeedLimitChangedAlert") + + toggle.custom_personalities = openpilot_longitudinal and self.params.get_bool("CustomPersonalities") + toggle.aggressive_profile = toggle.custom_personalities and self.params.get_bool("AggressivePersonalityProfile") + toggle.aggressive_jerk_acceleration = clip(self.params.get_int("AggressiveJerkAcceleration") / 100, 0.01, 5) if toggle.aggressive_profile else 0.5 + toggle.aggressive_jerk_deceleration = clip(self.params.get_int("AggressiveJerkDeceleration") / 100, 0.01, 5) if toggle.aggressive_profile else 0.5 + toggle.aggressive_jerk_danger = clip(self.params.get_int("AggressiveJerkDanger") / 100, 0.01, 5) if toggle.aggressive_profile else 0.5 + toggle.aggressive_jerk_speed = clip(self.params.get_int("AggressiveJerkSpeed") / 100, 0.01, 5) if toggle.aggressive_profile else 0.5 + toggle.aggressive_jerk_speed_decrease = clip(self.params.get_int("AggressiveJerkSpeedDecrease") / 100, 0.01, 5) if toggle.aggressive_profile else 0.5 + toggle.aggressive_follow = clip(self.params.get_float("AggressiveFollow"), 1, 5) if toggle.aggressive_profile else 1.25 + toggle.standard_profile = toggle.custom_personalities and self.params.get_bool("StandardPersonalityProfile") + toggle.standard_jerk_acceleration = clip(self.params.get_int("StandardJerkAcceleration") / 100, 0.01, 5) if toggle.standard_profile else 1.0 + toggle.standard_jerk_deceleration = clip(self.params.get_int("StandardJerkDeceleration") / 100, 0.01, 5) if toggle.standard_profile else 1.0 + toggle.standard_jerk_danger = clip(self.params.get_int("StandardJerkDanger") / 100, 0.01, 5) if toggle.standard_profile else 0.5 + toggle.standard_jerk_speed = clip(self.params.get_int("StandardJerkSpeed") / 100, 0.01, 5) if toggle.standard_profile else 1.0 + toggle.standard_jerk_speed_decrease = clip(self.params.get_int("StandardJerkSpeedDecrease") / 100, 0.01, 5) if toggle.standard_profile else 1.0 + toggle.standard_follow = clip(self.params.get_float("StandardFollow"), 1, 5) if toggle.standard_profile else 1.45 + toggle.relaxed_profile = toggle.custom_personalities and self.params.get_bool("RelaxedPersonalityProfile") + toggle.relaxed_jerk_acceleration = clip(self.params.get_int("RelaxedJerkAcceleration") / 100, 0.01, 5) if toggle.relaxed_profile else 1.0 + toggle.relaxed_jerk_deceleration = clip(self.params.get_int("RelaxedJerkDeceleration") / 100, 0.01, 5) if toggle.relaxed_profile else 1.0 + toggle.relaxed_jerk_danger = clip(self.params.get_int("RelaxedJerkDanger") / 100, 0.01, 5) if toggle.relaxed_profile else 0.5 + toggle.relaxed_jerk_speed = clip(self.params.get_int("RelaxedJerkSpeed") / 100, 0.01, 5) if toggle.relaxed_profile else 1.0 + toggle.relaxed_jerk_speed_decrease = clip(self.params.get_int("RelaxedJerkSpeedDecrease") / 100, 0.01, 5) if toggle.relaxed_profile else 1.0 + toggle.relaxed_follow = clip(self.params.get_float("RelaxedFollow"), 1, 5) if toggle.relaxed_profile else 1.75 + toggle.traffic_profile = toggle.custom_personalities and self.params.get_bool("TrafficPersonalityProfile") + toggle.traffic_mode_jerk_acceleration = [clip(self.params.get_int("TrafficJerkAcceleration") / 100, 0.01, 5), toggle.aggressive_jerk_acceleration] if toggle.traffic_profile else [0.5, 0.5] + toggle.traffic_mode_jerk_deceleration = [clip(self.params.get_int("TrafficJerkDeceleration") / 100, 0.01, 5), toggle.aggressive_jerk_deceleration] if toggle.traffic_profile else [0.5, 0.5] + toggle.traffic_mode_jerk_danger = [clip(self.params.get_int("TrafficJerkDanger") / 100, 0.01, 5), toggle.aggressive_jerk_danger] if toggle.traffic_profile else [1.0, 1.0] + toggle.traffic_mode_jerk_speed = [clip(self.params.get_int("TrafficJerkSpeed") / 100, 0.01, 5), toggle.aggressive_jerk_speed] if toggle.traffic_profile else [0.5, 0.5] + toggle.traffic_mode_jerk_speed_decrease = [clip(self.params.get_int("TrafficJerkSpeedDecrease") / 100, 0.01, 5), toggle.aggressive_jerk_speed_decrease] if toggle.traffic_profile else [0.5, 0.5] + toggle.traffic_mode_t_follow = [clip(self.params.get_float("TrafficFollow"), 0.5, 5), toggle.aggressive_follow] if toggle.traffic_profile else [0.5, 1.0] + + toggle.custom_ui = self.params.get_bool("CustomUI") + toggle.acceleration_path = toggle.custom_ui and self.params.get_bool("AccelerationPath") + toggle.adjacent_paths = toggle.custom_ui and self.params.get_bool("AdjacentPath") + toggle.blind_spot_path = has_bsm and toggle.custom_ui and self.params.get_bool("BlindSpotPath") + toggle.compass = toggle.custom_ui and self.params.get_bool("Compass") + toggle.pedals_on_ui = toggle.custom_ui and self.params.get_bool("PedalsOnUI") + toggle.dynamic_pedals_on_ui = toggle.pedals_on_ui and self.params.get_bool("DynamicPedalsOnUI") + toggle.static_pedals_on_ui = toggle.pedals_on_ui and self.params.get_bool("StaticPedalsOnUI") + toggle.rotating_wheel = toggle.custom_ui and self.params.get_bool("RotatingWheel") + + toggle.developer_ui = self.params.get_bool("DeveloperUI") + toggle.border_metrics = toggle.developer_ui and self.params.get_bool("BorderMetrics") + toggle.blind_spot_metrics = has_bsm and toggle.border_metrics and self.params.get_bool("BlindSpotMetrics") + toggle.signal_metrics = toggle.border_metrics and self.params.get_bool("SignalMetrics") + toggle.steering_metrics = toggle.border_metrics and self.params.get_bool("ShowSteering") + toggle.show_fps = toggle.developer_ui and self.params.get_bool("FPSCounter") + toggle.lateral_metrics = toggle.developer_ui and self.params.get_bool("LateralMetrics") + toggle.adjacent_path_metrics = toggle.lateral_metrics and self.params.get_bool("AdjacentPathMetrics") + toggle.lateral_tuning_metrics = toggle.lateral_metrics and self.params.get_bool("TuningInfo") + toggle.longitudinal_metrics = toggle.developer_ui and self.params.get_bool("LongitudinalMetrics") + toggle.adjacent_lead_tracking = has_radar and toggle.longitudinal_metrics and self.params.get_bool("AdjacentLeadsUI") + toggle.lead_metrics = toggle.longitudinal_metrics and self.params.get_bool("LeadInfo") + toggle.jerk_metrics = toggle.longitudinal_metrics and self.params.get_bool("JerkInfo") + toggle.numerical_temp = toggle.developer_ui and self.params.get_bool("NumericalTemp") + toggle.fahrenheit = toggle.numerical_temp and self.params.get_bool("Fahrenheit") + toggle.sidebar_metrics = toggle.developer_ui and self.params.get_bool("SidebarMetrics") + toggle.cpu_metrics = toggle.sidebar_metrics and self.params.get_bool("ShowCPU") + toggle.gpu_metrics = toggle.sidebar_metrics and self.params.get_bool("ShowGPU") + toggle.ip_metrics = toggle.sidebar_metrics and self.params.get_bool("ShowIP") + toggle.memory_metrics = toggle.sidebar_metrics and self.params.get_bool("ShowMemoryUsage") + toggle.storage_left_metrics = toggle.sidebar_metrics and self.params.get_bool("ShowStorageLeft") + toggle.storage_used_metrics = toggle.sidebar_metrics and self.params.get_bool("ShowStorageUsed") + toggle.use_si_metrics = toggle.developer_ui and self.params.get_bool("UseSI") toggle.device_management = self.params.get_bool("DeviceManagement") device_shutdown_setting = self.params.get_int("DeviceShutdown") if toggle.device_management else 33 toggle.device_shutdown_time = (device_shutdown_setting - 3) * 3600 if device_shutdown_setting >= 4 else device_shutdown_setting * (60 * 15) toggle.increase_thermal_limits = toggle.device_management and self.params.get_bool("IncreaseThermalLimits") - toggle.low_voltage_shutdown = self.params.get_float("LowVoltageShutdown") if toggle.device_management else VBATT_PAUSE_CHARGING + toggle.low_voltage_shutdown = clip(self.params.get_float("LowVoltageShutdown"), VBATT_PAUSE_CHARGING, 12.5) if toggle.device_management else VBATT_PAUSE_CHARGING + toggle.no_logging = toggle.device_management and self.params.get_bool("NoLogging") + toggle.no_uploads = toggle.device_management and self.params.get_bool("NoUploads") toggle.offline_mode = toggle.device_management and self.params.get_bool("OfflineMode") - toggle.experimental_mode_via_press = toggle.openpilot_longitudinal and self.params.get_bool("ExperimentalModeActivation") + toggle.experimental_gm_tune = openpilot_longitudinal and car_make == "gm" and self.params.get_bool("ExperimentalGMTune") + + toggle.experimental_mode_via_press = openpilot_longitudinal and self.params.get_bool("ExperimentalModeActivation") toggle.experimental_mode_via_distance = toggle.experimental_mode_via_press and self.params.get_bool("ExperimentalModeViaDistance") toggle.experimental_mode_via_lkas = not toggle.always_on_lateral_lkas and toggle.experimental_mode_via_press and car_make != "subaru" and self.params.get_bool("ExperimentalModeViaLKAS") + toggle.experimental_mode_via_tap = toggle.experimental_mode_via_press and self.params.get_bool("ExperimentalModeViaTap") + + toggle.frogsgomoo_tweak = openpilot_longitudinal and car_make == "toyota" and self.params.get_bool("FrogsGoMoosTweak") - lane_change_customizations = self.params.get_bool("LaneChangeCustomizations") - toggle.lane_change_delay = self.params.get_int("LaneChangeTime") if lane_change_customizations else 0 - toggle.lane_detection_width = self.params.get_int("LaneDetectionWidth") * distance_conversion if lane_change_customizations else 0 + toggle.lane_change_customizations = self.params.get_bool("LaneChangeCustomizations") + toggle.lane_change_delay = self.params.get_float("LaneChangeTime") if toggle.lane_change_customizations else 0 + toggle.lane_detection_width = self.params.get_float("LaneDetectionWidth") * distance_conversion if toggle.lane_change_customizations else 0 toggle.lane_detection = toggle.lane_detection_width != 0 - toggle.minimum_lane_change_speed = self.params.get_int("MinimumLaneChangeSpeed") * speed_conversion if lane_change_customizations else LANE_CHANGE_SPEED_MIN - toggle.nudgeless = lane_change_customizations and self.params.get_bool("NudgelessLaneChange") - toggle.one_lane_change = lane_change_customizations and self.params.get_bool("OneLaneChange") + toggle.minimum_lane_change_speed = self.params.get_int("MinimumLaneChangeSpeed") * speed_conversion if toggle.lane_change_customizations else LANE_CHANGE_SPEED_MIN + toggle.nudgeless = toggle.lane_change_customizations and self.params.get_bool("NudgelessLaneChange") + toggle.one_lane_change = toggle.lane_change_customizations and self.params.get_bool("OneLaneChange") - toggle.long_pitch = toggle.openpilot_longitudinal and car_make == "gm" and self.params.get_bool("LongPitch") - toggle.volt_sng = car_model == "CHEVROLET_VOLT" and self.params.get_bool("VoltSNG") + toggle.lateral_tuning = self.params.get_bool("LateralTune") + toggle.nnff = toggle.lateral_tuning and self.params.get_bool("NNFF") + toggle.smooth_curve_handling = toggle.lateral_tuning and self.params.get_bool("NNFFLite") + toggle.taco_tune = toggle.lateral_tuning and self.params.get_bool("TacoTune") + toggle.use_turn_desires = toggle.lateral_tuning and self.params.get_bool("TurnDesires") + + toggle.long_pitch = openpilot_longitudinal and car_make == "gm" and self.params.get_bool("LongPitch") - longitudinal_tune = toggle.openpilot_longitudinal and self.params.get_bool("LongitudinalTune") - toggle.acceleration_profile = self.params.get_int("AccelerationProfile") if longitudinal_tune else 0 - toggle.sport_plus = max_acceleration_allowed and toggle.acceleration_profile == 3 - toggle.deceleration_profile = self.params.get_int("DecelerationProfile") if longitudinal_tune else 0 - toggle.human_acceleration = longitudinal_tune and self.params.get_bool("HumanAcceleration") - toggle.human_following = longitudinal_tune and self.params.get_bool("HumanFollowing") - toggle.increase_stopped_distance = self.params.get_int("IncreasedStoppedDistance") * distance_conversion if longitudinal_tune else 0 - - toggle.model_manager = self.params.get_bool("ModelManagement", block=openpilot_installed) - available_models = self.params.get("AvailableModels", block=toggle.model_manager, encoding='utf-8') or "" - available_model_names = self.params.get("AvailableModelsNames", block=toggle.model_manager, encoding='utf-8') or "" - if toggle.model_manager and available_models: - toggle.model_randomizer = self.params.get_bool("ModelRandomizer") - if toggle.model_randomizer: + toggle.longitudinal_tuning = openpilot_longitudinal and self.params.get_bool("LongitudinalTune") + toggle.acceleration_profile = self.params.get_int("AccelerationProfile") if toggle.longitudinal_tuning else 0 + toggle.sport_plus = max_acceleration_enabled and toggle.acceleration_profile == 3 + toggle.deceleration_profile = self.params.get_int("DecelerationProfile") if toggle.longitudinal_tuning else 0 + toggle.human_acceleration = toggle.longitudinal_tuning and self.params.get_bool("HumanAcceleration") + toggle.human_following = toggle.longitudinal_tuning and self.params.get_bool("HumanFollowing") + toggle.increased_stopped_distance = self.params.get_int("IncreasedStoppedDistance") * distance_conversion if toggle.longitudinal_tuning else 0 + toggle.lead_detection_probability = clip(self.params.get_int("LeadDetectionThreshold") / 100, 0.01, 0.99) if toggle.longitudinal_tuning else 0.5 + toggle.max_desired_acceleration = clip(self.params.get_float("MaxDesiredAcceleration"), 0.1, 4.0) if toggle.longitudinal_tuning else 4.0 + + available_models = self.params.get("AvailableModels", block=True, encoding='utf-8') or "" + available_model_names = self.params.get("AvailableModelsNames", block=True, encoding='utf-8') or "" + if available_models: + if self.params.get_bool("ModelRandomizer"): blacklisted_models = (self.params.get("BlacklistedModels", encoding='utf-8') or "").split(',') existing_models = [model for model in available_models.split(',') if model not in blacklisted_models and os.path.exists(os.path.join(MODELS_PATH, f"{model}.thneed"))] toggle.model = random.choice(existing_models) if existing_models else DEFAULT_MODEL @@ -246,66 +600,558 @@ def update_frogpilot_params(self, started=True): self.params.check_key(toggle.part_model_param + "CalibrationParams") except UnknownKeyName: toggle.part_model_param = "" - elif toggle.model == "secret-good-openpilot": - toggle.part_model_param = "SecretGoodOpenpilot" else: toggle.model = DEFAULT_MODEL toggle.part_model_param = "" + classic_models = self.params.get("ClassicModels", encoding='utf-8') or "" + toggle.classic_model = classic_models and toggle.model in classic_models.split(',') navigation_models = self.params.get("NavigationModels", encoding='utf-8') or "" toggle.navigationless_model = navigation_models and toggle.model not in navigation_models.split(',') radarless_models = self.params.get("RadarlessModels", encoding='utf-8') or "" toggle.radarless_model = radarless_models and toggle.model in radarless_models.split(',') - toggle.secretgoodopenpilot_model = toggle.model == "secret-good-openpilot" - velocity_models = self.params.get("VelocityModels", encoding='utf-8') or "" - toggle.velocity_model = velocity_models and toggle.model in velocity_models.split(',') + + toggle.model_ui = self.params.get_bool("ModelUI") + toggle.dynamic_path_width = toggle.model_ui and self.params.get_bool("DynamicPathWidth") + toggle.lane_line_width = self.params.get_int("LaneLinesWidth") * small_distance_conversion / 200 if toggle.model_ui else 0.025 + toggle.path_edge_width = self.params.get_int("PathEdgeWidth") if toggle.model_ui else 20 + toggle.path_width = self.params.get_float("PathWidth") * distance_conversion / 2 if toggle.model_ui else 0.9 + toggle.road_edge_width = self.params.get_int("RoadEdgesWidth") * small_distance_conversion / 200 if toggle.model_ui else 0.025 + toggle.show_stopping_point = toggle.model_ui and self.params.get_bool("ShowStoppingPoint") + toggle.show_stopping_point_metrics = toggle.show_stopping_point and self.params.get_bool("ShowStoppingPointMetrics") + toggle.unlimited_road_ui_length = toggle.model_ui and self.params.get_bool("UnlimitedLength") + + toggle.navigation_ui = self.params.get_bool("NavigationUI") + toggle.big_map = toggle.navigation_ui and self.params.get_bool("BigMap") + toggle.full_map = toggle.big_map and self.params.get_bool("FullMap") + toggle.map_style = self.params.get_int("MapStyle") if toggle.navigation_ui else 0 + toggle.road_name_ui = toggle.navigation_ui and self.params.get_bool("RoadNameUI") + toggle.show_speed_limit_offset = toggle.navigation_ui and self.params.get_bool("ShowSLCOffset") + toggle.speed_limit_vienna = toggle.navigation_ui and self.params.get_bool("UseVienna") + + toggle.new_long_api_gm = openpilot_longitudinal and car_make == "gm" and self.params.get_bool("NewLongAPIGM") + toggle.new_long_api_hkg = openpilot_longitudinal and car_make == "hyundai" and self.params.get_bool("NewLongAPI") + + toggle.new_toyota_tune = openpilot_longitudinal and car_make == "toyota" and self.params.get_bool("NewToyotaTune") toggle.personalize_openpilot = self.params.get_bool("PersonalizeOpenpilot") - toggle.sound_pack = self.params.get("CustomSignals", encoding='utf-8') if toggle.personalize_openpilot else "stock" + toggle.color_scheme = self.params.get("CustomColors", encoding='utf-8') if toggle.personalize_openpilot else "stock" + toggle.distance_icons = self.params.get("CustomDistanceIcons", encoding='utf-8') if toggle.personalize_openpilot else "stock" + toggle.icon_pack = self.params.get("CustomIcons", encoding='utf-8') if toggle.personalize_openpilot else "stock" + toggle.signal_icons = self.params.get("CustomSignals", encoding='utf-8') if toggle.personalize_openpilot else "stock" + toggle.sound_pack = self.params.get("CustomSounds", encoding='utf-8') if toggle.personalize_openpilot else "stock" toggle.wheel_image = self.params.get("WheelIcon", encoding='utf-8') if toggle.personalize_openpilot else "stock" - quality_of_life_lateral = self.params.get_bool("QOLLateral") - toggle.pause_lateral_below_speed = self.params.get_int("PauseLateralSpeed") * speed_conversion if quality_of_life_lateral else 0 - - quality_of_life_longitudinal = self.params.get_bool("QOLLongitudinal") - toggle.custom_cruise_increase = self.params.get_int("CustomCruise") if quality_of_life_longitudinal and not pcm_cruise else 1 - toggle.custom_cruise_increase_long = self.params.get_int("CustomCruiseLong") if quality_of_life_longitudinal and not pcm_cruise else 5 - toggle.distance_icons = self.params.get("CustomDistanceIcons", encoding='utf-8') if quality_of_life_longitudinal and self.params.get_bool("OnroadDistanceButton") else "stock" - map_gears = quality_of_life_longitudinal and self.params.get_bool("MapGears") - toggle.map_acceleration = map_gears and self.params.get_bool("MapAcceleration") - toggle.map_deceleration = map_gears and self.params.get_bool("MapDeceleration") + toggle.quality_of_life_lateral = self.params.get_bool("QOLLateral") + toggle.pause_lateral_below_speed = self.params.get_int("PauseLateralSpeed") * speed_conversion if toggle.quality_of_life_lateral else 0 toggle.pause_lateral_below_signal = toggle.pause_lateral_below_speed != 0 and self.params.get_bool("PauseLateralOnSignal") - toggle.reverse_cruise_increase = quality_of_life_longitudinal and pcm_cruise and self.params.get_bool("ReverseCruise") + + toggle.quality_of_life_longitudinal = self.params.get_bool("QOLLongitudinal") + toggle.custom_cruise_increase = self.params.get_int("CustomCruise") if toggle.quality_of_life_longitudinal and not pcm_cruise else 1 + toggle.custom_cruise_increase_long = self.params.get_int("CustomCruiseLong") if toggle.quality_of_life_longitudinal and not pcm_cruise else 5 + toggle.force_standstill = toggle.quality_of_life_longitudinal and self.params.get_bool("ForceStandstill") + toggle.force_stops = toggle.quality_of_life_longitudinal and self.params.get_bool("ForceStops") + toggle.map_gears = toggle.quality_of_life_longitudinal and self.params.get_bool("MapGears") + toggle.map_acceleration = toggle.map_gears and self.params.get_bool("MapAcceleration") + toggle.map_deceleration = toggle.map_gears and self.params.get_bool("MapDeceleration") + toggle.reverse_cruise_increase = toggle.quality_of_life_longitudinal and pcm_cruise and self.params.get_bool("ReverseCruise") + toggle.set_speed_offset = self.params.get_int("SetSpeedOffset") * (1 if toggle.is_metric else CV.MPH_TO_KPH) if toggle.quality_of_life_longitudinal and not pcm_cruise else 0 + + toggle.quality_of_life_visuals = self.params.get_bool("QOLVisuals") + toggle.camera_view = self.params.get_int("CameraView") if toggle.quality_of_life_visuals else 0 + toggle.driver_camera_in_reverse = toggle.quality_of_life_visuals and self.params.get_bool("DriverCamera") + toggle.onroad_distance_button = toggle.quality_of_life_visuals and self.params.get_bool("OnroadDistanceButton") + toggle.standby_mode = toggle.quality_of_life_visuals and self.params.get_bool("StandbyMode") + toggle.stopped_timer = toggle.quality_of_life_visuals and self.params.get_bool("StoppedTimer") toggle.random_events = self.params.get_bool("RandomEvents") - toggle.sng_hack = toggle.openpilot_longitudinal and car_make == "toyota" and self.params.get_bool("SNGHack") + toggle.screen_management = self.params.get_bool("ScreenManagement") + toggle.screen_brightness = self.params.get_int("ScreenBrightness") if toggle.screen_management else 101 + toggle.screen_brightness_onroad = self.params.get_int("ScreenBrightnessOnroad") if toggle.screen_management else 101 + toggle.screen_recorder = toggle.screen_management and self.params.get_bool("ScreenRecorder") + toggle.screen_timeout = self.params.get_int("ScreenTimeout") if toggle.screen_management else 30 + toggle.screen_timeout_onroad = self.params.get_int("ScreenTimeoutOnroad") if toggle.screen_management else 10 + + toggle.sng_hack = openpilot_longitudinal and car_make == "toyota" and self.params.get_bool("SNGHack") - toggle.speed_limit_controller = toggle.openpilot_longitudinal and self.params.get_bool("SpeedLimitController") + toggle.speed_limit_controller = openpilot_longitudinal and self.params.get_bool("SpeedLimitController") toggle.force_mph_dashboard = toggle.speed_limit_controller and self.params.get_bool("ForceMPHDashboard") toggle.map_speed_lookahead_higher = self.params.get_int("SLCLookaheadHigher") if toggle.speed_limit_controller else 0 toggle.map_speed_lookahead_lower = self.params.get_int("SLCLookaheadLower") if toggle.speed_limit_controller else 0 - toggle.offset1 = self.params.get_int("Offset1") * speed_conversion if toggle.speed_limit_controller else 0 - toggle.offset2 = self.params.get_int("Offset2") * speed_conversion if toggle.speed_limit_controller else 0 - toggle.offset3 = self.params.get_int("Offset3") * speed_conversion if toggle.speed_limit_controller else 0 - toggle.offset4 = self.params.get_int("Offset4") * speed_conversion if toggle.speed_limit_controller else 0 toggle.set_speed_limit = toggle.speed_limit_controller and self.params.get_bool("SetSpeedLimit") - toggle.speed_limit_alert = toggle.speed_limit_controller and self.params.get_bool("SpeedLimitChangedAlert") - toggle.speed_limit_confirmation_higher = toggle.speed_limit_controller and self.params.get_bool("SLCConfirmationHigher") - toggle.speed_limit_confirmation_lower = toggle.speed_limit_controller and self.params.get_bool("SLCConfirmationLower") - speed_limit_controller_override = self.params.get_int("SLCOverride") if toggle.speed_limit_controller else 0 - toggle.speed_limit_controller_override_manual = speed_limit_controller_override == 1 - toggle.speed_limit_controller_override_set_speed = speed_limit_controller_override == 2 - toggle.use_set_speed = toggle.speed_limit_controller and self.params.get_int("SLCFallback") == 0 - toggle.slc_fallback_experimental = toggle.speed_limit_controller and self.params.get_int("SLCFallback") == 1 - toggle.slc_fallback_previous = toggle.speed_limit_controller and self.params.get_int("SLCFallback") == 2 + slc_fallback_method = self.params.get_int("SLCFallback") if toggle.speed_limit_controller else 0 + toggle.slc_fallback_experimental_mode = toggle.speed_limit_controller and slc_fallback_method == 1 + toggle.slc_fallback_previous_speed_limit = toggle.speed_limit_controller and slc_fallback_method == 2 + toggle.slc_fallback_set_speed = toggle.speed_limit_controller and slc_fallback_method == 0 + toggle.speed_limit_confirmation = toggle.speed_limit_controller and self.params.get_bool("SLCConfirmation") + toggle.speed_limit_confirmation_higher = toggle.speed_limit_confirmation and self.params.get_bool("SLCConfirmationHigher") + toggle.speed_limit_confirmation_lower = toggle.speed_limit_confirmation and self.params.get_bool("SLCConfirmationLower") + toggle.speed_limit_controller_override = self.params.get_int("SLCOverride") if toggle.speed_limit_controller else 0 + toggle.speed_limit_controller_override_manual = toggle.speed_limit_controller_override == 1 + toggle.speed_limit_controller_override_set_speed = toggle.speed_limit_controller_override == 2 + toggle.speed_limit_offset1 = self.params.get_int("Offset1") * speed_conversion if toggle.speed_limit_controller else 0 + toggle.speed_limit_offset2 = self.params.get_int("Offset2") * speed_conversion if toggle.speed_limit_controller else 0 + toggle.speed_limit_offset3 = self.params.get_int("Offset3") * speed_conversion if toggle.speed_limit_controller else 0 + toggle.speed_limit_offset4 = self.params.get_int("Offset4") * speed_conversion if toggle.speed_limit_controller else 0 toggle.speed_limit_priority1 = self.params.get("SLCPriority1", encoding='utf-8') if toggle.speed_limit_controller else None toggle.speed_limit_priority2 = self.params.get("SLCPriority2", encoding='utf-8') if toggle.speed_limit_controller else None toggle.speed_limit_priority3 = self.params.get("SLCPriority3", encoding='utf-8') if toggle.speed_limit_controller else None toggle.speed_limit_priority_highest = toggle.speed_limit_priority1 == "Highest" toggle.speed_limit_priority_lowest = toggle.speed_limit_priority1 == "Lowest" - toyota_doors = car_make == "toyota" and self.params.get_bool("ToyotaDoors") - toggle.lock_doors = toyota_doors and self.params.get_bool("LockDoors") - toggle.unlock_doors = toyota_doors and self.params.get_bool("UnlockDoors") + toggle.startup_alert_top = self.params.get("StartupMessageTop", encoding='utf-8') or "" + toggle.startup_alert_bottom = self.params.get("StartupMessageBottom", encoding='utf-8') or "" + + toggle.tethering_config = self.params.get_int("TetheringEnabled") + + toggle.toyota_doors = car_make == "toyota" and self.params.get_bool("ToyotaDoors") + toggle.lock_doors = toggle.toyota_doors and self.params.get_bool("LockDoors") + toggle.unlock_doors = toggle.toyota_doors and self.params.get_bool("UnlockDoors") + + toggle.volt_sng = car_model == "CHEVROLET_VOLT" and self.params.get_bool("VoltSNG") + + customization_level = self.params.get_int("CustomizationLevel") if self.params.get_bool("CustomizationLevelConfirmed") else 2 + + if customization_level == 0: + toggle.advanced_custom_onroad_ui = self.default_frogpilot_toggles.AdvancedCustomUI + toggle.hide_lead_marker = toggle.advanced_custom_onroad_ui and self.default_frogpilot_toggles.HideLeadMarker + toggle.hide_speed = toggle.advanced_custom_onroad_ui and self.default_frogpilot_toggles.HideSpeed + toggle.hide_map_icon = toggle.advanced_custom_onroad_ui and self.default_frogpilot_toggles.HideMapIcon + toggle.hide_max_speed = toggle.advanced_custom_onroad_ui and self.default_frogpilot_toggles.HideMaxSpeed + toggle.hide_alerts = toggle.advanced_custom_onroad_ui and self.default_frogpilot_toggles.HideAlerts + toggle.use_wheel_speed = toggle.advanced_custom_onroad_ui and self.default_frogpilot_toggles.WheelSpeed + + toggle.advanced_lateral_tuning = self.default_frogpilot_toggles.AdvancedLateralTune + toggle.use_custom_steer_friction = False + toggle.use_custom_kp = False + toggle.use_custom_lat_accel_factor = False + toggle.use_custom_steer_ratio = False + toggle.force_auto_tune = toggle.advanced_lateral_tuning and not has_auto_tune and not is_pid_car and self.default_frogpilot_toggles.ForceAutoTune + toggle.force_auto_tune_off = toggle.advanced_lateral_tuning and has_auto_tune and not is_pid_car and self.default_frogpilot_toggles.ForceAutoTuneOff + + toggle.alert_volume_control = self.default_frogpilot_toggles.AlertVolumeControl + toggle.disengage_volume = self.default_frogpilot_toggles.DisengageVolume if toggle.alert_volume_control else 101 + toggle.engage_volume = self.default_frogpilot_toggles.EngageVolume if toggle.alert_volume_control else 101 + toggle.prompt_volume = self.default_frogpilot_toggles.PromptVolume if toggle.alert_volume_control else 101 + toggle.promptDistracted_volume = self.default_frogpilot_toggles.PromptDistractedVolume if toggle.alert_volume_control else 101 + toggle.refuse_volume = self.default_frogpilot_toggles.RefuseVolume if toggle.alert_volume_control else 101 + toggle.warningSoft_volume = self.default_frogpilot_toggles.WarningSoftVolume if toggle.alert_volume_control else 101 + toggle.warningImmediate_volume = self.default_frogpilot_toggles.WarningImmediateVolume if toggle.alert_volume_control else 101 + + toggle.always_on_lateral = always_on_lateral_set and self.default_frogpilot_toggles.AlwaysOnLateral + toggle.always_on_lateral_lkas = toggle.always_on_lateral and car_make != "subaru" and self.default_frogpilot_toggles.AlwaysOnLateralLKAS + toggle.always_on_lateral_main = toggle.always_on_lateral and self.default_frogpilot_toggles.AlwaysOnLateralMain + toggle.always_on_lateral_pause_speed = self.default_frogpilot_toggles.PauseAOLOnBrake if toggle.always_on_lateral else 0 + toggle.always_on_lateral_status_bar = toggle.always_on_lateral and not self.default_frogpilot_toggles.HideAOLStatusBar + + toggle.cluster_offset = self.default_frogpilot_toggles.ClusterOffset if car_make == "toyota" else 1 + + toggle.conditional_experimental_mode = openpilot_longitudinal and self.default_frogpilot_toggles.ConditionalExperimental + toggle.conditional_curves = toggle.conditional_experimental_mode and self.default_frogpilot_toggles.CECurves + toggle.conditional_curves_lead = toggle.conditional_curves and self.default_frogpilot_toggles.CECurvesLead + toggle.conditional_lead = toggle.conditional_experimental_mode and self.default_frogpilot_toggles.CELead + toggle.conditional_slower_lead = toggle.conditional_lead and self.default_frogpilot_toggles.CESlowerLead + toggle.conditional_stopped_lead = toggle.conditional_lead and self.default_frogpilot_toggles.CEStoppedLead + toggle.conditional_limit = self.default_frogpilot_toggles.CESpeed * speed_conversion if toggle.conditional_experimental_mode else 0 + toggle.conditional_limit_lead = self.default_frogpilot_toggles.CESpeedLead * speed_conversion if toggle.conditional_experimental_mode else 0 + toggle.conditional_navigation = toggle.conditional_experimental_mode and self.default_frogpilot_toggles.CENavigation + toggle.conditional_navigation_intersections = toggle.conditional_navigation and self.default_frogpilot_toggles.CENavigationIntersections + toggle.conditional_navigation_lead = toggle.conditional_navigation and self.default_frogpilot_toggles.CENavigationLead + toggle.conditional_navigation_turns = toggle.conditional_navigation and self.default_frogpilot_toggles.CENavigationTurns + toggle.conditional_model_stop_time = self.default_frogpilot_toggles.CEModelStopTime if toggle.conditional_experimental_mode else 0 + toggle.conditional_signal = self.default_frogpilot_toggles.CESignalSpeed if toggle.conditional_experimental_mode else 0 + toggle.conditional_signal_lane_detection = toggle.conditional_signal != 0 and self.default_frogpilot_toggles.CESignalLaneDetection + toggle.conditional_status_bar = toggle.conditional_experimental_mode and not self.default_frogpilot_toggles.HideCEMStatusBar + if toggle.conditional_experimental_mode: + self.params.put_bool("ExperimentalMode", True) + + toggle.crosstrek_torque = car_model == "SUBARU_IMPREZA" and self.default_frogpilot_toggles.CrosstrekTorque + + toggle.curve_speed_controller = openpilot_longitudinal and self.default_frogpilot_toggles.CurveSpeedControl + toggle.curve_sensitivity = self.default_frogpilot_toggles.CurveSensitivity / 100 if toggle.curve_speed_controller else 1 + toggle.turn_aggressiveness = self.default_frogpilot_toggles.TurnAggressiveness / 100 if toggle.curve_speed_controller else 1 + toggle.disable_curve_speed_smoothing = toggle.curve_speed_controller and self.default_frogpilot_toggles.DisableCurveSpeedSmoothing + toggle.map_turn_speed_controller = toggle.curve_speed_controller and self.default_frogpilot_toggles.MTSCEnabled + toggle.mtsc_curvature_check = toggle.map_turn_speed_controller and self.default_frogpilot_toggles.MTSCCurvatureCheck + toggle.vision_turn_controller = toggle.curve_speed_controller and self.default_frogpilot_toggles.VisionTurnControl + + toggle.custom_personalities = openpilot_longitudinal and self.default_frogpilot_toggles.CustomPersonalities + toggle.aggressive_profile = toggle.custom_personalities and self.default_frogpilot_toggles.AggressivePersonalityProfile + toggle.aggressive_jerk_deceleration = self.default_frogpilot_toggles.AggressiveJerkDeceleration / 100 if toggle.aggressive_profile else 0.5 + toggle.aggressive_jerk_danger = self.default_frogpilot_toggles.AggressiveJerkDanger / 100 if toggle.aggressive_profile else 1.0 + toggle.aggressive_jerk_speed = self.default_frogpilot_toggles.AggressiveJerkSpeed / 100 if toggle.aggressive_profile else 0.5 + toggle.aggressive_jerk_speed_decrease = self.default_frogpilot_toggles.AggressiveJerkSpeedDecrease / 100 if toggle.aggressive_profile else 0.5 + toggle.aggressive_follow = self.default_frogpilot_toggles.AggressiveFollow if toggle.aggressive_profile else 1.25 + toggle.standard_profile = toggle.custom_personalities and self.default_frogpilot_toggles.StandardPersonalityProfile + toggle.standard_jerk_acceleration = self.default_frogpilot_toggles.StandardJerkAcceleration / 100 if toggle.standard_profile else 1.0 + toggle.standard_jerk_deceleration = self.default_frogpilot_toggles.StandardJerkDeceleration / 100 if toggle.standard_profile else 1.0 + toggle.standard_jerk_danger = self.default_frogpilot_toggles.StandardJerkDanger / 100 if toggle.standard_profile else 1.0 + toggle.standard_jerk_speed = self.default_frogpilot_toggles.StandardJerkSpeed / 100 if toggle.standard_profile else 1.0 + toggle.standard_jerk_speed_decrease = self.default_frogpilot_toggles.StandardJerkSpeedDecrease / 100 if toggle.standard_profile else 1.0 + toggle.standard_follow = self.default_frogpilot_toggles.StandardFollow if toggle.standard_profile else 1.45 + toggle.relaxed_profile = toggle.custom_personalities and self.default_frogpilot_toggles.RelaxedPersonalityProfile + toggle.relaxed_jerk_acceleration = self.default_frogpilot_toggles.RelaxedJerkAcceleration / 100 if toggle.relaxed_profile else 1.0 + toggle.relaxed_jerk_deceleration = self.default_frogpilot_toggles.RelaxedJerkDeceleration / 100 if toggle.relaxed_profile else 1.0 + toggle.relaxed_jerk_danger = self.default_frogpilot_toggles.RelaxedJerkDanger / 100 if toggle.relaxed_profile else 1.0 + toggle.relaxed_jerk_speed = self.default_frogpilot_toggles.RelaxedJerkSpeed / 100 if toggle.relaxed_profile else 1.0 + toggle.relaxed_jerk_speed_decrease = self.default_frogpilot_toggles.RelaxedJerkSpeedDecrease / 100 if toggle.relaxed_profile else 1.0 + toggle.relaxed_follow = self.default_frogpilot_toggles.RelaxedFollow if toggle.relaxed_profile else 1.75 + toggle.traffic_profile = toggle.custom_personalities and self.default_frogpilot_toggles.TrafficPersonalityProfile + toggle.traffic_mode_jerk_acceleration = [self.default_frogpilot_toggles.TrafficJerkAcceleration / 100, toggle.aggressive_jerk_acceleration] if toggle.traffic_profile else [0.5, 0.5] + toggle.traffic_mode_jerk_deceleration = [self.default_frogpilot_toggles.TrafficJerkDeceleration / 100, toggle.aggressive_jerk_deceleration] if toggle.traffic_profile else [0.5, 0.5] + toggle.traffic_mode_jerk_danger = [self.default_frogpilot_toggles.TrafficJerkDanger / 100, toggle.aggressive_jerk_danger] if toggle.traffic_profile else [1.0, 1.0] + toggle.traffic_mode_jerk_speed = [self.default_frogpilot_toggles.TrafficJerkSpeed / 100, toggle.aggressive_jerk_speed] if toggle.traffic_profile else [0.5, 0.5] + toggle.traffic_mode_jerk_speed_decrease = [self.default_frogpilot_toggles.TrafficJerkSpeedDecrease / 100, toggle.aggressive_jerk_speed_decrease] if toggle.traffic_profile else [0.5, 0.5] + toggle.traffic_mode_t_follow = [self.default_frogpilot_toggles.TrafficFollow, toggle.aggressive_follow] if toggle.traffic_profile else [0.5, 1.0] + + toggle.custom_ui = self.default_frogpilot_toggles.CustomUI + toggle.acceleration_path = toggle.custom_ui and self.default_frogpilot_toggles.AccelerationPath + toggle.adjacent_paths = toggle.custom_ui and self.default_frogpilot_toggles.AdjacentPath + toggle.blind_spot_path = has_bsm and toggle.custom_ui and self.default_frogpilot_toggles.BlindSpotPath + toggle.compass = toggle.custom_ui and self.default_frogpilot_toggles.Compass + toggle.pedals_on_ui = toggle.custom_ui and self.default_frogpilot_toggles.PedalsOnUI + toggle.dynamic_pedals_on_ui = toggle.pedals_on_ui and self.default_frogpilot_toggles.DynamicPedalsOnUI + toggle.static_pedals_on_ui = toggle.pedals_on_ui and self.default_frogpilot_toggles.StaticPedalsOnUI + toggle.rotating_wheel = toggle.custom_ui and self.default_frogpilot_toggles.RotatingWheel + + toggle.developer_ui = self.default_frogpilot_toggles.DeveloperUI + toggle.border_metrics = toggle.developer_ui and self.default_frogpilot_toggles.BorderMetrics + toggle.blind_spot_metrics = has_bsm and toggle.border_metrics and self.default_frogpilot_toggles.BlindSpotMetrics + toggle.signal_metrics = toggle.border_metrics and self.default_frogpilot_toggles.SignalMetrics + toggle.steering_metrics = toggle.border_metrics and self.default_frogpilot_toggles.ShowSteering + toggle.show_fps = toggle.developer_ui and self.default_frogpilot_toggles.FPSCounter + toggle.lateral_metrics = toggle.developer_ui and self.default_frogpilot_toggles.LateralMetrics + toggle.adjacent_path_metrics = toggle.lateral_metrics and self.default_frogpilot_toggles.AdjacentPathMetrics + toggle.lateral_tuning_metrics = toggle.lateral_metrics and self.default_frogpilot_toggles.TuningInfo + toggle.longitudinal_metrics = toggle.developer_ui and self.default_frogpilot_toggles.LongitudinalMetrics + toggle.adjacent_lead_tracking = has_radar and toggle.longitudinal_metrics and self.default_frogpilot_toggles.AdjacentLeadsUI + toggle.lead_metrics = toggle.longitudinal_metrics and self.default_frogpilot_toggles.LeadInfo + toggle.jerk_metrics = toggle.longitudinal_metrics and self.default_frogpilot_toggles.JerkInfo + toggle.numerical_temp = toggle.developer_ui and self.default_frogpilot_toggles.NumericalTemp + toggle.fahrenheit = toggle.numerical_temp and self.default_frogpilot_toggles.Fahrenheit + toggle.sidebar_metrics = toggle.developer_ui and self.default_frogpilot_toggles.SidebarMetrics + toggle.cpu_metrics = toggle.sidebar_metrics and self.default_frogpilot_toggles.ShowCPU + toggle.gpu_metrics = toggle.sidebar_metrics and self.default_frogpilot_toggles.ShowGPU + toggle.ip_metrics = toggle.sidebar_metrics and self.default_frogpilot_toggles.ShowIP + toggle.memory_metrics = toggle.sidebar_metrics and self.default_frogpilot_toggles.ShowMemoryUsage + toggle.storage_left_metrics = toggle.sidebar_metrics and self.default_frogpilot_toggles.ShowStorageLeft + toggle.storage_used_metrics = toggle.sidebar_metrics and self.default_frogpilot_toggles.ShowStorageUsed + toggle.use_si_metrics = toggle.developer_ui and self.default_frogpilot_toggles.UseSI + + toggle.device_management = self.default_frogpilot_toggles.DeviceManagement + device_shutdown_setting = self.default_frogpilot_toggles.DeviceShutdown if toggle.device_management else 33 + toggle.device_shutdown_time = (device_shutdown_setting - 3) * 3600 if device_shutdown_setting >= 4 else device_shutdown_setting * (60 * 15) + toggle.increase_thermal_limits = toggle.device_management and self.default_frogpilot_toggles.IncreaseThermalLimits + toggle.low_voltage_shutdown = clip(self.default_frogpilot_toggles.LowVoltageShutdown, VBATT_PAUSE_CHARGING, 12.5) if toggle.device_management else VBATT_PAUSE_CHARGING + toggle.no_logging = toggle.device_management and self.default_frogpilot_toggles.NoLogging + toggle.no_uploads = toggle.device_management and self.default_frogpilot_toggles.NoUploads + toggle.offline_mode = toggle.device_management and self.default_frogpilot_toggles.OfflineMode + + toggle.experimental_gm_tune = openpilot_longitudinal and car_make == "gm" and self.default_frogpilot_toggles.ExperimentalGMTune + + toggle.experimental_mode_via_press = openpilot_longitudinal and self.default_frogpilot_toggles.ExperimentalModeActivation + toggle.experimental_mode_via_distance = toggle.experimental_mode_via_press and self.default_frogpilot_toggles.ExperimentalModeViaDistance + toggle.experimental_mode_via_lkas = not toggle.always_on_lateral_lkas and toggle.experimental_mode_via_press and car_make != "subaru" and self.default_frogpilot_toggles.ExperimentalModeViaLKAS + toggle.experimental_mode_via_tap = toggle.experimental_mode_via_press and self.default_frogpilot_toggles.ExperimentalModeViaTap + + toggle.frogsgomoo_tweak = openpilot_longitudinal and car_make == "toyota" and self.default_frogpilot_toggles.FrogsGoMoosTweak + + toggle.lane_change_customizations = self.default_frogpilot_toggles.LaneChangeCustomizations + toggle.lane_change_delay = self.default_frogpilot_toggles.LaneChangeTime if toggle.lane_change_customizations else 0 + toggle.lane_detection_width = self.default_frogpilot_toggles.LaneDetectionWidth * distance_conversion if toggle.lane_change_customizations else 0 + toggle.lane_detection = toggle.lane_detection_width != 0 + toggle.minimum_lane_change_speed = self.default_frogpilot_toggles.MinimumLaneChangeSpeed * speed_conversion if toggle.lane_change_customizations else LANE_CHANGE_SPEED_MIN + toggle.nudgeless = toggle.lane_change_customizations and self.default_frogpilot_toggles.NudgelessLaneChange + toggle.one_lane_change = toggle.lane_change_customizations and self.default_frogpilot_toggles.OneLaneChange + + toggle.lateral_tuning = self.default_frogpilot_toggles.LateralTune + toggle.nnff = toggle.lateral_tuning and self.default_frogpilot_toggles.NNFF + toggle.smooth_curve_handling = toggle.lateral_tuning and self.default_frogpilot_toggles.NNFFLite + toggle.taco_tune = toggle.lateral_tuning and self.default_frogpilot_toggles.TacoTune + toggle.use_turn_desires = toggle.lateral_tuning and self.default_frogpilot_toggles.TurnDesires + + toggle.long_pitch = openpilot_longitudinal and car_make == "gm" and self.default_frogpilot_toggles.LongPitch + + toggle.human_acceleration = toggle.longitudinal_tuning and self.default_frogpilot_toggles.HumanAcceleration + toggle.human_following = toggle.longitudinal_tuning and self.default_frogpilot_toggles.HumanFollowing + toggle.increased_stopped_distance = self.default_frogpilot_toggles.IncreasedStoppedDistance * distance_conversion if toggle.longitudinal_tuning else 0 + toggle.lead_detection_probability = clip(self.default_frogpilot_toggles.LeadDetectionThreshold / 100, 0.01, 0.99) if toggle.longitudinal_tuning else 0.5 + toggle.max_desired_acceleration = clip(self.default_frogpilot_toggles.MaxDesiredAcceleration, 0.1, 4.0) if toggle.longitudinal_tuning else 4.0 + + toggle.model = DEFAULT_MODEL + toggle.part_model_param = "" + toggle.classic_model = classic_models and toggle.model in classic_models.split(',') + toggle.navigationless_model = navigation_models and toggle.model not in navigation_models.split(',') + toggle.radarless_model = radarless_models and toggle.model in radarless_models.split(',') + + toggle.model_ui = self.default_frogpilot_toggles.ModelUI + toggle.dynamic_path_width = toggle.model_ui and self.default_frogpilot_toggles.DynamicPathWidth + toggle.lane_line_width = self.default_frogpilot_toggles.LaneLinesWidth * small_distance_conversion / 200 if toggle.model_ui else 0.025 + toggle.path_edge_width = self.default_frogpilot_toggles.PathEdgeWidth if toggle.model_ui else 20 + toggle.path_width = self.default_frogpilot_toggles.PathWidth * distance_conversion / 2 if toggle.model_ui else 0.9 + toggle.road_edge_width = self.default_frogpilot_toggles.RoadEdgesWidth * small_distance_conversion / 200 if toggle.model_ui else 0.025 + toggle.show_stopping_point = toggle.model_ui and self.default_frogpilot_toggles.ShowStoppingPoint + toggle.show_stopping_point_metrics = toggle.show_stopping_point and self.default_frogpilot_toggles.ShowStoppingPointMetrics + toggle.unlimited_road_ui_length = toggle.model_ui and self.default_frogpilot_toggles.UnlimitedLength + + toggle.navigation_ui = self.default_frogpilot_toggles.NavigationUI + toggle.big_map = toggle.navigation_ui and self.params.get_bool("BigMap") + toggle.full_map = toggle.big_map and self.default_frogpilot_toggles.FullMap + toggle.map_style = self.default_frogpilot_toggles.MapStyle if toggle.navigation_ui else 0 + toggle.road_name_ui = toggle.navigation_ui and self.default_frogpilot_toggles.RoadNameUI + toggle.show_speed_limit_offset = toggle.navigation_ui and self.default_frogpilot_toggles.ShowSLCOffset + toggle.speed_limit_vienna = toggle.navigation_ui and self.default_frogpilot_toggles.UseVienna + + toggle.new_long_api_gm = openpilot_longitudinal and car_make == "gm" and self.default_frogpilot_toggles.NewLongAPIGM + toggle.new_long_api_hkg = openpilot_longitudinal and car_make == "hyundai" and self.default_frogpilot_toggles.NewLongAPI + + toggle.new_toyota_tune = openpilot_longitudinal and car_make == "toyota" and self.default_frogpilot_toggles.NewToyotaTune + + toggle.quality_of_life_lateral = self.default_frogpilot_toggles.QOLLateral + toggle.pause_lateral_below_speed = self.default_frogpilot_toggles.PauseLateralSpeed * speed_conversion if toggle.quality_of_life_lateral else 0 + + toggle.quality_of_life_longitudinal = self.default_frogpilot_toggles.QOLLongitudinal + toggle.custom_cruise_increase = self.default_frogpilot_toggles.CustomCruise if toggle.quality_of_life_longitudinal and not pcm_cruise else 1 + toggle.custom_cruise_increase_long = self.default_frogpilot_toggles.CustomCruiseLong if toggle.quality_of_life_longitudinal and not pcm_cruise else 5 + toggle.force_standstill = toggle.quality_of_life_longitudinal and self.default_frogpilot_toggles.ForceStandstill + toggle.force_stops = toggle.quality_of_life_longitudinal and self.default_frogpilot_toggles.ForceStops + toggle.map_gears = toggle.quality_of_life_longitudinal and self.default_frogpilot_toggles.MapGears + toggle.map_acceleration = toggle.map_gears and self.default_frogpilot_toggles.MapAcceleration + toggle.map_deceleration = toggle.map_gears and self.default_frogpilot_toggles.MapDeceleration + toggle.pause_lateral_below_signal = toggle.pause_lateral_below_speed != 0 and self.default_frogpilot_toggles.PauseLateralOnSignal + toggle.reverse_cruise_increase = toggle.quality_of_life_longitudinal and pcm_cruise and self.default_frogpilot_toggles.ReverseCruise + toggle.set_speed_offset = self.default_frogpilot_toggles.SetSpeedOffset * (1 if toggle.is_metric else CV.MPH_TO_KPH) if toggle.quality_of_life_longitudinal and not pcm_cruise else 0 + + toggle.camera_view = self.default_frogpilot_toggles.CameraView if toggle.quality_of_life_visuals else 0 + toggle.driver_camera_in_reverse = toggle.quality_of_life_visuals and self.default_frogpilot_toggles.DriverCamera + toggle.standby_mode = toggle.quality_of_life_visuals and self.default_frogpilot_toggles.StandbyMode + toggle.stopped_timer = toggle.quality_of_life_visuals and self.default_frogpilot_toggles.StoppedTimer + + toggle.random_events = self.default_frogpilot_toggles.RandomEvents + + toggle.screen_management = self.default_frogpilot_toggles.ScreenManagement + toggle.screen_brightness = self.default_frogpilot_toggles.ScreenBrightness if toggle.screen_management else 101 + toggle.screen_brightness_onroad = self.default_frogpilot_toggles.ScreenBrightnessOnroad if toggle.screen_management else 101 + toggle.screen_recorder = toggle.screen_management and self.default_frogpilot_toggles.ScreenRecorder + toggle.screen_timeout = self.default_frogpilot_toggles.ScreenTimeout if toggle.screen_management else 30 + toggle.screen_timeout_onroad = self.default_frogpilot_toggles.ScreenTimeoutOnroad if toggle.screen_management else 10 + + toggle.sng_hack = openpilot_longitudinal and car_make == "toyota" and self.default_frogpilot_toggles.SNGHack + + toggle.force_mph_dashboard = toggle.speed_limit_controller and self.default_frogpilot_toggles.ForceMPHDashboard + toggle.map_speed_lookahead_higher = self.default_frogpilot_toggles.SLCLookaheadHigher if toggle.speed_limit_controller else 0 + toggle.map_speed_lookahead_lower = self.default_frogpilot_toggles.SLCLookaheadLower if toggle.speed_limit_controller else 0 + toggle.set_speed_limit = toggle.speed_limit_controller and self.default_frogpilot_toggles.SetSpeedLimit + slc_fallback_method = self.default_frogpilot_toggles.SLCFallback if toggle.speed_limit_controller else 0 + toggle.slc_fallback_experimental_mode = toggle.speed_limit_controller and slc_fallback_method == 1 + toggle.slc_fallback_previous_speed_limit = toggle.speed_limit_controller and slc_fallback_method == 2 + toggle.slc_fallback_set_speed = toggle.speed_limit_controller and slc_fallback_method == 0 + toggle.speed_limit_controller_override = self.default_frogpilot_toggles.SLCOverride if toggle.speed_limit_controller else 0 + toggle.speed_limit_controller_override_manual = toggle.speed_limit_controller_override == 1 + toggle.speed_limit_controller_override_set_speed = toggle.speed_limit_controller_override == 2 + toggle.speed_limit_priority1 = self.default_frogpilot_toggles.SLCPriority1 if toggle.speed_limit_controller else None + toggle.speed_limit_priority2 = self.default_frogpilot_toggles.SLCPriority2 if toggle.speed_limit_controller else None + toggle.speed_limit_priority3 = self.default_frogpilot_toggles.SLCPriority3 if toggle.speed_limit_controller else None + toggle.speed_limit_priority_highest = toggle.speed_limit_priority1 == "Highest" + toggle.speed_limit_priority_lowest = toggle.speed_limit_priority1 == "Lowest" + toggle.speed_limit_vienna = toggle.speed_limit_controller and self.default_frogpilot_toggles.UseVienna + + toggle.startup_alert_top = self.default_frogpilot_toggles.StartupMessageTop + toggle.startup_alert_bottom = self.default_frogpilot_toggles.StartupMessageBottom + + toggle.volt_sng = car_model == "CHEVROLET_VOLT" and self.default_frogpilot_toggles.VoltSNG + + elif customization_level != 2: + toggle.advanced_custom_onroad_ui = self.default_frogpilot_toggles.AdvancedCustomUI + toggle.hide_lead_marker = toggle.advanced_custom_onroad_ui and self.default_frogpilot_toggles.HideLeadMarker + toggle.hide_speed = toggle.advanced_custom_onroad_ui and self.default_frogpilot_toggles.HideSpeed + toggle.hide_map_icon = toggle.advanced_custom_onroad_ui and self.default_frogpilot_toggles.HideMapIcon + toggle.hide_max_speed = toggle.advanced_custom_onroad_ui and self.default_frogpilot_toggles.HideMaxSpeed + toggle.hide_alerts = toggle.advanced_custom_onroad_ui and self.default_frogpilot_toggles.HideAlerts + toggle.use_wheel_speed = toggle.advanced_custom_onroad_ui and self.default_frogpilot_toggles.WheelSpeed + + toggle.advanced_lateral_tuning = self.default_frogpilot_toggles.AdvancedLateralTune + toggle.use_custom_steer_friction = False + toggle.use_custom_kp = False + toggle.use_custom_lat_accel_factor = False + toggle.use_custom_steer_ratio = False + toggle.force_auto_tune = toggle.advanced_lateral_tuning and not has_auto_tune and not is_pid_car and self.default_frogpilot_toggles.ForceAutoTune + toggle.force_auto_tune_off = toggle.advanced_lateral_tuning and has_auto_tune and not is_pid_car and self.default_frogpilot_toggles.ForceAutoTuneOff + + toggle.alert_volume_control = self.default_frogpilot_toggles.AlertVolumeControl + toggle.disengage_volume = self.default_frogpilot_toggles.DisengageVolume if toggle.alert_volume_control else 101 + toggle.engage_volume = self.default_frogpilot_toggles.EngageVolume if toggle.alert_volume_control else 101 + toggle.prompt_volume = self.default_frogpilot_toggles.PromptVolume if toggle.alert_volume_control else 101 + toggle.promptDistracted_volume = self.default_frogpilot_toggles.PromptDistractedVolume if toggle.alert_volume_control else 101 + toggle.refuse_volume = self.default_frogpilot_toggles.RefuseVolume if toggle.alert_volume_control else 101 + toggle.warningSoft_volume = self.default_frogpilot_toggles.WarningSoftVolume if toggle.alert_volume_control else 101 + toggle.warningImmediate_volume = self.default_frogpilot_toggles.WarningImmediateVolume if toggle.alert_volume_control else 101 + + toggle.always_on_lateral_status_bar = toggle.always_on_lateral and not self.default_frogpilot_toggles.HideAOLStatusBar + + toggle.cluster_offset = self.default_frogpilot_toggles.ClusterOffset if car_make == "toyota" else 1 + + toggle.conditional_navigation = toggle.conditional_experimental_mode and self.default_frogpilot_toggles.CENavigation + toggle.conditional_navigation_intersections = toggle.conditional_navigation and self.default_frogpilot_toggles.CENavigationIntersections + toggle.conditional_navigation_lead = toggle.conditional_navigation and self.default_frogpilot_toggles.CENavigationLead + toggle.conditional_navigation_turns = toggle.conditional_navigation and self.default_frogpilot_toggles.CENavigationTurns + toggle.conditional_signal = self.default_frogpilot_toggles.CESignalSpeed if toggle.conditional_experimental_mode else 0 + toggle.conditional_signal_lane_detection = toggle.conditional_signal != 0 and self.default_frogpilot_toggles.CESignalLaneDetection + toggle.conditional_status_bar = toggle.conditional_experimental_mode and not self.default_frogpilot_toggles.HideCEMStatusBar + if toggle.conditional_experimental_mode: + self.params.put_bool("ExperimentalMode", True) + + toggle.crosstrek_torque = car_model == "SUBARU_IMPREZA" and self.default_frogpilot_toggles.CrosstrekTorque + + toggle.curve_sensitivity = self.default_frogpilot_toggles.CurveSensitivity / 100 if toggle.curve_speed_controller else 1 + toggle.turn_aggressiveness = self.default_frogpilot_toggles.TurnAggressiveness / 100 if toggle.curve_speed_controller else 1 + toggle.mtsc_curvature_check = toggle.map_turn_speed_controller and self.default_frogpilot_toggles.MTSCCurvatureCheck + + toggle.custom_personalities = openpilot_longitudinal and self.default_frogpilot_toggles.CustomPersonalities + toggle.aggressive_profile = toggle.custom_personalities and self.default_frogpilot_toggles.AggressivePersonalityProfile + toggle.aggressive_jerk_deceleration = self.default_frogpilot_toggles.AggressiveJerkDeceleration / 100 if toggle.aggressive_profile else 0.5 + toggle.aggressive_jerk_danger = self.default_frogpilot_toggles.AggressiveJerkDanger / 100 if toggle.aggressive_profile else 1.0 + toggle.aggressive_jerk_speed = self.default_frogpilot_toggles.AggressiveJerkSpeed / 100 if toggle.aggressive_profile else 0.5 + toggle.aggressive_jerk_speed_decrease = self.default_frogpilot_toggles.AggressiveJerkSpeedDecrease / 100 if toggle.aggressive_profile else 0.5 + toggle.aggressive_follow = self.default_frogpilot_toggles.AggressiveFollow if toggle.aggressive_profile else 1.25 + toggle.standard_profile = toggle.custom_personalities and self.default_frogpilot_toggles.StandardPersonalityProfile + toggle.standard_jerk_acceleration = self.default_frogpilot_toggles.StandardJerkAcceleration / 100 if toggle.standard_profile else 1.0 + toggle.standard_jerk_deceleration = self.default_frogpilot_toggles.StandardJerkDeceleration / 100 if toggle.standard_profile else 1.0 + toggle.standard_jerk_danger = self.default_frogpilot_toggles.StandardJerkDanger / 100 if toggle.standard_profile else 1.0 + toggle.standard_jerk_speed = self.default_frogpilot_toggles.StandardJerkSpeed / 100 if toggle.standard_profile else 1.0 + toggle.standard_jerk_speed_decrease = self.default_frogpilot_toggles.StandardJerkSpeedDecrease / 100 if toggle.standard_profile else 1.0 + toggle.standard_follow = self.default_frogpilot_toggles.StandardFollow if toggle.standard_profile else 1.45 + toggle.relaxed_profile = toggle.custom_personalities and self.default_frogpilot_toggles.RelaxedPersonalityProfile + toggle.relaxed_jerk_acceleration = self.default_frogpilot_toggles.RelaxedJerkAcceleration / 100 if toggle.relaxed_profile else 1.0 + toggle.relaxed_jerk_deceleration = self.default_frogpilot_toggles.RelaxedJerkDeceleration / 100 if toggle.relaxed_profile else 1.0 + toggle.relaxed_jerk_danger = self.default_frogpilot_toggles.RelaxedJerkDanger / 100 if toggle.relaxed_profile else 1.0 + toggle.relaxed_jerk_speed = self.default_frogpilot_toggles.RelaxedJerkSpeed / 100 if toggle.relaxed_profile else 1.0 + toggle.relaxed_jerk_speed_decrease = self.default_frogpilot_toggles.RelaxedJerkSpeedDecrease / 100 if toggle.relaxed_profile else 1.0 + toggle.relaxed_follow = self.default_frogpilot_toggles.RelaxedFollow if toggle.relaxed_profile else 1.75 + toggle.traffic_profile = toggle.custom_personalities and self.default_frogpilot_toggles.TrafficPersonalityProfile + toggle.traffic_mode_jerk_acceleration = [self.default_frogpilot_toggles.TrafficJerkAcceleration / 100, toggle.aggressive_jerk_acceleration] if toggle.traffic_profile else [0.5, 0.5] + toggle.traffic_mode_jerk_deceleration = [self.default_frogpilot_toggles.TrafficJerkDeceleration / 100, toggle.aggressive_jerk_deceleration] if toggle.traffic_profile else [0.5, 0.5] + toggle.traffic_mode_jerk_danger = [self.default_frogpilot_toggles.TrafficJerkDanger / 100, toggle.aggressive_jerk_danger] if toggle.traffic_profile else [1.0, 1.0] + toggle.traffic_mode_jerk_speed = [self.default_frogpilot_toggles.TrafficJerkSpeed / 100, toggle.aggressive_jerk_speed] if toggle.traffic_profile else [0.5, 0.5] + toggle.traffic_mode_jerk_speed_decrease = [self.default_frogpilot_toggles.TrafficJerkSpeedDecrease / 100, toggle.aggressive_jerk_speed_decrease] if toggle.traffic_profile else [0.5, 0.5] + toggle.traffic_mode_t_follow = [self.default_frogpilot_toggles.TrafficFollow, toggle.aggressive_follow] if toggle.traffic_profile else [0.5, 1.0] + + toggle.adjacent_paths = toggle.custom_ui and self.default_frogpilot_toggles.AdjacentPath + + toggle.developer_ui = self.default_frogpilot_toggles.DeveloperUI + toggle.border_metrics = toggle.developer_ui and self.default_frogpilot_toggles.BorderMetrics + toggle.blind_spot_metrics = has_bsm and toggle.border_metrics and self.default_frogpilot_toggles.BlindSpotMetrics + toggle.signal_metrics = toggle.border_metrics and self.default_frogpilot_toggles.SignalMetrics + toggle.steering_metrics = toggle.border_metrics and self.default_frogpilot_toggles.ShowSteering + toggle.show_fps = toggle.developer_ui and self.default_frogpilot_toggles.FPSCounter + toggle.lateral_metrics = toggle.developer_ui and self.default_frogpilot_toggles.LateralMetrics + toggle.adjacent_path_metrics = toggle.lateral_metrics and self.default_frogpilot_toggles.AdjacentPathMetrics + toggle.lateral_tuning_metrics = toggle.lateral_metrics and self.default_frogpilot_toggles.TuningInfo + toggle.longitudinal_metrics = toggle.developer_ui and self.default_frogpilot_toggles.LongitudinalMetrics + toggle.adjacent_lead_tracking = has_radar and toggle.longitudinal_metrics and self.default_frogpilot_toggles.AdjacentLeadsUI + toggle.lead_metrics = toggle.longitudinal_metrics and self.default_frogpilot_toggles.LeadInfo + toggle.jerk_metrics = toggle.longitudinal_metrics and self.default_frogpilot_toggles.JerkInfo + toggle.numerical_temp = toggle.developer_ui and self.default_frogpilot_toggles.NumericalTemp + toggle.fahrenheit = toggle.numerical_temp and self.default_frogpilot_toggles.Fahrenheit + toggle.sidebar_metrics = toggle.developer_ui and self.default_frogpilot_toggles.SidebarMetrics + toggle.cpu_metrics = toggle.sidebar_metrics and self.default_frogpilot_toggles.ShowCPU + toggle.gpu_metrics = toggle.sidebar_metrics and self.default_frogpilot_toggles.ShowGPU + toggle.ip_metrics = toggle.sidebar_metrics and self.default_frogpilot_toggles.ShowIP + toggle.memory_metrics = toggle.sidebar_metrics and self.default_frogpilot_toggles.ShowMemoryUsage + toggle.storage_left_metrics = toggle.sidebar_metrics and self.default_frogpilot_toggles.ShowStorageLeft + toggle.storage_used_metrics = toggle.sidebar_metrics and self.default_frogpilot_toggles.ShowStorageUsed + toggle.use_si_metrics = toggle.developer_ui and self.default_frogpilot_toggles.UseSI + + toggle.device_management = self.default_frogpilot_toggles.DeviceManagement + toggle.increase_thermal_limits = toggle.device_management and self.default_frogpilot_toggles.IncreaseThermalLimits + toggle.low_voltage_shutdown = clip(self.default_frogpilot_toggles.LowVoltageShutdown, VBATT_PAUSE_CHARGING, 12.5) if toggle.device_management else VBATT_PAUSE_CHARGING + toggle.no_logging = toggle.device_management and self.default_frogpilot_toggles.NoLogging + toggle.no_uploads = toggle.device_management and self.default_frogpilot_toggles.NoUploads + toggle.offline_mode = toggle.device_management and self.default_frogpilot_toggles.OfflineMode + + toggle.experimental_gm_tune = openpilot_longitudinal and car_make == "gm" and self.default_frogpilot_toggles.ExperimentalGMTune + + toggle.frogsgomoo_tweak = openpilot_longitudinal and car_make == "toyota" and self.default_frogpilot_toggles.FrogsGoMoosTweak + + toggle.lane_detection_width = self.default_frogpilot_toggles.LaneDetectionWidth * distance_conversion if toggle.lane_change_customizations else 0 + toggle.lane_detection = toggle.lane_detection_width != 0 + toggle.minimum_lane_change_speed = self.default_frogpilot_toggles.MinimumLaneChangeSpeed * speed_conversion if toggle.lane_change_customizations else LANE_CHANGE_SPEED_MIN + + toggle.lateral_tuning = self.default_frogpilot_toggles.LateralTune + toggle.smooth_curve_handling = toggle.lateral_tuning and self.default_frogpilot_toggles.NNFFLite + toggle.taco_tune = toggle.lateral_tuning and self.default_frogpilot_toggles.TacoTune + toggle.use_turn_desires = toggle.lateral_tuning and self.default_frogpilot_toggles.TurnDesires + + toggle.long_pitch = openpilot_longitudinal and car_make == "gm" and self.default_frogpilot_toggles.LongPitch + + toggle.lead_detection_probability = clip(self.default_frogpilot_toggles.LeadDetectionThreshold / 100, 0.01, 0.99) if toggle.longitudinal_tuning else 0.5 + toggle.max_desired_acceleration = clip(self.default_frogpilot_toggles.MaxDesiredAcceleration, 0.1, 4.0) if toggle.longitudinal_tuning else 4.0 + + toggle.model = DEFAULT_MODEL + toggle.part_model_param = "" + toggle.classic_model = classic_models and toggle.model in classic_models.split(',') + toggle.navigationless_model = navigation_models and toggle.model not in navigation_models.split(',') + toggle.radarless_model = radarless_models and toggle.model in radarless_models.split(',') + + toggle.model_ui = self.default_frogpilot_toggles.ModelUI + toggle.dynamic_path_width = toggle.model_ui and self.default_frogpilot_toggles.DynamicPathWidth + toggle.lane_line_width = self.default_frogpilot_toggles.LaneLinesWidth * small_distance_conversion / 200 if toggle.model_ui else 0.025 + toggle.path_edge_width = self.default_frogpilot_toggles.PathEdgeWidth if toggle.model_ui else 20 + toggle.path_width = self.default_frogpilot_toggles.PathWidth * distance_conversion / 2 if toggle.model_ui else 0.9 + toggle.road_edge_width = self.default_frogpilot_toggles.RoadEdgesWidth * small_distance_conversion / 200 if toggle.model_ui else 0.025 + toggle.show_stopping_point = toggle.model_ui and self.default_frogpilot_toggles.ShowStoppingPoint + toggle.show_stopping_point_metrics = toggle.show_stopping_point and self.default_frogpilot_toggles.ShowStoppingPointMetrics + toggle.unlimited_road_ui_length = toggle.model_ui and self.default_frogpilot_toggles.UnlimitedLength + + toggle.map_style = self.default_frogpilot_toggles.MapStyle if toggle.navigation_ui else 0 + toggle.road_name_ui = toggle.navigation_ui and self.default_frogpilot_toggles.RoadNameUI + toggle.show_speed_limit_offset = toggle.navigation_ui and self.default_frogpilot_toggles.ShowSLCOffset + toggle.speed_limit_vienna = toggle.navigation_ui and self.default_frogpilot_toggles.UseVienna + + toggle.new_long_api_gm = openpilot_longitudinal and car_make == "gm" and self.default_frogpilot_toggles.NewLongAPIGM + toggle.new_long_api_hkg = openpilot_longitudinal and car_make == "hyundai" and self.default_frogpilot_toggles.NewLongAPI + + toggle.new_toyota_tune = openpilot_longitudinal and car_make == "toyota" and self.default_frogpilot_toggles.NewToyotaTune + + toggle.quality_of_life_lateral = self.default_frogpilot_toggles.QOLLateral + toggle.pause_lateral_below_speed = self.default_frogpilot_toggles.PauseLateralSpeed * speed_conversion if toggle.quality_of_life_lateral else 0 + + toggle.custom_cruise_increase = self.default_frogpilot_toggles.CustomCruise if toggle.quality_of_life_longitudinal and not pcm_cruise else 1 + toggle.custom_cruise_increase_long = self.default_frogpilot_toggles.CustomCruiseLong if toggle.quality_of_life_longitudinal and not pcm_cruise else 5 + toggle.force_standstill = toggle.quality_of_life_longitudinal and self.default_frogpilot_toggles.ForceStandstill + toggle.force_stops = toggle.quality_of_life_longitudinal and self.default_frogpilot_toggles.ForceStops + toggle.pause_lateral_below_signal = toggle.pause_lateral_below_speed != 0 and self.default_frogpilot_toggles.PauseLateralOnSignal + toggle.reverse_cruise_increase = toggle.quality_of_life_longitudinal and pcm_cruise and self.default_frogpilot_toggles.ReverseCruise + toggle.set_speed_offset = self.default_frogpilot_toggles.SetSpeedOffset * (1 if toggle.is_metric else CV.MPH_TO_KPH) if toggle.quality_of_life_longitudinal and not pcm_cruise else 0 + + toggle.quality_of_life_visuals = self.default_frogpilot_toggles.QOLVisuals + toggle.camera_view = self.default_frogpilot_toggles.CameraView if toggle.quality_of_life_visuals else 0 + toggle.standby_mode = toggle.quality_of_life_visuals and self.default_frogpilot_toggles.StandbyMode + + toggle.screen_management = self.default_frogpilot_toggles.ScreenManagement + toggle.screen_brightness = self.default_frogpilot_toggles.ScreenBrightness if toggle.screen_management else 101 + toggle.screen_brightness_onroad = self.default_frogpilot_toggles.ScreenBrightnessOnroad if toggle.screen_management else 101 + toggle.screen_recorder = toggle.screen_management and self.default_frogpilot_toggles.ScreenRecorder + toggle.screen_timeout = self.default_frogpilot_toggles.ScreenTimeout if toggle.screen_management else 30 + toggle.screen_timeout_onroad = self.default_frogpilot_toggles.ScreenTimeoutOnroad if toggle.screen_management else 10 + + toggle.sng_hack = openpilot_longitudinal and car_make == "toyota" and self.default_frogpilot_toggles.SNGHack + + toggle.force_mph_dashboard = toggle.speed_limit_controller and self.default_frogpilot_toggles.ForceMPHDashboard + toggle.map_speed_lookahead_higher = self.default_frogpilot_toggles.SLCLookaheadHigher if toggle.speed_limit_controller else 0 + toggle.map_speed_lookahead_lower = self.default_frogpilot_toggles.SLCLookaheadLower if toggle.speed_limit_controller else 0 + toggle.set_speed_limit = toggle.speed_limit_controller and self.default_frogpilot_toggles.SetSpeedLimit + toggle.speed_limit_priority1 = self.default_frogpilot_toggles.SLCPriority1 if toggle.speed_limit_controller else None + toggle.speed_limit_priority2 = self.default_frogpilot_toggles.SLCPriority2 if toggle.speed_limit_controller else None + toggle.speed_limit_priority3 = self.default_frogpilot_toggles.SLCPriority3 if toggle.speed_limit_controller else None + toggle.speed_limit_priority_highest = toggle.speed_limit_priority1 == "Highest" + toggle.speed_limit_priority_lowest = toggle.speed_limit_priority1 == "Lowest" + toggle.speed_limit_vienna = toggle.speed_limit_controller and self.default_frogpilot_toggles.UseVienna + + toggle.startup_alert_top = self.default_frogpilot_toggles.StartupMessageTop + toggle.startup_alert_bottom = self.default_frogpilot_toggles.StartupMessageBottom + + toggle.volt_sng = car_model == "CHEVROLET_VOLT" and self.default_frogpilot_toggles.VoltSNG -FrogPilotVariables = FrogPilotVariables() + self.params.put("FrogPilotToggles", json.dumps(toggle.__dict__)) + self.params_memory.remove("FrogPilotTogglesUpdated") diff --git a/selfdrive/frogpilot/navigation/mapd.py b/selfdrive/frogpilot/navigation/mapd.py index 9fbd6f825c4bd4..898fd48d67fba3 100644 --- a/selfdrive/frogpilot/navigation/mapd.py +++ b/selfdrive/frogpilot/navigation/mapd.py @@ -8,7 +8,7 @@ from openpilot.common.realtime import Ratekeeper -from openpilot.selfdrive.frogpilot.frogpilot_functions import is_url_pingable +from openpilot.selfdrive.frogpilot.frogpilot_utilities import is_url_pingable VERSION = 'v1' diff --git a/selfdrive/frogpilot/navigation/ui/maps_settings.cc b/selfdrive/frogpilot/navigation/ui/maps_settings.cc index c25150c04c4d38..91ea0bd0f177fc 100644 --- a/selfdrive/frogpilot/navigation/ui/maps_settings.cc +++ b/selfdrive/frogpilot/navigation/ui/maps_settings.cc @@ -8,14 +8,14 @@ FrogPilotMapsPanel::FrogPilotMapsPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent) { std::vector scheduleOptions{tr("Manually"), tr("Weekly"), tr("Monthly")}; - preferredSchedule = new ButtonParamControl("PreferredSchedule", tr("Maps Scheduler"), - tr("Choose the frequency for updating maps with the latest OpenStreetMap (OSM) changes. " + preferredSchedule = new ButtonParamControl("PreferredSchedule", tr("Automatically Update Maps"), + tr("Controls the frequency at which maps update with the latest OpenStreetMap (OSM) changes. " "Weekly updates begin at midnight every Sunday, while monthly updates start at midnight on the 1st of each month."), "", scheduleOptions); addItem(preferredSchedule); - selectMapsButton = new FrogPilotButtonsControl(tr("Select Offline Maps"), tr("Select your maps to use with 'Curve Speed Control' and 'Speed Limit Controller'."), {tr("COUNTRIES"), tr("STATES")}); + selectMapsButton = new FrogPilotButtonsControl(tr("Select Offline Maps"), tr("Offline maps to use with 'Curve Speed Control' and 'Speed Limit Controller'."), {tr("COUNTRIES"), tr("STATES")}); QObject::connect(selectMapsButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) { if (id == 0) { countriesOpen = true; @@ -25,7 +25,7 @@ FrogPilotMapsPanel::FrogPilotMapsPanel(FrogPilotSettingsWindow *parent) : FrogPi }); addItem(selectMapsButton); - downloadMapsButton = new ButtonControl(tr("Download Maps"), tr("DOWNLOAD"), tr("Download your selected maps to use with 'Curve Speed Control' and 'Speed Limit Controller'.")); + downloadMapsButton = new ButtonControl(tr("Download Maps"), tr("DOWNLOAD"), tr("Downloads the selected maps to use with 'Curve Speed Control' and 'Speed Limit Controller'.")); QObject::connect(downloadMapsButton, &ButtonControl::clicked, [this] { if (downloadMapsButton->text() == tr("CANCEL")) { cancelDownload(); @@ -45,7 +45,7 @@ FrogPilotMapsPanel::FrogPilotMapsPanel(FrogPilotSettingsWindow *parent) : FrogPi downloadStatus->setVisible(false); downloadTimeElapsed->setVisible(false); - removeMapsButton = new ButtonControl(tr("Remove Maps"), tr("REMOVE"), tr("Remove your downloaded maps to clear up storage space.")); + removeMapsButton = new ButtonControl(tr("Remove Maps"), tr("REMOVE"), tr("Removes downloaded maps to clear up storage space.")); QObject::connect(removeMapsButton, &ButtonControl::clicked, [this] { if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to delete all of your downloaded maps?"), this)) { std::thread([this] { diff --git a/selfdrive/frogpilot/navigation/ui/primeless_settings.cc b/selfdrive/frogpilot/navigation/ui/primeless_settings.cc index f181ab04ca70de..230970c8d243b4 100644 --- a/selfdrive/frogpilot/navigation/ui/primeless_settings.cc +++ b/selfdrive/frogpilot/navigation/ui/primeless_settings.cc @@ -5,14 +5,14 @@ FrogPilotPrimelessPanel::FrogPilotPrimelessPanel(FrogPilotSettingsWindow *parent std::vector searchOptions{tr("MapBox"), tr("Amap"), tr("Google")}; searchInput = new ButtonParamControl("SearchInput", tr("Destination Search Provider"), - tr("Select a search provider for destination queries in Navigate on Openpilot. Options include MapBox (recommended), Amap, and Google Maps."), + tr("The search provider used for destination queries in 'Navigate on Openpilot'. Options include 'MapBox' (recommended), 'Amap', and 'Google Maps'."), "", searchOptions); addItem(searchInput); createMapboxKeyControl(publicMapboxKeyControl, tr("Public Mapbox Key"), "MapboxPublicKey", "pk."); createMapboxKeyControl(secretMapboxKeyControl, tr("Secret Mapbox Key"), "MapboxSecretKey", "sk."); - setupButton = new ButtonControl(tr("Mapbox Setup Instructions"), tr("VIEW"), tr("View the instructions to set up MapBox for 'Primeless Navigation'."), this); + setupButton = new ButtonControl(tr("MapBox Setup Instructions"), tr("VIEW"), tr("View the instructions to set up 'MapBox' for 'Primeless Navigation'."), this); QObject::connect(setupButton, &ButtonControl::clicked, [this]() { displayMapboxInstructions(true); openMapBoxInstructions(); @@ -91,6 +91,7 @@ void FrogPilotPrimelessPanel::hideEvent(QHideEvent *event) { } void FrogPilotPrimelessPanel::mousePressEvent(QMouseEvent *event) { + closeMapBoxInstructions(); displayMapboxInstructions(false); } diff --git a/selfdrive/frogpilot/navigation/ui/primeless_settings.h b/selfdrive/frogpilot/navigation/ui/primeless_settings.h index 690f0842baa6c1..a5cd413f3d1278 100644 --- a/selfdrive/frogpilot/navigation/ui/primeless_settings.h +++ b/selfdrive/frogpilot/navigation/ui/primeless_settings.h @@ -9,6 +9,7 @@ class FrogPilotPrimelessPanel : public FrogPilotListWidget { explicit FrogPilotPrimelessPanel(FrogPilotSettingsWindow *parent); signals: + void closeMapBoxInstructions(); void openMapBoxInstructions(); private: diff --git a/selfdrive/frogpilot/ui/qt/offroad/advanced_driving_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/advanced_driving_settings.cc deleted file mode 100644 index 9bff861e86f156..00000000000000 --- a/selfdrive/frogpilot/ui/qt/offroad/advanced_driving_settings.cc +++ /dev/null @@ -1,935 +0,0 @@ -#include "selfdrive/frogpilot/ui/qt/offroad/advanced_driving_settings.h" - -FrogPilotAdvancedDrivingPanel::FrogPilotAdvancedDrivingPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { - const std::vector> advancedToggles { - {"AdvancedLateralTune", tr("Advanced Lateral Tuning"), tr("Advanced settings that control how openpilot manages steering."), "../frogpilot/assets/toggle_icons/icon_advanced_lateral_tune.png"}, - {"SteerFriction", steerFrictionStock != 0 ? QString(tr("Friction (Default: %1)")).arg(QString::number(steerFrictionStock, 'f', 2)) : tr("Friction"), tr("The resistance in steering. Higher values provide more stable steering but can make it feel heavy, while lower values allow lighter steering but may feel too sensitive."), ""}, - {"SteerKP", steerKPStock != 0 ? QString(tr("Kp Factor (Default: %1)")).arg(QString::number(steerKPStock, 'f', 2)) : tr("Kp Factor"), tr("How aggressively the car corrects its steering. Higher values offer quicker corrections but may feel jerky, while lower values make steering smoother but slower to respond."), ""}, - {"SteerLatAccel", steerLatAccelStock != 0 ? QString(tr("Lateral Accel (Default: %1)")).arg(QString::number(steerLatAccelStock, 'f', 2)) : tr("Lateral Accel"), tr("Adjust how fast the car can steer from side to side. Higher values allow quicker lane changes but can feel unstable, while lower values provide smoother steering but may feel sluggish."), ""}, - {"SteerRatio", steerRatioStock != 0 ? QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(steerRatioStock, 'f', 2)) : tr("Steer Ratio"), tr("Adjust how much openpilot needs to turn the wheel to steer. Higher values feel like driving a truck, more stable at high speeds, but harder to steer quickly at low speeds, while lower values feel like a go-kart, easier to steer in tight spots but more sensitive and less stable at high speeds."), ""}, - {"TacoTune", tr("comma's 2022 Taco Bell Turn Hack"), tr("Use comma's hack they used to help handle left and right turns more precisely during their 2022 'Taco Bell' drive."), ""}, - {"ForceAutoTune", tr("Force Auto Tune On"), tr("Forces comma's auto lateral tuning for unsupported vehicles."), ""}, - {"ForceAutoTuneOff", tr("Force Auto Tune Off"), tr("Forces comma's auto lateral tuning off for supported vehicles."), ""}, - {"TurnDesires", tr("Force Turn Desires Below Lane Change Speed"), tr("Force the model to use turn desires when driving below the minimum lane change speed to help make left and right turns more precisely."), ""}, - - {"AdvancedLongitudinalTune", tr("Advanced Longitudinal Tuning"), tr("Advanced settings that control how openpilot manages speed and acceleration."), "../frogpilot/assets/toggle_icons/icon_advanced_longitudinal_tune.png"}, - {"LeadDetectionThreshold", tr("Lead Detection Confidence"), tr("How sensitive openpilot is to detecting vehicles ahead. A lower value can help detect vehicles sooner and from farther away, but may occasionally mistake other objects for vehicles."), ""}, - {"MaxDesiredAcceleration", tr("Maximum Acceleration Rate"), tr("Set a cap on how fast openpilot can accelerate to prevent high acceleration at low speeds."), ""}, - - {"AdvancedQOLDriving", tr("Advanced Quality of Life"), tr("Miscellaneous advanced features to improve your overall openpilot experience."), "../frogpilot/assets/toggle_icons/advanced_quality_of_life.png"}, - {"ForceStandstill", tr("Force Keep openpilot in the Standstill State"), tr("Keep openpilot in the 'standstill' state until the gas pedal or 'resume' button is pressed."), ""}, - {"ForceStops", tr("Force Stop for 'Detected' Stop Lights/Signs"), tr("Whenever openpilot 'detects' a potential stop light/stop sign, force a stop where it originally detected it to prevent running the potential red light/stop sign."), ""}, - {"SetSpeedOffset", tr("Set Speed Offset"), tr("How much higher or lower the set speed should be compared to your current set speed. For example, if you prefer to drive 5 mph above the speed limit, this setting will automatically add that difference when you adjust your set speed."), ""}, - - {"CustomPersonalities", tr("Customize Driving Personalities"), tr("Customize the personality profiles to suit your preferences."), "../frogpilot/assets/toggle_icons/icon_advanced_personality.png"}, - {"TrafficPersonalityProfile", tr("Traffic Personality"), tr("Customize the 'Traffic' personality profile, tailored for navigating through traffic."), "../frogpilot/assets/stock_theme/distance_icons/traffic.png"}, - {"TrafficFollow", tr("Following Distance"), tr("The minimum following distance in 'Traffic Mode.' openpilot will adjust dynamically between this value and the 'Aggressive' profile distance based on your speed."), ""}, - {"TrafficJerkAcceleration", tr("Acceleration Sensitivity"), tr("How sensitive openpilot is to changes in acceleration in 'Traffic Mode.' Higher values result in smoother, more gradual acceleration and deceleration, while lower values allow for faster changes that may feel more abrupt."), ""}, - {"TrafficJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in deceleration in 'Traffic Mode.' Higher values result in smoother, more gradual braking, while lower values allow for quicker, more responsive braking that may feel abrupt."), ""}, - {"TrafficJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious openpilot is around other vehicles or obstacles in 'Traffic Mode.' Higher values increase following distances and prioritize safety, leading to more cautious driving, while lower values allow for closer following but may reduce reaction time."), ""}, - {"TrafficJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly openpilot adjusts speed in 'Traffic Mode.' Higher values ensure smoother, more gradual speed changes, while lower values enable quicker adjustments that might feel sharper or less smooth."), ""}, - {"TrafficJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly openpilot adjusts to decreasing speeds in 'Traffic Mode.' Higher values ensure smoother transitions when slowing down, while lower values allow for quicker, more responsive speed reductions that might feel sharper."), ""}, - {"ResetTrafficPersonality", tr("Reset Settings"), tr("Restore the 'Traffic Mode' settings to their default values."), ""}, - - {"AggressivePersonalityProfile", tr("Aggressive Personality"), tr("Customize the 'Aggressive' personality profile, designed for a more assertive driving style."), "../frogpilot/assets/stock_theme/distance_icons/aggressive.png"}, - {"AggressiveFollow", tr("Following Distance"), tr("Set the following distance for 'Aggressive' mode. This determines roughly how many seconds you'll follow behind the car ahead.\n\nDefault: 1.25 seconds."), ""}, - {"AggressiveJerkAcceleration", tr("Acceleration Sensitivity"), tr("Controls how sensitive openpilot is to acceleration changes in 'Aggressive' mode. Higher values make acceleration and deceleration smoother but slower, while lower values allow quicker changes that may feel jerky.\n\nDefault: 0.5."), ""}, - {"AggressiveJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive openpilot is to deceleration in 'Aggressive' mode. Higher values result in smoother braking, while lower values allow for more immediate braking that may feel abrupt.\n\nDefault: 0.5."), ""}, - {"AggressiveJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious openpilot is around vehicles or obstacles in 'Aggressive' mode. Higher values make it more cautious, while lower values allow for closer following, increasing the risk of sudden braking.\n\nDefault: 1.0."), ""}, - {"AggressiveJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly openpilot adjusts speed in 'Aggressive' mode. Higher values result in smoother but slower speed changes, while lower values make speed adjustments quicker but potentially more abrupt.\n\nDefault: 0.5."), ""}, - {"AggressiveJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly openpilot adjusts to speed reductions in 'Aggressive' mode. Higher values ensure smoother transitions when slowing down, while lower values allow for quicker, more responsive speed decreases that may feel sharp.\n\nDefault: 0.5."), ""}, - {"ResetAggressivePersonality", tr("Reset Settings"), tr("Restore the 'Aggressive' settings to their default values."), ""}, - - {"StandardPersonalityProfile", tr("Standard Personality"), tr("Customize the 'Standard' personality profile, optimized for balanced driving."), "../frogpilot/assets/stock_theme/distance_icons/standard.png"}, - {"StandardFollow", tr("Following Distance"), tr("Set the following distance for 'Standard' mode. This determines roughly how many seconds you'll follow behind the car ahead.\n\nDefault: 1.45 seconds."), ""}, - {"StandardJerkAcceleration", tr("Acceleration Sensitivity"), tr("Controls how sensitive openpilot is to acceleration changes in 'Standard' mode. Higher values make acceleration and deceleration smoother but slower, while lower values allow quicker changes that may feel jerky.\n\nDefault: 1.0."), ""}, - {"StandardJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive openpilot is to deceleration in 'Standard' mode. Higher values result in smoother braking, while lower values allow for quicker, more immediate braking that may feel abrupt.\n\nDefault: 1.0."), ""}, - {"StandardJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious openpilot is around vehicles or obstacles in 'Standard' mode. Higher values make it more cautious, while lower values allow for closer following, increasing the risk of sudden braking.\n\nDefault: 1.0."), ""}, - {"StandardJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly openpilot adjusts speed in 'Standard' mode. Higher values result in smoother but slower speed changes, while lower values make speed adjustments quicker but potentially more abrupt.\n\nDefault: 1.0."), ""}, - {"StandardJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly openpilot adjusts to speed reductions in 'Standard' mode. Higher values ensure smoother transitions when slowing down, while lower values allow for quicker, more responsive speed decreases that may feel sharp.\n\nDefault: 1.0."), ""}, - {"ResetStandardPersonality", tr("Reset Settings"), tr("Restore the 'Standard' settings to their default values."), ""}, - - {"RelaxedPersonalityProfile", tr("Relaxed Personality"), tr("Customize the 'Relaxed' personality profile, ideal for a more laid-back driving style."), "../frogpilot/assets/stock_theme/distance_icons/relaxed.png"}, - {"RelaxedFollow", tr("Following Distance"), tr("Set the following distance for 'Relaxed' mode. This determines roughly how many seconds you'll follow behind the car ahead.\n\nDefault: 1.75 seconds."), ""}, - {"RelaxedJerkAcceleration", tr("Acceleration Sensitivity"), tr("Controls how sensitive openpilot is to acceleration changes in 'Relaxed' mode. Higher values make acceleration and deceleration smoother but slower, while lower values allow quicker changes that may feel jerky.\n\nDefault: 1.0."), ""}, - {"RelaxedJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive openpilot is to deceleration in 'Relaxed' mode. Higher values result in smoother braking, while lower values allow for quicker, more immediate braking that may feel abrupt.\n\nDefault: 1.0."), ""}, - {"RelaxedJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious openpilot is around vehicles or obstacles in 'Relaxed' mode. Higher values make it more cautious, while lower values allow for closer following, increasing the risk of sudden braking.\n\nDefault: 1.0."), ""}, - {"RelaxedJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly openpilot adjusts speed in 'Relaxed' mode. Higher values result in smoother but slower speed changes, while lower values make speed adjustments quicker but potentially more abrupt.\n\nDefault: 1.0."), ""}, - {"RelaxedJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly openpilot adjusts to speed reductions in 'Relaxed' mode. Higher values ensure smoother transitions when slowing down, while lower values allow for quicker, more responsive speed decreases that may feel sharp.\n\nDefault: 1.0."), ""}, - {"ResetRelaxedPersonality", tr("Reset Settings"), tr("Restore the 'Relaxed' settings to their default values."), ""}, - - {"ModelManagement", tr("Model Management"), tr("Manage the driving models used by openpilot."), "../frogpilot/assets/toggle_icons/icon_advanced_model.png"}, - {"AutomaticallyUpdateModels", tr("Automatically Update and Download Models"), tr("Automatically download new or updated driving models."), ""}, - {"ModelRandomizer", tr("Model Randomizer"), tr("A random model is selected and can be reviewed at the end of each drive if it's longer than 15 minutes to help find your preferred model."), ""}, - {"ManageBlacklistedModels", tr("Manage Model Blacklist"), tr("Control which models are blacklisted and won't be used for future drives."), ""}, - {"ResetScores", tr("Reset Model Scores"), tr("Clear the ratings you've given to the driving models."), ""}, - {"ReviewScores", tr("Review Model Scores"), tr("View the ratings you've assigned to the driving models."), ""}, - {"DeleteModel", tr("Delete Model"), tr("Remove the selected driving model from your device."), ""}, - {"DownloadModel", tr("Download Model"), tr("Download undownloaded driving models."), ""}, - {"DownloadAllModels", tr("Download All Models"), tr("Download all undownloaded driving models."), ""}, - {"SelectModel", tr("Select Model"), tr("Select which model openpilot uses to drive."), ""}, - {"ResetCalibrations", tr("Reset Model Calibrations"), tr("Reset calibration settings for the driving models."), ""}, - }; - - for (const auto &[param, title, desc, icon] : advancedToggles) { - AbstractControl *advancedDrivingToggle; - - if (param == "AdvancedLateralTune") { - FrogPilotParamManageControl *advancedLateralTuneToggle = new FrogPilotParamManageControl(param, title, desc, icon); - QObject::connect(advancedLateralTuneToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - std::set modifiedLateralTuneKeys = lateralTuneKeys; - - bool usingNNFF = hasNNFFLog && params.getBool("LateralTune") && params.getBool("NNFF"); - if (usingNNFF) { - modifiedLateralTuneKeys.erase("ForceAutoTune"); - modifiedLateralTuneKeys.erase("ForceAutoTuneOff"); - } else { - if (hasAutoTune) { - modifiedLateralTuneKeys.erase("ForceAutoTune"); - } else if (isPIDCar) { - modifiedLateralTuneKeys.erase("ForceAutoTuneOff"); - modifiedLateralTuneKeys.erase("SteerFriction"); - modifiedLateralTuneKeys.erase("SteerKP"); - modifiedLateralTuneKeys.erase("SteerLatAccel"); - } else { - modifiedLateralTuneKeys.erase("ForceAutoTuneOff"); - } - } - - if (!liveValid || usingNNFF) { - modifiedLateralTuneKeys.erase("SteerFriction"); - modifiedLateralTuneKeys.erase("SteerLatAccel"); - } - - showToggles(modifiedLateralTuneKeys); - }); - advancedDrivingToggle = advancedLateralTuneToggle; - } else if (param == "SteerFriction") { - std::vector steerFrictionToggleNames{"Reset"}; - advancedDrivingToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0.01, 0.25, QString(), std::map(), 0.01, {}, steerFrictionToggleNames, false); - } else if (param == "SteerKP") { - std::vector steerKPToggleNames{"Reset"}; - advancedDrivingToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, steerKPStock * 0.50, steerKPStock * 1.50, QString(), std::map(), 0.01, {}, steerKPToggleNames, false); - } else if (param == "SteerLatAccel") { - std::vector steerLatAccelToggleNames{"Reset"}; - advancedDrivingToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, steerLatAccelStock * 0.25, steerLatAccelStock * 1.25, QString(), std::map(), 0.01, {}, steerLatAccelToggleNames, false); - } else if (param == "SteerRatio") { - std::vector steerRatioToggleNames{"Reset"}; - advancedDrivingToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, steerRatioStock * 0.75, steerRatioStock * 1.25, QString(), std::map(), 0.01, {}, steerRatioToggleNames, false); - - } else if (param == "AdvancedLongitudinalTune") { - FrogPilotParamManageControl *advancedLongitudinalTuneToggle = new FrogPilotParamManageControl(param, title, desc, icon); - QObject::connect(advancedLongitudinalTuneToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - std::set modifiedLongitudinalTuneKeys = longitudinalTuneKeys; - - bool radarlessModel = QString::fromStdString(params.get("RadarlessModels")).split(",").contains(QString::fromStdString(params.get("Model"))); - if (radarlessModel) { - modifiedLongitudinalTuneKeys.erase("LeadDetectionThreshold"); - } - - showToggles(modifiedLongitudinalTuneKeys); - }); - advancedDrivingToggle = advancedLongitudinalTuneToggle; - } else if (param == "LeadDetectionThreshold") { - advancedDrivingToggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 99, "%"); - } else if (param == "MaxDesiredAcceleration") { - advancedDrivingToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0.1, 4.0, "m/s", std::map(), 0.1); - - } else if (param == "AdvancedQOLDriving") { - FrogPilotParamManageControl *advancedQOLToggle = new FrogPilotParamManageControl(param, title, desc, icon); - QObject::connect(advancedQOLToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - std::set modifiedQolKeys = qolKeys; - - if (hasPCMCruise) { - modifiedQolKeys.erase("SetSpeedOffset"); - } - - showToggles(modifiedQolKeys); - }); - advancedDrivingToggle = advancedQOLToggle; - } else if (param == "SetSpeedOffset") { - advancedDrivingToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, tr("mph")); - - } else if (param == "CustomPersonalities") { - FrogPilotParamManageControl *customPersonalitiesToggle = new FrogPilotParamManageControl(param, title, desc, icon); - QObject::connect(customPersonalitiesToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - showToggles(customDrivingPersonalityKeys); - }); - advancedDrivingToggle = customPersonalitiesToggle; - } else if (param == "ResetTrafficPersonality" || param == "ResetAggressivePersonality" || param == "ResetStandardPersonality" || param == "ResetRelaxedPersonality") { - FrogPilotButtonsControl *profileBtn = new FrogPilotButtonsControl(title, desc, {tr("Reset")}); - advancedDrivingToggle = profileBtn; - } else if (param == "TrafficPersonalityProfile") { - FrogPilotParamManageControl *trafficPersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon); - QObject::connect(trafficPersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - customPersonalityOpen = true; - openSubParentToggle(); - showToggles(trafficPersonalityKeys); - }); - advancedDrivingToggle = trafficPersonalityToggle; - } else if (param == "AggressivePersonalityProfile") { - FrogPilotParamManageControl *aggressivePersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon); - QObject::connect(aggressivePersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - customPersonalityOpen = true; - openSubParentToggle(); - showToggles(aggressivePersonalityKeys); - }); - advancedDrivingToggle = aggressivePersonalityToggle; - } else if (param == "StandardPersonalityProfile") { - FrogPilotParamManageControl *standardPersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon); - QObject::connect(standardPersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - customPersonalityOpen = true; - openSubParentToggle(); - showToggles(standardPersonalityKeys); - }); - advancedDrivingToggle = standardPersonalityToggle; - } else if (param == "RelaxedPersonalityProfile") { - FrogPilotParamManageControl *relaxedPersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon); - QObject::connect(relaxedPersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - customPersonalityOpen = true; - openSubParentToggle(); - showToggles(relaxedPersonalityKeys); - }); - advancedDrivingToggle = relaxedPersonalityToggle; - } else if (trafficPersonalityKeys.find(param) != trafficPersonalityKeys.end() || - aggressivePersonalityKeys.find(param) != aggressivePersonalityKeys.end() || - standardPersonalityKeys.find(param) != standardPersonalityKeys.end() || - relaxedPersonalityKeys.find(param) != relaxedPersonalityKeys.end()) { - if (param == "TrafficFollow" || param == "AggressiveFollow" || param == "StandardFollow" || param == "RelaxedFollow") { - if (param == "TrafficFollow") { - advancedDrivingToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0.5, 5, tr(" seconds"), std::map(), 0.01); - } else { - advancedDrivingToggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 5, tr(" seconds"), std::map(), 0.01); - } - } else { - advancedDrivingToggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 500, "%"); - } - - } else if (param == "ModelManagement") { - FrogPilotParamManageControl *modelManagementToggle = new FrogPilotParamManageControl(param, title, desc, icon); - QObject::connect(modelManagementToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - availableModelNames = QString::fromStdString(params.get("AvailableModelsNames")).split(","); - availableModels = QString::fromStdString(params.get("AvailableModels")).split(","); - experimentalModels = QString::fromStdString(params.get("ExperimentalModels")).split(","); - - modelManagementOpen = true; - - QString currentModel = QString::fromStdString(params.get("Model")) + ".thneed"; - QStringList modelFiles = modelDir.entryList({"*.thneed"}, QDir::Files); - modelFiles.removeAll(currentModel); - haveModelsDownloaded = modelFiles.size() > 1; - modelsDownloaded = params.getBool("ModelsDownloaded"); - - showToggles(modelManagementKeys); - }); - advancedDrivingToggle = modelManagementToggle; - } else if (param == "ModelRandomizer") { - FrogPilotParamManageControl *modelRandomizerToggle = new FrogPilotParamManageControl(param, title, desc, icon); - QObject::connect(modelRandomizerToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - openSubParentToggle(); - showToggles(modelRandomizerKeys); - updateModelLabels(); - }); - advancedDrivingToggle = modelRandomizerToggle; - } else if (param == "ManageBlacklistedModels") { - FrogPilotButtonsControl *blacklistBtn = new FrogPilotButtonsControl(title, desc, {tr("ADD"), tr("REMOVE")}); - QObject::connect(blacklistBtn, &FrogPilotButtonsControl::buttonClicked, [this](int id) { - QStringList blacklistedModels = QString::fromStdString(params.get("BlacklistedModels")).split(",", QString::SkipEmptyParts); - QStringList selectableModels = availableModelNames; - - for (QString &model : blacklistedModels) { - selectableModels.removeAll(model); - if (model.contains("(Default)")) { - blacklistedModels.move(blacklistedModels.indexOf(model), 0); - } - } - - if (id == 0) { - if (selectableModels.size() == 1) { - FrogPilotConfirmationDialog::toggleAlert(tr("There's no more models to blacklist! The only available model is \"%1\"!").arg(selectableModels.first()), tr("OK"), this); - } else { - QString selectedModel = MultiOptionDialog::getSelection(tr("Select a model to add to the blacklist"), selectableModels, "", this); - if (!selectedModel.isEmpty()) { - if (ConfirmationDialog::confirm(tr("Are you sure you want to add the '%1' model to the blacklist?").arg(selectedModel), tr("Add"), this)) { - if (!blacklistedModels.contains(selectedModel)) { - blacklistedModels.append(selectedModel); - params.putNonBlocking("BlacklistedModels", blacklistedModels.join(",").toStdString()); - paramsStorage.putNonBlocking("BlacklistedModels", blacklistedModels.join(",").toStdString()); - } - } - } - } - } else if (id == 1) { - QString selectedModel = MultiOptionDialog::getSelection(tr("Select a model to remove from the blacklist"), blacklistedModels, "", this); - if (!selectedModel.isEmpty()) { - if (ConfirmationDialog::confirm(tr("Are you sure you want to remove the '%1' model from the blacklist?").arg(selectedModel), tr("Remove"), this)) { - if (blacklistedModels.contains(selectedModel)) { - blacklistedModels.removeAll(selectedModel); - params.putNonBlocking("BlacklistedModels", blacklistedModels.join(",").toStdString()); - paramsStorage.putNonBlocking("BlacklistedModels", blacklistedModels.join(",").toStdString()); - } - } - } - } - }); - advancedDrivingToggle = blacklistBtn; - } else if (param == "ResetScores") { - ButtonControl *resetCalibrationsBtn = new ButtonControl(title, tr("RESET"), desc); - QObject::connect(resetCalibrationsBtn, &ButtonControl::clicked, [this]() { - if (FrogPilotConfirmationDialog::yesorno(tr("Reset all model scores?"), this)) { - for (const QString &model : availableModelNames) { - QString cleanedModel = processModelName(model); - params.remove(QString("%1Drives").arg(cleanedModel).toStdString()); - paramsStorage.remove(QString("%1Drives").arg(cleanedModel).toStdString()); - params.remove(QString("%1Score").arg(cleanedModel).toStdString()); - paramsStorage.remove(QString("%1Score").arg(cleanedModel).toStdString()); - } - updateModelLabels(); - } - }); - advancedDrivingToggle = reinterpret_cast(resetCalibrationsBtn); - } else if (param == "ReviewScores") { - ButtonControl *reviewScoresBtn = new ButtonControl(title, tr("VIEW"), desc); - QObject::connect(reviewScoresBtn, &ButtonControl::clicked, [this]() { - openSubSubParentToggle(); - - for (LabelControl *label : labelControls) { - label->setVisible(true); - } - - for (auto &[key, toggle] : toggles) { - toggle->setVisible(false); - } - }); - advancedDrivingToggle = reinterpret_cast(reviewScoresBtn); - } else if (param == "DeleteModel") { - deleteModelBtn = new ButtonControl(title, tr("DELETE"), desc); - QObject::connect(deleteModelBtn, &ButtonControl::clicked, [this]() { - QStringList deletableModels, existingModels = modelDir.entryList({"*.thneed"}, QDir::Files); - QMap labelToFileMap; - QString currentModel = QString::fromStdString(params.get("Model")) + ".thneed"; - - for (int i = 0; i < availableModels.size(); ++i) { - QString modelFile = availableModels[i] + ".thneed"; - if (existingModels.contains(modelFile) && modelFile != currentModel && !availableModelNames[i].contains("(Default)")) { - deletableModels.append(availableModelNames[i]); - labelToFileMap[availableModelNames[i]] = modelFile; - } - } - - QString selectedModel = MultiOptionDialog::getSelection(tr("Select a model to delete"), deletableModels, "", this); - if (!selectedModel.isEmpty()) { - if (ConfirmationDialog::confirm(tr("Are you sure you want to delete the '%1' model?").arg(selectedModel), tr("Delete"), this)) { - std::thread([=]() { - modelDeleting = true; - modelsDownloaded = false; - update(); - - params.putBoolNonBlocking("ModelsDownloaded", false); - deleteModelBtn->setValue(tr("Deleting...")); - - QFile::remove(modelDir.absoluteFilePath(labelToFileMap[selectedModel])); - deleteModelBtn->setValue(tr("Deleted!")); - - util::sleep_for(1000); - deleteModelBtn->setValue(""); - modelDeleting = false; - - QStringList modelFiles = modelDir.entryList({"*.thneed"}, QDir::Files); - modelFiles.removeAll(currentModel); - - haveModelsDownloaded = modelFiles.size() > 1; - update(); - }).detach(); - } - } - }); - advancedDrivingToggle = reinterpret_cast(deleteModelBtn); - } else if (param == "DownloadModel") { - downloadModelBtn = new ButtonControl(title, tr("DOWNLOAD"), desc); - QObject::connect(downloadModelBtn, &ButtonControl::clicked, [this]() { - if (downloadModelBtn->text() == tr("CANCEL")) { - paramsMemory.remove("ModelToDownload"); - paramsMemory.putBool("CancelModelDownload", true); - cancellingDownload = true; - - device()->resetInteractiveTimeout(30); - } else { - QMap labelToModelMap; - QStringList existingModels = modelDir.entryList({"*.thneed"}, QDir::Files); - QStringList downloadableModels; - - for (int i = 0; i < availableModels.size(); ++i) { - QString modelFile = availableModels[i] + ".thneed"; - if (!existingModels.contains(modelFile) && !availableModelNames[i].contains("(Default)")) { - downloadableModels.append(availableModelNames[i]); - labelToModelMap.insert(availableModelNames[i], availableModels[i]); - } - } - - QString modelToDownload = MultiOptionDialog::getSelection(tr("Select a driving model to download"), downloadableModels, "", this); - if (!modelToDownload.isEmpty()) { - device()->resetInteractiveTimeout(300); - - modelDownloading = true; - paramsMemory.put("ModelToDownload", labelToModelMap.value(modelToDownload).toStdString()); - paramsMemory.put("ModelDownloadProgress", "0%"); - - downloadModelBtn->setValue(tr("Downloading %1...").arg(modelToDownload.remove(QRegularExpression("[🗺️👀📡]")).trimmed())); - - QTimer *progressTimer = new QTimer(this); - progressTimer->setInterval(100); - - QObject::connect(progressTimer, &QTimer::timeout, this, [=]() { - QString progress = QString::fromStdString(paramsMemory.get("ModelDownloadProgress")); - bool downloadComplete = progress.contains(QRegularExpression("downloaded", QRegularExpression::CaseInsensitiveOption)); - bool downloadFailed = progress.contains(QRegularExpression("cancelled|exists|failed|offline", QRegularExpression::CaseInsensitiveOption)); - - if (!progress.isEmpty() && progress != "0%") { - downloadModelBtn->setValue(progress); - } - - if (downloadComplete || downloadFailed) { - bool lastModelDownloaded = downloadComplete; - - if (downloadComplete) { - haveModelsDownloaded = true; - update(); - } - - if (downloadComplete) { - for (const QString &model : availableModels) { - if (!QFile::exists(modelDir.filePath(model + ".thneed"))) { - lastModelDownloaded = false; - break; - } - } - } - - downloadModelBtn->setValue(progress); - - paramsMemory.remove("CancelModelDownload"); - paramsMemory.remove("ModelDownloadProgress"); - - progressTimer->stop(); - progressTimer->deleteLater(); - - QTimer::singleShot(2000, this, [=]() { - cancellingDownload = false; - modelDownloading = false; - - downloadModelBtn->setValue(""); - - if (lastModelDownloaded) { - modelsDownloaded = true; - update(); - - params.putBoolNonBlocking("ModelsDownloaded", modelsDownloaded); - } - - device()->resetInteractiveTimeout(30); - }); - } - }); - progressTimer->start(); - } - } - }); - advancedDrivingToggle = reinterpret_cast(downloadModelBtn); - } else if (param == "DownloadAllModels") { - downloadAllModelsBtn = new ButtonControl(title, tr("DOWNLOAD"), desc); - QObject::connect(downloadAllModelsBtn, &ButtonControl::clicked, [this]() { - if (downloadAllModelsBtn->text() == tr("CANCEL")) { - paramsMemory.remove("DownloadAllModels"); - paramsMemory.putBool("CancelModelDownload", true); - cancellingDownload = true; - - device()->resetInteractiveTimeout(30); - } else { - device()->resetInteractiveTimeout(300); - - startDownloadAllModels(); - } - }); - advancedDrivingToggle = reinterpret_cast(downloadAllModelsBtn); - } else if (param == "SelectModel") { - selectModelBtn = new ButtonControl(title, tr("SELECT"), desc); - QObject::connect(selectModelBtn, &ButtonControl::clicked, [this]() { - QSet modelFilesBaseNames = QSet::fromList(modelDir.entryList({"*.thneed"}, QDir::Files).replaceInStrings(QRegExp("\\.thneed$"), "")); - QStringList selectableModels; - - for (int i = 0; i < availableModels.size(); ++i) { - if (modelFilesBaseNames.contains(availableModels[i]) || availableModelNames[i].contains("(Default)")) { - selectableModels.append(availableModelNames[i]); - } - } - - QString modelToSelect = MultiOptionDialog::getSelection(tr("Select a model - 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC"), selectableModels, "", this); - if (!modelToSelect.isEmpty()) { - selectModelBtn->setValue(modelToSelect); - int modelIndex = availableModelNames.indexOf(modelToSelect); - - params.putNonBlocking("Model", availableModels.at(modelIndex).toStdString()); - params.putNonBlocking("ModelName", modelToSelect.toStdString()); - - if (experimentalModels.contains(availableModels.at(modelIndex))) { - FrogPilotConfirmationDialog::toggleAlert(tr("WARNING: This is a very experimental model and may drive dangerously!"), tr("I understand the risks."), this); - } - - QString model = availableModelNames.at(modelIndex); - QString part_model_param = processModelName(model); - - if (!params.checkKey(part_model_param.toStdString() + "CalibrationParams") || !params.checkKey(part_model_param.toStdString() + "LiveTorqueParameters")) { - if (FrogPilotConfirmationDialog::yesorno(tr("Start with a fresh calibration for the newly selected model?"), this)) { - params.remove("CalibrationParams"); - params.remove("LiveTorqueParameters"); - } - } - - if (started) { - if (FrogPilotConfirmationDialog::toggle(tr("Reboot required to take effect."), tr("Reboot Now"), this)) { - Hardware::reboot(); - } - } - } - }); - selectModelBtn->setValue(QString::fromStdString(params.get("ModelName"))); - advancedDrivingToggle = reinterpret_cast(selectModelBtn); - } else if (param == "ResetCalibrations") { - FrogPilotButtonsControl *resetCalibrationsBtn = new FrogPilotButtonsControl(title, desc, {tr("RESET ALL"), tr("RESET ONE")}); - QObject::connect(resetCalibrationsBtn, &FrogPilotButtonsControl::showDescriptionEvent, this, &FrogPilotAdvancedDrivingPanel::updateCalibrationDescription); - QObject::connect(resetCalibrationsBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) { - if (id == 0) { - if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset all of your model calibrations?"), this)) { - for (const QString &model : availableModelNames) { - QString cleanedModel = processModelName(model); - params.remove(QString("%1CalibrationParams").arg(cleanedModel).toStdString()); - paramsStorage.remove(QString("%1CalibrationParams").arg(cleanedModel).toStdString()); - params.remove(QString("%1LiveTorqueParameters").arg(cleanedModel).toStdString()); - paramsStorage.remove(QString("%1LiveTorqueParameters").arg(cleanedModel).toStdString()); - } - } - } else if (id == 1) { - QStringList selectableModelLabels; - for (int i = 0; i < availableModels.size(); ++i) { - selectableModelLabels.append(availableModelNames[i]); - } - - QString modelToReset = MultiOptionDialog::getSelection(tr("Select a model to reset"), selectableModelLabels, "", this); - if (!modelToReset.isEmpty()) { - if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset this model's calibrations?"), this)) { - QString cleanedModel = processModelName(modelToReset); - params.remove(QString("%1CalibrationParams").arg(cleanedModel).toStdString()); - paramsStorage.remove(QString("%1CalibrationParams").arg(cleanedModel).toStdString()); - params.remove(QString("%1LiveTorqueParameters").arg(cleanedModel).toStdString()); - paramsStorage.remove(QString("%1LiveTorqueParameters").arg(cleanedModel).toStdString()); - } - } - } - }); - advancedDrivingToggle = resetCalibrationsBtn; - - } else { - advancedDrivingToggle = new ParamControl(param, title, desc, icon); - } - - addItem(advancedDrivingToggle); - toggles[param] = advancedDrivingToggle; - - makeConnections(advancedDrivingToggle); - - if (FrogPilotParamManageControl *frogPilotManageToggle = qobject_cast(advancedDrivingToggle)) { - QObject::connect(frogPilotManageToggle, &FrogPilotParamManageControl::manageButtonClicked, this, &FrogPilotAdvancedDrivingPanel::openParentToggle); - } - - QObject::connect(advancedDrivingToggle, &AbstractControl::showDescriptionEvent, [this]() { - update(); - }); - } - - QObject::connect(static_cast(toggles["ModelManagement"]), &ToggleControl::toggleFlipped, [this](bool state) { - modelManagement = state; - }); - - QObject::connect(static_cast(toggles["ModelRandomizer"]), &ToggleControl::toggleFlipped, [this](bool state) { - modelRandomizer = state; - if (state && !modelsDownloaded) { - if (FrogPilotConfirmationDialog::yesorno(tr("The 'Model Randomizer' only works with downloaded models. Do you want to download all the driving models?"), this)) { - startDownloadAllModels(); - } - } - }); - - steerFrictionToggle = static_cast(toggles["SteerFriction"]); - QObject::connect(steerFrictionToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() { - params.putFloat("SteerFriction", steerFrictionStock); - steerFrictionToggle->refresh(); - updateFrogPilotToggles(); - }); - - steerKPToggle = static_cast(toggles["SteerKP"]); - QObject::connect(steerKPToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() { - params.putFloat("SteerKP", steerKPStock); - steerKPToggle->refresh(); - updateFrogPilotToggles(); - }); - - steerLatAccelToggle = static_cast(toggles["SteerLatAccel"]); - QObject::connect(steerLatAccelToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() { - params.putFloat("SteerLatAccel", steerLatAccelStock); - steerLatAccelToggle->refresh(); - updateFrogPilotToggles(); - }); - - steerRatioToggle = static_cast(toggles["SteerRatio"]); - QObject::connect(steerRatioToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() { - params.putFloat("SteerRatio", steerRatioStock); - steerRatioToggle->refresh(); - updateFrogPilotToggles(); - }); - - FrogPilotParamValueControl *trafficFollowToggle = static_cast(toggles["TrafficFollow"]); - FrogPilotParamValueControl *trafficAccelerationToggle = static_cast(toggles["TrafficJerkAcceleration"]); - FrogPilotParamValueControl *trafficDecelerationToggle = static_cast(toggles["TrafficJerkDeceleration"]); - FrogPilotParamValueControl *trafficDangerToggle = static_cast(toggles["TrafficJerkDanger"]); - FrogPilotParamValueControl *trafficSpeedToggle = static_cast(toggles["TrafficJerkSpeed"]); - FrogPilotParamValueControl *trafficSpeedDecreaseToggle = static_cast(toggles["TrafficJerkSpeedDecrease"]); - FrogPilotButtonsControl *trafficResetButton = static_cast(toggles["ResetTrafficPersonality"]); - QObject::connect(trafficResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() { - if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for the 'Traffic Mode' personality?"), this)) { - params.putFloat("TrafficFollow", 0.5); - params.putFloat("TrafficJerkAcceleration", 50); - params.putFloat("TrafficJerkDeceleration", 50); - params.putFloat("TrafficJerkDanger", 100); - params.putFloat("TrafficJerkSpeed", 50); - params.putFloat("TrafficJerkSpeedDecrease", 50); - trafficFollowToggle->refresh(); - trafficAccelerationToggle->refresh(); - trafficDecelerationToggle->refresh(); - trafficDangerToggle->refresh(); - trafficSpeedToggle->refresh(); - trafficSpeedDecreaseToggle->refresh(); - updateFrogPilotToggles(); - } - }); - - FrogPilotParamValueControl *aggressiveFollowToggle = static_cast(toggles["AggressiveFollow"]); - FrogPilotParamValueControl *aggressiveAccelerationToggle = static_cast(toggles["AggressiveJerkAcceleration"]); - FrogPilotParamValueControl *aggressiveDecelerationToggle = static_cast(toggles["AggressiveJerkDeceleration"]); - FrogPilotParamValueControl *aggressiveDangerToggle = static_cast(toggles["AggressiveJerkDanger"]); - FrogPilotParamValueControl *aggressiveSpeedToggle = static_cast(toggles["AggressiveJerkSpeed"]); - FrogPilotParamValueControl *aggressiveSpeedDecreaseToggle = static_cast(toggles["AggressiveJerkSpeedDecrease"]); - FrogPilotButtonsControl *aggressiveResetButton = static_cast(toggles["ResetAggressivePersonality"]); - QObject::connect(aggressiveResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() { - if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for the 'Aggressive' personality?"), this)) { - params.putFloat("AggressiveFollow", 1.25); - params.putFloat("AggressiveJerkAcceleration", 50); - params.putFloat("AggressiveJerkDeceleration", 50); - params.putFloat("AggressiveJerkDanger", 100); - params.putFloat("AggressiveJerkSpeed", 50); - params.putFloat("AggressiveJerkSpeedDecrease", 50); - aggressiveFollowToggle->refresh(); - aggressiveAccelerationToggle->refresh(); - aggressiveDecelerationToggle->refresh(); - aggressiveDangerToggle->refresh(); - aggressiveSpeedToggle->refresh(); - aggressiveSpeedDecreaseToggle->refresh(); - updateFrogPilotToggles(); - } - }); - - FrogPilotParamValueControl *standardFollowToggle = static_cast(toggles["StandardFollow"]); - FrogPilotParamValueControl *standardAccelerationToggle = static_cast(toggles["StandardJerkAcceleration"]); - FrogPilotParamValueControl *standardDecelerationToggle = static_cast(toggles["StandardJerkDeceleration"]); - FrogPilotParamValueControl *standardDangerToggle = static_cast(toggles["StandardJerkDanger"]); - FrogPilotParamValueControl *standardSpeedToggle = static_cast(toggles["StandardJerkSpeed"]); - FrogPilotParamValueControl *standardSpeedDecreaseToggle = static_cast(toggles["StandardJerkSpeedDecrease"]); - FrogPilotButtonsControl *standardResetButton = static_cast(toggles["ResetStandardPersonality"]); - QObject::connect(standardResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() { - if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for the 'Standard' personality?"), this)) { - params.putFloat("StandardFollow", 1.45); - params.putFloat("StandardJerkAcceleration", 100); - params.putFloat("StandardJerkDeceleration", 100); - params.putFloat("StandardJerkDanger", 100); - params.putFloat("StandardJerkSpeed", 100); - params.putFloat("StandardJerkSpeedDecrease", 100); - standardFollowToggle->refresh(); - standardAccelerationToggle->refresh(); - standardDecelerationToggle->refresh(); - standardDangerToggle->refresh(); - standardSpeedToggle->refresh(); - standardSpeedDecreaseToggle->refresh(); - updateFrogPilotToggles(); - } - }); - - FrogPilotParamValueControl *relaxedFollowToggle = static_cast(toggles["RelaxedFollow"]); - FrogPilotParamValueControl *relaxedAccelerationToggle = static_cast(toggles["RelaxedJerkAcceleration"]); - FrogPilotParamValueControl *relaxedDecelerationToggle = static_cast(toggles["RelaxedJerkDeceleration"]); - FrogPilotParamValueControl *relaxedDangerToggle = static_cast(toggles["RelaxedJerkDanger"]); - FrogPilotParamValueControl *relaxedSpeedToggle = static_cast(toggles["RelaxedJerkSpeed"]); - FrogPilotParamValueControl *relaxedSpeedDecreaseToggle = static_cast(toggles["RelaxedJerkSpeedDecrease"]); - FrogPilotButtonsControl *relaxedResetButton = static_cast(toggles["ResetRelaxedPersonality"]); - QObject::connect(relaxedResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() { - if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for the 'Relaxed' personality?"), this)) { - params.putFloat("RelaxedFollow", 1.75); - params.putFloat("RelaxedJerkAcceleration", 100); - params.putFloat("RelaxedJerkDeceleration", 100); - params.putFloat("RelaxedJerkDanger", 100); - params.putFloat("RelaxedJerkSpeed", 100); - params.putFloat("RelaxedJerkSpeedDecrease", 100); - relaxedFollowToggle->refresh(); - relaxedAccelerationToggle->refresh(); - relaxedDecelerationToggle->refresh(); - relaxedDangerToggle->refresh(); - relaxedSpeedToggle->refresh(); - relaxedSpeedDecreaseToggle->refresh(); - updateFrogPilotToggles(); - } - }); - - QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotAdvancedDrivingPanel::hideToggles); - QObject::connect(parent, &FrogPilotSettingsWindow::closeSubParentToggle, this, &FrogPilotAdvancedDrivingPanel::hideSubToggles); - QObject::connect(parent, &FrogPilotSettingsWindow::closeSubSubParentToggle, this, &FrogPilotAdvancedDrivingPanel::hideSubSubToggles); - QObject::connect(parent, &FrogPilotSettingsWindow::updateCarToggles, this, &FrogPilotAdvancedDrivingPanel::updateCarToggles); - QObject::connect(parent, &FrogPilotSettingsWindow::updateMetric, this, &FrogPilotAdvancedDrivingPanel::updateMetric); - QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotAdvancedDrivingPanel::updateState); - - updateMetric(); -} - -void FrogPilotAdvancedDrivingPanel::updateMetric() { - bool previousIsMetric = isMetric; - isMetric = params.getBool("IsMetric"); - - if (isMetric != previousIsMetric) { - double speedConversion = isMetric ? MILE_TO_KM : KM_TO_MILE; - - params.putFloatNonBlocking("SetSpeedOffset", params.getFloat("SetSpeedOffset") * speedConversion); - } - - FrogPilotParamValueControl *setSpeedOffsetToggle = static_cast(toggles["SetSpeedOffset"]); - - if (isMetric) { - setSpeedOffsetToggle->updateControl(0, 150, tr("kph")); - } else { - setSpeedOffsetToggle->updateControl(0, 99, tr("mph")); - } -} - -void FrogPilotAdvancedDrivingPanel::showEvent(QShowEvent *event) { - modelManagement = params.getBool("ModelManagement"); - modelRandomizer = params.getBool("ModelRandomizer"); -} - -void FrogPilotAdvancedDrivingPanel::updateCarToggles() { - disableOpenpilotLongitudinal = parent->disableOpenpilotLongitudinal; - hasAutoTune = parent->hasAutoTune; - hasNNFFLog = parent->hasNNFFLog; - hasOpenpilotLongitudinal = parent->hasOpenpilotLongitudinal; - hasPCMCruise = parent->hasPCMCruise; - isPIDCar = parent->isPIDCar; - liveValid = parent->liveValid; - steerFrictionStock = parent->steerFrictionStock; - steerKPStock = parent->steerKPStock; - steerLatAccelStock = parent->steerLatAccelStock; - steerRatioStock = parent->steerRatioStock; - - steerFrictionToggle->setTitle(QString(tr("Friction (Default: %1)")).arg(QString::number(steerFrictionStock, 'f', 2))); - steerKPToggle->setTitle(QString(tr("Kp Factor (Default: %1)")).arg(QString::number(steerKPStock, 'f', 2))); - steerKPToggle->updateControl(steerKPStock * 0.50, steerKPStock * 1.50); - steerLatAccelToggle->setTitle(QString(tr("Lateral Accel (Default: %1)")).arg(QString::number(steerLatAccelStock, 'f', 2))); - steerLatAccelToggle->updateControl(steerLatAccelStock * 0.75, steerLatAccelStock * 1.25); - steerRatioToggle->setTitle(QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(steerRatioStock, 'f', 2))); - steerRatioToggle->updateControl(steerRatioStock * 0.75, steerRatioStock * 1.25); - - hideToggles(); -} - -void FrogPilotAdvancedDrivingPanel::updateState(const UIState &s) { - if (!isVisible()) return; - - if (modelManagementOpen) { - downloadAllModelsBtn->setText(modelDownloading && allModelsDownloading ? tr("CANCEL") : tr("DOWNLOAD")); - downloadModelBtn->setText(modelDownloading && !allModelsDownloading ? tr("CANCEL") : tr("DOWNLOAD")); - - deleteModelBtn->setEnabled(!modelDeleting && !modelDownloading); - downloadAllModelsBtn->setEnabled(s.scene.online && !cancellingDownload && !modelDeleting && (!modelDownloading || allModelsDownloading) && !modelsDownloaded); - downloadModelBtn->setEnabled(s.scene.online && !cancellingDownload && !modelDeleting && !allModelsDownloading && !modelsDownloaded); - selectModelBtn->setEnabled(!modelDeleting && !modelDownloading && !modelRandomizer); - } - - started = s.scene.started; -} - -void FrogPilotAdvancedDrivingPanel::startDownloadAllModels() { - allModelsDownloading = true; - modelDownloading = true; - - paramsMemory.putBool("DownloadAllModels", true); - - downloadAllModelsBtn->setValue(tr("Downloading models...")); - - QTimer *progressTimer = new QTimer(this); - progressTimer->setInterval(100); - - QObject::connect(progressTimer, &QTimer::timeout, this, [=]() { - QString progress = QString::fromStdString(paramsMemory.get("ModelDownloadProgress")); - bool downloadComplete = progress.contains(QRegularExpression("All models downloaded!", QRegularExpression::CaseInsensitiveOption)); - bool downloadFailed = progress.contains(QRegularExpression("cancelled|exists|failed|offline", QRegularExpression::CaseInsensitiveOption)); - - if (!progress.isEmpty() && progress != "0%") { - downloadAllModelsBtn->setValue(progress); - } - - if (downloadComplete || downloadFailed) { - if (downloadComplete) { - haveModelsDownloaded = true; - update(); - } - - downloadAllModelsBtn->setValue(progress); - - paramsMemory.remove("CancelModelDownload"); - paramsMemory.remove("ModelDownloadProgress"); - - progressTimer->stop(); - progressTimer->deleteLater(); - - QTimer::singleShot(2000, this, [=]() { - cancellingDownload = false; - modelDownloading = false; - - paramsMemory.remove("DownloadAllModels"); - - downloadAllModelsBtn->setValue(""); - - device()->resetInteractiveTimeout(30); - }); - } - }); - progressTimer->start(); -} - -void FrogPilotAdvancedDrivingPanel::updateCalibrationDescription() { - QString model = QString::fromStdString(params.get("ModelName")); - QString part_model_param = processModelName(model); - - QString desc = - tr("openpilot requires the device to be mounted within 4° left or right and " - "within 5° up or 9° down. openpilot is continuously calibrating, resetting is rarely required."); - std::string calib_bytes = params.get(part_model_param.toStdString() + "CalibrationParams"); - if (!calib_bytes.empty()) { - try { - AlignedBuffer aligned_buf; - capnp::FlatArrayMessageReader cmsg(aligned_buf.align(calib_bytes.data(), calib_bytes.size())); - auto calib = cmsg.getRoot().getLiveCalibration(); - if (calib.getCalStatus() != cereal::LiveCalibrationData::Status::UNCALIBRATED) { - double pitch = calib.getRpyCalib()[1] * (180 / M_PI); - double yaw = calib.getRpyCalib()[2] * (180 / M_PI); - desc += tr(" Your device is pointed %1° %2 and %3° %4.") - .arg(QString::number(std::abs(pitch), 'g', 1), pitch > 0 ? tr("down") : tr("up"), - QString::number(std::abs(yaw), 'g', 1), yaw > 0 ? tr("left") : tr("right")); - } - } catch (kj::Exception) { - qInfo() << "invalid CalibrationParams"; - } - } - qobject_cast(sender())->setDescription(desc); -} - -void FrogPilotAdvancedDrivingPanel::updateModelLabels() { - QVector> modelScores; - for (QString &model : availableModelNames) { - QString cleanedModel = processModelName(model); - int score = params.getInt((cleanedModel + "Score").toStdString()); - - if (model.contains("(Default)")) { - modelScores.prepend(qMakePair(model, score)); - } else { - modelScores.append(qMakePair(model, score)); - } - } - - labelControls.clear(); - - for (QPair &pair : modelScores) { - QString scoreDisplay = pair.second == 0 ? "N/A" : QString::number(pair.second) + "%"; - LabelControl *labelControl = new LabelControl(pair.first, scoreDisplay, ""); - labelControls.append(labelControl); - addItem(labelControl); - } - - for (LabelControl *label : labelControls) { - label->setVisible(false); - } -} - -void FrogPilotAdvancedDrivingPanel::showToggles(const std::set &keys) { - setUpdatesEnabled(false); - - for (auto &[key, toggle] : toggles) { - toggle->setVisible(keys.find(key) != keys.end()); - } - - setUpdatesEnabled(true); - update(); -} - -void FrogPilotAdvancedDrivingPanel::hideToggles() { - setUpdatesEnabled(false); - - customPersonalityOpen = false; - modelManagementOpen = false; - - for (LabelControl *label : labelControls) { - label->setVisible(false); - } - - std::set longitudinalKeys = {"AdvancedLongitudinalTune", "CustomPersonalities"}; - for (auto &[key, toggle] : toggles) { - bool subToggles = aggressivePersonalityKeys.find(key) != aggressivePersonalityKeys.end() || - customDrivingPersonalityKeys.find(key) != customDrivingPersonalityKeys.end() || - lateralTuneKeys.find(key) != lateralTuneKeys.end() || - longitudinalTuneKeys.find(key) != longitudinalTuneKeys.end() || - modelManagementKeys.find(key) != modelManagementKeys.end() || - modelRandomizerKeys.find(key) != modelRandomizerKeys.end() || - qolKeys.find(key) != qolKeys.end() || - relaxedPersonalityKeys.find(key) != relaxedPersonalityKeys.end() || - standardPersonalityKeys.find(key) != standardPersonalityKeys.end() || - trafficPersonalityKeys.find(key) != trafficPersonalityKeys.end(); - - if (disableOpenpilotLongitudinal || !hasOpenpilotLongitudinal) { - if (longitudinalKeys.find(key) != longitudinalKeys.end()) { - toggle->setVisible(false); - continue; - } - } - - toggle->setVisible(!subToggles); - } - - setUpdatesEnabled(true); - update(); -} - -void FrogPilotAdvancedDrivingPanel::hideSubToggles() { - if (customPersonalityOpen) { - customPersonalityOpen = false; - showToggles(customDrivingPersonalityKeys); - } else if (modelManagementOpen) { - for (LabelControl *label : labelControls) { - label->setVisible(false); - } - showToggles(modelManagementKeys); - } -} - -void FrogPilotAdvancedDrivingPanel::hideSubSubToggles() { - if (modelManagementOpen) { - for (LabelControl *label : labelControls) { - label->setVisible(false); - } - showToggles(modelRandomizerKeys); - } -} diff --git a/selfdrive/frogpilot/ui/qt/offroad/advanced_driving_settings.h b/selfdrive/frogpilot/ui/qt/offroad/advanced_driving_settings.h deleted file mode 100644 index 2e89c0a9629a93..00000000000000 --- a/selfdrive/frogpilot/ui/qt/offroad/advanced_driving_settings.h +++ /dev/null @@ -1,137 +0,0 @@ -#pragma once - -#include - -#include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h" - -class FrogPilotAdvancedDrivingPanel : public FrogPilotListWidget { - Q_OBJECT - -public: - explicit FrogPilotAdvancedDrivingPanel(FrogPilotSettingsWindow *parent); - -signals: - void openParentToggle(); - void openSubParentToggle(); - void openSubSubParentToggle(); - -protected: - void showEvent(QShowEvent *event) override; - -private: - void hideSubToggles(); - void hideSubSubToggles(); - void hideToggles(); - void showToggles(const std::set &keys); - - void startDownloadAllModels(); - void updateCalibrationDescription(); - void updateCarToggles(); - void updateMetric(); - void updateModelLabels(); - void updateState(const UIState &s); - - std::set aggressivePersonalityKeys = { - "AggressiveFollow", "AggressiveJerkAcceleration", "AggressiveJerkDeceleration", - "AggressiveJerkDanger", "AggressiveJerkSpeed", "AggressiveJerkSpeedDecrease", - "ResetAggressivePersonality" - }; - - std::set customDrivingPersonalityKeys = { - "AggressivePersonalityProfile", "RelaxedPersonalityProfile", "StandardPersonalityProfile", - "TrafficPersonalityProfile" - }; - - std::set lateralTuneKeys = { - "ForceAutoTune", "ForceAutoTuneOff", "SteerFriction", - "SteerLatAccel", "SteerKP", "SteerRatio", "TacoTune", - "TurnDesires" - }; - - std::set longitudinalTuneKeys = { - "LeadDetectionThreshold", "MaxDesiredAcceleration" - }; - - std::set modelManagementKeys = { - "AutomaticallyUpdateModels", "DeleteModel", "DownloadModel", - "DownloadAllModels", "ModelRandomizer", "ResetCalibrations", - "SelectModel" - }; - - std::set modelRandomizerKeys = { - "ManageBlacklistedModels", "ResetScores", "ReviewScores" - }; - - std::set qolKeys = { - "ForceStandstill", "ForceStops", "SetSpeedOffset" - }; - - std::set relaxedPersonalityKeys = { - "RelaxedFollow", "RelaxedJerkAcceleration", "RelaxedJerkDeceleration", - "RelaxedJerkDanger", "RelaxedJerkSpeed", "RelaxedJerkSpeedDecrease", - "ResetRelaxedPersonality" - }; - - std::set standardPersonalityKeys = { - "StandardFollow", "StandardJerkAcceleration", "StandardJerkDeceleration", - "StandardJerkDanger", "StandardJerkSpeed", "StandardJerkSpeedDecrease", - "ResetStandardPersonality" - }; - - std::set trafficPersonalityKeys = { - "TrafficFollow", "TrafficJerkAcceleration", "TrafficJerkDeceleration", - "TrafficJerkDanger", "TrafficJerkSpeed", "TrafficJerkSpeedDecrease", - "ResetTrafficPersonality" - }; - - ButtonControl *deleteModelBtn; - ButtonControl *downloadAllModelsBtn; - ButtonControl *downloadModelBtn; - ButtonControl *selectModelBtn; - - FrogPilotParamValueButtonControl *steerFrictionToggle; - FrogPilotParamValueButtonControl *steerLatAccelToggle; - FrogPilotParamValueButtonControl *steerKPToggle; - FrogPilotParamValueButtonControl *steerRatioToggle; - - FrogPilotSettingsWindow *parent; - - Params params; - Params paramsMemory{"/dev/shm/params"}; - Params paramsStorage{"/persist/params"}; - - QDir modelDir{"/data/models/"}; - - QList labelControls; - - QStringList availableModelNames; - QStringList availableModels; - QStringList experimentalModels; - - bool allModelsDownloading; - bool cancellingDownload; - bool customPersonalityOpen; - bool disableOpenpilotLongitudinal; - bool hasAutoTune; - bool hasNNFFLog; - bool hasOpenpilotLongitudinal; - bool hasPCMCruise; - bool haveModelsDownloaded; - bool isMetric = params.getBool("IsMetric"); - bool isPIDCar; - bool liveValid; - bool modelDeleting; - bool modelDownloading; - bool modelManagement; - bool modelManagementOpen; - bool modelRandomizer; - bool modelsDownloaded; - bool started; - - float steerFrictionStock; - float steerLatAccelStock; - float steerKPStock; - float steerRatioStock; - - std::map toggles; -}; diff --git a/selfdrive/frogpilot/ui/qt/offroad/advanced_visual_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/advanced_visual_settings.cc deleted file mode 100644 index c66d0c6e274210..00000000000000 --- a/selfdrive/frogpilot/ui/qt/offroad/advanced_visual_settings.cc +++ /dev/null @@ -1,226 +0,0 @@ -#include "selfdrive/frogpilot/ui/qt/offroad/advanced_visual_settings.h" - -FrogPilotAdvancedVisualsPanel::FrogPilotAdvancedVisualsPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { - const std::vector> advancedToggles { - {"AdvancedCustomUI", tr("Advanced Onroad UI Widgets"), tr("Advanced user customizations for the Onroad UI."), "../frogpilot/assets/toggle_icons/icon_advanced_road.png"}, - {"CameraView", tr("Camera View"), tr("Camera view for the onroad UI. This is purely a visual change and doesn't impact how openpilot drives."), ""}, - {"ShowStoppingPoint", tr("Display Stopping Points"), tr("Display an image on the screen where openpilot is detecting a potential red light/stop sign."), ""}, - {"HideLeadMarker", tr("Hide Lead Marker"), tr("Hide the marker for the vehicle ahead on the screen."), ""}, - {"HideSpeed", tr("Hide Speed"), tr("Hide the speed indicator in the onroad UI. Additional toggle allows it to be hidden/shown via tapping the speed itself."), ""}, - {"HideUIElements", tr("Hide UI Elements"), tr("Hide the selected UI elements from the onroad screen."), ""}, - {"WheelSpeed", tr("Use Wheel Speed"), tr("Use the wheel speed instead of the cluster speed in the onroad UI."), ""}, - - {"DeveloperUI", tr("Developer UI"), tr("Show detailed information about openpilot's internal operations."), "../frogpilot/assets/toggle_icons/icon_advanced_device.png"}, - {"BorderMetrics", tr("Border Metrics"), tr("Display performance metrics around the edge of the screen while driving."), ""}, - {"FPSCounter", tr("FPS Display"), tr("Display the 'Frames Per Second' (FPS) at the bottom of the screen while driving."), ""}, - {"LateralMetrics", tr("Lateral Metrics"), tr("Display metrics related to steering control at the top of the screen while driving."), ""}, - {"LongitudinalMetrics", tr("Longitudinal Metrics"), tr("Display metrics related to acceleration, speed, and desired following distance at the top of the screen while driving."), ""}, - {"NumericalTemp", tr("Numerical Temperature Gauge"), tr("Show exact temperature readings instead of general status labels like 'GOOD', 'OK', or 'HIGH' in the sidebar."), ""}, - {"SidebarMetrics", tr("Sidebar"), tr("Display system information like CPU, GPU, RAM usage, IP address, and storage space in the sidebar."), ""}, - {"UseSI", tr("Use International System of Units"), tr("Display measurements using the 'International System of Units' (SI)."), ""}, - - {"ModelUI", tr("Model UI"), tr("Customize the model visualizations on the screen."), "../frogpilot/assets/toggle_icons/icon_advanced_calibration.png"}, - {"LaneLinesWidth", tr("Lane Lines Width"), tr("How thick the lane lines appear on the display.\n\nDefault matches the MUTCD standard of 4 inches."), ""}, - {"PathEdgeWidth", tr("Path Edges Width"), tr("The width of the edges of the driving path to represent different modes and statuses.\n\nDefault is 20% of the total path width.\n\nColor Guide:\n- Blue: Navigation\n- Light Blue: 'Always On Lateral'\n- Green: Default\n- Orange: 'Experimental Mode'\n- Red: 'Traffic Mode'\n- Yellow: 'Conditional Experimental Mode' Overridden"), ""}, - {"PathWidth", tr("Path Width"), tr("How wide the driving path appears on your screen.\n\nDefault (6.1 feet / 1.9 meters) matches the width of a 2019 Lexus ES 350."), ""}, - {"RoadEdgesWidth", tr("Road Edges Width"), tr("How thick the road edges appear on the display.\n\nDefault matches half of the MUTCD standard lane line width of 4 inches."), ""}, - {"UnlimitedLength", tr("'Unlimited' Road UI"), tr("Extend the display of the path, lane lines, and road edges as far as the model can see."), ""}, - }; - - for (const auto &[param, title, desc, icon] : advancedToggles) { - AbstractControl *advancedVisualToggle; - - if (param == "AdvancedCustomUI") { - FrogPilotParamManageControl *advancedCustomUIToggle = new FrogPilotParamManageControl(param, title, desc, icon); - QObject::connect(advancedCustomUIToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - showToggles(advancedCustomOnroadUIKeys); - }); - advancedVisualToggle = advancedCustomUIToggle; - } else if (param == "CameraView") { - std::vector cameraOptions{tr("Auto"), tr("Driver"), tr("Standard"), tr("Wide")}; - ButtonParamControl *preferredCamera = new ButtonParamControl(param, title, desc, icon, cameraOptions); - advancedVisualToggle = preferredCamera; - } else if (param == "HideSpeed") { - std::vector hideSpeedToggles{"HideSpeedUI"}; - std::vector hideSpeedToggleNames{tr("Control Via UI")}; - advancedVisualToggle = new FrogPilotButtonToggleControl(param, title, desc, hideSpeedToggles, hideSpeedToggleNames); - } else if (param == "HideUIElements") { - std::vector uiElementsToggles{"HideAlerts", "HideMapIcon", "HideMaxSpeed"}; - std::vector uiElementsToggleNames{tr("Alerts"), tr("Map Icon"), tr("Max Speed")}; - advancedVisualToggle = new FrogPilotButtonToggleControl(param, title, desc, uiElementsToggles, uiElementsToggleNames); - } else if (param == "ShowStoppingPoint") { - std::vector stoppingPointToggles{"ShowStoppingPointMetrics"}; - std::vector stoppingPointToggleNames{tr("Show Distance")}; - advancedVisualToggle = new FrogPilotButtonToggleControl(param, title, desc, stoppingPointToggles, stoppingPointToggleNames); - - } else if (param == "DeveloperUI") { - FrogPilotParamManageControl *developerUIToggle = new FrogPilotParamManageControl(param, title, desc, icon); - QObject::connect(developerUIToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - borderMetricsBtn->setVisibleButton(0, hasBSM); - lateralMetricsBtn->setVisibleButton(1, hasAutoTune); - - std::set modifiedDeveloperUIKeys = developerUIKeys; - - if (disableOpenpilotLongitudinal || !hasOpenpilotLongitudinal) { - modifiedDeveloperUIKeys.erase("LongitudinalMetrics"); - } - - showToggles(modifiedDeveloperUIKeys); - }); - advancedVisualToggle = developerUIToggle; - } else if (param == "BorderMetrics") { - std::vector borderToggles{"BlindSpotMetrics", "ShowSteering", "SignalMetrics"}; - std::vector borderToggleNames{tr("Blind Spot"), tr("Steering Torque"), tr("Turn Signal")}; - borderMetricsBtn = new FrogPilotButtonToggleControl(param, title, desc, borderToggles, borderToggleNames); - advancedVisualToggle = borderMetricsBtn; - } else if (param == "LateralMetrics") { - std::vector lateralToggles{"AdjacentPathMetrics", "TuningInfo"}; - std::vector lateralToggleNames{tr("Adjacent Path Metrics"), tr("Auto Tune")}; - lateralMetricsBtn = new FrogPilotButtonToggleControl(param, title, desc, lateralToggles, lateralToggleNames); - advancedVisualToggle = lateralMetricsBtn; - } else if (param == "LongitudinalMetrics") { - std::vector longitudinalToggles{"LeadInfo", "JerkInfo"}; - std::vector longitudinalToggleNames{tr("Lead Info"), tr("Jerk Values")}; - longitudinalMetricsBtn = new FrogPilotButtonToggleControl(param, title, desc, longitudinalToggles, longitudinalToggleNames); - advancedVisualToggle = longitudinalMetricsBtn; - } else if (param == "NumericalTemp") { - std::vector temperatureToggles{"Fahrenheit"}; - std::vector temperatureToggleNames{tr("Fahrenheit")}; - advancedVisualToggle = new FrogPilotButtonToggleControl(param, title, desc, temperatureToggles, temperatureToggleNames); - } else if (param == "SidebarMetrics") { - std::vector sidebarMetricsToggles{"ShowCPU", "ShowGPU", "ShowIP", "ShowMemoryUsage", "ShowStorageLeft", "ShowStorageUsed"}; - std::vector sidebarMetricsToggleNames{tr("CPU"), tr("GPU"), tr("IP"), tr("RAM"), tr("SSD Left"), tr("SSD Used")}; - FrogPilotButtonToggleControl *sidebarMetricsToggle = new FrogPilotButtonToggleControl(param, title, desc, sidebarMetricsToggles, sidebarMetricsToggleNames, false, 150); - QObject::connect(sidebarMetricsToggle, &FrogPilotButtonToggleControl::buttonClicked, [this](int index) { - if (index == 0) { - params.putBool("ShowGPU", false); - } else if (index == 1) { - params.putBool("ShowCPU", false); - } else if (index == 3) { - params.putBool("ShowStorageLeft", false); - params.putBool("ShowStorageUsed", false); - } else if (index == 4) { - params.putBool("ShowMemoryUsage", false); - params.putBool("ShowStorageUsed", false); - } else if (index == 5) { - params.putBool("ShowMemoryUsage", false); - params.putBool("ShowStorageLeft", false); - } - }); - advancedVisualToggle = sidebarMetricsToggle; - - } else if (param == "ModelUI") { - FrogPilotParamManageControl *modelUIToggle = new FrogPilotParamManageControl(param, title, desc, icon); - QObject::connect(modelUIToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - std::set modifiedModelUIKeysKeys = modelUIKeys; - - if (disableOpenpilotLongitudinal || !hasOpenpilotLongitudinal) { - modifiedModelUIKeysKeys.erase("HideLeadMarker"); - } - - showToggles(modifiedModelUIKeysKeys); - }); - advancedVisualToggle = modelUIToggle; - } else if (param == "LaneLinesWidth" || param == "RoadEdgesWidth") { - advancedVisualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 24, tr(" inches")); - } else if (param == "PathEdgeWidth") { - advancedVisualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, tr("%")); - } else if (param == "PathWidth") { - advancedVisualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 10, tr(" feet"), std::map(), 0.1); - - } else { - advancedVisualToggle = new ParamControl(param, title, desc, icon); - } - - addItem(advancedVisualToggle); - toggles[param] = advancedVisualToggle; - - makeConnections(advancedVisualToggle); - - if (FrogPilotParamManageControl *frogPilotManageToggle = qobject_cast(advancedVisualToggle)) { - QObject::connect(frogPilotManageToggle, &FrogPilotParamManageControl::manageButtonClicked, this, &FrogPilotAdvancedVisualsPanel::openParentToggle); - } - - QObject::connect(advancedVisualToggle, &AbstractControl::showDescriptionEvent, [this]() { - update(); - }); - } - - QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotAdvancedVisualsPanel::hideToggles); - QObject::connect(parent, &FrogPilotSettingsWindow::updateCarToggles, this, &FrogPilotAdvancedVisualsPanel::updateCarToggles); - QObject::connect(parent, &FrogPilotSettingsWindow::updateMetric, this, &FrogPilotAdvancedVisualsPanel::updateMetric); - - updateMetric(); -} - -void FrogPilotAdvancedVisualsPanel::updateCarToggles() { - disableOpenpilotLongitudinal = parent->disableOpenpilotLongitudinal; - hasAutoTune = parent->hasAutoTune; - hasBSM = parent->hasBSM; - hasOpenpilotLongitudinal = parent->hasOpenpilotLongitudinal; - - hideToggles(); -} - -void FrogPilotAdvancedVisualsPanel::updateMetric() { - bool previousIsMetric = isMetric; - isMetric = params.getBool("IsMetric"); - - if (isMetric != previousIsMetric) { - double smallDistanceConversion = isMetric ? INCH_TO_CM : CM_TO_INCH; - double distanceConversion = isMetric ? FOOT_TO_METER : METER_TO_FOOT; - - params.putFloatNonBlocking("LaneLinesWidth", params.getFloat("LaneLinesWidth") * smallDistanceConversion); - params.putFloatNonBlocking("RoadEdgesWidth", params.getFloat("RoadEdgesWidth") * smallDistanceConversion); - - params.putFloatNonBlocking("PathWidth", params.getFloat("PathWidth") * distanceConversion); - } - - FrogPilotParamValueControl *laneLinesWidthToggle = static_cast(toggles["LaneLinesWidth"]); - FrogPilotParamValueControl *pathWidthToggle = static_cast(toggles["PathWidth"]); - FrogPilotParamValueControl *roadEdgesWidthToggle = static_cast(toggles["RoadEdgesWidth"]); - - if (isMetric) { - laneLinesWidthToggle->setDescription(tr("Adjust how thick the lane lines appear on the display.\n\nDefault matches the Vienna standard of 10 centimeters.")); - roadEdgesWidthToggle->setDescription(tr("Adjust how thick the road edges appear on the display.\n\nDefault matches half of the Vienna standard of 10 centimeters.")); - - laneLinesWidthToggle->updateControl(0, 60, tr(" centimeters")); - roadEdgesWidthToggle->updateControl(0, 60, tr(" centimeters")); - - pathWidthToggle->updateControl(0, 3, tr(" meters")); - } else { - laneLinesWidthToggle->setDescription(tr("Adjust how thick the lane lines appear on the display.\n\nDefault matches the MUTCD standard of 4 inches.")); - roadEdgesWidthToggle->setDescription(tr("Adjust how thick the road edges appear on the display.\n\nDefault matches half of the MUTCD standard of 4 inches.")); - - laneLinesWidthToggle->updateControl(0, 24, tr(" inches")); - roadEdgesWidthToggle->updateControl(0, 24, tr(" inches")); - - pathWidthToggle->updateControl(0, 10, tr(" feet")); - } -} - -void FrogPilotAdvancedVisualsPanel::showToggles(const std::set &keys) { - setUpdatesEnabled(false); - - for (auto &[key, toggle] : toggles) { - toggle->setVisible(keys.find(key) != keys.end()); - } - - setUpdatesEnabled(true); - update(); -} - -void FrogPilotAdvancedVisualsPanel::hideToggles() { - setUpdatesEnabled(false); - - for (auto &[key, toggle] : toggles) { - bool subToggles = advancedCustomOnroadUIKeys.find(key) != advancedCustomOnroadUIKeys.end() || - developerUIKeys.find(key) != developerUIKeys.end() || - modelUIKeys.find(key) != modelUIKeys.end(); - - toggle->setVisible(!subToggles); - } - - setUpdatesEnabled(true); - update(); -} diff --git a/selfdrive/frogpilot/ui/qt/offroad/advanced_visual_settings.h b/selfdrive/frogpilot/ui/qt/offroad/advanced_visual_settings.h deleted file mode 100644 index 20ad990119e05a..00000000000000 --- a/selfdrive/frogpilot/ui/qt/offroad/advanced_visual_settings.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include - -#include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h" - -class FrogPilotAdvancedVisualsPanel : public FrogPilotListWidget { - Q_OBJECT - -public: - explicit FrogPilotAdvancedVisualsPanel(FrogPilotSettingsWindow *parent); - -signals: - void openParentToggle(); - -private: - void hideToggles(); - void showToggles(const std::set &keys); - void updateCarToggles(); - void updateMetric(); - - std::set advancedCustomOnroadUIKeys = { - "CameraView", "HideLeadMarker", "HideSpeed", - "HideUIElements", "ShowStoppingPoint", "WheelSpeed" - }; - - std::set developerUIKeys = { - "BorderMetrics", "FPSCounter", "LateralMetrics", - "LongitudinalMetrics", "NumericalTemp", - "SidebarMetrics", "UseSI" - }; - - std::set modelUIKeys = { - "LaneLinesWidth", "PathEdgeWidth", "PathWidth", - "RoadEdgesWidth", "UnlimitedLength" - }; - - FrogPilotButtonToggleControl *borderMetricsBtn; - FrogPilotButtonToggleControl *lateralMetricsBtn; - FrogPilotButtonToggleControl *longitudinalMetricsBtn; - - FrogPilotSettingsWindow *parent; - - Params params; - - bool disableOpenpilotLongitudinal; - bool hasAutoTune; - bool hasBSM; - bool hasOpenpilotLongitudinal; - bool isMetric = params.getBool("IsMetric"); - - std::map toggles; -}; diff --git a/selfdrive/frogpilot/ui/qt/offroad/data_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/data_settings.cc index 930e82c9d9d455..dbc1d42fcde21c 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/data_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/data_settings.cc @@ -3,7 +3,7 @@ #include "selfdrive/frogpilot/ui/qt/offroad/data_settings.h" FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent) { - ButtonControl *deleteDrivingDataBtn = new ButtonControl(tr("Delete Driving Footage and Data"), tr("DELETE"), tr("This button provides a swift and secure way to permanently delete all stored driving footage and data from your device. Ideal for maintaining privacy or freeing up space.")); + ButtonControl *deleteDrivingDataBtn = new ButtonControl(tr("Delete Driving Footage and Data"), tr("DELETE"), tr("Permanently deletes all stored driving footage and data from your device. Ideal for maintaining privacy or freeing up space.")); QObject::connect(deleteDrivingDataBtn, &ButtonControl::clicked, [=]() { if (ConfirmationDialog::confirm(tr("Are you sure you want to permanently delete all of your driving footage and data?"), tr("Delete"), this)) { std::thread([=] { diff --git a/selfdrive/frogpilot/ui/qt/offroad/device_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/device_settings.cc index f74da2bf45166a..40e0ac123503e2 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/device_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/device_settings.cc @@ -1,21 +1,21 @@ #include "selfdrive/frogpilot/ui/qt/offroad/device_settings.h" -FrogPilotDevicePanel::FrogPilotDevicePanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent) { +FrogPilotDevicePanel::FrogPilotDevicePanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { const std::vector> deviceToggles { {"DeviceManagement", tr("Device Settings"), tr("Device behavior settings."), "../frogpilot/assets/toggle_icons/icon_device.png"}, - {"DeviceShutdown", tr("Device Shutdown Timer"), tr("How long the device stays on after you stop driving."), ""}, - {"OfflineMode", tr("Disable Internet Requirement"), tr("The device can work without an internet connection for as long as you need."), ""}, - {"IncreaseThermalLimits", tr("Increase Thermal Safety Limit"), tr("The device can run at higher temperatures than recommended."), ""}, - {"LowVoltageShutdown", tr("Low Battery Shutdown Threshold"), tr("Shut down the device when the car's battery gets too low to prevent damage to the 12V battery."), ""}, - {"NoLogging", tr("Turn Off Data Tracking"), tr("Disable all tracking to improve privacy."), ""}, - {"NoUploads", tr("Turn Off Data Uploads"), tr("Stop the device from sending any data to the servers."), ""}, + {"DeviceShutdown", tr("Device Shutdown Timer"), tr("Controls how long the device stays on after you stop driving."), ""}, + {"OfflineMode", tr("Disable Internet Requirement"), tr("Allows the device to work without an internet connection."), ""}, + {"IncreaseThermalLimits", tr("Increase Thermal Safety Limit"), tr("Allows the device to run at higher temperatures than recommended."), ""}, + {"LowVoltageShutdown", tr("Low Battery Shutdown Threshold"), tr("Manages the threshold for shutting down the device to protect the car's battery from excessive drain and potential damage."), ""}, + {"NoLogging", tr("Turn Off Data Tracking"), tr("Disables all data tracking to improve privacy."), ""}, + {"NoUploads", tr("Turn Off Data Uploads"), tr("Stops the device from sending any data to the servers."), ""}, {"ScreenManagement", tr("Screen Settings"), tr("Screen behavior settings."), "../frogpilot/assets/toggle_icons/icon_light.png"}, - {"ScreenBrightness", tr("Screen Brightness (Offroad)"), tr("The screen brightness when you're not driving."), ""}, - {"ScreenBrightnessOnroad", tr("Screen Brightness (Onroad)"), tr("The screen brightness while you're driving."), ""}, - {"ScreenRecorder", tr("Screen Recorder"), tr("Display a button in the onroad UI to record the screen."), ""}, - {"ScreenTimeout", tr("Screen Timeout (Offroad)"), tr("How long it takes for the screen to turn off when you're not driving."), ""}, - {"ScreenTimeoutOnroad", tr("Screen Timeout (Onroad)"), tr("How long it takes for the screen to turn off while you're driving."), ""} + {"ScreenBrightness", tr("Screen Brightness (Offroad)"), tr("Controls the screen brightness when you're not driving."), ""}, + {"ScreenBrightnessOnroad", tr("Screen Brightness (Onroad)"), tr("Controls the screen brightness while you're driving."), ""}, + {"ScreenRecorder", tr("Screen Recorder"), tr("Enables a button in the onroad UI to record the screen."), ""}, + {"ScreenTimeout", tr("Screen Timeout (Offroad)"), tr("Controls how long it takes for the screen to turn off when you're not driving."), ""}, + {"ScreenTimeoutOnroad", tr("Screen Timeout (Onroad)"), tr("Controls how long it takes for the screen to turn off while you're driving."), ""} }; for (const auto &[param, title, desc, icon] : deviceToggles) { @@ -24,7 +24,17 @@ FrogPilotDevicePanel::FrogPilotDevicePanel(FrogPilotSettingsWindow *parent) : Fr if (param == "DeviceManagement") { FrogPilotParamManageControl *deviceManagementToggle = new FrogPilotParamManageControl(param, title, desc, icon); QObject::connect(deviceManagementToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - showToggles(deviceManagementKeys); + std::set modifiedDeviceManagementKeys = deviceManagementKeys; + + if (customizationLevel != 2) { + modifiedDeviceManagementKeys.erase("IncreaseThermalLimits"); + modifiedDeviceManagementKeys.erase("LowVoltageShutdown"); + modifiedDeviceManagementKeys.erase("NoLogging"); + modifiedDeviceManagementKeys.erase("NoUploads"); + modifiedDeviceManagementKeys.erase("OfflineMode"); + } + + showToggles(modifiedDeviceManagementKeys); }); deviceToggle = deviceManagementToggle; } else if (param == "DeviceShutdown") { @@ -43,14 +53,16 @@ FrogPilotDevicePanel::FrogPilotDevicePanel(FrogPilotSettingsWindow *parent) : Fr } else if (param == "ScreenManagement") { FrogPilotParamManageControl *screenToggle = new FrogPilotParamManageControl(param, title, desc, icon); QObject::connect(screenToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - showToggles(screenKeys); + std::set modifiedScreenKeys = screenKeys; + + showToggles(modifiedScreenKeys); }); deviceToggle = screenToggle; } else if (param == "ScreenBrightness" || param == "ScreenBrightnessOnroad") { std::map brightnessLabels; int minBrightness = (param == "ScreenBrightnessOnroad") ? 0 : 1; - for (int i = 1; i <= 101; ++i) { - brightnessLabels[i] = (i == 101) ? tr("Auto") : QString::number(i) + "%"; + for (int i = 0; i <= 101; ++i) { + brightnessLabels[i] = i == 101 ? tr("Auto") : i == 0 ? tr("Screen Off") : QString::number(i) + "%"; } deviceToggle = new FrogPilotParamValueControl(param, title, desc, icon, minBrightness, 101, QString(), brightnessLabels, 1, false, true); } else if (param == "ScreenTimeout" || param == "ScreenTimeoutOnroad") { @@ -118,6 +130,12 @@ FrogPilotDevicePanel::FrogPilotDevicePanel(FrogPilotSettingsWindow *parent) : Fr hideToggles(); } +void FrogPilotDevicePanel::showEvent(QShowEvent *event) { + customizationLevel = parent->customizationLevel; + + toggles["ScreenManagement"]->setVisible(customizationLevel == 2); +} + void FrogPilotDevicePanel::updateState(const UIState &s) { started = s.scene.started; } @@ -142,6 +160,8 @@ void FrogPilotDevicePanel::hideToggles() { toggle->setVisible(!subToggles); } + toggles["ScreenManagement"]->setVisible(customizationLevel == 2); + setUpdatesEnabled(true); update(); } diff --git a/selfdrive/frogpilot/ui/qt/offroad/device_settings.h b/selfdrive/frogpilot/ui/qt/offroad/device_settings.h index d8d552b3322402..641335206e581e 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/device_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/device_settings.h @@ -15,6 +15,7 @@ class FrogPilotDevicePanel : public FrogPilotListWidget { private: void hideToggles(); + void showEvent(QShowEvent *event) override; void showToggles(const std::set &keys); void updateState(const UIState &s); @@ -28,9 +29,13 @@ class FrogPilotDevicePanel : public FrogPilotListWidget { "ScreenTimeout", "ScreenTimeoutOnroad" }; + FrogPilotSettingsWindow *parent; + Params params; bool started; + int customizationLevel; + std::map toggles; }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.cc index 213a763c10fe58..7577aff6c7d421 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.cc @@ -5,13 +5,12 @@ #include "selfdrive/frogpilot/navigation/ui/maps_settings.h" #include "selfdrive/frogpilot/navigation/ui/primeless_settings.h" -#include "selfdrive/frogpilot/ui/qt/offroad/advanced_driving_settings.h" -#include "selfdrive/frogpilot/ui/qt/offroad/advanced_visual_settings.h" #include "selfdrive/frogpilot/ui/qt/offroad/data_settings.h" #include "selfdrive/frogpilot/ui/qt/offroad/device_settings.h" #include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h" #include "selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h" #include "selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h" +#include "selfdrive/frogpilot/ui/qt/offroad/model_settings.h" #include "selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h" #include "selfdrive/frogpilot/ui/qt/offroad/theme_settings.h" #include "selfdrive/frogpilot/ui/qt/offroad/utilities.h" @@ -45,13 +44,15 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram FrogPilotListWidget *list = new FrogPilotListWidget(frogpilotSettingsWidget); - FrogPilotAdvancedDrivingPanel *frogpilotAdvancedDrivingPanel = new FrogPilotAdvancedDrivingPanel(this); - QObject::connect(frogpilotAdvancedDrivingPanel, &FrogPilotAdvancedDrivingPanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle); - QObject::connect(frogpilotAdvancedDrivingPanel, &FrogPilotAdvancedDrivingPanel::openSubParentToggle, this, &FrogPilotSettingsWindow::openSubParentToggle); - QObject::connect(frogpilotAdvancedDrivingPanel, &FrogPilotAdvancedDrivingPanel::openSubSubParentToggle, this, &FrogPilotSettingsWindow::openSubSubParentToggle); - - FrogPilotAdvancedVisualsPanel *frogpilotAdvancedVisualsPanel = new FrogPilotAdvancedVisualsPanel(this); - QObject::connect(frogpilotAdvancedVisualsPanel, &FrogPilotAdvancedVisualsPanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle); + std::vector toggle_presets{tr("Basic"), tr("Standard"), tr("Advanced")}; + ButtonParamControl *toggle_preset = new ButtonParamControl("CustomizationLevel", tr("Customization Level"), + tr("Choose your preferred customization level. 'Standard' is recommended for most users, offering a balanced experience and automatically managing more 'Advanced' features," + " while 'Basic' is designed for those new to customization or seeking simplicity."), + "../frogpilot/assets/toggle_icons/icon_customization.png", + toggle_presets); + QObject::connect(toggle_preset, &ButtonParamControl::buttonClicked, this, &FrogPilotSettingsWindow::updatePanelVisibility); + QObject::connect(toggle_preset, &ButtonParamControl::buttonClicked, this, &updateFrogPilotToggles); + list->addItem(toggle_preset); FrogPilotDevicePanel *frogpilotDevicePanel = new FrogPilotDevicePanel(this); QObject::connect(frogpilotDevicePanel, &FrogPilotDevicePanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle); @@ -63,10 +64,15 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram QObject::connect(frogpilotLongitudinalPanel, &FrogPilotLongitudinalPanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle); QObject::connect(frogpilotLongitudinalPanel, &FrogPilotLongitudinalPanel::openSubParentToggle, this, &FrogPilotSettingsWindow::openSubParentToggle); + FrogPilotModelPanel *frogpilotModelPanel = new FrogPilotModelPanel(this); + QObject::connect(frogpilotModelPanel, &FrogPilotModelPanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle); + QObject::connect(frogpilotModelPanel, &FrogPilotModelPanel::openSubParentToggle, this, &FrogPilotSettingsWindow::openSubParentToggle); + FrogPilotMapsPanel *frogpilotMapsPanel = new FrogPilotMapsPanel(this); QObject::connect(frogpilotMapsPanel, &FrogPilotMapsPanel::openMapSelection, this, &FrogPilotSettingsWindow::openMapSelection); FrogPilotPrimelessPanel *frogpilotPrimelessPanel = new FrogPilotPrimelessPanel(this); + QObject::connect(frogpilotPrimelessPanel, &FrogPilotPrimelessPanel::closeMapBoxInstructions, this, &FrogPilotSettingsWindow::closeMapBoxInstructions); QObject::connect(frogpilotPrimelessPanel, &FrogPilotPrimelessPanel::openMapBoxInstructions, this, &FrogPilotSettingsWindow::openMapBoxInstructions); FrogPilotSoundsPanel *frogpilotSoundsPanel = new FrogPilotSoundsPanel(this); @@ -79,9 +85,8 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram QObject::connect(frogpilotVisualsPanel, &FrogPilotVisualsPanel::openParentToggle, this, &FrogPilotSettingsWindow::openParentToggle); std::vector>> panels = { - {tr("Advanced Settings"), {frogpilotAdvancedDrivingPanel, frogpilotAdvancedVisualsPanel}}, {tr("Alerts and Sounds"), {frogpilotSoundsPanel}}, - {tr("Driving Controls"), {frogpilotLongitudinalPanel, frogpilotLateralPanel}}, + {tr("Driving Controls"), {frogpilotModelPanel, frogpilotLongitudinalPanel, frogpilotLateralPanel}}, {tr("Navigation"), {frogpilotMapsPanel, frogpilotPrimelessPanel}}, {tr("System Management"), {new FrogPilotDataPanel(this), frogpilotDevicePanel, new UtilitiesPanel(this)}}, {tr("Theme and Appearance"), {frogpilotVisualsPanel, frogpilotThemesPanel}}, @@ -89,7 +94,6 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram }; std::vector icons = { - "../frogpilot/assets/toggle_icons/icon_advanced.png", "../frogpilot/assets/toggle_icons/icon_sound.png", "../frogpilot/assets/toggle_icons/icon_steering.png", "../frogpilot/assets/toggle_icons/icon_map.png", @@ -99,7 +103,6 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram }; std::vector descriptions = { - tr("Advanced FrogPilot features for more experienced users."), tr("Options to customize FrogPilot's sound alerts and notifications."), tr("FrogPilot features that impact acceleration, braking, and steering."), tr("Offline maps downloader and 'Navigate On openpilot (NOO)' settings."), @@ -109,9 +112,8 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram }; std::vector> buttonLabels = { - {tr("DRIVING"), tr("VISUALS")}, {tr("MANAGE")}, - {tr("GAS / BRAKE"), tr("STEERING")}, + {tr("DRIVING MODEL"), tr("GAS / BRAKE"), tr("STEERING")}, {tr("OFFLINE MAPS"), tr("PRIMELESS NAVIGATION")}, {tr("DATA"), tr("DEVICE"), tr("UTILITIES")}, {tr("APPEARANCE"), tr("THEME")}, @@ -119,7 +121,7 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram }; for (size_t i = 0; i < panels.size(); ++i) { - addPanelControl(list, panels[i].first, descriptions[i], buttonLabels[i], icons[i], panels[i].second, panels[i].first == tr("Driving Controls"), panels[i].first == tr("Navigation")); + addPanelControl(list, panels[i].first, descriptions[i], buttonLabels[i], icons[i], panels[i].second, panels[i].first); } frogpilotSettingsLayout->addWidget(new ScrollView(list, frogpilotSettingsWidget)); @@ -133,11 +135,8 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram QObject::connect(parent, &SettingsWindow::closePanel, this, &FrogPilotSettingsWindow::closePanel); QObject::connect(parent, &SettingsWindow::closeParentToggle, this, &FrogPilotSettingsWindow::closeParentToggle); QObject::connect(parent, &SettingsWindow::closeSubParentToggle, this, &FrogPilotSettingsWindow::closeSubParentToggle); - QObject::connect(parent, &SettingsWindow::closeSubSubParentToggle, this, &FrogPilotSettingsWindow::closeSubSubParentToggle); QObject::connect(parent, &SettingsWindow::updateMetric, this, &FrogPilotSettingsWindow::updateMetric); QObject::connect(uiState(), &UIState::offroadTransition, this, &FrogPilotSettingsWindow::updateCarVariables); - - updateCarVariables(); } void FrogPilotSettingsWindow::showEvent(QShowEvent *event) { @@ -152,10 +151,18 @@ void FrogPilotSettingsWindow::closePanel() { } void FrogPilotSettingsWindow::updatePanelVisibility() { + customizationLevel = params.getInt("CustomizationLevel"); disableOpenpilotLongitudinal = params.getBool("DisableOpenpilotLongitudinal"); - drivingButton->setVisibleButton(0, hasOpenpilotLongitudinal && !disableOpenpilotLongitudinal); + if ((hasOpenpilotLongitudinal && !disableOpenpilotLongitudinal) || customizationLevel != 0) { + drivingButton->setVisibleButton(0, customizationLevel == 2); + drivingButton->setVisibleButton(1, hasOpenpilotLongitudinal && !disableOpenpilotLongitudinal); + drivingButton->setVisibleButton(2, customizationLevel != 0); + } else { + drivingButton->setVisible(false); + } navigationButton->setVisibleButton(1, !uiState()->hasPrime()); + systemButton->setVisibleButton(1, customizationLevel != 0); mainLayout->setCurrentWidget(frogpilotSettingsWidget); } @@ -183,7 +190,9 @@ void FrogPilotSettingsWindow::updateCarVariables() { hasNNFFLog = checkNNFFLogFileExists(carFingerprint); hasOpenpilotLongitudinal = hasLongitudinalControl(CP); hasPCMCruise = CP.getPcmCruise(); + hasRadar = !CP.getRadarUnavailable(); hasSNG = CP.getMinEnableSpeed() <= 0; + isBolt = carFingerprint == "CHEVROLET_BOLT_CC" || carFingerprint == "CHEVROLET_BOLT_EUV"; isGM = carModel == "gm"; isGMPCMCruise = CP.getCarName() == "gm" && CP.getPcmCruise(); isHKGCanFd = carModel == "hyundai" && safetyModel == cereal::CarParams::SafetyModel::HYUNDAI_CANFD; @@ -236,17 +245,21 @@ void FrogPilotSettingsWindow::updateCarVariables() { hasNNFFLog = true; hasOpenpilotLongitudinal = true; hasPCMCruise = true; + hasRadar = true; hasSNG = false; + isGM = true; isGMPCMCruise = false; isHKGCanFd = true; isImpreza = true; - isSubaru = false; + isPIDCar = false; + isSubaru = true; isToyota = true; + isToyotaTuneSupported = true; isVolt = true; } std::string liveTorqueParamsKey; - if (params.getBool("ModelManagement")) { + if (customizationLevel == 2) { QString model = QString::fromStdString(params.get("ModelName")); QString part_model_param = processModelName(model); liveTorqueParamsKey = part_model_param.toStdString() + "LiveTorqueParameters"; @@ -255,13 +268,13 @@ void FrogPilotSettingsWindow::updateCarVariables() { } if (params.checkKey(liveTorqueParamsKey)) { - auto torqueParams = params.get(liveTorqueParamsKey); + std::string torqueParams = params.get(liveTorqueParamsKey); if (!torqueParams.empty()) { AlignedBuffer aligned_buf; capnp::FlatArrayMessageReader cmsg(aligned_buf.align(torqueParams.data(), torqueParams.size())); cereal::Event::Reader LTP = cmsg.getRoot(); - auto liveTorqueParams = LTP.getLiveTorqueParameters(); + cereal::LiveTorqueParametersData::Reader liveTorqueParams = LTP.getLiveTorqueParameters(); liveValid = liveTorqueParams.getLiveValid(); } else { @@ -274,7 +287,7 @@ void FrogPilotSettingsWindow::updateCarVariables() { emit updateCarToggles(); } -void FrogPilotSettingsWindow::addPanelControl(FrogPilotListWidget *list, QString &title, QString &desc, std::vector &button_labels, QString &icon, std::vector &panels, bool isDrivingPanel, bool isNavigationPanel) { +void FrogPilotSettingsWindow::addPanelControl(FrogPilotListWidget *list, QString &title, QString &desc, std::vector &button_labels, QString &icon, std::vector &panels, QString ¤tPanel) { std::vector panelContainers; panelContainers.reserve(panels.size()); @@ -287,12 +300,15 @@ void FrogPilotSettingsWindow::addPanelControl(FrogPilotListWidget *list, QString } FrogPilotButtonsControl *button; - if (isDrivingPanel) { + if (currentPanel == tr("Driving Controls")) { drivingButton = new FrogPilotButtonsControl(title, desc, button_labels, false, true, icon); button = drivingButton; - } else if (isNavigationPanel) { + } else if (currentPanel == tr("Navigation")) { navigationButton = new FrogPilotButtonsControl(title, desc, button_labels, false, true, icon); button = navigationButton; + } else if (currentPanel == tr("System Management")) { + systemButton = new FrogPilotButtonsControl(title, desc, button_labels, false, true, icon); + button = systemButton; } else { button = new FrogPilotButtonsControl(title, desc, button_labels, false, true, icon); } diff --git a/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h b/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h index be87b1547f8f3b..90e384eeb82636 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h @@ -16,7 +16,9 @@ class FrogPilotSettingsWindow : public QFrame { bool hasNNFFLog; bool hasOpenpilotLongitudinal; bool hasPCMCruise; + bool hasRadar; bool hasSNG; + bool isBolt; bool isGM; bool isGMPCMCruise; bool isHKGCanFd; @@ -34,23 +36,23 @@ class FrogPilotSettingsWindow : public QFrame { float steerLatAccelStock; float steerRatioStock; + int customizationLevel; + signals: void closeMapBoxInstructions(); void closeMapSelection(); void closeParentToggle(); void closeSubParentToggle(); - void closeSubSubParentToggle(); void openMapBoxInstructions(); void openMapSelection(); void openPanel(); void openParentToggle(); void openSubParentToggle(); - void openSubSubParentToggle(); void updateCarToggles(); void updateMetric(); private: - void addPanelControl(FrogPilotListWidget *list, QString &title, QString &desc, std::vector &button_labels, QString &icon, std::vector &panels, bool isDrivingPanel, bool isNavigationPanel); + void addPanelControl(FrogPilotListWidget *list, QString &title, QString &desc, std::vector &button_labels, QString &icon, std::vector &panels, QString ¤tPanel); void closePanel(); void showEvent(QShowEvent *event) override; void updateCarVariables(); @@ -58,6 +60,7 @@ class FrogPilotSettingsWindow : public QFrame { FrogPilotButtonsControl *drivingButton; FrogPilotButtonsControl *navigationButton; + FrogPilotButtonsControl *systemButton; Params params; diff --git a/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.cc index c6040a9119dc33..2042570cef0507 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.cc @@ -2,22 +2,32 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { const std::vector> lateralToggles { + {"AdvancedLateralTune", tr("Advanced Lateral Tuning"), tr("Advanced settings for fine tuning openpilot's lateral controls."), "../frogpilot/assets/toggle_icons/icon_advanced_lateral_tune.png"}, + {"SteerFriction", steerFrictionStock != 0 ? QString(tr("Friction (Default: %1)")).arg(QString::number(steerFrictionStock, 'f', 2)) : tr("Friction"), tr("Adjusts the resistance in steering. Higher values provide more stable steering but can make it feel heavy, while lower values allow lighter steering but may feel too sensitive."), ""}, + {"SteerKP", steerKPStock != 0 ? QString(tr("Kp Factor (Default: %1)")).arg(QString::number(steerKPStock, 'f', 2)) : tr("Kp Factor"), tr("Adjusts how aggressively the car corrects its steering. Higher values offer quicker corrections but may feel jerky, while lower values make steering smoother but slower to respond."), ""}, + {"SteerLatAccel", steerLatAccelStock != 0 ? QString(tr("Lateral Accel (Default: %1)")).arg(QString::number(steerLatAccelStock, 'f', 2)) : tr("Lateral Accel"), tr("Adjusts how fast the car can steer from side to side. Higher values allow quicker lane changes but can feel unstable, while lower values provide smoother steering but may feel sluggish."), ""}, + {"SteerRatio", steerRatioStock != 0 ? QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(steerRatioStock, 'f', 2)) : tr("Steer Ratio"), tr("Adjusts how much openpilot needs to turn the wheel to steer. Higher values feel like driving a truck, more stable at high speeds, but harder to steer quickly at low speeds, while lower values feel like a go-kart, easier to steer in tight spots but more sensitive and less stable at high speeds."), ""}, + {"ForceAutoTune", tr("Force Auto Tune On"), tr("Forces comma's auto lateral tuning for unsupported vehicles."), ""}, + {"ForceAutoTuneOff", tr("Force Auto Tune Off"), tr("Forces comma's auto lateral tuning off for supported vehicles."), ""}, + {"AlwaysOnLateral", tr("Always on Lateral"), tr("openpilot's steering control stays active even when the brake or gas pedals are pressed.\n\nDeactivate only occurs with the 'Cruise Control' button."), "../frogpilot/assets/toggle_icons/icon_always_on_lateral.png"}, - {"AlwaysOnLateralLKAS", tr("Control with LKAS Button"), tr("'Always on Lateral' gets turned on or off using the 'LKAS' button."), ""}, - {"AlwaysOnLateralMain", tr("Enable with Cruise Control"), tr("'Always on Lateral' gets turned on by pressing the 'Cruise Control' button bypassing the requirement to enable openpilot first."), ""}, - {"PauseAOLOnBrake", tr("Pause on Brake Below"), tr("'Always on Lateral' pauses when the brake pedal is pressed below the set speed."), ""}, - {"HideAOLStatusBar", tr("Hide the Status Bar"), tr("The status bar for 'Always on Lateral' is hidden."), ""}, + {"AlwaysOnLateralLKAS", tr("Control with LKAS Button"), tr("Controls the current state of 'Always on Lateral' with the 'LKAS' button."), ""}, + {"AlwaysOnLateralMain", tr("Enable with Cruise Control"), tr("Activates 'Always on Lateral' whenever 'Cruise Control' is active bypassing the requirement to enable openpilot first."), ""}, + {"PauseAOLOnBrake", tr("Pause on Brake Below"), tr("Pauses 'Always on Lateral' when the brake pedal is pressed below the set speed."), ""}, + {"HideAOLStatusBar", tr("Hide the Status Bar"), tr("Hides status bar for 'Always On Lateral'."), ""}, {"LaneChangeCustomizations", tr("Lane Change Settings"), tr("How openpilot handles lane changes."), "../frogpilot/assets/toggle_icons/icon_lane.png"}, - {"NudgelessLaneChange", tr("Hands-Free Lane Change"), tr("Lane changes are conducted without needing to touch the steering wheel upon turn signal activation."), ""}, - {"LaneChangeTime", tr("Lane Change Delay"), tr("How long openpilot waits before changing lanes."), ""}, - {"LaneDetectionWidth", tr("Lane Width Requirement"), tr("The minimum lane width for openpilot to detect a lane as a lane."), ""}, - {"MinimumLaneChangeSpeed", tr("Minimum Speed for Lane Change"), tr("The minimum speed required for openpilot to perform a lane change."), ""}, - {"OneLaneChange", tr("Single Lane Change Per Signal"), tr("Lane changes are limited to one per turn signal activation."), ""}, - - {"LateralTune", tr("Lateral Tuning"), tr("Settings that control how openpilot manages steering."), "../frogpilot/assets/toggle_icons/icon_lateral_tune.png"}, - {"NNFF", tr("Neural Network Feedforward (NNFF)"), tr("Twilsonco's 'Neural Network FeedForward' for more precise steering control."), ""}, - {"NNFFLite", tr("Smooth Curve Handling"), tr("Smoother steering when entering and exiting curves with Twilsonco's torque adjustments."), ""}, + {"NudgelessLaneChange", tr("Automatic Lane Changes"), tr("Conducts lane changes without needing to touch the steering wheel upon turn signal activation."), ""}, + {"LaneChangeTime", tr("Lane Change Delay"), tr("Delays lane changes by the set time to prevent sudden changes."), ""}, + {"LaneDetectionWidth", tr("Lane Width Requirement"), tr("Sets the minimum lane width for openpilot to detect a lane as a lane."), ""}, + {"MinimumLaneChangeSpeed", tr("Minimum Speed for Lane Change"), tr("Sets the minimum speed required for openpilot to perform a lane change."), ""}, + {"OneLaneChange", tr("Only One Lane Change Per Signal"), tr("Limits lane changes to one per turn signal activation."), ""}, + + {"LateralTune", tr("Lateral Tuning"), tr("Settings for fine tuning openpilot's lateral controls."), "../frogpilot/assets/toggle_icons/icon_lateral_tune.png"}, + {"TacoTune", tr("comma's 2022 Taco Bell Turn Hack"), tr("Uses comma's hack they used to help handle left and right turns more precisely during their 2022 'Taco Bell' drive."), ""}, + {"TurnDesires", tr("Force Turn Desires Below Lane Change Speed"), tr("Forces the model to use turn desires when driving below the minimum lane change speed to help make left and right turns more precisely."), ""}, + {"NNFF", tr("Neural Network Feedforward (NNFF)"), tr("Uses Twilsonco's 'Neural Network FeedForward' for more precise steering control."), ""}, + {"NNFFLite", tr("Smooth Curve Handling"), tr("Smoothens the steering control when entering and exiting curves by using Twilsonco's torque adjustments."), ""}, {"QOLLateral", tr("Quality of Life Improvements"), tr("Miscellaneous lateral focused features to improve your overall openpilot experience."), "../frogpilot/assets/toggle_icons/quality_of_life.png"}, {"PauseLateralSpeed", tr("Pause Steering Below"), tr("Pauses steering control when driving below the set speed."), ""} @@ -26,7 +36,50 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) : for (const auto &[param, title, desc, icon] : lateralToggles) { AbstractControl *lateralToggle; - if (param == "AlwaysOnLateral") { + if (param == "AdvancedLateralTune") { + FrogPilotParamManageControl *advancedLateralTuneToggle = new FrogPilotParamManageControl(param, title, desc, icon); + QObject::connect(advancedLateralTuneToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { + std::set modifiedAdvancedLateralTuneKeys = advancedLateralTuneKeys; + + bool usingNNFF = hasNNFFLog && params.getBool("LateralTune") && params.getBool("NNFF"); + if (usingNNFF) { + modifiedAdvancedLateralTuneKeys.erase("ForceAutoTune"); + modifiedAdvancedLateralTuneKeys.erase("ForceAutoTuneOff"); + } else { + if (hasAutoTune) { + modifiedAdvancedLateralTuneKeys.erase("ForceAutoTune"); + } else if (isPIDCar) { + modifiedAdvancedLateralTuneKeys.erase("ForceAutoTuneOff"); + modifiedAdvancedLateralTuneKeys.erase("SteerFriction"); + modifiedAdvancedLateralTuneKeys.erase("SteerKP"); + modifiedAdvancedLateralTuneKeys.erase("SteerLatAccel"); + } else { + modifiedAdvancedLateralTuneKeys.erase("ForceAutoTuneOff"); + } + } + + if (!liveValid || usingNNFF) { + modifiedAdvancedLateralTuneKeys.erase("SteerFriction"); + modifiedAdvancedLateralTuneKeys.erase("SteerLatAccel"); + } + + showToggles(modifiedAdvancedLateralTuneKeys); + }); + lateralToggle = advancedLateralTuneToggle; + } else if (param == "SteerFriction") { + std::vector steerFrictionToggleNames{"Reset"}; + lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0.01, 0.25, QString(), std::map(), 0.01, {}, steerFrictionToggleNames, false, false); + } else if (param == "SteerKP") { + std::vector steerKPToggleNames{"Reset"}; + lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, steerKPStock * 0.50, steerKPStock * 1.50, QString(), std::map(), 0.01, {}, steerKPToggleNames, false, false); + } else if (param == "SteerLatAccel") { + std::vector steerLatAccelToggleNames{"Reset"}; + lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, steerLatAccelStock * 0.25, steerLatAccelStock * 1.25, QString(), std::map(), 0.01, {}, steerLatAccelToggleNames, false, false); + } else if (param == "SteerRatio") { + std::vector steerRatioToggleNames{"Reset"}; + lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, steerRatioStock * 0.75, steerRatioStock * 1.25, QString(), std::map(), 0.01, {}, steerRatioToggleNames, false, false); + + } else if (param == "AlwaysOnLateral") { FrogPilotParamManageControl *aolToggle = new FrogPilotParamManageControl(param, title, desc, icon); QObject::connect(aolToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { std::set modifiedAOLKeys = aolKeys; @@ -35,12 +88,37 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) : modifiedAOLKeys.erase("AlwaysOnLateralLKAS"); } + if (customizationLevel != 2) { + modifiedAOLKeys.erase("AlwaysOnLateralMain"); + modifiedAOLKeys.erase("HideAOLStatusBar"); + } + showToggles(modifiedAOLKeys); }); lateralToggle = aolToggle; } else if (param == "PauseAOLOnBrake") { lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, tr("mph")); + } else if (param == "LaneChangeCustomizations") { + FrogPilotParamManageControl *laneChangeToggle = new FrogPilotParamManageControl(param, title, desc, icon); + QObject::connect(laneChangeToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { + std::set modifiedLaneChangeKeys = laneChangeKeys; + + if (customizationLevel != 2) { + modifiedLaneChangeKeys.erase("LaneDetectionWidth"); + modifiedLaneChangeKeys.erase("MinimumLaneChangeSpeed"); + } + + showToggles(modifiedLaneChangeKeys); + }); + lateralToggle = laneChangeToggle; + } else if (param == "LaneChangeTime") { + lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 5, tr(" seconds"), {{0, "Instant"}, {10, "1.0 second"}}, 0.1); + } else if (param == "LaneDetectionWidth") { + lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 15, tr(" feet"), std::map(), 0.1); + } else if (param == "MinimumLaneChangeSpeed") { + lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, tr("mph")); + } else if (param == "LateralTune") { FrogPilotParamManageControl *lateralTuneToggle = new FrogPilotParamManageControl(param, title, desc, icon); QObject::connect(lateralTuneToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { @@ -53,6 +131,11 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) : modifiedLateralTuneKeys.erase("NNFFLite"); } + if (customizationLevel != 2) { + modifiedLateralTuneKeys.erase("TacoTune"); + modifiedLateralTuneKeys.erase("TurnDesires"); + } + showToggles(modifiedLateralTuneKeys); }); lateralToggle = lateralTuneToggle; @@ -66,27 +149,10 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) : } else if (param == "PauseLateralSpeed") { std::vector pauseLateralToggles{"PauseLateralOnSignal"}; std::vector pauseLateralToggleNames{"Turn Signal Only"}; - lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0, 99, tr("mph"), std::map(), 1, pauseLateralToggles, pauseLateralToggleNames); + lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0, 99, tr("mph"), std::map(), 1, pauseLateralToggles, pauseLateralToggleNames, true); } else if (param == "PauseLateralOnSignal") { lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, tr("mph")); - } else if (param == "LaneChangeCustomizations") { - FrogPilotParamManageControl *laneChangeToggle = new FrogPilotParamManageControl(param, title, desc, icon); - QObject::connect(laneChangeToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - showToggles(laneChangeKeys); - }); - lateralToggle = laneChangeToggle; - } else if (param == "LaneChangeTime") { - std::map laneChangeTimeLabels; - for (int i = 0; i <= 10; ++i) { - laneChangeTimeLabels[i] = i == 0 ? "Instant" : QString::number(i / 2.0) + " seconds"; - } - lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 10, QString(), laneChangeTimeLabels); - } else if (param == "LaneDetectionWidth") { - lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 15, tr(" feet"), std::map(), 0.1); - } else if (param == "MinimumLaneChangeSpeed") { - lateralToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, tr("mph")); - } else { lateralToggle = new ParamControl(param, title, desc, icon); } @@ -141,6 +207,34 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) : }); } + steerFrictionToggle = static_cast(toggles["SteerFriction"]); + QObject::connect(steerFrictionToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() { + params.putFloat("SteerFriction", steerFrictionStock); + steerFrictionToggle->refresh(); + updateFrogPilotToggles(); + }); + + steerKPToggle = static_cast(toggles["SteerKP"]); + QObject::connect(steerKPToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() { + params.putFloat("SteerKP", steerKPStock); + steerKPToggle->refresh(); + updateFrogPilotToggles(); + }); + + steerLatAccelToggle = static_cast(toggles["SteerLatAccel"]); + QObject::connect(steerLatAccelToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() { + params.putFloat("SteerLatAccel", steerLatAccelStock); + steerLatAccelToggle->refresh(); + updateFrogPilotToggles(); + }); + + steerRatioToggle = static_cast(toggles["SteerRatio"]); + QObject::connect(steerRatioToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this]() { + params.putFloat("SteerRatio", steerRatioStock); + steerRatioToggle->refresh(); + updateFrogPilotToggles(); + }); + QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotLateralPanel::hideToggles); QObject::connect(parent, &FrogPilotSettingsWindow::updateCarToggles, this, &FrogPilotLateralPanel::updateCarToggles); QObject::connect(parent, &FrogPilotSettingsWindow::updateMetric, this, &FrogPilotLateralPanel::updateMetric); @@ -149,10 +243,30 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) : updateMetric(); } +void FrogPilotLateralPanel::showEvent(QShowEvent *event) { + customizationLevel = parent->customizationLevel; + + toggles["AdvancedLateralTune"]->setVisible(customizationLevel == 2); +} + void FrogPilotLateralPanel::updateCarToggles() { hasAutoTune = parent->hasAutoTune; hasNNFFLog = parent->hasNNFFLog; + isPIDCar = parent->isPIDCar; isSubaru = parent->isSubaru; + liveValid = parent->liveValid; + steerFrictionStock = parent->steerFrictionStock; + steerKPStock = parent->steerKPStock; + steerLatAccelStock = parent->steerLatAccelStock; + steerRatioStock = parent->steerRatioStock; + + steerFrictionToggle->setTitle(QString(tr("Friction (Default: %1)")).arg(QString::number(steerFrictionStock, 'f', 2))); + steerKPToggle->setTitle(QString(tr("Kp Factor (Default: %1)")).arg(QString::number(steerKPStock, 'f', 2))); + steerKPToggle->updateControl(steerKPStock * 0.50, steerKPStock * 1.50); + steerLatAccelToggle->setTitle(QString(tr("Lateral Accel (Default: %1)")).arg(QString::number(steerLatAccelStock, 'f', 2))); + steerLatAccelToggle->updateControl(steerLatAccelStock * 0.75, steerLatAccelStock * 1.25); + steerRatioToggle->setTitle(QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(steerRatioStock, 'f', 2))); + steerRatioToggle->updateControl(steerRatioStock * 0.75, steerRatioStock * 1.25); hideToggles(); } @@ -214,7 +328,8 @@ void FrogPilotLateralPanel::hideToggles() { setUpdatesEnabled(false); for (auto &[key, toggle] : toggles) { - bool subToggles = aolKeys.find(key) != aolKeys.end() || + bool subToggles = advancedLateralTuneKeys.find(key) != advancedLateralTuneKeys.end() || + aolKeys.find(key) != aolKeys.end() || laneChangeKeys.find(key) != laneChangeKeys.end() || lateralTuneKeys.find(key) != lateralTuneKeys.end() || qolKeys.find(key) != qolKeys.end(); @@ -222,6 +337,8 @@ void FrogPilotLateralPanel::hideToggles() { toggle->setVisible(!subToggles); } + toggles["AdvancedLateralTune"]->setVisible(customizationLevel == 2); + setUpdatesEnabled(true); update(); } diff --git a/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h b/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h index 82d39415b2e3cc..e84845fa8a7cd3 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/lateral_settings.h @@ -15,30 +15,41 @@ class FrogPilotLateralPanel : public FrogPilotListWidget { private: void hideToggles(); + void showEvent(QShowEvent *event) override; void showToggles(const std::set &keys); void updateMetric(); void updateCarToggles(); void updateState(const UIState &s); + std::set advancedLateralTuneKeys = { + "ForceAutoTune", "ForceAutoTuneOff", "SteerFriction", + "SteerLatAccel", "SteerKP", "SteerRatio" + }; + std::set aolKeys = { - "AlwaysOnLateralLKAS", "AlwaysOnLateralMain", - "HideAOLStatusBar", "PauseAOLOnBrake" + "AlwaysOnLateralLKAS", "AlwaysOnLateralMain", "HideAOLStatusBar", + "PauseAOLOnBrake" }; std::set laneChangeKeys = { - "LaneChangeTime", "LaneDetectionWidth", - "MinimumLaneChangeSpeed", "NudgelessLaneChange", - "OneLaneChange" + "LaneChangeTime", "LaneDetectionWidth", "MinimumLaneChangeSpeed", + "NudgelessLaneChange", "OneLaneChange" }; std::set lateralTuneKeys = { - "NNFF", "NNFFLite" + "NNFF", "NNFFLite", "TacoTune", + "TurnDesires" }; std::set qolKeys = { "PauseLateralSpeed" }; + FrogPilotParamValueButtonControl *steerFrictionToggle; + FrogPilotParamValueButtonControl *steerLatAccelToggle; + FrogPilotParamValueButtonControl *steerKPToggle; + FrogPilotParamValueButtonControl *steerRatioToggle; + FrogPilotSettingsWindow *parent; Params params; @@ -46,8 +57,17 @@ class FrogPilotLateralPanel : public FrogPilotListWidget { bool hasAutoTune; bool hasNNFFLog; bool isMetric = params.getBool("IsMetric"); + bool isPIDCar; bool isSubaru; + bool liveValid; bool started; + float steerFrictionStock; + float steerLatAccelStock; + float steerKPStock; + float steerRatioStock; + + int customizationLevel; + std::map toggles; }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.cc index 2f85dd0d2d67df..a3897f785f39b1 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.cc @@ -2,68 +2,169 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { const std::vector> longitudinalToggles { - {"ConditionalExperimental", tr("Conditional Experimental Mode"), tr("Automatically switches to 'Experimental Mode' when specific conditions are met."), "../frogpilot/assets/toggle_icons/icon_conditional.png"}, - {"CESpeed", tr("Below"), tr("'Experimental Mode' is active when driving below the set speed without a lead vehicle."), ""}, - {"CECurves", tr("Curve Detected Ahead"), tr("'Experimental Mode' is active when a curve is detected in the road ahead."), ""}, - {"CELead", tr("Lead Detected Ahead"), tr("'Experimental Mode' is active when a slower or stopped vehicle is detected ahead."), ""}, - {"CENavigation", tr("Navigation Data"), tr("'Experimental Mode' is active based on navigation data, such as upcoming intersections or turns."), ""}, - {"CEModelStopTime", tr("openpilot Wants to Stop In"), tr("'Experimental Mode' is active when openpilot wants to stop such as for a stop sign or red light."), ""}, - {"CESignalSpeed", tr("Turn Signal Below"), tr("'Experimental Mode' is active when using turn signals below the set speed."), ""}, - {"HideCEMStatusBar", tr("Hide the Status Bar"), tr("The status bar for 'Conditional Experimental Mode' is hidden."), ""}, + {"ConditionalExperimental", tr("Conditional Experimental Mode"), tr("Automatically switch to 'Experimental Mode' when specific conditions are met."), "../frogpilot/assets/toggle_icons/icon_conditional.png"}, + {"CESpeed", tr("Below"), tr("Triggers 'Experimental Mode' when driving below the set speed without a lead vehicle."), ""}, + {"CECurves", tr("Curve Detected Ahead"), tr("Triggers 'Experimental Mode' when a curve is detected in the road ahead."), ""}, + {"CELead", tr("Lead Detected Ahead"), tr("Triggers 'Experimental Mode' when a slower or stopped vehicle is detected ahead."), ""}, + {"CENavigation", tr("Navigation Data"), tr("Triggers 'Experimental Mode' based on navigation data, such as upcoming intersections or turns."), ""}, + {"CEModelStopTime", tr("openpilot Wants to Stop In"), tr("Triggers 'Experimental Mode' when openpilot wants to stop such as for a stop sign or red light."), ""}, + {"CESignalSpeed", tr("Turn Signal Below"), tr("Triggers 'Experimental Mode' when using turn signals below the set speed."), ""}, + {"HideCEMStatusBar", tr("Hide the Status Bar"), tr("Hides status bar for 'Conditional Experimental Mode'."), ""}, {"CurveSpeedControl", tr("Curve Speed Control"), tr("Automatically slow down for curves detected ahead or through the downloaded maps."), "../frogpilot/assets/toggle_icons/icon_speed_map.png"}, - {"CurveDetectionMethod", tr("Curve Detection Method"), tr("The method used to detect curves."), ""}, - {"MTSCCurvatureCheck", tr("Curve Detection Failsafe"), tr("Curve control is triggered only when a curve is detected ahead. Use this as a failsafe to prevent false positives when using the 'Map Based' method."), ""}, - {"CurveSensitivity", tr("Curve Sensitivity"), tr("How sensitive openpilot is to detecting curves. Higher values trigger earlier responses at the risk of triggering too often, while lower values increase confidence at the risk of triggering too infrequently."), ""}, - {"TurnAggressiveness", tr("Turn Speed Aggressiveness"), tr("How aggressive openpilot takes turns. Higher values result in quicker turns, while lower values provide gentler turns."), ""}, + {"CurveDetectionMethod", tr("Curve Detection Method"), tr("Uses data from either the downloaded maps or the model to determine where curves are."), ""}, + {"MTSCCurvatureCheck", tr("Curve Detection Failsafe"), tr("Triggers 'Curve Speed Control' only when a curve is detected with the model as well when using the 'Map Based' method."), ""}, + {"CurveSensitivity", tr("Curve Detection Sensitivity"), tr("Controls how sensitive openpilot is to detecting curves. Higher values trigger earlier responses at the risk of triggering too often, while lower values increase confidence at the risk of triggering too infrequently."), ""}, + {"TurnAggressiveness", tr("Speed Aggressiveness"), tr("Controls how aggressive openpilot takes turns. Higher values result in faster turns, while lower values result in slower turns."), ""}, {"DisableCurveSpeedSmoothing", tr("Disable Speed Value Smoothing In the UI"), tr("Speed value smoothing is disabled in the UI to instead display the exact speed requested by the curve control."), ""}, - {"ExperimentalModeActivation", tr("Experimental Mode Activation"), tr("'Experimental Mode' is toggled off/on using the steering wheel buttons or the on-screen controls.\n\nThis overrides 'Conditional Experimental Mode'."), "../assets/img_experimental_white.svg"}, - {"ExperimentalModeViaLKAS", tr("Click the LKAS Button"), tr("'Experimental Mode' is toggled by pressing the 'LKAS' button on the steering wheel."), ""}, - {"ExperimentalModeViaTap", tr("Double-Tap the Screen"), tr("'Experimental Mode' is toggled by double-tapping the onroad UI within 0.5 seconds."), ""}, - {"ExperimentalModeViaDistance", tr("Long Press the Distance Button"), tr("'Experimental Mode' is toggled by holding the 'distance' button on the steering wheel for 0.5+ seconds."), ""}, + {"CustomPersonalities", tr("Customize Driving Personalities"), tr("Customize the personality profiles to suit your driving style."), "../frogpilot/assets/toggle_icons/icon_personality.png"}, + {"TrafficPersonalityProfile", tr("Traffic Personality"), tr("Customizes the 'Traffic' personality profile, tailored for navigating through traffic."), "../frogpilot/assets/stock_theme/distance_icons/traffic.png"}, + {"TrafficFollow", tr("Following Distance"), tr("Controls the minimum following distance in 'Traffic' mode. openpilot will automatically dynamically between this value and the 'Aggressive' profile distance based on your current speed."), ""}, + {"TrafficJerkAcceleration", tr("Acceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in acceleration in 'Traffic' mode. Higher values result in smoother, more gradual acceleration, while lower values allow for quicker, more responsive changes that may feel abrupt."), ""}, + {"TrafficJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in deceleration in 'Traffic' mode. Higher values result in smoother, more gradual deceleration, while lower values allow for quicker, more responsive changes that may feel abrupt."), ""}, + {"TrafficJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious openpilot is around other vehicles or obstacles in 'Traffic' mode. Higher values increase following distances and prioritize safety, leading to more cautious driving, while lower values allow for closer following but may reduce reaction time."), ""}, + {"TrafficJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly openpilot increases speed in 'Traffic' mode. Higher values ensure smoother, more gradual speed changes when accelerating, while lower values allow for quicker, more responsive changes that may feel abrupt."), ""}, + {"TrafficJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly openpilot adjusts to decreasing speeds in 'Traffic' mode. Higher values ensure smoother, more gradual speed changes when slowing down, while lower values allow for quicker, more responsive changes that may feel abrupt."), ""}, + {"ResetTrafficPersonality", tr("Reset Settings"), tr("Restores the 'Traffic Mode' settings to their default values."), ""}, + + {"AggressivePersonalityProfile", tr("Aggressive Personality"), tr("Customize the 'Aggressive' personality profile, designed for a more assertive driving style."), "../frogpilot/assets/stock_theme/distance_icons/aggressive.png"}, + {"AggressiveFollow", tr("Following Distance"), tr("Sets the following distance for 'Aggressive' mode. This determines roughly how many seconds you'll follow behind the car ahead.\n\nDefault: 1.25 seconds."), ""}, + {"AggressiveJerkAcceleration", tr("Acceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in acceleration in 'Aggressive' mode. Higher values result in smoother, more gradual acceleration, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 0.5."), ""}, + {"AggressiveJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in deceleration in 'Aggressive' mode. Higher values result in smoother, more gradual deceleration, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 0.5."), ""}, + {"AggressiveJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious openpilot is around other vehicles or obstacles in 'Aggressive' mode. Higher values increase following distances and prioritize safety, leading to more cautious driving, while lower values allow for closer following but may reduce reaction time.\n\nDefault: 1.0."), ""}, + {"AggressiveJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly openpilot increases speed in 'Aggressive' mode. Higher values ensure smoother, more gradual speed changes when accelerating, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 0.5."), ""}, + {"AggressiveJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly openpilot adjusts to decreasing speeds in 'Aggressive' mode. Higher values ensure smoother, more gradual speed changes when slowing down, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 0.5."), ""}, + {"ResetAggressivePersonality", tr("Reset Settings"), tr("Restores the 'Aggressive' settings to their default values."), ""}, + + {"StandardPersonalityProfile", tr("Standard Personality"), tr("Customize the 'Standard' personality profile, optimized for balanced driving."), "../frogpilot/assets/stock_theme/distance_icons/standard.png"}, + {"StandardFollow", tr("Following Distance"), tr("Set the following distance for 'Standard' mode. This determines roughly how many seconds you'll follow behind the car ahead.\n\nDefault: 1.45 seconds."), ""}, + {"StandardJerkAcceleration", tr("Acceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in acceleration in 'Standard' mode. Higher values result in smoother, more gradual acceleration, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 1.0."), ""}, + {"StandardJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in deceleration in 'Standard' mode. Higher values result in smoother braking, while lower values allow for quicker, more immediate braking that may feel abrupt.\n\nDefault: 1.0."), ""}, + {"StandardJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious openpilot is around other vehicles or obstacles in 'Standard' mode. Higher values increase following distances and prioritize safety, leading to more cautious driving, while lower values allow for closer following but may reduce reaction time.\n\nDefault: 1.0."), ""}, + {"StandardJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly openpilot increases speed in 'Standard' mode. Higher values ensure smoother, more gradual speed changes when accelerating, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 1.0."), ""}, + {"StandardJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly openpilot adjusts to decreasing speeds in 'Standard' mode. Higher values ensure smoother, more gradual speed changes when slowing down, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 1.0."), ""}, + {"ResetStandardPersonality", tr("Reset Settings"), tr("Restores the 'Standard' settings to their default values."), ""}, + + {"RelaxedPersonalityProfile", tr("Relaxed Personality"), tr("Customize the 'Relaxed' personality profile, ideal for a more laid-back driving style."), "../frogpilot/assets/stock_theme/distance_icons/relaxed.png"}, + {"RelaxedFollow", tr("Following Distance"), tr("Set the following distance for 'Relaxed' mode. This determines roughly how many seconds you'll follow behind the car ahead.\n\nDefault: 1.75 seconds."), ""}, + {"RelaxedJerkAcceleration", tr("Acceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in acceleration in 'Relaxed' mode. Higher values result in smoother, more gradual acceleration, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 1.0."), ""}, + {"RelaxedJerkDeceleration", tr("Deceleration Sensitivity"), tr("Controls how sensitive openpilot is to changes in deceleration in 'Relaxed' mode. Higher values result in smoother braking, while lower values allow for quicker, more immediate braking that may feel abrupt.\n\nDefault: 1.0."), ""}, + {"RelaxedJerkDanger", tr("Safety Distance Sensitivity"), tr("Adjusts how cautious openpilot is around other vehicles or obstacles in 'Relaxed' mode. Higher values increase following distances and prioritize safety, leading to more cautious driving, while lower values allow for closer following but may reduce reaction time.\n\nDefault: 1.0."), ""}, + {"RelaxedJerkSpeed", tr("Speed Increase Responsiveness"), tr("Controls how quickly openpilot increases speed in 'Relaxed' mode. Higher values ensure smoother, more gradual speed changes when accelerating, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 1.0."), ""}, + {"RelaxedJerkSpeedDecrease", tr("Speed Decrease Responsiveness"), tr("Sets how quickly openpilot adjusts to decreasing speeds in 'Relaxed' mode. Higher values ensure smoother, more gradual speed changes when slowing down, while lower values allow for quicker, more responsive changes that may feel abrupt.\n\nDefault: 1.0."), ""}, + {"ResetRelaxedPersonality", tr("Reset Settings"), tr("Restores the 'Relaxed' settings to their default values."), ""}, + + {"ExperimentalModeActivation", tr("Experimental Mode Activation"), tr("Toggle 'Experimental Mode' on/off using either the steering wheel buttons or screen.\n\nThis overrides 'Conditional Experimental Mode'."), "../assets/img_experimental_white.svg"}, + {"ExperimentalModeViaLKAS", tr("Click the LKAS Button"), tr("Toggles 'Experimental Mode' by pressing the 'LKAS' button on the steering wheel."), ""}, + {"ExperimentalModeViaTap", tr("Double-Tap the Screen"), tr("Toggles 'Experimental Mode' by double-tapping the onroad UI within a 0.5 second period."), ""}, + {"ExperimentalModeViaDistance", tr("Long Press the Distance Button"), tr("Toggles 'Experimental Mode' by holding down the 'distance' button on the steering wheel for 0.5 seconds."), ""}, {"LongitudinalTune", tr("Longitudinal Tuning"), tr("Settings that control how openpilot manages speed and acceleration."), "../frogpilot/assets/toggle_icons/icon_longitudinal_tune.png"}, - {"AccelerationProfile", tr("Acceleration Profile"), tr("Choose between a sporty or eco-friendly acceleration rate."), ""}, - {"DecelerationProfile", tr("Deceleration Profile"), tr("Choose between a sporty or eco-friendly deceleration rate."), ""}, - {"HumanAcceleration", tr("Human-Like Acceleration"), tr("Uses the lead's acceleration rate when at a takeoff and ramps off the acceleration rate when approaching the maximum set speed for a smoother max speed approach."), ""}, - {"HumanFollowing", tr("Human-Like Following Distance"), tr("Dynamically adjusts the following distance to feel more natural when approaching slower or stopped vehicles."), ""}, + {"AccelerationProfile", tr("Acceleration Profile"), tr("Enables either a sporty or eco-friendly acceleration rate. 'Sport+' aims to make openpilot accelerate as fast as possible."), ""}, + {"DecelerationProfile", tr("Deceleration Profile"), tr("Enables either a sporty or eco-friendly deceleration rate."), ""}, + {"HumanAcceleration", tr("Human-Like Acceleration"), tr("Uses the lead's acceleration rate when at a takeoff and ramps off the acceleration rate when approaching the maximum set speed for a more 'human-like' driving experience."), ""}, + {"HumanFollowing", tr("Human-Like Following Distance"), tr("Dynamically adjusts the following distance when approaching slower or stopped vehicles for a more 'human-like' driving experience."), ""}, {"IncreasedStoppedDistance", tr("Increase Stopped Distance"), tr("Increases the distance to stop behind vehicles."), ""}, + {"LeadDetectionThreshold", tr("Lead Detection Confidence"), tr("Controls how sensitive openpilot is to detecting vehicles ahead. A lower value can help detect vehicles sooner and from farther away, but increases the chance openpilot mistakes other objects for vehicles."), ""}, + {"MaxDesiredAcceleration", tr("Maximum Acceleration Rate"), tr("Sets a cap on how fast openpilot can accelerate."), ""}, {"QOLLongitudinal", tr("Quality of Life Improvements"), tr("Miscellaneous longitudinal focused features to improve your overall openpilot experience."), "../frogpilot/assets/toggle_icons/quality_of_life.png"}, - {"CustomCruise", tr("Cruise Increase Interval"), tr("Interval used when increasing the cruise control speed."), ""}, - {"CustomCruiseLong", tr("Custom Cruise Interval (Long Press)"), tr("Interval used when increasing the cruise control speed when holding down the button for 0.5+ seconds."), ""}, - {"MapGears", tr("Map Accel/Decel to Gears"), tr("Map the acceleration and deceleration profiles to the 'Eco' or 'Sport' gear modes."), ""}, - {"OnroadDistanceButton", tr("Onroad Personality Button"), tr("The current driving personality is displayed on the screen. Tap to switch personalities, or long press for 2.5 seconds to activate 'Traffic Mode'."), ""}, - {"ReverseCruise", tr("Reverse Cruise Increase"), tr("The long press feature is reversed in order to increase speed by 5 mph instead of 1."), ""}, - - {"SpeedLimitController", tr("Speed Limit Controller"), tr("Automatically adjust your max speed to match the speed limit using 'Open Street Maps', 'Navigate on openpilot', or your car's dashboard (Toyota/Lexus/HKG only)."), "../assets/offroad/icon_speed_limit.png"}, - {"SLCConfirmation", tr("Confirm New Speed Limits"), tr("Require manual confirmation before using a new speed limit."), ""}, - {"SLCFallback", tr("Fallback Method"), tr("Choose what happens when no speed limit data is available."), ""}, - {"SLCOverride", tr("Override Method"), tr("Choose how you want to override the current speed limit.\n\n"), ""}, - {"SLCPriority", tr("Speed Limit Source Priority"), tr("Set the order of priority for speed limit data sources."), ""}, - {"SLCOffsets", tr("Speed Limit Offsets"), tr("Manage toggles related to 'Speed Limit Controller's controls."), ""}, - {"Offset1", tr("Speed Limit Offset (0-34 mph)"), tr("Set the speed limit offset for speeds between 0 and 34 mph."), ""}, - {"Offset2", tr("Speed Limit Offset (35-54 mph)"), tr("Set the speed limit offset for speeds between 35 and 54 mph."), ""}, - {"Offset3", tr("Speed Limit Offset (55-64 mph)"), tr("Set the speed limit offset for speeds between 55 and 64 mph."), ""}, - {"Offset4", tr("Speed Limit Offset (65-99 mph)"), tr("Set the speed limit offset for speeds between 65 and 99 mph."), ""}, + {"CustomCruise", tr("Cruise Increase Interval"), tr("Controls the interval used when increasing the cruise control speed."), ""}, + {"CustomCruiseLong", tr("Custom Cruise Interval (Long Press)"), tr("Controls the interval used when increasing the cruise control speed while holding down the button for 0.5+ seconds."), ""}, + {"ForceStandstill", tr("Force Keep openpilot in the Standstill State"), tr("Keeps openpilot in the 'standstill' state until the gas pedal or 'resume' button is pressed."), ""}, + {"ForceStops", tr("Force Stop for 'Detected' Stop Lights/Signs"), tr("Forces a stop whenever openpilot 'detects' a potential red light/stop sign to prevent it from running the red light/stop sign."), ""}, + {"SetSpeedOffset", tr("Set Speed Offset"), tr("Controls how much higher or lower the set speed should be compared to your current set speed. For example, if you prefer to drive 5 mph above the speed limit, this setting will automatically add that difference when you adjust your set speed."), ""}, + {"MapGears", tr("Map Accel/Decel to Gears"), tr("Maps the acceleration and deceleration profiles to your car's 'Eco' or 'Sport' gear modes."), ""}, + {"ReverseCruise", tr("Reverse Cruise Increase"), tr("Reverses the long press cruise increase feature to increase the max speed by 5 mph instead of 1 on short presses."), ""}, + + {"SpeedLimitController", tr("Speed Limit Controller"), tr("Automatically adjust your max speed to match the speed limit using downloaded 'Open Street Maps' data, 'Navigate on openpilot', or your car's dashboard (Toyota/Lexus/HKG only)."), "../assets/offroad/icon_speed_limit.png"}, + {"SLCConfirmation", tr("Confirm New Speed Limits"), tr("Enables manual confirmations before using a new speed limit."), ""}, + {"SLCFallback", tr("Fallback Method"), tr("Controls what happens when no speed limit data is available."), ""}, + {"SLCOverride", tr("Override Method"), tr("Controls how the current speed limit is overriden.\n\n"), ""}, {"SLCQOL", tr("Quality of Life Improvements"), tr("Miscellaneous 'Speed Limit Controller' focused features to improve your overall openpilot experience."), ""}, - {"ForceMPHDashboard", tr("Force MPH Readings from Dashboard"), tr("Force speed limit readings in MPH from the dashboard if it normally displays in KPH."), ""}, - {"SLCLookaheadHigher", tr("Prepare for Higher Speed Limits"), tr("Set a lookahead value to prepare for upcoming higher speed limits based on map data."), ""}, - {"SLCLookaheadLower", tr("Prepare for Lower Speed Limits"), tr("Set a lookahead value to prepare for upcoming lower speed limits based on map data."), ""}, - {"SetSpeedLimit", tr("Set Speed to Current Limit"), tr("Set your max speed to match the current speed limit when enabling openpilot."), ""}, - {"SLCVisuals", tr("Visual Settings"), tr("Manage visual settings for the 'Speed Limit Controller'."), ""}, - {"UseVienna", tr("Use Vienna-Style Speed Signs"), tr("Switch to Vienna-style (EU) speed limit signs instead of MUTCD (US)."), ""}, - {"ShowSLCOffset", tr("Show Speed Limit Offset"), tr("Display the speed limit offset separately in the onroad UI when using the Speed Limit Controller."), ""}, + {"ForceMPHDashboard", tr("Force MPH Readings from Dashboard"), tr("Forces speed limit readings from the dashboard to MPH if it normally displays them in KPH."), ""}, + {"SLCLookaheadHigher", tr("Prepare for Higher Speed Limits"), tr("Sets a lookahead value to prepare for upcoming higher speed limits when using downloaded map data."), ""}, + {"SLCLookaheadLower", tr("Prepare for Lower Speed Limits"), tr("Sets a lookahead value to prepare for upcoming lower speed limits when using downloaded map data."), ""}, + {"SetSpeedLimit", tr("Set Speed to Current Limit"), tr("Sets your max speed to match the current speed limit when enabling openpilot."), ""}, + {"SLCPriority", tr("Speed Limit Source Priority"), tr("Sets the order of priority for speed limit data sources."), ""}, + {"SLCOffsets", tr("Speed Limit Offsets"), tr("Set speed limit offsets to drive over the posted speed limit."), ""}, + {"Offset1", tr("Speed Limit Offset (0-34 mph)"), tr("Sets the speed limit offset for speeds between 0 and 34 mph."), ""}, + {"Offset2", tr("Speed Limit Offset (35-54 mph)"), tr("Sets the speed limit offset for speeds between 35 and 54 mph."), ""}, + {"Offset3", tr("Speed Limit Offset (55-64 mph)"), tr("Sets the speed limit offset for speeds between 55 and 64 mph."), ""}, + {"Offset4", tr("Speed Limit Offset (65-99 mph)"), tr("Sets the speed limit offset for speeds between 65 and 99 mph."), ""}, }; for (const auto &[param, title, desc, icon] : longitudinalToggles) { AbstractControl *longitudinalToggle; - if (param == "ConditionalExperimental") { + if (param == "CustomPersonalities") { + FrogPilotParamManageControl *customPersonalitiesToggle = new FrogPilotParamManageControl(param, title, desc, icon); + QObject::connect(customPersonalitiesToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { + showToggles(customDrivingPersonalityKeys); + }); + longitudinalToggle = customPersonalitiesToggle; + } else if (param == "ResetTrafficPersonality" || param == "ResetAggressivePersonality" || param == "ResetStandardPersonality" || param == "ResetRelaxedPersonality") { + FrogPilotButtonsControl *profileBtn = new FrogPilotButtonsControl(title, desc, {tr("Reset")}); + longitudinalToggle = profileBtn; + } else if (param == "TrafficPersonalityProfile") { + FrogPilotParamManageControl *trafficPersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon); + QObject::connect(trafficPersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { + customPersonalityOpen = true; + openSubParentToggle(); + showToggles(trafficPersonalityKeys); + }); + longitudinalToggle = trafficPersonalityToggle; + } else if (param == "AggressivePersonalityProfile") { + FrogPilotParamManageControl *aggressivePersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon); + QObject::connect(aggressivePersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { + customPersonalityOpen = true; + openSubParentToggle(); + showToggles(aggressivePersonalityKeys); + }); + longitudinalToggle = aggressivePersonalityToggle; + } else if (param == "StandardPersonalityProfile") { + FrogPilotParamManageControl *standardPersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon); + QObject::connect(standardPersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { + customPersonalityOpen = true; + openSubParentToggle(); + showToggles(standardPersonalityKeys); + }); + longitudinalToggle = standardPersonalityToggle; + } else if (param == "RelaxedPersonalityProfile") { + FrogPilotParamManageControl *relaxedPersonalityToggle = new FrogPilotParamManageControl(param, title, desc, icon); + QObject::connect(relaxedPersonalityToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { + customPersonalityOpen = true; + openSubParentToggle(); + showToggles(relaxedPersonalityKeys); + }); + longitudinalToggle = relaxedPersonalityToggle; + } else if (trafficPersonalityKeys.find(param) != trafficPersonalityKeys.end() || + aggressivePersonalityKeys.find(param) != aggressivePersonalityKeys.end() || + standardPersonalityKeys.find(param) != standardPersonalityKeys.end() || + relaxedPersonalityKeys.find(param) != relaxedPersonalityKeys.end()) { + if (param == "TrafficFollow" || param == "AggressiveFollow" || param == "StandardFollow" || param == "RelaxedFollow") { + if (param == "TrafficFollow") { + longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0.5, 5, tr(" seconds"), std::map(), 0.01); + } else { + longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 5, tr(" seconds"), std::map(), 0.01); + } + } else { + longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 500, "%"); + } + + } else if (param == "ConditionalExperimental") { FrogPilotParamManageControl *conditionalExperimentalToggle = new FrogPilotParamManageControl(param, title, desc, icon); QObject::connect(conditionalExperimentalToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - showToggles(conditionalExperimentalKeys); + std::set modifiedConditionalExperimentalKeys = conditionalExperimentalKeys; + + if (customizationLevel != 2) { + modifiedConditionalExperimentalKeys.erase("CENavigation"); + modifiedConditionalExperimentalKeys.erase("CESignalSpeed"); + modifiedConditionalExperimentalKeys.erase("HideCEMStatusBar"); + } + + showToggles(modifiedConditionalExperimentalKeys); }); longitudinalToggle = conditionalExperimentalToggle; } else if (param == "CESpeed") { @@ -84,15 +185,11 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * std::vector navigationToggleNames{tr("Intersections"), tr("Turns"), tr("With Lead")}; longitudinalToggle = new FrogPilotButtonToggleControl(param, title, desc, navigationToggles, navigationToggleNames); } else if (param == "CEModelStopTime") { - std::map modelStopTimeLabels; - for (int i = 0; i <= 10; ++i) { - modelStopTimeLabels[i] = (i == 0) ? tr("Off") : QString::number(i) + " seconds"; - } - longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 10, QString(), modelStopTimeLabels); + longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 10, tr(" seconds"), {{0, "Off"}}); } else if (param == "CESignalSpeed") { std::vector ceSignalToggles{"CESignalLaneDetection"}; - std::vector ceSignalToggleNames{"Lane Detection"}; - longitudinalToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0, 99, tr("mph"), std::map(), 1.0, ceSignalToggles, ceSignalToggleNames); + std::vector ceSignalToggleNames{"Only For Detected Lanes"}; + longitudinalToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0, 99, tr("mph"), std::map(), 1.0, ceSignalToggles, ceSignalToggleNames, true); } else if (param == "CurveSpeedControl") { FrogPilotParamManageControl *curveControlToggle = new FrogPilotParamManageControl(param, title, desc, icon); @@ -107,6 +204,12 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * modifiedCurveSpeedKeys.erase("MTSCCurvatureCheck"); } + if (customizationLevel != 2) { + modifiedCurveSpeedKeys.erase("CurveSensitivity"); + modifiedCurveSpeedKeys.erase("MTSCCurvatureCheck"); + modifiedCurveSpeedKeys.erase("TurnAggressiveness"); + } + showToggles(modifiedCurveSpeedKeys); }); longitudinalToggle = curveControlToggle; @@ -162,19 +265,38 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * } else if (param == "LongitudinalTune") { FrogPilotParamManageControl *longitudinalTuneToggle = new FrogPilotParamManageControl(param, title, desc, icon); QObject::connect(longitudinalTuneToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - showToggles(longitudinalTuneKeys); + std::set modifiedLongitudinalTuneKeys = longitudinalTuneKeys; + + if (customizationLevel == 0) { + modifiedLongitudinalTuneKeys.erase("HumanAcceleration"); + modifiedLongitudinalTuneKeys.erase("HumanFollowing"); + modifiedLongitudinalTuneKeys.erase("IncreasedStoppedDistance"); + modifiedLongitudinalTuneKeys.erase("LeadDetectionThreshold"); + modifiedLongitudinalTuneKeys.erase("MaxDesiredAcceleration"); + } else if (customizationLevel == 1) { + modifiedLongitudinalTuneKeys.erase("HumanAcceleration"); + modifiedLongitudinalTuneKeys.erase("HumanFollowing"); + modifiedLongitudinalTuneKeys.erase("LeadDetectionThreshold"); + modifiedLongitudinalTuneKeys.erase("MaxDesiredAcceleration"); + } + + showToggles(modifiedLongitudinalTuneKeys); }); longitudinalToggle = longitudinalTuneToggle; } else if (param == "AccelerationProfile") { - std::vector profileOptions{tr("Standard"), tr("Eco"), tr("Sport"), tr("Sport+")}; - ButtonParamControl *profileSelection = new ButtonParamControl(param, title, desc, icon, profileOptions); - longitudinalToggle = profileSelection; + std::vector accelerationProfiles{tr("Standard"), tr("Eco"), tr("Sport"), tr("Sport+")}; + ButtonParamControl *accelerationProfileToggle = new ButtonParamControl(param, title, desc, icon, accelerationProfiles); + longitudinalToggle = accelerationProfileToggle; } else if (param == "DecelerationProfile") { - std::vector profileOptions{tr("Standard"), tr("Eco"), tr("Sport")}; - ButtonParamControl *profileSelection = new ButtonParamControl(param, title, desc, icon, profileOptions); - longitudinalToggle = profileSelection; + std::vector decelerationProfiles{tr("Standard"), tr("Eco"), tr("Sport")}; + ButtonParamControl *decelerationProfileToggle = new ButtonParamControl(param, title, desc, icon, decelerationProfiles); + longitudinalToggle = decelerationProfileToggle; } else if (param == "IncreasedStoppedDistance") { - longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 15, tr(" feet")); + longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 10, tr(" feet")); + } else if (param == "LeadDetectionThreshold") { + longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 99, "%"); + } else if (param == "MaxDesiredAcceleration") { + longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0.1, 4.0, "m/s", std::map(), 0.1); } else if (param == "QOLLongitudinal") { FrogPilotParamManageControl *qolLongitudinalToggle = new FrogPilotParamManageControl(param, title, desc, icon); @@ -186,12 +308,20 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * } else { modifiedQolKeys.erase("CustomCruise"); modifiedQolKeys.erase("CustomCruiseLong"); + modifiedQolKeys.erase("SetSpeedOffset"); } - if (!isToyota && !isGM && !isHKGCanFd) { + if (!(isGM || isHKGCanFd || isToyota)) { modifiedQolKeys.erase("MapGears"); } + if (customizationLevel != 2) { + modifiedQolKeys.erase("ForceStandstill"); + modifiedQolKeys.erase("ForceStops"); + modifiedQolKeys.erase("ReverseCruise"); + modifiedQolKeys.erase("SetSpeedOffset"); + } + showToggles(modifiedQolKeys); }); longitudinalToggle = qolLongitudinalToggle; @@ -203,58 +333,39 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * std::vector mapGearsToggles{"MapAcceleration", "MapDeceleration"}; std::vector mapGearsToggleNames{tr("Acceleration"), tr("Deceleration")}; longitudinalToggle = new FrogPilotButtonToggleControl(param, title, desc, mapGearsToggles, mapGearsToggleNames); + } else if (param == "SetSpeedOffset") { + longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, tr("mph")); } else if (param == "SpeedLimitController") { FrogPilotParamManageControl *speedLimitControllerToggle = new FrogPilotParamManageControl(param, title, desc, icon); QObject::connect(speedLimitControllerToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - bool slcLower = params.getBool("SLCConfirmationLower"); - bool slcHigher = params.getBool("SLCConfirmationHigher"); + std::set modifiedSpeedLimitControllerKeys = speedLimitControllerKeys; + + if (customizationLevel == 0) { + modifiedSpeedLimitControllerKeys.erase("SLCFallback"); + modifiedSpeedLimitControllerKeys.erase("SLCOverride"); + modifiedSpeedLimitControllerKeys.erase("SLCPriority"); + modifiedSpeedLimitControllerKeys.erase("SLCQOL"); + } else if (customizationLevel != 2) { + modifiedSpeedLimitControllerKeys.erase("SLCPriority"); + modifiedSpeedLimitControllerKeys.erase("SLCQOL"); + } - slcConfirmationBtn->setCheckedButton(0, slcLower); - slcConfirmationBtn->setCheckedButton(1, slcHigher); - slcConfirmationBtn->setCheckedButton(2, !(slcLower || slcHigher)); + showToggles(modifiedSpeedLimitControllerKeys); slcOpen = true; - showToggles(speedLimitControllerKeys); }); longitudinalToggle = speedLimitControllerToggle; } else if (param == "SLCConfirmation") { - slcConfirmationBtn = new FrogPilotButtonsControl(title, desc, {tr("Lower Limits"), tr("Higher Limits"), tr("None")}, true, false); - QObject::connect(slcConfirmationBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) { - bool lowerEnabled = params.getBool("SLCConfirmationLower"); - bool higherEnabled = params.getBool("SLCConfirmationHigher"); - - if (id == 0) { - params.putBool("SLCConfirmationLower", !lowerEnabled); - slcConfirmationBtn->setCheckedButton(0, !lowerEnabled); - slcConfirmationBtn->setCheckedButton(2, false); - - if (lowerEnabled & !higherEnabled) { - slcConfirmationBtn->setCheckedButton(2, true); - } - } else if (id == 1) { - params.putBool("SLCConfirmationHigher", !higherEnabled); - slcConfirmationBtn->setCheckedButton(1, !higherEnabled); - slcConfirmationBtn->setCheckedButton(2, false); - - if (higherEnabled & !lowerEnabled) { - slcConfirmationBtn->setCheckedButton(2, true); - } - } else { - params.putBool("SLCConfirmationLower", false); - params.putBool("SLCConfirmationHigher", false); - slcConfirmationBtn->setCheckedButton(0, false); - slcConfirmationBtn->setCheckedButton(1, false); - slcConfirmationBtn->setCheckedButton(2, true); - } - }); - longitudinalToggle = slcConfirmationBtn; + std::vector confirmationToggles{"SLCConfirmationLower", "SLCConfirmationHigher"}; + std::vector confirmationToggleNames{tr("Lower Limits"), tr("Higher Limits")}; + longitudinalToggle = new FrogPilotButtonToggleControl(param, title, desc, confirmationToggles, confirmationToggleNames); } else if (param == "SLCFallback") { std::vector fallbackOptions{tr("Set Speed"), tr("Experimental Mode"), tr("Previous Limit")}; ButtonParamControl *fallbackSelection = new ButtonParamControl(param, title, desc, icon, fallbackOptions); longitudinalToggle = fallbackSelection; } else if (param == "SLCOverride") { - std::vector overrideOptions{tr("None"), tr("Gas Pedal Press"), tr("Cruise Set Speed")}; + std::vector overrideOptions{tr("None"), tr("Set With Gas Pedal"), tr("Max Set Speed")}; ButtonParamControl *overrideSelection = new ButtonParamControl(param, title, desc, icon, overrideOptions); longitudinalToggle = overrideSelection; } else if (param == "SLCPriority") { @@ -269,7 +380,8 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * for (int i = 1; i <= 3; ++i) { QStringList currentPriorities = (i == 1) ? primaryPriorities : secondaryTertiaryPriorities; QStringList prioritiesToDisplay = currentPriorities; - for (const auto &selectedPriority : qAsConst(selectedPriorities)) { + + for (QString selectedPriority : qAsConst(selectedPriorities)) { prioritiesToDisplay.removeAll(selectedPriority); } @@ -284,12 +396,24 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * QString priorityKey = QString("SLCPriority%1").arg(i); QString selection = MultiOptionDialog::getSelection(priorityPrompts[i - 1], prioritiesToDisplay, "", this); - if (selection.isEmpty()) break; + if (selection.isEmpty()) { + break; + } params.putNonBlocking(priorityKey.toStdString(), selection.toStdString()); selectedPriorities.append(selection); - if (selection == tr("Lowest") || selection == tr("Highest") || selection == tr("None")) break; + if (selection == tr("None")) { + for (int j = i + 1; j <= 3; ++j) { + QString priorityKeyNext = QString("SLCPriority%1").arg(j); + params.putNonBlocking(priorityKeyNext.toStdString(), "None"); + } + break; + } + + if (selection == tr("Lowest") || selection == tr("Highest")) { + break; + } updateFrogPilotToggles(); } @@ -335,19 +459,8 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * longitudinalToggle = reinterpret_cast(manageSLCQOLBtn); } else if (param == "SLCLookaheadHigher" || param == "SLCLookaheadLower") { longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 60, tr(" seconds")); - } else if (param == "SLCVisuals") { - ButtonControl *manageSLCVisualsBtn = new ButtonControl(title, tr("MANAGE"), desc); - QObject::connect(manageSLCVisualsBtn, &ButtonControl::clicked, [this]() { - openSubParentToggle(); - showToggles(speedLimitControllerVisualsKeys); - }); - longitudinalToggle = reinterpret_cast(manageSLCVisualsBtn); } else if (param == "Offset1" || param == "Offset2" || param == "Offset3" || param == "Offset4") { longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, -99, 99, tr("mph")); - } else if (param == "ShowSLCOffset") { - std::vector slcOffsetToggles{"ShowSLCOffsetUI"}; - std::vector slcOffsetToggleNames{tr("Control Via UI")}; - longitudinalToggle = new FrogPilotButtonToggleControl(param, title, desc, slcOffsetToggles, slcOffsetToggleNames); } else { longitudinalToggle = new ParamControl(param, title, desc, icon); @@ -373,6 +486,106 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * } }); + FrogPilotParamValueControl *trafficFollowToggle = static_cast(toggles["TrafficFollow"]); + FrogPilotParamValueControl *trafficAccelerationToggle = static_cast(toggles["TrafficJerkAcceleration"]); + FrogPilotParamValueControl *trafficDecelerationToggle = static_cast(toggles["TrafficJerkDeceleration"]); + FrogPilotParamValueControl *trafficDangerToggle = static_cast(toggles["TrafficJerkDanger"]); + FrogPilotParamValueControl *trafficSpeedToggle = static_cast(toggles["TrafficJerkSpeed"]); + FrogPilotParamValueControl *trafficSpeedDecreaseToggle = static_cast(toggles["TrafficJerkSpeedDecrease"]); + FrogPilotButtonsControl *trafficResetButton = static_cast(toggles["ResetTrafficPersonality"]); + QObject::connect(trafficResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() { + if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for 'Traffic Mode'?"), this)) { + params.putFloat("TrafficFollow", 0.5); + params.putFloat("TrafficJerkAcceleration", 50); + params.putFloat("TrafficJerkDeceleration", 50); + params.putFloat("TrafficJerkDanger", 100); + params.putFloat("TrafficJerkSpeed", 50); + params.putFloat("TrafficJerkSpeedDecrease", 50); + trafficFollowToggle->refresh(); + trafficAccelerationToggle->refresh(); + trafficDecelerationToggle->refresh(); + trafficDangerToggle->refresh(); + trafficSpeedToggle->refresh(); + trafficSpeedDecreaseToggle->refresh(); + updateFrogPilotToggles(); + } + }); + + FrogPilotParamValueControl *aggressiveFollowToggle = static_cast(toggles["AggressiveFollow"]); + FrogPilotParamValueControl *aggressiveAccelerationToggle = static_cast(toggles["AggressiveJerkAcceleration"]); + FrogPilotParamValueControl *aggressiveDecelerationToggle = static_cast(toggles["AggressiveJerkDeceleration"]); + FrogPilotParamValueControl *aggressiveDangerToggle = static_cast(toggles["AggressiveJerkDanger"]); + FrogPilotParamValueControl *aggressiveSpeedToggle = static_cast(toggles["AggressiveJerkSpeed"]); + FrogPilotParamValueControl *aggressiveSpeedDecreaseToggle = static_cast(toggles["AggressiveJerkSpeedDecrease"]); + FrogPilotButtonsControl *aggressiveResetButton = static_cast(toggles["ResetAggressivePersonality"]); + QObject::connect(aggressiveResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() { + if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for the 'Aggressive' personality?"), this)) { + params.putFloat("AggressiveFollow", 1.25); + params.putFloat("AggressiveJerkAcceleration", 50); + params.putFloat("AggressiveJerkDeceleration", 50); + params.putFloat("AggressiveJerkDanger", 100); + params.putFloat("AggressiveJerkSpeed", 50); + params.putFloat("AggressiveJerkSpeedDecrease", 50); + aggressiveFollowToggle->refresh(); + aggressiveAccelerationToggle->refresh(); + aggressiveDecelerationToggle->refresh(); + aggressiveDangerToggle->refresh(); + aggressiveSpeedToggle->refresh(); + aggressiveSpeedDecreaseToggle->refresh(); + updateFrogPilotToggles(); + } + }); + + FrogPilotParamValueControl *standardFollowToggle = static_cast(toggles["StandardFollow"]); + FrogPilotParamValueControl *standardAccelerationToggle = static_cast(toggles["StandardJerkAcceleration"]); + FrogPilotParamValueControl *standardDecelerationToggle = static_cast(toggles["StandardJerkDeceleration"]); + FrogPilotParamValueControl *standardDangerToggle = static_cast(toggles["StandardJerkDanger"]); + FrogPilotParamValueControl *standardSpeedToggle = static_cast(toggles["StandardJerkSpeed"]); + FrogPilotParamValueControl *standardSpeedDecreaseToggle = static_cast(toggles["StandardJerkSpeedDecrease"]); + FrogPilotButtonsControl *standardResetButton = static_cast(toggles["ResetStandardPersonality"]); + QObject::connect(standardResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() { + if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for the 'Standard' personality?"), this)) { + params.putFloat("StandardFollow", 1.45); + params.putFloat("StandardJerkAcceleration", 100); + params.putFloat("StandardJerkDeceleration", 100); + params.putFloat("StandardJerkDanger", 100); + params.putFloat("StandardJerkSpeed", 100); + params.putFloat("StandardJerkSpeedDecrease", 100); + standardFollowToggle->refresh(); + standardAccelerationToggle->refresh(); + standardDecelerationToggle->refresh(); + standardDangerToggle->refresh(); + standardSpeedToggle->refresh(); + standardSpeedDecreaseToggle->refresh(); + updateFrogPilotToggles(); + } + }); + + FrogPilotParamValueControl *relaxedFollowToggle = static_cast(toggles["RelaxedFollow"]); + FrogPilotParamValueControl *relaxedAccelerationToggle = static_cast(toggles["RelaxedJerkAcceleration"]); + FrogPilotParamValueControl *relaxedDecelerationToggle = static_cast(toggles["RelaxedJerkDeceleration"]); + FrogPilotParamValueControl *relaxedDangerToggle = static_cast(toggles["RelaxedJerkDanger"]); + FrogPilotParamValueControl *relaxedSpeedToggle = static_cast(toggles["RelaxedJerkSpeed"]); + FrogPilotParamValueControl *relaxedSpeedDecreaseToggle = static_cast(toggles["RelaxedJerkSpeedDecrease"]); + FrogPilotButtonsControl *relaxedResetButton = static_cast(toggles["ResetRelaxedPersonality"]); + QObject::connect(relaxedResetButton, &FrogPilotButtonsControl::buttonClicked, this, [=]() { + if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset your settings for the 'Relaxed' personality?"), this)) { + params.putFloat("RelaxedFollow", 1.75); + params.putFloat("RelaxedJerkAcceleration", 100); + params.putFloat("RelaxedJerkDeceleration", 100); + params.putFloat("RelaxedJerkDanger", 100); + params.putFloat("RelaxedJerkSpeed", 100); + params.putFloat("RelaxedJerkSpeedDecrease", 100); + relaxedFollowToggle->refresh(); + relaxedAccelerationToggle->refresh(); + relaxedDecelerationToggle->refresh(); + relaxedDangerToggle->refresh(); + relaxedSpeedToggle->refresh(); + relaxedSpeedDecreaseToggle->refresh(); + updateFrogPilotToggles(); + } + }); + QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotLongitudinalPanel::hideToggles); QObject::connect(parent, &FrogPilotSettingsWindow::closeSubParentToggle, this, &FrogPilotLongitudinalPanel::hideSubToggles); QObject::connect(parent, &FrogPilotSettingsWindow::updateCarToggles, this, &FrogPilotLongitudinalPanel::updateCarToggles); @@ -381,6 +594,16 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow * updateMetric(); } +void FrogPilotLongitudinalPanel::showEvent(QShowEvent *event) { + customizationLevel = parent->customizationLevel; + + toggles["ConditionalExperimental"]->setVisible(customizationLevel != 0); + toggles["CurveSpeedControl"]->setVisible(customizationLevel != 0); + toggles["CustomPersonalities"]->setVisible(customizationLevel == 2); + toggles["ExperimentalModeActivation"]->setVisible(customizationLevel != 0); + toggles["QOLLongitudinal"]->setVisible(customizationLevel != 0); +} + void FrogPilotLongitudinalPanel::updateCarToggles() { hasDashSpeedLimits = parent->hasDashSpeedLimits; hasPCMCruise = parent->hasPCMCruise; @@ -411,6 +634,7 @@ void FrogPilotLongitudinalPanel::updateMetric() { params.putFloatNonBlocking("Offset2", params.getFloat("Offset2") * speedConversion); params.putFloatNonBlocking("Offset3", params.getFloat("Offset3") * speedConversion); params.putFloatNonBlocking("Offset4", params.getFloat("Offset4") * speedConversion); + params.putFloatNonBlocking("SetSpeedOffset", params.getFloat("SetSpeedOffset") * speedConversion); } FrogPilotDualParamControl *ceSpeedToggle = reinterpret_cast(toggles["CESpeed"]); @@ -422,6 +646,7 @@ void FrogPilotLongitudinalPanel::updateMetric() { FrogPilotParamValueControl *offset3Toggle = static_cast(toggles["Offset3"]); FrogPilotParamValueControl *offset4Toggle = static_cast(toggles["Offset4"]); FrogPilotParamValueControl *increasedStoppedDistanceToggle = static_cast(toggles["IncreasedStoppedDistance"]); + FrogPilotParamValueControl *setSpeedOffsetToggle = static_cast(toggles["SetSpeedOffset"]); if (isMetric) { offset1Toggle->setTitle(tr("Speed Limit Offset (0-34 kph)")); @@ -429,10 +654,10 @@ void FrogPilotLongitudinalPanel::updateMetric() { offset3Toggle->setTitle(tr("Speed Limit Offset (55-64 kph)")); offset4Toggle->setTitle(tr("Speed Limit Offset (65-99 kph)")); - offset1Toggle->setDescription(tr("Set speed limit offset for limits between 0-34 kph.")); - offset2Toggle->setDescription(tr("Set speed limit offset for limits between 35-54 kph.")); - offset3Toggle->setDescription(tr("Set speed limit offset for limits between 55-64 kph.")); - offset4Toggle->setDescription(tr("Set speed limit offset for limits between 65-99 kph.")); + offset1Toggle->setDescription(tr("Sets the speed limit offset for speeds between 0-34 kph.")); + offset2Toggle->setDescription(tr("Sets the speed limit offset for speeds between 35-54 kph.")); + offset3Toggle->setDescription(tr("Sets the speed limit offset for speeds between 55-64 kph.")); + offset4Toggle->setDescription(tr("Sets the speed limit offset for speeds between 65-99 kph.")); ceSignal->updateControl(0, 150, tr("kph")); ceSpeedToggle->updateControl(0, 150, tr("kph")); @@ -442,18 +667,19 @@ void FrogPilotLongitudinalPanel::updateMetric() { offset2Toggle->updateControl(-99, 99, tr("kph")); offset3Toggle->updateControl(-99, 99, tr("kph")); offset4Toggle->updateControl(-99, 99, tr("kph")); + setSpeedOffsetToggle->updateControl(0, 150, tr("kph")); - increasedStoppedDistanceToggle->updateControl(0, 5, tr(" meters")); + increasedStoppedDistanceToggle->updateControl(0, 3, tr(" meters")); } else { offset1Toggle->setTitle(tr("Speed Limit Offset (0-34 mph)")); offset2Toggle->setTitle(tr("Speed Limit Offset (35-54 mph)")); offset3Toggle->setTitle(tr("Speed Limit Offset (55-64 mph)")); offset4Toggle->setTitle(tr("Speed Limit Offset (65-99 mph)")); - offset1Toggle->setDescription(tr("Set speed limit offset for limits between 0-34 mph.")); - offset2Toggle->setDescription(tr("Set speed limit offset for limits between 35-54 mph.")); - offset3Toggle->setDescription(tr("Set speed limit offset for limits between 55-64 mph.")); - offset4Toggle->setDescription(tr("Set speed limit offset for limits between 65-99 mph.")); + offset1Toggle->setDescription(tr("Sets the speed limit offset for speeds between 0-34 mph.")); + offset2Toggle->setDescription(tr("Sets the speed limit offset for speeds between 35-54 mph.")); + offset3Toggle->setDescription(tr("Sets the speed limit offset for speeds between 55-64 mph.")); + offset4Toggle->setDescription(tr("Sets the speed limit offset for speeds between 65-99 mph.")); ceSignal->updateControl(0, 99, tr("mph")); ceSpeedToggle->updateControl(0, 99, tr("mph")); @@ -463,8 +689,9 @@ void FrogPilotLongitudinalPanel::updateMetric() { offset2Toggle->updateControl(-99, 99, tr("mph")); offset3Toggle->updateControl(-99, 99, tr("mph")); offset4Toggle->updateControl(-99, 99, tr("mph")); + setSpeedOffsetToggle->updateControl(0, 99, tr("mph")); - increasedStoppedDistanceToggle->updateControl(0, 15, tr(" feet")); + increasedStoppedDistanceToggle->updateControl(0, 10, tr(" feet")); } } @@ -482,28 +709,51 @@ void FrogPilotLongitudinalPanel::showToggles(const std::set &keys) { void FrogPilotLongitudinalPanel::hideToggles() { setUpdatesEnabled(false); + customPersonalityOpen = false; slcOpen = false; for (auto &[key, toggle] : toggles) { - bool subToggles = conditionalExperimentalKeys.find(key) != conditionalExperimentalKeys.end() || + bool subToggles = aggressivePersonalityKeys.find(key) != aggressivePersonalityKeys.end() || + conditionalExperimentalKeys.find(key) != conditionalExperimentalKeys.end() || curveSpeedKeys.find(key) != curveSpeedKeys.end() || + customDrivingPersonalityKeys.find(key) != customDrivingPersonalityKeys.end() || experimentalModeActivationKeys.find(key) != experimentalModeActivationKeys.end() || longitudinalTuneKeys.find(key) != longitudinalTuneKeys.end() || qolKeys.find(key) != qolKeys.end() || + relaxedPersonalityKeys.find(key) != relaxedPersonalityKeys.end() || speedLimitControllerKeys.find(key) != speedLimitControllerKeys.end() || speedLimitControllerOffsetsKeys.find(key) != speedLimitControllerOffsetsKeys.end() || speedLimitControllerQOLKeys.find(key) != speedLimitControllerQOLKeys.end() || - speedLimitControllerVisualsKeys.find(key) != speedLimitControllerVisualsKeys.end(); + standardPersonalityKeys.find(key) != standardPersonalityKeys.end() || + trafficPersonalityKeys.find(key) != trafficPersonalityKeys.end(); toggle->setVisible(!subToggles); } + toggles["ConditionalExperimental"]->setVisible(customizationLevel != 0); + toggles["CurveSpeedControl"]->setVisible(customizationLevel != 0); + toggles["CustomPersonalities"]->setVisible(customizationLevel == 2); + toggles["ExperimentalModeActivation"]->setVisible(customizationLevel != 0); + toggles["QOLLongitudinal"]->setVisible(customizationLevel != 0); + setUpdatesEnabled(true); update(); } void FrogPilotLongitudinalPanel::hideSubToggles() { - if (slcOpen) { - showToggles(speedLimitControllerKeys); + if (customPersonalityOpen) { + customPersonalityOpen = false; + showToggles(customDrivingPersonalityKeys); + } else if (slcOpen) { + std::set modifiedSpeedLimitControllerKeys = speedLimitControllerKeys; + + if (customizationLevel == 0) { + modifiedSpeedLimitControllerKeys.erase("SLCFallback"); + modifiedSpeedLimitControllerKeys.erase("SLCOverride"); + modifiedSpeedLimitControllerKeys.erase("SLCPriority"); + modifiedSpeedLimitControllerKeys.erase("SLCQOL"); + } + + showToggles(modifiedSpeedLimitControllerKeys); } } diff --git a/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h b/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h index 306abfc44ed79d..e18c86a92c4479 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/longitudinal_settings.h @@ -17,40 +17,58 @@ class FrogPilotLongitudinalPanel : public FrogPilotListWidget { private: void hideSubToggles(); void hideToggles(); + void showEvent(QShowEvent *event) override; void showToggles(const std::set &keys); void updateCarToggles(); void updateMetric(); + std::set aggressivePersonalityKeys = { + "AggressiveFollow", "AggressiveJerkAcceleration", "AggressiveJerkDeceleration", + "AggressiveJerkDanger", "AggressiveJerkSpeed", "AggressiveJerkSpeedDecrease", + "ResetAggressivePersonality" + }; + std::set conditionalExperimentalKeys = { - "CESpeed", "CESpeedLead", "CECurves", "CELead", - "CEModelStopTime", "CENavigation", "CESignalSpeed", - "HideCEMStatusBar" + "CESpeed", "CESpeedLead", "CECurves", + "CELead", "CEModelStopTime", "CENavigation", + "CESignalSpeed", "HideCEMStatusBar" }; std::set curveSpeedKeys = { - "CurveDetectionMethod", "CurveSensitivity", - "DisableCurveSpeedSmoothing", "MTSCCurvatureCheck", - "TurnAggressiveness" + "CurveDetectionMethod", "CurveSensitivity", "DisableCurveSpeedSmoothing", + "MTSCCurvatureCheck", "TurnAggressiveness" + }; + + std::set customDrivingPersonalityKeys = { + "AggressivePersonalityProfile", "RelaxedPersonalityProfile", "StandardPersonalityProfile", + "TrafficPersonalityProfile" }; std::set experimentalModeActivationKeys = { - "ExperimentalModeViaDistance", "ExperimentalModeViaLKAS", - "ExperimentalModeViaTap" + "ExperimentalModeViaDistance", "ExperimentalModeViaLKAS", "ExperimentalModeViaTap" }; std::set longitudinalTuneKeys = { - "AccelerationProfile", "DecelerationProfile", - "HumanAcceleration", "HumanFollowing", "IncreasedStoppedDistance" + "AccelerationProfile", "DecelerationProfile", "HumanAcceleration", + "HumanFollowing", "IncreasedStoppedDistance", "LeadDetectionThreshold", + "MaxDesiredAcceleration" }; std::set qolKeys = { - "CustomCruise", "CustomCruiseLong", "MapGears", - "OnroadDistanceButton", "ReverseCruise" + "CustomCruise", "CustomCruiseLong", "ForceStandstill", + "ForceStops", "MapGears", "ReverseCruise", + "SetSpeedOffset" + }; + + std::set relaxedPersonalityKeys = { + "RelaxedFollow", "RelaxedJerkAcceleration", "RelaxedJerkDeceleration", + "RelaxedJerkDanger", "RelaxedJerkSpeed", "RelaxedJerkSpeedDecrease", + "ResetRelaxedPersonality" }; std::set speedLimitControllerKeys = { - "SLCConfirmation", "SLCOffsets", "SLCFallback", "SLCOverride", - "SLCPriority", "SLCQOL", "SLCVisuals" + "SLCConfirmation", "SLCOffsets", "SLCFallback", + "SLCOverride", "SLCPriority", "SLCQOL" }; std::set speedLimitControllerOffsetsKeys = { @@ -62,17 +80,26 @@ class FrogPilotLongitudinalPanel : public FrogPilotListWidget { "SLCLookaheadLower" }; - std::set speedLimitControllerVisualsKeys = { - "ShowSLCOffset", "UseVienna" + std::set standardPersonalityKeys = { + "StandardFollow", "StandardJerkAcceleration", "StandardJerkDeceleration", + "StandardJerkDanger", "StandardJerkSpeed", "StandardJerkSpeedDecrease", + "ResetStandardPersonality" + }; + + std::set trafficPersonalityKeys = { + "TrafficFollow", "TrafficJerkAcceleration", "TrafficJerkDeceleration", + "TrafficJerkDanger", "TrafficJerkSpeed", "TrafficJerkSpeedDecrease", + "ResetTrafficPersonality" }; FrogPilotSettingsWindow *parent; FrogPilotButtonsControl *curveDetectionBtn; - FrogPilotButtonsControl *slcConfirmationBtn; Params params; + bool customPersonalityOpen; + bool disableOpenpilotLongitudinal; bool hasPCMCruise; bool hasDashSpeedLimits; bool isGM; @@ -82,5 +109,7 @@ class FrogPilotLongitudinalPanel : public FrogPilotListWidget { bool isToyota; bool slcOpen; + int customizationLevel; + std::map toggles; }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/model_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/model_settings.cc new file mode 100644 index 00000000000000..396995d7e7b631 --- /dev/null +++ b/selfdrive/frogpilot/ui/qt/offroad/model_settings.cc @@ -0,0 +1,527 @@ +#include "selfdrive/frogpilot/ui/qt/offroad/model_settings.h" + +FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent) { + const std::vector> modelToggles { + {"AutomaticallyUpdateModels", tr("Automatically Update and Download Models"), tr("Automatically downloads new models and updates them if needed."), ""}, + + {"ModelRandomizer", tr("Model Randomizer"), tr("Randomly selects a model each drive and brings up a prompt at the end of the drive to review the model if it's longer than 15 minutes to help find your preferred model."), ""}, + {"ManageBlacklistedModels", tr("Manage Model Blacklist"), tr("Controls which models are blacklisted and won't be used for future drives."), ""}, + {"ResetScores", tr("Reset Model Scores"), tr("Clears the ratings you've given to the driving models."), ""}, + {"ReviewScores", tr("Review Model Scores"), tr("Displays the ratings you've assigned to the driving models."), ""}, + + {"DeleteModel", tr("Delete Model"), tr("Removes the selected driving model from your device."), ""}, + {"DownloadModel", tr("Download Model"), tr("Downloads the selected driving model."), ""}, + {"DownloadAllModels", tr("Download All Models"), tr("Downloads all undownloaded driving models."), ""}, + {"SelectModel", tr("Select Model"), tr("Selects which model openpilot uses to drive."), ""}, + {"ResetCalibrations", tr("Reset Model Calibrations"), tr("Resets calibration settings for the driving models."), ""}, + }; + + for (const auto &[param, title, desc, icon] : modelToggles) { + AbstractControl *modelToggle; + + if (param == "ModelRandomizer") { + FrogPilotParamManageControl *modelRandomizerToggle = new FrogPilotParamManageControl(param, title, desc, icon); + QObject::connect(modelRandomizerToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { + showToggles(modelRandomizerKeys); + updateModelLabels(); + }); + modelToggle = modelRandomizerToggle; + } else if (param == "ManageBlacklistedModels") { + FrogPilotButtonsControl *blacklistBtn = new FrogPilotButtonsControl(title, desc, {tr("ADD"), tr("REMOVE")}); + QObject::connect(blacklistBtn, &FrogPilotButtonsControl::buttonClicked, [this](int id) { + QStringList blacklistedModels = QString::fromStdString(params.get("BlacklistedModels")).split(",", QString::SkipEmptyParts); + QStringList selectableModels = availableModelNames; + + for (QString &model : blacklistedModels) { + selectableModels.removeAll(model); + if (model.contains("(Default)")) { + blacklistedModels.move(blacklistedModels.indexOf(model), 0); + } + } + + if (id == 0) { + if (selectableModels.size() == 1) { + FrogPilotConfirmationDialog::toggleAlert(tr("There's no more models to blacklist! The only available model is \"%1\"!").arg(selectableModels.first()), tr("OK"), this); + } else { + QString selectedModel = MultiOptionDialog::getSelection(tr("Select a model to add to the blacklist"), selectableModels, "", this); + if (!selectedModel.isEmpty()) { + if (ConfirmationDialog::confirm(tr("Are you sure you want to add the '%1' model to the blacklist?").arg(selectedModel), tr("Add"), this)) { + if (!blacklistedModels.contains(selectedModel)) { + blacklistedModels.append(selectedModel); + params.putNonBlocking("BlacklistedModels", blacklistedModels.join(",").toStdString()); + paramsStorage.putNonBlocking("BlacklistedModels", blacklistedModels.join(",").toStdString()); + } + } + } + } + } else if (id == 1) { + QString selectedModel = MultiOptionDialog::getSelection(tr("Select a model to remove from the blacklist"), blacklistedModels, "", this); + if (!selectedModel.isEmpty()) { + if (ConfirmationDialog::confirm(tr("Are you sure you want to remove the '%1' model from the blacklist?").arg(selectedModel), tr("Remove"), this)) { + if (blacklistedModels.contains(selectedModel)) { + blacklistedModels.removeAll(selectedModel); + params.putNonBlocking("BlacklistedModels", blacklistedModels.join(",").toStdString()); + paramsStorage.putNonBlocking("BlacklistedModels", blacklistedModels.join(",").toStdString()); + } + } + } + } + }); + modelToggle = blacklistBtn; + } else if (param == "ResetScores") { + ButtonControl *resetCalibrationsBtn = new ButtonControl(title, tr("RESET"), desc); + QObject::connect(resetCalibrationsBtn, &ButtonControl::clicked, [this]() { + if (FrogPilotConfirmationDialog::yesorno(tr("Reset all model scores?"), this)) { + for (const QString &model : availableModelNames) { + QString cleanedModel = processModelName(model); + params.remove(QString("%1Drives").arg(cleanedModel).toStdString()); + paramsStorage.remove(QString("%1Drives").arg(cleanedModel).toStdString()); + params.remove(QString("%1Score").arg(cleanedModel).toStdString()); + paramsStorage.remove(QString("%1Score").arg(cleanedModel).toStdString()); + } + updateModelLabels(); + } + }); + modelToggle = reinterpret_cast(resetCalibrationsBtn); + } else if (param == "ReviewScores") { + ButtonControl *reviewScoresBtn = new ButtonControl(title, tr("VIEW"), desc); + QObject::connect(reviewScoresBtn, &ButtonControl::clicked, [this]() { + openSubParentToggle(); + + for (LabelControl *label : labelControls) { + label->setVisible(true); + } + + for (auto &[key, toggle] : toggles) { + toggle->setVisible(false); + } + }); + modelToggle = reinterpret_cast(reviewScoresBtn); + } else if (param == "DeleteModel") { + deleteModelBtn = new ButtonControl(title, tr("DELETE"), desc); + QObject::connect(deleteModelBtn, &ButtonControl::clicked, [this]() { + QStringList deletableModels, existingModels = modelDir.entryList({"*.thneed"}, QDir::Files); + QMap labelToFileMap; + QString currentModel = QString::fromStdString(params.get("Model")) + ".thneed"; + + for (int i = 0; i < availableModels.size(); ++i) { + QString modelFile = availableModels[i] + ".thneed"; + if (existingModels.contains(modelFile) && modelFile != currentModel && !availableModelNames[i].contains("(Default)")) { + deletableModels.append(availableModelNames[i]); + labelToFileMap[availableModelNames[i]] = modelFile; + } + } + + QString selectedModel = MultiOptionDialog::getSelection(tr("Select a model to delete"), deletableModels, "", this); + if (!selectedModel.isEmpty()) { + if (ConfirmationDialog::confirm(tr("Are you sure you want to delete the '%1' model?").arg(selectedModel), tr("Delete"), this)) { + std::thread([=]() { + modelDeleting = true; + modelsDownloaded = false; + update(); + + params.putBoolNonBlocking("ModelsDownloaded", false); + deleteModelBtn->setValue(tr("Deleting...")); + + QFile::remove(modelDir.absoluteFilePath(labelToFileMap[selectedModel])); + deleteModelBtn->setValue(tr("Deleted!")); + + util::sleep_for(1000); + deleteModelBtn->setValue(""); + modelDeleting = false; + + QStringList modelFiles = modelDir.entryList({"*.thneed"}, QDir::Files); + modelFiles.removeAll(currentModel); + + haveModelsDownloaded = modelFiles.size() > 1; + update(); + }).detach(); + } + } + }); + modelToggle = reinterpret_cast(deleteModelBtn); + } else if (param == "DownloadModel") { + downloadModelBtn = new ButtonControl(title, tr("DOWNLOAD"), desc); + QObject::connect(downloadModelBtn, &ButtonControl::clicked, [this]() { + if (downloadModelBtn->text() == tr("CANCEL")) { + paramsMemory.remove("ModelToDownload"); + paramsMemory.putBool("CancelModelDownload", true); + cancellingDownload = true; + + device()->resetInteractiveTimeout(30); + } else { + QMap labelToModelMap; + QStringList existingModels = modelDir.entryList({"*.thneed"}, QDir::Files); + QStringList downloadableModels; + + for (int i = 0; i < availableModels.size(); ++i) { + QString modelFile = availableModels[i] + ".thneed"; + if (!existingModels.contains(modelFile) && !availableModelNames[i].contains("(Default)")) { + downloadableModels.append(availableModelNames[i]); + labelToModelMap.insert(availableModelNames[i], availableModels[i]); + } + } + + QString modelToDownload = MultiOptionDialog::getSelection(tr("Select a driving model to download"), downloadableModels, "", this); + if (!modelToDownload.isEmpty()) { + device()->resetInteractiveTimeout(300); + + modelDownloading = true; + paramsMemory.put("ModelToDownload", labelToModelMap.value(modelToDownload).toStdString()); + paramsMemory.put("ModelDownloadProgress", "0%"); + + downloadModelBtn->setValue(tr("Downloading %1...").arg(modelToDownload.remove(QRegularExpression("[🗺️👀📡]")).trimmed())); + + QTimer *progressTimer = new QTimer(this); + progressTimer->setInterval(100); + + QObject::connect(progressTimer, &QTimer::timeout, this, [=]() { + QString progress = QString::fromStdString(paramsMemory.get("ModelDownloadProgress")); + bool downloadComplete = progress.contains(QRegularExpression("downloaded", QRegularExpression::CaseInsensitiveOption)); + bool downloadFailed = progress.contains(QRegularExpression("cancelled|exists|failed|offline", QRegularExpression::CaseInsensitiveOption)); + + if (!progress.isEmpty() && progress != "0%") { + downloadModelBtn->setValue(progress); + } + + if (downloadComplete || downloadFailed) { + bool lastModelDownloaded = downloadComplete; + + if (downloadComplete) { + haveModelsDownloaded = true; + update(); + } + + if (downloadComplete) { + for (const QString &model : availableModels) { + if (!QFile::exists(modelDir.filePath(model + ".thneed"))) { + lastModelDownloaded = false; + break; + } + } + } + + downloadModelBtn->setValue(progress); + + paramsMemory.remove("CancelModelDownload"); + paramsMemory.remove("ModelDownloadProgress"); + + progressTimer->stop(); + progressTimer->deleteLater(); + + QTimer::singleShot(2000, this, [=]() { + cancellingDownload = false; + modelDownloading = false; + + downloadModelBtn->setValue(""); + + if (lastModelDownloaded) { + modelsDownloaded = true; + update(); + + params.putBoolNonBlocking("ModelsDownloaded", modelsDownloaded); + } + + device()->resetInteractiveTimeout(30); + }); + } + }); + progressTimer->start(); + } + } + }); + modelToggle = reinterpret_cast(downloadModelBtn); + } else if (param == "DownloadAllModels") { + downloadAllModelsBtn = new ButtonControl(title, tr("DOWNLOAD"), desc); + QObject::connect(downloadAllModelsBtn, &ButtonControl::clicked, [this]() { + if (downloadAllModelsBtn->text() == tr("CANCEL")) { + paramsMemory.remove("DownloadAllModels"); + paramsMemory.putBool("CancelModelDownload", true); + cancellingDownload = true; + + device()->resetInteractiveTimeout(30); + } else { + device()->resetInteractiveTimeout(300); + + startDownloadAllModels(); + } + }); + modelToggle = reinterpret_cast(downloadAllModelsBtn); + } else if (param == "SelectModel") { + selectModelBtn = new ButtonControl(title, tr("SELECT"), desc); + QObject::connect(selectModelBtn, &ButtonControl::clicked, [this]() { + QSet modelFilesBaseNames = QSet::fromList(modelDir.entryList({"*.thneed"}, QDir::Files).replaceInStrings(QRegExp("\\.thneed$"), "")); + QStringList selectableModels; + + for (int i = 0; i < availableModels.size(); ++i) { + if (modelFilesBaseNames.contains(availableModels[i]) || availableModelNames[i].contains("(Default)")) { + selectableModels.append(availableModelNames[i]); + } + } + + QString modelToSelect = MultiOptionDialog::getSelection(tr("Select a model - 🗺️ = Navigation | 📡 = Radar | 👀 = VOACC"), selectableModels, "", this); + if (!modelToSelect.isEmpty()) { + selectModelBtn->setValue(modelToSelect); + int modelIndex = availableModelNames.indexOf(modelToSelect); + + params.put("Model", availableModels.at(modelIndex).toStdString()); + params.put("ModelName", modelToSelect.toStdString()); + + if (experimentalModels.contains(availableModels.at(modelIndex))) { + FrogPilotConfirmationDialog::toggleAlert(tr("WARNING: This is a very experimental model and may drive dangerously!"), tr("I understand the risks."), this); + } + + QString model = availableModelNames.at(modelIndex); + QString part_model_param = processModelName(model); + + if (!params.checkKey(part_model_param.toStdString() + "CalibrationParams") || !params.checkKey(part_model_param.toStdString() + "LiveTorqueParameters")) { + if (FrogPilotConfirmationDialog::yesorno(tr("Start with a fresh calibration for the newly selected model?"), this)) { + params.remove("CalibrationParams"); + params.remove("LiveTorqueParameters"); + } + } + + if (started) { + if (FrogPilotConfirmationDialog::toggle(tr("Reboot required to take effect."), tr("Reboot Now"), this)) { + Hardware::reboot(); + } + } + + updateFrogPilotToggles(); + } + }); + selectModelBtn->setValue(QString::fromStdString(params.get("ModelName"))); + modelToggle = reinterpret_cast(selectModelBtn); + } else if (param == "ResetCalibrations") { + FrogPilotButtonsControl *resetCalibrationsBtn = new FrogPilotButtonsControl(title, desc, {tr("RESET ALL"), tr("RESET ONE")}); + QObject::connect(resetCalibrationsBtn, &FrogPilotButtonsControl::showDescriptionEvent, this, &FrogPilotModelPanel::updateCalibrationDescription); + QObject::connect(resetCalibrationsBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) { + if (id == 0) { + if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset all of your model calibrations?"), this)) { + for (const QString &model : availableModelNames) { + QString cleanedModel = processModelName(model); + params.remove(QString("%1CalibrationParams").arg(cleanedModel).toStdString()); + paramsStorage.remove(QString("%1CalibrationParams").arg(cleanedModel).toStdString()); + params.remove(QString("%1LiveTorqueParameters").arg(cleanedModel).toStdString()); + paramsStorage.remove(QString("%1LiveTorqueParameters").arg(cleanedModel).toStdString()); + } + } + } else if (id == 1) { + QStringList selectableModelLabels; + for (int i = 0; i < availableModels.size(); ++i) { + selectableModelLabels.append(availableModelNames[i]); + } + + QString modelToReset = MultiOptionDialog::getSelection(tr("Select a model to reset"), selectableModelLabels, "", this); + if (!modelToReset.isEmpty()) { + if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely reset this model's calibrations?"), this)) { + QString cleanedModel = processModelName(modelToReset); + params.remove(QString("%1CalibrationParams").arg(cleanedModel).toStdString()); + paramsStorage.remove(QString("%1CalibrationParams").arg(cleanedModel).toStdString()); + params.remove(QString("%1LiveTorqueParameters").arg(cleanedModel).toStdString()); + paramsStorage.remove(QString("%1LiveTorqueParameters").arg(cleanedModel).toStdString()); + } + } + } + }); + modelToggle = resetCalibrationsBtn; + + } else { + modelToggle = new ParamControl(param, title, desc, icon); + } + + addItem(modelToggle); + toggles[param] = modelToggle; + + makeConnections(modelToggle); + + if (FrogPilotParamManageControl *frogPilotManageToggle = qobject_cast(modelToggle)) { + QObject::connect(frogPilotManageToggle, &FrogPilotParamManageControl::manageButtonClicked, this, &FrogPilotModelPanel::openParentToggle); + } + + QObject::connect(modelToggle, &AbstractControl::showDescriptionEvent, [this]() { + update(); + }); + } + + QObject::connect(static_cast(toggles["ModelRandomizer"]), &ToggleControl::toggleFlipped, [this](bool state) { + modelRandomizer = state; + if (state && !modelsDownloaded) { + if (FrogPilotConfirmationDialog::yesorno(tr("The 'Model Randomizer' only works with downloaded models. Do you want to download all the driving models?"), this)) { + startDownloadAllModels(); + } + } + }); + + QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotModelPanel::hideToggles); + QObject::connect(parent, &FrogPilotSettingsWindow::closeSubParentToggle, this, &FrogPilotModelPanel::hideSubToggles); + QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotModelPanel::updateState); + + hideToggles(); +} + +void FrogPilotModelPanel::showEvent(QShowEvent *event) { + QString currentModel = QString::fromStdString(params.get("Model")) + ".thneed"; + + availableModelNames = QString::fromStdString(params.get("AvailableModelsNames")).split(","); + availableModels = QString::fromStdString(params.get("AvailableModels")).split(","); + experimentalModels = QString::fromStdString(params.get("ExperimentalModels")).split(","); + + modelRandomizer = params.getBool("ModelRandomizer"); + modelsDownloaded = params.getBool("ModelsDownloaded"); + + QStringList modelFiles = modelDir.entryList({"*.thneed"}, QDir::Files); + modelFiles.removeAll(currentModel); + haveModelsDownloaded = modelFiles.size() > 1; +} + +void FrogPilotModelPanel::updateState(const UIState &s) { + if (!isVisible()) return; + + downloadAllModelsBtn->setText(modelDownloading && allModelsDownloading ? tr("CANCEL") : tr("DOWNLOAD")); + downloadModelBtn->setText(modelDownloading && !allModelsDownloading ? tr("CANCEL") : tr("DOWNLOAD")); + + deleteModelBtn->setEnabled(!modelDeleting && !modelDownloading); + downloadAllModelsBtn->setEnabled(s.scene.online && !cancellingDownload && !modelDeleting && (!modelDownloading || allModelsDownloading) && !modelsDownloaded); + downloadModelBtn->setEnabled(s.scene.online && !cancellingDownload && !modelDeleting && !allModelsDownloading && !modelsDownloaded); + selectModelBtn->setEnabled(!modelDeleting && !modelDownloading && !modelRandomizer); + + started = s.scene.started; +} + +void FrogPilotModelPanel::startDownloadAllModels() { + allModelsDownloading = true; + modelDownloading = true; + + paramsMemory.putBool("DownloadAllModels", true); + + downloadAllModelsBtn->setValue(tr("Downloading models...")); + + QTimer *progressTimer = new QTimer(this); + progressTimer->setInterval(100); + + QObject::connect(progressTimer, &QTimer::timeout, this, [=]() { + QString progress = QString::fromStdString(paramsMemory.get("ModelDownloadProgress")); + bool downloadComplete = progress.contains(QRegularExpression("All models downloaded!", QRegularExpression::CaseInsensitiveOption)); + bool downloadFailed = progress.contains(QRegularExpression("cancelled|exists|failed|offline", QRegularExpression::CaseInsensitiveOption)); + + if (!progress.isEmpty() && progress != "0%") { + downloadAllModelsBtn->setValue(progress); + } + + if (downloadComplete || downloadFailed) { + if (downloadComplete) { + haveModelsDownloaded = true; + update(); + } + + downloadAllModelsBtn->setValue(progress); + + paramsMemory.remove("CancelModelDownload"); + paramsMemory.remove("ModelDownloadProgress"); + + progressTimer->stop(); + progressTimer->deleteLater(); + + QTimer::singleShot(2000, this, [=]() { + cancellingDownload = false; + modelDownloading = false; + + paramsMemory.remove("DownloadAllModels"); + + downloadAllModelsBtn->setValue(""); + + device()->resetInteractiveTimeout(30); + }); + } + }); + progressTimer->start(); +} + +void FrogPilotModelPanel::updateCalibrationDescription() { + QString model = QString::fromStdString(params.get("ModelName")); + QString part_model_param = processModelName(model); + + QString desc = + tr("openpilot requires the device to be mounted within 4° left or right and " + "within 5° up or 9° down. openpilot is continuously calibrating, resetting is rarely required."); + std::string calib_bytes = params.get(part_model_param.toStdString() + "CalibrationParams"); + if (!calib_bytes.empty()) { + try { + AlignedBuffer aligned_buf; + capnp::FlatArrayMessageReader cmsg(aligned_buf.align(calib_bytes.data(), calib_bytes.size())); + auto calib = cmsg.getRoot().getLiveCalibration(); + if (calib.getCalStatus() != cereal::LiveCalibrationData::Status::UNCALIBRATED) { + double pitch = calib.getRpyCalib()[1] * (180 / M_PI); + double yaw = calib.getRpyCalib()[2] * (180 / M_PI); + desc += tr(" Your device is pointed %1° %2 and %3° %4.") + .arg(QString::number(std::abs(pitch), 'g', 1), pitch > 0 ? tr("down") : tr("up"), + QString::number(std::abs(yaw), 'g', 1), yaw > 0 ? tr("left") : tr("right")); + } + } catch (kj::Exception) { + qInfo() << "invalid CalibrationParams"; + } + } + qobject_cast(sender())->setDescription(desc); +} + +void FrogPilotModelPanel::updateModelLabels() { + QVector> modelScores; + for (QString &model : availableModelNames) { + QString cleanedModel = processModelName(model); + int score = params.getInt((cleanedModel + "Score").toStdString()); + + if (model.contains("(Default)")) { + modelScores.prepend(qMakePair(model, score)); + } else { + modelScores.append(qMakePair(model, score)); + } + } + + labelControls.clear(); + + for (QPair &pair : modelScores) { + QString scoreDisplay = pair.second == 0 ? "N/A" : QString::number(pair.second) + "%"; + LabelControl *labelControl = new LabelControl(pair.first, scoreDisplay, ""); + labelControls.append(labelControl); + addItem(labelControl); + } + + for (LabelControl *label : labelControls) { + label->setVisible(false); + } +} + +void FrogPilotModelPanel::showToggles(const std::set &keys) { + setUpdatesEnabled(false); + + for (auto &[key, toggle] : toggles) { + toggle->setVisible(keys.find(key) != keys.end()); + } + + setUpdatesEnabled(true); + update(); +} + +void FrogPilotModelPanel::hideToggles() { + setUpdatesEnabled(false); + + for (LabelControl *label : labelControls) { + label->setVisible(false); + } + + for (auto &[key, toggle] : toggles) { + bool subToggles = modelRandomizerKeys.find(key) != modelRandomizerKeys.end(); + + toggle->setVisible(!subToggles); + } + + setUpdatesEnabled(true); + update(); +} + +void FrogPilotModelPanel::hideSubToggles() { + for (LabelControl *label : labelControls) { + label->setVisible(false); + } +} diff --git a/selfdrive/frogpilot/ui/qt/offroad/model_settings.h b/selfdrive/frogpilot/ui/qt/offroad/model_settings.h new file mode 100644 index 00000000000000..4b7fd1d7c64a90 --- /dev/null +++ b/selfdrive/frogpilot/ui/qt/offroad/model_settings.h @@ -0,0 +1,60 @@ +#pragma once + +#include + +#include "selfdrive/frogpilot/ui/qt/offroad/frogpilot_settings.h" + +class FrogPilotModelPanel : public FrogPilotListWidget { + Q_OBJECT + +public: + explicit FrogPilotModelPanel(FrogPilotSettingsWindow *parent); + +signals: + void openParentToggle(); + void openSubParentToggle(); + +protected: + void showEvent(QShowEvent *event) override; + +private: + void hideSubToggles(); + void hideToggles(); + void showToggles(const std::set &keys); + void startDownloadAllModels(); + void updateCalibrationDescription(); + void updateModelLabels(); + void updateState(const UIState &s); + + std::set modelRandomizerKeys = { + "ManageBlacklistedModels", "ResetScores", "ReviewScores" + }; + + ButtonControl *deleteModelBtn; + ButtonControl *downloadAllModelsBtn; + ButtonControl *downloadModelBtn; + ButtonControl *selectModelBtn; + + Params params; + Params paramsMemory{"/dev/shm/params"}; + Params paramsStorage{"/persist/params"}; + + QDir modelDir{"/data/models/"}; + + QList labelControls; + + QStringList availableModelNames; + QStringList availableModels; + QStringList experimentalModels; + + bool allModelsDownloading; + bool cancellingDownload; + bool haveModelsDownloaded; + bool modelDeleting; + bool modelDownloading; + bool modelRandomizer; + bool modelsDownloaded; + bool started; + + std::map toggles; +}; diff --git a/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.cc index 4aaf032cd40de2..e161c0c780533a 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.cc @@ -11,12 +11,12 @@ FrogPilotSoundsPanel::FrogPilotSoundsPanel(FrogPilotSettingsWindow *parent) : Fr {"WarningSoftVolume", tr("Warning Soft Volume"), tr("Related alerts:\n\nBRAKE!, Risk of Collision\nTAKE CONTROL IMMEDIATELY"), ""}, {"WarningImmediateVolume", tr("Warning Immediate Volume"), tr("Related alerts:\n\nDISENGAGE IMMEDIATELY, Driver Distracted\nDISENGAGE IMMEDIATELY, Driver Unresponsive"), ""}, - {"CustomAlerts", tr("Custom Alerts"), tr("Enable custom alerts for openpilot events."), "../frogpilot/assets/toggle_icons/icon_green_light.png"}, - {"GoatScream", tr("Goat Scream Steering Saturated Alert"), tr("Enable the famed 'Goat Scream' that has brought both joy and anger to FrogPilot users all around the world!"), ""}, - {"GreenLightAlert", tr("Green Light Alert"), tr("Get an alert when a traffic light changes from red to green."), ""}, - {"LeadDepartingAlert", tr("Lead Departing Alert"), tr("Get an alert when the lead vehicle starts departing when at a standstill."), ""}, - {"LoudBlindspotAlert", tr("Loud Blindspot Alert"), tr("Enable a louder alert for when a vehicle is detected in the blindspot when attempting to change lanes."), ""}, - {"SpeedLimitChangedAlert", tr("Speed Limit Change Alert"), tr("Trigger an alert when the speed limit changes."), ""}, + {"CustomAlerts", tr("Custom Alerts"), tr("Custom alerts for openpilot events."), "../frogpilot/assets/toggle_icons/icon_green_light.png"}, + {"GoatScream", tr("Goat Scream Steering Saturated Alert"), tr("Enables the famed 'Goat Scream' that has brought both joy and anger to FrogPilot users all around the world!"), ""}, + {"GreenLightAlert", tr("Green Light Alert"), tr("Plays an alert when a traffic light changes from red to green."), ""}, + {"LeadDepartingAlert", tr("Lead Departing Alert"), tr("Plays an alert when the lead vehicle starts departing when at a standstill."), ""}, + {"LoudBlindspotAlert", tr("Loud Blindspot Alert"), tr("Plays a louder alert for when a vehicle is detected in the blindspot when attempting to change lanes."), ""}, + {"SpeedLimitChangedAlert", tr("Speed Limit Change Alert"), tr("Plays an alert when the speed limit changes."), ""}, }; for (const auto &[param, title, desc, icon] : soundsToggles) { @@ -29,10 +29,14 @@ FrogPilotSoundsPanel::FrogPilotSoundsPanel(FrogPilotSettingsWindow *parent) : Fr }); soundsToggle = alertVolumeControlToggle; } else if (alertVolumeControlKeys.find(param) != alertVolumeControlKeys.end()) { + std::map volumeLabels; + for (int i = 0; i <= 101; ++i) { + volumeLabels[i] = i == 101 ? tr("Auto") : i == 0 ? tr("Muted") : QString::number(i) + "%"; + } if (param == "WarningImmediateVolume") { - soundsToggle = new FrogPilotParamValueControl(param, title, desc, icon, 25, 100, "%"); + soundsToggle = new FrogPilotParamValueControl(param, title, desc, icon, 25, 101, QString(), volumeLabels); } else { - soundsToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, "%"); + soundsToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 101, QString(), volumeLabels); } } else if (param == "CustomAlerts") { @@ -74,6 +78,12 @@ FrogPilotSoundsPanel::FrogPilotSoundsPanel(FrogPilotSettingsWindow *parent) : Fr QObject::connect(parent, &FrogPilotSettingsWindow::updateCarToggles, this, &FrogPilotSoundsPanel::updateCarToggles); } +void FrogPilotSoundsPanel::showEvent(QShowEvent *event) { + customizationLevel = parent->customizationLevel; + + toggles["AlertVolumeControl"]->setVisible(customizationLevel == 2); +} + void FrogPilotSoundsPanel::updateCarToggles() { hasBSM = parent->hasBSM; hasOpenpilotLongitudinal = parent->hasOpenpilotLongitudinal; @@ -101,6 +111,8 @@ void FrogPilotSoundsPanel::hideToggles() { toggle->setVisible(!subToggles); } + toggles["AlertVolumeControl"]->setVisible(customizationLevel == 2); + setUpdatesEnabled(true); update(); } diff --git a/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h b/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h index 7414eeed24d7d1..11929d599c4d55 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/sounds_settings.h @@ -15,6 +15,7 @@ class FrogPilotSoundsPanel : public FrogPilotListWidget { private: void hideToggles(); + void showEvent(QShowEvent *event) override; void showToggles(const std::set &keys); void updateCarToggles(); @@ -36,5 +37,7 @@ class FrogPilotSoundsPanel : public FrogPilotListWidget { bool hasBSM; bool hasOpenpilotLongitudinal; + int customizationLevel; + std::map toggles; }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/theme_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/theme_settings.cc index 88fb36a9fb9e7e..cb8e8da7dfdb88 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/theme_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/theme_settings.cc @@ -3,19 +3,19 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { const std::vector> themeToggles { {"PersonalizeOpenpilot", tr("Custom Theme"), tr("Custom openpilot themes."), "../frogpilot/assets/toggle_icons/frog.png"}, - {"CustomColors", tr("Color Scheme"), tr("Themed color schemes.\n\nWant to submit your own color scheme? Share it in the 'feature-request' channel on the FrogPilot Discord!"), ""}, - {"CustomDistanceIcon", "Distance Button", "Themed distance button icons.\n\nWant to submit your own icon pack? Share it in the 'feature-request' channel on the FrogPilot Discord!", ""}, - {"CustomIcons", tr("Icon Pack"), tr("Themed icon packs.\n\nWant to submit your own icons? Share them in the 'feature-request' channel on the FrogPilot Discord!"), ""}, - {"CustomSounds", tr("Sound Pack"), tr("Themed sound effects.\n\nWant to submit your own sounds? Share them in the 'feature-request' channel on the FrogPilot Discord!"), ""}, - {"WheelIcon", tr("Steering Wheel"), tr("Custom steering wheel icons."), ""}, - {"CustomSignals", tr("Turn Signal Animation"), tr("Themed turn signal animations.\n\nWant to submit your own animations? Share them in the 'feature-request' channel on the FrogPilot Discord!"), ""}, + {"CustomColors", tr("Color Scheme"), tr("Changes out openpilot's color scheme.\n\nWant to submit your own color scheme? Share it in the 'custom-themes' channel on the FrogPilot Discord!"), ""}, + {"CustomDistanceIcons", "Distance Button", "Changes out openpilot's distance button icons.\n\nWant to submit your own icon pack? Share it in the 'custom-themes' channel on the FrogPilot Discord!", ""}, + {"CustomIcons", tr("Icon Pack"), tr("Changes out openpilot's icon pack.\n\nWant to submit your own icons? Share them in the 'custom-themes' channel on the FrogPilot Discord!"), ""}, + {"CustomSounds", tr("Sound Pack"), tr("Changes out openpilot's sound effects.\n\nWant to submit your own sounds? Share them in the 'custom-themes' channel on the FrogPilot Discord!"), ""}, + {"WheelIcon", tr("Steering Wheel"), tr("Enables a custom steering wheel icon in the top right of the screen."), ""}, + {"CustomSignals", tr("Turn Signal Animation"), tr("Enables themed turn signal animations.\n\nWant to submit your own animations? Share them in the 'custom-themes' channel on the FrogPilot Discord!"), ""}, {"DownloadStatusLabel", tr("Download Status"), "", ""}, - {"HolidayThemes", tr("Holiday Themes"), tr("Change the openpilot theme based on the current holiday. Minor holidays last one day, while major holidays (Easter, Christmas, Halloween, etc.) last a week."), "../frogpilot/assets/toggle_icons/icon_calendar.png"}, + {"HolidayThemes", tr("Holiday Themes"), tr("Changes the openpilot theme based on the current holiday. Minor holidays last one day, while major holidays (Easter, Christmas, Halloween, etc.) last the entire week."), "../frogpilot/assets/toggle_icons/icon_calendar.png"}, - {"RandomEvents", tr("Random Events"), tr("Random cosmetic events that happen during certain driving conditions. These events are purely for fun and don't affect driving controls!"), "../frogpilot/assets/toggle_icons/icon_random.png"}, + {"RandomEvents", tr("Random Events"), tr("Enables random cosmetic events that happen during certain driving conditions. These events are purely for fun and don't affect driving controls!"), "../frogpilot/assets/toggle_icons/icon_random.png"}, - {"StartupAlert", tr("Startup Alert"), tr("Custom 'Startup' alert message that appears when you start driving."), "../frogpilot/assets/toggle_icons/icon_message.png"} + {"StartupAlert", tr("Startup Alert"), tr("Controls the text of the 'Startup' alert message that appears when you start the drive."), "../frogpilot/assets/toggle_icons/icon_message.png"} }; for (const auto &[param, title, desc, icon] : themeToggles) { @@ -162,7 +162,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr } manageCustomColorsBtn->setValue(currentColor); themeToggle = manageCustomColorsBtn; - } else if (param == "CustomDistanceIcon") { + } else if (param == "CustomDistanceIcons") { manageDistanceIconsBtn = new FrogPilotButtonsControl(title, desc, {tr("DELETE"), tr("DOWNLOAD"), tr("SELECT")}); std::function formatIconName = [](QString name) -> QString { @@ -847,10 +847,10 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr params.put("StartupMessageTop", frogpilotTop.toStdString()); params.put("StartupMessageBottom", frogpilotBottom.toStdString()); } else if (id == 2) { - QString newTop = InputDialog::getText(tr("Enter your text for the top half"), this, tr("Characters: 0/%1").arg(maxLengthTop), false, -1, currentTop, maxLengthTop).trimmed(); + QString newTop = InputDialog::getText(tr("Enter the text for the top half"), this, tr("Characters: 0/%1").arg(maxLengthTop), false, -1, currentTop, maxLengthTop).trimmed(); if (newTop.length() > 0) { params.putNonBlocking("StartupMessageTop", newTop.toStdString()); - QString newBottom = InputDialog::getText(tr("Enter your text for the bottom half"), this, tr("Characters: 0/%1").arg(maxLengthBottom), false, -1, currentBottom, maxLengthBottom).trimmed(); + QString newBottom = InputDialog::getText(tr("Enter the text for the bottom half"), this, tr("Characters: 0/%1").arg(maxLengthBottom), false, -1, currentBottom, maxLengthBottom).trimmed(); if (newBottom.length() > 0) { params.putNonBlocking("StartupMessageBottom", newBottom.toStdString()); } @@ -859,6 +859,8 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr params.remove("StartupMessageTop"); params.remove("StartupMessageBottom"); } + + updateFrogPilotToggles(); }); themeToggle = startupAlertButton; @@ -889,7 +891,6 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr }); QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotThemesPanel::hideToggles); - QObject::connect(parent, &FrogPilotSettingsWindow::updateCarToggles, this, &FrogPilotThemesPanel::updateCarToggles); QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotThemesPanel::updateState); } @@ -900,17 +901,17 @@ void FrogPilotThemesPanel::showEvent(QShowEvent *event) { signalsDownloaded = params.get("DownloadableSignals").empty(); soundsDownloaded = params.get("DownloadableSounds").empty(); wheelsDownloaded = params.get("DownloadableWheels").empty(); -} -void FrogPilotThemesPanel::updateCarToggles() { - disableOpenpilotLongitudinal = parent->disableOpenpilotLongitudinal; - hasOpenpilotLongitudinal = parent->hasOpenpilotLongitudinal; + customizationLevel = parent->customizationLevel; - hideToggles(); + toggles["RandomEvents"]->setVisible(customizationLevel != 0); + toggles["StartupAlert"]->setVisible(customizationLevel == 2); } void FrogPilotThemesPanel::updateState(const UIState &s) { - if (!isVisible()) return; + if (!isVisible()) { + return; + } if (personalizeOpenpilotOpen) { if (themeDownloading) { @@ -999,6 +1000,9 @@ void FrogPilotThemesPanel::hideToggles() { toggle->setVisible(!subToggles); } + toggles["RandomEvents"]->setVisible(customizationLevel != 0); + toggles["StartupAlert"]->setVisible(customizationLevel == 2); + setUpdatesEnabled(true); update(); } diff --git a/selfdrive/frogpilot/ui/qt/offroad/theme_settings.h b/selfdrive/frogpilot/ui/qt/offroad/theme_settings.h index ebfe25478c46d1..7c08fecba860b1 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/theme_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/theme_settings.h @@ -19,11 +19,10 @@ class FrogPilotThemesPanel : public FrogPilotListWidget { private: void hideToggles(); void showToggles(const std::set &keys); - void updateCarToggles(); void updateState(const UIState &s); std::set customThemeKeys = { - "CustomColors", "CustomDistanceIcon", "CustomIcons", + "CustomColors", "CustomDistanceIcons", "CustomIcons", "CustomSignals", "CustomSounds", "DownloadStatusLabel", "WheelIcon" }; @@ -45,10 +44,8 @@ class FrogPilotThemesPanel : public FrogPilotListWidget { bool cancellingDownload; bool colorDownloading; bool colorsDownloaded; - bool disableOpenpilotLongitudinal; bool distanceIconDownloading; bool distanceIconsDownloaded; - bool hasOpenpilotLongitudinal; bool iconDownloading; bool iconsDownloaded; bool personalizeOpenpilotOpen; @@ -61,5 +58,7 @@ class FrogPilotThemesPanel : public FrogPilotListWidget { bool wheelDownloading; bool wheelsDownloaded; + int customizationLevel; + std::map toggles; }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/utilities.cc b/selfdrive/frogpilot/ui/qt/offroad/utilities.cc index 5b8462fb8d6cfc..b64a62db60d1c2 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/utilities.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/utilities.cc @@ -3,7 +3,7 @@ #include "selfdrive/frogpilot/ui/qt/offroad/utilities.h" UtilitiesPanel::UtilitiesPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent) { - ButtonControl *flashPandaBtn = new ButtonControl(tr("Flash Panda"), tr("FLASH"), tr("Use this button to flash the Panda device's firmware if you're running into issues.")); + ButtonControl *flashPandaBtn = new ButtonControl(tr("Flash Panda"), tr("FLASH"), tr("Flashes the Panda device's firmware if you're running into issues.")); QObject::connect(flashPandaBtn, &ButtonControl::clicked, [=]() { if (ConfirmationDialog::confirm(tr("Are you sure you want to flash the Panda?"), tr("Flash"), this)) { std::thread([=]() { @@ -26,7 +26,7 @@ UtilitiesPanel::UtilitiesPanel(FrogPilotSettingsWindow *parent) : FrogPilotListW }); addItem(flashPandaBtn); - forceStartedBtn = new FrogPilotButtonsControl(tr("Force Started State"), tr("Force openpilot either offroad or onroad."), {tr("OFFROAD"), tr("ONROAD"), tr("OFF")}, true); + forceStartedBtn = new FrogPilotButtonsControl(tr("Force Started State"), tr("Forces openpilot either offroad or onroad."), {tr("OFFROAD"), tr("ONROAD"), tr("OFF")}, true); QObject::connect(forceStartedBtn, &FrogPilotButtonsControl::buttonClicked, [=](int id) { if (id == 0) { paramsMemory.putBool("ForceOffroad", true); @@ -43,7 +43,7 @@ UtilitiesPanel::UtilitiesPanel(FrogPilotSettingsWindow *parent) : FrogPilotListW forceStartedBtn->setCheckedButton(2); addItem(forceStartedBtn); - ButtonControl *resetTogglesBtn = new ButtonControl(tr("Reset Toggles to Default"), tr("RESET"), tr("Reset your toggle settings back to their default settings.")); + ButtonControl *resetTogglesBtn = new ButtonControl(tr("Reset Toggles to Default"), tr("RESET"), tr("Resets your toggle settings back to their default settings.")); QObject::connect(resetTogglesBtn, &ButtonControl::clicked, [=]() { if (ConfirmationDialog::confirm(tr("Are you sure you want to completely reset all of your toggle settings?"), tr("Reset"), this)) { std::thread([=] { diff --git a/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.cc index d9ed9c4eaebc84..5589deee0eb25f 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.cc @@ -110,11 +110,11 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent) addItem(selectModelButton); selectModelButton->setVisible(false); - ParamControl *forceFingerprint = new ParamControl("ForceFingerprint", tr("Disable Automatic Fingerprint Detection"), tr("Forces the selected fingerprint and prevents it from ever changing."), ""); + forceFingerprint = new ParamControl("ForceFingerprint", tr("Disable Automatic Fingerprint Detection"), tr("Forces the selected fingerprint and prevents it from ever changing."), ""); addItem(forceFingerprint); bool disableOpenpilotLongState = params.getBool("DisableOpenpilotLongitudinal"); - disableOpenpilotLong = new ToggleControl(tr("Disable openpilot Longitudinal Control"), tr("Disable openpilot longitudinal control and use stock ACC instead."), "", disableOpenpilotLongState); + disableOpenpilotLong = new ToggleControl(tr("Disable openpilot Longitudinal Control"), tr("Disables openpilot longitudinal control and uses the car's stock ACC instead."), "", disableOpenpilotLongState); QObject::connect(disableOpenpilotLong, &ToggleControl::toggleFlipped, [this](bool state) { if (state) { if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to completely disable openpilot longitudinal control?"), this)) { @@ -134,20 +134,20 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent) addItem(disableOpenpilotLong); std::vector> vehicleToggles { - {"VoltSNG", tr("2017 Volt Stop and Go Hack"), tr("Force stop and go for the 2017 Chevy Volt."), ""}, - {"ExperimentalGMTune", tr("Experimental GM Tune"), tr("FrogsGoMoo's experimental GM tune that is based on nothing but guesswork. Use at your own risk!"), ""}, - {"LongPitch", tr("Uphill/Downhill Smoothing"), tr("Smoothen the car’s gas and brake response when driving on slopes."), ""}, - {"NewLongAPIGM", tr("Use comma's New Longitudinal API"), tr("Comma's new longitudinal control system that has shown great improvement with acceleration and braking, but has a few issues on some GM vehicles."), ""}, + {"VoltSNG", tr("2017 Volt Stop and Go Hack"), tr("Forces stop and go for the 2017 Chevy Volt."), ""}, + {"ExperimentalGMTune", tr("Experimental GM Tune"), tr("Enables FrogsGoMoo's experimental GM tune that is based on nothing but guesswork. Use at your own risk!"), ""}, + {"LongPitch", tr("Uphill/Downhill Smoothing"), tr("Smoothens the gas and brake response when driving on slopes."), ""}, + {"NewLongAPIGM", tr("Use comma's New Longitudinal API"), tr("Enables comma's new control system that has shown great improvement with acceleration and braking, but has a few issues on some GM vehicles."), ""}, - {"NewLongAPI", tr("Use comma's New Longitudinal API"), tr("Use comma's new longitudinal control system that has shown great improvement with acceleration and braking, but has a few issues on Hyundai/Kia/Genesis."), ""}, + {"NewLongAPI", tr("Use comma's New Longitudinal API"), tr("Enables comma's new control system that has shown great improvement with acceleration and braking, but has a few issues on Hyundai/Kia/Genesis."), ""}, {"CrosstrekTorque", tr("Subaru Crosstrek Torque Increase"), tr("Increases the maximum allowed torque for the Subaru Crosstrek."), ""}, - {"ToyotaDoors", tr("Automatically Lock/Unlock Doors"), tr("Automatically lock the doors when in drive and unlock when in park."), ""}, - {"ClusterOffset", tr("Cluster Speed Offset"), tr("Set the cluster offset openpilot uses to try and match the speed displayed on the dash."), ""}, - {"NewToyotaTune", tr("comma's New Toyota/Lexus Tune"), tr("Activate comma's latest Toyota tuning, expertly crafted by Shane for enhanced vehicle performance."), ""}, - {"FrogsGoMoosTweak", tr("FrogsGoMoo's Personal Tweaks"), tr("Use FrogsGoMoo's personal tweaks to the Toyota tune focused around his 2019 Lexus ES 350 to take off a bit quicker and stop a bit smoother."), ""}, - {"SNGHack", tr("Stop and Go Hack"), tr("Force stop and go for vehicles without stock stop and go functionality."), ""}, + {"ToyotaDoors", tr("Automatically Lock/Unlock Doors"), tr("Automatically locks the doors when in drive and unlocks when in park."), ""}, + {"ClusterOffset", tr("Cluster Speed Offset"), tr("Sets the cluster offset openpilot uses to try and match the speed displayed on the dash."), ""}, + {"NewToyotaTune", tr("comma's New Toyota/Lexus Tune"), tr("Enables comma's latest Toyota tuning, expertly crafted by Shane for enhanced vehicle performance."), ""}, + {"FrogsGoMoosTweak", tr("FrogsGoMoo's Personal Tweaks"), tr("Enables FrogsGoMoo's personal tweaks to the Toyota tune focused around his 2019 Lexus ES 350 to take off a bit quicker and stop a bit smoother."), ""}, + {"SNGHack", tr("Stop and Go Hack"), tr("Forces stop and go for vehicles without stock stop and go functionality."), ""}, }; for (const auto &[param, title, desc, icon] : vehicleToggles) { @@ -159,7 +159,7 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent) vehicleToggle = new FrogPilotButtonToggleControl(param, title, desc, lockToggles, lockToggleNames); } else if (param == "ClusterOffset") { std::vector clusterOffsetToggleNames{"Reset"}; - vehicleToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 1.000, 1.050, "x", std::map(), 0.001, {}, clusterOffsetToggleNames, false); + vehicleToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 1.000, 1.050, "x", std::map(), 0.001, {}, clusterOffsetToggleNames, false, false); } else { vehicleToggle = new ParamControl(param, title, desc, icon); @@ -216,11 +216,18 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent) QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotVehiclesPanel::updateState); } +void FrogPilotVehiclesPanel::showEvent(QShowEvent *event) { + customizationLevel = parent->customizationLevel; + + updateToggles(); +} + void FrogPilotVehiclesPanel::updateCarToggles() { disableOpenpilotLongitudinal = parent->disableOpenpilotLongitudinal; hasExperimentalOpenpilotLongitudinal = parent->hasExperimentalOpenpilotLongitudinal; hasOpenpilotLongitudinal = parent->hasOpenpilotLongitudinal; hasSNG = parent->hasSNG; + isBolt = parent->isBolt; isGMPCMCruise = parent->isGMPCMCruise; isImpreza = parent->isImpreza; isToyotaTuneSupported = parent->isToyotaTuneSupported; @@ -230,7 +237,9 @@ void FrogPilotVehiclesPanel::updateCarToggles() { } void FrogPilotVehiclesPanel::updateState(const UIState &s) { - if (!isVisible()) return; + if (!isVisible()) { + return; + } started = s.scene.started; } @@ -243,7 +252,8 @@ void FrogPilotVehiclesPanel::setModels() { void FrogPilotVehiclesPanel::updateToggles() { setUpdatesEnabled(false); - disableOpenpilotLong->setVisible((hasOpenpilotLongitudinal && !hasExperimentalOpenpilotLongitudinal && !isGMPCMCruise) || disableOpenpilotLongitudinal); + disableOpenpilotLong->setVisible((hasOpenpilotLongitudinal && !hasExperimentalOpenpilotLongitudinal && !isGMPCMCruise && customizationLevel == 2) || disableOpenpilotLongitudinal); + forceFingerprint ->setVisible(customizationLevel == 2 || isBolt); selectMakeButton->setValue(carMake); selectModelButton->setValue(carModel); @@ -294,6 +304,14 @@ void FrogPilotVehiclesPanel::updateToggles() { toggle->setVisible(setVisible); } + toggles["ClusterOffset"]->setVisible(toggles["ClusterOffset"]->isVisible() && customizationLevel == 2); + toggles["CrosstrekTorque"]->setVisible(toggles["CrosstrekTorque"]->isVisible() && customizationLevel == 2); + toggles["ExperimentalGMTune"]->setVisible(toggles["ExperimentalGMTune"]->isVisible() && customizationLevel == 2); + toggles["FrogsGoMoosTweak"]->setVisible(toggles["FrogsGoMoosTweak"]->isVisible() && customizationLevel == 2); + toggles["LongPitch"]->setVisible(toggles["LongPitch"]->isVisible() && customizationLevel == 2); + toggles["NewLongAPI"]->setVisible(toggles["NewLongAPI"]->isVisible() && customizationLevel == 2); + toggles["NewToyotaTune"]->setVisible(toggles["NewToyotaTune"]->isVisible() && customizationLevel == 2); + setUpdatesEnabled(true); update(); } diff --git a/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h b/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h index 568798a1fbc6a9..1eb992d3f7b53c 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h @@ -12,12 +12,14 @@ class FrogPilotVehiclesPanel : public FrogPilotListWidget { private: void setModels(); + void showEvent(QShowEvent *event) override; void updateCarToggles(); void updateState(const UIState &s); void updateToggles(); std::set gmKeys = { - "ExperimentalGMTune", "LongPitch", "NewLongAPIGM", "VoltSNG" + "ExperimentalGMTune", "LongPitch", "NewLongAPIGM", + "VoltSNG" }; std::set hyundaiKeys = { @@ -29,8 +31,8 @@ class FrogPilotVehiclesPanel : public FrogPilotListWidget { }; std::set longitudinalKeys = { - "ExperimentalGMTune", "LongPitch", "NewLongAPI", "NewLongAPIGM", - "SNGHack", "VoltSNG" + "ExperimentalGMTune", "LongPitch", "NewLongAPI", + "NewLongAPIGM", "SNGHack", "VoltSNG" }; std::set sngKeys = { @@ -42,8 +44,8 @@ class FrogPilotVehiclesPanel : public FrogPilotListWidget { }; std::set toyotaKeys = { - "ClusterOffset", "FrogsGoMoosTweak", "NewToyotaTune", "SNGHack", - "ToyotaDoors" + "ClusterOffset", "FrogsGoMoosTweak", "NewToyotaTune", + "SNGHack", "ToyotaDoors" }; std::set toyotaTuneKeys = { @@ -59,12 +61,14 @@ class FrogPilotVehiclesPanel : public FrogPilotListWidget { FrogPilotSettingsWindow *parent; + QMap carModels; + QString carMake; QString carModel; QStringList models; - QMap carModels; + ParamControl *forceFingerprint; Params params; @@ -74,11 +78,14 @@ class FrogPilotVehiclesPanel : public FrogPilotListWidget { bool hasExperimentalOpenpilotLongitudinal; bool hasOpenpilotLongitudinal; bool hasSNG; + bool isBolt; bool isGMPCMCruise; bool isImpreza; bool isToyotaTuneSupported; bool isVolt; bool started; + int customizationLevel; + std::map toggles; }; diff --git a/selfdrive/frogpilot/ui/qt/offroad/visual_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/visual_settings.cc index bfa9b392b1aa35..e058e4749572dc 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/visual_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/visual_settings.cc @@ -2,65 +2,204 @@ FrogPilotVisualsPanel::FrogPilotVisualsPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) { const std::vector> visualToggles { - {"CustomUI", tr("Onroad UI Widgets"), tr("Custom FrogPilot widgets used in the onroad user interface."), "../assets/offroad/icon_road.png"}, - {"Compass", tr("Compass"), tr("A compass in the onroad UI to show the current driving direction."), ""}, - {"DynamicPathWidth", tr("Dynamic Path Width"), tr("Automatically adjust the width of the driving path display based on the current engagement state:\n\nFully engaged = 100%\nAlways On Lateral Active = 75%\nFully disengaged = 50%"), ""}, - {"PedalsOnUI", tr("Gas/Brake Pedal Indicators"), tr("Pedal indicators in the onroad UI that change opacity based on the pressure applied."), ""}, - {"CustomPaths", tr("Paths"), tr("Projected acceleration path, detected lanes, and vehicles in the blind spot."), ""}, - {"RoadNameUI", tr("Road Name"), tr("The current road name is displayed at the bottom of the screen using data from 'OpenStreetMap'."), ""}, - {"RotatingWheel", tr("Rotating Steering Wheel"), tr("The steering wheel in the onroad UI rotates along with your steering wheel movements."), ""}, - - {"QOLVisuals", tr("Quality of Life Improvements"), tr("Miscellaneous visual focused features to improve your overall openpilot experience."), "../frogpilot/assets/toggle_icons/quality_of_life.png"}, - {"BigMap", tr("Larger Map Display"), tr("A larger size of the map in the onroad UI for easier navigation readings."), ""}, - {"MapStyle", tr("Map Style"), tr("Custom map styles for the map used during navigation."), ""}, - {"StandbyMode", tr("Screen Standby Mode"), tr("The screen is turned off after it times out when driving, but it automatically wakes up if engagement state changes or important alerts occur."), ""}, - {"DriverCamera", tr("Show Driver Camera When In Reverse"), tr("The driver camera feed is displayed when the vehicle is in reverse."), ""}, - {"StoppedTimer", tr("Stopped Timer"), tr("A timer on the onroad UI to indicate how long the vehicle has been stopped."), ""} + {"QOLVisuals", tr("Accessibility"), tr("Visual features to improve your overall openpilot experience."), "../frogpilot/assets/toggle_icons/icon_accessibility.png"}, + {"CameraView", tr("Camera View"), tr("Changes the camera view display. This is purely a visual change and doesn't impact how openpilot drives."), ""}, + {"OnroadDistanceButton", tr("Onroad Personality Button"), tr("Displays the current driving personality on the screen. Tap to switch personalities, or long press for 2.5 seconds to activate 'Traffic' mode."), ""}, + {"DriverCamera", tr("Show Driver Camera When In Reverse"), tr("Displays the driver camera feed when the vehicle is in reverse."), ""}, + {"StandbyMode", tr("Standby Mode"), tr("Turns the screen off when driving and automatically wakes it up if engagement state changes or important alerts occur."), ""}, + {"StoppedTimer", tr("Stopped Timer"), tr("Activates a timer when stopped to indicate how long the vehicle has been stopped for."), ""}, + + {"AdvancedCustomUI", tr("Advanced UI Controls"), tr("Advanced features to fine tune your personalized UI."), "../frogpilot/assets/toggle_icons/icon_advanced_device.png"}, + {"HideSpeed", tr("Hide Current Speed"), tr("Hides the current speed."), ""}, + {"HideLeadMarker", tr("Hide Lead Marker"), tr("Hides the marker for the vehicle ahead."), ""}, + {"HideMapIcon", tr("Hide Map Icon"), tr("Hides the map icon."), ""}, + {"HideMaxSpeed", tr("Hide Max Speed"), tr("Hides the max speed."), ""}, + {"HideAlerts", tr("Hide Non-Critical Alerts"), tr("Hides non-critical alerts."), ""}, + {"WheelSpeed", tr("Use Wheel Speed"), tr("Uses the wheel speed instead of the cluster speed. This is purely a visual change and doesn't impact how openpilot drives."), ""}, + + {"DeveloperUI", tr("Developer Metrics"), tr("Show detailed information about openpilot's internal operations."), "../assets/offroad/icon_shell.png"}, + {"BorderMetrics", tr("Border Metrics"), tr("Displays performance metrics around the edge of the screen while driving."), ""}, + {"FPSCounter", tr("FPS Display"), tr("Displays the 'Frames Per Second' (FPS) at the bottom of the screen while driving."), ""}, + {"LateralMetrics", tr("Lateral Metrics"), tr("Displays metrics related to steering control at the top of the screen while driving."), ""}, + {"LongitudinalMetrics", tr("Longitudinal Metrics"), tr("Displays metrics related to acceleration, speed, and desired following distance at the top of the screen while driving."), ""}, + {"NumericalTemp", tr("Numerical Temperature Gauge"), tr("Shows exact temperature readings instead of status labels like 'GOOD', 'OK', or 'HIGH' in the sidebar."), ""}, + {"SidebarMetrics", tr("Sidebar"), tr("Displays system information like CPU, GPU, RAM usage, IP address, and storage space in the sidebar."), ""}, + {"UseSI", tr("Use International System of Units"), tr("Displays measurements using the 'International System of Units' (SI)."), ""}, + + {"ModelUI", tr("Model UI"), tr("Customize the model visualizations on the screen."), "../frogpilot/assets/toggle_icons/icon_vtc.png"}, + {"DynamicPathWidth", tr("Dynamic Path Width"), tr("Automatically adjusts the width of the driving path display based on the current engagement state:\n\nFully engaged = 100%\nAlways On Lateral Active = 75%\nFully disengaged = 50%"), ""}, + {"LaneLinesWidth", tr("Lane Lines Width"), tr("Controls the thickness the lane lines appear on the display.\n\nDefault matches the MUTCD standard of 4 inches."), ""}, + {"PathEdgeWidth", tr("Path Edges Width"), tr("Controls the width of the edges of the driving path to represent different modes and statuses.\n\nDefault is 20% of the total path width.\n\nColor Guide:\n- Blue: Navigation\n- Light Blue: 'Always On Lateral'\n- Green: Default\n- Orange: 'Experimental Mode'\n- Red: 'Traffic Mode'\n- Yellow: 'Conditional Experimental Mode' Overridden"), ""}, + {"PathWidth", tr("Path Width"), tr("Controls how wide the driving path appears on your screen.\n\nDefault (6.1 feet / 1.9 meters) matches the width of a 2019 Lexus ES 350."), ""}, + {"RoadEdgesWidth", tr("Road Edges Width"), tr("Controls how thick the road edges appear on the display.\n\nDefault matches half of the MUTCD standard lane line width of 4 inches."), ""}, + {"ShowStoppingPoint", tr("Stopping Point"), tr("Displays an image on the screen where openpilot is detecting a potential red light/stop sign."), ""}, + {"UnlimitedLength", tr("'Unlimited' Road UI"), tr("Extends the display of the path, lane lines, and road edges as far as the model can see."), ""}, + + {"NavigationUI", tr("Navigation Widgets"), tr("Wwidgets focused around navigation."), "../frogpilot/assets/toggle_icons/icon_map.png"}, + {"BigMap", tr("Larger Map Display"), tr("Increases the size of the map for easier navigation readings."), ""}, + {"MapStyle", tr("Map Style"), tr("Swaps out the stock map style for community created ones."), ""}, + {"RoadNameUI", tr("Road Name"), tr("Displays the current road name at the bottom of the screen using data from 'OpenStreetMap'."), ""}, + {"ShowSLCOffset", tr("Show Speed Limit Offset"), tr("Displays the speed limit offset separately in the onroad UI when using 'Speed Limit Controller'."), ""}, + {"UseVienna", tr("Use Vienna-Style Speed Signs"), tr("Forces Vienna-style (EU) speed limit signs instead of MUTCD (US)."), ""}, + + {"CustomUI", tr("Onroad Screen Widgets"), tr("Custom FrogPilot widgets used in the onroad user interface."), "../assets/offroad/icon_road.png"}, + {"AccelerationPath", tr("Acceleration Path"), tr("Projects a path based on openpilot's current desired acceleration or deceleration."), ""}, + {"AdjacentPath", tr("Adjacent Lanes"), tr("Projects paths for the adjascent lanes."), ""}, + {"BlindSpotPath", tr("Blind Spot Path"), tr("Projects a red path when vehicles are detected in the blind spot for the respective lane."), ""}, + {"Compass", tr("Compass"), tr("Displays a compass to show the current driving direction."), ""}, + {"PedalsOnUI", tr("Gas / Brake Pedal Indicators"), tr("Displays pedal indicators to indicate when either of the pedals are currently being used."), ""}, + {"RotatingWheel", tr("Rotating Steering Wheel"), tr("Rotates the steering wheel in the onroad UI rotates along with your steering wheel movements."), ""} }; for (const auto &[param, title, desc, icon] : visualToggles) { AbstractControl *visualToggle; - if (param == "CustomUI") { - FrogPilotParamManageControl *customUIToggle = new FrogPilotParamManageControl(param, title, desc, icon); - QObject::connect(customUIToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - customPathsBtn->setVisibleButton(0, hasBSM); + if (param == "QOLVisuals") { + FrogPilotParamManageControl *qolToggle = new FrogPilotParamManageControl(param, title, desc, icon); + QObject::connect(qolToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { + std::set modifiedAccessibilityKeys = accessibilityKeys; - std::set modifiedCustomOnroadUIKeys = customOnroadUIKeys; + if (disableOpenpilotLongitudinal || !hasOpenpilotLongitudinal) { + modifiedAccessibilityKeys.erase("OnroadDistanceButton"); + } - showToggles(modifiedCustomOnroadUIKeys); + if (customizationLevel == 0) { + modifiedAccessibilityKeys.erase("CameraView"); + modifiedAccessibilityKeys.erase("DriverCamera"); + modifiedAccessibilityKeys.erase("StandbyMode"); + modifiedAccessibilityKeys.erase("StoppedTimer"); + } else if (customizationLevel != 2) { + modifiedAccessibilityKeys.erase("CameraView"); + modifiedAccessibilityKeys.erase("StandbyMode"); + } + + showToggles(modifiedAccessibilityKeys); }); - visualToggle = customUIToggle; - } else if (param == "CustomPaths") { - std::vector pathToggles{"AccelerationPath", "AdjacentPath", "BlindSpotPath"}; - std::vector pathToggleNames{tr("Acceleration"), tr("Adjacent"), tr("Blind Spot")}; - customPathsBtn = new FrogPilotButtonToggleControl(param, title, desc, pathToggles, pathToggleNames); - visualToggle = customPathsBtn; - } else if (param == "PedalsOnUI") { - std::vector pedalsToggles{"DynamicPedalsOnUI", "StaticPedalsOnUI"}; - std::vector pedalsToggleNames{tr("Dynamic"), tr("Static")}; - FrogPilotButtonToggleControl *pedalsToggle = new FrogPilotButtonToggleControl(param, title, desc, pedalsToggles, pedalsToggleNames, true); - QObject::connect(pedalsToggle, &FrogPilotButtonToggleControl::buttonClicked, [this](int index) { + visualToggle = qolToggle; + } else if (param == "CameraView") { + std::vector cameraOptions{tr("Auto"), tr("Driver"), tr("Standard"), tr("Wide")}; + ButtonParamControl *preferredCamera = new ButtonParamControl(param, title, desc, icon, cameraOptions); + visualToggle = preferredCamera; + + } else if (param == "AdvancedCustomUI") { + FrogPilotParamManageControl *advancedCustomUIToggle = new FrogPilotParamManageControl(param, title, desc, icon); + QObject::connect(advancedCustomUIToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { + std::set modifiedAdvancedCustomOnroadUIKeys = advancedCustomOnroadUIKeys; + + if (disableOpenpilotLongitudinal || !hasOpenpilotLongitudinal) { + modifiedAdvancedCustomOnroadUIKeys.erase("HideLeadMarker"); + } + + showToggles(modifiedAdvancedCustomOnroadUIKeys); + }); + visualToggle = advancedCustomUIToggle; + + } else if (param == "DeveloperUI") { + FrogPilotParamManageControl *developerUIToggle = new FrogPilotParamManageControl(param, title, desc, icon); + QObject::connect(developerUIToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { + borderMetricsBtn->setVisibleButton(0, hasBSM); + lateralMetricsBtn->setVisibleButton(1, hasAutoTune); + longitudinalMetricsBtn->setVisibleButton(0, hasRadar); + + std::set modifiedDeveloperUIKeys = developerUIKeys; + + if (disableOpenpilotLongitudinal || !hasOpenpilotLongitudinal) { + modifiedDeveloperUIKeys.erase("LongitudinalMetrics"); + } + + showToggles(modifiedDeveloperUIKeys); + }); + visualToggle = developerUIToggle; + } else if (param == "BorderMetrics") { + std::vector borderToggles{"BlindSpotMetrics", "ShowSteering", "SignalMetrics"}; + std::vector borderToggleNames{tr("Blind Spot"), tr("Steering Torque"), tr("Turn Signal")}; + borderMetricsBtn = new FrogPilotButtonToggleControl(param, title, desc, borderToggles, borderToggleNames); + visualToggle = borderMetricsBtn; + } else if (param == "LateralMetrics") { + std::vector lateralToggles{"AdjacentPathMetrics", "TuningInfo"}; + std::vector lateralToggleNames{tr("Adjacent Path Metrics"), tr("Auto Tune")}; + lateralMetricsBtn = new FrogPilotButtonToggleControl(param, title, desc, lateralToggles, lateralToggleNames); + visualToggle = lateralMetricsBtn; + } else if (param == "LongitudinalMetrics") { + std::vector longitudinalToggles{"AdjacentLeadsUI", "LeadInfo", "JerkInfo"}; + std::vector longitudinalToggleNames{tr("Adjacent Leads"), tr("Lead Info"), tr("Jerk Values")}; + longitudinalMetricsBtn = new FrogPilotButtonToggleControl(param, title, desc, longitudinalToggles, longitudinalToggleNames); + visualToggle = longitudinalMetricsBtn; + } else if (param == "NumericalTemp") { + std::vector temperatureToggles{"Fahrenheit"}; + std::vector temperatureToggleNames{tr("Fahrenheit")}; + visualToggle = new FrogPilotButtonToggleControl(param, title, desc, temperatureToggles, temperatureToggleNames); + } else if (param == "SidebarMetrics") { + std::vector sidebarMetricsToggles{"ShowCPU", "ShowGPU", "ShowIP", "ShowMemoryUsage", "ShowStorageLeft", "ShowStorageUsed"}; + std::vector sidebarMetricsToggleNames{tr("CPU"), tr("GPU"), tr("IP"), tr("RAM"), tr("SSD Left"), tr("SSD Used")}; + FrogPilotButtonToggleControl *sidebarMetricsToggle = new FrogPilotButtonToggleControl(param, title, desc, sidebarMetricsToggles, sidebarMetricsToggleNames, false, 150); + QObject::connect(sidebarMetricsToggle, &FrogPilotButtonToggleControl::buttonClicked, [sidebarMetricsToggle, this](int index) { if (index == 0) { - params.putBool("StaticPedalsOnUI", false); + params.putBool("ShowGPU", false); } else if (index == 1) { - params.putBool("DynamicPedalsOnUI", false); + params.putBool("ShowCPU", false); + } else if (index == 3) { + params.putBool("ShowStorageLeft", false); + params.putBool("ShowStorageUsed", false); + } else if (index == 4) { + params.putBool("ShowMemoryUsage", false); + params.putBool("ShowStorageUsed", false); + } else if (index == 5) { + params.putBool("ShowMemoryUsage", false); + params.putBool("ShowStorageLeft", false); } + sidebarMetricsToggle->refresh(); }); - visualToggle = pedalsToggle; + visualToggle = sidebarMetricsToggle; - } else if (param == "QOLVisuals") { - FrogPilotParamManageControl *qolToggle = new FrogPilotParamManageControl(param, title, desc, icon); - QObject::connect(qolToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { - showToggles(qolKeys); + } else if (param == "ModelUI") { + FrogPilotParamManageControl *modelUIToggle = new FrogPilotParamManageControl(param, title, desc, icon); + QObject::connect(modelUIToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { + std::set modifiedModelUIKeys = modelUIKeys; + + if (disableOpenpilotLongitudinal || !hasOpenpilotLongitudinal) { + modifiedModelUIKeys.erase("ShowStoppingPoint"); + } + + showToggles(modifiedModelUIKeys); }); - visualToggle = qolToggle; + visualToggle = modelUIToggle; + } else if (param == "LaneLinesWidth" || param == "RoadEdgesWidth") { + visualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 24, tr(" inches")); + } else if (param == "PathEdgeWidth") { + visualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 100, tr("%")); + } else if (param == "PathWidth") { + visualToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 10, tr(" feet"), std::map(), 0.1); + } else if (param == "ShowStoppingPoint") { + std::vector stoppingPointToggles{"ShowStoppingPointMetrics"}; + std::vector stoppingPointToggleNames{tr("Show Distance")}; + visualToggle = new FrogPilotButtonToggleControl(param, title, desc, stoppingPointToggles, stoppingPointToggleNames); + + } else if (param == "NavigationUI") { + FrogPilotParamManageControl *customUIToggle = new FrogPilotParamManageControl(param, title, desc, icon); + QObject::connect(customUIToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { + std::set modifiedNavigationUIKeys = navigationUIKeys; + + if (disableOpenpilotLongitudinal || !hasOpenpilotLongitudinal || !params.getBool("SpeedLimitController")) { + modifiedNavigationUIKeys.erase("ShowSLCOffset"); + } + + if (customizationLevel != 2) { + modifiedNavigationUIKeys.erase("MapStyle"); + modifiedNavigationUIKeys.erase("RoadNameUI"); + modifiedNavigationUIKeys.erase("ShowSLCOffset"); + modifiedNavigationUIKeys.erase("UseVienna"); + } + + showToggles(modifiedNavigationUIKeys); + }); + visualToggle = customUIToggle; } else if (param == "BigMap") { std::vector mapToggles{"FullMap"}; std::vector mapToggleNames{tr("Full Map")}; visualToggle = new FrogPilotButtonToggleControl(param, title, desc, mapToggles, mapToggleNames); } else if (param == "MapStyle") { - QMap styleMap = { + QMap styleMap { {0, tr("Stock openpilot")}, {1, tr("Mapbox Streets")}, {2, tr("Mapbox Outdoors")}, @@ -71,27 +210,58 @@ FrogPilotVisualsPanel::FrogPilotVisualsPanel(FrogPilotSettingsWindow *parent) : {7, tr("Mapbox Navigation Day")}, {8, tr("Mapbox Navigation Night")}, {9, tr("Mapbox Traffic Night")}, - {10, tr("mike854's (Satellite hybrid)")}, + {10, tr("mike854's (Satellite hybrid)")} }; - QStringList styles = styleMap.values(); ButtonControl *mapStyleButton = new ButtonControl(title, tr("SELECT"), desc); - QObject::connect(mapStyleButton, &ButtonControl::clicked, [=]() { - QStringList styles = styleMap.values(); - QString selection = MultiOptionDialog::getSelection(tr("Select a map style"), styles, "", this); + QObject::connect(mapStyleButton, &ButtonControl::clicked, [this, mapStyleButton, styleMap]() { + QString selection = MultiOptionDialog::getSelection(tr("Select a map style"), styleMap.values(), "", this); if (!selection.isEmpty()) { int selectedStyle = styleMap.key(selection); - params.putIntNonBlocking("MapStyle", selectedStyle); + params.putInt("MapStyle", selectedStyle); mapStyleButton->setValue(selection); updateFrogPilotToggles(); } }); - int currentStyle = params.getInt("MapStyle"); mapStyleButton->setValue(styleMap[currentStyle]); visualToggle = mapStyleButton; + } else if (param == "CustomUI") { + FrogPilotParamManageControl *customUIToggle = new FrogPilotParamManageControl(param, title, desc, icon); + QObject::connect(customUIToggle, &FrogPilotParamManageControl::manageButtonClicked, [this]() { + std::set modifiedCustomOnroadUIKeys = customOnroadUIKeys; + + if (!hasBSM) { + modifiedCustomOnroadUIKeys.erase("BlindSpotPath"); + } + + if (disableOpenpilotLongitudinal || !hasOpenpilotLongitudinal) { + modifiedCustomOnroadUIKeys.erase("AccelerationPath"); + modifiedCustomOnroadUIKeys.erase("PedalsOnUI"); + } + + if (customizationLevel != 2) { + modifiedCustomOnroadUIKeys.erase("AdjacentPath"); + } + + showToggles(modifiedCustomOnroadUIKeys); + }); + visualToggle = customUIToggle; + } else if (param == "PedalsOnUI") { + std::vector pedalsToggles{"DynamicPedalsOnUI", "StaticPedalsOnUI"}; + std::vector pedalsToggleNames{tr("Dynamic"), tr("Static")}; + FrogPilotButtonToggleControl *pedalsToggle = new FrogPilotButtonToggleControl(param, title, desc, pedalsToggles, pedalsToggleNames, true); + QObject::connect(pedalsToggle, &FrogPilotButtonToggleControl::buttonClicked, [this](int index) { + if (index == 0) { + params.putBool("StaticPedalsOnUI", false); + } else if (index == 1) { + params.putBool("DynamicPedalsOnUI", false); + } + }); + visualToggle = pedalsToggle; + } else { visualToggle = new ParamControl(param, title, desc, icon); } @@ -112,14 +282,67 @@ FrogPilotVisualsPanel::FrogPilotVisualsPanel(FrogPilotSettingsWindow *parent) : QObject::connect(parent, &FrogPilotSettingsWindow::closeParentToggle, this, &FrogPilotVisualsPanel::hideToggles); QObject::connect(parent, &FrogPilotSettingsWindow::updateCarToggles, this, &FrogPilotVisualsPanel::updateCarToggles); + QObject::connect(parent, &FrogPilotSettingsWindow::updateMetric, this, &FrogPilotVisualsPanel::updateMetric); +} + +void FrogPilotVisualsPanel::showEvent(QShowEvent *event) { + customizationLevel = parent->customizationLevel; + + toggles["AdvancedCustomUI"]->setVisible(customizationLevel == 2); + toggles["CustomUI"]->setVisible(customizationLevel != 0); + toggles["DeveloperUI"]->setVisible(customizationLevel == 2); + toggles["ModelUI"]->setVisible(customizationLevel == 2); + toggles["NavigationUI"]->setVisible(customizationLevel != 0); + toggles["QOLVisuals"]->setVisible(customizationLevel != 0 || !disableOpenpilotLongitudinal && hasOpenpilotLongitudinal); } void FrogPilotVisualsPanel::updateCarToggles() { + disableOpenpilotLongitudinal = parent->disableOpenpilotLongitudinal; + hasAutoTune = parent->hasAutoTune; hasBSM = parent->hasBSM; + hasOpenpilotLongitudinal = parent->hasOpenpilotLongitudinal; + hasRadar = parent->hasRadar; hideToggles(); } +void FrogPilotVisualsPanel::updateMetric() { + bool previousIsMetric = isMetric; + isMetric = params.getBool("IsMetric"); + + if (isMetric != previousIsMetric) { + double smallDistanceConversion = isMetric ? INCH_TO_CM : CM_TO_INCH; + double distanceConversion = isMetric ? FOOT_TO_METER : METER_TO_FOOT; + + params.putFloatNonBlocking("LaneLinesWidth", params.getFloat("LaneLinesWidth") * smallDistanceConversion); + params.putFloatNonBlocking("RoadEdgesWidth", params.getFloat("RoadEdgesWidth") * smallDistanceConversion); + + params.putFloatNonBlocking("PathWidth", params.getFloat("PathWidth") * distanceConversion); + } + + FrogPilotParamValueControl *laneLinesWidthToggle = static_cast(toggles["LaneLinesWidth"]); + FrogPilotParamValueControl *pathWidthToggle = static_cast(toggles["PathWidth"]); + FrogPilotParamValueControl *roadEdgesWidthToggle = static_cast(toggles["RoadEdgesWidth"]); + + if (isMetric) { + laneLinesWidthToggle->setDescription(tr("Adjust how thick the lane lines appear on the display.\n\nDefault matches the Vienna standard of 10 centimeters.")); + roadEdgesWidthToggle->setDescription(tr("Adjust how thick the road edges appear on the display.\n\nDefault matches half of the Vienna standard of 10 centimeters.")); + + laneLinesWidthToggle->updateControl(0, 60, tr(" centimeters")); + roadEdgesWidthToggle->updateControl(0, 60, tr(" centimeters")); + + pathWidthToggle->updateControl(0, 3, tr(" meters")); + } else { + laneLinesWidthToggle->setDescription(tr("Adjust how thick the lane lines appear on the display.\n\nDefault matches the MUTCD standard of 4 inches.")); + roadEdgesWidthToggle->setDescription(tr("Adjust how thick the road edges appear on the display.\n\nDefault matches half of the MUTCD standard of 4 inches.")); + + laneLinesWidthToggle->updateControl(0, 24, tr(" inches")); + roadEdgesWidthToggle->updateControl(0, 24, tr(" inches")); + + pathWidthToggle->updateControl(0, 10, tr(" feet")); + } +} + void FrogPilotVisualsPanel::showToggles(const std::set &keys) { setUpdatesEnabled(false); @@ -135,12 +358,23 @@ void FrogPilotVisualsPanel::hideToggles() { setUpdatesEnabled(false); for (auto &[key, toggle] : toggles) { - bool subToggles = customOnroadUIKeys.find(key) != customOnroadUIKeys.end() || - qolKeys.find(key) != qolKeys.end(); + bool subToggles = accessibilityKeys.find(key) != accessibilityKeys.end() || + advancedCustomOnroadUIKeys.find(key) != advancedCustomOnroadUIKeys.end() || + customOnroadUIKeys.find(key) != customOnroadUIKeys.end() || + developerUIKeys.find(key) != developerUIKeys.end() || + modelUIKeys.find(key) != modelUIKeys.end() || + navigationUIKeys.find(key) != navigationUIKeys.end(); toggle->setVisible(!subToggles); } + toggles["AdvancedCustomUI"]->setVisible(customizationLevel == 2); + toggles["CustomUI"]->setVisible(customizationLevel != 0); + toggles["DeveloperUI"]->setVisible(customizationLevel == 2); + toggles["ModelUI"]->setVisible(customizationLevel == 2); + toggles["NavigationUI"]->setVisible(customizationLevel != 0); + toggles["QOLVisuals"]->setVisible(customizationLevel != 0 || !disableOpenpilotLongitudinal && hasOpenpilotLongitudinal); + setUpdatesEnabled(true); update(); } diff --git a/selfdrive/frogpilot/ui/qt/offroad/visual_settings.h b/selfdrive/frogpilot/ui/qt/offroad/visual_settings.h index 0a4dd5a5025236..ea88ee6c4cfe77 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/visual_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/visual_settings.h @@ -15,26 +15,59 @@ class FrogPilotVisualsPanel : public FrogPilotListWidget { private: void hideToggles(); + void showEvent(QShowEvent *event) override; void showToggles(const std::set &keys); void updateCarToggles(); + void updateMetric(); + + std::set accessibilityKeys = { + "CameraView", "DriverCamera", "OnroadDistanceButton", + "StandbyMode", "StoppedTimer" + }; + + std::set advancedCustomOnroadUIKeys = { + "HideAlerts", "HideLeadMarker", "HideMapIcon", + "HideMaxSpeed", "HideSpeed", "WheelSpeed" + }; std::set customOnroadUIKeys = { - "Compass", "CustomPaths", "DynamicPathWidth", - "PedalsOnUI", "RoadNameUI", "RotatingWheel" + "AccelerationPath", "AdjacentPath", "BlindSpotPath", + "Compass", "PedalsOnUI", "RotatingWheel" }; - std::set qolKeys = { - "BigMap", "DriverCamera", "MapStyle", - "StandbyMode", "StoppedTimer" + std::set developerUIKeys = { + "BorderMetrics", "FPSCounter", "LateralMetrics", + "LongitudinalMetrics", "NumericalTemp", + "SidebarMetrics", "UseSI" }; - FrogPilotSettingsWindow *parent; + std::set modelUIKeys = { + "DynamicPathWidth", "LaneLinesWidth", "PathEdgeWidth", + "PathWidth", "RoadEdgesWidth", "ShowStoppingPoint", + "UnlimitedLength" + }; + + std::set navigationUIKeys = { + "BigMap", "MapStyle", "RoadNameUI", + "ShowSLCOffset", "UseVienna" + }; - FrogPilotButtonToggleControl *customPathsBtn; + FrogPilotButtonToggleControl *borderMetricsBtn; + FrogPilotButtonToggleControl *lateralMetricsBtn; + FrogPilotButtonToggleControl *longitudinalMetricsBtn; + + FrogPilotSettingsWindow *parent; Params params; + bool disableOpenpilotLongitudinal; + bool hasAutoTune; bool hasBSM; + bool hasOpenpilotLongitudinal; + bool hasRadar; + bool isMetric = params.getBool("IsMetric"); + + int customizationLevel; std::map toggles; }; diff --git a/selfdrive/frogpilot/ui/qt/widgets/drive_stats.cc b/selfdrive/frogpilot/ui/qt/widgets/drive_stats.cc index 07586a88b8e357..bcae769226db2a 100644 --- a/selfdrive/frogpilot/ui/qt/widgets/drive_stats.cc +++ b/selfdrive/frogpilot/ui/qt/widgets/drive_stats.cc @@ -61,13 +61,6 @@ void DriveStats::addStatsLayouts(const QString &title, StatsLabels &labels, bool main_layout->addStretch(1); } -void DriveStats::updateFrogPilotStats(const QJsonObject &obj, StatsLabels &labels) { - labels.routes->setText(QString::number(paramsTracking.getInt("FrogPilotDrives"))); - labels.distance->setText(QString::number(int(paramsTracking.getFloat("FrogPilotKilometers") * (metric ? 1 : KM_TO_MILE)))); - labels.distance_unit->setText(getDistanceUnit()); - labels.hours->setText(QString::number(int(paramsTracking.getFloat("FrogPilotMinutes") / 60))); -} - void DriveStats::updateStatsForLabel(const QJsonObject &obj, StatsLabels &labels) { labels.routes->setText(QString::number((int)obj["routes"].toDouble())); labels.distance->setText(QString::number(int(obj["distance"].toDouble() * (metric ? MILE_TO_KM : 1)))); @@ -75,12 +68,22 @@ void DriveStats::updateStatsForLabel(const QJsonObject &obj, StatsLabels &labels labels.hours->setText(QString::number((int)(obj["minutes"].toDouble() / 60))); } +void DriveStats::updateFrogPilotStats(const QJsonObject &obj, StatsLabels &labels) { + labels.routes->setText(QString::number(paramsTracking.getInt("FrogPilotDrives"))); + labels.distance->setText(QString::number(int(paramsTracking.getFloat("FrogPilotKilometers") * (metric ? 1 : KM_TO_MILE)))); + labels.distance_unit->setText(getDistanceUnit()); + labels.hours->setText(QString::number(int(paramsTracking.getFloat("FrogPilotMinutes") / 60))); +} + void DriveStats::updateStats() { QJsonObject json = stats.object(); updateFrogPilotStats(json["frogpilot"].toObject(), frogPilot); updateStatsForLabel(json["all"].toObject(), all); updateStatsForLabel(json["week"].toObject(), week); + + int all_time_minutes = (int)(json["all"].toObject()["minutes"].toDouble()); + params.put("openpilotMinutes", QString::number(all_time_minutes).toStdString()); } void DriveStats::parseResponse(const QString &response, bool success) { diff --git a/selfdrive/frogpilot/ui/qt/widgets/frogpilot_controls.cc b/selfdrive/frogpilot/ui/qt/widgets/frogpilot_controls.cc index c135a3937e3bd4..242f8ceaeb4acc 100644 --- a/selfdrive/frogpilot/ui/qt/widgets/frogpilot_controls.cc +++ b/selfdrive/frogpilot/ui/qt/widgets/frogpilot_controls.cc @@ -6,26 +6,7 @@ void updateFrogPilotToggles() { static Params paramsMemory{"/dev/shm/params"}; - static std::atomic isUpdating(false); - static std::thread resetThread; - - bool expected = false; - if (!isUpdating.compare_exchange_strong(expected, true)) { - return; - } - - if (resetThread.joinable()) { - resetThread.join(); - } - paramsMemory.putBool("FrogPilotTogglesUpdated", true); - - resetThread = std::thread([&]() { - util::sleep_for(1000); - paramsMemory.putBool("FrogPilotTogglesUpdated", false); - - isUpdating.store(false); - }); } QColor loadThemeColors(const QString &colorKey, bool clearCache) { diff --git a/selfdrive/frogpilot/ui/qt/widgets/frogpilot_controls.h b/selfdrive/frogpilot/ui/qt/widgets/frogpilot_controls.h index d456abbea55699..86178cbd3297f5 100644 --- a/selfdrive/frogpilot/ui/qt/widgets/frogpilot_controls.h +++ b/selfdrive/frogpilot/ui/qt/widgets/frogpilot_controls.h @@ -197,7 +197,7 @@ class FrogPilotButtonToggleControl : public ParamControl { for (int i = 0; i < buttons.size(); ++i) { QAbstractButton *button = buttons[i]; if (button) { - button->setVisible(state); + button->setEnabled(state); button->setChecked(params.getBool(buttonParams[i].toStdString())); } } @@ -280,6 +280,8 @@ class FrogPilotParamValueControl : public AbstractControl { Q_OBJECT public: + QLabel *valueLabel; + FrogPilotParamValueControl(const QString ¶m, const QString &title, const QString &desc, const QString &icon, const float minValue, const float maxValue, const QString &label = "", const std::map &valueLabels = {}, const float interval = 1.0f, const bool compactSize = false, const bool instantUpdate = false) @@ -379,7 +381,7 @@ private slots: } void updateValueDisplay() { - int intValue = static_cast(value); + int intValue = static_cast(value / interval); if (valueLabels.count(intValue)) { valueLabel->setText(valueLabels.at(intValue)); } else { @@ -411,8 +413,6 @@ private slots: Params params; - QLabel *valueLabel; - QPushButton decrementButton; QPushButton incrementButton; @@ -442,12 +442,11 @@ class FrogPilotParamValueButtonControl : public FrogPilotParamValueControl { const float minValue, const float maxValue, const QString &label = "", const std::map &valueLabels = {}, const float interval = 1.0f, const std::vector &buttonParams = {}, const std::vector &buttonLabels = {}, - const bool checkable = true, const int minimumButtonWidth = 225) + const bool leftButton = false, const bool checkable = true, const int minimumButtonWidth = 225) : FrogPilotParamValueControl(param, title, desc, icon, minValue, maxValue, label, valueLabels, interval, true), buttonParams(buttonParams), buttonGroup(new QButtonGroup(this)), checkable(checkable) { - buttonGroup->setExclusive(false); for (int i = 0; i < buttonLabels.size(); ++i) { @@ -456,7 +455,11 @@ class FrogPilotParamValueButtonControl : public FrogPilotParamValueControl { button->setStyleSheet(buttonStyle); button->setMinimumWidth(minimumButtonWidth); - hlayout->addWidget(button); + if (leftButton) { + hlayout->insertWidget(hlayout->indexOf(valueLabel) - 1, button); + } else { + hlayout->addWidget(button); + } buttonGroup->addButton(button, i); } diff --git a/selfdrive/locationd/calibrationd.py b/selfdrive/locationd/calibrationd.py index 363f1859c070c0..268634e5e046bc 100755 --- a/selfdrive/locationd/calibrationd.py +++ b/selfdrive/locationd/calibrationd.py @@ -20,7 +20,7 @@ from openpilot.common.transformations.orientation import rot_from_euler, euler_from_rot from openpilot.common.swaglog import cloudlog -from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables +from openpilot.selfdrive.frogpilot.frogpilot_variables import get_frogpilot_toggles MIN_SPEED_FILTER = 15 * CV.MPH_TO_MS MAX_VEL_ANGLE_STD = np.radians(0.25) @@ -61,19 +61,13 @@ def moving_avg_with_linear_decay(prev_mean: np.ndarray, new_val: np.ndarray, idx class Calibrator: def __init__(self, param_put: bool = False): - # FrogPilot variables - frogpilot_toggles = FrogPilotVariables.toggles - FrogPilotVariables.update_frogpilot_params() - - self.update_toggles = False - self.param_put = param_put self.not_car = False # Read saved calibration self.params = Params() - self.calibration_key = frogpilot_toggles.part_model_param + "CalibrationParams" + self.calibration_key = get_frogpilot_toggles(True).part_model_param + "CalibrationParams" calibration_params = self.params.get(self.calibration_key) rpy_init = RPY_INIT wide_from_device_euler = WIDE_FROM_DEVICE_EULER_INIT @@ -175,13 +169,6 @@ def update_status(self) -> None: if self.param_put and write_this_cycle: self.params.put_nonblocking(self.calibration_key, self.get_msg(True).to_bytes()) - # Update FrogPilot parameters - if FrogPilotVariables.toggles_updated: - self.update_toggles = True - elif self.update_toggles: - FrogPilotVariables.update_frogpilot_params() - self.update_toggles = False - def handle_v_ego(self, v_ego: float) -> None: self.v_ego = v_ego diff --git a/selfdrive/locationd/torqued.py b/selfdrive/locationd/torqued.py index e457096b1e5ea0..29a5eff220366e 100755 --- a/selfdrive/locationd/torqued.py +++ b/selfdrive/locationd/torqued.py @@ -11,7 +11,7 @@ from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY from openpilot.selfdrive.locationd.helpers import PointBuckets, ParameterEstimator -from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables +from openpilot.selfdrive.frogpilot.frogpilot_variables import get_frogpilot_toggles HISTORY = 5 # secs POINTS_PER_BUCKET = 1500 @@ -52,13 +52,7 @@ def add_point(self, x, y): class TorqueEstimator(ParameterEstimator): - def __init__(self, CP, decimated=False): - # FrogPilot variables - frogpilot_toggles = FrogPilotVariables.toggles - FrogPilotVariables.update_frogpilot_params() - - self.update_toggles = False - + def __init__(self, CP, torque_cache, decimated=False): self.hist_len = int(HISTORY / DT_MDL) self.lag = CP.steerActuatorDelay + .2 # from controlsd if decimated: @@ -101,8 +95,6 @@ def __init__(self, CP, decimated=False): # try to restore cached params params = Params() params_cache = params.get("CarParamsPrevRoute") - self.torque_key = frogpilot_toggles.part_model_param + "LiveTorqueParameters" - torque_cache = params.get(self.torque_key) if params_cache is not None and torque_cache is not None: try: with log.Event.from_bytes(torque_cache) as log_evt: @@ -165,13 +157,6 @@ def update_params(self, params): self.filtered_params[param].update(value) self.filtered_params[param].update_alpha(self.decay) - # Update FrogPilot parameters - if FrogPilotVariables.toggles_updated: - self.update_toggles = True - elif self.update_toggles: - FrogPilotVariables.update_frogpilot_params() - self.update_toggles = False - def handle_log(self, t, which, msg): if which == "carControl": self.raw_points["carControl_t"].append(t + self.lag) @@ -239,16 +224,12 @@ def main(demo=False): sm = messaging.SubMaster(['carControl', 'carOutput', 'carState', 'liveLocationKalman'], poll='liveLocationKalman') params = Params() - with car.CarParams.from_bytes(params.get("CarParams", block=True)) as CP: - estimator = TorqueEstimator(CP) - - # FrogPilot variables - frogpilot_toggles = FrogPilotVariables.toggles - FrogPilotVariables.update_frogpilot_params() - - torque_key = frogpilot_toggles.part_model_param + "LiveTorqueParameters" + torque_key = get_frogpilot_toggles(True).part_model_param + "LiveTorqueParameters" torque_cache = params.get(torque_key) + with car.CarParams.from_bytes(params.get("CarParams", block=True)) as CP: + estimator = TorqueEstimator(CP, torque_cache) + while True: sm.update() if sm.all_checks(): diff --git a/selfdrive/modeld/modeld.py b/selfdrive/modeld/modeld.py index 86cea2e013f2eb..ea242e3f34df7f 100755 --- a/selfdrive/modeld/modeld.py +++ b/selfdrive/modeld/modeld.py @@ -26,7 +26,7 @@ from openpilot.selfdrive.modeld.models.commonmodel_pyx import ModelFrame, CLContext from openpilot.selfdrive.frogpilot.frogpilot_functions import MODELS_PATH -from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables +from openpilot.selfdrive.frogpilot.frogpilot_variables import get_frogpilot_toggles PROCESS_NAME = "selfdrive.modeld.modeld" SEND_RAW_PRED = os.getenv('SEND_RAW_PRED') @@ -134,12 +134,6 @@ def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_ def main(demo=False): - # FrogPilot variables - frogpilot_toggles = FrogPilotVariables.toggles - FrogPilotVariables.update_frogpilot_params() - - update_toggles = False - cloudlog.warning("modeld init") sentry.set_tag("daemon", PROCESS_NAME) @@ -150,6 +144,10 @@ def main(demo=False): cloudlog.warning("setting up CL context") cl_context = CLContext() cloudlog.warning("CL context ready; loading model") + + # FrogPilot variables + frogpilot_toggles = get_frogpilot_toggles(True) + model = ModelState(cl_context, frogpilot_toggles) cloudlog.warning("models loaded, modeld starting") @@ -311,11 +309,8 @@ def main(demo=False): last_vipc_frame_id = meta_main.frame_id # Update FrogPilot parameters - if FrogPilotVariables.toggles_updated: - update_toggles = True - elif update_toggles: - FrogPilotVariables.update_frogpilot_params() - update_toggles = False + if sm['frogpilotPlan'].togglesUpdated: + frogpilot_toggles = get_frogpilot_toggles() if __name__ == "__main__": try: diff --git a/selfdrive/navd/navd.py b/selfdrive/navd/navd.py index 40f25b1aa2c95c..1387f8239811a6 100755 --- a/selfdrive/navd/navd.py +++ b/selfdrive/navd/navd.py @@ -18,7 +18,7 @@ parse_banner_instructions) from openpilot.common.swaglog import cloudlog -from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables +from openpilot.selfdrive.frogpilot.frogpilot_variables import get_frogpilot_toggles, has_prime REROUTE_DISTANCE = 25 MANEUVER_TRANSITION_THRESHOLD = 10 @@ -57,7 +57,7 @@ def __init__(self, sm, pm): if "MAPBOX_TOKEN" in os.environ: self.mapbox_token = os.environ["MAPBOX_TOKEN"] self.mapbox_host = "https://api.mapbox.com" - elif not FrogPilotVariables.has_prime: + elif not has_prime(): self.mapbox_token = self.params.get("MapboxSecretKey", encoding='utf8') self.mapbox_host = "https://api.mapbox.com" else: @@ -65,15 +65,13 @@ def __init__(self, sm, pm): self.mapbox_host = os.getenv('MAPS_HOST', 'https://maps.comma.ai') # FrogPilot variables - self.frogpilot_toggles = FrogPilotVariables.toggles - FrogPilotVariables.update_frogpilot_params() + self.frogpilot_toggles = get_frogpilot_toggles(True) self.stop_coord = [] self.stop_signal = [] self.approaching_intersection = False self.approaching_turn = False - self.update_toggles = False self.nav_speed_limit = 0 @@ -96,11 +94,8 @@ def update(self): cloudlog.exception("navd.failed_to_compute") # Update FrogPilot parameters - if FrogPilotVariables.toggles_updated: - self.update_toggles = True - elif self.update_toggles: - FrogPilotVariables.update_frogpilot_params() - self.update_toggles = False + if self.sm['frogpilotPlan'].togglesUpdated: + self.frogpilot_toggles = get_frogpilot_toggles() def update_location(self): location = self.sm['liveLocationKalman'] @@ -467,7 +462,7 @@ def should_recompute(self): def main(): pm = messaging.PubMaster(['navInstruction', 'navRoute', 'frogpilotNavigation']) - sm = messaging.SubMaster(['carState', 'liveLocationKalman', 'managerState']) + sm = messaging.SubMaster(['carState', 'liveLocationKalman', 'managerState', 'frogpilotPlan']) rk = Ratekeeper(1.0) route_engine = RouteEngine(sm, pm) diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 2d64a82953a914..aa9587dc02b449 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -46,12 +46,12 @@ frogpilot_src = ["../frogpilot/navigation/ui/maps_settings.cc", "../frogpilot/na "../frogpilot/screenrecorder/omx_encoder.cc", "../frogpilot/screenrecorder/screenrecorder.cc", "../frogpilot/navigation/ui/navigation_functions.cc", "../frogpilot/ui/qt/widgets/drive_stats.cc", "../frogpilot/ui/qt/widgets/frogpilot_controls.cc", "../frogpilot/ui/qt/widgets/model_reviewer.cc", - "../frogpilot/ui/qt/offroad/advanced_driving_settings.cc", "../frogpilot/ui/qt/offroad/advanced_visual_settings.cc", "../frogpilot/ui/qt/offroad/data_settings.cc", "../frogpilot/ui/qt/offroad/device_settings.cc", "../frogpilot/ui/qt/offroad/frogpilot_settings.cc", "../frogpilot/ui/qt/offroad/longitudinal_settings.cc", - "../frogpilot/ui/qt/offroad/lateral_settings.cc", "../frogpilot/ui/qt/offroad/sounds_settings.cc", - "../frogpilot/ui/qt/offroad/theme_settings.cc", "../frogpilot/ui/qt/offroad/utilities.cc", - "../frogpilot/ui/qt/offroad/vehicle_settings.cc", "../frogpilot/ui/qt/offroad/visual_settings.cc"] + "../frogpilot/ui/qt/offroad/lateral_settings.cc", "../frogpilot/ui/qt/offroad/model_settings.cc", + "../frogpilot/ui/qt/offroad/sounds_settings.cc", "../frogpilot/ui/qt/offroad/theme_settings.cc", + "../frogpilot/ui/qt/offroad/utilities.cc", "../frogpilot/ui/qt/offroad/vehicle_settings.cc", + "../frogpilot/ui/qt/offroad/visual_settings.cc"] qt_src += frogpilot_src diff --git a/selfdrive/ui/qt/home.cc b/selfdrive/ui/qt/home.cc index 43a59ecde6f471..e8f39cca2c4368 100644 --- a/selfdrive/ui/qt/home.cc +++ b/selfdrive/ui/qt/home.cc @@ -249,7 +249,10 @@ void OffroadHome::hideEvent(QHideEvent *event) { } void OffroadHome::refresh() { - QString model = QString::fromStdString(params.get("ModelName")); + QString model = QString::fromStdString(params.get("ModelName")).remove(QRegularExpression("[🗺️👀📡]")).trimmed(); + if (params.getBool("CustomizationLevelConfirmed") && params.getInt("CustomizationLevel") != 2) { + model = QString::fromStdString(params.get("DefaultModelName")); + } if (model.contains("(Default)")) { model = model.remove("(Default)").trimmed(); diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index f0fd395a7dffcd..74a0cdd8c0c3e9 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -363,7 +363,7 @@ void DevicePanel::showEvent(QShowEvent *event) { ListWidget::showEvent(event); // Frogpilot variables - resetCalibBtn->setVisible(!params.getBool("ModelManagement")); + resetCalibBtn->setVisible(params.getInt("CustomizationLevel") != 2); } void SettingsWindow::hideEvent(QHideEvent *event) { @@ -377,7 +377,6 @@ void SettingsWindow::hideEvent(QHideEvent *event) { panelOpen = false; parentToggleOpen = false; subParentToggleOpen = false; - subSubParentToggleOpen = false; } void SettingsWindow::showEvent(QShowEvent *event) { @@ -422,9 +421,6 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { } else if (mapSelectionOpen) { closeMapSelection(); mapSelectionOpen = false; - } else if (subSubParentToggleOpen) { - closeSubSubParentToggle(); - subSubParentToggleOpen = false; } else if (subParentToggleOpen) { closeSubParentToggle(); subParentToggleOpen = false; @@ -451,12 +447,12 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { QObject::connect(toggles, &TogglesPanel::updateMetric, this, &SettingsWindow::updateMetric); FrogPilotSettingsWindow *frogpilotSettingsWindow = new FrogPilotSettingsWindow(this); + QObject::connect(frogpilotSettingsWindow, &FrogPilotSettingsWindow::closeMapBoxInstructions, [this]() {mapboxInstructionsOpen=false;}); QObject::connect(frogpilotSettingsWindow, &FrogPilotSettingsWindow::openMapBoxInstructions, [this]() {mapboxInstructionsOpen=true;}); QObject::connect(frogpilotSettingsWindow, &FrogPilotSettingsWindow::openMapSelection, [this]() {mapSelectionOpen=true;}); QObject::connect(frogpilotSettingsWindow, &FrogPilotSettingsWindow::openPanel, [this]() {panelOpen=true;}); QObject::connect(frogpilotSettingsWindow, &FrogPilotSettingsWindow::openParentToggle, [this]() {parentToggleOpen=true;}); QObject::connect(frogpilotSettingsWindow, &FrogPilotSettingsWindow::openSubParentToggle, [this]() {subParentToggleOpen=true;}); - QObject::connect(frogpilotSettingsWindow, &FrogPilotSettingsWindow::openSubSubParentToggle, [this]() {subSubParentToggleOpen=true;}); QList> panels = { {tr("Device"), device}, @@ -497,6 +493,44 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) { panel_widget->addWidget(panel_frame); QObject::connect(btn, &QPushButton::clicked, [=, w = panel_frame]() { + if (w->widget() == frogpilotSettingsWindow) { + bool customizationLevelConfirmed = params.getBool("CustomizationLevelConfirmed"); + + if (!customizationLevelConfirmed) { + int frogpilotHours = paramsTracking.getInt("FrogPilotMinutes") / 60; + int openpilotHours = params.getInt("openpilotMinutes") / 60; + + if (frogpilotHours < 1 && openpilotHours < 100) { + if (FrogPilotConfirmationDialog::toggleAlert(tr("Welcome to FrogPilot! Since you're new to FrogPilot, the 'Basic' toggle preset has been applied, but you can change this at any time via the 'Customization Level' button!"), tr("Sounds good!"), this)) { + params.putBoolNonBlocking("CustomizationLevelConfirmed", true); + params.putIntNonBlocking("CustomizationLevel", 0); + } + } else if (frogpilotHours < 50 && openpilotHours < 100) { + if (FrogPilotConfirmationDialog::toggleAlert(tr("Since you're fairly new to FrogPilot, the 'Basic' toggle preset has been applied, but you can change this at any time via the 'Customization Level' button!"), tr("Sounds good!"), this)) { + params.putBoolNonBlocking("CustomizationLevelConfirmed", true); + params.putIntNonBlocking("CustomizationLevel", 0); + } + } else if (frogpilotHours < 100) { + if (openpilotHours >= 100 && frogpilotHours < 100) { + if (FrogPilotConfirmationDialog::toggleAlert(tr("Since you're experienced with openpilot, the 'Standard' toggle preset has been applied, but you can change this at any time via the 'Customization Level' button!"), tr("Sounds good!"), this)) { + params.putBoolNonBlocking("CustomizationLevelConfirmed", true); + params.putIntNonBlocking("CustomizationLevel", 1); + } + } else { + if (FrogPilotConfirmationDialog::toggleAlert(tr("Since you're experienced with FrogPilot, the 'Standard' toggle preset has been applied, but you can change this at any time via the 'Customization Level' button!"), tr("Sounds good!"), this)) { + params.putBoolNonBlocking("CustomizationLevelConfirmed", true); + params.putIntNonBlocking("CustomizationLevel", 1); + } + } + } else if (frogpilotHours >= 100) { + if (FrogPilotConfirmationDialog::toggleAlert(tr("Since you're very experienced with FrogPilot, the 'Advanced' toggle preset has been applied, but you can change this at any time via the 'Customization Level' button!"), tr("Sounds good!"), this)) { + params.putBoolNonBlocking("CustomizationLevelConfirmed", true); + params.putIntNonBlocking("CustomizationLevel", 2); + } + } + } + } + if (mapboxInstructionsOpen) { closeMapBoxInstructions(); mapboxInstructionsOpen = false; diff --git a/selfdrive/ui/qt/offroad/settings.h b/selfdrive/ui/qt/offroad/settings.h index 6a3ef27de30447..5b93ab01f0bcaf 100644 --- a/selfdrive/ui/qt/offroad/settings.h +++ b/selfdrive/ui/qt/offroad/settings.h @@ -40,7 +40,6 @@ class SettingsWindow : public QFrame { void closePanel(); void closeParentToggle(); void closeSubParentToggle(); - void closeSubSubParentToggle(); void updateMetric(); private: @@ -50,12 +49,14 @@ class SettingsWindow : public QFrame { QStackedWidget *panel_widget; // FrogPilot variables + Params params; + Params paramsTracking{"/persist/tracking"}; + bool mapboxInstructionsOpen; bool mapSelectionOpen; bool panelOpen; bool parentToggleOpen; bool subParentToggleOpen; - bool subSubParentToggleOpen; }; class DevicePanel : public ListWidget { diff --git a/selfdrive/ui/qt/onroad/alerts.cc b/selfdrive/ui/qt/onroad/alerts.cc index 355c71754b53dc..8e2a2c8464b9ac 100644 --- a/selfdrive/ui/qt/onroad/alerts.cc +++ b/selfdrive/ui/qt/onroad/alerts.cc @@ -17,8 +17,8 @@ void OnroadAlerts::updateState(const UIState &s) { hide_alerts = scene.hide_alerts; road_name_ui = scene.road_name_ui; - show_aol_status_bar = scene.show_aol_status_bar; - show_cem_status_bar = scene.show_cem_status_bar; + show_aol_status_bar = scene.aol_status_bar; + show_cem_status_bar = scene.cem_status_bar; } void OnroadAlerts::clear() { diff --git a/selfdrive/ui/qt/onroad/annotated_camera.cc b/selfdrive/ui/qt/onroad/annotated_camera.cc index fe965a760a2c36..5ef225b8fe9a54 100644 --- a/selfdrive/ui/qt/onroad/annotated_camera.cc +++ b/selfdrive/ui/qt/onroad/annotated_camera.cc @@ -65,7 +65,7 @@ void AnnotatedCameraWidget::updateState(int alert_height, const UIState &s) { // Handle older routes where vEgoCluster is not set v_ego_cluster_seen = v_ego_cluster_seen || car_state.getVEgoCluster() != 0.0; - float v_ego = v_ego_cluster_seen && !s.scene.wheel_speed ? car_state.getVEgoCluster() : car_state.getVEgo(); + float v_ego = v_ego_cluster_seen && !s.scene.use_wheel_speed ? car_state.getVEgoCluster() : car_state.getVEgo(); speed = cs_alive ? std::max(0.0, v_ego) : 0.0; speed *= s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH; @@ -77,7 +77,7 @@ void AnnotatedCameraWidget::updateState(int alert_height, const UIState &s) { } has_us_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::MUTCD) || (speedLimitController && !useViennaSLCSign); - has_eu_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA) && !(speedLimitController && !useViennaSLCSign) || (speedLimitController && useViennaSLCSign); + has_eu_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA) || (speedLimitController && useViennaSLCSign); is_metric = s.scene.is_metric; speedUnit = s.scene.is_metric ? tr("km/h") : tr("mph"); hideBottomIcons = (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE || turnSignalAnimation && (turnSignalLeft || turnSignalRight) && (signalStyle == "traditional" || signalStyle == "traditional_gif") || bigMapOpen); @@ -147,8 +147,6 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) { ), 10)); } else if (trafficModeActive) { p.setPen(QPen(redColor(), 10)); - } else if (reverseCruise) { - p.setPen(QPen(blueColor(), 6)); } else { p.setPen(QPen(whiteColor(75), 6)); } @@ -333,11 +331,7 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s, f // road edges for (int i = 0; i < std::size(scene.road_edge_vertices); ++i) { - if (useStockColors) { - painter.setBrush(QColor::fromRgbF(1.0, 0, 0, std::clamp(1.0 - scene.road_edge_stds[i], 0.0, 1.0))); - } else { - painter.setBrush(scene.road_edges_color); - } + painter.setBrush(QColor::fromRgbF(1.0, 0, 0, std::clamp(1.0 - scene.road_edge_stds[i], 0.0, 1.0))); painter.drawPolygon(scene.road_edge_vertices[i]); } @@ -367,14 +361,11 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s, f // If acceleration is between -0.25 and 0.25, resort to the theme color if (std::abs(acceleration[i]) < 0.25 && !useStockColors) { QColor color = scene.path_color; - - bg.setColorAt(0.0, color); - - color.setAlphaF(0.5); - bg.setColorAt(0.5, color); - - color.setAlphaF(0.1); - bg.setColorAt(1.0, color); + bg.setColorAt(0.0f, color); + color.setAlphaF(0.5f); + bg.setColorAt(0.5f, color); + color.setAlphaF(0.1f); + bg.setColorAt(1.0f, color); } else { // speed up: 120, slow down: 0 float path_hue = fmax(fmin(60 + acceleration[i] * 35, 120), 0); @@ -402,15 +393,13 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s, f if (scene.show_stopping_point && scene.red_light && speed > 1 && !(conditionalStatus == 1 || conditionalStatus == 3 || conditionalStatus == 5)) { QPointF last_point = scene.track_vertices.last(); - QPointF adjusted_point = last_point - QPointF(stopSignImg.width() / 2, stopSignImg.height()); painter.drawPixmap(adjusted_point, stopSignImg); if (scene.show_stopping_point_metrics) { - QString text = QString::number(modelLength * distanceConversion) + leadDistanceUnit; QFont font = InterFont(35, QFont::DemiBold); - QFontMetrics fm(font); - int text_width = fm.horizontalAdvance(text); + QString text = QString::number(modelLength * distanceConversion) + leadDistanceUnit; + int text_width = QFontMetrics(font).horizontalAdvance(text); QPointF text_position = last_point - QPointF(text_width / 2, stopSignImg.height() + 35); painter.save(); @@ -425,9 +414,9 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s, f if (scene.blind_spot_path) { QLinearGradient bs(0, height(), 0, 0); - bs.setColorAt(0.0, QColor::fromHslF(0 / 360., 0.75, 0.50, 0.6)); - bs.setColorAt(0.5, QColor::fromHslF(0 / 360., 0.75, 0.50, 0.4)); - bs.setColorAt(1.0, QColor::fromHslF(0 / 360., 0.75, 0.50, 0.2)); + bs.setColorAt(0.0f, QColor::fromHslF(0 / 360.0f, 0.75f, 0.5f, 0.6f)); + bs.setColorAt(0.5f, QColor::fromHslF(0 / 360.0f, 0.75f, 0.5f, 0.4f)); + bs.setColorAt(1.0f, QColor::fromHslF(0 / 360.0f, 0.75f, 0.5f, 0.2f)); painter.setBrush(bs); if (blindSpotLeft) { @@ -439,67 +428,68 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s, f } // Paint adjacent lane paths - if ((scene.adjacent_path || scene.adjacent_path_metrics) && v_ego > scene.minimum_lane_change_speed) { - const float minLaneWidth = laneDetectionWidth * 0.5f; - const float maxLaneWidth = laneDetectionWidth * 1.5f; + if ((scene.adjacent_path || scene.adjacent_path_metrics) && v_ego >= scene.minimum_lane_change_speed) { + QLinearGradient ap(0, height(), 0, 0); - auto paintLane = [&](const QPolygonF &lane, float laneWidth, bool blindspot) { - QLinearGradient gradient(0, height(), 0, 0); - - bool redPath = laneWidth < minLaneWidth || laneWidth > maxLaneWidth || blindspot; - float hue = redPath ? 0.0f : 120.0f * (laneWidth - minLaneWidth) / (maxLaneWidth - minLaneWidth); - float hueF = hue / 360.0f; + std::function setAdjacentPathColors = [&](float hue) { + ap.setColorAt(0.0f, QColor::fromHslF(hue / 360.0f, 0.75f, 0.5f, 0.6f)); + ap.setColorAt(0.5f, QColor::fromHslF(hue / 360.0f, 0.75f, 0.5f, 0.4f)); + ap.setColorAt(1.0f, QColor::fromHslF(hue / 360.0f, 0.75f, 0.5f, 0.2f)); + }; - gradient.setColorAt(0.0, QColor::fromHslF(hueF, 0.75f, 0.50f, 0.6f)); - gradient.setColorAt(0.5, QColor::fromHslF(hueF, 0.75f, 0.50f, 0.4f)); - gradient.setColorAt(1.0, QColor::fromHslF(hueF, 0.75f, 0.50f, 0.2f)); + std::function drawAdjacentLane = [&](const QPolygonF &lane, float laneWidth, bool isBlindSpot) { + if (isBlindSpot) { + setAdjacentPathColors(0.0f); + } else { + float hue = 120.0f * (1 - fmin(fabs(laneWidth - laneDetectionWidth) / (laneDetectionWidth / 2), 1)); + setAdjacentPathColors(hue); + } - painter.setBrush(gradient); + painter.setBrush(ap); painter.drawPolygon(lane); if (scene.adjacent_path_metrics) { painter.setFont(InterFont(30, QFont::DemiBold)); painter.setPen(Qt::white); - QRectF boundingRect = lane.boundingRect(); - QString text = blindspot ? tr("Vehicle in blind spot") : QString::number(laneWidth * distanceConversion, 'f', 2) + leadDistanceUnit; - painter.drawText(boundingRect, Qt::AlignCenter, text); - + QString text = isBlindSpot ? tr("Vehicle in blind spot") : QString::number(laneWidth * distanceConversion, 'f', 2) + leadDistanceUnit; + painter.drawText(lane.boundingRect(), Qt::AlignCenter, text); painter.setPen(Qt::NoPen); } }; - paintLane(scene.track_adjacent_vertices[4], scene.lane_width_left, blindSpotLeft); - paintLane(scene.track_adjacent_vertices[5], scene.lane_width_right, blindSpotRight); + drawAdjacentLane(scene.track_adjacent_vertices[4], scene.lane_width_left, blindSpotLeft); + drawAdjacentLane(scene.track_adjacent_vertices[5], scene.lane_width_right, blindSpotRight); } // Paint path edges QLinearGradient pe(0, height(), 0, 0); - auto setGradientColors = [&](const QColor &baseColor) { - pe.setColorAt(0.0, baseColor); + + std::function setPathEdgeColors = [&](QLinearGradient &gradient, const QColor &baseColor) { + gradient.setColorAt(0.0f, baseColor); QColor color = baseColor; - color.setAlphaF(0.5); - pe.setColorAt(0.5, color); - color.setAlphaF(0.1); - pe.setColorAt(1.0, color); + color.setAlphaF(0.5f); + gradient.setColorAt(0.5f, color); + color.setAlphaF(0.1f); + gradient.setColorAt(1.0f, color); }; if (alwaysOnLateralActive) { - setGradientColors(bg_colors[STATUS_ALWAYS_ON_LATERAL_ACTIVE]); + setPathEdgeColors(pe, bg_colors[STATUS_ALWAYS_ON_LATERAL_ACTIVE]); } else if (conditionalStatus == 1 || conditionalStatus == 3 || conditionalStatus == 5) { - setGradientColors(bg_colors[STATUS_CONDITIONAL_OVERRIDDEN]); + setPathEdgeColors(pe, bg_colors[STATUS_CONDITIONAL_OVERRIDDEN]); } else if (experimentalMode) { - setGradientColors(bg_colors[STATUS_EXPERIMENTAL_MODE_ACTIVE]); + setPathEdgeColors(pe, bg_colors[STATUS_EXPERIMENTAL_MODE_ACTIVE]); } else if (trafficModeActive) { - setGradientColors(bg_colors[STATUS_TRAFFIC_MODE_ACTIVE]); + setPathEdgeColors(pe, bg_colors[STATUS_TRAFFIC_MODE_ACTIVE]); } else if (scene.navigate_on_openpilot) { - setGradientColors(bg_colors[STATUS_NAVIGATION_ACTIVE]); + setPathEdgeColors(pe, bg_colors[STATUS_NAVIGATION_ACTIVE]); } else if (!useStockColors) { - setGradientColors(scene.path_edges_color); + setPathEdgeColors(pe, scene.path_edges_color); } else { - pe.setColorAt(0.0, QColor::fromHslF(148 / 360., 0.94, 0.51, 1.0)); - pe.setColorAt(0.5, QColor::fromHslF(112 / 360., 1.00, 0.68, 0.5)); - pe.setColorAt(1.0, QColor::fromHslF(112 / 360., 1.00, 0.68, 0.1)); + pe.setColorAt(0.0f, QColor::fromHslF(148 / 360.0f, 0.94f, 0.51f, 1.0f)); + pe.setColorAt(0.5f, QColor::fromHslF(112 / 360.0f, 1.00f, 0.68f, 0.5f)); + pe.setColorAt(1.0f, QColor::fromHslF(112 / 360.0f, 1.00f, 0.68f, 0.1f)); } QPainterPath path; @@ -559,12 +549,12 @@ void AnnotatedCameraWidget::drawDriverState(QPainter &painter, const UIState *s) painter.restore(); } -void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd, float v_ego, const QColor lead_marker_color) { +void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd, float v_ego, const QColor &lead_marker_color, bool adjacent) { painter.save(); - const float speedBuff = useStockColors ? 10. : 25.; // Make the center of the chevron appear sooner if a theme is active - const float leadBuff = useStockColors ? 40. : 100.; // Make the center of the chevron appear sooner if a theme is active - const float d_rel = lead_data.getDRel(); + const float speedBuff = useStockColors || adjacent ? 10. : 25.; // Make the center of the chevron appear sooner if a theme is active + const float leadBuff = useStockColors || adjacent ? 40. : 100.; // Make the center of the chevron appear sooner if a theme is active + const float d_rel = lead_data.getDRel() + (adjacent ? fabs(lead_data.getYRel()) : 0); const float v_rel = lead_data.getVRel(); float fillAlpha = 0; @@ -576,7 +566,7 @@ void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::RadarState fillAlpha = (int)(fmin(fillAlpha, 255)); } - float sz = std::clamp((25 * 30) / (d_rel / 3 + 30), 15.0f, 30.0f) * 2.35; + float sz = std::clamp((25 * 30) / (d_rel / 3 + 30), adjacent ? 5.0f : 15.0f, adjacent ? 20.0f : 30.0f) * 2.35; float x = std::clamp((float)vd.x(), 0.f, width() - sz / 2); float y = std::fmin(height() - sz * .6, (float)vd.y()); @@ -602,21 +592,38 @@ void AnnotatedCameraWidget::drawLead(QPainter &painter, const cereal::RadarState painter.setPen(Qt::white); painter.setFont(InterFont(35, QFont::Bold)); - QString text = QString("%1 %2 | %3 %4 | %5 %6") - .arg(qRound(d_rel * distanceConversion)) - .arg(leadDistanceUnit) - .arg(qRound(lead_speed * speedConversion)) - .arg(leadSpeedUnit) - .arg(QString::number(d_rel / std::max(v_ego, 1.0f), 'f', 1)) - .arg("s"); + QString text; + if (adjacent) { + text = QString("%1 %2 | %3 %4") + .arg(qRound(d_rel * distanceConversion)) + .arg(leadDistanceUnit) + .arg(qRound(lead_speed * speedConversion)) + .arg(leadSpeedUnit); + } else { + text = QString("%1 %2 | %3 %4 | %5 %6") + .arg(qRound(d_rel * distanceConversion)) + .arg(leadDistanceUnit) + .arg(qRound(lead_speed * speedConversion)) + .arg(leadSpeedUnit) + .arg(QString::number(d_rel / std::max(v_ego, 1.0f), 'f', 1)) + .arg("s"); + } QFontMetrics metrics(painter.font()); int middle_x = (chevron[2].x() + chevron[0].x()) / 2; + int textHeight = metrics.height(); int textWidth = metrics.horizontalAdvance(text); int text_x = middle_x - textWidth / 2; - int text_y = chevron[0].y() + metrics.height() + 5; + int text_y = chevron[0].y() + textHeight + 5; - painter.drawText(text_x, text_y, text); + if (!adjacent) { + lead_x = x + text_x + textWidth; + lead_y = y + text_y + textHeight; + } + + if (!adjacent || fabs((x + text_x + textWidth) - lead_x) >= textWidth || fabs((y + text_y + textHeight) - lead_y) >= textHeight) { + painter.drawText(text_x, text_y, text); + } } painter.restore(); @@ -691,11 +698,29 @@ void AnnotatedCameraWidget::paintEvent(QPaintEvent *event) { update_leads(s, radar_state, model.getPosition()); auto lead_one = radar_state.getLeadOne(); auto lead_two = radar_state.getLeadTwo(); - if (lead_one.getStatus()) { - drawLead(painter, lead_one, s->scene.lead_vertices[0], v_ego, s->scene.lead_marker_color); + auto lead_left = radar_state.getLeadLeft(); + auto lead_right = radar_state.getLeadRight(); + auto lead_left_far = radar_state.getLeadLeftFar(); + auto lead_right_far = radar_state.getLeadRightFar(); + if (lead_left.getStatus()) { + drawLead(painter, lead_left, s->scene.lead_vertices[2], v_ego, blueColor(), true); + } + if (lead_right.getStatus()) { + drawLead(painter, lead_right, s->scene.lead_vertices[3], v_ego, redColor(), true); + } + if (lead_left_far.getStatus()) { + drawLead(painter, lead_left_far, s->scene.lead_vertices[4], v_ego, greenColor(), true); + } + if (lead_right_far.getStatus()) { + drawLead(painter, lead_right_far, s->scene.lead_vertices[5], v_ego, whiteColor(), true); } if (lead_two.getStatus()) { drawLead(painter, lead_two, s->scene.lead_vertices[1], v_ego, s->scene.lead_marker_color); + } else if (lead_one.getStatus()) { + drawLead(painter, lead_one, s->scene.lead_vertices[0], v_ego, s->scene.lead_marker_color); + } else { + lead_x = 0; + lead_y = 0; } } } @@ -846,7 +871,7 @@ void AnnotatedCameraWidget::updateFrogPilotVariables(int alert_height, const UIS alertHeight = alert_height; alwaysOnLateralActive = scene.always_on_lateral_active; - showAlwaysOnLateralStatusBar = scene.show_aol_status_bar; + showAlwaysOnLateralStatusBar = scene.aol_status_bar; blindSpotLeft = scene.blind_spot_left; blindSpotRight = scene.blind_spot_right; @@ -861,10 +886,10 @@ void AnnotatedCameraWidget::updateFrogPilotVariables(int alert_height, const UIS bottom_layout->setAlignment(compass_img, (rightHandDM ? Qt::AlignLeft : Qt::AlignRight)); } - conditionalSpeed = scene.conditional_speed; - conditionalSpeedLead = scene.conditional_speed_lead; + conditionalSpeed = scene.conditional_limit; + conditionalSpeedLead = scene.conditional_limit_lead; conditionalStatus = scene.conditional_status; - showConditionalExperimentalStatusBar = scene.show_cem_status_bar; + showConditionalExperimentalStatusBar = scene.cem_status_bar; cruiseAdjustment = scene.disable_curve_speed_smoothing || !is_cruise_set ? fmax(setSpeed - scene.adjusted_cruise, 0) : fmax(0.25 * (setSpeed - scene.adjusted_cruise) + 0.75 * cruiseAdjustment - 1, 0); vtscControllingCurve = scene.vtsc_controlling_curve; @@ -882,7 +907,7 @@ void AnnotatedCameraWidget::updateFrogPilotVariables(int alert_height, const UIS laneDetectionWidth = scene.lane_detection_width; - leadInfo = scene.lead_info; + leadInfo = scene.lead_metrics; obstacleDistance = scene.obstacle_distance; obstacleDistanceStock = scene.obstacle_distance_stock; @@ -910,8 +935,6 @@ void AnnotatedCameraWidget::updateFrogPilotVariables(int alert_height, const UIS pedal_icons->updateState(scene); } - reverseCruise = scene.reverse_cruise; - roadNameUI = scene.road_name_ui; bool enableScreenRecorder = scene.screen_recorder && !mapOpen; @@ -921,12 +944,12 @@ void AnnotatedCameraWidget::updateFrogPilotVariables(int alert_height, const UIS } speedLimitController = scene.speed_limit_controller; - showSLCOffset = speedLimitController && scene.show_slc_offset; + showSLCOffset = speedLimitController && scene.show_speed_limit_offset; slcOverridden = speedLimitController && scene.speed_limit_overridden; slcSpeedLimitOffset = scene.speed_limit_offset * (is_metric ? MS_TO_KPH : MS_TO_MPH); speedLimitChanged = speedLimitController && scene.speed_limit_changed; unconfirmedSpeedLimit = speedLimitController ? scene.unconfirmed_speed_limit : 0; - useViennaSLCSign = scene.use_vienna_slc_sign; + useViennaSLCSign = scene.speed_limit_vienna; bool stoppedTimer = scene.stopped_timer && scene.standstill && scene.started_timer / UI_FREQ >= 10 && !mapOpen; if (stoppedTimer) { @@ -944,7 +967,7 @@ void AnnotatedCameraWidget::updateFrogPilotVariables(int alert_height, const UIS turnSignalLeft = scene.turn_signal_left; turnSignalRight = scene.turn_signal_right; - useSI = scene.use_si; + useSI = scene.use_si_metrics; useStockColors = scene.use_stock_colors; } diff --git a/selfdrive/ui/qt/onroad/annotated_camera.h b/selfdrive/ui/qt/onroad/annotated_camera.h index ed7abc88c4bbdf..19d7212bb444ad 100644 --- a/selfdrive/ui/qt/onroad/annotated_camera.h +++ b/selfdrive/ui/qt/onroad/annotated_camera.h @@ -90,15 +90,14 @@ class AnnotatedCameraWidget : public CameraWidget { bool wide_cam_requested = false; // FrogPilot widgets - void initializeFrogPilotWidgets(); - void paintFrogPilotWidgets(QPainter &painter); - void updateFrogPilotVariables(int alert_height, const UIScene &scene); - void updateSignals(); - void drawLeadInfo(QPainter &p); void drawSLCConfirmation(QPainter &p); void drawStatusBar(QPainter &p); void drawTurnSignals(QPainter &p); + void initializeFrogPilotWidgets(); + void paintFrogPilotWidgets(QPainter &painter); + void updateFrogPilotVariables(int alert_height, const UIScene &scene); + void updateSignals(); // FrogPilot variables Params paramsMemory{"/dev/shm/params"}; @@ -122,7 +121,6 @@ class AnnotatedCameraWidget : public CameraWidget { bool leadInfo; bool mapOpen; bool onroadDistanceButton; - bool reverseCruise; bool roadNameUI; bool showAlwaysOnLateralStatusBar; bool showConditionalExperimentalStatusBar; @@ -145,6 +143,8 @@ class AnnotatedCameraWidget : public CameraWidget { float cruiseAdjustment; float distanceConversion; float laneDetectionWidth; + float lead_x; + float lead_y; float slcSpeedLimitOffset; float speedConversion; float unconfirmedSpeedLimit; @@ -191,7 +191,7 @@ class AnnotatedCameraWidget : public CameraWidget { void showEvent(QShowEvent *event) override; void updateFrameMat() override; void drawLaneLines(QPainter &painter, const UIState *s, float v_ego); - void drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd, float v_ego, const QColor lead_marker_color); + void drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data, const QPointF &vd, float v_ego, const QColor &lead_marker_color, bool adjacent = false); void drawHud(QPainter &p); void drawDriverState(QPainter &painter, const UIState *s); void paintEvent(QPaintEvent *event) override; diff --git a/selfdrive/ui/qt/onroad/buttons.cc b/selfdrive/ui/qt/onroad/buttons.cc index 73a95455387b1d..969d5e5417c39a 100644 --- a/selfdrive/ui/qt/onroad/buttons.cc +++ b/selfdrive/ui/qt/onroad/buttons.cc @@ -56,7 +56,7 @@ void ExperimentalButton::changeMode() { } } -void ExperimentalButton::updateState(const UIState &s, bool lead_info) { +void ExperimentalButton::updateState(const UIState &s, bool lead_metrics) { const auto cs = (*s.sm)["controlsState"].getControlsState(); bool eng = cs.getEngageable() || cs.getEnabled() || always_on_lateral_active; if ((cs.getExperimentalMode() != experimental_mode) || (eng != engageable)) { @@ -77,7 +77,7 @@ void ExperimentalButton::updateState(const UIState &s, bool lead_info) { rotating_wheel = scene.rotating_wheel; traffic_mode_active = scene.traffic_mode_active; use_stock_wheel = scene.use_stock_wheel; - y_offset = lead_info ? 10 : 0; + y_offset = lead_metrics ? 10 : 0; if (rotating_wheel && steering_angle_deg != scene.steering_angle_deg) { steering_angle_deg = scene.steering_angle_deg; diff --git a/selfdrive/ui/qt/onroad/buttons.h b/selfdrive/ui/qt/onroad/buttons.h index bfe143b65d7dee..ec08db2b9f12ea 100644 --- a/selfdrive/ui/qt/onroad/buttons.h +++ b/selfdrive/ui/qt/onroad/buttons.h @@ -14,7 +14,7 @@ class ExperimentalButton : public QPushButton { public: explicit ExperimentalButton(QWidget *parent = 0); - void updateState(const UIState &s, bool lead_info); + void updateState(const UIState &s, bool lead_metrics); // FrogPilot widgets ~ExperimentalButton(); diff --git a/selfdrive/ui/qt/onroad/onroad_home.cc b/selfdrive/ui/qt/onroad/onroad_home.cc index 3634f99bcdfc7a..6a9aced2b71e50 100644 --- a/selfdrive/ui/qt/onroad/onroad_home.cc +++ b/selfdrive/ui/qt/onroad/onroad_home.cc @@ -95,10 +95,10 @@ void OnroadWindow::updateState(const UIState &s) { liveValid = scene.live_valid; showBlindspot = scene.show_blind_spot && (blindSpotLeft || blindSpotRight); showFPS = scene.show_fps; - showJerk = scene.show_jerk; - showSignal = scene.show_signal && (turnSignalLeft || turnSignalRight); - showSteering = scene.show_steering; - showTuning = scene.show_tuning; + showJerk = scene.jerk_metrics; + showSignal = scene.signal_metrics && (turnSignalLeft || turnSignalRight); + showSteering = scene.steering_metrics; + showTuning = scene.lateral_tuning_metrics; speedJerk = scene.speed_jerk; speedJerkDifference = scene.speed_jerk_difference; steer = scene.steer; @@ -127,9 +127,6 @@ void OnroadWindow::mousePressEvent(QMouseEvent* e) { QRect leftRect(0, 0, size.width() / 2, size.height()); QRect rightRect(size.width() / 2, 0, size.width() / 2, size.height()); - QRect hideSpeedRect(rect().center().x() - 175, 50, 350, 350); - QRect speedLimitRect(7, 250, 225, 225); - if (scene.speed_limit_changed && (leftRect.contains(pos) || rightRect.contains(pos))) { bool slcConfirmed = leftRect.contains(pos) ? !scene.right_hand_drive : scene.right_hand_drive; paramsMemory.putBoolNonBlocking("SLCConfirmed", slcConfirmed); @@ -137,19 +134,7 @@ void OnroadWindow::mousePressEvent(QMouseEvent* e) { return; } - if (hideSpeedRect.contains(pos) && scene.hide_speed_ui) { - scene.hide_speed = !scene.hide_speed; - params.putBoolNonBlocking("HideSpeed", scene.hide_speed); - return; - } - - if (speedLimitRect.contains(pos) && scene.show_slc_offset_ui) { - scene.show_slc_offset = !scene.show_slc_offset; - params.putBoolNonBlocking("ShowSLCOffset", scene.show_slc_offset); - return; - } - - if (scene.experimental_mode_via_screen && pos != timeoutPoint) { + if (scene.experimental_mode_via_tap && pos != timeoutPoint) { if (clickTimer.isActive()) { clickTimer.stop(); @@ -427,9 +412,8 @@ void OnroadWindow::paintEvent(QPaintEvent *event) { avgFPS = totalFPS / fpsQueue.size(); } - QString fpsDisplayString = QString("FPS: %1 (%2) | Min: %3 | Max: %4 | Avg: %5") + QString fpsDisplayString = QString("FPS: %1 | Min: %3 | Max: %4 | Avg: %5") .arg(qRound(fps)) - .arg(paramsMemory.getInt("CameraFPS")) .arg(qRound(minFPS)) .arg(qRound(maxFPS)) .arg(qRound(avgFPS)); diff --git a/selfdrive/ui/qt/sidebar.cc b/selfdrive/ui/qt/sidebar.cc index abadeaca9af493..a0de00fbac33ff 100644 --- a/selfdrive/ui/qt/sidebar.cc +++ b/selfdrive/ui/qt/sidebar.cc @@ -114,8 +114,8 @@ void Sidebar::mousePressEvent(QMouseEvent *event) { isCPU = (showChip == 1); isGPU = (showChip == 2); - scene.is_CPU = isCPU; - scene.is_GPU = isGPU; + scene.cpu_metrics = isCPU; + scene.gpu_metrics = isGPU; params.putBoolNonBlocking("ShowCPU", isCPU); params.putBoolNonBlocking("ShowGPU", isGPU); @@ -129,9 +129,9 @@ void Sidebar::mousePressEvent(QMouseEvent *event) { isStorageLeft = (showMemory == 2); isStorageUsed = (showMemory == 3); - scene.is_memory = isMemoryUsage; - scene.is_storage_left = isStorageLeft; - scene.is_storage_used = isStorageUsed; + scene.memory_metrics = isMemoryUsage; + scene.storage_left_metrics = isStorageLeft; + scene.storage_used_metrics = isStorageUsed; params.putBoolNonBlocking("ShowMemoryUsage", isMemoryUsage); params.putBoolNonBlocking("ShowStorageLeft", isStorageLeft); @@ -239,15 +239,15 @@ void Sidebar::updateState(const UIState &s) { // FrogPilot variables const UIScene &scene = s.scene; - isCPU = scene.is_CPU; + isCPU = scene.cpu_metrics; isFahrenheit = scene.fahrenheit; - isGPU = scene.is_GPU; - isIP = scene.is_IP; - isMemoryUsage = scene.is_memory; + isGPU = scene.gpu_metrics; + isIP = scene.ip_metrics; + isMemoryUsage = scene.memory_metrics; isNumericalTemp = scene.numerical_temp; isRandomEvents = scene.random_events; - isStorageLeft = scene.is_storage_left; - isStorageUsed = scene.is_storage_used; + isStorageLeft = scene.storage_left_metrics; + isStorageUsed = scene.storage_used_metrics; isSidebarMetrics = scene.sidebar_metrics; bool useStockColors = scene.use_stock_colors; diff --git a/selfdrive/ui/qt/widgets/input.cc b/selfdrive/ui/qt/widgets/input.cc index a69442e20f65b0..1f916ad0e5d664 100644 --- a/selfdrive/ui/qt/widgets/input.cc +++ b/selfdrive/ui/qt/widgets/input.cc @@ -214,7 +214,7 @@ ConfirmationDialog::ConfirmationDialog(const QString &prompt_text, const QString #confirm_btn:pressed { background-color: #3049F4; } )"); QVBoxLayout *main_layout = new QVBoxLayout(container); - main_layout->setContentsMargins(32, rich ? 32 : 120, 32, 32); + main_layout->setContentsMargins(32, 32, 32, 32); QLabel *prompt = new QLabel(prompt_text, this); prompt->setWordWrap(true); diff --git a/selfdrive/ui/soundd.py b/selfdrive/ui/soundd.py index 75e238373007ea..99d3eb91a7d5d7 100644 --- a/selfdrive/ui/soundd.py +++ b/selfdrive/ui/soundd.py @@ -15,7 +15,7 @@ from openpilot.system import micd from openpilot.selfdrive.frogpilot.frogpilot_functions import ACTIVE_THEME_PATH, RANDOM_EVENTS_PATH -from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables +from openpilot.selfdrive.frogpilot.frogpilot_variables import get_frogpilot_toggles SAMPLE_RATE = 48000 SAMPLE_BUFFER = 4096 # (approx 100ms) @@ -55,6 +55,7 @@ AudibleAlert.nessie: ("nessie.wav", 1, MAX_VOLUME), AudibleAlert.noice: ("noice.wav", 1, MAX_VOLUME), AudibleAlert.promptRepeat: ("prompt_repeat.wav", None, MAX_VOLUME), + AudibleAlert.thisIsFine: ("this_is_fine.wav", None, MAX_VOLUME), AudibleAlert.uwu: ("uwu.wav", 1, MAX_VOLUME), } @@ -79,8 +80,9 @@ def __init__(self): self.spl_filter_weighted = FirstOrderFilter(0, 2.5, FILTER_DT, initialized=False) # FrogPilot variables - self.frogpilot_toggles = FrogPilotVariables.toggles - FrogPilotVariables.update_frogpilot_params() + self.frogpilot_toggles = get_frogpilot_toggles(True) + + self.auto_volume = 0 self.previous_sound_pack = None @@ -97,11 +99,10 @@ def __init__(self): AudibleAlert.mail: MAX_VOLUME, AudibleAlert.nessie: MAX_VOLUME, AudibleAlert.noice: MAX_VOLUME, + AudibleAlert.thisIsFine: MAX_VOLUME, AudibleAlert.uwu: MAX_VOLUME, } - self.update_toggles = False - self.update_frogpilot_sounds() def load_sounds(self): @@ -186,7 +187,7 @@ def soundd_thread(self): # sounddevice must be imported after forking processes import sounddevice as sd - sm = messaging.SubMaster(['controlsState', 'microphone']) + sm = messaging.SubMaster(['controlsState', 'microphone', 'frogpilotPlan']) with self.get_stream(sd) as stream: rk = Ratekeeper(20) @@ -197,6 +198,8 @@ def soundd_thread(self): if sm.updated['microphone'] and self.current_alert == AudibleAlert.none: # only update volume filter when not playing alert if self.frogpilot_toggles.alert_volume_control: + self.spl_filter_weighted.update(sm["microphone"].soundPressureWeightedDb) + self.auto_volume = self.calculate_volume(float(self.spl_filter_weighted.x)) self.current_volume = 0.0 else: self.spl_filter_weighted.update(sm["microphone"].soundPressureWeightedDb) @@ -204,6 +207,8 @@ def soundd_thread(self): elif self.frogpilot_toggles.alert_volume_control and self.current_alert in self.volume_map: self.current_volume = self.volume_map[self.current_alert] / 100.0 + if self.current_volume == 1.01: + self.current_volume = self.auto_volume elif self.current_alert in self.random_events_map: self.current_volume = self.random_events_map[self.current_alert] @@ -215,12 +220,9 @@ def soundd_thread(self): assert stream.active # Update FrogPilot parameters - if FrogPilotVariables.toggles_updated: - self.update_toggles = True - elif self.update_toggles: - FrogPilotVariables.update_frogpilot_params() + if sm['frogpilotPlan'].togglesUpdated: + self.frogpilot_toggles = get_frogpilot_toggles() self.update_frogpilot_sounds() - self.update_toggles = False def update_frogpilot_sounds(self): self.volume_map = { diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index d8a219944843d5..2dda847f9c7e25 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -45,12 +45,16 @@ int get_path_length_idx(const cereal::XYZTData::Reader &line, const float path_h } void update_leads(UIState *s, const cereal::RadarState::Reader &radar_state, const cereal::XYZTData::Reader &line) { - cereal::RadarState::LeadData::Reader (cereal::RadarState::Reader::*get_lead_data[2])() const = { + cereal::RadarState::LeadData::Reader (cereal::RadarState::Reader::*get_lead_data[6])() const = { &cereal::RadarState::Reader::getLeadOne, &cereal::RadarState::Reader::getLeadTwo, + &cereal::RadarState::Reader::getLeadLeft, + &cereal::RadarState::Reader::getLeadRight, + &cereal::RadarState::Reader::getLeadLeftFar, + &cereal::RadarState::Reader::getLeadRightFar }; - for (int i = 0; i < 2; ++i) { + for (int i = 0; i < 6; ++i) { auto lead_data = (radar_state.*get_lead_data[i])(); if (lead_data.getStatus()) { float z = line.getZ()[get_path_length_idx(line, lead_data.getDRel())]; @@ -129,7 +133,7 @@ void update_model(UIState *s, auto lead_count = model.getLeadsV3().size(); if (lead_count > 0) { auto lead_one = model.getLeadsV3()[0]; - scene.has_lead = lead_one.getProb() > scene.lead_detection_threshold; + scene.has_lead = lead_one.getProb() > scene.lead_detection_probability; if (scene.has_lead) { const float lead_d = lead_one.getX()[0] * 2.; @@ -140,7 +144,7 @@ void update_model(UIState *s, } } else { auto lead_one = (*s->sm)["radarState"].getRadarState().getLeadOne(); - scene.has_lead = lead_one.getModelProb() > scene.lead_detection_threshold; + scene.has_lead = lead_one.getModelProb() > scene.lead_detection_probability; if (scene.has_lead) { const float lead_d = lead_one.getDRel() * 2.; @@ -148,7 +152,7 @@ void update_model(UIState *s, } } max_idx = get_path_length_idx(plan_position, max_distance); - update_line_data(s, plan_position, scene.model_ui ? path_width * (1 - scene.path_edge_width / 100.0f) : 0.9, 1.22, &scene.track_vertices, max_idx, false); + update_line_data(s, plan_position, scene.model_ui ? path_width * (1 - (scene.path_edge_width / 100.0f)) : 0.9, 1.22, &scene.track_vertices, max_idx, false); // Update path edges update_line_data(s, plan_position, scene.model_ui ? path_width : 0, 1.22, &scene.track_edge_vertices, max_idx, false); @@ -196,6 +200,7 @@ static void update_sockets(UIState *s) { } static void update_state(UIState *s) { + Params params = Params(); SubMaster &sm = *(s->sm); UIScene &scene = s->scene; @@ -297,6 +302,10 @@ static void update_state(UIState *s) { scene.stopped_equivalence = frogpilotPlan.getStoppedEquivalenceFactor(); scene.unconfirmed_speed_limit = frogpilotPlan.getUnconfirmedSlcSpeedLimit(); scene.vtsc_controlling_curve = frogpilotPlan.getVtscControllingCurve(); + if (frogpilotPlan.getTogglesUpdated()) { + scene.frogpilot_toggles = QJsonDocument::fromJson(QString::fromStdString(params.get("FrogPilotToggles")).toUtf8()).object(); + ui_update_params(uiState()); + } } if (sm.updated("liveLocationKalman")) { auto liveLocationKalman = sm["liveLocationKalman"].getLiveLocationKalman(); @@ -332,142 +341,94 @@ void ui_update_params(UIState *s) { s->scene.is_metric = params.getBool("IsMetric"); s->scene.map_on_left = params.getBool("NavSettingLeftSide"); - ui_update_frogpilot_params(s, params); + ui_update_frogpilot_params(s); } -void ui_update_frogpilot_params(UIState *s, Params ¶ms) { +void ui_update_frogpilot_params(UIState *s) { UIScene &scene = s->scene; - std::string carParams = params.get("CarParamsPersistent"); - if (!carParams.empty()) { - AlignedBuffer aligned_buf; - capnp::FlatArrayMessageReader cmsg(aligned_buf.align(carParams.data(), carParams.size())); - cereal::CarParams::Reader CP = cmsg.getRoot(); - scene.longitudinal_control = hasLongitudinalControl(CP); - } - - float distance_conversion = scene.is_metric ? 1.0f : FOOT_TO_METER; - float small_distance_conversion = scene.is_metric ? 1.0f : INCH_TO_CM; - float speed_conversion = scene.is_metric ? 1 / MS_TO_KPH : 1 / MS_TO_MPH; - - bool advanced_custom_onroad_ui = params.getBool("AdvancedCustomUI"); - scene.camera_view = advanced_custom_onroad_ui ? params.getInt("CameraView") : 0; - scene.hide_lead_marker = advanced_custom_onroad_ui && params.getBool("HideLeadMarker"); - scene.hide_speed = advanced_custom_onroad_ui && params.getBool("HideSpeed"); - scene.hide_speed_ui = scene.hide_speed && params.getBool("HideSpeedUI"); - bool hide_ui_elements = advanced_custom_onroad_ui && params.getBool("HideUIElements"); - scene.hide_alerts = hide_ui_elements && params.getBool("HideAlerts"); - scene.hide_map_icon = hide_ui_elements && params.getBool("HideMapIcon"); - scene.hide_max_speed = hide_ui_elements && params.getBool("HideMaxSpeed"); - scene.show_stopping_point = advanced_custom_onroad_ui && params.getBool("ShowStoppingPoint"); - scene.show_stopping_point_metrics = scene.show_stopping_point && params.getBool("ShowStoppingPointMetrics"); - scene.wheel_speed = advanced_custom_onroad_ui && params.getBool("WheelSpeed"); - - bool always_on_lateral = params.getBool("AlwaysOnLateral"); - scene.show_aol_status_bar = always_on_lateral && !params.getBool("HideAOLStatusBar"); - - bool personalize_openpilot = params.getBool("PersonalizeOpenpilot"); + scene.acceleration_path = static_cast(scene.frogpilot_toggles.value("acceleration_path").toBool()); + scene.adjacent_path = static_cast(scene.frogpilot_toggles.value("adjacent_paths").toBool()); + scene.adjacent_path_metrics = static_cast(scene.frogpilot_toggles.value("adjacent_path_metrics").toBool()); + scene.aol_status_bar = static_cast(scene.frogpilot_toggles.value("always_on_lateral_status_bar").toBool()); + scene.big_map = static_cast(scene.frogpilot_toggles.value("big_map").toBool()); + scene.blind_spot_path = static_cast(scene.frogpilot_toggles.value("blind_spot_path").toBool()); + scene.camera_view = scene.frogpilot_toggles.value("camera_view").toDouble(); + scene.cem_status_bar = static_cast(scene.frogpilot_toggles.value("conditional_status_bar").toBool()); + scene.compass = static_cast(scene.frogpilot_toggles.value("compass").toBool()); + scene.conditional_experimental = static_cast(scene.frogpilot_toggles.value("conditional_experimental_mode").toBool()); + scene.conditional_limit = scene.frogpilot_toggles.value("conditional_limit").toDouble(); + scene.conditional_limit_lead = scene.frogpilot_toggles.value("conditional_limit_lead").toDouble(); + scene.cpu_metrics = static_cast(scene.frogpilot_toggles.value("cpu_metrics").toBool()); + scene.disable_curve_speed_smoothing = static_cast(scene.frogpilot_toggles.value("disable_curve_speed_smoothing").toBool()); + scene.driver_camera_in_reverse = static_cast(scene.frogpilot_toggles.value("driver_camera_in_reverse").toBool()); + scene.dynamic_path_width = static_cast(scene.frogpilot_toggles.value("dynamic_path_width").toBool()); + scene.dynamic_pedals_on_ui = static_cast(scene.frogpilot_toggles.value("dynamic_pedals_on_ui").toBool()); + scene.experimental_mode_via_tap = static_cast(scene.frogpilot_toggles.value("experimental_mode_via_tap").toBool()); + scene.fahrenheit = static_cast(scene.frogpilot_toggles.value("fahrenheit").toBool()); + scene.full_map = static_cast(scene.frogpilot_toggles.value("full_map").toBool()); + scene.gpu_metrics = static_cast(scene.frogpilot_toggles.value("gpu_metrics").toBool()); + scene.hide_alerts = static_cast(scene.frogpilot_toggles.value("hide_alerts").toBool()); + scene.hide_lead_marker = static_cast(scene.frogpilot_toggles.value("hide_lead_marker").toBool()); + scene.hide_map_icon = static_cast(scene.frogpilot_toggles.value("hide_map_icon").toBool()); + scene.hide_max_speed = static_cast(scene.frogpilot_toggles.value("hide_max_speed").toBool()); + scene.hide_speed = static_cast(scene.frogpilot_toggles.value("hide_speed").toBool()); + scene.ip_metrics = static_cast(scene.frogpilot_toggles.value("ip_metrics").toBool()); + scene.jerk_metrics = static_cast(scene.frogpilot_toggles.value("jerk_metrics").toBool()); + scene.lateral_tuning_metrics = static_cast(scene.has_auto_tune && scene.frogpilot_toggles.value("lateral_tuning_metrics").toBool()); + scene.lane_detection_width = scene.frogpilot_toggles.value("lane_detection_width").toDouble(); + scene.lane_line_width = scene.frogpilot_toggles.value("lane_line_width").toDouble(); scene.lane_lines_color = loadThemeColors("LaneLines"); + scene.lead_detection_probability = scene.frogpilot_toggles.value("lead_detection_probability").toDouble(); scene.lead_marker_color = loadThemeColors("LeadMarker"); + scene.lead_metrics = static_cast(scene.frogpilot_toggles.value("lead_metrics").toBool()); + scene.map_style = scene.frogpilot_toggles.value("map_style").toDouble(); + scene.memory_metrics = static_cast(scene.frogpilot_toggles.value("memory_metrics").toBool()); + scene.minimum_lane_change_speed = scene.frogpilot_toggles.value("minimum_lane_change_speed").toDouble(); + scene.model_randomizer = static_cast(scene.frogpilot_toggles.value("model_randomizer").toBool()); + scene.model_ui = static_cast(scene.frogpilot_toggles.value("model_ui").toBool()); + scene.numerical_temp = static_cast(scene.frogpilot_toggles.value("numerical_temp").toBool()); + scene.onroad_distance_button = static_cast(scene.frogpilot_toggles.value("onroad_distance_button").toBool()); scene.path_color = loadThemeColors("Path"); + scene.path_edge_width = scene.frogpilot_toggles.value("path_edge_width").toDouble(); scene.path_edges_color = loadThemeColors("PathEdge"); - scene.road_edges_color = loadThemeColors("RoadEdges"); + scene.path_width = scene.frogpilot_toggles.value("path_width").toDouble(); + scene.pedals_on_ui = static_cast(scene.frogpilot_toggles.value("pedals_on_ui").toBool()); + scene.radarless_model = static_cast(scene.frogpilot_toggles.value("radarless_model").toBool()); + scene.random_events = static_cast(scene.frogpilot_toggles.value("random_events").toBool()); + scene.road_edge_width = scene.frogpilot_toggles.value("road_edge_width").toDouble(); + scene.road_name_ui = static_cast(scene.frogpilot_toggles.value("road_name_ui").toBool()); + scene.rotating_wheel = static_cast(scene.frogpilot_toggles.value("rotating_wheel").toBool()); + scene.screen_brightness = scene.frogpilot_toggles.value("screen_brightness").toDouble(); + scene.screen_brightness_onroad = scene.frogpilot_toggles.value("screen_brightness_onroad").toDouble(); + scene.screen_recorder = static_cast(scene.frogpilot_toggles.value("screen_recorder").toBool()); + scene.screen_timeout = scene.frogpilot_toggles.value("screen_timeout").toDouble(); + scene.screen_timeout_onroad = scene.frogpilot_toggles.value("screen_timeout_onroad").toDouble(); + scene.show_blind_spot = static_cast(scene.frogpilot_toggles.value("blind_spot_metrics").toBool()); + scene.show_fps = static_cast(scene.frogpilot_toggles.value("show_fps").toBool()); + scene.show_speed_limit_offset = static_cast(scene.frogpilot_toggles.value("show_speed_limit_offset").toBool()); + scene.show_stopping_point = static_cast(scene.frogpilot_toggles.value("show_stopping_point").toBool()); + scene.show_stopping_point_metrics = static_cast(scene.frogpilot_toggles.value("show_stopping_point_metrics").toBool()); scene.sidebar_color1 = loadThemeColors("Sidebar1"); scene.sidebar_color2 = loadThemeColors("Sidebar2"); scene.sidebar_color3 = loadThemeColors("Sidebar3"); - scene.use_stock_colors = !personalize_openpilot || params.getBool("UseStockColors"); - scene.use_stock_wheel = !personalize_openpilot || QString::fromStdString(params.get("WheelIcon")) == "stock"; - - scene.conditional_experimental = scene.longitudinal_control && params.getBool("ConditionalExperimental"); - scene.conditional_speed = scene.conditional_experimental ? params.getInt("CESpeed") : 0; - scene.conditional_speed_lead = scene.conditional_experimental ? params.getInt("CESpeedLead") : 0; - scene.show_cem_status_bar = scene.conditional_experimental && !params.getBool("HideCEMStatusBar"); - - bool custom_onroad_ui = params.getBool("CustomUI"); - bool custom_paths = custom_onroad_ui && params.getBool("CustomPaths"); - scene.acceleration_path = custom_paths && params.getBool("AccelerationPath"); - scene.adjacent_path = custom_paths && params.getBool("AdjacentPath"); - scene.blind_spot_path = custom_paths && params.getBool("BlindSpotPath"); - scene.compass = custom_onroad_ui && params.getBool("Compass"); - scene.dynamic_path_width = custom_onroad_ui && params.getBool("DynamicPathWidth"); - scene.pedals_on_ui = custom_onroad_ui && params.getBool("PedalsOnUI"); - scene.dynamic_pedals_on_ui = scene.pedals_on_ui && params.getBool("DynamicPedalsOnUI"); - scene.static_pedals_on_ui = scene.pedals_on_ui && params.getBool("StaticPedalsOnUI"); - scene.road_name_ui = custom_onroad_ui && params.getBool("RoadNameUI"); - scene.rotating_wheel = custom_onroad_ui && params.getBool("RotatingWheel"); - - bool developer_ui = params.getBool("DeveloperUI"); - bool border_metrics = developer_ui && params.getBool("BorderMetrics"); - scene.show_blind_spot = border_metrics && params.getBool("BlindSpotMetrics"); - scene.show_fps = developer_ui && params.getBool("FPSCounter"); - scene.show_signal = border_metrics && params.getBool("SignalMetrics"); - scene.show_steering = border_metrics && params.getBool("ShowSteering"); - bool show_lateral = developer_ui && params.getBool("LateralMetrics"); - scene.adjacent_path_metrics = show_lateral && params.getBool("AdjacentPathMetrics"); - scene.show_tuning = show_lateral && scene.has_auto_tune && params.getBool("TuningInfo"); - bool show_longitudinal = scene.longitudinal_control && developer_ui && params.getBool("LongitudinalMetrics"); - scene.lead_info = show_longitudinal && params.getBool("LeadInfo"); - scene.show_jerk = show_longitudinal && params.getBool("JerkInfo"); - scene.numerical_temp = developer_ui && params.getBool("NumericalTemp"); - scene.fahrenheit = scene.numerical_temp && params.getBool("Fahrenheit"); - scene.sidebar_metrics = developer_ui && params.getBool("SidebarMetrics"); - scene.is_CPU = scene.sidebar_metrics && params.getBool("ShowCPU"); - scene.is_GPU = scene.sidebar_metrics && params.getBool("ShowGPU"); - scene.is_IP = scene.sidebar_metrics && params.getBool("ShowIP"); - scene.is_memory = scene.sidebar_metrics && params.getBool("ShowMemoryUsage"); - scene.is_storage_left = scene.sidebar_metrics && params.getBool("ShowStorageLeft"); - scene.is_storage_used = scene.sidebar_metrics && params.getBool("ShowStorageUsed"); - scene.use_si = developer_ui && params.getBool("UseSI"); - - scene.disable_curve_speed_smoothing = params.getBool("CurveSpeedControl") && params.getBool("DisableCurveSpeedSmoothing"); - - scene.experimental_mode_via_screen = scene.longitudinal_control && params.getBool("ExperimentalModeActivation") && params.getBool("ExperimentalModeViaTap"); - - bool lane_change_customizations = params.getBool("LaneChangeCustomizations"); - scene.lane_detection_width = lane_change_customizations ? params.getInt("LaneDetectionWidth") * distance_conversion / 10.0f : 2.75f; - scene.minimum_lane_change_speed = lane_change_customizations ? params.getInt("MinimumLaneChangeSpeed") * speed_conversion : 20 * (1 / MS_TO_MPH); - - bool advanced_longitudinal_tune = scene.longitudinal_control && params.getBool("AdvancedLongitudinalTune"); - scene.radarless_model = params.get("Model") == "radical-turtle"; - scene.lead_detection_threshold = advanced_longitudinal_tune && !scene.radarless_model ? params.getInt("LeadDetectionThreshold") / 100.0f : 0.5; - - bool model_manager = params.getBool("ModelManagement"); - scene.model_randomizer = model_manager && params.getBool("ModelRandomizer"); - - scene.model_ui = params.getBool("ModelUI"); - scene.lane_line_width = params.getInt("LaneLinesWidth") * small_distance_conversion / 200.0f; - scene.path_edge_width = params.getInt("PathEdgeWidth"); - scene.path_width = params.getFloat("PathWidth") * distance_conversion / 2.0f; - scene.road_edge_width = params.getInt("RoadEdgesWidth") * small_distance_conversion / 200.0f; - scene.unlimited_road_ui_length = scene.model_ui && params.getBool("UnlimitedLength"); - - bool quality_of_life_longitudinal = params.getBool("QOLLongitudinal"); - scene.onroad_distance_button = quality_of_life_longitudinal && params.getBool("OnroadDistanceButton"); - scene.reverse_cruise = quality_of_life_longitudinal && params.getBool("ReverseCruise"); - - bool quality_of_life_visuals = params.getBool("QOLVisuals"); - scene.big_map = quality_of_life_visuals && params.getBool("BigMap"); - scene.full_map = scene.big_map && params.getBool("FullMap"); - scene.driver_camera = quality_of_life_visuals && params.getBool("DriverCamera"); - scene.map_style = quality_of_life_visuals ? params.getInt("MapStyle") : 0; - scene.standby_mode = quality_of_life_visuals && params.getBool("StandbyMode"); - scene.stopped_timer = quality_of_life_visuals && params.getBool("StoppedTimer"); - - scene.random_events = params.getBool("RandomEvents"); - - bool screen_management = params.getBool("ScreenManagement"); - scene.screen_brightness = screen_management ? params.getInt("ScreenBrightness") : 101; - scene.screen_brightness_onroad = screen_management ? params.getInt("ScreenBrightnessOnroad") : 101; - scene.screen_recorder = screen_management && params.getBool("ScreenRecorder"); - scene.screen_timeout = screen_management ? params.getInt("ScreenTimeout") : 30; - scene.screen_timeout_onroad = screen_management ? params.getInt("ScreenTimeoutOnroad") : 10; - - scene.speed_limit_controller = scene.longitudinal_control && params.getBool("SpeedLimitController"); - scene.show_slc_offset = scene.speed_limit_controller && params.getBool("ShowSLCOffset"); - scene.show_slc_offset_ui = scene.speed_limit_controller && params.getBool("ShowSLCOffsetUI"); - scene.use_vienna_slc_sign = scene.speed_limit_controller && params.getBool("UseVienna"); - - scene.tethering_config = params.getInt("TetheringEnabled"); + scene.sidebar_metrics = static_cast(scene.frogpilot_toggles.value("sidebar_metrics").toBool()); + scene.signal_metrics = static_cast(scene.frogpilot_toggles.value("signal_metrics").toBool()); + scene.speed_limit_controller = static_cast(scene.frogpilot_toggles.value("speed_limit_controller").toBool()); + scene.speed_limit_vienna = static_cast(scene.frogpilot_toggles.value("speed_limit_vienna").toBool()); + scene.standby_mode = static_cast(scene.frogpilot_toggles.value("standby_mode").toBool()); + scene.static_pedals_on_ui = static_cast(scene.frogpilot_toggles.value("static_pedals_on_ui").toBool()); + scene.steering_metrics = static_cast(scene.frogpilot_toggles.value("steering_metrics").toBool()); + scene.stopped_timer = static_cast(scene.frogpilot_toggles.value("stopped_timer").toBool()); + scene.storage_left_metrics = static_cast(scene.frogpilot_toggles.value("storage_left_metrics").toBool()); + scene.storage_used_metrics = static_cast(scene.frogpilot_toggles.value("storage_used_metrics").toBool()); + scene.tethering_config = scene.frogpilot_toggles.value("tethering_config").toDouble(); + scene.unlimited_road_ui_length = static_cast(scene.frogpilot_toggles.value("unlimited_road_ui_length").toBool()); + scene.use_si_metrics = static_cast(scene.frogpilot_toggles.value("use_si_metrics").toBool()); + scene.use_stock_colors = static_cast(scene.frogpilot_toggles.value("color_scheme").toString() == "stock"); + scene.use_stock_wheel = static_cast(scene.frogpilot_toggles.value("wheel_image").toString() == "stock"); + scene.use_wheel_speed = static_cast(scene.frogpilot_toggles.value("use_wheel_speed").toBool()); + if (scene.tethering_config == 1) { WifiManager(s).setTetheringEnabled(true); } @@ -535,6 +496,7 @@ UIState::UIState(QObject *parent) : QObject(parent) { // FrogPilot variables wifi = new WifiManager(this); + scene.frogpilot_toggles = QJsonDocument::fromJson(QString::fromStdString(params.get("FrogPilotToggles")).toUtf8()).object(); ui_update_params(this); } @@ -548,37 +510,21 @@ void UIState::update() { } emit uiUpdate(*this); - // Update FrogPilot parameters - static bool theme_updated = false; - static bool update_toggles = false; - - if (paramsMemory.getBool("FrogPilotTogglesUpdated")) { - update_toggles = true; - } else if (update_toggles) { - ui_update_params(this); - update_toggles = false; - } - + // Update FrogPilot variables if (paramsMemory.getBool("DriveRated")) { emit driveRated(); paramsMemory.remove("DriveRated"); } - if (theme_updated) { + if (paramsMemory.getBool("ThemeUpdated")) { loadThemeColors("", true); - ui_update_params(this); - - paramsMemory.remove("UpdateTheme"); - - theme_updated = false; - } else if (paramsMemory.getBool("UpdateTheme")) { - theme_updated = true; + paramsMemory.remove("ThemeUpdated"); } // FrogPilot variables that need to be constantly updated scene.conditional_status = scene.conditional_experimental && scene.enabled ? paramsMemory.getInt("CEStatus") : 0; - scene.driver_camera_timer = scene.driver_camera && scene.reverse ? scene.driver_camera_timer + 1 : 0; + scene.driver_camera_timer = scene.driver_camera_in_reverse && scene.reverse ? scene.driver_camera_timer + 1 : 0; scene.force_onroad = paramsMemory.getBool("ForceOnroad"); scene.started_timer = scene.started || started_prev ? scene.started_timer + 1 : 0; } diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index 9e5596921f08be..9e1d55ca60835d 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -104,7 +104,7 @@ typedef struct UIScene { QPolygonF road_edge_vertices[2]; // lead - QPointF lead_vertices[2]; + QPointF lead_vertices[6]; // DMoji state float driver_pose_vals[3]; @@ -122,27 +122,44 @@ typedef struct UIScene { uint64_t started_frame; // FrogPilot variables + QColor lane_lines_color; + QColor lead_marker_color; + QColor path_color; + QColor path_edges_color; + QColor sidebar_color1; + QColor sidebar_color2; + QColor sidebar_color3; + + QJsonObject frogpilot_toggles; + + QPolygonF track_adjacent_vertices[6]; + QPolygonF track_edge_vertices; + bool acceleration_path; bool adjacent_path; bool adjacent_path_metrics; bool always_on_lateral_active; + bool aol_status_bar; bool big_map; bool blind_spot_left; bool blind_spot_path; bool blind_spot_right; bool brake_lights_on; + bool cem_status_bar; bool compass; bool conditional_experimental; + bool cpu_metrics; bool disable_curve_speed_smoothing; - bool driver_camera; + bool driver_camera_in_reverse; bool dynamic_path_width; bool dynamic_pedals_on_ui; bool enabled; bool experimental_mode; - bool experimental_mode_via_screen; + bool experimental_mode_via_tap; bool fahrenheit; bool force_onroad; bool full_map; + bool gpu_metrics; bool has_auto_tune; bool has_lead; bool hide_alerts; @@ -150,16 +167,13 @@ typedef struct UIScene { bool hide_map_icon; bool hide_max_speed; bool hide_speed; - bool hide_speed_ui; - bool is_CPU; - bool is_GPU; - bool is_IP; - bool is_memory; - bool is_storage_left; - bool is_storage_used; - bool lead_info; + bool ip_metrics; + bool jerk_metrics; + bool lateral_tuning_metrics; + bool lead_metrics; bool live_valid; bool map_open; + bool memory_metrics; bool model_randomizer; bool model_ui; bool numerical_temp; @@ -171,44 +185,40 @@ typedef struct UIScene { bool random_events; bool red_light; bool reverse; - bool reverse_cruise; bool right_hand_drive; bool road_name_ui; bool rotating_wheel; bool screen_recorder; - bool show_aol_status_bar; bool show_blind_spot; - bool show_cem_status_bar; bool show_fps; - bool show_jerk; - bool show_signal; - bool show_slc_offset; - bool show_slc_offset_ui; - bool show_steering; + bool show_speed_limit_offset; bool show_stopping_point; bool show_stopping_point_metrics; - bool show_tuning; bool sidebar_metrics; + bool signal_metrics; bool speed_limit_changed; bool speed_limit_controller; bool speed_limit_overridden; + bool speed_limit_vienna; bool standby_mode; bool standstill; bool static_pedals_on_ui; + bool steering_metrics; bool stopped_timer; + bool storage_left_metrics; + bool storage_used_metrics; bool tethering_enabled; bool traffic_mode; bool traffic_mode_active; bool turn_signal_left; bool turn_signal_right; bool unlimited_road_ui_length; - bool use_si; + bool use_si_metrics; bool use_stock_colors; bool use_stock_wheel; - bool use_vienna_slc_sign; + bool use_wheel_speed; bool vtsc_controlling_curve; bool wake_up_screen; - bool wheel_speed; double fps; @@ -222,7 +232,7 @@ typedef struct UIScene { float lane_width_left; float lane_width_right; float lat_accel; - float lead_detection_threshold; + float lead_detection_probability; float path_edge_width; float path_width; float road_edge_width; @@ -236,8 +246,8 @@ typedef struct UIScene { int bearing_deg; int camera_view; - int conditional_speed; - int conditional_speed_lead; + int conditional_limit; + int conditional_limit_lead; int conditional_status; int desired_follow; int driver_camera_timer; @@ -255,18 +265,6 @@ typedef struct UIScene { int stopped_equivalence; int tethering_config; - QColor lane_lines_color; - QColor lead_marker_color; - QColor path_color; - QColor path_edges_color; - QColor road_edges_color; - QColor sidebar_color1; - QColor sidebar_color2; - QColor sidebar_color3; - - QPolygonF track_adjacent_vertices[6]; - QPolygonF track_edge_vertices; - } UIScene; class UIState : public QObject { @@ -368,4 +366,4 @@ void update_line_data(const UIState *s, const cereal::XYZTData::Reader &line, float y_off, float z_off, QPolygonF *pvd, int max_idx, bool allow_invert); // FrogPilot functions -void ui_update_frogpilot_params(UIState *s, Params ¶ms); +void ui_update_frogpilot_params(UIState *s); diff --git a/system/camerad/cameras/camera_qcom2.cc b/system/camerad/cameras/camera_qcom2.cc index 67215aea993226..096e288cc2e4a2 100644 --- a/system/camerad/cameras/camera_qcom2.cc +++ b/system/camerad/cameras/camera_qcom2.cc @@ -958,12 +958,6 @@ void process_road_camera(MultiCameraState *s, CameraState *c, int cnt) { } void cameras_run(MultiCameraState *s) { - // FrogPilot variables - Params paramsMemory{"/dev/shm/params"}; - const std::chrono::seconds fpsUpdateInterval(1); - std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now(); - int frameCount = 0; - LOG("-- Starting threads"); std::vector threads; if (s->driver_cam.enabled) threads.push_back(start_process_thread(s, &s->driver_cam, process_driver_camera)); @@ -1006,17 +1000,6 @@ void cameras_run(MultiCameraState *s) { // for debugging //do_exit = do_exit || event_data->u.frame_msg.frame_id > (30*20); - frameCount++; - - std::chrono::steady_clock::time_point currentTime = std::chrono::steady_clock::now(); - if (currentTime - startTime >= fpsUpdateInterval) { - auto duration = std::chrono::duration_cast(currentTime - startTime).count(); - double fps = frameCount / duration; - paramsMemory.putIntNonBlocking("CameraFPS", fps / 3); - frameCount = 0; - startTime = currentTime; - } - if (event_data->session_hdl == s->road_cam.session_handle) { s->road_cam.handle_camera_event(event_data); } else if (event_data->session_hdl == s->wide_road_cam.session_handle) { diff --git a/system/hardware/hardwared.py b/system/hardware/hardwared.py index cfde93ba101c69..0bb9d33284146c 100755 --- a/system/hardware/hardwared.py +++ b/system/hardware/hardwared.py @@ -25,7 +25,7 @@ from openpilot.system.hardware.fan_controller import TiciFanController from openpilot.system.version import terms_version, training_version -from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables +from openpilot.selfdrive.frogpilot.frogpilot_variables import get_frogpilot_toggles ThermalStatus = log.DeviceState.ThermalStatus NetworkType = log.DeviceState.NetworkType @@ -166,7 +166,7 @@ def hw_state_thread(end_event, hw_queue): def hardware_thread(end_event, hw_queue) -> None: pm = messaging.PubMaster(['deviceState', 'frogpilotDeviceState']) - sm = messaging.SubMaster(["peripheralState", "gpsLocationExternal", "controlsState", "pandaStates"], poll="pandaStates") + sm = messaging.SubMaster(["peripheralState", "gpsLocationExternal", "controlsState", "pandaStates", "frogpilotPlan"], poll="pandaStates") count = 0 @@ -207,13 +207,10 @@ def hardware_thread(end_event, hw_queue) -> None: fan_controller = None # FrogPilot variables - frogpilot_toggles = FrogPilotVariables.toggles - FrogPilotVariables.update_frogpilot_params() + frogpilot_toggles = get_frogpilot_toggles(True) params_memory = Params("/dev/shm/params") - update_toggles = False - while not end_event.is_set(): sm.update(PANDA_STATES_TIMEOUT) @@ -470,11 +467,8 @@ def hardware_thread(end_event, hw_queue) -> None: should_start_prev = should_start # Update FrogPilot parameters - if FrogPilotVariables.toggles_updated: - update_toggles = True - elif update_toggles: - FrogPilotVariables.update_frogpilot_params(started_ts is not None) - update_toggles = False + if sm['frogpilotPlan'].togglesUpdated: + frogpilot_toggles = get_frogpilot_toggles() def main(): hw_queue = queue.Queue(maxsize=1) diff --git a/system/manager/manager.py b/system/manager/manager.py index 8fa51a8b738bfa..32c34ee1d97291 100755 --- a/system/manager/manager.py +++ b/system/manager/manager.py @@ -24,6 +24,8 @@ from openpilot.selfdrive.frogpilot.assets.model_manager import DEFAULT_MODEL, DEFAULT_MODEL_NAME from openpilot.selfdrive.frogpilot.frogpilot_functions import convert_params, frogpilot_boot_functions, setup_frogpilot, uninstall_frogpilot +from openpilot.selfdrive.frogpilot.frogpilot_utilities import update_frogpilot_toggles +from openpilot.selfdrive.frogpilot.frogpilot_variables import frogpilot_default_params, get_frogpilot_toggles def manager_init() -> None: @@ -32,9 +34,7 @@ def manager_init() -> None: build_metadata = get_build_metadata() params = Params() - setup_frogpilot(build_metadata, params) - params_storage = Params("/persist/params") params.clear_all(ParamKeyType.CLEAR_ON_MANAGER_START) params.clear_all(ParamKeyType.CLEAR_ON_ONROAD_TRANSITION) @@ -47,10 +47,13 @@ def manager_init() -> None: default_params: list[tuple[str, str | bytes]] = [ ("AlwaysOnDM", "0"), + ("CalibrationParams", ""), ("CarParamsPersistent", ""), ("CompletedTrainingVersion", "0"), ("DisengageOnAccelerator", "0"), ("ExperimentalLongitudinalEnabled", "0"), + ("ExperimentalMode", "0"), + ("ExperimentalModeConfirmed", "0"), ("GithubSshKeys", ""), ("GithubUsername", ""), ("GsmApn", ""), @@ -60,327 +63,14 @@ def manager_init() -> None: ("IsLdwEnabled", "0"), ("IsMetric", "0"), ("LanguageSetting", "main_en"), + ("LiveTorqueParameters", ""), ("NavSettingLeftSide", "0"), ("NavSettingTime24h", "0"), ("OpenpilotEnabledToggle", "1"), ("RecordFront", "0"), ("SshEnabled", "0"), ("TetheringEnabled", "0"), - ("LongitudinalPersonality", str(log.LongitudinalPersonality.standard)), - - # Default FrogPilot parameters - ("AccelerationPath", "1"), - ("AccelerationProfile", "2"), - ("AdjacentPath", "0"), - ("AdjacentPathMetrics", "0"), - ("AdvancedCustomUI", "0"), - ("AdvancedLateralTune", "0"), - ("AdvancedLongitudinalTune", "1"), - ("AdvancedQOLDriving", "1"), - ("AggressiveFollow", "1.25"), - ("AggressiveJerkAcceleration", "50"), - ("AggressiveJerkDanger", "100"), - ("AggressiveJerkDeceleration", "50"), - ("AggressiveJerkSpeed", "50"), - ("AggressiveJerkSpeedDecrease", "50"), - ("AggressivePersonalityProfile", "1"), - ("AlertVolumeControl", "0"), - ("AlwaysOnLateral", "1"), - ("AlwaysOnLateralLKAS", "0"), - ("AlwaysOnLateralMain", "1"), - ("AMapKey1", ""), - ("AMapKey2", ""), - ("AutomaticallyUpdateModels", "0"), - ("AutomaticUpdates", "1"), - ("AvailableModels", ""), - ("AvailableModelsNames", ""), - ("BigMap", "0"), - ("BlacklistedModels", ""), - ("BlindSpotMetrics", "0"), - ("BlindSpotPath", "1"), - ("BorderMetrics", "1"), - ("CameraView", "2"), - ("CarMake", ""), - ("CarModel", ""), - ("CarModelName", ""), - ("CECurves", "1"), - ("CECurvesLead", "1"), - ("CELead", "1"), - ("CENavigation", "1"), - ("CENavigationIntersections", "1"), - ("CENavigationLead", "1"), - ("CENavigationTurns", "1"), - ("CertifiedHerbalistCalibrationParams", ""), - ("CertifiedHerbalistDrives", "0"), - ("CertifiedHerbalistLiveTorqueParameters", ""), - ("CertifiedHerbalistScore", "0"), - ("CEModelStopTime", "8"), - ("CESignalSpeed", "55"), - ("CESignalLaneDetection", "1"), - ("CESlowerLead", "1"), - ("CESpeed", "0"), - ("CESpeedLead", "0"), - ("CEStoppedLead", "1"), - ("ClusterOffset", "1.015"), - ("Compass", "0"), - ("ConditionalExperimental", "1"), - ("CrosstrekTorque", "1"), - ("CurveSensitivity", "100"), - ("CurveSpeedControl", "1"), - ("CustomAlerts", "1"), - ("CustomColors", "frog"), - ("CustomCruise", "1"), - ("CustomCruiseLong", "5"), - ("CustomDistanceIcons", "stock"), - ("CustomIcons", "frog-animated"), - ("CustomPaths", "1"), - ("CustomPersonalities", "0"), - ("CustomSignals", "frog"), - ("CustomSounds", "frog"), - ("CustomUI", "1"), - ("DecelerationProfile", "1"), - ("DeveloperUI", "0"), - ("DeviceManagement", "1"), - ("DeviceShutdown", "9"), - ("DisableCurveSpeedSmoothing", "0"), - ("DisableOnroadUploads", "0"), - ("DisableOpenpilotLongitudinal", "0"), - ("DisengageVolume", "100"), - ("DriverCamera", "0"), - ("DrivingPersonalities", "0"), - ("DuckAmigoCalibrationParams", ""), - ("DuckAmigoDrives", "0"), - ("DuckAmigoLiveTorqueParameters", ""), - ("DuckAmigoScore", "0"), - ("DynamicPathWidth", "0"), - ("DynamicPedalsOnUI", "1"), - ("EngageVolume", "100"), - ("ExperimentalGMTune", "0"), - ("ExperimentalModeActivation", "1"), - ("ExperimentalModels", ""), - ("ExperimentalModeViaDistance", "1"), - ("ExperimentalModeViaLKAS", "1"), - ("ExperimentalModeViaTap", "0"), - ("Fahrenheit", "0"), - ("ForceAutoTune", "1"), - ("ForceAutoTuneOff", "0"), - ("ForceFingerprint", "0"), - ("ForceMPHDashboard", "0"), - ("ForceStandstill", "0"), - ("ForceStops", "0"), - ("FPSCounter", "1"), - ("FrogsGoMoosTweak", "1"), - ("FullMap", "0"), - ("GameBoyCalibrationParams", ""), - ("GameBoyDrives", "0"), - ("GameBoyLiveTorqueParameters", ""), - ("GameBoyScore", "0"), - ("GasRegenCmd", "1"), - ("GMapKey", ""), - ("GoatScream", "0"), - ("GreenLightAlert", "0"), - ("HideAlerts", "0"), - ("HideAOLStatusBar", "0"), - ("HideCEMStatusBar", "0"), - ("HideLeadMarker", "0"), - ("HideMapIcon", "0"), - ("HideMaxSpeed", "0"), - ("HideSpeed", "0"), - ("HideSpeedUI", "0"), - ("HideUIElements", "0"), - ("HolidayThemes", "1"), - ("HumanAcceleration", "1"), - ("HumanFollowing", "1"), - ("IncreasedStoppedDistance", "3"), - ("IncreaseThermalLimits", "0"), - ("JerkInfo", "1"), - ("LaneChangeCustomizations", "1"), - ("LaneChangeTime", "0"), - ("LaneDetectionWidth", "6"), - ("LaneLinesWidth", "4"), - ("LateralMetrics", "1"), - ("LateralTune", "1"), - ("LeadDepartingAlert", "0"), - ("LeadDetectionThreshold", "35"), - ("LeadInfo", "1"), - ("LockDoors", "1"), - ("LongitudinalMetrics", "1"), - ("LongitudinalTune", "1"), - ("LongPitch", "1"), - ("LosAngelesCalibrationParams", ""), - ("LosAngelesDrives", "0"), - ("LosAngelesLiveTorqueParameters", ""), - ("LosAngelesScore", "0"), - ("LoudBlindspotAlert", "0"), - ("LowVoltageShutdown", str(VBATT_PAUSE_CHARGING)), - ("MapAcceleration", "0"), - ("MapDeceleration", "0"), - ("MapGears", "0"), - ("MapboxPublicKey", ""), - ("MapboxSecretKey", ""), - ("MapsSelected", ""), - ("MapStyle", "10"), - ("MaxDesiredAcceleration", "4.0"), - ("MinimumLaneChangeSpeed", str(LANE_CHANGE_SPEED_MIN / CV.MPH_TO_MS)), - ("Model", DEFAULT_MODEL), - ("ModelManagement", "0"), - ("ModelName", DEFAULT_MODEL_NAME), - ("ModelRandomizer", "0"), - ("ModelSelector", "0"), - ("ModelUI", "1"), - ("MTSCCurvatureCheck", "0"), - ("MTSCEnabled", "1"), - ("NavigationModels", ""), - ("NewLongAPI", "0"), - ("NewLongAPIGM", "1"), - ("NewToyotaTune", "0"), - ("NNFF", "1"), - ("NNFFLite", "1"), - ("NoLogging", "0"), - ("NorthDakotaCalibrationParams", ""), - ("NorthDakotaDrives", "0"), - ("NorthDakotaLiveTorqueParameters", ""), - ("NorthDakotaScore", "0"), - ("NotreDameCalibrationParams", ""), - ("NotreDameDrives", "0"), - ("NotreDameLiveTorqueParameters", ""), - ("NotreDameScore", "0"), - ("NoUploads", "0"), - ("NudgelessLaneChange", "1"), - ("NumericalTemp", "1"), - ("OfflineMode", "1"), - ("Offset1", "5"), - ("Offset2", "5"), - ("Offset3", "5"), - ("Offset4", "10"), - ("OneLaneChange", "1"), - ("OnroadDistanceButton", "0"), - ("PathEdgeWidth", "20"), - ("PathWidth", "6.1"), - ("PauseAOLOnBrake", "0"), - ("PauseLateralOnSignal", "0"), - ("PauseLateralSpeed", "0"), - ("PedalsOnUI", "1"), - ("PersonalizeOpenpilot", "1"), - ("PreferredSchedule", "0"), - ("PromptDistractedVolume", "100"), - ("PromptVolume", "100"), - ("QOLLateral", "1"), - ("QOLLongitudinal", "1"), - ("QOLVisuals", "1"), - ("RadarlessModels", ""), - ("RadicalTurtleCalibrationParams", ""), - ("RadicalTurtleDrives", "0"), - ("RadicalTurtleLiveTorqueParameters", ""), - ("RadicalTurtleScore", "0"), - ("RandomEvents", "0"), - ("RecertifiedHerbalistCalibrationParams", ""), - ("RecertifiedHerbalistDrives", "0"), - ("RecertifiedHerbalistLiveTorqueParameters", ""), - ("RecertifiedHerbalistScore", "0"), - ("RefuseVolume", "100"), - ("RelaxedFollow", "1.75"), - ("RelaxedJerkAcceleration", "100"), - ("RelaxedJerkDanger", "100"), - ("RelaxedJerkDeceleration", "100"), - ("RelaxedJerkSpeed", "100"), - ("RelaxedJerkSpeedDecrease", "100"), - ("RelaxedPersonalityProfile", "1"), - ("ResetFrogTheme", "0"), - ("ReverseCruise", "0"), - ("RoadEdgesWidth", "2"), - ("RoadNameUI", "1"), - ("RotatingWheel", "1"), - ("ScreenBrightness", "101"), - ("ScreenBrightnessOnroad", "101"), - ("ScreenManagement", "1"), - ("ScreenRecorder", "1"), - ("ScreenTimeout", "30"), - ("ScreenTimeoutOnroad", "30"), - ("SearchInput", "0"), - ("SecretGoodOpenpilotCalibrationParams", ""), - ("SecretGoodOpenpilotDrives", "0"), - ("SecretGoodOpenpilotLiveTorqueParameters", ""), - ("SecretGoodOpenpilotScore", "0"), - ("SetSpeedLimit", "0"), - ("SetSpeedOffset", "0"), - ("ShowCPU", "1"), - ("ShowGPU", "0"), - ("ShowIP", "0"), - ("ShowMemoryUsage", "1"), - ("ShowSLCOffset", "1"), - ("ShowSLCOffsetUI", "1"), - ("ShowSteering", "1"), - ("ShowStoppingPoint", "0"), - ("ShowStoppingPointMetrics", "0"), - ("ShowStorageLeft", "0"), - ("ShowStorageUsed", "0"), - ("Sidebar", "0"), - ("SidebarMetrics", "1"), - ("SignalMetrics", "0"), - ("SLCConfirmationHigher", "1"), - ("SLCConfirmationLower", "1"), - ("SLCFallback", "2"), - ("SLCLookaheadHigher", "5"), - ("SLCLookaheadLower", "5"), - ("SLCOverride", "1"), - ("SLCPriority1", "Dashboard"), - ("SLCPriority2", "Offline Maps"), - ("SLCPriority3", "Navigation"), - ("SNGHack", "1"), - ("SpeedLimitChangedAlert", "1"), - ("SpeedLimitController", "1"), - ("StartupMessageBottom", "so I do what I want 🐸"), - ("StartupMessageTop", "Hippity hoppity this is my property"), - ("StandardFollow", "1.45"), - ("StandardJerkAcceleration", "100"), - ("StandardJerkDanger", "100"), - ("StandardJerkDeceleration", "100"), - ("StandardJerkSpeed", "100"), - ("StandardJerkSpeedDecrease", "100"), - ("StandardPersonalityProfile", "1"), - ("StandbyMode", "0"), - ("StaticPedalsOnUI", "0"), - ("SteerFriction", "0.1"), - ("SteerFrictionStock", "0.1"), - ("SteerLatAccel", "2.5"), - ("SteerLatAccelStock", "2.5"), - ("SteerKP", "1"), - ("SteerKPStock", "1"), - ("SteerRatio", "15"), - ("SteerRatioStock", "15"), - ("StoppedTimer", "0"), - ("TacoTune", "0"), - ("TombRaiderCalibrationParams", ""), - ("TombRaiderDrives", "0"), - ("TombRaiderLiveTorqueParameters", ""), - ("TombRaiderScore", "0"), - ("ToyotaDoors", "0"), - ("TrafficFollow", "0.5"), - ("TrafficJerkAcceleration", "50"), - ("TrafficJerkDanger", "100"), - ("TrafficJerkDeceleration", "50"), - ("TrafficJerkSpeed", "50"), - ("TrafficJerkSpeedDecrease", "50"), - ("TrafficPersonalityProfile", "1"), - ("TuningInfo", "1"), - ("TurnAggressiveness", "100"), - ("TurnDesires", "0"), - ("UnlimitedLength", "1"), - ("UnlockDoors", "1"), - ("UseSI", "1"), - ("UseVienna", "0"), - ("VelocityModels", ""), - ("VisionTurnControl", "1"), - ("VoltSNG", "0"), - ("WarningImmediateVolume", "100"), - ("WarningSoftVolume", "100"), - ("WD40CalibrationParams", ""), - ("WD40Drives", "0"), - ("WD40LiveTorqueParameters", ""), - ("WD40Score", "0"), - ("WheelIcon", "frog"), - ("WheelSpeed", "0") + ("LongitudinalPersonality", str(log.LongitudinalPersonality.standard)) ] if not PC: default_params.append(("LastUpdateTime", datetime.datetime.utcnow().isoformat().encode('utf8'))) @@ -389,7 +79,7 @@ def manager_init() -> None: params.put_bool("RecordFront", True) # set unset params - for k, v in default_params: + for k, v in default_params + frogpilot_default_params: if params.get(k) is None or params.get_bool("DoToggleReset"): if params_storage.get(k) is None: params.put(k, v) @@ -476,16 +166,22 @@ def manager_thread() -> None: ignore.append("pandad") ignore += [x for x in os.getenv("BLOCK", "").split(",") if len(x) > 0] - sm = messaging.SubMaster(['deviceState', 'carParams'], poll='deviceState') + sm = messaging.SubMaster(['deviceState', 'carParams', 'frogpilotPlan'], poll='deviceState') pm = messaging.PubMaster(['managerState']) write_onroad_params(False, params) - ensure_running(managed_processes.values(), False, params=params, CP=sm['carParams'], not_run=ignore, secret_good_openpilot=False) + ensure_running(managed_processes.values(), False, params=params, CP=sm['carParams'], not_run=ignore, classic_model=False) started_prev = False # FrogPilot variables - secret_good_openpilot = params.get("Model", encoding='utf-8') == "secret-good-openpilot" + update_frogpilot_toggles() + frogpilot_toggles = get_frogpilot_toggles(True) + classic_model = frogpilot_toggles.classic_model + + error_log = os.path.join(sentry.CRASHES_DIR, 'error.txt') + if os.path.isfile(error_log): + os.remove(error_log) while True: sm.update(1000) @@ -495,12 +191,11 @@ def manager_thread() -> None: if started and not started_prev: params.clear_all(ParamKeyType.CLEAR_ON_ONROAD_TRANSITION) - error_log = os.path.join(sentry.CRASHES_DIR, 'error.txt') if os.path.isfile(error_log): os.remove(error_log) # FrogPilot variables - secret_good_openpilot = params.get("Model", encoding='utf-8') == "secret-good-openpilot" + classic_model = frogpilot_toggles.classic_model elif not started and started_prev: params.clear_all(ParamKeyType.CLEAR_ON_OFFROAD_TRANSITION) @@ -512,7 +207,7 @@ def manager_thread() -> None: started_prev = started - ensure_running(managed_processes.values(), started, params=params, CP=sm['carParams'], not_run=ignore, secret_good_openpilot=secret_good_openpilot) + ensure_running(managed_processes.values(), started, params=params, CP=sm['carParams'], not_run=ignore, classic_model=classic_model) running = ' '.join("{}{}\u001b[0m".format("\u001b[32m" if p.proc.is_alive() else "\u001b[31m", p.name) for p in managed_processes.values() if p.proc) @@ -535,6 +230,9 @@ def manager_thread() -> None: if shutdown: break + # Update FrogPilot parameters + if sm['frogpilotPlan'].togglesUpdated: + frogpilot_toggles = get_frogpilot_toggles() def main() -> None: manager_init() diff --git a/system/manager/process.py b/system/manager/process.py index 7eef9adfd09dc6..54ddd5b3f599ad 100644 --- a/system/manager/process.py +++ b/system/manager/process.py @@ -76,6 +76,9 @@ class ManagerProcess(ABC): watchdog_seen = False shutting_down = False + # FrogPilot variables + started_time = 0 + @abstractmethod def prepare(self) -> None: pass @@ -102,11 +105,14 @@ def check_watchdog(self, started: bool, params: Params) -> None: dt = time.monotonic() - self.last_watchdog_time / 1e9 + self.started_time = self.started_time + 1 if started else 0 + if dt > self.watchdog_max_dt: if self.watchdog_seen and ENABLE_WATCHDOG: cloudlog.error(f"Watchdog timeout for {self.name} (exitcode {self.proc.exitcode}) restarting ({started=})") self.restart() - sentry.capture_tmux(self.name, params) + if self.started_time > 100: + sentry.capture_tmux(self.name, self.started_time, params) else: self.watchdog_seen = True @@ -239,7 +245,7 @@ def __init__(self, name, module, param_name, enabled=True): self.params = None @staticmethod - def should_run(started, params, CP, secret_good_openpilot): + def should_run(started, params, CP, classic_model): return True def prepare(self) -> None: @@ -275,13 +281,13 @@ def stop(self, retry=True, block=True, sig=None) -> None: def ensure_running(procs: ValuesView[ManagerProcess], started: bool, params=None, CP: car.CarParams=None, - not_run: list[str] | None=None, secret_good_openpilot=False) -> list[ManagerProcess]: + not_run: list[str] | None=None, classic_model=False) -> list[ManagerProcess]: if not_run is None: not_run = [] running = [] for p in procs: - if p.enabled and p.name not in not_run and p.should_run(started, params, CP, secret_good_openpilot): + if p.enabled and p.name not in not_run and p.should_run(started, params, CP, classic_model): running.append(p) else: p.stop(block=False) diff --git a/system/manager/process_config.py b/system/manager/process_config.py index 9046c89ef33aa1..10724a530852c3 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -7,54 +7,54 @@ WEBCAM = os.getenv("USE_WEBCAM") is not None -def driverview(started: bool, params: Params, CP: car.CarParams, secret_good_openpilot) -> bool: +def driverview(started: bool, params: Params, CP: car.CarParams, classic_model) -> bool: return started or params.get_bool("IsDriverViewEnabled") -def notcar(started: bool, params: Params, CP: car.CarParams, secret_good_openpilot) -> bool: +def notcar(started: bool, params: Params, CP: car.CarParams, classic_model) -> bool: return started and CP.notCar -def iscar(started: bool, params: Params, CP: car.CarParams, secret_good_openpilot) -> bool: +def iscar(started: bool, params: Params, CP: car.CarParams, classic_model) -> bool: return started and not CP.notCar -def logging(started, params, CP: car.CarParams, secret_good_openpilot) -> bool: +def logging(started, params, CP: car.CarParams, classic_model) -> bool: run = (not CP.notCar) or not params.get_bool("DisableLogging") return started and run def ublox_available() -> bool: return os.path.exists('/dev/ttyHS0') and not os.path.exists('/persist/comma/use-quectel-gps') -def ublox(started, params, CP: car.CarParams, secret_good_openpilot) -> bool: +def ublox(started, params, CP: car.CarParams, classic_model) -> bool: use_ublox = ublox_available() if use_ublox != params.get_bool("UbloxAvailable"): params.put_bool("UbloxAvailable", use_ublox) return started and use_ublox -def qcomgps(started, params, CP: car.CarParams, secret_good_openpilot) -> bool: +def qcomgps(started, params, CP: car.CarParams, classic_model) -> bool: return started and not ublox_available() -def always_run(started, params, CP: car.CarParams, secret_good_openpilot) -> bool: +def always_run(started, params, CP: car.CarParams, classic_model) -> bool: return True -def only_onroad(started: bool, params, CP: car.CarParams, secret_good_openpilot) -> bool: +def only_onroad(started: bool, params, CP: car.CarParams, classic_model) -> bool: return started -def only_offroad(started, params, CP: car.CarParams, secret_good_openpilot) -> bool: +def only_offroad(started, params, CP: car.CarParams, classic_model) -> bool: return not started # FrogPilot functions -def allow_logging(started, params, CP: car.CarParams, secret_good_openpilot) -> bool: +def allow_logging(started, params, CP: car.CarParams, classic_model) -> bool: allow_logging = not (params.get_bool("DeviceManagement") and params.get_bool("NoLogging")) - return allow_logging and logging(started, params, CP, secret_good_openpilot) + return allow_logging and logging(started, params, CP, classic_model) -def allow_uploads(started, params, CP: car.CarParams, secret_good_openpilot) -> bool: +def allow_uploads(started, params, CP: car.CarParams, classic_model) -> bool: allow_uploads = not (params.get_bool("DeviceManagement") and params.get_bool("NoUploads") and not params.get_bool("DisableOnroadUploads")) return allow_uploads -def run_classic_modeld(started, params, CP: car.CarParams, secret_good_openpilot) -> bool: - return started and not secret_good_openpilot +def run_classic_modeld(started, params, CP: car.CarParams, classic_model) -> bool: + return started and classic_model -def run_new_modeld(started, params, CP: car.CarParams, secret_good_openpilot) -> bool: - return started and secret_good_openpilot +def run_new_modeld(started, params, CP: car.CarParams, classic_model) -> bool: + return started and not classic_model procs = [ DaemonProcess("manage_athenad", "system.athena.manage_athenad", "AthenadPid"), diff --git a/system/sentry.py b/system/sentry.py index 082c71457e9f13..15a402a37cf86b 100644 --- a/system/sentry.py +++ b/system/sentry.py @@ -100,7 +100,7 @@ def capture_fingerprint(candidate, params, blocked=False): sentry_sdk.flush() -def capture_tmux(process, params) -> None: +def capture_tmux(process, started_time, params) -> None: updated = params.get("Updated", encoding='utf-8') result = subprocess.run(['tmux', 'capture-pane', '-p', '-S', '-50'], stdout=subprocess.PIPE) @@ -109,7 +109,7 @@ def capture_tmux(process, params) -> None: if lines: with sentry_sdk.configure_scope() as scope: scope.set_extra("tmux_log", "\n".join(lines)) - sentry_sdk.capture_message(f"{process} crashed - Last updated: {updated}", level='info') + sentry_sdk.capture_message(f"{process} crashed - Last updated: {updated} - Started time: {started_time}", level='info') sentry_sdk.flush() diff --git a/system/updated/updated.py b/system/updated/updated.py index 61504c2945896e..9e4f8f5fcd5c93 100755 --- a/system/updated/updated.py +++ b/system/updated/updated.py @@ -22,7 +22,7 @@ from openpilot.system.hardware import AGNOS, HARDWARE from openpilot.system.version import get_build_metadata -from openpilot.selfdrive.frogpilot.frogpilot_variables import FrogPilotVariables +from openpilot.selfdrive.frogpilot.frogpilot_variables import get_frogpilot_toggles LOCK_FILE = os.getenv("UPDATER_LOCK_FILE", "/tmp/safe_staging_overlay.lock") STAGING_ROOT = os.getenv("UPDATER_STAGING_ROOT", "/data/safe_staging") @@ -327,7 +327,7 @@ def get_description(basedir: str) -> str: set_offroad_alert(alert, False) now = datetime.datetime.utcnow() - if FrogPilotVariables.toggles.offline_mode: + if get_frogpilot_toggles(True).offline_mode: last_update = now dt = now - last_update build_metadata = get_build_metadata()