Compare commits

..

1 Commits

Author SHA1 Message Date
hiimisaac 0a7f933346 Bumping opendbc 2025-05-02 18:30:22 -04:00
127 changed files with 1151 additions and 3245 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ simulation:
ui:
- changed-files:
- any-glob-to-all-files: '{selfdrive/ui/**,system/ui/**}'
- any-glob-to-all-files: 'selfdrive/ui/**'
tools:
- changed-files:
+1 -1
View File
@@ -12,7 +12,7 @@ inputs:
required: true
save:
description: 'whether to save the cache'
default: 'true'
default: 'false'
required: false
outputs:
cache-hit:
+2
View File
@@ -47,8 +47,10 @@ selfdrive/pandad/pandad
cereal/services.h
cereal/gen
cereal/messaging/bridge
selfdrive/logcatd/logcatd
selfdrive/mapd/default_speeds_by_region.json
system/proclogd/proclogd
selfdrive/ui/translations/alerts_generated.h
selfdrive/ui/translations/tmp
selfdrive/test/longitudinal_maneuvers/out
selfdrive/car/tests/cars_dump
+1
View File
@@ -4,6 +4,7 @@
[submodule "opendbc"]
path = opendbc_repo
url = https://github.com/sunnypilot/opendbc.git
branch = "q4-radar"
[submodule "msgq"]
path = msgq_repo
url = https://github.com/sunnypilot/msgq.git
-1
View File
@@ -4,7 +4,6 @@ Version 0.9.9 (2025-05-15)
* New training architecture supervised by MLSIM
* Steering actuator delay is now learned online
* Tesla Model 3 and Y support thanks to lukasloetkolben!
* Lexus RC 2023 support thanks to nelsonjchen!
* Coming soon
* New Honda models
* Bigger vision model
+14 -21
View File
@@ -39,6 +39,20 @@ struct ModelManagerSP @0xaedffd8f31e7b55d {
sha256 @1 :Text;
}
enum Type {
drive @0;
navigation @1;
metadata @2;
}
struct Model {
fullName @0 :Text;
fileName @1 :Text;
downloadUri @2 :DownloadUri;
downloadProgress @3 :DownloadProgress;
type @4 :Type;
}
enum DownloadStatus {
notDownloading @0;
downloading @1;
@@ -53,25 +67,6 @@ struct ModelManagerSP @0xaedffd8f31e7b55d {
eta @2 :UInt32;
}
struct Artifact {
fileName @0 :Text;
downloadUri @1 :DownloadUri;
downloadProgress @2 :DownloadProgress;
}
struct Model {
type @0 :Type;
artifact @1 :Artifact; # Main artifact
metadata @2 :Artifact; # Metadata artifact
enum Type {
supercombo @0;
navigation @1;
vision @2;
policy @3;
}
}
enum Runner {
snpe @0;
tinygrad @1;
@@ -88,8 +83,6 @@ struct ModelManagerSP @0xaedffd8f31e7b55d {
environment @6 :Text;
runner @7 :Runner;
is20hz @8 :Bool;
ref @9 :Text; # New field
minimumSelectorVersion @10 :UInt32;
}
}
+1 -1
View File
@@ -1 +1 @@
#define DEFAULT_MODEL "Tomb Raider 7 (Default)"
#define DEFAULT_MODEL "Tomb_Raider_6 (Default)"
-2
View File
@@ -128,7 +128,6 @@ inline static std::unordered_map<std::string, uint32_t> keys = {
{"CarParamsSPCache", CLEAR_ON_MANAGER_START},
{"CarParamsSPPersistent", PERSISTENT},
{"CarPlatformBundle", PERSISTENT},
{"DynamicPersonality", PERSISTENT | BACKUP},
{"EnableGithubRunner", PERSISTENT | BACKUP},
{"MaxTimeOffroad", PERSISTENT | BACKUP},
{"ModelRunnerTypeCache", CLEAR_ON_ONROAD_TRANSITION},
@@ -165,7 +164,6 @@ inline static std::unordered_map<std::string, uint32_t> keys = {
{"BackupManager_RestoreVersion", PERSISTENT},
// sunnypilot car specific params
{"HyundaiLongitudinalTuning", PERSISTENT},
{"HyundaiRadarTracks", PERSISTENT},
{"HyundaiRadarTracksConfirmed", PERSISTENT},
{"HyundaiRadarTracksPersistent", PERSISTENT},
+2 -3
View File
@@ -1,7 +1,6 @@
"""Utilities for reading real time clocks and keeping soft real time constraints."""
import gc
import os
import sys
import time
from setproctitle import getproctitle
@@ -29,13 +28,13 @@ class Priority:
def set_core_affinity(cores: list[int]) -> None:
if sys.platform == 'linux' and not PC:
if not PC:
os.sched_setaffinity(0, cores)
def config_realtime_process(cores: int | list[int], priority: int) -> None:
gc.disable()
if sys.platform == 'linux' and not PC:
if not PC:
os.sched_setscheduler(0, os.SCHED_FIFO, os.sched_param(priority))
c = cores if isinstance(cores, list) else [cores, ]
set_core_affinity(c)
+6 -13
View File
@@ -4,13 +4,12 @@
A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
# 311 Supported Cars
# 304 Supported Cars
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|<a href="##"><img width=2000></a>Hardware Needed<br>&nbsp;|Video|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|Acura|ILX 2016-18|Technology Plus Package or AcuraWatch Plus|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Acura&model=ILX 2016-18">Buy Here</a></sub></details>||
|Acura|ILX 2019|All|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Acura&model=ILX 2019">Buy Here</a></sub></details>||
|Acura|RDX 2016-18|AcuraWatch Plus or Advance Package|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Acura&model=RDX 2016-18">Buy Here</a></sub></details>||
|Acura|ILX 2016-19|AcuraWatch Plus|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Acura&model=ILX 2016-19">Buy Here</a></sub></details>||
|Acura|RDX 2016-18|AcuraWatch Plus|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Acura&model=RDX 2016-18">Buy Here</a></sub></details>||
|Acura|RDX 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Acura&model=RDX 2019-21">Buy Here</a></sub></details>||
|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=A3 2014-19">Buy Here</a></sub></details>||
|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=A3 Sportback e-tron 2017-18">Buy Here</a></sub></details>||
@@ -33,22 +32,17 @@ A supported vehicle is one that just works when you install a comma device. All
|Dodge|Durango 2020-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Dodge&model=Durango 2020-21">Buy Here</a></sub></details>||
|Ford|Bronco Sport 2021-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Bronco Sport 2021-24">Buy Here</a></sub></details>||
|Ford|Escape 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Escape 2020-22">Buy Here</a></sub></details>||
|Ford|Escape 2023-24|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Escape 2023-24">Buy Here</a></sub></details>||
|Ford|Escape Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Escape Hybrid 2020-22">Buy Here</a></sub></details>||
|Ford|Escape Hybrid 2023-24|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Escape Hybrid 2023-24">Buy Here</a></sub></details>||
|Ford|Escape Plug-in Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Escape Plug-in Hybrid 2020-22">Buy Here</a></sub></details>||
|Ford|Escape Plug-in Hybrid 2023-24|Co-Pilot360 Assist+|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Escape Plug-in Hybrid 2023-24">Buy Here</a></sub></details>||
|Ford|Explorer 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Explorer 2020-24">Buy Here</a></sub></details>||
|Ford|Explorer Hybrid 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Explorer Hybrid 2020-24">Buy Here</a></sub></details>||
|Ford|F-150 2021-23|Co-Pilot360 Assist 2.0|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=F-150 2021-23">Buy Here</a></sub></details>||
|Ford|F-150 Hybrid 2021-23|Co-Pilot360 Assist 2.0|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=F-150 Hybrid 2021-23">Buy Here</a></sub></details>||
|Ford|Focus 2018[<sup>3</sup>](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Focus 2018">Buy Here</a></sub></details>||
|Ford|Focus Hybrid 2018[<sup>3</sup>](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Focus Hybrid 2018">Buy Here</a></sub></details>||
|Ford|Kuga 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Kuga 2020-23">Buy Here</a></sub></details>||
|Ford|Kuga Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Kuga Hybrid 2020-23">Buy Here</a></sub></details>||
|Ford|Kuga Hybrid 2024|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Kuga Hybrid 2024">Buy Here</a></sub></details>||
|Ford|Kuga Plug-in Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Kuga Plug-in Hybrid 2020-23">Buy Here</a></sub></details>||
|Ford|Kuga Plug-in Hybrid 2024|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Kuga Plug-in Hybrid 2024">Buy Here</a></sub></details>||
|Ford|Kuga 2020-22|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Kuga 2020-22">Buy Here</a></sub></details>||
|Ford|Kuga Hybrid 2020-22|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Kuga Hybrid 2020-22">Buy Here</a></sub></details>||
|Ford|Kuga Plug-in Hybrid 2020-22|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Kuga Plug-in Hybrid 2020-22">Buy Here</a></sub></details>||
|Ford|Maverick 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Maverick 2022">Buy Here</a></sub></details>||
|Ford|Maverick 2023-24|Co-Pilot360 Assist|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Maverick 2023-24">Buy Here</a></sub></details>||
|Ford|Maverick Hybrid 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Maverick Hybrid 2022">Buy Here</a></sub></details>||
@@ -192,7 +186,6 @@ A supported vehicle is one that just works when you install a comma device. All
|Lexus|NX Hybrid 2018-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=NX Hybrid 2018-19">Buy Here</a></sub></details>||
|Lexus|NX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=NX Hybrid 2020-21">Buy Here</a></sub></details>||
|Lexus|RC 2018-20|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=RC 2018-20">Buy Here</a></sub></details>||
|Lexus|RC 2023|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=RC 2023">Buy Here</a></sub></details>||
|Lexus|RX 2016|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=RX 2016">Buy Here</a></sub></details>||
|Lexus|RX 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=RX 2017-19">Buy Here</a></sub></details>||
|Lexus|RX 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=RX 2020-22">Buy Here</a></sub></details>||
+2 -2
View File
@@ -25,9 +25,9 @@ ensuring two main safety requirements.
by stepping on the brake pedal or by pressing the cancel button.
2. The vehicle must not alter its trajectory too quickly for the driver to safely
react. This means that while the system is engaged, the actuators are constrained
to operate within reasonable limits[^1].
to operate within reasonable limits[^1].
For additional safety implementation details, refer to [panda safety model](https://github.com/commaai/panda#safety-model). For vehicle specific implementation of the safety concept, refer to [opendbc/safety/safety](https://github.com/commaai/opendbc/tree/master/opendbc/safety/safety).
For additional safety implementation details, refer to [panda safety model](https://github.com/commaai/panda#safety-model). For vehicle specific implementation of the safety concept, refer to [panda/board/safety/](https://github.com/commaai/panda/tree/master/board/safety).
**Extra note**: comma.ai strongly discourages the use of openpilot forks with safety code either missing or
not fully meeting the above requirements.
+1 -1
View File
@@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1
export VECLIB_MAXIMUM_THREADS=1
if [ -z "$AGNOS_VERSION" ]; then
export AGNOS_VERSION="12.1"
export AGNOS_VERSION="11.13"
fi
export STAGING_ROOT="/data/safe_staging"
-1
View File
@@ -47,7 +47,6 @@ dependencies = [
# logging
"pyzmq",
"sentry-sdk",
"xattr", # used in place of 'os.getxattr' for macos compatibility
# athena
"PyJWT",
+1 -1
View File
@@ -191,7 +191,7 @@ class CarSpecificEvents:
events.add(EventName.accFaulted)
if CS.steeringPressed:
events.add(EventName.steerOverride)
if CS.steeringDisengage and not CS_prev.steeringDisengage:
if CS.steeringDisengage:
events.add(EventName.steerDisengage)
if CS.brakePressed and CS.standstill:
events.add(EventName.preEnableStandstill)
+1 -1
View File
@@ -108,7 +108,7 @@ class Car:
fixed_fingerprint = json.loads(self.params.get("CarPlatformBundle", encoding='utf-8') or "{}").get("platform", None)
self.CI = get_car(*self.can_callbacks, obd_callback(self.params), alpha_long_allowed, num_pandas, cached_params, fixed_fingerprint)
sunnypilot_interfaces.setup_interfaces(self.CI, self.params)
sunnypilot_interfaces.setup_interfaces(self.CI.CP, self.CI.CP_SP, self.params)
self.RI = interfaces[self.CI.CP.carFingerprint].RadarInterface(self.CI.CP, self.CI.CP_SP)
self.CP = self.CI.CP
self.CP_SP = self.CI.CP_SP
+1 -1
View File
@@ -45,9 +45,9 @@ class TestCarInterfaces:
alpha_long=args['alpha_long'], docs=False)
car_params_sp = CarInterface.get_params_sp(car_params, car_name, args['fingerprints'], args['car_fw'],
alpha_long=args['alpha_long'], docs=False)
sunnypilot_interfaces.setup_interfaces(car_params, car_params_sp)
car_params = car_params.as_reader()
car_interface = CarInterface(car_params, car_params_sp)
sunnypilot_interfaces.setup_interfaces(car_interface)
assert car_params
assert car_params_sp
assert car_interface
+6 -2
View File
@@ -4,7 +4,7 @@ from openpilot.common.basedir import BASEDIR
from opendbc.car.docs import generate_cars_md, get_all_car_docs
from openpilot.selfdrive.debug.dump_car_docs import dump_car_docs
from openpilot.selfdrive.debug.print_docs_diff import print_car_docs_diff
from openpilot.selfdrive.car.docs import CARS_MD_TEMPLATE
from openpilot.selfdrive.car.docs import CARS_MD_OUT, CARS_MD_TEMPLATE
class TestCarDocs:
@@ -13,7 +13,11 @@ class TestCarDocs:
cls.all_cars = get_all_car_docs()
def test_generator(self):
generate_cars_md(self.all_cars, CARS_MD_TEMPLATE)
generated_cars_md = generate_cars_md(self.all_cars, CARS_MD_TEMPLATE)
with open(CARS_MD_OUT) as f:
current_cars_md = f.read()
assert generated_cars_md == current_cars_md, "Run selfdrive/car/docs.py to update the compatibility documentation"
def test_docs_diff(self):
dump_path = os.path.join(BASEDIR, "selfdrive", "car", "tests", "cars_dump")
-5
View File
@@ -338,7 +338,6 @@ class TestCarModelBase(unittest.TestCase):
prev_panda_gas = self.safety.get_gas_pressed_prev()
prev_panda_brake = self.safety.get_brake_pressed_prev()
prev_panda_regen_braking = self.safety.get_regen_braking_prev()
prev_panda_steering_disengage = self.safety.get_steering_disengage_prev()
prev_panda_vehicle_moving = self.safety.get_vehicle_moving()
prev_panda_vehicle_speed_min = self.safety.get_vehicle_speed_min()
prev_panda_vehicle_speed_max = self.safety.get_vehicle_speed_max()
@@ -366,9 +365,6 @@ class TestCarModelBase(unittest.TestCase):
if self.safety.get_regen_braking_prev() != prev_panda_regen_braking:
self.assertEqual(CS.regenBraking, self.safety.get_regen_braking_prev())
if self.safety.get_steering_disengage_prev() != prev_panda_steering_disengage:
self.assertEqual(CS.steeringDisengage, self.safety.get_steering_disengage_prev())
if self.safety.get_vehicle_moving() != prev_panda_vehicle_moving:
self.assertEqual(not CS.standstill, self.safety.get_vehicle_moving())
@@ -444,7 +440,6 @@ class TestCarModelBase(unittest.TestCase):
brake_pressed = False
checks['brakePressed'] += brake_pressed != self.safety.get_brake_pressed_prev()
checks['regenBraking'] += CS.regenBraking != self.safety.get_regen_braking_prev()
checks['steeringDisengage'] += CS.steeringDisengage != self.safety.get_steering_disengage_prev()
if self.CP.pcmCruise:
# On most pcmCruise cars, openpilot's state is always tied to the PCM's cruise state.
@@ -10,9 +10,6 @@ from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.modeld.constants import index_function
from openpilot.selfdrive.controls.radard import _LEAD_ACCEL_TAU
from openpilot.sunnypilot.selfdrive.controls.lib.dynamic_personality.dynamic_personality_controller import DynamicPersonalityController
if __name__ == '__main__': # generating code
from openpilot.third_party.acados.acados_template import AcadosModel, AcadosOcp, AcadosOcpSolver
else:
@@ -231,7 +228,6 @@ class LongitudinalMpc:
self.solver = AcadosOcpSolverCython(MODEL_NAME, ACADOS_SOLVER_TYPE, N)
self.reset()
self.source = SOURCES[2]
self.dynamic_personality_controller = DynamicPersonalityController()
def reset(self):
# self.solver = AcadosOcpSolverCython(MODEL_NAME, ACADOS_SOLVER_TYPE, N)
@@ -331,9 +327,9 @@ class LongitudinalMpc:
lead_xv = self.extrapolate_lead(x_lead, v_lead, a_lead, a_lead_tau)
return lead_xv
def update(self, radarstate, v_cruise, x, v, a, j, personality=log.LongitudinalPersonality.standard, dynamic_personality=False):
def update(self, radarstate, v_cruise, x, v, a, j, personality=log.LongitudinalPersonality.standard):
t_follow = get_T_FOLLOW(personality)
v_ego = self.x0[1]
t_follow = self.dynamic_personality_controller.get_dynamic_follow_distance(v_ego, personality) if dynamic_personality else get_T_FOLLOW(personality)
self.status = radarstate.leadOne.status or radarstate.leadTwo.status
lead_xv_0 = self.process_lead(radarstate.leadOne)
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
import math
import numpy as np
import cereal.messaging as messaging
from opendbc.car.interfaces import ACCEL_MIN, ACCEL_MAX
from openpilot.common.conversions import Conversions as CV
@@ -92,8 +93,8 @@ class LongitudinalPlanner(LongitudinalPlannerSP):
return x, v, a, j, throttle_prob
def update(self, sm):
self.mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc'
LongitudinalPlannerSP.update(self, sm)
self.mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc'
if dec_mpc_mode := self.get_mpc_mode():
self.mode = dec_mpc_mode
@@ -148,7 +149,7 @@ class LongitudinalPlanner(LongitudinalPlannerSP):
self.mpc.set_weights(prev_accel_constraint, personality=sm['selfdriveState'].personality)
self.mpc.set_cur_state(self.v_desired_filter.x, self.a_desired)
self.mpc.update(sm['radarState'], v_cruise, x, v, a, j, personality=sm['selfdriveState'].personality, dynamic_personality = self.dynamic_personality)
self.mpc.update(sm['radarState'], v_cruise, x, v, a, j, personality=sm['selfdriveState'].personality)
self.v_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.v_solution)
self.a_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.a_solution)
@@ -177,10 +178,6 @@ class LongitudinalPlanner(LongitudinalPlannerSP):
output_a_target = min(output_a_target_mpc, output_a_target_e2e)
self.output_should_stop = output_should_stop_e2e or output_should_stop_mpc
if not self.is_stock:
# To support non Tomb Raider models
output_a_target, self.output_should_stop = output_a_target_mpc, output_should_stop_mpc
for idx in range(2):
accel_clip[idx] = np.clip(accel_clip[idx], self.prev_accel_clip[idx] - 0.05, self.prev_accel_clip[idx] + 0.05)
self.output_a_target = np.clip(output_a_target, accel_clip[0], accel_clip[1])
@@ -23,7 +23,7 @@ class TestLatControl:
CP = CarInterface.get_non_essential_params(car_name)
CP_SP = CarInterface.get_non_essential_params_sp(CP, car_name)
CI = CarInterface(CP, CP_SP)
sunnypilot_interfaces.setup_interfaces(CI)
sunnypilot_interfaces.setup_interfaces(CP, CP_SP)
CP_SP = convert_to_capnp(CP_SP)
VM = VehicleModel(CP)
+12 -5
View File
@@ -90,11 +90,18 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D
fill_xyzt(modelV2.orientationRate, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.ORIENTATION_RATE].T)
# temporal pose
#temporal_pose = modelV2.temporalPose
#temporal_pose.trans = net_output_data['sim_pose'][0,:ModelConstants.POSE_WIDTH//2].tolist()
#temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:ModelConstants.POSE_WIDTH//2].tolist()
#temporal_pose.rot = net_output_data['sim_pose'][0,ModelConstants.POSE_WIDTH//2:].tolist()
#temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,ModelConstants.POSE_WIDTH//2:].tolist()
temporal_pose = modelV2.temporalPose
if 'sim_pose' in net_output_data:
temporal_pose.trans = net_output_data['sim_pose'][0,:ModelConstants.POSE_WIDTH//2].tolist()
temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:ModelConstants.POSE_WIDTH//2].tolist()
temporal_pose.rot = net_output_data['sim_pose'][0,ModelConstants.POSE_WIDTH//2:].tolist()
temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,ModelConstants.POSE_WIDTH//2:].tolist()
else:
temporal_pose.trans = net_output_data['plan'][0,0,Plan.VELOCITY].tolist()
temporal_pose.transStd = net_output_data['plan_stds'][0,0,Plan.VELOCITY].tolist()
temporal_pose.rot = net_output_data['plan'][0,0,Plan.ORIENTATION_RATE].tolist()
temporal_pose.rotStd = net_output_data['plan_stds'][0,0,Plan.ORIENTATION_RATE].tolist()
# poly path
fill_xyz_poly(driving_model_data.path, ModelConstants.POLY_PATH_DEGREE, *net_output_data['plan'][0,:,Plan.POSITION].T)
+4 -7
View File
@@ -41,7 +41,7 @@ POLICY_PKL_PATH = Path(__file__).parent / 'models/driving_policy_tinygrad.pkl'
VISION_METADATA_PATH = Path(__file__).parent / 'models/driving_vision_metadata.pkl'
POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.pkl'
LAT_SMOOTH_SECONDS = 0.1
LAT_SMOOTH_SECONDS = 0.3
LONG_SMOOTH_SECONDS = 0.3
MIN_LAT_CONTROL_SPEED = 0.3
@@ -54,12 +54,9 @@ def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log.
ModelConstants.T_IDXS,
action_t=long_action_t)
desired_accel = smooth_value(desired_accel, prev_action.desiredAcceleration, LONG_SMOOTH_SECONDS)
desired_curvature = get_curvature_from_plan(plan[:,Plan.T_FROM_CURRENT_EULER][:,2],
plan[:,Plan.ORIENTATION_RATE][:,2],
ModelConstants.T_IDXS,
v_ego,
lat_action_t)
desired_curvature = get_curvature_from_plan(plan[:, Plan.T_FROM_CURRENT_EULER][:, 2],
plan[:, Plan.ORIENTATION_RATE][:, 2],
ModelConstants.T_IDXS, v_ego, lat_action_t)
if v_ego > MIN_LAT_CONTROL_SPEED:
desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, LAT_SMOOTH_SECONDS)
else:
+1 -1
View File
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:19e30484236efff72d519938c3e26461dbeb89c11d81fa7ecbff8e0263333c18
oid sha256:8a478376723ba79e2393ca95e2d3e497571ba6fed113e5f13a36f0e4b4d4a7c5
size 15588463
+1 -6
View File
@@ -474,12 +474,7 @@ void pandad_run(std::vector<Panda *> &pandas) {
for (auto *panda : pandas) {
std::string log = panda->serial_read();
if (!log.empty()) {
if (log.find("Register 0x") != std::string::npos) {
// Log register divergent faults as errors
LOGE("%s", log.c_str());
} else {
LOGD("%s", log.c_str());
}
LOGD("%s", log.c_str());
}
}
+1 -5
View File
@@ -60,9 +60,6 @@ DeveloperPanel::DeveloperPanel(SettingsWindow *parent) : ListWidget(parent) {
enableGithubRunner = new ParamControl("EnableGithubRunner", tr("Enable GitHub runner service"), tr("Enables or disables the github runner service."), "");
addItem(enableGithubRunner);
dynamicpersonality = new ParamControl("DynamicPersonality", tr("Enable Dynamic Personality"), tr("Adjust follow distance dynamically "), "");
addItem(dynamicpersonality);
// error log button
errorLogBtn = new ButtonControl(tr("Error Log"), tr("VIEW"), tr("View the error log for sunnypilot crashes."));
connect(errorLogBtn, &ButtonControl::clicked, [=]() {
@@ -87,7 +84,7 @@ void DeveloperPanel::updateToggles(bool _offroad) {
* - visible, and
* - during onroad & offroad states
*/
if (btn != experimentalLongitudinalToggle && btn != dynamicpersonality) {
if (btn != experimentalLongitudinalToggle) {
btn->setEnabled(_offroad);
}
}
@@ -125,7 +122,6 @@ void DeveloperPanel::updateToggles(bool _offroad) {
// Handle specific controls visibility for release branches
enableGithubRunner->setVisible(!is_release);
dynamicpersonality->setVisible(!is_release);
errorLogBtn->setVisible(!is_release);
joystickToggle->setVisible(!is_release);
@@ -21,7 +21,6 @@ private:
ParamControl* experimentalLongitudinalToggle;
ParamControl* hyundaiRadarTracksToggle;
ParamControl* enableGithubRunner;
ParamControl* dynamicpersonality;
bool is_release;
bool offroad = false;
+1 -19
View File
@@ -21,7 +21,6 @@ qt_src = [
"sunnypilot/qt/offroad/offroad_home.cc",
"sunnypilot/qt/offroad/settings/device_panel.cc",
"sunnypilot/qt/offroad/settings/lateral_panel.cc",
"sunnypilot/qt/offroad/settings/longitudinal_panel.cc",
"sunnypilot/qt/offroad/settings/max_time_offroad.cc",
"sunnypilot/qt/offroad/settings/settings.cc",
"sunnypilot/qt/offroad/settings/software_panel.cc",
@@ -50,28 +49,11 @@ network_src = [
]
vehicle_panel_qt_src = [
"sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.cc",
"sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.cc",
"sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc",
]
brand_settings_qt_src = [
"sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/ford_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/gm_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/honda_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/mazda_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/nissan_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/rivian_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/subaru_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/tesla_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/toyota_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.cc",
]
sp_widgets_src = widgets_src + network_src
sp_qt_src = qt_src + lateral_panel_qt_src + vehicle_panel_qt_src + brand_settings_qt_src
sp_qt_src = qt_src + lateral_panel_qt_src + vehicle_panel_qt_src
sp_qt_util = qt_util
Export('sp_widgets_src', 'sp_qt_src', "sp_qt_util")
@@ -112,18 +112,9 @@ DevicePanelSP::DevicePanelSP(SettingsWindowSP *parent) : DevicePanel(parent) {
addItem(power_group_layout);
std::vector always_enabled_btns = {
rebootBtn,
poweroffBtn,
offroadBtn,
buttons["quietModeBtn"],
};
QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
for (auto btn : findChildren<PushButtonSP*>()) {
bool always_enabled = std::find(always_enabled_btns.begin(), always_enabled_btns.end(), btn) != always_enabled_btns.end();
if (!always_enabled) {
if (btn != rebootBtn && btn != poweroffBtn && btn != offroadBtn) {
btn->setEnabled(offroad);
}
}
@@ -1,11 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.h"
LongitudinalPanel::LongitudinalPanel(QWidget *parent) : QWidget(parent) {
}
@@ -1,17 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
class LongitudinalPanel : public QWidget {
Q_OBJECT
public:
explicit LongitudinalPanel(QWidget *parent = nullptr);
};
@@ -16,7 +16,6 @@
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/trips_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h"
@@ -80,7 +79,6 @@ SettingsWindowSP::SettingsWindowSP(QWidget *parent) : SettingsWindow(parent) {
PanelInfo(" " + tr("Toggles"), toggles, "../../sunnypilot/selfdrive/assets/offroad/icon_toggle.png"),
PanelInfo(" " + tr("Software"), new SoftwarePanelSP(this), "../../sunnypilot/selfdrive/assets/offroad/icon_software.png"),
PanelInfo(" " + tr("Steering"), new LateralPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_lateral.png"),
PanelInfo(" " + tr("Cruise"), new LongitudinalPanel(this), "../assets/offroad/icon_speed_limit.png"),
PanelInfo(" " + tr("Trips"), new TripsPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_trips.png"),
PanelInfo(" " + tr("Vehicle"), new VehiclePanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_vehicle.png"),
PanelInfo(" " + tr("Firehose"), new FirehosePanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_firehose.svg"),
@@ -47,24 +47,24 @@ void SoftwarePanelSP::handleBundleDownloadProgress() {
// Get status for each model type in order
for (const auto &model: models) {
QString typeName;
QString modelName = QString::fromStdString(bundle.getDisplayName());
QString modelName;
switch (model.getType()) {
case cereal::ModelManagerSP::Model::Type::SUPERCOMBO:
case cereal::ModelManagerSP::Type::DRIVE:
typeName = tr("Driving");
modelName = QString::fromStdString(bundle.getDisplayName());
break;
case cereal::ModelManagerSP::Model::Type::NAVIGATION:
case cereal::ModelManagerSP::Type::NAVIGATION:
typeName = tr("Navigation");
modelName = QString::fromStdString(model.getFullName());
break;
case cereal::ModelManagerSP::Model::Type::VISION:
typeName = tr("Vision");
break;
case cereal::ModelManagerSP::Model::Type::POLICY:
typeName = tr("Policy");
case cereal::ModelManagerSP::Type::METADATA:
typeName = tr("Metadata");
modelName = QString::fromStdString(model.getFullName());
break;
}
const auto &progress = model.getArtifact().getDownloadProgress();
const auto &progress = model.getDownloadProgress();
QString line;
if (progress.getStatus() == cereal::ModelManagerSP::DownloadStatus::DOWNLOADING) {
@@ -1,62 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brands.h"
static const QStringList supportedBrands = {
"chrysler",
"ford",
"gm",
"honda",
"hyundai",
"mazda",
"nissan",
"rivian",
"subaru",
"tesla",
"toyota",
"volkswagen",
};
BrandSettingsInterface* BrandSettingsFactory::createBrandSettings(const QString& brand, QWidget* parent) {
if (brand == "chrysler")
return new ChryslerSettings(parent);
if (brand == "ford")
return new FordSettings(parent);
if (brand == "gm")
return new GMSettings(parent);
if (brand == "honda")
return new HondaSettings(parent);
if (brand == "hyundai")
return new HyundaiSettings(parent);
if (brand == "mazda")
return new MazdaSettings(parent);
if (brand == "nissan")
return new NissanSettings(parent);
if (brand == "rivian")
return new RivianSettings(parent);
if (brand == "subaru")
return new SubaruSettings(parent);
if (brand == "tesla")
return new TeslaSettings(parent);
if (brand == "toyota")
return new ToyotaSettings(parent);
if (brand == "volkswagen")
return new VolkswagenSettings(parent);
// Default empty settings if brand not supported
return nullptr;
}
bool BrandSettingsFactory::isBrandSupported(const QString& brand) {
return supportedBrands.contains(brand);
}
QStringList BrandSettingsFactory::getSupportedBrands() {
return supportedBrands;
}
@@ -1,18 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
class BrandSettingsFactory {
public:
static BrandSettingsInterface* createBrandSettings(const QString &brand, QWidget *parent = nullptr);
static bool isBrandSupported(const QString& brand);
static QStringList getSupportedBrands();
};
@@ -1,21 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
BrandSettingsInterface::BrandSettingsInterface(QWidget *parent) : QWidget(parent) {
QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout->setContentsMargins(0, 0, 0, 0);
list = new ListWidget(this, false);
main_layout->addWidget(list);
}
void BrandSettingsInterface::updatePanel(bool _offroad) {
offroad = _offroad;
updateSettings();
}
@@ -1,28 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class BrandSettingsInterface : public QWidget {
Q_OBJECT
public:
explicit BrandSettingsInterface(QWidget *parent = nullptr);
virtual ~BrandSettingsInterface() = default;
void updatePanel(bool _offroad);
virtual void updateSettings() = 0;
protected:
ListWidget *list = nullptr;
Params params;
bool offroad = false;
};
@@ -1,21 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.h"
@@ -1,14 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.h"
ChryslerSettings::ChryslerSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void ChryslerSettings::updateSettings() {
}
@@ -1,26 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class ChryslerSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit ChryslerSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -1,14 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.h"
FordSettings::FordSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void FordSettings::updateSettings() {
}
@@ -1,26 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class FordSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit FordSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -1,14 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.h"
GMSettings::GMSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void GMSettings::updateSettings() {
}
@@ -1,26 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class GMSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit GMSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -1,14 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.h"
HondaSettings::HondaSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void HondaSettings::updateSettings() {
}
@@ -1,26 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class HondaSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit HondaSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -1,57 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h"
HyundaiSettings::HyundaiSettings(QWidget *parent) : BrandSettingsInterface(parent) {
std::vector<QString> tuning_texts{ tr("Off"), tr("Dynamic"), tr("Predictive") };
longitudinalTuningToggle = new ButtonParamControl(
"HyundaiLongitudinalTuning",
tr("Custom Longitudinal Tuning"),
"",
"",
tuning_texts,
500
);
QObject::connect(longitudinalTuningToggle, &ButtonParamControlSP::buttonToggled, this, &HyundaiSettings::updateSettings);
list->addItem(longitudinalTuningToggle);
longitudinalTuningToggle->showDescription();
}
void HyundaiSettings::updateSettings() {
auto longitudinal_tuning_param = std::atoi(params.get("HyundaiLongitudinalTuning").c_str());
auto cp_bytes = params.get("CarParamsPersistent");
if (!cp_bytes.empty()) {
AlignedBuffer aligned_buf;
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size()));
cereal::CarParams::Reader CP = cmsg.getRoot<cereal::CarParams>();
has_longitudinal_control = hasLongitudinalControl(CP);
} else {
has_longitudinal_control = false;
}
LongitudinalTuningOption longitudinal_tuning_option;
if (longitudinal_tuning_param == int(LongitudinalTuningOption::PREDICTIVE)) {
longitudinal_tuning_option = LongitudinalTuningOption::PREDICTIVE;
} else if (longitudinal_tuning_param == int(LongitudinalTuningOption::DYNAMIC)) {
longitudinal_tuning_option = LongitudinalTuningOption::DYNAMIC;
} else {
longitudinal_tuning_option = LongitudinalTuningOption::OFF;
}
bool longitudinal_tuning_disabled = !offroad || !has_longitudinal_control;
QString longitudinal_tuning_description = longitudinalTuningDescription(longitudinal_tuning_option);
if (longitudinal_tuning_disabled) {
longitudinal_tuning_description = toggleDisableMsg(offroad, has_longitudinal_control);
}
longitudinalTuningToggle->setEnabled(!longitudinal_tuning_disabled);
longitudinalTuningToggle->setDescription(longitudinal_tuning_description);
longitudinalTuningToggle->showDescription();
}
@@ -1,65 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
enum class LongitudinalTuningOption {
OFF,
DYNAMIC,
PREDICTIVE,
};
class HyundaiSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit HyundaiSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool has_longitudinal_control = false;
ButtonParamControl *longitudinalTuningToggle = nullptr;
static QString toggleDisableMsg(bool _offroad, bool _has_longitudinal_control) {
if (!_has_longitudinal_control) {
return tr("This feature can only be used with openpilot longitudinal control enabled.");
}
if (!_offroad) {
return tr("Enable \"Always Offroad\" in Device panel, or turn vehicle off to select an option.");
}
return QString();
}
static QString longitudinalTuningDescription(LongitudinalTuningOption option = LongitudinalTuningOption::OFF) {
QString off_str = tr("Off: Uses default tuning");
QString dynamic_str = tr("Dynamic: Adjusts acceleration limits based on current speed");
QString predictive_str = tr("Predictive: Uses future trajectory data to anticipate needed adjustments");
if (option == LongitudinalTuningOption::PREDICTIVE) {
predictive_str = "<font color='white'><b>" + predictive_str + "</b></font>";
} else if (option == LongitudinalTuningOption::DYNAMIC) {
dynamic_str = "<font color='white'><b>" + dynamic_str + "</b></font>";
} else {
off_str = "<font color='white'><b>" + off_str + "</b></font>";
}
return QString("%1<br><br>%2<br>%3<br>%4<br>")
.arg(tr("Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control."))
.arg(off_str)
.arg(dynamic_str)
.arg(predictive_str);
}
};
@@ -1,14 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.h"
MazdaSettings::MazdaSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void MazdaSettings::updateSettings() {
}
@@ -1,26 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class MazdaSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit MazdaSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -1,14 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.h"
NissanSettings::NissanSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void NissanSettings::updateSettings() {
}
@@ -1,26 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class NissanSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit NissanSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -43,73 +43,36 @@ PlatformSelector::PlatformSelector() : ButtonControl(tr("Vehicle"), "", "") {
}
});
main_layout->addStretch(0);
refresh(offroad);
}
void PlatformSelector::refresh(bool _offroad) {
QString name = getPlatformBundle("name").toString();
platform = unrecognized_str;
QString platform_color = YELLOW_PLATFORM;
if (!name.isEmpty()) {
platform = name;
platform_color = BLUE_PLATFORM;
brand = getPlatformBundle("brand").toString();
setValue(name);
setText(tr("REMOVE"));
} else {
setText(tr("SEARCH"));
platform = unrecognized_str;
brand = "";
auto cp_bytes = params.get("CarParamsPersistent");
if (!cp_bytes.empty()) {
AlignedBuffer aligned_buf;
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size()));
cereal::CarParams::Reader CP = cmsg.getRoot<cereal::CarParams>();
platform = QString::fromStdString(CP.getCarFingerprint().cStr());
for (auto it = platforms.constBegin(); it != platforms.constEnd(); ++it) {
if (it.value()["platform"].toString() == platform) {
brand = it.value()["brand"].toString();
break;
}
}
if (platform == "MOCK") {
platform = unrecognized_str;
} else {
platform_color = GREEN_PLATFORM;
}
setValue(QString::fromStdString(CP.getCarFingerprint().cStr()));
}
}
setValue(platform, platform_color);
setEnabled(true);
emit refreshPanel();
offroad = _offroad;
FingerprintStatus cur_status;
if (platform_color == GREEN_PLATFORM) {
cur_status = FingerprintStatus::AUTO_FINGERPRINT;
} else if (platform_color == BLUE_PLATFORM) {
cur_status = FingerprintStatus::MANUAL_FINGERPRINT;
} else {
cur_status = FingerprintStatus::UNRECOGNIZED;
}
setDescription(platformDescription(cur_status));
showDescription();
}
void PlatformSelector::setPlatform(const QString &_platform) {
QVariantMap platform_data = platforms[_platform];
void PlatformSelector::setPlatform(const QString &platform) {
QVariantMap platform_data = platforms[platform];
const QString offroad_msg = offroad ? tr("This setting will take effect immediately.") :
tr("This setting will take effect once the device enters offroad state.");
const QString msg = QString("<b>%1</b><br><br>%2")
.arg(_platform, offroad_msg);
.arg(platform, offroad_msg);
QString content("<body><h2 style=\"text-align: center;\">" + tr("Vehicle Selector") + "</h2><br>"
"<p style=\"text-align: center; margin: 0 128px; font-size: 50px;\">" + msg + "</p></body>");
@@ -117,7 +80,7 @@ void PlatformSelector::setPlatform(const QString &_platform) {
if (ConfirmationDialog(content, tr("Confirm"), tr("Cancel"), true, this).exec()) {
QJsonObject json_bundle;
json_bundle["platform"] = platform_data["platform"].toString();
json_bundle["name"] = _platform;
json_bundle["name"] = platform;
json_bundle["make"] = platform_data["make"].toString();
json_bundle["brand"] = platform_data["brand"].toString();
json_bundle["model"] = platform_data["model"].toString();
@@ -9,16 +9,6 @@
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
static const QString GREEN_PLATFORM = "#00F100";
static const QString BLUE_PLATFORM = "#0086E9";
static const QString YELLOW_PLATFORM = "#FFD500";
enum class FingerprintStatus {
AUTO_FINGERPRINT,
MANUAL_FINGERPRINT,
UNRECOGNIZED,
};
class PlatformSelector : public ButtonControl {
Q_OBJECT
@@ -26,15 +16,9 @@ public:
PlatformSelector();
QVariant getPlatformBundle(const QString &key);
QString platform;
QString brand;
public slots:
void refresh(bool _offroad);
signals:
void refreshPanel();
private:
void searchPlatforms(const QString &query);
void setPlatform(const QString &platform = "");
@@ -42,27 +26,4 @@ private:
Params params;
bool offroad;
QString unrecognized_str = tr("Unrecognized Vehicle");
static QString platformDescription(FingerprintStatus status = FingerprintStatus::UNRECOGNIZED) {
QString auto_str = "🟢 - " + tr("Fingerprinted automatically");
QString manual_str = "🔵 - " + tr("Manually selected");
QString unrecognized_str = "🟡 - " + tr("Not fingerprinted or manually selected");
if (status == FingerprintStatus::AUTO_FINGERPRINT) {
auto_str = "<font color='white'><b>" + auto_str + "</b></font>";
} else if (status == FingerprintStatus::MANUAL_FINGERPRINT) {
manual_str = "<font color='white'><b>" + manual_str + "</b></font>";
} else {
unrecognized_str = "<font color='white'><b>" + unrecognized_str + "</b></font>";
}
return QString("%1<br>%2<br><br>%3<br>%4<br>%5")
.arg(tr("Select vehicle to force fingerprint manually."))
.arg(tr("Colors represent fingerprint status:"))
.arg(auto_str)
.arg(manual_str)
.arg(unrecognized_str);
}
};
@@ -1,14 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.h"
RivianSettings::RivianSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void RivianSettings::updateSettings() {
}
@@ -1,26 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class RivianSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit RivianSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -1,14 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.h"
SubaruSettings::SubaruSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void SubaruSettings::updateSettings() {
}
@@ -1,26 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class SubaruSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit SubaruSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -1,14 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.h"
TeslaSettings::TeslaSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void TeslaSettings::updateSettings() {
}
@@ -1,26 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class TeslaSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit TeslaSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -1,14 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.h"
ToyotaSettings::ToyotaSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void ToyotaSettings::updateSettings() {
}
@@ -1,26 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class ToyotaSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit ToyotaSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -1,14 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.h"
VolkswagenSettings::VolkswagenSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void VolkswagenSettings::updateSettings() {
}
@@ -1,26 +0,0 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class VolkswagenSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit VolkswagenSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -7,32 +7,26 @@
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brands.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h"
VehiclePanel::VehiclePanel(QWidget *parent) : QFrame(parent) {
QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout->setContentsMargins(50, 20, 50, 20);
main_layout = new QStackedLayout(this);
ListWidget *list = new ListWidget(this);
vehicleScreen = new QWidget(this);
QVBoxLayout *vlayout = new QVBoxLayout(vehicleScreen);
vlayout->setContentsMargins(50, 20, 50, 20);
platformSelector = new PlatformSelector();
QObject::connect(platformSelector, &PlatformSelector::refreshPanel, this, &VehiclePanel::updateBrandSettings);
list->addItem(platformSelector);
brandSettingsContainer = new QWidget(this);
brandSettingsContainerLayout = new QVBoxLayout(brandSettingsContainer);
brandSettingsContainerLayout->setContentsMargins(0, 0, 0, 0);
brandSettingsContainerLayout->setSpacing(0);
list->addItem(brandSettingsContainer);
ScrollViewSP *scroller = new ScrollViewSP(list, this);
main_layout->addWidget(scroller);
currentBrandSettings = nullptr;
vlayout->addWidget(scroller);
QObject::connect(uiState(), &UIState::offroadTransition, this, &VehiclePanel::updatePanel);
main_layout->addWidget(vehicleScreen);
main_layout->setCurrentWidget(vehicleScreen);
}
void VehiclePanel::showEvent(QShowEvent *event) {
@@ -40,28 +34,7 @@ void VehiclePanel::showEvent(QShowEvent *event) {
}
void VehiclePanel::updatePanel(bool _offroad) {
offroad = _offroad;
platformSelector->refresh(_offroad);
updateBrandSettings();
}
void VehiclePanel::updateBrandSettings() {
if (!isVisible()) {
return;
}
if (currentBrandSettings) {
brandSettingsContainerLayout->removeWidget(currentBrandSettings);
delete currentBrandSettings;
currentBrandSettings = nullptr;
}
if (BrandSettingsFactory::isBrandSupported(platformSelector->brand)) {
currentBrandSettings = BrandSettingsFactory::createBrandSettings(platformSelector->brand, this);
if (currentBrandSettings) {
currentBrandSettings->setContentsMargins(0, 0, 0, 0);
brandSettingsContainerLayout->addWidget(currentBrandSettings);
currentBrandSettings->updatePanel(offroad);
}
}
offroad = _offroad;
}
@@ -9,7 +9,6 @@
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.h"
class VehiclePanel : public QFrame {
@@ -23,12 +22,8 @@ public slots:
void updatePanel(bool _offroad);
private:
PlatformSelector* platformSelector = nullptr;
BrandSettingsInterface* currentBrandSettings = nullptr;
QWidget* brandSettingsContainer = nullptr;
QVBoxLayout* brandSettingsContainerLayout = nullptr;
bool offroad = false;
private slots:
void updateBrandSettings();
QStackedLayout* main_layout = nullptr;
QWidget* vehicleScreen = nullptr;
PlatformSelector *platformSelector = nullptr;
bool offroad;
};
@@ -118,7 +118,7 @@ AbstractControlSP_SELECTOR::AbstractControlSP_SELECTOR(const QString &title, con
if (!title.isEmpty()) {
title_label = new QPushButton(title);
title_label->setFixedHeight(120);
title_label->setStyleSheet("font-size: 50px; font-weight: 450; text-align: left; border: none; padding: 0 0 0 0");
title_label->setStyleSheet("font-size: 50px; font-weight: 450; text-align: left; border: none; padding: 20 0 0 0");
main_layout->addWidget(title_label, 1);
connect(title_label, &QPushButton::clicked, [=]() {
@@ -148,7 +148,7 @@ AbstractControlSP_SELECTOR::AbstractControlSP_SELECTOR(const QString &title, con
// description
description = new QLabel(desc);
description->setContentsMargins(40, 20, 40, 20);
description->setContentsMargins(0, 20, 40, 20);
description->setStyleSheet("font-size: 40px; color: grey");
description->setWordWrap(true);
description->setVisible(false);
+24 -40
View File
@@ -219,7 +219,7 @@ class ButtonParamControlSP : public AbstractControlSP_SELECTOR {
public:
ButtonParamControlSP(const QString &param, const QString &title, const QString &desc, const QString &icon,
const std::vector<QString> &button_texts, const int minimum_button_width = 300, const bool inline_layout = false) : AbstractControlSP_SELECTOR(title, desc, icon), button_texts(button_texts), is_inline_layout(inline_layout) {
const std::vector<QString> &button_texts, const int minimum_button_width = 300) : AbstractControlSP_SELECTOR(title, desc, icon), button_texts(button_texts) {
const QString style = R"(
QPushButton {
border-radius: 20px;
@@ -246,19 +246,6 @@ public:
key = param.toStdString();
int value = atoi(params.get(key).c_str());
if (inline_layout) {
button_param_layout->setMargin(0);
button_param_layout->setSpacing(0);
if (!title.isEmpty()) {
main_layout->removeWidget(title_label);
hlayout->addWidget(title_label, 1);
}
if (spacingItem != nullptr && main_layout->indexOf(spacingItem) != -1) {
main_layout->removeItem(spacingItem);
spacingItem = nullptr;
}
}
button_group = new QButtonGroup(this);
button_group->setExclusive(true);
for (int i = 0; i < button_texts.size(); i++) {
@@ -267,18 +254,12 @@ public:
button->setChecked(i == value);
button->setStyleSheet(style);
button->setMinimumWidth(minimum_button_width);
if (i == 0) button_param_layout->addSpacing(2);
button_param_layout->addWidget(button);
if (i == 0) hlayout->addSpacing(2);
hlayout->addWidget(button);
button_group->addButton(button, i);
}
button_param_layout->setAlignment(Qt::AlignLeft);
if (is_inline_layout) {
QFrame *container = new QFrame;
container->setLayout(button_param_layout);
container->setStyleSheet("background-color: #393939; border-radius: 20px;");
hlayout->addWidget(container);
}
hlayout->setAlignment(Qt::AlignLeft);
QObject::connect(button_group, QOverload<int>::of(&QButtonGroup::buttonClicked), [=](int id) {
params.put(key, std::to_string(id));
@@ -333,10 +314,6 @@ public:
protected:
void paintEvent(QPaintEvent *event) override {
if (is_inline_layout) {
return;
}
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
@@ -371,8 +348,6 @@ private:
std::vector<QString> button_texts;
bool button_group_enabled = true;
bool is_inline_layout;
QHBoxLayout *button_param_layout = is_inline_layout ? new QHBoxLayout() : hlayout;
};
class ListWidgetSP : public QWidget {
@@ -385,7 +360,7 @@ public:
outer_layout.addLayout(&inner_layout);
inner_layout.setMargin(0);
inner_layout.setSpacing(25); // default spacing is 25
outer_layout.addStretch(1);
outer_layout.addStretch();
}
inline void addItem(QWidget *w) { inner_layout.addWidget(w); }
inline void addItem(QLayout *layout) { inner_layout.addLayout(layout); }
@@ -441,8 +416,8 @@ class OptionControlSP : public AbstractControlSP_SELECTOR {
Q_OBJECT
private:
bool is_inline_layout;
QHBoxLayout *optionSelectorLayout = is_inline_layout ? new QHBoxLayout() : hlayout;
bool isInlineLayout;
QHBoxLayout *optionSelectorLayout = isInlineLayout ? new QHBoxLayout() : hlayout;
struct MinMaxValue {
int min_value;
@@ -463,7 +438,7 @@ private:
public:
OptionControlSP(const QString &param, const QString &title, const QString &desc, const QString &icon,
const MinMaxValue &range, const int per_value_change = 1, const bool inline_layout = false, const QMap<QString, QString> *valMap = nullptr) : AbstractControlSP_SELECTOR(title, desc, icon, nullptr), _title(title), valueMap(valMap), is_inline_layout(inline_layout) {
const MinMaxValue &range, const int per_value_change = 1, const bool inline_layout = false, const QMap<QString, QString> *valMap = nullptr) : AbstractControlSP_SELECTOR(title, desc, icon, nullptr), _title(title), valueMap(valMap), isInlineLayout(inline_layout) {
const QString style = R"(
QPushButton {
border-radius: 20px;
@@ -538,7 +513,7 @@ public:
}
optionSelectorLayout->setAlignment(Qt::AlignLeft);
if (is_inline_layout) {
if (isInlineLayout) {
QFrame *container = new QFrame;
container->setLayout(optionSelectorLayout);
container->setStyleSheet("background-color: #393939; border-radius: 20px;");
@@ -565,7 +540,7 @@ public:
protected:
void paintEvent(QPaintEvent *event) override {
if (is_inline_layout) {
if (isInlineLayout) {
return;
}
@@ -658,6 +633,16 @@ public:
}
}
protected:
// Override mouse release event to handle style updates smoothly
void mouseReleaseEvent(QMouseEvent *event) override {
if (!key.empty()) {
bool next_state = !params.getBool(key);
updateStyle(next_state);
}
QPushButton::mouseReleaseEvent(event);
}
private:
std::string key = "";
Params params;
@@ -666,14 +651,13 @@ private:
QString btn_enabled_off_style = "QPushButton:enabled { background-color: #393939; }";
QString btn_enabled_on_style = "QPushButton:enabled { background-color: #1e79e8; }";
QString btn_off_pressed_style = "QPushButton:pressed { background-color: #4A4A4A; }";
QString btn_on_pressed_style = "QPushButton:pressed { background-color: #1E8FFF; }";
QString btn_disabled_style = "QPushButton:disabled { background-color: #121212; color: #5C5C5C; }";
QString btn_pressed_style = "QPushButton:pressed { background-color: #4A4A4A; }";
QString btn_disabled_stype = "QPushButton:disabled { background-color: #121212; color: #5C5C5C; }";
void updateStyle(bool enabled) {
QString enabled_style = enabled ? btn_enabled_on_style : btn_enabled_off_style;
QString pressed_style = enabled ? btn_on_pressed_style : btn_off_pressed_style;
setStyleSheet(buttonStyle + enabled_style + pressed_style + btn_disabled_style);
setStyleSheet(buttonStyle + enabled_style + btn_pressed_style + btn_disabled_stype);
}
};
+3 -11
View File
@@ -37,7 +37,7 @@ DATA: dict[str, capnp.lib.capnp._DynamicStructBuilder] = dict.fromkeys(
"driverStateV2", "roadCameraState", "wideRoadCameraState", "driverCameraState"], None)
def setup_homescreen(click, pm: PubMaster, scroll=None):
time.sleep(UI_DELAY)
pass
def setup_settings_device(click, pm: PubMaster, scroll=None):
click(100, 100)
@@ -61,7 +61,6 @@ def setup_settings_software(click, pm: PubMaster, scroll=None):
time.sleep(UI_DELAY)
def setup_settings_firehose(click, pm: PubMaster, scroll=None):
setup_settings_device(click, pm)
scroll(-400, 278, 962)
click(278, 862)
@@ -152,7 +151,7 @@ def setup_keyboard_uppercase(click, pm: PubMaster, scroll=None):
def setup_driver_camera(click, pm: PubMaster, scroll=None):
setup_settings_device(click, pm)
click(1720, 825)
click(1720, 620)
DATA['deviceState'].deviceState.started = False
setup_onroad(click, pm)
DATA['deviceState'].deviceState.started = True
@@ -240,15 +239,9 @@ def setup_settings_steering_alc(click, pm: PubMaster, scroll=None):
click(970, 534)
time.sleep(UI_DELAY)
def setup_settings_driving(click, pm: PubMaster, scroll=None):
setup_settings_device(click, pm)
click(278, 962)
time.sleep(UI_DELAY)
def setup_settings_trips(click, pm: PubMaster, scroll=None):
setup_settings_device(click, pm)
scroll(-400, 278, 962)
click(278, 646)
click(278, 962)
time.sleep(UI_DELAY)
def setup_settings_vehicle(click, pm: PubMaster, scroll=None):
@@ -298,7 +291,6 @@ CASES.update({
"settings_steering": setup_settings_steering,
"settings_steering_mads": setup_settings_steering_mads,
"settings_steering_alc": setup_settings_steering_alc,
"settings_driving": setup_settings_driving,
"settings_trips": setup_settings_trips,
"settings_vehicle": setup_settings_vehicle,
})
-52
View File
@@ -1,52 +0,0 @@
import numpy as np
def compute_symmetric_slopes(x, y):
"""
Compute symmetric slopes for use in Hermite interpolation.
Args:
x: Array of x coordinates
y: Array of y coordinates
Returns:
Array of computed slopes at each point
"""
n = len(x)
m = np.zeros(n)
for i in range(n):
if i == 0:
m[i] = (y[i+1] - y[i]) / (x[i+1] - x[i])
elif i == n-1:
m[i] = (y[i] - y[i-1]) / (x[i] - x[i-1])
else:
m[i] = ((y[i+1] - y[i]) / (x[i+1] - x[i]) + (y[i] - y[i-1]) / (x[i] - x[i-1])) / 2
return m
def hermite_interpolate(x, xp, yp, slopes):
"""
Perform Hermite interpolation at point x given data points and slopes.
Args:
x: Point at which to interpolate
xp: Array of x coordinates of data points
yp: Array of y coordinates of data points
slopes: Array of slopes at data points
Returns:
Interpolated value at x
"""
x = np.clip(x, xp[0], xp[-1])
idx = np.searchsorted(xp, x) - 1
idx = np.clip(idx, 0, len(slopes) - 2)
x0, x1 = xp[idx], xp[idx+1]
y0, y1 = yp[idx], yp[idx+1]
m0, m1 = slopes[idx], slopes[idx+1]
t = (x - x0) / (x1 - x0)
h00 = 2*t**3 - 3*t**2 + 1
h10 = t**3 - 2*t**2 + t
h01 = -2*t**3 + 3*t**2
h11 = t**3 - t**2
return (h00 * y0) + (h10 * (x1 - x0) * m0) + (h01 * y1) + (h11 * (x1 - x0) * m1)
+1 -1
View File
@@ -72,7 +72,7 @@ class ModularAssistiveDrivingSystem:
if self.events.has(EventName.seatbeltNotLatched):
replace_event(EventName.seatbeltNotLatched, EventNameSP.silentSeatbeltNotLatched)
transition_paused_state()
if self.events.has(EventName.wrongGear) and (CS.vEgo < 2.5 or CS.gearShifter == GearShifter.reverse):
if self.events.has(EventName.wrongGear) and (CS.standstill or CS.gearShifter == GearShifter.reverse):
replace_event(EventName.wrongGear, EventNameSP.silentWrongGear)
transition_paused_state()
if self.events.has(EventName.reverseGear):
-5
View File
@@ -52,8 +52,3 @@ commonmodel_lib = lenv.Library('commonmodel', common_src)
lenvCython.Program('runners/runmodel_pyx.so', 'runners/runmodel_pyx.pyx', LIBS=cython_libs, FRAMEWORKS=frameworks)
lenvCython.Program('runners/snpemodel_pyx.so', 'runners/snpemodel_pyx.pyx', LIBS=[snpemodel_lib, snpe_lib, *cython_libs], FRAMEWORKS=frameworks, RPATH=snpe_rpath)
lenvCython.Program('models/commonmodel_pyx.so', 'models/commonmodel_pyx.pyx', LIBS=[commonmodel_lib, *cython_libs], FRAMEWORKS=frameworks)
if arch == "larch64":
thneed_lib = env.SharedLibrary('thneed', thneed_src, LIBS=[gpucommon, common, 'OpenCL', 'dl'])
thneedmodel_lib = env.Library('thneedmodel', ['runners/thneedmodel.cc'])
lenvCython.Program('runners/thneedmodel_pyx.so', 'runners/thneedmodel_pyx.pyx', LIBS=envCython["LIBS"]+[thneedmodel_lib, thneed_lib, gpucommon, common, 'dl', 'OpenCL'])
+1 -1
View File
@@ -23,7 +23,7 @@ from openpilot.sunnypilot.modeld.parse_model_outputs import Parser
from openpilot.sunnypilot.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState
from openpilot.sunnypilot.modeld.constants import ModelConstants
from openpilot.sunnypilot.modeld.models.commonmodel_pyx import ModelFrame, CLContext
from openpilot.sunnypilot.models.helpers import get_model_path, load_metadata, prepare_inputs, load_meta_constants
from openpilot.sunnypilot.modeld.runners.run_helpers import get_model_path, load_metadata, prepare_inputs, load_meta_constants
PROCESS_NAME = "selfdrive.modeld.modeld_snpe"
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
+92
View File
@@ -0,0 +1,92 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import os
import pickle
import numpy as np
from cereal import custom
from openpilot.sunnypilot.modeld.constants import Meta, MetaTombRaider, MetaSimPose
from openpilot.sunnypilot.modeld.runners import ModelRunner
from openpilot.sunnypilot.models.helpers import get_active_bundle
from openpilot.system.hardware import PC
from openpilot.system.hardware.hw import Paths
from pathlib import Path
USE_ONNX = os.getenv('USE_ONNX', PC)
CUSTOM_MODEL_PATH = Paths.model_root()
METADATA_PATH = Path(__file__).parent / '../models/supercombo_metadata.pkl'
ModelManager = custom.ModelManagerSP
def get_model_path():
if USE_ONNX:
return {ModelRunner.ONNX: Path(__file__).parent / '../models/supercombo.onnx'}
if bundle := get_active_bundle():
drive_model = next(model for model in bundle.models if model.type == ModelManager.Type.drive)
return {ModelRunner.THNEED: f"{CUSTOM_MODEL_PATH}/{drive_model.fileName}"}
return {ModelRunner.THNEED: Path(__file__).parent / '../models/supercombo.thneed'}
def load_metadata():
metadata_path = METADATA_PATH
if bundle := get_active_bundle():
metadata_model = next(model for model in bundle.models if model.type == ModelManager.Type.metadata)
metadata_path = f"{CUSTOM_MODEL_PATH}/{metadata_model.fileName}"
with open(metadata_path, 'rb') as f:
return pickle.load(f)
def prepare_inputs(model_metadata) -> dict[str, np.ndarray]:
# img buffers are managed in openCL transform code so we don't pass them as inputs
inputs = {
k: np.zeros(v, dtype=np.float32).flatten()
for k, v in model_metadata['input_shapes'].items()
if 'img' not in k
}
return inputs
def load_meta_constants(model_metadata):
"""
Determines and loads the appropriate meta model class based on the metadata provided. The function checks
specific keys and conditions within the provided metadata dictionary to identify the corresponding meta
model class to return.
:param model_metadata: Dictionary containing metadata about the model. It includes
details such as input shapes, output slices, and other configurations for identifying
metadata-dependent meta model classes.
:type model_metadata: dict
:return: The appropriate meta model class (Meta, MetaSimPose, or MetaTombRaider)
based on the conditions and metadata provided.
:rtype: type
"""
meta = Meta # Default Meta
if 'sim_pose' in model_metadata['input_shapes'].keys():
# Meta for models with sim_pose input
meta = MetaSimPose
else:
# Meta for Tomb Raider, it does not include sim_pose input but has the same meta slice as previous models
meta_slice = model_metadata['output_slices']['meta']
meta_tf_slice = slice(5868, 5921, None)
if (
meta_slice.start == meta_tf_slice.start and
meta_slice.stop == meta_tf_slice.stop and
meta_slice.step == meta_tf_slice.step
):
meta = MetaTombRaider
return meta
+7 -15
View File
@@ -8,7 +8,6 @@ from openpilot.sunnypilot.modeld_v2 import MODEL_PATH, MODEL_PKL_PATH, METADATA_
from openpilot.sunnypilot.modeld_v2.models.commonmodel_pyx import DrivingModelFrame, CLMem
from openpilot.sunnypilot.modeld_v2.runners.ort_helpers import make_onnx_cpu_runner, ORT_TYPES_TO_NP_TYPES
from openpilot.sunnypilot.modeld_v2.runners.tinygrad_helpers import qcom_tensor_from_opencl_address
from openpilot.sunnypilot.modeld_v2.parse_model_outputs import Parser
from openpilot.system.hardware import TICI
from openpilot.system.hardware.hw import Paths
@@ -35,8 +34,8 @@ class ModelRunner(ABC):
if bundle := get_active_bundle():
bundle_models = {model.type.raw: model for model in bundle.models}
self._drive_model = bundle_models.get(ModelManager.Model.Type.supercombo)
self._metadata_model = self._drive_model.metadata
self._drive_model = bundle_models.get(ModelManager.Type.drive)
self._metadata_model = bundle_models.get(ModelManager.Type.metadata)
self.is_20hz = bundle.is20hz
# Override the metadata path if a metadata model is found in the active bundle
@@ -49,7 +48,6 @@ class ModelRunner(ABC):
self.input_shapes = self.model_metadata['input_shapes']
self.output_slices = self.model_metadata['output_slices']
self.inputs: dict = {}
self.parser = Parser()
@abstractmethod
def prepare_inputs(self, imgs_cl: dict[str, CLMem], numpy_inputs: dict[str, np.ndarray], frames: dict[str, DrivingModelFrame]) -> dict:
@@ -57,22 +55,16 @@ class ModelRunner(ABC):
raise NotImplementedError
@abstractmethod
def _run_model(self):
def run_model(self):
"""Run model inference with prepared inputs."""
raise NotImplementedError("This method should be implemented in subclasses.")
def _slice_outputs(self, model_outputs: np.ndarray) -> dict:
def slice_outputs(self, model_outputs: np.ndarray) -> dict:
"""Slice model outputs according to metadata configuration."""
parsed_outputs = {k: model_outputs[np.newaxis, v] for k, v in self.output_slices.items()}
if SEND_RAW_PRED:
parsed_outputs['raw_pred'] = model_outputs.copy()
return parsed_outputs
def run_model(self) -> dict[str, np.ndarray]:
"""Run model inference with prepared inputs and parse outputs."""
result: dict[str, np.ndarray] = self.parser.parse_outputs(self._slice_outputs(self._run_model()))
return result
class TinygradRunner(ModelRunner):
"""Tinygrad implementation of model runner for TICI hardware."""
@@ -82,7 +74,7 @@ class TinygradRunner(ModelRunner):
model_pkl_path = MODEL_PKL_PATH
if self._drive_model:
model_pkl_path = f"{CUSTOM_MODEL_PATH}/{self._drive_model.artifact.fileName}"
model_pkl_path = f"{CUSTOM_MODEL_PATH}/{self._drive_model.fileName}"
assert model_pkl_path.endswith('_tinygrad.pkl'), f"Invalid model file: {model_pkl_path} for TinygradRunner"
# Load Tinygrad model
@@ -116,7 +108,7 @@ class TinygradRunner(ModelRunner):
return self.inputs
def _run_model(self):
def run_model(self):
return self.model_run(**self.inputs).numpy().flatten()
@@ -138,5 +130,5 @@ class ONNXRunner(ModelRunner):
self.inputs[key] = frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.input_shapes[key]).astype(dtype=self.input_to_nptype[key])
return self.inputs
def _run_model(self):
def run_model(self):
return self.runner.run(None, self.inputs)[0].flatten()
+21 -14
View File
@@ -18,6 +18,7 @@ from openpilot.common.transformations.camera import DEVICE_CAMERAS
from openpilot.common.transformations.model import get_warp_matrix
from openpilot.system import sentry
from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper
from openpilot.sunnypilot.modeld_v2.parse_model_outputs import Parser
from openpilot.sunnypilot.modeld_v2.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState
from openpilot.sunnypilot.modeld_v2.constants import ModelConstants
from openpilot.sunnypilot.modeld_v2.models.commonmodel_pyx import DrivingModelFrame, CLContext
@@ -26,7 +27,6 @@ from openpilot.sunnypilot.modeld_v2.meta_helper import load_meta_constants
from openpilot.sunnypilot.modeld_v2.model_runner import ONNXRunner, TinygradRunner
PROCESS_NAME = "selfdrive.modeld.modeld"
LAT_SMOOTH_SECONDS = 0.0
class FrameMeta:
@@ -41,6 +41,7 @@ class FrameMeta:
class ModelState:
frames: dict[str, DrivingModelFrame]
inputs: dict[str, np.ndarray]
output: np.ndarray
prev_desire: np.ndarray # for tracking the rising edge of the pulse
def __init__(self, context: CLContext):
@@ -53,8 +54,8 @@ class ModelState:
self.frames = {'input_imgs': DrivingModelFrame(context, buffer_length), 'big_input_imgs': DrivingModelFrame(context, buffer_length)}
self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32)
if self.model_runner.is_20hz:
self.full_features_buffer = np.zeros((ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32)
self.full_desire = np.zeros((ModelConstants.FULL_HISTORY_BUFFER_LEN + 1, ModelConstants.DESIRE_LEN), dtype=np.float32)
self.full_features_20Hz = np.zeros((ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32)
self.desire_20Hz = np.zeros((ModelConstants.FULL_HISTORY_BUFFER_LEN + 1, ModelConstants.DESIRE_LEN), dtype=np.float32)
# img buffers are managed in openCL transform code
self.numpy_inputs = {}
@@ -63,10 +64,15 @@ class ModelState:
if key not in self.frames: # Managed by opencl
self.numpy_inputs[key] = np.zeros(shape, dtype=np.float32)
self.parser = Parser()
if self.model_runner.is_20hz:
net_output_size = self.model_runner.model_metadata['output_shapes']['outputs'][1]
self.output = np.zeros(net_output_size, dtype=np.float32)
num_elements = self.numpy_inputs['features_buffer'].shape[1]
step_size = int(-100 / num_elements)
self.full_features_buffer_idxs = np.arange(step_size, step_size * (num_elements + 1), step_size)[::-1]
self.full_features_20Hz_idxs = np.arange(step_size, step_size * (num_elements + 1), step_size)[::-1]
self.desire_reshape_dims = (self.numpy_inputs['desire'].shape[0], self.numpy_inputs['desire'].shape[1], -1, self.numpy_inputs['desire'].shape[2])
def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray,
@@ -77,9 +83,9 @@ class ModelState:
self.prev_desire[:] = inputs['desire']
if self.model_runner.is_20hz:
self.full_desire[:-1] = self.full_desire[1:]
self.full_desire[-1] = new_desire
self.numpy_inputs['desire'][:] = self.full_desire.reshape(self.desire_reshape_dims).max(axis=2)
self.desire_20Hz[:-1] = self.desire_20Hz[1:]
self.desire_20Hz[-1] = new_desire
self.numpy_inputs['desire'][:] = self.desire_20Hz.reshape(self.desire_reshape_dims).max(axis=2)
else:
length = inputs['desire'].shape[0]
self.numpy_inputs['desire'][0, :-1] = self.numpy_inputs['desire'][0, 1:]
@@ -99,12 +105,13 @@ class ModelState:
return None
# Run model inference
outputs = self.model_runner.run_model()
self.output = self.model_runner.run_model()
outputs = self.parser.parse_outputs(self.model_runner.slice_outputs(self.output))
if self.model_runner.is_20hz:
self.full_features_buffer[:-1] = self.full_features_buffer[1:]
self.full_features_buffer[-1] = outputs['hidden_state'][0, :]
self.numpy_inputs['features_buffer'][:] = self.full_features_buffer[self.full_features_buffer_idxs]
self.full_features_20Hz[:-1] = self.full_features_20Hz[1:]
self.full_features_20Hz[-1] = outputs['hidden_state'][0, :]
self.numpy_inputs['features_buffer'][:] = self.full_features_20Hz[self.full_features_20Hz_idxs]
else:
feature_len = outputs['hidden_state'].shape[1]
self.numpy_inputs['features_buffer'][0, :-1] = self.numpy_inputs['features_buffer'][0, 1:]
@@ -164,7 +171,7 @@ def main(demo=False):
# messaging
pm = PubMaster(["modelV2", "drivingModelData", "cameraOdometry"])
sm = SubMaster(["deviceState", "carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "carControl", "liveDelay"])
sm = SubMaster(["deviceState", "carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "carControl"])
publish_state = PublishState()
params = Params()
@@ -189,8 +196,8 @@ def main(demo=False):
CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams)
cloudlog.info("modeld got CarParams: %s", CP.brand)
# Enable lagd support for modeld_v2
steer_delay = sm["liveDelay"].lateralDelay + LAT_SMOOTH_SECONDS
# TODO this needs more thought, use .2s extra for now to estimate other delays
steer_delay = CP.steerActuatorDelay + .2
DH = DesireHelper()
+52 -47
View File
@@ -11,7 +11,6 @@ import time
import requests
from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
from sunnypilot.models.helpers import is_bundle_version_compatible
from cereal import custom
@@ -20,49 +19,68 @@ class ModelParser:
"""Handles parsing of model data into cereal objects"""
@staticmethod
def _parse_download_uri(download_uri_data) -> custom.ModelManagerSP.DownloadUri:
download_uri = custom.ModelManagerSP.DownloadUri()
download_uri.uri = download_uri_data.get("url")
download_uri.sha256 = download_uri_data.get("sha256")
return download_uri
@staticmethod
def _parse_artifact(artifact_data) -> custom.ModelManagerSP.Artifact:
artifact = custom.ModelManagerSP.Artifact()
artifact.fileName = artifact_data.get("file_name")
artifact.downloadUri = ModelParser._parse_download_uri(artifact_data.get("download_uri", {}))
return artifact
@staticmethod
def _parse_model(model_data) -> custom.ModelManagerSP.Model:
def _parse_model(full_name: str, file_name: str, uri_data: dict,
model_type: custom.ModelManagerSP.Type) -> custom.ModelManagerSP.Model:
model = custom.ModelManagerSP.Model()
download_uri = custom.ModelManagerSP.DownloadUri()
download_uri.uri = uri_data["url"]
download_uri.sha256 = uri_data["sha256"]
model.fullName = full_name
model.fileName = file_name
model.downloadUri = download_uri
model.type = model_type
model.type = model_data.get("type")
model.artifact = ModelParser._parse_artifact(model_data.get("artifact", {}))
if metadata := model_data.get("metadata"):
model.metadata = ModelParser._parse_artifact(metadata)
return model
@staticmethod
def _parse_bundle(bundle) -> custom.ModelManagerSP.ModelBundle:
def _parse_bundle(key: str, value: dict) -> custom.ModelManagerSP.ModelBundle:
model_bundle = custom.ModelManagerSP.ModelBundle()
model_bundle.index = int(bundle["index"])
model_bundle.internalName = bundle["short_name"]
model_bundle.displayName = bundle["display_name"]
model_bundle.models = [ModelParser._parse_model(model) for model in bundle.get("models",[])]
# Parse main driving model
models = [
ModelParser._parse_model(
value["full_name"],
value["file_name"],
value["download_uri"],
custom.ModelManagerSP.Type.drive
)
]
# Parse navigation model if exists
if value.get("download_uri_nav"):
models.append(ModelParser._parse_model(
value["full_name_nav"],
value["file_name_nav"],
value["download_uri_nav"],
custom.ModelManagerSP.Type.navigation
))
# Parse metadata model if exists
if value.get("download_uri_metadata"):
models.append(ModelParser._parse_model(
value["full_name_metadata"],
value["file_name_metadata"],
value["download_uri_metadata"],
custom.ModelManagerSP.Type.metadata
))
model_bundle.index = int(value["index"])
model_bundle.internalName = key
model_bundle.displayName = value["display_name"]
model_bundle.models = models
model_bundle.status = 0
model_bundle.generation = int(bundle["generation"])
model_bundle.environment = bundle["environment"]
model_bundle.runner = bundle.get("runner", custom.ModelManagerSP.Runner.snpe)
model_bundle.is20hz = bundle.get("is_20hz", False)
model_bundle.minimumSelectorVersion = int(bundle["minimum_selector_version"])
model_bundle.generation = int(value["generation"])
model_bundle.environment = value["environment"]
model_bundle.runner = value.get("runner", custom.ModelManagerSP.Runner.snpe)
model_bundle.is20hz = value.get("is_20hz", False)
return model_bundle
@staticmethod
def parse_models(json_data: dict) -> list[custom.ModelManagerSP.ModelBundle]:
found_bundles = [ModelParser._parse_bundle(bundle) for bundle in json_data.get("bundles", [])]
return [bundle for bundle in found_bundles if is_bundle_version_compatible(bundle.to_dict())]
return [ModelParser._parse_bundle(key, value) for key, value in json_data.items()]
class ModelCache:
@@ -104,7 +122,7 @@ class ModelCache:
class ModelFetcher:
"""Handles fetching and caching of model data from remote source"""
MODEL_URL = "https://docs.sunnypilot.ai/driving_models_v3.json"
MODEL_URL = "https://docs.sunnypilot.ai/driving_models_v2.json"
def __init__(self, params: Params):
self.params = params
@@ -125,7 +143,7 @@ class ModelFetcher:
cloudlog.exception("Error fetching models")
raise
def get_available_bundles(self) -> list[custom.ModelManagerSP.ModelBundle]:
def get_available_models(self) -> list[custom.ModelManagerSP.ModelBundle]:
"""Gets the list of available models, with smart cache handling"""
cached_data, is_expired = self.model_cache.get()
@@ -142,16 +160,3 @@ class ModelFetcher:
cloudlog.warning("Failed to fetch fresh data. Using expired cache as fallback")
return self.model_parser.parse_models(cached_data)
if __name__ == "__main__":
params = Params()
model_fetcher = ModelFetcher(params)
bundles = model_fetcher.get_available_bundles()
for bundle in bundles:
for model in bundle.models:
# Print model details
print(f"Bundle: {bundle.internalName}, Type: {model.type}, Status: {bundle.status}")
# Print artifact details
print(f"Artifact: {model.artifact.fileName}, Download URI: {model.artifact.downloadUri.uri}")
# Print metadata details
print(f"Metadata: {model.metadata.fileName}, Download URI: {model.metadata.downloadUri.uri}")
+3 -116
View File
@@ -7,27 +7,8 @@ See the LICENSE.md file in the root directory for more details.
import hashlib
import os
import pickle
import numpy as np
import json
from openpilot.common.params import Params
from cereal import custom
from openpilot.sunnypilot.modeld.constants import Meta, MetaTombRaider, MetaSimPose
from openpilot.sunnypilot.modeld.runners import ModelRunner
from openpilot.system.hardware import PC
from openpilot.system.hardware.hw import Paths
from pathlib import Path
CURRENT_SELECTOR_VERSION = 3
REQUIRED_MIN_SELECTOR_VERSION = 2
USE_ONNX = os.getenv('USE_ONNX', PC)
CUSTOM_MODEL_PATH = Paths.model_root()
METADATA_PATH = Path(__file__).parent / '../models/supercombo_metadata.pkl'
ModelManager = custom.ModelManagerSP
from cereal import custom, messaging
async def verify_file(file_path: str, expected_hash: str) -> bool:
@@ -43,36 +24,13 @@ async def verify_file(file_path: str, expected_hash: str) -> bool:
return sha256_hash.hexdigest().lower() == expected_hash.lower()
def is_bundle_version_compatible(bundle: dict) -> bool:
"""
Checks whether the model bundle is compatible with the current selector version constraints.
The bundle specifies a `minimum_selector_version`, which defines the minimum selector version
required to load the model. This function ensures that:
1. The model is not too old: the bundle must require at least `REQUIRED_MIN_SELECTOR_VERSION`.
2. The model is not too new: it must support the current selector version (`CURRENT_SELECTOR_VERSION`).
This allows the selector to enforce both a minimum and maximum range of supported models,
even if a model would otherwise be compatible.
:param bundle: Dictionary containing `minimum_selector_version`, as defined by the model bundle.
:type bundle: Dict
:return: True if the selector version is within the accepted range for the bundle; otherwise False.
:rtype: Bool
"""
return bool(REQUIRED_MIN_SELECTOR_VERSION <= bundle.get("minimumSelectorVersion", 0) <= CURRENT_SELECTOR_VERSION)
def get_active_bundle(params: Params = None) -> custom.ModelManagerSP.ModelBundle:
"""Gets the active model bundle from cache"""
if params is None:
params = Params()
try:
if (active_bundle := json.loads(params.get("ModelManager_ActiveBundle") or "{}")) and is_bundle_version_compatible(active_bundle):
return custom.ModelManagerSP.ModelBundle(**active_bundle)
except Exception:
pass
if active_bundle := params.get("ModelManager_ActiveBundle"):
return messaging.log_from_bytes(active_bundle, custom.ModelManagerSP.ModelBundle)
return None
@@ -113,74 +71,3 @@ def get_active_model_runner(params: Params = None, force_check=False) -> custom.
params.put("ModelRunnerTypeCache", str(int(runner_type)))
return runner_type
def _get_model():
if bundle := get_active_bundle():
drive_model = next(model for model in bundle.models if model.type == ModelManager.Model.Type.supercombo)
return drive_model
return None
def get_model_path():
if USE_ONNX:
return {ModelRunner.ONNX: Path(__file__).parent / '../models/supercombo.onnx'}
if model := _get_model():
return {ModelRunner.THNEED: f"{CUSTOM_MODEL_PATH}/{model.artifact.fileName}"}
return {ModelRunner.THNEED: Path(__file__).parent / '../models/supercombo.thneed'}
def load_metadata():
metadata_path = METADATA_PATH
if model := _get_model():
metadata_path = f"{CUSTOM_MODEL_PATH}/{model.metadata.fileName}"
with open(metadata_path, 'rb') as f:
return pickle.load(f)
def prepare_inputs(model_metadata) -> dict[str, np.ndarray]:
# img buffers are managed in openCL transform code so we don't pass them as inputs
inputs = {
k: np.zeros(v, dtype=np.float32).flatten()
for k, v in model_metadata['input_shapes'].items()
if 'img' not in k
}
return inputs
def load_meta_constants(model_metadata):
"""
Determines and loads the appropriate meta model class based on the metadata provided. The function checks
specific keys and conditions within the provided metadata dictionary to identify the corresponding meta
model class to return.
:param model_metadata: Dictionary containing metadata about the model. It includes
details such as input shapes, output slices, and other configurations for identifying
metadata-dependent meta model classes.
:type model_metadata: dict
:return: The appropriate meta model class (Meta, MetaSimPose, or MetaTombRaider)
based on the conditions and metadata provided.
:rtype: type
"""
meta = Meta # Default Meta
if 'sim_pose' in model_metadata['input_shapes'].keys():
# Meta for models with sim_pose input
meta = MetaSimPose
else:
# Meta for Tomb Raider, it does not include sim_pose input but has the same meta slice as previous models
meta_slice = model_metadata['output_slices']['meta']
meta_tf_slice = slice(5868, 5921, None)
if (
meta_slice.start == meta_tf_slice.start and
meta_slice.stop == meta_tf_slice.stop and
meta_slice.step == meta_tf_slice.step
):
meta = MetaTombRaider
return meta
+15 -27
View File
@@ -8,7 +8,6 @@ See the LICENSE.md file in the root directory for more details.
import asyncio
import os
import time
import json
import aiohttp
from openpilot.common.params import Params
@@ -74,54 +73,43 @@ class ModelManagerSP:
# Clean up start time after download completes
del self._download_start_times[model.fileName]
async def _process_artifact(self, artifact, destination_path: str) -> None:
async def _process_model(self, model, destination_path: str) -> None:
"""Processes a single model download including verification"""
if not artifact.downloadUri.uri:
return None
url = artifact.downloadUri.uri
expected_hash = artifact.downloadUri.sha256
filename = artifact.fileName
url = model.downloadUri.uri
expected_hash = model.downloadUri.sha256
filename = model.fileName
full_path = os.path.join(destination_path, filename)
try:
# Check existing file
if os.path.exists(full_path) and await verify_file(full_path, expected_hash):
artifact.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.cached
artifact.downloadProgress.progress = 100
artifact.downloadProgress.eta = 0
model.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.cached
model.downloadProgress.progress = 100
model.downloadProgress.eta = 0
self._report_status()
return
# Download and verify
await self._download_file(url, full_path, artifact)
await self._download_file(url, full_path, model)
if not await verify_file(full_path, expected_hash):
raise ValueError(f"Hash validation failed for {filename}")
artifact.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.downloaded
artifact.downloadProgress.eta = 0
model.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.downloaded
model.downloadProgress.eta = 0
self._report_status()
except Exception as e:
cloudlog.error(f"Error downloading {filename}: {str(e)}")
if os.path.exists(full_path):
os.remove(full_path)
artifact.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.failed
artifact.downloadProgress.eta = 0
model.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.failed
model.downloadProgress.eta = 0
self.selected_bundle.status = custom.ModelManagerSP.DownloadStatus.failed
self._report_status()
# Clean up start time if it exists
self._download_start_times.pop(artifact.fileName, None)
self._download_start_times.pop(model.fileName, None)
raise
async def _process_model(self, model, destination_path: str) -> None:
"""Processes a single model download including verification"""
model_artifact = model.artifact
metadata_artifact = model.metadata
await self._process_artifact(metadata_artifact, destination_path)
await self._process_artifact(model_artifact, destination_path)
def _report_status(self) -> None:
"""Reports current status through messaging system"""
msg = messaging.new_message('modelManagerSP', valid=True)
@@ -146,7 +134,7 @@ class ModelManagerSP:
await asyncio.gather(*tasks)
self.active_bundle = self.selected_bundle
self.active_bundle.status = custom.ModelManagerSP.DownloadStatus.downloaded
self.params.put("ModelManager_ActiveBundle", json.dumps(self.active_bundle.to_dict()))
self.params.put("ModelManager_ActiveBundle", self.active_bundle.to_bytes())
self.selected_bundle = None
except Exception:
@@ -166,7 +154,7 @@ class ModelManagerSP:
while True:
try:
self.available_models = self.model_fetcher.get_available_bundles()
self.available_models = self.model_fetcher.get_available_models()
self.active_bundle = get_active_bundle(self.params)
if index_to_download := self.params.get("ModelManager_DownloadIndex", block=False, encoding="utf-8"):
+1 -1
View File
@@ -1 +1 @@
8ef2dbcae743eb132167074a374f0a834308be31cffd532598bb13c3d7144a57
82ead1a4ecffbde961382329a6cf71bc97a6b112fbfb1f175a51c28ee2472bec
+5 -25
View File
@@ -11,7 +11,6 @@ from opendbc.car.car_helpers import can_fingerprint
from opendbc.car.interfaces import CarInterfaceBase
from opendbc.car.hyundai.radar_interface import RADAR_START_ADDR
from opendbc.car.hyundai.values import HyundaiFlags, DBC as HYUNDAI_DBC
from opendbc.sunnypilot.car.hyundai.longitudinal.helpers import LongitudinalTuningType
from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP
from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
@@ -26,24 +25,9 @@ def log_fingerprint(CP: structs.CarParams) -> None:
else:
sentry.capture_fingerprint(CP.carFingerprint, CP.brand)
def _initialize_custom_longitudinal_tuning(CI: CarInterfaceBase, CP: structs.CarParams, CP_SP: structs.CarParamsSP,
params: Params = None) -> None:
if params is None:
params = Params()
# Hyundai Custom Longitudinal Tuning
if CP.brand == 'hyundai':
hyundai_longitudinal_tuning = int(params.get("HyundaiLongitudinalTuning", encoding="utf8") or 0)
if hyundai_longitudinal_tuning == LongitudinalTuningType.DYNAMIC:
CP_SP.flags |= HyundaiFlagsSP.LONG_TUNING_DYNAMIC.value
if hyundai_longitudinal_tuning == LongitudinalTuningType.PREDICTIVE:
CP_SP.flags |= HyundaiFlagsSP.LONG_TUNING_PREDICTIVE.value
CP_SP = CI.get_longitudinal_tuning_sp(CP, CP_SP)
def _initialize_neural_network_lateral_control(CI: CarInterfaceBase, CP: structs.CarParams, CP_SP: structs.CarParamsSP,
params: Params = None, enabled: bool = False) -> None:
def _initialize_neural_network_lateral_control(CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params = None,
enabled: bool = False) -> None:
if params is None:
params = Params()
@@ -56,7 +40,7 @@ def _initialize_neural_network_lateral_control(CI: CarInterfaceBase, CP: structs
enabled = params.get_bool("NeuralNetworkLateralControl")
if enabled:
CI.configure_torque_tune(CP.carFingerprint, CP.lateralTuning)
CarInterfaceBase.configure_torque_tune(CP.carFingerprint, CP.lateralTuning)
CP_SP.neuralNetworkLateralControl.model.path = nnlc_model_path
CP_SP.neuralNetworkLateralControl.model.name = nnlc_model_name
@@ -77,12 +61,8 @@ def _initialize_radar_tracks(CP: structs.CarParams, CP_SP: structs.CarParamsSP,
CP.radarUnavailable = False
def setup_interfaces(CI: CarInterfaceBase, params: Params = None) -> None:
CP = CI.CP
CP_SP = CI.CP_SP
_initialize_custom_longitudinal_tuning(CI, CP, CP_SP, params)
_initialize_neural_network_lateral_control(CI, CP, CP_SP, params)
def setup_interfaces(CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params = None):
_initialize_neural_network_lateral_control(CP, CP_SP, params)
_initialize_radar_tracks(CP, CP_SP, params)
@@ -1,39 +0,0 @@
from cereal import log
from openpilot.sunnypilot.common.utils import compute_symmetric_slopes, hermite_interpolate
class DynamicPersonalityController:
"""
Controller for managing dynamic personality-based following distances
that adapt based on vehicle speed and selected driving personality.
"""
def __init__(self):
pass
def get_dynamic_follow_distance(self, v_ego, personality=log.LongitudinalPersonality.standard):
"""
Calculate the dynamic follow distance based on current speed and personality.
Args:
v_ego: Current vehicle speed
personality: Selected longitudinal personality mode
Returns:
float: The calculated follow distance factor
"""
if personality == log.LongitudinalPersonality.relaxed:
x_vel = [0., 2.5, 5., 19.7, 22.2, 40.]
y_dist = [1.25, 1.25, 1.3, 1.3, 1.75, 1.75]
elif personality == log.LongitudinalPersonality.standard:
x_vel = [0., 2.5, 5., 19.7, 22.2, 40.]
y_dist = [1.20, 1.20, 1.275, 1.275, 1.50, 1.50]
elif personality == log.LongitudinalPersonality.aggressive:
x_vel = [0., 2.5, 6., 19.7, 22.2, 40.]
y_dist = [0.92, 0.9, 1.25, 1.25, 1.30, 1.30]
else:
raise NotImplementedError("Dynamic personality not supported")
slopes = compute_symmetric_slopes(x_vel, y_dist)
result = float(hermite_interpolate(v_ego, x_vel, y_dist, slopes))
return result
@@ -1,116 +0,0 @@
import pytest
import numpy as np
from cereal import log
from openpilot.sunnypilot.selfdrive.controls.lib.dynamic_personality.dynamic_personality_controller import DynamicPersonalityController
from openpilot.sunnypilot.common.utils import compute_symmetric_slopes, hermite_interpolate
class TestDynamicPersonalityController:
def setup_method(self):
"""Set up the controller before each test"""
self.controller = DynamicPersonalityController()
def test_compute_symmetric_slopes(self):
"""Test the symmetric slope computation method"""
x = np.array([1.0, 2.0, 3.0, 4.0])
y = np.array([2.0, 3.0, 5.0, 8.0])
# Manual calculation of expected slopes based on the algorithm:
# First slope: direct derivative from first segment
# Middle slopes: average of adjacent segments
# Last slope: direct derivative from last segment
expected_slopes = np.zeros(4)
expected_slopes[0] = (y[1] - y[0]) / (x[1] - x[0]) # 1.0
expected_slopes[1] = ((y[2] - y[1]) / (x[2] - x[1]) + (y[1] - y[0]) / (x[1] - x[0])) / 2 # (2.0 + 1.0) / 2 = 1.5
expected_slopes[2] = ((y[3] - y[2]) / (x[3] - x[2]) + (y[2] - y[1]) / (x[2] - x[1])) / 2 # (3.0 + 2.0) / 2 = 2.5
expected_slopes[3] = (y[3] - y[2]) / (x[3] - x[2]) # 3.0
computed_slopes = compute_symmetric_slopes(x, y)
np.testing.assert_allclose(computed_slopes, expected_slopes, rtol=1e-5)
def test_hermite_interpolate(self):
"""Test hermite interpolation with known values"""
xp = np.array([0.0, 10.0, 20.0])
yp = np.array([5.0, 15.0, 10.0])
slopes = np.array([1.0, 0.0, -1.0])
# Test at data points
assert hermite_interpolate(0.0, xp, yp, slopes) == pytest.approx(5.0)
assert hermite_interpolate(10.0, xp, yp, slopes) == pytest.approx(15.0)
assert hermite_interpolate(20.0, xp, yp, slopes) == pytest.approx(10.0)
# Test interpolation
assert hermite_interpolate(5.0, xp, yp, slopes) == pytest.approx(11.25)
assert hermite_interpolate(15.0, xp, yp, slopes) == pytest.approx(13.75)
def test_hermite_interpolate_clipping(self):
"""Test that hermite interpolation properly clips values outside the range"""
xp = np.array([0.0, 10.0, 20.0])
yp = np.array([5.0, 15.0, 10.0])
slopes = np.array([1.0, 0.0, -1.0])
# Test clipping below minimum
assert hermite_interpolate(-5.0, xp, yp, slopes) == pytest.approx(5.0)
# Test clipping above maximum
assert hermite_interpolate(25.0, xp, yp, slopes) == pytest.approx(10.0)
def test_get_dynamic_follow_distance_relaxed(self):
"""Test follow distance calculation for relaxed personality"""
# Test at specific data points
assert self.controller.get_dynamic_follow_distance(0.0, log.LongitudinalPersonality.relaxed) == pytest.approx(1.25)
assert self.controller.get_dynamic_follow_distance(5.0, log.LongitudinalPersonality.relaxed) == pytest.approx(1.3)
assert self.controller.get_dynamic_follow_distance(40.0, log.LongitudinalPersonality.relaxed) == pytest.approx(1.75)
# Test interpolation
assert self.controller.get_dynamic_follow_distance(20.0, log.LongitudinalPersonality.relaxed) > 1.3
assert self.controller.get_dynamic_follow_distance(20.0, log.LongitudinalPersonality.relaxed) < 1.75
def test_get_dynamic_follow_distance_standard(self):
"""Test follow distance calculation for standard personality"""
# Test at specific data points
assert self.controller.get_dynamic_follow_distance(0.0, log.LongitudinalPersonality.standard) == pytest.approx(1.20)
assert self.controller.get_dynamic_follow_distance(5.0, log.LongitudinalPersonality.standard) == pytest.approx(1.275)
assert self.controller.get_dynamic_follow_distance(40.0, log.LongitudinalPersonality.standard) == pytest.approx(1.50)
# Test interpolation
mid_value = self.controller.get_dynamic_follow_distance(21.0, log.LongitudinalPersonality.standard)
assert mid_value > 1.275
assert mid_value < 1.50
def test_get_dynamic_follow_distance_aggressive(self):
"""Test follow distance calculation for aggressive personality"""
# Test at specific data points
assert self.controller.get_dynamic_follow_distance(0.0, log.LongitudinalPersonality.aggressive) == pytest.approx(0.92)
assert self.controller.get_dynamic_follow_distance(6.0, log.LongitudinalPersonality.aggressive) == pytest.approx(1.25)
assert self.controller.get_dynamic_follow_distance(40.0, log.LongitudinalPersonality.aggressive) == pytest.approx(1.30)
# Test intermediate value
assert self.controller.get_dynamic_follow_distance(4.0, log.LongitudinalPersonality.aggressive) > 0.9
assert self.controller.get_dynamic_follow_distance(4.0, log.LongitudinalPersonality.aggressive) < 1.25
def test_get_dynamic_follow_distance_invalid(self):
"""Test that an invalid personality raises NotImplementedError"""
with pytest.raises(NotImplementedError, match="Dynamic personality not supported"):
self.controller.get_dynamic_follow_distance(10.0, 999) # Using an invalid personality value
def test_get_dynamic_follow_distance_comparison(self):
"""Test that relaxed > standard > aggressive for follow distances"""
speed = 20.0
relaxed = self.controller.get_dynamic_follow_distance(speed, log.LongitudinalPersonality.relaxed)
standard = self.controller.get_dynamic_follow_distance(speed, log.LongitudinalPersonality.standard)
aggressive = self.controller.get_dynamic_follow_distance(speed, log.LongitudinalPersonality.aggressive)
assert relaxed > standard
assert standard > aggressive
def test_speed_impact_on_follow_distance(self):
"""Test that follow distance increases with speed"""
personality = log.LongitudinalPersonality.standard
low_speed = self.controller.get_dynamic_follow_distance(5.0, personality)
high_speed = self.controller.get_dynamic_follow_distance(30.0, personality)
assert high_speed > low_speed
@@ -7,9 +7,7 @@ See the LICENSE.md file in the root directory for more details.
from cereal import messaging, custom
from opendbc.car import structs
from openpilot.sunnypilot.models.helpers import get_active_model_runner
from openpilot.sunnypilot.selfdrive.controls.lib.dec.dec import DynamicExperimentalController
from openpilot.common.params import Params
DecState = custom.LongitudinalPlanSP.DynamicExperimentalControl.DynamicExperimentalControlState
@@ -17,18 +15,6 @@ DecState = custom.LongitudinalPlanSP.DynamicExperimentalControl.DynamicExperimen
class LongitudinalPlannerSP:
def __init__(self, CP: structs.CarParams, mpc):
self.dec = DynamicExperimentalController(CP, mpc)
self.is_stock = get_active_model_runner() == custom.ModelManagerSP.Runner.stock
self.params = Params()
self.param_read_counter = 0
self.dynamic_personality = False
self.read_param()
def read_param(self):
try:
self.dynamic_personality = self.params.get_bool("DynamicPersonality")
except AttributeError:
pass
def get_mpc_mode(self) -> str | None:
if not self.dec.active():
@@ -37,9 +23,6 @@ class LongitudinalPlannerSP:
return self.dec.mode()
def update(self, sm: messaging.SubMaster) -> None:
self.param_read_counter += 1
if self.param_read_counter % 50 == 0:
self.read_param()
self.dec.update(sm)
def publish_longitudinal_plan_sp(self, sm: messaging.SubMaster, pm: messaging.PubMaster) -> None:
@@ -24,7 +24,7 @@ class TestNNLCFingerprintBase:
CP_SP = CarInterface.get_non_essential_params_sp(CP, car_name)
CI = CarInterface(CP, CP_SP)
sunnypilot_interfaces.setup_interfaces(CI, Params())
sunnypilot_interfaces.setup_interfaces(CP, CP_SP, Params())
return CI
@@ -22,7 +22,7 @@ class TestNNTorqueModel:
CP_SP = CarInterface.get_non_essential_params_sp(CP, car_name)
CI = CarInterface(CP, CP_SP)
sunnypilot_interfaces.setup_interfaces(CI, params)
sunnypilot_interfaces.setup_interfaces(CP, CP_SP, params)
CP_SP = convert_to_capnp(CP_SP)
@@ -51,7 +51,7 @@ class TestNeuralNetworkLateralControl:
CP_SP = CarInterface.get_non_essential_params_sp(CP, car_name)
CI = CarInterface(CP, CP_SP)
sunnypilot_interfaces.setup_interfaces(CI, params)
sunnypilot_interfaces.setup_interfaces(CP, CP_SP, params)
CP_SP = convert_to_capnp(CP_SP)
VM = VehicleModel(CP)
+7 -7
View File
@@ -845,15 +845,15 @@ def ws_manage(ws: WebSocket, end_event: threading.Event) -> None:
onroad_prev = onroad
if sock is not None:
# While not sending data, onroad, we can expect to time out in 7 + (7 * 2) = 21s
# offroad, we can expect to time out in 30 + (10 * 3) = 60s
# FIXME: TCP_USER_TIMEOUT is effectively 2x for some reason (32s), so it's mostly unused
if sys.platform == 'linux':
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, 16000 if onroad else 0)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 7 if onroad else 30)
elif sys.platform == 'darwin':
if sys.platform == 'darwin': # macOS
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPALIVE, 7 if onroad else 30)
else:
# While not sending data, onroad, we can expect to time out in 7 + (7 * 2) = 21s
# offroad, we can expect to time out in 30 + (10 * 3) = 60s
# FIXME: TCP_USER_TIMEOUT is effectively 2x for some reason (32s), so it's mostly unused
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, 16000 if onroad else 0)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 7 if onroad else 30)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 7 if onroad else 10)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 2 if onroad else 3)
+12 -12
View File
@@ -56,29 +56,29 @@
},
{
"name": "boot",
"url": "https://commadist.azureedge.net/agnosupdate/boot-3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425.img.xz",
"hash": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425",
"hash_raw": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425",
"url": "https://commadist.azureedge.net/agnosupdate/boot-9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892.img.xz",
"hash": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892",
"hash_raw": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892",
"size": 18479104,
"sparse": false,
"full_check": true,
"has_ab": true,
"ondevice_hash": "2075104847d1c96a06f07e85efb9f48d0e792d75a059047eae7ba4b463ffeadf"
"ondevice_hash": "41d31b862fec1b87879b508c405adb9d7b5c0a3324f7350bd904f451605b06cf"
},
{
"name": "system",
"url": "https://commadist.azureedge.net/agnosupdate/system-b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7.img.xz",
"hash": "cb9bfde1e995b97f728f5d5ad8d7a0f7a01544db5d138ead9b2350f222640939",
"hash_raw": "b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7",
"size": 5368709120,
"url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img.xz",
"hash": "c56256a64e6d7e16886e39a4263ffb686ed0f03d3a665c3552f54a39723f8824",
"hash_raw": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde",
"size": 4404019200,
"sparse": true,
"full_check": false,
"has_ab": true,
"ondevice_hash": "e92a1f34158c60364c8d47b8ebbb6e59edf8d4865cd5edfeb2355d6f54f617fc",
"ondevice_hash": "ed2e11f52beb8559223bf9fb989fd4ef5d2ce66eeb11ae0053fff8e41903a533",
"alt": {
"hash": "b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7",
"url": "https://commadist.azureedge.net/agnosupdate/system-b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7.img",
"size": 5368709120
"hash": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde",
"url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img",
"size": 4404019200
}
}
]
+25 -25
View File
@@ -339,62 +339,62 @@
},
{
"name": "boot",
"url": "https://commadist.azureedge.net/agnosupdate/boot-3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425.img.xz",
"hash": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425",
"hash_raw": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425",
"url": "https://commadist.azureedge.net/agnosupdate/boot-9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892.img.xz",
"hash": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892",
"hash_raw": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892",
"size": 18479104,
"sparse": false,
"full_check": true,
"has_ab": true,
"ondevice_hash": "2075104847d1c96a06f07e85efb9f48d0e792d75a059047eae7ba4b463ffeadf"
"ondevice_hash": "41d31b862fec1b87879b508c405adb9d7b5c0a3324f7350bd904f451605b06cf"
},
{
"name": "system",
"url": "https://commadist.azureedge.net/agnosupdate/system-b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7.img.xz",
"hash": "cb9bfde1e995b97f728f5d5ad8d7a0f7a01544db5d138ead9b2350f222640939",
"hash_raw": "b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7",
"size": 5368709120,
"url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img.xz",
"hash": "c56256a64e6d7e16886e39a4263ffb686ed0f03d3a665c3552f54a39723f8824",
"hash_raw": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde",
"size": 4404019200,
"sparse": true,
"full_check": false,
"has_ab": true,
"ondevice_hash": "e92a1f34158c60364c8d47b8ebbb6e59edf8d4865cd5edfeb2355d6f54f617fc",
"ondevice_hash": "ed2e11f52beb8559223bf9fb989fd4ef5d2ce66eeb11ae0053fff8e41903a533",
"alt": {
"hash": "b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7",
"url": "https://commadist.azureedge.net/agnosupdate/system-b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7.img",
"size": 5368709120
"hash": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde",
"url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img",
"size": 4404019200
}
},
{
"name": "userdata_90",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_90-4bb7239f7e82c846e4d2584c0c433f03c582a80950de4094e6c190563d6d84ac.img.xz",
"hash": "b18001a2a87caa070fabf6321f8215ac353d6444564e3f86329b4dccc039ce54",
"hash_raw": "4bb7239f7e82c846e4d2584c0c433f03c582a80950de4094e6c190563d6d84ac",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_90-175a5d3353daa5e7b7d9939cb51a2f1d7e6312b4708ad654c351da2f1ef4f108.img.xz",
"hash": "ff01a0ca5a2ea6661f836248043a211cd8d71c3269c139cb574b56855fabc3f4",
"hash_raw": "175a5d3353daa5e7b7d9939cb51a2f1d7e6312b4708ad654c351da2f1ef4f108",
"size": 96636764160,
"sparse": true,
"full_check": true,
"has_ab": false,
"ondevice_hash": "15ce16f2349d5b4d5fec6ad1e36222b1ae744ed10b8930bc9af75bd244dccb3c"
"ondevice_hash": "2f3d69e5015a45a18c3553f2edc5706aacd6d84a4b3d5010a3d76a1a3aa910b0"
},
{
"name": "userdata_89",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_89-e36b59bf9ff755b6ca488df2ba1e20da8f7dab6b8843129f3fdcccd7ff2ff7d8.img.xz",
"hash": "12682cf54596ab1bd1c2464c4ca85888e4e06b47af5ff7d0432399e9907e2f64",
"hash_raw": "e36b59bf9ff755b6ca488df2ba1e20da8f7dab6b8843129f3fdcccd7ff2ff7d8",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_89-61bdaf82d3036af6e45e86adbaab02918b41debd5b58b6708d7987084d514d1b.img.xz",
"hash": "714970777e02bb53a71640735bdb84b3071ecbc0346b978ce12eb667d75634ec",
"hash_raw": "61bdaf82d3036af6e45e86adbaab02918b41debd5b58b6708d7987084d514d1b",
"size": 95563022336,
"sparse": true,
"full_check": true,
"has_ab": false,
"ondevice_hash": "e4df9dea47ff04967d971263d50c17460ef240457e8d814e7c4f409f7493eb8a"
"ondevice_hash": "95e6889a808b8d266660990e67e917cf3b63179f23588565af7f2fa54f70ac76"
},
{
"name": "userdata_30",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_30-fe1d86f5322c675c58b3ae9753a4670abf44a25746bf6ac822aed108bb577282.img.xz",
"hash": "fa471703be0f0647617d183312d5209d23407f1628e4ab0934e6ec54b1a6b263",
"hash_raw": "fe1d86f5322c675c58b3ae9753a4670abf44a25746bf6ac822aed108bb577282",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_30-a40553d3fd339cb0107f1cc55fd532820f192a7a9a90d05243ad00fcbf804997.img.xz",
"hash": "33e5ab398620f147b885a9627b2608591bd9e1c9aa481eb705dc86707d706ea2",
"hash_raw": "a40553d3fd339cb0107f1cc55fd532820f192a7a9a90d05243ad00fcbf804997",
"size": 32212254720,
"sparse": true,
"full_check": true,
"has_ab": false,
"ondevice_hash": "0b5b2402c9caa1ed7b832818e66580c974251e735bda91f2f226c41499d5616e"
"ondevice_hash": "cd6291dea40968123f7af0b831cbfbbd6e515b676f2e427ae47ff358f6ac148e"
}
]
]
@@ -56,10 +56,10 @@ class TestPowerDraw:
def valid_msg_count(self, proc, msg_counts):
msgs_received = sum(msg_counts[msg] for msg in proc.msgs)
msgs_expected = self.get_expected_messages(proc)
return np.isclose(msgs_expected, msgs_received, rtol=.02, atol=2)
return np.core.numeric.isclose(msgs_expected, msgs_received, rtol=.02, atol=2)
def valid_power_draw(self, proc, used):
return np.isclose(used, proc.power, rtol=proc.rtol, atol=proc.atol)
return np.core.numeric.isclose(used, proc.power, rtol=proc.rtol, atol=proc.atol)
def tabulate_msg_counts(self, msgs_and_power):
msg_counts = defaultdict(int)
+1 -1
View File
@@ -203,7 +203,7 @@ void handle_user_flag(LoggerdState *s) {
// mark route for uploading
Params params;
std::string routes = params.get("AthenadRecentlyViewedRoutes");
std::string routes = Params().get("AthenadRecentlyViewedRoutes");
params.put("AthenadRecentlyViewedRoutes", routes + "," + s->logger.routeName());
prev_segment = s->logger.segment();
+1 -1
View File
@@ -89,7 +89,7 @@ class Uploader:
def list_upload_files(self, metered: bool) -> Iterator[tuple[str, str, str]]:
r = self.params.get("AthenadRecentlyViewedRoutes", encoding="utf8")
requested_routes = [] if r is None else [route for route in r.split(",") if route]
requested_routes = [] if r is None else r.split(",")
for logdir in listdir_by_creation(self.root):
path = os.path.join(self.root, logdir)
+5 -6
View File
@@ -1,17 +1,16 @@
import os
import errno
import xattr
_cached_attributes: dict[tuple, bytes | None] = {}
def getxattr(path: str, attr_name: str) -> bytes | None:
key = (path, attr_name)
if key not in _cached_attributes:
try:
response = xattr.getxattr(path, attr_name)
response = os.getxattr(path, attr_name)
except OSError as e:
# ENODATA (Linux) or ENOATTR (macOS) means attribute hasn't been set
if e.errno == errno.ENODATA or (hasattr(errno, 'ENOATTR') and e.errno == errno.ENOATTR):
# ENODATA means attribute hasn't been set
if e.errno == errno.ENODATA:
response = None
else:
raise
@@ -20,4 +19,4 @@ def getxattr(path: str, attr_name: str) -> bytes | None:
def setxattr(path: str, attr_name: str, attr_value: bytes) -> None:
_cached_attributes.pop((path, attr_name), None)
xattr.setxattr(path, attr_name, attr_value)
return os.setxattr(path, attr_name, attr_value)
-2
View File
@@ -45,8 +45,6 @@ def manager_init() -> None:
("AutoLaneChangeTimer", "0"),
("AutoLaneChangeBsmDelay", "0"),
("DynamicExperimentalControl", "0"),
("DynamicPersonality", "0"),
("HyundaiLongitudinalTuning", "0"),
("Mads", "1"),
("MadsMainCruiseAllowed", "1"),
("MadsPauseLateralOnBrake", "0"),
+12 -19
View File
@@ -1,7 +1,6 @@
#!/usr/bin/env python3
import numpy as np
from functools import cache
import threading
from cereal import messaging
from openpilot.common.realtime import Ratekeeper
@@ -53,18 +52,12 @@ class Mic:
self.sound_pressure_weighted = 0
self.sound_pressure_level_weighted = 0
self.lock = threading.Lock()
def update(self):
with self.lock:
sound_pressure = self.sound_pressure
sound_pressure_weighted = self.sound_pressure_weighted
sound_pressure_level_weighted = self.sound_pressure_level_weighted
msg = messaging.new_message('microphone', valid=True)
msg.microphone.soundPressure = float(sound_pressure)
msg.microphone.soundPressureWeighted = float(sound_pressure_weighted)
msg.microphone.soundPressureWeightedDb = float(sound_pressure_level_weighted)
msg.microphone.soundPressure = float(self.sound_pressure)
msg.microphone.soundPressureWeighted = float(self.sound_pressure_weighted)
msg.microphone.soundPressureWeightedDb = float(self.sound_pressure_level_weighted)
self.pm.send('microphone', msg)
self.rk.keep_time()
@@ -76,17 +69,17 @@ class Mic:
Logged A-weighted equivalents are rough approximations of the human-perceived loudness.
"""
with self.lock:
self.measurements = np.concatenate((self.measurements, indata[:, 0]))
while self.measurements.size >= FFT_SAMPLES:
measurements = self.measurements[:FFT_SAMPLES]
self.measurements = np.concatenate((self.measurements, indata[:, 0]))
self.sound_pressure, _ = calculate_spl(measurements)
measurements_weighted = apply_a_weighting(measurements)
self.sound_pressure_weighted, self.sound_pressure_level_weighted = calculate_spl(measurements_weighted)
while self.measurements.size >= FFT_SAMPLES:
measurements = self.measurements[:FFT_SAMPLES]
self.measurements = self.measurements[FFT_SAMPLES:]
self.sound_pressure, _ = calculate_spl(measurements)
measurements_weighted = apply_a_weighting(measurements)
self.sound_pressure_weighted, self.sound_pressure_level_weighted = calculate_spl(measurements_weighted)
self.measurements = self.measurements[FFT_SAMPLES:]
@retry(attempts=7, delay=3)
def get_stream(self, sd):
+1 -1
View File
@@ -7,7 +7,7 @@ from openpilot.common.basedir import BASEDIR
from openpilot.common.swaglog import cloudlog
from openpilot.system.hardware import HARDWARE
DEFAULT_FPS = 60
DEFAULT_FPS = 30
FPS_LOG_INTERVAL = 5 # Seconds between logging FPS drops
FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning
FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions
+2 -15
View File
@@ -4,7 +4,6 @@ from enum import IntEnum
MOUSE_WHEEL_SCROLL_SPEED = 30
INERTIA_FRICTION = 0.95 # The rate at which the inertia slows down
MIN_VELOCITY = 0.1 # Minimum velocity before stopping the inertia
DRAG_THRESHOLD = 5 # Pixels of movement to consider it a drag, not a click
class ScrollState(IntEnum):
@@ -17,12 +16,10 @@ class GuiScrollPanel:
def __init__(self, show_vertical_scroll_bar: bool = False):
self._scroll_state: ScrollState = ScrollState.IDLE
self._last_mouse_y: float = 0.0
self._start_mouse_y: float = 0.0 # Track the initial mouse position for drag detection
self._offset = rl.Vector2(0, 0)
self._view = rl.Rectangle(0, 0, 0, 0)
self._show_vertical_scroll_bar: bool = show_vertical_scroll_bar
self._velocity_y = 0.0 # Velocity for inertia
self._is_dragging = False
def handle_scroll(self, bounds: rl.Rectangle, content: rl.Rectangle) -> rl.Vector2:
mouse_pos = rl.get_mouse_position()
@@ -38,27 +35,20 @@ class GuiScrollPanel:
self._scroll_state = ScrollState.DRAGGING_SCROLLBAR
self._last_mouse_y = mouse_pos.y
self._start_mouse_y = mouse_pos.y # Record starting position
self._velocity_y = 0.0 # Reset velocity when drag starts
self._is_dragging = False # Reset dragging flag
if self._scroll_state != ScrollState.IDLE:
if rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT):
delta_y = mouse_pos.y - self._last_mouse_y
# Check if movement exceeds the drag threshold
total_drag = abs(mouse_pos.y - self._start_mouse_y)
if total_drag > DRAG_THRESHOLD:
self._is_dragging = True
if self._scroll_state == ScrollState.DRAGGING_CONTENT:
self._offset.y += delta_y
elif self._scroll_state == ScrollState.DRAGGING_SCROLLBAR:
else:
delta_y = -delta_y
self._last_mouse_y = mouse_pos.y
self._velocity_y = delta_y # Update velocity during drag
elif rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT):
else:
self._scroll_state = ScrollState.IDLE
# Handle mouse wheel scrolling
@@ -83,6 +73,3 @@ class GuiScrollPanel:
self._offset.y = max(min(self._offset.y, 0), -max_scroll_y)
return self._offset
def is_click_valid(self) -> bool:
return self._scroll_state == ScrollState.IDLE and not self._is_dragging and rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT)

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