November 1st, 2025 Update

This commit is contained in:
James 2025-11-01 12:00:00 -07:00
parent 2d115c7882
commit ac7eb28792
161 changed files with 21281 additions and 686 deletions

1
.github/update_date vendored Normal file
View File

@ -0,0 +1 @@
2025-11-01

View File

@ -9,6 +9,7 @@ on:
env:
BRANCH: FrogPilot-Staging
GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
jobs:
schedule-update:
@ -22,6 +23,7 @@ jobs:
- name: Checkout ${{ env.BRANCH }}
uses: actions/checkout@v3
with:
persist-credentials: false
ref: ${{ env.BRANCH }}
fetch-depth: 3
@ -42,16 +44,30 @@ jobs:
echo "COMMITTER_DATE=$COMMITTER_DATE" >> $GITHUB_ENV
echo "TARGET_COMMIT=$TARGET_COMMIT" >> $GITHUB_ENV
- name: Create Fixup Commit for ${{ env.TARGET_COMMIT }}
run: git commit --fixup="${{ env.TARGET_COMMIT }}"
- name: Create Fixup Commit if Changes Exist
id: fixup_commit
run: |
if git diff --cached --quiet; then
echo "No changes to commit."
echo "has_changes=false" >> "$GITHUB_OUTPUT"
else
echo "Changes found, creating fixup commit."
git commit --fixup="${{ env.TARGET_COMMIT }}"
echo "has_changes=true" >> "$GITHUB_OUTPUT"
fi
- name: Autosquash Fixup into Target Commit
if: steps.fixup_commit.outputs.has_changes == 'true'
run: |
GIT_SEQUENCE_EDITOR=: git rebase --autosquash -i HEAD~3
- name: Restore Timestamps on Final Two Commits
if: steps.fixup_commit.outputs.has_changes == 'true'
run: |
git rebase --exec "GIT_COMMITTER_DATE='${{ env.COMMITTER_DATE }}' git commit --amend --no-edit --date='${{ env.AUTHOR_DATE }}'" HEAD~2
- name: Push Updated ${{ env.BRANCH }} Branch
run: git push origin "${{ env.BRANCH }}" --force-with-lease
if: steps.fixup_commit.outputs.has_changes == 'true'
run: |
git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}
git push origin "${{ env.BRANCH }}" --force-with-lease

View File

@ -3,12 +3,12 @@ name: Update MAKE-PRS-HERE
on:
push:
branches:
- FrogPilot-Staging
- FrogPilot-Testing
env:
GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
SOURCE_BRANCH: FrogPilot-Staging
SOURCE_BRANCH: FrogPilot-Testing
TARGET_BRANCH: MAKE-PRS-HERE
jobs:

View File

