Compare commits

...

601 Commits

Author SHA1 Message Date
Jason Wen 383933094c Merge branch 'sync-20251114' into hkg-angle-steering-2025-sync-20251114 2025-11-15 01:53:09 -05:00
Jason Wen 201504ab27 too extra 2025-11-15 01:53:04 -05:00
Jason Wen 4ffa883428 Merge branch 'sync-20251114' into hkg-angle-steering-2025-sync-20251114 2025-11-15 01:51:58 -05:00
Jason Wen 88d03af58a need to bring this back 2025-11-15 01:51:50 -05:00
Jason Wen 96f7b8af94 Merge branch 'sync-20251114' into hkg-angle-steering-2025-sync-20251114 2025-11-15 01:44:05 -05:00
Jason Wen 0f6ea18d14 too extra red diff on the side 2025-11-15 01:39:21 -05:00
Jason Wen fc8b14a73c no more 2025-11-14 13:32:32 -05:00
Jason Wen 3bff055385 Merge branch 'sync-20251114' into hkg-angle-steering-2025-sync-20251114
# Conflicts:
#	opendbc_repo
#	selfdrive/ui/sunnypilot/SConscript
#	selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.cc
#	selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.h
#	selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.cc
2025-11-14 13:32:01 -05:00
Jason Wen 9e0a88bc5e no more 2025-11-14 03:47:34 -05:00
Jason Wen 77a1c7a7a6 more fixes 2025-11-14 03:25:45 -05:00
Jason Wen 32ca63afd1 quiet mode back 2025-11-14 03:24:48 -05:00
Jason Wen abceb1f936 raylib says wut 2025-11-14 03:21:18 -05:00
Jason Wen 63a3a95d65 should've been symlink'ed 2025-11-14 03:14:20 -05:00
Jason Wen a77c5a97dc not yet 2025-11-14 02:57:56 -05:00
Jason Wen 370a7a8fa6 bump opendbc 2025-11-14 02:52:26 -05:00
Jason Wen 81ec281eca sum more 2025-11-14 02:51:54 -05:00
Jason Wen ec48b66503 commaai/openpilot:7534b2a160faa683412c04c1254440e338931c5e 2025-11-14 02:25:10 -05:00
Jason Wen a61839db40 commaai/openpilot:954b567b9ba0f3d1ae57d6aa7797fa86dd92ec6e 2025-11-14 02:16:45 -05:00
Jason Wen e55e0949e7 commaai/openpilot:5198b1b079c37742c1050f02ce0aa6dd42b038b9 2025-11-14 02:12:47 -05:00
Jason Wen a8ec2a4dc0 cabana: revert to stock Qt 2025-11-14 01:56:19 -05:00
Jason Wen 0c98c33e79 sunnypilot: remove Qt 2025-11-14 01:53:22 -05:00
Jason Wen a6e43a77d9 bump opendbc 2025-11-14 01:49:18 -05:00
Jason Wen 9d1716c4f7 bump opendbc 2025-11-14 01:46:24 -05:00
Jason Wen d9930a4f7c bump opendbc 2025-11-14 01:36:23 -05:00
Jason Wen 6f83806ae7 bump opendbc 2025-11-14 01:32:38 -05:00
Jason Wen 716e86c707 bump opendbc 2025-11-14 01:31:57 -05:00
Jason Wen 0d4cfa806b commaai/openpilot:d05cb31e2e916fba41ba8167030945f427fd811b 2025-11-14 01:19:17 -05:00
Jason Wen c23516a8de Merge branch 'upstream/openpilot/master' into sync-20251114
# Conflicts:
#	.github/workflows/ci_weekly_run.yaml
#	.github/workflows/raylib_ui_preview.yaml
#	.github/workflows/tests.yaml
#	.gitmodules
#	README.md
#	SConstruct
#	common/api.py
#	common/params_keys.h
#	docs/CARS.md
#	msgq_repo
#	opendbc_repo
#	panda
#	selfdrive/car/tests/test_car_interfaces.py
#	selfdrive/controls/controlsd.py
#	selfdrive/controls/lib/latcontrol.py
#	selfdrive/controls/lib/latcontrol_angle.py
#	selfdrive/controls/lib/latcontrol_pid.py
#	selfdrive/controls/lib/latcontrol_torque.py
#	selfdrive/controls/tests/test_latcontrol.py
#	selfdrive/monitoring/helpers.py
#	selfdrive/ui/SConscript
#	selfdrive/ui/main.cc
#	selfdrive/ui/qt/body.h
#	selfdrive/ui/qt/home.cc
#	selfdrive/ui/qt/home.h
#	selfdrive/ui/qt/network/networking.cc
#	selfdrive/ui/qt/network/networking.h
#	selfdrive/ui/qt/network/wifi_manager.cc
#	selfdrive/ui/qt/offroad/developer_panel.cc
#	selfdrive/ui/qt/offroad/developer_panel.h
#	selfdrive/ui/qt/offroad/experimental_mode.cc
#	selfdrive/ui/qt/offroad/firehose.cc
#	selfdrive/ui/qt/offroad/firehose.h
#	selfdrive/ui/qt/offroad/onboarding.cc
#	selfdrive/ui/qt/offroad/onboarding.h
#	selfdrive/ui/qt/offroad/settings.cc
#	selfdrive/ui/qt/offroad/settings.h
#	selfdrive/ui/qt/offroad/software_settings.cc
#	selfdrive/ui/qt/onroad/alerts.cc
#	selfdrive/ui/qt/onroad/annotated_camera.h
#	selfdrive/ui/qt/onroad/buttons.cc
#	selfdrive/ui/qt/onroad/buttons.h
#	selfdrive/ui/qt/onroad/driver_monitoring.cc
#	selfdrive/ui/qt/onroad/hud.cc
#	selfdrive/ui/qt/onroad/hud.h
#	selfdrive/ui/qt/onroad/model.cc
#	selfdrive/ui/qt/onroad/model.h
#	selfdrive/ui/qt/onroad/onroad_home.cc
#	selfdrive/ui/qt/onroad/onroad_home.h
#	selfdrive/ui/qt/request_repeater.h
#	selfdrive/ui/qt/sidebar.cc
#	selfdrive/ui/qt/sidebar.h
#	selfdrive/ui/qt/util.cc
#	selfdrive/ui/qt/widgets/cameraview.h
#	selfdrive/ui/qt/widgets/controls.cc
#	selfdrive/ui/qt/widgets/controls.h
#	selfdrive/ui/qt/widgets/input.cc
#	selfdrive/ui/qt/widgets/input.h
#	selfdrive/ui/qt/widgets/prime.cc
#	selfdrive/ui/qt/widgets/prime.h
#	selfdrive/ui/qt/widgets/ssh_keys.h
#	selfdrive/ui/qt/widgets/toggle.h
#	selfdrive/ui/qt/widgets/wifi.cc
#	selfdrive/ui/qt/widgets/wifi.h
#	selfdrive/ui/qt/window.cc
#	selfdrive/ui/qt/window.h
#	selfdrive/ui/tests/cycle_offroad_alerts.py
#	selfdrive/ui/tests/test_ui/run.py
#	selfdrive/ui/translations/main_ar.ts
#	selfdrive/ui/translations/main_de.ts
#	selfdrive/ui/translations/main_es.ts
#	selfdrive/ui/translations/main_fr.ts
#	selfdrive/ui/translations/main_ja.ts
#	selfdrive/ui/translations/main_ko.ts
#	selfdrive/ui/translations/main_nl.ts
#	selfdrive/ui/translations/main_pl.ts
#	selfdrive/ui/translations/main_pt-BR.ts
#	selfdrive/ui/translations/main_th.ts
#	selfdrive/ui/translations/main_tr.ts
#	selfdrive/ui/translations/main_zh-CHS.ts
#	selfdrive/ui/translations/main_zh-CHT.ts
#	selfdrive/ui/ui.cc
#	selfdrive/ui/ui.h
#	system/manager/build.py
#	system/version.py
2025-11-14 01:11:37 -05:00
Maxime Desroches 9c19ec8409 bump raylib 2025-11-13 15:09:33 -08:00
Shane Smiskol fc253fe1ee Don't resize images that are the same size 2025-11-13 14:59:34 -08:00
Shane Smiskol d72a01d739 raylib: fix texture wrapping filtering artifacts (#36618)
fix wrapping artifacts
2025-11-13 14:58:16 -08:00
Trey Moen f93b3f51c9 fix: install missing x deps for building raylib from src (#36614)
* fix: install missing x deps for building raylib from src

* move here

* cleaner
2025-11-12 20:04:20 -08:00
Dean Lee 3d08a5048b replay: Only send bookmarkButton message when --all flag is set (#36612)
Only send BookmarkButton message when --all flag is set
2025-11-12 14:22:14 -08:00
Maxime Desroches 9ee66008db AGNOS 15 (#36611)
* stage

* production
2025-11-11 22:59:54 -08:00
Dean Lee 6a257fe2de ui: increase profile output from 25 to 100 functions (#36607)
increase profile output from 20 to 100 functions
2025-11-11 16:10:37 -08:00
Maxime Desroches dad7bb53a2 ui: let ui_state set brightness 2025-11-10 22:24:35 -08:00
Maxime Desroches 47ba86af33 enable ADB in release 2025-11-10 19:57:59 -08:00
Trey Moen 9689de426b chore: adb rules (#36544)
* chore: adb rules

* i think 51 is common, lets use our own
2025-11-10 18:38:49 -08:00
Dean Lee 124eb42758 ui: fix CameraView crash caused by stale frame (#36563)
fix CameraView crash from stale frame
2025-11-10 18:10:50 -08:00
Trey Moen 85404c184b fix: badges (#36566)
* re-add

* need to validate

* ok looks good

* oops

* lint
2025-11-10 18:08:55 -08:00
Dean Lee ed42cfe699 ui: refactor GuiApplication.render into smaller helper methods (#36569)
refactor render into smaller helper method
2025-11-10 18:08:02 -08:00
Dean Lee 3099f4f12d ui: cache the version text to avoid redundant Params.get calls every frame (#36601)
cache the version text to avoid redundant Params.get calls every frame
2025-11-10 18:05:55 -08:00
Dean Lee 8fceb9d957 cabana: replace deprecated Qt and OpenSSL functions (#36605)
replace deprecated functions
2025-11-10 13:58:23 -08:00
Jason Young d4185a5d57 docs: car porting (#36590)
* checkpoint

* door states, notes

* updates

* not worth it yet

* wordsmith

* more

* more reverse engineering script content

* Revise stationary ignition-only test steps

Updated the steps for stationary ignition-only tests to include closing the driver's door and fastening the seatbelt before pressing the accelerator and brake pedals.

* fix numbering
2025-11-07 20:37:40 -05:00
ZwX1616 1262fca36b add check driver camera alert (#36577)
* add event

* missing arg

* creation_delay is wrong

* add logging

* set offroad alert

* Update selfdrive/selfdrived/alerts_offroad.json

Co-authored-by: Shane Smiskol <shane@smiskol.com>

* rm onard

* add details

* rename to DM

* log rename

* no poss

---------

Co-authored-by: Shane Smiskol <shane@smiskol.com>
2025-11-07 15:18:45 -08:00
Maxime Desroches 890b1cf512 AGNOS 14.7 (#36597)
* stage

* prod
2025-11-07 02:54:52 -08:00
Maxime Desroches 1633641055 bump raylib (#36596)
this
2025-11-06 21:00:26 -08:00
David 2dcb67091f remove unused MAX_POINTS constant from model_renderer.py (#36593) 2025-11-06 20:51:17 -08:00
Maxime Desroches fb34601d5a Revert "bump panda" (#36594)
This reverts commit 36e53c7394.
2025-11-06 20:46:04 -08:00
Adeeb Shihadeh b6bcc8cca3 ui: fix running on macOS 2025-11-06 09:42:23 -08:00
DevTekVE 8b94f8b2f8 Merge branch 'master' into hkg-angle-steering-2025 2025-11-06 18:31:16 +01:00
Maxime Desroches e5a7deb6ad AGNOS 14.6 (#36586)
* stage

* ver

* prod
2025-11-06 01:22:37 -08:00
Maxime Desroches 10100e34e1 bump raylib 2025-11-06 01:04:17 -08:00
Adeeb Shihadeh 2d31b422c8 ui: prep for 60fps (#36585) 2025-11-05 23:01:10 -08:00
Adeeb Shihadeh 89919c8832 this is not a good api 2025-11-05 18:55:50 -08:00
Adeeb Shihadeh dc5f5eaf65 make github LFS work if you want it 2025-11-05 16:34:19 -08:00
Adeeb Shihadeh ee8970dc42 ui: add route-based profiler (#36576)
* ui: add route-based profiler

* cleanup

* this is stupid
2025-11-05 16:23:33 -08:00
David 0a44b48e21 gitignore: add raylib test UI screenshots report path (#36570)
ui: update .gitignore to include raylib_report
2025-11-05 10:35:56 -08:00
Maxime Desroches 36e53c7394 bump panda 2025-11-04 19:18:16 -08:00
ZwX1616 38eb400e41 monitoring: account for OS cam distribution shift (#36567)
* this should match

* roughly matching FPR at 2 to 1 cost
2025-11-04 16:00:15 -08:00
Jason Wen dec014cd17 Merge remote-tracking branch 'sunnypilot/sunnypilot/master' into hkg-angle-steering-2025
# Conflicts:
#	sunnypilot/selfdrive/car/interfaces.py
2025-11-04 17:45:05 -05:00
DevTekVE 7b40272866 Add sunnypilot-specific stats logging and handling
- Introduced `StatLogSP` for sunnypilot-specific metrics.
- Integrated stats collection and submission pathways for sunnylink.
- Extended parameters and handlers to support additional metrics.
- Added gzip compression and base64 encoding for oversized payload handling.
2025-11-04 21:20:16 +01:00
Maxime Desroches 5198b1b079 support ECDSA (#36555)
* keys

* remove

* remove

* too small
2025-11-03 22:45:00 -08:00
Adeeb Shihadeh e8a11591a8 ui: add render loop profiling (#36558) 2025-11-03 21:45:46 -08:00
Adeeb Shihadeh cbc8f98682 ui: fix RuntimeError on exit on PC 2025-11-03 16:20:36 -08:00
Adeeb Shihadeh ecdcb5d0c6 tici: affine DRM IRQ to same core as ui (#36554) 2025-11-03 14:36:19 -08:00
Adeeb Shihadeh c7494aed0f ui: move to GPU core (#36553)
* ui: move to GPU core

* we're on the big boy core now
2025-11-03 14:31:45 -08:00
Dean Lee 215ef16803 ui: fix LineSeparator horizontal centering issue (#36533)
fix LineSeparator horizontal centering issue
2025-11-03 11:22:42 -08:00
Dean Lee 350b846d3a ui: fix vertical centering for multi-line labels (#36538)
fix vertical centering for multi-line labels
2025-11-03 11:21:41 -08:00
Dean Lee 9ce9920ff7 ui: fix label text eliding to account for icon width (#36539)
fix label text eliding to account for icon width
2025-11-03 11:21:20 -08:00
Dean Lee 1c0b087105 ui: fix keyboard.reset() to properly clear all interaction state (#36541)
fix keyboard.reset() to properly clear all interaction state
2025-11-03 11:20:24 -08:00
Dean Lee 137d4b89b4 ui: fix icon vertical positioning using width instead of height (#36542)
fix icon vertical positioning using width instead of height
2025-11-03 11:20:08 -08:00
Shane Smiskol 2cc4885a2e raylib: fix window freezing (#36517)
fix window freezing
2025-11-03 11:17:38 -08:00
felsager 736e1fa7b7 Revert "latcontrol_torque: make feed-forward jerk independent of individual platform lag (#36334)"
This reverts commit fc4e5007fd.
2025-11-03 10:31:27 -08:00
felsager 177c7f1cf3 Revert "latcontrol_torque: retune torque controller (#36392)"
This reverts commit 76c5cb6d87.
2025-11-03 10:31:22 -08:00
Dean Lee 9bf904e8a6 ui: scale mouse positions in touch history (#36530)
scale mouse position in touch history
2025-11-03 08:55:05 -08:00
Adeeb Shihadeh 5ea5f6f267 ui: timeout touch points (#36550) 2025-11-02 21:48:33 -08:00
Shane Smiskol 525b6e48e9 raylib: fix word wrap (#36545)
* fix word wrap underestimating width

* and that
2025-11-02 04:32:29 -08:00
Shane Smiskol c7b115b68e raylib: fix text measure with emojis (#36546)
fix
2025-11-02 04:30:08 -08:00
DevTekVE 506456e7f0 Merge branch 'master' into hkg-angle-steering-2025 2025-11-01 13:31:23 +01:00
Dean Lee 62aef9cd34 tools: update README (#36531)
update readme
2025-10-31 08:16:55 -07:00
DevTekVE 4ea4b9d177 Merge branch 'master' into hkg-angle-steering-2025
# Conflicts:
#	opendbc_repo
2025-10-31 06:59:39 +01:00
Adeeb Shihadeh f57617c944 expose more state from gui_app 2025-10-30 16:03:28 -07:00
Adeeb Shihadeh c4a0e57046 ui: add debug toggle (#36529)
* ui: add debug toggle

* initial state
2025-10-30 15:52:56 -07:00
felsager 76c5cb6d87 latcontrol_torque: retune torque controller (#36392) 2025-10-30 13:34:44 -07:00
felsager fc4e5007fd latcontrol_torque: make feed-forward jerk independent of individual platform lag (#36334) 2025-10-30 13:29:38 -07:00
Dean Lee af24fd6842 remove qrcode library from third_party (#36528) 2025-10-30 09:24:22 -07:00
Maxime Desroches 002a22a097 AGNOS 14.5 (#36523)
* stage

* updater

* prod
2025-10-28 23:05:44 -07:00
Maxime Desroches 9f20eb8ce6 setup: handle incompatible versions (#36520)
check
2025-10-28 19:15:43 -07:00
Adeeb Shihadeh 2e636458a6 op adb: forward all openpilot service ports (#36518)
* op adb: forward all openpilot service ports

* cleanup
2025-10-28 16:47:22 -07:00
David 47d0a95fd6 font: remove unifont anti-aliasing and reduce font size to 16 (#36508)
remove unifont anti-aliasing and reduce font size to 16

Co-authored-by: Shane Smiskol <shane@smiskol.com>
2025-10-28 14:49:33 -07:00
Dean Lee 5d142326f5 ui: remove unused get_width() method (#36512)
remove unused get_width() method
2025-10-28 14:47:17 -07:00
Dean Lee ef9683ee79 ui: skip rendering when screen is off (#36510)
* skip rendering when screen is off

* continue and rename

* revert that

* flip

---------

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
2025-10-28 13:09:49 -07:00
Maxime Desroches 8a77534d02 fix zipapp with multilang (#36511)
* fix

* fix

* fix

* more
2025-10-27 22:47:41 -07:00
David 73ed45f9d7 ui screenshots: add screenshot for unifont rendering (#36506)
* ui: add homescreen setup for unifont language setting

* fix params
2025-10-27 20:00:01 -07:00
Shane Smiskol 2d6df2e125 raylib: minor tweaks (#36507)
* try

* generic

* check

* why was this here?!
2025-10-27 19:59:35 -07:00
Shane Smiskol e754b738ad raylib: fix prime state thread (#36504)
fix
2025-10-27 15:15:48 -07:00
David 1dadb3fcc9 multilang: fix missing translation for longitudinal personality toggle description (#36446)
fix: add translation wrapper for longitudinal personality toggle description
2025-10-27 13:34:35 -07:00
Shane Smiskol 4e88245745 raylib: rename set_callbacks (#36462)
rn
2025-10-27 13:34:16 -07:00
David debc9bf7cf screenshots: reuse alert setup function (#36473)
ui: refactor onroad alert setup functions for improved reusability
2025-10-27 13:32:15 -07:00
Dean Lee e03673485b ui: unify cache key in AugmentedRoadView (#36477)
unified cache key
2025-10-27 13:31:55 -07:00
David 6efe4e1998 ci: fix selfdrive_tests weekly run and badge (#36500) 2025-10-27 13:28:27 -07:00
David ff6ed7055d ci: include assets path for UI label and preview (#36499)
* workflow: include 'selfdrive/assets/**' path for triggering UI preview

* ui: include 'selfdrive/assets/**' path in labeler configuration
2025-10-27 13:28:09 -07:00
Maxime Desroches 6c85e2c697 ModemManager restart (#36433)
* res

* limit

* not needed

* comments + explicit
2025-10-26 18:30:57 -07:00
Adeeb Shihadeh 2d0340cefd ui: stop loading unused fonts (#36493)
Co-authored-by: Comma Device <device@comma.ai>
2025-10-26 14:18:48 -07:00
Adeeb Shihadeh a974deeb59 ui: replace close button text with icon (#36492) 2025-10-26 14:13:53 -07:00
Adeeb Shihadeh cf5bb4e16e reduce unifont impact on init time (#36490) 2025-10-26 13:47:47 -07:00
Adeeb Shihadeh 0d4b0ee116 pre-process fonts for raylib (#36489)
* pre-process fonts for raylib

* it's fast!

* raylib processing

* build with scons

* padding

* happy ruff

* all exported

* cleanup

* more pad
2025-10-26 13:20:11 -07:00
Adeeb Shihadeh 03cb3e9dc0 make ruff happy :) 2025-10-26 12:15:34 -07:00
Shane Smiskol f0dd0b5c8c raylib ui: assert system time valid (#36486)
* assert system time valid

* nl
2025-10-26 00:47:07 -07:00
Adeeb Shihadeh 94ca077e69 ui: add startup profiling (#36482)
* ui: add startup profiling

* lil more
2025-10-25 12:27:01 -07:00
eFini e92e59ca78 multilang: add gettext package (#36476)
needed for gettext
2025-10-25 09:17:59 -07:00
Shane Smiskol e0cabc1174 raylib: only load glyphs for unifont (#36475)
* again llm is terrible

* Revert "again llm is terrible"

This reverts commit 423dd289ae5701e2f5bb034efd9329175fc275cc.

* try this
2025-10-24 23:56:59 -07:00
Adeeb Shihadeh 5e2f142704 increase CPU budget 2025-10-24 22:02:25 -07:00
Dean Lee 2beb0ffad1 ui: move INF_POINT to constant (#36470)
move INF_POINT to constant
2025-10-24 22:00:45 -07:00
Dean Lee fa373af9b5 ui: cleanup .gitignore (#36471)
cleanup .gitignore
2025-10-24 22:00:38 -07:00
Adeeb Shihadeh 7909716c1f ui: realtime scheduling (#36467)
* ui: realtime scheduling

* try this

* update cpu

---------

Co-authored-by: Comma Device <device@comma.ai>
2025-10-24 21:57:45 -07:00
Adeeb Shihadeh c1cb971bca hardwared: disable power save when screen is on (#36466) 2025-10-24 21:34:37 -07:00
Adeeb Shihadeh 538ec25ad9 gc unused stuff in HW abstraction layer (#36465)
* gc unused stuff in HW abstraction layer

* lil more
2025-10-24 21:07:04 -07:00
Adeeb Shihadeh 17152484c2 selfdrive_tests -> tests 2025-10-24 20:54:13 -07:00
Adeeb Shihadeh 954b567b9b merge a bunch of misc stuff into common.utils (#36463)
just utils
2025-10-24 20:45:56 -07:00
Shane Smiskol 6061476d8e fix spinner (#36458) 2025-10-24 19:43:46 -07:00
Dean Lee ad903aeaa1 ui: simplify draw_border (#36440)
simplify draw_border
2025-10-24 19:28:58 -07:00
Dean Lee c8c1b0f781 ui: clear available camera streams after going offroad (#36441)
clear available camera streams after going offroad
2025-10-24 19:27:33 -07:00
Dean Lee 534f096bb8 ui: reset cached height when description changes (#36454)
reset cached height when description changes
2025-10-24 19:27:03 -07:00
David 7da36b2470 multilang: fix missing translations in developer panel (#36445)
fix: translate description strings in DeveloperLayout settings
2025-10-24 19:15:14 -07:00
Dean Lee f2db7f7665 ui: cache emoji font to avoid repeated loading (#36451)
cache font to avoid repleated loads
2025-10-24 19:13:34 -07:00
David 40a1af97b9 ui: only auto scale on PC if SCALE env not set (#36455)
only use auto scale if SCALE env not set
2025-10-24 19:12:45 -07:00
Dean Lee 53ff5413cd ui: auto-size on PC if screen is smaller than tici (#36452)
auto-scale on PC to fit screen
2025-10-24 08:50:32 -07:00
Maxime Desroches dc889587ce bump version to 0.10.2 2025-10-23 22:25:54 -07:00
David 6486ab6cab raylib: fix crash when toggling advanced network toggles (#36443)
use get_state()
2025-10-23 15:09:14 -07:00
Shane Smiskol ab234c72a3 wait slightly longer to take screenshot 2025-10-23 00:55:31 -07:00
Shane Smiskol 485c7b2725 multilib: relative paths (#36439)
* relative

* clean up
2025-10-23 00:54:31 -07:00
Shane Smiskol 4861d15056 reduce ui scons imports 2025-10-23 00:45:51 -07:00
Shane Smiskol 1e73025f86 Remove Qt (#36427)
* rm qt from ui scons

* rm qt translation litter

* rm ccs

* more

* fix cabana

* more

* more

* more
2025-10-22 22:18:07 -07:00
Dean Lee 378212e5ab cabana: remove dependency on selfdrive/ui (#36434)
remove dependency on selfdrive/ui
2025-10-22 21:24:07 -07:00
David 4f52f3f3c5 raylib: match QT colors for danger button style (#36431)
match colors for DANGER style
2025-10-22 19:48:44 -07:00
Shane Smiskol a0d48b6c63 raylib: unifont for CJK languages (#36430)
* add rest of langs

* unifont

* all langs are supported

* add japanese translations

* fix strip!

* add language name chars

* use unifont in lang selection

* add korean

* test all langs

* doesn't work

* unifont font fallback for multilang

* add ar translations

* fix labels not updating until scrolling

* t chinese

* more chn

* we already default

* wrap

* update

* fix thai

* fix missing chinese langs and all are supported!

* clean up

* update

* ??? mypy r u ok ???

* fix default option font weight
2025-10-22 19:18:54 -07:00
YassineYousfi b14270bd71 update RELEASES.md 2025-10-22 18:56:18 -07:00
Shane Smiskol 8f720a54f6 raylib: add branch switcher (#36359)
* it's adversarial

* try 2

* just do this

* kinda works but doesn' tmatch

* fine

* qt is banned word

* test

* fix test

* add elide support to Label

* fixup

* Revert "add elide support to Label"

This reverts commit 28c3e0e7457345083d93f7b6a909a4103bd50d55.

* Reapply "add elide support to Label"

This reverts commit 92c2d6694146f164f30060d7621e19006e2fe2df.

* todo

* elide button value properly + debug/stash

* clean up

* clean up

* yep looks good

* clean up

* eval visible once

* no s

* don't need

* can do this

* but this also works

* clip to parent rect

* fixes and multilang

* clean up

* set target branch

* whops
2025-10-22 18:54:09 -07:00
Shane Smiskol 2c41dbc472 raylib: hit rect for scroller items (#36432)
* hit rect

* clean up

* comment

* oh this is actually epic

* rm line

* type
2025-10-22 18:10:32 -07:00
Shane Smiskol a8660b5b4f Revert "raylib: add branch switcher (#36411)"
This reverts commit 856f8d3d47.
2025-10-22 17:55:28 -07:00
Shane Smiskol 4ccafff123 raylib: multilang (#36195)
* fix multilang dialog height

* split to file

* stash

* Revert "stash"

This reverts commit deb4239fe69f0260420fad03f2350e622e31542f.

* add updater

* add files

* stuff

* try

rev

* stash

* works!

* works!

* this should be the flow?

* cursor wrapping -- it missed entire sections, changed formatting, and didn't use trn properly!!!!!!!!!!!!!!!!!

* update translations

* learned my lesson

* this should be the one thing it's good at

* update trans

* onroad wrap

* spanish

* rename

* clean up

* load all

* Revert "load all"

This reverts commit 6f2a45861c914ffb9d40a5edd15751afd798d614.

* jp translations

* try jp

* Revert "try jp"

This reverts commit d0524b10110104baafcdc1ec385c3d57bc5ef901.

* remove languages we can't add rn

* tr

* pt and fr

* ai cannot be trusted

* ai cannot be trusted

* missing trans

* add fonts

* Revert "remove languages we can't add rn"

This reverts commit 73dc75fae2b9e347d867b6636dab6e2b5fe59da7.

* painfully slow to startup

* only load what we need

* Reapply "remove languages we can't add rn"

This reverts commit 52cb48f3b838520a421f9b90e5ea4409c27d4bd0.

* add system

* that's sick that this just works (dynamic)

* fix description falling back to first str + support callable titles in list items

* device is now live!

* make firehose live

* developer

* network live

* software live

* and that

* toggles live

* regen

* start to clean up gpt

* revert op sans

* bruh

* update translations

* rm old script

* add noops for descriptions to fix translating away from non-english after startup

* missing de

* do filtering in multilang.py

* clean up

clean up

* codespell: ignore po

* fix update

* should not depend

* more live

* sidebar and offroad alert panel live

* fix issues with offroad alerts

* fix firehose live

* fix weird tr("") behavior

* sh key live bugfix

* setup.py live

* update

* update

* no fuzzy matching -- breaks dynamic translations

* rm this

* fix calib desc live trans

* change onroad

* rm dfonts

* clean up device

* missing live

* update

* op lint

* not true

* add to gitignore

* speed up startup by reducing chars by ~half

* fix scons

* fix crash going from qt

* preserve original lang

* cancel kb live translate

* no preserve

* fix lint
2025-10-22 16:28:28 -07:00
Dean Lee 856f8d3d47 raylib: add branch switcher (#36411)
* add branch switcher

* improve
2025-10-22 16:22:51 -07:00
David 00e20f1524 raylib: fix "Reboot" button pressed style (#36412)
use normal style for dual button action left button
2025-10-22 16:19:37 -07:00
Dean Lee 215acefbb4 raylib: precompile regex patterns for faster HTML parsing (#36417)
precompiled regex
2025-10-22 16:18:11 -07:00
Dean Lee c33c9ff22a raylib: optimize html renderer with height caching (#36418)
optimize html renderer with height caching
2025-10-22 16:17:57 -07:00
Adeeb Shihadeh 99fdd59042 agnos 14.3 (#36426) 2025-10-22 16:11:37 -07:00
Adeeb Shihadeh 5af1099fbf rm watchdog (#36425) 2025-10-22 15:36:09 -07:00
ZwX1616 f983df0c70 camerad: faster exposure convergence at startup (#36424)
* might converge faster

* accept darker at start

* accept darker at start

* it was unreasonably lax
2025-10-22 15:35:58 -07:00
felsager 936740201c latcontrol_torque: refactor low speed factor into pid controller (#36364) 2025-10-22 11:50:37 -07:00
David 4489517eeb keyboard: replace duplicate period key (#36361)
switch between underscore and hypen instead of period
2025-10-22 11:35:58 -07:00
David b1b7c505a1 raylib: add danger button pressed style (#36413)
add danger hover style
2025-10-22 11:28:37 -07:00
felsager a2e7f3788f LateralTorqueState: log controller version and desired lateral jerk (#36421) 2025-10-22 10:56:34 -07:00
felsager d2bb8fe537 latcontrol_torque: more descriptive variable names (#36422) 2025-10-22 10:44:14 -07:00
Maxime Desroches 5289b08bcf bump retry count for micd and soundd (#36415)
retry
2025-10-21 21:54:40 -07:00
ZwX1616 cc8f6eadfe DM: Medium Fanta model 🥤 (#36409)
M fanta: e456b6c5-2dd0-400e-bf0f-6bb5a908971a
2025-10-21 13:58:48 -07:00
Shane Smiskol 9b2f7341d8 raylib: wrap text for multilang (#36410)
* fix multilang dialog height

* split to file

* stash

* Revert "stash"

This reverts commit deb4239fe69f0260420fad03f2350e622e31542f.

* add updater

* add files

* stuff

* try

rev

* stash

* works!

* works!

* this should be the flow?

* cursor wrapping -- it missed entire sections, changed formatting, and didn't use trn properly!!!!!!!!!!!!!!!!!

* update translations

* learned my lesson

* this should be the one thing it's good at

* update trans

* onroad wrap

* spanish

* rename

* clean up

* load all

* Revert "load all"

This reverts commit 6f2a45861c914ffb9d40a5edd15751afd798d614.

* jp translations

* try jp

* Revert "try jp"

This reverts commit d0524b10110104baafcdc1ec385c3d57bc5ef901.

* remove languages we can't add rn

* tr

* pt and fr

* ai cannot be trusted

* ai cannot be trusted

* missing trans

* add fonts

* Revert "remove languages we can't add rn"

This reverts commit 73dc75fae2b9e347d867b6636dab6e2b5fe59da7.

* painfully slow to startup

* only load what we need

* Reapply "remove languages we can't add rn"

This reverts commit 52cb48f3b838520a421f9b90e5ea4409c27d4bd0.

* stash!

* rm

* Revert "stash!"

This reverts commit 31d7c361079a8e57039a0117c81d59bf84f191c7.

* revert this

* revert that

* make this dynamic!

* device

* revert

* firehose

* stuff

* revert application

* back

* full revert

* clean up

* network

* more system

* fix dat

* fixy
2025-10-20 21:39:04 -07:00
Dean Lee 650946cd2a raylib:use context manager for BytesIO (#36407)
use context manager for BytesIO
2025-10-20 19:17:10 -07:00
Shane Smiskol 9801e486d9 fix incorrect Button argument 2025-10-20 18:36:13 -07:00
Shane Smiskol 3381192297 Multilang: remove main prefix (#36406)
* rename

* fix
2025-10-20 18:35:34 -07:00
Harald Schäfer b2e3dd17ea torque gains not car specific (#36404)
* torque gains not car specific

* remove opendbc interfaces longitudinal control kf field assignment that makes hitl test fail

* typo

* another typo

* bump

* bump openbc

* update ref

---------

Co-authored-by: felsager <d.felsager@gmail.com>
2025-10-20 17:16:03 -07:00
Bruce Wayne 01715f6f9a test car model: use factor for torque 2025-10-20 16:26:22 -07:00
Adeeb Shihadeh 8720e5d712 tools: pass args to op adb 2025-10-20 16:14:02 -07:00
Shane Smiskol 8752093801 raylib: fix option dialog (#36405)
* fix dialog

* rm
2025-10-20 15:45:44 -07:00
YassineYousfi 3c957c6e9d The Cool People's model 😎 (#36249)
* cb8f0d7e-6627-4d7f-ad97-10d0078f2d2c/400

* ci?

* fd9a6816-8758-466b-bbde-3c1413b98f0a/400
2025-10-20 14:09:42 -07:00
Shane Smiskol 3ef5037c16 uploader: fix env var parsing 2025-10-20 11:40:03 -07:00
Harald Schäfer 7534b2a160 PID: no more ff gain (#36398)
* No more ff gain

* typo
2025-10-18 11:12:47 -07:00
DevTekVE 0e2313dc31 Merge branch 'master' into hkg-angle-steering-2025 2025-10-18 11:14:45 +02:00
Jason Wen 79ea7db103 Merge remote-tracking branch 'sunnypilot/sunnypilot/master' into hkg-angle-steering-2025 2025-10-17 23:42:01 -04:00
Shane Smiskol b28425b8c3 raylib: fix broken pairing dialog first 5m after startup (#36397)
* always try on dialog show

* except logging

* huge oof
2025-10-17 19:11:12 -07:00
Shane Smiskol 1f5e0b6f68 raylib: show dialog when attempting to pair without internet (#36396)
* match qt

* clean up

* bb

* ofc

* use alert_dialog
2025-10-17 19:00:06 -07:00
Jason Wen f9ae9192fa Merge branch 'ui-icbm-universal' into hkg-angle-steering-2025 2025-10-17 21:55:00 -04:00
Jason Wen 7ec23006c6 check this 2025-10-17 21:54:44 -04:00
Jason Wen 2be9447a6c Merge branch 'ui-icbm-universal' into hkg-angle-steering-2025 2025-10-17 21:19:09 -04:00
Jason Wen cfd926778e always init true 2025-10-17 21:19:03 -04:00
Jason Wen a97a67e3d0 need 2025-10-17 21:17:31 -04:00
Jason Wen 518b6de08d Merge branch 'ui-icbm-universal' into hkg-angle-steering-2025 2025-10-17 21:15:59 -04:00
Jason Wen 6933e3bcdb fix cruise toggles 2025-10-17 21:15:43 -04:00
Jason Wen 05e0ca8bee some more 2025-10-17 20:46:36 -04:00
David 646f6a1006 raylib screenshots: alpha long toggle confirmation dialog (#36386)
add alpha long toggle confirmation test
2025-10-17 17:27:13 -07:00
Jason Wen 410614fcf3 single location 2025-10-17 20:26:18 -04:00
Jason Wen e2bc0996ef Merge branch 'ui-icbm-universal' into hkg-angle-steering-2025 2025-10-17 12:21:47 -04:00
Jason Wen 1be0c20cf5 oops 2025-10-17 12:21:37 -04:00
Jason Wen 839143b9ed oops 2025-10-17 12:20:23 -04:00
Jason Wen bce86637ae Merge branch 'ui-icbm-universal' into hkg-angle-steering-2025 2025-10-17 12:15:23 -04:00
Jason Wen b833d3ee89 ui: update ICBM-related settings handling 2025-10-17 12:14:44 -04:00
Jason Wen e0441dfb4b Merge branch 'sla-event' into hkg-angle-steering-2025 2025-10-17 11:58:42 -04:00
Jason Wen 62ec40bba6 Speed Limit Assist: update active event handling 2025-10-17 11:58:19 -04:00
Maxime Desroches cc683f2040 AGNOS 14.2 (#36390)
* version

* env
2025-10-17 02:50:17 -07:00
Maxime Desroches 18e8f648c2 Revert "AGNOS 14.1 (#36389)"
This reverts commit 821e4da2c7.
2025-10-17 01:48:08 -07:00
Maxime Desroches 821e4da2c7 AGNOS 14.1 (#36389)
* stag

* prod
2025-10-17 01:12:55 -07:00
Maxime Desroches 13d98fd2d5 test_onroad: skip more frames for ui timings 2025-10-17 00:36:15 -07:00
Maxime Desroches 92cd656c68 ui: remove watchdog (#36388)
out
2025-10-17 00:26:29 -07:00
Maxime Desroches 727a750b34 ci: stop power_monitor once 2025-10-16 23:37:44 -07:00
Maxime Desroches 5dabb678ce ci: just stop power_monitor on devices 2025-10-16 23:35:31 -07:00
Maxime Desroches ef988aca28 raylib: bump version 2025-10-16 23:23:39 -07:00
Maxime Desroches 64f3759fd0 cleanup release branches 2025-10-16 15:07:45 -07:00
Jason Wen 15c6d38028 bump 2025-10-16 17:05:58 -04:00
Shane Smiskol d71d2bd2d0 test_onroad: ignore first few ui timing frames (#36385)
clean up
2025-10-16 03:45:50 -07:00
Shane Smiskol 702bebf176 raylib: fix temporarily untoggleable onroad experimental mode button (#36383)
* gpt got it after 2 tries, but still not immed mergeable

* bad bot
2025-10-16 02:22:49 -07:00
Shane Smiskol 25da8e9d44 raylib: fix crash from too many colors (#36382)
* fix

* bump
2025-10-16 02:22:02 -07:00
Maxime Desroches 845f6ec8cf build new staging branch 2025-10-16 02:06:09 -07:00
Maxime Desroches e1ad4daf8d installer: branch migration (#36315)
* mig

* fix

* fix

* more

* staging
2025-10-16 01:59:23 -07:00
Maxime Desroches 783b717af8 AGNOS 14 (#36313)
* version

* updater

* this order

* manifest

* update

* prod

* logic

* magic

* new

* bump

* bump

* new

* b

* bump

* prod
2025-10-16 00:49:05 -07:00
Shane Smiskol 65e1fd299e raylib: fix full size alert text (#36379)
* stash so far

* try this

* better

* fast

* rename

* revert

* clean up

* yes

* hack to make it work for now

* actually fix

* fix
2025-10-15 22:53:37 -07:00
Jason Wen 56eb9f555c Merge remote-tracking branch 'sunnypilot/sunnypilot/master' into hkg-angle-steering-2025
# Conflicts:
#	sunnypilot/selfdrive/controls/lib/e2e_alerts_helper.py
2025-10-16 01:12:10 -04:00
Shane Smiskol b29b1964ba raylib screenshots: test onroad (#36369)
* test onroad

* person

* onroad alert

* mid and full

* all

* can do this

* tf

* tf

* clean up
2025-10-15 22:10:55 -07:00
Shane Smiskol 80a8df0643 raylib: fix emoji centering with Label (#36376)
* kinda works

* but spacing was off, so back to big emoji

* rm debug

* fixed!

* fixed!

* fix newline in emoji pattern

* fix

* fix dat
2025-10-15 21:53:52 -07:00
Shane Smiskol d9fc6c0086 raylib: small Label clean up (#36377)
* do

* clean up

* text raw is the default!
2025-10-15 21:11:00 -07:00
Jason Wen 2f9951df02 Merge branch 'e2e-alert-state-machine' into hkg-angle-steering-2025 2025-10-15 23:55:40 -04:00
Jason Wen 6030bf4da3 less 2025-10-15 23:52:03 -04:00
Jason Wen 074694d660 lead depart: only arm if we have a confirmed close lead for over a second after allowing alert 2025-10-15 23:48:13 -04:00
Shane Smiskol cb612a4b90 raylib: no Label padding (#36374)
* none

* try this

* fix

* stash

* remove text padding from label, but keep for button

* simpler is to default to 0

* fix
2025-10-15 20:13:42 -07:00
Jason Wen df35f48f3b magic 2025-10-15 22:56:08 -04:00
Jason Wen 4fb9704540 time based 2025-10-15 22:51:40 -04:00
Jason Wen 48cbe266fc 10 frames for both 2025-10-15 22:50:42 -04:00
Jason Wen 21aa7ff367 rename 2025-10-15 22:47:08 -04:00
Jason Wen 7caf05dd51 not used 2025-10-15 22:41:00 -04:00
Jason Wen 2d779f5db9 E2E Helper: universal state machine 2025-10-15 22:38:57 -04:00
Shane Smiskol 36d77debd0 raylib: remove redundant text center enum (#36372)
* rm

* type

* fix

* fix
2025-10-15 19:32:13 -07:00
Maxime Desroches 530ad2925d ui: clean raylib even on SIGINT (#36368)
* fix

* keep

* fix
2025-10-15 17:03:49 -07:00
Jason Wen 18208f1da0 Merge remote-tracking branch 'sunnypilot/sunnypilot/master' into hkg-angle-steering-2025 2025-10-15 17:41:56 -04:00
Shane Smiskol ec33519dc7 raylib: revert 0 button padding (#36360)
* back to 20

* here only
2025-10-15 01:38:00 -07:00
Shane Smiskol 2fd4b53aaf raylib: smooth path distance (#36278)
* smooth max distance

* junk

* clean up

* final

* you can read

* Update selfdrive/ui/onroad/model_renderer.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* true

* fix

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-15 00:48:05 -07:00
Shane Smiskol a2c4fe1c90 raylib: fix setup styles (#36322)
* hardcoding is bad for you

* do updater

* reset

* lint

* duh!

* fixup setup

* fixup updater

* unround
2025-10-15 00:11:16 -07:00
Shane Smiskol 3e56612990 raylib: fix emoji vertical centering (#36358)
* space

* font scale

* fix centering
2025-10-15 00:06:04 -07:00
Shane Smiskol 75858673c4 less rounded border 2025-10-15 00:01:52 -07:00
Shane Smiskol 57223958b5 raylib screenshots: add more homescreen states (#36356)
* hmm can do yeidl approach

* clean up

* clean up

* flip

* and add paired + prime

* sort and add update params

* try

* all should have branch name

* test

* clean up

* add offroad alert to update screen
2025-10-14 23:56:15 -07:00
Shane Smiskol 3553a754a4 Fix vendored emoji font (#36357)
* add font

* use it

* rm old one
2025-10-14 22:45:33 -07:00
David f290fb1e05 keyboard: fix double space (#36345)
* support multiple characters added add cursor position

* fix

* remove double space

* Revert "fix"

This reverts commit c938a52995b6f5343b461f408af5838b78f453d2.

* Revert "support multiple characters added add cursor position"

This reverts commit d8225a768686a88f2bdaabae6d2a57c541ac7f77.
2025-10-14 22:15:31 -07:00
David 0c64818f52 Add screenshot for advanced network settings (#36351)
add screenshot for advanced network settings
2025-10-14 21:36:04 -07:00
ZwX1616 c44548ba0f camerad: make wide brightness more consistent with road (#36355)
align
2025-10-14 21:27:52 -07:00
Shane Smiskol 59bddfba8d raylib: rounded onroad corners (#36348)
* rounded corners

* use scissor

* 0.1

* middle

* don't trust chatter

* round

* clean p

* cleanup

* rev
2025-10-14 21:24:36 -07:00
Shane Smiskol 8a1fcd8991 raylib: fix possible DM crash (#36354)
* fix

* bruh

* clean up

* here

* rm
2025-10-14 21:17:36 -07:00
Jason Wen 5a3c6ddf57 gate lka angle steering out of alpha long 2025-10-14 23:25:00 -04:00
felsager 3546b625e7 latcontrol_torque: change in kp should not affect effective low speed factor gain (#36335) 2025-10-14 13:22:17 -07:00
Jason Wen eaa8732ab0 Merge branch 'e2e-alerts-cooldown' into hkg-angle-steering-2025 2025-10-14 14:44:00 -04:00
Jason Wen 6f0284c84f try preventing startup false trigger 2025-10-14 14:43:47 -04:00
Jason Wen 999ea03f23 try preventing startup false trigger 2025-10-14 14:42:42 -04:00
Jason Wen 0975db3ff1 Merge branch 'e2e-alerts-cooldown' into hkg-angle-steering-2025 2025-10-14 14:31:59 -04:00
Jason Wen 8d70a8b80a only when long not engaged 2025-10-14 14:31:48 -04:00
Jason Wen da93f92887 rename 2025-10-14 14:26:57 -04:00
Jason Wen 9117f6c071 Merge branch 'e2e-alerts-cooldown' into hkg-angle-steering-2025 2025-10-14 14:23:46 -04:00
Jason Wen 3567ff9691 introduce recent moving check 2025-10-14 14:22:41 -04:00
Jason Wen f44ae2ced9 too complicated 2025-10-14 14:08:40 -04:00
Jason Wen 376e0ca615 only allow one trigger per standstill session 2025-10-14 14:07:16 -04:00
Jason Wen 32b7686468 Merge branch 'master' into e2e-alerts-cooldown 2025-10-14 11:40:58 -04:00
nayan b9e0f52ea9 E2E Alert Cooldown 2025-10-14 07:43:15 -04:00
Shane Smiskol 87443cd34d raylib: background onboarding texture loading (#36343)
* this seems best so far

* better

* clean up

* debug

* debug

* clean up

* final
2025-10-14 00:45:03 -07:00
Shane Smiskol 5f0e9fce61 raylib: update experimental mode homescreen button (#36344)
* update homescreen exp mode button

* and here

* Apply suggestions from code review
2025-10-14 00:40:41 -07:00
Shane Smiskol a2cce7f897 raylib: fix border radius (#36346)
* colors

* revert color

* rev
2025-10-14 00:39:22 -07:00
Shane Smiskol f4041dc1f0 raylib: black sidebar (#36347)
black sidebar
2025-10-14 00:33:38 -07:00
Jason Wen a3163b680f Merge branch 'sla-chimes' into hkg-angle-steering-2025 2025-10-14 02:01:05 -04:00
Jason Wen 8a927d808f Merge remote-tracking branch 'sunnypilot/sunnypilot/master' into hkg-angle-steering-2025
# Conflicts:
#	opendbc_repo
2025-10-14 02:00:36 -04:00
Jason Wen bc7d5e474d Speed Limit Assist: audible alerts for certain states 2025-10-14 01:45:13 -04:00
Shane Smiskol 8e3757ac87 raylib: image dimensions are optional (#36332)
* meas

* now no resizing

* clean up
2025-10-13 16:49:39 -07:00
David 41fa0cdf82 fix cycle offroad alerts (#36302)
* fix: use parse_release_notes in cycle_offroad_alerts

* fix: set update available param true

* Update selfdrive/ui/tests/cycle_offroad_alerts.py

---------

Co-authored-by: Shane Smiskol <shane@smiskol.com>
2025-10-13 14:07:11 -07:00
David 692f5fdd72 Add screenshot for experimental mode description (raylib UI test) (#36327)
* feat: add test case to expand experimental mode description

* Update selfdrive/ui/tests/test_ui/raylib_screenshots.py

---------

Co-authored-by: Shane Smiskol <shane@smiskol.com>
2025-10-13 13:56:47 -07:00
Shane Smiskol de0a1e66d8 software download screenshot (#36326)
* software

* clean up

* Qt ButtonControl has 0 padding

* clean up

* clean up
2025-10-13 13:54:50 -07:00
Jason Wen 36f192b5fe Merge remote-tracking branch 'sunnypilot/sunnypilot/master' into hkg-angle-steering-2025 2025-10-13 03:10:47 -04:00
Maxime Desroches 129445cd1d setup: don't wait for so long (#36323)
nice
2025-10-12 00:37:36 -07:00
Shane Smiskol 13d0aefd7c raylib: don't get old onroad alert on startup 2025-10-11 23:40:42 -07:00
Shane Smiskol 5f7b05e808 raylib: don't create vipc client twice first time 2025-10-11 23:36:05 -07:00
Shane Smiskol 32f65bae55 alpha long: allow toggle while onroad + restart op
it's alpha, and some cars don't fault (we allow other toggles which would fault, so why not enable)
2025-10-11 23:29:08 -07:00
Maxime Desroches 49d9b8bb00 ui: fix cloudlog spam (#36321)
* dark office

* check

* back

* fix

* remove

* remove
2025-10-11 23:06:06 -07:00
Shane Smiskol b3eba70b7a raylib: flip! (#36319)
* flip!

* add ui

* ?

* qt is extra

* low node

* add uiDebug

* fix

* fix dat

* bump double increase for tol

* it's ~11ms but double for tol

* fix report

* Update selfdrive/test/test_onroad.py
2025-10-11 07:41:29 -07:00
Shane Smiskol cec7a5dc98 raylib: fix styling for fullscreen alerts (#36318)
* fix that

* fix styling

* and this

* revert

* fix full

* revert
2025-10-11 06:55:35 -07:00
Shane Smiskol 14993f58e3 raylib: set speed fixes (#36317)
* remove msaa artifacting by heavily reducing segments and match radius

* always draw set speed with '-' like qt

* clean up

* match qt behavior for rivian
2025-10-11 06:28:49 -07:00
Shane Smiskol e8a17b4963 raylib: fix stale frames going onroad (#36314)
* fix

* try

* flip

* now flip

* fix network nav button heights

* revert
2025-10-11 06:04:27 -07:00
Shane Smiskol fb77212221 raylib: add more spacing to network nav buttons 2025-10-11 06:02:10 -07:00
Shane Smiskol aa7f6973c0 raylib: match Qt onroad button disabling (#36316)
* fixxx

* clean up

* disable onroad: adb, joystick, alpha long
2025-10-11 06:01:17 -07:00
Maxime Desroches c2af5a82ff add earcut python package 2025-10-11 04:29:05 -07:00
Shane Smiskol 348114e5bd raylib: remove cut stuff (#36310)
remove cut stuff
2025-10-11 04:27:19 -07:00
Shane Smiskol a6e28ac2ee raylib: disable mouse thread lag print (#36312)
every other process disables this
2025-10-11 04:27:01 -07:00
Maxime Desroches 0e6f78a656 raylib is now a core dependancy 2025-10-11 04:23:16 -07:00
Shane Smiskol 2305fb59a2 raylib: fix mic indicator (#36309)
* fix mic

* move out
2025-10-11 03:40:40 -07:00
Shane Smiskol cc816043c1 raylib: add non-inline b tag (#36305)
* debug

* here too

* clean up
2025-10-11 03:36:47 -07:00
Shane Smiskol b6dbb0fd8d raylib: font sizes from QT should match (#36306)
* pt 2

* fix line height

* fixup html renderer

* fix sidebar

* fix label line height

* firehose fixups

* fix ssh value font styling

* fixup inputbot

* do experimental mode

* pairing dialog numbers

* fix radius for prime user

* add emoji to firehose mode

* full screen registration

* fix registration btn size

* fix update and alerts

* debugging

* Revert "debugging"

This reverts commit 0095372e9479d8c727bcc8a78061f582d852133d.

* firehose styling

* fix offroad alerts missing bottom spacing expansion

* huge oof

* huge oof
2025-10-11 03:29:24 -07:00
Jason Wen ffd5cd4ac2 Merge remote-tracking branch 'sunnypilot/sunnypilot/master' into hkg-angle-steering-2025 2025-10-11 02:29:47 -04:00
Shane Smiskol fdcf8b592e raylib: reset scrollers and description expansions on show event (#36304)
* scroll up on hide

* switch to show

* dismiss descriptions too!

* all is show

* all is show

* clean up

* visible items helper

* Revert "visible items helper"

This reverts commit e64f05b69155483aa0f3d74bd511f5d7c1ecfb79.

* reset
2025-10-10 21:07:36 -07:00
Jason Wen e4a00fcd6c Merge remote-tracking branch 'sunnypilot/sunnypilot/master' into hkg-angle-steering-2025 2025-10-10 17:28:16 -04:00
Jason Wen 0bdcb41103 Merge remote-tracking branch 'sunnypilot/sunnypilot/master' into hkg-angle-steering-2025
# Conflicts:
#	common/params_keys.h
#	opendbc_repo
#	selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.cc
2025-10-10 17:02:14 -04:00
Shane Smiskol 4ff77a4752 raylib: fix DMoji (#36301)
* wow first time cursor made a tiny change to fix a problem!

* rm
2025-10-10 04:10:24 -07:00
Shane Smiskol f04ee80452 raylib: implement calibration description (#36300)
* this is all cursor

* also cursor

* inline reset calib

* calib desc

* way better

* huh

* clean up

* rcvr

* stash changes to change params

* Revert "stash changes to change params"

This reverts commit ee998f04c4235ed20493b83e35c9f28e182d89b0.
2025-10-10 03:57:12 -07:00
Shane Smiskol ddbbcc6f5d raylib: add experimental mode + alpha long confirmation dialog + related fixes (#36295)
* here's everything

* just the dev part

* same for exp mode!

* use rich

* fix br not working in p

* html height needs to be different than content/text rect

* fix confirmation

* fix

* fix 2.5s lag

* clean up

* use correct param

* add offroad and engaged callback too

* nl

* lint
2025-10-10 03:03:35 -07:00
Shane Smiskol 0f40afa357 raylib: fix black updater bg for network (#36299)
fix black bg
2025-10-10 01:41:11 -07:00
Shane Smiskol cac8d3f405 raylib: fix missing showing dialog in setup/updater (#36298)
* fix showing dialog

* here for safety
2025-10-10 01:40:29 -07:00
Shane Smiskol b521a913ab raylib: confirm dialog uses HTML renderer 2 (#36297)
* start

* keep it simple

* rm
2025-10-10 01:18:07 -07:00
Shane Smiskol d6651ccd82 raylib: implement developer panel (#36292)
* first pass by cursor

* fix

* tell it what's good

* stash

* desc

* clean up junk

* alpha long can't use onroad cycle again due to faults

* lint

* fix kb
2025-10-09 23:09:55 -07:00
Shane Smiskol 2976798852 raylib: implement toggles (#36284)
* start on exp mode

* more

* fmt

* rm

* 2nd try

* almost there

* clean up

* and this

* fmt

* more

* exp is colored when active

* move out, and rm redudnant self.state

* revert html changes for now

* fix untagged text inheriting previous tag

* why would this be unknown

* here too

* update live with car

* clean up + refresh toggles on showEvent + catch from cursor about setting desc if no carparams

* not sure why

* fix disengaged re-enabling locked toggles
2025-10-09 19:50:27 -07:00
Shane Smiskol 1b90b42647 Html renderer: reset tag so untagged text doesn't inherit last tag 2025-10-09 19:10:10 -07:00
felsager de805e4af7 Lateral torque controller: use measurement rate as error rate (#36291) 2025-10-09 14:57:12 -07:00
YassineYousfi 4d085424f8 North Nevada Model 🏜️ (#36276)
* e2d9c622-25a8-4ccd-8c8e-c62537b7aa0c/400

* 0e620593-e85f-40c2-9adf-1e945651ed13/400
2025-10-09 12:58:27 -07:00
felsager d07981ea3c bump opendbc (#36289) 2025-10-09 11:48:13 -07:00
felsager 22d5cbd0fa PID: coefficients should be in front, i_rate should be i_dt (#36288) 2025-10-09 11:10:44 -07:00
felsager 4c9ca91b98 Latcontrol: use more accurate naming for saturation time (#36286) 2025-10-09 10:34:26 -07:00
felsager 0736f325fc Latcontrol torque: cleaner low_speed_factor calculation (#36287) 2025-10-09 10:29:35 -07:00
kostas.pats dcc5afa8fa improve webrtc stack for use in camera focusing (#36268)
* made LiveStreamVideoStreamTrack use system time to calculate pts

* fixes as requested

* Align panda submodule with master (panda@615009c)

* made loggerd accept a run time env variable to pick stream bitrate

* added /notify endpoint to send json to all session's data channel

* fixed static analysis error

* adapted webrtc stream test to new pts calculation method

* fixed static erro

* fixed wrong indent

* fixed import order

* delete accidental newline

* remove excess spaces

Co-authored-by: Maxime Desroches <desroches.maxime@gmail.com>

* remove excess spaces

Co-authored-by: Maxime Desroches <desroches.maxime@gmail.com>

* changed exeption handling based on review

* fixed typo on exception handling

---------

Co-authored-by: Maxime Desroches <desroches.maxime@gmail.com>
2025-10-08 22:30:32 -07:00
felsager 226465e882 Latcontrol: refactor pid error to factor out lateral jerk component (#36280) 2025-10-08 18:29:54 -07:00
Shane Smiskol 0b62dbe16b raylib: more closely match Qt alert sizes (#36283)
* hmm this doesn't work

* clean up

* more

* bad fmtr

* match sidebar net texts

* better
2025-10-08 17:29:45 -07:00
felsager 2deb4e6f65 Lateral controllers: pass dt (delta time) explictly (#36281) 2025-10-08 14:39:05 -07:00
felsager 9f32f217e6 Latcontrol: type annotate update inputs and clip_curvature output (#36282) 2025-10-08 14:26:53 -07:00
Shane Smiskol e62781cccb Revert "raylib: font sizes from QT should match (#36237)"
This reverts commit 7933c10c97.
2025-10-08 04:05:49 -07:00
Shane Smiskol e1912fa5be raylib: speed up polygon shader (#36275)
* actually works

* fix shader grad

* switch

* our own triangulate

* this is amazing

* ok 100 is too much for 3x. 10?

* fix colors

* review intern chad

* fmt

* rm for the line count

* bye

* rm

* see the diff

* start to revert nulleffect

* fix

* fix

* always feather

* aliasing doesn't seem necessary

* aliasing doesn't seem necessary

* fix lane lines disappearing halfway up due to buggy deduping -- very simple triangulation function takes ~same CPU time + same GPU utilization on PC (nvidia-smi)

* remove old

* even simpler triangulate

* this is useless

* more revert

* split color out again

* clean up ai bs

* back to original names

* more clean up

* stop it

* this limiting logic split out feels more even // less super dense

* typing

* clean up a little

* move to get grad color

* RM

* flip

* document

* clean up

* clean up

* clean

* clean up

* not a "state"

* clean up

* that did nothing

* cmt
2025-10-08 03:51:37 -07:00
Maxime Desroches a7fe9db773 fix installer build 2025-10-06 16:37:50 -07:00
Shane Smiskol 35296a8692 flip setting order (#36266)
flip
2025-10-06 00:56:13 -07:00
Maxime Desroches 31801a7312 no more wayland for installer 2025-10-04 02:47:25 -07:00
Maxime Desroches cc7ecd53c7 raylib: bump commit 2025-10-04 02:45:40 -07:00
Shane Smiskol 586e49cab3 Revert "Switch to raylib for UI (#36238)"
This reverts commit c88ab5cd12.
2025-10-04 01:04:20 -07:00
Shane Smiskol ebe47a580c raylib: fix registration box height 2025-10-04 01:04:05 -07:00
Shane Smiskol 7933c10c97 raylib: font sizes from QT should match (#36237)
* debug

* hacks everywhere but kind of works

* by font

* fix sidebar

* stash

* test update

* just use a const

* just use a const

* better

* clean up

* fix label

* simplify

* gpt5 is yet again garbage

* rm that

* clean up

* rm

* blank

* clean up

* I really don't like this but shrug

* fix

* fix experimental text
2025-10-04 00:32:49 -07:00
Shane Smiskol 2bc97ee23f raylib: fix DM popup (#36265)
* come on

* try

* better

* better

* multiple places!

* debug

* works

* temp

* whoops

* wonder if this wortks

* ah need this!

* wtf is this when deleted?

* another day no modal show event

* clean

* fix

* ugh

* need this
2025-10-04 00:05:20 -07:00
Shane Smiskol c88ab5cd12 Switch to raylib for UI (#36238)
* flip

* change this
2025-10-03 23:38:10 -07:00
Shane Smiskol 943aaef76a raylib: match Qt onroad alert colors (#36264)
fix alert colors
2025-10-03 23:32:17 -07:00
Shane Smiskol 3fd9e94a34 raylib: all system apps work without anything built (#36261)
* all system apps work without scons

* better

* fix

* revert

* fix

* dont add

* huh
2025-10-03 23:18:20 -07:00
Shane Smiskol e423f8f605 raylib: elide version on homescreen (#36263)
* elide ver on hom

* rm line

* blank
2025-10-03 23:17:51 -07:00
Shane Smiskol 0eb90ecb3e raylib: elide list item actions (#36262)
fix
2025-10-03 23:04:55 -07:00
Maxime Desroches 703f3d0573 disable sim test for now 2025-10-03 22:09:00 -07:00
Shane Smiskol 2337704602 raylib: release notes are drawn with HTML renderer (#36245)
* stash

* ok chatter is useful for once

* draw text outside tags

* hmm

* undo that shit

* i don't like this chatgpt

* Revert "i don't like this chatgpt"

This reverts commit 5b511911d81242457bfb5fc808a9b9f35fe9f7a2.

* more robust parsing (works with missing tags, markdown.py actually had bug) + add indent level

* the html looks weird but is correct - the old parser didn't handle it

* clean up

* some

* move out

* clean up

* oh this was wrong

* draft

* rm that

* fix

* fix indentation for new driving model

* clean up

* some clean up

* more clean up

* more clean up

* and this

* cmt

* ok this is egregious mypy
2025-10-03 21:47:53 -07:00
Shane Smiskol bd9888a439 raylib screenshots: add software release notes (#36259)
add software
2025-10-03 21:29:20 -07:00
Shane Smiskol 12b3d0e08d raylib: cache wrap text (#36258)
* cache html height

* clean up

* todo
2025-10-03 20:52:50 -07:00
Shane Smiskol 89d350a791 raylib html renderer: fixups (#36257)
* this wasn't used

* override text size and color

* render untagged text as paragraph

* and indent

* cache expensive height calc

* fmt

* fix that

* unclear if this is even needed

* and that

* huh

* debug

* Revert "debug"

This reverts commit 7d446d2a37a96e6bd1001c566d4f8e8f417f8fb7.
2025-10-03 20:42:42 -07:00
Shane Smiskol 99a83e5522 Revert "raylib screenshots: find diff faster (#36255)"
This reverts commit a8328cb5ff.
2025-10-03 20:35:32 -07:00
Armand du Parc Locmaria 4d53a26a06 relock after inplace metadrive update (#36256)
* relock after inplace metadrive update

* Revert "relock after inplace metadrive update"

This reverts commit 18193ffe34b66085e18605e6c9289ddcd658844d.

* just the hash
2025-10-03 19:43:03 -07:00
Shane Smiskol a8328cb5ff raylib screenshots: find diff faster (#36255)
* ?

* run it

* wrong

* here too

* revert
2025-10-03 17:59:42 -07:00
Shane Smiskol 844c328625 raylib screenshots: prevent saving black frame
raylib screenshots: prevent saving black frame
2025-10-03 17:59:19 -07:00
Shane Smiskol 39b97d4e18 raylib screenshots: use long branch name (#36254)
* stress test

* everything
2025-10-03 17:50:02 -07:00
Shane Smiskol 45f497e8f6 raylib screenshots: add mouse click helper (#36253)
* add helper

* rm

* name

* fix
2025-10-03 17:46:39 -07:00
Shane Smiskol edc5a0412c rename to setup_settings 2025-10-03 17:35:10 -07:00
Shane Smiskol 9670e3a5eb raylib: add confirmation dialog (#36252)
* conf

* update case

* fix

* fix

* rm

* back

* alread setup

* avail
2025-10-03 17:34:53 -07:00
Shane Smiskol 7b2b10bc9e raylib screenshots: raise ui delay 2025-10-03 17:34:22 -07:00
YassineYousfi bd357adb8b update release notes for 0.10.1 2025-10-03 17:01:20 -07:00
Shane Smiskol 670b6011da raylib: match QT confirmation dialog size (#36248)
* closer to qt

* this too

* eval
2025-10-03 16:13:40 -07:00
Armand du Parc Locmaria 150ff72646 Dockerfile.openpilot: don't set UV_PROJECT_ENVIRONMENT (#36246)
* Dockerfile.openpilot: don't uv sync with root

* Revert "Dockerfile.openpilot: don't uv sync with root"

This reverts commit 2c271d0b5b55d6ae2ece6b28dc90a96e6e891ded.

* don't set UV_PROJECT_ENVIRONMENT
2025-10-03 14:36:12 -07:00
Shane Smiskol d567442136 raylib: split out HTML renderer (#36244)
* stash

* ok chatter is useful for once

* why doesn't it understand?!

* rm that

* clean up
2025-10-03 00:37:47 -07:00
Shane Smiskol 540fff5226 raylib: draw update button and fix incorrect font (#36243)
* always update layout rects

* don't ever use raylib font

* use it

* such as
2025-10-03 00:20:30 -07:00
Shane Smiskol 21273c921e raylib: excessive actuation offroad alert (#36242)
* excessive actuation check

* from gpt

* back

* use buttons

* use widgets for ultimate clean up - no ai slop

* feature parity

* revert

* clean up
2025-10-02 23:54:31 -07:00
Shane Smiskol 75e52427d1 raylib: fix when we show offroad alerts and styles (#36240)
* fix how we show alerts

* test this too

* match border radius

* simplifty

* keep

* back

* fix alert spacing

* fix alert text padding

* cmt

* cmt
2025-10-02 22:53:02 -07:00
Shane Smiskol 21fd3d0320 raylib: use extra text in offroad alerts (#36241)
* replace properly

* test
2025-10-02 21:21:50 -07:00
Shane Smiskol 1ee798439a raylib: WiFi fixes (#36239)
* proces in AN and WM

* clean

* ban api

* fix

* fiix

* fix pairing dialog

* cleanup

* fix multi action button hard to click

* fix

* fix right margin of multi action

* clean up
2025-10-02 21:09:17 -07:00
Armand du Parc Locmaria cc52f980b3 Dockerfile.openpilot uv run scons (#36236)
* Dockerfile.openpilot_base use UV_PROJECT_ENVIRONMENT

* Revert "Dockerfile.openpilot_base use UV_PROJECT_ENVIRONMENT"

This reverts commit 3725e54ce0727077ca4347d24ca38e25d5864d47.

* Reapply "Dockerfile.openpilot_base use UV_PROJECT_ENVIRONMENT"

This reverts commit 11b04f57acb9c81fcc5a22a6a6d78d666c59ca6c.

* use uv run to pick up correct ppath
2025-10-02 15:31:39 -07:00
Shane Smiskol ec7e3192bb revert that 2025-10-02 04:00:09 -07:00
Shane Smiskol 3fd352a7ef raylib: updater UI (#36235)
* auto attempt

* gpt5

* Revert "gpt5"

This reverts commit 556d6d9ee4d53aca0f4612023db6cfb2bed7ce29.

* clean up

* fixes

* use raylib

* fixes

* debug

* test update

* more

* rm

* add value to button like qt

* bump

* bump

* fixes

* bump

* fix

* bump

* clean up

* time ago like qt

rm

* bump

* clean up

* updated can fail to respond on boot leading to stuck state

* fix color

fix

* bump

* bump

* add back

* test update

* no unknown just ''

* ffix
2025-10-02 03:57:10 -07:00
Shane Smiskol 49570c11c6 Remove animation from networking 2025-10-02 02:04:09 -07:00
Shane Smiskol b8ae62a0b1 raylib scroll panel: check bounds (#36233)
check in bounds rect for scroll panel!!
2025-10-01 01:02:35 -07:00
Shane Smiskol 29a6f0504a raylib: fix WiFi in setup and updater (#36232)
move back to more base class
2025-10-01 00:46:51 -07:00
Shane Smiskol eadab06f59 raylib: remove gui_button (#36229)
* vibing can be good

* and listview

* rm that

* html render

* text.py

* ssh keys

* updater w/ Auto

* wow gpt5 actually is better

* well this is better

* huh wifi still doesn't work

* lfg

* lint

* manager waits for exit

* wait a minute this changes nothing

* this will work

* whoops

* clean up html

* actually useless

* clean up option

* typing

* bump
2025-10-01 00:32:09 -07:00
Shane Smiskol 9493f2a0eb raylib: remove functional confirmation dialog (#36231)
* rm

* yess

* clean up
2025-09-30 23:48:04 -07:00
Shane Smiskol b593b7cc43 raylib: SSH key text entry works more than once (#36230)
* impossible

* jarn

* actually space

* forgot
2025-09-30 23:43:23 -07:00
Shane Smiskol 5c0c2a17b0 raylib: add mic indicator (#36207)
* update lang

* mic indicator

* clean up

* clean up

* switch

* fix

* revert
2025-09-30 22:30:45 -07:00
Shane Smiskol 5f33b2fb2d raylib: frame independent scroller (#36227)
* rm that

* almost

* yess

* some work

* more

* todo

* okay viber is good once in a while

* temp

* chadder can't do this

* revert

* this was broken anyway

* fixes

* mouse wheel scroll

* some clean up

* kinda works

* way better

* can tap to stop

* more clean up

* more clean up

* revert last mouse

* fix

* debug only

* no print

* ahh setup.py fps doesn't affect DEFAULT_FPS ofc

* rest

* fix text

* fix touch valid for network
2025-09-30 22:25:43 -07:00
Shane Smiskol 63e0e038fa raylib: don't use DEFAULT_FPS (#36228)
* dont use DEFAULT_FPS

* replace
2025-09-30 22:11:21 -07:00
ZwX1616 d24a14cb39 DM: Large Donut model 🍩 (#36198)
* 59cfd731-6f80-4857-9271-10d952165079/225

* deprecate at the end
2025-09-30 20:32:19 -07:00
Shane Smiskol 3efa52f53b fix missing import 2025-09-30 20:05:40 -07:00
Shane Smiskol 16a4206720 Revert "Reapply "raylib: 20 FPS onroad (#36208)""
This reverts commit ed185e90f6.
2025-09-30 16:34:45 -07:00
Maxime Desroches e4784d44f6 bump panda (#36226)
bump
2025-09-30 14:33:10 -07:00
Shane Smiskol aaf2aac050 raylib: training guide (#36224)
* fix regulatory

* debug slow loading

* easy gather step coords

* gotcha

* and fix

* dm option

* fix final

* fixes

* progress bar!

* "vibe coding is great"

* wtf gpt5

* jfc

* hand crafted >> vibe

* it's slow so only load images if we're doing any kind of training

* tf

* format

* clean up

* clean up

* no float

* cmt

* more clean up

* clean up

* eww

* rm

* no debug

* match y

* clean that up

* here too

* windows
2025-09-30 03:11:42 -07:00
Shane Smiskol b5ec0e9744 raylib: fix regulatory 2025-09-30 02:39:19 -07:00
Shane Smiskol 070a13096b raylib: add todo for niceness (#36210)
* not nice

* hmm

* debug

* todo

* revert

* yep
2025-09-29 13:03:03 -07:00
commaci-public 7ccab2bdb9 [bot] Update Python packages (#36220)
* Update Python packages

* revert tg, model diff looks a bit fishy

---------

Co-authored-by: Vehicle Researcher <user@comma.ai>
Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
2025-09-28 13:50:13 -07:00
Shane Smiskol e9434befaa Refactor offroad alerts loading to use OFFROAD_ALERTS (#36214)
* Refactor offroad alerts loading to use OFFROAD_ALERTS

* clean up
2025-09-27 03:37:23 -07:00
Shane Smiskol 56c77fd5fa re-run 2025-09-27 03:32:19 -07:00
Shane Smiskol e6bd88371e fix! 2025-09-27 02:55:40 -07:00
Shane Smiskol bc30b01eb7 Fix raylib ui report (#36215)
hmm
2025-09-27 02:53:21 -07:00
Shane Smiskol ef93981bfa raylib: ui diff test (#36213)
* add raylib ui test

* match qt

* exe

* vibing is epic

* this is epic

* format

* add more settings

* fix to actually use raylib

* add kb

* global

* pair

* rm cmts

* show event

* this is so stupid

clean up

* clean up

* rename dir

* clean up

* no more vibe

* rm

* ugh it's always slightly different for no reason

* nvm region is actually broken

* 1l
2025-09-27 02:37:35 -07:00
Shane Smiskol 35e2fc7dd9 raylib: use touch thread in all places (#36212)
* fix not opening alerts

* whops

* rm mouse pressed from offroad alerts

* ah its a base class

* one last place

* fix

* rm lines
2025-09-26 23:49:17 -07:00
Shane Smiskol 2feddf32b2 raylib: fix lost onroad tap events (#36211)
* debug

* see it's good to have abstraction

* clean up

* fine

* wtf do you mean mypy? how can you not coerce this?
2025-09-26 22:40:38 -07:00
Shane Smiskol ed185e90f6 Reapply "raylib: 20 FPS onroad (#36208)"
This reverts commit 5cbfc7705b.
2025-09-26 21:43:15 -07:00
Shane Smiskol 19fc66f88a Fix tearing offroad 2025-09-26 21:41:51 -07:00
Shane Smiskol 5cbfc7705b Revert "raylib: 20 FPS onroad (#36208)"
This reverts commit 8de8c3eb00.
2025-09-26 21:41:12 -07:00
Shane Smiskol 8de8c3eb00 raylib: 20 FPS onroad (#36208)
* 20

* dynamic fps

* flip

* init to be safe

* fix possible fps weirdness

* gate on change

* not now

* rev
2025-09-26 21:38:59 -07:00
Shane Smiskol 04365f12ff raylib: remove unused globals 2025-09-26 21:10:09 -07:00
Shane Smiskol 9297cd2f3e raylib: use filter for allow throttle (#36209)
* use time here

* use epic filter

* rm

* intermediary

* tune
2025-09-26 21:08:39 -07:00
Shane Smiskol 0711160b1c raylib: dismiss dialog on pair (#36205)
* show for unknown

* use Button to make clicking work

* close on pair

* close on pair

* make widget!

* dynamic pairing btn

* whyyy

* clean up

* can do this

* this button is also hard to tap
2025-09-26 18:59:15 -07:00
Shane Smiskol 33f01084d1 raylib: implement cell settings (#36204)
* get vibing

* simplify

* vibing is bad

* simplify

* fix that

* now update

* clean up

* last two

* cell is UpdateUnsaved so we don't need to disable

* we only need actions

* we only need actions

* sort

* stuff

* dont deactivate

* clean up

* clean up

* more

* ipv4 fwd

* warns

* fixz

* rm

* clean up

* one return point

* format

* top
2025-09-25 23:44:12 -07:00
Adeeb Shihadeh 1fbec6f601 remove .clang-tidy 2025-09-25 21:02:26 -07:00
Adeeb Shihadeh cf5b743de6 build system cleanups (#36202)
* it's all common

* never getting fixed

* it's just tici

* reorders

* qcom2 -> tici

* Revert "qcom2 -> tici"

This reverts commit f4d849b2952cb0e662975805db6a1d32511ed392.

* Reapply "qcom2 -> tici"

This reverts commit 58b193cb8de872830f8a7821a339edca14e4a337.

* is tici

* lil more

* Revert "is tici"

This reverts commit a169be18d3fdcb3ef8317a63a89d8becadabfad8.

* Revert "Reapply "qcom2 -> tici""

This reverts commit 26f9c0e7d068fc8a1a5f07383b3616e619cd4e8c.

* qcom2 -> __tici__

* lil more

* mv lenv

* clean that up

* lil more]

* fix

* lil more
2025-09-25 20:55:14 -07:00
Shane Smiskol 2c377e534f raylib: wifi manager initialize function (#36203)
* init func

* rm print

* rm

* use get_conn settings in another place
2025-09-25 20:48:12 -07:00
Shane Smiskol 1ca9fe35c2 raylib: networking parity with QT (#36197)
* match style

* all this was not naught

* cool can do this

* fix toggle callback - also not for naught

* always process callbacks

* toggle stuff

* cleaner

* tethering password

* clean up

* todos for later

* this is fineee

* add metered options

* wifi metered button

* add hidden network buutton and fix instant modal to modal

* damped filter

* Revert "damped filter"

This reverts commit f9f98d5d708fb15cf1ebef4bdace577f0e347658.

* fix metered toggle when disconnected

* fix tethering enabled

* ohh

* fix keyboard title

* disable edit button temp

* move here

* proper disable

* clean up

* more

* move for loop into enqueue function

* flippy

* got more :(

* todo

* clean up

* mypy

* rename

* todo

* rename

* again

* again

* format
2025-09-25 20:16:14 -07:00
Adeeb Shihadeh 56c49b3b42 cleanup dead build flags 2025-09-25 19:28:16 -07:00
Shane Smiskol 5429748767 raylib: fix button clicking on device (#36201)
* fix button clicking on device

* clean up
2025-09-25 19:27:13 -07:00
Greg Hogan 6aecf59536 add ssh hostname comma- prefix for convenience (#36199) 2025-09-25 17:42:11 -07:00
Shane Smiskol afc7ff1b7a raylib: fix multilang dialog height (#36196)
* fix multilang dialog height

* clean up
2025-09-24 17:14:06 -07:00
Jason Young 222e880561 Honda: Add 2021 Acura TLX to release (#36193)
* bump opendbc

* regen CARS.md

* add to RELEASES.md
2025-09-24 15:24:15 -04:00
DevTekVE 78051085ca Merge branch 'master' into hkg-angle-steering-2025 2025-09-23 07:54:32 +02:00
DevTekVE 4910d5809a bump opendbc 2025-09-23 07:45:39 +02:00
Maxime Desroches 6901e3417b add 3X release branch to RELEASE_BRANCHES (#36190)
add
2025-09-22 15:18:43 -07:00
commaci-public cd33562379 [bot] Update Python packages (#36188)
Update Python packages

Co-authored-by: Vehicle Researcher <user@comma.ai>
2025-09-22 13:43:26 -07:00
Maxime Desroches 073503a6f2 fix is_dirty when fetching branch with updated (#36187)
fix is_dirty
2025-09-21 23:53:07 -07:00
Maxime Desroches 61d5a50534 Revert "fix is_dirty when switching branch with updated (#36162)"
This reverts commit 30c388aea8.
2025-09-21 22:44:14 -07:00
commaci-public b6e0d4807a [bot] Update Python packages (#36184)
* Update Python packages

* not available anymore

* also this

* also this

* maybe?

* version

* try

* Revert "version"

This reverts commit 9ac4401b9ca59677b82736faff8baf66861df5f2.

* revert

* cffi

* issue

* comment

---------

Co-authored-by: Vehicle Researcher <user@comma.ai>
Co-authored-by: Maxime Desroches <desroches.maxime@gmail.com>
2025-09-20 20:10:51 -07:00
Shane Smiskol c7a37c06d8 Revert "Capnp memoryview (#36163)"
This reverts commit 6ed8f07cb6.

bump
2025-09-19 17:18:08 -07:00
Shane Smiskol efbd0b9ea0 cabana typo 2025-09-19 16:47:07 -07:00
Shane Smiskol 6ed8f07cb6 Capnp memoryview (#36163)
* lock

* bump opendbc

* fix one

* and

* Add a memoryview fallback in webrtcd

* fix

* revert

* rerevert

* bump to master

---------

Co-authored-by: Kacper Rączy <gfw.kra@gmail.com>
2025-09-19 16:46:23 -07:00
mvl-boston 5164555c4f Steering Assist warning clarification (#36135)
* clarifying warning message

* more clarity with steering assist warnings
2025-09-19 16:46:05 -07:00
Jason Young c5999702ae Honda: Add 2026 Honda Passport to release (#36179)
* bump opendbc

* regen CARS.md

* update RELEASES.md
2025-09-19 19:40:20 -04:00
Shane Smiskol 2a5de8e0f8 raylib: fix shader antialiasing (#36176)
* fix

* np
2025-09-18 17:11:53 -07:00
Shane Smiskol d05cb31e2e raylib: fix pairing url 2025-09-18 17:03:16 -07:00
Adeeb Shihadeh c7a9ea2bf4 add back libbz2-dev (#36172)
* add back libbz2-dev

* try this

* revert
2025-09-18 10:59:03 -07:00
Adeeb Shihadeh b637ad49d9 vendor all fonts (#36170)
add noto color
2025-09-18 09:25:41 -07:00
Adeeb Shihadeh c6a2c99123 prep for vendoring (#36169)
* prep for vendoring

* less stuff

* comment
2025-09-18 09:17:26 -07:00
Adeeb Shihadeh 852598fa0a fix mac build (#36168) 2025-09-18 08:34:28 -07:00
Adeeb Shihadeh 3751d9cf51 Remove libsystemd-dev from Ubuntu dependencies (#36167)
Removed 'libsystemd-dev' from the list of dependencies.
2025-09-18 08:22:15 -07:00
Maxime Desroches 30c388aea8 fix is_dirty when switching branch with updated (#36162)
* clean

* fix
2025-09-18 00:07:06 -07:00
Shane Smiskol b622e3e0a7 raylib: generic click callback (#36166)
* not to be used outside

* same

* rm

* fix that

* another fix

* ehh probably better to still have

* optional
2025-09-17 16:33:48 -07:00
Mitchell Goff 086e33dd6e Revert "minimal ffmpeg build (#36138)"
This reverts commit 347b23055d.
2025-09-16 14:25:18 -07:00
Kacper Rączy 889ce4c4fb torqued: add DEBUG flag (#36161)
Add a debug flag to torqued
2025-09-15 21:04:33 +00:00
Jason Young 96c00271e3 pin pycapnp (#36160) 2025-09-15 14:37:52 -04:00
DevTekVE 8dd862ff28 yikes, becoming picky huh? 2025-09-14 22:50:21 +02:00
DevTekVE 1b57497da9 Merge remote-tracking branch 'origin/master' into hkg-angle-steering-2025
# Conflicts:
#	opendbc_repo
2025-09-14 22:48:00 +02:00
DevTekVE 8d8d1ffc7a bump opendbc again 2025-09-14 22:46:20 +02:00
DevTekVE c5919d5495 wrong dbc lol 2025-09-14 22:42:19 +02:00
DevTekVE da71951c95 This is no longer in use nor needed. Bai! 2025-09-14 12:56:28 +02:00
DevTekVE 25a152cd8b Merge remote-tracking branch 'origin/master' into hkg-angle-steering-2025
# Conflicts:
#	opendbc_repo
2025-09-14 12:44:23 +02:00
DevTekVE 9077a1082a Sorry for C3 :( but moving you out to a last working branch before I sync it up 2025-09-14 06:52:24 +02:00
DevTekVE 7ae7000254 Rework override behavior and feeling 2025-09-14 06:38:00 +02:00
Adeeb Shihadeh a6adedf6e0 prep for python pandad (#36155) 2025-09-13 11:58:49 -07:00
commaci-public eb821ceb5c [bot] Update Python packages (#36118)
* Update Python packages

* revert tinygrad

* can cnt

* bump panda

* bump panda

* update panda test

* revert that

---------

Co-authored-by: Vehicle Researcher <user@comma.ai>
Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
2025-09-13 11:48:35 -07:00
Jimmy 98d61982f9 jotpluggler: add README (#36153)
* add README

* fix typo
2025-09-13 00:35:35 -07:00
Jimmy 04a26ada69 jotpluggler: fix bug with char width after scaling text (#36154)
fix bug with char width after scaling text
2025-09-13 00:35:12 -07:00
Jimmy 3e0dd06374 jotpluggler: accept --layout argument to pluggle (#36152)
accept layouts as arg to pluggle
2025-09-13 00:20:53 -07:00
Jimmy f18828228a jotpluggler: fix layout folder path loading and total segment (#36151)
* forgot to commit this earlier with total segments

* look in correct directory
2025-09-13 00:13:27 -07:00
Jimmy c812c3192d jotpluggler: fix hidpi/mac font scaling (#36150)
fix hidpi/mac font scaling
2025-09-12 23:53:41 -07:00
DevTekVE 7029455706 better juggle 2025-09-13 08:32:27 +02:00
Jimmy 8d3b919ef6 jotpluggler: better defaults for zooming/fitting (#36149)
better defaults for zooming/fitting
2025-09-12 23:28:23 -07:00
Jimmy 63df46bf22 jotpluggler: store and load layouts (#36148)
* store and load layouts

* torque controller layout

* ignore missing yaml stubs for mypy
2025-09-12 23:20:12 -07:00
Jimmy 826c5e96a1 jotpluggler: migrate logs (#36147)
migrate logs
2025-09-12 22:15:56 -07:00
Jimmy 1870d4905b jotpluggler: add tabs to layout (#36146)
* queue syncs in main thread to avoid Glfw Error/segfault

* tabs
2025-09-12 21:52:01 -07:00
Adeeb Shihadeh 347b23055d minimal ffmpeg build (#36138)
* min ffmpeg

* remove avfilter

* x264

* merge x264

* simpler

* pin x264

* mac

* rm that

* lil more

* move includes to lfs

* try this

* cleanup

* larch

---------

Co-authored-by: Comma Device <device@comma.ai>
2025-09-12 18:59:15 -07:00
Maxime Desroches cbea5f198f op.sh: more robust switch for submodules 2025-09-12 16:05:49 -07:00
Jimmy be379e188b jotpluggler: fix off by one error (#36144)
fix off by one error sometimes causing missed items in datatree
2025-09-12 14:37:00 -07:00
Jimmy 42d9bd0516 jotpluggler: sync x axes and autofit y axis (#36143)
* sync x axes of all timeseries plots

* always autofit y-axis

* fix typing
2025-09-12 14:36:50 -07:00
Armand du Parc Locmaria 3ca9f351a0 nevada model 🌵 (#36114)
cd29ffcf-01dd-4f1c-8808-dc197c174f1d
2025-09-12 12:45:52 -07:00
YassineYousfi a1d6a062a9 add PR ref to new driving model in RELEASES.md 2025-09-12 12:44:11 -07:00
DevTekVE 3a71a62215 Merge branch 'master' into hkg-angle-steering-2025 2025-09-12 10:14:52 +02:00
DevTekVE 00622e8c33 Merge remote-tracking branch 'origin/master' into hkg-angle-steering-2025
# Conflicts:
#	opendbc_repo
2025-09-05 09:46:41 +02:00
DevTekVE 549da3ee92 Merge branch 'master' into hkg-angle-steering-2025 2025-09-04 20:20:37 +02:00
DevTekVE e038a65ef8 Merge branch 'master' into hkg-angle-steering-2025 2025-08-31 13:14:33 +02:00
DevTekVE 8d0513c657 dbc: update CHECKSUM format for multiple messages to improve data integrity 2025-08-30 19:56:21 +02:00
DevTekVE 2cea48f4cd Merge remote-tracking branch 'origin/master' into hkg-angle-steering-2025
# Conflicts:
#	opendbc_repo
2025-08-30 15:27:45 +02:00
DevTekVE 8855a9ab65 Improve surprise jerk by safety blocks 2025-08-26 14:47:26 +02:00
DevTekVE 32321c01cc A bit more helpful safety block investigation help 2025-08-26 10:09:11 +02:00
DevTekVE 52cd65fefb lint dont bother me 2025-08-25 08:24:24 +02:00
DevTekVE 1fcdeccd40 Merge branch 'master' into hkg-angle-steering-2025
# Conflicts:
#	common/params_keys.h
#	opendbc_repo
2025-08-25 08:01:51 +02:00
DevTekVE 3517c36978 Revert "Add HkgAngleDebug structure and enhance angle debugging in car controller" 2025-08-24 19:02:48 +02:00
DevTekVE fb30c3c1e8 cleanup and honour params 2025-08-23 15:42:47 +02:00
DevTekVE 1c25e568d5 Update steering pressed logic to include hands-on-wheel detection for improved safety 2025-08-23 15:11:07 +02:00
DevTekVE f172122b7c Update steering pressed logic to include hands-on-wheel detection for improved safety 2025-08-22 19:55:16 +02:00
Jason Wen 67d6cdc7cd Merge remote-tracking branch 'sunnypilot/sunnypilot/hkg-angle-steering-2025' into hkg-angle-steering-2025 2025-08-21 16:39:36 -04:00
DevTekVE 6724085cfd Refactor angle limit calculations and adjust average road roll for improved steering dynamics 2025-08-21 20:09:57 +02:00
DevTekVE 6774f34eee Refine non-linear mapping in torque reduction gain calculation for improved steering response 2025-08-21 00:21:46 +02:00
DevTekVE 6d0402896d Adjust STEER_THRESHOLD and refine non-linear mapping in torque reduction gain calculation for improved steering response 2025-08-20 23:16:04 +02:00
DevTekVE 344021a3d9 Adjust non-linear mapping in torque reduction gain calculation for improved response 2025-08-20 19:54:31 +02:00
DevTekVE a9b85ab27d Refactor HkgAngleDebug structure to include current and baseline limits for angle parameters 2025-08-20 19:54:10 +02:00
DevTekVE 17204a46e4 Add HkgAngleDebug structure and enhance angle debugging in car controller 2025-08-20 18:39:53 +02:00
DevTekVE 7c4d415462 Enhance torque reduction gain calculation with non-linear mapping and smoothing 2025-08-20 00:12:43 +02:00
DevTekVE b8985b6d72 Enhance torque reduction gain calculation with non-linear mapping and smoothing 2025-08-19 19:50:13 +02:00
DevTekVE c669473f88 Refine torque reduction parameters and update UI for angle error analysis 2025-08-19 19:35:34 +02:00
DevTekVE 8751435bf5 Improving tq redc gain and override behavior 2025-08-19 10:08:51 +02:00
DevTekVE 30ae210761 Merge branch 'master' into hkg-angle-steering-2025 2025-08-19 10:07:42 +02:00
Jason Wen 3eb693f58b Merge remote-tracking branch 'sunnypilot/sunnypilot/hkg-angle-steering-2025' into hkg-angle-steering-2025 2025-08-18 12:30:29 -04:00
DevTekVE 7b4a31c5ac bugfix 2025-08-17 16:07:45 +02:00
DevTekVE 1527c8cf88 bump opendbc 2025-08-17 15:31:31 +02:00
DevTekVE ab0a7ae666 no joystick on this branch, causing issues 2025-08-17 15:25:40 +02:00
DevTekVE 11ec2f1f21 Apply suggestions from code review 2025-08-17 15:10:17 +02:00
DevTekVE 2c6808d37e Merge branch 'master' into hkg-angle-steering-2025
# Conflicts:
#	opendbc_repo
2025-08-17 15:02:28 +02:00
Jason Wen 2e96382c49 notebook init 2025-08-17 00:34:01 -04:00
DevTekVE 0239e440ca Adjust replay 2025-08-15 10:32:07 +02:00
Jason Wen 76b972daff Hyundai angle steering: STEERING_ANGLE_2 available on all cars 2025-08-14 01:56:46 -04:00
Jason Wen bc3ef3e7dd Hyundai angle steering: hugging no more - use the true steering angle signal from MDPS 2025-08-13 23:08:32 -04:00
DevTekVE 9839291dd0 Merge remote-tracking branch 'origin/master' into hkg-angle-steering-2025 2025-08-13 20:13:42 +02:00
DevTekVE 17cba328d6 refactor: update torque tuning configuration for angle steering
- Adjusted torque tuning configuration to avoid reliance on torque controller for Hyundai angle steering.
- Simplified control logic by removing unnecessary checks for torque control type.
refactor: clean up code formatting and improve test structure for torque reduction gain
2025-08-13 20:03:51 +02:00
DevTekVE 86093765d8 Merge branch 'master' into hkg-angle-steering-2025
# Conflicts:
#	opendbc_repo
2025-08-13 07:55:46 +02:00
DevTekVE 05fa1c8ae8 Adjust default params and cleanup 2025-08-12 21:36:22 +02:00
DevTekVE e8a40d6b85 Merge branch 'master' into hkg-angle-steering-2025
# Conflicts:
#	opendbc_repo
#	system/manager/process_config.py
2025-08-10 14:24:10 +02:00
DevTekVE c265e0bb85 Merge remote-tracking branch 'origin/master' into hkg-angle-steering-2025
# Conflicts:
#	common/params_keys.h
#	opendbc_repo
#	system/manager/manager.py
2025-08-02 09:56:58 +02:00
DevTekVE c6b118788b Merge branch 'master-new' into hkg-angle-steering-2025 2025-07-31 18:25:08 +02:00
DevTekVE b004f6dbdc Merge branch 'master-new' into hkg-angle-steering-2025 2025-07-31 18:17:34 +02:00
DevTekVE cd4930b680 Bump opendbc 2025-07-26 10:56:49 +02:00
DevTekVE f861aca628 Refactor vehicle model initialization and adjust angle limits for baseline model 2025-07-26 08:12:06 +02:00
DevTekVE 1ff0d8e2ee please don't bother me anymore! 2025-07-26 08:09:51 +02:00
DevTekVE d76d70764e Update slip factor precision for Hyundai steering parameters
- Adjusted `slip_factor` in Hyundai CANFD safety modes for improved consistency and accuracy.
- Ensured proper representation of `slip_factor` output in test logs.
2025-07-25 20:17:19 +02:00
DevTekVE dc73ce0b71 save tools replay 2025-07-25 19:47:31 +02:00
DevTekVE 3f666748af bump opendbc 2025-07-25 14:16:02 +02:00
DevTekVE 8de8a8838c bumo opendbc 2025-07-25 14:08:53 +02:00
DevTekVE f563b7eb71 Refactor steering angle limit application for improved safety and model compliance 2025-07-25 12:12:42 +02:00
DevTekVE 3c18b83708 save temp 2025-07-25 09:56:30 +02:00
DevTekVE bd35f5904b Enhance steering angle rate limiting and safety enforcement logic
- Introduced explicit post-rate limiting using model-specific dynamics.
- Improved low-speed smoothing and precision of applied angles.
2025-07-25 08:12:41 +02:00
DevTekVE 474f2737f6 Refactor steering angle limit logic for conservative seleion
- Removed unused lateral accel/jerk logic for simplicity.
- Updated angle limit calculations to choose the smallest delta for safer control.
2025-07-24 21:48:24 +02:00
DevTekVE ea1ac4a212 Revert "Add configurable max lateral accel and jerk parameters for Hyundai vehicles"
This reverts commit b95f8c5929.

Revert "Add baseline safety model and improve steering angle limiting logic"

This reverts commit b53cbb2e18.

Revert "Disable lateral accel/jerk params and ensure float consistency in angle limits"

This reverts commit 165d7c7b36.
2025-07-24 10:02:48 +02:00
DevTekVE 165d7c7b36 Disable lateral accel/jerk params and ensure float consistency in angle limits
- Commented out unused lateral accel/jerk parameters for clarity.
- Ensured `np.clip` always returns a float for precision.
2025-07-24 09:41:52 +02:00
DevTekVE b53cbb2e18 Add baseline safety model and improve steering angle limiting logic
- Introduced a baseline safety model (`GENESIS_GV80_2025`) for comparison.
- Enhanced steer angle limit calculation using both baseline and current limits for improved safety and precision.
2025-07-24 09:38:03 +02:00
DevTekVE b95f8c5929 Add configurable max lateral accel and jerk parameters for Hyundai vehicles
- Introduced user-configurable options for max lateral acceleration and jerk.
- Enables fine-tuning of vehicle handling for smoother control.
2025-07-24 08:39:10 +02:00
DevTekVE e564bb0b85 bumo openbc 2025-07-23 18:46:52 +02:00
DevTekVE e2ec8a7b13 Refine lateral control limits and simplify safety model handling
- Reduced max lateral acceleration and jerk by 20% for smoother handling.
- Removed unused `get_safety_CP` function, simplifying `VehicleModel` initialization.
2025-07-22 08:07:51 +02:00
DevTekVE 75c6f0f10e Test with gv80 as baseline for limits 2025-07-20 22:14:18 +02:00
DevTekVE af38044b42 Merge branch 'master-new' into hkg-angle-steering-2025 2025-07-20 10:14:46 +02:00
DevTekVE 684fa846d8 Merge branch 'master-new' into hkg-angle-steering-2025
# Conflicts:
#	.codespellignore
#	opendbc_repo
#	system/manager/manager.py
2025-07-19 21:51:46 +02:00
DevTekVE 416e722855 Update baseline model to IONIQ 5 PE for improved angle safety tuning
- Replaced conservative GENESIS_GV80_2025 model with IONIQ 5 PE parameters.
- Adjusted steering parameters (ratio, slip factor, wheelbase) for better lateral control performance.
2025-07-19 21:46:17 +02:00
DevTekVE 9fd4613bbb Add 2025 Kia EV6 support with updated radar and camera fingerprints 2025-07-09 09:47:31 +02:00
DevTekVE a0362e3c5f "Refined UI labels and tooltips for HKG tuning options to improve clarity and user understanding." 2025-06-29 16:48:53 +02:00
DevTekVE e32ef1cdc0 Fix incorrect torque sign usage in torque reduction calculation
Ensure `actuators.torque` uses its absolute value in the `calculate_angle_torque_reduction_gain` method to prevent sign-related issues during Hyundai steering angle control.
2025-06-29 14:33:30 +02:00
DevTekVE 20673ec8a6 Adjust warning font size in angle tuning settings panel. 2025-06-29 13:35:09 +02:00
DevTekVE 53fbdf7329 Rename "IdleTorque" to "ActiveTorque" for clarity.
The parameter name "HkgTuningAngleIdleTorqueReductionGain" was updated to "HkgTuningAngleActiveTorqueReductionGain" across multiple files for better clarity and alignment with its functionality. This change ensures consistency in naming conventions and improves code readability.
2025-06-29 13:23:56 +02:00
DevTekVE 6f72b74fac Add idle torque reduction for Hyundai lateral control
Introduced `ANGLE_IDLE_TORQUE_REDUCTION_GAIN` to manage torque when the vehicle is stationary, ensuring smoother handling and better lane centering. Updated parsing, parameters, and UI settings to support this new idle torque parameter. Adjusted torque calculation logic and smoothing factor behavior for enhanced control flexibility.
2025-06-29 13:22:18 +02:00
DevTekVE e83705a32e Merge branch 'master-new' into hkg-angle-steering-2025
# Conflicts:
#	opendbc_repo
2025-06-29 09:44:01 +02:00
DevTekVE 95d36b9ba2 Refactor and enhance HKG angle tuning logic.
Introduced a toggle for angle smoothing factor and renamed related parameters for clarity. Refactored backend settings to use new parameter names and expanded smoothing matrices for better tuning granularity. Updated UI elements to reflect these changes, emphasizing usability and consistency.
2025-06-28 21:25:44 +02:00
DevTekVE b9e74254bd Merge branch 'master-new' into hkg-angle-steering-2025 2025-06-27 10:46:22 +02:00
DevTekVE b4405b200d Merge remote-tracking branch 'origin/master-new' into hkg-angle-steering-2025 2025-06-25 09:27:21 +02:00
DevTekVE a8a3fdac54 Rename parameter in calculate_target_torque for clarity. 2025-06-23 22:53:45 +02:00
DevTekVE 4eddc622a7 Refactor torque calculations in Hyundai controller
Rename methods and variables for clarity in torque reduction and override calculations. Adjust logic to streamline handling of steering inputs and improve maintainability.
2025-06-23 22:51:40 +02:00
DevTekVE 4e42ada240 Refactor torque management in Hyundai controller for cleaner override and ramp logic
Extract torque ramping and override functionality into dedicated methods within `LkasTorqueManager` to improve maintainability and reduce redundancy. Simplify `update` logic by delegating state-specific operations to new methods.
2025-06-23 22:47:51 +02:00
DevTekVE 6266217655 Introduce LkasTorqueManager for LKAS torque handling in Hyundai controller
Encapsulate LKAS torque calculations, ramping, and override logic into the new `LkasTorqueManager` class to improve modularity and maintainability. Replace existing torque logic with calls to the manager.
2025-06-23 22:20:10 +02:00
DevTekVE ea09d32e98 Remove lateral acceleration logic from Hyundai steering controller 2025-06-23 21:55:17 +02:00
DevTekVE 15958c88d3 Refactor lateral acceleration scaling logic in Hyundai controller
Move scaling of `max_angle_delta` under high lateral acceleration to improve clarity and prevent redundant operations.
2025-06-23 20:22:03 +02:00
DevTekVE b80d7fb5ea Adding GV70 electrified 2026 2025-06-22 14:30:59 +02:00
DevTekVE 1c3d25c6ff Refine lateral acceleration handling in Hyundai steering logic
Enforce absolute check for `real_a_lat` against `MAX_LATERAL_ACCEL` to improve angle scaling under high lateral acceleration conditions.
2025-06-22 11:03:35 +02:00
rav4kumar c9f22b32c7 Revert "Incorporate lateral acceleration in Hyundai angle steering logic"
This reverts commit c0524985bb.
2025-06-21 11:40:58 -07:00
DevTekVE c0524985bb Incorporate lateral acceleration in Hyundai angle steering logic
Add handling for IMU lateral acceleration to refine steering angle limits in CAN FD configurations. Parse and utilize `IMU_LatAccelVal` signal for enhanced lateral control accuracy.
2025-06-21 17:34:13 +02:00
DevTekVE 83839c7ea7 Add angle steering support and refactor related logic for Hyundai CAN FD.
Introduced support for CAN FD angle steering, including updated parameters, signal parsing, and new tests. Refactored related steering logic for clarity, reducing unused code and enhancing maintainability.
2025-06-21 13:38:50 +02:00
DevTekVE c1a1d4b4c3 Update lint script to exclude .xml files in layouts directory
Added `layouts/.*\.xml` to `IGNORED_FILES` in `lint.sh` to prevent linting of layout XML files.
2025-06-20 10:50:45 +02:00
DevTekVE b5af7a905a Merge branch 'master-new' into hkg-angle-steering-2025 2025-06-18 20:13:45 +02:00
DevTekVE 96b1b2f55f Update steering request logic in Hyundai controller
Ensure steering request activation depends on lateral control being active. This adds clarity and aligns better with control logic requirements.
2025-06-17 18:51:54 +02:00
DevTekVE 9361ba5d70 Refactor Hyundai steering angle handling logic
Streamline steering angle calculations and fault avoidance logic by removing redundant comments and unused code. Simplified `round_angle` implementation for clarity and consistency.
2025-06-17 12:08:06 +02:00
DevTekVE 4e9014311e Refactor steeringPressed logic in Hyundai carstate.py.
Revised the determination of `steeringPressed` to account for both hands-on-wheel detection and torque overriding in CAN FD setups. Simplified fallback logic for non-CAN FD configurations for better code clarity and maintainability.
2025-06-12 00:31:07 +02:00
DevTekVE 232873fc70 Refine steering press detection logic.
Adjusted the sensitivity and threshold values for `HOD_Dir_Status` in steering press updates, improving accuracy in detecting steering input. This change aligns with updated parameter requirements for better responsiveness.
2025-06-12 00:14:05 +02:00
DevTekVE 0a61fca9c9 Fix steering press detection for Hyundai models.
Updated the condition to detect steering press by changing HOD_Dir_Status threshold from `> 2` to `>= 2`. This ensures the detection logic aligns correctly with expected behavior.
2025-06-12 00:06:52 +02:00
DevTekVE 480bdc34dc Add support for CANFD angle steering in Hyundai cars
Introduced handling for the `HOD_FD_01_100ms` message when the CANFD angle steering flag is enabled. This ensures proper message parsing and extends compatibility for specific Hyundai vehicle configurations.
2025-06-12 00:04:44 +02:00
DevTekVE 716b475a13 Update Hyundai controls for HOD status and steer limits
Adjusted the steering override frame window and incorporated new HOD_Dir_Status to improve hands-on detection. Added parsing for new signals in Hyundai CAN FD, enhancing steering override responsiveness and reliability.
2025-06-12 00:01:31 +02:00
DevTekVE b1ec5ec034 Adjust override angle cap in Hyundai car controller
Increased the minimum override angle cap from 0.01 to 0.1 and explicitly cast the maximum cap to a float. This change improves consistency and ensures proper handling of steering limits.
2025-06-11 23:20:19 +02:00
DevTekVE 470613c2b7 Adjust Hyundai steer override parameters for improved control.
Reduced the override frame window and updated the angle cap logic to use MAX_ANGLE_RATE. These changes aim to enhance steering responsiveness and safety by fine-tuning steer angle limits.
2025-06-11 23:07:49 +02:00
DevTekVE 336c5b4154 Remove smoothing_factor from Hyundai car controller logic
The `smoothing_factor` parameter and related logic have been removed to simplify the steering angle smoothing approach. All references and usage of this parameter have been eliminated, relying solely on speed-based dynamic interpolation. This change streamlines the code while maintaining functionality.
2025-06-11 23:04:32 +02:00
DevTekVE abdb9dc750 Adjust Hyundai steering override frame logic
Reduced `OVERRIDE_FRAME_WINDOW` and updated condition to properly respect override frame limits. This ensures smoother handling and more precise steering adjustments under certain driving scenarios.
2025-06-11 22:49:04 +02:00
DevTekVE ab98683973 Refactor steering override logic in Hyundai carcontroller
Replaced `recently_overridden` with `frames_since_override` for better granularity and added dynamic override angle limits using interpolation. These changes enhance steering control accuracy during user overrides and improve overall code readability.
2025-06-11 22:42:05 +02:00
DevTekVE 186c24dbe6 Refine Hyundai steering override handling logic
Adjusted logic for recently overridden steering to improve angle limits and torque smoothing. Removed unused or redundant code, optimizing the functionality and maintaining cleaner readability.
2025-06-11 21:49:18 +02:00
DevTekVE 9cdf6340a1 Refactor steering angle smoothing for clarity and reuse.
Extracted the steering angle smoothing logic into a standalone function `sp_smooth_angle` to enhance readability and reusability. Adjusted angle smoothing parameters and introduced a maximum vehicle speed threshold for applying smoothing. Minor updates improve maintainability and ensure consistent behavior across speed ranges.
2025-06-11 10:07:44 +02:00
DevTekVE 2855b1341c Adjust steering thresholds for Hyundai CAN FD vehicles
Updated `STEER_THRESHOLD` to 350 and `NO_LONGER_OVERRIDING_THRESHOLD` to 150 for better alignment with Hyundai CAN FD steering behavior. These changes ensure improved compatibility and more accurate steering response.
2025-06-10 09:53:08 +02:00
DevTekVE cf28f99976 Revert "Add twilsonco's LKAS torque calculator for improved lateral control"
This reverts commit b1770fb0e7aece0e160b1b083cb260edbbdc53dd.
2025-06-10 09:40:59 +02:00
DevTekVE a39d67dc47 Fix apply_angle_last reset logic in Hyundai carcontroller
Re-enables resetting `apply_angle_last` to `steering_angle` when steering is recently overridden. This ensures proper handling of steering angle limits during transitions.
2025-06-08 19:04:02 +02:00
DevTekVE 7e75257f12 Refine Hyundai steering control logic.
Simplified torque ramp-up logic by combining conditions and adjusted `STEER_THRESHOLD` for CANFD angle steering. These changes aim to enhance control precision and maintain consistency in overrides.
2025-06-08 19:02:49 +02:00
DevTekVE df38449553 Reduce override timeout for Hyundai carcontroller
Decrease the override timeout from 100 to 50 frames, ensuring quicker recognition of driver input override. This improves responsiveness and aligns with refined control behavior.
2025-06-08 18:22:44 +02:00
DevTekVE 1385ef3bc5 Fix steering control behavior during user override
Removed restrictive rate limiting during recent user overrides to improve steering response. Adjusted logic to ensure correct handling of steering angle when lateral control is inactive or overridden.
2025-06-08 18:01:47 +02:00
DevTekVE 7c23c11c51 Refine steering logic with override detection.
Adjust steering behavior to account for recent user overrides, improving safety and control. Introduced a "recently_overridden" check to limit angle rates and torque adjustments when user intervention is detected.
2025-06-08 17:52:16 +02:00
DevTekVE aeff2e12ec Refine steering logic with user override handling.
Added logic to use the current steering angle when the steering wheel is pressed, ensuring smoother transitions during user overrides. Updated function parameters and implementation to reflect this enhancement.
2025-06-08 17:38:34 +02:00
DevTekVE 7274899671 Refactor Hyundai override logic for steering thresholds
Removed redundant `recently_overridden` logic and introduced a more robust approach for tracking user steering overrides. Added `NO_LONGER_OVERRIDING_THRESHOLD` and updated conditions to improve steer override handling. Adjustments ensure smoother torque transitions and more accurate steering state detection.
2025-06-08 17:00:44 +02:00
DevTekVE 6c00fd608f pass tests? 2025-06-08 12:28:11 +02:00
DevTekVE df6a034c11 Bump opendbc 2025-06-08 12:26:09 +02:00
DevTekVE acb109c290 adding plotjuggler stuff 2025-06-08 10:02:44 +02:00
DevTekVE b227b00249 Update torque clamping to use parameterized min torque
Replaced hardcoded `angle_min_active_torque` with `ANGLE_MIN_TORQUE` from params for better configurability and consistency. This ensures the torque clamping logic aligns with defined parameters.
2025-06-07 19:43:01 +02:00
DevTekVE 3ec9d6c18a Merge branch 'master-new' into hkg-angle-steering-2025
# Conflicts:
#	common/params_keys.h
2025-06-07 15:04:16 +02:00
DevTekVE 86db8b95f0 Refactor torque calculation and deactivate live tuning.
Updated torque calculation logic with a new optional parameter for minimum active torque, streamlining control behavior. Deactivated and cleaned up references to HkgAngleLiveTuning, simplifying configuration and reducing runtime complexities. Updated relevant UI and parameter descriptions for clarity.
2025-06-07 11:55:57 +02:00
DevTekVE 4cfff8a35f Merge branch 'master-new' into hkg-angle-steering-2025 2025-06-06 23:08:37 +02:00
DevTekVE 962fedf48c Merge branch 'master-new' into hkg-angle-steering-2025
# Conflicts:
#	opendbc/car/tests/routes.py
2025-06-06 20:49:06 +02:00
DevTekVE 04494414d1 Merge branch 'master-new' into hkg-angle-steering-2025
# Conflicts:
#	opendbc_repo
2025-06-05 09:18:53 +02:00
DevTekVE 0b83576e9b Adjust torque ramping logic and update steering thresholds
Increase the override window and refine torque ramp-up behavior to avoid conflicts during recent overrides. Updated steering driver allowance and threshold values for CANFD angle steering to improve compatibility and performance.
2025-06-02 09:49:31 +02:00
DevTekVE ce4ef0f817 Refine steering override logic in Hyundai car controller
Added logic to track recent steering overrides and adjust LKAS torque behavior accordingly. This ensures smoother transitions when the steering is overridden and reduces potential conflicts with driver input. Updated CANFD-specific steering thresholds for enhanced compatibility.
2025-06-02 09:12:03 +02:00
DevTekVE f0b15c1c56 Adding twil's torque calculation 2025-06-01 19:04:34 +02:00
DevTekVE f898e9fdfe Merge branch 'master-new' into hkg-angle-steering-2025
# Conflicts:
#	opendbc_repo
2025-06-01 09:46:45 +02:00
DevTekVE 8ee7804b0e Bump opendbc (no tesla controls, no twil yet) 2025-05-29 16:31:42 +02:00
DevTekVE 923228194e bump opendbc to prior tesla changes until i can pass safety validations 2025-05-28 12:44:44 +02:00
DevTekVE 8837b2e3f6 Merge branch 'master-new' into hkg-angle-steering-2025
# Conflicts:
#	opendbc_repo
2025-05-28 12:36:02 +02:00
DevTekVE f48c9dc1c2 bump opendbc 2025-05-25 17:34:24 +02:00
DevTekVE 74aa07a8cd Ingore something i dont control thx 2025-05-25 17:31:37 +02:00
DevTekVE 5236e4860f Make lint happy, maybe 2025-05-25 17:31:37 +02:00
DevTekVE 3d174da1c3 adding some of my tests and validaitons 2025-05-25 17:31:25 +02:00
DevTekVE 8faa40f3a3 clean 2025-05-25 17:31:25 +02:00
DevTekVE 3e03275f28 Add PlotJuggler layout for analyzing torque and angle data
This new layout visualizes actuator data, CAN steering messages, and car state variables. It provides multiple time-series plots to aid in debugging and analysis. Plugin configurations are also included for extended functionality.
2025-05-25 17:31:22 +02:00
DevTekVE 7595cf8a25 Refine Hyundai angle and torque control logic.
Simplified control flag handling for angle steering, adjusted torque calculations for smoother ramp rates, and updated tuning parameters for the Hyundai Ioniq 5 PE. Minor adjustment to return value handling in lateral control functions.
2025-05-25 17:31:22 +02:00
DevTekVE 2675d43adb bump opendbc
Remove duplicate STEER_ANGLE_SATURATION_THRESHOLD import

Cleaned up an unnecessary duplicate import of STEER_ANGLE_SATURATION_THRESHOLD from latcontrol_angle_torque. This simplifies the module imports and prevents potential redundancy or confusion.

Refactor lateral control to combine torque and angle logic

Merged functionalities of LatControlTorque and LatControlAngle into a single LatControlAngleTorque class. Refactored code to utilize methods from both parent classes, reducing duplication and improving maintainability.

Add angle-torque hybrid lateral control for Hyundai CAN FD

Introduces `LatControlAngleTorque` to enable hybrid angle and torque-based steering for specific Hyundai models. Updates related logic in carcontroller, interface, and controlsd to accommodate this new lateral control method. Adjusts torque parameters for enhanced control in supported models.
2025-05-25 17:31:21 +02:00
DevTekVE d9f4ce82e6 clean 2025-05-25 17:31:21 +02:00
DevTekVE 648a1845d8 cleanup the mess 2025-05-25 17:31:21 +02:00
DevTekVE a87eff6d1c Add HKG Angle Live Tuning parameter and update related handling 2025-05-25 17:31:21 +02:00
DevTekVE dd6ad37e23 Absolutely zero clue on this, I did it with AI and it's for me to play. Don't take this notebook seriously please 2025-05-25 17:31:21 +02:00
DevTekVE c5e778b939 How annoying the linter on a comment lol 2025-05-25 17:31:21 +02:00
DevTekVE a9ab81a77a useless but should keep linter happy 2025-05-25 17:31:21 +02:00
DevTekVE 2d40e1d8e5 Refactor torque parameter handling in Hyundai carcontroller
Replaced direct access to `params` with instance variables for torque parameters to improve code clarity and maintainability. Updated smoothing factor description in angle tuning settings to include speed-related behavior. This enhances readability and prepares for further tuning adjustments.
2025-05-25 17:31:20 +02:00
DevTekVE 7c8f367a5d Fix data type for HkgTuningOverridingCycles value
Updated the value of HkgTuningOverridingCycles to a string for consistency with other parameters in the tuning configuration. This ensures proper handling and avoids potential issues with type mismatches.

Add overriding cycles parameter for torque adjustment

Introduced "HkgTuningOverridingCycles" for configurable user override torque ramp-down cycles. Updated relevant logic in torque control and UI settings to handle the new parameter. This improves flexibility in adjusting steering torque override behavior.
2025-05-25 17:31:20 +02:00
DevTekVE ff4cf558aa Add HKG angle tuning settings with min/max torque parameters
Introduce separate angle tuning controls for HKG vehicles, including smoothing factor, min torque, and max torque parameters. Refactor developer panel to integrate the new settings into a dedicated UI panel, enhancing modularity and customization capabilities.
2025-05-25 17:31:20 +02:00
DevTekVE 0e151e51bc Update HKG Angle Smoothing Factor description in Developer Panel
Enhanced the description to clarify its effect on steering behavior. Included details on how the smoothing factor impacts steering smoothness using EMA, aiding user understanding.
2025-05-25 17:31:20 +02:00
DevTekVE 60cc0031b0 Revert "Revert "Revert the EMA calculation on the curvature to test another approach""
This reverts commit 58fcda8c
2025-05-25 17:31:20 +02:00
DevTekVE d24cac0998 Refactor steering angle logic for smoother control adjustments
Refactored the calculation and application of the steering angle to improve code clarity and ensure smoother transitions. Removed unused parameter update logic in `latcontrol_angle.py` and enhanced handling of driver overrides in `carcontroller.py`.
2025-05-25 17:31:20 +02:00
DevTekVE 45d110830c Fix typo in parameter access method.
Replaced `self._params` with `self.params` to correctly access the parameter `HkgTuningAngleSmoothingFactor`. This ensures the smoothing factor is updated as intended during the control loop.
2025-05-25 17:31:20 +02:00
DevTekVE ea3a9ae911 Improve angle smoothing by integrating dynamic parameter tuning
Introduced a dynamic smoothing factor using the `HkgTuningAngleSmoothingFactor` parameter. This allows more granular control over curvature smoothing based on customizable user input, enhancing driving smoothness. Added necessary logic to process and apply this parameter efficiently.
2025-05-25 17:31:19 +02:00
DevTekVE 6bff8c0e7c Revert "Revert the EMA calculation on the curvature to test another approach"
This reverts commit bd471b3498.
2025-05-25 17:31:19 +02:00
DevTekVE bc6b8802b8 Add HKG angle smoothing factor for steering adjustments
Introduced a new parameter, `HkgTuningAngleSmoothingFactor`, to apply exponential moving average (EMA) smoothing to steering angle changes, reducing sudden adjustments. Added associated UI controls, parameter persistence, and integration into Hyundai carcontroller logic for improved steering stability.
2025-05-25 17:31:19 +02:00
DevTekVE 252ef572d3 Revert the EMA calculation on the curvature to test another approach 2025-05-25 17:31:19 +02:00
DevTekVE 414d397e3f Handle missing pygame import gracefully
Wrap the pygame import in a try-except block to catch ImportError. This prevents the script from crashing and provides a clear message prompting the user to install pygame if it's missing.

Remove "inputs" package and update "pygame" dependency

The "inputs" package has been removed from the lockfile and dependency list, while "pygame" is now included universally without the "dev" extra marker. This change simplifies dependencies and ensures consistency across environments.

Update dependencies: replace 'inputs' with 'pygame'

Replaced the 'inputs' library with 'pygame' for joystickd dependencies in `pyproject.toml`. Additionally, removed a redundant 'pygame' entry from the general dependencies.

Ugly, I know, but soundd is unhappy with joystick

Allowing lat with mads

Invert steering input for joystick control

The steering axis input is now multiplied by -1 to reverse its direction. This ensures correct handling of the left stick's horizontal input, aligning behavior with expected control dynamics.

Refactor joystick control to use pygame for broader support

Replaced the `inputs` library with `pygame` for joystick handling, providing improved compatibility with Xbox and PlayStation controllers. Added initialization, adaptive mappings, deadzone handling, and enhanced event processing for robust joystick operation. Updated README with dependencies and usage information for Xbox controllers.
2025-05-25 17:31:19 +02:00
DevTekVE 11b7b3789d Adjust speed thresholds in filter_speed_matrox.
Updated the `filter_speed_matrox` values to improve curvature filtering behavior at different speeds. This change ensures better handling and stability across a wider range of driving conditions.
2025-05-25 17:31:19 +02:00
DevTekVE 871ac53717 Optimize curvature filtering by adding speed-dependent logic.
Introduced speed-based dynamic alpha adjustment using interpolation for smoother curvature filtering. This improves steering angle calculations by adapting filter sensitivity to vehicle speed, enhancing control performance.
2025-05-25 17:31:19 +02:00
DevTekVE 64ea66b6e6 chsnge alpha to nicer value 2025-05-25 17:31:19 +02:00
DevTekVE 6d7c6759b3 Adjust curvature handling and filtering parameters
Updated curvature breakpoints and torque scaling for improved control in sharp turns. Increased filter alpha for faster curvature response while maintaining system stability.
2025-05-25 17:31:18 +02:00
DevTekVE 4cea013570 Adjust curvature handling and filtering parameters
Updated curvature breakpoints in Hyundai carcontroller to improve torque scaling for curved driving. Slightly refined the filter coefficient in lateral control for smoother curvature filtering and more accurate steering adjustments.
2025-05-25 17:31:18 +02:00
DevTekVE eb375c0587 Refactor curvature-based steering angle and torque logic.
Introduced dynamic torque scaling based on curvature for smoother and more adaptive steering control. Replaced raw curvature inputs with filtered curvature for enhanced stability and reduced noise in steering angle calculations. Removed unused speed scaling logic to simplify the lateral control flow.
2025-05-25 17:31:18 +02:00
DevTekVE 7fd8a5a4bd Reapply "Significant improvement on the jerkiness"
This reverts commit 85ce84e7b7.
2025-05-25 17:31:18 +02:00
DevTekVE b3c90216bb Revert "Significant improvement on the jerkiness"
This reverts commit ea1af879ba2905b076ccfe65993a9db701d689dd.

Revert "More improvement but still not quite"

This reverts commit ad95493c5c61b2ace7c459d2ebc151ddaa80040f.

Revert "Adjust low-speed scaling for lateral control angle"

This reverts commit 6f789ac1ebb66b0239b4028303573c2d7d386b39.

Revert "Refactor speed-based steering scaling logic."

This reverts commit 1d40735ab8db8d470ff3b287a6b42847beffff7d.
2025-05-25 17:31:18 +02:00
DevTekVE 10f345f956 Refactor speed-based steering scaling logic.
Updated the steering angle computation to use a clearer and more descriptive speed-scaling configuration. Replaced low-speed-specific logic with a generalized approach based on speed breakpoints and corresponding influence factors. This improves maintainability and ensures smoother steering adjustments at varying speeds.
2025-05-25 17:31:18 +02:00
DevTekVE 956d2c36d0 Adjust low-speed scaling for lateral control angle
Refined the low-speed scaling parameters by modifying speed breakpoints and factors. This improves handling at lower speeds for smoother and more predictable behavior.
2025-05-25 17:31:18 +02:00
DevTekVE 55e688b6f2 More improvement but still not quite 2025-05-25 17:31:17 +02:00
DevTekVE f017954027 Significant improvement on the jerkiness 2025-05-25 17:31:17 +02:00
DevTekVE 7e992d11b1 bump panda and opendbc 2025-05-25 17:31:15 +02:00
510 changed files with 26133 additions and 59321 deletions
-19
View File
@@ -1,19 +0,0 @@
---
Checks: '
bugprone-*,
-bugprone-integer-division,
-bugprone-narrowing-conversions,
performance-*,
clang-analyzer-*,
misc-*,
-misc-unused-parameters,
modernize-*,
-modernize-avoid-c-arrays,
-modernize-deprecated-headers,
-modernize-use-auto,
-modernize-use-using,
-modernize-use-nullptr,
-modernize-use-trailing-return-type,
'
CheckOptions:
...
+1
View File
@@ -2,5 +2,6 @@ Wen
REGIST
PullRequest
cancelled
indeces
FOF
NoO
-21
View File
@@ -13,27 +13,6 @@
*.o-*
*.os
*.os-*
*.so
*.a
venv/
.venv/
notebooks
phone
massivemap
neos
installer
chffr/app2
chffr/backend/env
selfdrive/nav
selfdrive/baseui
selfdrive/test/simulator2
**/cache_data
xx/plus
xx/community
xx/projects
!xx/projects/eon_testing_master
!xx/projects/map3d
xx/ops
xx/junk
+3 -1
View File
@@ -7,10 +7,12 @@
*.png filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text
*.ttf filter=lfs diff=lfs merge=lfs -text
*.otf filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
selfdrive/car/tests/test_models_segs.txt filter=lfs diff=lfs merge=lfs -text
system/hardware/tici/updater filter=lfs diff=lfs merge=lfs -text
system/hardware/tici/updater_weston filter=lfs diff=lfs merge=lfs -text
system/hardware/tici/updater_magic filter=lfs diff=lfs merge=lfs -text
third_party/**/*.a filter=lfs diff=lfs merge=lfs -text
third_party/**/*.so filter=lfs diff=lfs merge=lfs -text
third_party/**/*.so.* filter=lfs diff=lfs merge=lfs -text
+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/assets/**,selfdrive/ui/**,system/ui/**}'
tools:
- changed-files:
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
- uses: ./.github/workflows/setup-with-retry
- name: Push badges
run: |
${{ env.RUN }} "scons -j$(nproc) && python3 selfdrive/ui/translations/create_badges.py"
${{ env.RUN }} "python3 selfdrive/ui/translations/create_badges.py"
rm .gitattributes
+2 -2
View File
@@ -11,7 +11,7 @@ concurrency:
cancel-in-progress: true
jobs:
selfdrive_tests:
uses: sunnypilot/sunnypilot/.github/workflows/selfdrive_tests.yaml@master
tests:
uses: sunnypilot/sunnypilot/.github/workflows/tests.yaml@master
with:
run_number: ${{ inputs.run_number }}
@@ -1,4 +1,4 @@
name: "ui preview"
name: "raylib ui preview"
on:
push:
branches:
@@ -8,14 +8,16 @@ on:
branches:
- 'master'
paths:
- 'selfdrive/assets/**'
- 'selfdrive/ui/**'
- 'system/ui/**'
workflow_dispatch:
env:
UI_JOB_NAME: "Create UI Report"
UI_JOB_NAME: "Create raylib UI Report"
REPORT_NAME: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
SHA: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.sha || github.event.pull_request.head.sha }}
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}"
BRANCH_NAME: "openpilot/pr-${{ github.event.number }}-raylib-ui"
jobs:
preview:
@@ -52,7 +54,7 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }}
run_id: ${{ steps.get_run_id.outputs.run_id }}
search_artifacts: true
name: report-1-${{ env.REPORT_NAME }}
name: raylib-report-1-${{ env.REPORT_NAME }}
path: ${{ github.workspace }}/pr_ui
- name: Getting master ui
@@ -60,23 +62,23 @@ jobs:
with:
repository: sunnypilot/ci-artifacts
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
path: ${{ github.workspace }}/master_ui
ref: openpilot_master_ui
path: ${{ github.workspace }}/master_ui_raylib
ref: openpilot_master_ui_raylib
- name: Saving new master ui
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
working-directory: ${{ github.workspace }}/master_ui
working-directory: ${{ github.workspace }}/master_ui_raylib
run: |
git checkout --orphan=new_master_ui
git checkout --orphan=new_master_ui_raylib
git rm -rf *
git branch -D openpilot_master_ui
git branch -m openpilot_master_ui
git branch -D openpilot_master_ui_raylib
git branch -m openpilot_master_ui_raylib
git config user.name "GitHub Actions Bot"
git config user.email "<>"
mv ${{ github.workspace }}/pr_ui/*.png .
git add .
git commit -m "screenshots for commit ${{ env.SHA }}"
git push origin openpilot_master_ui --force
git commit -m "raylib screenshots for commit ${{ env.SHA }}"
git push origin openpilot_master_ui_raylib --force
- name: Finding diff
if: github.event_name == 'pull_request_target'
@@ -94,7 +96,7 @@ jobs:
for ((i=0; i<${#A[*]}; i=i+1));
do
# Check if the master file exists
if [ ! -f "${{ github.workspace }}/master_ui/${A[$i]}.png" ]; then
if [ ! -f "${{ github.workspace }}/master_ui_raylib/${A[$i]}.png" ]; then
# This is a new file in PR UI that doesn't exist in master
DIFF="${DIFF}<details open>"
DIFF="${DIFF}<summary>${A[$i]} : \$\${\\color{cyan}\\text{NEW}}\$\$</summary>"
@@ -106,12 +108,12 @@ jobs:
DIFF="${DIFF}</table>"
DIFF="${DIFF}</details>"
elif ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png; then
elif ! compare -fuzz 2% -highlight-color DeepSkyBlue1 -lowlight-color Black -compose Src ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png; then
convert ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png -transparent black mask.png
composite mask.png ${{ github.workspace }}/master_ui/${A[$i]}.png composite_diff.png
convert -delay 100 ${{ github.workspace }}/master_ui/${A[$i]}.png composite_diff.png -loop 0 ${{ github.workspace }}/pr_ui/${A[$i]}_diff.gif
composite mask.png ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png
convert -delay 100 ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png composite_diff.png -loop 0 ${{ github.workspace }}/pr_ui/${A[$i]}_diff.gif
mv ${{ github.workspace }}/master_ui/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_master_ref.png
mv ${{ github.workspace }}/master_ui_raylib/${A[$i]}.png ${{ github.workspace }}/pr_ui/${A[$i]}_master_ref.png
DIFF="${DIFF}<details open>"
DIFF="${DIFF}<summary>${A[$i]} : \$\${\\color{red}\\text{DIFFERENT}}\$\$</summary>"
@@ -149,7 +151,7 @@ jobs:
- name: Saving proposed ui
if: github.event_name == 'pull_request_target'
working-directory: ${{ github.workspace }}/master_ui
working-directory: ${{ github.workspace }}/master_ui_raylib
run: |
git config user.name "GitHub Actions Bot"
git config user.email "<>"
@@ -157,7 +159,7 @@ jobs:
git rm -rf *
mv ${{ github.workspace }}/pr_ui/* .
git add .
git commit -m "screenshots for PR #${{ github.event.number }}"
git commit -m "raylib screenshots for PR #${{ github.event.number }}"
git push origin ${{ env.BRANCH_NAME }} --force
- name: Comment Screenshots on PR
@@ -165,9 +167,9 @@ jobs:
uses: thollander/actions-comment-pull-request@v2
with:
message: |
<!-- _(run_id_screenshots **${{ github.run_id }}**)_ -->
## UI Preview
<!-- _(run_id_screenshots_raylib **${{ github.run_id }}**)_ -->
## raylib UI Preview
${{ steps.find_diff.outputs.DIFF }}
comment_tag: run_id_screenshots
comment_tag: run_id_screenshots_raylib
pr_number: ${{ github.event.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -1,4 +1,4 @@
name: selfdrive
name: tests
on:
push:
@@ -14,7 +14,7 @@ on:
type: string
concurrency:
group: selfdrive-tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
group: tests-ci-run-${{ inputs.run_number }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.run_id || github.head_ref || github.ref }}-${{ github.workflow }}-${{ github.event_name }}
cancel-in-progress: true
env:
@@ -195,8 +195,6 @@ jobs:
# Pre-compile Python bytecode so each pytest worker doesn't need to
$PYTEST --collect-only -m 'not slow' -qq && \
MAX_EXAMPLES=1 $PYTEST -m 'not slow' && \
./selfdrive/ui/tests/create_test_translations.sh && \
QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \
chmod -R 777 /tmp/comma_download_cache"
process_replay:
@@ -257,7 +255,7 @@ jobs:
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|| fromJSON('["ubuntu-24.04"]') }}
if: (github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
if: false # FIXME: Started to timeout recently
steps:
- uses: actions/checkout@v4
with:
@@ -274,38 +272,28 @@ jobs:
source selfdrive/test/setup_vsound.sh && \
CI=1 pytest -s tools/sim/tests/test_metadrive_bridge.py"
create_ui_report:
# This job name needs to be the same as UI_JOB_NAME in ui_preview.yaml
name: Create UI Report
create_raylib_ui_report:
name: Create raylib UI Report
runs-on: ${{
(github.repository == 'commaai/openpilot') &&
((github.event_name != 'pull_request') ||
(github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))
&& fromJSON('["namespace-profile-amd64-8x16", "namespace-experiments:docker.builds.local-cache=separate"]')
|| fromJSON('["ubuntu-24.04"]') }}
if: false # FIXME: FrameReader is broken on CI runners
steps:
- uses: actions/checkout@v4
with:
submodules: true
- uses: ./.github/workflows/setup-with-retry
- name: caching frames
id: frames-cache
uses: actions/cache@v4
with:
path: .ci_cache/comma_download_cache
key: ui_screenshots_test_${{ hashFiles('selfdrive/ui/tests/test_ui/run.py') }}
- name: Build openpilot
run: ${{ env.RUN }} "scons -j$(nproc)"
- name: Create Test Report
timeout-minutes: ${{ ((steps.frames-cache.outputs.cache-hit == 'true') && 2 || 4) }}
- name: Create raylib UI Report
run: >
${{ env.RUN }} "PYTHONWARNINGS=ignore &&
source selfdrive/test/setup_xvfb.sh &&
CACHE_ROOT=/tmp/comma_download_cache python3 selfdrive/ui/tests/test_ui/run.py &&
chmod -R 777 /tmp/comma_download_cache"
- name: Upload Test Report
${{ env.RUN }} "PYTHONWARNINGS=ignore &&
source selfdrive/test/setup_xvfb.sh &&
python3 selfdrive/ui/tests/test_ui/raylib_screenshots.py"
- name: Upload Raylib UI Report
uses: actions/upload-artifact@v4
with:
name: report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
path: selfdrive/ui/tests/test_ui/report_1/screenshots
name: raylib-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
path: selfdrive/ui/tests/test_ui/raylib_report/screenshots
+3 -9
View File
@@ -10,7 +10,6 @@ venv/
.overlay_init
.overlay_consistent
.sconsign.dblite
model2.png
a.out
.hypothesis
.cache/
@@ -37,29 +36,23 @@ a.out
*.class
*.pyxbldc
*.vcd
*.qm
*.mo
*_pyx.cpp
*.stats
config.json
clcache
compile_commands.json
compare_runtime*.html
persist
selfdrive/pandad/pandad
cereal/services.h
cereal/gen
cereal/messaging/bridge
selfdrive/mapd/default_speeds_by_region.json
selfdrive/ui/translations/tmp
selfdrive/test/longitudinal_maneuvers/out
selfdrive/car/tests/cars_dump
system/camerad/camerad
system/camerad/test/ae_gray_test
notebooks
hyperthneed
provisioning
.coverage*
coverage.xml
htmlcov
@@ -76,6 +69,7 @@ sunnypilot/modeld*/thneed/compile
sunnypilot/modeld*/models/*.thneed
sunnypilot/modeld*/models/*.pkl
# openpilot log files
*.bz2
*.zst
+1 -1
View File
@@ -15,7 +15,7 @@
url = https://github.com/commaai/teleoprtc
[submodule "tinygrad"]
path = tinygrad_repo
url = https://github.com/tinygrad/tinygrad.git
url = https://github.com/commaai/tinygrad.git
[submodule "sunnypilot/neural_network_data"]
path = sunnypilot/neural_network_data
url = https://github.com/sunnypilot/neural-network-data.git
+7
View File
@@ -21,5 +21,12 @@
</clean>
</configuration>
</target>
<target id="f2590b2b-9b93-49f9-8510-da3f3724a2ae" name="replay" defaultType="TOOL">
<configuration id="d475264f-6f4c-4092-9b4e-6773309f38b7" name="replay" toolchainName="Default">
<build type="TOOL">
<tool actionId="Tool_External Tools_uv build tools replay" />
</build>
</configuration>
</target>
</component>
</project>
+7
View File
@@ -20,4 +20,11 @@
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$" />
</exec>
</tool>
<tool name="uv build tools replay" showInMainMenu="false" showInEditor="false" showInProject="false" showInSearchPopup="false" disabled="false" useConsole="true" showConsoleOnStdOut="false" showConsoleOnStdErr="false" synchronizeAfterRun="true">
<exec>
<option name="COMMAND" value="bash" />
<option name="PARAMETERS" value="-c &quot;source .venv/bin/activate &amp;&amp; scons -u -j$(nproc) tools/replay/&quot;" />
<option name="WORKING_DIRECTORY" value="$ProjectFileDir$" />
</exec>
</tool>
</toolSet>
+1 -1
View File
@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Build Debug" type="CLionExternalRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$ProjectFileDir$/selfdrive/ui" PASS_PARENT_ENVS_2="true" PROJECT_NAME="sunnypilot" TARGET_NAME="uv Scons Build Debug" CONFIG_NAME="uv Scons Build Debug" RUN_PATH="ui">
<configuration default="false" name="Build Debug" type="CLionExternalRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$ProjectFileDir$/selfdrive/ui" PASS_PARENT_ENVS_2="true" PROJECT_NAME="openpilot-special" TARGET_NAME="uv Scons Build Debug" CONFIG_NAME="uv Scons Build Debug" RUN_PATH="ui">
<envs>
<env name="QT_DBL_CLICK_DIST" value="150" />
</envs>
+27
View File
@@ -0,0 +1,27 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Debug Route Controls" type="PythonConfigurationType" factoryName="Python">
<module name="openpilot-special" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
<env name="FINGERPRINT" value="KIA_EV9" />
<env name="SKIP_FW_QUERY" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/selfdrive/car" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/selfdrive/car/card.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="true" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
</component>
+7
View File
@@ -0,0 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Replay for controls + ui" type="Multirun" separateTabs="false" reuseTabsWithFailures="false" startOneByOne="true" markFailedProcess="true" hideSuccessProcess="false" delayTime="0.0">
<runConfiguration name="replay for controls" type="Native Application" />
<runConfiguration name="Build Debug" type="Custom Build Application" />
<method v="2" />
</configuration>
</component>
+7
View File
@@ -0,0 +1,7 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="replay for controls" type="CLionNativeAppRunConfigurationType" focusToolWindowBeforeRun="true" PROGRAM_PARAMS="&quot;$Prompt$&quot; --block &quot;sendcan,carState,carParams,carOutput,liveTracks,carParamsSP,carStateSP,bookmarkButton&quot;" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="true" WORKING_DIR="file://$ProjectFileDir$/tools/replay" PASS_PARENT_ENVS_2="true" PROJECT_NAME="openpilot-special" TARGET_NAME="replay" CONFIG_NAME="replay" version="1" RUN_PATH="replay">
<method v="2">
<option name="CLION.COMPOUND.BUILD" enabled="true" />
</method>
</configuration>
</component>
+3 -1
View File
@@ -9,4 +9,6 @@ WORKDIR ${OPENPILOT_PATH}
COPY . ${OPENPILOT_PATH}/
RUN scons --cache-readonly -j$(nproc)
ENV UV_BIN="/home/batman/.local/bin/"
ENV PATH="$UV_BIN:$PATH"
RUN UV_PROJECT_ENVIRONMENT=$VIRTUAL_ENV uv run scons --cache-readonly -j$(nproc)
Vendored
+3 -3
View File
@@ -167,7 +167,7 @@ node {
env.GIT_COMMIT = checkout(scm).GIT_COMMIT
def excludeBranches = ['__nightly', 'devel', 'devel-staging', 'release3', 'release3-staging',
'release-tici', 'testing-closet*', 'hotfix-*']
'release-tici', 'release-tizi', 'release-tizi-staging', 'testing-closet*', 'hotfix-*']
def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*')
if (env.BRANCH_NAME != 'master' && !env.BRANCH_NAME.contains('__jenkins_loop_')) {
@@ -178,8 +178,8 @@ node {
try {
if (env.BRANCH_NAME == 'devel-staging') {
deviceStage("build release3-staging", "tizi-needs-can", [], [
step("build release3-staging", "RELEASE_BRANCH=release3-staging $SOURCE_DIR/release/build_release.sh"),
deviceStage("build release-tizi-staging", "tizi-needs-can", [], [
step("build release-tizi-staging", "RELEASE_BRANCH=release-tizi-staging $SOURCE_DIR/release/build_release.sh"),
])
}
+8 -1
View File
@@ -1,13 +1,20 @@
Version 0.10.2 (2025-11-23)
========================
Version 0.10.1 (2025-09-08)
========================
* New driving model
* New driving model #36276
* World Model: removed global localization inputs
* World Model: 2x the number of parameters
* World Model: trained on 4x the number of segments
* VAE Compression Model: new architecture and training objective
* Driving Vision Model: trained on 4x the number of segments
* New Driver Monitoring model #36198
* Acura TLX 2021 support thanks to MVL!
* Honda City 2023 support thanks to vanillagorillaa and drFritz!
* Honda N-Box 2018 support thanks to miettal!
* Honda Odyssey 2021-25 support thanks to csouers and MVL!
* Honda Passport 2026 support thanks to vanillagorillaa and MVL!
Version 0.10.0 (2025-08-05)
========================
+103 -248
View File
@@ -3,176 +3,52 @@ import subprocess
import sys
import sysconfig
import platform
import shlex
import numpy as np
import SCons.Errors
SCons.Warnings.warningAsException(True)
# pending upstream fix - https://github.com/SCons/scons/issues/4461
#SetOption('warn', 'all')
TICI = os.path.isfile('/TICI')
AGNOS = TICI
Decider('MD5-timestamp')
SetOption('num_jobs', max(1, int(os.cpu_count()/2)))
AddOption('--kaitai',
action='store_true',
help='Regenerate kaitai struct parsers')
AddOption('--asan',
action='store_true',
help='turn on ASAN')
AddOption('--ubsan',
action='store_true',
help='turn on UBSan')
AddOption('--coverage',
action='store_true',
help='build with test coverage options')
AddOption('--clazy',
action='store_true',
help='build with clazy')
AddOption('--ccflags',
action='store',
type='string',
default='',
help='pass arbitrary flags over the command line')
AddOption('--external-sconscript',
action='store',
metavar='FILE',
dest='external_sconscript',
help='add an external SConscript to the build')
AddOption('--mutation',
action='store_true',
help='generate mutation-ready code')
AddOption('--kaitai', action='store_true', help='Regenerate kaitai struct parsers')
AddOption('--asan', action='store_true', help='turn on ASAN')
AddOption('--ubsan', action='store_true', help='turn on UBSan')
AddOption('--mutation', action='store_true', help='generate mutation-ready code')
AddOption('--ccflags', action='store', type='string', default='', help='pass arbitrary flags over the command line')
AddOption('--minimal',
action='store_false',
dest='extras',
default=os.path.exists(File('#.lfsconfig').abspath), # minimal by default on release branch (where there's no LFS)
default=os.path.exists(File('#.gitattributes').abspath), # minimal by default on release branch (where there's no LFS)
help='the minimum build to run openpilot. no tests, tools, etc.')
AddOption('--stock-ui',
action='store_true',
dest='stock_ui',
default=False,
help='Build stock openpilot UI instead of sunnypilot UI')
## Architecture name breakdown (arch)
## - larch64: linux tici aarch64
## - aarch64: linux pc aarch64
## - x86_64: linux pc x64
## - Darwin: mac x64 or arm64
real_arch = arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
# Detect platform
arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip()
if platform.system() == "Darwin":
arch = "Darwin"
brew_prefix = subprocess.check_output(['brew', '--prefix'], encoding='utf8').strip()
elif arch == "aarch64" and AGNOS:
elif arch == "aarch64" and os.path.isfile('/TICI'):
arch = "larch64"
assert arch in ["larch64", "aarch64", "x86_64", "Darwin"]
lenv = {
"PATH": os.environ['PATH'],
"PYTHONPATH": Dir("#").abspath + ':' + Dir(f"#third_party/acados").abspath,
"ACADOS_SOURCE_DIR": Dir("#third_party/acados").abspath,
"ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath,
"TERA_PATH": Dir("#").abspath + f"/third_party/acados/{arch}/t_renderer"
}
rpath = []
if arch == "larch64":
cpppath = [
"#third_party/opencl/include",
]
libpath = [
"/usr/local/lib",
"/system/vendor/lib64",
f"#third_party/acados/{arch}/lib",
]
libpath += [
"#third_party/snpe/larch64",
"#third_party/libyuv/larch64/lib",
"/usr/lib/aarch64-linux-gnu"
]
cflags = ["-DQCOM2", "-mcpu=cortex-a57"]
cxxflags = ["-DQCOM2", "-mcpu=cortex-a57"]
rpath += ["/usr/local/lib"]
else:
cflags = []
cxxflags = []
cpppath = []
rpath += []
# MacOS
if arch == "Darwin":
libpath = [
f"#third_party/libyuv/{arch}/lib",
f"#third_party/acados/{arch}/lib",
f"{brew_prefix}/lib",
f"{brew_prefix}/opt/openssl@3.0/lib",
"/System/Library/Frameworks/OpenGL.framework/Libraries",
]
cflags += ["-DGL_SILENCE_DEPRECATION"]
cxxflags += ["-DGL_SILENCE_DEPRECATION"]
cpppath += [
f"{brew_prefix}/include",
f"{brew_prefix}/opt/openssl@3.0/include",
]
# Linux
else:
libpath = [
f"#third_party/acados/{arch}/lib",
f"#third_party/libyuv/{arch}/lib",
"/usr/lib",
"/usr/local/lib",
]
if arch == "x86_64":
libpath += [
f"#third_party/snpe/{arch}"
]
rpath += [
Dir(f"#third_party/snpe/{arch}").abspath,
]
if GetOption('asan'):
ccflags = ["-fsanitize=address", "-fno-omit-frame-pointer"]
ldflags = ["-fsanitize=address"]
elif GetOption('ubsan'):
ccflags = ["-fsanitize=undefined"]
ldflags = ["-fsanitize=undefined"]
else:
ccflags = []
ldflags = []
# no --as-needed on mac linker
if arch != "Darwin":
ldflags += ["-Wl,--as-needed", "-Wl,--no-undefined"]
if not GetOption('stock_ui'):
cflags += ["-DSUNNYPILOT"]
cxxflags += ["-DSUNNYPILOT"]
ccflags_option = GetOption('ccflags')
if ccflags_option:
ccflags += ccflags_option.split(' ')
assert arch in [
"larch64", # linux tici arm64
"aarch64", # linux pc arm64
"x86_64", # linux pc x64
"Darwin", # macOS arm64 (x86 not supported)
]
env = Environment(
ENV=lenv,
ENV={
"PATH": os.environ['PATH'],
"PYTHONPATH": Dir("#").abspath + ':' + Dir(f"#third_party/acados").abspath,
"ACADOS_SOURCE_DIR": Dir("#third_party/acados").abspath,
"ACADOS_PYTHON_INTERFACE_PATH": Dir("#third_party/acados/acados_template").abspath,
"TERA_PATH": Dir("#").abspath + f"/third_party/acados/{arch}/t_renderer"
},
CC='clang',
CXX='clang++',
CCFLAGS=[
"-g",
"-fPIC",
@@ -185,37 +61,32 @@ env = Environment(
"-Wno-c99-designator",
"-Wno-reorder-init-list",
"-Wno-vla-cxx-extension",
] + cflags + ccflags,
CPPPATH=cpppath + [
],
CFLAGS=["-std=gnu11"],
CXXFLAGS=["-std=c++1z"],
CPPPATH=[
"#",
"#msgq",
"#third_party",
"#third_party/json11",
"#third_party/linux/include",
"#third_party/acados/include",
"#third_party/acados/include/blasfeo/include",
"#third_party/acados/include/hpipm/include",
"#third_party/catch2/include",
"#third_party/libyuv/include",
"#third_party/json11",
"#third_party/linux/include",
"#third_party/snpe/include",
"#third_party",
"#msgq",
],
CC='clang',
CXX='clang++',
LINKFLAGS=ldflags,
RPATH=rpath,
CFLAGS=["-std=gnu11"] + cflags,
CXXFLAGS=["-std=c++1z"] + cxxflags,
LIBPATH=libpath + [
LIBPATH=[
"#common",
"#msgq_repo",
"#third_party",
"#selfdrive/pandad",
"#common",
"#rednose/helpers",
f"#third_party/libyuv/{arch}/lib",
f"#third_party/acados/{arch}/lib",
],
RPATH=[],
CYTHONCFILESUFFIX=".cpp",
COMPILATIONDB_USE_ABSPATH=True,
REDNOSE_ROOT="#",
@@ -223,30 +94,72 @@ env = Environment(
toolpath=["#site_scons/site_tools", "#rednose_repo/site_scons/site_tools"],
)
if arch == "Darwin":
# RPATH is not supported on macOS, instead use the linker flags
darwin_rpath_link_flags = [f"-Wl,-rpath,{path}" for path in env["RPATH"]]
env["LINKFLAGS"] += darwin_rpath_link_flags
# Arch-specific flags and paths
if arch == "larch64":
env.Append(CPPPATH=["#third_party/opencl/include"])
env.Append(LIBPATH=[
"/usr/local/lib",
"/system/vendor/lib64",
"/usr/lib/aarch64-linux-gnu",
"#third_party/snpe/larch64",
])
arch_flags = ["-D__TICI__", "-mcpu=cortex-a57", "-DQCOM2"]
env.Append(CCFLAGS=arch_flags)
env.Append(CXXFLAGS=arch_flags)
elif arch == "Darwin":
env.Append(LIBPATH=[
f"{brew_prefix}/lib",
f"{brew_prefix}/opt/openssl@3.0/lib",
f"{brew_prefix}/opt/llvm/lib/c++",
"/System/Library/Frameworks/OpenGL.framework/Libraries",
])
env.Append(CCFLAGS=["-DGL_SILENCE_DEPRECATION"])
env.Append(CXXFLAGS=["-DGL_SILENCE_DEPRECATION"])
env.Append(CPPPATH=[
f"{brew_prefix}/include",
f"{brew_prefix}/opt/openssl@3.0/include",
])
else:
env.Append(LIBPATH=[
"/usr/lib",
"/usr/local/lib",
])
env.CompilationDatabase('compile_commands.json')
if arch == "x86_64":
env.Append(LIBPATH=[
f"#third_party/snpe/{arch}"
])
env.Append(RPATH=[
Dir(f"#third_party/snpe/{arch}").abspath,
])
# Setup cache dir
default_cache_dir = '/data/scons_cache' if AGNOS else '/tmp/scons_cache'
cache_dir = ARGUMENTS.get('cache_dir', default_cache_dir)
CacheDir(cache_dir)
Clean(["."], cache_dir)
# Sanitizers and extra CCFLAGS from CLI
if GetOption('asan'):
env.Append(CCFLAGS=["-fsanitize=address", "-fno-omit-frame-pointer"])
env.Append(LINKFLAGS=["-fsanitize=address"])
elif GetOption('ubsan'):
env.Append(CCFLAGS=["-fsanitize=undefined"])
env.Append(LINKFLAGS=["-fsanitize=undefined"])
_extra_cc = shlex.split(GetOption('ccflags') or '')
if _extra_cc:
env.Append(CCFLAGS=_extra_cc)
# no --as-needed on mac linker
if arch != "Darwin":
env.Append(LINKFLAGS=["-Wl,--as-needed", "-Wl,--no-undefined"])
# progress output
node_interval = 5
node_count = 0
def progress_function(node):
global node_count
node_count += node_interval
sys.stderr.write("progress: %d\n" % node_count)
if os.environ.get('SCONS_PROGRESS'):
Progress(progress_function, interval=node_interval)
# Cython build environment
# ********** Cython build environment **********
py_include = sysconfig.get_paths()['include']
envCython = env.Clone()
envCython["CPPPATH"] += [py_include, np.get_include()]
@@ -255,84 +168,27 @@ envCython["CCFLAGS"].remove("-Werror")
envCython["LIBS"] = []
if arch == "Darwin":
envCython["LINKFLAGS"] = ["-bundle", "-undefined", "dynamic_lookup"] + darwin_rpath_link_flags
envCython["LINKFLAGS"] = env["LINKFLAGS"] + ["-bundle", "-undefined", "dynamic_lookup"]
else:
envCython["LINKFLAGS"] = ["-pthread", "-shared"]
np_version = SCons.Script.Value(np.__version__)
Export('envCython', 'np_version')
# Qt build environment
qt_env = env.Clone()
qt_modules = ["Widgets", "Gui", "Core", "Network", "Concurrent", "DBus", "Xml"]
Export('env', 'arch')
qt_libs = []
if arch == "Darwin":
qt_env['QTDIR'] = f"{brew_prefix}/opt/qt@5"
qt_dirs = [
os.path.join(qt_env['QTDIR'], "include"),
]
qt_dirs += [f"{qt_env['QTDIR']}/include/Qt{m}" for m in qt_modules]
qt_env["LINKFLAGS"] += ["-F" + os.path.join(qt_env['QTDIR'], "lib")]
qt_env["FRAMEWORKS"] += [f"Qt{m}" for m in qt_modules] + ["OpenGL"]
qt_env.AppendENVPath('PATH', os.path.join(qt_env['QTDIR'], "bin"))
else:
qt_install_prefix = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_PREFIX'], encoding='utf8').strip()
qt_install_headers = subprocess.check_output(['qmake', '-query', 'QT_INSTALL_HEADERS'], encoding='utf8').strip()
# Setup cache dir
cache_dir = '/data/scons_cache' if arch == "larch64" else '/tmp/scons_cache'
CacheDir(cache_dir)
Clean(["."], cache_dir)
qt_env['QTDIR'] = qt_install_prefix
qt_dirs = [
f"{qt_install_headers}",
]
qt_gui_path = os.path.join(qt_install_headers, "QtGui")
qt_gui_dirs = [d for d in os.listdir(qt_gui_path) if os.path.isdir(os.path.join(qt_gui_path, d))]
qt_dirs += [f"{qt_install_headers}/QtGui/{qt_gui_dirs[0]}/QtGui", ] if qt_gui_dirs else []
qt_dirs += [f"{qt_install_headers}/Qt{m}" for m in qt_modules]
qt_libs = [f"Qt5{m}" for m in qt_modules]
if arch == "larch64":
qt_libs += ["GLESv2", "wayland-client"]
qt_env.PrependENVPath('PATH', Dir("#third_party/qt5/larch64/bin/").abspath)
elif arch != "Darwin":
qt_libs += ["GL"]
qt_env['QT3DIR'] = qt_env['QTDIR']
qt_env.Tool('qt3')
qt_env['CPPPATH'] += qt_dirs + ["#third_party/qrcode"]
qt_flags = [
"-D_REENTRANT",
"-DQT_NO_DEBUG",
"-DQT_WIDGETS_LIB",
"-DQT_GUI_LIB",
"-DQT_CORE_LIB",
"-DQT_MESSAGELOGCONTEXT",
]
qt_env['CXXFLAGS'] += qt_flags
qt_env['LIBPATH'] += ['#selfdrive/ui', ]
qt_env['LIBS'] = qt_libs
if GetOption("clazy"):
checks = [
"level0",
"level1",
"no-range-loop",
"no-non-pod-global-static",
]
qt_env['CXX'] = 'clazy'
qt_env['ENV']['CLAZY_IGNORE_DIRS'] = qt_dirs[0]
qt_env['ENV']['CLAZY_CHECKS'] = ','.join(checks)
Export('env', 'qt_env', 'arch', 'real_arch')
# ********** start building stuff **********
# Build common module
SConscript(['common/SConscript'])
Import('_common', '_gpucommon')
Import('_common')
common = [_common, 'json11', 'zmq']
gpucommon = [_gpucommon]
Export('common', 'gpucommon')
Export('common')
# Build messaging (cereal + msgq + socketmaster + their dependencies)
# Enable swaglog include in submodules
@@ -375,6 +231,5 @@ if Dir('#tools/cabana/').exists() and GetOption('extras'):
if arch != "larch64":
SConscript(['tools/cabana/SConscript'])
external_sconscript = GetOption('external_sconscript')
if external_sconscript:
SConscript([external_sconscript])
env.CompilationDatabase('compile_commands.json')
+8 -5
View File
@@ -918,6 +918,8 @@ struct ControlsState @0x97ff69c53601abf1 {
saturated @7 :Bool;
actualLateralAccel @9 :Float32;
desiredLateralAccel @10 :Float32;
desiredLateralJerk @11 :Float32;
version @12 :Int32;
}
struct LateralLQRState {
@@ -2146,13 +2148,10 @@ struct Joystick {
struct DriverStateV2 {
frameId @0 :UInt32;
modelExecutionTime @1 :Float32;
dspExecutionTimeDEPRECATED @2 :Float32;
gpuExecutionTime @8 :Float32;
rawPredictions @3 :Data;
poorVisionProb @4 :Float32;
wheelOnRightProb @5 :Float32;
leftDriverData @6 :DriverData;
rightDriverData @7 :DriverData;
@@ -2167,10 +2166,13 @@ struct DriverStateV2 {
leftBlinkProb @7 :Float32;
rightBlinkProb @8 :Float32;
sunglassesProb @9 :Float32;
occludedProb @10 :Float32;
readyProb @11 :List(Float32);
notReadyProb @12 :List(Float32);
occludedProbDEPRECATED @10 :Float32;
readyProbDEPRECATED @11 :List(Float32);
}
dspExecutionTimeDEPRECATED @2 :Float32;
poorVisionProbDEPRECATED @4 :Float32;
}
struct DriverStateDEPRECATED @0xb83c6cc593ed0a00 {
@@ -2222,6 +2224,7 @@ struct DriverMonitoringState @0xb83cda094a1da284 {
hiStdCount @14 :UInt32;
isActiveMode @16 :Bool;
isRHD @4 :Bool;
uncertainCount @19 :UInt32;
isPreviewDEPRECATED @15 :Bool;
rhdCheckedDEPRECATED @5 :Bool;
+3 -9
View File
@@ -4,18 +4,12 @@ common_libs = [
'params.cc',
'swaglog.cc',
'util.cc',
'watchdog.cc',
'ratekeeper.cc'
]
_common = env.Library('common', common_libs, LIBS="json11")
files = [
'ratekeeper.cc',
'clutil.cc',
]
_gpucommon = env.Library('gpucommon', files)
Export('_common', '_gpucommon')
_common = env.Library('common', common_libs, LIBS="json11")
Export('_common')
if GetOption('extras'):
env.Program('tests/test_common',
+6 -2
View File
@@ -14,9 +14,13 @@ class Api:
def post(self, *args, **kwargs):
return self.service.post(*args, **kwargs)
def get_token(self, expiry_hours=1):
return self.service.get_token(expiry_hours)
def get_token(self, payload_extra=None, expiry_hours=1):
return self.service.get_token(payload_extra, expiry_hours)
def api_get(endpoint, method='GET', timeout=None, access_token=None, **params):
return CommaConnectApi(None).api_get(endpoint, method, timeout, access_token, **params)
def get_key_pair():
return CommaConnectApi(None).get_key_pair()
+20 -6
View File
@@ -1,18 +1,22 @@
import jwt
import os
import requests
import unicodedata
from datetime import datetime, timedelta, UTC
from openpilot.system.hardware.hw import Paths
from openpilot.system.version import get_version
# name : jwt signature algorithm
KEYS = {"id_rsa" : "RS256",
"id_ecdsa" : "ES256"}
class BaseApi:
def __init__(self, dongle_id, api_host, user_agent="openpilot-"):
self.dongle_id = dongle_id
self.api_host = api_host
self.user_agent = user_agent
with open(f'{Paths.persist_root()}/comma/id_rsa') as f:
self.private_key = f.read()
self.jwt_algorithm, self.private_key, _ = self.get_key_pair()
def get(self, *args, **kwargs):
return self.request('GET', *args, **kwargs)
@@ -23,7 +27,7 @@ class BaseApi:
def request(self, method, endpoint, timeout=None, access_token=None, **params):
return self.api_get(endpoint, method=method, timeout=timeout, access_token=access_token, **params)
def _get_token(self, expiry_hours=1, **extra_payload):
def _get_token(self, payload_extra=None, expiry_hours=1, **extra_payload):
now = datetime.now(UTC).replace(tzinfo=None)
payload = {
'identity': self.dongle_id,
@@ -32,13 +36,15 @@ class BaseApi:
'exp': now + timedelta(hours=expiry_hours),
**extra_payload
}
token = jwt.encode(payload, self.private_key, algorithm='RS256')
if payload_extra is not None:
payload.update(payload_extra)
token = jwt.encode(payload, self.private_key, algorithm=self.jwt_algorithm)
if isinstance(token, bytes):
token = token.decode('utf8')
return token
def get_token(self, expiry_hours=1):
return self._get_token(expiry_hours)
def get_token(self, payload_extra=None, expiry_hours=1):
return self._get_token(payload_extra, expiry_hours)
def remove_non_ascii_chars(self, text):
normalized_text = unicodedata.normalize('NFD', text)
@@ -54,3 +60,11 @@ class BaseApi:
headers['User-Agent'] = self.user_agent + version
return requests.request(method, f"{self.api_host}/{endpoint}", timeout=timeout, headers=headers, json=json, params=params)
@staticmethod
def get_key_pair():
for key in KEYS:
if os.path.isfile(Paths.persist_root() + f'/comma/{key}') and os.path.isfile(Paths.persist_root() + f'/comma/{key}.pub'):
with open(Paths.persist_root() + f'/comma/{key}') as private, open(Paths.persist_root() + f'/comma/{key}.pub') as public:
return KEYS[key], private.read(), public.read()
return None, None, None
-9
View File
@@ -1,9 +0,0 @@
# remove all keys that end in DEPRECATED
def strip_deprecated_keys(d):
for k in list(d.keys()):
if isinstance(k, str):
if k.endswith('DEPRECATED'):
d.pop(k)
elif isinstance(d[k], dict):
strip_deprecated_keys(d[k])
return d
+1 -1
View File
@@ -1,6 +1,6 @@
from functools import cache
import subprocess
from openpilot.common.run import run_cmd, run_cmd_default
from openpilot.common.utils import run_cmd, run_cmd_default
@cache
+1 -1
View File
@@ -1 +1 @@
#define DEFAULT_MODEL "Firehose (Default)"
#define DEFAULT_MODEL "The Cool People (Default)"
+10 -1
View File
@@ -66,7 +66,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"IsTakingSnapshot", {CLEAR_ON_MANAGER_START, BOOL}},
{"IsTestedBranch", {CLEAR_ON_MANAGER_START, BOOL}},
{"JoystickDebugMode", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
{"LanguageSetting", {PERSISTENT | BACKUP, STRING, "main_en"}},
{"LanguageSetting", {PERSISTENT | BACKUP, STRING, "en"}},
{"LastAthenaPingTime", {CLEAR_ON_MANAGER_START, INT}},
{"LastGPSPosition", {PERSISTENT, STRING}},
{"LastManagerExitReason", {CLEAR_ON_MANAGER_START, STRING}},
@@ -97,6 +97,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"Offroad_TemperatureTooHigh", {CLEAR_ON_MANAGER_START, JSON}},
{"Offroad_UnregisteredHardware", {CLEAR_ON_MANAGER_START, JSON}},
{"Offroad_UpdateFailed", {CLEAR_ON_MANAGER_START, JSON}},
{"Offroad_DriverMonitoringUncertain", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, JSON}},
{"OnroadCycleRequested", {CLEAR_ON_MANAGER_START, BOOL}},
{"OpenpilotEnabledToggle", {PERSISTENT | BACKUP, BOOL, "1"}},
{"PandaHeartbeatLost", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
@@ -108,6 +109,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"RecordFront", {PERSISTENT | BACKUP, BOOL}},
{"RecordFrontLock", {PERSISTENT, BOOL}}, // for the internal fleet
{"SecOCKey", {PERSISTENT | DONT_LOG | BACKUP, STRING}},
{"ShowDebugInfo", {PERSISTENT, BOOL}},
{"RouteCount", {PERSISTENT, INT, "0"}},
{"SnoozeUpdate", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
{"SshEnabled", {PERSISTENT | BACKUP, BOOL}},
@@ -260,4 +262,11 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"TorqueParamsOverrideEnabled", {PERSISTENT | BACKUP, BOOL, "0"}},
{"TorqueParamsOverrideFriction", {PERSISTENT | BACKUP, FLOAT, "0.1"}},
{"TorqueParamsOverrideLatAccelFactor", {PERSISTENT | BACKUP, FLOAT, "2.5"}},
// Tuning keys
{"EnableHkgTuningAngleSmoothingFactor", {PERSISTENT | BACKUP, BOOL, "1"}},
{"HkgTuningAngleMinTorqueReductionGain", {PERSISTENT | BACKUP, INT, "10"}},
{"HkgTuningAngleMaxTorqueReductionGain", {PERSISTENT | BACKUP, INT, "100"}},
{"HkgTuningAngleActiveTorqueReductionGain", {PERSISTENT | BACKUP, INT, "100"}},
{"HkgTuningOverridingCycles", {PERSISTENT | BACKUP, INT, "17"}},
};
+6 -7
View File
@@ -2,11 +2,10 @@ import numpy as np
from numbers import Number
class PIDController:
def __init__(self, k_p, k_i, k_f=0., k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100):
def __init__(self, k_p, k_i, k_d=0., pos_limit=1e308, neg_limit=-1e308, rate=100):
self._k_p = k_p
self._k_i = k_i
self._k_d = k_d
self.k_f = k_f # feedforward gain
if isinstance(self._k_p, Number):
self._k_p = [[0], [self._k_p]]
if isinstance(self._k_i, Number):
@@ -16,7 +15,7 @@ class PIDController:
self.set_limits(pos_limit, neg_limit)
self.i_rate = 1.0 / rate
self.i_dt = 1.0 / rate
self.speed = 0.0
self.reset()
@@ -46,12 +45,12 @@ class PIDController:
def update(self, error, error_rate=0.0, speed=0.0, feedforward=0., freeze_integrator=False):
self.speed = speed
self.p = float(error) * self.k_p
self.f = feedforward * self.k_f
self.d = error_rate * self.k_d
self.p = self.k_p * float(error)
self.d = self.k_d * error_rate
self.f = feedforward
if not freeze_integrator:
i = self.i + error * self.k_i * self.i_rate
i = self.i + self.k_i * self.i_dt * error
# Don't allow windup if already clipping
test_control = self.p + i + self.d + self.f
-30
View File
@@ -1,30 +0,0 @@
import time
import functools
from openpilot.common.swaglog import cloudlog
def retry(attempts=3, delay=1.0, ignore_failure=False):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for _ in range(attempts):
try:
return func(*args, **kwargs)
except Exception:
cloudlog.exception(f"{func.__name__} failed, trying again")
time.sleep(delay)
if ignore_failure:
cloudlog.error(f"{func.__name__} failed after retry")
else:
raise Exception(f"{func.__name__} failed after retry")
return wrapper
return decorator
if __name__ == "__main__":
@retry(attempts=10)
def abc():
raise ValueError("abc failed :(")
abc()
-28
View File
@@ -1,28 +0,0 @@
import subprocess
from contextlib import contextmanager
from subprocess import Popen, PIPE, TimeoutExpired
def run_cmd(cmd: list[str], cwd=None, env=None) -> str:
return subprocess.check_output(cmd, encoding='utf8', cwd=cwd, env=env).strip()
def run_cmd_default(cmd: list[str], default: str = "", cwd=None, env=None) -> str:
try:
return run_cmd(cmd, cwd=cwd, env=env)
except subprocess.CalledProcessError:
return default
@contextmanager
def managed_proc(cmd: list[str], env: dict[str, str]):
proc = Popen(cmd, env=env, stdout=PIPE, stderr=PIPE)
try:
yield proc
finally:
if proc.poll() is None:
proc.terminate()
try:
proc.wait(timeout=5)
except TimeoutExpired:
proc.kill()
+1 -1
View File
@@ -1,7 +1,7 @@
import os
from uuid import uuid4
from openpilot.common.file_helpers import atomic_write_in_dir
from openpilot.common.utils import atomic_write_in_dir
class TestFileHelpers:
+62 -2
View File
@@ -2,9 +2,14 @@ import io
import os
import tempfile
import contextlib
import subprocess
import time
import functools
from subprocess import Popen, PIPE, TimeoutExpired
import zstandard as zstd
from openpilot.common.swaglog import cloudlog
LOG_COMPRESSION_LEVEL = 10 # little benefit up to level 15. level ~17 is a small step change
LOG_COMPRESSION_LEVEL = 10 # little benefit up to level 15. level ~17 is a small step change
class CallbackReader:
@@ -27,7 +32,7 @@ class CallbackReader:
@contextlib.contextmanager
def atomic_write_in_dir(path: str, mode: str = 'w', buffering: int = -1, encoding: str = None, newline: str = None,
def atomic_write_in_dir(path: str, mode: str = 'w', buffering: int = -1, encoding: str | None = None, newline: str | None = None,
overwrite: bool = False):
"""Write to a file atomically using a temporary file in the same directory as the destination file."""
dir_name = os.path.dirname(path)
@@ -56,3 +61,58 @@ def get_upload_stream(filepath: str, should_compress: bool) -> tuple[io.Buffered
compressed_size = compressed_stream.tell()
compressed_stream.seek(0)
return compressed_stream, compressed_size
# remove all keys that end in DEPRECATED
def strip_deprecated_keys(d):
for k in list(d.keys()):
if isinstance(k, str):
if k.endswith('DEPRECATED'):
d.pop(k)
elif isinstance(d[k], dict):
strip_deprecated_keys(d[k])
return d
def run_cmd(cmd: list[str], cwd=None, env=None) -> str:
return subprocess.check_output(cmd, encoding='utf8', cwd=cwd, env=env).strip()
def run_cmd_default(cmd: list[str], default: str = "", cwd=None, env=None) -> str:
try:
return run_cmd(cmd, cwd=cwd, env=env)
except subprocess.CalledProcessError:
return default
@contextlib.contextmanager
def managed_proc(cmd: list[str], env: dict[str, str]):
proc = Popen(cmd, env=env, stdout=PIPE, stderr=PIPE)
try:
yield proc
finally:
if proc.poll() is None:
proc.terminate()
try:
proc.wait(timeout=5)
except TimeoutExpired:
proc.kill()
def retry(attempts=3, delay=1.0, ignore_failure=False):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for _ in range(attempts):
try:
return func(*args, **kwargs)
except Exception:
cloudlog.exception(f"{func.__name__} failed, trying again")
time.sleep(delay)
if ignore_failure:
cloudlog.error(f"{func.__name__} failed after retry")
else:
raise Exception(f"{func.__name__} failed after retry")
return wrapper
return decorator
+1 -1
View File
@@ -1 +1 @@
#define COMMA_VERSION "0.10.1"
#define COMMA_VERSION "0.10.2"
-12
View File
@@ -1,12 +0,0 @@
#include <string>
#include "common/watchdog.h"
#include "common/util.h"
#include "system/hardware/hw.h"
const std::string watchdog_fn_prefix = Path::shm_path() + "/wd_"; // + <pid>
bool watchdog_kick(uint64_t ts) {
static std::string fn = watchdog_fn_prefix + std::to_string(getpid());
return util::write_file(fn.c_str(), &ts, sizeof(ts), O_WRONLY | O_CREAT) > 0;
}
-5
View File
@@ -1,5 +0,0 @@
#pragma once
#include <cstdint>
bool watchdog_kick(uint64_t ts);
-22
View File
@@ -1,22 +0,0 @@
import os
import time
import struct
from openpilot.system.hardware.hw import Paths
WATCHDOG_FN = f"{Paths.shm_path()}/wd_"
_LAST_KICK = 0.0
def kick_watchdog():
global _LAST_KICK
current_time = time.monotonic()
if current_time - _LAST_KICK < 1.0:
return
try:
with open(f"{WATCHDOG_FN}{os.getpid()}", 'wb') as f:
f.write(struct.pack('<Q', int(current_time * 1e9)))
f.flush()
_LAST_KICK = current_time
except OSError:
pass
+65
View File
@@ -0,0 +1,65 @@
# CarState signals
## Required for basic lateral control
* `brakePressed`
* `cruiseState`
* `doorOpen`
* `espDisabled`
* `gasPressed`
* `gearShifter`
* `leftBlinker` / `rightBlinker`
* `seatbeltUnlatched`
* `standstill`
* `steeringAngleDeg`
* `steeringPressed`
* `steeringTorque`
* `steerFaultPermanent`
* `steerFaultTemporary`
* `vCruise`
* `wheelSpeeds.[fl|fr|rl|rr]`: Speed of each of the car's four wheels, in m/s. The car's CAN bus often broadcasts the
speed in kph, so the helper function `parse_wheel_speeds` performs this conversion by default.
## Recommended / Required for openpilot longitudinal control
* `accFaulted`
* `espActive`
* `parkingBrake`
## Application Dependent
* `blockPcmEnable`
* `buttonEnable`
* `brakeHoldActive`
* `carFaultedNonCritical`
* `invalidLkasSetting`
* `lowSpeedAlert`
* `regenBraking`
* `steeringAngleOffsetDeg`
* `steeringDisengage`
* `steeringTorqueEps`
* `stockLkas`
* `vCruiseCluster`
* `vEgoCluster`
* `vehicleSensorsInvalid`
## Automatically populated
* `buttonEvents`
These values are populated automatically by `parse_wheel_speeds`:
* `aEgo`: Acceleration of the ego vehicle, Kalman filtered derivative of `vEgo`.
* `vEgo`: Speed of the ego vehicle, Kalman filtered from `vEgoRaw`.
* `vEgoRaw`: Speed of the ego vehicle, based on the average of all four wheel speeds, unfiltered.
## Optional
* `brake`
* `charging`
* `fuelGauge`
* `leftBlindspot` / `rightBlindspot`
* `steeringRateDeg`
* `stockAeb`
* `stockFcw`
* `yawRate`
+85
View File
@@ -0,0 +1,85 @@
# Stimulus-Response Tests
These are example test drives that can help identify the CAN bus messaging necessary for ADAS control. Each scripted
test should be done in a separate route (ignition cycle). These tests are a guide, not necessarily exhaustive.
While testing, constant power to the comma device is highly recommended, using [comma power](https://comma.ai/shop/comma-power) if
necessary to make sure all test activity is fully captured and for ease of uploading. If constant power isn't
available, keep the ignition on for at least one minute after your test to make sure power loss doesn't result
in loss of the last minute of testing data.
## Stationary ignition-only tests, part 1
1. Ignition on, but don't start engine, remain in Park
2. Open and close each door in a defined order: driver, passenger, rear left, rear right
3. Re-enter the vehicle, close the driver's door, and fasten the driver's seatbelt
4. Slowly press and release the accelerator pedal 3 times
5. Slowly press and release the brake pedal 3 times
6. Hold the brake and move the gearshift to reverse, then neutral, then drive, then sport/eco/etc if applicable
7. Return to Park, ignition off
Brake-pressed information may show up in several messages and signals, both as on/off states and as a percentage or
pressure. It may reflect a switch on the driver's brake pedal, or a pressure-threshold state, or signals to turn on
the rear brake lights. Start by identifying all the potential signals, and confirm while driving with ACC later.
Locate signals for all four door states if possible, but some cars only expose the driver's door state on the ADAS bus.
Driver/passenger door signals may or may not change positions for LHD vs RHD cars. For cars where only the driver's
door signal is available, the same signal may follow the driver.
## Stationary ignition-only tests, part 2
1. Ignition on, but don't start engine, remain in Park
2. Press each ACC button in a defined order: main switch on/off, set, resume, cancel, accel, decel, gap adjust
3. Set the left turn signal for about five seconds
4. Operate the left turn signal one time in its touch-to-pass mode
5. Set the right turn signal for about five seconds
6. Operate the right turn signal one time in its touch-to-pass mode
7. Set the hazard / emergency indicator switch for about five seconds
8. Ignition off
Your vehicle may have a momentary-press main ACC switch or a physical toggle that remains set. Actual ACC engagement
isn't necessary for purposes of detecting the ACC button presses.
## Steering angle and steering torque tests
Power steering should be available. On ICE cars, engine RPM may be present.
1. Ignition on, start engine if applicable, remain in Park
2. Rotate the steering wheel as follows, with a few seconds pause between each step
* Start as close to exact center as possible
* Turn to 45 degrees right and hold
* Turn to 90 degrees right and hold
* Turn to 180 degrees right and hold
* Turn to full lock right and hold, with firm pressure against lock
* Release the wheel and allow it to bounce back slightly from lock
* Turn to 180 degrees left and hold
* Return to center and release
3. Ignition off
Performing the full test to the right, followed by an abbreviated test to the left, helps give additional confirmation
of signal scale, and sign/direction for both the steering wheel angle and driver input torque signals.
## Low speed / parking lot driving tests
Before this test, drive to a place like an empty parking lot where you are free to drive in a series of curves.
1. Ignition on, start engine if applicable, prepare to drive
2. Slowly (10-20mph at most) drive a figure-8 if possible, or at least one sharp left and one sharp right.
3. Come to a complete stop
4. When and where safe, drive in reverse for a short distance (10-15 feet)
5. Park the car in a safe place, ignition off
## High speed / highway driving tests
Select a place and time where you can safely set cruise control at normal travel speeds with little interference from
traffic ahead, and safely test the response of your factory lane guidance system.
1. Ignition on, start engine if applicable, prepare to drive
2. When safely able, engage adaptive cruise control below 50 mph
3. When safely able, use the ACC buttons to accelerate to 50mph, then 55mph, then 60mph
4. Disengage adaptive cruise
5. When safely able, allow your factory lane guidance to prevent lane departures, 2-3 times on both the left and right
The series of setpoints can be adjusted to local traffic regulations, and of course metric units. The specific cruise
setpoints are useful for locating the ACC HUD signals later, and confirming their precise scaling. When the car reaches
and holds the setpoint, that can also provide additional confirmation of wheel speed scaling.
+10 -1
View File
@@ -6,8 +6,17 @@ export NUMEXPR_NUM_THREADS=1
export OPENBLAS_NUM_THREADS=1
export VECLIB_MAXIMUM_THREADS=1
# models get lower priority than ui
# - ui is ~5ms
# - modeld is 20ms
# - DM is 10ms
# in order to run ui at 60fps (16.67ms), we need to allow
# it to preempt the model workloads. we have enough
# headroom for this until ui is moved to the CPU.
export QCOM_PRIORITY=12
if [ -z "$AGNOS_VERSION" ]; then
export AGNOS_VERSION="13.1"
export AGNOS_VERSION="15"
fi
export STAGING_ROOT="/data/safe_staging"
+1 -1
Submodule panda updated: 69ab12ee2a...e4115086b0
+10 -5
View File
@@ -23,7 +23,7 @@ dependencies = [
# core
"cffi",
"scons",
"pycapnp",
"pycapnp==2.1.0",
"Cython",
"setuptools",
"numpy >=2.0",
@@ -72,7 +72,9 @@ dependencies = [
"zstandard",
# ui
"raylib < 5.5.0.3", # TODO: unpin when they fix https://github.com/electronstudio/raylib-python-cffi/issues/186
"qrcode",
"mapbox-earcut",
]
[project.optional-dependencies]
@@ -119,7 +121,6 @@ dev = [
"tabulate",
"types-requests",
"types-tabulate",
"raylib",
]
tools = [
@@ -177,7 +178,7 @@ quiet-level = 3
# if you've got a short variable name that's getting flagged, add it here
ignore-words-list = "bu,ro,te,ue,alo,hda,ois,nam,nams,ned,som,parm,setts,inout,warmup,bumb,nd,sie,preints,whit,indexIn,ws,uint,grey,deque,stdio,amin,BA,LITE,atEnd,UIs,errorString,arange,FocusIn,od,tim,relA,hist,copyable,jupyter,thead,TGE,abl,lite"
builtin = "clear,rare,informal,code,names,en-GB_to_en-US"
skip = "./third_party/*, ./tinygrad/*, ./tinygrad_repo/*, ./msgq/*, ./panda/*, ./opendbc/*, ./opendbc_repo/*, ./rednose/*, ./rednose_repo/*, ./teleoprtc/*, ./teleoprtc_repo/*, *.ts, uv.lock, *.onnx, ./cereal/gen/*, */c_generated_code/*, docs/assets/*, tools/plotjuggler/layouts/*"
skip = "./third_party/*, ./tinygrad/*, ./tinygrad_repo/*, ./msgq/*, ./panda/*, ./opendbc/*, ./opendbc_repo/*, ./rednose/*, ./rednose_repo/*, ./teleoprtc/*, ./teleoprtc_repo/*, *.po, uv.lock, *.onnx, ./cereal/gen/*, */c_generated_code/*, docs/assets/*, tools/plotjuggler/layouts/*"
[tool.mypy]
python_version = "3.11"
@@ -235,7 +236,6 @@ lint.ignore = [
"B027",
"B024",
"NPY002", # new numpy random syntax is worse
"UP038", # (x, y) -> x|y for isinstance
]
line-length = 160
target-version ="py311"
@@ -263,8 +263,13 @@ lint.flake8-implicit-str-concat.allow-multiline = false
"tools".msg = "Use openpilot.tools"
"pytest.main".msg = "pytest.main requires special handling that is easy to mess up!"
"unittest".msg = "Use pytest"
"pyray.measure_text_ex".msg = "Use openpilot.system.ui.lib.text_measure"
"time.time".msg = "Use time.monotonic"
# raylib banned APIs
"pyray.measure_text_ex".msg = "Use openpilot.system.ui.lib.text_measure"
"pyray.is_mouse_button_pressed".msg = "This can miss events. Use Widget._handle_mouse_press"
"pyray.is_mouse_button_released".msg = "This can miss events. Use Widget._handle_mouse_release"
"pyray.draw_text".msg = "Use a function (such as rl.draw_font_ex) that takes font as an argument"
[tool.ruff.format]
quote-style = "preserve"
+1 -1
View File
@@ -12,7 +12,7 @@ from openpilot.common.basedir import BASEDIR
DIRS = ['cereal', 'openpilot']
EXTS = ['.png', '.py', '.ttf', '.capnp']
EXTS = ['.png', '.py', '.ttf', '.capnp', '.json', '.fnt', '.mo']
INTERPRETER = '/usr/bin/env python3'
+1 -1
View File
@@ -13,7 +13,7 @@ cd $ROOT
FAILED=0
IGNORED_FILES="uv\.lock|docs\/CARS.md|LICENSE\.md"
IGNORED_FILES="uv\.lock|docs\/CARS.md|LICENSE\.md|layouts\/.*\.xml|.*\.ipynb"
IGNORED_DIRS="^third_party.*|^msgq.*|^msgq_repo.*|^opendbc.*|^opendbc_repo.*|^cereal.*|^panda.*|^rednose.*|^rednose_repo.*|^tinygrad.*|^tinygrad_repo.*|^teleoprtc.*|^teleoprtc_repo.*"
function run() {
+1 -1
View File
@@ -3,4 +3,4 @@ SConscript(['controls/lib/lateral_mpc_lib/SConscript'])
SConscript(['controls/lib/longitudinal_mpc_lib/SConscript'])
SConscript(['locationd/SConscript'])
SConscript(['modeld/SConscript'])
SConscript(['ui/SConscript'])
SConscript(['ui/SConscript'])
+2
View File
@@ -1,2 +1,4 @@
*.cc
fonts/*.fnt
fonts/*.png
translations_assets.qrc
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:93cdc4ee9aa40e2afceecc63da0ca05ec7aab4bec991ece51a6b52389f48a477
size 10788068
+128
View File
@@ -0,0 +1,128 @@
#!/usr/bin/env python3
from pathlib import Path
import json
import pyray as rl
FONT_DIR = Path(__file__).resolve().parent
SELFDRIVE_DIR = FONT_DIR.parents[1]
TRANSLATIONS_DIR = SELFDRIVE_DIR / "ui" / "translations"
LANGUAGES_FILE = TRANSLATIONS_DIR / "languages.json"
GLYPH_PADDING = 6
EXTRA_CHARS = "–‑✓×°§•€£¥"
UNIFONT_LANGUAGES = {"ar", "th", "zh-CHT", "zh-CHS", "ko", "ja"}
def _languages():
if not LANGUAGES_FILE.exists():
return {}
with LANGUAGES_FILE.open(encoding="utf-8") as f:
return json.load(f)
def _char_sets():
base = set(map(chr, range(32, 127))) | set(EXTRA_CHARS)
unifont = set(base)
for language, code in _languages().items():
unifont.update(language)
po_path = TRANSLATIONS_DIR / f"app_{code}.po"
try:
chars = set(po_path.read_text(encoding="utf-8"))
except FileNotFoundError:
continue
(unifont if code in UNIFONT_LANGUAGES else base).update(chars)
return tuple(sorted(ord(c) for c in base)), tuple(sorted(ord(c) for c in unifont))
def _glyph_metrics(glyphs, rects, codepoints):
entries = []
min_offset_y, max_extent = None, 0
for idx, codepoint in enumerate(codepoints):
glyph = glyphs[idx]
rect = rects[idx]
width = int(round(rect.width))
height = int(round(rect.height))
offset_y = int(round(glyph.offsetY))
min_offset_y = offset_y if min_offset_y is None else min(min_offset_y, offset_y)
max_extent = max(max_extent, offset_y + height)
entries.append({
"id": codepoint,
"x": int(round(rect.x)),
"y": int(round(rect.y)),
"width": width,
"height": height,
"xoffset": int(round(glyph.offsetX)),
"yoffset": offset_y,
"xadvance": int(round(glyph.advanceX)),
})
if min_offset_y is None:
raise RuntimeError("No glyphs were generated")
line_height = int(round(max_extent - min_offset_y))
base = int(round(max_extent))
return entries, line_height, base
def _write_bmfont(path: Path, font_size: int, face: str, atlas_name: str, line_height: int, base: int, atlas_size, entries):
lines = [
f"info face=\"{face}\" size=-{font_size} bold=0 italic=0 charset=\"\" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=0,0 outline=0",
f"common lineHeight={line_height} base={base} scaleW={atlas_size[0]} scaleH={atlas_size[1]} pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4",
f"page id=0 file=\"{atlas_name}\"",
f"chars count={len(entries)}",
]
for entry in entries:
lines.append(
("char id={id:<4} x={x:<5} y={y:<5} width={width:<5} height={height:<5} " +
"xoffset={xoffset:<5} yoffset={yoffset:<5} xadvance={xadvance:<5} page=0 chnl=15").format(**entry)
)
path.write_text("\n".join(lines) + "\n")
def _process_font(font_path: Path, codepoints: tuple[int, ...]):
print(f"Processing {font_path.name}...")
font_size = {
"unifont.otf": 16, # unifont is only 16x8 or 16x16 pixels per glyph
}.get(font_path.name, 200)
data = font_path.read_bytes()
file_buf = rl.ffi.new("unsigned char[]", data)
cp_buffer = rl.ffi.new("int[]", codepoints)
cp_ptr = rl.ffi.cast("int *", cp_buffer)
glyphs = rl.load_font_data(rl.ffi.cast("unsigned char *", file_buf), len(data), font_size, cp_ptr, len(codepoints), rl.FontType.FONT_DEFAULT)
if glyphs == rl.ffi.NULL:
raise RuntimeError("raylib failed to load font data")
rects_ptr = rl.ffi.new("Rectangle **")
image = rl.gen_image_font_atlas(glyphs, rects_ptr, len(codepoints), font_size, GLYPH_PADDING, 0)
if image.width == 0 or image.height == 0:
raise RuntimeError("raylib returned an empty atlas")
rects = rects_ptr[0]
atlas_name = f"{font_path.stem}.png"
atlas_path = FONT_DIR / atlas_name
entries, line_height, base = _glyph_metrics(glyphs, rects, codepoints)
if not rl.export_image(image, atlas_path.as_posix()):
raise RuntimeError("Failed to export atlas image")
_write_bmfont(FONT_DIR / f"{font_path.stem}.fnt", font_size, font_path.stem, atlas_name, line_height, base, (image.width, image.height), entries)
def main():
base_cp, unifont_cp = _char_sets()
fonts = sorted(FONT_DIR.glob("*.ttf")) + sorted(FONT_DIR.glob("*.otf"))
for font in fonts:
if "emoji" in font.name.lower():
continue
glyphs = unifont_cp if font.stem.lower().startswith("unifont") else base_cp
_process_font(font, glyphs)
return 0
if __name__ == "__main__":
raise SystemExit(main())
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9712a9bc089af7ddc06e0826aa84f2ee23ed2f1a1dddaf2a89c2483e753a8475
size 5321484
+3 -3
View File
@@ -62,8 +62,8 @@ class TestCarInterfaces:
# hypothesis also slows down significantly with just one more message draw
LongControl(car_params, car_params_sp)
if car_params.steerControlType == CarParams.SteerControlType.angle:
LatControlAngle(car_params, car_params_sp, car_interface)
LatControlAngle(car_params, car_params_sp, car_interface, DT_CTRL)
elif car_params.lateralTuning.which() == 'pid':
LatControlPID(car_params, car_params_sp, car_interface)
LatControlPID(car_params, car_params_sp, car_interface, DT_CTRL)
elif car_params.lateralTuning.which() == 'torque':
LatControlTorque(car_params, car_params_sp, car_interface)
LatControlTorque(car_params, car_params_sp, car_interface, DT_CTRL)
+1 -1
View File
@@ -189,7 +189,7 @@ class TestCarModelBase(unittest.TestCase):
if tuning == 'pid':
self.assertTrue(len(self.CP.lateralTuning.pid.kpV))
elif tuning == 'torque':
self.assertTrue(self.CP.lateralTuning.torque.kf > 0)
self.assertTrue(self.CP.lateralTuning.torque.latAccelFactor > 0)
else:
raise Exception("unknown tuning")
+8 -6
View File
@@ -8,7 +8,7 @@ from cereal import car, log
import cereal.messaging as messaging
from openpilot.common.constants import CV
from openpilot.common.params import Params
from openpilot.common.realtime import config_realtime_process, Priority, Ratekeeper
from openpilot.common.realtime import config_realtime_process, DT_CTRL, Priority, Ratekeeper
from openpilot.common.swaglog import cloudlog
from opendbc.car.car_helpers import interfaces
@@ -19,6 +19,7 @@ from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID
from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle, STEER_ANGLE_SATURATION_THRESHOLD
from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
from openpilot.selfdrive.controls.lib.longcontrol import LongControl
from openpilot.selfdrive.modeld.modeld import LAT_SMOOTH_SECONDS
from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose
from openpilot.sunnypilot.livedelay.helpers import get_lat_delay
@@ -45,7 +46,7 @@ class Controls(ControlsExt, ModelStateBase):
self.CI = interfaces[self.CP.carFingerprint](self.CP, self.CP_SP)
self.sm = messaging.SubMaster(['liveParameters', 'liveTorqueParameters', 'modelV2', 'selfdriveState',
self.sm = messaging.SubMaster(['liveDelay', 'liveParameters', 'liveTorqueParameters', 'modelV2', 'selfdriveState',
'liveCalibration', 'livePose', 'longitudinalPlan', 'carState', 'carOutput',
'driverMonitoringState', 'onroadEvents', 'driverAssistance', 'liveDelay'] + self.sm_services_ext,
poll='selfdriveState')
@@ -62,11 +63,11 @@ class Controls(ControlsExt, ModelStateBase):
self.VM = VehicleModel(self.CP)
self.LaC: LatControl
if self.CP.steerControlType == car.CarParams.SteerControlType.angle:
self.LaC = LatControlAngle(self.CP, self.CP_SP, self.CI)
self.LaC = LatControlAngle(self.CP, self.CP_SP, self.CI, DT_CTRL)
elif self.CP.lateralTuning.which() == 'pid':
self.LaC = LatControlPID(self.CP, self.CP_SP, self.CI)
self.LaC = LatControlPID(self.CP, self.CP_SP, self.CI, DT_CTRL)
elif self.CP.lateralTuning.which() == 'torque':
self.LaC = LatControlTorque(self.CP, self.CP_SP, self.CI)
self.LaC = LatControlTorque(self.CP, self.CP_SP, self.CI, DT_CTRL)
def update(self):
self.sm.update(15)
@@ -139,11 +140,12 @@ class Controls(ControlsExt, ModelStateBase):
# Reset desired curvature to current to avoid violating the limits on engage
new_desired_curvature = model_v2.action.desiredCurvature if CC.latActive else self.curvature
self.desired_curvature, curvature_limited = clip_curvature(CS.vEgo, self.desired_curvature, new_desired_curvature, lp.roll)
lat_delay = self.sm["liveDelay"].lateralDelay + LAT_SMOOTH_SECONDS
actuators.curvature = self.desired_curvature
steer, steeringAngleDeg, lac_log = self.LaC.update(CC.latActive, CS, self.VM, lp,
self.steer_limited_by_safety, self.desired_curvature,
self.calibrated_pose, curvature_limited) # TODO what if not available
self.calibrated_pose, curvature_limited, lat_delay)
actuators.torque = float(steer)
actuators.steeringAngleDeg = float(steeringAngleDeg)
# Ensure no NaNs/Infs
+1 -1
View File
@@ -22,7 +22,7 @@ def smooth_value(val, prev_val, tau, dt=DT_MDL):
alpha = 1 - np.exp(-dt/tau) if tau > 0 else 1
return alpha * val + (1 - alpha) * prev_val
def clip_curvature(v_ego, prev_curvature, new_curvature, roll):
def clip_curvature(v_ego, prev_curvature, new_curvature, roll) -> tuple[float, bool]:
# This function respects ISO lateral jerk and acceleration limits + a max curvature
v_ego = max(v_ego, MIN_SPEED)
max_curvature_rate = MAX_LATERAL_JERK / (v_ego ** 2) # inexact calculation, check https://github.com/commaai/openpilot/pull/24755
+11 -11
View File
@@ -1,31 +1,31 @@
import numpy as np
from abc import abstractmethod, ABC
from openpilot.common.realtime import DT_CTRL
from openpilot.selfdrive.locationd.helpers import Pose
class LatControl(ABC):
def __init__(self, CP, CP_SP, CI):
self.sat_count_rate = 1.0 * DT_CTRL
def __init__(self, CP, CP_SP, CI, dt):
self.dt = dt
self.sat_limit = CP.steerLimitTimer
self.sat_count = 0.
self.sat_time = 0.
self.sat_check_min_speed = 10.
# we define the steer torque scale as [-1.0...1.0]
self.steer_max = 1.0
@abstractmethod
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, calibrated_pose, curvature_limited):
def update(self, active: bool, CS, VM, params, steer_limited_by_safety: bool, desired_curvature: float, calibrated_pose: Pose,
curvature_limited: bool, lat_delay: float):
pass
def reset(self):
self.sat_count = 0.
self.sat_time = 0.
def _check_saturation(self, saturated, CS, steer_limited_by_safety, curvature_limited):
# Saturated only if control output is not being limited by car torque/angle rate limits
if (saturated or curvature_limited) and CS.vEgo > self.sat_check_min_speed and not steer_limited_by_safety and not CS.steeringPressed:
self.sat_count += self.sat_count_rate
self.sat_time += self.dt
else:
self.sat_count -= self.sat_count_rate
self.sat_count = np.clip(self.sat_count, 0.0, self.sat_limit)
return self.sat_count > (self.sat_limit - 1e-3)
self.sat_time -= self.dt
self.sat_time = np.clip(self.sat_time, 0.0, self.sat_limit)
return self.sat_time > (self.sat_limit - 1e-3)
+3 -3
View File
@@ -8,12 +8,12 @@ STEER_ANGLE_SATURATION_THRESHOLD = 2.5 # Degrees
class LatControlAngle(LatControl):
def __init__(self, CP, CP_SP, CI):
super().__init__(CP, CP_SP, CI)
def __init__(self, CP, CP_SP, CI, dt):
super().__init__(CP, CP_SP, CI, dt)
self.sat_check_min_speed = 5.
self.use_steer_limited_by_safety = CP.brand == "tesla"
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, calibrated_pose, curvature_limited):
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, calibrated_pose, curvature_limited, lat_delay):
angle_log = log.ControlsState.LateralAngleState.new_message()
if not active:
+6 -5
View File
@@ -6,14 +6,15 @@ from openpilot.common.pid import PIDController
class LatControlPID(LatControl):
def __init__(self, CP, CP_SP, CI):
super().__init__(CP, CP_SP, CI)
def __init__(self, CP, CP_SP, CI, dt):
super().__init__(CP, CP_SP, CI, dt)
self.pid = PIDController((CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
(CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
k_f=CP.lateralTuning.pid.kf, pos_limit=self.steer_max, neg_limit=-self.steer_max)
pos_limit=self.steer_max, neg_limit=-self.steer_max)
self.ff_factor = CP.lateralTuning.pid.kf
self.get_steer_feedforward = CI.get_steer_feedforward_function()
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, calibrated_pose, curvature_limited):
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, calibrated_pose, curvature_limited, lat_delay):
pid_log = log.ControlsState.LateralPIDState.new_message()
pid_log.steeringAngleDeg = float(CS.steeringAngleDeg)
pid_log.steeringRateDeg = float(CS.steeringRateDeg)
@@ -30,7 +31,7 @@ class LatControlPID(LatControl):
else:
# offset does not contribute to resistive torque
ff = self.get_steer_feedforward(angle_steers_des_no_offset, CS.vEgo)
ff = self.ff_factor * self.get_steer_feedforward(angle_steers_des_no_offset, CS.vEgo)
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5
output_torque = self.pid.update(error,
+52 -30
View File
@@ -1,9 +1,11 @@
import math
import numpy as np
from collections import deque
from cereal import log
from opendbc.car.lateral import FRICTION_THRESHOLD, get_friction
from openpilot.common.constants import ACCELERATION_DUE_TO_GRAVITY
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.selfdrive.controls.lib.latcontrol import LatControl
from openpilot.common.pid import PIDController
@@ -15,25 +17,34 @@ from openpilot.sunnypilot.selfdrive.controls.lib.latcontrol_torque_ext import La
# wheel slip, or to speed.
# This controller applies torque to achieve desired lateral
# accelerations. To compensate for the low speed effects we
# use a LOW_SPEED_FACTOR in the error. Additionally, there is
# friction in the steering wheel that needs to be overcome to
# move it at all, this is compensated for too.
# accelerations. To compensate for the low speed effects the
# proportional gain is increased at low speeds by the PID controller.
# Additionally, there is friction in the steering wheel that needs
# to be overcome to move it at all, this is compensated for too.
LOW_SPEED_X = [0, 10, 20, 30]
LOW_SPEED_Y = [15, 13, 10, 5]
KP = 1.0
KI = 0.3
KD = 0.0
INTERP_SPEEDS = [1, 1.5, 2.0, 3.0, 5, 7.5, 10, 15, 30]
KP_INTERP = [250, 120, 65, 30, 11.5, 5.5, 3.5, 2.0, KP]
LP_FILTER_CUTOFF_HZ = 1.2
LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0
VERSION = 0
class LatControlTorque(LatControl):
def __init__(self, CP, CP_SP, CI):
super().__init__(CP, CP_SP, CI)
def __init__(self, CP, CP_SP, CI, dt):
super().__init__(CP, CP_SP, CI, dt)
self.torque_params = CP.lateralTuning.torque.as_builder()
self.torque_from_lateral_accel = CI.torque_from_lateral_accel()
self.lateral_accel_from_torque = CI.lateral_accel_from_torque()
self.pid = PIDController(self.torque_params.kp, self.torque_params.ki,
k_f=self.torque_params.kf)
self.pid = PIDController([INTERP_SPEEDS, KP_INTERP], KI, KD, rate=1/self.dt)
self.update_limits()
self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg
self.lat_accel_request_buffer_len = int(LAT_ACCEL_REQUEST_BUFFER_SECONDS / self.dt)
self.lat_accel_request_buffer = deque([0.] * self.lat_accel_request_buffer_len , maxlen=self.lat_accel_request_buffer_len)
self.previous_measurement = 0.0
self.measurement_rate_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * LP_FILTER_CUTOFF_HZ), self.dt)
self.extension = LatControlTorqueExt(self, CP, CP_SP, CI)
@@ -47,57 +58,68 @@ class LatControlTorque(LatControl):
self.pid.set_limits(self.lateral_accel_from_torque(self.steer_max, self.torque_params),
self.lateral_accel_from_torque(-self.steer_max, self.torque_params))
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, calibrated_pose, curvature_limited):
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, calibrated_pose, curvature_limited, lat_delay):
# Override torque params from extension
if self.extension.update_override_torque_params(self.torque_params):
self.update_limits()
pid_log = log.ControlsState.LateralTorqueState.new_message()
pid_log.version = VERSION
if not active:
output_torque = 0.0
pid_log.active = False
else:
actual_curvature = -VM.calc_curvature(math.radians(CS.steeringAngleDeg - params.angleOffsetDeg), CS.vEgo, params.roll)
measured_curvature = -VM.calc_curvature(math.radians(CS.steeringAngleDeg - params.angleOffsetDeg), CS.vEgo, params.roll)
roll_compensation = params.roll * ACCELERATION_DUE_TO_GRAVITY
curvature_deadzone = abs(VM.calc_curvature(math.radians(self.steering_angle_deadzone_deg), CS.vEgo, 0.0))
desired_lateral_accel = desired_curvature * CS.vEgo ** 2
actual_lateral_accel = actual_curvature * CS.vEgo ** 2
lateral_accel_deadzone = curvature_deadzone * CS.vEgo ** 2
low_speed_factor = np.interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y)**2
setpoint = desired_lateral_accel + low_speed_factor * desired_curvature
measurement = actual_lateral_accel + low_speed_factor * actual_curvature
gravity_adjusted_lateral_accel = desired_lateral_accel - roll_compensation
delay_frames = int(np.clip(lat_delay / self.dt, 1, self.lat_accel_request_buffer_len))
expected_lateral_accel = self.lat_accel_request_buffer[-delay_frames]
# TODO factor out lateral jerk from error to later replace it with delay independent alternative
future_desired_lateral_accel = desired_curvature * CS.vEgo ** 2
self.lat_accel_request_buffer.append(future_desired_lateral_accel)
gravity_adjusted_future_lateral_accel = future_desired_lateral_accel - roll_compensation
desired_lateral_jerk = (future_desired_lateral_accel - expected_lateral_accel) / lat_delay
measurement = measured_curvature * CS.vEgo ** 2
measurement_rate = self.measurement_rate_filter.update((measurement - self.previous_measurement) / self.dt)
self.previous_measurement = measurement
setpoint = lat_delay * desired_lateral_jerk + expected_lateral_accel
error = setpoint - measurement
# do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly
pid_log.error = float(setpoint - measurement)
ff = gravity_adjusted_lateral_accel
pid_log.error = float(error)
ff = gravity_adjusted_future_lateral_accel
# latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll
ff -= self.torque_params.latAccelOffset
ff += get_friction(desired_lateral_accel - actual_lateral_accel, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params)
# TODO jerk is weighted by lat_delay for legacy reasons, but should be made independent of it
ff += get_friction(error, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params)
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5
output_lataccel = self.pid.update(pid_log.error,
feedforward=ff,
speed=CS.vEgo,
freeze_integrator=freeze_integrator)
-measurement_rate,
feedforward=ff,
speed=CS.vEgo,
freeze_integrator=freeze_integrator)
output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params)
# Lateral acceleration torque controller extension updates
# Overrides pid_log.error and output_torque
pid_log, output_torque = self.extension.update(CS, VM, self.pid, params, ff, pid_log, setpoint, measurement, calibrated_pose, roll_compensation,
desired_lateral_accel, actual_lateral_accel, lateral_accel_deadzone, gravity_adjusted_lateral_accel,
desired_curvature, actual_curvature, steer_limited_by_safety, output_torque)
future_desired_lateral_accel, measurement, lateral_accel_deadzone, gravity_adjusted_future_lateral_accel,
desired_curvature, measured_curvature, steer_limited_by_safety, output_torque)
pid_log.active = True
pid_log.p = float(self.pid.p)
pid_log.i = float(self.pid.i)
pid_log.d = float(self.pid.d)
pid_log.f = float(self.pid.f)
pid_log.output = float(-output_torque) # TODO: log lat accel?
pid_log.actualLateralAccel = float(actual_lateral_accel)
pid_log.desiredLateralAccel = float(desired_lateral_accel)
pid_log.output = float(-output_torque) # TODO: log lat accel?
pid_log.actualLateralAccel = float(measurement)
pid_log.desiredLateralAccel = float(setpoint)
pid_log.desiredLateralJerk = float(desired_lateral_jerk)
pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited))
# TODO left is positive in this convention
+1 -1
View File
@@ -54,7 +54,7 @@ class LongControl:
self.long_control_state = LongCtrlState.off
self.pid = PIDController((CP.longitudinalTuning.kpBP, CP.longitudinalTuning.kpV),
(CP.longitudinalTuning.kiBP, CP.longitudinalTuning.kiV),
k_f=CP.longitudinalTuning.kf, rate=1 / DT_CTRL)
rate=1 / DT_CTRL)
self.last_output_accel = 0.0
def reset(self):
+5 -4
View File
@@ -7,6 +7,7 @@ from opendbc.car.toyota.values import CAR as TOYOTA
from opendbc.car.nissan.values import CAR as NISSAN
from opendbc.car.gm.values import CAR as GM
from opendbc.car.vehicle_model import VehicleModel
from openpilot.common.realtime import DT_CTRL
from openpilot.selfdrive.car.helpers import convert_to_capnp
from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID
from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
@@ -29,7 +30,7 @@ class TestLatControl:
CP_SP = convert_to_capnp(CP_SP)
VM = VehicleModel(CP)
controller = controller(CP.as_reader(), CP_SP.as_reader(), CI)
controller = controller(CP.as_reader(), CP_SP.as_reader(), CI, DT_CTRL)
CS = car.CarState.new_message()
CS.vEgo = 30
@@ -42,13 +43,13 @@ class TestLatControl:
# Saturate for curvature limited and controller limited
for _ in range(1000):
_, _, lac_log = controller.update(True, CS, VM, params, False, 0, pose, True)
_, _, lac_log = controller.update(True, CS, VM, params, False, 0, pose, True, 0.2)
assert lac_log.saturated
for _ in range(1000):
_, _, lac_log = controller.update(True, CS, VM, params, False, 0, pose, False)
_, _, lac_log = controller.update(True, CS, VM, params, False, 0, pose, False, 0.2)
assert not lac_log.saturated
for _ in range(1000):
_, _, lac_log = controller.update(True, CS, VM, params, False, 1, pose, False)
_, _, lac_log = controller.update(True, CS, VM, params, False, 1, pose, False, 0.2)
assert lac_log.saturated
+1 -1
View File
@@ -6,7 +6,7 @@ from collections import defaultdict
import matplotlib.pyplot as plt
from cereal.services import SERVICE_LIST
from openpilot.common.file_helpers import LOG_COMPRESSION_LEVEL
from openpilot.common.utils import LOG_COMPRESSION_LEVEL
from openpilot.tools.lib.logreader import LogReader
from tqdm import tqdm
+4 -1
View File
@@ -1,4 +1,5 @@
#!/usr/bin/env python3
import os
import numpy as np
from collections import deque, defaultdict
@@ -250,6 +251,8 @@ class TorqueEstimator(ParameterEstimator, TorqueEstimatorExt):
def main(demo=False):
config_realtime_process([0, 1, 2, 3], 5)
DEBUG = bool(int(os.getenv("DEBUG", "0")))
pm = messaging.PubMaster(['liveTorqueParameters'])
sm = messaging.SubMaster(['carControl', 'carOutput', 'carState', 'liveCalibration', 'livePose', 'liveDelay'], poll='livePose')
@@ -268,7 +271,7 @@ def main(demo=False):
# 4Hz driven by livePose
if sm.frame % 5 == 0:
pm.send('liveTorqueParameters', estimator.get_msg(valid=sm.all_checks()))
pm.send('liveTorqueParameters', estimator.get_msg(valid=sm.all_checks(), with_points=DEBUG))
# Cache points every 60 seconds while onroad
if sm.frame % 240 == 0:
+2 -2
View File
@@ -1,11 +1,11 @@
import os
import glob
Import('env', 'envCython', 'arch', 'cereal', 'messaging', 'common', 'gpucommon', 'visionipc', 'transformations')
Import('env', 'envCython', 'arch', 'cereal', 'messaging', 'common', 'visionipc', 'transformations')
lenv = env.Clone()
lenvCython = envCython.Clone()
libs = [cereal, messaging, visionipc, gpucommon, common, 'capnp', 'kj', 'pthread']
libs = [cereal, messaging, visionipc, common, 'capnp', 'kj', 'pthread']
frameworks = []
common_src = [
+4 -8
View File
@@ -25,13 +25,13 @@ from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from
MODEL_WIDTH, MODEL_HEIGHT = DM_INPUT_SIZE
CALIB_LEN = 3
FEATURE_LEN = 512
OUTPUT_SIZE = 84 + FEATURE_LEN
OUTPUT_SIZE = 83 + FEATURE_LEN
PROCESS_NAME = "selfdrive.modeld.dmonitoringmodeld"
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
MODEL_PKL_PATH = Path(__file__).parent / 'models/dmonitoring_model_tinygrad.pkl'
# TODO: slice from meta
class DriverStateResult(ctypes.Structure):
_fields_ = [
("face_orientation", ctypes.c_float*3),
@@ -46,8 +46,8 @@ class DriverStateResult(ctypes.Structure):
("left_blink_prob", ctypes.c_float),
("right_blink_prob", ctypes.c_float),
("sunglasses_prob", ctypes.c_float),
("occluded_prob", ctypes.c_float),
("ready_prob", ctypes.c_float*4),
("_unused_c", ctypes.c_float),
("_unused_d", ctypes.c_float*4),
("not_ready_prob", ctypes.c_float*2)]
@@ -55,7 +55,6 @@ class DMonitoringModelResult(ctypes.Structure):
_fields_ = [
("driver_state_lhd", DriverStateResult),
("driver_state_rhd", DriverStateResult),
("poor_vision_prob", ctypes.c_float),
("wheel_on_right_prob", ctypes.c_float),
("features", ctypes.c_float*FEATURE_LEN)]
@@ -107,8 +106,6 @@ def fill_driver_state(msg, ds_result: DriverStateResult):
msg.leftBlinkProb = float(sigmoid(ds_result.left_blink_prob))
msg.rightBlinkProb = float(sigmoid(ds_result.right_blink_prob))
msg.sunglassesProb = float(sigmoid(ds_result.sunglasses_prob))
msg.occludedProb = float(sigmoid(ds_result.occluded_prob))
msg.readyProb = [float(sigmoid(x)) for x in ds_result.ready_prob]
msg.notReadyProb = [float(sigmoid(x)) for x in ds_result.not_ready_prob]
@@ -119,7 +116,6 @@ def get_driverstate_packet(model_output: np.ndarray, frame_id: int, location_ts:
ds.frameId = frame_id
ds.modelExecutionTime = execution_time
ds.gpuExecutionTime = gpu_execution_time
ds.poorVisionProb = float(sigmoid(model_result.poor_vision_prob))
ds.wheelOnRightProb = float(sigmoid(model_result.wheel_on_right_prob))
ds.rawPredictions = model_output.tobytes() if SEND_RAW_PRED else b''
fill_driver_state(ds.leftDriverData, model_result.driver_state_lhd)
+1 -2
View File
@@ -62,6 +62,5 @@ Refer to **slice_outputs** and **parse_vision_outputs/parse_policy_outputs** in
* (deprecated) distracted probabilities: 2
* using phone probability: 1
* distracted probability: 1
* common outputs 2
* poor camera vision probability: 1
* common outputs 1
* left hand drive probability: 1
@@ -1,2 +0,0 @@
fa69be01-b430-4504-9d72-7dcb058eb6dd
d9fb22d1c4fa3ca3d201dbc8edf1d0f0918e53e6
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:50efe6451a3fb3fa04b6bb0e846544533329bd46ecefe9e657e91214dee2aaeb
size 7196502
oid sha256:3a53626ab84757813fb16a1441704f2ae7192bef88c331bdc2415be6981d204f
size 7191776
+2 -2
View File
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ebb38a934d6472c061cc6010f46d9720ca132d631a47e585a893bdd41ade2419
size 12343535
oid sha256:c5a1f0655ddf266ed42ad1980389d96f47cc5e756da1fa3ca1477a920bb9b157
size 13926324
+2 -2
View File
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:befac016a247b7ad5dc5b55d339d127774ed7bd2b848f1583f72aa4caee37781
size 46271991
oid sha256:8f16d548ea4eb5d01518a9e90d4527cd97c31a84bcaf6f695dead8f0015fecc4
size 46271942
+31 -12
View File
@@ -4,11 +4,13 @@ import numpy as np
from cereal import car, log
import cereal.messaging as messaging
from openpilot.selfdrive.selfdrived.events import Events
from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert
from openpilot.common.realtime import DT_DMON
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.params import Params
from openpilot.common.stat_live import RunningStatFilter
from openpilot.common.transformations.camera import DEVICE_CAMERAS
from openpilot.system.hardware import HARDWARE
EventName = log.OnroadEvent.EventName
@@ -34,12 +36,13 @@ class DRIVER_MONITOR_SETTINGS:
self._SG_THRESHOLD = 0.9
self._BLINK_THRESHOLD = 0.865
self._EE_THRESH11 = 0.4
if HARDWARE.get_device_type() == 'mici':
self._EE_THRESH11 = 0.75
else:
self._EE_THRESH11 = 0.4
self._EE_THRESH12 = 15.0
self._EE_MAX_OFFSET1 = 0.06
self._EE_MIN_OFFSET1 = 0.025
self._EE_THRESH21 = 0.01
self._EE_THRESH22 = 0.35
self._POSE_PITCH_THRESHOLD = 0.3133
self._POSE_PITCH_THRESHOLD_SLACK = 0.3237
@@ -55,6 +58,9 @@ class DRIVER_MONITOR_SETTINGS:
self._YAW_MAX_OFFSET = 0.289
self._YAW_MIN_OFFSET = -0.0246
self._DCAM_UNCERTAIN_ALERT_THRESHOLD = 0.1
self._DCAM_UNCERTAIN_ALERT_COUNT = int(60 / self._DT_DMON)
self._DCAM_UNCERTAIN_RESET_COUNT = int(20 / self._DT_DMON)
self._POSESTD_THRESHOLD = 0.3
self._HI_STD_FALLBACK_TIME = int(10 / self._DT_DMON) # fall back to wheel touch if model is uncertain for 10s
self._DISTRACTED_FILTER_TS = 0.25 # 0.6Hz
@@ -137,11 +143,8 @@ class DriverMonitoring:
self.pose = DriverPose(self.settings._POSE_OFFSET_MAX_COUNT)
self.blink = DriverBlink()
self.eev1 = 0.
self.eev2 = 1.
self.ee1_offseter = RunningStatFilter(max_trackable=self.settings._POSE_OFFSET_MAX_COUNT)
self.ee2_offseter = RunningStatFilter(max_trackable=self.settings._POSE_OFFSET_MAX_COUNT)
self.ee1_calibrated = False
self.ee2_calibrated = False
self.always_on = always_on
self.distracted_types = []
@@ -159,6 +162,9 @@ class DriverMonitoring:
self.hi_stds = 0
self.threshold_pre = self.settings._DISTRACTED_PRE_TIME_TILL_TERMINAL / self.settings._DISTRACTED_TIME
self.threshold_prompt = self.settings._DISTRACTED_PROMPT_TIME_TILL_TERMINAL / self.settings._DISTRACTED_TIME
self.dcam_uncertain_cnt = 0
self.dcam_uncertain_alerted = False # once per drive
self.dcam_reset_cnt = 0
self.params = Params()
self.too_distracted = self.params.get_bool("DriverTooDistracted")
@@ -246,7 +252,7 @@ class DriverMonitoring:
return distracted_types
def _update_states(self, driver_state, cal_rpy, car_speed, op_engaged):
def _update_states(self, driver_state, cal_rpy, car_speed, op_engaged, standstill):
rhd_pred = driver_state.wheelOnRightProb
# calibrates only when there's movement and either face detected
if car_speed > self.settings._WHEELPOS_CALIB_MIN_SPEED and (driver_state.leftDriverData.faceProb > self.settings._FACE_THRESHOLD or
@@ -262,7 +268,7 @@ class DriverMonitoring:
driver_data = driver_state.rightDriverData if self.wheel_on_right else driver_state.leftDriverData
if not all(len(x) > 0 for x in (driver_data.faceOrientation, driver_data.facePosition,
driver_data.faceOrientationStd, driver_data.facePositionStd,
driver_data.readyProb, driver_data.notReadyProb)):
driver_data.notReadyProb)):
return
self.face_detected = driver_data.faceProb > self.settings._FACE_THRESHOLD
@@ -279,7 +285,6 @@ class DriverMonitoring:
self.blink.right = driver_data.rightBlinkProb * (driver_data.rightEyeProb > self.settings._EYE_THRESHOLD) \
* (driver_data.sunglassesProb < self.settings._SG_THRESHOLD)
self.eev1 = driver_data.notReadyProb[0]
self.eev2 = driver_data.readyProb[0]
self.distracted_types = self._get_distracted_types()
self.driver_distracted = (DistractedType.DISTRACTED_E2E in self.distracted_types or DistractedType.DISTRACTED_POSE in self.distracted_types
@@ -293,12 +298,20 @@ class DriverMonitoring:
self.pose.pitch_offseter.push_and_update(self.pose.pitch)
self.pose.yaw_offseter.push_and_update(self.pose.yaw)
self.ee1_offseter.push_and_update(self.eev1)
self.ee2_offseter.push_and_update(self.eev2)
self.pose.calibrated = self.pose.pitch_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT and \
self.pose.yaw_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT
self.ee1_calibrated = self.ee1_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT
self.ee2_calibrated = self.ee2_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT
if self.face_detected and not self.driver_distracted:
if model_std_max > self.settings._DCAM_UNCERTAIN_ALERT_THRESHOLD:
if not standstill:
self.dcam_uncertain_cnt += 1
self.dcam_reset_cnt = 0
else:
self.dcam_reset_cnt += 1
if self.dcam_reset_cnt > self.settings._DCAM_UNCERTAIN_RESET_COUNT:
self.dcam_uncertain_cnt = 0
self.is_model_uncertain = self.hi_stds > self.settings._HI_STD_FALLBACK_TIME
self._set_timers(self.face_detected and not self.is_model_uncertain)
@@ -376,6 +389,10 @@ class DriverMonitoring:
if alert is not None:
self.current_events.add(alert)
if self.dcam_uncertain_cnt > self.settings._DCAM_UNCERTAIN_ALERT_COUNT and not self.dcam_uncertain_alerted:
set_offroad_alert("Offroad_DriverMonitoringUncertain", True)
self.dcam_uncertain_alerted = True
def get_state_packet(self, valid=True):
# build driverMonitoringState packet
@@ -397,6 +414,7 @@ class DriverMonitoring:
"hiStdCount": self.hi_stds,
"isActiveMode": self.active_monitoring_mode,
"isRHD": self.wheel_on_right,
"uncertainCount": self.dcam_uncertain_cnt,
}
return dat
@@ -412,7 +430,8 @@ class DriverMonitoring:
driver_state=sm['driverStateV2'],
cal_rpy=sm['liveCalibration'].rpyCalib,
car_speed=sm['carState'].vEgo,
op_engaged=sm['selfdriveState'].enabled or sm['carControl'].latActive
op_engaged=sm['selfdriveState'].enabled or sm['carControl'].latActive,
standstill=sm['carState'].standstill,
)
# Update distraction events
+1 -2
View File
@@ -25,7 +25,6 @@ def make_msg(face_detected, distracted=False, model_uncertain=False):
ds.leftDriverData.faceOrientationStd = [1.*model_uncertain, 1.*model_uncertain, 1.*model_uncertain]
ds.leftDriverData.facePositionStd = [1.*model_uncertain, 1.*model_uncertain]
# TODO: test both separately when e2e is used
ds.leftDriverData.readyProb = [0., 0., 0., 0.]
ds.leftDriverData.notReadyProb = [0., 0.]
return ds
@@ -54,7 +53,7 @@ class TestMonitoring:
DM = DriverMonitoring()
events = []
for idx in range(len(msgs)):
DM._update_states(msgs[idx], [0, 0, 0], 0, engaged[idx])
DM._update_states(msgs[idx], [0, 0, 0], 0, engaged[idx], standstill[idx])
# cal_rpy and car_speed don't matter here
# evaluate events at 10Hz for tests
+1 -1
View File
@@ -80,7 +80,7 @@ Panda *connect(std::string serial="", uint32_t index=0) {
}
//panda->enable_deepsleep();
for (int i = 0; i < PANDA_BUS_CNT; i++) {
for (int i = 0; i < PANDA_CAN_CNT; i++) {
panda->set_can_fd_auto(i, true);
}
+8 -19
View File
@@ -5,8 +5,7 @@ import time
import cereal.messaging as messaging
from cereal import log
from openpilot.common.gpio import gpio_set, gpio_init
from panda import Panda, PandaDFU, PandaProtocolMismatch
from openpilot.common.retry import retry
from panda import Panda, PandaDFU
from openpilot.system.manager.process_config import managed_processes
from openpilot.system.hardware import HARDWARE
from openpilot.system.hardware.tici.pins import GPIO
@@ -50,8 +49,7 @@ class TestPandad:
assert not Panda.wait_for_dfu(None, 3)
assert not Panda.wait_for_panda(None, 3)
@retry(attempts=3)
def _flash_bootstub_and_test(self, fn, expect_mismatch=False):
def _flash_bootstub(self, fn):
self._go_to_dfu()
pd = PandaDFU(None)
if fn is None:
@@ -61,16 +59,6 @@ class TestPandad:
pd.reset()
HARDWARE.reset_internal_panda()
assert Panda.wait_for_panda(None, 10)
if expect_mismatch:
with pytest.raises(PandaProtocolMismatch):
Panda()
else:
with Panda() as p:
assert p.bootstub
self._run_test(45)
def test_in_dfu(self):
HARDWARE.recover_internal_panda()
self._run_test(60)
@@ -106,13 +94,14 @@ class TestPandad:
print("startup times", ts, sum(ts) / len(ts))
assert 0.1 < (sum(ts)/len(ts)) < 0.7
def test_protocol_version_check(self):
# flash old fw
fn = os.path.join(HERE, "bootstub.panda_h7_spiv0.bin")
self._flash_bootstub_and_test(fn, expect_mismatch=True)
def test_old_spi_protocol(self):
# flash firmware with old SPI protocol
self._flash_bootstub(os.path.join(HERE, "bootstub.panda_h7_spiv0.bin"))
self._run_test(45)
def test_release_to_devel_bootstub(self):
self._flash_bootstub_and_test(None)
self._flash_bootstub(None)
self._run_test(45)
def test_recover_from_bad_bootstub(self):
self._go_to_dfu()
@@ -9,7 +9,7 @@ from pprint import pprint
import cereal.messaging as messaging
from cereal import car, log
from opendbc.car.can_definitions import CanData
from openpilot.common.retry import retry
from openpilot.common.utils import retry
from openpilot.common.params import Params
from openpilot.common.timeout import Timeout
from openpilot.selfdrive.pandad import can_list_to_can_capnp
+4
View File
@@ -41,6 +41,10 @@
"text": "OpenStreetMap database is out of date. New maps must be downloaded if you wish to continue using OpenStreetMap data for Enhanced Speed Control and road name display.\n\n%1",
"severity": 0
},
"Offroad_DriverMonitoringUncertain": {
"text": "openpilot detected poor visibility for driver monitoring. Ensure the device has a clear view of the driver. This can be checked using Settings -> Device -> Driver Camera Preview. Extreme lighting conditions and/or unconventional mounting positions may also trigger this alert.",
"severity": 0
},
"Offroad_ExcessiveActuation": {
"text": "openpilot detected excessive %1 actuation on your last drive. Please contact support at https://comma.ai/support and share your device's Dongle ID for troubleshooting.",
"severity": 1,
+3 -3
View File
@@ -80,7 +80,7 @@ def below_engage_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.
def below_steer_speed_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert:
return Alert(
f"Steer Unavailable Below {get_display_speed(CP.minSteerSpeed, metric)}",
f"Steer Assist Unavailable Below {get_display_speed(CP.minSteerSpeed, metric)}",
"",
AlertStatus.userPrompt, AlertSize.small,
Priority.LOW, VisualAlert.none, AudibleAlert.prompt, 0.4)
@@ -322,7 +322,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
EventName.steerTempUnavailableSilent: {
ET.WARNING: Alert(
"Steering Temporarily Unavailable",
"Steering Assist Temporarily Unavailable",
"",
AlertStatus.userPrompt, AlertSize.small,
Priority.LOW, VisualAlert.steerRequired, AudibleAlert.prompt, 1.8),
@@ -568,7 +568,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
},
EventName.steerTempUnavailable: {
ET.SOFT_DISABLE: soft_disable_alert("Steering Temporarily Unavailable"),
ET.SOFT_DISABLE: soft_disable_alert("Steering Assist Temporarily Unavailable"),
ET.NO_ENTRY: NoEntryAlert("Steering Temporarily Unavailable"),
},
+1 -1
View File
@@ -1 +1 @@
afcab1abb62b9d5678342956cced4712f44e909e
b508f43fb0481bce0859c9b6ab4f45ee690b8dab
+1 -1
View File
@@ -42,6 +42,7 @@ sudo systemctl restart NetworkManager
sudo systemctl disable ssh-param-watcher.path
sudo systemctl disable ssh-param-watcher.service
sudo mount -o ro,remount /
sudo systemctl stop power_monitor
while true; do
if ! sudo systemctl is-active -q ssh; then
@@ -54,7 +55,6 @@ while true; do
# /data/ciui.py &
#fi
awk '{print \$1}' /proc/uptime > /var/tmp/power_watchdog
sleep 5s
done
+6 -5
View File
@@ -32,7 +32,7 @@ CPU usage budget
TEST_DURATION = 25
LOG_OFFSET = 8
MAX_TOTAL_CPU = 300. # total for all 8 cores
MAX_TOTAL_CPU = 350. # total for all 8 cores
PROCS = {
# Baseline CPU usage by process
"selfdrive.controls.controlsd": 16.0,
@@ -42,7 +42,7 @@ PROCS = {
"./encoderd": 13.0,
"./camerad": 10.0,
"selfdrive.controls.plannerd": 8.0,
"./ui": 18.0,
"selfdrive.ui.ui": 40.0,
"system.sensord.sensord": 13.0,
"selfdrive.controls.radard": 2.0,
"selfdrive.modeld.modeld": 22.0,
@@ -206,7 +206,8 @@ class TestOnroad:
result += "-------------- UI Draw Timing ------------------\n"
result += "------------------------------------------------\n"
ts = self.ts['uiDebug']['drawTimeMillis']
# skip first few frames -- connecting to vipc
ts = self.ts['uiDebug']['drawTimeMillis'][15:]
result += f"min {min(ts):.2f}ms\n"
result += f"max {max(ts):.2f}ms\n"
result += f"std {np.std(ts):.2f}ms\n"
@@ -215,7 +216,7 @@ class TestOnroad:
print(result)
assert max(ts) < 250.
assert np.mean(ts) < 10.
assert np.mean(ts) < 20. # TODO: ~6-11ms, increase consistency
#self.assertLess(np.std(ts), 5.)
# some slow frames are expected since camerad/modeld can preempt ui
@@ -285,7 +286,7 @@ class TestOnroad:
# check for big leaks. note that memory usage is
# expected to go up while the MSGQ buffers fill up
assert np.average(mems) <= 65, "Average memory usage above 65%"
assert np.average(mems) <= 85, "Average memory usage above 85%"
assert np.max(np.diff(mems)) <= 4, "Max memory increase too high"
assert np.average(np.diff(mems)) <= 1, "Average memory increase too high"
-13
View File
@@ -1,14 +1 @@
moc_*
*.moc
translations/main_test_en.*
ui
mui
watch3
installer/installers/*
qt/setup/setup
qt/setup/reset
qt/setup/wifi
qt/setup/updater
translations/alerts_generated.h
+27 -65
View File
@@ -1,75 +1,37 @@
import os
import re
import json
Import('env', 'qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations')
from pathlib import Path
Import('env', 'arch', 'common')
base_libs = [common, messaging, visionipc, transformations,
'm', 'OpenCL', 'ssl', 'crypto', 'pthread'] + qt_env["LIBS"]
# build the fonts
generator = File("#selfdrive/assets/fonts/process.py")
source_files = Glob("#selfdrive/assets/fonts/*.ttf") + Glob("#selfdrive/assets/fonts/*.otf")
output_files = [
(f.abspath.split('.')[0] + ".fnt", f.abspath.split('.')[0] + ".png")
for f in source_files
if "NotoColor" not in f.name
]
env.Command(
target=output_files,
source=[generator, source_files],
action=f"python3 {generator}",
)
if arch == 'larch64':
base_libs.append('EGL')
if arch == "Darwin":
del base_libs[base_libs.index('OpenCL')]
qt_env['FRAMEWORKS'] += ['OpenCL']
sp_widgets_src = []
sp_qt_src = []
sp_qt_util = []
if not GetOption('stock_ui'):
SConscript(['sunnypilot/SConscript'])
Import('sp_widgets_src', 'sp_qt_src', 'sp_qt_util')
# FIXME: remove this once we're on 5.15 (24.04)
qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"]
qt_util = qt_env.Library("qt_util", ["#selfdrive/ui/qt/api.cc", "#selfdrive/ui/qt/util.cc"] + sp_qt_util, LIBS=base_libs)
widgets_src = ["qt/widgets/input.cc", "qt/widgets/wifi.cc", "qt/prime_state.cc",
"qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc",
"qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc",
"qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc",
"qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"] + sp_widgets_src
widgets = qt_env.Library("qt_widgets", widgets_src, LIBS=base_libs)
Export('widgets')
qt_libs = [widgets, qt_util] + base_libs
qt_src = ["main.cc", "ui.cc", "qt/sidebar.cc", "qt/body.cc",
"qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc", "qt/offroad/offroad_home.cc",
"qt/offroad/software_settings.cc", "qt/offroad/developer_panel.cc", "qt/offroad/onboarding.cc",
"qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc", "qt/offroad/firehose.cc",
"qt/onroad/onroad_home.cc", "qt/onroad/annotated_camera.cc", "qt/onroad/model.cc",
"qt/onroad/buttons.cc", "qt/onroad/alerts.cc", "qt/onroad/driver_monitoring.cc", "qt/onroad/hud.cc"] + sp_qt_src
# build translation files
# compile gettext .po -> .mo translations
with open(File("translations/languages.json").abspath) as f:
languages = json.loads(f.read())
translation_sources = [f"#selfdrive/ui/translations/{l}.ts" for l in languages.values()]
translation_targets = [src.replace(".ts", ".qm") for src in translation_sources]
lrelease_bin = 'third_party/qt5/larch64/bin/lrelease' if arch == 'larch64' else 'lrelease'
lrelease = qt_env.Command(translation_targets, translation_sources, f"{lrelease_bin} $SOURCES")
qt_env.NoClean(translation_sources)
qt_env.Precious(translation_sources)
po_sources = [f"#selfdrive/ui/translations/app_{l}.po" for l in languages.values()]
po_sources = [src for src in po_sources if os.path.exists(File(src).abspath)]
mo_targets = [src.replace(".po", ".mo") for src in po_sources]
mo_build = []
for src, tgt in zip(po_sources, mo_targets):
mo_build.append(env.Command(tgt, src, "msgfmt -o $TARGET $SOURCE"))
mo_alias = env.Alias('mo', mo_build)
env.AlwaysBuild(mo_alias)
# create qrc file for compiled translations to include with assets
translations_assets_src = "#selfdrive/assets/translations_assets.qrc"
with open(File(translations_assets_src).abspath, 'w') as f:
f.write('<!DOCTYPE RCC><RCC version="1.0">\n<qresource>\n')
f.write('\n'.join([f'<file alias="{l}">../ui/translations/{l}.qm</file>' for l in languages.values()]))
f.write('\n</qresource>\n</RCC>')
# build assets
assets = "#selfdrive/assets/assets.cc"
assets_src = "#selfdrive/assets/assets.qrc"
qt_env.Command(assets, [assets_src, translations_assets_src], f"rcc $SOURCES -o $TARGET")
qt_env.Depends(assets, Glob('#selfdrive/assets/*', exclude=[assets, assets_src, translations_assets_src, "#selfdrive/assets/assets.o"]) + [lrelease])
asset_obj = qt_env.Object("assets", assets)
# build main UI
qt_env.Program("ui", qt_src + [asset_obj], LIBS=qt_libs)
if GetOption('extras'):
qt_src.remove("main.cc") # replaced by test_runner
qt_env.Program('tests/test_translations', [asset_obj, 'tests/test_runner.cc', 'tests/test_translations.cc'] + qt_src, LIBS=qt_libs)
# build installers
if arch != "Darwin":
raylib_env = env.Clone()
@@ -78,7 +40,7 @@ if GetOption('extras'):
raylib_libs = common + ["raylib"]
if arch == "larch64":
raylib_libs += ["GLESv2", "wayland-client", "wayland-egl", "EGL"]
raylib_libs += ["GLESv2", "EGL", "gbm", "drm"]
else:
raylib_libs += ["GL"]
+1
View File
@@ -0,0 +1 @@
UI_BORDER_SIZE = 30
+29 -5
View File
@@ -5,6 +5,7 @@
#include "common/swaglog.h"
#include "common/util.h"
#include "system/hardware/hw.h"
#include "third_party/raylib/include/raylib.h"
int freshClone();
@@ -38,6 +39,27 @@ extern const uint8_t inter_ttf_end[] asm("_binary_selfdrive_ui_installer_inter_a
Font font;
std::vector<std::string> tici_prebuilt_branches = {"release3", "release-tizi", "release3-staging", "nightly", "nightly-dev"};
std::string migrated_branch;
void branchMigration() {
migrated_branch = BRANCH_STR;
cereal::InitData::DeviceType device_type = Hardware::get_device_type();
if (device_type == cereal::InitData::DeviceType::TICI) {
if (std::find(tici_prebuilt_branches.begin(), tici_prebuilt_branches.end(), BRANCH_STR) != tici_prebuilt_branches.end()) {
migrated_branch = "release-tici";
} else if (BRANCH_STR == "master") {
migrated_branch = "master-tici";
}
} else if (device_type == cereal::InitData::DeviceType::TIZI) {
if (BRANCH_STR == "release3") {
migrated_branch = "release-tizi";
} else if (BRANCH_STR == "release3-staging") {
migrated_branch = "release-tizi-staging";
}
}
}
void run(const char* cmd) {
int err = std::system(cmd);
assert(err == 0);
@@ -87,7 +109,7 @@ int doInstall() {
int freshClone() {
LOGD("Doing fresh clone");
std::string cmd = util::string_format("git clone --progress %s -b %s --depth=1 --recurse-submodules %s 2>&1",
GIT_URL.c_str(), BRANCH_STR.c_str(), TMP_INSTALL_PATH);
GIT_URL.c_str(), migrated_branch.c_str(), TMP_INSTALL_PATH);
return executeGitCommand(cmd);
}
@@ -95,11 +117,11 @@ int cachedFetch(const std::string &cache) {
LOGD("Fetching with cache: %s", cache.c_str());
run(util::string_format("cp -rp %s %s", cache.c_str(), TMP_INSTALL_PATH).c_str());
run(util::string_format("cd %s && git remote set-branches --add origin %s", TMP_INSTALL_PATH, BRANCH_STR.c_str()).c_str());
run(util::string_format("cd %s && git remote set-branches --add origin %s", TMP_INSTALL_PATH, migrated_branch.c_str()).c_str());
renderProgress(10);
return executeGitCommand(util::string_format("cd %s && git fetch --progress origin %s 2>&1", TMP_INSTALL_PATH, BRANCH_STR.c_str()));
return executeGitCommand(util::string_format("cd %s && git fetch --progress origin %s 2>&1", TMP_INSTALL_PATH, migrated_branch.c_str()));
}
int executeGitCommand(const std::string &cmd) {
@@ -142,8 +164,8 @@ void cloneFinished(int exitCode) {
// ensure correct branch is checked out
int err = chdir(TMP_INSTALL_PATH);
assert(err == 0);
run(("git checkout " + BRANCH_STR).c_str());
run(("git reset --hard origin/" + BRANCH_STR).c_str());
run(("git checkout " + migrated_branch).c_str());
run(("git reset --hard origin/" + migrated_branch).c_str());
run("git submodule update --init");
// move into place
@@ -193,6 +215,8 @@ int main(int argc, char *argv[]) {
font = LoadFontFromMemory(".ttf", inter_ttf, inter_ttf_end - inter_ttf, FONT_SIZE, NULL, 0);
SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR);
branchMigration();
if (util::file_exists(CONTINUE_PATH)) {
finishInstall();
} else {
+64 -44
View File
@@ -8,7 +8,9 @@ from openpilot.selfdrive.ui.widgets.exp_mode_button import ExperimentalModeButto
from openpilot.selfdrive.ui.widgets.prime import PrimeWidget
from openpilot.selfdrive.ui.widgets.setup import SetupWidget
from openpilot.system.ui.lib.text_measure import measure_text_cached
from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_TEXT_COLOR
from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos
from openpilot.system.ui.lib.multilang import tr, trn
from openpilot.system.ui.widgets.label import gui_label
from openpilot.system.ui.widgets import Widget
HEADER_HEIGHT = 80
@@ -35,12 +37,17 @@ class HomeLayout(Widget):
self.update_alert = UpdateAlert()
self.offroad_alert = OffroadAlert()
self._layout_widgets = {HomeLayoutState.UPDATE: self.update_alert, HomeLayoutState.ALERTS: self.offroad_alert}
self.current_state = HomeLayoutState.HOME
self.last_refresh = 0
self.settings_callback: callable | None = None
self.update_available = False
self.alert_count = 0
self._version_text = ""
self._prev_update_available = False
self._prev_alerts_present = False
self.header_rect = rl.Rectangle(0, 0, 0, 0)
self.content_rect = rl.Rectangle(0, 0, 0, 0)
@@ -56,14 +63,30 @@ class HomeLayout(Widget):
self._exp_mode_button = ExperimentalModeButton()
self._setup_callbacks()
def show_event(self):
self._exp_mode_button.show_event()
self.last_refresh = time.monotonic()
self._refresh()
def _setup_callbacks(self):
self.update_alert.set_dismiss_callback(lambda: self._set_state(HomeLayoutState.HOME))
self.offroad_alert.set_dismiss_callback(lambda: self._set_state(HomeLayoutState.HOME))
self._exp_mode_button.set_click_callback(lambda: self.settings_callback() if self.settings_callback else None)
def set_settings_callback(self, callback: Callable):
self.settings_callback = callback
def _set_state(self, state: HomeLayoutState):
# propagate show/hide events
if state != self.current_state:
if state == HomeLayoutState.HOME:
self._exp_mode_button.show_event()
if state in self._layout_widgets:
self._layout_widgets[state].show_event()
if self.current_state in self._layout_widgets:
self._layout_widgets[self.current_state].hide_event()
self.current_state = state
def _render(self, rect: rl.Rectangle):
@@ -72,7 +95,6 @@ class HomeLayout(Widget):
self._refresh()
self.last_refresh = current_time
self._handle_input()
self._render_header()
# Render content based on current state
@@ -83,7 +105,7 @@ class HomeLayout(Widget):
elif self.current_state == HomeLayoutState.ALERTS:
self._render_alerts_view()
def _update_layout_rects(self):
def _update_state(self):
self.header_rect = rl.Rectangle(
self._rect.x + CONTENT_MARGIN, self._rect.y + CONTENT_MARGIN, self._rect.width - 2 * CONTENT_MARGIN, HEADER_HEIGHT
)
@@ -110,59 +132,54 @@ class HomeLayout(Widget):
self.alert_notif_rect.x = notif_x
self.alert_notif_rect.y = self.header_rect.y + (self.header_rect.height - 60) // 2
def _handle_input(self):
if not rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT):
return
mouse_pos = rl.get_mouse_position()
def _handle_mouse_release(self, mouse_pos: MousePos):
super()._handle_mouse_release(mouse_pos)
if self.update_available and rl.check_collision_point_rec(mouse_pos, self.update_notif_rect):
self._set_state(HomeLayoutState.UPDATE)
return
if self.alert_count > 0 and rl.check_collision_point_rec(mouse_pos, self.alert_notif_rect):
elif self.alert_count > 0 and rl.check_collision_point_rec(mouse_pos, self.alert_notif_rect):
self._set_state(HomeLayoutState.ALERTS)
return
# Content area input handling
if self.current_state == HomeLayoutState.UPDATE:
self.update_alert.handle_input(mouse_pos, True)
elif self.current_state == HomeLayoutState.ALERTS:
self.offroad_alert.handle_input(mouse_pos, True)
def _render_header(self):
font = gui_app.font(FontWeight.MEDIUM)
version_text_width = self.header_rect.width
# Update notification button
if self.update_available:
version_text_width -= self.update_notif_rect.width
# Highlight if currently viewing updates
highlight_color = rl.Color(255, 140, 40, 255) if self.current_state == HomeLayoutState.UPDATE else rl.Color(255, 102, 0, 255)
highlight_color = rl.Color(75, 95, 255, 255) if self.current_state == HomeLayoutState.UPDATE else rl.Color(54, 77, 239, 255)
rl.draw_rectangle_rounded(self.update_notif_rect, 0.3, 10, highlight_color)
text = "UPDATE"
text_width = measure_text_cached(font, text, HEAD_BUTTON_FONT_SIZE).x
text_x = self.update_notif_rect.x + (self.update_notif_rect.width - text_width) // 2
text_y = self.update_notif_rect.y + (self.update_notif_rect.height - HEAD_BUTTON_FONT_SIZE) // 2
text = tr("UPDATE")
text_size = measure_text_cached(font, text, HEAD_BUTTON_FONT_SIZE)
text_x = self.update_notif_rect.x + (self.update_notif_rect.width - text_size.x) // 2
text_y = self.update_notif_rect.y + (self.update_notif_rect.height - text_size.y) // 2
rl.draw_text_ex(font, text, rl.Vector2(int(text_x), int(text_y)), HEAD_BUTTON_FONT_SIZE, 0, rl.WHITE)
# Alert notification button
if self.alert_count > 0:
version_text_width -= self.alert_notif_rect.width
# Highlight if currently viewing alerts
highlight_color = rl.Color(255, 70, 70, 255) if self.current_state == HomeLayoutState.ALERTS else rl.Color(226, 44, 44, 255)
rl.draw_rectangle_rounded(self.alert_notif_rect, 0.3, 10, highlight_color)
alert_text = f"{self.alert_count} ALERT{'S' if self.alert_count > 1 else ''}"
text_width = measure_text_cached(font, alert_text, HEAD_BUTTON_FONT_SIZE).x
text_x = self.alert_notif_rect.x + (self.alert_notif_rect.width - text_width) // 2
text_y = self.alert_notif_rect.y + (self.alert_notif_rect.height - HEAD_BUTTON_FONT_SIZE) // 2
alert_text = trn("{} ALERT", "{} ALERTS", self.alert_count).format(self.alert_count)
text_size = measure_text_cached(font, alert_text, HEAD_BUTTON_FONT_SIZE)
text_x = self.alert_notif_rect.x + (self.alert_notif_rect.width - text_size.x) // 2
text_y = self.alert_notif_rect.y + (self.alert_notif_rect.height - text_size.y) // 2
rl.draw_text_ex(font, alert_text, rl.Vector2(int(text_x), int(text_y)), HEAD_BUTTON_FONT_SIZE, 0, rl.WHITE)
# Version text (right aligned)
version_text = self._get_version_text()
text_width = measure_text_cached(gui_app.font(FontWeight.NORMAL), version_text, 48).x
version_x = self.header_rect.x + self.header_rect.width - text_width
version_y = self.header_rect.y + (self.header_rect.height - 48) // 2
rl.draw_text_ex(gui_app.font(FontWeight.NORMAL), version_text, rl.Vector2(int(version_x), int(version_y)), 48, 0, DEFAULT_TEXT_COLOR)
if self.update_available or self.alert_count > 0:
version_text_width -= SPACING * 1.5
version_rect = rl.Rectangle(self.header_rect.x + self.header_rect.width - version_text_width, self.header_rect.y,
version_text_width, self.header_rect.height)
gui_label(version_rect, self._version_text, 48, rl.WHITE, alignment=rl.GuiTextAlignment.TEXT_ALIGN_RIGHT)
def _render_home_content(self):
self._render_left_column()
@@ -193,20 +210,23 @@ class HomeLayout(Widget):
self._setup_widget.render(setup_rect)
def _refresh(self):
# TODO: implement _update_state with a timer
self.update_available = self.update_alert.refresh()
self.alert_count = self.offroad_alert.refresh()
self._update_state_priority(self.update_available, self.alert_count > 0)
def _update_state_priority(self, update_available: bool, alerts_present: bool):
current_state = self.current_state
self._version_text = self._get_version_text()
update_available = self.update_alert.refresh()
alert_count = self.offroad_alert.refresh()
alerts_present = alert_count > 0
# Show panels on transition from no alert/update to any alerts/update
if not update_available and not alerts_present:
self.current_state = HomeLayoutState.HOME
elif update_available and (current_state == HomeLayoutState.HOME or (not alerts_present and current_state == HomeLayoutState.ALERTS)):
self.current_state = HomeLayoutState.UPDATE
elif alerts_present and (current_state == HomeLayoutState.HOME or (not update_available and current_state == HomeLayoutState.UPDATE)):
self.current_state = HomeLayoutState.ALERTS
self._set_state(HomeLayoutState.HOME)
elif update_available and ((not self._prev_update_available) or (not alerts_present and self.current_state == HomeLayoutState.ALERTS)):
self._set_state(HomeLayoutState.UPDATE)
elif alerts_present and ((not self._prev_alerts_present) or (not update_available and self.current_state == HomeLayoutState.UPDATE)):
self._set_state(HomeLayoutState.ALERTS)
self.update_available = update_available
self.alert_count = alert_count
self._prev_update_available = update_available
self._prev_alerts_present = alerts_present
def _get_version_text(self) -> str:
brand = "openpilot"
+11 -2
View File
@@ -1,12 +1,14 @@
import pyray as rl
from enum import IntEnum
import cereal.messaging as messaging
from openpilot.system.ui.lib.application import gui_app
from openpilot.selfdrive.ui.layouts.sidebar import Sidebar, SIDEBAR_WIDTH
from openpilot.selfdrive.ui.layouts.home import HomeLayout
from openpilot.selfdrive.ui.layouts.settings.settings import SettingsLayout, PanelType
from openpilot.selfdrive.ui.onroad.augmented_road_view import AugmentedRoadView
from openpilot.selfdrive.ui.ui_state import device, ui_state
from openpilot.system.ui.widgets import Widget
from openpilot.selfdrive.ui.layouts.onboarding import OnboardingWindow
class MainState(IntEnum):
@@ -34,16 +36,23 @@ class MainLayout(Widget):
# Set callbacks
self._setup_callbacks()
# Start onboarding if terms or training not completed
self._onboarding_window = OnboardingWindow()
if not self._onboarding_window.completed:
gui_app.set_modal_overlay(self._onboarding_window)
def _render(self, _):
self._handle_onroad_transition()
self._render_main_content()
def _setup_callbacks(self):
self._sidebar.set_callbacks(on_settings=self._on_settings_clicked,
on_flag=self._on_bookmark_clicked)
on_flag=self._on_bookmark_clicked,
open_settings=lambda: self.open_settings(PanelType.TOGGLES))
self._layouts[MainState.HOME]._setup_widget.set_open_settings_callback(lambda: self.open_settings(PanelType.FIREHOSE))
self._layouts[MainState.HOME].set_settings_callback(lambda: self.open_settings(PanelType.TOGGLES))
self._layouts[MainState.SETTINGS].set_callbacks(on_close=self._set_mode_for_state)
self._layouts[MainState.ONROAD].set_callbacks(on_click=self._on_onroad_clicked)
self._layouts[MainState.ONROAD].set_click_callback(self._on_onroad_clicked)
device.add_interactive_timeout_callback(self._set_mode_for_state)
def _update_layout_rects(self):
+214
View File
@@ -0,0 +1,214 @@
import os
import re
import threading
from enum import IntEnum
import pyray as rl
from openpilot.common.basedir import BASEDIR
from openpilot.system.ui.lib.application import FontWeight, gui_app
from openpilot.system.ui.lib.multilang import tr
from openpilot.system.ui.widgets import Widget
from openpilot.system.ui.widgets.button import Button, ButtonStyle
from openpilot.system.ui.widgets.label import Label
from openpilot.selfdrive.ui.ui_state import ui_state
DEBUG = False
STEP_RECTS = [rl.Rectangle(104, 800, 633, 175), rl.Rectangle(1835, 0, 2159, 1080), rl.Rectangle(1835, 0, 2156, 1080),
rl.Rectangle(1526, 473, 427, 472), rl.Rectangle(1643, 441, 217, 223), rl.Rectangle(1835, 0, 2155, 1080),
rl.Rectangle(1786, 591, 267, 236), rl.Rectangle(1353, 0, 804, 1080), rl.Rectangle(1458, 485, 633, 211),
rl.Rectangle(95, 794, 1158, 187), rl.Rectangle(1560, 170, 392, 397), rl.Rectangle(1835, 0, 2159, 1080),
rl.Rectangle(1351, 0, 807, 1080), rl.Rectangle(1835, 0, 2158, 1080), rl.Rectangle(1531, 82, 441, 920),
rl.Rectangle(1336, 438, 490, 393), rl.Rectangle(1835, 0, 2159, 1080), rl.Rectangle(1835, 0, 2159, 1080),
rl.Rectangle(87, 795, 1187, 186)]
DM_RECORD_STEP = 9
DM_RECORD_YES_RECT = rl.Rectangle(695, 794, 558, 187)
RESTART_TRAINING_RECT = rl.Rectangle(87, 795, 472, 186)
class OnboardingState(IntEnum):
TERMS = 0
ONBOARDING = 1
DECLINE = 2
class TrainingGuide(Widget):
def __init__(self, completed_callback=None):
super().__init__()
self._completed_callback = completed_callback
self._step = 0
self._load_image_paths()
# Load first image now so we show something immediately
self._textures = [gui_app.texture(self._image_paths[0])]
self._image_objs = []
threading.Thread(target=self._preload_thread, daemon=True).start()
def _load_image_paths(self):
paths = [fn for fn in os.listdir(os.path.join(BASEDIR, "selfdrive/assets/training")) if re.match(r'^step\d*\.png$', fn)]
paths = sorted(paths, key=lambda x: int(re.search(r'\d+', x).group()))
self._image_paths = [os.path.join(BASEDIR, "selfdrive/assets/training", fn) for fn in paths]
def _preload_thread(self):
# PNG loading is slow in raylib, so we preload in a thread and upload to GPU in main thread
# We've already loaded the first image on init
for path in self._image_paths[1:]:
self._image_objs.append(gui_app._load_image_from_path(path))
def _handle_mouse_release(self, mouse_pos):
if rl.check_collision_point_rec(mouse_pos, STEP_RECTS[self._step]):
# Record DM camera?
if self._step == DM_RECORD_STEP:
yes = rl.check_collision_point_rec(mouse_pos, DM_RECORD_YES_RECT)
print(f"putting RecordFront to {yes}")
ui_state.params.put_bool("RecordFront", yes)
# Restart training?
elif self._step == len(self._image_paths) - 1:
if rl.check_collision_point_rec(mouse_pos, RESTART_TRAINING_RECT):
self._step = -1
self._step += 1
# Finished?
if self._step >= len(self._image_paths):
self._step = 0
if self._completed_callback:
self._completed_callback()
def _update_state(self):
if len(self._image_objs):
self._textures.append(gui_app._load_texture_from_image(self._image_objs.pop(0)))
def _render(self, _):
# Safeguard against fast tapping
step = min(self._step, len(self._textures) - 1)
rl.draw_texture(self._textures[step], 0, 0, rl.WHITE)
# progress bar
if 0 < step < len(STEP_RECTS) - 1:
h = 20
w = int((step / (len(STEP_RECTS) - 1)) * self._rect.width)
rl.draw_rectangle(int(self._rect.x), int(self._rect.y + self._rect.height - h),
w, h, rl.Color(70, 91, 234, 255))
if DEBUG:
rl.draw_rectangle_lines_ex(STEP_RECTS[step], 3, rl.RED)
return -1
class TermsPage(Widget):
def __init__(self, on_accept=None, on_decline=None):
super().__init__()
self._on_accept = on_accept
self._on_decline = on_decline
self._title = Label(tr("Welcome to openpilot"), font_size=90, font_weight=FontWeight.BOLD, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT)
self._desc = Label(tr("You must accept the Terms and Conditions to use openpilot. Read the latest terms at https://comma.ai/terms before continuing."),
font_size=90, font_weight=FontWeight.MEDIUM, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT)
self._decline_btn = Button(tr("Decline"), click_callback=on_decline)
self._accept_btn = Button(tr("Agree"), button_style=ButtonStyle.PRIMARY, click_callback=on_accept)
def _render(self, _):
welcome_x = self._rect.x + 165
welcome_y = self._rect.y + 165
welcome_rect = rl.Rectangle(welcome_x, welcome_y, self._rect.width - welcome_x, 90)
self._title.render(welcome_rect)
desc_x = welcome_x
# TODO: Label doesn't top align when wrapping
desc_y = welcome_y - 100
desc_rect = rl.Rectangle(desc_x, desc_y, self._rect.width - desc_x, self._rect.height - desc_y - 250)
self._desc.render(desc_rect)
btn_y = self._rect.y + self._rect.height - 160 - 45
btn_width = (self._rect.width - 45 * 3) / 2
self._decline_btn.render(rl.Rectangle(self._rect.x + 45, btn_y, btn_width, 160))
self._accept_btn.render(rl.Rectangle(self._rect.x + 45 * 2 + btn_width, btn_y, btn_width, 160))
if DEBUG:
rl.draw_rectangle_lines_ex(welcome_rect, 3, rl.RED)
rl.draw_rectangle_lines_ex(desc_rect, 3, rl.RED)
return -1
class DeclinePage(Widget):
def __init__(self, back_callback=None):
super().__init__()
self._text = Label(tr("You must accept the Terms and Conditions in order to use openpilot."),
font_size=90, font_weight=FontWeight.MEDIUM, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_LEFT)
self._back_btn = Button(tr("Back"), click_callback=back_callback)
self._uninstall_btn = Button(tr("Decline, uninstall openpilot"), button_style=ButtonStyle.DANGER,
click_callback=self._on_uninstall_clicked)
def _on_uninstall_clicked(self):
ui_state.params.put_bool("DoUninstall", True)
gui_app.request_close()
def _render(self, _):
btn_y = self._rect.y + self._rect.height - 160 - 45
btn_width = (self._rect.width - 45 * 3) / 2
self._back_btn.render(rl.Rectangle(self._rect.x + 45, btn_y, btn_width, 160))
self._uninstall_btn.render(rl.Rectangle(self._rect.x + 45 * 2 + btn_width, btn_y, btn_width, 160))
# text rect in middle of top and button
text_height = btn_y - (200 + 45)
text_rect = rl.Rectangle(self._rect.x + 165, self._rect.y + (btn_y - text_height) / 2 + 10, self._rect.width - (165 * 2), text_height)
if DEBUG:
rl.draw_rectangle_lines_ex(text_rect, 3, rl.RED)
self._text.render(text_rect)
class OnboardingWindow(Widget):
def __init__(self):
super().__init__()
self._current_terms_version = ui_state.params.get("TermsVersion")
self._current_training_version = ui_state.params.get("TrainingVersion")
self._accepted_terms: bool = ui_state.params.get("HasAcceptedTerms") == self._current_terms_version
self._training_done: bool = ui_state.params.get("CompletedTrainingVersion") == self._current_training_version
self._state = OnboardingState.TERMS if not self._accepted_terms else OnboardingState.ONBOARDING
# Windows
self._terms = TermsPage(on_accept=self._on_terms_accepted, on_decline=self._on_terms_declined)
self._training_guide: TrainingGuide | None = None
self._decline_page = DeclinePage(back_callback=self._on_decline_back)
@property
def completed(self) -> bool:
return self._accepted_terms and self._training_done
def _on_terms_declined(self):
self._state = OnboardingState.DECLINE
def _on_decline_back(self):
self._state = OnboardingState.TERMS
def _on_terms_accepted(self):
ui_state.params.put("HasAcceptedTerms", self._current_terms_version)
self._state = OnboardingState.ONBOARDING
if self._training_done:
gui_app.set_modal_overlay(None)
def _on_completed_training(self):
ui_state.params.put("CompletedTrainingVersion", self._current_training_version)
gui_app.set_modal_overlay(None)
def _render(self, _):
if self._training_guide is None:
self._training_guide = TrainingGuide(completed_callback=self._on_completed_training)
if self._state == OnboardingState.TERMS:
self._terms.render(self._rect)
if self._state == OnboardingState.ONBOARDING:
self._training_guide.render(self._rect)
elif self._state == OnboardingState.DECLINE:
self._decline_page.render(self._rect)
return -1
+159 -35
View File
@@ -1,20 +1,30 @@
from openpilot.common.params import Params
from openpilot.selfdrive.ui.widgets.ssh_key import ssh_key_item
from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.system.ui.widgets import Widget
from openpilot.system.ui.widgets.list_view import toggle_item
from openpilot.system.ui.widgets.scroller import Scroller
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.lib.multilang import tr, tr_noop
from openpilot.system.ui.widgets import DialogResult
# Description constants
DESCRIPTIONS = {
'enable_adb': (
'enable_adb': tr_noop(
"ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. " +
"See https://docs.comma.ai/how-to/connect-to-comma for more info."
),
'joystick_debug_mode': "Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)",
'ssh_key': (
'ssh_key': tr_noop(
"Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username " +
"other than your own. A comma employee will NEVER ask you to add their GitHub username."
),
'alpha_longitudinal': tr_noop(
"<b>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</b><br><br>" +
"On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. " +
"Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha. " +
"Changing this setting will restart openpilot if the car is powered on."
),
}
@@ -22,40 +32,154 @@ class DeveloperLayout(Widget):
def __init__(self):
super().__init__()
self._params = Params()
items = [
toggle_item(
"Enable ADB",
description=DESCRIPTIONS["enable_adb"],
initial_state=self._params.get_bool("AdbEnabled"),
callback=self._on_enable_adb,
),
ssh_key_item("SSH Key", description=DESCRIPTIONS["ssh_key"]),
toggle_item(
"Joystick Debug Mode",
description=DESCRIPTIONS["joystick_debug_mode"],
initial_state=self._params.get_bool("JoystickDebugMode"),
callback=self._on_joystick_debug_mode,
),
toggle_item(
"Longitudinal Maneuver Mode",
description="",
initial_state=self._params.get_bool("LongitudinalManeuverMode"),
callback=self._on_long_maneuver_mode,
),
toggle_item(
"openpilot Longitudinal Control (Alpha)",
description="",
initial_state=self._params.get_bool("AlphaLongitudinalEnabled"),
callback=self._on_alpha_long_enabled,
),
]
self._is_release = self._params.get_bool("IsReleaseBranch")
self._scroller = Scroller(items, line_separator=True, spacing=0)
# Build items and keep references for callbacks/state updates
self._adb_toggle = toggle_item(
lambda: tr("Enable ADB"),
description=lambda: tr(DESCRIPTIONS["enable_adb"]),
initial_state=self._params.get_bool("AdbEnabled"),
callback=self._on_enable_adb,
enabled=ui_state.is_offroad,
)
# SSH enable toggle + SSH key management
self._ssh_toggle = toggle_item(
lambda: tr("Enable SSH"),
description="",
initial_state=self._params.get_bool("SshEnabled"),
callback=self._on_enable_ssh,
)
self._ssh_keys = ssh_key_item(lambda: tr("SSH Keys"), description=lambda: tr(DESCRIPTIONS["ssh_key"]))
self._joystick_toggle = toggle_item(
lambda: tr("Joystick Debug Mode"),
description="",
initial_state=self._params.get_bool("JoystickDebugMode"),
callback=self._on_joystick_debug_mode,
enabled=ui_state.is_offroad,
)
self._long_maneuver_toggle = toggle_item(
lambda: tr("Longitudinal Maneuver Mode"),
description="",
initial_state=self._params.get_bool("LongitudinalManeuverMode"),
callback=self._on_long_maneuver_mode,
)
self._alpha_long_toggle = toggle_item(
lambda: tr("openpilot Longitudinal Control (Alpha)"),
description=lambda: tr(DESCRIPTIONS["alpha_longitudinal"]),
initial_state=self._params.get_bool("AlphaLongitudinalEnabled"),
callback=self._on_alpha_long_enabled,
enabled=lambda: not ui_state.engaged,
)
self._ui_debug_toggle = toggle_item(
lambda: tr("UI Debug Mode"),
description="",
initial_state=self._params.get_bool("ShowDebugInfo"),
callback=self._on_enable_ui_debug,
)
self._on_enable_ui_debug(self._params.get_bool("ShowDebugInfo"))
self._scroller = Scroller([
self._adb_toggle,
self._ssh_toggle,
self._ssh_keys,
self._joystick_toggle,
self._long_maneuver_toggle,
self._alpha_long_toggle,
self._ui_debug_toggle,
], line_separator=True, spacing=0)
# Toggles should be not available to change in onroad state
ui_state.add_offroad_transition_callback(self._update_toggles)
def _render(self, rect):
self._scroller.render(rect)
def _on_enable_adb(self): pass
def _on_joystick_debug_mode(self): pass
def _on_long_maneuver_mode(self): pass
def _on_alpha_long_enabled(self): pass
def show_event(self):
self._scroller.show_event()
self._update_toggles()
def _update_toggles(self):
ui_state.update_params()
# Hide non-release toggles on release builds
# TODO: we can do an onroad cycle, but alpha long toggle requires a deinit function to re-enable radar and not fault
for item in (self._joystick_toggle, self._long_maneuver_toggle, self._alpha_long_toggle):
item.set_visible(not self._is_release)
# CP gating
if ui_state.CP is not None:
alpha_avail = ui_state.CP.alphaLongitudinalAvailable
if not alpha_avail or self._is_release:
self._alpha_long_toggle.set_visible(False)
self._params.remove("AlphaLongitudinalEnabled")
else:
self._alpha_long_toggle.set_visible(True)
long_man_enabled = ui_state.has_longitudinal_control and ui_state.is_offroad()
self._long_maneuver_toggle.action_item.set_enabled(long_man_enabled)
if not long_man_enabled:
self._long_maneuver_toggle.action_item.set_state(False)
self._params.put_bool("LongitudinalManeuverMode", False)
else:
self._long_maneuver_toggle.action_item.set_enabled(False)
self._alpha_long_toggle.set_visible(False)
# TODO: make a param control list item so we don't need to manage internal state as much here
# refresh toggles from params to mirror external changes
for key, item in (
("AdbEnabled", self._adb_toggle),
("SshEnabled", self._ssh_toggle),
("JoystickDebugMode", self._joystick_toggle),
("LongitudinalManeuverMode", self._long_maneuver_toggle),
("AlphaLongitudinalEnabled", self._alpha_long_toggle),
("ShowDebugInfo", self._ui_debug_toggle),
):
item.action_item.set_state(self._params.get_bool(key))
def _on_enable_ui_debug(self, state: bool):
self._params.put_bool("ShowDebugInfo", state)
gui_app.set_show_touches(state)
gui_app.set_show_fps(state)
def _on_enable_adb(self, state: bool):
self._params.put_bool("AdbEnabled", state)
def _on_enable_ssh(self, state: bool):
self._params.put_bool("SshEnabled", state)
def _on_joystick_debug_mode(self, state: bool):
self._params.put_bool("JoystickDebugMode", state)
self._params.put_bool("LongitudinalManeuverMode", False)
self._long_maneuver_toggle.action_item.set_state(False)
def _on_long_maneuver_mode(self, state: bool):
self._params.put_bool("LongitudinalManeuverMode", state)
self._params.put_bool("JoystickDebugMode", False)
self._joystick_toggle.action_item.set_state(False)
def _on_alpha_long_enabled(self, state: bool):
if state:
def confirm_callback(result: int):
if result == DialogResult.CONFIRM:
self._params.put_bool("AlphaLongitudinalEnabled", True)
self._params.put_bool("OnroadCycleRequested", True)
self._update_toggles()
else:
self._alpha_long_toggle.action_item.set_state(False)
# show confirmation dialog
content = (f"<h1>{self._alpha_long_toggle.title}</h1><br>" +
f"<p>{self._alpha_long_toggle.description}</p>")
dlg = ConfirmDialog(content, tr("Enable"), rich=True)
gui_app.set_modal_overlay(dlg, callback=confirm_callback)
else:
self._params.put_bool("AlphaLongitudinalEnabled", False)
self._params.put_bool("OnroadCycleRequested", True)
self._update_toggles()
+128 -68
View File
@@ -1,29 +1,30 @@
import os
import json
import math
from cereal import messaging, log
from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.ui.onroad.driver_camera_dialog import DriverCameraDialog
from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.selfdrive.ui.layouts.onboarding import TrainingGuide
from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog
from openpilot.system.hardware import TICI
from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.lib.application import FontWeight, gui_app
from openpilot.system.ui.lib.multilang import multilang, tr, tr_noop
from openpilot.system.ui.widgets import Widget, DialogResult
from openpilot.system.ui.widgets.confirm_dialog import confirm_dialog, alert_dialog
from openpilot.system.ui.widgets.html_render import HtmlRenderer
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog, alert_dialog
from openpilot.system.ui.widgets.html_render import HtmlModal
from openpilot.system.ui.widgets.list_view import text_item, button_item, dual_button_item
from openpilot.system.ui.widgets.option_dialog import MultiOptionDialog
from openpilot.system.ui.widgets.scroller import Scroller
# Description constants
DESCRIPTIONS = {
'pair_device': "Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer.",
'driver_camera': "Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)",
'reset_calibration': (
"openpilot requires the device to be mounted within 4° left or right and within 5° " +
"up or 9° down. openpilot is continuously calibrating, resetting is rarely required."
),
'review_guide': "Review the rules, features, and limitations of openpilot",
'pair_device': tr_noop("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."),
'driver_camera': tr_noop("Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)"),
'reset_calibration': tr_noop("openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down."),
'review_guide': tr_noop("Review the rules, features, and limitations of openpilot"),
}
@@ -35,49 +36,61 @@ class DeviceLayout(Widget):
self._select_language_dialog: MultiOptionDialog | None = None
self._driver_camera: DriverCameraDialog | None = None
self._pair_device_dialog: PairingDialog | None = None
self._fcc_dialog: HtmlRenderer | None = None
self._fcc_dialog: HtmlModal | None = None
self._training_guide: TrainingGuide | None = None
items = self._initialize_items()
self._scroller = Scroller(items, line_separator=True, spacing=0)
ui_state.add_offroad_transition_callback(self._offroad_transition)
def _initialize_items(self):
dongle_id = self._params.get("DongleId") or "N/A"
serial = self._params.get("HardwareSerial") or "N/A"
self._pair_device_btn = button_item(lambda: tr("Pair Device"), lambda: tr("PAIR"), lambda: tr(DESCRIPTIONS['pair_device']), callback=self._pair_device)
self._pair_device_btn.set_visible(lambda: not ui_state.prime_state.is_paired())
self._reset_calib_btn = button_item(lambda: tr("Reset Calibration"), lambda: tr("RESET"), lambda: tr(DESCRIPTIONS['reset_calibration']),
callback=self._reset_calibration_prompt)
self._reset_calib_btn.set_description_opened_callback(self._update_calib_description)
self._power_off_btn = dual_button_item(lambda: tr("Reboot"), lambda: tr("Power Off"),
left_callback=self._reboot_prompt, right_callback=self._power_off_prompt)
items = [
text_item("Dongle ID", dongle_id),
text_item("Serial", serial),
button_item("Pair Device", "PAIR", DESCRIPTIONS['pair_device'], callback=self._pair_device),
button_item("Driver Camera", "PREVIEW", DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad),
button_item("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt),
regulatory_btn := button_item("Regulatory", "VIEW", callback=self._on_regulatory),
button_item("Review Training Guide", "REVIEW", DESCRIPTIONS['review_guide'], self._on_review_training_guide),
button_item("Change Language", "CHANGE", callback=self._show_language_selection, enabled=ui_state.is_offroad),
dual_button_item("Reboot", "Power Off", left_callback=self._reboot_prompt, right_callback=self._power_off_prompt),
text_item(lambda: tr("Dongle ID"), self._params.get("DongleId") or (lambda: tr("N/A"))),
text_item(lambda: tr("Serial"), self._params.get("HardwareSerial") or (lambda: tr("N/A"))),
self._pair_device_btn,
button_item(lambda: tr("Driver Camera"), lambda: tr("PREVIEW"), lambda: tr(DESCRIPTIONS['driver_camera']),
callback=self._show_driver_camera, enabled=ui_state.is_offroad),
self._reset_calib_btn,
button_item(lambda: tr("Review Training Guide"), lambda: tr("REVIEW"), lambda: tr(DESCRIPTIONS['review_guide']),
self._on_review_training_guide, enabled=ui_state.is_offroad),
regulatory_btn := button_item(lambda: tr("Regulatory"), lambda: tr("VIEW"), callback=self._on_regulatory, enabled=ui_state.is_offroad),
button_item(lambda: tr("Change Language"), lambda: tr("CHANGE"), callback=self._show_language_dialog),
self._power_off_btn,
]
regulatory_btn.set_visible(TICI)
return items
def _offroad_transition(self):
self._power_off_btn.action_item.right_button.set_visible(ui_state.is_offroad())
def show_event(self):
self._scroller.show_event()
def _render(self, rect):
self._scroller.render(rect)
def _show_language_selection(self):
try:
languages_file = os.path.join(BASEDIR, "selfdrive/ui/translations/languages.json")
with open(languages_file, encoding='utf-8') as f:
languages = json.load(f)
def _show_language_dialog(self):
def handle_language_selection(result: int):
if result == 1 and self._select_language_dialog:
selected_language = multilang.languages[self._select_language_dialog.selection]
multilang.change_language(selected_language)
self._update_calib_description()
self._select_language_dialog = None
self._select_language_dialog = MultiOptionDialog("Select a language", languages)
gui_app.set_modal_overlay(self._select_language_dialog, callback=self._handle_language_selection)
except FileNotFoundError:
pass
def _handle_language_selection(self, result: int):
if result == 1 and self._select_language_dialog:
selected_language = self._select_language_dialog.selection
self._params.put("LanguageSetting", selected_language)
self._select_language_dialog = None
self._select_language_dialog = MultiOptionDialog(tr("Select a language"), multilang.languages, multilang.codes[multilang.language],
option_font_weight=FontWeight.UNIFONT)
gui_app.set_modal_overlay(self._select_language_dialog, callback=handle_language_selection)
def _show_driver_camera(self):
if not self._driver_camera:
@@ -87,34 +100,80 @@ class DeviceLayout(Widget):
def _reset_calibration_prompt(self):
if ui_state.engaged:
gui_app.set_modal_overlay(lambda: alert_dialog("Disengage to Reset Calibration"))
gui_app.set_modal_overlay(alert_dialog(tr("Disengage to Reset Calibration")))
return
gui_app.set_modal_overlay(
lambda: confirm_dialog("Are you sure you want to reset calibration?", "Reset"),
callback=self._reset_calibration,
)
def reset_calibration(result: int):
# Check engaged again in case it changed while the dialog was open
if ui_state.engaged or result != DialogResult.CONFIRM:
return
def _reset_calibration(self, result: int):
if ui_state.engaged or result != DialogResult.CONFIRM:
return
self._params.remove("CalibrationParams")
self._params.remove("LiveTorqueParameters")
self._params.remove("LiveParameters")
self._params.remove("LiveParametersV2")
self._params.remove("LiveDelay")
self._params.put_bool("OnroadCycleRequested", True)
self._update_calib_description()
self._params.remove("CalibrationParams")
self._params.remove("LiveTorqueParameters")
self._params.remove("LiveParameters")
self._params.remove("LiveParametersV2")
self._params.remove("LiveDelay")
self._params.put_bool("OnroadCycleRequested", True)
dialog = ConfirmDialog(tr("Are you sure you want to reset calibration?"), tr("Reset"))
gui_app.set_modal_overlay(dialog, callback=reset_calibration)
def _update_calib_description(self):
desc = tr(DESCRIPTIONS['reset_calibration'])
calib_bytes = self._params.get("CalibrationParams")
if calib_bytes:
try:
calib = messaging.log_from_bytes(calib_bytes, log.Event).liveCalibration
if calib.calStatus != log.LiveCalibrationData.Status.uncalibrated:
pitch = math.degrees(calib.rpyCalib[1])
yaw = math.degrees(calib.rpyCalib[2])
desc += tr(" Your device is pointed {:.1f}° {} and {:.1f}° {}.").format(abs(pitch), tr("down") if pitch > 0 else tr("up"),
abs(yaw), tr("left") if yaw > 0 else tr("right"))
except Exception:
cloudlog.exception("invalid CalibrationParams")
lag_perc = 0
lag_bytes = self._params.get("LiveDelay")
if lag_bytes:
try:
lag_perc = messaging.log_from_bytes(lag_bytes, log.Event).liveDelay.calPerc
except Exception:
cloudlog.exception("invalid LiveDelay")
if lag_perc < 100:
desc += tr("<br><br>Steering lag calibration is {}% complete.").format(lag_perc)
else:
desc += tr("<br><br>Steering lag calibration is complete.")
torque_bytes = self._params.get("LiveTorqueParameters")
if torque_bytes:
try:
torque = messaging.log_from_bytes(torque_bytes, log.Event).liveTorqueParameters
# don't add for non-torque cars
if torque.useParams:
torque_perc = torque.calPerc
if torque_perc < 100:
desc += tr(" Steering torque response calibration is {}% complete.").format(torque_perc)
else:
desc += tr(" Steering torque response calibration is complete.")
except Exception:
cloudlog.exception("invalid LiveTorqueParameters")
desc += "<br><br>"
desc += tr("openpilot is continuously calibrating, resetting is rarely required. " +
"Resetting calibration will restart openpilot if the car is powered on.")
self._reset_calib_btn.set_description(desc)
def _reboot_prompt(self):
if ui_state.engaged:
gui_app.set_modal_overlay(lambda: alert_dialog("Disengage to Reboot"))
gui_app.set_modal_overlay(alert_dialog(tr("Disengage to Reboot")))
return
gui_app.set_modal_overlay(
lambda: confirm_dialog("Are you sure you want to reboot?", "Reboot"),
callback=self._perform_reboot,
)
dialog = ConfirmDialog(tr("Are you sure you want to reboot?"), tr("Reboot"))
gui_app.set_modal_overlay(dialog, callback=self._perform_reboot)
def _perform_reboot(self, result: int):
if not ui_state.engaged and result == DialogResult.CONFIRM:
@@ -122,13 +181,11 @@ class DeviceLayout(Widget):
def _power_off_prompt(self):
if ui_state.engaged:
gui_app.set_modal_overlay(lambda: alert_dialog("Disengage to Power Off"))
gui_app.set_modal_overlay(alert_dialog(tr("Disengage to Power Off")))
return
gui_app.set_modal_overlay(
lambda: confirm_dialog("Are you sure you want to power off?", "Power Off"),
callback=self._perform_power_off,
)
dialog = ConfirmDialog(tr("Are you sure you want to power off?"), tr("Power Off"))
gui_app.set_modal_overlay(dialog, callback=self._perform_power_off)
def _perform_power_off(self, result: int):
if not ui_state.engaged and result == DialogResult.CONFIRM:
@@ -141,10 +198,13 @@ class DeviceLayout(Widget):
def _on_regulatory(self):
if not self._fcc_dialog:
self._fcc_dialog = HtmlRenderer(os.path.join(BASEDIR, "selfdrive/assets/offroad/fcc.html"))
self._fcc_dialog = HtmlModal(os.path.join(BASEDIR, "selfdrive/assets/offroad/fcc.html"))
gui_app.set_modal_overlay(self._fcc_dialog)
gui_app.set_modal_overlay(self._fcc_dialog,
callback=lambda result: setattr(self, '_fcc_dialog', None),
)
def _on_review_training_guide(self):
if not self._training_guide:
def completed_callback():
gui_app.set_modal_overlay(None)
def _on_review_training_guide(self): pass
self._training_guide = TrainingGuide(completed_callback=completed_callback)
gui_app.set_modal_overlay(self._training_guide)
+41 -60
View File
@@ -7,21 +7,23 @@ from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.system.athena.registration import UNREGISTERED_DONGLE_ID
from openpilot.system.ui.lib.application import gui_app, FontWeight
from openpilot.system.ui.lib.application import gui_app, FontWeight, FONT_SCALE
from openpilot.system.ui.lib.multilang import tr, trn, tr_noop
from openpilot.system.ui.lib.text_measure import measure_text_cached
from openpilot.system.ui.lib.scroll_panel import GuiScrollPanel
from openpilot.system.ui.lib.wrap_text import wrap_text
from openpilot.system.ui.widgets import Widget
from openpilot.selfdrive.ui.lib.api_helpers import get_token
TITLE = "Firehose Mode"
DESCRIPTION = (
TITLE = tr_noop("Firehose Mode")
DESCRIPTION = tr_noop(
"openpilot learns to drive by watching humans, like you, drive.\n\n"
+ "Firehose Mode allows you to maximize your training data uploads to improve "
+ "openpilot's driving models. More data means bigger models, which means better Experimental Mode."
)
INSTRUCTIONS = (
INSTRUCTIONS = tr_noop(
"For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.\n\n"
+ "Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.\n\n"
+ "Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.\n\n\n"
+ "Frequently Asked Questions\n\n"
+ "Does it matter how or where I drive? Nope, just drive as you normally would.\n\n"
+ "Do all of my segments get pulled in Firehose Mode? No, we selectively pull a subset of your segments.\n\n"
@@ -43,12 +45,16 @@ class FirehoseLayout(Widget):
self.params = Params()
self.segment_count = self._get_segment_count()
self.scroll_panel = GuiScrollPanel()
self._content_height = 0
self.running = True
self.update_thread = threading.Thread(target=self._update_loop, daemon=True)
self.update_thread.start()
self.last_update_time = 0
def show_event(self):
self.scroll_panel.set_offset(0)
def _get_segment_count(self) -> int:
stats = self.params.get(self.PARAM_KEY)
if not stats:
@@ -66,97 +72,72 @@ class FirehoseLayout(Widget):
def _render(self, rect: rl.Rectangle):
# Calculate content dimensions
content_width = rect.width - 80
content_height = self._calculate_content_height(int(content_width))
content_rect = rl.Rectangle(rect.x, rect.y, rect.width, content_height)
content_rect = rl.Rectangle(rect.x, rect.y, rect.width, self._content_height)
# Handle scrolling and render with clipping
scroll_offset = self.scroll_panel.handle_scroll(rect, content_rect)
scroll_offset = self.scroll_panel.update(rect, content_rect)
rl.begin_scissor_mode(int(rect.x), int(rect.y), int(rect.width), int(rect.height))
self._render_content(rect, scroll_offset)
self._content_height = self._render_content(rect, scroll_offset)
rl.end_scissor_mode()
def _calculate_content_height(self, content_width: int) -> int:
height = 80 # Top margin
# Title
height += 100 + 40
# Description
desc_font = gui_app.font(FontWeight.NORMAL)
desc_lines = wrap_text(desc_font, DESCRIPTION, 45, content_width)
height += len(desc_lines) * 45 + 40
# Status section
height += 32 # Separator
status_text, _ = self._get_status()
status_lines = wrap_text(gui_app.font(FontWeight.BOLD), status_text, 60, content_width)
height += len(status_lines) * 60 + 20
# Contribution count (if available)
if self.segment_count > 0:
contrib_text = f"{self.segment_count} segment(s) of your driving is in the training dataset so far."
contrib_lines = wrap_text(gui_app.font(FontWeight.BOLD), contrib_text, 52, content_width)
height += len(contrib_lines) * 52 + 20
# Instructions section
height += 32 # Separator
inst_lines = wrap_text(gui_app.font(FontWeight.NORMAL), INSTRUCTIONS, 40, content_width)
height += len(inst_lines) * 40 + 40 # Bottom margin
return height
def _render_content(self, rect: rl.Rectangle, scroll_offset: rl.Vector2):
def _render_content(self, rect: rl.Rectangle, scroll_offset: float) -> int:
x = int(rect.x + 40)
y = int(rect.y + 40 + scroll_offset.y)
y = int(rect.y + 40 + scroll_offset)
w = int(rect.width - 80)
# Title
# Title (centered)
title_text = tr(TITLE) # live translate
title_font = gui_app.font(FontWeight.MEDIUM)
rl.draw_text_ex(title_font, TITLE, rl.Vector2(x, y), 100, 0, rl.WHITE)
y += 140
text_width = measure_text_cached(title_font, title_text, 100).x
title_x = rect.x + (rect.width - text_width) / 2
rl.draw_text_ex(title_font, title_text, rl.Vector2(title_x, y), 100, 0, rl.WHITE)
y += 200
# Description
y = self._draw_wrapped_text(x, y, w, DESCRIPTION, gui_app.font(FontWeight.NORMAL), 45, rl.WHITE)
y += 40
y = self._draw_wrapped_text(x, y, w, tr(DESCRIPTION), gui_app.font(FontWeight.NORMAL), 45, rl.WHITE)
y += 40 + 20
# Separator
rl.draw_rectangle(x, y, w, 2, self.GRAY)
y += 30
y += 30 + 20
# Status
status_text, status_color = self._get_status()
y = self._draw_wrapped_text(x, y, w, status_text, gui_app.font(FontWeight.BOLD), 60, status_color)
y += 20
y += 20 + 20
# Contribution count (if available)
if self.segment_count > 0:
contrib_text = f"{self.segment_count} segment(s) of your driving is in the training dataset so far."
contrib_text = trn("{} segment of your driving is in the training dataset so far.",
"{} segments of your driving is in the training dataset so far.", self.segment_count).format(self.segment_count)
y = self._draw_wrapped_text(x, y, w, contrib_text, gui_app.font(FontWeight.BOLD), 52, rl.WHITE)
y += 20
y += 20 + 20
# Separator
rl.draw_rectangle(x, y, w, 2, self.GRAY)
y += 30
y += 30 + 20
# Instructions
self._draw_wrapped_text(x, y, w, INSTRUCTIONS, gui_app.font(FontWeight.NORMAL), 40, self.LIGHT_GRAY)
y = self._draw_wrapped_text(x, y, w, tr(INSTRUCTIONS), gui_app.font(FontWeight.NORMAL), 40, self.LIGHT_GRAY)
def _draw_wrapped_text(self, x, y, width, text, font, size, color):
wrapped = wrap_text(font, text, size, width)
# bottom margin + remove effect of scroll offset
return int(round(y - self.scroll_panel.offset + 40))
def _draw_wrapped_text(self, x, y, width, text, font, font_size, color):
wrapped = wrap_text(font, text, font_size, width)
for line in wrapped:
rl.draw_text_ex(font, line, rl.Vector2(x, y), size, 0, color)
y += size
return y
rl.draw_text_ex(font, line, rl.Vector2(x, y), font_size, 0, color)
y += font_size * FONT_SCALE
return round(y)
def _get_status(self) -> tuple[str, rl.Color]:
network_type = ui_state.sm["deviceState"].networkType
network_metered = ui_state.sm["deviceState"].networkMetered
if not network_metered and network_type != 0: # Not metered and connected
return "ACTIVE", self.GREEN
return tr("ACTIVE"), self.GREEN
else:
return "INACTIVE: connect to an unmetered network", self.RED
return tr("INACTIVE: connect to an unmetered network"), self.RED
def _fetch_firehose_stats(self):
try:
+27 -18
View File
@@ -8,18 +8,16 @@ from openpilot.selfdrive.ui.layouts.settings.firehose import FirehoseLayout
from openpilot.selfdrive.ui.layouts.settings.software import SoftwareLayout
from openpilot.selfdrive.ui.layouts.settings.toggles import TogglesLayout
from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos
from openpilot.system.ui.lib.multilang import tr, tr_noop
from openpilot.system.ui.lib.text_measure import measure_text_cached
from openpilot.system.ui.lib.wifi_manager import WifiManager
from openpilot.system.ui.widgets import Widget
from openpilot.system.ui.widgets.network import WifiManagerUI
# Settings close button
SETTINGS_CLOSE_TEXT = "×"
SETTINGS_CLOSE_TEXT_Y_OFFSET = 8 # The '×' character isn't quite vertically centered in the font so we need to offset it a bit to fully center it
from openpilot.system.ui.widgets.network import NetworkUI
# Constants
SIDEBAR_WIDTH = 500
CLOSE_BTN_SIZE = 200
CLOSE_ICON_SIZE = 70
NAV_BTN_HEIGHT = 110
PANEL_MARGIN = 50
@@ -58,15 +56,16 @@ class SettingsLayout(Widget):
wifi_manager.set_active(False)
self._panels = {
PanelType.DEVICE: PanelInfo("Device", DeviceLayout()),
PanelType.NETWORK: PanelInfo("Network", WifiManagerUI(wifi_manager)),
PanelType.TOGGLES: PanelInfo("Toggles", TogglesLayout()),
PanelType.SOFTWARE: PanelInfo("Software", SoftwareLayout()),
PanelType.FIREHOSE: PanelInfo("Firehose", FirehoseLayout()),
PanelType.DEVELOPER: PanelInfo("Developer", DeveloperLayout()),
PanelType.DEVICE: PanelInfo(tr_noop("Device"), DeviceLayout()),
PanelType.NETWORK: PanelInfo(tr_noop("Network"), NetworkUI(wifi_manager)),
PanelType.TOGGLES: PanelInfo(tr_noop("Toggles"), TogglesLayout()),
PanelType.SOFTWARE: PanelInfo(tr_noop("Software"), SoftwareLayout()),
PanelType.FIREHOSE: PanelInfo(tr_noop("Firehose"), FirehoseLayout()),
PanelType.DEVELOPER: PanelInfo(tr_noop("Developer"), DeveloperLayout()),
}
self._font_medium = gui_app.font(FontWeight.MEDIUM)
self._close_icon = gui_app.texture("icons/close2.png", CLOSE_ICON_SIZE, CLOSE_ICON_SIZE)
# Callbacks
self._close_callback: Callable | None = None
@@ -96,12 +95,21 @@ class SettingsLayout(Widget):
close_color = CLOSE_BTN_PRESSED if pressed else CLOSE_BTN_COLOR
rl.draw_rectangle_rounded(close_btn_rect, 1.0, 20, close_color)
close_text_size = measure_text_cached(self._font_medium, SETTINGS_CLOSE_TEXT, 140)
close_text_pos = rl.Vector2(
close_btn_rect.x + (close_btn_rect.width - close_text_size.x) / 2,
close_btn_rect.y + (close_btn_rect.height - close_text_size.y) / 2 - SETTINGS_CLOSE_TEXT_Y_OFFSET,
icon_color = rl.Color(255, 255, 255, 255) if not pressed else rl.Color(220, 220, 220, 255)
icon_dest = rl.Rectangle(
close_btn_rect.x + (close_btn_rect.width - self._close_icon.width) / 2,
close_btn_rect.y + (close_btn_rect.height - self._close_icon.height) / 2,
self._close_icon.width,
self._close_icon.height,
)
rl.draw_texture_pro(
self._close_icon,
rl.Rectangle(0, 0, self._close_icon.width, self._close_icon.height),
icon_dest,
rl.Vector2(0, 0),
0,
icon_color,
)
rl.draw_text_ex(self._font_medium, SETTINGS_CLOSE_TEXT, close_text_pos, 140, 0, TEXT_SELECTED)
# Store close button rect for click detection
self._close_btn_rect = close_btn_rect
@@ -115,11 +123,12 @@ class SettingsLayout(Widget):
is_selected = panel_type == self._current_panel
text_color = TEXT_SELECTED if is_selected else TEXT_NORMAL
# Draw button text (right-aligned)
text_size = measure_text_cached(self._font_medium, panel_info.name, 65)
panel_name = tr(panel_info.name)
text_size = measure_text_cached(self._font_medium, panel_name, 65)
text_pos = rl.Vector2(
button_rect.x + button_rect.width - text_size.x, button_rect.y + (button_rect.height - text_size.y) / 2
)
rl.draw_text_ex(self._font_medium, panel_info.name, text_pos, 65, 0, text_color)
rl.draw_text_ex(self._font_medium, panel_name, text_pos, 65, 0, text_color)
# Store button rect for click detection
panel_info.button_rect = button_rect
+175 -23
View File
@@ -1,42 +1,194 @@
from openpilot.common.params import Params
import os
import time
import datetime
from openpilot.common.time_helpers import system_time_valid
from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.lib.multilang import tr, trn
from openpilot.system.ui.widgets import Widget, DialogResult
from openpilot.system.ui.widgets.confirm_dialog import confirm_dialog
from openpilot.system.ui.widgets.list_view import button_item, text_item
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
from openpilot.system.ui.widgets.list_view import button_item, text_item, ListItem
from openpilot.system.ui.widgets.option_dialog import MultiOptionDialog
from openpilot.system.ui.widgets.scroller import Scroller
# TODO: remove this. updater fails to respond on startup if time is not correct
UPDATED_TIMEOUT = 10 # seconds to wait for updated to respond
def time_ago(date: datetime.datetime | None) -> str:
if not date:
return tr("never")
if not system_time_valid():
return date.strftime("%a %b %d %Y")
now = datetime.datetime.now(datetime.UTC)
if date.tzinfo is None:
date = date.replace(tzinfo=datetime.UTC)
diff_seconds = int((now - date).total_seconds())
if diff_seconds < 60:
return tr("now")
if diff_seconds < 3600:
m = diff_seconds // 60
return trn("{} minute ago", "{} minutes ago", m).format(m)
if diff_seconds < 86400:
h = diff_seconds // 3600
return trn("{} hour ago", "{} hours ago", h).format(h)
if diff_seconds < 604800:
d = diff_seconds // 86400
return trn("{} day ago", "{} days ago", d).format(d)
return date.strftime("%a %b %d %Y")
class SoftwareLayout(Widget):
def __init__(self):
super().__init__()
self._params = Params()
items = self._init_items()
self._scroller = Scroller(items, line_separator=True, spacing=0)
self._onroad_label = ListItem(lambda: tr("Updates are only downloaded while the car is off."))
self._version_item = text_item(lambda: tr("Current Version"), ui_state.params.get("UpdaterCurrentDescription") or "")
self._download_btn = button_item(lambda: tr("Download"), lambda: tr("CHECK"), callback=self._on_download_update)
def _init_items(self):
items = [
text_item("Current Version", ""),
button_item("Download", "CHECK", callback=self._on_download_update),
button_item("Install Update", "INSTALL", callback=self._on_install_update),
button_item("Target Branch", "SELECT", callback=self._on_select_branch),
button_item("Uninstall", "UNINSTALL", callback=self._on_uninstall),
]
return items
# Install button is initially hidden
self._install_btn = button_item(lambda: tr("Install Update"), lambda: tr("INSTALL"), callback=self._on_install_update)
self._install_btn.set_visible(False)
# Track waiting-for-updater transition to avoid brief re-enable while still idle
self._waiting_for_updater = False
self._waiting_start_ts: float = 0.0
# Branch switcher
self._branch_btn = button_item(lambda: tr("Target Branch"), lambda: tr("SELECT"), callback=self._on_select_branch)
self._branch_btn.set_visible(not ui_state.params.get_bool("IsTestedBranch"))
self._branch_btn.action_item.set_value(ui_state.params.get("UpdaterTargetBranch") or "")
self._branch_dialog: MultiOptionDialog | None = None
self._scroller = Scroller([
self._onroad_label,
self._version_item,
self._download_btn,
self._install_btn,
self._branch_btn,
button_item(lambda: tr("Uninstall"), lambda: tr("UNINSTALL"), callback=self._on_uninstall),
], line_separator=True, spacing=0)
def show_event(self):
self._scroller.show_event()
def _render(self, rect):
self._scroller.render(rect)
def _on_download_update(self): pass
def _on_install_update(self): pass
def _on_select_branch(self): pass
def _update_state(self):
# Show/hide onroad warning
self._onroad_label.set_visible(ui_state.is_onroad())
# Update current version and release notes
current_desc = ui_state.params.get("UpdaterCurrentDescription") or ""
current_release_notes = (ui_state.params.get("UpdaterCurrentReleaseNotes") or b"").decode("utf-8", "replace")
self._version_item.action_item.set_text(current_desc)
self._version_item.set_description(current_release_notes)
# Update download button visibility and state
self._download_btn.set_visible(ui_state.is_offroad())
updater_state = ui_state.params.get("UpdaterState") or "idle"
failed_count = ui_state.params.get("UpdateFailedCount") or 0
fetch_available = ui_state.params.get_bool("UpdaterFetchAvailable")
update_available = ui_state.params.get_bool("UpdateAvailable")
if updater_state != "idle":
# Updater responded
self._waiting_for_updater = False
self._download_btn.action_item.set_enabled(False)
self._download_btn.action_item.set_value(updater_state)
else:
if failed_count > 0:
self._download_btn.action_item.set_value(tr("failed to check for update"))
self._download_btn.action_item.set_text(tr("CHECK"))
elif fetch_available:
self._download_btn.action_item.set_value(tr("update available"))
self._download_btn.action_item.set_text(tr("DOWNLOAD"))
else:
last_update = ui_state.params.get("LastUpdateTime")
if last_update:
formatted = time_ago(last_update)
self._download_btn.action_item.set_value(tr("up to date, last checked {}").format(formatted))
else:
self._download_btn.action_item.set_value(tr("up to date, last checked never"))
self._download_btn.action_item.set_text(tr("CHECK"))
# If we've been waiting too long without a state change, reset state
if self._waiting_for_updater and (time.monotonic() - self._waiting_start_ts > UPDATED_TIMEOUT):
self._waiting_for_updater = False
# Only enable if we're not waiting for updater to flip out of idle
self._download_btn.action_item.set_enabled(not self._waiting_for_updater)
# Update target branch button value
current_branch = ui_state.params.get("UpdaterTargetBranch") or ""
self._branch_btn.action_item.set_value(current_branch)
# Update install button
self._install_btn.set_visible(ui_state.is_offroad() and update_available)
if update_available:
new_desc = ui_state.params.get("UpdaterNewDescription") or ""
new_release_notes = (ui_state.params.get("UpdaterNewReleaseNotes") or b"").decode("utf-8", "replace")
self._install_btn.action_item.set_text(tr("INSTALL"))
self._install_btn.action_item.set_value(new_desc)
self._install_btn.set_description(new_release_notes)
# Enable install button for testing (like Qt showEvent)
self._install_btn.action_item.set_enabled(True)
else:
self._install_btn.set_visible(False)
def _on_download_update(self):
# Check if we should start checking or start downloading
self._download_btn.action_item.set_enabled(False)
if self._download_btn.action_item.text == tr("CHECK"):
# Start checking for updates
self._waiting_for_updater = True
self._waiting_start_ts = time.monotonic()
os.system("pkill -SIGUSR1 -f system.updated.updated")
else:
# Start downloading
self._waiting_for_updater = True
self._waiting_start_ts = time.monotonic()
os.system("pkill -SIGHUP -f system.updated.updated")
def _on_uninstall(self):
def handle_uninstall_confirmation(result):
if result == DialogResult.CONFIRM:
self._params.put_bool("DoUninstall", True)
ui_state.params.put_bool("DoUninstall", True)
gui_app.set_modal_overlay(
lambda: confirm_dialog("Are you sure you want to uninstall?", "Uninstall"),
callback=handle_uninstall_confirmation,
)
dialog = ConfirmDialog(tr("Are you sure you want to uninstall?"), tr("Uninstall"))
gui_app.set_modal_overlay(dialog, callback=handle_uninstall_confirmation)
def _on_install_update(self):
# Trigger reboot to install update
self._install_btn.action_item.set_enabled(False)
ui_state.params.put_bool("DoReboot", True)
def _on_select_branch(self):
# Get available branches and order
current_git_branch = ui_state.params.get("GitBranch") or ""
branches_str = ui_state.params.get("UpdaterAvailableBranches") or ""
branches = [b for b in branches_str.split(",") if b]
for b in [current_git_branch, "devel-staging", "devel", "nightly", "nightly-dev", "master"]:
if b in branches:
branches.remove(b)
branches.insert(0, b)
current_target = ui_state.params.get("UpdaterTargetBranch") or ""
self._branch_dialog = MultiOptionDialog(tr("Select a branch"), branches, current_target)
def handle_selection(result):
# Confirmed selection
if result == DialogResult.CONFIRM and self._branch_dialog is not None and self._branch_dialog.selection:
selection = self._branch_dialog.selection
ui_state.params.put("UpdaterTargetBranch", selection)
self._branch_btn.action_item.set_value(selection)
os.system("pkill -SIGUSR1 -f system.updated.updated")
self._branch_dialog = None
gui_app.set_modal_overlay(self._branch_dialog, callback=handle_selection)
+214 -65
View File
@@ -1,28 +1,36 @@
from openpilot.common.params import Params
from cereal import log
from openpilot.common.params import Params, UnknownKeyName
from openpilot.system.ui.widgets import Widget
from openpilot.system.ui.widgets.list_view import multiple_button_item, toggle_item
from openpilot.system.ui.widgets.scroller import Scroller
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.lib.multilang import tr, tr_noop
from openpilot.system.ui.widgets import DialogResult
from openpilot.selfdrive.ui.ui_state import ui_state
PERSONALITY_TO_INT = log.LongitudinalPersonality.schema.enumerants
# Description constants
DESCRIPTIONS = {
"OpenpilotEnabledToggle": (
"OpenpilotEnabledToggle": tr_noop(
"Use the openpilot system for adaptive cruise control and lane keep driver assistance. " +
"Your attention is required at all times to use this feature."
),
"DisengageOnAccelerator": "When enabled, pressing the accelerator pedal will disengage openpilot.",
"LongitudinalPersonality": (
"DisengageOnAccelerator": tr_noop("When enabled, pressing the accelerator pedal will disengage openpilot."),
"LongitudinalPersonality": tr_noop(
"Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. " +
"In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with " +
"your steering wheel distance button."
),
"IsLdwEnabled": (
"IsLdwEnabled": tr_noop(
"Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line " +
"without a turn signal activated while driving over 31 mph (50 km/h)."
),
"AlwaysOnDM": "Enable driver monitoring even when openpilot is not engaged.",
'RecordFront': "Upload data from the driver facing camera and help improve the driver monitoring algorithm.",
"IsMetric": "Display speed in km/h instead of mph.",
"RecordAudio": "Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect.",
"AlwaysOnDM": tr_noop("Enable driver monitoring even when openpilot is not engaged."),
'RecordFront': tr_noop("Upload data from the driver facing camera and help improve the driver monitoring algorithm."),
"IsMetric": tr_noop("Display speed in km/h instead of mph."),
"RecordAudio": tr_noop("Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect."),
}
@@ -30,66 +38,207 @@ class TogglesLayout(Widget):
def __init__(self):
super().__init__()
self._params = Params()
items = [
toggle_item(
"Enable openpilot",
DESCRIPTIONS["OpenpilotEnabledToggle"],
self._params.get_bool("OpenpilotEnabledToggle"),
icon="chffr_wheel.png",
),
toggle_item(
"Experimental Mode",
initial_state=self._params.get_bool("ExperimentalMode"),
icon="experimental_white.png",
),
toggle_item(
"Disengage on Accelerator Pedal",
DESCRIPTIONS["DisengageOnAccelerator"],
self._params.get_bool("DisengageOnAccelerator"),
icon="disengage_on_accelerator.png",
),
multiple_button_item(
"Driving Personality",
DESCRIPTIONS["LongitudinalPersonality"],
buttons=["Aggressive", "Standard", "Relaxed"],
button_width=255,
callback=self._set_longitudinal_personality,
selected_index=self._params.get("LongitudinalPersonality", return_default=True),
icon="speed_limit.png"
),
toggle_item(
"Enable Lane Departure Warnings",
DESCRIPTIONS["IsLdwEnabled"],
self._params.get_bool("IsLdwEnabled"),
icon="warning.png",
),
toggle_item(
"Always-On Driver Monitoring",
DESCRIPTIONS["AlwaysOnDM"],
self._params.get_bool("AlwaysOnDM"),
icon="monitoring.png",
),
toggle_item(
"Record and Upload Driver Camera",
DESCRIPTIONS["RecordFront"],
self._params.get_bool("RecordFront"),
icon="monitoring.png",
),
toggle_item(
"Record Microphone Audio",
DESCRIPTIONS["RecordAudio"],
self._params.get_bool("RecordAudio"),
icon="microphone.png",
),
toggle_item(
"Use Metric System", DESCRIPTIONS["IsMetric"], self._params.get_bool("IsMetric"), icon="metric.png"
),
]
self._is_release = self._params.get_bool("IsReleaseBranch")
self._scroller = Scroller(items, line_separator=True, spacing=0)
# param, title, desc, icon, needs_restart
self._toggle_defs = {
"OpenpilotEnabledToggle": (
lambda: tr("Enable openpilot"),
DESCRIPTIONS["OpenpilotEnabledToggle"],
"chffr_wheel.png",
True,
),
"ExperimentalMode": (
lambda: tr("Experimental Mode"),
"",
"experimental_white.png",
False,
),
"DisengageOnAccelerator": (
lambda: tr("Disengage on Accelerator Pedal"),
DESCRIPTIONS["DisengageOnAccelerator"],
"disengage_on_accelerator.png",
False,
),
"IsLdwEnabled": (
lambda: tr("Enable Lane Departure Warnings"),
DESCRIPTIONS["IsLdwEnabled"],
"warning.png",
False,
),
"AlwaysOnDM": (
lambda: tr("Always-On Driver Monitoring"),
DESCRIPTIONS["AlwaysOnDM"],
"monitoring.png",
False,
),
"RecordFront": (
lambda: tr("Record and Upload Driver Camera"),
DESCRIPTIONS["RecordFront"],
"monitoring.png",
True,
),
"RecordAudio": (
lambda: tr("Record and Upload Microphone Audio"),
DESCRIPTIONS["RecordAudio"],
"microphone.png",
True,
),
"IsMetric": (
lambda: tr("Use Metric System"),
DESCRIPTIONS["IsMetric"],
"metric.png",
False,
),
}
self._long_personality_setting = multiple_button_item(
lambda: tr("Driving Personality"),
lambda: tr(DESCRIPTIONS["LongitudinalPersonality"]),
buttons=[lambda: tr("Aggressive"), lambda: tr("Standard"), lambda: tr("Relaxed")],
button_width=255,
callback=self._set_longitudinal_personality,
selected_index=self._params.get("LongitudinalPersonality", return_default=True),
icon="speed_limit.png"
)
self._toggles = {}
self._locked_toggles = set()
for param, (title, desc, icon, needs_restart) in self._toggle_defs.items():
toggle = toggle_item(
title,
desc,
self._params.get_bool(param),
callback=lambda state, p=param: self._toggle_callback(state, p),
icon=icon,
)
try:
locked = self._params.get_bool(param + "Lock")
except UnknownKeyName:
locked = False
toggle.action_item.set_enabled(not locked)
# Make description callable for live translation
additional_desc = ""
if needs_restart and not locked:
additional_desc = tr("Changing this setting will restart openpilot if the car is powered on.")
toggle.set_description(lambda og_desc=toggle.description, add_desc=additional_desc: tr(og_desc) + (" " + tr(add_desc) if add_desc else ""))
# track for engaged state updates
if locked:
self._locked_toggles.add(param)
self._toggles[param] = toggle
# insert longitudinal personality after NDOG toggle
if param == "DisengageOnAccelerator":
self._toggles["LongitudinalPersonality"] = self._long_personality_setting
self._update_experimental_mode_icon()
self._scroller = Scroller(list(self._toggles.values()), line_separator=True, spacing=0)
ui_state.add_engaged_transition_callback(self._update_toggles)
def _update_state(self):
if ui_state.sm.updated["selfdriveState"]:
personality = PERSONALITY_TO_INT[ui_state.sm["selfdriveState"].personality]
if personality != ui_state.personality and ui_state.started:
self._long_personality_setting.action_item.set_selected_button(personality)
ui_state.personality = personality
def show_event(self):
self._scroller.show_event()
self._update_toggles()
def _update_toggles(self):
ui_state.update_params()
e2e_description = tr(
"openpilot defaults to driving in chill mode. Experimental mode enables alpha-level features that aren't ready for chill mode. " +
"Experimental features are listed below:<br>" +
"<h4>End-to-End Longitudinal Control</h4><br>" +
"Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. " +
"Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; " +
"mistakes should be expected.<br>" +
"<h4>New Driving Visualization</h4><br>" +
"The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. " +
"The Experimental mode logo will also be shown in the top right corner."
)
if ui_state.CP is not None:
if ui_state.has_longitudinal_control:
self._toggles["ExperimentalMode"].action_item.set_enabled(True)
self._toggles["ExperimentalMode"].set_description(e2e_description)
self._long_personality_setting.action_item.set_enabled(True)
else:
# no long for now
self._toggles["ExperimentalMode"].action_item.set_enabled(False)
self._toggles["ExperimentalMode"].action_item.set_state(False)
self._long_personality_setting.action_item.set_enabled(False)
self._params.remove("ExperimentalMode")
unavailable = tr("Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control.")
long_desc = unavailable + " " + tr("openpilot longitudinal control may come in a future update.")
if ui_state.CP.alphaLongitudinalAvailable:
if self._is_release:
long_desc = unavailable + " " + tr("An alpha version of openpilot longitudinal control can be tested, along with " +
"Experimental mode, on non-release branches.")
else:
long_desc = tr("Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode.")
self._toggles["ExperimentalMode"].set_description("<b>" + long_desc + "</b><br><br>" + e2e_description)
else:
self._toggles["ExperimentalMode"].set_description(e2e_description)
self._update_experimental_mode_icon()
# TODO: make a param control list item so we don't need to manage internal state as much here
# refresh toggles from params to mirror external changes
for param in self._toggle_defs:
self._toggles[param].action_item.set_state(self._params.get_bool(param))
# these toggles need restart, block while engaged
for toggle_def in self._toggle_defs:
if self._toggle_defs[toggle_def][3] and toggle_def not in self._locked_toggles:
self._toggles[toggle_def].action_item.set_enabled(not ui_state.engaged)
def _render(self, rect):
self._scroller.render(rect)
def _update_experimental_mode_icon(self):
icon = "experimental.png" if self._toggles["ExperimentalMode"].action_item.get_state() else "experimental_white.png"
self._toggles["ExperimentalMode"].set_icon(icon)
def _handle_experimental_mode_toggle(self, state: bool):
confirmed = self._params.get_bool("ExperimentalModeConfirmed")
if state and not confirmed:
def confirm_callback(result: int):
if result == DialogResult.CONFIRM:
self._params.put_bool("ExperimentalMode", True)
self._params.put_bool("ExperimentalModeConfirmed", True)
else:
self._toggles["ExperimentalMode"].action_item.set_state(False)
self._update_experimental_mode_icon()
# show confirmation dialog
content = (f"<h1>{self._toggles['ExperimentalMode'].title}</h1><br>" +
f"<p>{self._toggles['ExperimentalMode'].description}</p>")
dlg = ConfirmDialog(content, tr("Enable"), rich=True)
gui_app.set_modal_overlay(dlg, callback=confirm_callback)
else:
self._update_experimental_mode_icon()
self._params.put_bool("ExperimentalMode", state)
def _toggle_callback(self, state: bool, param: str):
if param == "ExperimentalMode":
self._handle_experimental_mode_toggle(state)
return
self._params.put_bool(param, state)
if self._toggle_defs[param][3]:
self._params.put_bool("OnroadCycleRequested", True)
def _set_longitudinal_personality(self, button_index: int):
self._params.put("LongitudinalPersonality", button_index)
+49 -28
View File
@@ -4,7 +4,8 @@ from dataclasses import dataclass
from collections.abc import Callable
from cereal import log
from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos
from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos, FONT_SCALE
from openpilot.system.ui.lib.multilang import tr, tr_noop
from openpilot.system.ui.lib.text_measure import measure_text_cached
from openpilot.system.ui.widgets import Widget
@@ -23,7 +24,6 @@ NetworkType = log.DeviceState.NetworkType
# Color scheme
class Colors:
SIDEBAR_BG = rl.Color(57, 57, 57, 255)
WHITE = rl.WHITE
WHITE_DIM = rl.Color(255, 255, 255, 85)
GRAY = rl.Color(84, 84, 84, 255)
@@ -40,13 +40,13 @@ class Colors:
NETWORK_TYPES = {
NetworkType.none: "Offline",
NetworkType.wifi: "WiFi",
NetworkType.cell2G: "2G",
NetworkType.cell3G: "3G",
NetworkType.cell4G: "LTE",
NetworkType.cell5G: "5G",
NetworkType.ethernet: "Ethernet",
NetworkType.none: tr_noop("--"),
NetworkType.wifi: tr_noop("Wi-Fi"),
NetworkType.ethernet: tr_noop("ETH"),
NetworkType.cell2G: tr_noop("2G"),
NetworkType.cell3G: tr_noop("3G"),
NetworkType.cell4G: tr_noop("LTE"),
NetworkType.cell5G: tr_noop("5G"),
}
@@ -68,27 +68,33 @@ class Sidebar(Widget):
self._net_type = NETWORK_TYPES.get(NetworkType.none)
self._net_strength = 0
self._temp_status = MetricData("TEMP", "GOOD", Colors.GOOD)
self._panda_status = MetricData("VEHICLE", "ONLINE", Colors.GOOD)
self._connect_status = MetricData("CONNECT", "OFFLINE", Colors.WARNING)
self._temp_status = MetricData(tr_noop("TEMP"), tr_noop("GOOD"), Colors.GOOD)
self._panda_status = MetricData(tr_noop("VEHICLE"), tr_noop("ONLINE"), Colors.GOOD)
self._connect_status = MetricData(tr_noop("CONNECT"), tr_noop("OFFLINE"), Colors.WARNING)
self._recording_audio = False
self._home_img = gui_app.texture("images/button_home.png", HOME_BTN.width, HOME_BTN.height)
self._flag_img = gui_app.texture("images/button_flag.png", HOME_BTN.width, HOME_BTN.height)
self._settings_img = gui_app.texture("images/button_settings.png", SETTINGS_BTN.width, SETTINGS_BTN.height)
self._mic_img = gui_app.texture("icons/microphone.png", 30, 30)
self._mic_indicator_rect = rl.Rectangle(0, 0, 0, 0)
self._font_regular = gui_app.font(FontWeight.NORMAL)
self._font_bold = gui_app.font(FontWeight.SEMI_BOLD)
# Callbacks
self._on_settings_click: Callable | None = None
self._on_flag_click: Callable | None = None
self._open_settings_callback: Callable | None = None
def set_callbacks(self, on_settings: Callable | None = None, on_flag: Callable | None = None):
def set_callbacks(self, on_settings: Callable | None = None, on_flag: Callable | None = None,
open_settings: Callable | None = None):
self._on_settings_click = on_settings
self._on_flag_click = on_flag
self._open_settings_callback = open_settings
def _render(self, rect: rl.Rectangle):
# Background
rl.draw_rectangle_rec(rect, Colors.SIDEBAR_BG)
rl.draw_rectangle_rec(rect, rl.BLACK)
self._draw_buttons(rect)
self._draw_network_indicator(rect)
@@ -101,13 +107,14 @@ class Sidebar(Widget):
device_state = sm['deviceState']
self._recording_audio = ui_state.recording_audio
self._update_network_status(device_state)
self._update_temperature_status(device_state)
self._update_connection_status(device_state)
self._update_panda_status()
def _update_network_status(self, device_state):
self._net_type = NETWORK_TYPES.get(device_state.networkType.raw, "Unknown")
self._net_type = NETWORK_TYPES.get(device_state.networkType.raw, tr_noop("Unknown"))
strength = device_state.networkStrength
self._net_strength = max(0, min(5, strength.raw + 1)) if strength > 0 else 0
@@ -115,26 +122,26 @@ class Sidebar(Widget):
thermal_status = device_state.thermalStatus
if thermal_status == ThermalStatus.green:
self._temp_status.update("TEMP", "GOOD", Colors.GOOD)
self._temp_status.update(tr_noop("TEMP"), tr_noop("GOOD"), Colors.GOOD)
elif thermal_status == ThermalStatus.yellow:
self._temp_status.update("TEMP", "OK", Colors.WARNING)
self._temp_status.update(tr_noop("TEMP"), tr_noop("OK"), Colors.WARNING)
else:
self._temp_status.update("TEMP", "HIGH", Colors.DANGER)
self._temp_status.update(tr_noop("TEMP"), tr_noop("HIGH"), Colors.DANGER)
def _update_connection_status(self, device_state):
last_ping = device_state.lastAthenaPingTime
if last_ping == 0:
self._connect_status.update("CONNECT", "OFFLINE", Colors.WARNING)
self._connect_status.update(tr_noop("CONNECT"), tr_noop("OFFLINE"), Colors.WARNING)
elif time.monotonic_ns() - last_ping < 80_000_000_000: # 80 seconds in nanoseconds
self._connect_status.update("CONNECT", "ONLINE", Colors.GOOD)
self._connect_status.update(tr_noop("CONNECT"), tr_noop("ONLINE"), Colors.GOOD)
else:
self._connect_status.update("CONNECT", "ERROR", Colors.DANGER)
self._connect_status.update(tr_noop("CONNECT"), tr_noop("ERROR"), Colors.DANGER)
def _update_panda_status(self):
if ui_state.panda_type == log.PandaState.PandaType.unknown:
self._panda_status.update("NO", "PANDA", Colors.DANGER)
self._panda_status.update(tr_noop("NO"), tr_noop("PANDA"), Colors.DANGER)
else:
self._panda_status.update("VEHICLE", "ONLINE", Colors.GOOD)
self._panda_status.update(tr_noop("VEHICLE"), tr_noop("ONLINE"), Colors.GOOD)
def _handle_mouse_release(self, mouse_pos: MousePos):
if rl.check_collision_point_rec(mouse_pos, SETTINGS_BTN):
@@ -143,6 +150,9 @@ class Sidebar(Widget):
elif rl.check_collision_point_rec(mouse_pos, HOME_BTN) and ui_state.started:
if self._on_flag_click:
self._on_flag_click()
elif self._recording_audio and rl.check_collision_point_rec(mouse_pos, self._mic_indicator_rect):
if self._open_settings_callback:
self._open_settings_callback()
def _draw_buttons(self, rect: rl.Rectangle):
mouse_pos = rl.get_mouse_position()
@@ -160,6 +170,17 @@ class Sidebar(Widget):
tint = Colors.BUTTON_PRESSED if (ui_state.started and flag_pressed) else Colors.BUTTON_NORMAL
rl.draw_texture(button_img, int(HOME_BTN.x), int(HOME_BTN.y), tint)
# Microphone button
if self._recording_audio:
self._mic_indicator_rect = rl.Rectangle(rect.x + rect.width - 130, rect.y + 245, 75, 40)
mic_pressed = mouse_down and rl.check_collision_point_rec(mouse_pos, self._mic_indicator_rect)
bg_color = rl.Color(Colors.DANGER.r, Colors.DANGER.g, Colors.DANGER.b, int(255 * 0.65)) if mic_pressed else Colors.DANGER
rl.draw_rectangle_rounded(self._mic_indicator_rect, 1, 10, bg_color)
rl.draw_texture(self._mic_img, int(self._mic_indicator_rect.x + (self._mic_indicator_rect.width - self._mic_img.width) / 2),
int(self._mic_indicator_rect.y + (self._mic_indicator_rect.height - self._mic_img.height) / 2), Colors.WHITE)
def _draw_network_indicator(self, rect: rl.Rectangle):
# Signal strength dots
x_start = rect.x + 58
@@ -176,7 +197,7 @@ class Sidebar(Widget):
# Network type text
text_y = rect.y + 247
text_pos = rl.Vector2(rect.x + 58, text_y)
rl.draw_text_ex(self._font_regular, self._net_type, text_pos, FONT_SIZE, 0, Colors.WHITE)
rl.draw_text_ex(self._font_regular, tr(self._net_type), text_pos, FONT_SIZE, 0, Colors.WHITE)
def _draw_metrics(self, rect: rl.Rectangle):
metrics = [(self._temp_status, 338), (self._panda_status, 496), (self._connect_status, 654)]
@@ -189,15 +210,15 @@ class Sidebar(Widget):
# Draw colored left edge (clipped rounded rectangle)
edge_rect = rl.Rectangle(metric_rect.x + 4, metric_rect.y + 4, 100, 118)
rl.begin_scissor_mode(int(metric_rect.x + 4), int(metric_rect.y), 18, int(metric_rect.height))
rl.draw_rectangle_rounded(edge_rect, 0.18, 10, metric.color)
rl.draw_rectangle_rounded(edge_rect, 0.3, 10, metric.color)
rl.end_scissor_mode()
# Draw border
rl.draw_rectangle_rounded_lines_ex(metric_rect, 0.15, 10, 2, Colors.METRIC_BORDER)
rl.draw_rectangle_rounded_lines_ex(metric_rect, 0.3, 10, 2, Colors.METRIC_BORDER)
# Draw label and value
labels = [metric.label, metric.value]
text_y = metric_rect.y + (metric_rect.height / 2 - len(labels) * FONT_SIZE)
labels = [tr(metric.label), tr(metric.value)]
text_y = metric_rect.y + (metric_rect.height / 2 - len(labels) * FONT_SIZE * FONT_SCALE)
for text in labels:
text_size = measure_text_cached(self._font_bold, text, FONT_SIZE)
text_y += text_size.y
+4
View File
@@ -1,12 +1,16 @@
import time
from functools import lru_cache
from openpilot.common.api import Api
from openpilot.common.time_helpers import system_time_valid
TOKEN_EXPIRY_HOURS = 2
@lru_cache(maxsize=1)
def _get_token(dongle_id: str, t: int):
if not system_time_valid():
raise RuntimeError("System time is not valid, cannot generate token")
return Api(dongle_id).get_token(expiry_hours=TOKEN_EXPIRY_HOURS)
+12 -9
View File
@@ -11,14 +11,14 @@ from openpilot.selfdrive.ui.lib.api_helpers import get_token
class PrimeType(IntEnum):
UNKNOWN = -2,
UNPAIRED = -1,
NONE = 0,
MAGENTA = 1,
LITE = 2,
BLUE = 3,
MAGENTA_NEW = 4,
PURPLE = 5,
UNKNOWN = -2
UNPAIRED = -1
NONE = 0
MAGENTA = 1
LITE = 2
BLUE = 3
MAGENTA_NEW = 4
PURPLE = 5
class PrimeState:
@@ -33,7 +33,6 @@ class PrimeState:
self._running = False
self._thread = None
self.start()
def _load_initial_state(self) -> PrimeType:
prime_type_str = os.getenv("PRIME_TYPE") or self._params.get("PrimeType")
@@ -96,5 +95,9 @@ class PrimeState:
with self._lock:
return bool(self.prime_type > PrimeType.NONE)
def is_paired(self) -> bool:
with self._lock:
return self.prime_type > PrimeType.UNPAIRED
def __del__(self):
self.stop()
-36
View File
@@ -1,36 +0,0 @@
#include <sys/resource.h>
#include <QApplication>
#include <QTranslator>
#include "system/hardware/hw.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/window.h"
#ifdef SUNNYPILOT
#include "selfdrive/ui/sunnypilot/qt/window.h"
#define MainWindow MainWindowSP
#else
#include "selfdrive/ui/qt/qt_window.h"
#endif
int main(int argc, char *argv[]) {
setpriority(PRIO_PROCESS, 0, -20);
qInstallMessageHandler(swagLogMessageHandler);
initApp(argc, argv);
QTranslator translator;
QString translation_file = QString::fromStdString(Params().get("LanguageSetting"));
if (!translator.load(QString(":/%1").arg(translation_file)) && translation_file.length()) {
qCritical() << "Failed to load translation file:" << translation_file;
}
QApplication a(argc, argv);
a.installTranslator(&translator);
MainWindow w;
setMainWindow(&w);
a.installEventFilter(&w);
return a.exec();
}
+46 -33
View File
@@ -4,10 +4,11 @@ from dataclasses import dataclass
from cereal import messaging, log
from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.system.hardware import TICI
from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_FPS
from openpilot.system.ui.lib.application import gui_app, FontWeight
from openpilot.system.ui.lib.multilang import tr
from openpilot.system.ui.lib.text_measure import measure_text_cached
from openpilot.system.ui.widgets import Widget
from openpilot.system.ui.widgets.label import gui_text_box
from openpilot.system.ui.widgets.label import Label
AlertSize = log.SelfdriveState.AlertSize
AlertStatus = log.SelfdriveState.AlertStatus
@@ -21,14 +22,19 @@ ALERT_FONT_SMALL = 66
ALERT_FONT_MEDIUM = 74
ALERT_FONT_BIG = 88
ALERT_HEIGHTS = {
AlertSize.small: 271,
AlertSize.mid: 420,
}
SELFDRIVE_STATE_TIMEOUT = 5 # Seconds
SELFDRIVE_UNRESPONSIVE_TIMEOUT = 10 # Seconds
# Constants
ALERT_COLORS = {
AlertStatus.normal: rl.Color(0, 0, 0, 235), # Black
AlertStatus.userPrompt: rl.Color(0xFE, 0x8C, 0x34, 235), # Orange
AlertStatus.critical: rl.Color(0xC9, 0x22, 0x31, 235), # Red
AlertStatus.normal: rl.Color(0x15, 0x15, 0x15, 0xF1), # #151515 with alpha 0xF1
AlertStatus.userPrompt: rl.Color(0xDA, 0x6F, 0x25, 0xF1), # #DA6F25 with alpha 0xF1
AlertStatus.critical: rl.Color(0xC9, 0x22, 0x31, 0xF1), # #C92231 with alpha 0xF1
}
@@ -42,24 +48,24 @@ class Alert:
# Pre-defined alert instances
ALERT_STARTUP_PENDING = Alert(
text1="openpilot Unavailable",
text2="Waiting to start",
text1=tr("openpilot Unavailable"),
text2=tr("Waiting to start"),
size=AlertSize.mid,
status=AlertStatus.normal,
)
ALERT_CRITICAL_TIMEOUT = Alert(
text1="TAKE CONTROL IMMEDIATELY",
text2="System Unresponsive",
text1=tr("TAKE CONTROL IMMEDIATELY"),
text2=tr("System Unresponsive"),
size=AlertSize.full,
status=AlertStatus.critical,
)
ALERT_CRITICAL_REBOOT = Alert(
text1="System Unresponsive",
text2="Reboot Device",
size=AlertSize.full,
status=AlertStatus.critical,
text1=tr("System Unresponsive"),
text2=tr("Reboot Device"),
size=AlertSize.mid,
status=AlertStatus.normal,
)
@@ -69,14 +75,20 @@ class AlertRenderer(Widget):
self.font_regular: rl.Font = gui_app.font(FontWeight.NORMAL)
self.font_bold: rl.Font = gui_app.font(FontWeight.BOLD)
# font size is set dynamically
self._full_text1_label = Label("", font_size=0, font_weight=FontWeight.BOLD, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER,
text_alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_TOP)
self._full_text2_label = Label("", font_size=ALERT_FONT_BIG, text_alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER,
text_alignment_vertical=rl.GuiTextAlignmentVertical.TEXT_ALIGN_TOP)
def get_alert(self, sm: messaging.SubMaster) -> Alert | None:
"""Generate the current alert based on selfdrive state."""
ss = sm['selfdriveState']
# Check if selfdriveState messages have stopped arriving
recv_frame = sm.recv_frame['selfdriveState']
if not sm.updated['selfdriveState']:
recv_frame = sm.recv_frame['selfdriveState']
time_since_onroad = (sm.frame - ui_state.started_frame) / DEFAULT_FPS
time_since_onroad = time.monotonic() - ui_state.started_time
# 1. Never received selfdriveState since going onroad
waiting_for_startup = recv_frame < ui_state.started_frame
@@ -95,13 +107,17 @@ class AlertRenderer(Widget):
if ss.alertSize == 0:
return None
# Don't get old alert
if recv_frame < ui_state.started_frame:
return None
# Return current alert
return Alert(text1=ss.alertText1, text2=ss.alertText2, size=ss.alertSize.raw, status=ss.alertStatus.raw)
def _render(self, rect: rl.Rectangle) -> bool:
def _render(self, rect: rl.Rectangle):
alert = self.get_alert(ui_state.sm)
if not alert:
return False
return
alert_rect = self._get_alert_rect(rect, alert.size)
self._draw_background(alert_rect, alert)
@@ -113,21 +129,14 @@ class AlertRenderer(Widget):
alert_rect.height - 2 * ALERT_PADDING
)
self._draw_text(text_rect, alert)
return True
def _get_alert_rect(self, rect: rl.Rectangle, size: int) -> rl.Rectangle:
if size == AlertSize.full:
return rect
height = (ALERT_FONT_MEDIUM + 2 * ALERT_PADDING if size == AlertSize.small else
ALERT_FONT_BIG + ALERT_LINE_SPACING + ALERT_FONT_SMALL + 2 * ALERT_PADDING)
return rl.Rectangle(
rect.x + ALERT_MARGIN,
rect.y + rect.height - ALERT_MARGIN - height,
rect.width - 2 * ALERT_MARGIN,
height
)
h = ALERT_HEIGHTS.get(size, rect.height)
return rl.Rectangle(rect.x + ALERT_MARGIN, rect.y + rect.height - h + ALERT_MARGIN,
rect.width - ALERT_MARGIN * 2, h - ALERT_MARGIN * 2)
def _draw_background(self, rect: rl.Rectangle, alert: Alert) -> None:
color = ALERT_COLORS.get(alert.status, ALERT_COLORS[AlertStatus.normal])
@@ -150,13 +159,17 @@ class AlertRenderer(Widget):
else:
is_long = len(alert.text1) > 15
font_size1 = 132 if is_long else 177
align_ment = rl.GuiTextAlignment.TEXT_ALIGN_CENTER
vertical_align = rl.GuiTextAlignmentVertical.TEXT_ALIGN_MIDDLE
text_rect = rl.Rectangle(rect.x, rect.y, rect.width, rect.height // 2)
gui_text_box(text_rect, alert.text1, font_size1, alignment=align_ment, alignment_vertical=vertical_align, font_weight=FontWeight.BOLD)
text_rect.y = rect.y + rect.height // 2
gui_text_box(text_rect, alert.text2, ALERT_FONT_BIG, alignment=align_ment)
top_offset = 200 if is_long or '\n' in alert.text1 else 270
title_rect = rl.Rectangle(rect.x, rect.y + top_offset, rect.width, 600)
self._full_text1_label.set_font_size(font_size1)
self._full_text1_label.set_text(alert.text1)
self._full_text1_label.render(title_rect)
bottom_offset = 361 if is_long else 420
subtitle_rect = rl.Rectangle(rect.x, rect.y + rect.height - bottom_offset, rect.width, 300)
self._full_text2_label.set_text(alert.text2)
self._full_text2_label.render(subtitle_rect)
def _draw_centered(self, text, rect, font, font_size, center_y=True, color=rl.WHITE) -> None:
text_size = measure_text_cached(font, text, font_size)
+43 -36
View File
@@ -1,9 +1,10 @@
import time
import numpy as np
import pyray as rl
from collections.abc import Callable
from cereal import log
from cereal import log, messaging
from msgq.visionipc import VisionStreamType
from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus, UI_BORDER_SIZE
from openpilot.selfdrive.ui import UI_BORDER_SIZE
from openpilot.selfdrive.ui.ui_state import ui_state, UIStatus
from openpilot.selfdrive.ui.onroad.alert_renderer import AlertRenderer
from openpilot.selfdrive.ui.onroad.driver_state import DriverStateRenderer
from openpilot.selfdrive.ui.onroad.hud_renderer import HudRenderer
@@ -20,13 +21,14 @@ WIDE_CAM = VisionStreamType.VISION_STREAM_WIDE_ROAD
DEFAULT_DEVICE_CAMERA = DEVICE_CAMERAS["tici", "ar0231"]
BORDER_COLORS = {
UIStatus.DISENGAGED: rl.Color(0x17, 0x33, 0x49, 0xC8), # Blue for disengaged state
UIStatus.OVERRIDE: rl.Color(0x91, 0x9B, 0x95, 0xF1), # Gray for override state
UIStatus.ENGAGED: rl.Color(0x17, 0x86, 0x44, 0xF1), # Green for engaged state
UIStatus.DISENGAGED: rl.Color(0x12, 0x28, 0x39, 0xFF), # Blue for disengaged state
UIStatus.OVERRIDE: rl.Color(0x89, 0x92, 0x8D, 0xFF), # Gray for override state
UIStatus.ENGAGED: rl.Color(0x16, 0x7F, 0x40, 0xFF), # Green for engaged state
}
WIDE_CAM_MAX_SPEED = 10.0 # m/s (22 mph)
ROAD_CAM_MIN_SPEED = 15.0 # m/s (34 mph)
INF_POINT = np.array([1000.0, 0.0, 0.0])
class AugmentedRoadView(CameraView):
@@ -38,9 +40,7 @@ class AugmentedRoadView(CameraView):
self.view_from_calib = view_frame_from_device_frame.copy()
self.view_from_wide_calib = view_frame_from_device_frame.copy()
self._last_calib_time: float = 0
self._last_rect_dims = (0.0, 0.0)
self._last_stream_type = stream_type
self._matrix_cache_key = (0, 0.0, 0.0, stream_type)
self._cached_matrix: np.ndarray | None = None
self._content_rect = rl.Rectangle()
@@ -49,14 +49,12 @@ class AugmentedRoadView(CameraView):
self.alert_renderer = AlertRenderer()
self.driver_state_renderer = DriverStateRenderer()
# Callbacks
self._click_callback: Callable | None = None
def set_callbacks(self, on_click: Callable | None = None):
self._click_callback = on_click
# debug
self._pm = messaging.PubMaster(['uiDebug'])
def _render(self, rect):
# Only render when system is started to avoid invalid data access
start_draw = time.monotonic()
if not ui_state.started:
return
@@ -73,9 +71,6 @@ class AugmentedRoadView(CameraView):
rect.height - 2 * UI_BORDER_SIZE,
)
# Draw colored border based on driving state
self._draw_border(rect)
# Enable scissor mode to clip all rendering within content rectangle boundaries
# This creates a rendering viewport that prevents graphics from drawing outside the border
rl.begin_scissor_mode(
@@ -91,8 +86,8 @@ class AugmentedRoadView(CameraView):
# Draw all UI overlays
self.model_renderer.render(self._content_rect)
self._hud_renderer.render(self._content_rect)
if not self.alert_renderer.render(self._content_rect):
self.driver_state_renderer.render(self._content_rect)
self.alert_renderer.render(self._content_rect)
self.driver_state_renderer.render(self._content_rect)
# Custom UI extension point - add custom overlays here
# Use self._content_rect for positioning within camera bounds
@@ -100,15 +95,29 @@ class AugmentedRoadView(CameraView):
# End clipping region
rl.end_scissor_mode()
# Handle click events if no HUD interaction occurred
if not self._hud_renderer.handle_mouse_event():
if self._click_callback and rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT):
if rl.check_collision_point_rec(rl.get_mouse_position(), self._content_rect):
self._click_callback()
# Draw colored border based on driving state
self._draw_border(rect)
# publish uiDebug
msg = messaging.new_message('uiDebug')
msg.uiDebug.drawTimeMillis = (time.monotonic() - start_draw) * 1000
self._pm.send('uiDebug', msg)
def _handle_mouse_press(self, _):
if not self._hud_renderer.user_interacting() and self._click_callback is not None:
self._click_callback()
def _handle_mouse_release(self, _):
# We only call click callback on press if not interacting with HUD
pass
def _draw_border(self, rect: rl.Rectangle):
rl.draw_rectangle_lines_ex(rect, UI_BORDER_SIZE, rl.BLACK)
border_roundness = 0.12
border_color = BORDER_COLORS.get(ui_state.status, BORDER_COLORS[UIStatus.DISENGAGED])
rl.draw_rectangle_lines_ex(rect, UI_BORDER_SIZE, border_color)
border_rect = rl.Rectangle(rect.x + UI_BORDER_SIZE, rect.y + UI_BORDER_SIZE,
rect.width - 2 * UI_BORDER_SIZE, rect.height - 2 * UI_BORDER_SIZE)
rl.draw_rectangle_rounded_lines_ex(border_rect, border_roundness, 10, UI_BORDER_SIZE, border_color)
def _switch_stream_if_needed(self, sm):
if sm['selfdriveState'].experimentalMode and WIDE_CAM in self.available_streams:
@@ -151,12 +160,13 @@ class AugmentedRoadView(CameraView):
def _calc_frame_matrix(self, rect: rl.Rectangle) -> np.ndarray:
# Check if we can use cached matrix
calib_time = ui_state.sm.recv_frame['liveCalibration']
current_dims = (self._content_rect.width, self._content_rect.height)
if (self._last_calib_time == calib_time and
self._last_rect_dims == current_dims and
self._last_stream_type == self.stream_type and
self._cached_matrix is not None):
cache_key = (
ui_state.sm.recv_frame['liveCalibration'],
self._content_rect.width,
self._content_rect.height,
self.stream_type
)
if cache_key == self._matrix_cache_key and self._cached_matrix is not None:
return self._cached_matrix
# Get camera configuration
@@ -167,9 +177,8 @@ class AugmentedRoadView(CameraView):
zoom = 2.0 if is_wide_camera else 1.1
# Calculate transforms for vanishing point
inf_point = np.array([1000.0, 0.0, 0.0])
calib_transform = intrinsic @ calibration
kep = calib_transform @ inf_point
kep = calib_transform @ INF_POINT
# Calculate center points and dimensions
x, y = self._content_rect.x, self._content_rect.y
@@ -192,9 +201,7 @@ class AugmentedRoadView(CameraView):
x_offset, y_offset = 0, 0
# Cache the computed transformation matrix to avoid recalculations
self._last_calib_time = calib_time
self._last_rect_dims = current_dims
self._last_stream_type = self.stream_type
self._matrix_cache_key = cache_key
self._cached_matrix = np.array([
[zoom * 2 * cx / w, 0, -x_offset / w * 2],
[0, zoom * 2 * cy / h, -y_offset / h * 2],
+21
View File
@@ -8,6 +8,7 @@ from openpilot.system.hardware import TICI
from openpilot.system.ui.lib.application import gui_app
from openpilot.system.ui.lib.egl import init_egl, create_egl_image, destroy_egl_image, bind_egl_image_to_texture, EGLImage
from openpilot.system.ui.widgets import Widget
from openpilot.selfdrive.ui.ui_state import ui_state
CONNECTION_RETRY_INTERVAL = 0.2 # seconds between connection attempts
@@ -67,6 +68,7 @@ else:
class CameraView(Widget):
def __init__(self, name: str, stream_type: VisionStreamType):
super().__init__()
# TODO: implement a receiver and connect thread
self._name = name
# Primary stream
self.client = VisionIpcClient(name, stream_type, conflate=True)
@@ -103,6 +105,20 @@ class CameraView(Widget):
self.egl_texture = rl.load_texture_from_image(temp_image)
rl.unload_image(temp_image)
ui_state.add_offroad_transition_callback(self._offroad_transition)
def _offroad_transition(self):
# Reconnect if not first time going onroad
if ui_state.is_onroad() and self.frame is not None:
# Prevent old frames from showing when going onroad. Qt has a separate thread
# which drains the VisionIpcClient SubSocket for us. Re-connecting is not enough
# and only clears internal buffers, not the message queue.
self.frame = None
self.available_streams.clear()
if self.client:
del self.client
self.client = VisionIpcClient(self._name, self._stream_type, conflate=True)
def _set_placeholder_color(self, color: rl.Color):
"""Set a placeholder color to be drawn when no frame is available."""
self._placeholder_color = color
@@ -139,6 +155,8 @@ class CameraView(Widget):
if self.shader and self.shader.id:
rl.unload_shader(self.shader)
self.frame = None
self.available_streams.clear()
self.client = None
def __del__(self):
@@ -175,6 +193,9 @@ class CameraView(Widget):
if buffer:
self._texture_needs_update = True
self.frame = buffer
elif not self.client.is_connected():
# ensure we clear the displayed frame when the connection is lost
self.frame = None
if not self.frame:
self._draw_placeholder(rect)

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