@ -530,14 +530,14 @@ struct CarParams {
struct LateralTorqueTuning {
useSteeringAngle @0 :Bool;
kp @1 :Float32;
ki @2 :Float32;
kd @8 : Float32;
friction @3 :Float32;
kf @4 :Float32;
steeringAngleDeadzoneDeg @5 :Float32;
latAccelFactor @6 :Float32;
latAccelOffset @7 :Float32;
kpDEPRECATED @1 :Float32;
kiDEPRECATED @2 :Float32;
kfDEPRECATED @4 :Float32;
kdDEPRECATED @8 : Float32;
}
struct LongitudinalPIDTuning {
@ -545,7 +545,7 @@ struct CarParams {
kpV @1 :List(Float32);
kiBP @2 :List(Float32);
kiV @3 :List(Float32);
kf @6 :Float32;
kfDEPRECATED @6 :Float32;
deadzoneBP @4 :List(Float32);
deadzoneV @5 :List(Float32);
}

View File

@ -216,6 +216,8 @@ struct FrogPilotPlan @0xa1680744031fdb2d {
trackingLead @32 :Bool;
unconfirmedSlcSpeedLimit @33 :Float32;
vCruise @34 :Float32;
weatherDaytime @35 :Bool;
weatherId @36 :Int16;
}
struct FrogPilotRadarState @0xcb9fd56c7057593a {

View File

@ -795,6 +795,8 @@ struct ControlsState @0x97ff69c53601abf1 {
saturated @7 :Bool;
actualLateralAccel @9 :Float32;
desiredLateralAccel @10 :Float32;
desiredLateralJerk @11 :Float32;
version @12 :Int32;
}
struct LateralLQRState {

View File

@ -268,6 +268,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"CESpeed", PERSISTENT},
{"CESpeedLead", PERSISTENT},
{"CEStatus", CLEAR_ON_OFFROAD_TRANSITION},
{"CEStopLights", PERSISTENT},
{"CEStoppedLead", PERSISTENT},
{"ClusterOffset", PERSISTENT},
{"ColorToDownload", CLEAR_ON_MANAGER_START},
@ -356,10 +357,22 @@ std::unordered_map<std::string, uint32_t> keys = {
{"HideSpeedLimit", PERSISTENT},
{"HigherBitrate", PERSISTENT},
{"HolidayThemes", PERSISTENT},
{"HondaAltTune", PERSISTENT},
{"HondaLowSpeedPedal", PERSISTENT},
{"HondaMaxBrake", PERSISTENT},
{"HumanAcceleration", PERSISTENT},
{"HumanFollowing", PERSISTENT},
{"HumanLaneChanges", PERSISTENT},
{"IconToDownload", CLEAR_ON_MANAGER_START},
{"IncreasedStoppedDistance", PERSISTENT},
{"IncreaseFollowingLowVisibility", PERSISTENT},
{"IncreaseFollowingRain", PERSISTENT},
{"IncreaseFollowingRainStorm", PERSISTENT},
{"IncreaseFollowingSnow", PERSISTENT},
{"IncreasedStoppedDistanceLowVisibility", PERSISTENT},
{"IncreasedStoppedDistanceRain", PERSISTENT},
{"IncreasedStoppedDistanceRainStorm", PERSISTENT},
{"IncreasedStoppedDistanceSnow", PERSISTENT},
{"IncreaseThermalLimits", PERSISTENT},
{"IssueReported", CLEAR_ON_MANAGER_START},
{"KonikDongleId", PERSISTENT},
@ -447,6 +460,14 @@ std::unordered_map<std::string, uint32_t> keys = {
{"RainbowPath", PERSISTENT},
{"RandomEvents", PERSISTENT},
{"RandomThemes", PERSISTENT},
{"ReduceAccelerationLowVisibility", PERSISTENT},
{"ReduceAccelerationRain", PERSISTENT},
{"ReduceAccelerationRainStorm", PERSISTENT},
{"ReduceAccelerationSnow", PERSISTENT},
{"ReduceLateralAccelerationLowVisibility", PERSISTENT},
{"ReduceLateralAccelerationRain", PERSISTENT},
{"ReduceLateralAccelerationRainStorm", PERSISTENT},
{"ReduceLateralAccelerationSnow", PERSISTENT},
{"RefuseVolume", PERSISTENT},
{"RelaxedFollow", PERSISTENT},
{"RelaxedJerkAcceleration", PERSISTENT},
@ -536,6 +557,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"StoppingDecelRate", PERSISTENT},
{"StoppingDecelRateStock", PERSISTENT},
{"StoppedTimer", PERSISTENT},
{"SubaruSNG", PERSISTENT},
{"TacoTune", PERSISTENT},
{"TacoTuneHacks", PERSISTENT},
{"TestAlert", CLEAR_ON_MANAGER_START},
@ -543,7 +565,6 @@ std::unordered_map<std::string, uint32_t> keys = {
{"ThemeDownloadProgress", CLEAR_ON_MANAGER_START},
{"ThemesDownloaded", PERSISTENT},
{"TinygradUpdateAvailable", PERSISTENT},
{"TogglesUpdated", PERSISTENT},
{"ToyotaDoors", PERSISTENT},
{"TrafficFollow", PERSISTENT},
{"TrafficJerkAcceleration", PERSISTENT},
@ -558,6 +579,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"UnlimitedLength", PERSISTENT},
{"UnlockDoors", PERSISTENT},
{"Updated", PERSISTENT},
{"UpdatedToggles", PERSISTENT},
{"UpdateSpeedLimits", CLEAR_ON_MANAGER_START},
{"UpdateSpeedLimitsStatus", CLEAR_ON_MANAGER_START},
{"UpdateTinygrad", CLEAR_ON_MANAGER_START},
@ -574,6 +596,8 @@ std::unordered_map<std::string, uint32_t> keys = {
{"VoltSNG", PERSISTENT},
{"WarningImmediateVolume", PERSISTENT},
{"WarningSoftVolume", PERSISTENT},
{"WeatherPresets", PERSISTENT},
{"WeatherToken", PERSISTENT | DONT_LOG},
{"WheelIcon", PERSISTENT},
{"WheelSpeed", PERSISTENT},
{"WheelToDownload", CLEAR_ON_MANAGER_START},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 630 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -12,7 +12,10 @@ from urllib.parse import quote_plus
from openpilot.common.basedir import BASEDIR
from openpilot.frogpilot.assets.download_functions import GITLAB_URL, download_file, get_remote_file_size, get_repository_url, handle_error, handle_request_error, verify_download
from openpilot.frogpilot.common.frogpilot_utilities import delete_file, extract_tar, load_json_file, update_json_file
from openpilot.frogpilot.common.frogpilot_variables import DEFAULT_MODEL, MODELS_PATH, RESOURCES_REPO, TINYGRAD_FILES, params, params_default, params_memory, update_frogpilot_toggles
from openpilot.frogpilot.common.frogpilot_variables import (
DEFAULT_MODEL, DEFAULT_MODEL_NAME, DEFAULT_MODEL_VERSION, MODELS_PATH, RESOURCES_REPO, TINYGRAD_FILES,
params, params_default, params_memory, update_frogpilot_toggles
)
VERSION = "v16"
VERSION_PATH = MODELS_PATH / "model_version"
@ -528,3 +531,13 @@ class ModelManager:
VERSION_PATH.write_text(VERSION)
print(f"Updated {VERSION_PATH} to {VERSION}")
if len(self.available_models) != len(self.available_model_names) or len(self.available_models) != len(self.model_versions):
print("Model lists are out of sync. Resetting parameters...")
self.available_models = DEFAULT_MODEL
self.available_model_names = DEFAULT_MODEL_NAME
self.model_versions = DEFAULT_MODEL_VERSION
params.put("AvailableModels", self.available_models)
params.put("AvailableModelNames", self.available_model_names)
params.put("ModelVersions", self.model_versions)

View File

@ -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"}]}

View File

@ -0,0 +1 @@
{"input_std":[[8.716972],[1.5770903],[0.76125735],[0.035141002],[1.6173459],[1.608055],[1.5956668],[1.495505],[1.4262352],[1.3462695],[1.2594373],[0.035050083],[0.035089202],[0.035124455],[0.035208944],[0.035322115],[0.035380527],[0.035195734]],"model_test_loss":0.02083713933825493,"input_size":18,"current_date_and_time":"2025-07-15_19-46-38","input_mean":[[16.164469],[-0.030030865],[0.4530188],[-0.011929912],[-0.029048208],[-0.029084828],[-0.028234662],[-0.030135768],[-0.026924115],[-0.022959951],[-0.021531954],[-0.011769504],[-0.011809296],[-0.011859953],[-0.012107499],[-0.012191469],[-0.012183576],[-0.012161244]],"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.05593524],[-2.802646],[0.18381701],[2.7735913],[0.70043844],[-0.67402124],[-0.0306403]],"dense_1_W":[[-0.0060694856,0.708295,-0.0040300265,-0.14072545,0.86508894,-0.20933008,-0.47414628,0.13602145,-0.37351108,-0.1507088,0.8062229,0.32620648,-0.4413763,0.27845934,0.046796944,-0.20166004,0.2805364,-0.17857681],[-1.5183858,-1.1884525,0.012948404,-0.4015399,-0.4482837,1.2997682,0.74758935,-0.18140315,-0.08297701,0.43953654,-1.01308,-0.03576563,0.48874712,-0.35160765,-0.13189487,0.2400938,-0.02507816,0.16568868],[0.012290816,-0.3639789,-0.030819947,0.23707484,0.74310154,0.1940028,0.030501615,0.39026827,0.058221884,0.30168098,0.056437366,-0.20590998,-0.5798354,-0.48759502,0.19185027,-0.1474303,0.01184457,0.2231587],[1.5026484,-1.0851525,0.012162996,0.16663143,0.17487548,1.102379,0.4405227,-0.67651707,0.44908693,-0.2039782,-0.6490339,-0.35403496,0.20746203,-0.3432079,0.028647806,0.107211135,0.23621367,-0.08824623],[0.07099266,-0.93228585,1.2776774,0.0044325506,0.15857935,1.1165909,0.9740256,-1.5084538,-0.9056588,0.12456019,0.5993922,0.3822549,-0.13951926,-0.10822766,0.0038337011,0.18770072,-0.049219564,-0.076232985],[-0.0752,-0.20068114,-1.2384826,-0.049861677,0.4789055,1.0113648,0.5513206,-1.3342785,-0.92097133,-0.06880563,0.89944464,0.117162146,-0.5786451,0.1731132,-0.07269918,-0.08290825,0.61927795,-0.32732028],[0.00064757804,2.5136843,0.013637969,0.09395495,-0.7916261,-0.5194506,-0.6840161,0.43426317,-0.14782813,0.19477805,-0.35974234,0.1649517,0.25925198,-0.43630955,-0.5161664,0.34094948,0.0005879998,-0.01794689]],"activation":"σ"},{"dense_2_W":[[0.0914673,-0.116974674,0.6729949,-0.77771014,-0.41403642,0.14140436,0.73223853],[-0.42032346,0.49695835,-0.5017433,0.87220526,0.1466709,-0.094482444,-1.1789782],[0.680824,-0.33863154,0.41371307,-0.5763291,0.3732767,-0.6742655,0.05377676],[-1.0674618,0.8340688,-0.36008632,0.5650019,0.43301603,0.3166691,-0.33432913],[-0.29696158,-0.7246317,0.16882418,-0.09805153,-0.2640285,0.2763439,0.6544825],[-0.39634275,-0.18197323,0.012897031,0.7196241,0.73943686,0.6189228,-0.44182113],[0.61815614,-0.42130506,0.20303723,-0.53927803,-0.33029187,-0.14950582,0.60663986],[-1.241183,1.4418663,-0.05617688,0.21847261,0.67225105,0.55988765,-1.1410111],[-0.61954373,0.55230546,-0.048563246,0.4581752,-0.59391123,0.20338446,-0.41244057],[-0.56894773,0.7917731,0.059939034,1.0460548,0.0929983,0.5222918,-0.4436079],[0.28442153,0.50281626,0.12484341,0.087528534,0.24470627,-0.27797765,-0.6426289],[0.49003553,-0.2646059,-0.015263944,-0.0018261729,0.23279527,0.0076607405,0.69959176],[0.5684231,-0.27704594,0.45853093,-0.15373382,-0.4746461,-0.5218501,0.7240048]],"activation":"σ","dense_2_b":[[-0.03126267],[-0.10072103],[0.014238525],[-0.08748218],[-0.007306198],[-0.06704365],[-0.015472214],[-0.26747772],[-0.2887335],[-0.20642278],[-0.0904173],[-0.04132339],[-0.055377904]]},{"dense_3_W":[[0.48765612,-0.19100322,0.22134759,-0.21214958,0.017000003,-0.6330459,0.48456302,-0.56035495,-0.4338575,-0.15609962,0.17514223,0.12882961,0.2161585],[-0.2575735,0.010117811,-0.4424225,0.62049,-0.55049926,0.19114582,0.08136277,0.0047532627,-0.36334708,0.23096415,0.23023362,-0.477106,0.0447247],[-0.30809823,-0.56456196,0.18880828,-0.15554532,0.32209295,-0.39576378,0.6519796,-0.52746034,-0.43441087,-0.043550245,0.08788765,-0.019096207,0.4919662]],"activation":"identity","dense_3_b":[[0.026150323],[-0.020686748],[0.031266637]]},{"dense_4_W":[[0.8366031,-0.86834574,0.4038856]],"dense_4_b":[[0.025044668]],"activation":"identity"}]}

View File

@ -1 +1 @@
{"input_std":[[9.69185],[1.540828],[1.1422535],[0.030263359],[1.6225281],[1.5997777],[1.5730089],[1.424277],[1.3139973],[1.1868707],[1.0676206],[0.030144755],[0.030192483],[0.030228745],[0.03032813],[0.030303925],[0.03031116],[0.030152585]],"model_test_loss":0.016827212646603584,"input_size":18,"current_date_and_time":"2025-04-19_23-39-22","input_mean":[[18.069342],[0.022936927],[-0.035108753],[-0.0031993124],[0.025608419],[0.0257225],[0.025207438],[0.018243022],[0.0072243717],[-0.003662435],[-0.011087718],[-0.0031280508],[-0.00311736],[-0.003131182],[-0.0035267863],[-0.004009139],[-0.0044690953],[-0.004855515]],"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.23484495],[-3.458155],[-0.65061724],[-0.26108137],[3.8761656],[-0.41541365],[0.45187888]],"dense_1_W":[[0.13635573,1.1536281,0.06798237,-0.046843782,-0.7127543,-0.34835252,-0.47236907,1.0148009,0.20753199,0.7614391,-0.2116509,-0.045529384,-0.20713837,0.0848377,0.30062005,0.092873566,-0.0018172141,-0.17572787],[-2.305817,-0.5387374,-0.45938638,0.036208823,-0.68773305,-0.26441213,-0.03711056,0.6739384,0.16898149,0.41130275,-0.22770037,0.059460163,0.27309912,-0.30170524,-0.100190274,-0.057042886,-0.05511625,0.14404908],[0.63851106,-0.9484218,0.003824309,-0.14493565,-0.7270558,-0.53677094,1.2788634,-0.3673263,-0.13816923,0.1536996,-0.069223404,-0.018855695,-0.37567106,0.52541614,0.15179054,0.270581,-0.040576197,-0.019753123],[0.4546859,1.1357512,-0.019132193,-0.38276812,0.8006601,-0.15111838,-0.71221024,0.23249602,0.03264209,-0.17714578,0.1318619,0.3071488,0.18388422,-0.13612886,-0.14823768,-0.2395975,-0.0354379,0.12196627],[2.605286,0.015709635,-0.47207975,-0.021277286,-0.37507743,-0.6815685,-0.25641406,0.39210537,0.21238214,0.28833768,-0.13859881,0.23703852,0.10589715,-0.3116963,-0.09627983,0.015015259,-0.085346624,0.14887743],[-0.0121305175,-0.22616854,12.655966,-0.35262594,-1.4045227,-1.9047667,-2.7032876,3.181054,2.0532372,0.6247262,-0.115705006,0.5258064,-0.07909483,0.0964521,-0.10150481,0.09466132,0.0021597208,-0.16544223],[0.23776147,-1.368538,-0.08586094,0.021609895,0.6048695,0.13279381,0.2958448,-0.58526164,-0.1610705,-0.42544538,-0.23418613,-0.1616309,0.25446972,-0.15877886,-0.0019331241,-0.10039961,0.13613056,0.0012668837]],"activation":"σ"},{"dense_2_W":[[-0.5056371,0.81436396,-0.13917822,-0.80037946,-0.59604025,-0.78999925,-0.27873737],[-0.4497414,-1.368353,1.3196373,-0.49935067,3.606633,-2.2615685,2.3584154],[-0.3412033,0.6810419,0.7249957,-1.0309223,0.82100374,-0.77988064,0.18884675],[-1.7150979,3.5381098,0.28818595,-1.2821207,-1.7621884,-1.8116575,1.2756097],[-0.11904634,-0.040943474,-0.83569926,0.25027093,-0.68735474,-0.039093345,0.31753448],[0.44574547,-0.31597528,-0.31122974,0.5979627,-0.31958082,-0.2559935,-0.3460548],[0.26562846,0.13387749,-0.69484717,0.5503631,-0.6730771,0.15695189,-0.59696543],[-0.07987114,0.4068772,0.73369247,-0.9445576,0.6558189,-0.19511124,-0.1866442],[-0.34541175,0.575807,0.13408744,-0.8944516,0.82263863,0.15363207,-0.2057351],[0.66298026,-0.7701199,-0.43378943,0.8820758,0.25864363,0.56878936,0.017015105],[0.4665641,-0.28459665,-0.6549732,0.49505192,-0.8118166,0.046624012,0.20086582],[0.72007984,-0.114670254,0.08311235,0.5432469,0.23292236,-0.13743235,-0.4399662],[0.14168906,0.18236077,0.5422965,-0.1936209,0.08167626,-0.34699216,0.42852318]],"activation":"σ","dense_2_b":[[-0.48524407],[-0.064743735],[-0.02042389],[-0.4295334],[-0.20415938],[0.09099535],[-0.01917629],[-0.030794926],[-0.05813007],[0.08901565],[-0.24796419],[0.112134784],[-0.10460872]]},{"dense_3_W":[[-0.16837993,-0.5737468,0.17649585,-0.49820793,0.524844,0.03699279,0.624474,0.245737,-0.32824486,-0.37557223,0.74005604,0.007622024,0.034378406],[0.18686184,0.20867407,0.27604485,0.8802843,-0.53500175,-0.41362065,-0.5105349,0.40075368,0.5311374,-0.4655133,-0.5392229,-0.6545931,0.42653474],[-0.17580743,0.79863507,0.15938231,0.04163453,0.37848914,-0.17208116,-0.41934988,0.29429907,-0.18246712,-0.33918083,0.23200992,-0.08154047,0.22350831]],"activation":"identity","dense_3_b":[[0.100435525],[-0.05763998],[-0.07539215]]},{"dense_4_W":[[0.063197866,-0.89677685,-0.6206098]],"dense_4_b":[[0.059508733]],"activation":"identity"}]}
{"input_std":[[9.668097],[1.5244726],[1.2700657],[0.03216253],[1.6078212],[1.5848233],[1.5577372],[1.3927742],[1.2935301],[1.1931615],[1.1020583],[0.032136116],[0.03217024],[0.032196537],[0.03225218],[0.032214258],[0.032031752],[0.03179372]],"model_test_loss":0.018995385617017746,"input_size":18,"current_date_and_time":"2025-05-15_14-47-45","input_mean":[[18.09652],[-0.017798662],[0.027145168],[-0.0054331655],[-0.025215305],[-0.022640774],[-0.019500963],[-0.010056124],[-0.0057649272],[0.001960505],[0.0020118994],[-0.005499858],[-0.005477372],[-0.005444631],[-0.0053683305],[-0.005257134],[-0.005035867],[-0.0050971555]],"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.016405884],[1.4187009],[-0.30671495],[-1.4047697],[-0.20383339],[-0.3632745],[-0.028944347]],"dense_1_W":[[0.13070685,-0.6662394,-1.0739188,-0.41523784,0.019285621,-0.5666617,0.02941118,0.2413377,0.11430891,-0.15557496,-0.36541316,0.44118378,0.038414165,0.117383294,-0.026998837,-0.3802126,-0.23443018,0.43427357],[0.5493235,0.7341044,0.30259728,-0.09170222,-0.066015296,0.41798133,-0.062179696,-0.1925674,-0.15322368,0.017662844,0.21162434,-0.12112619,-0.027021965,0.05653779,0.14749303,0.28017008,-0.15880422,-0.07834965],[1.3052936,-0.038109165,1.2192534,-0.18676366,0.21888737,0.45998427,0.94506985,-0.87686205,0.044812016,-0.07512292,0.69492793,-0.59623814,0.3278255,0.39913544,0.15354112,0.04995451,-0.031020898,-0.10582342],[-0.4683151,1.0002598,0.29120648,-0.05860625,0.010191997,0.17896307,-0.3321235,0.19332078,-0.44497126,0.09236495,0.17685813,0.022076255,-0.23972619,0.11622197,0.11574386,0.1640551,0.028836682,-0.14775941],[1.2240227,0.5504716,-1.161672,0.10151114,-0.6311718,-0.4365897,-0.63896364,0.4033335,0.012773022,0.025944846,-0.62420815,0.05751297,0.043608528,-0.13193272,-0.33345297,0.18925929,0.0925279,-0.03600548],[-0.0033800902,-0.41361037,-8.50345,0.27523437,2.0881453,2.3587928,2.1091182,-2.467406,-1.9167688,-0.8961217,-0.22510983,0.037862107,-0.23643681,0.03247474,0.067485854,0.04660773,-0.2583531,0.077512406],[0.0035197088,-0.43985325,0.07051937,0.38219255,-0.67769414,-0.29911998,1.0818627,-0.6217103,-0.5443828,0.22882973,0.013560748,-0.29820752,0.11540977,-0.40309906,-0.00034299958,0.69976795,0.13301164,-0.32934943]],"activation":"σ"},{"dense_2_W":[[-0.014842439,-0.3306524,0.254514,-0.6639507,-0.534733,0.07840164,0.2324318],[-0.15334646,-0.290343,-1.047474,0.41780674,0.04612693,-0.73001504,-0.3465528],[-0.6260714,-0.5115335,-0.71161246,0.4046027,-1.2324069,-1.2851475,-0.16620061],[-0.05566019,0.48536706,-0.42460826,0.83332616,0.47045597,-0.061004266,-1.1440438],[0.42156544,-0.6722497,-0.10824958,-0.32994938,0.31952205,-0.46266714,-0.07230821],[-0.38416666,-0.7271572,-0.39645803,-0.4259647,-0.24740903,-0.17667961,-0.10970643],[0.19652602,0.026280575,-0.1651507,-0.38475782,-0.5457258,-0.1882665,0.82107437],[-0.08976335,0.35238677,0.25231764,0.004611504,-0.5936927,-0.29342756,-0.22477138],[0.25490662,-0.4851788,-0.07538396,-0.36808714,-0.05157315,-0.2439695,0.9720866],[0.48479185,-0.68759876,0.42557424,-0.56957704,-0.32759178,-0.3047872,0.7597776],[0.4211342,-1.4932247,-0.66635305,-0.10591562,-0.9298172,1.04872,0.32208702],[-0.2918295,0.63893354,0.044817235,0.87641054,0.38387123,-0.48429132,-0.8803729],[-0.60936576,-0.29061803,-0.94329685,0.6586833,0.15280105,-0.8124564,-0.538523]],"activation":"σ","dense_2_b":[[-0.009386154],[-0.25074136],[0.02986496],[-0.028872054],[-0.06481484],[-0.2976481],[-0.072934344],[-0.117011935],[0.028236326],[-0.009000545],[-0.42297846],[0.030614687],[-0.105583444]]},{"dense_3_W":[[-0.035136532,-0.27008098,-0.5048698,-0.5295355,0.29823285,0.5058221,-0.09186966,0.26433602,0.50751793,0.49806535,0.8154631,-0.5854975,-0.525581],[0.08374179,0.16962804,0.2263017,0.5973136,0.2815238,-0.0505521,0.26001385,0.36577544,-0.68805265,-0.8057493,-0.42594707,0.633128,0.08724391],[0.5107981,0.064214274,-0.061156582,-0.33331737,0.07700428,-0.40398338,0.3673148,-0.42669702,0.38095382,0.33056983,-0.3025643,-0.58125204,-0.3484819]],"activation":"identity","dense_3_b":[[0.01880667],[-0.04986152],[0.030190725]]},{"dense_4_W":[[-1.0120764,0.477425,-1.0690569]],"dense_4_b":[[-0.027314244]],"activation":"identity"}]}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 KiB

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 493 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 746 KiB

View File

@ -216,6 +216,8 @@ def is_url_pingable(url):
if response.status_code in (405, 501):
response = requests.get(url, headers=headers, timeout=10, allow_redirects=True, stream=True)
return response.ok
except (requests.exceptions.ConnectionError, requests.exceptions.SSLError):
return False
except requests.exceptions.RequestException as error:
print(f"{error.__class__.__name__} while pinging {url}: {error}")
return False
@ -337,12 +339,12 @@ def update_openpilot():
if params.get("UpdaterState", encoding="utf-8") != "idle":
return
while params.get_bool("IsOnroad") or params_memory.get_bool("UpdateSpeedLimits") or running_threads.get("lock_doors", threading.Thread()).is_alive():
time.sleep(60)
if not update_available():
return
while params.get_bool("IsOnroad") or params_memory.get_bool("UpdateSpeedLimits") or running_threads.get("lock_doors", threading.Thread()).is_alive():
time.sleep(60)
while True:
if not update_available():
break

View File

@ -3,6 +3,7 @@ import json
import numpy as np
import os
import random
import tomllib
from functools import cache
from pathlib import Path
@ -15,11 +16,13 @@ from openpilot.common.params import Params
from openpilot.selfdrive.car import gen_empty_fingerprint
from openpilot.selfdrive.car.car_helpers import interfaces
from openpilot.selfdrive.car.gm.values import GMFlags
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
from openpilot.selfdrive.car.interfaces import TORQUE_SUBSTITUTE_PATH, CarInterfaceBase
from openpilot.selfdrive.car.mock.interface import CarInterface
from openpilot.selfdrive.car.mock.values import CAR as MOCK
from openpilot.selfdrive.car.subaru.values import SubaruFlags
from openpilot.selfdrive.car.toyota.values import ToyotaFlags, ToyotaFrogPilotFlags
from openpilot.selfdrive.controls.lib.desire_helper import LANE_CHANGE_SPEED_MIN
from openpilot.selfdrive.controls.lib.latcontrol_torque import KP
from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.system.hardware import HARDWARE
from openpilot.system.hardware.power_monitoring import VBATT_PAUSE_CHARGING
@ -102,12 +105,27 @@ TINYGRAD_FILES = [
@cache
def get_nnff_model_files():
model_dir = Path(NNFF_MODELS_PATH)
return [file.stem for file in model_dir.iterdir() if file.is_file()]
return [file.stem for file in NNFF_MODELS_PATH.iterdir() if file.is_file()]
@cache
def get_nnff_substitutes():
substitutes = {}
with open(TORQUE_SUBSTITUTE_PATH, "rb") as f:
substitutes_data = tomllib.load(f)
substitutes = {key: value for key, value in substitutes_data.items()}
return substitutes
def nnff_supported(car_fingerprint):
for file in get_nnff_model_files():
if file.startswith(car_fingerprint):
model_files = get_nnff_model_files()
substitutes = get_nnff_substitutes()
fingerprints_to_check = [car_fingerprint]
if car_fingerprint in substitutes:
fingerprints_to_check.append(substitutes[car_fingerprint])
for fingerprint in fingerprints_to_check:
for file in model_files:
if file.startswith(fingerprint):
return True
return False
@ -160,7 +178,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("CECurves", "0", 1, "0"),
("CECurvesLead", "0", 1, "0"),
("CELead", "0", 1, "0"),
("CEModelStopTime", str(PLANNER_TIME - 2), 2, "0"),
("CEModelStopTime", str(PLANNER_TIME - 2), 3, "0"),
("CENavigation", "1", 2, "0"),
("CENavigationIntersections", "0", 2, "0"),
("CENavigationLead", "1", 2, "0"),
@ -170,6 +188,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("CESlowerLead", "0", 1, "0"),
("CESpeed", "0", 1, "0"),
("CESpeedLead", "0", 1, "0"),
("CEStopLights", "1", 1, "0"),
("CEStoppedLead", "0", 1, "0"),
("ClusterOffset", "1.015", 2, "1.015"),
("Compass", "0", 1, "0"),
@ -241,9 +260,21 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("HideSpeedLimit", "0", 2, "0"),
("HigherBitrate", "0", 2, "0"),
("HolidayThemes", "1", 0, "0"),
("HondaAltTune", "0", 2, "0"),
("HondaLowSpeedPedal", "0", 2, "0"),
("HondaMaxBrake", "0", 2, "0"),
("HumanAcceleration", "1", 2, "0"),
("HumanFollowing", "1", 2, "0"),
("HumanLaneChanges", "1", 2, "0"),
("IncreasedStoppedDistance", "0", 1, "0"),
("IncreaseFollowingLowVisibility", "0", 2, "0"),
("IncreaseFollowingRain", "0", 2, "0"),
("IncreaseFollowingRainStorm", "0", 2, "0"),
("IncreaseFollowingSnow", "0", 2, "0"),
("IncreasedStoppedDistanceLowVisibility", "0", 2, "0"),
("IncreasedStoppedDistanceRain", "0", 2, "0"),
("IncreasedStoppedDistanceRainStorm", "0", 2, "0"),
("IncreasedStoppedDistanceSnow", "0", 2, "0"),
("IncreaseThermalLimits", "0", 2, "0"),
("IsLdwEnabled", "0", 0, "0"),
("IsMetric", "0", 0, "0"),
@ -320,6 +351,14 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("RandomEvents", "0", 1, "0"),
("RandomThemes", "0", 1, "0"),
("RecordFront", "0", 0, "0"),
("ReduceAccelerationLowVisibility", "0", 2, "0"),
("ReduceAccelerationRain", "0", 2, "0"),
("ReduceAccelerationRainStorm", "0", 2, "0"),
("ReduceAccelerationSnow", "0", 2, "0"),
("ReduceLateralAccelerationLowVisibility", "0", 2, "0"),
("ReduceLateralAccelerationRain", "0", 2, "0"),
("ReduceLateralAccelerationRainStorm", "0", 2, "0"),
("ReduceLateralAccelerationSnow", "0", 2, "0"),
("RefuseVolume", "101", 2, "101"),
("RelaxedFollow", "1.75", 2, "1.75"),
("RelaxedJerkAcceleration", "100", 3, "100"),
@ -404,6 +443,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("StoppingDecelRate", "", 3, ""),
("StoppingDecelRateStock", "", 3, ""),
("StoppedTimer", "0", 1, "0"),
("SubaruSNG", "1", 2, "0"),
("TacoTune", "0", 2, "0"),
("TacoTuneHacks", "0", 2, "0"),
("TetheringEnabled", "0", 0, "0"),
@ -422,6 +462,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("TurnDesires", "0", 2, "0"),
("UnlimitedLength", "1", 2, "0"),
("UnlockDoors", "1", 0, "0"),
("UpdatedToggles", "1", 0, "0"),
("UpdaterAvailableBranches", "", 0, ""),
("UseKonikServer", "0", 2, "0"),
("UseSI", "1", 3, "1"),
@ -434,6 +475,8 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
("VoltSNG", "0", 2, "0"),
("WarningImmediateVolume", "101", 2, "101"),
("WarningSoftVolume", "101", 2, "101"),
("WeatherPresets", "0", 2, "0"),
("WeatherToken", "", 2, ""),
("WheelIcon", "frog", 0, "stock"),
("WheelSpeed", "0", 2, "0")
]
@ -556,6 +599,7 @@ class FrogPilotVariables:
toggle.has_sdsu = toggle.car_make == "toyota" and bool(CP.flags & ToyotaFlags.SMART_DSU.value)
has_sng = CP.autoResumeSng
toggle.has_zss = toggle.car_make == "toyota" and bool(FPCP.fpFlags & ToyotaFrogPilotFlags.ZSS.value)
honda_nidec = CP.safetyConfigs[0].safetyModel == SafetyModel.hondaNidec
is_angle_car = CP.steerControlType == car.CarParams.SteerControlType.angle
latAccelFactor = CP.lateralTuning.torque.latAccelFactor
longitudinalActuatorDelay = CP.longitudinalActuatorDelay
@ -564,7 +608,7 @@ class FrogPilotVariables:
startAccel = CP.startAccel
stopAccel = CP.stopAccel
steerActuatorDelay = CP.steerActuatorDelay
steerKp = CP.lateralTuning.torque.kp
steerKp = CP.lateralTuning.pid.kp if CP.lateralTuning.which() == "pid" else KP
steerRatio = CP.steerRatio
toggle.stoppingDecelRate = CP.stoppingDecelRate
taco_hacks_allowed = CP.safetyConfigs[0].safetyModel == SafetyModel.hyundaiCanfd
@ -605,7 +649,7 @@ class FrogPilotVariables:
toggle.steerRatio = np.clip(params.get_float("SteerRatio"), steerRatio * 0.5, steerRatio * 1.5) if advanced_lateral_tuning and tuning_level >= level["SteerRatio"] else steerRatio
toggle.use_custom_steerRatio = bool(round(toggle.steerRatio, 2) != round(steerRatio, 2)) and not toggle.force_auto_tune or toggle.force_auto_tune_off
advanced_longitudinal_tuning = params.get_bool("AdvancedLongitudinalTune") if tuning_level >= level["AdvancedLongitudinalTune"] else default.get_bool("AdvancedLongitudinalTune")
advanced_longitudinal_tuning = toggle.openpilot_longitudinal and (params.get_bool("AdvancedLongitudinalTune") if tuning_level >= level["AdvancedLongitudinalTune"] else default.get_bool("AdvancedLongitudinalTune"))
toggle.longitudinalActuatorDelay = np.clip(params.get_float("LongitudinalActuatorDelay"), 0, 1) if advanced_longitudinal_tuning and tuning_level >= level["LongitudinalActuatorDelay"] else longitudinalActuatorDelay
toggle.max_desired_acceleration = np.clip(params.get_float("MaxDesiredAcceleration"), 0.1, 4.0) if advanced_longitudinal_tuning and tuning_level >= level["MaxDesiredAcceleration"] else default.get_float("MaxDesiredAcceleration")
toggle.startAccel = np.clip(params.get_float("StartAccel"), 0, 4) if advanced_longitudinal_tuning and tuning_level >= level["StartAccel"] else startAccel
@ -647,7 +691,10 @@ class FrogPilotVariables:
toggle.conditional_navigation_intersections = toggle.conditional_navigation and (params.get_bool("CENavigationIntersections") if tuning_level >= level["CENavigationIntersections"] else default.get_bool("CENavigationIntersections"))
toggle.conditional_navigation_lead = toggle.conditional_navigation and (params.get_bool("CENavigationLead") if tuning_level >= level["CENavigationLead"] else default.get_bool("CENavigationLead"))
toggle.conditional_navigation_turns = toggle.conditional_navigation and (params.get_bool("CENavigationTurns") if tuning_level >= level["CENavigationTurns"] else default.get_bool("CENavigationTurns"))
toggle.conditional_model_stop_time = params.get_int("CEModelStopTime") if toggle.conditional_experimental_mode and tuning_level >= level["CEModelStopTime"] else default.get_int("CEModelStopTime")
if tuning_level >= level["CEModelStopTime"]:
toggle.conditional_model_stop_time = params.get_int("CEModelStopTime") if toggle.conditional_experimental_mode else default.get_int("CEModelStopTime")
else:
toggle.conditional_model_stop_time = default.get_int("CEModelStopTime") if toggle.conditional_experimental_mode and params.get_bool("CEStopLights") else 0
toggle.conditional_signal = params.get_int("CESignalSpeed") * speed_conversion if toggle.conditional_experimental_mode and tuning_level >= level["CESignalSpeed"] else default.get_int("CESignalSpeed") * CV.MPH_TO_MS
toggle.conditional_signal_lane_detection = toggle.conditional_signal != 0 and (params.get_bool("CESignalLaneDetection") if tuning_level >= level["CESignalLaneDetection"] else default.get_bool("CESignalLaneDetection"))
toggle.cem_status = toggle.conditional_experimental_mode and (params.get_bool("ShowCEMStatus") if tuning_level >= level["ShowCEMStatus"] else default.get_bool("ShowCEMStatus")) or toggle.debug_mode
@ -785,6 +832,10 @@ class FrogPilotVariables:
toggle.holiday_themes = params.get_bool("HolidayThemes") if tuning_level >= level["HolidayThemes"] else default.get_bool("HolidayThemes")
toggle.current_holiday_theme = holiday_theme if toggle.holiday_themes else "stock"
toggle.honda_alt_Tune = toggle.openpilot_longitudinal and toggle.car_make == "honda" and honda_nidec and (params.get_bool("HondaAltTune") if tuning_level >= level["HondaAltTune"] else default.get_bool("HondaAltTune"))
toggle.honda_low_speed_pedal = toggle.openpilot_longitudinal and toggle.car_make == "honda" and toggle.has_pedal and (params.get_bool("HondaLowSpeedPedal") if tuning_level >= level["HondaLowSpeedPedal"] else default.get_bool("HondaLowSpeedPedal"))
toggle.honda_nidec_max_brake = toggle.openpilot_longitudinal and toggle.car_make == "honda" and honda_nidec and (params.get_bool("HondaMaxBrake") if tuning_level >= level["HondaMaxBrake"] else default.get_bool("HondaMaxBrake"))
toggle.lane_changes = params.get_bool("LaneChanges") if tuning_level >= level["LaneChanges"] else default.get_bool("LaneChanges")
toggle.lane_change_delay = params.get_float("LaneChangeTime") if toggle.lane_changes and tuning_level >= level["LaneChangeTime"] else default.get_float("LaneChangeTime")
toggle.lane_detection_width = params.get_float("LaneDetectionWidth") * distance_conversion if toggle.lane_changes and tuning_level >= level["LaneDetectionWidth"] else default.get_float("LaneDetectionWidth") * CV.FOOT_TO_METER
@ -817,6 +868,7 @@ class FrogPilotVariables:
toggle.deceleration_profile = params.get_int("DecelerationProfile") if longitudinal_tuning and tuning_level >= level["DecelerationProfile"] else default.get_int("DecelerationProfile")
toggle.human_acceleration = longitudinal_tuning and (params.get_bool("HumanAcceleration") if tuning_level >= level["HumanAcceleration"] else default.get_bool("HumanAcceleration"))
toggle.human_following = longitudinal_tuning and (params.get_bool("HumanFollowing") if tuning_level >= level["HumanFollowing"] else default.get_bool("HumanFollowing"))
toggle.human_lane_changes = longitudinal_tuning and has_radar and (params.get_bool("HumanLaneChanges") if tuning_level >= level["HumanLaneChanges"] else default.get_bool("HumanLaneChanges"))
toggle.lead_detection_probability = np.clip(params.get_int("LeadDetectionThreshold") / 100, 0.25, 0.50) if longitudinal_tuning and tuning_level >= level["LeadDetectionThreshold"] else default.get_int("LeadDetectionThreshold") / 100
toggle.taco_tune = longitudinal_tuning and (params.get_bool("TacoTune") if tuning_level >= level["TacoTune"] else default.get_bool("TacoTune"))
@ -882,7 +934,7 @@ class FrogPilotVariables:
toggle.pause_lateral_below_speed = params.get_int("PauseLateralSpeed") * speed_conversion if quality_of_life_lateral and tuning_level >= level["PauseLateralSpeed"] else default.get_int("PauseLateralSpeed") * CV.MPH_TO_MS
toggle.pause_lateral_below_signal = toggle.pause_lateral_below_speed != 0 and (params.get_bool("PauseLateralOnSignal") if tuning_level >= level["PauseLateralOnSignal"] else default.get_bool("PauseLateralOnSignal"))
quality_of_life_longitudinal = params.get_bool("QOLLongitudinal") if tuning_level >= level["QOLLongitudinal"] else default.get_bool("QOLLongitudinal")
quality_of_life_longitudinal = toggle.openpilot_longitudinal and (params.get_bool("QOLLongitudinal") if tuning_level >= level["QOLLongitudinal"] else default.get_bool("QOLLongitudinal"))
toggle.cruise_increase = params.get_int("CustomCruise") if quality_of_life_longitudinal and not pcm_cruise and tuning_level >= level["CustomCruise"] else default.get_int("CustomCruise")
toggle.cruise_increase_long = params.get_int("CustomCruiseLong") if quality_of_life_longitudinal and not pcm_cruise and tuning_level >= level["CustomCruiseLong"] else default.get_int("CustomCruiseLong")
toggle.force_stops = quality_of_life_longitudinal and (params.get_bool("ForceStops") if tuning_level >= level["ForceStops"] else default.get_bool("ForceStops"))
@ -892,6 +944,23 @@ class FrogPilotVariables:
toggle.map_deceleration = map_gears and (params.get_bool("MapDeceleration") if tuning_level >= level["MapDeceleration"] else default.get_bool("MapDeceleration"))
toggle.reverse_cruise_increase = quality_of_life_longitudinal and toggle.car_make == "toyota" and pcm_cruise and (params.get_bool("ReverseCruise") if tuning_level >= level["ReverseCruise"] else default.get_bool("ReverseCruise"))
toggle.set_speed_offset = params.get_int("SetSpeedOffset") * (1 if toggle.is_metric else CV.MPH_TO_KPH) if quality_of_life_longitudinal and not pcm_cruise and tuning_level >= level["SetSpeedOffset"] else default.get_int("SetSpeedOffset") * CV.MPH_TO_KPH
toggle.weather_presets = quality_of_life_longitudinal and (params.get_bool("WeatherPresets") if tuning_level >= level["WeatherPresets"] else default.get_bool("WeatherPresets"))
toggle.increase_following_distance_low_visibility = params.get_float("IncreaseFollowingLowVisibility") if toggle.weather_presets and tuning_level >= level["IncreaseFollowingLowVisibility"] else default.get_float("IncreaseFollowingLowVisibility")
toggle.increase_following_distance_rain = params.get_float("IncreaseFollowingRain") if toggle.weather_presets and tuning_level >= level["IncreaseFollowingRain"] else default.get_float("IncreaseFollowingRain")
toggle.increase_following_distance_rain_storm = params.get_float("IncreaseFollowingRainStorm") if toggle.weather_presets and tuning_level >= level["IncreaseFollowingRainStorm"] else default.get_float("IncreaseFollowingRainStorm")
toggle.increase_following_distance_snow = params.get_float("IncreaseFollowingSnow") if toggle.weather_presets and tuning_level >= level["IncreaseFollowingSnow"] else default.get_float("IncreaseFollowingSnow")
toggle.increase_stopped_distance_low_visibility = params.get_int("IncreasedStoppedDistanceLowVisibility") * distance_conversion if toggle.weather_presets and tuning_level >= level["IncreasedStoppedDistanceLowVisibility"] else default.get_int("IncreasedStoppedDistanceLowVisibility") * CV.FOOT_TO_METER
toggle.increase_stopped_distance_rain = params.get_int("IncreasedStoppedDistanceRain") * distance_conversion if toggle.weather_presets and tuning_level >= level["IncreasedStoppedDistanceRain"] else default.get_int("IncreasedStoppedDistanceRain") * CV.FOOT_TO_METER
toggle.increase_stopped_distance_rain_storm = params.get_int("IncreasedStoppedDistanceRainStorm") * distance_conversion if toggle.weather_presets and tuning_level >= level["IncreasedStoppedDistanceRainStorm"] else default.get_int("IncreasedStoppedDistanceRainStorm") * CV.FOOT_TO_METER
toggle.increase_stopped_distance_snow = params.get_int("IncreasedStoppedDistanceSnow") * distance_conversion if toggle.weather_presets and tuning_level >= level["IncreasedStoppedDistanceSnow"] else default.get_int("IncreasedStoppedDistanceSnow") * CV.FOOT_TO_METER
toggle.reduce_acceleration_low_visibility = (params.get_int("ReduceAccelerationLowVisibility") if toggle.weather_presets and tuning_level >= level["ReduceAccelerationLowVisibility"] else default.get_int("ReduceAccelerationLowVisibility")) / 100
toggle.reduce_acceleration_rain = (params.get_int("ReduceAccelerationRain") if toggle.weather_presets and tuning_level >= level["ReduceAccelerationRain"] else default.get_int("ReduceAccelerationRain")) / 100
toggle.reduce_acceleration_rain_storm = (params.get_int("ReduceAccelerationRainStorm") if toggle.weather_presets and tuning_level >= level["ReduceAccelerationRainStorm"] else default.get_int("ReduceAccelerationRainStorm")) / 100
toggle.reduce_acceleration_snow = (params.get_int("ReduceAccelerationSnow") if toggle.weather_presets and tuning_level >= level["ReduceAccelerationSnow"] else default.get_int("ReduceAccelerationSnow")) / 100
toggle.reduce_lateral_acceleration_low_visibility = (params.get_int("ReduceLateralAccelerationLowVisibility") if toggle.weather_presets and tuning_level >= level["ReduceLateralAccelerationLowVisibility"] else default.get_int("ReduceLateralAccelerationLowVisibility")) / 100
toggle.reduce_lateral_acceleration_rain = (params.get_int("ReduceLateralAccelerationRain") if toggle.weather_presets and tuning_level >= level["ReduceLateralAccelerationRain"] else default.get_int("ReduceLateralAccelerationRain")) / 100
toggle.reduce_lateral_acceleration_rain_storm = (params.get_int("ReduceLateralAccelerationRainStorm") if toggle.weather_presets and tuning_level >= level["ReduceLateralAccelerationRainStorm"] else default.get_int("ReduceLateralAccelerationRainStorm")) / 100
toggle.reduce_lateral_acceleration_snow = (params.get_int("ReduceLateralAccelerationSnow") if toggle.weather_presets and tuning_level >= level["ReduceLateralAccelerationSnow"] else default.get_int("ReduceLateralAccelerationSnow")) / 100
quality_of_life_visuals = params.get_bool("QOLVisuals") if tuning_level >= level["QOLVisuals"] else default.get_bool("QOLVisuals")
toggle.camera_view = params.get_int("CameraView") if quality_of_life_visuals and tuning_level >= level["CameraView"] else default.get_int("CameraView")
@ -949,6 +1018,8 @@ class FrogPilotVariables:
toggle.startup_alert_top = params.get("StartupMessageTop", encoding="utf-8") if tuning_level >= level["StartupMessageTop"] else default.get("StartupMessageTop", encoding="utf-8")
toggle.startup_alert_bottom = params.get("StartupMessageBottom", encoding="utf-8") if tuning_level >= level["StartupMessageBottom"] else default.get("StartupMessageBottom", encoding="utf-8")
toggle.subaru_sng = toggle.car_make == "subaru" and not (CP.flags & SubaruFlags.GLOBAL_GEN2 or CP.flags & SubaruFlags.HYBRID) and (params.get_bool("SubaruSNG") if tuning_level >= level["SubaruSNG"] else default.get_bool("SubaruSNG"))
toggle.taco_tune_hacks = taco_hacks_allowed and (params.get_bool("TacoTuneHacks") if tuning_level >= level["TacoTuneHacks"] else default.get_bool("TacoTuneHacks"))
toggle.tethering_config = params.get_int("TetheringEnabled")

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3
from cereal import car, custom
from openpilot.selfdrive.controls.lib.drive_helpers import CRUISE_LONG_PRESS
from openpilot.selfdrive.controls.lib.events import EventName
from openpilot.selfdrive.controls.lib.events import ET
from openpilot.frogpilot.common.frogpilot_variables import NON_DRIVING_GEARS, params, params_memory
@ -98,8 +98,8 @@ class FrogPilotCard:
self.always_on_lateral_enabled &= carState.gearShifter not in NON_DRIVING_GEARS
self.always_on_lateral_enabled &= sm["frogpilotPlan"].lateralCheck
self.always_on_lateral_enabled &= sm["liveCalibration"].calPerc >= 1
self.always_on_lateral_enabled &= sm["controlsState"].alertType != ET.IMMEDIATE_DISABLE or frogpilot_toggles.frogs_go_moo
self.always_on_lateral_enabled &= not (carState.brakePressed and carState.vEgo < self.car.frogpilot_toggles.always_on_lateral_pause_speed) or carState.standstill
self.always_on_lateral_enabled &= not any(event.immediateDisable for events in (sm["onroadEvents"], sm["frogpilotOnroadEvents"]) for event in events if event.name != EventName.speedTooLow) or self.car.frogpilot_toggles.frogs_go_moo
if sm.updated["frogpilotPlan"] or any(be.type in (ButtonType.accelCruise, ButtonType.resumeCruise) for be in carState.buttonEvents):
self.accel_pressed = any(be.type in (ButtonType.accelCruise, ButtonType.resumeCruise) for be in carState.buttonEvents)

View File

@ -18,6 +18,7 @@ from openpilot.frogpilot.controls.lib.frogpilot_acceleration import FrogPilotAcc
from openpilot.frogpilot.controls.lib.frogpilot_events import FrogPilotEvents
from openpilot.frogpilot.controls.lib.frogpilot_following import FrogPilotFollowing
from openpilot.frogpilot.controls.lib.frogpilot_vcruise import FrogPilotVCruise
from openpilot.frogpilot.controls.lib.weather_checker import WeatherChecker
class FrogPilotPlanner:
def __init__(self, error_log, ThemeManager):
@ -26,6 +27,7 @@ class FrogPilotPlanner:
self.frogpilot_events = FrogPilotEvents(self, error_log, ThemeManager)
self.frogpilot_following = FrogPilotFollowing(self)
self.frogpilot_vcruise = FrogPilotVCruise(self)
self.frogpilot_weather = WeatherChecker()
with car.CarParams.from_bytes(params.get("CarParams", block=True)) as msg:
self.CP = msg
@ -113,6 +115,11 @@ class FrogPilotPlanner:
self.v_cruise = self.frogpilot_vcruise.update(gps_position, now, time_validated, v_cruise, v_ego, sm, frogpilot_toggles)
if gps_position and time_validated and frogpilot_toggles.weather_presets:
self.frogpilot_weather.update_weather(gps_position, now, frogpilot_toggles)
else:
self.frogpilot_weather.weather_id = 0
def update_lead_status(self):
following_lead = self.lead_one.status
following_lead &= self.lead_one.dRel < self.model_length + STOP_DISTANCE
@ -146,6 +153,8 @@ class FrogPilotPlanner:
frogpilotPlan.frogpilotEvents = self.frogpilot_events.events.to_msg()
frogpilotPlan.increasedStoppedDistance = frogpilot_toggles.increase_stopped_distance if not sm["frogpilotCarState"].trafficModeEnabled else 0
if self.frogpilot_weather.weather_id != 0:
frogpilotPlan.increasedStoppedDistance += self.frogpilot_weather.increase_stopped_distance
frogpilotPlan.laneWidthLeft = self.lane_width_left
frogpilotPlan.laneWidthRight = self.lane_width_right
@ -177,4 +186,7 @@ class FrogPilotPlanner:
frogpilotPlan.vCruise = self.v_cruise
frogpilotPlan.weatherDaytime = self.frogpilot_weather.is_daytime
frogpilotPlan.weatherId = self.frogpilot_weather.weather_id
pm.send("frogpilotPlan", frogpilot_plan_send)

View File

@ -90,6 +90,8 @@ class CurveSpeedController:
def update_target(self, v_ego):
lateral_acceleration = self.lateral_acceleration
if self.frogpilot_planner.frogpilot_weather.weather_id != 0:
lateral_acceleration -= self.lateral_acceleration * self.frogpilot_planner.frogpilot_weather.reduce_lateral_acceleration
if self.target_set:
csc_speed = (lateral_acceleration / abs(self.frogpilot_planner.road_curvature))**0.5

View File

@ -64,6 +64,9 @@ class FrogPilotAcceleration:
self.max_accel = min(get_max_accel_low_speeds(self.max_accel, self.frogpilot_planner.v_cruise), self.max_accel)
self.max_accel = min(get_max_accel_ramp_off(self.max_accel, self.frogpilot_planner.v_cruise, v_ego), self.max_accel)
if self.frogpilot_planner.frogpilot_weather.weather_id != 0:
self.max_accel -= self.max_accel * self.frogpilot_planner.frogpilot_weather.reduce_acceleration
if self.frogpilot_planner.tracking_lead:
self.min_accel = ACCEL_MIN
elif sm["frogpilotCarState"].forceCoast:

View File

@ -3,7 +3,7 @@ import numpy as np
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import COMFORT_BRAKE, STOP_DISTANCE, desired_follow_distance, get_jerk_factor, get_T_FOLLOW
from openpilot.frogpilot.common.frogpilot_variables import CITY_SPEED_LIMIT
from openpilot.frogpilot.common.frogpilot_variables import CITY_SPEED_LIMIT, MAX_T_FOLLOW
TRAFFIC_MODE_BP = [0., CITY_SPEED_LIMIT]
@ -63,9 +63,13 @@ class FrogPilotFollowing:
self.danger_jerk = self.base_danger_jerk
self.speed_jerk = self.base_speed_jerk
self.following_lead = self.frogpilot_planner.tracking_lead and self.frogpilot_planner.lead_one.dRel < (self.t_follow + 1) * v_ego
self.following_lead = self.frogpilot_planner.tracking_lead and self.frogpilot_planner.lead_one.dRel < (self.t_follow * 2) * v_ego
if self.frogpilot_planner.frogpilot_weather.weather_id != 0:
self.t_follow = min(self.t_follow + self.frogpilot_planner.frogpilot_weather.increase_following_distance, MAX_T_FOLLOW)
if sm["controlsState"].enabled and self.frogpilot_planner.tracking_lead:
if not sm["frogpilotCarState"].trafficModeEnabled:
self.update_follow_values(self.frogpilot_planner.lead_one.dRel, v_ego, self.frogpilot_planner.lead_one.vLead, frogpilot_toggles)
self.desired_follow_distance = int(desired_follow_distance(v_ego, self.frogpilot_planner.lead_one.vLead, self.t_follow))
else:

View File

@ -1,14 +1,16 @@
#!/usr/bin/env python3
import json
from cereal import log
from openpilot.common.conversions import Conversions as CV
from openpilot.common.realtime import DT_MDL
from openpilot.selfdrive.controls.controlsd import EventName, FrogPilotEventName, State
from openpilot.selfdrive.controls.controlsd import ACTIVE_STATES, EventName, FrogPilotEventName, State
from openpilot.selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX
from openpilot.selfdrive.ui.soundd import FrogPilotAudibleAlert
from openpilot.frogpilot.common.frogpilot_utilities import clean_model_name
from openpilot.frogpilot.common.frogpilot_variables import params
from openpilot.frogpilot.controls.lib.weather_checker import WEATHER_CATEGORIES
RANDOM_EVENTS = {
FrogPilotEventName.accel30: "accel30",
@ -28,6 +30,7 @@ RANDOM_EVENTS = {
class FrogPilotTracking:
def __init__(self, frogpilot_planner, frogpilot_toggles):
self.frogpilot_events = frogpilot_planner.frogpilot_events
self.frogpilot_weather = frogpilot_planner.frogpilot_weather
self.frogpilot_stats = json.loads(params.get("FrogPilotStats") or "{}")
self.frogpilot_stats.setdefault("AOLTime", self.frogpilot_stats.get("TotalAOLTime", 0))
@ -37,6 +40,14 @@ class FrogPilotTracking:
self.frogpilot_stats = {key: value for key, value in self.frogpilot_stats.items() if not key.startswith("Total")}
if "ResetStats" not in self.frogpilot_stats:
self.frogpilot_stats["Disengages"] = 0
self.frogpilot_stats["Engages"] = 0
self.frogpilot_stats["FrogChirps"] = 0
self.frogpilot_stats["FrogSqueaks"] = 0
self.frogpilot_stats["Overrides"] = 0
self.frogpilot_stats["ResetStats"] = True
params.put("FrogPilotStats", json.dumps(self.frogpilot_stats))
self.drive_added = False
@ -48,8 +59,10 @@ class FrogPilotTracking:
self.previous_events = set()
self.previous_random_events = set()
self.personality_map = {v: k.capitalize() for k, v in log.LongitudinalPersonality.schema.enumerants.items()}
self.previous_state = State.disabled
self.sound = FrogPilotAudibleAlert.none
self.state = State.disabled
self.model_name = clean_model_name(dict(zip(frogpilot_toggles.available_models.split(","), frogpilot_toggles.available_model_names.split(",")))[frogpilot_toggles.model])
@ -83,6 +96,11 @@ class FrogPilotTracking:
self.frogpilot_stats["LateralTime"] = self.frogpilot_stats.get("LateralTime", 0) + DT_MDL
if sm["carControl"].longActive:
self.frogpilot_stats["LongitudinalTime"] = self.frogpilot_stats.get("LongitudinalTime", 0) + DT_MDL
personality_name = self.personality_map.get(sm["controlsState"].personality, "Unknown")
total_personality_times = self.frogpilot_stats.get("PersonalityTimes", {})
total_personality_times[personality_name] = total_personality_times.get(personality_name, 0) + DT_MDL
self.frogpilot_stats["PersonalityTimes"] = total_personality_times
elif sm["frogpilotCarState"].alwaysOnLateralEnabled:
self.frogpilot_stats["AOLTime"] = self.frogpilot_stats.get("AOLTime", 0) + DT_MDL
@ -101,21 +119,21 @@ class FrogPilotTracking:
self.distance_since_override += v_ego * DT_MDL
self.frogpilot_stats["LongestDistanceWithoutOverride"] = max(self.distance_since_override, self.frogpilot_stats.get("LongestDistanceWithoutOverride", 0))
if sm["controlsState"].state != self.state:
if sm["controlsState"].state == State.disabled:
self.frogpilot_stats["Disengages"] = self.frogpilot_stats.get("Disengages", 0) + 1
if frogpilot_toggles.sound_pack == "frog":
self.frogpilot_stats["FrogSqueaks"] = self.frogpilot_stats.get("FrogSqueaks", 0) + 1
elif sm["controlsState"].state == State.enabled:
if sm["controlsState"].state != self.previous_state:
if sm["controlsState"].state in ACTIVE_STATES and self.previous_state not in ACTIVE_STATES:
self.frogpilot_stats["Engages"] = self.frogpilot_stats.get("Engages", 0) + 1
if frogpilot_toggles.sound_pack == "frog":
self.frogpilot_stats["FrogChirps"] = self.frogpilot_stats.get("FrogChirps", 0) + 1
elif sm["controlsState"].state == State.overriding:
elif sm["controlsState"].state == State.disabled and self.previous_state in ACTIVE_STATES:
self.frogpilot_stats["Disengages"] = self.frogpilot_stats.get("Disengages", 0) + 1
if frogpilot_toggles.sound_pack == "frog":
self.frogpilot_stats["FrogSqueaks"] = self.frogpilot_stats.get("FrogSqueaks", 0) + 1
if sm["controlsState"].state == State.overriding and self.previous_state != State.overriding:
self.frogpilot_stats["Overrides"] = self.frogpilot_stats.get("Overrides", 0) + 1
self.state = sm["controlsState"].state
self.previous_state = sm["controlsState"].state
current_events = {event for event in self.frogpilot_events.event_names}
if len(current_events) > 0:
@ -140,6 +158,30 @@ class FrogPilotTracking:
self.previous_random_events = current_random_events
if self.frogpilot_weather.sunrise != 0 and self.frogpilot_weather.sunset != 0:
if self.frogpilot_weather.is_daytime:
self.frogpilot_stats["DayTime"] = self.frogpilot_stats.get("DayTime", 0) + DT_MDL
else:
self.frogpilot_stats["NightTime"] = self.frogpilot_stats.get("NightTime", 0) + DT_MDL
weather_api_calls = self.frogpilot_stats.get("WeatherAPICalls", {})
weather_api_calls["2.5"] = weather_api_calls.get("2.5", 0) + self.frogpilot_weather.api_25_calls
weather_api_calls["3.0"] = weather_api_calls.get("3.0", 0) + self.frogpilot_weather.api_3_calls
self.frogpilot_stats["WeatherAPICalls"] = weather_api_calls
self.frogpilot_weather.api_25_calls = 0
self.frogpilot_weather.api_3_calls = 0
suffix = "unknown"
for category in WEATHER_CATEGORIES.values():
if any(start <= self.frogpilot_weather.weather_id <= end for start, end in category["ranges"]):
suffix = category["suffix"]
break
weather_times = self.frogpilot_stats.get("WeatherTimes", {})
weather_times[suffix] = weather_times.get(suffix, 0) + DT_MDL
self.frogpilot_stats["WeatherTimes"] = weather_times
if self.tracked_time > 60 and sm["carState"].standstill and self.enabled:
if time_validated:
current_month = now.month

View File

@ -58,16 +58,6 @@ class FrogPilotVCruise:
self.csc_target = v_cruise
# Mike's extended lead linear braking
if self.frogpilot_planner.lead_one.vLead < v_ego > CRUISING_SPEED and sm["controlsState"].enabled and self.frogpilot_planner.tracking_lead and frogpilot_toggles.human_following:
if not self.frogpilot_planner.frogpilot_following.following_lead:
decel_rate = (v_ego - self.frogpilot_planner.lead_one.vLead)**2 / self.frogpilot_planner.lead_one.dRel
self.braking_target = max(v_ego - (decel_rate * DT_MDL), self.frogpilot_planner.lead_one.vLead + CRUISING_SPEED)
else:
self.braking_target = v_cruise
else:
self.braking_target = v_cruise
# Pfeiferj's Speed Limit Controller
self.slc.frogpilot_toggles = frogpilot_toggles
@ -97,10 +87,10 @@ class FrogPilotVCruise:
self.tracked_model_length = self.frogpilot_planner.model_length
targets = [self.braking_target, self.csc_target, v_cruise]
targets = [self.csc_target, v_cruise]
if frogpilot_toggles.speed_limit_controller:
targets.append(max(self.slc.overridden_speed, self.slc_target + self.slc_offset) - v_ego_diff)
v_cruise = min([target if target > CRUISING_SPEED else v_cruise for target in targets])
v_cruise = min([target if target >= CRUISING_SPEED else v_cruise for target in targets])
return v_cruise

View File

@ -18,7 +18,7 @@ from openpilot.selfdrive.controls.lib.pid import PIDController
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
from openpilot.selfdrive.modeld.constants import ModelConstants
from openpilot.frogpilot.common.frogpilot_variables import NNFF_MODELS_PATH, get_nnff_model_files
from openpilot.frogpilot.common.frogpilot_variables import NNFF_MODELS_PATH, get_nnff_model_files, get_nnff_substitutes
# At higher speeds (25+mph) we can assume:
# Lateral acceleration achieved by a specific car correlates to
@ -32,7 +32,7 @@ from openpilot.frogpilot.common.frogpilot_variables import NNFF_MODELS_PATH, get
# move it at all, this is compensated for too.
# dict used to rename activation functions whose names aren't valid python identifiers
ACTIVATION_FUNCTION_NAMES = {'σ': 'sigmoid'}
ACTIVATION_FUNCTION_NAMES = {"σ": "sigmoid"}
LOW_SPEED_X = [0, 10, 20, 30]
LOW_SPEED_Y = [12, 3, 1, 0]
@ -52,8 +52,8 @@ class FluxModel:
self.layers = []
for layer_params in params["layers"]:
bias_array = np.array(layer_params[next(key for key in layer_params.keys() if key.endswith('_b'))], dtype=np.float32).T
weight_array = np.array(layer_params[next(key for key in layer_params.keys() if key.endswith('_W'))], dtype=np.float32).T
bias_array = np.array(layer_params[next(key for key in layer_params.keys() if key.endswith("_b"))], dtype=np.float32).T
weight_array = np.array(layer_params[next(key for key in layer_params.keys() if key.endswith("_W"))], dtype=np.float32).T
activation = layer_params["activation"]
for name, replacement in ACTIVATION_FUNCTION_NAMES.items():
@ -126,15 +126,27 @@ def get_nn_model_path(car, eps_firmware) -> str | None:
best = max(candidates, key=lambda model: similarity(model, query))
return os.path.join(NNFF_MODELS_PATH, f"{best}.json"), similarity(best, query)
def find_valid_model(*queries):
for query in queries:
def find_valid_model(*queries_with_candidates):
for query, candidate in queries_with_candidates:
path, score = best_model_path(query)
if path and car in path and score >= 0.9:
if path and candidate in path and score >= 0.9:
return path
return None
query1 = f"{car} {eps_firmware}" if len(eps_firmware) > 3 else car
return find_valid_model(query1, car)
substitutes = get_nnff_substitutes()
sub_candidate = substitutes.get(car, car)
candidates_to_check = [car]
if car != sub_candidate:
candidates_to_check.append(sub_candidate)
queries = []
for candidate in candidates_to_check:
query_with_fw = f"{candidate} {eps_firmware}" if len(eps_firmware) > 3 else candidate
queries.append((query_with_fw, candidate))
queries.append((candidate, candidate))
return find_valid_model(*queries)
def get_predicted_lateral_jerk(lat_accels, t_diffs):
# compute finite difference between subsequent model_data.acceleration.y values
@ -162,8 +174,8 @@ class LatControlNNFF(LatControl):
self.nnff_loaded = self.lat_torque_nn_model is not None
self.torque_params = CP.lateralTuning.torque
self.pid = PIDController(self.torque_params.kp, self.torque_params.ki,
k_f=self.torque_params.kf, pos_limit=self.steer_max, neg_limit=-self.steer_max)
self.pid = PIDController(1.0, 0.3, k_d=0.0,
pos_limit=self.steer_max, neg_limit=-self.steer_max)
self.torque_from_lateral_accel = CI.torque_from_lateral_accel()
self.use_steering_angle = self.torque_params.useSteeringAngle
self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg
@ -336,7 +348,6 @@ class LatControlNNFF(LatControl):
ff = self.torque_from_lateral_accel(gravity_adjusted_lateral_accel, self.torque_params)
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5
self.pid._k_p = frogpilot_toggles.steerKp
output_torque = self.pid.update(pid_log.error,
feedforward=ff,
speed=CS.vEgo,

View File

@ -0,0 +1,171 @@
#!/usr/bin/env python3
import os
import requests
import time
from concurrent.futures import ThreadPoolExecutor
from openpilot.common.conversions import Conversions as CV
from openpilot.common.params import Params
from openpilot.frogpilot.common.frogpilot_utilities import calculate_distance_to_point, is_url_pingable
CACHE_DISTANCE = 25
MAX_RETRIES = 3
RETRY_DELAY = 60
# Reference: https://openweathermap.org/weather-conditions
WEATHER_CATEGORIES = {
"RAIN": {
"ranges": [(300, 321), (500, 504)],
"suffix": "rain",
},
"RAIN_STORM": {
"ranges": [(200, 232), (511, 511), (520, 531)],
"suffix": "rain_storm",
},
"SNOW": {
"ranges": [(600, 622)],
"suffix": "snow",
},
"LOW_VISIBILITY": {
"ranges": [(701, 762)],
"suffix": "low_visibility",
},
"CLEAR": {
"ranges": [(800, 800)],
"suffix": "clear",
},
}
class WeatherChecker:
def __init__(self):
self.is_daytime = False
self.api_25_calls = 0
self.api_3_calls = 0
self.increase_following_distance = 0
self.increase_stopped_distance = 0
self.reduce_acceleration = 0
self.reduce_lateral_acceleration = 0
self.sunrise = 0
self.sunset = 0
self.weather_id = 0
self.hourly_forecast = None
self.last_gps_position = None
self.last_updated = None
user_api_key = Params().get("WeatherToken", encoding="utf-8")
self.api_key = user_api_key or os.environ.get("WEATHER_TOKEN", "")
if user_api_key:
self.check_interval = 60
else:
self.check_interval = 15 * 60
self.session = requests.Session()
self.session.headers.update({"Accept-Language": "en"})
self.session.headers.update({"User-Agent": "frogpilot-weather-checker/1.0 (https://github.com/FrogAi/FrogPilot)"})
self.executor = ThreadPoolExecutor(max_workers=1)
def update_offsets(self, frogpilot_toggles):
suffix = WEATHER_CATEGORIES["CLEAR"]["suffix"]
for category in WEATHER_CATEGORIES.values():
if any(start <= self.weather_id <= end for start, end in category["ranges"]):
suffix = category["suffix"]
break
if suffix != WEATHER_CATEGORIES["CLEAR"]["suffix"]:
self.increase_following_distance = getattr(frogpilot_toggles, f"increase_following_distance_{suffix}")
self.increase_stopped_distance = getattr(frogpilot_toggles, f"increase_stopped_distance_{suffix}")
self.reduce_acceleration = getattr(frogpilot_toggles, f"reduce_acceleration_{suffix}")
self.reduce_lateral_acceleration = getattr(frogpilot_toggles, f"reduce_lateral_acceleration_{suffix}")
else:
self.increase_following_distance = 0
self.increase_stopped_distance = 0
self.reduce_acceleration = 0
self.reduce_lateral_acceleration = 0
def update_weather(self, gps_position, now, frogpilot_toggles):
if not self.api_key:
self.weather_id = 0
return
if self.last_gps_position and self.last_updated:
distance = calculate_distance_to_point(
self.last_gps_position["latitude"] * CV.DEG_TO_RAD,
self.last_gps_position["longitude"] * CV.DEG_TO_RAD,
gps_position.get("latitude") * CV.DEG_TO_RAD,
gps_position.get("longitude") * CV.DEG_TO_RAD
)
if distance / 1000 > CACHE_DISTANCE:
self.hourly_forecast = None
self.last_updated = None
if self.sunrise and self.sunset:
self.is_daytime = self.sunrise <= int(now.timestamp()) < self.sunset
if self.last_updated and (now - self.last_updated).total_seconds() < self.check_interval:
if self.hourly_forecast:
current_forecast = min(self.hourly_forecast, key=lambda f: abs(f["dt"] - now.timestamp()))
self.weather_id = current_forecast.get("weather", [{}])[0].get("id", 0)
self.update_offsets(frogpilot_toggles)
return
self.last_updated = now
def complete_request(future):
data = future.result()
if data:
self.hourly_forecast = data.get("hourly")
self.last_gps_position = gps_position
if "current" in data:
source_data = data.get("current", {})
current_data = source_data
else:
source_data = data
current_data = source_data.get("sys", source_data)
self.sunrise = current_data.get("sunrise", 0)
self.sunset = current_data.get("sunset", 0)
self.weather_id = source_data.get("weather", [{}])[0].get("id", 0)
self.update_offsets(frogpilot_toggles)
def make_request():
if not is_url_pingable("https://api.openweathermap.org"):
return None
params = {
"lat": gps_position["latitude"],
"lon": gps_position["longitude"],
"appid": self.api_key,
"units": "metric",
"exclude": "alerts,minutely,daily",
}
for attempt in range(1, MAX_RETRIES + 1):
try:
self.api_3_calls += 1
response = self.session.get("https://api.openweathermap.org/data/3.0/onecall", params=params, timeout=10)
if response.status_code == 429:
fallback_params = params.copy()
fallback_params.pop("exclude", None)
self.api_25_calls += 1
fallback_response = self.session.get("https://api.openweathermap.org/data/2.5/weather", params=fallback_params, timeout=10)
fallback_response.raise_for_status()
return fallback_response.json()
response.raise_for_status()
return response.json()
except Exception:
if attempt < MAX_RETRIES:
time.sleep(RETRY_DELAY)
continue
return None
future = self.executor.submit(make_request)
future.add_done_callback(complete_request)

View File

@ -2,20 +2,18 @@ import json
import os
import random
import requests
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "third_party"))
from collections import Counter
from datetime import datetime, timezone
from influxdb_client import InfluxDBClient, Point
from influxdb_client.client.write_api import SYNCHRONOUS
from cereal import car
from openpilot.common.conversions import Conversions as CV
from openpilot.system.hardware import HARDWARE
from openpilot.system.version import get_build_metadata
from openpilot.frogpilot.common.frogpilot_utilities import clean_model_name, run_cmd
from openpilot.frogpilot.common.frogpilot_utilities import clean_model_name
from openpilot.frogpilot.common.frogpilot_variables import get_frogpilot_toggles, params
BASE_URL = "https://nominatim.openstreetmap.org"
@ -93,7 +91,7 @@ def get_city_center(latitude, longitude):
print(f"Falling back to (0, 0) for {latitude}, {longitude}")
return float(0.0), float(0.0), "N/A", "N/A", "N/A"
except Exception as exception:
except Exception:
print(f"Falling back to (0, 0) for {latitude}, {longitude}")
return float(0.0), float(0.0), "N/A", "N/A", "N/A"
@ -107,18 +105,13 @@ def update_branch_commits(now):
points.append(Point("branch_commits").field("commit", sha).tag("branch", branch).time(now))
except Exception as e:
print(f"Failed to fetch commit for {branch}: {e}")
return points
def send_stats():
try:
build_metadata = get_build_metadata()
frogpilot_toggles = get_frogpilot_toggles()
if frogpilot_toggles.frogs_go_moo:
return
if frogpilot_toggles.car_make == "mock":
return
@ -127,71 +120,72 @@ def send_stats():
token = os.environ.get("STATS_TOKEN", "")
url = os.environ.get("STATS_URL", "")
car_params = "{}"
msg_bytes = params.get("CarParamsPersistent")
if msg_bytes:
with car.CarParams.from_bytes(msg_bytes) as CP:
cp_dict = CP.to_dict()
cp_dict.pop("carFw", None)
car_params = json.dumps(cp_dict)
dongle_id = params.get("FrogPilotDongleId", encoding="utf-8")
frogpilot_stats = json.loads(params.get("FrogPilotStats") or "{}")
location = json.loads(params.get("LastGPSPosition") or "{}")
if not (location.get("latitude") and location.get("longitude")):
return
original_latitude = location.get("latitude")
original_longitude = location.get("longitude")
original_latitude = location.get("latitude", 0.0)
original_longitude = location.get("longitude", 0.0)
latitude, longitude, city, state, country = get_city_center(original_latitude, original_longitude)
theme_sources = [
frogpilot_toggles.icon_pack.replace("-animated", ""),
frogpilot_toggles.color_scheme,
frogpilot_toggles.distance_icons.replace("-animated", ""),
frogpilot_toggles.signal_icons.replace("-animated", ""),
frogpilot_toggles.sound_pack
]
theme_counter = Counter(theme_sources)
most_common = theme_counter.most_common()
max_count = most_common[0][1]
selected_theme = random.choice([item for item, count in most_common if count == max_count]).replace("-user_created", "").replace("_", " ")
now = datetime.now(timezone.utc)
theme_attributes = sorted(["color_scheme", "distance_icons", "icon_pack", "signal_icons", "sound_pack"])
theme_counts = Counter(getattr(frogpilot_toggles, attribute).replace("-animated", "") for attribute in theme_attributes)
winners = [theme for theme, count in theme_counts.items() if count == max(theme_counts.values(), default=0)]
if len(winners) > 1 and "stock" in winners:
winners.remove("stock")
selected_theme = random.choice(winners).replace("-user_created", "").replace("_", " ") if winners else "stock"
user_point = (
Point("user_stats")
.field("blocked_user", frogpilot_toggles.block_user)
.field("car_make", "GM" if frogpilot_toggles.car_make == "gm" else frogpilot_toggles.car_make.title())
.field("car_model", frogpilot_toggles.car_model)
.field("calibrated_lateral_acceleration", params.get_float("CalibratedLateralAcceleration"))
.field("calibration_progress", params.get_float("CalibrationProgress"))
.field("car_params", car_params)
.field("city", city)
.field("commit", build_metadata.openpilot.git_commit)
.field("country", country)
.field("current_months_kilometers", int(frogpilot_stats.get("CurrentMonthsKilometers", 0)))
.field("device", HARDWARE.get_device_type())
.field("driving_model", clean_model_name(frogpilot_toggles.model_name))
.field("event", 1)
.field("frogpilot_drives", int(frogpilot_stats.get("FrogPilotDrives", 0)))
.field("frogpilot_hours", float(frogpilot_stats.get("FrogPilotSeconds", 0)) / (60 * 60))
.field("frogpilot_miles", float(frogpilot_stats.get("FrogPilotMeters", 0)) * CV.METER_TO_MILE)
.field("goat_scream", frogpilot_toggles.goat_scream_alert)
.field("has_cc_long", frogpilot_toggles.has_cc_long)
.field("has_openpilot_longitudinal", frogpilot_toggles.openpilot_longitudinal)
.field("has_pedal", frogpilot_toggles.has_pedal)
.field("has_sdsu", frogpilot_toggles.has_sdsu)
.field("has_zss", frogpilot_toggles.has_zss)
.field("latitude", latitude)
.field("longitude", longitude)
.field("rainbow_path", frogpilot_toggles.rainbow_path)
.field("random_events", frogpilot_toggles.random_events)
.field("state", state)
.field("stats", json.dumps(frogpilot_stats))
.field("theme", selected_theme.title())
.field("total_aol_seconds", float(frogpilot_stats.get("AOLTime", 0)))
.field("total_lateral_seconds", float(frogpilot_stats.get("LateralTime", 0)))
.field("total_longitudinal_seconds", float(frogpilot_stats.get("LongitudinalTime", 0)))
.field("total_stopped_seconds", float(frogpilot_stats.get("StandstillTime", 0)))
.field("total_tracked_seconds", float(frogpilot_stats.get("TrackedTime", 0)))
.field("toggles", json.dumps(frogpilot_toggles.__dict__))
.field("tuning_level", params.get_int("TuningLevel") + 1 if params.get_bool("TuningLevelConfirmed") else 0)
.field("using_default_model", params.get("Model", encoding="utf-8").endswith("_default"))
.field("using_stock_acc", not (frogpilot_toggles.has_cc_long or frogpilot_toggles.openpilot_longitudinal))
.tag("branch", build_metadata.channel)
.tag("dongle_id", params.get("FrogPilotDongleId", encoding="utf-8"))
.tag("dongle_id", dongle_id)
.time(now)
)
all_points = [user_point] + update_branch_commits(now)
model_scores = json.loads(params.get("ModelDrivesAndScores") or "{}")
model_points = []
for model_name, data in sorted(model_scores.items()):
drives = data.get("Drives", 0)
score = data.get("Score", 0)
if drives > 0:
point = (
Point("model_scores")
.field("drives", int(drives))
.field("score", int(score))
.tag("dongle_id", dongle_id)
.tag("model_name", clean_model_name(model_name))
.time(now)
)
model_points.append(point)
all_points = model_points + [user_point] + update_branch_commits(now)
client = InfluxDBClient(org=org_ID, token=token, url=url)
client.write_api(write_options=SYNCHRONOUS).write(bucket=bucket, org=org_ID, record=all_points)

View File

@ -9,6 +9,7 @@ from datetime import datetime, timedelta, timezone
from cereal import log, messaging
from openpilot.common.conversions import Conversions as CV
from openpilot.frogpilot.common.frogpilot_utilities import calculate_distance_to_point, calculate_lane_width, is_url_pingable
from openpilot.frogpilot.common.frogpilot_variables import params, params_memory
@ -79,7 +80,7 @@ class MapSpeedLogger:
@staticmethod
def meters_to_deg_lon(meters, latitude):
return meters / (METERS_PER_DEG_LAT * math.cos(math.radians(latitude)))
return meters / (METERS_PER_DEG_LAT * math.cos(latitude * CV.DEG_TO_RAD))
def get_speed_limit_source(self):
sources = [
@ -165,7 +166,7 @@ class MapSpeedLogger:
return []
def filter_segments_for_entry(self, entry):
bearing_rad = math.radians(entry["bearing"])
bearing_rad = entry["bearing"] * CV.DEG_TO_RAD
start_lat, start_lon = entry["start_coordinates"]["latitude"], entry["start_coordinates"]["longitude"]
end_lat, end_lon = entry["end_coordinates"]["latitude"], entry["end_coordinates"]["longitude"]
mid_lat = (start_lat + end_lat) / 2
@ -229,10 +230,10 @@ class MapSpeedLogger:
return
distance = calculate_distance_to_point(
math.radians(self.previous_coordinates["latitude"]),
math.radians(self.previous_coordinates["longitude"]),
math.radians(current_latitude),
math.radians(current_longitude)
self.previous_coordinates["latitude"] * CV.DEG_TO_RAD,
self.previous_coordinates["longitude"] * CV.DEG_TO_RAD,
current_latitude * CV.DEG_TO_RAD,
current_longitude * CV.DEG_TO_RAD
)
if distance < 1:
return
@ -318,7 +319,6 @@ class MapSpeedLogger:
self.update_params(dataset, filtered_dataset)
params_memory.put("UpdateSpeedLimitsStatus", "Completed!")
params_memory.remove("UpdateSpeedLimits")
def update_cached_segments(self, latitude, longitude, vetting=False):
if not self.is_in_cached_box(latitude, longitude):
@ -398,6 +398,8 @@ def main():
previously_started = False
elif params_memory.get_bool("UpdateSpeedLimits"):
logger.process_speed_limits()
params_memory.remove("UpdateSpeedLimits")
else:
time.sleep(5)

View File

@ -236,7 +236,7 @@ export function NavDestination() {
state.suggestions = "[]";
} catch (err) {
console.error("Failed to calculate route:", err);
showSnackbar("Failed to calculate route");
showSnackbar("Failed to calculate route...");
} finally {
state.loadingRoute = false;
}
@ -433,7 +433,7 @@ export function NavDestination() {
showSnackbar(`"${fav.name}" renamed to "${newName}"!`, "success");
} catch {
showSnackbar("Failed to edit favorite name");
showSnackbar("Failed to edit favorite name...");
} finally {
state.showRenameFavoriteModal = false;
}
@ -771,7 +771,7 @@ function NavigationDestination({
showSnackbar(message || "Added to favorites!");
await loadFavorites();
} catch {
showSnackbar("Failed to add to favorites");
showSnackbar("Failed to add to favorites...");
}
}
async function toggleFavorite() {
@ -782,7 +782,7 @@ function NavigationDestination({
if (fav) {
removeFavorite(fav);
} else {
showSnackbar("Couldn't find favorite entry");
showSnackbar("Couldn't find favorite entry...");
}
} else {
await favoriteDestination();

View File

@ -74,7 +74,6 @@ public:
WifiManager *wifi;
signals:
void reviewModel();
void themeUpdated();
};

View File

@ -20,6 +20,10 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
ScrollView *dataMainPanel = new ScrollView(dataMainList, this);
dataLayout->addWidget(dataMainPanel);
FrogPilotListWidget *statsLabelsList = new FrogPilotListWidget(this);
ScrollView *statsLabelsPanel = new ScrollView(statsLabelsList, this);
dataLayout->addWidget(statsLabelsPanel);
ButtonControl *deleteDrivingDataButton = new ButtonControl(tr("Delete Driving Data"), tr("DELETE"), tr("<b>Delete all stored driving footage and data</b> to free up space and clear private information."));
QObject::connect(deleteDrivingDataButton, &ButtonControl::clicked, [=]() {
QDir hdDataDir("/data/media/0/realdata_HD/");
@ -594,4 +598,245 @@ FrogPilotDataPanel::FrogPilotDataPanel(FrogPilotSettingsWindow *parent) : FrogPi
toggleBackupButton->showDescription();
}
dataMainList->addItem(toggleBackupButton);
FrogPilotButtonsControl *viewStatsButton = new FrogPilotButtonsControl(tr("FrogPilot Stats"), tr("<b>View your collected FrogPilot stats.</b>"), "", {tr("RESET"), tr("VIEW")});
QObject::connect(viewStatsButton, &FrogPilotButtonsControl::buttonClicked, [dataLayout, statsLabelsPanel, this](int id) {
if (id == 0) {
if (ConfirmationDialog::confirm(tr("Are you sure you want to reset all of your FrogPilot stats?"), tr("Reset"), this)) {
params.remove("FrogPilotStats");
params_cache.remove("FrogPilotStats");
}
} else if (id == 1) {
emit openSubPanel();
dataLayout->setCurrentWidget(statsLabelsPanel);
}
});
if (forceOpenDescriptions) {
viewStatsButton->showDescription();
}
dataMainList->addItem(viewStatsButton);
QObject::connect(parent, &FrogPilotSettingsWindow::closeSubPanel, [dataLayout, dataMainPanel] {
dataLayout->setCurrentWidget(dataMainPanel);
});
QObject::connect(parent, &FrogPilotSettingsWindow::updateMetric, [this](bool metric){isMetric = metric;});
QObject::connect(uiState(), &UIState::offroadTransition, [statsLabelsList, this](bool offroad) {
if (offroad) {
updateStatsLabels(statsLabelsList);
}
});
updateStatsLabels(statsLabelsList);
}
void FrogPilotDataPanel::updateStatsLabels(FrogPilotListWidget *labelsList) {
labelsList->clear();
QJsonObject stats = QJsonDocument::fromJson(QString::fromStdString(params.get("FrogPilotStats")).toUtf8()).object();
static QSet<QString> ignored_keys = {
"Month"
};
static QMap<QString, QPair<QString, QString>> key_map = {
{"AEBEvents", {tr("Total Emergency Brake Alerts"), "count"}},
{"AOLTime", {tr("Time Using \"Always On Lateral\""), "time"}},
{"CruiseSpeedTimes", {tr("Favorite Set Speed"), "speed"}},
{"Disengages", {tr("Total Disengagements"), "count"}},
{"Engages", {tr("Total Engagements"), "count"}},
{"ExperimentalModeTime", {tr("Time Using \"Experimental Mode\""), "time"}},
{"FrogChirps", {tr("Total Frog Chirps"), "count"}},
{"FrogHops", {tr("Total Frog Hops"), "count"}},
{"FrogPilotDrives", {tr("Total Drives"), "count"}},
{"FrogPilotMeters", {tr("Total Distance Driven"), "distance"}},
{"FrogPilotSeconds", {tr("Total Driving Time"), "time"}},
{"FrogSqueaks", {tr("Total Frog Squeaks"), "count"}},
{"GoatScreams", {tr("Total Goat Screams"), "count"}},
{"HighestAcceleration", {tr("Highest Acceleration Rate"), "accel"}},
{"LateralTime", {tr("Time Using Lateral Control"), "time"}},
{"LongestDistanceWithoutOverride", {tr("Longest Distance Without an Override"), "distance"}},
{"LongitudinalTime", {tr("Time Using Longitudinal Control"), "time"}},
{"ModelTimes", {tr("Driving Models:"), "other"}},
{"Month", {tr("Month"), "other"}},
{"Overrides", {tr("Total Overrides"), "count"}},
{"OverrideTime", {tr("Time Overriding openpilot"), "time"}},
{"RandomEvents", {tr("Random Events:"), "other"}},
{"StandstillTime", {tr("Time Stopped"), "time"}},
{"StopLightTime", {tr("Time Spent at Stoplights"), "time"}},
{"TrackedTime", {tr("Total Time Tracked"), "time"}}
};
static QSet<QString> parent_keys = {
"ModelTimes",
"RandomEvents"
};
static QSet<QString> percentage_keys = {
"AOLTime",
"ExperimentalModeTime",
"LateralTime",
"LongitudinalTime",
"OverrideTime",
"StandstillTime",
"StopLightTime"
};
static QMap<QString, QString> random_events_map = {
{"accel30", tr("UwUs")},
{"accel35", tr("Loch Ness Encounters")},
{"accel40", tr("Visits to 1955")},
{"dejaVuCurve", tr("Deja Vu Moments")},
{"firefoxSteerSaturated", tr("Internet Explorer Weeeeeeees")},
{"hal9000", tr("HAL 9000 Denials")},
{"openpilotCrashedRandomEvent", tr("openpilot Crashes")},
{"thisIsFineSteerSaturated", tr("This Is Fine Moments")},
{"toBeContinued", tr("To Be Continued Moments")},
{"vCruise69", tr("Noices")},
{"yourFrogTriedToKillMe", tr("Attempted Frog Murders")},
{"youveGotMail", tr("Total Mail Received")}
};
QStringList keys = key_map.keys();
std::sort(keys.begin(), keys.end(), [&](const QString &a, const QString &b) {
return key_map.value(a).first.toLower() < key_map.value(b).first.toLower();
});
double tracked_time = stats.contains("TrackedTime") ? stats.value("TrackedTime").toDouble() : 0.0;
std::function<QString(double)> format_number = [&](double number) {
return QLocale().toString(number);
};
std::function<QString(double)> format_distance = [&](double meters) {
double value;
QString unit;
if (isMetric) {
value = meters / 1000.0;
unit = (value == 1.0) ? tr(" kilometer") : tr(" kilometers");
} else {
value = meters * METER_TO_MILE;
unit = (value == 1.0) ? tr(" mile") : tr(" miles");
}
return format_number(qRound(value)) + unit;
};
std::function<QString(int)> format_time = [&](int seconds) {
static int seconds_in_day = 60 * 60 * 24;
static int seconds_in_hour = 60 * 60;
int days = seconds / seconds_in_day;
int hours = (seconds % seconds_in_day) / seconds_in_hour;
int minutes = (seconds % seconds_in_hour) / 60;
QString result;
if (days > 0) result += format_number(days) + (days == 1 ? tr(" day ") : tr(" days "));
if (hours > 0 || days > 0) result += format_number(hours) + (hours == 1 ? tr(" hour ") : tr(" hours "));
result += format_number(minutes) + (minutes == 1 ? tr(" minute") : tr(" minutes"));
return result.trimmed();
};
for (const QString &key : keys) {
if (ignored_keys.contains(key)) continue;
QJsonValue value = stats.contains(key) ? stats.value(key) : QJsonValue(0);
QString label_text = key_map.value(key).first;
QString type = key_map.value(key).second;
if (key == "CruiseSpeedTimes" && value.isObject()) {
QJsonObject speeds = value.toObject();
double max_time = -1.0;
QString best_speed;
for (QJsonObject::const_iterator it = speeds.begin(); it != speeds.end(); ++it) {
double time = it.value().toDouble();
if (time > max_time) {
best_speed = it.key();
max_time = time;
}
}
QString display_speed;
if (isMetric) {
display_speed = QString::number(qRound(best_speed.toDouble() * MS_TO_KPH)) + " " + tr("km/h");
} else {
display_speed = QString::number(qRound(best_speed.toDouble() * MS_TO_MPH)) + " " + tr("mph");
}
labelsList->addItem(new LabelControl(label_text, display_speed + " (" + format_time(max_time) + ")", "", this));
} else if (parent_keys.contains(key) && value.isObject()) {
labelsList->addItem(new LabelControl(label_text, "", "", this));
QJsonObject subobj = value.toObject();
QStringList subkeys;
if (key == "RandomEvents") {
subkeys = random_events_map.keys();
} else {
subkeys = subobj.keys();
}
std::sort(subkeys.begin(), subkeys.end(), [&](const QString &a, const QString &b) {
QString display_a, display_b;
if (key == "ModelTimes") {
display_a = processModelName(a);
display_b = processModelName(b);
} else if (key == "RandomEvents") {
display_a = random_events_map.value(a, a);
display_b = random_events_map.value(b, b);
} else {
display_a = a;
display_b = b;
}
return display_a.toLower() < display_b.toLower();
});
for (const QString &subkey : subkeys) {
QString display_subkey;
if (key == "ModelTimes") {
display_subkey = processModelName(subkey);
} else if (key == "RandomEvents") {
display_subkey = random_events_map.value(subkey, subkey);
} else {
display_subkey = subkey;
}
QString subvalue;
if (key == "ModelTimes") {
subvalue = format_time(subobj.value(subkey).toDouble());
} else {
subvalue = subobj.value(subkey).toVariant().toString().isEmpty() ? "0" : format_number(subobj.value(subkey).toInt());
}
labelsList->addItem(new LabelControl(" " + display_subkey, subvalue, "", this));
}
} else {
QString display_value;
if (type == "accel") {
display_value = QString::number(value.toDouble(), 'f', 2) + " " + tr("m/s²");
} else if (type == "count") {
QString trimmed_label = label_text;
if (trimmed_label.startsWith(tr("Total "))) {
trimmed_label = trimmed_label.mid(6);
}
display_value = format_number(value.toInt()) + " " + trimmed_label;
} else if (type == "distance") {
display_value = format_distance(value.toDouble());
} else if (type == "time") {
display_value = format_time(value.toDouble());
} else {
display_value = value.toVariant().toString().isEmpty() ? "0" : value.toVariant().toString();
}
labelsList->addItem(new LabelControl(label_text, display_value, "", this));
if (percentage_keys.contains(key)) {
int percent = 0;
if (tracked_time > 0.0) {
percent = (value.toDouble() * 100.0) / tracked_time;
}
labelsList->addItem(new LabelControl(tr("% of ") + label_text, format_number(percent) + "%", "", this));
}
}
}
}

View File

@ -8,8 +8,16 @@ class FrogPilotDataPanel : public FrogPilotListWidget {
public:
explicit FrogPilotDataPanel(FrogPilotSettingsWindow *parent);
signals:
void openSubPanel();
private:
void updateStatsLabels(FrogPilotListWidget *labelsList);
bool isMetric;
FrogPilotSettingsWindow *parent;
Params params;
Params params_cache{"/cache/params"};
};

View File

@ -15,24 +15,55 @@
bool nnffLogFileExists(const QString &carFingerprint) {
static QStringList files;
static QMap<QString, QString> substitutes;
if (files.isEmpty()) {
QFileInfoList fileInfoList = QDir(QStringLiteral("../../frogpilot/assets/nnff_models")).entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
for (const QFileInfo &fileInfo : fileInfoList) {
files.append(fileInfo.completeBaseName());
}
QFile sub_file(QStringLiteral("../../selfdrive/car/torque_data/substitute.toml"));
if (sub_file.open(QIODevice::ReadOnly)) {
QTextStream in(&sub_file);
while (!in.atEnd()) {
QString line = in.readLine().trimmed();
if (line.startsWith("#") || line.startsWith("legend") || !line.contains("=")) {
continue;
}
QStringList parts = line.split("=");
if (parts.size() == 2) {
QString key = parts[0].trimmed().remove('"');
QString value = parts[1].trimmed().remove('"');
if (!key.isEmpty() && !value.isEmpty()) {
substitutes[key] = value;
}
}
}
}
}
QStringList fingerprintsToCheck;
fingerprintsToCheck.append(carFingerprint);
if (substitutes.contains(carFingerprint)) {
fingerprintsToCheck.append(substitutes.value(carFingerprint));
}
for (const QString &fingerprint : fingerprintsToCheck) {
for (const QString &file : files) {
if (file.startsWith(carFingerprint)) {
std::cout << "NNFF supports fingerprint: " << file.toStdString() << std::endl;
if (file.startsWith(fingerprint)) {
std::cout << "NNFF model found for fingerprint: " << fingerprint.toStdString() << std::endl;
return true;
}
}
}
return false;
}
void FrogPilotSettingsWindow::createPanelButtons(FrogPilotListWidget *list) {
FrogPilotDataPanel *frogpilotDataPanel = new FrogPilotDataPanel(this);
FrogPilotDevicePanel *frogpilotDevicePanel = new FrogPilotDevicePanel(this);
FrogPilotLateralPanel *frogpilotLateralPanel = new FrogPilotLateralPanel(this);
FrogPilotLongitudinalPanel *frogpilotLongitudinalPanel = new FrogPilotLongitudinalPanel(this);
@ -49,7 +80,7 @@ void FrogPilotSettingsWindow::createPanelButtons(FrogPilotListWidget *list) {
{{tr("MANAGE"), frogpilotSoundsPanel}},
{{tr("DRIVING MODEL"), frogpilotModelPanel}, {tr("GAS / BRAKE"), frogpilotLongitudinalPanel}, {tr("STEERING"), frogpilotLateralPanel}},
{{tr("MAP DATA"), frogpilotMapsPanel}, {tr("NAVIGATION"), frogpilotNavigationPanel}},
{{tr("DATA"), new FrogPilotDataPanel(this)}, {tr("DEVICE CONTROLS"), frogpilotDevicePanel}, {tr("UTILITIES"), new FrogPilotUtilitiesPanel(this)}},
{{tr("DATA"), frogpilotDataPanel}, {tr("DEVICE CONTROLS"), frogpilotDevicePanel}, {tr("UTILITIES"), new FrogPilotUtilitiesPanel(this)}},
{{tr("APPEARANCE"), frogpilotVisualsPanel}, {tr("THEME"), frogpilotThemesPanel}},
{{tr("VEHICLE SETTINGS"), frogpilotVehiclesPanel}, {tr("WHEEL CONTROLS"), frogpilotWheelPanel}}
};
@ -107,10 +138,12 @@ void FrogPilotSettingsWindow::createPanelButtons(FrogPilotListWidget *list) {
list->addItem(panelButton);
}
QObject::connect(frogpilotDataPanel, &FrogPilotDataPanel::openSubPanel, this, &FrogPilotSettingsWindow::openSubPanel);
QObject::connect(frogpilotDevicePanel, &FrogPilotDevicePanel::openSubPanel, this, &FrogPilotSettingsWindow::openSubPanel);
QObject::connect(frogpilotLateralPanel, &FrogPilotLateralPanel::openSubPanel, this, &FrogPilotSettingsWindow::openSubPanel);
QObject::connect(frogpilotLongitudinalPanel, &FrogPilotLongitudinalPanel::openSubPanel, this, &FrogPilotSettingsWindow::openSubPanel);
QObject::connect(frogpilotLongitudinalPanel, &FrogPilotLongitudinalPanel::openSubSubPanel, this, &FrogPilotSettingsWindow::openSubSubPanel);
QObject::connect(frogpilotLongitudinalPanel, &FrogPilotLongitudinalPanel::openSubSubSubPanel, this, &FrogPilotSettingsWindow::openSubSubSubPanel);
QObject::connect(frogpilotMapsPanel, &FrogPilotMapsPanel::openSubPanel, this, &FrogPilotSettingsWindow::openSubPanel);
QObject::connect(frogpilotModelPanel, &FrogPilotModelPanel::openSubPanel, this, &FrogPilotSettingsWindow::openSubPanel);
QObject::connect(frogpilotNavigationPanel, &FrogPilotNavigationPanel::closeSubPanel, this, &FrogPilotSettingsWindow::closeSubPanel);
@ -176,6 +209,7 @@ FrogPilotSettingsWindow::FrogPilotSettingsWindow(SettingsWindow *parent) : QFram
QObject::connect(parent, &SettingsWindow::closePanel, this, &FrogPilotSettingsWindow::closePanel);
QObject::connect(parent, &SettingsWindow::closeSubPanel, this, &FrogPilotSettingsWindow::closeSubPanel);
QObject::connect(parent, &SettingsWindow::closeSubSubPanel, this, &FrogPilotSettingsWindow::closeSubSubPanel);
QObject::connect(parent, &SettingsWindow::closeSubSubSubPanel, this, &FrogPilotSettingsWindow::closeSubSubSubPanel);
QObject::connect(parent, &SettingsWindow::updateMetric, this, &FrogPilotSettingsWindow::updateMetric);
QObject::connect(parent, &SettingsWindow::updateTuningLevel, this, &FrogPilotSettingsWindow::updateTuningLevel);
QObject::connect(uiState(), &UIState::offroadTransition, this, &FrogPilotSettingsWindow::updateVariables);
@ -270,13 +304,15 @@ void FrogPilotSettingsWindow::updateVariables() {
hasPedal = CP.getEnableGasInterceptor();
hasRadar = !CP.getRadarUnavailable();
hasSDSU = frogpilot_toggles.value("has_sdsu").toBool();
hasSNG = hasOpenpilotLongitudinal && CP.getAutoResumeSng();
hasSNG = CP.getAutoResumeSng();
hasZSS = frogpilot_toggles.value("has_zss").toBool();
isAngleCar = CP.getSteerControlType() == cereal::CarParams::SteerControlType::ANGLE;
isBolt = carFingerprint == "CHEVROLET_BOLT_CC" || carFingerprint == "CHEVROLET_BOLT_EUV";
isGM = carMake == "gm";
isHKG = carMake == "hyundai";
isHKGCanFd = isHKG && safetyModel == cereal::CarParams::SafetyModel::HYUNDAI_CANFD;
isHonda = carMake == "honda";
isHondaNidec = isHonda && safetyModel == cereal::CarParams::SafetyModel::HONDA_NIDEC;
isSubaru = carMake == "subaru";
isTorqueCar = CP.getLateralTuning().which() == cereal::CarParams::LateralTuning::TORQUE;
isToyota = carMake == "toyota";
@ -286,7 +322,7 @@ void FrogPilotSettingsWindow::updateVariables() {
longitudinalActuatorDelay = CP.getLongitudinalActuatorDelay();
startAccel = CP.getStartAccel();
steerActuatorDelay = CP.getSteerActuatorDelay();
steerKp = CP.getLateralTuning().getTorque().getKp();
steerKp = 1.0;
steerRatio = CP.getSteerRatio();
stopAccel = CP.getStopAccel();
stoppingDecelRate = CP.getStoppingDecelRate();

View File

@ -1,8 +1,14 @@
#pragma once
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include "selfdrive/ui/qt/offroad/settings.h"
#include "selfdrive/ui/qt/widgets/scrollview.h"
class QNetworkAccessManager;
class FrogPilotSettingsWindow : public QFrame {
Q_OBJECT
@ -32,6 +38,8 @@ public:
bool isGM = true;
bool isHKG = true;
bool isHKGCanFd = true;
bool isHonda = true;
bool isHondaNidec = true;
bool isSubaru = false;
bool isTorqueCar = false;
bool isToyota = true;
@ -59,9 +67,11 @@ public:
signals:
void closeSubPanel();
void closeSubSubPanel();
void closeSubSubSubPanel();
void openPanel();
void openSubPanel();
void openSubSubPanel();
void openSubSubSubPanel();
void updateMetric(bool metric, bool bootRun=false);
private:

View File

@ -365,7 +365,7 @@ void FrogPilotLateralPanel::updateToggles() {
else if (key == "ForceAutoTune") {
setVisible &= !parent->hasAutoTune;
setVisible &= !parent->isAngleCar;
setVisible &= parent->isTorqueCar || forcingTorqueController;
setVisible &= parent->isTorqueCar || forcingTorqueController || usingNNFF;
}
else if (key == "ForceAutoTuneOff") {
@ -402,20 +402,20 @@ void FrogPilotLateralPanel::updateToggles() {
else if (key == "SteerFriction") {
setVisible &= parent->friction != 0;
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : !forcingAutoTune;
setVisible &= parent->isTorqueCar || forcingTorqueController;
setVisible &= parent->isTorqueCar || forcingTorqueController || usingNNFF;
setVisible &= !usingNNFF;
}
else if (key == "SteerKP") {
setVisible &= parent->steerKp != 0;
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : !forcingAutoTune;
setVisible &= parent->isTorqueCar || forcingTorqueController;
setVisible &= parent->isTorqueCar || forcingTorqueController || usingNNFF;
setVisible &= !parent->isAngleCar;
}
else if (key == "SteerLatAccel") {
setVisible &= parent->latAccelFactor != 0;
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : !forcingAutoTune;
setVisible &= parent->isTorqueCar || forcingTorqueController;
setVisible &= parent->isTorqueCar || forcingTorqueController || usingNNFF;
setVisible &= !usingNNFF;
}

View File

@ -1,6 +1,8 @@
#include "frogpilot/ui/qt/offroad/longitudinal_settings.h"
FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
networkManager = new QNetworkAccessManager(this);
QJsonObject shownDescriptions = QJsonDocument::fromJson(QString::fromStdString(params.get("ShownToggleDescriptions")).toUtf8()).object();
QString className = this->metaObject()->className();
@ -33,6 +35,11 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
FrogPilotListWidget *speedLimitControllerVisualList = new FrogPilotListWidget(this);
FrogPilotListWidget *standardPersonalityList = new FrogPilotListWidget(this);
FrogPilotListWidget *trafficPersonalityList = new FrogPilotListWidget(this);
FrogPilotListWidget *weatherList = new FrogPilotListWidget(this);
FrogPilotListWidget *weatherLowVisibilityList = new FrogPilotListWidget(this);
FrogPilotListWidget *weatherRainList = new FrogPilotListWidget(this);
FrogPilotListWidget *weatherRainStormList = new FrogPilotListWidget(this);
FrogPilotListWidget *weatherSnowList = new FrogPilotListWidget(this);
ScrollView *advancedLongitudinalTunePanel = new ScrollView(advancedLongitudinalTuneList, this);
ScrollView *aggressivePersonalityPanel = new ScrollView(aggressivePersonalityList, this);
@ -48,6 +55,11 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
ScrollView *speedLimitControllerVisualPanel = new ScrollView(speedLimitControllerVisualList, this);
ScrollView *standardPersonalityPanel = new ScrollView(standardPersonalityList, this);
ScrollView *trafficPersonalityPanel = new ScrollView(trafficPersonalityList, this);
ScrollView *weatherLowVisibilityPanel = new ScrollView(weatherLowVisibilityList, this);
ScrollView *weatherPanel = new ScrollView(weatherList, this);
ScrollView *weatherRainPanel = new ScrollView(weatherRainList, this);
ScrollView *weatherRainStormPanel = new ScrollView(weatherRainStormList, this);
ScrollView *weatherSnowPanel = new ScrollView(weatherSnowList, this);
longitudinalLayout->addWidget(advancedLongitudinalTunePanel);
longitudinalLayout->addWidget(aggressivePersonalityPanel);
@ -63,6 +75,11 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
longitudinalLayout->addWidget(speedLimitControllerVisualPanel);
longitudinalLayout->addWidget(standardPersonalityPanel);
longitudinalLayout->addWidget(trafficPersonalityPanel);
longitudinalLayout->addWidget(weatherLowVisibilityPanel);
longitudinalLayout->addWidget(weatherPanel);
longitudinalLayout->addWidget(weatherRainPanel);
longitudinalLayout->addWidget(weatherRainStormPanel);
longitudinalLayout->addWidget(weatherSnowPanel);
const std::vector<std::tuple<QString, QString, QString, QString>> longitudinalToggles {
{"AdvancedLongitudinalTune", tr("Advanced Longitudinal Tuning"), tr("<b>Advanced acceleration and braking control changes</b> to fine-tune how openpilot drives."), "../../frogpilot/assets/toggle_icons/icon_advanced_longitudinal_tune.png"},
@ -77,9 +94,10 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
{"ConditionalExperimental", tr("Conditional Experimental Mode"), tr("<b>Automatically switch to \"Experimental Mode\" when set conditions are met.</b> Allows the model to handle challenging situations with smarter decision making."), "../../frogpilot/assets/toggle_icons/icon_conditional.png"},
{"CESpeed", tr("Below"), tr("<b>Switch to \"Experimental Mode\" when driving below this speed without a lead</b> to help openpilot handle low-speed situations more smoothly."), ""},
{"CECurves", tr("Curve Detected Ahead"), tr("<b>Switch to \"Experimental Mode\" when a curve is detected</b> to allow the model to set an appropriate speed for the curve."), ""},
{"CEStopLights", tr("\"Detected\" Stop Lights/Signs"), tr("<b>Switch to \"Experimental Mode\" whenever the driving model \"detects\" a red light or stop sign.</b><br><br><i><b>Disclaimer</b>: openpilot does not explicitly detect traffic lights or stop signs. In \"Experimental Mode\", openpilot makes end-to-end driving decisions from camera input, which means it may stop even when there's no clear reason!</i>"), ""},
{"CELead", tr("Lead Detected Ahead"), tr("<b>Switch to \"Experimental Mode\" when a slower or stopped vehicle is detected.</b> Can make braking smoother and more reliable on some vehicles."), ""},
{"CENavigation", tr("Navigation-Based"), tr("<b>Switch to \"Experimental Mode\" when approaching intersections or turns on the active route</b> while using \"Navigate on openpilot\" (NOO) to allow the model to set an appropriate speed for upcoming maneuvers."), ""},
{"CEModelStopTime", tr("Predicted Stop In"), tr("<b>Switch to \"Experimental Mode\" when openpilot predicts a stop within the set time.</b> This is usually triggered when the model \"sees\" a red light or stop sign ahead.<br><br><i><b>Disclaimer</b>: openpilot does not explicitly detect traffic lights or stop signs. In \"Experimental Mode\", openpilot makes end-to-end driving decisions from camera input, which means it may stop even when there's no clear reason.</i>"), ""},
{"CEModelStopTime", tr("Predicted Stop In"), tr("<b>Switch to \"Experimental Mode\" when openpilot predicts a stop within the set time.</b> This is usually triggered when the model \"sees\" a red light or stop sign ahead.<br><br><i><b>Disclaimer</b>: openpilot does not explicitly detect traffic lights or stop signs. In \"Experimental Mode\", openpilot makes end-to-end driving decisions from camera input, which means it may stop even when there's no clear reason!</i>"), ""},
{"CESignalSpeed", tr("Turn Signal Below"), tr("<b>Switch to \"Experimental Mode\" when using a turn signal below the set speed</b> to allow the model to choose an appropriate speed for smoother left and right turns."), ""},
{"ShowCEMStatus", tr("Status Widget"), tr("<b>Show which condition triggered \"Experimental Mode\"</b> on the driving screen."), ""},
@ -132,17 +150,45 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
{"DecelerationProfile", tr("Deceleration Profile"), tr("<b>How firmly openpilot slows down.</b> \"Eco\" favors coasting, \"Sport\" applies stronger braking."), ""},
{"HumanAcceleration", tr("Human-Like Acceleration"), tr("<b>Acceleration that mimics human behavior</b> by easing the throttle at low speeds and adding extra power when taking off from a stop."), ""},
{"HumanFollowing", tr("Human-Like Following"), tr("<b>Following behavior that mimics human drivers</b> by closing gaps behind faster vehicles for quicker takeoffs and dynamically adjusting the desired following distance for gentler, more efficient braking."), ""},
{"HumanLaneChanges", tr("Human-Like Lane Changes"), tr("<b>Lane-change behavior that mimics human drivers</b> by anticipating and tracking adjacent vehicles during lane changes."), ""},
{"LeadDetectionThreshold", tr("Lead Detection Sensitivity"), tr("<b>How sensitive openpilot is to detecting vehicles.</b> Higher sensitivity allows quicker detection at longer distances but may react to non-vehicle objects; lower sensitivity is more conservative and reduces false detections."), ""},
{"TacoTune", tr("\"Taco Bell Run\" Turn Speed Hack"), tr("<b>The turn-speed hack from comma's 2022 \"Taco Bell Run\".</b> Designed to slow down for left and right turns."), ""},
{"QOLLongitudinal", tr("Quality of Life"), tr("<b>Miscellaneous acceleration and braking control changes</b> to fine-tune how openpilot drives."), "../../frogpilot/assets/toggle_icons/icon_quality_of_life.png"},
{"CustomCruise", tr("Cruise Interval"), tr("<b>How much the set speed increases or decreases</b> for each + or cruise control button press."), ""},
{"CustomCruiseLong", tr("Cruise Interval (Hold)"), tr("<b>How much the set speed increases or decreases while holding the + or cruise control buttons.</b>"), ""},
{"ForceStops", tr("Force Stop at \"Detected\" Stop Lights/Signs"), tr("<b>Force openpilot to stop whenever the driving model \"detects\" a red light or stop sign.</b><br><br><i><b>Disclaimer</b>: openpilot does not explicitly detect traffic lights or stop signs. In \"Experimental Mode\", openpilot makes end-to-end driving decisions from camera input, which means it may stop even when there's no clear reason.</i>"), ""},
{"ForceStops", tr("Force Stop at \"Detected\" Stop Lights/Signs"), tr("<b>Force openpilot to stop whenever the driving model \"detects\" a red light or stop sign.</b><br><br><i><b>Disclaimer</b>: openpilot does not explicitly detect traffic lights or stop signs. In \"Experimental Mode\", openpilot makes end-to-end driving decisions from camera input, which means it may stop even when there's no clear reason!</i>"), ""},
{"IncreasedStoppedDistance", tr("Increase Stopped Distance by:"), tr("<b>Add extra space when stopped behind vehicles.</b> Increase for more room; decrease for shorter gaps."), ""},
{"MapGears", tr("Map Accel/Decel to Gears"), tr("<b>Map the Acceleration or Deceleration profiles to the vehicle's \"Eco\" and \"Sport\" gear modes.</b>"), ""},
{"SetSpeedOffset", tr("Offset Set Speed by:"), tr("<b>Increase the set speed by the chosen offset.</b> For example, set +5 if you usually drive 5 over the limit."), ""},
{"ReverseCruise", tr("Reverse Cruise Increase"), tr("<b>Reverse the cruise control button behavior</b> so a short press increases the set speed by 5 instead of 1."), ""},
{"WeatherPresets", tr("Weather Condition Offsets"), tr("<b>Automatically adjust driving behavior based on real-time weather.</b> Helps maintain comfort and safety in low visibility, rain, or snow."), ""},
{"LowVisibilityOffsets", tr("Low Visibility"), tr("<b>Driving adjustments for fog, haze, or other low-visibility conditions.</b>"), ""},
{"IncreaseFollowingLowVisibility", tr("Increase Following Distance by:"), tr("<b>Add extra space behind lead vehicles in low visibility.</b> Increase for more space; decrease for tighter gaps."), ""},
{"IncreasedStoppedDistanceLowVisibility", tr("Increase Stopped Distance by:"), tr("<b>Add extra buffer when stopped behind vehicles in low visibility.</b> Increase for more room; decrease for shorter gaps."), ""},
{"ReduceAccelerationLowVisibility", tr("Reduce Acceleration by:"), tr("<b>Lower the maximum acceleration in low visibility.</b> Increase for softer takeoffs; decrease for quicker but less stable takeoffs."), ""},
{"ReduceLateralAccelerationLowVisibility", tr("Reduce Speed in Curves by:"), tr("<b>Lower the desired speed while driving through curves in low visibility.</b> Increase for safer, gentler turns; decrease for more aggressive driving in curves."), ""},
{"RainOffsets", tr("Rain"), tr("<b>Driving adjustments for rainy conditions.</b>"), ""},
{"IncreaseFollowingRain", tr("Increase Following Distance by:"), tr("<b>Add extra space behind lead vehicles in rain.</b> Increase for more space; decrease for tighter gaps."), ""},
{"IncreasedStoppedDistanceRain", tr("Increase Stopped Distance by:"), tr("<b>Add extra buffer when stopped behind vehicles in rain.</b> Increase for more room; decrease for shorter gaps."), ""},
{"ReduceAccelerationRain", tr("Reduce Acceleration by:"), tr("<b>Lower the maximum acceleration in rain.</b> Increase for softer takeoffs; decrease for quicker but less stable takeoffs."), ""},
{"ReduceLateralAccelerationRain", tr("Reduce Speed in Curves by:"), tr("<b>Lower the desired speed while driving through curves in rain.</b> Increase for safer, gentler turns; decrease for more aggressive driving in curves."), ""},
{"RainStormOffsets", tr("Rainstorms"), tr("<b>Driving adjustments for rainstorms.</b>"), ""},
{"IncreaseFollowingRainStorm", tr("Increase Following Distance by:"), tr("<b>Add extra space behind lead vehicles in a rainstorm.</b> Increase for more space; decrease for tighter gaps."), ""},
{"IncreasedStoppedDistanceRainStorm", tr("Increase Stopped Distance by:"), tr("<b>Add extra buffer when stopped behind vehicles in a rainstorm.</b> Increase for more room; decrease for shorter gaps."), ""},
{"ReduceAccelerationRainStorm", tr("Reduce Acceleration by:"), tr("<b>Lower the maximum acceleration in a rainstorm.</b> Increase for softer takeoffs; decrease for quicker but less stable takeoffs."), ""},
{"ReduceLateralAccelerationRainStorm", tr("Reduce Speed in Curves by:"), tr("<b>Lower the desired speed while driving through curves in a rainstorm.</b> Increase for safer, gentler turns; decrease for more aggressive driving in curves."), ""},
{"SnowOffsets", tr("Snow"), tr("<b>Driving adjustments for snowy conditions.</b>"), ""},
{"IncreaseFollowingSnow", tr("Increase Following Distance by:"), tr("<b>Add extra space behind lead vehicles in snow.</b> Increase for more space; decrease for tighter gaps."), ""},
{"IncreasedStoppedDistanceSnow", tr("Increase Stopped Distance by:"), tr("<b>Add extra buffer when stopped behind vehicles in snow.</b> Increase for more room; decrease for shorter gaps."), ""},
{"ReduceAccelerationSnow", tr("Reduce Acceleration by:"), tr("<b>Lower the maximum acceleration in snow.</b> Increase for softer takeoffs; decrease for quicker but less stable takeoffs."), ""},
{"ReduceLateralAccelerationSnow", tr("Reduce Speed in Curves by:"), tr("<b>Lower the desired speed while driving through curves in snow.</b> Increase for safer, gentler turns; decrease for more aggressive driving in curves."), ""},
{"SetWeatherKey", tr("Set Your Own Key"), tr("<b>Set your own \"OpenWeatherMap\" key to increase the weather update rate.</b><br><br><i>Personal keys grant 1,000 free calls per day, allowing for updates every minute. The default key is shared and only updates every 15 minutes.</i>"), ""},
{"SpeedLimitController", tr("Speed Limit Controller"), tr("<b>Limit openpilot's maximum driving speed to the current speed limit</b> obtained from downloaded maps, Mapbox, Navigate on openpilot, or the dashboard for supported vehicles (Ford, Genesis, Hyundai, Kia, Lexus, Toyota)."), "../../frogpilot/assets/toggle_icons/icon_speed_limit.png"},
{"SLCFallback", tr("Fallback Speed"), tr("<b>The speed used by \"Speed Limit Controller\" when no speed limit is found.</b><br><br>- <b>Set Speed</b>: Use the cruise set speed<br>- <b>Experimental Mode</b>: Estimate the limit using the driving model<br>- <b>Previous Limit</b>: Keep using the last confirmed limit"), ""},
@ -361,6 +407,116 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
longitudinalToggle = new FrogPilotButtonToggleControl(param, title, desc, icon, mapGearsToggles, mapGearsToggleNames);
} else if (param == "SetSpeedOffset") {
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, tr(" mph"));
} else if (param == "WeatherPresets") {
FrogPilotManageControl *weatherToggle = new FrogPilotManageControl(param, title, desc, icon);
QObject::connect(weatherToggle, &FrogPilotManageControl::manageButtonClicked, [longitudinalLayout, weatherPanel, this]() {
openSubSubPanel();
longitudinalLayout->setCurrentWidget(weatherPanel);
qolOpen = true;
});
longitudinalToggle = weatherToggle;
} else if (param == "SetWeatherKey") {
weatherKeyControl = new FrogPilotButtonsControl(title, desc, icon, {tr("ADD"), tr("TEST")});
QObject::connect(weatherKeyControl, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
if (id == 0) {
if (!params.get("WeatherToken").empty()) {
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to remove your key?"), this)) {
params.remove("WeatherToken");
params_cache.remove("WeatherToken");
weatherKeyControl->setText(0, tr("ADD"));
weatherKeyControl->setVisibleButton(1, false);
}
} else {
QString key = InputDialog::getText(tr("Enter your \"OpenWeatherMap\" key"), this).trimmed();
if (key.length() == 32) {
params.put("WeatherToken", key.toStdString());
weatherKeyControl->setText(0, tr("REMOVE"));
weatherKeyControl->setVisibleButton(1, true);
} else if (!key.isEmpty()) {
ConfirmationDialog::alert(tr("Invalid key!"), this);
}
}
} else {
weatherKeyControl->setValue(tr("Testing..."));
QString key = QString::fromStdString(params.get("WeatherToken"));
QString url = QString("https://api.openweathermap.org/data/2.5/weather?lat=42.4293&lon=-83.9850&appid=%1").arg(key);
QNetworkRequest request(url);
QNetworkReply *reply = networkManager->get(request);
connect(reply, &QNetworkReply::finished, [=]() {
weatherKeyControl->setValue("");
QString message;
if (reply->error() == QNetworkReply::NoError) {
message = tr("Key is valid!");
} else if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 401) {
message = tr("Invalid key!");
} else {
message = tr("An error occurred: %1").arg(reply->errorString());
}
ConfirmationDialog::alert(message, this);
reply->deleteLater();
});
}
});
longitudinalToggle = weatherKeyControl;
} else if (param == "LowVisibilityOffsets") {
ButtonControl *manageLowVisibilitOffsetsButton = new ButtonControl(title, tr("MANAGE"), desc);
QObject::connect(manageLowVisibilitOffsetsButton, &ButtonControl::clicked, [longitudinalLayout, weatherLowVisibilityPanel, this]() {
openSubSubSubPanel();
longitudinalLayout->setCurrentWidget(weatherLowVisibilityPanel);
weatherOpen = true;
});
longitudinalToggle = manageLowVisibilitOffsetsButton;
} else if (param == "RainOffsets") {
ButtonControl *manageRainOffsetsButton = new ButtonControl(title, tr("MANAGE"), desc);
QObject::connect(manageRainOffsetsButton, &ButtonControl::clicked, [longitudinalLayout, weatherRainPanel, this]() {
openSubSubSubPanel();
longitudinalLayout->setCurrentWidget(weatherRainPanel);
weatherOpen = true;
});
longitudinalToggle = manageRainOffsetsButton;
} else if (param == "RainStormOffsets") {
ButtonControl *manageRainStormOffsetsButton = new ButtonControl(title, tr("MANAGE"), desc);
QObject::connect(manageRainStormOffsetsButton, &ButtonControl::clicked, [longitudinalLayout, weatherRainStormPanel, this]() {
openSubSubSubPanel();
longitudinalLayout->setCurrentWidget(weatherRainStormPanel);
weatherOpen = true;
});
longitudinalToggle = manageRainStormOffsetsButton;
} else if (param == "SnowOffsets") {
ButtonControl *manageSnowOffsetsButton = new ButtonControl(title, tr("MANAGE"), desc);
QObject::connect(manageSnowOffsetsButton, &ButtonControl::clicked, [longitudinalLayout, weatherSnowPanel, this]() {
openSubSubSubPanel();
longitudinalLayout->setCurrentWidget(weatherSnowPanel);
weatherOpen = true;
});
longitudinalToggle = manageSnowOffsetsButton;
} else if (param == "IncreaseFollowingLowVisibility" || param == "IncreaseFollowingRain" || param == "IncreaseFollowingRainStorm" || param == "IncreaseFollowingSnow") {
std::map<float, QString> followTimeLabels;
for (float i = 0; i <= 3; i += 0.01) {
followTimeLabels[i] = std::lround(i / 0.01) == 1 / 0.01 ? QString::number(i, 'f', 2) + tr(" second") : QString::number(i, 'f', 2) + tr(" seconds");
}
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 3, QString(), followTimeLabels, 0.01, true);
} else if (param == "IncreasedStoppedDistanceLowVisibility" || param == "IncreasedStoppedDistanceRain" || param == "IncreasedStoppedDistanceRainStorm" || param == "IncreasedStoppedDistanceSnow") {
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 10, tr(" feet"));
} else if (param == "ReduceAccelerationLowVisibility" || param == "ReduceAccelerationRain" || param == "ReduceAccelerationRainStorm" || param == "ReduceAccelerationSnow") {
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, "%", std::map<float, QString>(), 1);
} else if (param == "ReduceLateralAccelerationLowVisibility" || param == "ReduceLateralAccelerationRain" || param == "ReduceLateralAccelerationRainStorm" || param == "ReduceLateralAccelerationSnow") {
longitudinalToggle = new FrogPilotParamValueControl(param, title, desc, icon, 0, 99, "%", std::map<float, QString>(), 1);
} else if (param == "SpeedLimitController") {
FrogPilotManageControl *speedLimitControllerToggle = new FrogPilotManageControl(param, title, desc, icon);
@ -505,6 +661,16 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
standardPersonalityList->addItem(longitudinalToggle);
} else if (trafficPersonalityKeys.contains(param)) {
trafficPersonalityList->addItem(longitudinalToggle);
} else if (weatherKeys.contains(param)) {
weatherList->addItem(longitudinalToggle);
} else if (weatherLowVisibilityKeys.contains(param)) {
weatherLowVisibilityList->addItem(longitudinalToggle);
} else if (weatherRainKeys.contains(param)) {
weatherRainList->addItem(longitudinalToggle);
} else if (weatherRainStormKeys.contains(param)) {
weatherRainStormList->addItem(longitudinalToggle);
} else if (weatherSnowKeys.contains(param)) {
weatherSnowList->addItem(longitudinalToggle);
} else {
longitudinalList->addItem(longitudinalToggle);
@ -637,23 +803,38 @@ FrogPilotLongitudinalPanel::FrogPilotLongitudinalPanel(FrogPilotSettingsWindow *
openDescriptions(forceOpenDescriptions, toggles);
longitudinalLayout->setCurrentWidget(longitudinalPanel);
});
QObject::connect(parent, &FrogPilotSettingsWindow::closeSubSubPanel, [longitudinalLayout, customDrivingPersonalityPanel, speedLimitControllerPanel, this]() {
QObject::connect(parent, &FrogPilotSettingsWindow::closeSubSubPanel, [longitudinalLayout, customDrivingPersonalityPanel, qolPanel, speedLimitControllerPanel, this]() {
openDescriptions(forceOpenDescriptions, toggles);
if (customPersonalityOpen) {
longitudinalLayout->setCurrentWidget(customDrivingPersonalityPanel);
customPersonalityOpen = false;
} else if (qolOpen) {
longitudinalLayout->setCurrentWidget(qolPanel);
qolOpen = false;
} else if (slcOpen) {
longitudinalLayout->setCurrentWidget(speedLimitControllerPanel);
slcOpen = false;
}
});
QObject::connect(parent, &FrogPilotSettingsWindow::closeSubSubSubPanel, [longitudinalLayout, weatherPanel, this]() {
openDescriptions(forceOpenDescriptions, toggles);
if (weatherOpen) {
longitudinalLayout->setCurrentWidget(weatherPanel);
weatherOpen = false;
}
});
QObject::connect(parent, &FrogPilotSettingsWindow::updateMetric, this, &FrogPilotLongitudinalPanel::updateMetric);
}
void FrogPilotLongitudinalPanel::showEvent(QShowEvent *event) {
FrogPilotUIState &fs = *frogpilotUIState();
frogpilotToggleLevels = parent->frogpilotToggleLevels;
calibratedLateralAccelerationLabel->setText(QString::number(params.getFloat("CalibratedLateralAcceleration"), 'f', 2) + tr(" m/s²"));
@ -666,6 +847,10 @@ void FrogPilotLongitudinalPanel::showEvent(QShowEvent *event) {
vEgoStartingToggle->setTitle(QString(tr("Start Speed (Default: %1)")).arg(QString::number(parent->vEgoStarting, 'f', 2)));
vEgoStoppingToggle->setTitle(QString(tr("Stop Speed (Default: %1)")).arg(QString::number(parent->vEgoStopping, 'f', 2)));
bool keyExists = !params.get("WeatherToken").empty();
weatherKeyControl->setText(0, keyExists ? tr("REMOVE") : tr("ADD"));
weatherKeyControl->setVisibleButton(1, keyExists && fs.frogpilot_scene.online);
updateToggles();
}
@ -676,6 +861,10 @@ void FrogPilotLongitudinalPanel::updateMetric(bool metric, bool bootRun) {
double speedConversion = metric ? MILE_TO_KM : KM_TO_MILE;
params.putIntNonBlocking("IncreasedStoppedDistance", params.getInt("IncreasedStoppedDistance") * distanceConversion);
params.putIntNonBlocking("IncreasedStoppedDistanceLowVisibility", params.getInt("IncreasedStoppedDistanceLowVisibility") * distanceConversion);
params.putIntNonBlocking("IncreasedStoppedDistanceRain", params.getInt("IncreasedStoppedDistanceRain") * distanceConversion);
params.putIntNonBlocking("IncreasedStoppedDistanceRainStorm", params.getInt("IncreasedStoppedDistanceRainStorm") * distanceConversion);
params.putIntNonBlocking("IncreasedStoppedDistanceSnow", params.getInt("IncreasedStoppedDistanceSnow") * distanceConversion);
params.putIntNonBlocking("CESignalSpeed", params.getInt("CESignalSpeed") * speedConversion);
params.putIntNonBlocking("CESpeed", params.getInt("CESpeed") * speedConversion);
@ -731,6 +920,10 @@ void FrogPilotLongitudinalPanel::updateMetric(bool metric, bool bootRun) {
FrogPilotParamValueControl *offset6Toggle = static_cast<FrogPilotParamValueControl*>(toggles["Offset6"]);
FrogPilotParamValueControl *offset7Toggle = static_cast<FrogPilotParamValueControl*>(toggles["Offset7"]);
FrogPilotParamValueControl *increasedStoppedDistanceToggle = static_cast<FrogPilotParamValueControl*>(toggles["IncreasedStoppedDistance"]);
FrogPilotParamValueControl *increasedStoppedDistanceLowVisibilityToggle = static_cast<FrogPilotParamValueControl*>(toggles["IncreasedStoppedDistanceLowVisibility"]);
FrogPilotParamValueControl *increasedStoppedDistanceRainToggle = static_cast<FrogPilotParamValueControl*>(toggles["IncreasedStoppedDistanceRain"]);
FrogPilotParamValueControl *increasedStoppedDistanceRainStormToggle = static_cast<FrogPilotParamValueControl*>(toggles["IncreasedStoppedDistanceRainStorm"]);
FrogPilotParamValueControl *increasedStoppedDistanceSnowToggle = static_cast<FrogPilotParamValueControl*>(toggles["IncreasedStoppedDistanceSnow"]);
FrogPilotParamValueControl *setSpeedOffsetToggle = static_cast<FrogPilotParamValueControl*>(toggles["SetSpeedOffset"]);
if (metric) {
@ -751,6 +944,10 @@ void FrogPilotLongitudinalPanel::updateMetric(bool metric, bool bootRun) {
offset7Toggle->setDescription(tr("<b>How much to offset posted speed-limits</b> between 75 and 99 mph."));
increasedStoppedDistanceToggle->updateControl(0, 3, metricDistanceLabels);
increasedStoppedDistanceLowVisibilityToggle->updateControl(0, 3, metricDistanceLabels);
increasedStoppedDistanceRainToggle->updateControl(0, 3, metricDistanceLabels);
increasedStoppedDistanceRainStormToggle->updateControl(0, 3, metricDistanceLabels);
increasedStoppedDistanceSnowToggle->updateControl(0, 3, metricDistanceLabels);
ceSignal->updateControl(0, 150, metricSpeedLabels);
ceSpeedToggle->updateControl(0, 150, metricSpeedLabels);
@ -782,6 +979,10 @@ void FrogPilotLongitudinalPanel::updateMetric(bool metric, bool bootRun) {
offset7Toggle->setDescription(tr("<b>How much to offset posted speed-limits</b> between 75 and 99 mph."));
increasedStoppedDistanceToggle->updateControl(0, 10, imperialDistanceLabels);
increasedStoppedDistanceLowVisibilityToggle->updateControl(0, 10, imperialDistanceLabels);
increasedStoppedDistanceRainToggle->updateControl(0, 10, imperialDistanceLabels);
increasedStoppedDistanceRainStormToggle->updateControl(0, 10, imperialDistanceLabels);
increasedStoppedDistanceSnowToggle->updateControl(0, 10, imperialDistanceLabels);
ceSignal->updateControl(0, 99, imperialSpeedLabels);
ceSpeedToggle->updateControl(0, 99, imperialSpeedLabels);
@ -812,7 +1013,11 @@ void FrogPilotLongitudinalPanel::updateToggles() {
bool setVisible = parent->tuningLevel >= frogpilotToggleLevels[key].toDouble();
if (key == "CustomCruise" || key == "CustomCruiseLong" || key == "SetSpeedLimit" || key == "SetSpeedOffset") {
if (key == "CEStopLights") {
setVisible &= !toggles["CEModelStopTime"]->isVisible();
}
else if (key == "CustomCruise" || key == "CustomCruiseLong" || key == "SetSpeedLimit" || key == "SetSpeedOffset") {
setVisible &= !parent->hasPCMCruise;
}
@ -820,6 +1025,10 @@ void FrogPilotLongitudinalPanel::updateToggles() {
setVisible &= parent->isToyota;
}
else if (key == "HumanLaneChanges") {
setVisible &= parent->hasRadar;
}
else if (key == "MapGears") {
setVisible &= parent->isGM || parent->isHKGCanFd || parent->isToyota;
setVisible &= !parent->isTSK;

View File

@ -11,6 +11,7 @@ public:
signals:
void openSubPanel();
void openSubSubPanel();
void openSubSubSubPanel();
protected:
void showEvent(QShowEvent *event) override;
@ -21,17 +22,19 @@ private:
bool customPersonalityOpen;
bool forceOpenDescriptions;
bool qolOpen;
bool slcOpen;
bool weatherOpen;
std::map<QString, AbstractControl*> toggles;
QSet<QString> advancedLongitudinalTuneKeys = {"LongitudinalActuatorDelay", "MaxDesiredAcceleration", "StartAccel", "StopAccel", "StoppingDecelRate", "VEgoStarting", "VEgoStopping"};
QSet<QString> aggressivePersonalityKeys = {"AggressiveFollow", "AggressiveJerkAcceleration", "AggressiveJerkDeceleration", "AggressiveJerkDanger", "AggressiveJerkSpeed", "AggressiveJerkSpeedDecrease", "ResetAggressivePersonality"};
QSet<QString> conditionalExperimentalKeys = {"CESpeed", "CESpeedLead", "CECurves", "CELead", "CEModelStopTime", "CENavigation", "CESignalSpeed", "ShowCEMStatus"};
QSet<QString> conditionalExperimentalKeys = {"CESpeed", "CESpeedLead", "CECurves", "CELead", "CEModelStopTime", "CENavigation", "CESignalSpeed", "CEStopLights", "ShowCEMStatus"};
QSet<QString> curveSpeedKeys = {"CalibratedLateralAcceleration", "CalibrationProgress", "ResetCurveData", "ShowCSCStatus"};
QSet<QString> customDrivingPersonalityKeys = {"AggressivePersonalityProfile", "RelaxedPersonalityProfile", "StandardPersonalityProfile", "TrafficPersonalityProfile"};
QSet<QString> longitudinalTuneKeys = {"AccelerationProfile", "DecelerationProfile", "HumanAcceleration", "HumanFollowing", "LeadDetectionThreshold", "TacoTune"};
QSet<QString> qolKeys = {"CustomCruise", "CustomCruiseLong", "ForceStops", "IncreasedStoppedDistance", "MapGears", "ReverseCruise", "SetSpeedOffset"};
QSet<QString> longitudinalTuneKeys = {"AccelerationProfile", "DecelerationProfile", "HumanAcceleration", "HumanFollowing", "HumanLaneChanges", "LeadDetectionThreshold", "TacoTune"};
QSet<QString> qolKeys = {"CustomCruise", "CustomCruiseLong", "ForceStops", "IncreasedStoppedDistance", "MapGears", "ReverseCruise", "SetSpeedOffset", "WeatherPresets"};
QSet<QString> relaxedPersonalityKeys = {"RelaxedFollow", "RelaxedJerkAcceleration", "RelaxedJerkDeceleration", "RelaxedJerkDanger", "RelaxedJerkSpeed", "RelaxedJerkSpeedDecrease", "ResetRelaxedPersonality"};
QSet<QString> speedLimitControllerKeys = {"SLCOffsets", "SLCFallback", "SLCOverride", "SLCPriority", "SLCQOL", "SLCVisuals"};
QSet<QString> speedLimitControllerOffsetsKeys = {"Offset1", "Offset2", "Offset3", "Offset4", "Offset5", "Offset6", "Offset7"};
@ -39,9 +42,16 @@ private:
QSet<QString> speedLimitControllerVisualKeys = {"ShowSLCOffset", "SpeedLimitSources"};
QSet<QString> standardPersonalityKeys = {"StandardFollow", "StandardJerkAcceleration", "StandardJerkDeceleration", "StandardJerkDanger", "StandardJerkSpeed", "StandardJerkSpeedDecrease", "ResetStandardPersonality"};
QSet<QString> trafficPersonalityKeys = {"TrafficFollow", "TrafficJerkAcceleration", "TrafficJerkDeceleration", "TrafficJerkDanger", "TrafficJerkSpeed", "TrafficJerkSpeedDecrease", "ResetTrafficPersonality"};
QSet<QString> weatherKeys = {"LowVisibilityOffsets", "RainOffsets", "RainStormOffsets", "SetWeatherKey", "SnowOffsets"};
QSet<QString> weatherLowVisibilityKeys = {"IncreaseFollowingLowVisibility", "IncreasedStoppedDistanceLowVisibility", "ReduceAccelerationLowVisibility", "ReduceLateralAccelerationLowVisibility"};
QSet<QString> weatherRainKeys = {"IncreaseFollowingRain", "IncreasedStoppedDistanceRain", "ReduceAccelerationRain", "ReduceLateralAccelerationRain"};
QSet<QString> weatherRainStormKeys = {"IncreaseFollowingRainStorm", "IncreasedStoppedDistanceRainStorm", "ReduceAccelerationRainStorm", "ReduceLateralAccelerationRainStorm"};
QSet<QString> weatherSnowKeys = {"IncreaseFollowingSnow", "IncreasedStoppedDistanceSnow", "ReduceAccelerationSnow", "ReduceLateralAccelerationSnow"};
QSet<QString> parentKeys;
FrogPilotButtonsControl *weatherKeyControl;
FrogPilotParamValueControl *longitudinalActuatorDelayToggle;
FrogPilotParamValueControl *startAccelToggle;
FrogPilotParamValueControl *stopAccelToggle;
@ -60,4 +70,6 @@ private:
Params params_cache{"/cache/params"};
Params params_default{"/dev/shm/params_default"};
Params params_memory{"/dev/shm/params"};
QNetworkAccessManager *networkManager;
};

View File

@ -63,7 +63,7 @@ FrogPilotMapsPanel::FrogPilotMapsPanel(FrogPilotSettingsWindow *parent) : FrogPi
QObject::connect(removeMapsButton, &ButtonControl::clicked, [this] {
if (FrogPilotConfirmationDialog::yesorno(tr("Delete all downloaded maps?"), this)) {
std::thread([this] {
mapsSize->setText("0 MB");
mapsSize->setText(tr("0 MB"));
mapsFolderPath.removeRecursively();
}).detach();
@ -181,7 +181,7 @@ void FrogPilotMapsPanel::showEvent(QShowEvent *event) {
std::string osmDownloadProgress = params.get("OSMDownloadProgress");
if (!osmDownloadProgress.empty()) {
downloadMapsButton->setText(tr("CANCEL"));
downloadStatus->setText("Calculating...");
downloadStatus->setText(tr("Calculating..."));
downloadStatus->setVisible(true);
@ -192,7 +192,7 @@ void FrogPilotMapsPanel::showEvent(QShowEvent *event) {
updateDownloadLabels(osmDownloadProgress);
} else {
downloadMapsButton->setEnabled(!cancellingDownload && hasMapsSelected && fs.frogpilot_scene.online && parked);
downloadMapsButton->setValue(fs.frogpilot_scene.online ? (parked ? "" : "Not parked") : tr("Offline..."));
downloadMapsButton->setValue(fs.frogpilot_scene.online ? (parked ? "" : tr("Not parked")) : tr("Offline..."));
}
}
@ -209,7 +209,7 @@ void FrogPilotMapsPanel::updateState(const UIState &s, const FrogPilotUIState &f
updateDownloadLabels(osmDownloadProgress);
} else {
downloadMapsButton->setEnabled(!cancellingDownload && hasMapsSelected && fs.frogpilot_scene.online && parked);
downloadMapsButton->setValue(fs.frogpilot_scene.online ? (parked ? "" : "Not parked") : tr("Offline..."));
downloadMapsButton->setValue(fs.frogpilot_scene.online ? (parked ? "" : tr("Not parked")) : tr("Offline..."));
}
parent->keepScreenOn = !osmDownloadProgress.empty();
@ -220,10 +220,10 @@ void FrogPilotMapsPanel::cancelDownload() {
downloadMapsButton->setEnabled(false);
downloadETA->setText("Cancelling...");
downloadMapsButton->setText(tr("CANCELLED"));
downloadStatus->setText("Cancelling...");
downloadTimeElapsed->setText("Cancelling...");
downloadETA->setText(tr("Calculating..."));
downloadMapsButton->setText(tr("CANCEL"));
downloadStatus->setText(tr("Calculating..."));
downloadTimeElapsed->setText(tr("Calculating..."));
params.remove("OSMDownloadProgress");
params_memory.remove("OSMDownloadLocations");
@ -250,10 +250,10 @@ void FrogPilotMapsPanel::cancelDownload() {
}
void FrogPilotMapsPanel::startDownload() {
downloadETA->setText("Calculating...");
downloadETA->setText(tr("Calculating..."));
downloadMapsButton->setText(tr("CANCEL"));
downloadStatus->setText("Calculating...");
downloadTimeElapsed->setText("Calculating...");
downloadStatus->setText(tr("Calculating..."));
downloadTimeElapsed->setText(tr("Calculating..."));
downloadETA->setVisible(true);
downloadStatus->setVisible(true);

View File

@ -151,7 +151,7 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
downloadModelButton->setText(0, tr("CANCEL"));
downloadModelButton->setValue("Downloading...");
downloadModelButton->setValue(tr("Downloading..."));
downloadModelButton->setVisibleButton(1, false);
@ -169,7 +169,7 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
downloadModelButton->setText(1, tr("CANCEL"));
downloadModelButton->setValue("Downloading...");
downloadModelButton->setValue(tr("Downloading..."));
downloadModelButton->setVisibleButton(0, false);
@ -346,7 +346,7 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
params_memory.putBool("DownloadAllModels", true);
params_memory.put("ModelDownloadProgress", "Downloading...");
downloadModelButton->setValue("Downloading...");
downloadModelButton->setValue(tr("Downloading..."));
allModelsDownloading = true;
}
@ -422,7 +422,7 @@ void FrogPilotModelPanel::showEvent(QShowEvent *event) {
downloadModelButton->setEnabledButtons(0, !allModelsDownloaded && !allModelsDownloading && !cancellingDownload && !updatingTinygrad && fs.frogpilot_scene.online && parked);
downloadModelButton->setEnabledButtons(1, !allModelsDownloaded && !modelDownloading && !cancellingDownload && !updatingTinygrad && fs.frogpilot_scene.online && parked);
downloadModelButton->setValue(fs.frogpilot_scene.online ? (parked ? "" : "Not parked") : tr("Offline..."));
downloadModelButton->setValue(fs.frogpilot_scene.online ? (parked ? "" : tr("Not parked")) : tr("Offline..."));
updateTinygradButton->setEnabled(!modelDownloading && !cancellingDownload && fs.frogpilot_scene.online && parked && tinygradUpdate);
updateTinygradButton->setValue(tinygradUpdate ? tr("Update available!") : tr("Up to date!"));
@ -443,8 +443,26 @@ void FrogPilotModelPanel::updateState(const UIState &s, const FrogPilotUIState &
QString progress = QString::fromStdString(params_memory.get("ModelDownloadProgress"));
bool downloadFailed = progress.contains(QRegularExpression("cancelled|exists|failed|missing|offline", QRegularExpression::CaseInsensitiveOption));
if (progress != "Downloading...") {
downloadModelButton->setValue(progress);
{
QString translatedProgress;
if (progress == "Downloading...") {
translatedProgress = tr("Downloading...");
} else if (progress == "Downloaded!") {
translatedProgress = tr("Downloaded!");
} else if (progress == "All models downloaded!") {
translatedProgress = tr("All models downloaded!");
} else if (progress.contains("cancelled", Qt::CaseInsensitive)) {
translatedProgress = tr("Download cancelled...");
} else if (progress.contains("failed", Qt::CaseInsensitive)) {
translatedProgress = tr("Download failed...");
} else if (progress.contains("offline", Qt::CaseInsensitive)) {
translatedProgress = tr("GitHub and GitLab are offline...");
} else if (progress == "Repository unavailable") {
translatedProgress = tr("Repository unavailable");
} else {
translatedProgress = progress;
}
downloadModelButton->setValue(translatedProgress);
}
if (progress == "All models downloaded!" || progress == "Downloaded!" && !allModelsDownloading || downloadFailed) {
@ -473,15 +491,33 @@ void FrogPilotModelPanel::updateState(const UIState &s, const FrogPilotUIState &
});
}
} else {
downloadModelButton->setValue(fs.frogpilot_scene.online ? (parked ? "" : "Not parked") : tr("Offline..."));
downloadModelButton->setValue(fs.frogpilot_scene.online ? (parked ? "" : tr("Not parked")) : tr("Offline..."));
}
if (updatingTinygrad) {
QString progress = QString::fromStdString(params_memory.get("ModelDownloadProgress"));
bool downloadFailed = progress.contains(QRegularExpression("cancelled|exists|failed|missing|offline", QRegularExpression::CaseInsensitiveOption));
if (progress != "Downloading...") {
updateTinygradButton->setValue(progress);
{
QString translatedProgress;
if (progress == "Downloading...") {
translatedProgress = tr("Downloading...");
} else if (progress == "Downloaded!") {
translatedProgress = tr("Downloaded!");
} else if (progress == "All models downloaded!") {
translatedProgress = tr("All models downloaded!");
} else if (progress.contains("cancelled", Qt::CaseInsensitive)) {
translatedProgress = tr("Download cancelled...");
} else if (progress.contains("failed", Qt::CaseInsensitive)) {
translatedProgress = tr("Download failed...");
} else if (progress.contains("offline", Qt::CaseInsensitive)) {
translatedProgress = tr("GitHub and GitLab are offline...");
} else if (progress == "Repository unavailable") {
translatedProgress = tr("Repository unavailable");
} else {
translatedProgress = progress;
}
updateTinygradButton->setValue(translatedProgress);
}
if (progress == "Updated!" && updatingTinygrad || downloadFailed) {
@ -493,7 +529,7 @@ void FrogPilotModelPanel::updateState(const UIState &s, const FrogPilotUIState &
if (modelDownloading) {
downloadModelButton->setText(1, tr("CANCEL"));
downloadModelButton->setValue("Downloading...");
downloadModelButton->setValue(tr("Downloading..."));
downloadModelButton->setVisibleButton(0, false);
} else {

View File

@ -1,6 +1,8 @@
#include "frogpilot/ui/qt/offroad/navigation_settings.h"
FrogPilotNavigationPanel::FrogPilotNavigationPanel(FrogPilotSettingsWindow *parent) : FrogPilotListWidget(parent), parent(parent) {
networkManager = new QNetworkAccessManager(this);
QJsonObject shownDescriptions = QJsonDocument::fromJson(QString::fromStdString(params.get("ShownToggleDescriptions")).toUtf8()).object();
QString className = this->metaObject()->className();
@ -25,6 +27,9 @@ FrogPilotNavigationPanel::FrogPilotNavigationPanel(FrogPilotSettingsWindow *pare
QObject::connect(searchInput, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
amapKeyControl1->setVisible(id == 1);
amapKeyControl2->setVisible(id == 1);
publicMapboxKeyControl->setVisible(id == 0);
secretMapboxKeyControl->setVisible(id == 0);
setupButton->setVisible(id == 0);
params.putInt("SearchInput", id);
@ -36,8 +41,107 @@ FrogPilotNavigationPanel::FrogPilotNavigationPanel(FrogPilotSettingsWindow *pare
createKeyControl(amapKeyControl1, tr("Amap Key #1"), "AMapKey1", "", 39, settingsList);
createKeyControl(amapKeyControl2, tr("Amap Key #2"), "AMapKey2", "", 39, settingsList);
createKeyControl(publicMapboxKeyControl, tr("Public Mapbox Key"), "MapboxPublicKey", "pk.", 80, settingsList);
createKeyControl(secretMapboxKeyControl, tr("Secret Mapbox Key"), "MapboxSecretKey", "sk.", 80, settingsList);
publicMapboxKeyControl = new FrogPilotButtonsControl(tr("Public Mapbox Key"), tr("<b>Manage your Public Mapbox Key.</b>"), "", {tr("ADD"), tr("TEST")});
QObject::connect(publicMapboxKeyControl, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
if (id == 0) {
if (mapboxPublicKeySet) {
if (FrogPilotConfirmationDialog::yesorno(tr("Remove your Public Mapbox Key?"), this)) {
params.remove("MapboxPublicKey");
params_cache.remove("MapboxPublicKey");
updateButtons();
}
} else {
QString key = InputDialog::getText(tr("Enter your Public Mapbox Key"), this).trimmed();
if (!key.isEmpty()) {
if (!key.startsWith("pk.")) {
key = "pk." + key;
}
if (key.length() >= 80) {
params.put("MapboxPublicKey", key.toStdString());
updateButtons();
} else {
ConfirmationDialog::alert(tr("Inputted key is invalid or too short!"), this);
}
}
}
} else {
publicMapboxKeyControl->setValue(tr("Testing..."));
QString key = QString::fromStdString(params.get("MapboxPublicKey"));
QString url = QString("https://api.mapbox.com/geocoding/v5/mapbox.places/mapbox.json?access_token=%1").arg(key);
QNetworkRequest request(url);
QNetworkReply *reply = networkManager->get(request);
connect(reply, &QNetworkReply::finished, [=]() {
publicMapboxKeyControl->setValue("");
QString message;
if (reply->error() == QNetworkReply::NoError) {
message = tr("Key is valid!");
} else if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 401) {
message = tr("Key is invalid!");
} else {
message = tr("An error occurred: %1").arg(reply->errorString());
}
ConfirmationDialog::alert(message, this);
reply->deleteLater();
});
}
});
settingsList->addItem(publicMapboxKeyControl);
secretMapboxKeyControl = new FrogPilotButtonsControl(tr("Secret Mapbox Key"), tr("<b>Manage your Secret Mapbox Key.</b>"), "", {tr("ADD"), tr("TEST")});
QObject::connect(secretMapboxKeyControl, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
if (id == 0) {
if (mapboxSecretKeySet) {
if (FrogPilotConfirmationDialog::yesorno(tr("Remove your Secret Mapbox Key?"), this)) {
params.remove("MapboxSecretKey");
params_cache.remove("MapboxSecretKey");
updateButtons();
}
} else {
QString key = InputDialog::getText(tr("Enter your Secret Mapbox Key"), this).trimmed();
if (!key.isEmpty()) {
if (!key.startsWith("sk.")) {
key = "sk." + key;
}
if (key.length() >= 80) {
params.put("MapboxSecretKey", key.toStdString());
updateButtons();
} else {
ConfirmationDialog::alert(tr("Inputted key is invalid or too short!"), this);
}
}
}
} else {
secretMapboxKeyControl->setValue(tr("Testing..."));
QString key = QString::fromStdString(params.get("MapboxSecretKey"));
QString url = QString("https://api.mapbox.com/directions/v5/mapbox/driving/-73.989,40.733;-74,40.733?access_token=%1").arg(key);
QNetworkRequest request(url);
QNetworkReply *reply = networkManager->get(request);
connect(reply, &QNetworkReply::finished, [=]() {
secretMapboxKeyControl->setValue("");
QString message;
if (reply->error() == QNetworkReply::NoError) {
message = tr("Key is valid!");
} else if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 401) {
message = tr("Key is invalid!");
} else {
message = tr("An error occurred: %1").arg(reply->errorString());
}
ConfirmationDialog::alert(message, this);
reply->deleteLater();
});
}
});
settingsList->addItem(secretMapboxKeyControl);
setupButton = new ButtonControl(tr("Mapbox Setup Instructions"), tr("VIEW"), tr("<b>Instructions on how to set up Mapbox</b> for \"Primeless Navigation\"."), this);
QObject::connect(setupButton, &ButtonControl::clicked, [this]() {
@ -179,6 +283,9 @@ void FrogPilotNavigationPanel::showEvent(QShowEvent *event) {
amapKeyControl1->setVisible(selectedSearchInput == 1);
amapKeyControl2->setVisible(selectedSearchInput == 1);
publicMapboxKeyControl->setVisible(selectedSearchInput == 0);
secretMapboxKeyControl->setVisible(selectedSearchInput == 0);
setupButton->setVisible(selectedSearchInput == 0);
updateSpeedLimitsToggle->setVisibleButton(0, updatingLimits);
updateSpeedLimitsToggle->setVisibleButton(1, !updatingLimits);
@ -245,14 +352,18 @@ void FrogPilotNavigationPanel::createKeyControl(ButtonControl *&control, const Q
}
void FrogPilotNavigationPanel::updateButtons() {
FrogPilotUIState &fs = *frogpilotUIState();
amapKeyControl1->setText(params.get("AMapKey1").empty() ? tr("ADD") : tr("REMOVE"));
amapKeyControl2->setText(params.get("AMapKey2").empty() ? tr("ADD") : tr("REMOVE"));
mapboxPublicKeySet = QString::fromStdString(params.get("MapboxPublicKey")).startsWith("pk");
mapboxSecretKeySet = QString::fromStdString(params.get("MapboxSecretKey")).startsWith("sk");
publicMapboxKeyControl->setText(mapboxPublicKeySet ? tr("REMOVE") : tr("ADD"));
secretMapboxKeyControl->setText(mapboxSecretKeySet ? tr("REMOVE") : tr("ADD"));
publicMapboxKeyControl->setText(0, mapboxPublicKeySet ? tr("REMOVE") : tr("ADD"));
publicMapboxKeyControl->setVisibleButton(1, mapboxPublicKeySet && fs.frogpilot_scene.online);
secretMapboxKeyControl->setText(0, mapboxSecretKeySet ? tr("REMOVE") : tr("ADD"));
secretMapboxKeyControl->setVisibleButton(1, mapboxSecretKeySet && fs.frogpilot_scene.online);
}
void FrogPilotNavigationPanel::updateState(const UIState &s, const FrogPilotUIState &fs) {

View File

@ -31,8 +31,8 @@ private:
ButtonControl *amapKeyControl1;
ButtonControl *amapKeyControl2;
ButtonControl *publicMapboxKeyControl;
ButtonControl *secretMapboxKeyControl;
FrogPilotButtonsControl *publicMapboxKeyControl;
FrogPilotButtonsControl *secretMapboxKeyControl;
ButtonControl *setupButton;
FrogPilotButtonControl *updateSpeedLimitsToggle;
@ -49,5 +49,7 @@ private:
QLabel *imageLabel;
QNetworkAccessManager *networkManager;
QStackedLayout *primelessLayout;
};

View File

@ -288,7 +288,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr
downloadThemeAsset(colorSchemeToDownload, "ColorToDownload", "DownloadableColors", params, params_memory);
downloadStatusLabel->setText("Downloading...");
downloadStatusLabel->setText(tr("Downloading..."));
}
}
} else if (id == 2) {
@ -340,7 +340,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr
downloadThemeAsset(distanceIconPackToDownload, "DistanceIconToDownload", "DownloadableDistanceIcons", params, params_memory);
downloadStatusLabel->setText("Downloading...");
downloadStatusLabel->setText(tr("Downloading..."));
}
}
} else if (id == 2) {
@ -392,7 +392,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr
downloadThemeAsset(iconPackToDownload, "IconToDownload", "DownloadableIcons", params, params_memory);
downloadStatusLabel->setText("Downloading...");
downloadStatusLabel->setText(tr("Downloading..."));
}
}
} else if (id == 2) {
@ -444,7 +444,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr
downloadThemeAsset(signalAnimationToDownload, "SignalToDownload", "DownloadableSignals", params, params_memory);
downloadStatusLabel->setText("Downloading...");
downloadStatusLabel->setText(tr("Downloading..."));
}
}
} else if (id == 2) {
@ -496,7 +496,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr
downloadThemeAsset(soundPackToDownload, "SoundToDownload", "DownloadableSounds", params, params_memory);
downloadStatusLabel->setText("Downloading...");
downloadStatusLabel->setText(tr("Downloading..."));
}
}
} else if (id == 2) {
@ -548,7 +548,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr
downloadThemeAsset(wheelToDownload, "WheelToDownload", "DownloadableWheels", params, params_memory);
downloadStatusLabel->setText("Downloading...");
downloadStatusLabel->setText(tr("Downloading..."));
}
}
} else if (id == 2) {
@ -566,7 +566,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr
manageWheelIconsButton->setValue(getThemeName(param.toStdString(), params));
themeToggle = manageWheelIconsButton;
} else if (param == "DownloadStatusLabel") {
downloadStatusLabel = new LabelControl(title, "Idle");
downloadStatusLabel = new LabelControl(title, tr("Idle"));
themeToggle = downloadStatusLabel;
} else if (param == "StartupAlert") {
FrogPilotButtonsControl *startupAlertButton = new FrogPilotButtonsControl(title, desc, icon, {tr("STOCK"), tr("FROGPILOT"), tr("CUSTOM"), tr("CLEAR")}, true);
@ -748,7 +748,15 @@ void FrogPilotThemesPanel::updateState(const UIState &s, const FrogPilotUIState
bool downloadFailed = progress.contains(QRegularExpression("cancelled|exists|failed|offline", QRegularExpression::CaseInsensitiveOption));
if (progress != "Downloading...") {
downloadStatusLabel->setText(progress);
static const QMap<QString, QString> progressTranslations = {
{"Unpacking theme...", tr("Unpacking theme...")},
{"Downloaded!", tr("Downloaded!")},
{"Download cancelled...", tr("Download cancelled...")},
{"Download failed...", tr("Download failed...")},
{"Repository unavailable", tr("Repository unavailable")},
{"GitHub and GitLab are offline...", tr("GitHub and GitLab are offline...")}
};
downloadStatusLabel->setText(progressTranslations.value(progress, tr("Idle")));
}
if (progress == "Downloaded!" || downloadFailed) {
@ -774,7 +782,7 @@ void FrogPilotThemesPanel::updateState(const UIState &s, const FrogPilotUIState
params_memory.remove("CancelThemeDownload");
params_memory.remove("ThemeDownloadProgress");
downloadStatusLabel->setText("Idle");
downloadStatusLabel->setText(tr("Idle"));
});
}
}

View File

@ -150,16 +150,22 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
FrogPilotListWidget *gmList = new FrogPilotListWidget(this);
FrogPilotListWidget *hkgList = new FrogPilotListWidget(this);
FrogPilotListWidget *hondaList = new FrogPilotListWidget(this);
FrogPilotListWidget *subaruList = new FrogPilotListWidget(this);
FrogPilotListWidget *toyotaList = new FrogPilotListWidget(this);
FrogPilotListWidget *vehicleInfoList = new FrogPilotListWidget(this);
ScrollView *gmPanel = new ScrollView(gmList, this);
ScrollView *hkgPanel = new ScrollView(hkgList, this);
ScrollView *hondaPanel = new ScrollView(hondaList, this);
ScrollView *subaruPanel = new ScrollView(subaruList, this);
ScrollView *toyotaPanel = new ScrollView(toyotaList, this);
ScrollView *vehicleInfoPanel = new ScrollView(vehicleInfoList, this);
vehiclesLayout->addWidget(gmPanel);
vehiclesLayout->addWidget(hkgPanel);
vehiclesLayout->addWidget(hondaPanel);
vehiclesLayout->addWidget(subaruPanel);
vehiclesLayout->addWidget(toyotaPanel);
vehiclesLayout->addWidget(vehicleInfoPanel);
@ -173,6 +179,14 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
{"NewLongAPI", tr("comma's New Longitudinal API"), tr("<b>comma's new gas and brake control system</b> that improves acceleration and braking but may cause issues on some Genesis/Hyundai/Kia vehicles."), ""},
{"TacoTuneHacks", tr("\"Taco Bell Run\" Torque Hack"), tr("<b>The steering torque hack from comma's 2022 \"Taco Bell Run\".</b> Designed to increase steering torque at low speeds for left and right turns."), ""},
{"HondaToggles", tr("Acura/Honda Settings"), tr("<b>FrogPilot features for Acura and Honda vehicles.</b>"), ""},
{"HondaAltTune", tr("Gentle Following"), tr("<b>Reduces jerky acceleration and braking when following a lead vehicle.</b> Ideal for stop-and-go traffic."), ""},
{"HondaMaxBrake", tr("Increased Braking Force"), tr("<b>Increases the maximum braking force for improved stopping performance.</b>"), ""},
{"HondaLowSpeedPedal", tr("Responsive Pedal at Low Speeds"), tr("<b>Improves acceleration from a standstill for a more responsive throttle feel in city driving.</b>"), ""},
{"SubaruToggles", tr("Subaru Settings"), tr("<b>FrogPilot features for Subaru vehicles.</b>"), ""},
{"SubaruSNG", tr("Stop and Go"), tr("Stop and go for supported Subaru vehicles."), ""},
{"ToyotaToggles", tr("Toyota/Lexus Settings"), tr("<b>FrogPilot features for Lexus and Toyota vehicles.</b>"), ""},
{"ToyotaDoors", tr("Automatically Lock/Unlock Doors"), tr("<b>Automatically lock/unlock doors</b> when shifting in and out of drive."), ""},
{"ClusterOffset", tr("Dashboard Speed Offset"), tr("<b>The speed offset openpilot uses to match the speed on the dashboard display.</b>"), ""},
@ -209,6 +223,22 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
});
vehicleToggle = hkgButton;
} else if (param == "HondaToggles") {
ButtonControl *hondaButton = new ButtonControl(title, tr("MANAGE"), desc);
QObject::connect(hondaButton, &ButtonControl::clicked, [vehiclesLayout, hondaPanel, this]() {
openDescriptions(forceOpenDescriptions, toggles);
vehiclesLayout->setCurrentWidget(hondaPanel);
});
vehicleToggle = hondaButton;
} else if (param == "SubaruToggles") {
ButtonControl *subaruButton = new ButtonControl(title, tr("MANAGE"), desc);
QObject::connect(subaruButton, &ButtonControl::clicked, [vehiclesLayout, subaruPanel, this]() {
openDescriptions(forceOpenDescriptions, toggles);
vehiclesLayout->setCurrentWidget(subaruPanel);
});
vehicleToggle = subaruButton;
} else if (param == "ToyotaToggles") {
ButtonControl *toyotaButton = new ButtonControl(title, tr("MANAGE"), desc);
QObject::connect(toyotaButton, &ButtonControl::clicked, [vehiclesLayout, toyotaPanel, this]() {
@ -255,6 +285,10 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
gmList->addItem(vehicleToggle);
} else if (hkgKeys.contains(param)) {
hkgList->addItem(vehicleToggle);
} else if (hondaKeys.contains(param)) {
hondaList->addItem(vehicleToggle);
} else if (subaruKeys.contains(param)) {
subaruList->addItem(vehicleToggle);
} else if (toyotaKeys.contains(param)) {
toyotaList->addItem(vehicleToggle);
} else if (vehicleInfoKeys.contains(param)) {
@ -279,11 +313,11 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
static_cast<FrogPilotParamValueControl*>(toggles["LockDoorsTimer"])->setWarning("<b>Warning:</b> openpilot can't detect if keys are still inside the car, so ensure you have a spare key to prevent accidental lockouts!");
QSet<QString> rebootKeys = {"NewLongAPI", "TacoTuneHacks"};
QSet<QString> rebootKeys = {"HondaAltTune", "NewLongAPI", "TacoTuneHacks"};
for (const QString &key : rebootKeys) {
QObject::connect(static_cast<ToggleControl*>(toggles[key]), &ToggleControl::toggleFlipped, [key, this](bool state) {
if (started) {
if (key == "TacoTuneHacks" && state) {
if (key == "HondaAltTune" || key == "TacoTuneHacks" && state) {
if (FrogPilotConfirmationDialog::toggleReboot(this)) {
Hardware::reboot();
}
@ -367,6 +401,10 @@ void FrogPilotVehiclesPanel::updateToggles() {
setVisible &= parent->isGM;
} else if (hkgKeys.contains(key)) {
setVisible &= parent->isHKG;
} else if (hondaKeys.contains(key)) {
setVisible &= parent->isHonda;
} else if (subaruKeys.contains(key)) {
setVisible &= parent->isSubaru;
} else if (toyotaKeys.contains(key)) {
setVisible &= parent->isToyota;
} else if (vehicleInfoKeys.contains(key)) {
@ -377,7 +415,19 @@ void FrogPilotVehiclesPanel::updateToggles() {
setVisible &= parent->hasOpenpilotLongitudinal;
}
if (key == "LockDoorsTimer") {
if (key == "HondaAltTune") {
setVisible &= parent->isHondaNidec;
}
else if (key == "HondaLowSpeedPedal") {
setVisible &= parent->hasPedal;
}
else if (key == "HondaMaxBrake") {
setVisible &= parent->isHondaNidec;
}
else if (key == "LockDoorsTimer") {
setVisible &= !parent->isC3;
}
@ -385,6 +435,10 @@ void FrogPilotVehiclesPanel::updateToggles() {
setVisible &= !parent->hasPedal && !parent->hasSNG;
}
else if (key == "SubaruSNG") {
setVisible &= parent->hasSNG;
}
else if (key == "TacoTuneHacks") {
setVisible &= parent->isHKGCanFd;
}
@ -400,6 +454,10 @@ void FrogPilotVehiclesPanel::updateToggles() {
toggles["GMToggles"]->setVisible(true);
} else if (hkgKeys.contains(key)) {
toggles["HKGToggles"]->setVisible(true);
} else if (hondaKeys.contains(key)) {
toggles["HondaToggles"]->setVisible(true);
} else if (subaruKeys.contains(key)) {
toggles["SubaruToggles"]->setVisible(true);
} else if (toyotaKeys.contains(key)) {
toggles["ToyotaToggles"]->setVisible(true);
} else if (vehicleInfoKeys.contains(key)) {

View File

@ -25,7 +25,9 @@ private:
QSet<QString> gmKeys = {"ExperimentalGMTune", "LongPitch", "VoltSNG"};
QSet<QString> hkgKeys = {"NewLongAPI", "TacoTuneHacks"};
QSet<QString> longitudinalKeys = {"ExperimentalGMTune", "FrogsGoMoosTweak", "LongPitch", "NewLongAPI", "SNGHack", "VoltSNG"};
QSet<QString> hondaKeys = {"HondaAltTune", "HondaLowSpeedPedal", "HondaMaxBrake"};
QSet<QString> longitudinalKeys = {"ExperimentalGMTune", "FrogsGoMoosTweak", "HondaAltTune", "HondaMaxBrake", "HondaLowSpeedPedal", "LongPitch", "NewLongAPI", "SNGHack", "SubaruSNG", "VoltSNG"};
QSet<QString> subaruKeys = {"SubaruSNG"};
QSet<QString> toyotaKeys = {"ClusterOffset", "FrogsGoMoosTweak", "LockDoorsTimer", "SNGHack", "ToyotaDoors"};
QSet<QString> vehicleInfoKeys = {"BlindSpotSupport", "HardwareDetected", "OpenpilotLongitudinal", "PedalSupport", "RadarSupport", "SDSUSupport", "SNGSupport"};

View File

@ -24,6 +24,10 @@ FrogPilotAnnotatedCameraWidget::FrogPilotAnnotatedCameraWidget(QWidget *parent)
loadGif("../../frogpilot/assets/other_images/turn_icon.gif", cemTurnIcon, QSize(btn_size / 2, btn_size / 2), this);
loadGif("../../frogpilot/assets/other_images/chill_mode_icon.gif", chillModeIcon, QSize(btn_size / 2, btn_size / 2), this);
loadGif("../../frogpilot/assets/other_images/experimental_mode_icon.gif", experimentalModeIcon, QSize(btn_size / 2, btn_size / 2), this);
loadGif("../../frogpilot/assets/other_images/weather_clear_day.gif", weatherClearDay, QSize(btn_size / 2, btn_size / 2), this);
loadGif("../../frogpilot/assets/other_images/weather_clear_night.gif", weatherClearNight, QSize(btn_size / 2, btn_size / 2), this);
loadGif("../../frogpilot/assets/other_images/weather_rain.gif", weatherRain, QSize(btn_size / 2, btn_size / 2), this);
loadGif("../../frogpilot/assets/other_images/weather_snow.gif", weatherSnow, QSize(btn_size / 2, btn_size / 2), this);
QObject::connect(animationTimer, &QTimer::timeout, [this] {
animationFrameIndex = (animationFrameIndex + 1) % totalFrames;
@ -252,6 +256,10 @@ void FrogPilotAnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p, UIState
} else if (animationTimer->isActive()) {
animationTimer->stop();
}
if (!frogpilot_scene.map_open && !hideBottomIcons) {
paintWeather(p, frogpilotPlan, frogpilot_scene);
}
}
void FrogPilotAnnotatedCameraWidget::paintAdjacentPaths(QPainter &p, const cereal::CarState::Reader &carState, const FrogPilotUIScene &frogpilot_scene, const QJsonObject &frogpilot_toggles) {
@ -540,11 +548,11 @@ void FrogPilotAnnotatedCameraWidget::paintLeadMetrics(QPainter &p, bool adjacent
text = QString("%1 %2 (%3) | %4 %5 | %6 %7")
.arg(qRound(leadDistance * distanceConversion))
.arg(leadDistanceUnit)
.arg(QString("Desired: %1").arg(frogpilotPlan.getDesiredFollowDistance() * distanceConversion))
.arg(QString(tr("Desired: %1")).arg(frogpilotPlan.getDesiredFollowDistance() * distanceConversion))
.arg(qRound(leadSpeed * speedConversionMetrics))
.arg(leadSpeedUnit)
.arg(QString::number(leadDistance / std::max(speed / speedConversion, 1.0f), 'f', 2))
.arg("s");
.arg(tr("s"));
}
QFontMetrics metrics(p.font());
@ -899,7 +907,7 @@ void FrogPilotAnnotatedCameraWidget::paintStandstillTimer(QPainter &p) {
p.setFont(InterFont(176, QFont::Bold));
{
QString minuteStr = (minutes == 1) ? "1 minute" : QString("%1 minutes").arg(minutes);
QString minuteStr = (minutes == 1) ? tr("1 minute") : QString(tr("%1 minutes")).arg(minutes);
QRect textRect = p.fontMetrics().boundingRect(minuteStr);
textRect.moveCenter({rect().center().x(), 210 - textRect.height() / 2});
p.setPen(QPen(blendedColor));
@ -908,7 +916,7 @@ void FrogPilotAnnotatedCameraWidget::paintStandstillTimer(QPainter &p) {
p.setFont(InterFont(66));
{
QString secondStr = (seconds == 1) ? "1 second" : QString("%1 seconds").arg(seconds);
QString secondStr = (seconds == 1) ? tr("1 second") : QString(tr("%1 seconds")).arg(seconds);
QRect textRect = p.fontMetrics().boundingRect(secondStr);
textRect.moveCenter({rect().center().x(), 290 - textRect.height() / 2});
p.setPen(QPen(whiteColor()));
@ -972,3 +980,47 @@ void FrogPilotAnnotatedCameraWidget::paintTurnSignals(QPainter &p, const cereal:
p.restore();
}
void FrogPilotAnnotatedCameraWidget::paintWeather(QPainter &p, const cereal::FrogPilotPlan::Reader &frogpilotPlan, FrogPilotUIScene &frogpilot_scene) {
int weatherId = frogpilotPlan.getWeatherId();
if (weatherId == 0) {
return;
}
p.save();
QPoint weatherIconPosition;
if (compassPosition != QPoint(0, 0)) {
weatherIconPosition = compassPosition;
weatherIconPosition.rx() += (rightHandDM ? UI_BORDER_SIZE + widget_size + UI_BORDER_SIZE : -UI_BORDER_SIZE - widget_size - UI_BORDER_SIZE) / (frogpilot_scene.map_open ? 1.25 : 1);
} else {
weatherIconPosition.rx() = rightHandDM ? UI_BORDER_SIZE + widget_size / 2 : width() - UI_BORDER_SIZE - btn_size;
if (mapButtonVisible) {
if (rightHandDM) {
weatherIconPosition.rx() += btn_size - UI_BORDER_SIZE;
} else {
weatherIconPosition.rx() -= btn_size + UI_BORDER_SIZE;
}
}
weatherIconPosition.ry() = dmIconPosition.y() - widget_size / 2;
}
QRect weatherRect(weatherIconPosition, QSize(widget_size, widget_size));
p.setBrush(blackColor(166));
p.setPen(QPen(blackColor(), 10));
p.drawRoundedRect(weatherRect, 24, 24);
QSharedPointer<QMovie> icon = weatherClearDay;
if ((weatherId >= 200 && weatherId <= 232) || (weatherId >= 300 && weatherId <= 321) || (weatherId >= 500 && weatherId <= 531)) {
icon = weatherRain;
} else if (weatherId >= 600 && weatherId <= 622) {
icon = weatherSnow;
} else if (weatherId == 800) {
icon = frogpilotPlan.getWeatherDaytime() ? weatherClearDay : weatherClearNight;
}
p.drawPixmap(weatherRect, icon->currentPixmap());
p.restore();
}

View File

@ -79,6 +79,7 @@ private:
void paintStandstillTimer(QPainter &p);
void paintStoppingPoint(QPainter &p, UIScene &scene, FrogPilotUIScene &frogpilot_scene, QJsonObject &frogpilot_toggles);
void paintTurnSignals(QPainter &p, const cereal::CarState::Reader &carState);
void paintWeather(QPainter &p, const cereal::FrogPilotPlan::Reader &frogpilotPlan, FrogPilotUIScene &frogpilot_scene);
void updateSignals();
int animationFrameIndex;
@ -123,6 +124,10 @@ private:
QSharedPointer<QMovie> cemTurnIcon;
QSharedPointer<QMovie> chillModeIcon;
QSharedPointer<QMovie> experimentalModeIcon;
QSharedPointer<QMovie> weatherClearDay;
QSharedPointer<QMovie> weatherClearNight;
QSharedPointer<QMovie> weatherRain;
QSharedPointer<QMovie> weatherSnow;
QString cscSpeedStr;

View File

@ -92,7 +92,7 @@ void FrogPilotOnroadWindow::paintFPS(QPainter &p, const QRect &rect) {
minFPS = std::min(minFPS, fps);
maxFPS = std::max(maxFPS, fps);
QString fpsDisplayString = QString("FPS: %1 | Min: %2 | Max: %3 | Avg: %4")
QString fpsDisplayString = QString(tr("FPS: %1 | Min: %2 | Max: %3 | Avg: %4"))
.arg(qRound(fps))
.arg(qRound(minFPS))
.arg(qRound(maxFPS))

View File

@ -15,8 +15,8 @@ DriveStats::DriveStats(QWidget *parent) : QFrame(parent) {
QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout->setContentsMargins(50, 25, 50, 20);
addStatsLayouts(tr(konik ? "ALL TIME (KONIK)" : "ALL TIME"), all);
addStatsLayouts(tr(konik ? "PAST WEEK (KONIK)" : "PAST WEEK"), week);
addStatsLayouts(konik ? tr("ALL TIME (KONIK)") : tr("ALL TIME"), all);
addStatsLayouts(konik ? tr("PAST WEEK (KONIK)") : tr("PAST WEEK"), week);
addStatsLayouts(tr("FROGPILOT"), frogPilot, true);
std::optional<QString> dongleId = getDongleId();

View File

@ -0,0 +1,225 @@
#include "selfdrive/ui/qt/widgets/scrollview.h"
#include "frogpilot/ui/qt/widgets/drive_summary.h"
FrogPilotDriveSummary::FrogPilotDriveSummary(QWidget *parent, bool randomEvents) : QFrame(parent), displayRandomEvents(randomEvents) {
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(20, 20, 20, 20);
mainLayout->setSpacing(15);
titleLabel = new QLabel(randomEvents ? tr("Random Events Summary") : tr("Drive Summary"), this);
titleLabel->setAlignment(Qt::AlignCenter);
titleLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
titleLabel->setStyleSheet(R"(
QLabel {
background-color: #444444;
border-radius: 12px;
color: #FFFFFF;
font-size: 50px;
font-weight: bold;
padding: 12px 28px;
}
)");
titleLabel->setMaximumHeight(titleLabel->sizeHint().height());
mainLayout->addWidget(titleLabel);
mainLayout->addSpacing(10);
QWidget *containerWidget = new QWidget(this);
QVBoxLayout *listLayout = new QVBoxLayout(containerWidget);
listLayout->setAlignment(Qt::AlignTop);
listLayout->setSpacing(20);
if (displayRandomEvents) {
randomEventsMap.insert("accel30", tr("UwUs"));
randomEventsMap.insert("accel35", tr("Loch Ness Encounters"));
randomEventsMap.insert("accel40", tr("Visits to 1955"));
randomEventsMap.insert("dejaVuCurve", tr("Deja Vu Moments"));
randomEventsMap.insert("firefoxSteerSaturated", tr("Internet Explorer Weeeeeeees"));
randomEventsMap.insert("hal9000", tr("HAL 9000 Denials"));
randomEventsMap.insert("openpilotCrashedRandomEvent", tr("openpilot Crashes"));
randomEventsMap.insert("thisIsFineSteerSaturated", tr("This Is Fine Moments"));
randomEventsMap.insert("toBeContinued", tr("To Be Continued Moments"));
randomEventsMap.insert("vCruise69", tr("Noices"));
randomEventsMap.insert("yourFrogTriedToKillMe", tr("Attempted Frog Murders"));
randomEventsMap.insert("youveGotMail", tr("Total Mail Received"));
} else {
listLayout->addWidget(createStatBox(tr("% of Drive With openpilot Engaged"), &engagementValue, this));
listLayout->addWidget(createStatBox(tr("Drive Distance"), &frogPilotMetersValue, this));
listLayout->addWidget(createStatBox(tr("Drive Time"), &trackedTimeValue, this));
listLayout->addWidget(createStatBox(tr("% of Drive In \"Experimental Mode\""), &experimentalModeTimeValue, this));
}
if (displayRandomEvents) {
eventsListLayout = listLayout;
mainLayout->addWidget(new ScrollView(containerWidget, this), 1);
} else {
mainLayout->addWidget(containerWidget, 1);
}
setLayout(mainLayout);
setStyleSheet(R"(
QFrame {
background-color: #333333;
}
)");
QObject::connect(device(), &Device::interactiveTimeout, [this]() {
emit panelClosed();
});
QObject::connect(uiState(), &UIState::offroadTransition, [this](bool offroad) {
if (!offroad) {
previousStats = QJsonDocument::fromJson(QString::fromStdString(params.get("FrogPilotStats")).toUtf8()).object();
}
});
}
void FrogPilotDriveSummary::mousePressEvent(QMouseEvent *e) {
emit panelClosed();
}
void FrogPilotDriveSummary::showEvent(QShowEvent *event) {
bool isMetric = params.getBool("IsMetric");
QJsonObject currentStats = QJsonDocument::fromJson(QString::fromStdString(params.get("FrogPilotStats")).toUtf8()).object();
if (displayRandomEvents) {
QJsonObject currentRandomEvents = currentStats.value("RandomEvents").toObject();
QJsonObject previousRandomEvents = previousStats.value("RandomEvents").toObject();
QList<QPair<QString, int>> eventsList;
for (QMap<QString, QString>::const_iterator it = randomEventsMap.constBegin(); it != randomEventsMap.constEnd(); ++it) {
int currentValue = currentRandomEvents.value(it.key()).toInt();
int previousValue = previousRandomEvents.value(it.key()).toInt();
int diffValue = currentValue - previousValue;
if (diffValue > 0) {
eventsList.append(qMakePair(it.value(), diffValue));
}
}
std::sort(eventsList.begin(), eventsList.end(), [](const QPair<QString, int> &a, const QPair<QString, int> &b) {
if (a.second != b.second) {
return a.second > b.second;
} else {
return a.first.localeAwareCompare(b.first) < 0;
}
});
QLayoutItem *child;
while ((child = eventsListLayout->takeAt(0)) != nullptr) {
if (QWidget *widget = child->widget()) {
widget->deleteLater();
}
delete child;
}
randomEventLabels.clear();
if (eventsList.isEmpty()) {
eventsListLayout->setAlignment(Qt::AlignCenter);
QLabel *noEventsLabel = new QLabel(tr("No Random Events Played!"), this);
noEventsLabel->setAlignment(Qt::AlignCenter);
noEventsLabel->setStyleSheet(R"(
QLabel {
font-size: 50px;
font-weight: bold;
color: #FFFFFF;
}
)");
eventsListLayout->addWidget(noEventsLabel);
} else {
eventsListLayout->setAlignment(Qt::AlignTop);
for (QList<QPair<QString, int>>::const_iterator it = eventsList.constBegin(); it != eventsList.constEnd(); ++it) {
QLabel *valueLabel = nullptr;
eventsListLayout->addWidget(createStatBox(it->first, &valueLabel, this));
randomEventLabels.insert(it->first, valueLabel);
valueLabel->setText(QLocale().toString(it->second));
}
}
} else {
std::function<double(const QString &)> diffDouble = [currentStats, this](const QString &key) -> double {
return currentStats.value(key).toDouble() - previousStats.value(key).toDouble();
};
std::function<QString(double)> formatDistance = [&](double meters) {
double value;
QString unit;
if (isMetric) {
value = meters / 1000.0;
unit = (qRound(value) == 1) ? tr(" kilometer") : tr(" kilometers");
} else {
value = meters * METER_TO_MILE;
unit = (qRound(value) == 1) ? tr(" mile") : tr(" miles");
}
return QLocale().toString(qRound(value)) + unit;
};
std::function<QString(int)> formatTime = [&](int seconds) {
static int secondsInDay = 60 * 60 * 24;
static int secondsInHour = 60 * 60;
int days = seconds / secondsInDay;
int hours = (seconds % secondsInDay) / secondsInHour;
int minutes = (seconds % secondsInHour) / 60;
QString result;
if (days > 0) result += QLocale().toString(days) + (days == 1 ? tr(" day ") : tr(" days "));
if (hours > 0 || days > 0) result += QLocale().toString(hours) + (hours == 1 ? tr(" hour ") : tr(" hours "));
result += QLocale().toString(minutes) + (minutes == 1 ? tr(" minute") : tr(" minutes"));
return result.trimmed();
};
int engagedTime = diffDouble("AOLTime") + diffDouble("LongitudinalTime");
int experimentalTime = diffDouble("ExperimentalModeTime");
int trackedTime = diffDouble("TrackedTime");
engagementValue->setText(QLocale().toString((trackedTime > 0) ? (engagedTime * 100 / trackedTime) : 0) + "%");
experimentalModeTimeValue->setText(QLocale().toString((trackedTime > 0) ? (experimentalTime * 100 / trackedTime) : 0) + "%");
frogPilotMetersValue->setText(formatDistance(diffDouble("FrogPilotMeters")));
trackedTimeValue->setText(formatTime(trackedTime));
}
}
void FrogPilotDriveSummary::hideEvent(QHideEvent *event) {
emit panelClosed();
}
QWidget *FrogPilotDriveSummary::createStatBox(const QString &title, QLabel **valueLabel, QWidget *parent) {
QWidget *box = new QWidget(parent);
QVBoxLayout *layout = new QVBoxLayout(box);
layout->setAlignment(Qt::AlignCenter);
layout->setContentsMargins(10, 10, 10, 10);
layout->setSpacing(8);
QLabel *statTitleLabel = new QLabel(title, box);
statTitleLabel->setAlignment(Qt::AlignCenter);
statTitleLabel->setStyleSheet(R"(
QLabel {
color: #AAAAAA;
font-size: 40px;
font-weight: bold;
}
)");
QLabel *value = new QLabel("-", box);
value->setAlignment(Qt::AlignCenter);
value->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
value->setStyleSheet(R"(
QLabel {
color: #FFFFFF;
font-size: 75px;
font-weight: bold;
}
)");
*valueLabel = value;
layout->addWidget(statTitleLabel);
layout->addWidget(value);
return box;
}

View File

@ -0,0 +1,39 @@
#pragma once
#include "selfdrive/ui/ui.h"
class FrogPilotDriveSummary : public QFrame {
Q_OBJECT
public:
explicit FrogPilotDriveSummary(QWidget *parent = nullptr, bool random_events = false);
signals:
void panelClosed();
protected:
void showEvent(QShowEvent *event) override;
void hideEvent(QHideEvent *event) override;
void mousePressEvent(QMouseEvent *e);
private:
QWidget *createStatBox(const QString &title, QLabel **valueLabel, QWidget *parent);
bool displayRandomEvents;
Params params;
QJsonObject previousStats;
QLabel *experimentalModeTimeValue;
QLabel *frogPilotMetersValue;
QLabel *engagementValue;
QLabel *titleLabel;
QLabel *trackedTimeValue;
QMap<QString, QLabel*> randomEventLabels;
QMap<QString, QString> randomEventsMap;
QVBoxLayout *eventsListLayout;
};

View File

@ -130,7 +130,7 @@ inline QString calculateDirectorySize(const QDir &directory) {
constexpr double GB = 1024.0 * MB;
if (!directory.exists()) {
return QStringLiteral("0 MB");
return QObject::tr("0 MB");
}
double totalSize = 0;
@ -141,9 +141,9 @@ inline QString calculateDirectorySize(const QDir &directory) {
}
if (totalSize >= GB) {
return QString::number(totalSize / GB, 'f', 2) + QStringLiteral(" GB");
return QString::number(totalSize / GB, 'f', 2) + QObject::tr(" GB");
}
return QString::number(totalSize / MB, 'f', 2) + QStringLiteral(" MB");
return QString::number(totalSize / MB, 'f', 2) + QObject::tr(" MB");
}
inline QString daySuffix(int day) {
@ -166,12 +166,12 @@ inline QString formatElapsedTime(float elapsedMilliseconds) {
QString formattedTime;
if (hours > 0) {
formattedTime += QString::number(hours) + (hours == 1 ? " hour " : " hours ");
formattedTime += QString::number(hours) + (hours == 1 ? QObject::tr(" hour ") : QObject::tr(" hours "));
}
if (minutes > 0) {
formattedTime += QString::number(minutes) + (minutes == 1 ? " minute " : " minutes ");
formattedTime += QString::number(minutes) + (minutes == 1 ? QObject::tr(" minute ") : QObject::tr(" minutes "));
}
formattedTime += QString::number(seconds) + (seconds == 1 ? " second" : " seconds");
formattedTime += QString::number(seconds) + (seconds == 1 ? QObject::tr(" second") : QObject::tr(" seconds"));
return formattedTime.trimmed();
}

Some files were not shown because too many files have changed in this diff Show More