Compare commits

...

461 Commits

Author SHA1 Message Date
firestar5683 847d19d029 Microslop 2026-06-03 22:00:28 -05:00
firestar5683 1554392614 build 2026-06-02 20:08:09 -05:00
firestar5683 2486728e11 hi 2026-06-02 20:05:38 -05:00
firestarsdog 3cf48ae3c8 BigUI WIP: Some CEM Stuff 2026-06-02 17:49:10 -04:00
firestar5683 e44e612465 build 2026-06-02 14:26:19 -05:00
whoisdomi 78e9b5139c Faster Leads for $300 Trebek, and say hi to your motha
Faster takeoffs after leads and green lights.
2026-06-02 14:12:21 -05:00
whoisdomi d2a8163f32 Human Lane Change ui fix
Toggled on, would show adjecent radar leads, even if that toggle was off.
Now gated to its toggle in developer ui.
2026-06-02 14:12:20 -05:00
firestar5683 9f30661f6d long planner / i5 / sonata 2026-06-02 11:18:34 -05:00
firestar5683 5632559e5a fcw 2026-06-02 10:31:16 -05:00
firestar5683 ae2f502767 g90 long 2026-06-02 00:29:10 -05:00
firestarsdog 669143a178 BUICK_LACROSSE_ASCM 2026-06-02 00:07:16 -04:00
firestar5683 54cb76ddfd build 2026-06-01 20:52:31 -05:00
whoisdomi fabf122627 Wheel controls qt fix 2026-06-01 20:50:11 -05:00
whoisdomi 3486c695b8 QT StarPilot Logo 2026-06-01 20:50:11 -05:00
whoisdomi 6eb513501d Fire Firehose 2026-06-01 20:50:11 -05:00
whoisdomi 45060a07b7 Add CC Main and LKAS buttons to Wheel Controls
Clean up so all buttons are handled from wheel controls
2026-06-01 20:50:11 -05:00
firestarsdog 7f5e78fd8f BigUI WIP: Delete Stuff 2026-06-01 02:51:51 -04:00
firestarsdog b178e67ae5 BigUI WIP: Scale up dialogs 2026-06-01 02:31:32 -04:00
firestarsdog 026c83fb1b BigUI WIP: Align System Settings, again... 2026-06-01 02:13:23 -04:00
firestarsdog c7b3251918 BigUI WIP: System Settings Bump 2026-06-01 01:46:36 -04:00
firestarsdog 14f94151a5 BigUP WIP: System Settings Pass 2026-06-01 01:19:50 -04:00
firestar5683 6dc473713c i5 2026-06-01 00:10:14 -05:00
firestarsdog 594f525c0c BigUI WIP: System Settings Cleanup Pass 2026-06-01 00:39:18 -04:00
firestar5683 2132a5ceb0 build 2026-05-31 21:06:39 -05:00
firestar5683 5d86b7ac5b IsRHD 2026-05-31 21:03:42 -05:00
firestarsdog 0e7123d632 BigUI WIP: Huh need this afterall 2026-05-31 16:56:39 -04:00
firestarsdog b96c9290f7 BigUI WIP: Settings Cleanup 2026-05-31 16:48:12 -04:00
firestarsdog 416b5e3cd2 BigUI WIP: System Settings update 2026-05-31 15:57:01 -04:00
firestar5683 8a3a872a93 build 2026-05-31 13:45:41 -05:00
firestar5683 6f5690618f param 2026-05-31 13:42:11 -05:00
firestarsdog fad4434104 Expose Python UI Screen Timeout Variables 2026-05-31 14:40:08 -04:00
firestarsdog 86359bacab BigUI WIP: Sounds Default=Auto 2026-05-31 14:00:03 -04:00
firestarsdog a7d8d7a544 BigUP WIP: Sound Polish 2026-05-31 13:53:06 -04:00
firestar5683 86d059bdb4 long 2026-05-31 12:37:09 -05:00
firestar5683 caa5f41b1e radars 2026-05-31 12:26:54 -05:00
firestar5683 fcda700625 nav fixes 2026-05-31 12:14:04 -05:00
firestarsdog cdca40f018 BigUI WIP: Sound Panel Alignment 2026-05-31 04:27:57 -04:00
firestarsdog 736c344cdd BigUI WIP: Sheep > Goat 2026-05-31 02:27:54 -04:00
firestarsdog a28250c81b BigUI WIP: Icons and removal of magic math 2026-05-31 02:14:32 -04:00
firestar5683 b406a1a9d8 nightly roundup 2026-05-31 00:55:43 -05:00
firestarsdog a44e306343 Hm.. XT5? lol 2026-05-30 23:00:45 -04:00
firestarsdog ecbed0f4f8 BigUI WIP: Remove dock lol 2026-05-30 21:31:43 -04:00
firestar5683 04273eefad above 2026-05-30 14:41:03 -05:00
firestar5683 bd5436c55f nav / planner / sonata buttons 2026-05-30 14:26:09 -05:00
firestarsdog 6ca397b840 BigUI WIP: Optimizations 2026-05-30 03:24:04 -04:00
firestarsdog cc2b53a6e9 BigUI WIP: Drop starpilot_icon 2026-05-29 18:10:59 -04:00
firestar5683 6c4717cc39 g90test 2026-05-29 16:07:34 -05:00
firestar5683 6afdc99dd5 redneck 2026-05-29 15:57:20 -05:00
firestarsdog 07fcbbdab8 BigUI WIP: Poof 2026-05-29 16:38:59 -04:00
firestar5683 d55d1bff69 fort hey 2026-05-29 14:42:42 -05:00
firestarsdog a80d3680d0 BigUI WIP: Remove icon code 2026-05-29 15:36:11 -04:00
firestar5683 9673c72d44 bleehhhhh 2026-05-29 14:29:49 -05:00
firestar5683 7051b79674 nav 2026-05-29 14:15:46 -05:00
elkoled e7fa0f51a6 comment
(cherry picked from commit 81fceadb7817b831e47e679a07f515b02950e6e8)
2026-05-29 13:56:21 -05:00
elkoled e9dc540f64 adjust fan setpoint
(cherry picked from commit 269d2189b2383fa5b7c0d5e7cab4fb527f29a636)
2026-05-29 13:56:18 -05:00
firestar5683 e26a91147d build 2026-05-29 13:50:51 -05:00
firestar5683 3de95a341a bokoblin loves mutton; I fix the button 2026-05-29 13:48:07 -05:00
firestar5683 dff6f898a8 done goofed 2026-05-29 12:07:46 -05:00
firestar5683 dfd614c31f build 2026-05-29 12:00:14 -05:00
whoisdomi 5020d33748 ui+selfdrived: pin UI after init, move selfdrived to core 5
Move the UI core-affinity pin to after MainWindow init so startup and
restart recovery can use all cores (pinning before init starved the
restarting UI on the contended little cores, stretching recovery from
~30s to minutes). Move selfdrived off the saturated core 4 onto core 5
so its 100 Hz loop is no longer starved, eliminating selfdrivedLagging.

Verified over a full drive: selfdrivedLagging=0, core4/core5 sustained
>=95% only 0.3%/1.2% of the time, UI restart recovery ~15s with the
device staying engaged, radar tracking unaffected.
2026-05-29 11:56:33 -05:00
firestarsdog bd687f353f BigUI WIP: Sine Bonis P1 2026-05-29 03:40:04 -04:00
firestar5683 ce19d2c5f0 Bat bat come under my hat 2026-05-29 01:18:27 -05:00
firestar5683 29ade037d3 Offer applies with enrollment and triple advantage 2026-05-29 00:55:19 -05:00
firestarsdog 75b1deaf60 BigUI WIP: Try dock 2026-05-28 21:48:23 -04:00
firestar5683 a9930207ba v16 2026-05-28 17:38:14 -05:00
firestar5683 742c104964 on 2026-05-28 15:42:42 -05:00
firestarsdog 4183698fc6 BigUI: Widget Scale 2026-05-28 16:05:59 -04:00
firestar5683 f06c82b2b8 ev6 2026-05-28 14:19:03 -05:00
firestarsdog b23b4384f1 BigUI WIP: Aethergauge offset location 2026-05-28 15:11:44 -04:00
firestar5683 93bad13469 patches 2026-05-28 13:31:59 -05:00
firestar5683 31e0a5b8cc volt bsm 2026-05-28 13:29:45 -05:00
firestar5683 732830b095 g90 radar 2026-05-28 13:18:20 -05:00
firestarsdog 43d42594ef BigUI WIP: Aethergauge Helpers 2026-05-28 13:58:40 -04:00
firestar5683 e61039bb67 DM ruh roh 2026-05-28 12:27:45 -05:00
firestar5683 8e8225b6ae Nav Long 2026-05-28 11:41:05 -05:00
firestar5683 01bd291b78 you used to drive me onto sidewalks 2026-05-28 10:41:46 -05:00
firestar5683 61e28a0ac9 build 2026-05-28 10:23:35 -05:00
firestar5683 c744f66b2a Optimus Prime 2026-05-28 10:21:01 -05:00
firestarsdog 9b177b25b5 Revert "test cycle"
This reverts commit b9d89a3e85.
2026-05-28 11:05:47 -04:00
firestarsdog b9d89a3e85 test cycle 2026-05-28 11:04:09 -04:00
firestar5683 903fd2264e build 2026-05-28 00:37:45 -05:00
firestar5683 0f8142a15c default on 2026-05-28 00:35:16 -05:00
firestar5683 b95b5b1d53 build 2026-05-28 00:12:16 -05:00
firestar5683 3d0c3a8a45 Powers You Never Knew You Had and What To Do With Them Now You've Wised Up 2026-05-28 00:09:41 -05:00
firestar5683 9af23999d1 navgal 2026-05-28 00:09:34 -05:00
firestar5683 74a9bb5a4e build 2026-05-28 00:09:34 -05:00
firestar5683 cf506e59b0 nav 2026-05-28 00:09:20 -05:00
firestar5683 68d7fd09e2 Legion 2026-05-27 23:26:52 -05:00
firestarsdog b70680aa86 Silverado CC 2026-05-27 23:47:46 -04:00
Dom 823ff696ab Merge pull request #59 from Thinkpad4by3/Dom
Dom
2026-05-27 21:55:13 -05:00
firestar5683 9b530d520c XT5 2026-05-27 21:37:23 -05:00
firestar5683 3d35284a49 build 2026-05-27 19:53:14 -05:00
Jason Jackrel f425d86457 update tire stiffness 2026-05-27 16:48:27 -07:00
Jason Jackrel 98a9373346 finally the volt tune 2026-05-27 16:47:18 -07:00
firestar5683 35a7e43045 blocky block 2026-05-27 17:40:56 -05:00
firestar5683 b535c0845e ioniq 5 radar 2026-05-27 17:40:56 -05:00
whoisdomi 0ed821d32d 25hz 2026-05-27 17:35:19 -05:00
whoisdomi 2317057411 50hz poll 2026-05-27 17:35:19 -05:00
whoisdomi dc3b39a198 are we there? 2026-05-27 17:35:19 -05:00
whoisdomi ce5611f24c is this radar data? 2026-05-27 17:35:19 -05:00
firestar5683 f819386399 happy wife happy life 2026-05-26 22:20:19 -05:00
firestar5683 e1812ec3d7 build 2026-05-26 21:59:47 -05:00
firestar5683 e9935624f7 smoosh smoosh 2026-05-26 21:59:46 -05:00
firestar5683 0fec51a5d9 volt 2026-05-26 18:58:11 -05:00
firestar5683 3fa2d370bf i6 2026-05-26 18:19:51 -05:00
firestar5683 0750072ef0 build 2026-05-26 16:05:46 -05:00
firestar5683 fb93198a92 logg 2026-05-26 16:02:58 -05:00
firestar5683 a4789c9f61 i6 2026-05-26 15:53:05 -05:00
firestar5683 7386073c71 build 2026-05-26 11:36:09 -05:00
firestar5683 7d8be7079c I h8 u nudgeless 2026-05-26 11:33:49 -05:00
firestar5683 148929d2f1 i5 2026-05-26 11:09:12 -05:00
firestar5683 13e4f301b9 oopsie 2026-05-26 10:42:59 -05:00
firestarsdog dc91ac13b1 BigUI WIP: Clip lanes 2026-05-26 02:15:52 -04:00
firestar5683 a129d591e7 hkg radar 2026-05-26 00:17:24 -05:00
firestar5683 8aa0cf327a build 2026-05-26 00:00:25 -05:00
firestar5683 b78e44303d CCNC 2026-05-25 23:57:56 -05:00
firestarsdog feba46db30 BigUI WIP: Onroad Path Cleanup 2026-05-26 00:56:31 -04:00
firestarsdog a8d501fcd9 BigUI WIP: Scissor Borders 2026-05-26 00:40:47 -04:00
firestarsdog fb17126f68 BigUI WIP: Consolidated Border Effects 2026-05-26 00:32:31 -04:00
firestar5683 693b1f08a3 color calib 2026-05-25 02:12:44 -05:00
firestar5683 6e81630e49 build 2026-05-25 01:42:33 -05:00
firestar5683 76dbf9412a cameradisableFullDisable 2026-05-25 01:40:01 -05:00
firestar5683 68123093c3 stop UI log 2026-05-25 00:48:26 -05:00
firestar5683 73cbee1b85 tune 2026-05-25 00:48:20 -05:00
firestar5683 0cb613b6ce volt aeb 2026-05-25 00:48:13 -05:00
firestarsdog 7834d15127 BigUI WIP: Curves again 2026-05-25 01:02:08 -04:00
firestarsdog 71ce49a301 BigUI WIP: Useless banner 2026-05-24 23:17:21 -04:00
firestarsdog fd7f75578d BigUI WIP: Remove white border on sign + less steppy movement 2026-05-24 03:59:11 -04:00
firestarsdog 4c8452de78 BigUI WIP: Update Curves Again 2026-05-24 01:48:08 -04:00
firestarsdog 7bebf0ef3c BigUI WIP: Force Stop -> Aethergauge 2026-05-24 01:01:31 -04:00
firestarsdog 56a0c4e0d9 BigUI WIP: Center Bar 2026-05-23 13:24:40 -04:00
firestarsdog 666b9d35d6 BigUI WIP: Curvey 2026-05-23 12:49:27 -04:00
firestarsdog 2ef8f8ebef BigUI WIP: Aethergauge Curvey 2026-05-23 05:41:52 -04:00
firestarsdog b88c886592 BigUI WIP: Aethergauge 2026-05-23 02:19:23 -04:00
firestarsdog e742482175 BigUI WIP: Wow, simpler glow 2026-05-23 01:06:58 -04:00
firestarsdog 8c63451137 BigUI WIP: Glow up
BigUI WIP: Glow up
2026-05-22 23:21:28 -04:00
firestar5683 a30a59331d toyota 2026-05-22 01:30:56 -05:00
firestar5683 95d074eb92 slow lead depart 2026-05-21 23:15:48 -05:00
firestar5683 103c44bfd8 Tůne 2026-05-21 22:35:02 -05:00
firestar5683 bfcd43452e deep 2026-05-21 14:41:38 -05:00
firestar5683 0c5cf54d57 elantra 2026-05-21 13:23:20 -05:00
firestarsdog 3f4f2a1813 BigUI WIP: Onroad Cleaner-er 2026-05-21 13:47:56 -04:00
firestar5683 a94c3b72d3 honda 2026-05-21 11:03:10 -05:00
firestar5683 c848b5f1b4 toyota 2026-05-21 10:03:12 -05:00
firestar5683 55f110351e elantra hybrid 2026-05-21 10:00:09 -05:00
firestar5683 58e7f84627 i6 2026-05-21 09:56:27 -05:00
firestarsdog db769226b8 BigUI WIP: The Trail We Blaze 2026-05-21 04:37:41 -04:00
firestar5683 bfcab984e2 toyota 2026-05-20 23:35:50 -05:00
firestar5683 b33a8dea15 Elantra Hybrid 2026-05-20 22:19:05 -05:00
firestar5683 5e00550c6a avalon surge 2026-05-20 22:00:25 -05:00
firestar5683 329b41a84b op16 2026-05-20 21:58:17 -05:00
firestar5683 5e74861311 genesis 2026-05-20 21:04:59 -05:00
firestar5683 70229486bb toyota 2026-05-20 20:56:39 -05:00
firestar5683 813507267c i6 brake adjust 2026-05-20 16:49:52 -05:00
firestar5683 f55402e033 OP16Deep 2026-05-20 15:22:08 -05:00
firestar5683 f3cf4d6969 build 2026-05-20 11:03:16 -05:00
firestar5683 da88d20b1e Toyota 2026-05-20 11:00:08 -05:00
firestar5683 b17bba7e93 Ioniq 6 2026-05-20 10:59:38 -05:00
firestar5683 c99a999f1a Add lateral resume delay settings surfaces and tests 2026-05-20 10:56:59 -05:00
prabhaavp c5c5253de5 Lateral Resume Delay Feature W/O Testing 2026-05-20 10:49:51 -05:00
firestar5683 1f8652d809 Fix wide camera disable runtime gating 2026-05-20 10:44:10 -05:00
prabhaavp 8846b907d7 Disable Widecam Feature
Disable Widecam Feature

Disable Widecam Feature

Disable Widecam Feature

Disable Widecam Feature
2026-05-20 10:39:06 -05:00
firestar5683 3d6c55962b genesis 2026-05-19 21:26:54 -05:00
firestarsdog 0b0f18399c We represent the Lollipop Guild 2026-05-19 19:23:34 -04:00
firestar5683 5856e77f3b i6 turn in 2026-05-19 16:54:16 -05:00
firestar5683 c80d9ffb99 non hda ioniq 2026-05-19 16:43:55 -05:00
firestar5683 1f84395539 toyota pedal 2026-05-19 12:42:41 -05:00
firestarsdog f3a7620188 StarPilot Tools Auth Cleanup 2026-05-19 10:57:20 -04:00
firestar5683 180b0c6768 g90 revert 2026-05-19 09:10:57 -05:00
firestarsdog 55a93013c7 BigUI WIP: Delete tuning gate 2026-05-19 03:21:58 -04:00
firestarsdog 982e742322 BigUI WIP: Always show developer, hide firehose 2026-05-19 03:19:19 -04:00
firestarsdog 3676191fc8 BigUI WIP: The Grinch 2026-05-19 03:11:43 -04:00
firestarsdog 5b19d38d88 BigUI WIP: Bah Hambug 2026-05-19 02:58:09 -04:00
firestarsdog 32c088ac00 BigUI WIP: Refinement/More Extraction 2026-05-19 01:16:53 -04:00
firestar5683 123adc7edc redneck and g90 attempt lkas 2026-05-18 20:21:22 -05:00
firestarsdog dbecce602c Follow The Yellow Brick Road 2026-05-18 19:16:31 -04:00
firestar5683 2b10dc21b0 2021 2026-05-18 15:22:35 -05:00
firestar5683 51701ecffc build 2026-05-18 13:05:07 -05:00
firestar5683 601b8855f6 redneck Hyundai 2026-05-18 12:58:14 -05:00
firestar5683 f56bfb7d4b build and kia forte 2026-05-18 11:49:28 -05:00
whoisdomi 3f8d368b87 Pending Speed Limit Cleanup
Fixed the speed limit display so it no longer flashes an empty speeds, and now flashes over the current speed for a cleaner look.
2026-05-18 11:19:06 -05:00
whoisdomi 70ccc64831 SLC abbreviated icons
Gives the options to display SLC sources as abbreviated and the option to only show sources with data in them to give a cleaner UI look.
2026-05-18 11:19:06 -05:00
whoisdomi 2f5aef1fb8 Remove Taco Tune Hacks
No longer needed toggle. Values.py do the same now
2026-05-18 11:19:06 -05:00
firestar5683 3581a9ecf0 final piece 2026-05-18 11:10:24 -05:00
firestarsdog ed06aa8920 Yellow Brick Road, Emerald City, Oz 2026-05-18 03:35:51 -04:00
firestar5683 5bb401772a Other edge case 2026-05-16 21:53:04 -05:00
firestar5683 5c30a69e10 final stop 2026-05-16 09:50:21 -05:00
firestar5683 be585f72a3 gen2 bolt 2026-05-15 21:39:32 -05:00
StarPilot Build Bot 309665f63e build 2026-05-16 00:20:57 +00:00
firestar5683 9d8a055a04 build change 2026-05-15 18:40:41 -05:00
firestar5683 45a1d3fa8e volt and old ioniq and new ioniq and honda 2026-05-15 18:34:50 -05:00
firestar5683 473e3efae9 túne 2026-05-15 12:42:50 -05:00
StarPilot Build Bot e2607ea6e8 build 2026-05-15 17:20:46 +00:00
firestarsdog 75dcc0772e Club Penguin 2.0 2026-05-15 12:19:08 -04:00
firestar5683 38ad334e12 Create compile_bluescreens.yaml 2026-05-15 10:22:30 -05:00
firestar5683 5fcc1bc196 delete old actions 2026-05-15 10:00:41 -05:00
firestar5683 5bd1977c89 A440 2026-05-15 01:14:06 -05:00
firestarsdog 3eb5db5b31 Speed Limit Controller : Deletion. Bye Bye. 2026-05-14 22:19:19 -04:00
StarPilot Build Bot aa84176c7d build 2026-05-14 23:54:40 +00:00
firestar5683 f30e540690 buildy build 2026-05-14 18:18:44 -05:00
firestar5683 1729d3cda3 i6 2026-05-14 10:08:41 -05:00
firestar5683 c7c10822fb woahhhhh buddy 2026-05-13 23:24:39 -05:00
firestar5683 6b39e4103e Sonata hybrid 2026-05-13 22:57:57 -05:00
firestar5683 5a90937c52 fixy 2026-05-13 20:48:29 -05:00
firestarsdog 69e7ed3eae SLC Wrong Speed Rejection 2026-05-13 20:40:14 -04:00
firestar5683 01bcb40c50 Longfixes 2026-05-13 13:51:25 -05:00
firestar5683 3ecb73dc8a i6 highway 2026-05-13 11:29:40 -05:00
firestar5683 06c00b0895 long bugs 2026-05-13 10:51:19 -05:00
firestar5683 3f8b47aa91 planner hesitations 2026-05-12 22:17:21 -05:00
firestar5683 dc82e51ef6 peter 2026-05-12 22:17:13 -05:00
firestar5683 2516d13bc5 sonata 2026-05-12 21:59:24 -05:00
firestar5683 2ffb286569 Build 2026-05-12 21:47:15 -05:00
firestar5683 af84c8802b HonDeez 2026-05-12 21:47:14 -05:00
firestar5683 d7e43b7ecc otter 2026-05-12 21:32:20 -05:00
firestar5683 471a33d64d build 2026-05-12 16:21:58 -05:00
firestar5683 334481c78d make banner optional 2026-05-12 16:19:10 -05:00
firestar5683 c2bb8f9c56 tune 2026-05-11 22:51:16 -05:00
firestarsdog c650edd193 speed limit filler test? 2026-05-11 22:56:08 -04:00
firestar5683 3e03917edc test 2026-05-11 21:50:07 -05:00
firestar5683 4d0f65a6d1 F 2026-05-11 21:17:43 -05:00
firestar5683 02717ebe73 build 2026-05-11 19:11:53 -05:00
firestar5683 a67c8f98b7 RIP iPedal 2026-05-11 19:09:47 -05:00
firestar5683 21589861fd huge if work 2026-05-11 17:50:09 -05:00
firestar5683 0ce8161cbf i6 2026-05-11 17:04:43 -05:00
firestar5683 013ecbed97 i6 2026-05-11 15:34:33 -05:00
firestarsdog 4370e9c7a3 BigUI WIP: + newer options 2026-05-11 02:43:06 -04:00
firestarsdog a5dedeb164 BigUI WIP: Accents 2026-05-11 02:17:28 -04:00
Fufs 7b557144b3 Fix catastrophic mistake 2026-05-11 00:55:17 -05:00
firestar5683 4747d26cf6 palisade and gen2 bolt 2026-05-11 00:53:27 -05:00
firestar5683 3c3969b09d exp c4 ui 2026-05-11 00:36:41 -05:00
firestar5683 8bd080b3e0 fcw 2026-05-11 00:36:08 -05:00
firestarsdog 5a044781fc BigUI WIP: Accents 2026-05-11 01:33:31 -04:00
firestarsdog 98480a3018 BigUI WIP: Chips 2026-05-11 01:16:18 -04:00
firestarsdog 5c412cea96 BigUI WIP: Standardize Sliders 2026-05-11 01:09:15 -04:00
firestar5683 ee3d33526b honda 2026-05-10 21:25:30 -05:00
firestar5683 25633be976 log grabber 2026-05-10 20:17:17 -05:00
Fufs cd20d94c7c Add legal stuff after adding mdi icons 2026-05-10 17:16:04 -05:00
Fufs 90fbb74f93 C4 UI Vehicle Panel 2026-05-10 17:16:04 -05:00
firestar5683 867159198a túnē 2026-05-10 16:53:33 -05:00
firestar5683 6c4ae9749d build 2026-05-10 13:02:33 -05:00
firestar5683 4254090cf4 button 2026-05-10 13:00:25 -05:00
firestar5683 006319deff build 2026-05-10 11:36:21 -05:00
firestar5683 9872c5fb4b button 2026-05-10 11:25:58 -05:00
firestar5683 2bc5247dc0 i6 wiggle 2026-05-10 10:48:58 -05:00
firestar5683 2ca5f36590 build 2026-05-10 01:18:49 -05:00
firestar5683 c1e3525929 I cannot stop planning long 2026-05-10 01:18:49 -05:00
firestar5683 ef44741760 build 2026-05-09 21:37:54 -05:00
firestar5683 eebfd509d7 LongyLongNeck 2026-05-09 19:28:03 -05:00
firestar5683 61ca62df5f konik 2026-05-09 19:24:05 -05:00
firestar5683 ba18142718 i6 2026-05-09 16:12:44 -05:00
firestar5683 1714c516ab all the small things 2026-05-09 08:54:04 -05:00
firestar5683 1b2b13881c Lead 2026-05-08 11:37:20 -05:00
firestar5683 7dc26d1a68 angle 2026-05-08 11:01:30 -05:00
firestar5683 fbba97f9a6 build 2026-05-08 10:41:15 -05:00
firestar5683 e969a96c17 Update params_keys.h 2026-05-08 10:35:43 -05:00
firestar5683 930ef6e646 honda 2026-05-08 10:10:16 -05:00
firestar5683 b0bba1917f pedal surge 2026-05-08 01:11:41 -05:00
firestar5683 1fbd6ecdfa pedal long brake bias 2026-05-07 23:59:22 -05:00
firestar5683 888a674675 i5 2026-05-07 23:51:33 -05:00
firestar5683 510e2c2225 build 2026-05-07 23:13:33 -05:00
firestar5683 ffd8f6404b teslurrrr 2026-05-07 23:11:06 -05:00
firestar5683 e0259e191c HERCULES 2026-05-07 21:33:21 -05:00
firestar5683 245a5eaaeb who put the glad in gladiator 2026-05-07 21:30:52 -05:00
firestarsdog 1fb31b2e12 BigUI WIP: Cleanup/uniformity 2026-05-07 19:57:18 -04:00
firestar5683 ea64cc4de3 tune 2026-05-07 17:10:31 -05:00
firestar5683 b038b102d8 teslurr 2026-05-07 17:07:25 -05:00
firestar5683 5e0a1bbec3 cacao nibs 2026-05-07 15:27:39 -05:00
firestar5683 5f3d71798d The Well-Tempered Clavier 2026-05-07 14:07:24 -05:00
firestar5683 c1804b1fd8 kia 2026-05-07 11:03:37 -05:00
firestar5683 86ce0b03ee i6 2026-05-07 10:48:44 -05:00
firestar5683 e5ac0c511d tunes 2026-05-06 22:47:07 -05:00
firestar5683 8c0e8375f4 build 2026-05-06 22:17:17 -05:00
whoisdomi 357d7e545f Force Stop Icon replaces banner 2026-05-06 22:14:10 -05:00
whoisdomi d2bd12e9a1 Honda and Mazda Dashboard signals
Mazda CX-5 2017-21
Mazda CX-5 2022-25
Mazda CX-9 2016-20
Mazda CX-9 2021-23
Mazda 3 2017-18
Mazda 6 2017-20

The Mazda caveat worth noting: unlike Honda (where HAS_CAMERA_MESSAGES was already confirmed per-car), Mazda has no such gate — all six models will attempt to read CAM_TRAFFIC_SIGNS since it's registered with freq=0

Honda: limited to HONDA_CAMERA_MESSAGE_CARS, all radarless Bosch:
Honda Accord 2018-22 (+ Accord Hybrid 2018-22, Inspire 2018)
Honda Civic 2019-21 (+ Civic Hatchback 2017-21)
Honda Civic 2022-24 (+ Civic Hatchback 2022-24, Civic Hybrid 2025-26, Civic Hatchback Hybrid 2023/2025-26)
Honda CR-V 2017-22
Honda CR-V Hybrid 2017-22
Honda HR-V 2023-25
Honda Insight 2019-22

Not included (not in HONDA_CAMERA_MESSAGE_CARS): Accord 2023-25, CR-V 2023-26, Odyssey, Pilot, Passport, Acura RDX 3G, Acura TLX — those are either CANFD or use a different DBC and haven't been confirmed to publish CAMERA_MESSAGES
2026-05-06 22:14:10 -05:00
whoisdomi 300ad3d49c Hide the bodies
User can now hide:
DM Icon
Lane Change Banner
Turning Banner
Distance Profile Banner
2026-05-06 22:14:09 -05:00
whoisdomi be9ab0a7e6 Force Stop bugfix: gap behind leads 2 2026-05-06 22:14:09 -05:00
whoisdomi c71a9aed6e Force Stop bugfix: gap behind leads
Force Stop (cem_path) was missing a tracking_lead guard, causing it to activate behind stopped leads at red lights and stop signs — stopping the car far short of the lead instead of letting ACC handle following distance. Added and not self.starpilot_planner.tracking_lead to cem_path, matching the guard already present on dash_path.
2026-05-06 22:14:09 -05:00
whoisdomi ecf6161f9c Efficiency batch 3: UI
F1 — Toggle cache (starpilot_annotated_camera.h + .cc): Added 25 typed cached members; all 32 starpilot_toggles.value() calls in paint code replaced with the cached versions. All reads happen once at the top of updateState() per frame. The only surviving raw call is in mousePressEvent (intentional — touch is rare, not paint-path).

F2 — HUD gradient hoist (selfdrive/ui/qt/onroad/hud.cc): Header gradient is now a static const built once via lambda, no allocation per frame.

Build, flash, and verify that toggling compass, simple_mode, radar_tracks etc. from settings still take effect live without a restart.
2026-05-06 22:14:09 -05:00
whoisdomi 78d5f71fed Efficiency batch 2
D2 — update() in starpilot_onroad.cc now only fires when at least one overlay (showBlindspot, showSignal, showSteering, showFPS) is active. ~30–60 wasted repaints/sec eliminated when all overlays are off.

E4 — Holiday theme check reordered so current_holiday_theme != "stock" (cheapest) is first. Default is stock, so the rest of the chain never evaluates on a normal non-holiday drive.

E7 — paintSteeringTorqueBorder no longer rebuilds the QLinearGradient and computes lighter(120)/setAlpha/5 setColorAt calls every frame. The gradient is now built once in resizeEvent and reused. The per-frame work is now just the two geometry calculations and fillRect.

E5/E6/E8 — Audited clean: all remaining params_memory.put calls are event-driven (button presses, random events), no hot-path logging exists in the UI code, and all QTimers are event-driven or single-shot offroad dialogs.
2026-05-06 22:14:09 -05:00
whoisdomi c1d3e4540b Efficiency batch 1
Wave A (pure refactors):

A2: nnff toggle moved to front of NNFF check — kills ~100 IPC reads/sec when NNFF disabled
A4: 3 filters now use update_alpha() instead of being recreated — ~60 allocs/sec eliminated
Wave B (write reduction):

B1: CEStatus only writes on state transitions (+ E3 folded in: extracted ce_write_value local at both sites). Reset path added in the manual-override branch. ~19 writes/sec saved during cruise
B2: GPS position only persists when compass is enabled AND bearing shifts >0.5° (+ E1 folded in: json.dumps inside the gate). ~95% of writes eliminated on highway
Wave C (polling throttles):

C1: Onscreen distance button reads at 10 Hz instead of 100 Hz — ~90 IPC reads/sec eliminated. Wheel button unaffected
C2: SpeedLimitAccepted IPC read throttled to 5 Hz via existing _slc_adopt_counter. Wheel accel still instant
C3: Lane width numpy calls at 5 Hz instead of 20 Hz — ~300 allocs/sec eliminated. Counter resets when speed drops below threshold so values are fresh on re-entry
2026-05-06 22:14:09 -05:00
firestar5683 2e7036ab07 cem 2026-05-06 16:18:18 -05:00
firestar5683 cfff7d7cb7 honda 2026-05-06 15:02:41 -05:00
firestar5683 1f68e0deaf plan stan man ban 2026-05-06 14:06:47 -05:00
firestar5683 c79e2b3ad7 i6 2026-05-06 13:54:16 -05:00
firestar5683 49aad9cce3 tůne 2026-05-06 10:38:47 -05:00
firestar5683 8ec27d25c5 up 2026-05-05 21:03:40 -05:00
firestar5683 cd9115b0b2 tesla 2026-05-05 20:50:28 -05:00
firestar5683 7918dab915 build 2026-05-05 20:41:25 -05:00
firestar5683 0913a22dfe poodle 2026-05-05 20:41:24 -05:00
firestar5683 e36a9c6c61 ye 2026-05-05 19:53:18 -05:00
firestar5683 a98bf277c8 oops 2026-05-05 17:57:48 -05:00
firestar5683 c5b12e0a16 dbc 2026-05-05 17:24:55 -05:00
firestar5683 e421eb271e neeneeneeneeneenee 2026-05-05 16:20:53 -05:00
whoisdomi 9b97124f80 Finally, a girl is no one 2026-05-05 15:39:44 -05:00
firestar5683 1a39c51617 build 2026-05-05 12:45:15 -05:00
firestar5683 2f74f93308 peedle 2026-05-05 12:41:05 -05:00
firestar5683 4747792c37 p 2026-05-05 11:59:29 -05:00
firestar5683 5e0447df1e ipedal 2026-05-05 11:11:10 -05:00
firestar5683 d9676ca194 models 2026-05-05 10:48:08 -05:00
firestar5683 026a2eb528 op11 2026-05-05 10:39:52 -05:00
firestar5683 e49cfe44b9 honda/ipedal 2026-05-05 10:29:18 -05:00
firestar5683 2e113835a7 build 2026-05-04 23:17:54 -05:00
firestar5683 5f51e6704d ipedal2 2026-05-04 23:15:38 -05:00
firestar5683 806a210467 build 2026-05-04 20:32:12 -05:00
firestar5683 1ddbc24a14 always I-pedal 2026-05-04 20:30:03 -05:00
firestar5683 aed0b7fdb9 f 2026-05-04 19:30:51 -05:00
firestar5683 7837a3c1aa Tesla 2026-05-04 19:12:20 -05:00
firestar5683 7e4818916b Freshly Caught 2026-05-04 17:48:07 -05:00
firestar5683 32185c0c2e honda 2026-05-04 15:40:38 -05:00
firestar5683 90b05a1d18 i6 2026-05-04 15:29:54 -05:00
whoisdomi 37882e86d5 Don't buy the dip
A slight acceleration dip was felt/seen as PID integrator takes over. Now it will be seeded with a_target, for a smoother transiiton and less dip.
2026-05-04 14:52:10 -05:00
firestar5683 1fd3f2cb9d i6 2026-05-04 13:36:11 -05:00
firestar5683 3f3a63a14b i6 2026-05-04 12:49:37 -05:00
firestar5683 4abda00404 i6 2026-05-04 12:15:57 -05:00
firestar5683 b2ded79155 Revert "STOCK LONG I6 wobble fix?"
This reverts commit 7ea096686d.
2026-05-04 12:05:30 -05:00
whoisdomi 7ea096686d STOCK LONG I6 wobble fix? 2026-05-04 11:44:04 -05:00
firestar5683 847333d536 i6 2026-05-04 11:19:28 -05:00
firestar5683 f132bd7b82 i6 2026-05-04 11:19:24 -05:00
firestar5683 d7ec15bddb planner 2026-05-04 11:19:23 -05:00
firestar5683 41fef69a57 honda 2026-05-04 11:19:23 -05:00
firestar5683 14cde4a64c You could be drivin a honda 2026-05-04 11:19:23 -05:00
firestar5683 abd2721a15 This car b steery 2026-05-04 11:19:23 -05:00
firestar5683 3deb8fbb84 Update the_pond.py 2026-05-04 11:19:23 -05:00
firestar5683 c49b4ec228 hurdy gurdy 2026-05-04 11:19:23 -05:00
firestarsdog 8125fd789a BigUI WIP: Size Queen II 2026-05-04 11:19:22 -05:00
firestar5683 6764db8b3b ain't no test shoooo 2026-05-04 11:19:22 -05:00
firestar5683 24af3405f2 big if true 2026-05-04 11:19:22 -05:00
firestarsdog 6626202ff1 BigUI WIP: Size Queen 2026-05-04 11:19:22 -05:00
firestar5683 261b139bcc honda 2026-05-04 11:19:22 -05:00
firestar5683 da5a028057 honda 2026-05-04 11:19:22 -05:00
firestar5683 b9ab07d7cd leedle 2026-05-04 11:19:22 -05:00
firestar5683 a9d57b521e honda 2026-05-04 11:19:21 -05:00
firestar5683 7f03e2880c yeet 2026-05-04 11:19:21 -05:00
firestar5683 6923c35e7d Update latcontrol_pid.py 2026-05-04 11:19:21 -05:00
firestar5683 48ea42c026 honda 2026-05-04 11:19:21 -05:00
firestar5683 5bc24ff148 Honda Long Tune 2026-05-04 11:19:21 -05:00
firestar5683 8ddf2b4757 longplan 2026-05-04 11:19:21 -05:00
firestar5683 863273edbc build 2026-05-04 11:19:20 -05:00
firestar5683 b8a84d6196 honda 2026-05-04 11:19:20 -05:00
whoisdomi 997c1be7b6 Could this be the last accel chain?
Issue: limit_accel_in_turns breakpoint table started at 20 m/s, causing np.interp to clamp a_total_max to 1.7 m/s² for all speeds below 20 m/s (~45 mph), overriding acceleration profiles during the entire low-speed pull.
Fix: Extended _A_TOTAL_MAX_BP down to 0 m/s with a 3.5 m/s² ceiling, matching the maximum ACCEL_MAX.
Turn protection still works correctly since a_y grows with v² and naturally consumes the budget in actual turns.
2026-05-04 10:28:29 -05:00
whoisdomi 471981f732 Hyundai Unchained 2: Let my people go
PID limits clampping max accel from 3.5 to 2.0m/s2
Sport+ should now work.
2026-05-03 09:08:23 -05:00
firestarsdog 989b69ee92 BigUI WIP: Apperance + Cleanup 2026-05-03 02:50:46 -04:00
firestar5683 650b79941f palisade 2026-05-03 00:19:28 -05:00
firestar5683 f7d79c613e honda 2026-05-03 00:15:15 -05:00
firestarsdog 37c82cc2d4 BigUI WIP: Start of lateral 2026-05-03 00:14:43 -04:00
firestar5683 1c38b731bf Omnifarious Panoply 2026-05-02 20:39:08 -05:00
firestarsdog 5312ce3082 SLC Gods, pls 2026-05-02 21:06:31 -04:00
whoisdomi 3c27a6a273 Fix Domi's rant 2026-05-02 19:44:26 -05:00
firestar5683 d4f73f56c4 honda 2026-05-02 15:54:34 -05:00
firestar5683 5584535842 honda 2026-05-02 15:33:29 -05:00
firestar5683 5607d3d01a i6 2026-05-02 15:29:05 -05:00
firestar5683 5ace618860 Update latcontrol_pid.py 2026-05-02 15:02:44 -05:00
firestar5683 fc4a53c02e update 2026-05-02 13:29:21 -05:00
firestar5683 1a016da5fa i6 2026-05-02 12:20:57 -05:00
firestar5683 0f5ab2f6f6 cem light latch 2026-05-02 09:57:49 -05:00
firestar5683 be88d86b21 patch 2026-05-02 09:19:39 -05:00
firestarsdog d98d1c3432 BigUI WIP: Long + Clean...er..ish 2026-05-02 02:49:47 -04:00
firestar5683 873275374d build 2026-05-01 23:26:21 -05:00
firestar5683 712aa081bc HKG Blended CAN/CAN-FD 2026-05-01 23:20:45 -05:00
firestarsdog 0c39558afb BigUI WIP: Lil cleanup 2026-05-01 21:36:30 -04:00
firestar5683 762146babc tǔne 2026-05-01 18:57:37 -05:00
firestar5683 708af332de build 2026-05-01 18:35:08 -05:00
firestar5683 c612f7a102 tüne 2026-05-01 17:32:30 -05:00
whoisdomi 943a060db2 Developer sidebar fix
Auto-tune off would still display auto-tune values.
Correct chosen values were being used but not displayed.
2026-05-01 15:00:42 -05:00
firestar5683 b77341c804 honda lpf 2026-05-01 14:54:13 -05:00
firestar5683 f69599fabb honda wobble 2026-05-01 14:34:17 -05:00
firestar5683 617aa0a5db honda I6 2026-05-01 14:19:28 -05:00
firestar5683 86f5102158 multiplier 2026-05-01 13:09:43 -05:00
firestar5683 900bb97459 red light stop 2026-05-01 12:40:29 -05:00
firestar5683 86f99b9e1b auto hold 2026-05-01 12:20:42 -05:00
firestar5683 e57d75a3d7 honda 2026-05-01 11:24:47 -05:00
firestarsdog 375a73891c BigUI WIP: Start on Vehicle Panel 2026-05-01 03:43:12 -04:00
firestarsdog ae4c155c6e BigUI WIP: Cleanerish 2026-05-01 02:39:46 -04:00
firestar5683 eedcfe3c56 bumpity 2026-05-01 00:12:31 -05:00
firestar5683 345747a042 more stuff 2026-04-30 23:31:59 -05:00
firestar5683 26e25bf9a8 Errybody wants tuning 2026-04-30 23:11:16 -05:00
firestar5683 8b12ed459f i6 cem 2026-04-30 17:52:50 -05:00
firestar5683 0041a79823 Genesis 2026-04-30 15:29:11 -05:00
firestarsdog b820a3214f BigUI WIP: In-line sliders 2026-04-30 14:28:09 -04:00
firestar5683 f93a4f5adb build 2026-04-30 11:07:47 -05:00
firestar5683 4503811202 DM and volt auto hold 2026-04-30 11:01:46 -05:00
firestar5683 8ec92d12fd toyboi 2026-04-30 10:31:59 -05:00
firestar5683 edc9a9ce60 I6 and CEM 2026-04-30 10:18:17 -05:00
firestarsdog f58b6fc6c7 BigUI WIP: System Settings Parity 2026-04-30 09:43:34 -04:00
firestar5683 d5b8ab3262 build 2026-04-30 08:34:16 -05:00
whoisdomi 609ba6cf63 Slow takeoff possible fix 2026-04-30 07:22:44 -05:00
whoisdomi b9c5a78d16 Prebuilt flag bug fix 2026-04-30 05:42:43 -05:00
whoisdomi a919998872 Unchain me - HKG
Raises max_accel to match sport+ mode of 3.5m/s2
2026-04-30 05:12:03 -05:00
whoisdomi f7fdd7634e CCmain for SLC
CC main button now accepts the current best speed limit source, even if new speed limit source is not flashing.
It snaps cruise speed to match (+ your offset), in either direction.
It works even before engaging longitudinal, so you can pre-set speed limit.
Won't interfere with cars using CC main for Always-On Lateral.
2026-04-30 05:12:02 -05:00
whoisdomi feb9d49c93 SLC Cleanup
Fix three SLC bugs:
1. Auto-accept limits when not engaged to keep previous_target current.
2. Gate Vision out of the limits dict when not a configured priority slot.
3. Replace mapdOut fallback with 0 when filler has no data to prevent stale OSM speed limits from leaking in.
2026-04-30 05:10:48 -05:00
firestarsdog d0c19745c3 BigUI WIP: Extraction 2026-04-30 01:49:24 -04:00
whoisdomi 3adc8592da Acceleration Profile Changes 2026-04-29 23:58:35 -05:00
firestar5683 dcaec4f160 build 2026-04-29 23:44:59 -05:00
firestar5683 b5340a3f3d blinky 2026-04-29 23:44:59 -05:00
firestar5683 b3a2f141d6 updatz 2026-04-29 23:44:59 -05:00
firestar5683 86db8e39c6 patch 2026-04-29 16:41:42 -05:00
firestar5683 749b0d6d79 build 2026-04-29 15:52:46 -05:00
firestar5683 0c7d259e7f sng 2026-04-29 15:50:30 -05:00
firestar5683 f9d6c0d648 Doms plan small update 2026-04-29 15:38:45 -05:00
firestar5683 3e82e773a9 build 2026-04-29 14:25:51 -05:00
firestar5683 04dbd8d0fd HKG 2026-04-29 14:22:52 -05:00
firestar5683 60d6cc4476 i6 2026-04-29 14:22:46 -05:00
firestar5683 91a6df08b0 Plexy 2026-04-29 14:02:42 -05:00
firestar5683 40aff87f88 HKG Long Tune 2026-04-29 11:34:07 -05:00
firestar5683 e19f7d29b4 i6 2026-04-29 11:33:57 -05:00
firestar5683 f27069f2d2 SLC fix 2026-04-29 11:22:58 -05:00
firestarsdog aac395ccd2 BigUI WIP: Mapsssssss 2026-04-29 11:47:46 -04:00
firestar5683 422d92f0ea RL code 2026-04-28 21:14:00 -05:00
firestar5683 d547e3632d rl 2026-04-28 21:06:03 -05:00
firestar5683 ef57854b81 Doms Plan v5 2026-04-28 20:45:13 -05:00
firestar5683 d7a29e6d81 cry more 2026-04-28 20:43:01 -05:00
firestar5683 a68bc7be2e volt 2026-04-28 18:52:55 -05:00
firestar5683 2ed89e4bb7 i6 2026-04-28 18:16:01 -05:00
firestarsdog 145f9d2b19 BigUI WIP: maps are dumb 2026-04-28 19:09:37 -04:00
firestar5683 94c71b8b20 i6 2026-04-28 18:08:41 -05:00
firestar5683 52071db172 i6 2026-04-28 15:13:22 -05:00
firestar5683 66d2a63bcf Patch 2026-04-28 11:33:20 -05:00
firestar5683 53116c29c8 toyota sng 2026-04-28 11:33:12 -05:00
firestar5683 d8fccdb01c build 2026-04-28 10:46:03 -05:00
firestar5683 be77164d8d Bribe Mayhem 2026-04-28 10:42:47 -05:00
firestar5683 0889b3b0c2 Dom's Plan 4 2026-04-28 10:03:38 -05:00
firestarsdog 67c88675da BigUI WIP: Fix callbacks 2026-04-28 03:00:07 -04:00
firestarsdog 65a9fcf3ec BigUI WIP: More map cleanup 2026-04-28 02:14:16 -04:00
firestar5683 cb56102da7 volt 2026-04-27 23:55:25 -05:00
firestarsdog 2e28374fc9 BigUI WIP: Map Panel WIP 2026-04-28 00:35:31 -04:00
firestar5683 9598e47162 build 2026-04-27 21:26:20 -05:00
whoisdomi 40372c63ee Force Stop 2.0
Force Stop 2.0 — Smoother deceleration curve. On cars with the ADAS stop-sign signal, stops are even more confident; cars without dashboard stop sign signal will also benefit from a new deceleration curve and MPC handoff.

Toggle off = stock behavior.
2026-04-27 21:26:20 -05:00
firestar5683 e24520254b Ioniq 6 Long Tune 2026-04-27 21:26:19 -05:00
firestar5683 43a5c09412 Dom's Plan V3 2026-04-27 11:49:39 -05:00
firestar5683 426dbd1718 Dom's plan 2026-04-27 00:35:13 -05:00
firestar5683 8bae6a1063 Dom's Plan 2026-04-26 23:50:48 -05:00
firestar5683 8f7b28deed genesis 2026-04-26 23:20:43 -05:00
firestar5683 4026b6af9b volt 2026-04-26 23:16:42 -05:00
firestar5683 a5dae4669c i6 2026-04-26 23:01:47 -05:00
firestar5683 6b1e11c314 ioniq 2026-04-26 19:28:17 -05:00
firestar5683 e2681b17a4 lane change? 2026-04-26 17:36:12 -05:00
firestar5683 da61e79f2e hud bsm? 2026-04-26 17:14:39 -05:00
firestar5683 31fefd8eb8 bSM 2026-04-26 15:51:16 -05:00
firestar5683 bcf78fec02 i6 2026-04-26 15:40:15 -05:00
firestar5683 71528fd547 ioniq tests 2026-04-26 14:38:27 -05:00
firestar5683 962e0d740a test 2026-04-26 12:37:51 -05:00
firestar5683 1262a2d8f3 Lane change? 2026-04-26 11:03:42 -05:00
firestar5683 a8ba10aac6 ioniq 6 2026-04-26 00:18:31 -05:00
firestarsdog ff67d2eedd BigUI WIP: Finish nuking nav from ui 2026-04-25 23:40:39 -04:00
firestarsdog a2c39aa2ed BigUI WIP: Remove nav junk 2026-04-25 22:10:29 -04:00
firestarsdog a1a781bcf6 BigUI WIP: Tile title and description cleanup 2026-04-25 22:08:38 -04:00
firestar5683 6d0559c6d9 Dom's Plan 2026-04-25 20:51:58 -05:00
firestar5683 330a811114 ioniq tune | blindspot | paddle 2026-04-25 20:51:58 -05:00
firestarsdog ed695fab1b BigUI WIP: Simpleton 2026-04-25 17:17:13 -04:00
firestar5683 a2d51807c7 ioniq 6 2026-04-25 15:58:10 -05:00
firestar5683 df0b6c3e2b YOU WERE THE CHOSEN ONE 2026-04-25 15:39:43 -05:00
firestar5683 636e0d0585 Suppress Dash Faults? 2026-04-25 14:54:33 -05:00
firestar5683 124daf1f51 ioniq 6 2026-04-25 14:26:42 -05:00
whoisdomi e8d0d7bc3a ECU disable fallback stock HDA2 fix
When car is started in "ready" mode now stock acc will work as intended
2026-04-25 10:37:31 -05:00
firestar5683 f074dcc3b5 Revert "Fallback mode HDA2"
This reverts commit 2e4c3f43ee.
2026-04-25 10:37:19 -05:00
whoisdomi 2eb4dfbdc9 Lane Smoothing Tune
Tune to prevent overshoot
2026-04-25 01:15:57 -05:00
firestar5683 2e4c3f43ee Fallback mode HDA2
Revert "Fallback mode HDA2"

This reverts commit 81b2b3584227bd1aa62089770245e50e131e0a3b.

Fallback mode hda2
2026-04-25 01:15:57 -05:00
firestar5683 be87ecec3e ioniq 6 2026-04-24 22:22:34 -05:00
firestar5683 317be3b383 nostalgia 2026-04-24 20:44:12 -05:00
firestar5683 ca5b641b96 Voacc refinement and non-scc cruise check 2026-04-24 18:19:18 -05:00
firestar5683 031ad89851 kia forte 2026-04-24 16:16:25 -05:00
513 changed files with 100237 additions and 65594 deletions
+9
View File
@@ -0,0 +1,9 @@
{
"permissions": {
"allow": [
"Bash(python3 -c \"import json; json.load\\(open\\(r'c:\\\\Users\\\\user\\\\Documents\\\\GitHub\\\\openpilot\\\\starpilot\\\\system\\\\the_pond\\\\assets\\\\components\\\\tools\\\\device_settings_layout.json'\\)\\)\")",
"Bash(python3 -m py_compile \"c:\\\\Users\\\\user\\\\Documents\\\\GitHub\\\\openpilot\\\\starpilot\\\\controls\\\\lib\\\\starpilot_vcruise.py\")",
"Bash(python3 -m py_compile \"c:\\\\Users\\\\user\\\\Documents\\\\GitHub\\\\openpilot\\\\selfdrive\\\\ui\\\\layouts\\\\settings\\\\starpilot\\\\longitudinal.py\")"
]
}
}
-52
View File
@@ -1,52 +0,0 @@
name: "PR review"
on:
pull_request_target:
types: [opened, reopened, synchronize, edited]
jobs:
labeler:
name: review
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: false
# Label PRs
- uses: actions/labeler@v5.0.0
with:
dot: true
configuration-path: .github/labeler.yaml
# Check PR target branch
- name: check branch
uses: Vankka/pr-target-branch-action@def32ec9d93514138d6ac0132ee62e120a72aed5
if: github.repository == 'commaai/openpilot'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
target: /^(?!master$).*/
exclude: /commaai:.*/
change-to: ${{ github.base_ref }}
already-exists-action: close_this
already-exists-comment: "Your PR should be made against the `master` branch"
# Welcome comment
- name: "First timers PR"
uses: actions/first-interaction@v1
if: github.event.pull_request.head.repo.full_name != 'commaai/openpilot'
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
pr-message: |
<!-- _(run_id **${{ github.run_id }}**)_ -->
Thanks for contributing to openpilot! In order for us to review your PR as quickly as possible, check the following:
* Convert your PR to a draft unless it's ready to review
* Read the [contributing docs](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md)
* Before marking as "ready for review", ensure:
* the goal is clearly stated in the description
* all the tests are passing
* the change is [something we merge](https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md#what-gets-merged)
* include a route or your device' dongle ID if relevant
-37
View File
@@ -1,37 +0,0 @@
name: badges
on:
schedule:
- cron: '0 * * * *'
workflow_dispatch:
env:
BASE_IMAGE: openpilot-base
DOCKER_REGISTRY: ghcr.io/commaai
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $DOCKER_REGISTRY/$BASE_IMAGE:latest /bin/bash -c
jobs:
badges:
name: create badges
runs-on: ubuntu-latest
if: github.repository == 'commaai/openpilot'
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
submodules: true
- uses: ./.github/workflows/setup-with-retry
- name: Push badges
run: |
${{ env.RUN }} "python3 selfdrive/ui/translations/create_badges.py"
rm .gitattributes
git checkout --orphan badges
git rm -rf --cached .
git config user.email "badge-researcher@comma.ai"
git config user.name "Badge Researcher"
git add translation_badge.svg
git commit -m "Add/Update badges"
git push -f origin HEAD
-101
View File
@@ -1,101 +0,0 @@
name: weekly CI test report
on:
schedule:
- cron: '37 9 * * 1' # 9:37AM UTC -> 2:37AM PST every monday
workflow_dispatch:
inputs:
ci_runs:
description: 'The amount of runs to trigger in CI test report'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
CI_RUNS: ${{ github.event.inputs.ci_runs || '50' }}
jobs:
setup:
if: github.repository == 'commaai/openpilot'
runs-on: ubuntu-latest
outputs:
ci_runs: ${{ steps.ci_runs_setup.outputs.matrix }}
steps:
- id: ci_runs_setup
name: CI_RUNS=${{ env.CI_RUNS }}
run: |
matrix=$(python3 -c "import json; print(json.dumps({ 'run_number' : list(range(${{ env.CI_RUNS }})) }))")
echo "matrix=$matrix" >> $GITHUB_OUTPUT
ci_matrix_run:
needs: [ setup ]
strategy:
fail-fast: false
matrix: ${{fromJSON(needs.setup.outputs.ci_runs)}}
uses: commaai/openpilot/.github/workflows/ci_weekly_run.yaml@master
with:
run_number: ${{ matrix.run_number }}
report:
needs: [ci_matrix_run]
runs-on: ubuntu-latest
if: always() && github.repository == 'commaai/openpilot'
steps:
- name: Get job results
uses: actions/github-script@v7
id: get-job-results
with:
script: |
const jobs = await github
.paginate("GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt}/jobs", {
owner: "commaai",
repo: "${{ github.event.repository.name }}",
run_id: "${{ github.run_id }}",
attempt: "${{ github.run_attempt }}",
})
var report = {}
jobs.slice(1, jobs.length-1).forEach(job => {
if (job.conclusion === "skipped") return;
const jobName = job.name.split(" / ")[2];
const runRegex = /\((.*?)\)/;
const run = job.name.match(runRegex)[1];
report[jobName] = report[jobName] || { successes: [], failures: [], canceled: [] };
switch (job.conclusion) {
case "success":
report[jobName].successes.push({ "run_number": run, "link": job.html_url}); break;
case "failure":
report[jobName].failures.push({ "run_number": run, "link": job.html_url }); break;
case "canceled":
report[jobName].canceled.push({ "run_number": run, "link": job.html_url }); break;
}
});
return JSON.stringify({"jobs": report});
- name: Add job results to summary
env:
JOB_RESULTS: ${{ fromJSON(steps.get-job-results.outputs.result) }}
run: |
cat <<EOF >> template.html
<table>
<thead>
<tr>
<th></th>
<th>Job</th>
<th>✅ Passing</th>
<th>❌ Failure Details</th>
</tr>
</thead>
<tbody>
{% for key in jobs.keys() %}<tr>
<td>{% for i in range(5) %}{% if i+1 <= (5 * jobs[key]["successes"]|length // ${{ env.CI_RUNS }}) %}🟩{% else %}🟥{% endif %}{% endfor%}</td>
<td>{{ key }}</td>
<td>{{ 100 * jobs[key]["successes"]|length // ${{ env.CI_RUNS }} }}%</td>
<td>{% if jobs[key]["failures"]|length > 0 %}<details>{% for failure in jobs[key]["failures"] %}<a href="{{ failure['link'] }}">Log for run #{{ failure['run_number'] }}</a><br>{% endfor %}</details>{% else %}{% endif %}</td>
</td>
</tr>{% endfor %}
</table>
EOF
pip install jinja2-cli
echo $JOB_RESULTS | jinja2 template.html > report.html
echo "# CI Test Report - ${{ env.CI_RUNS }} Runs" >> $GITHUB_STEP_SUMMARY
cat report.html >> $GITHUB_STEP_SUMMARY
-17
View File
@@ -1,17 +0,0 @@
name: weekly CI test run
on:
workflow_call:
inputs:
run_number:
required: true
type: string
concurrency:
group: ci-run-${{ inputs.run_number }}-${{ github.ref }}
cancel-in-progress: true
jobs:
tests:
uses: commaai/openpilot/.github/workflows/tests.yaml@master
with:
run_number: ${{ inputs.run_number }}
+211
View File
@@ -0,0 +1,211 @@
name: Compile Bluescreens
on:
workflow_dispatch:
inputs:
target_ref:
description: "Branch to build and publish back to"
type: string
default: "bluescreensonly2"
required: true
jobs:
description: "Optional parallel job count passed to ./build"
type: string
default: "12"
required: false
enable_scons_cache:
description: "Enable SCons cache for faster incremental builds"
type: boolean
default: true
required: false
build_all:
description: "Build everything"
type: boolean
default: true
required: false
build_ui:
description: "Build UI (selfdrive/ui)"
type: boolean
default: true
required: false
build_params:
description: "Build params Python extension"
type: boolean
default: false
required: false
build_cereal:
description: "Build cereal messaging bridge"
type: boolean
default: false
required: false
build_panda:
description: "Build panda firmware outputs"
type: boolean
default: true
required: false
build_modeld:
description: "Build modeld Python extension"
type: boolean
default: false
required: false
build_controls:
description: "Build controls solvers + locationd generated models"
type: boolean
default: false
required: false
concurrency:
group: compile-bluescreens-${{ inputs.target_ref }}
cancel-in-progress: false
permissions:
contents: write
env:
GIT_EMAIL: "168790843+firestar5683@users.noreply.github.com"
GIT_NAME: "StarPilot Build Bot"
TARGET_REF: ${{ inputs.target_ref }}
jobs:
build_and_push:
runs-on: [self-hosted, truenas, starpilot-build]
timeout-minutes: 720
steps:
- name: Validate target branch
shell: bash
run: |
set -euo pipefail
if [[ -z "${TARGET_REF}" ]]; then
echo "target_ref is required." >&2
exit 1
fi
if [[ "${TARGET_REF}" == refs/heads/* ]]; then
echo "BUILD_BRANCH=${TARGET_REF#refs/heads/}" >> "$GITHUB_ENV"
else
echo "BUILD_BRANCH=${TARGET_REF}" >> "$GITHUB_ENV"
fi
- name: Checkout target branch
uses: actions/checkout@v4
with:
ref: ${{ env.BUILD_BRANCH }}
fetch-depth: 0
submodules: recursive
- name: Configure git identity
shell: bash
run: |
set -euo pipefail
git config user.name "$GIT_NAME"
git config user.email "$GIT_EMAIL"
git config http.postBuffer 104857600
- name: Ensure branch exists on origin
shell: bash
run: |
set -euo pipefail
git ls-remote --exit-code --heads origin "refs/heads/${BUILD_BRANCH}" >/dev/null
- name: Link persistent sysroot
shell: bash
run: |
set -euo pipefail
ls -ld /runner /runner/sysroots /runner/sysroots/default || true
ls -ld /runner/sysroots/default/usr /runner/sysroots/default/usr/local /runner/sysroots/default/usr/local/lib || true
rm -rf .comma_sysroot
ln -s /runner/sysroots/default .comma_sysroot
ls -ld .comma_sysroot .comma_sysroot/usr .comma_sysroot/usr/local .comma_sysroot/usr/local/lib
- name: Run Bluescreens build
shell: bash
env:
SP_ENABLE_SCONS_CACHE: ${{ inputs.enable_scons_cache && '1' || '0' }}
run: |
set -euo pipefail
RUNNER_HOST_ROOT="/mnt/Apparition/My_App_Data/starpilot-runner"
if [[ "${GITHUB_WORKSPACE}" != /runner/_work/* ]]; then
echo "Unexpected GITHUB_WORKSPACE: ${GITHUB_WORKSPACE}" >&2
exit 1
fi
WORKSPACE_REL="${GITHUB_WORKSPACE#/runner}"
export COMMA_HOST_ROOT_DIR="${RUNNER_HOST_ROOT}${WORKSPACE_REL}"
export COMMA_HOST_SYSROOT_DIR="${RUNNER_HOST_ROOT}/sysroots/default"
export COMMA_HOST_CACHE_DIR="${RUNNER_HOST_ROOT}/cache/work"
export COMMA_HOST_VENV_DIR="${RUNNER_HOST_ROOT}/cache/venv-linux-arm64"
export COMMA_SYSROOT_DIR="/runner/sysroots/default"
echo "GITHUB_WORKSPACE=${GITHUB_WORKSPACE}"
echo "COMMA_HOST_ROOT_DIR=${COMMA_HOST_ROOT_DIR}"
echo "COMMA_HOST_SYSROOT_DIR=${COMMA_HOST_SYSROOT_DIR}"
echo "COMMA_HOST_CACHE_DIR=${COMMA_HOST_CACHE_DIR}"
echo "COMMA_HOST_VENV_DIR=${COMMA_HOST_VENV_DIR}"
JOBS="${{ inputs.jobs }}"
if [[ "${{ inputs.build_all }}" == "true" ]]; then
if [[ -n "${JOBS}" ]]; then
./build "${JOBS}"
else
./build
fi
exit 0
fi
TARGETS=()
[[ "${{ inputs.build_ui }}" == "true" ]] && TARGETS+=("selfdrive/ui/ui")
[[ "${{ inputs.build_params }}" == "true" ]] && TARGETS+=("common/params_pyx.so")
[[ "${{ inputs.build_cereal }}" == "true" ]] && TARGETS+=("cereal/messaging/bridge")
if [[ "${{ inputs.build_panda }}" == "true" ]]; then
TARGETS+=(
"panda/board/obj/panda.bin.signed"
"panda/board/obj/panda_h7.bin.signed"
"panda/board/obj/panda_remote.bin.signed"
"panda/board/obj/panda_h7_remote.bin.signed"
"panda/board/obj/panda_jungle_h7.bin.signed"
"panda/board/obj/body_h7.bin.signed"
)
fi
[[ "${{ inputs.build_modeld }}" == "true" ]] && TARGETS+=("selfdrive/modeld/models/commonmodel_pyx.so")
if [[ "${{ inputs.build_controls }}" == "true" ]]; then
TARGETS+=(
"selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/libacados_ocp_solver_lat.so"
"selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/libacados_ocp_solver_long.so"
"selfdrive/locationd/models/generated/car.cpp"
"selfdrive/locationd/models/generated/pose.cpp"
)
fi
if [[ ${#TARGETS[@]} -eq 0 ]]; then
echo "No partial targets selected; defaulting to UI + panda"
TARGETS=("selfdrive/ui/ui" "panda/board/obj/panda.bin.signed")
fi
echo "Running partial build for targets: ${TARGETS[*]}"
if [[ -n "${JOBS}" ]]; then
./build "${JOBS}" "${TARGETS[@]}"
else
./build "$(nproc)" "${TARGETS[@]}"
fi
- name: Commit build output
shell: bash
run: |
set -euo pipefail
if [[ -z "$(git status --porcelain --untracked-files=no)" ]]; then
echo "No build output changes detected."
exit 0
fi
git add -u
git commit -m "build"
- name: Push build commit
shell: bash
run: |
set -euo pipefail
git push origin "HEAD:refs/heads/${BUILD_BRANCH}"
+81 -205
View File
@@ -3,237 +3,113 @@ name: Compile StarPilot
on:
workflow_dispatch:
inputs:
not_vetted:
description: "This branch is not vetted"
type: boolean
default: false
required: false
publish_custom_branch:
description: "Push to custom branch:"
target_ref:
description: "Branch to build and publish back to"
type: string
default: ""
required: false
publish_starpilot:
description: "Push to StarPilot"
type: boolean
default: false
required: false
publish_staging:
description: "Push to StarPilot-Staging"
type: boolean
default: false
required: false
publish_testing:
description: "Push to StarPilot-Testing"
type: boolean
default: false
required: false
runner:
description: "Select runner"
type: choice
options:
- c3
- c3x
default: "c3"
default: "master"
required: true
update_translations:
description: "Update missing/outdated translations"
type: boolean
default: false
required: false
vet_existing_translations:
description: "Vet existing translations"
type: boolean
default: false
jobs:
description: "Optional parallel job count passed to ./build"
type: string
default: "12"
required: false
concurrency:
group: compile-starpilot-${{ inputs.target_ref }}
cancel-in-progress: false
permissions:
contents: write
env:
BASE_DIR: ${{ github.workspace }}
BUILD_DIR: "/data/openpilot"
CUSTOM_BRANCH: ${{ inputs.publish_custom_branch }}
GIT_EMAIL: "91348155+FrogAi@users.noreply.github.com"
GIT_NAME: "James"
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GIT_EMAIL: "168790843+firestar5683@users.noreply.github.com"
GIT_NAME: "StarPilot Build Bot"
TARGET_REF: ${{ inputs.target_ref }}
jobs:
get_branch:
runs-on: [self-hosted, "${{ inputs.runner }}"]
outputs:
branch: ${{ steps.get_branch.outputs.branch }}
python_version: ${{ steps.get_python_version.outputs.python_version }}
build_and_push:
runs-on: [self-hosted, truenas, starpilot-build]
timeout-minutes: 720
steps:
- name: Get Current Branch
id: get_branch
- name: Validate target branch
shell: bash
run: |
cd "$BUILD_DIR"
echo "branch=$(git rev-parse --abbrev-ref HEAD)" >> "$GITHUB_OUTPUT"
set -euo pipefail
- name: Get Python Version
id: get_python_version
run: |
echo "python_version=$(tr -d '[:space:]' < "$BUILD_DIR/.python-version")" >> $GITHUB_OUTPUT
if [[ -z "${TARGET_REF}" ]]; then
echo "target_ref is required." >&2
exit 1
fi
translate:
needs: get_branch
if: inputs.update_translations
runs-on: ubuntu-latest
steps:
- name: Configure Git Identity
run: |
git config --global user.name "$GIT_NAME"
git config --global user.email "$GIT_EMAIL"
if [[ "${TARGET_REF}" == refs/heads/* ]]; then
echo "BUILD_BRANCH=${TARGET_REF#refs/heads/}" >> "$GITHUB_ENV"
else
echo "BUILD_BRANCH=${TARGET_REF}" >> "$GITHUB_ENV"
fi
- name: Checkout Required Files
- name: Checkout target branch
uses: actions/checkout@v4
with:
ref: ${{ needs.get_branch.outputs.branch }}
sparse-checkout: |
starpilot/ui/
selfdrive/controls/lib/alerts_offroad.json
selfdrive/ui/
selfdrive/ui/translations/
selfdrive/ui/translations/auto_translate.py
selfdrive/ui/update_translations.py
ref: ${{ env.BUILD_BRANCH }}
fetch-depth: 0
submodules: recursive
- name: Set Up Python
uses: actions/setup-python@v4
with:
cache: "pip"
python-version: ${{ needs.get_branch.outputs.python_version }}
- name: Install Dependencies
- name: Configure git identity
shell: bash
run: |
pip install requests
sudo apt-get update && sudo apt-get install -y --no-install-recommends qttools5-dev-tools
set -euo pipefail
git config user.name "$GIT_NAME"
git config user.email "$GIT_EMAIL"
git config http.postBuffer 104857600
- name: Update Translations
- name: Ensure branch exists on origin
shell: bash
run: |
python selfdrive/ui/update_translations.py --vanish
set -euo pipefail
git ls-remote --exit-code --heads origin "refs/heads/${BUILD_BRANCH}" >/dev/null
- name: Update Missing Translations
continue-on-error: true
timeout-minutes: 300
- name: Link persistent sysroot
shell: bash
run: |
python selfdrive/ui/translations/auto_translate.py --all-files
set -euo pipefail
ls -ld /runner /runner/sysroots /runner/sysroots/default || true
ls -ld /runner/sysroots/default/usr /runner/sysroots/default/usr/local /runner/sysroots/default/usr/local/lib || true
rm -rf .comma_sysroot
ln -s /runner/sysroots/default .comma_sysroot
ls -ld .comma_sysroot .comma_sysroot/usr .comma_sysroot/usr/local .comma_sysroot/usr/local/lib
- name: Vet Existing Translations
if: inputs.vet_existing_translations
continue-on-error: true
timeout-minutes: 300
- name: Run StarPilot device build
shell: bash
run: |
python selfdrive/ui/translations/auto_translate.py --all-files --vet-translations
set -euo pipefail
- name: Commit and Push Translations
export COMMA_HOST_ROOT_DIR="/mnt/Apparition/My_App_Data/starpilot-runner/_work/${GITHUB_REPOSITORY#*/}/${GITHUB_REPOSITORY#*/}"
export COMMA_HOST_SYSROOT_DIR="/mnt/Apparition/My_App_Data/starpilot-runner/sysroots/default"
export COMMA_HOST_CACHE_DIR="/mnt/Apparition/My_App_Data/starpilot-runner/cache/work"
export COMMA_HOST_VENV_DIR="/mnt/Apparition/My_App_Data/starpilot-runner/cache/venv-linux-arm64"
export COMMA_SYSROOT_DIR="/runner/sysroots/default"
if [[ -n "${{ inputs.jobs }}" ]]; then
./build "${{ inputs.jobs }}"
else
./build
fi
- name: Commit build output
shell: bash
run: |
if git diff --quiet selfdrive/ui/translations/*.ts; then
echo "No translation updates detected."
set -euo pipefail
if [[ -z "$(git status --porcelain --untracked-files=no)" ]]; then
echo "No build output changes detected."
exit 0
fi
git fetch --unshallow origin "${{ needs.get_branch.outputs.branch }}"
git checkout "${{ needs.get_branch.outputs.branch }}"
git add selfdrive/ui/translations/*.ts
git commit --amend --no-edit
git push --force origin "${{ needs.get_branch.outputs.branch }}"
git add -u
git commit -m "build"
build_and_push:
needs: [get_branch, translate]
if: ${{ !failure() && !cancelled() && needs.get_branch.result == 'success' }}
runs-on: [self-hosted, "${{ inputs.runner }}"]
permissions:
contents: write
defaults:
run:
working-directory: ${{ env.BUILD_DIR }}
steps:
- name: Configure Git
- name: Push build commit
shell: bash
run: |
git config http.postBuffer 104857600
git config user.name "$GIT_NAME"
git config user.email "$GIT_EMAIL"
git remote set-url origin "https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/FrogAi/StarPilot.git"
- name: Sync Translation Updates
if: inputs.update_translations
run: |
git fetch origin "${{ needs.get_branch.outputs.branch }}"
git reset --hard FETCH_HEAD
- name: Take Ownership of Build Directory
run: |
sudo chown -R $(whoami):$(whoami) .
- name: Clean Build Artifacts
run: |
find . -name "matlab.*.md" -delete
find . -type d \( -iname "debug" -o -iname "test" -o -iname "tests" -o -name '__pycache__' \) -exec rm -rf {} +
find . -type f \( \
-name '*.a' -o \
-name '*.o' -o \
-name '*.onnx' -o \
-name '*.os' -o \
-name '*.pyc' -o \
-name 'moc_*' \
\) -delete
find .github -mindepth 1 -maxdepth 1 ! -name 'workflows' -exec rm -rf {} +
find .github/workflows -mindepth 1 ! \( \
-type f \( \
-name 'compile_starpilot.yaml' -o \
-name 'review_pull_request.yaml' -o \
-name 'schedule_update.yaml' -o \
-name 'update_pr_branch.yaml' -o \
-name 'update_release_branch.yaml' -o \
-name 'update_tinygrad.yaml' \
\) \
\) -exec rm -rf {} +
find panda/board -type f \
! -name '__init__.py' \
! -name 'bootstub.panda.bin' \
! -name 'bootstub.panda_h7.bin' \
! -name 'panda.bin.signed' \
! -name 'panda_h7.bin.signed' \
-delete
find third_party/ -name '*Darwin*' -exec rm -rf {} +
find third_party/ -name '*x86*' -exec rm -rf {} +
rm -f .gitignore .gitmodules .gitattributes .lfsconfig .overlay_init
rm -rf .sconsign.dblite .vscode/ Jenkinsfile release/ scripts/ site_scons/ teleoprtc_repo/
find . -type d -empty ! -path "./.git*" -delete
touch prebuilt
[ "${{ inputs.not_vetted }}" = "true" ] && touch not_vetted || true
- name: Add Update Date File
if: inputs.publish_staging
continue-on-error: true
run: |
curl -fLsS https://raw.githubusercontent.com/FrogAi/StarPilot/StarPilot-Staging/.github/update_date -o .github/update_date || echo "No update_date found, skipping..."
- name: Commit and Push Build
run: |
git add -f .
git commit -m "Compile StarPilot"
git push --force origin HEAD
if [ "${{ inputs.publish_starpilot }}" = "true" ]; then
git push --force origin HEAD:StarPilot
fi
if [ "${{ inputs.publish_staging }}" = "true" ]; then
git push --force origin HEAD:StarPilot-Staging
fi
if [ "${{ inputs.publish_testing }}" = "true" ]; then
git push --force origin HEAD:StarPilot-Testing
fi
if [ -n "$CUSTOM_BRANCH" ]; then
git push --force origin HEAD:"$CUSTOM_BRANCH"
fi
set -euo pipefail
git push origin "HEAD:refs/heads/${BUILD_BRANCH}"
-65
View File
@@ -1,65 +0,0 @@
name: docs
on:
push:
branches:
- master
pull_request:
workflow_call:
inputs:
run_number:
default: '1'
required: true
type: string
concurrency:
group: docs-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
jobs:
docs:
name: build docs
runs-on: ubuntu-24.04
steps:
- uses: commaai/timeout@v1
- uses: actions/checkout@v4
with:
submodules: true
# Build
- name: Build docs
run: |
# TODO: can we install just the "docs" dependency group without the normal deps?
pip install mkdocs
mkdocs build
# Push to docs.comma.ai
- uses: actions/checkout@v4
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
with:
path: openpilot-docs
ssh-key: ${{ secrets.OPENPILOT_DOCS_KEY }}
repository: commaai/openpilot-docs
- name: Push
if: github.ref == 'refs/heads/master' && github.repository == 'commaai/openpilot'
run: |
set -x
source release/identity.sh
cd openpilot-docs
git checkout --orphan tmp
git rm -rf .
# copy over docs
cp -r ../docs_site/ docs/
# GitHub pages config
touch docs/.nojekyll
echo -n docs.comma.ai > docs/CNAME
git add -f .
git commit -m "build docs"
# docs live in different repo to not bloat openpilot's full clone size
git push -f origin tmp:gh-pages
-59
View File
@@ -1,59 +0,0 @@
name: jenkins scan
on:
issue_comment:
types: [created, edited]
jobs:
# TODO: gc old branches in a separate job in this workflow
scan-comments:
runs-on: ubuntu-latest
if: ${{ github.event.issue.pull_request }}
permissions:
contents: write
issues: write
steps:
- name: Check for trigger phrase
id: check_comment
uses: actions/github-script@v7
with:
script: |
const triggerPhrase = "trigger-jenkins";
const comment = context.payload.comment.body;
const commenter = context.payload.comment.user.login;
const { data: permissions } = await github.rest.repos.getCollaboratorPermissionLevel({
owner: context.repo.owner,
repo: context.repo.repo,
username: commenter
});
const hasWriteAccess = permissions.permission === 'write' || permissions.permission === 'admin';
return (hasWriteAccess && comment.includes(triggerPhrase));
result-encoding: json
- name: Checkout repository
if: steps.check_comment.outputs.result == 'true'
uses: actions/checkout@v4
with:
ref: refs/pull/${{ github.event.issue.number }}/head
- name: Push to tmp-jenkins branch
if: steps.check_comment.outputs.result == 'true'
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git checkout -b tmp-jenkins-${{ github.event.issue.number }}
GIT_LFS_SKIP_PUSH=1 git push -f origin tmp-jenkins-${{ github.event.issue.number }}
- name: Delete trigger comment
if: steps.check_comment.outputs.result == 'true' && always()
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
});
@@ -1,151 +0,0 @@
name: "mici raylib ui preview"
on:
push:
branches:
- master
pull_request_target:
types: [assigned, opened, synchronize, reopened, edited]
branches:
- 'master'
paths:
- 'selfdrive/assets/**'
- 'selfdrive/ui/**'
- 'system/ui/**'
workflow_dispatch:
env:
UI_JOB_NAME: "Create mici 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 }}-mici-raylib-ui"
MASTER_BRANCH_NAME: "openpilot_master_ui_mici_raylib"
# All report files are pushed here
REPORT_FILES_BRANCH_NAME: "mici-raylib-ui-reports"
jobs:
preview:
if: github.repository == 'commaai/openpilot'
name: preview
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
pull-requests: write
actions: read
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Waiting for ui generation to end
uses: lewagon/wait-on-check-action@v1.3.4
with:
ref: ${{ env.SHA }}
check-name: ${{ env.UI_JOB_NAME }}
repo-token: ${{ secrets.GITHUB_TOKEN }}
allowed-conclusions: success
wait-interval: 20
- name: Getting workflow run ID
id: get_run_id
run: |
echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?<number>[0-9]+)") | .number')" >> $GITHUB_OUTPUT
- name: Getting proposed ui # filename: pr_ui/mici_ui_replay.mp4
id: download-artifact
uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
run_id: ${{ steps.get_run_id.outputs.run_id }}
search_artifacts: true
name: mici-raylib-report-1-${{ env.REPORT_NAME }}
path: ${{ github.workspace }}/pr_ui
- name: Getting master ui # filename: master_ui_raylib/mici_ui_replay.mp4
uses: actions/checkout@v4
with:
repository: commaai/ci-artifacts
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
path: ${{ github.workspace }}/master_ui_raylib
ref: ${{ env.MASTER_BRANCH_NAME }}
- name: Saving new master ui
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
working-directory: ${{ github.workspace }}/master_ui_raylib
run: |
git checkout --orphan=new_master_ui_mici_raylib
git rm -rf *
git branch -D ${{ env.MASTER_BRANCH_NAME }}
git branch -m ${{ env.MASTER_BRANCH_NAME }}
git config user.name "GitHub Actions Bot"
git config user.email "<>"
mv ${{ github.workspace }}/pr_ui/* .
git add .
git commit -m "mici raylib video for commit ${{ env.SHA }}"
git push origin ${{ env.MASTER_BRANCH_NAME }} --force
- name: Setup FFmpeg
uses: AnimMouse/setup-ffmpeg@ae28d57dabbb148eff63170b6bf7f2b60062cbae
- name: Finding diff
if: github.event_name == 'pull_request_target'
id: find_diff
run: |
# Find the video file from PR
pr_video="${{ github.workspace }}/pr_ui/mici_ui_replay_proposed.mp4"
mv "${{ github.workspace }}/pr_ui/mici_ui_replay.mp4" "$pr_video"
master_video="${{ github.workspace }}/pr_ui/mici_ui_replay_master.mp4"
mv "${{ github.workspace }}/master_ui_raylib/mici_ui_replay.mp4" "$master_video"
# Run report
export PYTHONPATH=${{ github.workspace }}
baseurl="https://github.com/commaai/ci-artifacts/raw/refs/heads/${{ env.BRANCH_NAME }}"
diff_exit_code=0
python3 ${{ github.workspace }}/selfdrive/ui/tests/diff/diff.py "${{ github.workspace }}/pr_ui/mici_ui_replay_master.mp4" "${{ github.workspace }}/pr_ui/mici_ui_replay_proposed.mp4" "diff.html" --basedir "$baseurl" --no-open || diff_exit_code=$?
# Copy diff report files
cp ${{ github.workspace }}/selfdrive/ui/tests/diff/report/diff.html ${{ github.workspace }}/pr_ui/
cp ${{ github.workspace }}/selfdrive/ui/tests/diff/report/diff.mp4 ${{ github.workspace }}/pr_ui/
REPORT_URL="https://commaai.github.io/ci-artifacts/diff_pr_${{ github.event.number }}.html"
if [ $diff_exit_code -eq 0 ]; then
DIFF="✅ Videos are identical! [View Diff Report]($REPORT_URL)"
else
DIFF="❌ <strong>Videos differ!</strong> [View Diff Report]($REPORT_URL)"
fi
echo "DIFF=$DIFF" >> "$GITHUB_OUTPUT"
- name: Saving proposed ui
if: github.event_name == 'pull_request_target'
working-directory: ${{ github.workspace }}/master_ui_raylib
run: |
# Overwrite PR branch w/ proposed ui, and master ui at this point in time for future reference
git config user.name "GitHub Actions Bot"
git config user.email "<>"
git checkout --orphan=${{ env.BRANCH_NAME }}
git rm -rf *
mv ${{ github.workspace }}/pr_ui/* .
git add .
git commit -m "mici raylib video for PR #${{ github.event.number }}"
git push origin ${{ env.BRANCH_NAME }} --force
# Append diff report to report files branch
git fetch origin ${{ env.REPORT_FILES_BRANCH_NAME }}
git checkout ${{ env.REPORT_FILES_BRANCH_NAME }}
cp ${{ github.workspace }}/selfdrive/ui/tests/diff/report/diff.html diff_pr_${{ github.event.number }}.html
git add diff_pr_${{ github.event.number }}.html
git commit -m "mici raylib ui diff report for PR #${{ github.event.number }}" || echo "No changes to commit"
git push origin ${{ env.REPORT_FILES_BRANCH_NAME }}
- name: Comment Video on PR
if: github.event_name == 'pull_request_target'
uses: thollander/actions-comment-pull-request@v2
with:
message: |
<!-- _(run_id_video_mici_raylib **${{ github.run_id }}**)_ -->
## mici raylib UI Preview
${{ steps.find_diff.outputs.DIFF }}
comment_tag: run_id_video_mici_raylib
pr_number: ${{ github.event.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-42
View File
@@ -1,42 +0,0 @@
name: "model review"
on:
pull_request:
types: [opened, reopened, synchronize]
paths:
- 'selfdrive/modeld/models/*.onnx'
workflow_dispatch:
jobs:
comment:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
if: github.repository == 'commaai/openpilot'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Checkout master
uses: actions/checkout@v4
with:
ref: master
path: base
- run: git lfs pull
- run: cd base && git lfs pull
- run: pip install onnx
- name: scripts/reporter.py
id: report
run: |
echo "content<<EOF" >> $GITHUB_OUTPUT
echo "## Model Review" >> $GITHUB_OUTPUT
MASTER_PATH=${{ github.workspace }}/base python scripts/reporter.py >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Post model report comment
uses: marocchino/sticky-pull-request-comment@baa7203ed60924babbe5dcd0ac8eae3b66ec5e16
with:
header: model-review
message: ${{ steps.report.outputs.content }}
-39
View File
@@ -1,39 +0,0 @@
name: prebuilt
on:
schedule:
- cron: '0 * * * *'
workflow_dispatch:
env:
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
BUILD: selfdrive/test/docker_build.sh prebuilt
jobs:
build_prebuilt:
name: build prebuilt
runs-on: ubuntu-latest
if: github.repository == 'commaai/openpilot'
env:
PUSH_IMAGE: true
permissions:
checks: read
contents: read
packages: write
steps:
- name: Wait for green check mark
if: ${{ github.event_name != 'workflow_dispatch' }}
uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc
with:
ref: master
wait-interval: 30
running-workflow-name: 'build prebuilt'
repo-token: ${{ secrets.GITHUB_TOKEN }}
check-regexp: ^((?!.*(build master-ci).*).)*$
- uses: actions/checkout@v4
with:
submodules: true
- run: git lfs pull
- name: Build and Push docker image
run: |
$DOCKER_LOGIN
eval "$BUILD"
@@ -0,0 +1,35 @@
name: Publish TrueNAS Runner Image
on:
workflow_dispatch:
inputs:
image_tag:
description: "Container tag to publish to GHCR"
type: string
default: "latest"
required: true
permissions:
contents: read
packages: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push runner image
uses: docker/build-push-action@v6
with:
context: .
file: tools/truenas_github_runner/Dockerfile
push: true
tags: ghcr.io/${{ github.repository_owner }}/starpilot-truenas-runner:${{ inputs.image_tag }}
-175
View File
@@ -1,175 +0,0 @@
name: "raylib ui preview"
on:
push:
branches:
- master
pull_request_target:
types: [assigned, opened, synchronize, reopened, edited]
branches:
- 'master'
paths:
- 'selfdrive/assets/**'
- 'selfdrive/ui/**'
- 'system/ui/**'
workflow_dispatch:
env:
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 }}-raylib-ui"
jobs:
preview:
if: github.repository == 'commaai/openpilot'
name: preview
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
pull-requests: write
actions: read
steps:
- name: Waiting for ui generation to start
run: sleep 30
- name: Waiting for ui generation to end
uses: lewagon/wait-on-check-action@v1.3.4
with:
ref: ${{ env.SHA }}
check-name: ${{ env.UI_JOB_NAME }}
repo-token: ${{ secrets.GITHUB_TOKEN }}
allowed-conclusions: success
wait-interval: 20
- name: Getting workflow run ID
id: get_run_id
run: |
echo "run_id=$(curl https://api.github.com/repos/${{ github.repository }}/commits/${{ env.SHA }}/check-runs | jq -r '.check_runs[] | select(.name == "${{ env.UI_JOB_NAME }}") | .html_url | capture("(?<number>[0-9]+)") | .number')" >> $GITHUB_OUTPUT
- name: Getting proposed ui
id: download-artifact
uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
run_id: ${{ steps.get_run_id.outputs.run_id }}
search_artifacts: true
name: raylib-report-1-${{ env.REPORT_NAME }}
path: ${{ github.workspace }}/pr_ui
- name: Getting master ui
uses: actions/checkout@v4
with:
repository: commaai/ci-artifacts
ssh-key: ${{ secrets.CI_ARTIFACTS_DEPLOY_KEY }}
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_raylib
run: |
git checkout --orphan=new_master_ui_raylib
git rm -rf *
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 "raylib screenshots for commit ${{ env.SHA }}"
git push origin openpilot_master_ui_raylib --force
- name: Finding diff
if: github.event_name == 'pull_request_target'
id: find_diff
run: >-
sudo apt-get update && sudo apt-get install -y imagemagick
scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device')
A=($scenes)
DIFF=""
TABLE="<details><summary>All Screenshots</summary>"
TABLE="${TABLE}<table>"
for ((i=0; i<${#A[*]}; i=i+1));
do
# Check if the master file exists
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>"
DIFF="${DIFF}<table>"
DIFF="${DIFF}<tr>"
DIFF="${DIFF} <td> <img src=\"https://raw.githubusercontent.com/commaai/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
DIFF="${DIFF}</tr>"
DIFF="${DIFF}</table>"
DIFF="${DIFF}</details>"
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_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_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>"
DIFF="${DIFF}<table>"
DIFF="${DIFF}<tr>"
DIFF="${DIFF} <td> master <img src=\"https://raw.githubusercontent.com/commaai/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_master_ref.png\"> </td>"
DIFF="${DIFF} <td> proposed <img src=\"https://raw.githubusercontent.com/commaai/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
DIFF="${DIFF}</tr>"
DIFF="${DIFF}<tr>"
DIFF="${DIFF} <td> diff <img src=\"https://raw.githubusercontent.com/commaai/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_diff.png\"> </td>"
DIFF="${DIFF} <td> composite diff <img src=\"https://raw.githubusercontent.com/commaai/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}_diff.gif\"> </td>"
DIFF="${DIFF}</tr>"
DIFF="${DIFF}</table>"
DIFF="${DIFF}</details>"
else
rm -f ${{ github.workspace }}/pr_ui/${A[$i]}_diff.png
fi
INDEX=$(($i % 2))
if [[ $INDEX -eq 0 ]]; then
TABLE="${TABLE}<tr>"
fi
TABLE="${TABLE} <td> <img src=\"https://raw.githubusercontent.com/commaai/ci-artifacts/${{ env.BRANCH_NAME }}/${A[$i]}.png\"> </td>"
if [[ $INDEX -eq 1 || $(($i + 1)) -eq ${#A[*]} ]]; then
TABLE="${TABLE}</tr>"
fi
done
TABLE="${TABLE}</table></details>"
echo "DIFF=$DIFF$TABLE" >> "$GITHUB_OUTPUT"
- name: Saving proposed ui
if: github.event_name == 'pull_request_target'
working-directory: ${{ github.workspace }}/master_ui_raylib
run: |
git config user.name "GitHub Actions Bot"
git config user.email "<>"
git checkout --orphan=${{ env.BRANCH_NAME }}
git rm -rf *
mv ${{ github.workspace }}/pr_ui/* .
git add .
git commit -m "raylib screenshots for PR #${{ github.event.number }}"
git push origin ${{ env.BRANCH_NAME }} --force
- name: Comment Screenshots on PR
if: github.event_name == 'pull_request_target'
uses: thollander/actions-comment-pull-request@v2
with:
message: |
<!-- _(run_id_screenshots_raylib **${{ github.run_id }}**)_ -->
## raylib UI Preview
${{ steps.find_diff.outputs.DIFF }}
comment_tag: run_id_screenshots_raylib
pr_number: ${{ github.event.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-42
View File
@@ -1,42 +0,0 @@
name: release
on:
schedule:
- cron: '0 9 * * *'
workflow_dispatch:
jobs:
build_masterci:
name: build master-ci
env:
ImageOS: ubuntu24
container:
image: ghcr.io/commaai/openpilot-base:latest
runs-on: ubuntu-latest
if: github.repository == 'commaai/openpilot'
permissions:
checks: read
contents: write
steps:
- name: Install wait-on-check-action dependencies
run: |
sudo apt-get update
sudo apt-get install -y libyaml-dev
- name: Wait for green check mark
if: ${{ github.event_name == 'schedule' }}
uses: lewagon/wait-on-check-action@ccfb013c15c8afb7bf2b7c028fb74dc5a068cccc
with:
ref: master
wait-interval: 30
running-workflow-name: 'build master-ci'
repo-token: ${{ secrets.GITHUB_TOKEN }}
check-regexp: ^((?!.*(build prebuilt).*).)*$
- uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 0
- name: Pull LFS
run: |
git config --global --add safe.directory '*'
git lfs pull
- name: Push master-ci
run: BRANCH=__nightly release/build_stripped.sh
-72
View File
@@ -1,72 +0,0 @@
name: repo maintenance
on:
schedule:
- cron: "0 14 * * 1" # every Monday at 2am UTC (6am PST)
workflow_dispatch:
env:
BASE_IMAGE: openpilot-base
BUILD: selfdrive/test/docker_build.sh base
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
jobs:
update_translations:
runs-on: ubuntu-latest
if: github.repository == 'commaai/openpilot'
steps:
- uses: actions/checkout@v4
- uses: ./.github/workflows/setup-with-retry
- name: Update translations
run: |
${{ env.RUN }} "python3 selfdrive/ui/update_translations.py --vanish"
- name: Create Pull Request
uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83
with:
author: Vehicle Researcher <user@comma.ai>
commit-message: "Update translations"
title: "[bot] Update translations"
body: "Automatic PR from repo-maintenance -> update_translations"
branch: "update-translations"
base: "master"
delete-branch: true
labels: bot
package_updates:
name: package_updates
runs-on: ubuntu-latest
container:
image: ghcr.io/commaai/openpilot-base:latest
if: github.repository == 'commaai/openpilot'
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: uv lock
run: |
python3 -m ensurepip --upgrade
pip3 install uv
uv lock --upgrade
- name: bump submodules
run: |
git config --global --add safe.directory '*'
git submodule update --remote
git add .
- name: update car docs
run: |
export PYTHONPATH="$PWD"
scons -j$(nproc) --minimal opendbc_repo
python selfdrive/car/docs.py
git add docs/CARS.md
- name: Create Pull Request
uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83
with:
author: Vehicle Researcher <user@comma.ai>
token: ${{ secrets.ACTIONS_CREATE_PR_PAT }}
commit-message: Update Python packages
title: '[bot] Update Python packages'
branch: auto-package-updates
base: master
delete-branch: true
body: 'Automatic PR from repo-maintenance -> package_updates'
labels: bot
-71
View File
@@ -1,71 +0,0 @@
name: Schedule StarPilot Update
on:
workflow_dispatch:
inputs:
scheduled_date:
description: "Enter the date to update the \"StarPilot\" branch (YYYY-MM-DD)"
required: true
env:
GIT_EMAIL: "91348155+FrogAi@users.noreply.github.com"
GIT_NAME: "James"
TARGET_BRANCH: "StarPilot-Staging"
UPDATE_FILE_PATH: ".github/update_date"
jobs:
schedule_update:
runs-on: ubuntu-latest
steps:
- name: Checkout Target Branch
uses: actions/checkout@v4
with:
ref: ${{ env.TARGET_BRANCH }}
token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
fetch-depth: 3
- name: Configure Git Identity
run: |
git config --global user.name "$GIT_NAME"
git config --global user.email "$GIT_EMAIL"
- name: Write Schedule Date
env:
SCHEDULED_DATE: ${{ github.event.inputs.scheduled_date }}
run: |
echo "$SCHEDULED_DATE" > "$UPDATE_FILE_PATH"
git add "$UPDATE_FILE_PATH"
- name: Get Target Commit Data
id: get_target
run: |
TARGET_COMMIT=$(git rev-parse HEAD~1)
AUTHOR_DATE=$(git show -s --format=%aD "$TARGET_COMMIT")
COMMITTER_DATE=$(git show -s --format=%cD "$TARGET_COMMIT")
echo "AUTHOR_DATE=$AUTHOR_DATE" >> "$GITHUB_ENV"
echo "COMMITTER_DATE=$COMMITTER_DATE" >> "$GITHUB_ENV"
echo "TARGET_COMMIT=$TARGET_COMMIT" >> "$GITHUB_ENV"
- name: Create Fixup Commit
id: fixup_commit
run: |
if git diff --cached --quiet; then
echo "No changes detected."
echo "has_changes=false" >> "$GITHUB_OUTPUT"
else
echo "Changes detected. Creating fixup commit."
git commit --fixup="$TARGET_COMMIT"
echo "has_changes=true" >> "$GITHUB_OUTPUT"
fi
- name: Autosquash and Restore Timestamps
if: steps.fixup_commit.outputs.has_changes == 'true'
run: |
GIT_SEQUENCE_EDITOR=: git rebase --autosquash -i HEAD~3
git rebase --exec "GIT_COMMITTER_DATE='$COMMITTER_DATE' git commit --amend --no-edit --date='$AUTHOR_DATE'" HEAD~2
- name: Push Changes
if: steps.fixup_commit.outputs.has_changes == 'true'
run: |
git push origin "$TARGET_BRANCH" --force-with-lease
-52
View File
@@ -1,52 +0,0 @@
name: stale
on:
schedule:
- cron: '30 1 * * *'
workflow_dispatch:
env:
DAYS_BEFORE_PR_CLOSE: 7
DAYS_BEFORE_PR_STALE: 24
DAYS_BEFORE_PR_STALE_DRAFT: 30
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
exempt-all-milestones: true
# pull request config
stale-pr-message: 'This PR has had no activity for ${{ env.DAYS_BEFORE_PR_STALE }} days. It will be automatically closed in ${{ env.DAYS_BEFORE_PR_CLOSE }} days if there is no activity.'
close-pr-message: 'This PR has been automatically closed due to inactivity. Feel free to re-open once activity resumes.'
stale-pr-label: stale
delete-branch: ${{ github.event.pull_request.head.repo.full_name == 'commaai/openpilot' }} # only delete branches on the main repo
exempt-pr-labels: "ignore stale,needs testing" # if wip or it needs testing from the community, don't mark as stale
days-before-pr-stale: ${{ env.DAYS_BEFORE_PR_STALE }}
days-before-pr-close: ${{ env.DAYS_BEFORE_PR_CLOSE }}
exempt-draft-pr: false
# issue config
days-before-issue-stale: -1 # ignore issues for now
# same as above, but give draft PRs more time
stale_drafts:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
exempt-all-milestones: true
# pull request config
stale-pr-message: 'This PR has had no activity for ${{ env.DAYS_BEFORE_PR_STALE_DRAFT }} days. It will be automatically closed in ${{ env.DAYS_BEFORE_PR_CLOSE }} days if there is no activity.'
close-pr-message: 'This PR has been automatically closed due to inactivity. Feel free to re-open once activity resumes.'
stale-pr-label: stale
delete-branch: ${{ github.event.pull_request.head.repo.full_name == 'commaai/openpilot' }} # only delete branches on the main repo
exempt-pr-labels: "ignore stale,needs testing" # if wip or it needs testing from the community, don't mark as stale
days-before-pr-stale: ${{ env.DAYS_BEFORE_PR_STALE_DRAFT }}
days-before-pr-close: ${{ env.DAYS_BEFORE_PR_CLOSE }}
exempt-draft-pr: true
# issue config
days-before-issue-stale: -1 # ignore issues for now
-294
View File
@@ -1,294 +0,0 @@
name: tests
on:
push:
branches:
- master
pull_request:
workflow_dispatch:
workflow_call:
inputs:
run_number:
default: '1'
required: true
type: string
concurrency:
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:
PYTHONWARNINGS: error
BASE_IMAGE: openpilot-base
AZURE_TOKEN: ${{ secrets.AZURE_COMMADATACI_OPENPILOTCI_TOKEN }}
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
BUILD: selfdrive/test/docker_build.sh base
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
PYTEST: pytest --continue-on-collection-errors --durations=0 -n logical
jobs:
build_release:
name: build release
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"]') }}
env:
STRIPPED_DIR: /tmp/releasepilot
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Getting LFS files
uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e
with:
timeout_minutes: 2
max_attempts: 3
command: git lfs pull
- name: Build devel
timeout-minutes: 1
run: TARGET_DIR=$STRIPPED_DIR release/build_stripped.sh
- uses: ./.github/workflows/setup-with-retry
- name: Build openpilot and run checks
timeout-minutes: ${{ ((steps.restore-scons-cache.outputs.cache-hit == 'true') && 10 || 30) }} # allow more time when we missed the scons cache
run: |
cd $STRIPPED_DIR
${{ env.RUN }} "python3 system/manager/build.py"
- name: Run tests
timeout-minutes: 1
run: |
cd $STRIPPED_DIR
${{ env.RUN }} "release/check-dirty.sh"
- name: Check submodules
if: github.repository == 'commaai/openpilot'
timeout-minutes: 3
run: release/check-submodules.sh
build:
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"]') }}
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Setup docker push
if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' && github.repository == 'commaai/openpilot'
run: |
echo "PUSH_IMAGE=true" >> "$GITHUB_ENV"
$DOCKER_LOGIN
- uses: ./.github/workflows/setup-with-retry
- uses: ./.github/workflows/compile-openpilot
timeout-minutes: 30
build_mac:
name: build macOS
if: false # tmp disable due to brew install not working
runs-on: ${{ ((github.repository == 'commaai/openpilot') && ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.full_name == 'commaai/openpilot'))) && 'namespace-profile-macos-8x14' || 'macos-latest' }}
steps:
- uses: actions/checkout@v4
with:
submodules: true
- run: echo "CACHE_COMMIT_DATE=$(git log -1 --pretty='format:%cd' --date=format:'%Y-%m-%d-%H:%M')" >> $GITHUB_ENV
- name: Homebrew cache
uses: ./.github/workflows/auto-cache
with:
path: ~/Library/Caches/Homebrew
key: brew-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
restore-keys: |
brew-macos-${{ env.CACHE_COMMIT_DATE }}
brew-macos
- name: Install dependencies
run: ./tools/mac_setup.sh
env:
PYTHONWARNINGS: default # package install has DeprecationWarnings
HOMEBREW_DISPLAY_INSTALL_TIMES: 1
- run: git lfs pull
- name: Getting scons cache
uses: ./.github/workflows/auto-cache
with:
path: /tmp/scons_cache
key: scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}-${{ github.sha }}
restore-keys: |
scons-${{ runner.arch }}-macos-${{ env.CACHE_COMMIT_DATE }}
scons-${{ runner.arch }}-macos
- name: Building openpilot
run: . .venv/bin/activate && scons -j$(nproc)
static_analysis:
name: static analysis
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"]') }}
env:
PYTHONWARNINGS: default
steps:
- uses: actions/checkout@v4
with:
submodules: true
- uses: ./.github/workflows/setup-with-retry
- name: Static analysis
timeout-minutes: 1
run: ${{ env.RUN }} "scripts/lint/lint.sh"
unit_tests:
name: unit tests
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"]') }}
steps:
- uses: actions/checkout@v4
with:
submodules: true
- uses: ./.github/workflows/setup-with-retry
id: setup-step
- name: Build openpilot
run: ${{ env.RUN }} "scons -j$(nproc)"
- name: Run unit tests
timeout-minutes: ${{ contains(runner.name, 'nsc') && ((steps.setup-step.outputs.duration < 18) && 1 || 2) || 20 }}
run: |
${{ env.RUN }} "source selfdrive/test/setup_xvfb.sh && \
# 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' && \
chmod -R 777 /tmp/comma_download_cache"
process_replay:
name: process replay
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"]') }}
steps:
- uses: actions/checkout@v4
with:
submodules: true
- uses: ./.github/workflows/setup-with-retry
id: setup-step
- name: Cache test routes
id: dependency-cache
uses: actions/cache@v4
with:
path: .ci_cache/comma_download_cache
key: proc-replay-${{ hashFiles('selfdrive/test/process_replay/ref_commit', 'selfdrive/test/process_replay/test_processes.py') }}
- name: Build openpilot
run: |
${{ env.RUN }} "scons -j$(nproc)"
- name: Run replay
timeout-minutes: ${{ contains(runner.name, 'nsc') && (steps.dependency-cache.outputs.cache-hit == 'true') && ((steps.setup-step.outputs.duration < 18) && 1 || 2) || 20 }}
run: |
${{ env.RUN }} "selfdrive/test/process_replay/test_processes.py -j$(nproc) && \
chmod -R 777 /tmp/comma_download_cache"
- name: Print diff
id: print-diff
if: always()
run: cat selfdrive/test/process_replay/diff.txt
- uses: actions/upload-artifact@v4
if: always()
continue-on-error: true
with:
name: process_replay_diff.txt
path: selfdrive/test/process_replay/diff.txt
- name: Upload reference logs
if: false # TODO: move this to github instead of azure
run: |
${{ env.RUN }} "unset PYTHONWARNINGS && AZURE_TOKEN='$AZURE_TOKEN' python3 selfdrive/test/process_replay/test_processes.py -j$(nproc) --upload-only"
- name: Run regen
if: false
timeout-minutes: 4
run: |
${{ env.RUN }} "ONNXCPU=1 $PYTEST selfdrive/test/process_replay/test_regen.py && \
chmod -R 777 /tmp/comma_download_cache"
simulator_driving:
name: simulator driving
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: Started to timeout recently
steps:
- uses: actions/checkout@v4
with:
submodules: true
- uses: ./.github/workflows/setup-with-retry
id: setup-step
- name: Build openpilot
run: |
${{ env.RUN }} "scons -j$(nproc)"
- name: Driving test
timeout-minutes: ${{ (steps.setup-step.outputs.duration < 18) && 1 || 2 }}
run: |
${{ env.RUN }} "source selfdrive/test/setup_xvfb.sh && \
source selfdrive/test/setup_vsound.sh && \
CI=1 pytest -s tools/sim/tests/test_metadrive_bridge.py"
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"]') }}
steps:
- uses: actions/checkout@v4
with:
submodules: true
- uses: ./.github/workflows/setup-with-retry
- name: Build openpilot
run: ${{ env.RUN }} "scons -j$(nproc)"
- name: Create raylib UI Report
run: >
${{ 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: 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
create_mici_raylib_ui_report:
name: Create mici 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"]') }}
steps:
- uses: actions/checkout@v4
with:
submodules: true
- uses: ./.github/workflows/setup-with-retry
- name: Build openpilot
run: ${{ env.RUN }} "scons -j$(nproc)"
- name: Create mici raylib UI Report
run: >
${{ env.RUN }} "PYTHONWARNINGS=ignore &&
source selfdrive/test/setup_xvfb.sh &&
WINDOWED=1 python3 selfdrive/ui/tests/diff/replay.py"
- name: Upload Raylib UI Report
uses: actions/upload-artifact@v4
with:
name: mici-raylib-report-${{ inputs.run_number || '1' }}-${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && 'master' || github.event.number }}
path: selfdrive/ui/tests/diff/report
@@ -1,107 +0,0 @@
name: Update StarPilot Branch
on:
schedule:
- cron: "0 18 * * 6"
env:
BRANCH_STARPILOT: StarPilot
BRANCH_PREVIOUS: StarPilot-Previous
BRANCH_STAGING: StarPilot-Staging
GIT_EMAIL: "91348155+FrogAi@users.noreply.github.com"
GIT_NAME: "James"
GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
TZ: America/Phoenix
UPDATE_FILE: .github/update_date
jobs:
check_update:
runs-on: ubuntu-latest
outputs:
update_due: ${{ steps.check_update.outputs.update_due }}
scheduled_date: ${{ steps.check_update.outputs.scheduled_date }}
steps:
- name: Check Update Status
id: check_update
env:
REPO_NAME: ${{ github.repository }}
run: |
URL="https://raw.githubusercontent.com/$REPO_NAME/$BRANCH_STAGING/$UPDATE_FILE"
STATUS=$(curl -o /dev/null -s -w "%{http_code}\n" "$URL")
if [ "$STATUS" != "200" ]; then
echo "update_due=false" >> "$GITHUB_OUTPUT"
exit 0
fi
SCHEDULED_DATE=$(curl -s "$URL")
CURRENT_DATE=$(TZ="$TZ" date +%F)
if [ "$SCHEDULED_DATE" == "$CURRENT_DATE" ]; then
echo "update_due=true" >> "$GITHUB_OUTPUT"
echo "scheduled_date=$SCHEDULED_DATE" >> "$GITHUB_OUTPUT"
else
echo "update_due=false" >> "$GITHUB_OUTPUT"
fi
update_branch:
needs: check_update
if: ${{ needs.check_update.outputs.update_due == 'true' }}
runs-on: ubuntu-latest
steps:
- name: Checkout Staging
uses: actions/checkout@v4
with:
ref: ${{ env.BRANCH_STAGING }}
fetch-depth: 0
token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
- name: Configure Git Identity
run: |
git config --global user.name "$GIT_NAME"
git config --global user.email "$GIT_EMAIL"
- name: Update README and Cleanup
env:
SCHEDULED_DATE: ${{ needs.check_update.outputs.scheduled_date }}
run: |
DAY=$(TZ="$TZ" date +'%d' | sed 's/^0//')
case "$DAY" in
1|21|31) SUFFIX="st" ;;
2|22) SUFFIX="nd" ;;
3|23) SUFFIX="rd" ;;
*) SUFFIX="th" ;;
esac
MONTH=$(TZ="$TZ" date +'%B')
YEAR=$(TZ="$TZ" date +'%Y')
DATE_FMT="${MONTH} ${DAY}${SUFFIX}, ${YEAR}"
DATE_ESCAPED=$(printf '%s' "$DATE_FMT" | sed -E 's/ /%20/g; s/,/%2C/g')
sed -i -E "s|(Last%20Updated-)[^-)]*|\1${DATE_ESCAPED}|g" README.md
git rm -f "$UPDATE_FILE"
git add README.md
git commit -m "Updated README date to ${DATE_FMT}"
git reset --soft HEAD~2
ORIGINAL_MSG=$(git log -1 --pretty=%B HEAD)
COMMIT_PHX=$(TZ="$TZ" date -d "$SCHEDULED_DATE 12:00" +"%Y-%m-%dT%H:%M:%S %z")
GIT_COMMITTER_DATE="$COMMIT_PHX" GIT_AUTHOR_DATE="$COMMIT_PHX" git commit -m "$ORIGINAL_MSG"
- name: Wait Until Noon ${{ env.TZ }}
run: |
NOW=$(TZ="$TZ" date +%s)
TARGET=$(TZ="$TZ" date -d "12:00" +%s)
if [ "$NOW" -lt "$TARGET" ]; then
sleep $((TARGET - NOW))
fi
- name: Push and Sync Branches
run: |
git push origin "$BRANCH_STAGING" --force
git fetch origin "$BRANCH_STARPILOT:$BRANCH_STARPILOT"
git push origin "$BRANCH_STARPILOT:$BRANCH_PREVIOUS" --force
git push origin "$BRANCH_STAGING:$BRANCH_STARPILOT" --force
-95
View File
@@ -1,95 +0,0 @@
name: Update Tinygrad
on:
workflow_dispatch:
inputs:
runner:
description: "Select runner"
type: choice
options:
- c3
- c3x
default: "c3"
required: true
env:
GIT_EMAIL: "91348155+FrogAi@users.noreply.github.com"
GIT_NAME: "James"
GITLAB_REPO_DIR: "StarPilot-Resources"
GITLAB_URL: "gitlab.com/FrogAi/StarPilot-Resources.git"
OPENPILOT_DIR: "/data/openpilot"
jobs:
update_tinygrad:
runs-on: [self-hosted, "${{ inputs.runner }}"]
steps:
- name: Get Version
id: get_version
run: |
VERSION=$(grep -oP '^VERSION\s*=\s*"\K[^"]+' "$OPENPILOT_DIR/starpilot/assets/model_manager.py")
echo "VERSION=$VERSION"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Setup Workspace and Clone GitLab
id: setup
env:
GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }}
run: |
WORK_DIR="$RUNNER_TEMP/starpilot_tinygrad"
rm -rf "$WORK_DIR" && mkdir -p "$WORK_DIR"
echo "work_dir=$WORK_DIR" >> "$GITHUB_OUTPUT"
cd "$WORK_DIR"
git clone --depth 1 --branch Tinygrad "https://oauth2:${GITLAB_TOKEN}@$GITLAB_URL"
- name: Create Tinygrad Archive
working-directory: ${{ env.OPENPILOT_DIR }}
env:
WORK_DIR: ${{ steps.setup.outputs.work_dir }}
VERSION: ${{ steps.get_version.outputs.version }}
run: |
set -euo pipefail
ARCHIVE_DEST="$WORK_DIR/$GITLAB_REPO_DIR"
ARCHIVE_NAME="Tinygrad_$VERSION.tar.gz"
DUMMY_DIR=$(mktemp -d)
touch "$DUMMY_DIR/SConscript"
tar -czf "$ARCHIVE_DEST/$ARCHIVE_NAME" \
--exclude="*.a" \
--exclude="*.o" \
--exclude="*.onnx" \
--exclude="*__pycache__*" \
--exclude="*tests*" \
--exclude="selfdrive/modeld/SConscript" \
selfdrive/modeld tinygrad_repo \
-C "$DUMMY_DIR" \
--transform 's|^SConscript$|selfdrive/modeld/SConscript|' \
SConscript
rm -rf "$DUMMY_DIR"
- name: Push Updated Tinygrad
working-directory: ${{ steps.setup.outputs.work_dir }}/${{ env.GITLAB_REPO_DIR }}
env:
VERSION: ${{ steps.get_version.outputs.version }}
run: |
git config user.name "$GIT_NAME"
git config user.email "$GIT_EMAIL"
git add Tinygrad_*.tar.gz
if git diff --staged --quiet; then
echo "No changes to commit."
else
git commit -m "Updated Tinygrad: $VERSION"
git push origin Tinygrad
fi
- name: Cleanup Temporary Files
if: always()
env:
WORK_DIR: ${{ steps.setup.outputs.work_dir }}
run: |
rm -rf "$WORK_DIR"
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+3 -1
View File
@@ -8,7 +8,6 @@
**StarPilot** is a custom fork of [comma.ai's openpilot](https://comma.ai/openpilot),
an open source driver assistance system.
whoisdomi
Openpilot provides
* Automated Lane Centering
@@ -75,3 +74,6 @@ Download models, change settings, update software, visualize live model outputs
Uses your comma's sysroot/toolchain
* Toggle: "Use Precompiled Binaries" to allow switching between fast boot / editable builds
* Custom long maneuver tests, specifically designed for regen-only vehicles
## Third-Party Notices
* Portions of this software include modified versions of the Material Design Icons provided by Google under the Apache License 2.0. A copy of the license is included in the `LICENSE-MDI` file.
+17 -9
View File
@@ -153,7 +153,7 @@ lenv = {
}
# Allow callers to override cache/temp dirs used by subprocesses (e.g. tinygrad model compilation).
for key in ("HOME", "TMPDIR", "XDG_CACHE_HOME", "CACHEDB"):
for key in ("HOME", "TMPDIR", "XDG_CACHE_HOME", "CACHEDB", "PARAMS_ROOT"):
if key in os.environ:
lenv[key] = os.environ[key]
@@ -404,12 +404,22 @@ if arch == "larch64" and os.environ.get("SP_TICI_SYSROOT"):
qt_arm_moc = os.path.join(qt_tool_bin, "moc")
qt_arm_uic = os.path.join(qt_tool_bin, "uic")
qt_arm_rcc = os.path.join(qt_tool_bin, "rcc")
qt_host_bin = os.environ.get("SP_QT_HOST_BIN", "/usr/lib/qt5/bin")
qt_host_moc = os.environ.get("SP_QT_HOST_MOC", os.path.join(qt_host_bin, "moc"))
qt_host_uic = os.environ.get("SP_QT_HOST_UIC", os.path.join(qt_host_bin, "uic"))
qt_host_rcc = os.environ.get("SP_QT_HOST_RCC", "rcc")
if platform.machine() in ("aarch64", "arm64"):
if os.path.isfile(qt_arm_moc):
if "SP_QT_HOST_MOC" in os.environ:
qt_env['QT3_MOC'] = qt_host_moc
elif os.path.isfile(qt_arm_moc):
qt_env['QT3_MOC'] = qt_arm_moc
if os.path.isfile(qt_arm_uic):
if "SP_QT_HOST_UIC" in os.environ:
qt_env['QT3_UIC'] = qt_host_uic
elif os.path.isfile(qt_arm_uic):
qt_env['QT3_UIC'] = qt_arm_uic
if os.path.isfile(qt_arm_rcc):
if "SP_QT_HOST_RCC" in os.environ:
qt_env['SP_QT_RCC'] = qt_host_rcc
elif os.path.isfile(qt_arm_rcc):
qt_env['SP_QT_RCC'] = qt_arm_rcc
else:
qt_qemu = shutil.which("qemu-aarch64-static") or shutil.which("qemu-aarch64")
@@ -417,19 +427,17 @@ if arch == "larch64" and os.environ.get("SP_TICI_SYSROOT"):
if qt_qemu and os.path.isfile(qt_arm_moc):
qt_env['QT3_MOC'] = f"{qt_qemu} -L {qt_tool_root} {qt_arm_moc}"
else:
qt_host_bin = os.environ.get("SP_QT_HOST_BIN", "/usr/lib/qt5/bin")
qt_env['QT3_MOC'] = os.environ.get("SP_QT_HOST_MOC", os.path.join(qt_host_bin, "moc"))
qt_env['QT3_MOC'] = qt_host_moc
if qt_qemu and os.path.isfile(qt_arm_uic):
qt_env['QT3_UIC'] = f"{qt_qemu} -L {qt_tool_root} {qt_arm_uic}"
else:
qt_host_bin = os.environ.get("SP_QT_HOST_BIN", "/usr/lib/qt5/bin")
qt_env['QT3_UIC'] = os.environ.get("SP_QT_HOST_UIC", os.path.join(qt_host_bin, "uic"))
qt_env['QT3_UIC'] = qt_host_uic
if qt_qemu and os.path.isfile(qt_arm_rcc):
qt_env['SP_QT_RCC'] = f"{qt_qemu} -L {qt_tool_root} {qt_arm_rcc}"
else:
qt_env['SP_QT_RCC'] = os.environ.get("SP_QT_HOST_RCC", "rcc")
qt_env['SP_QT_RCC'] = qt_host_rcc
qt_env['CPPPATH'] += qt_dirs + ["#third_party/qrcode"]
qt_flags = [
+7
View File
@@ -59,6 +59,8 @@ struct StarPilotCarParams @0xaedffd8f31e7b55d {
isHDA2 @4 :Bool;
openpilotLongitudinalControlDisabled @5 :Bool;
safetyConfigs @6 :List(SafetyConfig);
pcmCruiseSpeed @7 :Bool = true;
redneckCruiseAvailable @8 :Bool;
struct SafetyConfig {
safetyParam @0 :UInt16;
@@ -85,6 +87,10 @@ struct StarPilotCarState @0xf35cc4560bbf6ec2 {
modePressed @16 :Bool;
customPressed @17 :Bool;
alwaysOnLateralAllowed @18 :Bool;
dashboardStopSign @19 :UInt8; # 0 = no signal / platform doesn't publish
cancelPressed @20 :Bool;
cancelLongPressed @21 :Bool;
cancelVeryLongPressed @22 :Bool;
}
struct StarPilotDeviceState @0xda96579883444c35 {
@@ -193,6 +199,7 @@ struct StarPilotPlan @0xf98d843bfd7004a3 {
weatherId @34 :Int16;
disableThrottle @35 :Bool;
trackingLead @36 :Bool;
stopSignConfirmed @37 :Bool;
}
struct StarPilotRadarState @0xb86e6369214c01c8 {
Binary file not shown.
+13 -13
View File
@@ -131,6 +131,13 @@ struct OnroadEvent @0xc4fa6047f024e718 {
excessiveActuation @96;
audioFeedback @97;
lateralManeuver @98;
pedalCruiseEnabled @99;
pedalCruiseDisabled @100;
pedalMaxRegen @101;
teslaCCEngaged @102;
teslaCCDisengaged @103;
teslaCCNotArmed @104;
pedalNotCalibrated @105;
soundsUnavailableDEPRECATED @47;
}
@@ -1262,12 +1269,6 @@ struct LongitudinalPlan @0xe00b5b3eba12876c {
solverExecutionTime @35 :Float32;
# lead trajectories the MPC solved against (13 points at T_IDXS)
leadTrajectoryX0 @40 :List(Float32);
leadTrajectoryV0 @41 :List(Float32);
leadTrajectoryX1 @42 :List(Float32);
leadTrajectoryV1 @43 :List(Float32);
enum LongitudinalPlanSource {
cruise @0;
lead0 @1;
@@ -2172,14 +2173,13 @@ struct DriverStateV2 {
facePosition @2 :List(Float32);
facePositionStd @3 :List(Float32);
faceProb @4 :Float32;
eyesVisibleProb @14 :Float32;
eyesClosedProb @15 :Float32;
leftEyeProb @5 :Float32;
rightEyeProb @6 :Float32;
leftBlinkProb @7 :Float32;
rightBlinkProb @8 :Float32;
sunglassesProb @9 :Float32;
phoneProb @13 :Float32;
leftEyeProbDEPRECATED @5 :Float32;
rightEyeProbDEPRECATED @6 :Float32;
leftBlinkProbDEPRECATED @7 :Float32;
rightBlinkProbDEPRECATED @8 :Float32;
sunglassesProbDEPRECATED @9 :Float32;
sleepProb @14 :Float32;
notReadyProbDEPRECATED @12 :List(Float32);
occludedProbDEPRECATED @10 :Float32;
readyProbDEPRECATED @11 :List(Float32);
Binary file not shown.
+243 -58
View File
@@ -1,70 +1,255 @@
from openpilot.common.params_pyx import Params as _Params, ParamKeyFlag, ParamKeyType, UnknownKeyName
assert _Params
assert ParamKeyFlag
assert ParamKeyType
assert UnknownKeyName
from __future__ import annotations
from enum import IntEnum, IntFlag
from pathlib import Path
import tempfile
class Params(_Params):
def get(self, key, block=False, return_default=False, encoding=None, default=None):
try:
value = super().get(key, block=block, return_default=return_default)
except UnknownKeyName:
return default
if value is None:
return default
if encoding is not None and isinstance(value, bytes):
try:
from openpilot.common.params_pyx import Params as _Params, ParamKeyFlag, ParamKeyType, UnknownKeyName
except Exception:
class UnknownKeyName(KeyError):
pass
class ParamKeyFlag(IntFlag):
PERSISTENT = 0x02
CLEAR_ON_MANAGER_START = 0x04
CLEAR_ON_ONROAD_TRANSITION = 0x08
CLEAR_ON_OFFROAD_TRANSITION = 0x10
DONT_LOG = 0x20
DEVELOPMENT_ONLY = 0x40
CLEAR_ON_IGNITION_ON = 0x80
ALL = 0xFFFFFFFF
class ParamKeyType(IntEnum):
STRING = 0
BOOL = 1
INT = 2
FLOAT = 3
TIME = 4
JSON = 5
BYTES = 6
def _load_key_types() -> dict[str, ParamKeyType]:
key_types: dict[str, ParamKeyType] = {}
params_keys = Path(__file__).with_name("params_keys.h")
if not params_keys.exists():
return key_types
for line in params_keys.read_text(encoding="utf-8", errors="ignore").splitlines():
if not line.lstrip().startswith('{"'):
continue
parts = line.split('"')
if len(parts) < 2:
continue
key = parts[1]
for type_name in ParamKeyType.__members__:
if f", {type_name}" in line:
key_types[key] = ParamKeyType[type_name]
break
else:
key_types[key] = ParamKeyType.STRING
return key_types
_KEY_TYPES = _load_key_types()
_PERSISTENT_STORE: dict[str, object] = {}
_MEMORY_STORE: dict[str, object] = {}
class Params:
def __init__(self, d: str | None = None, memory: bool = False, return_defaults: bool = False):
self.d = d if d is not None else ""
self.m = memory
self.return_defaults = return_defaults
self._store = _MEMORY_STORE if memory else _PERSISTENT_STORE
def __reduce__(self):
return type(self), (self.d, self.m, self.return_defaults)
def clear_all(self, tx_flag=ParamKeyFlag.ALL):
self._store.clear()
def check_key(self, key):
if isinstance(key, bytes):
key = key.decode("utf-8")
return str(key)
def _coerce_bool(self, value) -> bool:
if isinstance(value, bool):
return value
if isinstance(value, (int, float)):
return bool(value)
if isinstance(value, bytes):
return value == b"1"
if isinstance(value, str):
return value.strip().lower() not in ("", "0", "false", "none", "null")
return bool(value)
def _coerce_value(self, key: str, value):
value_type = self.get_type(key)
if value_type == ParamKeyType.BOOL:
return self._coerce_bool(value)
if value_type == ParamKeyType.INT:
return int(float(value))
if value_type == ParamKeyType.FLOAT:
return float(value)
if value_type == ParamKeyType.BYTES and isinstance(value, str):
return value.encode("utf-8")
return value
def get(self, key, block: bool = False, return_default: bool = False, encoding=None, default=None):
key = self.check_key(key)
value = self._store.get(key, default)
if value is None:
return default
if encoding is not None and isinstance(value, bytes):
try:
return value.decode(encoding)
except Exception:
return value.decode("utf-8", errors="replace")
return value
def get_bool(self, key, block: bool = False, default: bool = False):
value = self.get(key, block=block, return_default=True, default=default)
return self._coerce_bool(value)
def get_int(self, key, block: bool = False, return_default: bool = False, default: int = 0):
value = self.get(key, block=block, return_default=return_default, encoding="utf-8", default=default)
if value is None or value == "":
return default
try:
return value.decode(encoding)
except Exception:
return value.decode("utf-8", errors="replace")
return value
return int(float(value))
except (TypeError, ValueError):
return default
def get_bool(self, key, block=False, default=False):
try:
return super().get_bool(key, block=block)
except UnknownKeyName:
return bool(default)
def get_float(self, key, block: bool = False, return_default: bool = False, default: float = 0.0):
value = self.get(key, block=block, return_default=return_default, encoding="utf-8", default=default)
if value is None or value == "":
return default
try:
return float(value)
except (TypeError, ValueError):
return default
def get_int(self, key, block=False, return_default=False, default=0):
val = self.get(key, block=block, return_default=return_default, encoding="utf-8")
if val is None or val == "":
return default
try:
return int(float(val))
except ValueError:
return default
def put(self, key, dat):
key = self.check_key(key)
self._store[key] = self._coerce_value(key, dat)
def get_float(self, key, block=False, return_default=False, default=0.0):
val = self.get(key, block=block, return_default=return_default, encoding="utf-8")
if val is None or val == "":
return default
try:
return float(val)
except ValueError:
return default
def put_int(self, key, val):
t = self.get_type(key)
if t == ParamKeyType.FLOAT:
self.put(key, float(val))
elif t == ParamKeyType.INT:
self.put(key, int(val))
elif t == ParamKeyType.BOOL:
def put_bool(self, key, val: bool):
self.put(key, bool(val))
else:
self.put(key, str(int(val)))
def put_float(self, key, val):
t = self.get_type(key)
if t == ParamKeyType.FLOAT:
self.put(key, float(val))
elif t == ParamKeyType.INT:
def put_nonblocking(self, key, dat):
self.put(key, dat)
def put_bool_nonblocking(self, key, val: bool):
self.put_bool(key, val)
def put_int(self, key, val):
self.put(key, int(val))
elif t == ParamKeyType.BOOL:
self.put(key, bool(val))
else:
self.put(key, str(float(val)))
def put_float(self, key, val):
self.put(key, float(val))
def remove(self, key):
self._store.pop(self.check_key(key), None)
def get_param_path(self, key: str = ""):
base = Path(tempfile.gettempdir()) / ("params_memory" if self.m else "params")
base.mkdir(parents=True, exist_ok=True)
return str(base / key) if key else str(base)
def get_type(self, key):
return _KEY_TYPES.get(self.check_key(key), ParamKeyType.STRING)
def all_keys(self):
return list(_KEY_TYPES)
def get_default_value(self, key):
return None
def cpp2python(self, key, value):
return self._coerce_value(self.check_key(key), value)
def get_key_flag(self, key):
return ParamKeyFlag.PERSISTENT
def get_stock_value(self, key):
return None
def get_tuning_level(self, key):
return 0
else:
assert _Params
assert ParamKeyFlag
assert ParamKeyType
assert UnknownKeyName
class Params(_Params):
def get(self, key, block=False, return_default=False, encoding=None, default=None):
try:
value = super().get(key, block=block, return_default=return_default)
except UnknownKeyName:
return default
if value is None:
return default
if encoding is not None and isinstance(value, bytes):
try:
return value.decode(encoding)
except Exception:
return value.decode("utf-8", errors="replace")
return value
def get_bool(self, key, block=False, default=False):
try:
result = super().get(key, block=block, return_default=True)
if result is None:
return bool(default)
return bool(result)
except UnknownKeyName:
return bool(default)
def get_int(self, key, block=False, return_default=False, default=0):
val = self.get(key, block=block, return_default=return_default, encoding="utf-8")
if val is None or val == "":
return default
try:
return int(float(val))
except ValueError:
return default
def get_float(self, key, block=False, return_default=False, default=0.0):
val = self.get(key, block=block, return_default=return_default, encoding="utf-8")
if val is None or val == "":
return default
try:
return float(val)
except ValueError:
return default
def put_int(self, key, val):
t = self.get_type(key)
if t == ParamKeyType.FLOAT:
self.put(key, float(val))
elif t == ParamKeyType.INT:
self.put(key, int(val))
elif t == ParamKeyType.BOOL:
self.put(key, bool(val))
else:
self.put(key, str(int(val)))
def put_float(self, key, val):
t = self.get_type(key)
if t == ParamKeyType.FLOAT:
self.put(key, float(val))
elif t == ParamKeyType.INT:
self.put(key, int(val))
elif t == ParamKeyType.BOOL:
self.put(key, bool(val))
else:
self.put(key, str(float(val)))
if __name__ == "__main__":
import sys
+62 -19
View File
@@ -11,7 +11,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"AlwaysAllowUploads", {PERSISTENT, BOOL, "0"}},
{"AlwaysOnDM", {PERSISTENT, BOOL}},
{"ApiCache_Device", {PERSISTENT, STRING}},
{"ApiCache_FirehoseStats", {PERSISTENT, JSON}},
{"AssistNowToken", {PERSISTENT, STRING}},
{"AthenadPid", {PERSISTENT, INT}},
{"AthenadUploadQueue", {PERSISTENT, JSON}},
@@ -59,6 +58,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"GsmRoaming", {PERSISTENT, BOOL}},
{"HardwareSerial", {PERSISTENT, STRING}},
{"HasAcceptedTerms", {PERSISTENT, STRING, "0"}},
{"HondaGasFactorParams", {PERSISTENT, FLOAT}},
{"HondaWindFactorParams", {PERSISTENT, FLOAT}},
{"InstallDate", {PERSISTENT, TIME}},
{"IsDriverViewEnabled", {CLEAR_ON_MANAGER_START, BOOL}},
{"IsEngaged", {PERSISTENT, BOOL}},
@@ -66,7 +67,9 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"IsMetric", {PERSISTENT, BOOL}},
{"IsOffroad", {CLEAR_ON_MANAGER_START, BOOL}},
{"IsOnroad", {PERSISTENT, BOOL}},
{"IsRHD", {PERSISTENT, BOOL}},
{"IsRhdDetected", {PERSISTENT, BOOL}},
{"IsRHDOverride", {PERSISTENT, BOOL}},
{"IsReleaseBranch", {CLEAR_ON_MANAGER_START, BOOL}},
{"IsTakingSnapshot", {CLEAR_ON_MANAGER_START, BOOL}},
{"IsTestedBranch", {CLEAR_ON_MANAGER_START, BOOL}},
@@ -142,7 +145,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
// StarPilot variables
{"AccelerationPath", {PERSISTENT, BOOL, "1", "0", 2}},
{"AccelerationProfile", {PERSISTENT, INT, "2", "0", 0}},
{"AccelerationProfile", {PERSISTENT, INT, "0", "0", 0}},
{"AdjacentLeadsUI", {PERSISTENT, BOOL, "1", "0", 3}},
{"AdjacentPath", {PERSISTENT, BOOL, "0", "0", 3}},
{"AdjacentPathMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
@@ -150,7 +153,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"AdvancedLateralTune", {PERSISTENT, BOOL, "1", "0", 2}},
{"AdvancedLongitudinalTune", {PERSISTENT, BOOL, "1", "0", 3}},
{"AggressiveFollow", {PERSISTENT, FLOAT, "1.25", "1.25", 2}},
{"AggressiveFollowHigh", {PERSISTENT, FLOAT, "1.25", "1.25", 2}},
{"AggressiveFollowHigh", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
{"AggressiveJerkAcceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"AggressiveJerkDanger", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
{"AggressiveJerkDeceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
@@ -177,6 +180,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"CalibrationProgress", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"CameraView", {PERSISTENT, INT, "3", "0", 2}},
{"CancelDownloadMaps", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"DisableWideRoad", {PERSISTENT, BOOL, "0", "0", 3}},
{"CancelModelDownload", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"CancelThemeDownload", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"CarMake", {PERSISTENT, STRING, "mock", "mock", 0}},
@@ -193,7 +197,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"CESpeedLead", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
{"CEStatus", {CLEAR_ON_OFFROAD_TRANSITION, INT, "0", "0"}},
{"CEStopLights", {PERSISTENT, BOOL, "1", "0", 1}},
{"CEStoppedLead", {PERSISTENT, BOOL, "1", "0", 1}},
{"CEStoppedLead", {PERSISTENT, BOOL, "0", "0", 1}},
{"ClusterOffset", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
{"ColorScheme", {PERSISTENT, STRING, "frog", "stock", 0}},
{"ColorToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
@@ -216,6 +220,9 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"CustomCruise", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
{"CustomCruiseLong", {PERSISTENT, FLOAT, "5.0", "5.0", 2}},
{"CustomPersonalities", {PERSISTENT, BOOL, "0", "0", 2}},
{"CancelButtonControl", {PERSISTENT, INT, "1", "0", 2}},
{"CancelButtonControlsMigrated", {PERSISTENT, BOOL, "0", "0"}},
{"AOLLKASMigratedToButtonControl", {PERSISTENT, BOOL, "0", "0"}},
{"TrafficPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
{"AggressivePersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
{"StandardPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
@@ -270,12 +277,27 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"LongPitch", {PERSISTENT, BOOL, "1", "0", 2}},
{"RemoteStartBootsComma", {PERSISTENT, BOOL, "0", "0"}},
{"RemapCancelToDistance", {PERSISTENT, BOOL, "0", "0"}},
{"NAPAdaptiveAccel", {PERSISTENT, BOOL, "1", "1"}},
{"NAPFollowDistance", {PERSISTENT, INT, "4", "4"}},
{"NAPForcePreAP", {PERSISTENT, BOOL, "0", "0"}},
{"NAPPedalEnabled", {PERSISTENT, BOOL, "0", "0"}},
{"NAPPedalCanBus", {PERSISTENT, INT, "2", "2"}},
{"NAPPedalCalibDone", {PERSISTENT, BOOL, "0", "0"}},
{"NAPPedalCalibMin", {PERSISTENT, FLOAT, "-3.0", "-3.0"}},
{"NAPPedalCalibMax", {PERSISTENT, FLOAT, "99.6", "99.6"}},
{"NAPPedalCalibFactor", {PERSISTENT, FLOAT, "1.0", "1.0"}},
{"NAPPedalCalibZero", {PERSISTENT, FLOAT, "0.0", "0.0"}},
{"NAPPedalProfile", {PERSISTENT, INT, "4", "4"}},
{"NAPRadarBehindNosecone", {PERSISTENT, BOOL, "0", "0"}},
{"NAPRadarEnabled", {PERSISTENT, BOOL, "0", "0"}},
{"NAPRadarOffset", {PERSISTENT, FLOAT, "0.0", "0.0"}},
{"ForceAutoTune", {PERSISTENT, BOOL, "0", "0", 3}},
{"ForceAutoTuneOff", {PERSISTENT, BOOL, "1", "0", 2}},
{"ForceFingerprint", {PERSISTENT, BOOL, "0", "0", 2}},
{"ForceOffroad", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"ForceOnroad", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"ForceStops", {PERSISTENT, BOOL, "0", "0", 2}},
{"ForceStops", {PERSISTENT, BOOL, "1", "0", 2}},
{"ForceStopDistanceOffset", {PERSISTENT, INT, "0", "0", 2}},
{"ForceStandstill", {PERSISTENT, BOOL, "0", "0", 2}},
{"ForceTorqueController", {PERSISTENT, BOOL, "0", "0", 3}},
{"FPSCounter", {PERSISTENT, BOOL, "1", "0", 3}},
@@ -290,6 +312,10 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"GoatScreamCriticalAlerts", {PERSISTENT, BOOL, "0", "0", 1}},
{"GreenLightAlert", {PERSISTENT, BOOL, "0", "0", 0}},
{"HideAlerts", {PERSISTENT, BOOL, "0", "0", 2}},
{"HideChangingLanesBanner", {PERSISTENT, BOOL, "0", "0", 2}},
{"HideDistanceProfileBanner", {PERSISTENT, BOOL, "0", "0", 2}},
{"HideTurningBanner", {PERSISTENT, BOOL, "0", "0", 2}},
{"HideDMIcon", {PERSISTENT, BOOL, "0", "0", 2}},
{"HideLeadMarker", {PERSISTENT, BOOL, "0", "0", 2}},
{"HideMaxSpeed", {PERSISTENT, BOOL, "0", "0", 2}},
{"HideSpeed", {PERSISTENT, BOOL, "0", "0", 2}},
@@ -297,7 +323,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"HigherBitrate", {PERSISTENT, BOOL, "0", "0", 2}},
{"HolidayThemes", {PERSISTENT, BOOL, "1", "0", 0}},
{"HumanAcceleration", {PERSISTENT, BOOL, "0", "0", 2}},
{"HumanFollowing", {PERSISTENT, BOOL, "0", "0", 2}},
{"CoastUpToLeads", {PERSISTENT, BOOL, "1", "1", 2}},
{"HumanLaneChanges", {PERSISTENT, BOOL, "0", "0", 2}},
{"IconPack", {PERSISTENT, STRING, "frog-animated", "stock", 0}},
@@ -307,6 +332,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"IncreasedStoppedDistanceRain", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
{"IncreasedStoppedDistanceRainStorm", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
{"IncreasedStoppedDistanceSnow", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
{"RedneckCruise", {PERSISTENT, BOOL, "0", "0", 1}},
{"IncreaseFollowingLowVisibility", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
{"IncreaseFollowingRain", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
{"IncreaseFollowingRainStorm", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
@@ -330,6 +356,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"LKASButtonControl", {PERSISTENT, INT, "5", "0", 2}},
{"LockDoors", {PERSISTENT, BOOL, "1", "0", 0}},
{"LockDoorsTimer", {PERSISTENT, INT, "0", "0", 0}},
{"LongCancelButtonControl", {PERSISTENT, INT, "5", "0", 2}},
{"LongDistanceButtonControl", {PERSISTENT, INT, "5", "0", 2}},
{"LongModeButtonControl", {PERSISTENT, INT, "0", "0", 2}},
{"LongStarButtonControl", {PERSISTENT, INT, "0", "0", 2}},
@@ -341,7 +368,11 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"LongitudinalTune", {PERSISTENT, BOOL, "1", "0", 0}},
{"LoudBlindspotAlert", {PERSISTENT, BOOL, "0", "0", 0}},
{"LowVoltageShutdown", {PERSISTENT, FLOAT, "11.8", "11.8", 3}},
{"MainCruiseButtonControl", {PERSISTENT, INT, "9", "9", 2}},
{"ManualUpdateInitiated", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"AMapKey1", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"AMapKey2", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"ApiCache_NavDestinations", {PERSISTENT, JSON, "[]", "[]"}},
{"MapAcceleration", {PERSISTENT, BOOL, "0", "0", 1}},
{"MapboxPublicKey", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"MapBoxRequests", {PERSISTENT, JSON, "{}", "{}"}},
@@ -351,6 +382,11 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"MapGears", {PERSISTENT, BOOL, "0", "0", 2}},
{"MapsSelected", {PERSISTENT, STRING, "", "", 0}},
{"MapSpeedLimit", {CLEAR_ON_MANAGER_START, FLOAT, "0.0", "0.0"}},
{"NavDesiresAllowed", {PERSISTENT, BOOL, "1", "0", 2}},
{"NavLongitudinalAllowed", {PERSISTENT, BOOL, "1", "0", 2}},
{"NavDestination", {PERSISTENT | CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"NavInstructionCollapsed", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0", "0"}},
{"NavInstructionState", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, JSON, "{}", "{}"}},
{"NextMapSpeedLimit", {CLEAR_ON_MANAGER_START, JSON, "{}", "{}"}},
{"VisionSpeedLimit", {CLEAR_ON_MANAGER_START, FLOAT, "0.0", "0.0"}},
{"VisionSpeedLimitConfidence", {CLEAR_ON_MANAGER_START, FLOAT, "0.0", "0.0"}},
@@ -378,7 +414,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"NNFFModelName", {CLEAR_ON_MANAGER_START, STRING, "", "", 0}},
{"NoLogging", {PERSISTENT, BOOL, "0", "0", 2}},
{"NoUploads", {PERSISTENT, BOOL, "0", "0", 2}},
{"NudgelessLaneChange", {PERSISTENT, BOOL, "1", "0", 0}},
{"NudgelessLaneChange", {PERSISTENT, BOOL, "0", "0", 0}},
{"NumericalTemp", {PERSISTENT, BOOL, "1", "0", 3}},
{"Offset1", {PERSISTENT, FLOAT, "5.0", "0.0", 0}},
{"Offset2", {PERSISTENT, FLOAT, "5.0", "0.0", 0}},
@@ -400,6 +436,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"PauseAOLOnBrake", {PERSISTENT, BOOL, "0", "0", 1}},
{"PauseLateralOnSignal", {PERSISTENT, BOOL, "0", "0", 1}},
{"PauseLateralSpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
{"LateralResumeDelay", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
{"PedalsOnUI", {PERSISTENT, BOOL, "0", "0", 1}},
{"PondPaired", {PERSISTENT, BOOL, "0", "0", 0}},
{"PondUploadPending", {PERSISTENT, BOOL, "0", "0", 0}},
@@ -424,13 +461,13 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"ReduceLateralAccelerationRainStorm", {PERSISTENT, INT, "0", "0", 2}},
{"ReduceLateralAccelerationSnow", {PERSISTENT, INT, "0", "0", 2}},
{"RefuseVolume", {PERSISTENT, INT, "101", "101", 2}},
{"RelaxedFollow", {PERSISTENT, FLOAT, "1.75", "1.75", 2}},
{"RelaxedFollowHigh", {PERSISTENT, FLOAT, "1.75", "1.75", 2}},
{"RelaxedJerkAcceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"RelaxedFollow", {PERSISTENT, FLOAT, "1.6", "1.6", 2}},
{"RelaxedFollowHigh", {PERSISTENT, FLOAT, "1.4", "1.4", 2}},
{"RelaxedJerkAcceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
{"RelaxedJerkDanger", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
{"RelaxedJerkDeceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"RelaxedJerkSpeed", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"RelaxedJerkSpeedDecrease", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"RelaxedJerkDeceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
{"RelaxedJerkSpeed", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
{"RelaxedJerkSpeedDecrease", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
{"ReverseCruise", {PERSISTENT, BOOL, "0", "0", 1}},
{"RecoveryPower", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
{"RoadEdgesWidth", {PERSISTENT, FLOAT, "2.0", "2.0", 2}},
@@ -453,6 +490,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"ShowGPU", {PERSISTENT, BOOL, "0", "0", 3}},
{"ShowIP", {PERSISTENT, BOOL, "0", "0", 3}},
{"ShowMemoryUsage", {PERSISTENT, BOOL, "1", "0", 3}},
{"ShowModeStatusBanner", {PERSISTENT, BOOL, "1", "0", 2}},
{"ShownToggleDescriptions", {PERSISTENT, JSON, "{}", "{}"}},
{"ShowSLCOffset", {PERSISTENT, BOOL, "1", "0", 0}},
{"ShowSpeedLimits", {PERSISTENT, BOOL, "1", "0", 1}},
@@ -467,6 +505,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"SignalMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
{"SignalToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"SimpleMode", {PERSISTENT, BOOL, "0", "0", 0}},
{"SLCAbbreviatedSources", {PERSISTENT, BOOL, "0", "0", 3}},
{"SLCActiveSourcesOnly", {PERSISTENT, BOOL, "0", "0", 3}},
{"SLCConfirmation", {PERSISTENT, BOOL, "0", "0", 0}},
{"SLCConfirmationHigher", {PERSISTENT, BOOL, "0", "0", 0}},
{"SLCConfirmationLower", {PERSISTENT, BOOL, "0", "0", 0}},
@@ -481,6 +521,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"SNGHack", {PERSISTENT, BOOL, "1", "0", 2}},
{"SoundPack", {PERSISTENT, STRING, "frog", "stock", 0}},
{"SoundToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"SLCAdoptSpeedLimit", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"SLCForceCruiseSpeed", {CLEAR_ON_MANAGER_START, FLOAT, "0.0", "0.0"}},
{"SpeedLimitAccepted", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"SpeedLimitChangedAlert", {PERSISTENT, BOOL, "0", "0", 0}},
{"SpeedLimitController", {PERSISTENT, BOOL, "0", "0", 0}},
@@ -493,12 +535,12 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"VisionSpeedLimitDetection", {PERSISTENT, BOOL, "0", "0", 0}},
{"VisionSpeedLimitTrainingCollector", {PERSISTENT, BOOL, "1", "1", 0}},
{"StandardFollow", {PERSISTENT, FLOAT, "1.45", "1.45", 2}},
{"StandardFollowHigh", {PERSISTENT, FLOAT, "1.45", "1.45", 2}},
{"StandardJerkAcceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"StandardFollowHigh", {PERSISTENT, FLOAT, "1.2", "1.2", 2}},
{"StandardJerkAcceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
{"StandardJerkDanger", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
{"StandardJerkDeceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"StandardJerkSpeed", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"StandardJerkSpeedDecrease", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"StandardJerkDeceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
{"StandardJerkSpeed", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
{"StandardJerkSpeedDecrease", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
{"StandbyMode", {PERSISTENT, BOOL, "0", "0", 1}},
{"StartAccel", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"StartAccelStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
@@ -529,7 +571,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"SwitchbackModeEnabled", {CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0", "0"}},
{"SubaruSNG", {PERSISTENT, BOOL, "1", "0", 2}},
{"TacoTune", {PERSISTENT, BOOL, "0", "0", 2}},
{"TacoTuneHacks", {PERSISTENT, BOOL, "0", "0", 2}},
{"TestAlert", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"TetheringEnabled", {PERSISTENT, INT, "0", "0", 0}},
{"ThemeDownloadProgress", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
@@ -562,10 +603,12 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"VEgoStartingStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"VEgoStopping", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"VEgoStoppingStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"VeryLongCancelButtonControl", {PERSISTENT, INT, "6", "0", 2}},
{"VeryLongDistanceButtonControl", {PERSISTENT, INT, "6", "0", 2}},
{"VeryLongModeButtonControl", {PERSISTENT, INT, "0", "0", 2}},
{"VeryLongStarButtonControl", {PERSISTENT, INT, "0", "0", 2}},
{"VoltSNG", {PERSISTENT, BOOL, "0", "0", 2}},
{"GMAutoHold", {PERSISTENT, BOOL, "0", "0", 2}},
{"WarningImmediateVolume", {PERSISTENT, INT, "101", "101", 2}},
{"WarningSoftVolume", {PERSISTENT, INT, "101", "101", 2}},
{"WeatherPresets", {PERSISTENT, BOOL, "0", "0", 2}},
Binary file not shown.
+2 -2
View File
@@ -133,8 +133,8 @@ class TestParams:
def test_params_get_type(self):
# json
self.params.put("ApiCache_FirehoseStats", {"a": 0})
assert self.params.get("ApiCache_FirehoseStats") == {"a": 0}
self.params.put("ApiCache_DriveStats", {"a": 0})
assert self.params.get("ApiCache_DriveStats") == {"a": 0}
# int
self.params.put("BootCount", 1441)
+113
View File
@@ -0,0 +1,113 @@
# TrueNAS SCALE GitHub Build Runner
This setup runs a single GitHub self-hosted runner as a TrueNAS SCALE custom app and routes the `Compile StarPilot` workflow onto that runner.
The app container:
- registers one repository-scoped GitHub Actions runner
- stays visible in the TrueNAS Apps UI
- mounts the host Docker socket so `./build` can launch the existing larch64 build container flow
- persists runner state and build caches in one dataset mounted at `/runner`
## What this workflow does
`.github/workflows/compile_starpilot.yaml` now:
- lets an admin choose a `target_ref` branch from the Actions UI
- checks out that branch on the NAS runner
- runs `./build`
- prunes the tree with `scripts/ci_package_prebuilt_tree.sh`
- commits the result as `build`
- pushes that commit back to the selected branch without force-pushing
Use a branch intended for built output. The prune step removes developer-facing files and is not meant for normal day-to-day development branches.
## 1. Publish the runner image
You need a registry image for TrueNAS to deploy. The repo includes `.github/workflows/publish_truenas_runner.yaml` for this.
1. Open **Actions** on GitHub.
2. Run **Publish TrueNAS Runner Image**.
3. Leave `image_tag` as `latest` unless you want a versioned tag.
4. After it finishes, the image is available at:
```text
ghcr.io/<repo-owner>/starpilot-truenas-runner:latest
```
If you prefer to build it locally, run:
```bash
docker build -f tools/truenas_github_runner/Dockerfile -t ghcr.io/<repo-owner>/starpilot-truenas-runner:latest .
docker push ghcr.io/<repo-owner>/starpilot-truenas-runner:latest
```
## 2. Create a runner token
Generate a repository-scoped self-hosted runner token in GitHub:
1. Open the repository.
2. Go to **Settings > Actions > Runners**.
3. Click **New self-hosted runner**.
4. Choose **Linux** and **x64**.
5. Copy the one-time registration token from the generated setup commands.
That token expires quickly. Paste it into TrueNAS during deployment and restart the app if you need to re-register.
## 3. Deploy on TrueNAS SCALE
TrueNAS custom apps support Docker Compose YAML through **Apps > Discover Apps > Custom App > Install via YAML**.
Create a dataset for persistent runner state first, for example:
```text
/mnt/<pool>/apps/starpilot-runner
```
Then paste this compose YAML into TrueNAS after replacing the placeholders:
```yaml
services:
starpilot-runner:
image: ghcr.io/<repo-owner>/starpilot-truenas-runner:latest
container_name: starpilot-runner
restart: unless-stopped
environment:
GITHUB_REPOSITORY_URL: https://github.com/<repo-owner>/<repo-name>
RUNNER_NAME: starpilot-truenas-runner
RUNNER_LABELS: self-hosted,truenas,starpilot-build
RUNNER_WORKDIR: /runner/_work
RUNNER_TOKEN: <paste-one-time-runner-token>
DOCKER_GID: ""
REMOVE_RUNNER_ON_EXIT: "0"
volumes:
- /mnt/<pool>/apps/starpilot-runner:/runner
- /var/run/docker.sock:/var/run/docker.sock
```
If the container starts but cannot talk to Docker, inspect the Docker socket group on TrueNAS and set `DOCKER_GID` to that numeric group id.
## 4. Verify runner registration
Confirm all of the following:
- the app shows as `Running` in TrueNAS
- the container passes its healthcheck
- GitHub shows the runner as online with labels `self-hosted`, `truenas`, and `starpilot-build`
## 5. Run a build
1. Open **Actions > Compile StarPilot**.
2. Click **Run workflow**.
3. Enter the branch name in `target_ref`.
4. Optionally set `jobs` to the parallelism you want for `./build`.
5. Start the run.
On success, the workflow pushes a new commit named `build` back to that branch.
## Operational notes
- `RUNNER_TOKEN` is only required on first registration or when you want to replace a stale runner with the same name.
- Runner state is persisted in `/runner`, so normal restarts do not need a new token.
- `REMOVE_RUNNER_ON_EXIT=1` enables best-effort deregistration on shutdown, but that only works when the provided token is still valid.
- The build cache, GitHub workspace, and `.comma_sysroot` data all persist under the runner dataset as long as the workflow runs in the same mounted work area.
+1 -58
View File
@@ -33,64 +33,7 @@ function agnos_init {
# StarPilot variables
sudo chmod 0777 /cache
# Weston loads display color correction from /data/misc/display/color_cal/color_cal.
# Prefer a factory /persist/comma/color_cal blob when present. Otherwise, derive a
# Weston-compatible calibration blob from the device's legacy dwo gamma tables.
COLOR_CAL_SRC="/persist/comma/color_cal"
DWO_GAMMA_SRC="/persist/comma/dwo_gamma_curves"
COLOR_CAL_DST_DIR="/data/misc/display/color_cal"
COLOR_CAL_DST="${COLOR_CAL_DST_DIR}/color_cal"
COLOR_CAL_HASH_PATH="${COLOR_CAL_DST_DIR}/source.sha256"
DWO_REFERENCE="$DIR/tools/reference_dwo_gamma_curves.txt"
DWO_GENERATOR="$DIR/tools/generate_color_cal_from_dwo.py"
COLOR_CAL_UPDATED=0
COLOR_CAL_TMP=""
DWO_SOURCE_HASH=""
if [ -f "$COLOR_CAL_SRC" ]; then
sudo mkdir -p "$COLOR_CAL_DST_DIR"
if [ ! -f "$COLOR_CAL_DST" ] || ! cmp -s "$COLOR_CAL_SRC" "$COLOR_CAL_DST"; then
sudo cp "$COLOR_CAL_SRC" "$COLOR_CAL_DST"
sudo chown -R comma:comma "$COLOR_CAL_DST_DIR"
sudo chmod 664 "$COLOR_CAL_DST"
sudo rm -f "$COLOR_CAL_HASH_PATH"
COLOR_CAL_UPDATED=1
fi
elif [ -f "$DWO_GAMMA_SRC" ] && [ -f "$DWO_REFERENCE" ] && [ -f "$DWO_GENERATOR" ]; then
DWO_SOURCE_HASH="$(cat "$DWO_GAMMA_SRC" "$DWO_REFERENCE" "$DWO_GENERATOR" | sha256sum | awk '{print $1}')"
if [ ! -f "$COLOR_CAL_DST" ] || [ ! -f "$COLOR_CAL_HASH_PATH" ] || [ "$(cat "$COLOR_CAL_HASH_PATH" 2>/dev/null)" != "$DWO_SOURCE_HASH" ]; then
COLOR_CAL_TMP="$(mktemp)"
if python3 "$DWO_GENERATOR" --reference "$DWO_REFERENCE" --input "$DWO_GAMMA_SRC" --output "$COLOR_CAL_TMP"; then
sudo mkdir -p "$COLOR_CAL_DST_DIR"
if [ ! -f "$COLOR_CAL_DST" ] || ! cmp -s "$COLOR_CAL_TMP" "$COLOR_CAL_DST"; then
sudo cp "$COLOR_CAL_TMP" "$COLOR_CAL_DST"
COLOR_CAL_UPDATED=1
fi
printf '%s' "$DWO_SOURCE_HASH" | sudo tee "$COLOR_CAL_HASH_PATH" >/dev/null
sudo chown -R comma:comma "$COLOR_CAL_DST_DIR"
sudo chmod 664 "$COLOR_CAL_DST" "$COLOR_CAL_HASH_PATH"
fi
rm -f "$COLOR_CAL_TMP"
fi
fi
if [ "$COLOR_CAL_UPDATED" = "1" ] && systemctl is-active --quiet weston.service; then
sudo systemctl restart weston.service
# Weston can recreate wayland-0 as root on service restart before weston-ready
# fixes ownership. Repair it here so the Qt UI can always reconnect.
if [ -d /var/tmp/weston ]; then
for _ in $(seq 1 50); do
if [ -S /var/tmp/weston/wayland-0 ]; then
sudo chown -R comma:comma /var/tmp/weston 2>/dev/null || true
sudo chmod -R 700 /var/tmp/weston 2>/dev/null || true
SOCKET_OWNER="$(stat -c '%U:%G' /var/tmp/weston/wayland-0 2>/dev/null || true)"
[ "$SOCKET_OWNER" = "comma:comma" ] && break
fi
sleep 0.1
done
fi
fi
sudo rm -f /data/misc/display/color_cal/color_cal /data/misc/display/color_cal/source.sha256
# Check if AGNOS update is required
if [ $(< /VERSION) != "$AGNOS_VERSION" ]; then
Executable
+573
View File
@@ -0,0 +1,573 @@
#!/bin/sh
""":"
REPO_ROOT="$(CDPATH= cd -- "$(dirname "$0")" && pwd)"
PYTHON_BIN="$REPO_ROOT/.venv/bin/python"
if [ ! -x "$PYTHON_BIN" ]; then
echo "Missing $PYTHON_BIN. Run 'uv sync' from $REPO_ROOT first." >&2
exit 1
fi
exec "$PYTHON_BIN" "$0" "$@"
":"""
from __future__ import annotations
import argparse
import bz2
import io
import json
import os
import re
import shutil
import sys
import zipfile
from dataclasses import dataclass
from datetime import UTC, datetime
from email.utils import parsedate_to_datetime
from pathlib import Path
from typing import Any
from urllib.parse import quote, urlparse
import requests
import zstandard
from cereal import log
API_HOST = os.getenv("COMMA_API_HOST", "https://api.commadotai.com").rstrip("/")
ROUTE_ID_RE = re.compile(r"([0-9a-f]{16})/([^/]+)")
@dataclass(frozen=True)
class StreamSpec:
api_key: str
display_name: str
@dataclass(frozen=True)
class RouteId:
dongle_id: str
log_id: str
@property
def canonical_name(self) -> str:
return f"{self.dongle_id}|{self.log_id}"
@property
def cli_name(self) -> str:
return f"{self.dongle_id}/{self.log_id}"
@property
def safe_name(self) -> str:
return f"{self.dongle_id}_{self.log_id}"
@dataclass
class DownloadedFile:
segment: int
stream: str
filename: str
relative_path: str
mtime_epoch: int | None
@dataclass
class MapTileSummary:
qlogs_scanned: int = 0
mapd_messages: int = 0
tile_loaded_messages: int = 0
first_tile_segment: int | None = None
@property
def tile_loaded_any(self) -> bool:
return self.tile_loaded_messages > 0
class ValidationError(Exception):
def __init__(self, route: RouteId, reasons: list[str]):
self.route = route
self.reasons = reasons
super().__init__(self._render())
def _render(self) -> str:
lines = [f"Validation failed for {self.route.cli_name}:"]
lines.extend(f"- {reason}" for reason in self.reasons)
return "\n".join(lines)
REQUIRED_STREAMS = (
StreamSpec("qlogs", "qlog"),
StreamSpec("logs", "rlog"),
StreamSpec("cameras", "fcamera.hevc"),
)
def parse_args() -> argparse.Namespace:
argv = list(sys.argv[1:])
if argv and ROUTE_ID_RE.fullmatch(argv[0].lstrip("-")):
if argv[0].startswith("--"):
argv[0] = argv[0][2:]
argv = ["--routeid", argv[0], *argv[1:]]
parser = argparse.ArgumentParser(
description="Validate and bundle a public comma route for speed-limit vision review."
)
parser.add_argument("routeid_positional", nargs="?", help="Route id like 'dongle/logid' or 'dongle/logid--7'.")
parser.add_argument("--routeid", help="Route id like 'dongle/logid' or 'dongle/logid--7'.")
parser.add_argument("--desktop-root", type=Path, default=Path.home() / "Desktop", help="Destination root for the bundle folder and zip.")
parser.add_argument("--timeout", type=float, default=60.0, help="HTTP timeout in seconds.")
parser.add_argument("--overwrite", action="store_true", help="Replace an existing Desktop bundle for this route.")
parser.add_argument("--validate-only", action="store_true", help="Run validation only and skip downloads/zipping.")
args = parser.parse_args(argv)
args.routeid = args.routeid or args.routeid_positional
if not args.routeid:
parser.error("the following arguments are required: --routeid")
return args
def parse_route_id(raw: str) -> RouteId:
text = raw.strip().strip("'\"").replace("|", "/")
match = ROUTE_ID_RE.fullmatch(text)
if match is None:
raise ValueError(f"Unrecognized route id: {raw}")
dongle_id = match.group(1)
tail = match.group(2)
parts = tail.split("--")
if len(parts) == 3 and parts[-1].isdigit():
log_id = "--".join(parts[:2])
else:
log_id = tail
if len(log_id) != 20:
raise ValueError(f"Invalid log id in route: {raw}")
return RouteId(dongle_id=dongle_id, log_id=log_id)
def route_url(route: RouteId) -> str:
return f"{API_HOST}/v1/route/{quote(route.canonical_name, safe='')}/"
def route_files_url(route: RouteId) -> str:
return f"{API_HOST}/v1/route/{quote(route.canonical_name, safe='')}/files"
def format_segments(segments: list[int]) -> str:
if not segments:
return "none"
ranges: list[str] = []
start = prev = segments[0]
for segment in segments[1:]:
if segment == prev + 1:
prev = segment
continue
ranges.append(f"{start}-{prev}" if start != prev else str(start))
start = prev = segment
ranges.append(f"{start}-{prev}" if start != prev else str(start))
return ", ".join(ranges)
def as_int(value: Any) -> int | None:
if isinstance(value, bool):
return int(value)
if isinstance(value, int):
return value
if isinstance(value, str) and value.isdigit():
return int(value)
return None
def relative_posix(path: Path, root: Path) -> str:
return path.relative_to(root).as_posix()
def fetch_json(session: requests.Session, url: str, timeout: float) -> Any:
response = session.get(url, timeout=timeout, allow_redirects=True)
response.raise_for_status()
return response.json()
def fetch_bytes(session: requests.Session, url: str, timeout: float) -> bytes:
response = session.get(url, timeout=timeout, allow_redirects=True)
response.raise_for_status()
return response.content
def fetch_stream_to_path(session: requests.Session, url: str, dest_path: Path, timeout: float) -> int | None:
dest_path.parent.mkdir(parents=True, exist_ok=True)
response = session.get(url, stream=True, timeout=timeout, allow_redirects=True)
response.raise_for_status()
temp_path = dest_path.with_suffix(dest_path.suffix + ".part")
with temp_path.open("wb") as handle:
for chunk in response.iter_content(chunk_size=1 << 20):
if chunk:
handle.write(chunk)
temp_path.replace(dest_path)
last_modified = response.headers.get("Last-Modified")
if not last_modified:
return None
epoch = int(parsedate_to_datetime(last_modified).timestamp())
os.utime(dest_path, (epoch, epoch))
return epoch
def decompress_log_bytes(payload: bytes) -> bytes:
if payload[:3] == b"BZh":
return bz2.decompress(payload)
if payload[:4] == b"\x28\xb5\x2f\xfd":
reader = zstandard.ZstdDecompressor().stream_reader(io.BytesIO(payload))
try:
return reader.read()
finally:
reader.close()
return payload
def load_init_params(qlog_bytes: bytes) -> dict[str, str]:
payload = decompress_log_bytes(qlog_bytes)
for msg in log.Event.read_multiple_bytes(payload):
if msg.which() != "initData":
continue
params: dict[str, str] = {}
entries = msg.initData.params.to_dict().get("entries", [])
for entry in entries:
key = entry.get("key")
if not key:
continue
value = entry.get("value", b"")
if isinstance(value, bytes):
value = value.decode("utf-8", "replace")
elif value is None:
value = ""
else:
value = str(value)
params[str(key)] = value
return params
raise RuntimeError("No initData params found in qlog.")
def scan_qlogs_for_map_tiles(session: requests.Session, qlog_urls: list[str | None], timeout: float) -> MapTileSummary:
summary = MapTileSummary()
for segment, url in enumerate(qlog_urls):
if not isinstance(url, str) or not url:
continue
summary.qlogs_scanned += 1
payload = decompress_log_bytes(fetch_bytes(session, url, timeout))
for msg in log.Event.read_multiple_bytes(payload):
if msg.which() != "mapdOut":
continue
summary.mapd_messages += 1
if msg.mapdOut.tileLoaded:
summary.tile_loaded_messages += 1
if summary.first_tile_segment is None:
summary.first_tile_segment = segment
if summary.tile_loaded_any:
break
return summary
def expected_segment_count(route_meta: dict[str, Any], files_payload: dict[str, Any]) -> int:
candidates: list[int] = []
for key in ("maxqlog", "maxlog"):
value = as_int(route_meta.get(key))
if value is not None:
candidates.append(value + 1)
for key in ("qlogs", "logs", "cameras"):
value = files_payload.get(key)
if isinstance(value, list):
candidates.append(len(value))
return max(candidates, default=0)
def collect_missing_segments(urls: list[Any], expected_segments: int) -> list[int]:
missing: list[int] = []
for segment in range(expected_segments):
url = urls[segment] if segment < len(urls) else None
if not isinstance(url, str) or not url:
missing.append(segment)
return missing
def first_available_url(urls: list[Any]) -> str | None:
for url in urls:
if isinstance(url, str) and url:
return url
return None
def filename_from_url(url: str) -> str:
return Path(urlparse(url).path).name
def validate_route(route: RouteId, session: requests.Session, timeout: float) -> dict[str, Any]:
try:
route_meta = fetch_json(session, route_url(route), timeout)
except requests.HTTPError as exc:
status_code = exc.response.status_code if exc.response is not None else "unknown"
raise ValidationError(route, [f"route is not publicly accessible from comma connect (HTTP {status_code})."]) from exc
if not route_meta.get("is_public", False):
raise ValidationError(route, ["route metadata loaded, but `is_public` was false."])
try:
files_payload = fetch_json(session, route_files_url(route), timeout)
except requests.HTTPError as exc:
status_code = exc.response.status_code if exc.response is not None else "unknown"
raise ValidationError(route, [f"public route files could not be fetched from comma connect (HTTP {status_code})."]) from exc
expected_segments = expected_segment_count(route_meta, files_payload)
if expected_segments <= 0:
raise ValidationError(route, ["could not determine any route segments from comma connect."])
failures: list[str] = []
stream_urls: dict[str, list[Any]] = {}
for spec in REQUIRED_STREAMS:
urls = files_payload.get(spec.api_key)
if not isinstance(urls, list):
urls = []
stream_urls[spec.api_key] = urls
missing_segments = collect_missing_segments(urls, expected_segments)
if missing_segments:
failures.append(f"missing uploaded {spec.display_name} files for segment(s): {format_segments(missing_segments)}.")
first_qlog_url = first_available_url(stream_urls["qlogs"])
params: dict[str, str] = {}
map_tiles = MapTileSummary()
if first_qlog_url is None:
failures.append("no qlog was available to inspect route params.")
else:
try:
params = load_init_params(fetch_bytes(session, first_qlog_url, timeout))
except Exception as exc:
failures.append(f"could not read `initData.params` from the first qlog: {exc}.")
if params:
auto_bookmark = params.get("VisionSpeedLimitAutoBookmark", "")
if auto_bookmark != "1":
failures.append(f"`VisionSpeedLimitAutoBookmark` was {auto_bookmark!r}; it must be '1'.")
vision_detection = params.get("VisionSpeedLimitDetection", "")
if vision_detection != "1":
failures.append(f"`VisionSpeedLimitDetection` was {vision_detection!r}; it must be '1' for useful vision-model collection.")
try:
qlog_urls = [url if isinstance(url, str) and url else None for url in stream_urls["qlogs"]]
map_tiles = scan_qlogs_for_map_tiles(session, qlog_urls, timeout)
if not map_tiles.tile_loaded_any:
maps_selected = params.get("MapsSelected", "")
if maps_selected:
failures.append(
"offline map tiles never loaded during this route even though "
f"`MapsSelected` was {maps_selected!r}; tell the user to download local maps for the driven area so collection trains faster."
)
else:
failures.append(
"offline map tiles never loaded during this route and `MapsSelected` was empty; "
"tell the user to download local maps for the driven area so collection trains faster."
)
except Exception as exc:
failures.append(f"could not inspect qlogs for offline map usage: {exc}.")
if failures:
raise ValidationError(route, failures)
return {
"route_meta": route_meta,
"files_payload": files_payload,
"expected_segments": expected_segments,
"params": params,
"map_tiles": map_tiles,
}
def prepare_output_paths(route: RouteId, desktop_root: Path, overwrite: bool) -> tuple[Path, Path]:
desktop_root = desktop_root.expanduser().resolve()
desktop_root.mkdir(parents=True, exist_ok=True)
bundle_root = desktop_root / f"speed_limit_route_bundle_{route.safe_name}"
zip_path = desktop_root / f"{bundle_root.name}.zip"
if overwrite:
if bundle_root.exists():
shutil.rmtree(bundle_root)
if zip_path.exists():
zip_path.unlink()
else:
if bundle_root.exists():
raise FileExistsError(f"Bundle folder already exists: {bundle_root}")
if zip_path.exists():
raise FileExistsError(f"Bundle zip already exists: {zip_path}")
return bundle_root, zip_path
def write_bundle_manifests(
bundle_root: Path,
route: RouteId,
validation: dict[str, Any],
downloaded_files: list[DownloadedFile],
) -> None:
live_routes_meta = bundle_root / "live_routes_meta"
live_routes_meta.mkdir(parents=True, exist_ok=True)
files_txt_lines: list[str] = []
qlog_mtimes_lines: list[str] = []
for record in sorted(downloaded_files, key=lambda item: (item.segment, item.stream, item.filename)):
segment_name = f"{route.log_id}--{record.segment}"
files_txt_lines.append(f"{segment_name} {record.relative_path}")
if record.filename.startswith("qlog") and record.mtime_epoch is not None:
qlog_mtimes_lines.append(f"{record.relative_path} {record.mtime_epoch}")
(live_routes_meta / "files.txt").write_text("\n".join(files_txt_lines) + ("\n" if files_txt_lines else ""), encoding="utf-8")
(live_routes_meta / "qlog_mtimes.txt").write_text("\n".join(qlog_mtimes_lines) + ("\n" if qlog_mtimes_lines else ""), encoding="utf-8")
params = validation["params"]
route_meta = validation["route_meta"]
map_tiles: MapTileSummary = validation["map_tiles"]
manifest = {
"bundleCreatedAt": datetime.now(UTC).isoformat(),
"routeId": route.cli_name,
"routeFullname": route.canonical_name,
"expectedSegments": validation["expected_segments"],
"downloadedStreams": [spec.display_name for spec in REQUIRED_STREAMS],
"validation": {
"isPublic": bool(route_meta.get("is_public")),
"visionSpeedLimitAutoBookmark": params.get("VisionSpeedLimitAutoBookmark", ""),
"visionSpeedLimitDetection": params.get("VisionSpeedLimitDetection", ""),
"mapsSelected": params.get("MapsSelected", ""),
"lastMapsUpdate": params.get("LastMapsUpdate", ""),
"mapdMessagesScanned": map_tiles.mapd_messages,
"offlineTileLoaded": map_tiles.tile_loaded_any,
"firstTileSegment": map_tiles.first_tile_segment,
},
"routeMeta": {
"startTime": route_meta.get("start_time"),
"endTime": route_meta.get("end_time"),
"distance": route_meta.get("distance"),
"startLat": route_meta.get("start_lat"),
"startLng": route_meta.get("start_lng"),
"endLat": route_meta.get("end_lat"),
"endLng": route_meta.get("end_lng"),
"gitBranch": route_meta.get("git_branch"),
"gitCommit": route_meta.get("git_commit"),
"platform": route_meta.get("platform"),
"version": route_meta.get("version"),
"make": route_meta.get("make"),
},
"files": [
{
"segment": record.segment,
"stream": record.stream,
"filename": record.filename,
"relativePath": record.relative_path,
"mtimeEpoch": record.mtime_epoch,
}
for record in sorted(downloaded_files, key=lambda item: (item.segment, item.stream, item.filename))
],
}
(bundle_root / "bundle_manifest.json").write_text(json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8")
def zip_bundle(bundle_root: Path, zip_path: Path) -> None:
with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_STORED) as archive:
for path in sorted(bundle_root.rglob("*")):
if path.is_file():
archive.write(path, arcname=Path(bundle_root.name) / path.relative_to(bundle_root))
def download_bundle(
route: RouteId,
validation: dict[str, Any],
session: requests.Session,
desktop_root: Path,
timeout: float,
overwrite: bool,
) -> tuple[Path, Path]:
bundle_root, zip_path = prepare_output_paths(route, desktop_root, overwrite)
clip_root = bundle_root / "data" / "media" / "0" / "realdata"
expected_segments = validation["expected_segments"]
files_payload = validation["files_payload"]
downloaded_files: list[DownloadedFile] = []
for spec in REQUIRED_STREAMS:
urls = files_payload.get(spec.api_key, [])
for segment in range(expected_segments):
url = urls[segment]
if not isinstance(url, str) or not url:
raise RuntimeError(f"Unexpected missing {spec.display_name} URL for segment {segment} after validation.")
filename = filename_from_url(url)
segment_name = f"{route.log_id}--{segment}"
dest_path = clip_root / segment_name / filename
print(f"Downloading {segment_name}/{filename}")
mtime_epoch = fetch_stream_to_path(session, url, dest_path, timeout)
downloaded_files.append(
DownloadedFile(
segment=segment,
stream=spec.display_name,
filename=filename,
relative_path=relative_posix(dest_path, bundle_root),
mtime_epoch=mtime_epoch,
)
)
write_bundle_manifests(bundle_root, route, validation, downloaded_files)
print(f"Creating {zip_path.name}")
zip_bundle(bundle_root, zip_path)
return bundle_root, zip_path
def print_validation_summary(route: RouteId, validation: dict[str, Any]) -> None:
params = validation["params"]
map_tiles: MapTileSummary = validation["map_tiles"]
print(f"Validated {route.cli_name}")
print(f" public route: yes")
print(f" segments: {validation['expected_segments']}")
print(f" VisionSpeedLimitDetection: {params.get('VisionSpeedLimitDetection', '')}")
print(f" VisionSpeedLimitAutoBookmark: {params.get('VisionSpeedLimitAutoBookmark', '')}")
print(f" MapsSelected: {params.get('MapsSelected', '') or '(empty)'}")
print(f" offline tile loaded: {'yes' if map_tiles.tile_loaded_any else 'no'}")
def main() -> int:
args = parse_args()
route = parse_route_id(args.routeid)
session = requests.Session()
session.headers.update({"User-Agent": "starpilot-speed-limit-route-bundler/1.0"})
try:
validation = validate_route(route, session, args.timeout)
print_validation_summary(route, validation)
if args.validate_only:
return 0
bundle_root, zip_path = download_bundle(route, validation, session, args.desktop_root, args.timeout, args.overwrite)
print(f"Bundle folder: {bundle_root}")
print(f"Bundle zip: {zip_path}")
return 0
except ValidationError as exc:
print(str(exc), file=sys.stderr)
return 2
except FileExistsError as exc:
print(f"{exc}\nUse --overwrite to replace the existing Desktop bundle, or --validate-only to skip packaging.", file=sys.stderr)
return 3
except Exception as exc:
print(f"Error: {exc}", file=sys.stderr)
return 1
if __name__ == "__main__":
raise SystemExit(main())
+1 -1
View File
@@ -122,7 +122,7 @@ class CanBusBase:
assert fingerprint is not None
num = max([k for k, v in fingerprint.items() if len(v)], default=0) // 4 + 1
else:
num = len(CP.safetyConfigs)
num = max(1, len(CP.safetyConfigs))
self.offset = 4 * (num - 1)
+6
View File
@@ -205,6 +205,11 @@ struct CarState {
vehicleSensorsInvalid @52 :Bool; # invalid steering angle readings, etc.
lowSpeedAlert @56 :Bool; # lost steering control due to a dynamic min steering speed
blockPcmEnable @60 :Bool; # whether to allow PCM to enable this frame
pedalMaxRegen @61 :Bool; # pedal at max regen, driver should use brake for more decel
pedalLongActive @62 :Bool; # Pre-AP pedal longitudinal mode is active (enableLongControl)
teslaCCEngaged @63 :Bool; # rising edge of stock Tesla CC engaging (no-pedal mode)
teslaCCDisengaged @64 :Bool; # falling edge of stock Tesla CC
teslaCCNotArmed @65 :Bool; # lateral engaged but DI_cruiseState != STANDBY/ENABLED
# cruise state
cruiseState @10 :CruiseState;
@@ -635,6 +640,7 @@ struct CarParams {
fcaGiorgio @32;
rivian @33;
volkswagenMeb @34;
teslaPreap @35;
}
enum SteerControlType {
+21 -2
View File
@@ -11,6 +11,7 @@ from opendbc.car.structs import CarParams, CarParamsT
from opendbc.car.fingerprints import eliminate_incompatible_cars, all_legacy_fingerprint_cars
from opendbc.car.fw_versions import ObdCallback, get_fw_versions_ordered, get_present_ecus, match_fw_to_car
from opendbc.car.mock.values import CAR as MOCK
from opendbc.car.toyota.values import ToyotaSafetyFlags
from opendbc.car.values import BRANDS
from opendbc.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN
from openpilot.common.params import Params
@@ -108,6 +109,20 @@ def _normalize_gm_bolt_candidate(candidate: str | None, fingerprints: dict[int,
return candidate
def _apply_disable_openpilot_long(CP: CarParams, FPCP: StarPilotCarParams) -> None:
CP.openpilotLongitudinalControl = False
CP.pcmCruise = True
FPCP.openpilotLongitudinalControlDisabled = True
if CP.brand == "toyota":
# Toyota stock longitudinal safety changes the forwarding rules so the
# camera's ACC_CONTROL message can reach the PT bus again.
for cfg in CP.safetyConfigs:
cfg.safetyParam |= ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
for cfg in FPCP.safetyConfigs:
cfg.safetyParam |= ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
def _normalize_gm_volt_candidate(candidate: str | None, fingerprints: dict[int, dict]) -> str | None:
cam = fingerprints.get(GM_CAMERA_BUS, {})
has_live_camera_msg = GM_VOLT_CAMERA_MSG in cam
@@ -196,6 +211,11 @@ def fingerprint(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_mu
disable_fw_cache = os.environ.get('DISABLE_FW_CACHE', False)
ecu_rx_addrs = set()
if not fixed_fingerprint and Params().get_bool("NAPForcePreAP"):
fixed_fingerprint = "TESLA_MODEL_S_PREAP"
skip_fw_query = True
carlog.warning("NAPForcePreAP enabled; forcing TESLA_MODEL_S_PREAP fingerprint")
start_time = time.monotonic()
if not skip_fw_query:
if cached_params is not None and cached_params.brand != "mock" and len(cached_params.carFw) > 0 and \
@@ -318,8 +338,7 @@ def get_car(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multip
FPCP: StarPilotCarParams = CarInterface.get_starpilot_params(candidate, fingerprints, car_fw, CP, starpilot_toggles)
if not CP.alphaLongitudinalAvailable and starpilot_toggles.disable_openpilot_long:
CP.openpilotLongitudinalControl = False
FPCP.openpilotLongitudinalControlDisabled = True
_apply_disable_openpilot_long(CP, FPCP)
return interfaces[CP.carFingerprint](CP, FPCP)
@@ -97,6 +97,25 @@ class CAR(Platforms):
)
PACIFICA_HYBRID_AOL_CARS = frozenset({
CAR.CHRYSLER_PACIFICA_2019_HYBRID,
})
def pacifica_hybrid_aol_requires_set_press(car_fingerprint, pcm_cruise: bool) -> bool:
return car_fingerprint in PACIFICA_HYBRID_AOL_CARS and pcm_cruise
def pacifica_hybrid_aol_stock_acc_mode(car_fingerprint, pcm_cruise: bool,
controls_enabled: bool, always_on_lateral_enabled: bool) -> bool:
# Keep this narrow until we have logs proving other Chrysler platforms need the same exemption.
return (
pacifica_hybrid_aol_requires_set_press(car_fingerprint, pcm_cruise) and
always_on_lateral_enabled and
not controls_enabled
)
class CarControllerParams:
def __init__(self, CP):
self.STEER_STEP = 2 # 50 Hz
+18 -1
View File
@@ -5,6 +5,8 @@ from opendbc.car.isotp_parallel_query import IsoTpParallelQuery
EXT_DIAG_REQUEST = b'\x10\x03'
EXT_DIAG_RESPONSE = b'\x50\x03'
RESET_REQUEST = b'\x11\x01'
RESET_RESPONSE = b''
# File-based logging for debugging
ECU_LOG_FILE = "/data/ecu_disable.log"
@@ -21,7 +23,7 @@ def ecu_log(msg):
pass
def disable_ecu(can_recv, can_send, bus=0, addr=0x7d0, sub_addr=None, com_cont_req=b'\x28\x83\x01', timeout=0.1, retry=10):
def disable_ecu(can_recv, can_send, bus=0, addr=0x7d0, sub_addr=None, com_cont_req=b'\x28\x83\x01', timeout=0.1, retry=10, reset=False):
"""Silence an ECU by disabling sending and receiving messages using UDS 0x28.
The ECU will stay silent as long as openpilot keeps sending Tester Present.
@@ -29,6 +31,15 @@ def disable_ecu(can_recv, can_send, bus=0, addr=0x7d0, sub_addr=None, com_cont_r
WARNING: THIS DISABLES AEB!"""
ecu_log(f"=== ECU DISABLE START === addr={hex(addr)}, bus={bus}")
if reset:
try:
ecu_log("sending ECU reset before communication control...")
reset_query = IsoTpParallelQuery(can_send, can_recv, bus, [(addr, sub_addr)], [RESET_REQUEST], [RESET_RESPONSE])
reset_query.get_data(timeout=timeout)
time.sleep(0.2)
except Exception as e:
ecu_log(f"reset exception: {e}")
# Try multiple times with different approaches
for i in range(retry):
try:
@@ -50,6 +61,7 @@ def disable_ecu(can_recv, can_send, bus=0, addr=0x7d0, sub_addr=None, com_cont_r
# Log what we got back
cc_success = False
cc_rejected = False
cc_nrc = None
for (rx_addr, _), data in cc_response.items():
ecu_log(f"CC response: {data.hex() if data else 'empty'}")
# Check for positive response (0x68 = 0x28 + 0x40)
@@ -59,6 +71,7 @@ def disable_ecu(can_recv, can_send, bus=0, addr=0x7d0, sub_addr=None, com_cont_r
# Check for negative response
elif len(data) >= 3 and data[0] == 0x7F:
nrc = data[2]
cc_nrc = nrc
nrc_meanings = {
0x12: "subFunctionNotSupported",
0x13: "incorrectMessageLengthOrInvalidFormat",
@@ -73,6 +86,10 @@ def disable_ecu(can_recv, can_send, bus=0, addr=0x7d0, sub_addr=None, com_cont_r
if cc_success:
return True
elif cc_rejected:
if reset and cc_nrc == 0x22 and i < retry - 1:
ecu_log("CC rejected with NRC 0x22 after reset; retrying...")
time.sleep(0.2)
continue
# ECU explicitly rejected - don't retry, it won't work
ecu_log("=== ECU DISABLE REJECTED ===")
return False
+3 -1
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
import argparse
import os
from types import SimpleNamespace
from typing import get_args
from collections import defaultdict
@@ -32,7 +33,8 @@ def get_params_for_docs(platform) -> CarParams:
cp_platform = platform if platform in interfaces else MOCK.MOCK
CP: CarParams = interfaces[cp_platform].get_params(cp_platform, fingerprint=gen_empty_fingerprint(),
car_fw=[CarParams.CarFw(ecu=CarParams.Ecu.unknown)],
alpha_long=True, is_release=False, docs=True)
alpha_long=True, is_release=False, docs=True,
starpilot_toggles=SimpleNamespace())
return CP
@@ -131,6 +131,7 @@ class CarHarness(EnumBase):
hyundai_p = BaseCarHarness("Hyundai P connector")
hyundai_q = BaseCarHarness("Hyundai Q connector")
hyundai_r = BaseCarHarness("Hyundai R connector")
hyundai_s = BaseCarHarness("Hyundai S connector")
custom = BaseCarHarness("Developer connector")
obd_ii = BaseCarHarness("OBD-II connector", parts=[Cable.long_obdc_cable, Cable.usbc_coupler], has_connector=False)
gm = BaseCarHarness("GM connector", parts=[Accessory.harness_box])
+23
View File
@@ -72,6 +72,7 @@ MIGRATION = {
"HONDA CR-V 2016 TOURING": HONDA.HONDA_CRV,
"HONDA CR-V 2017 EX": HONDA.HONDA_CRV_5G,
"HONDA CR-V 2019 HYBRID": HONDA.HONDA_CRV_HYBRID,
"HONDA CLARITY 2018": HONDA.HONDA_CLARITY,
"HONDA FIT 2018 EX": HONDA.HONDA_FIT,
"HONDA HRV 2019 TOURING": HONDA.HONDA_HRV,
"HONDA INSIGHT 2019 TOURING": HONDA.HONDA_INSIGHT,
@@ -192,28 +193,43 @@ MIGRATION = {
"HYUNDAI IONIQ PLUG-IN HYBRID 2019": HYUNDAI.HYUNDAI_IONIQ_PHEV_2019,
"HYUNDAI IONIQ PHEV 2020": HYUNDAI.HYUNDAI_IONIQ_PHEV,
"HYUNDAI KONA 2020": HYUNDAI.HYUNDAI_KONA,
"HYUNDAI KONA 2ND GEN": HYUNDAI.HYUNDAI_KONA_2ND_GEN,
"HYUNDAI KONA ELECTRIC 2019": HYUNDAI.HYUNDAI_KONA_EV,
"HYUNDAI KONA ELECTRIC 2022": HYUNDAI.HYUNDAI_KONA_EV_2022,
"HYUNDAI KONA ELECTRIC 2ND GEN": HYUNDAI.HYUNDAI_KONA_EV_2ND_GEN,
"HYUNDAI KONA HYBRID 2020": HYUNDAI.HYUNDAI_KONA_HEV,
"HYUNDAI KONA HYBRID 2ND GEN": HYUNDAI.HYUNDAI_KONA_HEV_2ND_GEN,
"HYUNDAI SANTA FE 2019": HYUNDAI.HYUNDAI_SANTA_FE,
"HYUNDAI SANTA FE 2022": HYUNDAI.HYUNDAI_SANTA_FE_2022,
"HYUNDAI SANTA FE HYBRID 2022": HYUNDAI.HYUNDAI_SANTA_FE_HEV_2022,
"HYUNDAI SANTA FE HYBRID 5TH GEN": HYUNDAI.HYUNDAI_SANTA_FE_HEV_5TH_GEN,
"HYUNDAI SANTA FE PlUG-IN HYBRID 2022": HYUNDAI.HYUNDAI_SANTA_FE_PHEV_2022,
"HYUNDAI SONATA 2020": HYUNDAI.HYUNDAI_SONATA,
"HYUNDAI SONATA 2024": HYUNDAI.HYUNDAI_SONATA_2024,
"HYUNDAI SONATA 2019": HYUNDAI.HYUNDAI_SONATA_LF,
"HYUNDAI STARIA 4TH GEN": HYUNDAI.HYUNDAI_STARIA_4TH_GEN,
"HYUNDAI TUCSON 2019": HYUNDAI.HYUNDAI_TUCSON,
"HYUNDAI TUCSON 2025": HYUNDAI.HYUNDAI_TUCSON_2025,
"HYUNDAI TUCSON HYBRID 2025": HYUNDAI.HYUNDAI_TUCSON_HEV_2025,
"HYUNDAI TUCSON PLUG-IN HYBRID 2025": HYUNDAI.HYUNDAI_TUCSON_PHEV_2025,
"HYUNDAI PALISADE 2020": HYUNDAI.HYUNDAI_PALISADE,
"HYUNDAI PALISADE 2023": HYUNDAI.HYUNDAI_PALISADE_2023,
"HYUNDAI VELOSTER 2019": HYUNDAI.HYUNDAI_VELOSTER,
"HYUNDAI SONATA HYBRID 2021": HYUNDAI.HYUNDAI_SONATA_HYBRID,
"HYUNDAI SONATA HYBRID 2024": HYUNDAI.HYUNDAI_SONATA_HEV_2024,
"HYUNDAI IONIQ 5 2022": HYUNDAI.HYUNDAI_IONIQ_5,
"HYUNDAI IONIQ 5 2025": HYUNDAI.HYUNDAI_IONIQ_5_PE,
"HYUNDAI IONIQ 5 N 2024": HYUNDAI.HYUNDAI_IONIQ_5_N,
"HYUNDAI IONIQ 6 2023": HYUNDAI.HYUNDAI_IONIQ_6,
"HYUNDAI IONIQ 9 2025": HYUNDAI.HYUNDAI_IONIQ_9,
"HYUNDAI TUCSON 4TH GEN": HYUNDAI.HYUNDAI_TUCSON_4TH_GEN,
"HYUNDAI SANTA CRUZ 1ST GEN": HYUNDAI.HYUNDAI_SANTA_CRUZ_1ST_GEN,
"HYUNDAI SANTA CRUZ 2025": HYUNDAI.HYUNDAI_SANTA_CRUZ_2025,
"HYUNDAI CUSTIN 1ST GEN": HYUNDAI.HYUNDAI_CUSTIN_1ST_GEN,
"KIA FORTE E 2018 & GT 2021": HYUNDAI.KIA_FORTE,
"KIA K4 2025": HYUNDAI.KIA_K4_2025,
"KIA K5 2021": HYUNDAI.KIA_K5_2021,
"KIA K5 2025": HYUNDAI.KIA_K5_2025,
"KIA K5 HYBRID 2020": HYUNDAI.KIA_K5_HEV_2020,
"KIA K8 HYBRID 1ST GEN": HYUNDAI.KIA_K8_HEV_1ST_GEN,
"KIA NIRO EV 2020": HYUNDAI.KIA_NIRO_EV,
@@ -228,22 +244,29 @@ MIGRATION = {
"KIA OPTIMA HYBRID 4TH GEN FACELIFT": HYUNDAI.KIA_OPTIMA_H_G4_FL,
"KIA SELTOS 2021": HYUNDAI.KIA_SELTOS,
"KIA SPORTAGE 5TH GEN": HYUNDAI.KIA_SPORTAGE_5TH_GEN,
"KIA SPORTAGE 2026": HYUNDAI.KIA_SPORTAGE_2026,
"KIA SPORTAGE HYBRID 2026": HYUNDAI.KIA_SPORTAGE_HEV_2026,
"KIA SORENTO GT LINE 2018": HYUNDAI.KIA_SORENTO,
"KIA SORENTO 4TH GEN": HYUNDAI.KIA_SORENTO_4TH_GEN,
"KIA SORENTO 2024": HYUNDAI.KIA_SORENTO_2024,
"KIA SORENTO HYBRID 4TH GEN": HYUNDAI.KIA_SORENTO_HEV_4TH_GEN,
"KIA SORENTO HYBRID 4TH GEN LFA2": HYUNDAI.KIA_SORENTO_HEV_4TH_GEN_LFA2,
"KIA STINGER GT2 2018": HYUNDAI.KIA_STINGER,
"KIA STINGER 2022": HYUNDAI.KIA_STINGER_2022,
"KIA CEED INTRO ED 2019": HYUNDAI.KIA_CEED,
"KIA EV6 2022": HYUNDAI.KIA_EV6,
"KIA EV6 2025": HYUNDAI.KIA_EV6_2025,
"KIA EV9 2025": HYUNDAI.KIA_EV9,
"KIA CARNIVAL 4TH GEN": HYUNDAI.KIA_CARNIVAL_4TH_GEN,
"GENESIS GV60 ELECTRIC 1ST GEN": HYUNDAI.GENESIS_GV60_EV_1ST_GEN,
"GENESIS G70 2018": HYUNDAI.GENESIS_G70,
"GENESIS G70 2020": HYUNDAI.GENESIS_G70_2020,
"GENESIS GV70 1ST GEN": HYUNDAI.GENESIS_GV70_1ST_GEN,
"GENESIS GV70 ELECTRIFIED 2026": HYUNDAI.GENESIS_GV70_ELECTRIFIED_2ND_GEN,
"GENESIS G80 2017": HYUNDAI.GENESIS_G80,
"GENESIS G90 2017": HYUNDAI.GENESIS_G90,
"GENESIS GV80 2023": HYUNDAI.GENESIS_GV80,
"GENESIS GV80 2025": HYUNDAI.GENESIS_GV80_2025,
"MAZDA CX-5": MAZDA.MAZDA_CX5,
"MAZDA CX-9": MAZDA.MAZDA_CX9,
"MAZDA 3": MAZDA.MAZDA_3,
@@ -0,0 +1,8 @@
from opendbc.car.gm.values import AccState, CAR
def get_stock_cc_active_for_cancel(CP, CS):
stock_cc_active = CS.out.cruiseState.enabled or CS.pcm_acc_status != AccState.OFF
if CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL:
return CS.out.cruiseState.enabled
return stock_cc_active
+176 -14
View File
@@ -7,7 +7,7 @@ from opendbc.car.gm import gmcan
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.gm.values import (
ASCM_INT, CAR, CC_ONLY_CAR, CC_REGEN_PADDLE_CAR, DBC, EV_CAR, SDGM_CAR, AccState, CanBus, CarControllerParams,
CruiseButtons, GMFlags,
CruiseButtons, GMFlags, GMSafetyFlags,
)
from opendbc.car.interfaces import CarControllerBase
from openpilot.common.params import Params, UnknownKeyName
@@ -16,11 +16,26 @@ from openpilot.starpilot.common.testing_grounds import testing_ground
VisualAlert = structs.CarControl.HUDControl.VisualAlert
NetworkLocation = structs.CarParams.NetworkLocation
LongCtrlState = structs.CarControl.Actuators.LongControlState
GearShifter = structs.CarState.GearShifter
# Camera cancels up to 0.1s after brake is pressed, ECM allows 0.5s
CAMERA_CANCEL_DELAY_FRAMES = 10
# Enforce a minimum interval between steering messages to avoid a fault
MIN_STEER_MSG_INTERVAL_MS = 15
AUTO_HOLD_VOLT_CARS = {
CAR.CHEVROLET_VOLT,
CAR.CHEVROLET_VOLT_2019,
CAR.CHEVROLET_VOLT_ASCM,
CAR.CHEVROLET_VOLT_CAMERA,
}
AUTO_HOLD_DRIVE_GEARS = {
GearShifter.drive,
GearShifter.low,
GearShifter.manumatic,
}
AUTO_HOLD_MIN_BRAKE = 80
AUTO_HOLD_MAX_BRAKE = 240
AUTO_HOLD_MIN_DRIVE_TIME_S = 3.0
def get_stock_cc_active_for_cancel(CP, CS):
@@ -62,6 +77,44 @@ def should_send_acc_dashboard_status(CP, dash_speed_spoof_active):
return status_car and (dash_speed_spoof_active or volt_camera_no_camera)
def get_acc_dashboard_fcw_alert(hud_alert, CS):
if hud_alert == VisualAlert.fcw:
return 0x3
stock_fcw_alert = int(getattr(CS, "stock_fcw_alert", 0)) & 0x3
if stock_fcw_alert != 0:
return stock_fcw_alert
cs_out = getattr(CS, "out", None)
if cs_out is not None and (getattr(cs_out, "stockAeb", False) or getattr(cs_out, "stockFcw", False)):
return 0x3
return 0
def get_acc_dashboard_status_values(enabled, target_speed_kph, hud_control, CS):
if enabled:
return {
"ACCCruiseState": 0,
"ACCLeadCar": int(hud_control.leadVisible) & 0x1,
"ACCResumeButton": 0,
"ACCSpeedSetpoint": target_speed_kph,
"ACCGapLevel": int(hud_control.leadDistanceBars) & 0x3,
"ACCCmdActive": 1,
}
# Replay the stock camera dashboard context when openpilot long is enabled
# but openpilot itself is not actively driving the ACC cluster state.
return {
"ACCCruiseState": int(getattr(CS, "stock_acc_cruise_state", 0)) & 0x7,
"ACCLeadCar": int(getattr(CS, "stock_acc_lead_car", 0)) & 0x1,
"ACCResumeButton": int(getattr(CS, "stock_acc_resume_button", 0)) & 0x1,
"ACCSpeedSetpoint": float(getattr(CS, "stock_acc_speed_setpoint_kph", 0.0)),
"ACCGapLevel": int(getattr(CS, "stock_acc_gap_level", 0)) & 0x3,
"ACCCmdActive": int(getattr(CS, "stock_acc_cmd_active", 0)) & 0x1,
}
ECM_CRUISE_SPOOF_CARS = {
CAR.CHEVROLET_BOLT_CC_2017,
CAR.CHEVROLET_BOLT_CC_2018_2021,
@@ -101,6 +154,53 @@ def get_testing_ground_1_brake_switch_bias(v_ego: float) -> int:
return int(round(np.interp(v_ego, [0.0, 6.0, 15.0, 30.0], [40.0, 85.0, 130.0, 170.0])))
def supports_volt_auto_hold(CP, auto_hold_enabled: bool):
safety_cfg = getattr(CP, "safetyConfigs", ())
safety_param = safety_cfg[0].safetyParam if safety_cfg else 0
stock_hold_safety_ready = CP.openpilotLongitudinalControl or bool(safety_param & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value)
return (
auto_hold_enabled and
stock_hold_safety_ready and
CP.carFingerprint in AUTO_HOLD_VOLT_CARS
)
def estimate_auto_hold_brake(driver_brake: float, op_brake: float) -> int:
driver_hold = np.interp(float(driver_brake), [8.0, 20.0, 40.0, 80.0], [80.0, 110.0, 150.0, 220.0])
hold_brake = max(float(op_brake), float(driver_hold))
return int(round(np.clip(hold_brake, AUTO_HOLD_MIN_BRAKE, AUTO_HOLD_MAX_BRAKE)))
def should_activate_auto_hold(hold_ready: bool, auto_hold_armed: bool, auto_hold_engaged: bool,
brake_pressed: bool, standstill: bool, long_active: bool,
regen_braking: bool, v_ego: float) -> bool:
stopped = standstill or v_ego < 0.02
return (
hold_ready and
(auto_hold_armed or auto_hold_engaged or brake_pressed) and
stopped and
not long_active and
not regen_braking
)
def get_friction_brake_bus(CP):
volt_gateway_alt_brake = (
CP.carFingerprint == CAR.CHEVROLET_VOLT and
CP.networkLocation == NetworkLocation.gateway and
bool(CP.flags & GMFlags.NO_ACCELERATOR_POS_MSG.value)
)
if volt_gateway_alt_brake:
return CanBus.POWERTRAIN
if CP.networkLocation == NetworkLocation.fwdCamera:
if CP.carFingerprint in SDGM_CAR:
return CanBus.CAMERA
return CanBus.POWERTRAIN
return CanBus.CHASSIS
class CarController(CarControllerBase):
def __init__(self, dbc_names, CP):
super().__init__(dbc_names, CP)
@@ -153,6 +253,11 @@ class CarController(CarControllerBase):
self.malibu_cancel_phase = 0
self.malibu_button_phase = 0
self.malibu_last_button_ts_nanos = 0
self.auto_hold_brake = 0
try:
self.gm_auto_hold_enabled = self.params_.get_bool("GMAutoHold")
except UnknownKeyName:
self.gm_auto_hold_enabled = False
def calc_pedal_command(self, accel: float, long_active: bool, v_ego: float):
if not long_active:
@@ -275,6 +380,9 @@ class CarController(CarControllerBase):
else:
urgency = float(np.clip(abs(accel) / 2.0, 0.0, 1.0))
rate_up = np.interp(v_ego, [0.0, 3.0, 8.0, 20.0], [0.007, 0.012, 0.022, 0.036]) + 0.011 * urgency
if accel > 0.0 and v_ego > 6.0:
comfort_factor = np.interp(abs(accel), [0.0, 0.12, 0.25, 0.45, 0.8], [0.55, 0.58, 0.68, 0.82, 1.0])
rate_up *= comfort_factor
if accel > 1.2:
rate_up += np.interp(v_ego, [0.0, 4.0, 12.0, 25.0], [0.006, 0.005, 0.003, 0.002])
rate_down = np.interp(v_ego, [0.0, 3.0, 8.0, 20.0], [0.008, 0.014, 0.026, 0.045]) + 0.015 * urgency
@@ -302,8 +410,32 @@ class CarController(CarControllerBase):
self.aego = CS.out.aEgo
accel = actuators.accel
press_regen_paddle = False
auto_hold_enabled = supports_volt_auto_hold(self.CP, self.gm_auto_hold_enabled)
stock_hold_apply_brake = self.apply_brake if self.CP.openpilotLongitudinalControl else 0
hold_ready = (
auto_hold_enabled and
CS.out.cruiseState.available and
CS.out.gearShifter in AUTO_HOLD_DRIVE_GEARS and
CS.auto_hold_drive_time >= AUTO_HOLD_MIN_DRIVE_TIME_S
)
if not hold_ready or CS.out.gasPressed:
CS.auto_hold_armed = False
elif CS.regen_release_timer > 0.0:
CS.auto_hold_armed = False
elif not CS.auto_hold_armed and (CS.out.vEgo > 0.03 or ((CS.out.standstill or CS.out.vEgo < 0.02) and CS.out.brakePressed)):
CS.auto_hold_armed = True
if CS.out.vEgo > 0.1 or CS.out.gasPressed or CS.out.gearShifter not in AUTO_HOLD_DRIVE_GEARS:
self.auto_hold_brake = 0
elif CS.out.brakePressed or stock_hold_apply_brake > 0:
self.auto_hold_brake = estimate_auto_hold_brake(CS.out.brake, stock_hold_apply_brake)
if self.frame % 25 == 0:
try:
self.gm_auto_hold_enabled = self.params_.get_bool("GMAutoHold")
except UnknownKeyName:
self.gm_auto_hold_enabled = False
try:
mode = self.params_.get("LongitudinalManeuverPaddleMode")
except UnknownKeyName:
@@ -383,6 +515,16 @@ class CarController(CarControllerBase):
paddle_sched_feed_active = False
paddle_spoof_pressed = raw_regen_active and (CS.out.vEgo > 2.68)
auto_hold_active = should_activate_auto_hold(
hold_ready,
CS.auto_hold_armed,
CS.auto_hold_engaged,
CS.out.brakePressed,
CS.out.standstill,
CC.longActive,
CS.out.regenBraking,
CS.out.vEgo,
)
# Steering (Active: 50Hz, inactive: 10Hz)
steer_step = self.params.STEER_STEP if CC.latActive else self.params.INACTIVE_STEER_STEP
@@ -551,14 +693,11 @@ class CarController(CarControllerBase):
if self.CP.enableGasInterceptorDEPRECATED:
can_sends.append(create_gas_interceptor_command(self.packer_pt, interceptor_gas_cmd, idx))
if self.CP.carFingerprint not in CC_ONLY_CAR:
friction_brake_bus = CanBus.CHASSIS
friction_brake_bus = get_friction_brake_bus(self.CP)
# GM Camera exceptions
# TODO: can we always check the longControlState?
if self.CP.networkLocation == NetworkLocation.fwdCamera:
at_full_stop = at_full_stop and stopping
friction_brake_bus = CanBus.POWERTRAIN
if self.CP.carFingerprint in SDGM_CAR:
friction_brake_bus = CanBus.CAMERA
if self.CP.autoResumeSng:
resume = actuators.longControlState != LongCtrlState.starting or CC.cruiseControl.resume
@@ -569,17 +708,28 @@ class CarController(CarControllerBase):
else:
acc_engaged = CC.enabled
# GasRegenCmdActive needs to be 1 to avoid cruise faults. It describes the ACC state, not actuation
can_sends.append(gmcan.create_gas_regen_command(
self.packer_pt, CanBus.POWERTRAIN, self.apply_gas, idx, acc_engaged, at_full_stop,
include_always_one3=self.CP.carFingerprint in kaofui_cars, use_volt_layout=self.is_volt))
can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, friction_brake_bus, self.apply_brake,
idx, CC.enabled, near_stop, at_full_stop, self.CP))
if auto_hold_active:
hold_brake = self.auto_hold_brake or estimate_auto_hold_brake(CS.out.brake, self.apply_brake)
hold_standstill = CS.pcm_acc_status == AccState.STANDSTILL
hold_near_stop = CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE
can_sends.append(gmcan.create_friction_brake_command(
self.packer_ch, friction_brake_bus, hold_brake, idx, False, hold_near_stop, hold_standstill, self.CP))
CS.auto_hold_engaged = True
CS.auto_hold_fault_suppression_timer = 1.0
else:
# GasRegenCmdActive needs to be 1 to avoid cruise faults. It describes the ACC state, not actuation
can_sends.append(gmcan.create_gas_regen_command(
self.packer_pt, CanBus.POWERTRAIN, self.apply_gas, idx, acc_engaged, at_full_stop,
include_always_one3=self.CP.carFingerprint in kaofui_cars, use_volt_layout=self.is_volt))
can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, friction_brake_bus, self.apply_brake,
idx, CC.enabled, near_stop, at_full_stop, self.CP))
CS.auto_hold_engaged = False
if should_send_acc_dashboard_status(self.CP, dash_speed_spoof_active):
send_fcw = hud_alert == VisualAlert.fcw
can_sends.append(gmcan.create_acc_dashboard_command(self.packer_pt, CanBus.POWERTRAIN, CC.enabled,
hud_v_cruise * CV.MS_TO_KPH, hud_control, send_fcw))
acc_dashboard_status = get_acc_dashboard_status_values(CC.enabled, hud_v_cruise * CV.MS_TO_KPH, hud_control, CS)
fcw_alert = get_acc_dashboard_fcw_alert(hud_alert, CS)
can_sends.append(gmcan.create_acc_dashboard_command(self.packer_pt, CanBus.POWERTRAIN,
acc_dashboard_status, fcw_alert))
# Radar needs to know current speed and yaw rate (50hz),
# and that ADAS is alive (10hz)
@@ -633,6 +783,18 @@ class CarController(CarControllerBase):
can_sends.append(gmcan.create_buttons(self.packer_pt, cancel_bus, (CS.buttons_counter + 1) % 4, CruiseButtons.CANCEL))
else:
if self.frame % 4 == 0 and auto_hold_active:
idx = (self.frame // 4) % 4
hold_brake = self.auto_hold_brake or estimate_auto_hold_brake(CS.out.brake, stock_hold_apply_brake)
hold_standstill = CS.pcm_acc_status == AccState.STANDSTILL
hold_near_stop = CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE
can_sends.append(gmcan.create_friction_brake_command(
self.packer_ch, get_friction_brake_bus(self.CP), hold_brake, idx, False, hold_near_stop, hold_standstill, self.CP))
CS.auto_hold_engaged = True
CS.auto_hold_fault_suppression_timer = 1.0
elif self.frame % 4 == 0:
CS.auto_hold_engaged = False
# While car is braking, cancel button causes ECM to enter a soft disable state with a fault status.
# A delayed cancellation allows camera to cancel and avoids a fault when user depresses brake quickly
self.cancel_counter = self.cancel_counter + 1 if CC.cruiseControl.cancel else 0
+80 -29
View File
@@ -2,6 +2,7 @@ import copy
from cereal import custom
from opendbc.can import CANDefine, CANParser
from opendbc.car import Bus, create_button_events, structs
from opendbc.car import DT_CTRL
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.interfaces import CarStateBase
from opendbc.car.gm.values import (
@@ -26,6 +27,9 @@ TransmissionType = structs.CarParams.TransmissionType
NetworkLocation = structs.CarParams.NetworkLocation
STANDSTILL_THRESHOLD = 10 * 0.0311
VOLT_EBCM_BRAKE_PRESSED_THRESHOLD = 6 / 0xd0
AUTO_HOLD_MIN_DRIVE_TIME_S = 3.0
AUTO_HOLD_REGEN_RELEASE_COOLDOWN_S = 1.0
BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise,
CruiseButtons.MAIN: ButtonType.mainCruise, CruiseButtons.CANCEL: ButtonType.cancel}
@@ -35,6 +39,10 @@ BOLT_GEN1_CANCEL_PERSONALITY_CARS = {
CAR.CHEVROLET_BOLT_CC_2017,
CAR.CHEVROLET_BOLT_CC_2018_2021,
}
BOLT_CANCEL_BUTTON_CARS = BOLT_GEN1_CANCEL_PERSONALITY_CARS | {
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
CAR.CHEVROLET_BOLT_CC_2022_2023,
}
class CarState(CarStateBase):
@@ -58,6 +66,12 @@ class CarState(CarStateBase):
self.distance_button = 0
self.single_pedal_mode = False
self.auto_hold_armed = False
self.auto_hold_engaged = False
self.auto_hold_drive_time = 0.0
self.auto_hold_fault_suppression_timer = 0.0
self.regen_release_timer = 0.0
self.user_regen_paddle_pressed = False
self.pedal_steady = 0
self.ecm_cruise_control_ts_nanos = 0
@@ -66,6 +80,13 @@ class CarState(CarStateBase):
self.lkas_previously_enabled = 0
self.lkas_enabled = 0
self.pcm_acc_status = AccState.OFF
self.stock_fcw_alert = 0
self.stock_acc_cruise_state = 0
self.stock_acc_lead_car = 0
self.stock_acc_resume_button = 0
self.stock_acc_speed_setpoint_kph = 0.0
self.stock_acc_gap_level = 0
self.stock_acc_cmd_active = 0
def update_button_enable(self, buttonEvents: list[structs.CarState.ButtonEvent]):
if not self.CP.pcmCruise:
@@ -153,7 +174,9 @@ class CarState(CarStateBase):
else:
ret.brake = pt_cp.vl["ECMAcceleratorPos"]["BrakePedalPos"]
if self.CP.carFingerprint in {CAR.CHEVROLET_MALIBU_CC} or (self.CP.carFingerprint == CAR.CHEVROLET_BLAZER and not no_accel_pos):
if self.CP.carFingerprint == CAR.CHEVROLET_VOLT and no_accel_pos:
ret.brakePressed = ret.brake >= VOLT_EBCM_BRAKE_PRESSED_THRESHOLD
elif self.CP.carFingerprint in {CAR.CHEVROLET_MALIBU_CC} or (self.CP.carFingerprint == CAR.CHEVROLET_BLAZER and not no_accel_pos):
ret.brakePressed = ret.brake >= 8
elif (self.CP.flags & GMFlags.FORCE_BRAKE_C9.value) or ((self.CP.networkLocation == NetworkLocation.fwdCamera) and (self.CP.carFingerprint != CAR.CHEVROLET_BLAZER)):
ret.brakePressed = pt_cp.vl["ECMEngineStatus"]["BrakePressed"] != 0
@@ -165,9 +188,22 @@ class CarState(CarStateBase):
analog_thresh = 0.10 if no_accel_pos else 8
ret.brakePressed = ret.brake >= analog_thresh
in_drive_for_hold = ret.gearShifter in (GearShifter.drive, GearShifter.low, GearShifter.manumatic)
if in_drive_for_hold:
if ret.brakePressed:
self.auto_hold_drive_time = AUTO_HOLD_MIN_DRIVE_TIME_S
else:
self.auto_hold_drive_time = min(self.auto_hold_drive_time + DT_CTRL, AUTO_HOLD_MIN_DRIVE_TIME_S)
else:
self.auto_hold_drive_time = 0.0
self.auto_hold_armed = False
self.auto_hold_engaged = False
# Regen braking is braking
if self.CP.transmissionType == TransmissionType.direct:
ret.regenBraking = pt_cp.vl["EBCMRegenPaddle"]["RegenPaddle"] != 0
if not ret.regenBraking and self.user_regen_paddle_pressed:
self.regen_release_timer = AUTO_HOLD_REGEN_RELEASE_COOLDOWN_S
self.single_pedal_mode = (ret.gearShifter == GearShifter.low or
pt_cp.vl["EVDriveMode"]["SinglePedalModeActive"] == 1 or
(ret.regenBraking and ret.gearShifter == GearShifter.manumatic) or
@@ -176,6 +212,10 @@ class CarState(CarStateBase):
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
CAR.CHEVROLET_BOLT_CC_2022_2023,
} and self.CP.enableGasInterceptorDEPRECATED))
self.user_regen_paddle_pressed = ret.regenBraking
if self.regen_release_timer > 0.0:
self.regen_release_timer = max(self.regen_release_timer - DT_CTRL, 0.0)
if self.CP.enableGasInterceptorDEPRECATED:
gas = (pt_cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS"] + pt_cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS2"]) / 2.
@@ -232,11 +272,26 @@ class CarState(CarStateBase):
ret.cruiseState.enabled = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] != AccState.OFF
ret.cruiseState.standstill = pt_cp.vl["AcceleratorPedal2"]["CruiseState"] == AccState.STANDSTILL
self.stock_fcw_alert = 0
ret.stockFcw = False
if self.CP.networkLocation == NetworkLocation.fwdCamera and not self.CP.flags & GMFlags.NO_CAMERA.value:
if self.CP.carFingerprint not in CC_ONLY_CAR:
ret.cruiseState.speed = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCSpeedSetpoint"] * CV.KPH_TO_MS
has_acc_dashboard_status = self.CP.carFingerprint not in CC_ONLY_CAR or self.CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL
if has_acc_dashboard_status:
acc_dashboard_status = cam_cp.vl["ASCMActiveCruiseControlStatus"]
if self.CP.carFingerprint not in CC_ONLY_CAR:
ret.cruiseState.speed = acc_dashboard_status["ACCSpeedSetpoint"] * CV.KPH_TO_MS
self.stock_acc_cruise_state = int(acc_dashboard_status["ACCCruiseState"])
self.stock_acc_lead_car = int(acc_dashboard_status["ACCLeadCar"])
self.stock_acc_resume_button = int(acc_dashboard_status["ACCResumeButton"])
self.stock_acc_speed_setpoint_kph = float(acc_dashboard_status["ACCSpeedSetpoint"])
self.stock_acc_gap_level = int(acc_dashboard_status["ACCGapLevel"])
self.stock_acc_cmd_active = int(acc_dashboard_status["ACCCmdActive"])
# Preserve the stock camera FCW level from 0x370 so the controller can
# replay it when that message is blocked and spoofed by openpilot long.
self.stock_fcw_alert = int(acc_dashboard_status["FCWAlert"])
ret.stockFcw = self.stock_fcw_alert != 0
if self.CP.carFingerprint not in (SDGM_CAR | ASCM_INT):
if self.CP.carFingerprint not in SDGM_CAR:
ret.stockAeb = cam_cp.vl["AEBCmd"]["AEBCmdActive"] != 0
else:
ret.stockAeb = False
@@ -264,6 +319,10 @@ class CarState(CarStateBase):
self.ecm_cruise_control_ts_nanos = 0
self.accelerator_pedal2_ts_nanos = 0
if self.auto_hold_fault_suppression_timer > 0.0:
self.auto_hold_fault_suppression_timer = max(self.auto_hold_fault_suppression_timer - DT_CTRL, 0.0)
ret.accFaulted = False
if self.CP.enableBsm and not sdgm_non_volt:
ret.leftBlindspot = pt_cp.vl["BCMBlindSpotMonitor"]["LeftBSM"] == 1
ret.rightBlindspot = pt_cp.vl["BCMBlindSpotMonitor"]["RightBSM"] == 1
@@ -278,46 +337,34 @@ class CarState(CarStateBase):
self.lkas_enabled = pt_cp.vl["ASCMSteeringButton"]["LKAButton"]
self.pcm_acc_status = pt_cp.vl["AcceleratorPedal2"]["CruiseState"]
# Only activate cancel remap when panda safety was configured for it at startup.
remap_cancel_to_distance = bool(self.CP.alternativeExperience & ALTERNATIVE_EXPERIENCE.GM_REMAP_CANCEL_TO_DISTANCE)
if not remap_cancel_to_distance:
remap_cancel_to_distance = (
getattr(starpilot_toggles, "remap_cancel_to_distance", False) and
self.CP.openpilotLongitudinalControl and
bool(self.CP.flags & GMFlags.PEDAL_LONG.value) and
self.CP.carFingerprint in (BOLT_GEN1_CANCEL_PERSONALITY_CARS | {CAR.CHEVROLET_MALIBU_HYBRID_CC})
)
malibu_cancel_passthrough = (
remap_cancel_to_distance and
self.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC and
self.CP.openpilotLongitudinalControl and
bool(self.CP.flags & GMFlags.PEDAL_LONG.value)
)
bolt_cancel_personality = (
bolt_cancel_button = (
remap_cancel_to_distance and
self.CP.carFingerprint in BOLT_GEN1_CANCEL_PERSONALITY_CARS and
self.CP.carFingerprint in BOLT_CANCEL_BUTTON_CARS and
self.CP.openpilotLongitudinalControl and
bool(self.CP.flags & GMFlags.PEDAL_LONG.value)
)
bolt_cancel_lkas_conflict = bolt_cancel_button and self.CP.carFingerprint in BOLT_GEN1_CANCEL_PERSONALITY_CARS
cruise_button_map = BUTTONS_DICT
if malibu_cancel_passthrough or bolt_cancel_personality:
if malibu_cancel_passthrough or bolt_cancel_button:
cruise_button_map = {k: v for k, v in BUTTONS_DICT.items() if k != CruiseButtons.CANCEL}
cruise_events = create_button_events(
self.cruise_buttons, prev_cruise_buttons, cruise_button_map, unpressed_btn=CruiseButtons.UNPRESS
)
cancel_gap_events = []
if bolt_cancel_personality and self.cruise_buttons != prev_cruise_buttons:
if prev_cruise_buttons == CruiseButtons.CANCEL:
cancel_gap_events.append(structs.CarState.ButtonEvent(pressed=False, type=ButtonType.gapAdjustCruise))
if self.cruise_buttons == CruiseButtons.CANCEL:
cancel_gap_events.append(structs.CarState.ButtonEvent(pressed=True, type=ButtonType.gapAdjustCruise))
suppress_malibu_side_buttons = malibu_cancel_passthrough and (
self.cruise_buttons in (CruiseButtons.CANCEL, CruiseButtons.MAIN) or
prev_cruise_buttons in (CruiseButtons.CANCEL, CruiseButtons.MAIN)
)
suppress_bolt_cancel_lkas = bolt_cancel_personality and (
suppress_bolt_cancel_lkas = bolt_cancel_lkas_conflict and (
self.cruise_buttons == CruiseButtons.CANCEL or
prev_cruise_buttons == CruiseButtons.CANCEL
)
@@ -332,7 +379,6 @@ class CarState(CarStateBase):
if self.cruise_buttons != CruiseButtons.UNPRESS or prev_cruise_buttons != CruiseButtons.INIT:
ret.buttonEvents = [
*cruise_events,
*cancel_gap_events,
*distance_events,
*lkas_events,
]
@@ -341,9 +387,8 @@ class CarState(CarStateBase):
ret.lowSpeedAlert = True
fp_ret = custom.StarPilotCarState.new_message()
if bolt_cancel_personality and self.cruise_buttons == CruiseButtons.CANCEL:
# Feed long-press personality logic as if distance is held while CANCEL is held.
fp_ret.distancePressed = True
if bolt_cancel_button and self.cruise_buttons == CruiseButtons.CANCEL:
fp_ret.cancelPressed = True
fp_ret.sportGear = pt_cp.vl["SportMode"]["SportMode"] == 1
return ret, fp_ret
@@ -393,7 +438,7 @@ class CarState(CarStateBase):
("ASCMSteeringButton", 33),
]
if CP.enableBsm:
pt_messages.append(("BCMBlindSpotMonitor", 10))
pt_messages.append(("BCMBlindSpotMonitor", 0))
if CP.flags & GMFlags.NO_ACCELERATOR_POS_MSG.value:
if ("ECMAcceleratorPos", 80) in pt_messages:
@@ -435,8 +480,14 @@ class CarState(CarStateBase):
("ASCMSteeringButton", 33),
]
if CP.enableBsm:
cam_messages.append(("BCMBlindSpotMonitor", 10))
elif CP.carFingerprint not in (SDGM_CAR | ASCM_INT):
cam_messages.append(("BCMBlindSpotMonitor", 0))
elif CP.carFingerprint in ASCM_INT:
# Volt/ASCM-int variants don't reliably have AEBCmd present at startup,
# but when it appears we still want to surface OEM AEB state.
cam_messages += [
("AEBCmd", 0),
]
elif CP.carFingerprint not in SDGM_CAR:
cam_messages += [
("AEBCmd", 10),
]
@@ -213,9 +213,12 @@ FINGERPRINTS.update({
CAR.CHEVROLET_SUBURBAN: FINGERPRINTS[CAR.CHEVROLET_SUBURBAN_CC],
CAR.GMC_YUKON_CC: FINGERPRINTS[CAR.GMC_YUKON],
CAR.CADILLAC_XT6: FINGERPRINTS[CAR.CHEVROLET_TRAVERSE],
CAR.CADILLAC_XT5: FINGERPRINTS[CAR.CHEVROLET_TRAVERSE],
CAR.CHEVROLET_BLAZER: FINGERPRINTS[CAR.CHEVROLET_TRAVERSE],
CAR.CHEVROLET_MALIBU_SDGM: FINGERPRINTS[CAR.CHEVROLET_MALIBU_CC],
CAR.BUICK_BABYENCLAVE: FINGERPRINTS[CAR.CHEVROLET_TRAVERSE],
CAR.CHEVROLET_SILVERADO_CC: FINGERPRINTS[CAR.CHEVROLET_SILVERADO],
CAR.BUICK_LACROSSE_ASCM: FINGERPRINTS[CAR.BUICK_LACROSSE],
})
FW_VERSIONS: dict[str, dict[tuple, list[bytes]]] = {
+8 -7
View File
@@ -209,18 +209,19 @@ def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_s
return packer.make_can_msg("EBCMFrictionBrakeCmd", bus, values)
def create_acc_dashboard_command(packer, bus, enabled, target_speed_kph, hud_control, fcw):
target_speed = min(target_speed_kph, 255)
def create_acc_dashboard_command(packer, bus, status_values, fcw_alert):
target_speed = min(max(float(status_values.get("ACCSpeedSetpoint", 0.0)), 0.0), 255.0)
values = {
"ACCAlwaysOne": 1,
"ACCResumeButton": 0,
"ACCCruiseState": int(status_values.get("ACCCruiseState", 0)) & 0x7,
"ACCResumeButton": int(status_values.get("ACCResumeButton", 0)) & 0x1,
"ACCSpeedSetpoint": target_speed,
"ACCGapLevel": hud_control.leadDistanceBars * enabled, # 3 "far", 0 "inactive"
"ACCCmdActive": enabled,
"ACCGapLevel": int(status_values.get("ACCGapLevel", 0)) & 0x3,
"ACCCmdActive": int(status_values.get("ACCCmdActive", 0)) & 0x1,
"ACCAlwaysOne2": 1,
"ACCLeadCar": hud_control.leadVisible,
"FCWAlert": 0x3 if fcw else 0
"ACCLeadCar": int(status_values.get("ACCLeadCar", 0)) & 0x1,
"FCWAlert": int(fcw_alert) & 0x3,
}
return packer.make_can_msg("ASCMActiveCruiseControlStatus", bus, values)
+50 -7
View File
@@ -58,6 +58,14 @@ NON_LINEAR_TORQUE_PARAMS = {
"left": [3.8, 0.81, 0.24, 0.0465122],
"right": [3.8, 0.81, 0.24, 0.0465122],
},
CAR.CHEVROLET_SILVERADO_CC: {
"left": [3.8, 0.81, 0.24, 0.0465122],
"right": [3.8, 0.81, 0.24, 0.0465122],
},
CAR.CHEVROLET_VOLT: {
"left": [1.5, 1.0, 0.155, 0.0],
"right": [1.5, 1.0, 0.155, 0.0],
},
}
PEDAL_MSG = 0x201
@@ -85,6 +93,14 @@ VOLT_LONG_TEST_TUNE_CARS = {
CAR.CHEVROLET_VOLT_CC,
}
VOLT_BSM_CARS = {
CAR.CHEVROLET_VOLT,
CAR.CHEVROLET_VOLT_2019,
CAR.CHEVROLET_VOLT_ASCM,
CAR.CHEVROLET_VOLT_CAMERA,
CAR.CHEVROLET_VOLT_CC,
}
BOLT_PEDAL_LONG_CARS = {
CAR.CHEVROLET_BOLT_CC_2017,
CAR.CHEVROLET_BOLT_CC_2018_2021,
@@ -93,12 +109,16 @@ BOLT_PEDAL_LONG_CARS = {
CAR.CHEVROLET_MALIBU_HYBRID_CC,
}
# Cancel-to-personality mapping target: gen1 Bolt pedal-long paths only.
# Cancel-button remap support uses the same safety path on all pedal-long Bolts,
# but only gen1 needs extra LKAS suppression in CarState.
BOLT_GEN1_CANCEL_PERSONALITY_CARS = {
CAR.CHEVROLET_BOLT_CC_2017,
CAR.CHEVROLET_BOLT_CC_2018_2021,
}
CANCEL_REMAP_DISTANCE_CARS = BOLT_GEN1_CANCEL_PERSONALITY_CARS
CANCEL_REMAP_DISTANCE_CARS = BOLT_GEN1_CANCEL_PERSONALITY_CARS | {
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
CAR.CHEVROLET_BOLT_CC_2022_2023,
}
class CarInterface(CarInterfaceBase):
@@ -189,11 +209,16 @@ class CarInterface(CarInterfaceBase):
disable_openpilot_long = params.get_bool("DisableOpenpilotLongitudinal")
except UnknownKeyName:
disable_openpilot_long = False
try:
gm_auto_hold = params.get_bool("GMAutoHold")
except UnknownKeyName:
gm_auto_hold = False
ret.brand = "gm"
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.gm)]
ret.autoResumeSng = False
ret.enableBsm = 0x142 in fingerprint[CanBus.POWERTRAIN]
# Some Volt installs don't expose the BSM frame during startup fingerprinting.
ret.enableBsm = 0x142 in fingerprint[CanBus.POWERTRAIN] or candidate in VOLT_BSM_CARS
has_sascm = 0x2FF in fingerprint[CanBus.POWERTRAIN]
if has_sascm:
ret.flags |= GMFlags.SASCM.value
@@ -385,8 +410,8 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.BUICK_LACROSSE:
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate in (CAR.BUICK_LACROSSE, CAR.BUICK_LACROSSE_ASCM):
CarInterfaceBase.configure_torque_tune(CAR.BUICK_LACROSSE, ret.lateralTuning)
elif candidate == CAR.CADILLAC_ESCALADE:
ret.minEnableSpeed = -1. # engage speed is decided by pcm
@@ -444,7 +469,7 @@ class CarInterface(CarInterfaceBase):
# ACC Bolts use pedal for full longitudinal control, not just SNG.
ret.flags |= GMFlags.PEDAL_LONG.value
elif candidate == CAR.CHEVROLET_SILVERADO:
elif candidate in (CAR.CHEVROLET_SILVERADO, CAR.CHEVROLET_SILVERADO_CC):
# On the Bolt, the ECM and camera independently check that you are either above 5 kph or at a stop
# with foot on brake to allow engagement, but this platform only has that check in the camera.
# TODO: check if this is split by EV/ICE with more platforms in the future
@@ -481,7 +506,7 @@ class CarInterface(CarInterfaceBase):
ret.minSteerSpeed = 7 * CV.MPH_TO_MS
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.CADILLAC_XT5_CC:
elif candidate in (CAR.CADILLAC_XT5, CAR.CADILLAC_XT5_CC):
ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
@@ -620,6 +645,9 @@ class CarInterface(CarInterfaceBase):
if ACCELERATOR_POS_MSG not in fingerprint[CanBus.POWERTRAIN]:
ret.flags |= GMFlags.NO_ACCELERATOR_POS_MSG.value
if candidate == CAR.CHEVROLET_VOLT and ret.networkLocation == NetworkLocation.gateway:
# Reuse the no-camera safety bit as an ASCM Volt selector for the alternate EBCM brake path.
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_NO_CAMERA.value
try:
remote_start_boots_comma = params.get_bool("RemoteStartBootsComma")
@@ -629,6 +657,21 @@ class CarInterface(CarInterfaceBase):
if remote_start_boots_comma:
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_REMOTE_START_BOOTS_COMMA.value
volt_stock_auto_hold_safety = (
gm_auto_hold and
not ret.openpilotLongitudinalControl and
candidate in {
CAR.CHEVROLET_VOLT,
CAR.CHEVROLET_VOLT_2019,
CAR.CHEVROLET_VOLT_ASCM,
CAR.CHEVROLET_VOLT_CAMERA,
}
)
if volt_stock_auto_hold_safety:
# Reuse the paddle-scheduler safety bit as a stock-Volt auto-hold marker on
# non-pedal paths. The scheduler logic remains inactive without pedal-long.
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
use_panda_3d1_sched = (
ret.openpilotLongitudinalControl and
ret.enableGasInterceptorDEPRECATED and
@@ -0,0 +1,387 @@
import sys
import types
from types import SimpleNamespace
fake_interfaces = types.ModuleType("opendbc.car.interfaces")
class _FakeCarControllerBase:
def __init__(self, dbc_names=None, CP=None):
self.CP = CP
fake_interfaces.CarControllerBase = _FakeCarControllerBase
sys.modules.setdefault("opendbc.car.interfaces", fake_interfaces)
fake_params = types.ModuleType("openpilot.common.params")
class _FakeParams:
pass
class _FakeUnknownKeyName(Exception):
pass
fake_params.Params = _FakeParams
fake_params.UnknownKeyName = _FakeUnknownKeyName
sys.modules.setdefault("openpilot.common.params", fake_params)
fake_testing_grounds = types.ModuleType("openpilot.starpilot.common.testing_grounds")
fake_testing_grounds.testing_ground = SimpleNamespace(use_1=False)
sys.modules.setdefault("openpilot.starpilot.common.testing_grounds", fake_testing_grounds)
from opendbc.car.gm.carcontroller import (
CarController,
estimate_auto_hold_brake,
get_adas_keepalive_step,
get_lka_steering_cmd_counter,
get_testing_ground_1_brake_switch_bias,
get_stock_cc_active_for_cancel,
should_activate_auto_hold,
should_send_stock_long_cancel,
should_spoof_dash_speed,
should_spoof_ecm_cruise_status,
supports_volt_auto_hold,
use_interceptor_sng_launch,
)
from opendbc.car.gm.values import AccState, CAR, GMFlags
from opendbc.car.structs import CarParams
def _cs(enabled, pcm_acc_status):
return SimpleNamespace(
out=SimpleNamespace(cruiseState=SimpleNamespace(enabled=enabled), accFaulted=False),
pcm_acc_status=pcm_acc_status,
)
def _sng_cs(v_ego, standstill, cruise_standstill):
return SimpleNamespace(
out=SimpleNamespace(
vEgo=v_ego,
standstill=standstill,
cruiseState=SimpleNamespace(standstill=cruise_standstill),
),
)
def _controller(car_fingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021):
controller = CarController.__new__(CarController)
controller.CP = SimpleNamespace(carFingerprint=car_fingerprint)
controller.planner_regen_hold = False
controller.regen_paddle_pressed = False
controller.regen_paddle_timer = 0
controller.regen_press_counter = 0
controller.regen_release_counter = 0
controller.regen_min_on_frames = 0
controller.regen_min_off_frames = 0
controller.pedal_active_last = False
controller.pedal_steady = 0.0
controller.aego = 0.0
controller.maneuver_paddle_mode = "auto"
return controller
def test_gen1_bolt_pedal_cancel_uses_pcm_acc_status():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021)
assert get_stock_cc_active_for_cancel(CP, _cs(False, AccState.ACTIVE))
def test_gen2_bolt_acc_pedal_cancel_uses_enabled_only():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL)
assert not get_stock_cc_active_for_cancel(CP, _cs(False, AccState.ACTIVE))
def test_stock_cancel_is_suppressed_when_acc_is_faulted():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_CAMERA)
cs = _cs(True, AccState.FAULTED)
cs.out.accFaulted = True
assert not get_stock_cc_active_for_cancel(CP, cs)
assert not should_send_stock_long_cancel(11, cs)
def test_stock_cancel_requires_delay_and_no_acc_fault():
cs = _cs(True, AccState.ACTIVE)
assert not should_send_stock_long_cancel(10, cs)
assert should_send_stock_long_cancel(11, cs)
def test_gen1_bolt_pedal_ecm_cruise_spoof_is_not_gated_by_dash_speed_toggle():
CP = SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
openpilotLongitudinalControl=True,
)
assert not should_spoof_dash_speed(CP, SimpleNamespace(disable_openpilot_long=True, gm_pedal_longitudinal=True))
assert should_spoof_ecm_cruise_status(CP)
def test_gateway_keepalive_uses_gateway_cadence():
cp = SimpleNamespace(networkLocation=CarParams.NetworkLocation.gateway, flags=0)
assert get_adas_keepalive_step(cp, is_kaofui_car=True) == 100
assert get_adas_keepalive_step(cp, is_kaofui_car=False) == 200
def test_removed_camera_keepalive_uses_camera_cadence():
cp = SimpleNamespace(networkLocation=CarParams.NetworkLocation.fwdCamera, flags=GMFlags.NO_CAMERA.value)
assert get_adas_keepalive_step(cp, is_kaofui_car=True) == 100
def test_live_camera_path_does_not_send_pt_keepalive():
cp = SimpleNamespace(networkLocation=CarParams.NetworkLocation.fwdCamera, flags=0)
assert get_adas_keepalive_step(cp, is_kaofui_car=True) is None
def test_volt_auto_hold_requires_toggle_supported_non_cc_only_volt_and_stock_safety():
stock_safety = [SimpleNamespace(safetyParam=0x8000)]
no_safety = [SimpleNamespace(safetyParam=0)]
assert supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
openpilotLongitudinalControl=True,
networkLocation=CarParams.NetworkLocation.fwdCamera,
safetyConfigs=no_safety,
),
True,
)
assert not supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CC,
openpilotLongitudinalControl=True,
networkLocation=CarParams.NetworkLocation.fwdCamera,
safetyConfigs=no_safety,
),
True,
)
assert supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT,
openpilotLongitudinalControl=False,
networkLocation=CarParams.NetworkLocation.gateway,
safetyConfigs=stock_safety,
),
True,
)
assert supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_2019,
openpilotLongitudinalControl=False,
networkLocation=CarParams.NetworkLocation.fwdCamera,
safetyConfigs=stock_safety,
),
True,
)
assert not supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_2019,
openpilotLongitudinalControl=False,
networkLocation=CarParams.NetworkLocation.fwdCamera,
safetyConfigs=no_safety,
),
True,
)
assert not supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
openpilotLongitudinalControl=True,
networkLocation=CarParams.NetworkLocation.fwdCamera,
safetyConfigs=no_safety,
),
False,
)
def test_auto_hold_brake_estimate_uses_driver_or_op_brake_and_clamps():
assert estimate_auto_hold_brake(0.0, 20.0) == 80
assert estimate_auto_hold_brake(20.0, 40.0) == 110
assert estimate_auto_hold_brake(20.0, 160.0) == 160
assert estimate_auto_hold_brake(100.0, 400.0) == 240
def test_auto_hold_activation_allows_direct_entry_from_stopped_brake_press():
assert should_activate_auto_hold(
True,
False,
False,
True,
True,
False,
False,
0.01,
)
def test_auto_hold_activation_stays_latched_after_brake_release():
assert should_activate_auto_hold(
True,
False,
True,
False,
True,
False,
False,
0.0,
)
def test_auto_hold_activation_blocks_when_long_is_active_or_motion_is_above_threshold():
assert not should_activate_auto_hold(
True,
True,
False,
True,
True,
True,
False,
0.0,
)
assert not should_activate_auto_hold(
True,
True,
False,
False,
False,
False,
0.03,
)
def test_auto_hold_activation_allows_standstill_even_if_speed_filter_is_slightly_above_threshold():
assert should_activate_auto_hold(
True,
True,
False,
False,
True,
False,
False,
0.05,
)
def test_calc_pedal_command_small_accel_deadband_keeps_creep_target_stable():
pos_controller = _controller()
neg_controller = _controller()
pos_pedal, pos_regen = pos_controller.calc_pedal_command(0.02, True, 0.3)
neg_pedal, neg_regen = neg_controller.calc_pedal_command(-0.02, True, 0.3)
assert not pos_regen
assert not neg_regen
assert pos_pedal == neg_pedal
def test_calc_pedal_command_creep_switch_does_not_snap_to_target():
controller = _controller()
controller.pedal_active_last = True
controller.pedal_steady = 0.18
controller.regen_press_counter = 20
pedal_gas, press_regen = controller.calc_pedal_command(-1.0, True, 0.5)
assert press_regen
assert pedal_gas > 0.15
def test_calc_pedal_command_softens_small_positive_follow_ramp_at_road_speed():
controller = _controller()
controller.pedal_active_last = True
controller.pedal_steady = 0.18
pedal_gas, press_regen = controller.calc_pedal_command(0.2, True, 18.0)
assert not press_regen
assert pedal_gas - 0.18 < 0.026
def test_calc_pedal_command_keeps_strong_positive_requests_responsive():
controller = _controller()
controller.pedal_active_last = True
controller.pedal_steady = 0.18
pedal_gas, press_regen = controller.calc_pedal_command(1.4, True, 18.0)
assert not press_regen
assert pedal_gas - 0.18 > 0.04
def test_use_interceptor_sng_launch_requires_actual_near_stop():
CP = SimpleNamespace(vEgoStarting=0.25)
assert use_interceptor_sng_launch(CP, _sng_cs(0.0, True, True))
assert use_interceptor_sng_launch(CP, _sng_cs(0.2, False, True))
assert not use_interceptor_sng_launch(CP, _sng_cs(1.2, False, True))
assert not use_interceptor_sng_launch(CP, _sng_cs(0.0, True, False))
def test_use_interceptor_sng_launch_extends_for_maneuver_mode():
CP = SimpleNamespace(vEgoStarting=0.25)
assert use_interceptor_sng_launch(CP, _sng_cs(1.2, False, True), maneuver_mode=True)
assert not use_interceptor_sng_launch(CP, _sng_cs(2.2, False, True), maneuver_mode=True)
def test_testing_ground_1_brake_switch_bias_is_softened_but_still_speed_scaled():
assert get_testing_ground_1_brake_switch_bias(0.0) == 40
assert get_testing_ground_1_brake_switch_bias(6.0) == 85
assert get_testing_ground_1_brake_switch_bias(15.0) == 130
assert get_testing_ground_1_brake_switch_bias(30.0) == 170
def test_lka_counter_uses_returned_loopback_counter():
cs = SimpleNamespace(
loopback_lka_steering_cmd_updated=True,
loopback_lka_steering_cmd_counter=2,
loopback_lka_steering_cmd_ts_nanos=1,
pt_lka_steering_cmd_counter=0,
)
assert get_lka_steering_cmd_counter(0, cs) == 3
def test_lka_counter_keeps_advancing_without_loopback_updates():
cs = SimpleNamespace(
loopback_lka_steering_cmd_updated=False,
loopback_lka_steering_cmd_counter=0,
loopback_lka_steering_cmd_ts_nanos=1,
pt_lka_steering_cmd_counter=0,
)
next_counter = 1
sent = []
for _ in range(6):
idx = get_lka_steering_cmd_counter(next_counter, cs)
sent.append(idx)
next_counter = (idx + 1) % 4
assert sent == [1, 2, 3, 0, 1, 2]
def test_lka_counter_only_seeds_from_pt_counter_once_without_loopback():
cs = SimpleNamespace(
loopback_lka_steering_cmd_updated=False,
loopback_lka_steering_cmd_counter=0,
loopback_lka_steering_cmd_ts_nanos=0,
pt_lka_steering_cmd_counter=0,
)
next_counter = -1
sent = []
for _ in range(6):
idx = get_lka_steering_cmd_counter(next_counter, cs)
sent.append(idx)
next_counter = (idx + 1) % 4
assert sent == [1, 2, 3, 0, 1, 2]
+227 -11
View File
@@ -2,15 +2,25 @@ import pytest
from types import SimpleNamespace
from parameterized import parameterized
from opendbc.can import CANPacker
from opendbc.car import Bus, DT_CTRL
from opendbc.can import CANPacker, CANParser
from opendbc.car import Bus, DT_CTRL, structs
from opendbc.car.car_helpers import interfaces
from opendbc.car.gm import gmcan
from opendbc.car.gm.carcontroller import should_send_acc_dashboard_status, should_send_cc_button_spam, should_spoof_dash_speed
from opendbc.car.gm.carstate import CarState as GMCarState
from opendbc.car.gm.carcontroller import (
VisualAlert,
get_acc_dashboard_fcw_alert,
get_acc_dashboard_status_values,
should_send_acc_dashboard_status,
should_send_cc_button_spam,
should_spoof_dash_speed,
)
import opendbc.car.gm.interface as gm_interface
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.gm.fingerprints import FINGERPRINTS
from opendbc.car.gm.values import CAMERA_ACC_CAR, CAR, CC_ONLY_CAR, DBC, GM_RX_OFFSET, GMFlags, GMSafetyFlags
from opendbc.safety import ALTERNATIVE_EXPERIENCE
from openpilot.common.params import Params
CAMERA_DIAGNOSTIC_ADDRESS = 0x24b
VOLT_CARS = (
@@ -32,6 +42,7 @@ def _test_starpilot_toggles():
cluster_offset=1.0,
disable_openpilot_long=False,
force_fingerprint=False,
remap_cancel_to_distance=False,
vEgoStopping=0.5,
volt_sng=False,
)
@@ -64,22 +75,34 @@ class TestGMInterface:
("interceptor", True),
("ascm_int", False),
])
def test_volt_testing_ground_tune_sets_nonzero_p_and_starting_state(self, _name, pedal_present, monkeypatch):
def test_volt_testing_ground_tune_sets_nonzero_p_and_starting_state(self, _name, pedal_present):
CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM]
fingerprint = _empty_fingerprint()
if pedal_present:
fingerprint[0][0x201] = 8 # pedal detected
fingerprint[0][0x2FF] = 8 # SASCM detected
monkeypatch.setattr(gm_interface.testing_ground, "use_2", True, raising=False)
old_testing_ground = gm_interface.testing_ground
gm_interface.testing_ground = SimpleNamespace(use_2=True)
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=False, is_release=False, docs=False,
starpilot_toggles=_test_starpilot_toggles())
try:
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=False, is_release=False, docs=False,
starpilot_toggles=_test_starpilot_toggles())
finally:
gm_interface.testing_ground = old_testing_ground
assert list(car_params.longitudinalTuning.kpV) == [0.10, 0.072, 0.05, 0.04]
assert list(car_params.longitudinalTuning.kiV) == [0.025, 0.03, 0.04, 0.055]
assert car_params.startingState
assert car_params.startAccel == pytest.approx(1.15)
if pedal_present:
assert list(car_params.longitudinalTuning.kpV) == pytest.approx([0.10, 0.072, 0.05, 0.04])
assert list(car_params.longitudinalTuning.kiV) == pytest.approx([0.025, 0.03, 0.04, 0.055])
assert car_params.startingState
assert car_params.startAccel == pytest.approx(1.15)
else:
assert not car_params.openpilotLongitudinalControl
assert not car_params.enableGasInterceptorDEPRECATED
assert list(car_params.longitudinalTuning.kpV) == [0.0]
assert list(car_params.longitudinalTuning.kiV) == [0.5, 0.5]
assert not car_params.startingState
assert car_params.startAccel == pytest.approx(0.0)
def test_volt_cc_sparse_fingerprint_without_camera_sets_no_camera(self):
CarInterface = interfaces[CAR.CHEVROLET_VOLT_CC]
@@ -103,11 +126,91 @@ class TestGMInterface:
starpilot_toggles=_test_starpilot_toggles())
assert car_params.flags & GMFlags.NO_ACCELERATOR_POS_MSG.value
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_NO_CAMERA.value
pt_parser = CarInterface.CarState.get_can_parsers(car_params)[Bus.pt]
assert "ECMAcceleratorPos" not in pt_parser.vl
assert "EBCMBrakePedalPosition" in pt_parser.vl
@parameterized.expand(VOLT_CARS)
def test_volt_bsm_is_enabled_without_fingerprint_match(self, car_model):
CarInterface = interfaces[car_model]
car_params = CarInterface.get_params(car_model, _empty_fingerprint(), [], alpha_long=False, is_release=False, docs=False,
starpilot_toggles=_test_starpilot_toggles())
assert car_params.enableBsm
def test_volt_bsm_parser_is_optional(self):
cp = SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_ASCM,
networkLocation=structs.CarParams.NetworkLocation.fwdCamera,
flags=0,
transmissionType=structs.CarParams.TransmissionType.direct,
enableGasInterceptorDEPRECATED=False,
enableBsm=True,
)
pt_parser = GMCarState.get_can_parsers(cp)[Bus.pt]
bsm_addr = pt_parser.dbc.name_to_msg["BCMBlindSpotMonitor"].address
assert "BCMBlindSpotMonitor" in pt_parser.vl
assert pt_parser.message_states[bsm_addr].ignore_alive
def test_volt_ascm_cam_parser_includes_optional_aeb_cmd(self):
cam_parser = GMCarState.get_can_parsers(SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_ASCM,
networkLocation=structs.CarParams.NetworkLocation.fwdCamera,
flags=0,
transmissionType=structs.CarParams.TransmissionType.direct,
enableGasInterceptorDEPRECATED=False,
enableBsm=False,
))[Bus.cam]
aeb_addr = cam_parser.dbc.name_to_msg["AEBCmd"].address
assert "AEBCmd" in cam_parser.vl
assert cam_parser.message_states[aeb_addr].ignore_alive
def test_bolt_gen2_pedal_cancel_remap_sets_alt_exp(self):
CarInterface = interfaces[CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL]
fingerprint = _empty_fingerprint()
fingerprint[0][0x201] = 8
params = Params()
toggles = _test_starpilot_toggles()
try:
params.put_bool("RemapCancelToDistance", True)
car_params = CarInterface.get_params(CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL, fingerprint, [], alpha_long=False,
is_release=False, docs=False, starpilot_toggles=toggles)
finally:
params.remove("RemapCancelToDistance")
assert car_params.alternativeExperience & ALTERNATIVE_EXPERIENCE.GM_REMAP_CANCEL_TO_DISTANCE
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_BOLT_2022_PEDAL.value
def test_cadillac_xt5_sdgm_sascm_gates_alpha_long(self):
CarInterface = interfaces[CAR.CADILLAC_XT5]
fingerprint = _empty_fingerprint()
fingerprint[0][0xBE] = 6
stock_params = CarInterface.get_params(CAR.CADILLAC_XT5, fingerprint, [], alpha_long=True, is_release=False,
docs=False, starpilot_toggles=_test_starpilot_toggles())
assert stock_params.networkLocation == structs.CarParams.NetworkLocation.fwdCamera
assert stock_params.pcmCruise
assert stock_params.alphaLongitudinalAvailable is False
assert stock_params.openpilotLongitudinalControl is False
assert stock_params.safetyConfigs[0].safetyParam & GMSafetyFlags.HW_SDGM.value
fingerprint[0][0x2FF] = 8
sascm_params = CarInterface.get_params(CAR.CADILLAC_XT5, fingerprint, [], alpha_long=True, is_release=False,
docs=False, starpilot_toggles=_test_starpilot_toggles())
assert sascm_params.flags & GMFlags.SASCM.value
assert sascm_params.alphaLongitudinalAvailable
assert sascm_params.openpilotLongitudinalControl
assert not sascm_params.pcmCruise
assert sascm_params.safetyConfigs[0].safetyParam & GMSafetyFlags.HW_CAM_LONG.value
class TestGMCarController:
def test_dash_speed_spoof_respects_live_stock_acc_toggles(self):
@@ -213,3 +316,116 @@ class TestGMCarController:
msgs = gmcan.create_gm_cc_spam_command(packer, controller, cs, actuators, SimpleNamespace(is_metric=False))
assert [msg[2] for msg in msgs] == [0]
def test_acc_dashboard_command_preserves_raw_fcw_alert_level(self):
packer = CANPacker(DBC[CAR.CHEVROLET_BOLT_ACC_2022_2023][Bus.pt])
parser = CANParser(DBC[CAR.CHEVROLET_BOLT_ACC_2022_2023][Bus.pt], [("ASCMActiveCruiseControlStatus", 0)], 0)
msg = gmcan.create_acc_dashboard_command(
packer,
0,
{
"ACCCruiseState": 0,
"ACCLeadCar": 1,
"ACCResumeButton": 0,
"ACCSpeedSetpoint": 100,
"ACCGapLevel": 3,
"ACCCmdActive": 1,
},
0x2,
)
parser.update([0, [msg]])
assert parser.vl["ASCMActiveCruiseControlStatus"]["FCWAlert"] == 2
def test_acc_dashboard_command_can_replay_stock_status_payload(self):
packer = CANPacker(DBC[CAR.CHEVROLET_VOLT_ASCM][Bus.pt])
msg = gmcan.create_acc_dashboard_command(
packer,
0,
{
"ACCCruiseState": 0,
"ACCLeadCar": 1,
"ACCResumeButton": 0,
"ACCSpeedSetpoint": 50,
"ACCGapLevel": 2,
"ACCCmdActive": 0,
},
0x3,
)
assert msg[1].hex() == "010023200113"
def test_acc_dashboard_fcw_alert_prefers_openpilot_alert(self):
cs = SimpleNamespace(
stock_fcw_alert=1,
out=SimpleNamespace(stockAeb=False, stockFcw=False),
)
assert get_acc_dashboard_fcw_alert(VisualAlert.fcw, cs) == 0x3
def test_acc_dashboard_fcw_alert_replays_stock_camera_alert_level(self):
cs = SimpleNamespace(
stock_fcw_alert=2,
out=SimpleNamespace(stockAeb=False, stockFcw=False),
)
assert get_acc_dashboard_fcw_alert(VisualAlert.none, cs) == 2
def test_acc_dashboard_fcw_alert_falls_back_to_stock_aeb_event(self):
cs = SimpleNamespace(
stock_fcw_alert=0,
out=SimpleNamespace(stockAeb=True, stockFcw=False),
)
assert get_acc_dashboard_fcw_alert(VisualAlert.none, cs) == 0x3
def test_acc_dashboard_fcw_alert_falls_back_to_stock_fcw_event(self):
cs = SimpleNamespace(
stock_fcw_alert=0,
out=SimpleNamespace(stockAeb=False, stockFcw=True),
)
assert get_acc_dashboard_fcw_alert(VisualAlert.none, cs) == 0x3
def test_acc_dashboard_status_values_use_openpilot_hud_when_enabled(self):
cs = SimpleNamespace(
stock_acc_cruise_state=5,
stock_acc_lead_car=0,
stock_acc_resume_button=1,
stock_acc_speed_setpoint_kph=42.0,
stock_acc_gap_level=1,
stock_acc_cmd_active=0,
)
values = get_acc_dashboard_status_values(True, 105.0, SimpleNamespace(leadDistanceBars=3, leadVisible=True), cs)
assert values == {
"ACCCruiseState": 0,
"ACCLeadCar": 1,
"ACCResumeButton": 0,
"ACCSpeedSetpoint": 105.0,
"ACCGapLevel": 3,
"ACCCmdActive": 1,
}
def test_acc_dashboard_status_values_reuse_stock_camera_status_when_disabled(self):
cs = SimpleNamespace(
stock_acc_cruise_state=0,
stock_acc_lead_car=1,
stock_acc_resume_button=0,
stock_acc_speed_setpoint_kph=50.0,
stock_acc_gap_level=2,
stock_acc_cmd_active=0,
)
values = get_acc_dashboard_status_values(False, 0.0, SimpleNamespace(leadDistanceBars=0, leadVisible=False), cs)
assert values == {
"ACCCruiseState": 0,
"ACCLeadCar": 1,
"ACCResumeButton": 0,
"ACCSpeedSetpoint": 50.0,
"ACCGapLevel": 2,
"ACCCmdActive": 0,
}
@@ -0,0 +1,52 @@
from opendbc.can import CANPacker
from opendbc.car.gm import gmcan
from opendbc.car.gm.values import CAR, DBC
class TestGMCan:
def setup_method(self):
self.packer = CANPacker(DBC[CAR.CHEVROLET_BOLT_ACC_2022_2023]["pt"])
def test_gas_regen_command_matches_starpilot_bolt_acc(self):
addr, dat, bus = gmcan.create_gas_regen_command(self.packer, 0, 5000, 1, True, False)
assert addr == 0x2CB
assert bus == 0
assert dat.hex() == "41429c4000bd63bf"
def test_gas_regen_command_preserves_always_one3_layout(self):
_, dat, _ = gmcan.create_gas_regen_command(self.packer, 0, 0, 1, True, False, include_always_one3=True)
assert dat.hex() == "4142800000bd7fff"
def test_gas_regen_command_encodes_high_bit_above_8191(self):
_, dat, _ = gmcan.create_gas_regen_command(self.packer, 0, 8848, 1, True, False)
decoded = ((dat[1] & 0x1) << 13) | (dat[2] << 5) | ((dat[3] & 0xF8) >> 3)
assert dat[1] & 0x1
assert decoded == 8848
def test_gas_regen_command_matches_starpilot_volt_2019(self):
packer = CANPacker(DBC[CAR.CHEVROLET_VOLT_2019]["pt"])
addr, dat, bus = gmcan.create_gas_regen_command(packer, 0, 5000, 1, True, False, include_always_one3=True, use_volt_layout=True)
assert addr == 0x2CB
assert bus == 0
assert dat.hex() == "41429c4000bd63bf"
def test_gas_regen_command_matches_starpilot_volt_ascm(self):
packer = CANPacker(DBC[CAR.CHEVROLET_VOLT_ASCM]["pt"])
addr, dat, bus = gmcan.create_gas_regen_command(packer, 0, 5000, 1, True, False, include_always_one3=True, use_volt_layout=True)
assert addr == 0x2CB
assert bus == 0
assert dat.hex() == "41429c4000bd63bf"
def test_gas_regen_command_matches_opgm_plain_volt_layout(self):
packer = CANPacker("gm_global_a_powertrain_generated")
addr, dat, bus = gmcan.create_gas_regen_command(packer, 0, 5000, 1, True, False, use_generated_layout=True)
assert addr == 0x2CB
assert bus == 0
assert dat.hex() == "41435c7000bca38f"
+20 -3
View File
@@ -233,7 +233,7 @@ class CAR(Platforms):
)
CHEVROLET_VOLT = GMASCMPlatformConfig(
[GMCarDocs("Chevrolet Volt 2017-18", min_enable_speed=0, video="https://youtu.be/QeMCN_4TFfQ")],
GMCarSpecs(mass=1607, wheelbase=2.69, steerRatio=15.7, centerToFrontRatio=0.45, tireStiffnessFactor=0.469, minEnableSpeed=-1),
GMCarSpecs(mass=1607, wheelbase=2.69, steerRatio=15.7, centerToFrontRatio=0.45, tireStiffnessFactor=1.0, minEnableSpeed=-1), #tire stiffness factor hasn't been updated since 2018 and every other gm is on 1.0
dbc_dict={
Bus.pt: "gm_global_a_powertrain_volt",
Bus.radar: "gm_global_a_object",
@@ -279,6 +279,10 @@ class CAR(Platforms):
[GMCarDocs("Buick LaCrosse 2017-19", "Driver Confidence Package 2")],
GMCarSpecs(mass=1712, wheelbase=2.91, steerRatio=15.8, centerToFrontRatio=0.4),
)
BUICK_LACROSSE_ASCM = GMPlatformConfig(
[GMCarDocs("Buick LaCrosse 2017-19 ASCM Harness")],
BUICK_LACROSSE.specs,
)
BUICK_REGAL = GMASCMPlatformConfig(
[GMCarDocs("Buick Regal Essence 2018")],
GMCarSpecs(mass=1714, wheelbase=2.83, steerRatio=14.4, centerToFrontRatio=0.4),
@@ -329,6 +333,13 @@ class CAR(Platforms):
],
GMCarSpecs(mass=2994, wheelbase=3.75, steerRatio=16.3, tireStiffnessFactor=1.0),
)
CHEVROLET_SILVERADO_CC = GMPlatformConfig(
[
GMCarDocs("Chevrolet Silverado 1500 - No-ACC"),
GMCarDocs("GMC Sierra 1500 - No-ACC"),
],
CHEVROLET_SILVERADO.specs,
)
CHEVROLET_EQUINOX = GMPlatformConfig(
[GMCarDocs("Chevrolet Equinox 2019-22")],
GMCarSpecs(mass=1588, wheelbase=2.72, steerRatio=14.4, centerToFrontRatio=0.4),
@@ -353,6 +364,10 @@ class CAR(Platforms):
[GMCarDocs("Cadillac XT4 2023", "Driver Assist Package")],
GMCarSpecs(mass=1660, wheelbase=2.78, steerRatio=14.4, centerToFrontRatio=0.4),
)
CADILLAC_XT5 = GMSDGMPlatformConfig(
[GMCarDocs("Cadillac XT5 2022", "Driver Assist Package")],
CarSpecs(mass=1810, wheelbase=2.86, steerRatio=16.34, centerToFrontRatio=0.5),
)
CADILLAC_XT6 = GMPlatformConfig(
[GMCarDocs("Cadillac XT6 2020", "Driver Assist Package")],
GMCarSpecs(mass=2050, wheelbase=2.86, steerRatio=16.5, centerToFrontRatio=0.4),
@@ -383,7 +398,7 @@ class CAR(Platforms):
)
CADILLAC_XT5_CC = GMPlatformConfig(
[GMCarDocs("Cadillac XT5 - No-ACC")],
CarSpecs(mass=1810, wheelbase=2.86, steerRatio=16.34, centerToFrontRatio=0.5),
CADILLAC_XT5.specs,
)
CHEVROLET_EQUINOX_CC = GMPlatformConfig(
[GMCarDocs("Chevrolet Equinox 2019-22 - No-ACC")],
@@ -527,6 +542,7 @@ ALT_ACCS = {CAR.GMC_YUKON, CAR.GMC_YUKON_CC}
# We're integrated at the Safety Data Gateway Module on these cars
SDGM_CAR = {
CAR.CADILLAC_XT4,
CAR.CADILLAC_XT5,
CAR.CADILLAC_XT6,
CAR.CHEVROLET_TRAVERSE,
CAR.CHEVROLET_BLAZER,
@@ -549,6 +565,7 @@ CC_ONLY_CAR = {
CAR.CADILLAC_XT5_CC,
CAR.CHEVROLET_MALIBU_CC,
CAR.CHEVROLET_MALIBU_HYBRID_CC,
CAR.CHEVROLET_SILVERADO_CC,
}
CC_REGEN_PADDLE_CAR = {
CAR.CHEVROLET_BOLT_CC_2018_2021,
@@ -559,7 +576,7 @@ CC_REGEN_PADDLE_CAR = {
CAMERA_ACC_CAR.update(CC_ONLY_CAR)
# ASCM-INT paths are only enabled when SASCM (0x2FF) is detected at runtime
ASCM_INT = {CAR.CHEVROLET_VOLT_ASCM, CAR.GMC_ACADIA_ASCM, CAR.CHEVROLET_MALIBU_ASCM, CAR.CADILLAC_ESCALADE_ASCM}
ASCM_INT = {CAR.CHEVROLET_VOLT_ASCM, CAR.GMC_ACADIA_ASCM, CAR.CHEVROLET_MALIBU_ASCM, CAR.CADILLAC_ESCALADE_ASCM, CAR.BUICK_LACROSSE_ASCM}
STEER_THRESHOLD = 1.0
+281 -47
View File
@@ -1,16 +1,130 @@
import math
import numpy as np
from opendbc.can import CANPacker
from opendbc.car import Bus, DT_CTRL, rate_limit, make_tester_present_msg, structs
from opendbc.car import ACCELERATION_DUE_TO_GRAVITY, Bus, DT_CTRL, create_gas_interceptor_command, rate_limit, make_tester_present_msg, structs
from opendbc.car.honda import hondacan
from opendbc.car.honda.values import CAR, CruiseButtons, HONDA_BOSCH, HONDA_BOSCH_CANFD, HONDA_BOSCH_RADARLESS, \
HONDA_BOSCH_TJA_CONTROL, HONDA_NIDEC_ALT_PCM_ACCEL, CarControllerParams
from opendbc.car.honda.values import (
CAR,
CruiseButtons,
HONDA_BOSCH,
HONDA_BOSCH_CANFD,
HONDA_BOSCH_RADARLESS,
HONDA_BOSCH_TJA_CONTROL,
HONDA_NIDEC_ALT_PCM_ACCEL,
CarControllerParams,
HondaFlags,
)
from opendbc.car.interfaces import CarControllerBase
from openpilot.common.params import Params
VisualAlert = structs.CarControl.HUDControl.VisualAlert
LongCtrlState = structs.CarControl.Actuators.LongControlState
def get_civic_bosch_modified_torque_lpf_tau(torque_cmd: float, prev_torque_cmd: float, v_ego: float) -> float:
torque_delta = abs(float(torque_cmd) - float(prev_torque_cmd))
torque_cmd_abs = abs(float(torque_cmd))
sign_change = (float(torque_cmd) * float(prev_torque_cmd)) < 0.0
highway = v_ego > (50.0 * 0.44704)
low_speed = v_ego < (30.0 * 0.44704)
if highway:
if torque_cmd_abs < 0.12:
return 0.18 if sign_change else 0.16
if sign_change and torque_delta > 0.15:
return 0.10
return 0.12
if sign_change and torque_cmd_abs < 0.25:
return 0.28 if low_speed else 0.22
# Extra damping for the tiny near-center commands where both modified EPS
# firmwares still show hunting and escalating sway.
if torque_cmd_abs < 0.12:
return 0.28 if low_speed else 0.20
if low_speed:
if torque_delta > 0.50:
return 0.14
elif torque_delta > 0.20:
return 0.16
elif torque_delta > 0.05:
return 0.18
else:
return 0.22
if torque_delta > 0.50:
return 0.12
elif torque_delta > 0.20:
return 0.13
elif torque_delta > 0.05:
return 0.15
else:
return 0.18
def get_civic_bosch_modified_steering_pressed(
raw_pressed: bool, steering_torque: float, torque_cmd: float, filter_s: float, was_pressed: bool
) -> tuple[float, bool]:
torque_product = steering_torque * torque_cmd
torque_cmd_abs = abs(torque_cmd)
if raw_pressed:
if torque_product < 0.0:
trigger_s = 0.08 if was_pressed else 0.10
rise_rate = 1.0
elif torque_cmd_abs < 0.10:
trigger_s = 0.20 if was_pressed else 0.24
rise_rate = 0.75
else:
trigger_s = 0.70 if was_pressed else 0.80
rise_rate = 0.50
filter_s = min(1.0, filter_s + (rise_rate * DT_CTRL))
steering_pressed = filter_s >= trigger_s
else:
filter_s = max(0.0, filter_s - 8.0 * DT_CTRL)
steering_pressed = filter_s > 0.04 and was_pressed
return filter_s, steering_pressed
def get_honda_bosch_wind_brake_mps2(v_ego: float) -> float:
return float(np.interp(v_ego, [0.0, 13.4, 22.4, 31.3, 40.2], [0.000, 0.049, 0.136, 0.267, 0.441]))
def update_honda_bosch_live_learning(
gas_factor: float,
wind_factor: float,
wind_factor_before_brake: float,
desired_accel: float,
actual_accel: float,
gas_pedal_force: float,
wind_brake_mps2: float,
brake_pressed: bool,
v_ego: float,
) -> tuple[float, float, float]:
accel_error = desired_accel - actual_accel
if accel_error != 0.0 and gas_pedal_force > 0.0:
gas_factor = float(np.clip(gas_factor + accel_error / 50.0 * gas_pedal_force, 0.1, 3.0))
if accel_error != 0.0 and not brake_pressed and v_ego > 0.0:
wind_adjust = 1.0 + wind_brake_mps2 / 1000.0
if accel_error > 0.0:
wind_factor = float(np.clip(wind_factor * wind_adjust, 0.1, 3.0))
else:
wind_factor = float(np.clip(wind_factor / wind_adjust, 0.1, 3.0))
if gas_pedal_force <= 0.0:
wind_factor = max(wind_factor, wind_factor_before_brake)
else:
wind_factor_before_brake = wind_factor
return gas_factor, wind_factor, wind_factor_before_brake
def compute_gb_honda_bosch(accel, speed):
# TODO returns 0s, is unused
return 0.0, 0.0
@@ -36,18 +150,18 @@ def compute_gas_brake(accel, speed, fingerprint):
# TODO not clear this does anything useful
def actuator_hysteresis(brake, braking, brake_steady, v_ego, car_fingerprint):
# hyst params
brake_hyst_on = 0.02 # to activate brakes exceed this value
brake_hyst_on = 0.02 # to activate brakes exceed this value
brake_hyst_off = 0.005 # to deactivate brakes below this value
brake_hyst_gap = 0.01 # don't change brake command for small oscillations within this value
brake_hyst_gap = 0.01 # don't change brake command for small oscillations within this value
# *** hysteresis logic to avoid brake blinking. go above 0.1 to trigger
if (brake < brake_hyst_on and not braking) or brake < brake_hyst_off:
brake = 0.
braking = brake > 0.
brake = 0.0
braking = brake > 0.0
# for small brake oscillations within brake_hyst_gap, don't change the brake command
if brake == 0.:
brake_steady = 0.
if brake == 0.0:
brake_steady = 0.0
elif brake > brake_steady + brake_hyst_gap:
brake_steady = brake - brake_hyst_gap
elif brake < brake_steady - brake_hyst_gap:
@@ -64,7 +178,7 @@ def brake_pump_hysteresis(apply_brake, apply_brake_last, last_pump_ts, ts):
# - there is an increment in brake request
# - we are applying steady state brakes and we haven't been running the pump
# for more than 20s (to prevent pressure bleeding)
if apply_brake > apply_brake_last or (ts - last_pump_ts > 20. and apply_brake > 0):
if apply_brake > apply_brake_last or (ts - last_pump_ts > 20.0 and apply_brake > 0):
last_pump_ts = ts
# once the pump is on, run it for at least 0.2s
@@ -93,14 +207,15 @@ class CarController(CarControllerBase):
super().__init__(dbc_names, CP)
self.packer = CANPacker(dbc_names[Bus.pt])
self.params = CarControllerParams(CP)
self.param_store = Params()
self.CAN = hondacan.CanBus(CP)
self.tja_control = CP.carFingerprint in HONDA_BOSCH_TJA_CONTROL
self.braking = False
self.brake_steady = 0.
self.brake_last = 0.
self.brake_steady = 0.0
self.brake_last = 0.0
self.apply_brake_last = 0
self.last_pump_ts = 0.
self.last_pump_ts = 0.0
self.stopping_counter = 0
self.accel = 0.0
@@ -108,12 +223,41 @@ class CarController(CarControllerBase):
self.gas = 0.0
self.brake = 0.0
self.last_torque = 0.0
self.torque_lpf = 0.0
self.prev_torque_cmd = 0.0
self.steering_pressed_filter_s = 0.0
self.steering_pressed_robust_prev = False
self.bosch_last_gas = 0.0
self.bosch_gas_factor = self.param_store.get_float("HondaGasFactorParams", default=1.0)
self.bosch_wind_factor = self.param_store.get_float("HondaWindFactorParams", default=1.0)
self.bosch_wind_factor_before_brake = self.bosch_wind_factor
self.bosch_gas_factor_before_gasmax = self.bosch_gas_factor
self.bosch_wind_factor_before_gasmax = self.bosch_wind_factor
self.pitch = 0.0
def _modified_civic_standard_active(self) -> bool:
return self.CP.carFingerprint == CAR.HONDA_CIVIC_BOSCH and bool(self.CP.flags & HondaFlags.EPS_MODIFIED)
def _filtered_steering_pressed(self, CS, torque_cmd: float) -> bool:
self.steering_pressed_filter_s, steering_pressed = get_civic_bosch_modified_steering_pressed(
bool(CS.out.steeringPressed),
float(getattr(CS.out, "steeringTorque", 0.0)),
float(torque_cmd),
self.steering_pressed_filter_s,
self.steering_pressed_robust_prev,
)
self.steering_pressed_robust_prev = steering_pressed
return steering_pressed
def update(self, CC, CS, now_nanos, starpilot_toggles):
actuators = CC.actuators
hud_control = CC.hudControl
hud_v_cruise = hud_control.setSpeed / CS.v_cruise_factor if hud_control.speedVisible else 255
pcm_cancel_cmd = CC.cruiseControl.cancel
gas_interceptor_command = 0.0
if len(CC.orientationNED) == 3:
self.pitch = CC.orientationNED[1]
hill_brake = math.sin(self.pitch) * ACCELERATION_DUE_TO_GRAVITY
if CC.longActive:
accel = actuators.accel
@@ -122,17 +266,36 @@ class CarController(CarControllerBase):
accel = 0.0
gas, brake = 0.0, 0.0
torque_cmd = float(actuators.torque)
filtered_steering_pressed = bool(CS.out.steeringPressed)
if self._modified_civic_standard_active():
if CC.latActive:
filtered_steering_pressed = self._filtered_steering_pressed(CS, torque_cmd)
if filtered_steering_pressed:
self.torque_lpf = 0.0
self.prev_torque_cmd = 0.0
torque_cmd = 0.0
else:
tau = get_civic_bosch_modified_torque_lpf_tau(torque_cmd, self.prev_torque_cmd, CS.out.vEgo)
alpha = DT_CTRL / (tau + DT_CTRL)
self.torque_lpf = alpha * torque_cmd + ((1.0 - alpha) * self.torque_lpf)
self.prev_torque_cmd = torque_cmd
torque_cmd = self.torque_lpf
else:
self.torque_lpf = 0.0
self.prev_torque_cmd = 0.0
self.steering_pressed_filter_s = 0.0
self.steering_pressed_robust_prev = False
# *** rate limit steer ***
limited_torque = rate_limit(actuators.torque, self.last_torque, -self.params.STEER_DELTA_DOWN * DT_CTRL,
self.params.STEER_DELTA_UP * DT_CTRL)
limited_torque = rate_limit(torque_cmd, self.last_torque, -self.params.STEER_DELTA_DOWN * DT_CTRL, self.params.STEER_DELTA_UP * DT_CTRL)
self.last_torque = limited_torque
# *** apply brake hysteresis ***
pre_limit_brake, self.braking, self.brake_steady = actuator_hysteresis(brake, self.braking, self.brake_steady,
CS.out.vEgo, self.CP.carFingerprint)
pre_limit_brake, self.braking, self.brake_steady = actuator_hysteresis(brake, self.braking, self.brake_steady, CS.out.vEgo, self.CP.carFingerprint)
# *** rate limit after the enable check ***
self.brake_last = rate_limit(pre_limit_brake, self.brake_last, -2., DT_CTRL)
self.brake_last = rate_limit(pre_limit_brake, self.brake_last, -2.0, DT_CTRL)
# vehicle hud display, wait for one update from 10Hz 0x304 msg
alert_fcw, alert_steer_required = process_hud_alert(hud_control.visualAlert)
@@ -140,8 +303,7 @@ class CarController(CarControllerBase):
# **** process the car messages ****
# steer torque is converted back to CAN reference (positive when steering right)
apply_torque = int(np.interp(-limited_torque * self.params.STEER_MAX,
self.params.STEER_LOOKUP_BP, self.params.STEER_LOOKUP_V))
apply_torque = int(np.interp(-limited_torque * self.params.STEER_MAX, self.params.STEER_LOOKUP_BP, self.params.STEER_LOOKUP_V))
# Send CAN commands
can_sends = []
@@ -149,37 +311,29 @@ class CarController(CarControllerBase):
# tester present - w/ no response (keeps radar disabled)
if self.CP.carFingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS) and self.CP.openpilotLongitudinalControl:
if self.frame % 10 == 0:
can_sends.append(make_tester_present_msg(0x18DAB0F1, 1, suppress_response=True))
can_sends.append(make_tester_present_msg(0x18DAB0F1, self.CAN.pt, suppress_response=True))
# Send steering command.
can_sends.append(hondacan.create_steering_control(self.packer, self.CAN, apply_torque, CC.latActive, self.tja_control))
# wind brake from air resistance decel at high speed
wind_brake = np.interp(CS.out.vEgo, [0.0, 2.3, 35.0], [0.001, 0.002, 0.15])
wind_brake = float(np.interp(CS.out.vEgo, [0.0, 2.3, 35.0], [0.001, 0.002, 0.15]))
wind_brake_mps2 = get_honda_bosch_wind_brake_mps2(CS.out.vEgo)
# all of this is only relevant for HONDA NIDEC
max_accel = np.interp(CS.out.vEgo, self.params.NIDEC_MAX_ACCEL_BP, self.params.NIDEC_MAX_ACCEL_V)
# TODO this 1.44 is just to maintain previous behavior
pcm_speed_BP = [-wind_brake,
-wind_brake * (3 / 4),
0.0,
0.5]
pcm_speed_BP = [-wind_brake, -wind_brake * (3 / 4), 0.0, 0.5]
# The Honda ODYSSEY seems to have different PCM_ACCEL
# msgs, is it other cars too?
if not CC.longActive:
if self.CP.enableGasInterceptorDEPRECATED or not CC.longActive:
pcm_speed = 0.0
pcm_accel = int(0.0)
elif self.CP.carFingerprint in HONDA_NIDEC_ALT_PCM_ACCEL:
pcm_speed_V = [0.0,
np.clip(CS.out.vEgo - 3.0, 0.0, 100.0),
np.clip(CS.out.vEgo + 0.0, 0.0, 100.0),
np.clip(CS.out.vEgo + 5.0, 0.0, 100.0)]
pcm_speed_V = [0.0, np.clip(CS.out.vEgo - 3.0, 0.0, 100.0), np.clip(CS.out.vEgo + 0.0, 0.0, 100.0), np.clip(CS.out.vEgo + 5.0, 0.0, 100.0)]
pcm_speed = float(np.interp(gas - brake, pcm_speed_BP, pcm_speed_V))
pcm_accel = int(1.0 * self.params.NIDEC_GAS_MAX)
else:
pcm_speed_V = [0.0,
np.clip(CS.out.vEgo - 2.0, 0.0, 100.0),
np.clip(CS.out.vEgo + 2.0, 0.0, 100.0),
np.clip(CS.out.vEgo + 5.0, 0.0, 100.0)]
pcm_speed_V = [0.0, np.clip(CS.out.vEgo - 2.0, 0.0, 100.0), np.clip(CS.out.vEgo + 2.0, 0.0, 100.0), np.clip(CS.out.vEgo + 5.0, 0.0, 100.0)]
pcm_speed = float(np.interp(gas - brake, pcm_speed_BP, pcm_speed_V))
pcm_accel = int(np.clip((accel / 1.44) / max_accel, 0.0, 1.0) * self.params.NIDEC_GAS_MAX)
@@ -199,35 +353,108 @@ class CarController(CarControllerBase):
if self.CP.carFingerprint in HONDA_BOSCH:
self.accel = float(np.clip(accel, self.params.BOSCH_ACCEL_MIN, self.params.BOSCH_ACCEL_MAX))
self.gas = float(np.interp(accel, self.params.BOSCH_GAS_LOOKUP_BP, self.params.BOSCH_GAS_LOOKUP_V))
gas_pedal_force = self.accel + hill_brake
if self.CP.carFingerprint not in HONDA_BOSCH_RADARLESS:
gas_pedal_force += wind_brake_mps2 * self.bosch_wind_factor
if actuators.longControlState == LongCtrlState.pid and not CS.out.gasPressed:
gas_error = self.accel - CS.out.aEgo
if gas_error != 0.0 and gas_pedal_force > 0.0:
if self.CP.carFingerprint == CAR.HONDA_INSIGHT:
gas_learn_speed = 150.0
elif self.CP.carFingerprint in (CAR.ACURA_RDX_3G, CAR.ACURA_RDX_3G_MMR):
gas_learn_speed = 300.0
else:
gas_learn_speed = 50.0
self.bosch_gas_factor = float(np.clip(self.bosch_gas_factor + gas_error / gas_learn_speed * gas_pedal_force, 0.1, 3.0))
if gas_error != 0.0 and not CS.out.brakePressed and CS.out.vEgo > 0.0:
wind_learn_speed = 100.0 if self.CP.carFingerprint in (CAR.ACURA_RDX_3G, CAR.ACURA_RDX_3G_MMR) else 1000.0
wind_adjust = 1.0 + wind_brake_mps2 / wind_learn_speed
if gas_error > 0.0:
self.bosch_wind_factor = float(np.clip(self.bosch_wind_factor * wind_adjust, 0.1, 3.0))
else:
self.bosch_wind_factor = float(np.clip(self.bosch_wind_factor / wind_adjust, 0.1, 3.0))
if gas_pedal_force <= 0.0:
self.bosch_wind_factor = max(self.bosch_wind_factor, self.bosch_wind_factor_before_brake)
else:
self.bosch_wind_factor_before_brake = self.bosch_wind_factor
if gas_pedal_force >= self.params.BOSCH_ACCEL_MAX:
self.bosch_gas_factor = min(self.bosch_gas_factor, self.bosch_gas_factor_before_gasmax)
self.bosch_wind_factor = min(self.bosch_wind_factor, self.bosch_wind_factor_before_gasmax)
else:
self.bosch_gas_factor_before_gasmax = self.bosch_gas_factor
self.bosch_wind_factor_before_gasmax = self.bosch_wind_factor
self.gas = float(np.interp(gas_pedal_force * self.bosch_gas_factor, self.params.BOSCH_GAS_LOOKUP_BP, self.params.BOSCH_GAS_LOOKUP_V))
self.gas = min(self.gas, max(60.0, self.bosch_last_gas + 60.0))
self.bosch_last_gas = self.gas
stopping = actuators.longControlState == LongCtrlState.stopping
self.stopping_counter = self.stopping_counter + 1 if stopping else 0
can_sends.extend(hondacan.create_acc_commands(self.packer, self.CAN, CC.enabled, CC.longActive, self.accel, self.gas,
self.stopping_counter, self.CP.carFingerprint))
can_sends.extend(
hondacan.create_acc_commands(self.packer, self.CAN, CC.enabled, CC.longActive, self.accel, self.gas, self.stopping_counter, self.CP.carFingerprint)
)
else:
apply_brake = np.clip(self.brake_last - wind_brake, 0.0, 1.0)
apply_brake = int(np.clip(apply_brake * self.params.NIDEC_BRAKE_MAX, 0, self.params.NIDEC_BRAKE_MAX - 1))
pump_on, self.last_pump_ts = brake_pump_hysteresis(apply_brake, self.apply_brake_last, self.last_pump_ts, ts)
pcm_override = True
can_sends.append(hondacan.create_brake_command(self.packer, self.CAN, apply_brake, pump_on,
pcm_override, pcm_cancel_cmd, alert_fcw,
self.CP.carFingerprint, CS.stock_brake))
can_sends.append(
hondacan.create_brake_command(
self.packer, self.CAN, apply_brake, pump_on, pcm_override, pcm_cancel_cmd, alert_fcw, self.CP.carFingerprint, CS.stock_brake
)
)
self.apply_brake_last = apply_brake
self.brake = apply_brake / self.params.NIDEC_BRAKE_MAX
if self.CP.enableGasInterceptorDEPRECATED:
gas_error = actuators.accel - CS.out.aEgo
if not CS.out.gasPressed and actuators.longControlState == LongCtrlState.pid:
if gas_error != 0.0 and gas > 0.0:
self.bosch_gas_factor = float(np.clip(self.bosch_gas_factor + gas_error / 150.0 * (gas * 4.8), 0.1, 3.0))
if gas_error != 0.0 and not CS.out.brakePressed and CS.out.vEgo > 0.0:
wind_adjust = 1.0 + (wind_brake * 4.8) / 1000.0
if gas_error > 0.0:
self.bosch_wind_factor = float(np.clip(self.bosch_wind_factor * wind_adjust, 0.1, 5.0))
else:
self.bosch_wind_factor = float(np.clip(self.bosch_wind_factor / wind_adjust, 0.1, 5.0))
if gas <= 0.0:
self.bosch_wind_factor = max(self.bosch_wind_factor, self.bosch_wind_factor_before_brake)
else:
self.bosch_wind_factor_before_brake = self.bosch_wind_factor
gas_mult = float(np.interp(CS.out.vEgo, [0.0, 10.0], [0.4, 1.0]))
if CC.longActive:
gas_interceptor_command = float(np.clip(
gas_mult * ((gas * self.bosch_gas_factor) - brake + (wind_brake * self.bosch_wind_factor * 3.0 / 4.0)),
0.0,
1.0,
))
idx = (self.frame // 2) % 0x10
can_sends.append(create_gas_interceptor_command(self.packer, gas_interceptor_command, idx))
# Send dashboard UI commands.
if self.frame % 10 == 0:
if self.CP.openpilotLongitudinalControl:
# On Nidec, this also controls longitudinal positive acceleration
can_sends.append(hondacan.create_acc_hud(self.packer, self.CAN.pt, self.CP, CC.enabled, pcm_speed, pcm_accel,
hud_control, hud_v_cruise, CS.is_metric, CS.acc_hud))
can_sends.append(
hondacan.create_acc_hud(self.packer, self.CAN.pt, self.CP, CC.enabled, pcm_speed, pcm_accel, hud_control, hud_v_cruise, CS.is_metric, CS.acc_hud)
)
steering_available = CS.out.cruiseState.available and CS.out.vEgo > self.CP.minSteerSpeed
reduced_steering = CS.out.steeringPressed
can_sends.extend(hondacan.create_lkas_hud(self.packer, self.CAN.lkas, self.CP, hud_control, CC.latActive,
steering_available, reduced_steering, alert_steer_required, CS.lkas_hud))
reduced_steering = filtered_steering_pressed
can_sends.extend(
hondacan.create_lkas_hud(
self.packer, self.CAN.lkas, self.CP, hud_control, CC.latActive, steering_available, reduced_steering, alert_steer_required, CS.lkas_hud
)
)
if self.CP.openpilotLongitudinalControl:
# TODO: combining with create_acc_hud block above will change message order and will need replay logs regenerated
@@ -237,7 +464,14 @@ class CarController(CarControllerBase):
can_sends.append(hondacan.create_legacy_brake_command(self.packer, self.CAN.pt))
if self.CP.carFingerprint not in HONDA_BOSCH:
self.speed = pcm_speed
self.gas = pcm_accel / self.params.NIDEC_GAS_MAX
if self.CP.enableGasInterceptorDEPRECATED:
self.gas = gas_interceptor_command
else:
self.gas = pcm_accel / self.params.NIDEC_GAS_MAX
if self.frame > 0 and self.frame % 6000 == 0:
self.param_store.put_float("HondaGasFactorParams", self.bosch_gas_factor)
self.param_store.put_float("HondaWindFactorParams", self.bosch_wind_factor)
new_actuators = actuators.as_builder()
new_actuators.speed = self.speed
+52 -22
View File
@@ -3,11 +3,11 @@ from collections import defaultdict
from cereal import custom
from opendbc.can import CANDefine, CANParser
from opendbc.car import Bus, create_button_events, structs
from opendbc.car import Bus, DT_CTRL, create_button_events, structs
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.honda.hondacan import CanBus
from opendbc.car.honda.values import CAR, DBC, STEER_THRESHOLD, HONDA_BOSCH, HONDA_BOSCH_ALT_RADAR, HONDA_BOSCH_CANFD, \
HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_RADARLESS, \
HONDA_NIDEC_ALT_SCM_MESSAGES, HONDA_BOSCH_RADARLESS, HONDA_BOSCH_TJA_CONTROL, \
HondaFlags, CruiseButtons, CruiseSettings, GearShifter, CarControllerParams, HondaStarPilotFlags
from opendbc.car.interfaces import CarStateBase
@@ -54,13 +54,19 @@ class CarState(CarStateBase):
self.brake_switch_active = False
self.low_speed_alert = False
self.dynamic_v_cruise_units = self.CP.carFingerprint in (HONDA_BOSCH_RADARLESS | HONDA_BOSCH_ALT_RADAR | HONDA_BOSCH_CANFD)
self.dynamic_v_cruise_units = self.CP.carFingerprint in (HONDA_BOSCH_RADARLESS | HONDA_BOSCH_ALT_RADAR |
HONDA_BOSCH_TJA_CONTROL | HONDA_BOSCH_CANFD)
self.cruise_setting = 0
self.v_cruise_pcm_prev = 0
# When available we use cp.vl["CAR_SPEED"]["ROUGH_CAR_SPEED_2"] to populate vEgoCluster
# However, on cars without a digital speedometer this is not always present (HRV, FIT, CRV 2016, ILX and RDX)
self.dash_speed_seen = False
self.is_metric = False
self.v_cruise_factor = 1.
self.initial_accFault_cleared = False
self.initial_accFault_cleared_timer = int(10 / DT_CTRL)
def update(self, can_parsers, starpilot_toggles) -> structs.CarState:
cp = can_parsers[Bus.pt]
@@ -81,18 +87,19 @@ class CarState(CarStateBase):
self.cruise_buttons = cp.vl["SCM_BUTTONS"]["CRUISE_BUTTONS"]
# used for car hud message
self.is_metric = not cp.vl["CAR_SPEED"]["IMPERIAL_UNIT"]
self.is_metric = self.CP.carFingerprint in (CAR.HONDA_ODYSSEY_TWN,) or not cp.vl["CAR_SPEED"]["IMPERIAL_UNIT"]
self.v_cruise_factor = CV.MPH_TO_MS if self.dynamic_v_cruise_units and not self.is_metric else CV.KPH_TO_MS
# ******************* parse out can *******************
# blend in transmission speed at low speed, since it has more low speed accuracy
# STANDSTILL->WHEELS_MOVING bit can be noisy around zero, so use XMISSION_SPEED
lowspeed_source = cp.vl["CAR_SPEED"]["CAR_SPEED"] if self.CP.carFingerprint == CAR.ACURA_INTEGRA else cp.vl["ENGINE_DATA"]["XMISSION_SPEED"]
v_wheel = sum([cp.vl["WHEEL_SPEEDS"][f"WHEEL_SPEED_{s}"] for s in ("FL", "FR", "RL", "RR")]) / 4.0 * CV.KPH_TO_MS
v_weight = float(np.interp(v_wheel, v_weight_bp, v_weight_v))
ret.vEgoRaw = (1. - v_weight) * cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] * CV.KPH_TO_MS * self.CP.wheelSpeedFactor + v_weight * v_wheel
ret.vEgoRaw = (1. - v_weight) * lowspeed_source * CV.KPH_TO_MS * self.CP.wheelSpeedFactor + v_weight * v_wheel
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.standstill = cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] < 1e-5
ret.standstill = lowspeed_source < 1e-5
# doorOpen is true if we can find any door open, but signal locations vary, and we may only see the driver's door
# TODO: Test the eight Nidec cars without SCM signals for driver's door state, may be able to consolidate further
@@ -107,7 +114,8 @@ class CarState(CarStateBase):
ret.seatbeltUnlatched = bool(cp.vl["SEATBELT_STATUS"]["SEATBELT_DRIVER_LAMP"] or not cp.vl["SEATBELT_STATUS"]["SEATBELT_DRIVER_LATCHED"])
steer_status = self.steer_status_values[cp.vl["STEER_STATUS"]["STEER_STATUS"]]
ret.steerFaultPermanent = steer_status not in ("NORMAL", "NO_TORQUE_ALERT_1", "NO_TORQUE_ALERT_2", "LOW_SPEED_LOCKOUT", "TMP_FAULT")
ret.steerFaultPermanent = steer_status not in ("NORMAL", "NO_TORQUE_ALERT_1", "NO_TORQUE_ALERT_2", "LOW_SPEED_LOCKOUT", "TJA_LOW_SPEED_LOCKOUT",
"TMP_FAULT")
if self.CP.carFingerprint in HONDA_BOSCH_ALT_RADAR:
# TODO: See if this logic works for all other Honda
min_steer_speed = max(CarControllerParams.STEER_GLOBAL_MIN_SPEED, self.CP.minSteerSpeed)
@@ -117,7 +125,11 @@ class CarState(CarStateBase):
# LOW_SPEED_LOCKOUT is not worth a warning
# NO_TORQUE_ALERT_2 can be caused by bump or steering nudge from driver
# FIXME: the stock camera stops steering on NO_TORQUE_ALERT_1
ret.steerFaultTemporary = steer_status not in ("NORMAL", "LOW_SPEED_LOCKOUT", "NO_TORQUE_ALERT_2")
ret.steerFaultTemporary = steer_status not in ("NORMAL", "LOW_SPEED_LOCKOUT", "TJA_LOW_SPEED_LOCKOUT", "NO_TORQUE_ALERT_2")
if self.CP.carFingerprint == CAR.ACURA_MDX_4G and steer_status == "TJA_LOW_SPEED_LOCKOUT":
ret.steerFaultPermanent = False
ret.steerFaultTemporary = False
# All Honda EPS cut off slightly above standstill, some much higher
# Don't alert in the near-standstill range, but alert for per-vehicle configured minimums above that
@@ -128,22 +140,23 @@ class CarState(CarStateBase):
self.low_speed_alert = False
ret.lowSpeedAlert = self.low_speed_alert
if self.CP.carFingerprint in HONDA_BOSCH_RADARLESS:
if self.CP.carFingerprint not in HONDA_BOSCH:
ret.carFaultedNonCritical = bool(cp_cam.vl["ACC_HUD"]["ACC_PROBLEM"] or cp_cam.vl["LKAS_HUD"]["LKAS_PROBLEM"])
elif self.CP.carFingerprint in HONDA_BOSCH_RADARLESS:
ret.accFaulted = bool(cp.vl["CRUISE_FAULT_STATUS"]["CRUISE_FAULT"])
else:
if self.CP.openpilotLongitudinalControl:
elif self.CP.openpilotLongitudinalControl:
if self.CP.carFingerprint in (HONDA_BOSCH_CANFD | HONDA_BOSCH_TJA_CONTROL) and (self.CP.flags & HondaFlags.BOSCH_ALT_BRAKE):
ret.accFaulted = bool(cp.vl["BRAKE_MODULE"]["CRUISE_FAULT"])
else:
ret.accFaulted = bool(cp.vl[self.brake_error_msg]["BRAKE_ERROR_1"] or cp.vl[self.brake_error_msg]["BRAKE_ERROR_2"])
# Log non-critical stock ACC/LKAS faults if Nidec (camera)
if self.CP.carFingerprint not in HONDA_BOSCH:
ret.carFaultedNonCritical = bool(cp_cam.vl["ACC_HUD"]["ACC_PROBLEM"] or cp_cam.vl["LKAS_HUD"]["LKAS_PROBLEM"])
ret.espDisabled = cp.vl["VSA_STATUS"]["ESP_DISABLED"] != 0
self.dash_speed_seen = self.dash_speed_seen or cp.vl["CAR_SPEED"]["ROUGH_CAR_SPEED_2"] > 1e-3
if self.dash_speed_seen:
conversion = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
ret.vEgoCluster = cp.vl["CAR_SPEED"]["ROUGH_CAR_SPEED_2"] * conversion
if self.CP.carFingerprint not in (CAR.HONDA_ODYSSEY_TWN,):
self.dash_speed_seen = self.dash_speed_seen or cp.vl["CAR_SPEED"]["ROUGH_CAR_SPEED_2"] > 1e-3
if self.dash_speed_seen:
conversion = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
ret.vEgoCluster = cp.vl["CAR_SPEED"]["ROUGH_CAR_SPEED_2"] * conversion
ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEER_ANGLE"]
ret.steeringRateDeg = cp.vl["STEERING_SENSORS"]["STEER_ANGLE_RATE"]
@@ -154,12 +167,16 @@ class CarState(CarStateBase):
ret.parkingBrake = bool(cp.vl[self.car_state_scm_msg]["PARKING_BRAKE_ON"])
if self.CP.transmissionType == TransmissionType.manual:
ret.gearShifter = GearShifter.reverse if bool(cp.vl["SCM_FEEDBACK"]["REVERSE_LIGHT"]) else GearShifter.drive
ret.gearShifter = GearShifter.reverse if bool(cp.vl[self.car_state_scm_msg]["REVERSE_LIGHT"]) else GearShifter.drive
else:
gear_position = self.shifter_values.get(cp.vl[self.gearbox_msg]["GEAR_SHIFTER"], None)
ret.gearShifter = self.parse_gear_shifter(gear_position)
ret.gasPressed = cp.vl["POWERTRAIN_DATA"]["PEDAL_GAS"] > 1e-5
if self.CP.enableGasInterceptorDEPRECATED:
gas = (cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS"] + cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS2"]) / 2.
ret.gasPressed = gas > 492
else:
ret.gasPressed = cp.vl["POWERTRAIN_DATA"]["PEDAL_GAS"] > 1e-5
ret.steeringTorque = cp.vl["STEER_STATUS"]["STEER_TORQUE_SENSOR"]
ret.steeringPressed = abs(ret.steeringTorque) > STEER_THRESHOLD.get(self.CP.carFingerprint, 1200)
@@ -200,6 +217,16 @@ class CarState(CarStateBase):
ret.cruiseState.enabled = cp.vl["POWERTRAIN_DATA"]["ACC_STATUS"] != 0
ret.cruiseState.available = bool(cp.vl[self.car_state_scm_msg]["MAIN_ON"])
if ret.accFaulted:
if self.CP.carFingerprint in HONDA_BOSCH and not self.initial_accFault_cleared:
ret.accFaulted = False
ret.cruiseState.available = False
elif self.initial_accFault_cleared_timer == 0:
self.initial_accFault_cleared = True
if self.initial_accFault_cleared_timer > 0:
self.initial_accFault_cleared_timer -= 1
# Gets rid of Pedal Grinding noise when brake is pressed at slow speeds for some models
if self.CP.carFingerprint in (CAR.HONDA_PILOT, CAR.HONDA_RIDGELINE):
if ret.brake > 0.1:
@@ -218,7 +245,7 @@ class CarState(CarStateBase):
ret.stockFcw = cp_cam.vl["BRAKE_COMMAND"]["FCW"] != 0
self.acc_hud = cp_cam.vl["ACC_HUD"]
self.stock_brake = cp_cam.vl["BRAKE_COMMAND"]
if self.CP.carFingerprint in HONDA_BOSCH_RADARLESS:
if self.CP.carFingerprint in (HONDA_BOSCH_RADARLESS | HONDA_BOSCH_CANFD):
self.lkas_hud = cp_cam.vl["LKAS_HUD"]
if self.CP.enableBsm:
@@ -235,6 +262,9 @@ class CarState(CarStateBase):
fp_ret = custom.StarPilotCarState.new_message()
fp_ret.dashboardSpeedLimit = calculate_speed_limit(self.CP, self.FPCP, cp, cp_cam)
if self.FPCP.flags & HondaStarPilotFlags.HAS_CAMERA_MESSAGES:
sign_bus = cp if (self.CP.carFingerprint in (HONDA_BOSCH - HONDA_BOSCH_RADARLESS - HONDA_BOSCH_CANFD)) else cp_cam
fp_ret.dashboardStopSign = 1 if sign_bus.vl["CAMERA_MESSAGES"]["ROAD_SIGN"] == 89 else 0
return ret, fp_ret
@@ -84,6 +84,7 @@ FW_VERSIONS = {
],
(Ecu.eps, 0x18da30f1, None): [
b'39990-TBX-H120\x00\x00',
b'39990-TVA,A150\x00\x00',
b'39990-TVA-A140\x00\x00',
b'39990-TVA-A150\x00\x00',
b'39990-TVA-A160\x00\x00',
@@ -142,6 +143,29 @@ FW_VERSIONS = {
b'38897-TWD-J020\x00\x00',
],
},
CAR.HONDA_CLARITY: {
(Ecu.shiftByWire, 0x18da0bf1, None): [
b'54008-TRW-A910\x00\x00',
],
(Ecu.vsa, 0x18da28f1, None): [
b'57114-TRW-A010\x00\x00',
b'57114-TRW-A020\x00\x00',
],
(Ecu.eps, 0x18da30f1, None): [
b'39990-TRW-A020\x00\x00',
b'39990-TRW,A020\x00\x00',
],
(Ecu.srs, 0x18da53f1, None): [
b'77959-TRW-A210\x00\x00',
b'77959-TRW-A220\x00\x00',
],
(Ecu.gateway, 0x18daeff1, None): [
b'38897-TRW-A010\x00\x00',
],
(Ecu.fwdRadar, 0x18dab0f1, None): [
b'36161-TRW-A110\x00\x00',
],
},
CAR.HONDA_CIVIC: {
(Ecu.transmission, 0x18da1ef1, None): [
b'28101-5CG-A040\x00\x00',
@@ -163,6 +187,7 @@ FW_VERSIONS = {
b'57114-TEA-Q220\x00\x00',
],
(Ecu.eps, 0x18da30f1, None): [
b'39990-TBA,A030\x00\x00',
b'39990-TBA-A030\x00\x00',
b'39990-TBG-A030\x00\x00',
b'39990-TEA-T020\x00\x00',
@@ -230,11 +255,14 @@ FW_VERSIONS = {
b'57114-TGL-G330\x00\x00',
],
(Ecu.eps, 0x18da30f1, None): [
b'39990-TBA-C120\x00\x00',
b'39990-TBA-C020\x00\x00',
b'39990-TBA-C120\x00\x00',
b'39990-TEA-T330\x00\x00',
b'39990-TEA-T820\x00\x00',
b'39990-TEZ-T020\x00\x00',
b'39990-TGG,A020\x00\x00',
b'39990-TGG,A120\x00\x00',
b'39990-TGG-A020\x00\x00',
b'39990-TGG-A120\x00\x00',
b'39990-TGG-J510\x00\x00',
@@ -368,6 +396,7 @@ FW_VERSIONS = {
b'57114-TMC-Z050\x00\x00',
],
(Ecu.eps, 0x18da30f1, None): [
b'39990-TLA,A040\x00\x00',
b'39990-TLA-A040\x00\x00',
b'39990-TLA-A110\x00\x00',
b'39990-TLA-A220\x00\x00',
+5 -4
View File
@@ -167,18 +167,19 @@ def create_acc_hud(packer, bus, CP, enabled, pcm_speed, pcm_accel, hud_control,
def create_lkas_hud(packer, bus, CP, hud_control, lat_active, steering_available, reduced_steering, alert_steer_required, lkas_hud):
commands = []
lanes_visible = bool(hud_control.lanesVisible or lat_active)
lkas_hud_values = {
'LKAS_READY': 1,
'LKAS_STATE_CHANGE': 1,
'STEERING_REQUIRED': alert_steer_required,
'SOLID_LANES': hud_control.lanesVisible,
'SOLID_LANES': lanes_visible,
'BEEP': 0,
}
if CP.carFingerprint in (HONDA_BOSCH_RADARLESS | HONDA_BOSCH_CANFD):
lkas_hud_values['LANE_LINES'] = 3
lkas_hud_values['DASHED_LANES'] = hud_control.lanesVisible
lkas_hud_values['DASHED_LANES'] = lanes_visible
# car likely needs to see LKAS_PROBLEM fall within a specific time frame, so forward from camera
# TODO: needed for Bosch CAN FD?
@@ -192,8 +193,8 @@ def create_lkas_hud(packer, bus, CP, hud_control, lat_active, steering_available
# New HUD concept for selected Bosch cars, overwrites some of the above
# TODO: make global across all Honda if feedback is favorable
if CP.carFingerprint in HONDA_BOSCH_ALT_RADAR:
lkas_hud_values['DASHED_LANES'] = steering_available
lkas_hud_values['SOLID_LANES'] = lat_active
lkas_hud_values['DASHED_LANES'] = bool(steering_available or lanes_visible)
lkas_hud_values['SOLID_LANES'] = lanes_visible
lkas_hud_values['LKAS_PROBLEM'] = lat_active and reduced_steering
if CP.flags & HondaFlags.BOSCH_EXT_HUD and not CP.openpilotLongitudinalControl:
+139 -17
View File
@@ -23,6 +23,8 @@ class CarInterface(CarInterfaceBase):
def get_pid_accel_limits(CP, current_speed, cruise_speed):
if CP.carFingerprint in HONDA_BOSCH:
return CarControllerParams.BOSCH_ACCEL_MIN, CarControllerParams.BOSCH_ACCEL_MAX
elif CP.enableGasInterceptorDEPRECATED:
return CarControllerParams.NIDEC_ACCEL_MIN, CarControllerParams.NIDEC_ACCEL_MAX
else:
# NIDECs don't allow acceleration near cruise_speed,
# so limit limits of pid to prevent windup
@@ -46,15 +48,17 @@ class CarInterface(CarInterfaceBase):
# Disable the radar and let openpilot control longitudinal
# WARNING: THIS DISABLES AEB!
# If Bosch radarless, this blocks ACC messages from the camera
# TODO: get radar disable working on Bosch CANFD
ret.alphaLongitudinalAvailable = candidate not in HONDA_BOSCH_CANFD
ret.openpilotLongitudinalControl = alpha_long and (candidate not in HONDA_BOSCH_CANFD)
ret.alphaLongitudinalAvailable = True
ret.openpilotLongitudinalControl = alpha_long
ret.pcmCruise = not ret.openpilotLongitudinalControl
else:
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.hondaNidec)]
ret.openpilotLongitudinalControl = True
ret.pcmCruise = True
ret.enableGasInterceptorDEPRECATED = 0x201 in fingerprint[CAN.pt]
if ret.enableGasInterceptorDEPRECATED:
ret.pcmCruise = False
if candidate == CAR.HONDA_CRV_5G:
ret.enableBsm = 0x12f8bfa7 in fingerprint[CAN.radar]
@@ -80,6 +84,7 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
ret.lateralTuning.pid.kf = 0.00006 # conservative feed-forward
ret.steerActuatorDelay = 0.1
ret.stoppingDecelRate = 0.3
if candidate in HONDA_BOSCH:
ret.longitudinalActuatorDelay = 0.5 # s
@@ -90,14 +95,32 @@ class CarInterface(CarInterfaceBase):
ret.longitudinalTuning.kiBP = [0., 5., 35.]
ret.longitudinalTuning.kiV = [1.2, 0.8, 0.5]
# Disable control if EPS mod detected
eps_modified = False
for fw in car_fw:
if fw.ecu == "eps" and b"," in fw.fwVersion:
ret.dashcamOnly = True
eps_modified = True
if eps_modified:
ret.flags |= HondaFlags.EPS_MODIFIED.value
if candidate == CAR.HONDA_CITY_7G:
ret.vEgoStopping = 2.0
ret.vEgoStarting = ret.vEgoStopping
ret.stoppingDecelRate = 0.3
if candidate == CAR.HONDA_CIVIC:
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560], [0, 2560]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[1.1], [0.33]]
if eps_modified:
# stock request input values: 0x0000, 0x00DE, 0x014D, 0x01EF, 0x0290, 0x0377, 0x0454, 0x0610, 0x06EE
# stock request output values: 0x0000, 0x0917, 0x0DC5, 0x1017, 0x119F, 0x140B, 0x1680, 0x1680, 0x1680
# modified request output values: 0x0000, 0x0917, 0x0DC5, 0x1017, 0x119F, 0x140B, 0x1680, 0x2880, 0x3180
# stock filter output values: 0x009F, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108
# modified filter output values: 0x009F, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0400, 0x0480
# note: max request allowed is 4096, but request is capped at 3840 in firmware, so modifications result in 2x max
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560, 8000], [0, 2560, 3840]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.3], [0.1]]
else:
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560], [0, 2560]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[1.1], [0.33]]
elif candidate in (CAR.HONDA_CIVIC_BOSCH, CAR.HONDA_CIVIC_BOSCH_DIESEL):
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
@@ -112,7 +135,17 @@ class CarInterface(CarInterfaceBase):
elif candidate == CAR.HONDA_ACCORD:
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
if eps_modified:
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.3], [0.09]]
else:
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
if ret.transmissionType == TransmissionType.manual:
CarControllerParams.BOSCH_GAS_LOOKUP_BP = [-0.2, 2.0]
elif candidate == CAR.HONDA_ACCORD_11G:
ret.steerActuatorDelay = 0.22
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560, 5200], [0, 2560, 12747]]
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.ACURA_ILX:
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]] # TODO: determine if there is a dead zone at the top end
@@ -124,8 +157,15 @@ class CarInterface(CarInterfaceBase):
ret.wheelSpeedFactor = 1.025
elif candidate == CAR.HONDA_CRV_5G:
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.64], [0.192]]
if eps_modified:
# stock request input values: 0x0000, 0x00DB, 0x01BB, 0x0296, 0x0377, 0x0454, 0x0532, 0x0610, 0x067F
# stock request output values: 0x0000, 0x0500, 0x0A15, 0x0E6D, 0x1100, 0x1200, 0x129A, 0x134D, 0x1400
# modified request output values: 0x0000, 0x0500, 0x0A15, 0x0E6D, 0x1100, 0x1200, 0x1ACD, 0x239A, 0x2800
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560, 10000], [0, 2560, 3840]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.21], [0.07]]
else:
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.64], [0.192]]
ret.wheelSpeedFactor = 1.025
elif candidate == CAR.HONDA_CRV_HYBRID:
@@ -133,6 +173,13 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
ret.wheelSpeedFactor = 1.025
elif candidate == CAR.HONDA_CRV_6G:
ret.steerActuatorDelay = 0.15
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 5100], [0, 5100]]
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
if ret.flags & HondaFlags.HYBRID:
CarControllerParams.BOSCH_GAS_LOOKUP_BP = [-0.3, 2.0]
elif candidate == CAR.HONDA_FIT:
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.05]]
@@ -148,22 +195,52 @@ class CarInterface(CarInterfaceBase):
ret.wheelSpeedFactor = 1.025
else:
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]] # TODO: can probably use some tuning
# The 3G HR-V settles more cleanly in lead follow when planner delay
# better matches its stronger immediate longitudinal response.
ret.longitudinalActuatorDelay = 0.4
elif candidate == CAR.HONDA_CLARITY:
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560], [0, 2560]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
ret.stopAccel = 0.0
elif candidate == CAR.ACURA_RDX:
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 1000], [0, 1000]] # TODO: determine if there is a dead zone at the top end
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
elif candidate == CAR.ACURA_RDX_3G:
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]]
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4095], [0, 4095]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.2], [0.06]]
CarControllerParams.BOSCH_GAS_LOOKUP_V = [0, 2200]
elif candidate == CAR.ACURA_RDX_3G_MMR:
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]]
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
CarControllerParams.BOSCH_GAS_LOOKUP_V = [0, 2000]
if not ret.openpilotLongitudinalControl:
ret.minSteerSpeed = 70. * CV.KPH_TO_MS
elif candidate == CAR.HONDA_ODYSSEY:
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.28], [0.08]]
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
elif candidate == CAR.HONDA_PILOT:
elif candidate == CAR.HONDA_ODYSSEY_TWN:
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.28], [0.08]]
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 32767], [0, 32767]]
elif candidate in (CAR.HONDA_PILOT, CAR.HONDA_PILOT_4G):
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]]
ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kpV = [[0, 10], [0.05, 0.5]]
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kiV = [[0, 10], [0.0125, 0.125]]
elif candidate == CAR.ACURA_MDX_4G:
ret.steerActuatorDelay = 0.15
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560, 4209], [0, 2560, 9150]]
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.ACURA_MDX_4G_MMR:
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560, 4920], [0, 2560, 12000]]
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.HONDA_RIDGELINE:
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
@@ -173,7 +250,7 @@ class CarInterface(CarInterfaceBase):
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]]
elif candidate == CAR.HONDA_E:
elif candidate in (CAR.HONDA_E, CAR.HONDA_E_ADVANCE):
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.6], [0.18]] # TODO: can probably use some tuning
@@ -189,19 +266,48 @@ class CarInterface(CarInterfaceBase):
# When using stock ACC, the radar intercepts and filters steering commands the EPS would otherwise accept
ret.minSteerSpeed = 70. * CV.KPH_TO_MS
elif candidate == CAR.ACURA_TLX_2G_MMR:
ret.steerActuatorDelay = 0.15
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]]
ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kpV = [[0, 10], [0.05, 0.5]]
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kiV = [[0, 10], [0.0125, 0.125]]
elif candidate in (CAR.HONDA_FIT_4G,):
ret.steerActuatorDelay = 0.15
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]]
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.ACURA_INTEGRA:
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.8], [0.24]]
elif candidate == CAR.ACURA_ADX:
ret.steerActuatorDelay = 0.15
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 5000], [0, 5000]]
ret.lateralTuning.pid.kpBP, ret.lateralTuning.pid.kpV = [[0, 10], [0.05, 0.5]]
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kiV = [[0, 10], [0.0125, 0.125]]
elif candidate == CAR.HONDA_PASSPORT_4G:
ret.steerActuatorDelay = 0.15
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560, 5120], [0, 2560, 12789]]
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
else:
ret.steerActuatorDelay = 0.15
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 2560], [0, 2560]]
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
# These cars use alternate user brake msg (0x1BE)
if 0x1BE in fingerprint[CAN.pt] and candidate in (CAR.HONDA_ACCORD, CAR.HONDA_HRV_3G, *HONDA_BOSCH_CANFD):
if 0x1BE in fingerprint[CAN.pt] and candidate in (CAR.HONDA_ACCORD, CAR.HONDA_HRV_3G, CAR.ACURA_RDX_3G, CAR.ACURA_MDX_4G,
CAR.ACURA_ADX, *HONDA_BOSCH_CANFD):
ret.flags |= HondaFlags.BOSCH_ALT_BRAKE.value
if ret.flags & HondaFlags.BOSCH_ALT_BRAKE:
ret.safetyConfigs[-1].safetyParam |= HondaSafetyFlags.ALT_BRAKE.value
if candidate in HONDA_NIDEC_ALT_SCM_MESSAGES:
ret.safetyConfigs[-1].safetyParam |= HondaSafetyFlags.NIDEC_ALT.value
if ret.enableGasInterceptorDEPRECATED:
ret.safetyConfigs[-1].safetyParam |= HondaSafetyFlags.GAS_INTERCEPTOR.value
if ret.openpilotLongitudinalControl and candidate in HONDA_BOSCH:
ret.safetyConfigs[-1].safetyParam |= HondaSafetyFlags.BOSCH_LONG.value
if candidate in HONDA_BOSCH_RADARLESS:
@@ -212,8 +318,24 @@ class CarInterface(CarInterfaceBase):
# min speed to enable ACC. if car can do stop and go, then set enabling speed
# to a negative value, so it won't matter. Otherwise, add 0.5 mph margin to not
# conflict with PCM acc
ret.autoResumeSng = candidate in (HONDA_BOSCH | {CAR.HONDA_CIVIC})
ret.minEnableSpeed = -1. if ret.autoResumeSng else 25.51 * CV.MPH_TO_MS
if candidate == CAR.HONDA_FIT_4G and not ret.openpilotLongitudinalControl:
ret.autoResumeSng = False
elif ret.transmissionType == TransmissionType.manual and not ret.openpilotLongitudinalControl:
ret.autoResumeSng = False
else:
ret.autoResumeSng = candidate in (HONDA_BOSCH | {CAR.HONDA_CIVIC, CAR.HONDA_CLARITY}) or ret.enableGasInterceptorDEPRECATED
if ret.autoResumeSng:
ret.minEnableSpeed = -1.
elif candidate == CAR.HONDA_ODYSSEY_TWN:
ret.minEnableSpeed = 19. * CV.MPH_TO_MS
elif candidate == CAR.HONDA_FIT_4G:
ret.minEnableSpeed = 30. * CV.KPH_TO_MS
else:
ret.minEnableSpeed = 25.51 * CV.MPH_TO_MS
if candidate == CAR.HONDA_PILOT_4G:
CarControllerParams.BOSCH_GAS_LOOKUP_V = [0, 2200]
ret.steerLimitTimer = 0.8
ret.radarDelay = 0.1
@@ -1,12 +1,44 @@
import re
from types import SimpleNamespace
import pytest
from opendbc.car import structs
from opendbc.car.structs import CarParams
from opendbc.car import gen_empty_fingerprint
from opendbc.car.honda.interface import CarInterface
from opendbc.car.honda.carcontroller import (
CarController,
get_civic_bosch_modified_steering_pressed,
get_civic_bosch_modified_torque_lpf_tau,
get_honda_bosch_wind_brake_mps2,
update_honda_bosch_live_learning,
)
from opendbc.car.honda.hondacan import create_lkas_hud
from opendbc.car.honda.fingerprints import FW_VERSIONS
from opendbc.car.honda.values import HONDA_BOSCH, HONDA_BOSCH_TJA_CONTROL
from opendbc.car.honda.values import CAR, DBC, HONDA_BOSCH, HONDA_BOSCH_TJA_CONTROL, CarControllerParams, HondaFlags, HondaSafetyFlags, \
HondaStarPilotFlags
HONDA_FW_VERSION_RE = br"[A-Z0-9]{5}-[A-Z0-9]{3}(-|,)[A-Z0-9]{4}(\x00){2}$"
HONDA_FW_VERSION_RE = rb"[A-Z0-9]{5}-[A-Z0-9]{3}(-|,)[A-Z0-9]{4}(\x00){2}$"
def get_test_toggles() -> SimpleNamespace:
return SimpleNamespace(always_on_lateral_lkas=False, force_torque_controller=False, nnff=False, nnff_lite=False)
class TestHondaFingerprint:
def test_honda_lkas_hud_shows_lane_lines_when_lateral_only_is_active(self):
class FakePacker:
@staticmethod
def make_can_msg(name, bus, values):
return name, bus, values
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC_BOSCH)
hud_control = SimpleNamespace(lanesVisible=False)
cmds = create_lkas_hud(FakePacker(), 0, CP, hud_control, True, True, False, False, {})
assert cmds[0][2]["SOLID_LANES"] is True
def test_fw_version_format(self):
# Asserts all FW versions follow an expected format
for fw_by_ecu in FW_VERSIONS.values():
@@ -16,3 +48,230 @@ class TestHondaFingerprint:
def test_tja_bosch_only(self):
assert set(HONDA_BOSCH_TJA_CONTROL).issubset(set(HONDA_BOSCH)), "Nidec car found in TJA control list"
def test_modified_civic_torque_lpf_tau_reacts_to_sign_change(self):
assert get_civic_bosch_modified_torque_lpf_tau(0.7, -0.1, 25.0) == 0.10
assert get_civic_bosch_modified_torque_lpf_tau(0.02, -0.01, 8.0) == 0.28
assert get_civic_bosch_modified_torque_lpf_tau(0.02, 0.01, 12.0) == 0.28
assert get_civic_bosch_modified_torque_lpf_tau(0.02, 0.01, 20.0) == 0.20
assert get_civic_bosch_modified_torque_lpf_tau(0.02, 0.01, 25.0) == 0.16
assert get_civic_bosch_modified_torque_lpf_tau(0.30, 0.0, 12.0) == 0.16
assert get_civic_bosch_modified_torque_lpf_tau(0.30, 0.0, 20.0) == 0.13
def test_modified_civic_steering_pressed_filter_rejects_short_same_direction_spikes(self):
filter_s, pressed = get_civic_bosch_modified_steering_pressed(True, 1500.0, 0.8, 0.01, False)
assert not pressed
assert filter_s > 0.01
filter_s = 0.79
filter_s, pressed = get_civic_bosch_modified_steering_pressed(True, 1500.0, 0.8, filter_s, False)
assert not pressed
filter_s = 0.80
filter_s, pressed = get_civic_bosch_modified_steering_pressed(True, 1500.0, 0.8, filter_s, False)
assert pressed
def test_modified_civic_steering_pressed_filter_allows_opposing_driver_torque_quickly(self):
filter_s, pressed = get_civic_bosch_modified_steering_pressed(True, -1500.0, 0.8, 0.10, False)
assert pressed
def test_honda_bosch_wind_brake_curve_matches_reference_points(self):
assert get_honda_bosch_wind_brake_mps2(0.0) == pytest.approx(0.0)
assert get_honda_bosch_wind_brake_mps2(22.4) == pytest.approx(0.136)
assert get_honda_bosch_wind_brake_mps2(40.2) == pytest.approx(0.441)
def test_honda_bosch_live_learning_increases_factors_when_under_accelerating(self):
gas_factor, wind_factor, wind_factor_before_brake = update_honda_bosch_live_learning(
1.0,
1.0,
0.0,
desired_accel=1.0,
actual_accel=0.5,
gas_pedal_force=1.2,
wind_brake_mps2=0.136,
brake_pressed=False,
v_ego=22.4,
)
assert gas_factor == pytest.approx(1.012)
assert wind_factor == pytest.approx(1.000136)
assert wind_factor_before_brake == pytest.approx(wind_factor)
def test_honda_bosch_live_learning_restores_wind_factor_while_braking(self):
gas_factor, wind_factor, wind_factor_before_brake = update_honda_bosch_live_learning(
1.4,
1.1,
1.3,
desired_accel=-0.2,
actual_accel=0.0,
gas_pedal_force=-0.1,
wind_brake_mps2=0.136,
brake_pressed=True,
v_ego=22.4,
)
assert gas_factor == pytest.approx(1.4)
assert wind_factor == pytest.approx(1.3)
assert wind_factor_before_brake == pytest.approx(1.3)
def test_official_modified_eps_firmwares_restored(self):
assert b'39990-TVA,A150\x00\x00' in FW_VERSIONS[CAR.HONDA_ACCORD][(CarParams.Ecu.eps, 0x18DA30F1, None)]
assert b'39990-TBA,A030\x00\x00' in FW_VERSIONS[CAR.HONDA_CIVIC][(CarParams.Ecu.eps, 0x18DA30F1, None)]
assert b'39990-TBA-C120\x00\x00' in FW_VERSIONS[CAR.HONDA_CIVIC_BOSCH][(CarParams.Ecu.eps, 0x18DA30F1, None)]
assert b'39990-TGG,A020\x00\x00' in FW_VERSIONS[CAR.HONDA_CIVIC_BOSCH][(CarParams.Ecu.eps, 0x18DA30F1, None)]
assert b'39990-TLA,A040\x00\x00' in FW_VERSIONS[CAR.HONDA_CRV_5G][(CarParams.Ecu.eps, 0x18DA30F1, None)]
def test_modified_eps_candidates_keep_support_and_restore_upstream_tunes(self):
toggles = SimpleNamespace(force_torque_controller=False, nnff=False, nnff_lite=False)
civic_fw = [CarParams.CarFw(ecu=CarParams.Ecu.eps, fwVersion=b'39990-TBA,A030\x00\x00', address=0x18DA30F1, subAddress=0)]
civic_cp = CarInterface.get_params(CAR.HONDA_CIVIC, gen_empty_fingerprint(), civic_fw, False, False, False, toggles)
assert not civic_cp.dashcamOnly
assert civic_cp.flags & HondaFlags.EPS_MODIFIED
assert list(civic_cp.lateralParams.torqueBP) == [0, 2560, 8000]
assert list(civic_cp.lateralParams.torqueV) == [0, 2560, 3840]
assert list(civic_cp.lateralTuning.pid.kpV) == pytest.approx([0.3])
assert list(civic_cp.lateralTuning.pid.kiV) == pytest.approx([0.1])
civic_bosch_fw = [CarParams.CarFw(ecu=CarParams.Ecu.eps, fwVersion=b'39990-TGG,A020\x00\x00', address=0x18DA30F1, subAddress=0)]
civic_bosch_cp = CarInterface.get_params(CAR.HONDA_CIVIC_BOSCH, gen_empty_fingerprint(), civic_bosch_fw, False, False, False, toggles)
assert not civic_bosch_cp.dashcamOnly
assert civic_bosch_cp.flags & HondaFlags.EPS_MODIFIED
accord_fw = [CarParams.CarFw(ecu=CarParams.Ecu.eps, fwVersion=b'39990-TVA,A150\x00\x00', address=0x18DA30F1, subAddress=0)]
accord_cp = CarInterface.get_params(CAR.HONDA_ACCORD, gen_empty_fingerprint(), accord_fw, False, False, False, toggles)
assert not accord_cp.dashcamOnly
assert accord_cp.flags & HondaFlags.EPS_MODIFIED
assert list(accord_cp.lateralTuning.pid.kpV) == pytest.approx([0.3])
assert list(accord_cp.lateralTuning.pid.kiV) == pytest.approx([0.09])
crv_fw = [CarParams.CarFw(ecu=CarParams.Ecu.eps, fwVersion=b'39990-TLA,A040\x00\x00', address=0x18DA30F1, subAddress=0)]
crv_cp = CarInterface.get_params(CAR.HONDA_CRV_5G, gen_empty_fingerprint(), crv_fw, False, False, False, toggles)
assert not crv_cp.dashcamOnly
assert crv_cp.flags & HondaFlags.EPS_MODIFIED
assert list(crv_cp.lateralParams.torqueBP) == [0, 2560, 10000]
assert list(crv_cp.lateralParams.torqueV) == [0, 2560, 3840]
assert list(crv_cp.lateralTuning.pid.kpV) == pytest.approx([0.21])
assert list(crv_cp.lateralTuning.pid.kiV) == pytest.approx([0.07])
def test_modified_civic_bosch_keeps_official_support(self):
toggles = SimpleNamespace(force_torque_controller=False, nnff=False, nnff_lite=False)
car_fw = [CarParams.CarFw(ecu=CarParams.Ecu.eps, fwVersion=b'39990-TGG,A020\x00\x00', address=0x18DA30F1, subAddress=0)]
CP = CarInterface.get_params(CAR.HONDA_CIVIC_BOSCH, gen_empty_fingerprint(), car_fw, False, False, False, toggles)
assert not CP.dashcamOnly
assert CP.flags & HondaFlags.EPS_MODIFIED
assert CP.lateralTuning.which() == "torque"
def test_honda_clarity_supports_pid_and_torque_paths(self):
pid_toggles = SimpleNamespace(force_torque_controller=False, nnff=False, nnff_lite=False)
car_fw = [CarParams.CarFw(ecu=CarParams.Ecu.eps, fwVersion=b'39990-TRW,A020\x00\x00', address=0x18DA30F1, subAddress=0)]
pid_cp = CarInterface.get_params(CAR.HONDA_CLARITY, gen_empty_fingerprint(), car_fw, False, False, False, pid_toggles)
assert not pid_cp.dashcamOnly
assert pid_cp.flags & HondaFlags.EPS_MODIFIED
assert pid_cp.lateralTuning.which() == "pid"
assert list(pid_cp.lateralParams.torqueBP) == [0, 2560]
assert list(pid_cp.lateralParams.torqueV) == [0, 2560]
assert list(pid_cp.lateralTuning.pid.kpV) == pytest.approx([0.8])
assert list(pid_cp.lateralTuning.pid.kiV) == pytest.approx([0.24])
assert pid_cp.autoResumeSng
assert pid_cp.minEnableSpeed == pytest.approx(-1.0)
assert pid_cp.stopAccel == pytest.approx(0.0)
torque_toggles = SimpleNamespace(force_torque_controller=True, nnff=False, nnff_lite=False)
torque_cp = CarInterface.get_params(CAR.HONDA_CLARITY, gen_empty_fingerprint(), car_fw, False, False, False, torque_toggles)
assert torque_cp.lateralTuning.which() == "torque"
def test_canfd_bosch_alpha_long_is_available(self):
toggles = get_test_toggles()
CP = CarInterface.get_params(CAR.HONDA_PILOT_4G, gen_empty_fingerprint(), [], True, False, False, toggles)
assert CP.alphaLongitudinalAvailable
assert CP.openpilotLongitudinalControl
assert CP.safetyConfigs[-1].safetyParam & HondaSafetyFlags.BOSCH_CANFD
assert CP.safetyConfigs[-1].safetyParam & HondaSafetyFlags.BOSCH_LONG
def test_nidec_pedal_detection_enables_interceptor_path(self):
toggles = get_test_toggles()
fingerprint = gen_empty_fingerprint()
fingerprint[0][0x201] = 6
CP = CarInterface.get_params(CAR.HONDA_CIVIC, fingerprint, [], False, False, False, toggles)
accel_limits = CarInterface.get_pid_accel_limits(CP, current_speed=5.0, cruise_speed=12.0)
assert CP.enableGasInterceptorDEPRECATED
assert not CP.pcmCruise
assert accel_limits == (CarControllerParams.NIDEC_ACCEL_MIN, CarControllerParams.NIDEC_ACCEL_MAX)
def test_honda_camera_message_flag_uses_fingerprint_detection(self):
toggles = get_test_toggles()
fingerprint = gen_empty_fingerprint()
fingerprint[0][0x35E] = 8
CP = CarInterface.get_params(CAR.HONDA_ACCORD, fingerprint, [], True, False, False, toggles)
FPCP = CarInterface.get_starpilot_params(CAR.HONDA_ACCORD, fingerprint, [], CP, toggles)
assert FPCP.flags & HondaStarPilotFlags.HAS_CAMERA_MESSAGES
def test_honda_live_learning_params_reload(self, monkeypatch):
toggles = get_test_toggles()
class FakeParams:
def get_float(self, key, block=False, return_default=False, default=0.0):
if key == "HondaGasFactorParams":
return 1.25
if key == "HondaWindFactorParams":
return 0.85
return default
monkeypatch.setattr("opendbc.car.honda.carcontroller.Params", lambda: FakeParams())
CP = CarInterface.get_params(CAR.HONDA_ACCORD, gen_empty_fingerprint(), [], True, False, False, toggles)
controller = CarController(DBC[CP.carFingerprint], CP)
assert controller.bosch_gas_factor == pytest.approx(1.25)
assert controller.bosch_wind_factor == pytest.approx(0.85)
def test_honda_bosch_controller_does_not_deepen_planner_braking(self, monkeypatch):
toggles = get_test_toggles()
CP = CarInterface.get_params(CAR.HONDA_HRV_3G, gen_empty_fingerprint(), [], True, False, False, toggles)
controller = CarController(DBC[CP.carFingerprint], CP)
monkeypatch.setattr("opendbc.car.honda.carcontroller.hondacan.create_steering_control", lambda *args, **kwargs: (0, []))
monkeypatch.setattr("opendbc.car.honda.carcontroller.hondacan.create_acc_commands", lambda *args, **kwargs: [])
CC = structs.CarControl.new_message()
CC.enabled = True
CC.longActive = True
CC.latActive = False
CC.cruiseControl.cancel = False
CC.cruiseControl.resume = False
CC.hudControl.speedVisible = False
CC.hudControl.setSpeed = 0.0
CC.hudControl.visualAlert = structs.CarControl.HUDControl.VisualAlert.none
CC.actuators.accel = -0.3
CC.actuators.torque = 0.0
CC.actuators.longControlState = structs.CarControl.Actuators.LongControlState.pid
controller.frame = 2
CS = SimpleNamespace(
out=SimpleNamespace(vEgo=25.0, aEgo=1.5, steeringPressed=False, gasPressed=False, brakePressed=False),
v_cruise_factor=1.0,
)
new_actuators, _ = controller.update(CC.as_reader(), CS, 0, toggles)
assert new_actuators.accel == pytest.approx(-0.3)
def test_honda_hrv_3g_uses_matched_longitudinal_delay(self):
toggles = get_test_toggles()
hrv3g_cp = CarInterface.get_params(CAR.HONDA_HRV_3G, gen_empty_fingerprint(), [], True, False, False, toggles)
accord_cp = CarInterface.get_params(CAR.HONDA_ACCORD, gen_empty_fingerprint(), [], True, False, False, toggles)
assert hrv3g_cp.longitudinalActuatorDelay == pytest.approx(0.4)
assert accord_cp.longitudinalActuatorDelay == pytest.approx(0.5)
+114 -2
View File
@@ -56,6 +56,7 @@ class HondaSafetyFlags(IntFlag):
NIDEC_ALT = 4
RADARLESS = 8
BOSCH_CANFD = 16
GAS_INTERCEPTOR = 32
class HondaFlags(IntFlag):
@@ -79,6 +80,7 @@ class HondaFlags(IntFlag):
ALLOW_MANUAL_TRANS = 1024
HYBRID = 2048
BOSCH_TJA_CONTROL = 4096
EPS_MODIFIED = 8192
# Car button codes
@@ -246,6 +248,12 @@ class CAR(Platforms):
{Bus.pt: 'acura_rdx_2020_can_generated'},
flags=HondaFlags.BOSCH_ALT_BRAKE,
)
ACURA_RDX_3G_MMR = HondaBoschPlatformConfig(
[HondaCarDocs("Acura RDX 2022-26", "All", min_steer_speed=70. * CV.KPH_TO_MS)],
CarSpecs(mass=4079 * CV.LB_TO_KG, wheelbase=2.75, centerToFrontRatio=0.41, steerRatio=16.2),
{Bus.pt: 'acura_rdx_2020_can_generated'},
flags=HondaFlags.BOSCH_ALT_BRAKE | HondaFlags.BOSCH_ALT_RADAR,
)
HONDA_INSIGHT = HondaBoschPlatformConfig(
[HondaCarDocs("Honda Insight 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS)],
CarSpecs(mass=2987 * CV.LB_TO_KG, wheelbase=2.7, steerRatio=15.0, centerToFrontRatio=0.39, tireStiffnessFactor=0.82), # as spec
@@ -256,6 +264,11 @@ class CAR(Platforms):
CarSpecs(mass=3338.8 * CV.LB_TO_KG, wheelbase=2.5, centerToFrontRatio=0.5, steerRatio=16.71, tireStiffnessFactor=0.82),
{Bus.pt: 'acura_rdx_2020_can_generated'},
)
HONDA_E_ADVANCE = HondaBoschPlatformConfig(
[], # don't show in docs, base trim already in docs
CarSpecs(mass=1527, wheelbase=2.5, centerToFrontRatio=0.5, steerRatio=16.71, tireStiffnessFactor=0.82),
{Bus.pt: 'honda_e_advance_2020_can_generated'},
)
HONDA_PILOT_4G = HondaBoschCANFDPlatformConfig(
[HondaCarDocs("Honda Pilot 2023-25", "All")],
CarSpecs(mass=4660 * CV.LB_TO_KG, wheelbase=2.89, centerToFrontRatio=0.442, steerRatio=17.5),
@@ -264,6 +277,12 @@ class CAR(Platforms):
[HondaCarDocs("Honda Passport 2026", "All")],
CarSpecs(mass=4620 * CV.LB_TO_KG, wheelbase=2.89, centerToFrontRatio=0.442, steerRatio=18.5),
)
ACURA_MDX_4G = HondaBoschPlatformConfig(
[HondaCarDocs("Acura MDX 2022-24", "All", min_steer_speed=70. * CV.KPH_TO_MS)],
CarSpecs(mass=4788 * CV.LB_TO_KG, wheelbase=2.89, steerRatio=15.8, centerToFrontRatio=0.428),
{Bus.pt: 'honda_common_canfd_generated'},
flags=HondaFlags.BOSCH_ALT_RADAR | HondaFlags.BOSCH_TJA_CONTROL,
)
# mid-model refresh
ACURA_MDX_4G_MMR = HondaBoschCANFDPlatformConfig(
[HondaCarDocs("Acura MDX 2025", "All except Type S")],
@@ -281,6 +300,34 @@ class CAR(Platforms):
{Bus.pt: 'honda_civic_hatchback_ex_2017_can_generated'},
flags=HondaFlags.BOSCH_ALT_RADAR,
)
ACURA_TLX_2G_MMR = HondaBoschCANFDPlatformConfig(
[HondaCarDocs("Acura TLX 2024-25", "All")],
CarSpecs(mass=3990 * CV.LB_TO_KG, wheelbase=2.87, centerToFrontRatio=0.43, steerRatio=13.7),
)
HONDA_FIT_4G = HondaBoschPlatformConfig(
[
HondaCarDocs("Honda Fit (Taiwan) 2021", "All"),
HondaCarDocs("Honda Fit (Taiwan) 2024-25", "All"),
],
CarSpecs(mass=1229, wheelbase=2.53, steerRatio=19.7, centerToFrontRatio=0.39, minSteerSpeed=23. * CV.KPH_TO_MS),
{Bus.pt: 'honda_bosch_radarless_generated'},
flags=HondaFlags.BOSCH_RADARLESS,
)
ACURA_INTEGRA = HondaBoschPlatformConfig(
[
HondaCarDocs("Acura Integra 2023-26", "All"),
HondaCarDocs("Honda Prelude 2026", "All"),
],
CarSpecs(mass=3338.8 * CV.LB_TO_KG, wheelbase=2.5, centerToFrontRatio=0.5, steerRatio=16.71, tireStiffnessFactor=0.82),
{Bus.pt: 'honda_bosch_radarless_generated'},
flags=HondaFlags.BOSCH_RADARLESS,
)
ACURA_ADX = HondaBoschPlatformConfig(
[HondaCarDocs("Acura ADX 2025-26", "All")],
CarSpecs(mass=3578 * CV.LB_TO_KG, wheelbase=2.65, steerRatio=16.6, centerToFrontRatio=0.43),
{Bus.pt: 'honda_bosch_radarless_generated'},
flags=HondaFlags.BOSCH_RADARLESS,
)
# Nidec Cars
ACURA_ILX = HondaNidecPlatformConfig(
@@ -304,6 +351,12 @@ class CAR(Platforms):
radar_dbc_dict('honda_crv_touring_2016_can_generated'),
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES | HondaFlags.HAS_ALL_DOOR_STATES,
)
HONDA_CRV_SA = HondaNidecPlatformConfig(
[], # South Africa version of CRV Touring, don't show in docs
HONDA_CRV.specs,
radar_dbc_dict('acura_rdx_2018_can_generated'),
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES | HondaFlags.HAS_ALL_DOOR_STATES,
)
HONDA_FIT = HondaNidecPlatformConfig(
[HondaCarDocs("Honda Fit 2018-20", min_steer_speed=12. * CV.MPH_TO_MS)],
CarSpecs(mass=2644 * CV.LB_TO_KG, wheelbase=2.53, steerRatio=13.06, centerToFrontRatio=0.39, tireStiffnessFactor=0.75),
@@ -322,12 +375,27 @@ class CAR(Platforms):
radar_dbc_dict('acura_ilx_2016_can_generated'),
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
)
HONDA_CLARITY = HondaNidecPlatformConfig(
[HondaCarDocs("Honda Clarity 2018-21", "All", min_steer_speed=3. * CV.MPH_TO_MS)],
CarSpecs(mass=1838, wheelbase=2.75, centerToFrontRatio=0.4, steerRatio=16.5),
radar_dbc_dict('honda_clarity_hybrid_2018_can_generated'),
flags=HondaFlags.HAS_ALL_DOOR_STATES,
)
HONDA_ODYSSEY = HondaNidecPlatformConfig(
[HondaCarDocs("Honda Odyssey 2018-20")],
CarSpecs(mass=1900, wheelbase=3.0, steerRatio=14.35, centerToFrontRatio=0.41, tireStiffnessFactor=0.82),
radar_dbc_dict('honda_odyssey_exl_2018_generated'),
flags=HondaFlags.NIDEC_ALT_PCM_ACCEL | HondaFlags.HAS_ALL_DOOR_STATES,
)
HONDA_ODYSSEY_TWN = HondaNidecPlatformConfig(
[
HondaCarDocs("Honda Odyssey (Taiwan) 2018-19"),
HondaCarDocs("Honda Odyssey (Singapore) 2021"),
],
CarSpecs(mass=1865, wheelbase=2.9, steerRatio=14.35, centerToFrontRatio=0.44, tireStiffnessFactor=0.82),
radar_dbc_dict('honda_odyssey_twn_2018_generated'),
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
)
ACURA_RDX = HondaNidecPlatformConfig(
[HondaCarDocs("Acura RDX 2016-18", "AcuraWatch Plus or Advance Package", min_steer_speed=12. * CV.MPH_TO_MS)],
CarSpecs(mass=3925 * CV.LB_TO_KG, wheelbase=2.68, steerRatio=15.0, centerToFrontRatio=0.38, tireStiffnessFactor=0.444), # as spec
@@ -355,6 +423,43 @@ class CAR(Platforms):
radar_dbc_dict('honda_civic_touring_2016_can_generated'),
flags=HondaFlags.HAS_ALL_DOOR_STATES
)
HONDA_ACCORD_9G = HondaNidecPlatformConfig(
[
HondaCarDocs("Honda Accord 2016-17"),
HondaCarDocs("Honda Accord Hybrid 2017", "All"),
],
CarSpecs(mass=3343 * CV.LB_TO_KG, wheelbase=2.78, steerRatio=17.5, centerToFrontRatio=0.37),
radar_dbc_dict('honda_accord_2017_can_ext_generated'),
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES | HondaFlags.HAS_ALL_DOOR_STATES,
)
ACURA_MDX_3G = HondaNidecPlatformConfig(
[
HondaCarDocs("Acura MDX 2014-16", "Advance Package"),
HondaCarDocs("Acura MDX 2017-19", "All"),
HondaCarDocs("Acura MDX Hybrid 2017-19", "All"),
],
CarSpecs(mass=4215 * CV.LB_TO_KG, wheelbase=2.82, steerRatio=16.8, centerToFrontRatio=0.428),
radar_dbc_dict('acura_mdx_2017_can_ext_generated'),
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
)
ACURA_MDX_3G_MMR = HondaNidecPlatformConfig(
[
HondaCarDocs("Acura MDX 2020", "All"),
HondaCarDocs("Acura MDX Hybrid 2020", "All"),
],
CarSpecs(mass=4215 * CV.LB_TO_KG, wheelbase=2.82, steerRatio=16.8, centerToFrontRatio=0.428),
radar_dbc_dict('acura_ilx_2016_can_generated'),
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
)
ACURA_TLX_1G = HondaNidecPlatformConfig(
[
HondaCarDocs("Acura TLX 2015-17", "Advance Package"),
HondaCarDocs("Acura TLX 2018-20", "All"),
],
CarSpecs(mass=3680 * CV.LB_TO_KG, wheelbase=2.78, steerRatio=17.0, centerToFrontRatio=0.40, tireStiffnessFactor=0.18),
radar_dbc_dict('acura_mdx_2017_can_ext_generated'),
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES | HondaFlags.HAS_ALL_DOOR_STATES,
)
HONDA_NIDEC_ALT_PCM_ACCEL = CAR.with_flags(HondaFlags.NIDEC_ALT_PCM_ACCEL)
@@ -389,6 +494,11 @@ STEER_THRESHOLD = {
CAR.HONDA_CRV_6G: 600,
CAR.HONDA_CITY_7G: 600,
CAR.HONDA_NBOX_2G: 600,
CAR.HONDA_ODYSSEY_5G_MMR: 600,
CAR.HONDA_ACCORD_9G: 30,
CAR.ACURA_MDX_3G: 30,
CAR.ACURA_MDX_3G_MMR: 30,
CAR.ACURA_TLX_1G: 30,
}
@@ -433,9 +543,11 @@ FW_QUERY_CONFIG = FwQueryConfig(
# Note that we still attempt to match with them when they are present
# This is or'd with (ALL_ECUS - ESSENTIAL_ECUS) from fw_versions.py
non_essential_ecus={
Ecu.eps: [CAR.ACURA_RDX_3G, CAR.HONDA_ACCORD, CAR.HONDA_E, *HONDA_BOSCH_ALT_RADAR, *HONDA_BOSCH_RADARLESS, *HONDA_BOSCH_CANFD],
Ecu.eps: [CAR.ACURA_RDX_3G, CAR.HONDA_ACCORD, CAR.HONDA_E, CAR.HONDA_E_ADVANCE, CAR.ACURA_MDX_4G, CAR.HONDA_CRV_SA,
CAR.ACURA_MDX_3G, CAR.HONDA_ACCORD_9G, *HONDA_BOSCH_ALT_RADAR, *HONDA_BOSCH_RADARLESS, *HONDA_BOSCH_CANFD],
Ecu.vsa: [CAR.ACURA_RDX_3G, CAR.HONDA_ACCORD, CAR.HONDA_CIVIC, CAR.HONDA_CIVIC_BOSCH, CAR.HONDA_CRV_5G, CAR.HONDA_CRV_HYBRID,
CAR.HONDA_E, CAR.HONDA_INSIGHT, CAR.HONDA_NBOX_2G, *HONDA_BOSCH_ALT_RADAR, *HONDA_BOSCH_RADARLESS, *HONDA_BOSCH_CANFD],
CAR.HONDA_E, CAR.HONDA_E_ADVANCE, CAR.HONDA_INSIGHT, CAR.HONDA_NBOX_2G, CAR.ACURA_MDX_4G, CAR.HONDA_ACCORD_9G,
*HONDA_BOSCH_ALT_RADAR, *HONDA_BOSCH_RADARLESS, *HONDA_BOSCH_CANFD],
},
extra_ecus=[
(Ecu.combinationMeter, 0x18da60f1, None),
+421 -24
View File
@@ -1,3 +1,5 @@
from dataclasses import dataclass
import numpy as np
from opendbc.can import CANPacker
from opendbc.car import Bus, DT_CTRL, make_tester_present_msg, structs
@@ -5,7 +7,7 @@ from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_steer_an
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.hyundai import hyundaicanfd, hyundaican
from opendbc.car.hyundai.hyundaicanfd import CanBus
from opendbc.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CAR
from opendbc.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CAR, CANFD_RADAR_LIVE_LONGITUDINAL_CAR
from opendbc.car.interfaces import CarControllerBase
from opendbc.car.vehicle_model import VehicleModel
from openpilot.common.params import Params
@@ -18,6 +20,179 @@ LongCtrlState = structs.CarControl.Actuators.LongControlState
MAX_ANGLE = 85
MAX_ANGLE_FRAMES = 89
MAX_ANGLE_CONSECUTIVE_FRAMES = 2
CANFD_BLINDSPOT_STATUS_STALE_NS = 200_000_000
CANFD_CAMERA_LEAD_STALE_NS = 300_000_000
CANFD_LEAD_MIN_DISTANCE = 0.1
CANFD_FALLBACK_LEAD_DISTANCE = 20.0
HYUNDAI_DASH_DISENGAGE_BLINK_TIME = 1.0
HYUNDAI_CANFD_SCC_ACCEL_STEP = 5.0 / 50.0
HYUNDAI_CANFD_SCC_DECEL_STEP = 12.5 / 50.0
IONIQ_6_RESPONSE_MULTIPLIER = 1.2
IONIQ_6_CANFD_SCC_ACCEL_STEP = (6.0 / 50.0) * IONIQ_6_RESPONSE_MULTIPLIER
IONIQ_6_CANFD_SCC_DECEL_STEP = (15.0 / 50.0) * IONIQ_6_RESPONSE_MULTIPLIER
GENESIS_G90_STOP_HOLD_SPEED_BP = [0.0, 0.03, 0.08, 0.16, 0.3, 0.5, 0.8, 1.2, 2.0, 3.0]
GENESIS_G90_STOP_HOLD_ACCEL_V = [-0.10, -0.10, -0.12, -0.18, -0.30, -0.50, -0.75, -1.00, -1.40, -1.80]
GENESIS_G90_STOP_HOLD_RELAX_SPEED_BP = [0.0, 0.08, 0.16, 0.3, 0.5, 0.8, 1.2, 2.0, 3.0]
GENESIS_G90_STOP_HOLD_RELAX_STEP_V = [0.10, 0.10, 0.08, 0.06, 0.04, 0.035, 0.03, 0.022, 0.018]
GENESIS_G90_RELEASE_SPEED_BP = [0.0, 0.3, 0.6]
GENESIS_G90_RELEASE_ACCEL_STEP_V = [0.05, 0.07, 0.11]
GENESIS_G90_RELEASE_DECEL_STEP_V = [0.16, 0.18, 0.18]
GENESIS_G90_RELEASE_MAX_SPEED = 0.8
IONIQ_6_LONG_MIN_JERK = 0.5 * IONIQ_6_RESPONSE_MULTIPLIER
IONIQ_6_LONG_JERK_LIMIT = 4.8 * IONIQ_6_RESPONSE_MULTIPLIER
IONIQ_6_LONG_LOOKAHEAD_JERK_BP = [2.0, 5.0, 20.0]
IONIQ_6_LONG_LOOKAHEAD_JERK_V = [0.3 / IONIQ_6_RESPONSE_MULTIPLIER,
0.45 / IONIQ_6_RESPONSE_MULTIPLIER,
0.6 / IONIQ_6_RESPONSE_MULTIPLIER]
IONIQ_6_DYNAMIC_LOWER_JERK_BP = [-2.0, -1.5, -1.0, -0.25, -0.1, -0.025, -0.01, -0.005]
IONIQ_6_DYNAMIC_LOWER_JERK_V = [3.3, 1.5, 1.0, 0.8, 0.7, 0.65, 0.55, 0.5]
IONIQ_6_LAUNCH_HOLD_SPEED_BP = [0.0, 0.6, 1.25, 2.5]
IONIQ_6_LAUNCH_HOLD_SPEED_V = [0.75, 0.6, 0.4, 0.0]
IONIQ_6_STOP_BRAKE_CAP_MAX_SPEED = 2.0
IONIQ_6_STOP_BRAKE_CAP_SPEED_BP = [0.0, 0.08, 0.25, 0.6, 1.2, 2.0, 3.0]
IONIQ_6_STOP_BRAKE_CAP_ACCEL_V = [-0.15, -0.16, -0.22, -0.42, -0.78, -1.15, -1.40]
IONIQ_6_STOP_HOLD_JERK_BP = [0.0, 0.15, 0.6, 1.2, 2.0, 3.0]
IONIQ_6_STOP_HOLD_JERK_V = [0.35, 0.40, 0.48, 0.65, 0.85, 1.10]
IONIQ_6_STOP_RELEASE_JERK_BP = [0.0, 0.15, 0.5]
IONIQ_6_STOP_RELEASE_JERK_V = [3.6 * IONIQ_6_RESPONSE_MULTIPLIER,
4.2 * IONIQ_6_RESPONSE_MULTIPLIER,
4.8 * IONIQ_6_RESPONSE_MULTIPLIER]
REDNECK_BUTTON_COPIES = 2
REDNECK_BUTTON_COPIES_TIME = 7
REDNECK_BUTTON_COPIES_TIME_IMPERIAL = [REDNECK_BUTTON_COPIES_TIME + 3, 70]
REDNECK_BUTTON_COPIES_TIME_METRIC = [REDNECK_BUTTON_COPIES_TIME, 40]
@dataclass
class Ioniq6LongitudinalTuningState:
desired_accel: float = 0.0
actual_accel: float = 0.0
accel_last: float = 0.0
jerk_upper: float = 0.0
jerk_lower: float = 0.0
launch_active: bool = False
stopping: bool = False
stopping_count: int = 0
long_control_state_last: LongCtrlState = LongCtrlState.off
@dataclass
class GenesisG90LongitudinalTuningState:
actual_accel: float = 0.0
release_active: bool = False
long_control_state_last: LongCtrlState = LongCtrlState.off
def _jerk_limited_integrator(desired_accel: float, last_accel: float, jerk_upper: float, jerk_lower: float) -> float:
step = (jerk_upper if desired_accel >= last_accel else jerk_lower) * DT_CTRL * 5.0
return float(np.clip(desired_accel, last_accel - step, last_accel + step))
def _calculate_ioniq_6_dynamic_lower_jerk(accel_error: float) -> float:
if accel_error < 0.0:
scaled_values = np.array(IONIQ_6_DYNAMIC_LOWER_JERK_V) * (IONIQ_6_LONG_JERK_LIMIT / IONIQ_6_DYNAMIC_LOWER_JERK_V[0])
return float(np.interp(accel_error, IONIQ_6_DYNAMIC_LOWER_JERK_BP, scaled_values))
return IONIQ_6_LONG_MIN_JERK
def update_ioniq_6_longitudinal_tuning(state: Ioniq6LongitudinalTuningState, accel_cmd: float, v_ego: float, a_ego: float,
long_control_state: LongCtrlState, long_active: bool) -> Ioniq6LongitudinalTuningState:
starting = long_control_state == LongCtrlState.starting
stopping = long_control_state == LongCtrlState.stopping
restart_from_stop = state.long_control_state_last in (LongCtrlState.stopping, LongCtrlState.starting) and \
long_control_state in (LongCtrlState.starting, LongCtrlState.pid) and accel_cmd > 0.0 and v_ego < 0.5
state.stopping = long_active and stopping
state.stopping_count = state.stopping_count + 1 if state.stopping else 0
if not long_active:
state.desired_accel = 0.0
state.actual_accel = 0.0
state.accel_last = 0.0
state.jerk_upper = 0.0
state.jerk_lower = 0.0
state.launch_active = False
state.long_control_state_last = long_control_state
return state
if accel_cmd <= 0.0 or v_ego >= IONIQ_6_LAUNCH_HOLD_SPEED_BP[-1]:
state.launch_active = False
elif starting or (state.launch_active and v_ego < IONIQ_6_LAUNCH_HOLD_SPEED_BP[-1]) or \
(state.long_control_state_last == LongCtrlState.starting and long_control_state == LongCtrlState.pid and v_ego < IONIQ_6_LAUNCH_HOLD_SPEED_BP[-1]):
state.launch_active = True
upper_speed_limit = float(np.interp(v_ego, [0.0, 5.0, 20.0], [2.0, 3.0, 2.0])) * IONIQ_6_RESPONSE_MULTIPLIER if long_control_state == LongCtrlState.pid else IONIQ_6_LONG_MIN_JERK
lower_speed_limit = float(np.interp(v_ego, [0.0, 5.0, 20.0], [5.0, 3.5, 3.0])) * IONIQ_6_RESPONSE_MULTIPLIER
future_t_upper = float(np.interp(v_ego, IONIQ_6_LONG_LOOKAHEAD_JERK_BP, IONIQ_6_LONG_LOOKAHEAD_JERK_V))
future_t_lower = float(np.interp(v_ego, IONIQ_6_LONG_LOOKAHEAD_JERK_BP, IONIQ_6_LONG_LOOKAHEAD_JERK_V))
accel_error = accel_cmd - state.accel_last
j_ego_upper = float(np.clip(accel_error / future_t_upper, -IONIQ_6_LONG_JERK_LIMIT, IONIQ_6_LONG_JERK_LIMIT))
j_ego_lower = float(np.clip(accel_error / future_t_lower, -IONIQ_6_LONG_JERK_LIMIT, IONIQ_6_LONG_JERK_LIMIT))
desired_jerk_upper = min(max(j_ego_upper, IONIQ_6_LONG_MIN_JERK), upper_speed_limit)
dynamic_accel_error = a_ego - state.accel_last
dynamic_lower_jerk = _calculate_ioniq_6_dynamic_lower_jerk(dynamic_accel_error)
state.jerk_upper = desired_jerk_upper
state.jerk_lower = min(dynamic_lower_jerk, lower_speed_limit)
if state.stopping:
if v_ego <= IONIQ_6_STOP_BRAKE_CAP_MAX_SPEED:
stop_brake_cap = float(np.interp(v_ego, IONIQ_6_STOP_BRAKE_CAP_SPEED_BP, IONIQ_6_STOP_BRAKE_CAP_ACCEL_V))
state.desired_accel = min(0.0, max(accel_cmd, stop_brake_cap))
state.jerk_upper = min(state.jerk_upper, float(np.interp(v_ego, IONIQ_6_STOP_HOLD_JERK_BP, IONIQ_6_STOP_HOLD_JERK_V)) * IONIQ_6_RESPONSE_MULTIPLIER)
else:
state.desired_accel = float(np.clip(accel_cmd, CarControllerParams.ACCEL_MIN, 0.0))
else:
state.desired_accel = float(np.clip(accel_cmd, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX))
if state.launch_active:
state.desired_accel = max(state.desired_accel, float(np.interp(v_ego, IONIQ_6_LAUNCH_HOLD_SPEED_BP, IONIQ_6_LAUNCH_HOLD_SPEED_V)))
state.jerk_upper = max(state.jerk_upper, float(np.interp(v_ego, [0.0, 2.5], [4.8, 3.2])) * IONIQ_6_RESPONSE_MULTIPLIER)
state.jerk_lower = max(state.jerk_lower, 1.0)
if restart_from_stop:
state.jerk_upper = min(state.jerk_upper, float(np.interp(v_ego, IONIQ_6_STOP_RELEASE_JERK_BP, IONIQ_6_STOP_RELEASE_JERK_V)))
state.actual_accel = _jerk_limited_integrator(state.desired_accel, state.accel_last, state.jerk_upper, state.jerk_lower)
state.accel_last = state.actual_accel
state.long_control_state_last = long_control_state
return state
def update_genesis_g90_longitudinal_tuning(state: GenesisG90LongitudinalTuningState, accel_cmd: float, v_ego: float,
long_control_state: LongCtrlState, long_active: bool) -> GenesisG90LongitudinalTuningState:
if not long_active:
state.actual_accel = 0.0
state.release_active = False
state.long_control_state_last = long_control_state
return state
stopping = long_control_state == LongCtrlState.stopping
if stopping and v_ego <= GENESIS_G90_STOP_HOLD_SPEED_BP[-1]:
state.release_active = False
stop_brake_cap = float(np.interp(v_ego, GENESIS_G90_STOP_HOLD_SPEED_BP, GENESIS_G90_STOP_HOLD_ACCEL_V))
target_hold = min(0.0, max(accel_cmd, stop_brake_cap))
if state.actual_accel < target_hold:
relax_step = float(np.interp(v_ego, GENESIS_G90_STOP_HOLD_RELAX_SPEED_BP, GENESIS_G90_STOP_HOLD_RELAX_STEP_V))
state.actual_accel = min(state.actual_accel + relax_step, target_hold)
else:
state.actual_accel = target_hold
else:
if state.long_control_state_last == LongCtrlState.stopping and long_control_state == LongCtrlState.pid and \
accel_cmd > 0.0 and v_ego < GENESIS_G90_RELEASE_MAX_SPEED:
state.release_active = True
if state.release_active:
accel_step = float(np.interp(v_ego, GENESIS_G90_RELEASE_SPEED_BP, GENESIS_G90_RELEASE_ACCEL_STEP_V))
decel_step = float(np.interp(v_ego, GENESIS_G90_RELEASE_SPEED_BP, GENESIS_G90_RELEASE_DECEL_STEP_V))
state.actual_accel = float(np.clip(accel_cmd, state.actual_accel - decel_step, state.actual_accel + accel_step))
if v_ego >= GENESIS_G90_RELEASE_MAX_SPEED or accel_cmd <= 0.0 or state.actual_accel >= accel_cmd - 1e-3:
state.release_active = False
else:
state.actual_accel = accel_cmd
state.long_control_state_last = long_control_state
return state
def process_hud_alert(enabled, fingerprint, hud_control):
@@ -58,14 +233,100 @@ class CarController(CarControllerBase):
self.apply_angle_last = 0.0
self.car_fingerprint = CP.carFingerprint
self.last_button_frame = 0
self.redneck_button_frame = 0
self.ecu_disable_failed = False
self._ecu_disable_checked = False
self._params = Params()
self.long_active_ecu = self.CP.openpilotLongitudinalControl
self._ioniq_6_lane_change_ui_side = None
self._ioniq_6_lane_change_ui_frames = 0
self._ioniq_6_long_tuning = Ioniq6LongitudinalTuningState()
self._genesis_g90_long_tuning = GenesisG90LongitudinalTuningState()
self._dash_lat_disengage_blink_frame = 0
self._dash_lat_disengage_init = False
self._dash_prev_lat_active = False
def _update_dash_icon_state(self, CC):
if CC.latActive:
self._dash_lat_disengage_init = False
elif self._dash_prev_lat_active:
self._dash_lat_disengage_init = True
if not self._dash_lat_disengage_init:
self._dash_lat_disengage_blink_frame = self.frame
disengaging = self._dash_lat_disengage_init and \
(self.frame - self._dash_lat_disengage_blink_frame) * DT_CTRL < HYUNDAI_DASH_DISENGAGE_BLINK_TIME
self._dash_prev_lat_active = CC.latActive
lat_or_enabled = CC.enabled or CC.latActive
lka_icon = 2 if lat_or_enabled else 3 if disengaging else 1
lfa_icon = 2 if lat_or_enabled else 3 if disengaging else 0
return lka_icon, lfa_icon
def _get_canfd_scc_lead_state(self, CC, CS, now_nanos):
openpilot_lead_visible = bool(getattr(CS, "openpilot_lead_visible", False) or CC.hudControl.leadVisible)
openpilot_lead_distance = float(np.clip(getattr(CS, "openpilot_lead_distance", 0.0), 0.0, 204.7))
openpilot_lead_rel_speed = float(np.clip(getattr(CS, "openpilot_lead_rel_speed", 0.0), -16.4, 34.7))
stock_camera_lead_fresh = now_nanos - getattr(CS, "stock_camera_lead_ts", 0) <= CANFD_CAMERA_LEAD_STALE_NS
stock_camera_lead_visible = stock_camera_lead_fresh and getattr(CS, "stock_camera_lead_visible", False)
if openpilot_lead_visible and openpilot_lead_distance > CANFD_LEAD_MIN_DISTANCE:
return True, openpilot_lead_distance, openpilot_lead_rel_speed
if stock_camera_lead_visible:
lead_distance = float(np.clip(getattr(CS, "stock_camera_lead_distance", 0.0), 0.0, 204.7))
lead_rel_speed = float(np.clip(getattr(CS, "stock_camera_lead_rel_speed", 0.0), -16.4, 34.7))
return True, lead_distance, lead_rel_speed
if openpilot_lead_visible:
return True, CANFD_FALLBACK_LEAD_DISTANCE, 0.0
return False, 0.0, 0.0
@staticmethod
def _get_redneck_button(CS):
return {
1: Buttons.RES_ACCEL,
2: Buttons.SET_DECEL,
}.get(getattr(CS, "redneck_send_button", 0), Buttons.NONE)
def _create_can_redneck_button_messages(self, CS):
send_button = self._get_redneck_button(CS)
if send_button == Buttons.NONE or (self.frame - self.last_button_frame) * DT_CTRL <= 0.1:
return []
copies_xp = REDNECK_BUTTON_COPIES_TIME_METRIC if CS.is_metric else REDNECK_BUTTON_COPIES_TIME_IMPERIAL
copies = int(np.interp(REDNECK_BUTTON_COPIES_TIME, copies_xp, [1, REDNECK_BUTTON_COPIES]))
can_sends = [hyundaican.create_clu11(self.packer, self.frame, CS.clu11, send_button, self.CP)] * copies
CS.redneck_last_sent_button = getattr(CS, "redneck_send_button", 0)
if (self.frame - self.last_button_frame) * DT_CTRL >= 0.15:
self.last_button_frame = self.frame
return can_sends
def _create_canfd_redneck_button_messages(self, CS):
send_button = self._get_redneck_button(CS)
if send_button == Buttons.NONE or self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS or \
(self.frame - self.last_button_frame) * DT_CTRL <= 0.2:
return []
self.redneck_button_frame += 1
button_counter_offset = [1, 1, 0, None][self.redneck_button_frame % 4]
if button_counter_offset is None:
return []
can_sends = [
hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, (CS.buttons_counter + button_counter_offset) % 0xF, send_button)
for _ in range(20)
]
CS.redneck_last_sent_button = getattr(CS, "redneck_send_button", 0)
self.last_button_frame = self.frame
return can_sends
def update(self, CC, CS, now_nanos, starpilot_toggles):
actuators = CC.actuators
hud_control = CC.hudControl
lka_icon, lfa_icon = self._update_dash_icon_state(CC)
self.params = CarControllerParams(self.CP, CS.out.vEgoRaw)
apply_angle = CS.out.steeringAngleDeg
@@ -86,6 +347,12 @@ class CarController(CarControllerBase):
apply_steer_req = CC.latActive and apply_torque > 0.0
torque_fault = False
if apply_angle is None:
apply_torque = 0.0
apply_angle = CS.out.steeringAngleDeg
apply_steer_req = False
self.apply_angle_last = apply_angle
else:
# steering torque
@@ -107,9 +374,11 @@ class CarController(CarControllerBase):
self.apply_torque_last = apply_torque
# accel + longitudinal
accel = float(np.clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX))
accel_cmd = float(np.clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX))
accel = accel_cmd
stopping = actuators.longControlState == LongCtrlState.stopping
set_speed_in_units = hud_control.setSpeed * (CV.MS_TO_KPH if CS.is_metric else CV.MS_TO_MPH)
CS.redneck_last_sent_button = 0
can_sends = []
@@ -122,12 +391,46 @@ class CarController(CarControllerBase):
# longitudinal messages - stock ECU is still active and these would conflict
self.long_active_ecu = self.CP.openpilotLongitudinalControl and not self.ecu_disable_failed
use_ioniq_6_dynamic_long_tuning = self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and self.long_active_ecu and \
actuators.longControlState in (LongCtrlState.starting, LongCtrlState.pid, LongCtrlState.stopping)
if use_ioniq_6_dynamic_long_tuning and self.frame % 5 == 0:
self._ioniq_6_long_tuning = update_ioniq_6_longitudinal_tuning(self._ioniq_6_long_tuning, accel_cmd,
CS.out.vEgo, CS.out.aEgo,
actuators.longControlState, self.long_active_ecu)
use_ioniq_6_smoothed_accel = use_ioniq_6_dynamic_long_tuning and (
accel_cmd >= self._ioniq_6_long_tuning.actual_accel or
self._ioniq_6_long_tuning.launch_active or
self._ioniq_6_long_tuning.stopping
)
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and self.long_active_ecu:
if use_ioniq_6_smoothed_accel:
accel = self._ioniq_6_long_tuning.actual_accel
stopping = self._ioniq_6_long_tuning.stopping
elif use_ioniq_6_dynamic_long_tuning:
accel = float(np.clip(accel_cmd,
self.accel_last - IONIQ_6_CANFD_SCC_DECEL_STEP,
self.accel_last + IONIQ_6_CANFD_SCC_ACCEL_STEP))
self._ioniq_6_long_tuning.desired_accel = accel_cmd
self._ioniq_6_long_tuning.actual_accel = accel
self._ioniq_6_long_tuning.accel_last = accel
self._ioniq_6_long_tuning.jerk_upper = 3.0
self._ioniq_6_long_tuning.jerk_lower = 5.0 if CC.enabled else 1.0
self._ioniq_6_long_tuning.launch_active = False
self._ioniq_6_long_tuning.stopping = stopping
self._ioniq_6_long_tuning.long_control_state_last = actuators.longControlState
if self.CP.carFingerprint == CAR.GENESIS_G90 and self.long_active_ecu:
self._genesis_g90_long_tuning = update_genesis_g90_longitudinal_tuning(self._genesis_g90_long_tuning, accel_cmd,
CS.out.vEgo, actuators.longControlState,
self.long_active_ecu)
accel = self._genesis_g90_long_tuning.actual_accel
# *** common hyundai stuff ***
# tester present - w/ no response (keeps relevant ECU disabled)
if self.frame % 100 == 0 and not (self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC) and self.long_active_ecu:
# for longitudinal control, either radar or ADAS driving ECU
addr, bus = 0x7d0, self.CAN.ECAN if self.CP.flags & HyundaiFlags.CANFD else 0
addr, bus = 0x7d0, self.CAN.ECAN if self.CP.flags & (HyundaiFlags.CANFD | HyundaiFlags.CAN_CANFD_BLENDED) else 0
if self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING.value:
addr, bus = 0x730, self.CAN.ECAN
can_sends.append(make_tester_present_msg(addr, bus, suppress_response=True))
@@ -138,11 +441,11 @@ class CarController(CarControllerBase):
# *** CAN/CAN FD specific ***
if self.CP.flags & HyundaiFlags.CANFD:
can_sends.extend(self.create_canfd_msgs(apply_steer_req, apply_torque, apply_angle, set_speed_in_units, accel,
stopping, hud_control, CS, CC))
can_sends.extend(self.create_canfd_msgs(now_nanos, apply_steer_req, apply_torque, apply_angle, set_speed_in_units, accel,
stopping, hud_control, CS, CC, starpilot_toggles, lka_icon, lfa_icon))
else:
can_sends.extend(self.create_can_msgs(apply_steer_req, apply_torque, torque_fault, set_speed_in_units, accel,
stopping, hud_control, actuators, CS, CC))
stopping, hud_control, actuators, CS, CC, lfa_icon))
new_actuators = actuators.as_builder()
if self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
@@ -157,17 +460,24 @@ class CarController(CarControllerBase):
self.frame += 1
return new_actuators, can_sends
def create_can_msgs(self, apply_steer_req, apply_torque, torque_fault, set_speed_in_units, accel, stopping, hud_control, actuators, CS, CC):
def create_can_msgs(self, apply_steer_req, apply_torque, torque_fault, set_speed_in_units, accel, stopping, hud_control, actuators, CS, CC, lfa_icon):
can_sends = []
can_canfd_blended = bool(self.CP.flags & HyundaiFlags.CAN_CANFD_BLENDED)
# HUD messages
sys_warning, sys_state, left_lane_warning, right_lane_warning = process_hud_alert(CC.enabled, self.car_fingerprint,
hud_control)
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.CP, apply_torque, apply_steer_req,
torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled,
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
left_lane_warning, right_lane_warning))
if can_canfd_blended:
can_sends.extend(hyundaican.create_lkas11_can_canfd_blended(self.packer, self.frame, self.CP, apply_torque, apply_steer_req,
torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled,
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
left_lane_warning, right_lane_warning, CS.msg_364))
else:
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.CP, apply_torque, apply_steer_req,
torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled,
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
left_lane_warning, right_lane_warning))
# Button messages
if not self.long_active_ecu:
@@ -180,38 +490,58 @@ class CarController(CarControllerBase):
can_sends.extend([hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.RES_ACCEL, self.CP)] * 25)
if (self.frame - self.last_button_frame) * DT_CTRL >= 0.15:
self.last_button_frame = self.frame
else:
can_sends.extend(self._create_can_redneck_button_messages(CS))
if self.long_active_ecu and can_canfd_blended:
can_sends.extend(hyundaican.create_radar_aux_messages(self.packer, self.CAN, self.frame))
if self.frame % 2 == 0 and self.long_active_ecu:
# TODO: unclear if this is needed
jerk = 3.0 if actuators.longControlState == LongCtrlState.pid else 1.0
use_fca = self.CP.flags & HyundaiFlags.USE_FCA.value
can_sends.extend(hyundaican.create_acc_commands(self.packer, CC.enabled, accel, jerk, int(self.frame / 2),
hud_control, set_speed_in_units, stopping,
CC.cruiseControl.override, use_fca, self.CP))
if can_canfd_blended:
can_sends.extend(hyundaican.create_acc_commands_can_canfd_blended(self.packer, CC.enabled, accel, jerk,
int(self.frame / 2), hud_control,
set_speed_in_units, stopping,
CC.cruiseControl.override, use_fca, self.CP))
else:
can_sends.extend(hyundaican.create_acc_commands(self.packer, CC.enabled, accel, jerk, int(self.frame / 2),
hud_control, set_speed_in_units, stopping,
CC.cruiseControl.override, use_fca, self.CP))
# 20 Hz LFA MFA message
if self.frame % 5 == 0 and self.CP.flags & HyundaiFlags.SEND_LFA.value:
can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled))
can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled, self.frame, self.CP, lfa_icon))
# 5 Hz ACC options
if self.frame % 20 == 0 and self.long_active_ecu:
if self.frame % 20 == 0 and self.long_active_ecu and not can_canfd_blended:
can_sends.extend(hyundaican.create_acc_opt(self.packer, self.CP))
# 2 Hz front radar options
if self.frame % 50 == 0 and self.long_active_ecu:
if self.frame % 50 == 0 and self.long_active_ecu and not can_canfd_blended:
can_sends.append(hyundaican.create_frt_radar_opt(self.packer))
return can_sends
def create_canfd_msgs(self, apply_steer_req, apply_torque, apply_angle, set_speed_in_units, accel, stopping, hud_control, CS, CC):
def create_canfd_msgs(self, now_nanos, apply_steer_req, apply_torque, apply_angle, set_speed_in_units, accel, stopping,
hud_control, CS, CC, starpilot_toggles, lka_icon, lfa_icon):
can_sends = []
lka_steering = self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING
lka_steering_long = lka_steering and self.long_active_ecu
ccnc_non_hda2 = self.CP.flags & HyundaiFlags.CCNC and not lka_steering
use_ioniq_6_dynamic_long_tuning = self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and self.long_active_ecu and \
CC.actuators.longControlState == LongCtrlState.pid
use_ioniq_6_smoothed_accel = use_ioniq_6_dynamic_long_tuning and CC.actuators.accel >= self._ioniq_6_long_tuning.actual_accel
# steering control
preserve_stock_lkas = self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and not self.long_active_ecu
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled,
apply_steer_req, apply_torque, apply_angle))
apply_steer_req, apply_torque, apply_angle,
CS.stock_lfa_msg,
CS.stock_lkas_msg if preserve_stock_lkas else None,
lka_icon=lka_icon))
# prevent LFA from activating on LKA steering cars by sending "no lane lines detected" to ADAS ECU
if self.frame % 5 == 0 and lka_steering:
@@ -220,26 +550,91 @@ class CarController(CarControllerBase):
# LFA and HDA icons
if self.frame % 5 == 0 and (not lka_steering or lka_steering_long):
can_sends.append(hyundaicanfd.create_lfahda_cluster(self.packer, self.CAN, CC.enabled))
if ccnc_non_hda2:
can_sends.extend(hyundaicanfd.create_ccnc(self.packer, self.CAN, self.long_active_ecu, CC.enabled, CC.hudControl,
CC.leftBlinker, CC.rightBlinker, CS.msg_161, CS.msg_162, CS.msg_1b5,
CS.is_metric, CS.out, CS.out.cruiseState.available, lfa_icon))
else:
can_sends.append(hyundaicanfd.create_lfahda_cluster(self.packer, self.CAN, CC.enabled, CS.stock_lfahda_cluster_msg,
lfa_icon=lfa_icon))
# blinkers
if lka_steering and self.CP.flags & HyundaiFlags.ENABLE_BLINKERS:
can_sends.extend(hyundaicanfd.create_spas_messages(self.packer, self.CAN, CC.leftBlinker, CC.rightBlinker))
lane_change_ui_side = None
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
if CC.leftBlinker and not CC.rightBlinker:
lane_change_ui_side = "left"
elif CC.rightBlinker and not CC.leftBlinker:
lane_change_ui_side = "right"
if lane_change_ui_side != self._ioniq_6_lane_change_ui_side:
self._ioniq_6_lane_change_ui_side = lane_change_ui_side
self._ioniq_6_lane_change_ui_frames = 0
if lane_change_ui_side is None or not self.long_active_ecu:
self._ioniq_6_lane_change_ui_frames = 0
else:
# The stock Ioniq 6 lane-change animation stops when the ADAS ECU is disabled,
# so replay the captured ECAN cluster frames ourselves while OP long is active.
can_sends.extend(hyundaicanfd.create_ioniq_6_cluster_lane_change_messages(self.CAN,
self._ioniq_6_lane_change_ui_frames,
lane_change_ui_side))
self._ioniq_6_lane_change_ui_frames += 1
if self.long_active_ecu:
if lka_steering:
can_sends.extend(hyundaicanfd.create_adrv_messages(self.packer, self.CAN, self.frame))
else:
# Ioniq 5/6: front radar treats ADAS_DRV's 0x100 broadcast as its host heartbeat
# and stops publishing object tracks when it disappears. Spoof it periodically on
# PT bus so the radar keeps tracking.
if self.CP.carFingerprint in CANFD_RADAR_LIVE_LONGITUDINAL_CAR and self.frame % 4 == 0:
can_sends.append(hyundaicanfd.create_accelerator_brake_alt_spoof(0, self.frame // 4, CS.out.brakePressed, CS.out.gasPressed))
elif not ccnc_non_hda2:
can_sends.extend(hyundaicanfd.create_fca_warning_light(self.packer, self.CAN, self.frame))
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and self.frame % 5 == 0:
rear_stale = now_nanos - CS.blindspots_rear_corners_ts > CANFD_BLINDSPOT_STATUS_STALE_NS
front_stale = now_nanos - CS.blindspots_front_corner_1_ts > CANFD_BLINDSPOT_STATUS_STALE_NS
if CS.blindspots_rear_corners_ts > 0 and CS.blindspots_front_corner_1_ts > 0 and rear_stale and front_stale:
can_sends.extend(hyundaicanfd.create_blindspot_status_messages(self.packer, self.CAN,
CS.blindspots_rear_corners,
CS.blindspots_front_corner_1,
CS.left_blindspot_from_radar,
CS.right_blindspot_from_radar,
CC.leftBlinker,
CC.rightBlinker))
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and lane_change_ui_side is None:
can_sends.extend(hyundaicanfd.create_ioniq_6_cluster_blindspot_messages(self.CAN, self.frame,
CS.left_blindspot_from_radar,
CS.right_blindspot_from_radar,
CC.leftBlinker,
CC.rightBlinker))
if self.frame % 2 == 0:
lead_visible, lead_distance, lead_rel_speed = self._get_canfd_scc_lead_state(CC, CS, now_nanos)
acc_kwargs = {
"main_mode_acc": int(CS.out.cruiseState.available),
"direct_accel": True,
"jerk_lower": 5.0,
"jerk_upper": 3.0 if CC.actuators.longControlState == LongCtrlState.pid else 1.0,
"lead_distance": lead_distance,
"lead_rel_speed": lead_rel_speed,
"lead_visible": lead_visible,
}
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
if use_ioniq_6_smoothed_accel:
acc_kwargs["jerk_lower"] = self._ioniq_6_long_tuning.jerk_lower
acc_kwargs["jerk_upper"] = self._ioniq_6_long_tuning.jerk_upper
can_sends.append(hyundaicanfd.create_acc_control(self.packer, self.CAN, CC.enabled, self.accel_last, accel, stopping, CC.cruiseControl.override,
set_speed_in_units, hud_control))
set_speed_in_units, hud_control, cruise_info=CS.cruise_info if ccnc_non_hda2 else None,
**acc_kwargs))
self.accel_last = accel
else:
# button presses
if (self.frame - self.last_button_frame) * DT_CTRL > 0.25:
# cruise cancel
if CC.cruiseControl.cancel:
# cruise cancel - suppress when stock ACC is the fallback (ECU disable failed),
# so openpilot doesn't fight/cancel the user's stock cruise
if CC.cruiseControl.cancel and not self.ecu_disable_failed:
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
can_sends.append(hyundaicanfd.create_acc_cancel(self.packer, self.CP, self.CAN, CS.cruise_info))
self.last_button_frame = self.frame
@@ -257,5 +652,7 @@ class CarController(CarControllerBase):
for _ in range(20):
can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter + 1, Buttons.RES_ACCEL))
self.last_button_frame = self.frame
else:
can_sends.extend(self._create_canfd_redneck_button_messages(CS))
return can_sends
+234 -35
View File
@@ -7,7 +7,8 @@ from opendbc.can import CANDefine, CANParser
from opendbc.car import Bus, create_button_events, structs
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.hyundai.hyundaicanfd import CanBus
from opendbc.car.hyundai.values import HyundaiFlags, HyundaiStarPilotFlags, CAR, DBC, Buttons, CarControllerParams
from opendbc.car.hyundai.values import HyundaiFlags, HyundaiStarPilotFlags, HyundaiStarPilotSafetyFlags, CAR, DBC, Buttons, CarControllerParams, \
hyundai_cancel_button_enables_cruise, ALT_BUS_LDA_BUTTON_CARS, ALT_BUS_LDA_BUTTON_SWL_STAT_CARS
from opendbc.car.interfaces import CarStateBase
ButtonType = structs.CarState.ButtonEvent.Type
@@ -21,6 +22,19 @@ ENABLE_BUTTONS = (Buttons.RES_ACCEL, Buttons.SET_DECEL, Buttons.CANCEL)
BUTTONS_DICT = {Buttons.RES_ACCEL: ButtonType.accelCruise, Buttons.SET_DECEL: ButtonType.decelCruise,
Buttons.GAP_DIST: ButtonType.gapAdjustCruise, Buttons.CANCEL: ButtonType.cancel}
IONIQ_6_BLINDSPOT_RIGHT_MASK = 0x08
IONIQ_6_BLINDSPOT_LEFT_MASK = 0x10
CANFD_CAMERA_LEAD_MIN_DISTANCE = 0.1
ALT_BUS_LDA_BUTTON_BURST_DEBOUNCE_NS = int(1.3e9)
def get_non_scc_cruise_signals(CP) -> tuple[str, str, str, str, str, str]:
if CP.flags & HyundaiFlags.EV:
return "LABEL11", "CC_React", "EMS12", "ACC_ACT", "E_EMS11", "Cruise_Limit_Target"
if CP.flags & HyundaiFlags.HYBRID:
return "E_CRUISE_CONTROL", "CRUISE_LAMP_M", "E_CRUISE_CONTROL", "CRUISE_LAMP_S", "ELECT_GEAR", "SLC_SET_SPEED"
return "EMS16", "CRUISE_LAMP_M", "EMS16", "CRUISE_LAMP_S", "LVR12", "CF_Lvr_CruiseSet"
def calculate_canfd_speed_limit(CP, FPCP, cp, cp_cam, speed_factor):
if not (FPCP.flags & HyundaiStarPilotFlags.SPEED_LIMIT_AVAILABLE):
@@ -34,6 +48,19 @@ def calculate_canfd_speed_limit(CP, FPCP, cp, cp_cam, speed_factor):
return 0.0
def decode_ioniq_6_blindspot_radar_state(state: int) -> tuple[bool, bool]:
state_int = int(state)
return bool(state_int & IONIQ_6_BLINDSPOT_LEFT_MASK), bool(state_int & IONIQ_6_BLINDSPOT_RIGHT_MASK)
def decode_canfd_camera_lead(distance: float, rel_speed: float) -> tuple[bool, float, float]:
lead_distance = float(distance)
lead_visible = lead_distance > CANFD_CAMERA_LEAD_MIN_DISTANCE
if not lead_visible:
return False, 0.0, 0.0
return True, lead_distance, float(rel_speed)
class CarState(CarStateBase):
@staticmethod
def get_canfd_blinker_sig_names(car_fingerprint, use_alt_lamp: bool) -> tuple[str, str]:
@@ -48,9 +75,15 @@ class CarState(CarStateBase):
self.cruise_buttons: deque = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
self.main_buttons: deque = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
self.lda_button = 0
self.lda_button_raw = 0
self.lda_button_last_raw_rise_ts_nanos = 0
self.left_paddle = 0
self.mode_button = 0
self.custom_button = 0
self.cancel_button_enable_in_progress = False
self.cruise_buttons_msg = {}
self.redneck_send_button = Buttons.NONE
self.redneck_v_target = 0
self.gear_msg_canfd = "ACCELERATOR" if CP.flags & HyundaiFlags.EV else \
"GEAR_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS else \
@@ -78,6 +111,24 @@ class CarState(CarStateBase):
self.buttons_counter = 0
self.cruise_info = {}
self.msg_161 = {}
self.msg_162 = {}
self.msg_1b5 = {}
self.msg_364 = {}
self.stock_lkas_msg = {}
self.stock_lfa_msg = {}
self.stock_lfahda_cluster_msg = {}
self.stock_camera_lead_visible = False
self.stock_camera_lead_distance = 0.0
self.stock_camera_lead_rel_speed = 0.0
self.stock_camera_lead_ts = 0
self.stock_blinker_stalks_ts = 0
self.blindspots_rear_corners = {}
self.blindspots_front_corner_1 = {}
self.blindspots_rear_corners_ts = 0
self.blindspots_front_corner_1_ts = 0
self.left_blindspot_from_radar = False
self.right_blindspot_from_radar = False
# On some cars, CLU15->CF_Clu_VehicleSpeed can oscillate faster than the dash updates. Sample at 5 Hz
self.cluster_speed = 0
@@ -91,9 +142,76 @@ class CarState(CarStateBase):
# Main button also can trigger an engagement on these cars
return any(btn in ENABLE_BUTTONS for btn in self.cruise_buttons) or any(self.main_buttons)
def create_cruise_button_events(self, cur_button: int, prev_button: int) -> list[structs.CarState.ButtonEvent]:
if cur_button != prev_button and prev_button != Buttons.CANCEL and cur_button == Buttons.CANCEL:
self.cancel_button_enable_in_progress = (
self.CP.openpilotLongitudinalControl and
hyundai_cancel_button_enables_cruise(self.CP.carFingerprint) and
not self.out.cruiseState.enabled
)
buttons_dict = BUTTONS_DICT
if self.cancel_button_enable_in_progress:
buttons_dict = BUTTONS_DICT | {Buttons.CANCEL: ButtonType.accelCruise}
events = create_button_events(cur_button, prev_button, buttons_dict)
if cur_button != Buttons.CANCEL:
self.cancel_button_enable_in_progress = False
return events
def update_button_enable(self, buttonEvents: list[structs.CarState.ButtonEvent]):
if super().update_button_enable(buttonEvents):
return True
if not self.CP.pcmCruise and hyundai_cancel_button_enables_cruise(self.CP.carFingerprint):
for b in buttonEvents:
# Some Palisade 2023 routes still surface the pause/resume interaction as a
# plain cancel event even though stock ACC engages on the release edge.
if b.type == ButtonType.cancel and not b.pressed and not self.out.cruiseState.enabled:
return True
return False
def create_alt_bus_lda_button_events(self, cp_source: CANParser) -> list[structs.CarState.ButtonEvent]:
if self.CP.carFingerprint in ALT_BUS_LDA_BUTTON_SWL_STAT_CARS:
raw_lda_button = int(cp_source.vl["CLU13"]["CF_Clu_SWL_Stat"] == 4)
raw_lda_button_ts_nanos = cp_source.ts_nanos["CLU13"]["CF_Clu_SWL_Stat"]
else:
raw_lda_button = int(cp_source.vl["CLU13"]["CF_Clu_LdwsLkasSW"])
raw_lda_button_ts_nanos = cp_source.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"]
button_events: list[structs.CarState.ButtonEvent] = []
# Some alt-bus LKAS button layouts pulse several times per physical press burst.
# Collapse each burst into a single synthetic press/release pair.
if raw_lda_button and not self.lda_button_raw:
if self.lda_button_last_raw_rise_ts_nanos == 0 or \
raw_lda_button_ts_nanos - self.lda_button_last_raw_rise_ts_nanos > ALT_BUS_LDA_BUTTON_BURST_DEBOUNCE_NS:
button_events = [
structs.CarState.ButtonEvent(pressed=True, type=ButtonType.lkas),
structs.CarState.ButtonEvent(pressed=False, type=ButtonType.lkas),
]
self.lda_button_last_raw_rise_ts_nanos = raw_lda_button_ts_nanos
self.lda_button_raw = raw_lda_button
return button_events
def create_lkas_button_events(self, cp: CANParser, prev_lda_button: int) -> list[structs.CarState.ButtonEvent]:
# Some classic HKG platforms publish the LKAS button on the cluster bus instead of BCM_PO_11.
if cp.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"] > 0:
self.lda_button = int(cp.vl["CLU13"]["CF_Clu_LdwsLkasSW"])
elif cp.ts_nanos["BCM_PO_11"]["LDA_BTN"] > 0:
self.lda_button = int(cp.vl["BCM_PO_11"]["LDA_BTN"])
else:
self.lda_button = 0
return create_button_events(self.lda_button, prev_lda_button, {1: ButtonType.lkas})
def update(self, can_parsers, starpilot_toggles) -> structs.CarState:
cp = can_parsers[Bus.pt]
cp_cam = can_parsers[Bus.cam]
cp_alt = can_parsers.get(Bus.alt)
if self.CP.flags & HyundaiFlags.CANFD:
return self.update_canfd(can_parsers)
@@ -147,22 +265,22 @@ class CarState(CarStateBase):
ret.cruiseState.standstill = False
ret.cruiseState.nonAdaptive = False
elif no_scc:
cruise_enabled = cp.vl["TCS13"]["ACC_REQ"] == 1
cruise_set_speed = cp.vl["LVR12"]["CF_Lvr_CruiseSet"]
# Regular-cruise Forte trims don't publish SCC11/SCC12; use the stock cruise request and set speed.
ret.cruiseState.available = cruise_enabled or cp.vl["TCS13"]["ACCEnable"] == 0
ret.cruiseState.enabled = cruise_enabled
cruise_available_msg, cruise_available_sig, cruise_enabled_msg, cruise_enabled_sig, cruise_speed_msg, cruise_speed_sig = get_non_scc_cruise_signals(self.CP)
ret.cruiseState.available = cp.vl[cruise_available_msg][cruise_available_sig] != 0
ret.cruiseState.enabled = cp.vl[cruise_enabled_msg][cruise_enabled_sig] != 0
ret.cruiseState.standstill = False
ret.cruiseState.nonAdaptive = False
if 0 < cruise_set_speed < 255:
ret.cruiseState.speed = cruise_set_speed * speed_conv
ret.cruiseState.speed = cp.vl[cruise_speed_msg][cruise_speed_sig] * speed_conv
else:
ret.cruiseState.available = cp_cruise.vl["SCC11"]["MainMode_ACC"] == 1
scc_msg = "SCC12" if self.CP.flags & HyundaiFlags.CAN_CANFD_BLENDED else "SCC11"
ret.cruiseState.available = cp_cruise.vl[scc_msg]["MainMode_ACC"] == 1
ret.cruiseState.enabled = cp_cruise.vl["SCC12"]["ACCMode"] != 0
ret.cruiseState.standstill = cp_cruise.vl["SCC11"]["SCCInfoDisplay"] == 4.
ret.cruiseState.nonAdaptive = cp_cruise.vl["SCC11"]["SCCInfoDisplay"] == 2. # Shows 'Cruise Control' on dash
ret.cruiseState.speed = cp_cruise.vl["SCC11"]["VSetDis"] * speed_conv
ret.cruiseState.standstill = cp_cruise.vl[scc_msg]["SCCInfoDisplay"] == 4.
ret.cruiseState.nonAdaptive = cp_cruise.vl[scc_msg]["SCCInfoDisplay"] == 2. # Shows 'Cruise Control' on dash
ret.cruiseState.speed = cp_cruise.vl[scc_msg]["VSetDis"] * speed_conv
if self.CP.flags & HyundaiFlags.CAN_CANFD_BLENDED:
self.msg_364 = copy.copy(cp_cam.vl["ALERTS_364"])
# TODO: Find brake pressure
ret.brake = 0
@@ -198,14 +316,23 @@ class CarState(CarStateBase):
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear))
if not self.CP.openpilotLongitudinalControl or self.CP.flags & HyundaiFlags.CAMERA_SCC:
aeb_src = "FCA11" if self.CP.flags & HyundaiFlags.USE_FCA.value else "SCC12"
aeb_sig = "FCA_CmdAct" if self.CP.flags & HyundaiFlags.USE_FCA.value else "AEB_CmdAct"
aeb_warning = cp_cruise.vl[aeb_src]["CF_VSM_Warn"] != 0
scc_warning = cp_cruise.vl["SCC12"]["TakeOverReq"] == 1 # sometimes only SCC system shows an FCW
aeb_braking = cp_cruise.vl[aeb_src]["CF_VSM_DecCmdAct"] != 0 or cp_cruise.vl[aeb_src][aeb_sig] != 0
ret.stockFcw = (aeb_warning or scc_warning) and not aeb_braking
ret.stockAeb = aeb_warning and aeb_braking
if (not self.CP.openpilotLongitudinalControl or self.CP.flags & HyundaiFlags.CAMERA_SCC) and \
not (self.CP.flags & HyundaiFlags.CAN_CANFD_BLENDED):
if no_scc:
if not (self.CP.flags & HyundaiFlags.NON_SCC_NO_FCA):
cp_fca = cp if self.CP.flags & HyundaiFlags.NON_SCC_RADAR_FCA else cp_cam
aeb_warning = cp_fca.vl["FCA11"]["CF_VSM_Warn"] != 0
aeb_braking = cp_fca.vl["FCA11"]["CF_VSM_DecCmdAct"] != 0 or cp_fca.vl["FCA11"]["FCA_CmdAct"] != 0
ret.stockFcw = aeb_warning and not aeb_braking
ret.stockAeb = aeb_warning and aeb_braking
else:
aeb_src = "FCA11" if self.CP.flags & HyundaiFlags.USE_FCA.value else "SCC12"
aeb_sig = "FCA_CmdAct" if self.CP.flags & HyundaiFlags.USE_FCA.value else "AEB_CmdAct"
aeb_warning = cp_cruise.vl[aeb_src]["CF_VSM_Warn"] != 0
scc_warning = cp_cruise.vl["SCC12"]["TakeOverReq"] == 1
aeb_braking = cp_cruise.vl[aeb_src]["CF_VSM_DecCmdAct"] != 0 or cp_cruise.vl[aeb_src][aeb_sig] != 0
ret.stockFcw = (aeb_warning or scc_warning) and not aeb_braking
ret.stockAeb = aeb_warning and aeb_braking
if self.CP.enableBsm:
ret.leftBlindspot = cp.vl["LCA11"]["CF_Lca_IndLeft"] != 0
@@ -218,14 +345,17 @@ class CarState(CarStateBase):
prev_cruise_buttons = self.cruise_buttons[-1]
prev_main_buttons = self.main_buttons[-1]
prev_lda_button = self.lda_button
lkas_button_events = []
self.cruise_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwState"])
self.main_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwMain"])
if self.CP.flags & HyundaiFlags.HAS_LDA_BUTTON:
self.lda_button = cp.vl["BCM_PO_11"]["LDA_BTN"]
if self.CP.carFingerprint in ALT_BUS_LDA_BUTTON_CARS and cp_alt is not None and cp_alt.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"] > 0:
lkas_button_events = self.create_alt_bus_lda_button_events(cp_alt)
else:
lkas_button_events = self.create_lkas_button_events(cp, prev_lda_button)
ret.buttonEvents = [*create_button_events(self.cruise_buttons[-1], prev_cruise_buttons, BUTTONS_DICT),
ret.buttonEvents = [*self.create_cruise_button_events(self.cruise_buttons[-1], prev_cruise_buttons),
*create_button_events(self.main_buttons[-1], prev_main_buttons, {1: ButtonType.mainCruise}),
*create_button_events(self.lda_button, prev_lda_button, {1: ButtonType.lkas})]
*lkas_button_events]
ret.blockPcmEnable = not self.recent_button_interaction()
@@ -279,17 +409,41 @@ class CarState(CarStateBase):
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > self.params.STEER_THRESHOLD, 5)
ret.steerFaultTemporary = cp.vl["MDPS"]["LKA_FAULT"] != 0
left_blinker_sig, right_blinker_sig = self.get_canfd_blinker_sig_names(self.CP.carFingerprint,
cp.vl["BLINKERS"]["USE_ALT_LAMP"] == 1)
ccnc_non_hda2 = self.CP.flags & HyundaiFlags.CCNC and not self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING
if ccnc_non_hda2:
self.msg_161 = copy.copy(cp_cam.vl["CCNC_0x161"])
self.msg_162 = copy.copy(cp_cam.vl["CCNC_0x162"])
self.msg_1b5 = copy.copy(cp_cam.vl["FR_CMR_03_50ms"])
cp_cruise_info = cp_cam if self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC else cp
self.cruise_info = copy.copy(cp_cruise_info.vl["SCC_CONTROL"])
use_alt_lamp = cp.vl["BLINKERS"]["USE_ALT_LAMP"] == 1 or bool(self.CP.flags & HyundaiFlags.CCNC)
left_blinker_sig, right_blinker_sig = self.get_canfd_blinker_sig_names(self.CP.carFingerprint, use_alt_lamp)
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(50, cp.vl["BLINKERS"][left_blinker_sig],
cp.vl["BLINKERS"][right_blinker_sig])
self.left_blindspot_from_radar = False
self.right_blindspot_from_radar = False
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
self.left_blindspot_from_radar, self.right_blindspot_from_radar = decode_ioniq_6_blindspot_radar_state(
cp.vl["BLINDSPOTS_FRONT_CORNER_2"]["SIDE_DETECT_STATE"])
if self.CP.enableBsm:
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
ret.leftBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["LEFT_MB"] != 0
ret.rightBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["MORE_LEFT_PROB"] != 0
ret.leftBlindspot = (bool(cp.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_LtIndSta"]) or
self.left_blindspot_from_radar)
ret.rightBlindspot = (bool(cp.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_RtIndSta"]) or
self.right_blindspot_from_radar)
elif self.CP.flags & HyundaiFlags.CCNC:
ret.leftBlindspot = bool(cp.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_LtIndSta"])
ret.rightBlindspot = bool(cp.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_RtIndSta"])
else:
ret.leftBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["FL_INDICATOR"] != 0
ret.rightBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["FR_INDICATOR"] != 0
ret.leftBlindspot = bool(cp.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_LtIndSta"])
ret.rightBlindspot = bool(cp.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_RtIndSta"])
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
self.blindspots_rear_corners = copy.copy(cp.vl["BLINDSPOTS_REAR_CORNERS"])
self.blindspots_front_corner_1 = copy.copy(cp.vl["BLINDSPOTS_FRONT_CORNER_1"])
self.blindspots_rear_corners_ts = cp.ts_nanos["BLINDSPOTS_REAR_CORNERS"]["CHECKSUM"]
self.blindspots_front_corner_1_ts = cp.ts_nanos["BLINDSPOTS_FRONT_CORNER_1"]["CHECKSUM"]
# cruise state
# CAN FD cars enable on main button press, set available if no TCS faults preventing engagement
@@ -312,7 +466,6 @@ class CarState(CarStateBase):
# TODO: find this message on ICE & HYBRID cars + cruise control signals (if exists)
if self.CP.flags & HyundaiFlags.EV:
ret.cruiseState.nonAdaptive = cp.vl["MANUAL_SPEED_LIMIT_ASSIST"]["MSLA_ENABLED"] == 1
prev_cruise_buttons = self.cruise_buttons[-1]
prev_main_buttons = self.main_buttons[-1]
prev_lda_button = self.lda_button
@@ -322,15 +475,32 @@ class CarState(CarStateBase):
self.lda_button = cp.vl[self.cruise_btns_msg_canfd]["LDA_BTN"]
self.left_paddle = 0
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
self.cruise_buttons_msg = copy.copy(cp.vl["CRUISE_BUTTONS"])
self.left_paddle = cp.vl["CRUISE_BUTTONS"]["LEFT_PADDLE"]
self.buttons_counter = cp.vl[self.cruise_btns_msg_canfd]["COUNTER"]
ret.accFaulted = cp.vl["TCS"]["ACCEnable"] != 0 # 0 ACC CONTROL ENABLED, 1-3 ACC CONTROL DISABLED
if self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
lkas_msg = "LKAS_ALT" if self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT else "LKAS"
self.lfa_block_msg = copy.copy(cp_cam.vl["CAM_0x362"] if self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
else cp_cam.vl["CAM_0x2a4"])
if cp_cam.ts_nanos[lkas_msg]["CHECKSUM"] > 0:
self.stock_lkas_msg = copy.copy(cp_cam.vl[lkas_msg])
lead_cp = cp if self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING else cp_cam
if lead_cp.ts_nanos["FR_CMR_03_50ms"]["FR_CMR_Crc3Val"] > 0:
self.stock_camera_lead_ts = lead_cp.ts_nanos["FR_CMR_03_50ms"]["FR_CMR_Crc3Val"]
self.stock_camera_lead_visible, self.stock_camera_lead_distance, self.stock_camera_lead_rel_speed = decode_canfd_camera_lead(
lead_cp.vl["FR_CMR_03_50ms"]["Longitudinal_Distance"],
lead_cp.vl["FR_CMR_03_50ms"]["Relative_Velocity"],
)
if cp.ts_nanos["LFA"]["CHECKSUM"] > 0:
self.stock_lfa_msg = copy.copy(cp.vl["LFA"])
if cp.ts_nanos["LFAHDA_CLUSTER"]["CHECKSUM"] > 0:
self.stock_lfahda_cluster_msg = copy.copy(cp.vl["LFAHDA_CLUSTER"])
if cp.ts_nanos["BLINKER_STALKS"]["CHECKSUM_MAYBE"] > 0:
self.stock_blinker_stalks_ts = cp.ts_nanos["BLINKER_STALKS"]["CHECKSUM_MAYBE"]
ret.buttonEvents = [*create_button_events(self.cruise_buttons[-1], prev_cruise_buttons, BUTTONS_DICT),
ret.buttonEvents = [*self.create_cruise_button_events(self.cruise_buttons[-1], prev_cruise_buttons),
*create_button_events(self.main_buttons[-1], prev_main_buttons, {1: ButtonType.mainCruise}),
*create_button_events(self.lda_button, prev_lda_button, {1: ButtonType.lkas}),
*create_button_events(self.left_paddle, prev_left_paddle, {1: ButtonType.altButton2})]
@@ -350,6 +520,10 @@ class CarState(CarStateBase):
fp_ret.modePressed = bool(self.mode_button)
fp_ret.customPressed = bool(self.custom_button)
# ADAS camera dashboard stop-sign signal (CANFD HKG only). Registered as optional
# (freq=0); cars without it never publish, so the read returns 0 and the field stays 0.
fp_ret.dashboardStopSign = 1 if bool(cp_cam.vl["ADAS_0x380"]["STOP_SIGN"]) else 0
return ret, fp_ret
def get_can_parsers_canfd(self, CP):
@@ -363,11 +537,26 @@ class CarState(CarStateBase):
]
if CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
msgs.append(("FR_CMR_02_100ms", 10))
msgs.append(("FR_CMR_03_50ms", 0))
cam_msgs.append(("LKAS_ALT" if CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT else "LKAS", 0))
else:
cam_msgs.append(("FR_CMR_02_100ms", 0)) # optional: not all non-LKA CANFD cars have this on CAM bus
cam_msgs.append(("FR_CMR_03_50ms", 0)) # optional: camera lead/cipv data is not present on every CAN-FD trim
if CP.flags & HyundaiFlags.CCNC:
cam_msgs += [
("CCNC_0x161", 0),
("CCNC_0x162", 0),
]
msgs += [
("LFA", 0), # optional: may stop once OP takes over, but preserve stock UI fields when present
("LFAHDA_CLUSTER", 0), # optional: carries cluster icon state on some variants
("BLINKER_STALKS", 0), # optional: some trims publish live stalk/light state on ECAN during turn camera events
]
if CP.flags & HyundaiFlags.EV:
msgs.append(("DRIVE_MODE_EV", 0)) # optional: not all CAN-FD EV variants publish drive mode
msgs.append(("MANUAL_SPEED_LIMIT_ASSIST", 0)) # optional: used for non-adaptive cruise state and Ioniq 6 i-Pedal latch detection
msgs.append(("STEERING_WHEEL_MEDIA_BUTTONS", 0)) # optional: absent or slower on some CAN-FD variants
cam_msgs.append(("ADAS_0x380", 0)) # optional: dashboard stop-sign signal, only on ADAS-equipped HKG CANFD
return {
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], msgs, CanBus(CP).ECAN),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], cam_msgs, CanBus(CP).CAM),
@@ -377,7 +566,17 @@ class CarState(CarStateBase):
if CP.flags & HyundaiFlags.CANFD:
return self.get_can_parsers_canfd(CP)
return {
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 0),
msgs = [
("BCM_PO_11", 0),
("CLU13", 0),
]
if CP.flags & HyundaiFlags.NON_SCC and not (CP.flags & HyundaiFlags.NON_SCC_NO_FCA):
msgs.append(("FCA11", 0)) # Non-SCC trims can stop publishing FCA11; don't let it poison canValid
parsers = {
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], msgs, 0),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 2),
}
if CP.carFingerprint in ALT_BUS_LDA_BUTTON_CARS:
parsers[Bus.alt] = CANParser(DBC[CP.carFingerprint][Bus.pt], [("CLU13", 0)], 1)
return parsers
@@ -467,6 +467,33 @@ FW_VERSIONS = {
b'\xf1\x00ON MFC AT USA LHD 1.00 1.04 99211-S9100 211227',
],
},
CAR.HYUNDAI_PALISADE_2023: {
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00ON MDPS C 1.00 1.01 56300-S9500 2922',
b'\xf1\x00LXP MDPS C 1.00 1.00 56310-S8620 4LXPC100',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00ON MFC AT USA LHD 1.00 1.00 99211-S9170 240531',
b'\xf1\x00ON MFC AT USA LHD 1.00 1.01 99211-S9160 230802',
b'\xf1\x00LX2 MFC AT USA LHD 1.00 1.04 99211-S8150 220622',
b'\xf1\x00LX2 MFC AT RUS LHD 1.00 1.04 99211-S8150 220622',
b'\xf1\x00ON MFC AT USA LHD 1.00 1.01 99211-S9150 220708',
b'\xf1\x00ON MFC AT USA LHD 1.00 1.00 99211-S9160 230303',
b'\xf1\x00LX2 MFC AT USA LHD 1.00 1.01 99211-S8600 230817',
b'\xf1\x00LX2 MFC AT USA LHD 1.00 1.00 99211-S8700 240221',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00ON__ SCC FHCUP 1.00 1.00 99110-S9170 ',
b'\xf1\x00ON__ SCC FHCUP 1.00 1.00 99110-S9160 ',
b'\xf1\x00LX2_ SCC ----- 1.00 1.01 99110-S8150 ',
b'\xf1\x00ON__ SCC ----- 1.00 1.01 99110-S9150 ',
b'\xf1\x00LX2_ SCC FHCUP 1.00 1.01 99110-S8150 ',
b'\xf1\x00ON__ SCC FHCUP 1.00 1.01 99110-S9150 ',
b'\xf1\x00LX2 SCC FHCUP 1.00 1.00 99110-S8600 ',
b'\xf1\x00LX2_ SCC FHCUP 1.00 1.01 99110-S8700 ',
b'\xf1\x00LX2_ SCC F-CUP 1.00 1.01 99110-S8150 ',
],
},
CAR.HYUNDAI_VELOSTER: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00JS__ SCC H-CUP 1.00 1.02 95650-J3200 ',
@@ -583,12 +610,14 @@ FW_VERSIONS = {
CAR.KIA_FORTE: {
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00BD MDPS C 1.00 1.02 56310-XX000 4BD2C102',
b'\xf1\x00BD MDPS C 1.00 1.07 56310/M6300 4BDDC107',
b'\xf1\x00BD MDPS C 1.00 1.08 56310/M6300 4BDDC108',
b'\xf1\x00BD MDPS C 1.00 1.08 56310M6300\x00 4BDDC108',
b'\xf1\x00BDm MDPS C A.01 1.01 56310M7800\x00 4BPMC101',
b'\xf1\x00BDm MDPS C A.01 1.03 56310M7800\x00 4BPMC103',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00BD LKAS AT USA LHD 1.00 1.02 95740-M6000 J31',
b'\xf1\x00BD LKAS AT USA LHD 1.00 1.04 95740-M6000 J33',
b'\xf1\x00BDP LKAS AT USA LHD 1.00 1.05 99211-M6500 744',
],
@@ -712,6 +741,27 @@ FW_VERSIONS = {
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00SX2EMFC AT KOR LHD 1.00 1.00 99211-BF000 230410',
b'\xf1\x00SX2EMFC AT USA LHD 1.00 1.02 99211-BF000 230823',
],
},
CAR.HYUNDAI_KONA_2ND_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00SX2 MFC AT USA LHD 1.00 1.03 99211-BE000 230517',
b'\xf1\x00SX2 MFC AT USA LHD 1.00 1.07 99211-BE000 240611',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00SX2_ RDR ----- 1.00 1.02 99110-BE000 ',
b'\xf1\x00SX2_ RDR ----- 1.00 1.02 99110-BE500 ',
],
},
CAR.HYUNDAI_KONA_HEV_2ND_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00SX2HMFC AT AUS RHD 1.00 1.00 99211-BE001 241015',
b'\xf1\x00SX2HMFC AT EUR RHD 1.00 1.04 99211-BE000 231010',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00SX2_ RDR ----- 1.00 1.02 99110-BE000 ',
b'\xf1\x00SX2_ RDR ----- 1.00 1.02 99110-BE500 ',
],
},
CAR.KIA_NIRO_EV: {
@@ -1066,6 +1116,7 @@ FW_VERSIONS = {
b'\xf1\x00CE MFC AT EUR LHD 1.00 1.04 99211-KL000 221213',
b'\xf1\x00CE MFC AT USA LHD 1.00 1.04 99211-KL000 221213',
b'\xf1\x00CE MFC AT USA LHD 1.00 1.06 99211-KL000 230915',
b'\xf1\x00CE MFC AT CAN LHD 1.00 1.06 99211-KL000 230915',
],
},
CAR.HYUNDAI_TUCSON_4TH_GEN: {
@@ -1129,6 +1180,186 @@ FW_VERSIONS = {
b'\xf1\x00NQ5__ 1.00 1.04 99110CH100 ',
],
},
CAR.HYUNDAI_SANTA_FE_HEV_5TH_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00MX5HMFC AT CAN LHD 1.00 1.07 99211-P6000 231218',
b'\xf1\x00MX5HMFC AT KOR LHD 1.00 1.07 99211-P6000 231218',
b'\xf1\x00MX5HMFC AT USA LHD 1.00 1.06 99211-R6000 231218',
b'\xf1\x00MX5HMFC AT USA LHD 1.00 1.09 99211-R6200 250519',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MX5_ RDR ----- 1.00 1.01 99110-P6000 ',
b'\xf1\x00MX5_ RDR ----- 1.00 1.01 99110-R6000 ',
],
},
CAR.HYUNDAI_SONATA_2024: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00DN8 MFC AT KOR LHD 1.00 1.01 99211-L1800 230512',
b'\xf1\x00DN8 MFC AT USA LHD 1.00 1.01 99211-L1800 230512',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00DN8_ RDR ----- 1.00 1.00 99110-L1800 ',
],
},
CAR.HYUNDAI_SONATA_HEV_2024: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00DN8HMFC AT KOR LHD 1.00 1.01 99211-L1800 230512',
b'\xf1\x00DN8HMFC AT USA LHD 1.00 1.01 99211-L1800 230512',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00DN8_ RDR ----- 1.00 1.00 99110-L1800 ',
],
},
CAR.HYUNDAI_IONIQ_5_PE: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NE__ RDR ----- 1.00 1.00 99110-PI000 ',
b'\xf1\x00NE__ RDR ----- 1.00 1.01 99110-GI500 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NE MFC AT USA LHD 1.00 1.01 99211-PI000 240905',
b'\xf1\x00NE MFC AT EUR LHD 1.00 1.03 99211-GI500 240809',
b'\xf1\x00NE MFC AT USA LHD 1.00 1.00 99211-PI010 250407',
b'\xf1\x00NE MFC AT EUR LHD 1.00 1.00 99211-GI510 250513',
],
},
CAR.HYUNDAI_IONIQ_5_N: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NE1N RDR ----- 1.00 1.00 99110-NI000 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NE1NMFC AT KOR LHD 1.00 1.04 99211-NI000 231219',
b'\xf1\x00NE1NMFC AT KOR LHD 1.00 1.00 99211-NI010 240712',
b'\xf1\x00NE1NMFC AT USA LHD 1.00 1.04 99211-NI000 231219',
],
},
CAR.HYUNDAI_IONIQ_9: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MEev RDR ----- 1.00 1.00 99110-GO000 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00ME MFC AT KOR LHD 1.00 1.00 99211-GO000 241007',
b'\xf1\x00ME MFC AT KOR LHD 1.00 1.01 99211-GO000 250103',
],
},
CAR.HYUNDAI_TUCSON_2025: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.01 99211-N7050 C5A',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NX4__ 1.00 1.03 99110N7100 ',
],
},
CAR.HYUNDAI_TUCSON_HEV_2025: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N7030 C55',
b'\xf1\x00NX4 FR_CMR AT EUR LHD 1.00 1.00 99211-N7030 C55',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NX4__ 1.00 1.02 99110N7000 ',
b'\xf1\x00NX4__ 1.00 1.02 99110N7100 ',
b'\xf1\x00NX4__ 1.00 1.03 99110N7100 ',
],
},
CAR.HYUNDAI_TUCSON_PHEV_2025: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NX4 FR_CMR AT CAN LHD 1.00 1.00 99211-N7030 C55',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NX4__ 1.00 1.02 99110N7100 ',
],
},
CAR.HYUNDAI_SANTA_CRUZ_2025: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N7030 C55',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NX4__ 1.00 1.00 99110K5500 ',
],
},
CAR.KIA_K4_2025: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CL4 MFC AT CAN LHD 1.00 1.02 99210-GG000 240708',
b'\xf1\x00CL4 MFC AT USA LHD 1.00 1.02 99210-GG000 240708',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00CL4_ RDR ----- 1.00 1.01 99110-GG000 ',
b'\xf1\x00CL4_ RDR ----- 1.00 1.01 99110-GG100 ',
],
},
CAR.KIA_K5_2025: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00DL3 MFC AT USA LHD 1.00 1.04 99210-L2500 240117',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00DL3_ RDR ----- 1.00 1.01 99110-L2500 ',
],
},
CAR.KIA_SPORTAGE_2026: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NQ51.011.021.012551000HKP_NQ524_50509099211P1110',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NQ5__ 1.00 1.04 99110P1100 ',
],
},
CAR.KIA_SORENTO_2024: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00MQ4 MFC AT AUS RHD 1.01 1.04 99210-P2550 231127',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MQ4_ RDR ----- 1.00 1.01 99110-P2500 ',
],
},
CAR.KIA_SORENTO_HEV_4TH_GEN_LFA2: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00MQ4HMFC AT USA LHD 1.00 1.00 99210-P2600 250617',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MQ4_ RDR ----- 1.00 1.01 99110-P2500 ',
],
},
CAR.KIA_EV6_2025: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00CV__ RDR ----- 1.00 1.00 99110-XG500 ',
b'\xf1\x00CV__ RDR ----- 1.00 1.01 99110-CV500 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CV MFC AT KOR LHD 1.00 1.01 99210-CV500 240405',
b'\xf1\x00CV MFC AT USA LHD 1.00 1.02 99210-XG500 241223',
],
},
CAR.KIA_EV9: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MV__ RDR ----- 1.00 1.02 99110-DO000 ',
b'\xf1\x00MV__ RDR ----- 1.00 1.03 99110-DO000 ',
b'\xf1\x00MV__ RDR ----- 1.00 1.04 99110-DO000 ',
b'\xf1\x00MV__ RDR ----- 1.00 1.02 99110-DO700 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00MV MFC AT KOR LHD 1.00 1.01 99211-DO000 230419',
b'\xf1\x00MV MFC AT USA LHD 1.00 1.02 99211-DO000 230616',
b'\xf1\x00MV MFC AT EUR LHD 1.00 1.02 99211-DO000 230616',
b'\xf1\x00MV MFC AT CAN LHD 1.00 1.00 99211-DO100 240403',
b'\xf1\x00MV MFC AT USA LHD 1.00 1.01 99211-XA000 241023',
b'\xf1\x00MV MFC AT CAN LHD 1.00 1.01 99211-DO100 241023',
],
},
CAR.GENESIS_GV70_ELECTRIFIED_2ND_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00JK MFC AT USA LHD 1.00 1.03 99211-DS600 241125',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00JK__ RDR ----- 1.00 1.01 99110-DS500 ',
],
},
CAR.GENESIS_GV80_2025: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00JX__ RDR ----- 1.00 1.03 99110-T6500 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00JX MFC AT USA LHD 1.00 1.03 99211-T6510 240124',
],
},
CAR.GENESIS_GV70_1ST_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00JK1 MFC AT CAN LHD 1.00 1.02 99211-IY000 230627',
@@ -1282,4 +1513,109 @@ FW_VERSIONS = {
b'\xf1\x00T01G00BL T01I00A1 DOS2T16X4XI00NS0\x99L\xeeq',
],
},
CAR.KIA_CEED_PHEV_2022_NON_SCC: {
(Ecu.eps, 0x7D4, None): [
b'\xf1\x00CD MDPS C 1.00 1.01 56310-XX000 4CPHC101',
],
(Ecu.fwdCamera, 0x7C4, None): [
b'\xf1\x00CDH LKAS AT EUR LHD 1.00 1.01 99211-CR700 931',
],
},
CAR.GENESIS_G70_2021_NON_SCC: {
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00IK MDPS R 1.00 1.08 57700-G9200 4I2CL108',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00IK__ SCC --CUP 1.00 1.02 96400-G9100 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00IK MFC MT USA LHD 1.00 1.01 95740-G9000 170920',
],
},
CAR.HYUNDAI_KONA_NON_SCC: {
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00OS MDPS C 1.00 1.05 56310J9030\x00 4OSDC105',
b'\xf1\x00OS MDPS C 1.00 1.04 56310J9030\x00 4OSDC104',
b'\xf1\x00OS MDPS C 1.00 1.05 56310/J9500 4OSDC105',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00OS9 LKAS AT USA LHD 1.00 1.00 95740-J9200 g30',
b'\xf1\x00OS9 LKAS AT AUS RHD 1.00 1.00 95740-J9200 g30',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00OS__ FCA --CUP 1.00 1.00 95655-J9100 ',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x006T6J0_C2\x00\x006T6K1051\x00\x00TOS4N20NS2\x00\x00\x00\x00',
b'\xf1\x006U2V0_C2\x00\x006U2V1051\x00\x00DOS4T16AS2\x00\x00\x00\x00',
],
},
CAR.KIA_FORTE_2019_NON_SCC: {
(Ecu.eps, 0x7D4, None): [
b'\xf1\x00BD MDPS C 1.00 1.04 56310/M6000 4BDDC104',
b'\xf1\x00BD MDPS C 1.00 1.05 56310/M6000 4BDDC105',
],
(Ecu.fwdCamera, 0x7C4, None): [
b'\xf1\x00BD LKAS AT USA LHD 1.00 1.02 95740-M6000 J31',
],
},
CAR.KIA_FORTE_2021_NON_SCC: {
(Ecu.eps, 0x7D4, None): [
b'\xf1\x00BD MDPS C 1.00 1.07 56310/M6300 4BDDC107',
b'\xf1\x00BD MDPS C 1.00 1.08 56310M6000\x00 4BDDC108',
],
(Ecu.fwdCamera, 0x7C4, None): [
b'\xf1\x00BD LKAS AT USA LHD 1.00 1.02 95740-M6000 J31',
b'\xf1\x00BD LKAS AT USA LHD 1.00 1.04 95740-M6000 J33',
],
},
CAR.KIA_SELTOS_2023_NON_SCC: {
(Ecu.abs, 0x7d1, None): [
b'\xf1\x00SP ESC \t 101\"\t\x01 58910-Q5510',
b'\xf1\x00SP ESC \r 100\"\x04\x01 58910-Q5510',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00SP2 MDPS C 1.00 1.04 56310Q5240 4SPSC104',
b'\xf1\x00SP2 MDPS C 1.00 1.01 56300Q5920 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00SP2 MFC AT USA LHD 1.00 1.03 99210-Q5500 230208',
b'\xf1\x00SP2 MFC AT AUS RHD 1.00 1.02 99210-Q5500 220624',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x006V2B0_C2\x00\x006V2D5051\x00\x00CSP2N20NL0\x00\x00\x00\x00',
b'\xf1\x006V2B0_C2\x00\x006V2D4051\x00\x00CSP2N20KL1\x00\x00\x00\x00',
],
},
CAR.HYUNDAI_ELANTRA_2022_NON_SCC: {
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00CN7 MDPS R 1.00 1.04 57700-IB000 4CNNP104',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.01 99210-AB000 210205',
b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.00 99210-IB000 210531',
],
(Ecu.abs, 0x7d1, None): [
b'\xf1\x00CN ESC \t 100!\x05\x01 58910-IB000',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x00T02601BL T02900A1 WCN7T20XXX900NS4\xf7\xccz\xf6',
],
},
CAR.HYUNDAI_ELANTRA_HEV_2022_NON_SCC: {
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00CN7 MDPS C 1.00 1.02 56310/BY050 4CNHC102',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.04 99210-AA000 210205',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x006U3L0_C2\x00\x006U3K3051\x00\x00HCN0G16NS0\x00\x00\x00\x00',
],
},
CAR.HYUNDAI_BAYON_1ST_GEN_NON_SCC: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00BC3 LKA AT EUR LHD 1.00 1.01 99211-Q0100 261',
],
},
}
+132 -9
View File
@@ -1,4 +1,5 @@
import crcmod
from opendbc.car.hyundai.hyundaicanfd import CanBus
from opendbc.car.hyundai.values import CAR, HyundaiFlags
hyundai_checksum = crcmod.mkCrcFun(0x11D, initCrc=0xFD, rev=False, xorOut=0xdf)
@@ -96,6 +97,47 @@ def create_lkas11(packer, frame, CP, apply_torque, steer_req,
return packer.make_can_msg("LKAS11", 0, values)
def create_checksum_can_canfd_blended(packer, bus, addr, values):
dat = packer.make_can_msg(addr, bus, values)[1]
return hyundai_checksum(dat[1:8])
def create_lkas11_can_canfd_blended(packer, frame, CP, apply_steer, steer_req,
torque_fault, lkas11, sys_warning, sys_state, enabled,
left_lane, right_lane,
left_lane_depart, right_lane_depart, msg_364):
bus = CanBus(CP).ECAN
values = {
"CF_Lkas_LdwsActivemode": int(left_lane) + (int(right_lane) << 1),
"CF_Lkas_LdwsLHWarning": left_lane_depart,
"CF_Lkas_LdwsRHWarning": right_lane_depart,
"CF_Lkas_FcwOpt_USM": 2 if enabled else 1,
"CR_Lkas_StrToqReq": apply_steer,
"CF_Lkas_ActToi": steer_req,
"CF_Lkas_ToiFlt": torque_fault,
"CF_Lkas_MsgCount": frame % 0x10,
"NEW_SIGNAL_1": 0,
"NEW_SIGNAL_5": 100,
}
values["CF_Lkas_Chksum"] = create_checksum_can_canfd_blended(packer, bus, "LKAS11", values)
alerts_364 = {k: v for k, v in msg_364.items() if k not in ("CHECKSUM", "COUNTER")} if msg_364 else {}
alerts_364.setdefault("BYTE2", 0)
alerts_364.setdefault("BYTE3", 0)
alerts_364.setdefault("DAW_Status", 0)
alerts_364["DAW_Warning"] = 0
alerts_364.setdefault("BYTE5", 0)
alerts_364.setdefault("BYTE6", 0)
alerts_364.setdefault("BYTE7", 0)
alerts_364["COUNTER"] = frame % 0x10
alerts_364["CHECKSUM"] = create_checksum_can_canfd_blended(packer, bus, "ALERTS_364", alerts_364)
return [
packer.make_can_msg("LKAS11", bus, values),
packer.make_can_msg("ALERTS_364", bus, alerts_364),
]
def create_clu11(packer, frame, clu11, button, CP):
values = {s: clu11[s] for s in [
"CF_Clu_CruiseSwState",
@@ -114,15 +156,80 @@ def create_clu11(packer, frame, clu11, button, CP):
values["CF_Clu_CruiseSwState"] = button
values["CF_Clu_AliveCnt1"] = frame % 0x10
# send buttons to camera on camera-scc based cars
bus = 2 if CP.flags & HyundaiFlags.CAMERA_SCC else 0
if CP.flags & HyundaiFlags.CAMERA_SCC:
bus = 2
elif CP.flags & HyundaiFlags.CAN_CANFD_BLENDED:
bus = CanBus(CP).ECAN
else:
bus = 0
return packer.make_can_msg("CLU11", bus, values)
def create_lfahda_mfc(packer, enabled):
def create_lfahda_mfc(packer, enabled, frame=None, CP=None, lfa_icon=None):
if lfa_icon is None:
lfa_icon = 2 if enabled else 0
values = {
"LFA_Icon_State": 2 if enabled else 0,
"LFA_Icon_State": lfa_icon,
}
return packer.make_can_msg("LFAHDA_MFC", 0, values)
can_canfd_blended = CP is not None and bool(CP.flags & HyundaiFlags.CAN_CANFD_BLENDED)
bus = CanBus(CP).ECAN if can_canfd_blended else 0
if can_canfd_blended:
values["COUNTER"] = 0 if frame is None else frame % 0x10
values["CHECKSUM"] = create_checksum_can_canfd_blended(packer, bus, "LFAHDA_MFC", values)
return packer.make_can_msg("LFAHDA_MFC", bus, values)
def create_acc_commands_can_canfd_blended(packer, enabled, accel, upper_jerk, idx, hud_control, set_speed,
stopping, long_override, use_fca, CP):
commands = []
bus = CanBus(CP).ECAN
scc11_values = {
"aReqRaw": accel,
"aReqValue": accel,
"JerkUpperLimit": upper_jerk,
"JerkLowerLimit": 5.0,
"ComfortBandUpper": 0.0,
"ComfortBandLower": 0.0,
"COUNTER": idx % 0x10,
}
scc11_values["CHECKSUM"] = create_checksum_can_canfd_blended(packer, bus, "SCC11", scc11_values)
commands.append(packer.make_can_msg("SCC11", bus, scc11_values))
scc12_values = {
"MainMode_ACC": 1,
"ACCMode_Inactive": 0 if enabled else 1,
"TauGapSet": hud_control.leadDistanceBars,
"VSetDis": set_speed if enabled else 0,
"ACC_ObjDist": 1,
"ACCMode": 2 if enabled and long_override else 1 if enabled else 0,
"StopReq": 1 if stopping else 0,
"ACC_ObjDist_Ref": 1,
"COUNTER": idx % 0x10,
}
scc12_values["CHECKSUM"] = create_checksum_can_canfd_blended(packer, bus, "SCC12", scc12_values)
commands.append(packer.make_can_msg("SCC12", bus, scc12_values))
scc14_values = {
"ACC_ObjRelSpd": 0,
"ObjValid": 1,
"ObjStatus": 1,
"COUNTER": idx % 0x10,
}
scc14_values["CHECKSUM"] = create_checksum_can_canfd_blended(packer, bus, "SCC14", scc14_values)
commands.append(packer.make_can_msg("SCC14", bus, scc14_values))
if use_fca and not (CP.flags & HyundaiFlags.CAMERA_SCC):
fca11_values = {
"cr_vsm_deccmd": 255,
"cf_vsm_deccmdact": 127,
"COUNTER": idx % 0x10,
}
fca11_values["CHECKSUM"] = create_checksum_can_canfd_blended(packer, bus, "FCA11", fca11_values)
commands.append(packer.make_can_msg("FCA11", bus, fca11_values))
return commands
def create_acc_commands(packer, enabled, accel, upper_jerk, idx, hud_control, set_speed, stopping, long_override, use_fca, CP):
@@ -149,11 +256,10 @@ def create_acc_commands(packer, enabled, accel, upper_jerk, idx, hud_control, se
"CR_VSM_Alive": idx % 0xF,
}
# show AEB disabled indicator on dash with SCC12 if not sending FCA messages.
# these signals also prevent a TCS fault on non-FCA cars with alpha longitudinal
# Keep ESC/TCS happy on non-FCA cars without explicitly showing the disabled AEB icon.
if not use_fca:
scc12_values["CF_VSM_ConfMode"] = 1
scc12_values["AEB_Status"] = 1 # AEB disabled
scc12_values["AEB_Status"] = 2
scc12_dat = packer.make_can_msg("SCC12", 0, scc12_values)[1]
scc12_values["CR_VSM_ChkSum"] = 0x10 - sum(sum(divmod(i, 16)) for i in scc12_dat) % 0x10
@@ -179,7 +285,7 @@ def create_acc_commands(packer, enabled, accel, upper_jerk, idx, hud_control, se
"CR_FCA_Alive": idx % 0xF,
"PAINT1_Status": 1,
"FCA_DrvSetStatus": 1,
"FCA_Status": 1, # AEB disabled
"FCA_Status": 2,
}
fca11_dat = packer.make_can_msg("FCA11", 0, fca11_values)[1]
fca11_values["CR_FCA_ChkSum"] = hyundai_checksum(fca11_dat[:7])
@@ -203,7 +309,7 @@ def create_acc_opt(packer, CP):
if not (CP.flags & HyundaiFlags.CAMERA_SCC):
fca12_values = {
"FCA_DrvSetState": 2,
"FCA_USM": 1, # AEB disabled
"FCA_USM": 2,
}
commands.append(packer.make_can_msg("FCA12", 0, fca12_values))
@@ -215,3 +321,20 @@ def create_frt_radar_opt(packer):
"CF_FCA_Equip_Front_Radar": 1,
}
return packer.make_can_msg("FRT_RADAR11", 0, frt_radar11_values)
def create_radar_aux_messages(packer, CAN, frame):
commands = []
for addr, freq, values in (
("RADAR_0x363", 2, {"FCA_ESA": 1}),
("RADAR_0x398", 5, {"BYTE4": 0x80, "BYTE5": 0x10}),
):
if frame % freq != 0:
continue
msg_values = values | {"COUNTER": frame % 0x10}
msg_values["CHECKSUM"] = create_checksum_can_canfd_blended(packer, CAN.ECAN, addr, msg_values)
commands.append(packer.make_can_msg(addr, CAN.ECAN, msg_values))
return commands
+426 -25
View File
@@ -1,6 +1,7 @@
import copy
import numpy as np
from opendbc.car import CanBusBase
from opendbc.car import CanBusBase, CanData
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.crc import CRC16_XMODEM
from opendbc.car.hyundai.values import HyundaiFlags
@@ -91,24 +92,37 @@ def _create_angle_adas_cmd_msg(packer, CAN, apply_angle: float, lat_active: bool
return packer.make_can_msg("ADAS_CMD_35_10ms", CAN.ECAN, values)
def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque, apply_angle):
common_values = {
def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque, apply_angle,
lfa_base_values=None, lkas_base_values=None, lka_icon=None):
if lka_icon is None:
lka_icon = 2 if enabled else 1
control_values = {
"LKA_MODE": 2,
"LKA_ICON": 2 if enabled else 1,
"LKA_ICON": lka_icon,
"TORQUE_REQUEST": 0 if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING else apply_torque,
"LKA_ASSIST": 0,
"STEER_REQ": 0 if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING else (1 if lat_active else 0),
"STEER_MODE": 0,
"HAS_LANE_SAFETY": 0, # hide LKAS settings
"NEW_SIGNAL_2": 0,
"DAMP_FACTOR": 100, # can potentially tuned for better perf [3, 200]
}
lkas_values = copy.copy(common_values)
lkas_values["LKA_AVAILABLE"] = 0
if lkas_base_values:
lkas_values = {k: v for k, v in lkas_base_values.items() if k not in ("CHECKSUM", "COUNTER")}
lkas_values.update(control_values)
else:
lkas_values = copy.copy(control_values)
lkas_values["LKA_AVAILABLE"] = 0
lfa_values = copy.copy(common_values)
lfa_values["NEW_SIGNAL_1"] = 0
if lfa_base_values:
# Preserve stock UI/status fields and only override the actuation-relevant signals.
lfa_values = {k: v for k, v in lfa_base_values.items() if k not in ("CHECKSUM", "COUNTER")}
lfa_values.update(control_values)
else:
lfa_values = copy.copy(control_values)
lfa_values["HAS_LANE_SAFETY"] = 0 # hide LKAS settings
lfa_values["NEW_SIGNAL_1"] = 0
lfa_values["NEW_SIGNAL_2"] = 0
lfa_values["DAMP_FACTOR"] = 100 # can potentially tuned for better perf [3, 200]
if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING and CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT:
lkas_values["ADAS_StrAnglReqVal"] = apply_angle
@@ -124,6 +138,9 @@ def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque,
else:
if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
if CP.flags & HyundaiFlags.SEND_LFA:
# Some CAN-FD angle-steering trims still expect the stock-style LFA status/UI
# message to remain present even though angle actuation comes through ADAS_CMD.
ret.append(packer.make_can_msg("LFA", CAN.ECAN, lfa_values))
ret.append(_create_angle_adas_cmd_msg(packer, CAN, apply_angle, lat_active, apply_torque))
else:
ret.append(_create_angle_lfa_msg(packer, CAN, lfa_values, apply_angle, lat_active, apply_torque))
@@ -146,12 +163,15 @@ def create_suppress_lfa(packer, CAN, lfa_block_msg, lka_steering_alt):
return packer.make_can_msg(suppress_msg, CAN.ACAN, values)
def create_buttons(packer, CP, CAN, cnt, btn):
values = {
def create_buttons(packer, CP, CAN, cnt, btn=0, base_values=None, left_paddle=False, right_paddle=False):
values = {k: v for k, v in base_values.items() if k not in ("_CHECKSUM", "COUNTER")} if base_values else {}
values.update({
"COUNTER": cnt,
"SET_ME_1": 1,
"CRUISE_BUTTONS": btn,
}
"LEFT_PADDLE": int(left_paddle),
"RIGHT_PADDLE": int(right_paddle),
})
bus = CAN.ECAN if CP.flags & HyundaiFlags.CANFD_LKA_STEERING else CAN.CAM
return packer.make_can_msg("CRUISE_BUTTONS", bus, values)
@@ -188,41 +208,403 @@ def create_acc_cancel(packer, CP, CAN, cruise_info_copy):
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
def create_lfahda_cluster(packer, CAN, enabled):
values = {
def create_lfahda_cluster(packer, CAN, enabled, base_values=None, lfa_icon=None):
if lfa_icon is None:
lfa_icon = 2 if enabled else 0
values = {k: v for k, v in base_values.items() if k not in ("CHECKSUM", "COUNTER")} if base_values else {}
values.update({
"HDA_ICON": 1 if enabled else 0,
"LFA_ICON": 2 if enabled else 0,
}
"LFA_ICON": lfa_icon,
})
return packer.make_can_msg("LFAHDA_CLUSTER", CAN.ECAN, values)
def create_acc_control(packer, CAN, enabled, accel_last, accel, stopping, gas_override, set_speed, hud_control):
def create_ccnc(packer, CAN, openpilot_longitudinal, enabled, hud, left_blinker, right_blinker, msg_161, msg_162, msg_1b5,
is_metric, out, main_cruise_enabled, lfa_icon):
for fault in ("FAULT_LSS", "FAULT_HDA", "FAULT_DAS", "FAULT_LFA", "FAULT_DAW", "FAULT_ESS"):
msg_162[fault] = 0
if msg_161["ALERTS_2"] == 5:
msg_161.update({"ALERTS_2": 0, "SOUNDS_2": 0})
if msg_161["ALERTS_3"] == 17:
msg_161["ALERTS_3"] = 0
if msg_161["ALERTS_5"] in (2, 5):
msg_161["ALERTS_5"] = 0
if msg_161["SOUNDS_4"] == 2 and msg_161["LFA_ICON"] in (3, 0):
msg_161["SOUNDS_4"] = 0
lane_change_speed_min = 8.9408
any_blinker = left_blinker or right_blinker
curvature = {i: (31 if i == -1 else 13 - abs(i + 15)) if i < 0 else 15 + i for i in range(-15, 16)}
msg_161.update({
"DAW_ICON": 0,
"LKA_ICON": 0,
"LFA_ICON": 2 if lfa_icon else 0,
"CENTERLINE": 1 if lfa_icon else 0,
"LANELINE_CURVATURE": curvature.get(max(-15, min(int(out.steeringAngleDeg / 4.5), 15)), 14) if lfa_icon and not any_blinker else 15,
"LANELINE_LEFT": 0 if not lfa_icon else 1 if not hud.leftLaneVisible else 4 if hud.leftLaneDepart else 6 if any_blinker else 2,
"LANELINE_RIGHT": 0 if not lfa_icon else 1 if not hud.rightLaneVisible else 4 if hud.rightLaneDepart else 6 if any_blinker else 2,
"LCA_LEFT_ICON": 0 if not lfa_icon or out.vEgo < lane_change_speed_min else 1 if out.leftBlindspot else 2 if any_blinker else 4,
"LCA_RIGHT_ICON": 0 if not lfa_icon or out.vEgo < lane_change_speed_min else 1 if out.rightBlindspot else 2 if any_blinker else 4,
"LCA_LEFT_ARROW": 2 if left_blinker else 0,
"LCA_RIGHT_ARROW": 2 if right_blinker else 0,
})
if lfa_icon and any_blinker:
left_lane_raw = msg_1b5["Info_LftLnPosVal"]
right_lane_raw = msg_1b5["Info_RtLnPosVal"]
scale_per_m = 15 / 1.7
left_lane = abs(int(round(15 + (left_lane_raw - 1.7) * scale_per_m)))
right_lane = abs(int(round(15 + (right_lane_raw - 1.7) * scale_per_m)))
if msg_1b5["Info_LftLnQualSta"] not in (2, 3):
left_lane = 0
if msg_1b5["Info_RtLnQualSta"] not in (2, 3):
right_lane = 0
if left_lane_raw == -2.0248375:
left_lane = 30 - right_lane
if right_lane_raw == 2.0248375:
right_lane = 30 - left_lane
if left_lane_raw == right_lane_raw == 0:
left_lane = right_lane = 15
elif left_lane_raw == 0:
left_lane = 30 - right_lane
elif right_lane_raw == 0:
right_lane = 30 - left_lane
total = left_lane + right_lane
if total == 0:
left_lane = right_lane = 15
else:
left_lane = round((left_lane / total) * 30)
right_lane = 30 - left_lane
msg_161["LANELINE_LEFT_POSITION"] = left_lane
msg_161["LANELINE_RIGHT_POSITION"] = right_lane
if hud.leftLaneDepart or hud.rightLaneDepart:
msg_162["VIBRATE"] = 1
if openpilot_longitudinal:
if msg_161["ALERTS_3"] in (1, 2, 3, 4, 7, 8, 9, 10):
msg_161["ALERTS_3"] = 0
if msg_161["ALERTS_5"] == 4:
msg_161["ALERTS_5"] = 0
if msg_161["SOUNDS_3"] == 5:
msg_161["SOUNDS_3"] = 0
cruise_speed = round(out.vCruiseCluster * (1 if is_metric else CV.KPH_TO_MPH))
msg_161.update({
"SETSPEED": 3 if enabled else 1,
"SETSPEED_HUD": 0 if not main_cruise_enabled else 2 if enabled else 1,
"SETSPEED_SPEED": 255 if not main_cruise_enabled else (40 if is_metric else 25) if cruise_speed > (145 if is_metric else 90) else cruise_speed,
"DISTANCE": hud.leadDistanceBars,
"DISTANCE_SPACING": 0 if not main_cruise_enabled else 1 if enabled else 3,
"DISTANCE_LEAD": 0 if not main_cruise_enabled else 2 if enabled and hud.leadVisible else 1 if hud.leadVisible else 0,
"DISTANCE_CAR": 0 if not main_cruise_enabled else 2 if enabled else 1,
"SLA_ICON": 0,
"NAV_ICON": 0,
"TARGET": 0,
})
msg_162["LEAD"] = 0 if not main_cruise_enabled else 2 if enabled else 1
msg_162["LEAD_DISTANCE"] = msg_1b5["Longitudinal_Distance"]
return [packer.make_can_msg(msg, CAN.ECAN, values) for msg, values in (("CCNC_0x161", msg_161), ("CCNC_0x162", msg_162))]
def create_blindspot_status_messages(packer, CAN, rear_values, front_corner_values,
left_blindspot=False, right_blindspot=False,
left_blinker=False, right_blinker=False):
# Reuse the last known-good payload but regenerate the rolling counter/checksum.
rear = {k: v for k, v in rear_values.items() if k not in ("CHECKSUM", "COUNTER")}
front = {k: v for k, v in front_corner_values.items() if k not in ("CHECKSUM", "COUNTER")}
left_state = 2 if left_blindspot and left_blinker else (1 if left_blindspot else 0)
right_state = 2 if right_blindspot and right_blinker else (1 if right_blindspot else 0)
rear["BCW_Sta"] = int(left_blindspot or right_blindspot)
rear["BCW_LtIndSta"] = left_state
rear["BCW_RtIndSta"] = right_state
rear["BCW_IndSta"] = max(left_state, right_state)
rear["OSMrrLamp_LtIndSta"] = left_state
rear["OSMrrLamp_RtIndSta"] = right_state
# Keep the older fields aligned where they still correlate on some platforms.
rear["FL_INDICATOR"] = left_state
rear["FR_INDICATOR"] = right_state
if "NEW_SIGNAL_3" not in front:
front["NEW_SIGNAL_3"] = 1
return [
packer.make_can_msg("BLINDSPOTS_REAR_CORNERS", CAN.ECAN, rear),
packer.make_can_msg("BLINDSPOTS_FRONT_CORNER_1", CAN.ECAN, front),
]
IONIQ_6_CLUSTER_BLINDSPOT_31A = {
"right": (
bytes.fromhex("fa7c10f0f0ffff03898aff0b0a8678ff000000007e0055550000000000000000"),
bytes.fromhex("ac0e11f0f0ffff03898aff0c0a8678ff000000007e0055550000000000000000"),
bytes.fromhex("76ce12f0f0ffff03898aff0b0a8678ff000000007e0055550000000000000000"),
bytes.fromhex("309713f0f0ffff03898aff0b0a8678ff000000007e0055550000000000000000"),
bytes.fromhex("d32214f0f0ffff03898aff0c0a8678ff000000007e0055550000000000000000"),
bytes.fromhex("957b15f0f0ffff03898aff0c0a8678ff000000007e0055550000000000000000"),
),
"left": (
bytes.fromhex("851828f0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
bytes.fromhex("c34129f0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
bytes.fromhex("09aa2af0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
bytes.fromhex("4ff32bf0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
bytes.fromhex("bc6d2cf0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
bytes.fromhex("fa342df0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
),
}
IONIQ_6_CLUSTER_BLINDSPOT_3B5 = {
"right": (
bytes.fromhex("caa95c00000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("8cf05d00000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("461b5e00000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("00425f00000000464600000000000000d7020000000069070000000000000000"),
),
"left": (
bytes.fromhex("2c69c500000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("e682c600000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("21afc800000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("67f6c900000000464600000000000000da020000000069070000000000000000"),
),
}
IONIQ_6_CLUSTER_LANE_CHANGE_3C1 = {
"right": {
"trigger": bytes.fromhex("e910300041000000"),
"steady": bytes.fromhex("ab20300001000000"),
},
"left": {
"trigger": bytes.fromhex("3d40304010000000"),
"steady": bytes.fromhex("3e50300000000000"),
},
}
# Captured from a stock Ioniq 6 route that shows the cluster lane-change animation
# on ECAN after the trigger/hold 0x3C1 states above.
IONIQ_6_CLUSTER_LANE_CHANGE_3B5 = {
"right": (
bytes.fromhex("9f687600000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("d9317700000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("58457800000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("1e1c7900000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("d4f77a00000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("92ae7b00000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("61307c00000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("27697d00000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("ed827e00000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("abdb7f00000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("dd978000000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("9bce8100000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("51258200000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("177c8300000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("e4e28400000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("18ba8500000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("68508600000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("94088700000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("157c8800000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("53258900000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("99ce8a00000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("df978b00000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("2c098c00000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("6a508d00000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("a0bb8e00000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("e6e28f00000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("a2529000000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("e40b9100000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("2ee09200000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("d2b89300000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("9b279400000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("677f9500000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("17959600000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("ebcd9700000000464600000000000000d7020000000069070000000000000000"),
bytes.fromhex("d0b89800000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("96e19900000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("5c0a9a00000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("1a539b00000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("e9cd9c00000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("af949d00000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("657f9e00000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("23269f00000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("cc0fa000000000464600000000000000d8020000000069070000000000000000"),
bytes.fromhex("bba6a100000000464600000000000000d9020000000069070000000000000000"),
bytes.fromhex("714da200000000464600000000000000d9020000000069070000000000000000"),
bytes.fromhex("3714a300000000464600000000000000d9020000000069070000000000000000"),
),
"left": (
bytes.fromhex("e682c600000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("d2dbc700000000464600000000000000d9020000000069070000000000000000"),
bytes.fromhex("21afc800000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("67f6c900000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("ad1dca00000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("eb44cb00000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("18dacc00000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("5e83cd00000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("9468ce00000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("d231cf00000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("9681d000000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("d0d8d100000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("1a33d200000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("5c6ad300000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("ddf4d400000000464600000000000000d9020000000069070000000000000000"),
bytes.fromhex("e9add500000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("2346d600000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("651fd700000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("e46bd800000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("d032d900000000464600000000000000d9020000000069070000000000000000"),
bytes.fromhex("68d9da00000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("2e80db00000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("dd1edc00000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("9b47dd00000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("51acde00000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("17f5df00000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("f8dce000000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("be85e100000000464600000000000000da020000000069070000000000000000"),
bytes.fromhex("746ee200000000464600000000000000da020000000069070000000000000000"),
),
}
IONIQ_6_CLUSTER_LANE_CHANGE_31A = {
"right": (
bytes.fromhex("eb4518f0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
bytes.fromhex("757119f0f0ffff03898aff0a088678ff000000007e0055550000000000000000"),
bytes.fromhex("bf9a1af0f0ffff03898aff0a088678ff000000007e0055550000000000000000"),
bytes.fromhex("f9c31bf0f0ffff03898aff0a088678ff000000007e0055550000000000000000"),
bytes.fromhex("0a5d1cf0f0ffff03898aff0a088678ff000000007e0055550000000000000000"),
bytes.fromhex("4c041df0f0ffff03898aff0a088678ff000000007e0055550000000000000000"),
bytes.fromhex("86ef1ef0f0ffff03898aff0a088678ff000000007e0055550000000000000000"),
bytes.fromhex("18db1ff0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
bytes.fromhex("f7f220f0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
),
"left": (
bytes.fromhex("851828f0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
bytes.fromhex("c34129f0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
bytes.fromhex("09aa2af0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
bytes.fromhex("4ff32bf0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
bytes.fromhex("bc6d2cf0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
bytes.fromhex("fa342df0f0ffff03898aff0a098678ff000000007e0055550000000000000000"),
),
}
IONIQ_6_CLUSTER_LANE_CHANGE_3C1_BURST = {
0: "trigger",
4: "trigger",
7: "steady",
10: "steady",
13: "steady",
16: "steady",
}
IONIQ_6_CLUSTER_LANE_CHANGE_3C1_STEADY_START = 34
IONIQ_6_CLUSTER_LANE_CHANGE_3B5_START = 4
IONIQ_6_CLUSTER_LANE_CHANGE_31A_START = 30
def create_ioniq_6_cluster_blindspot_messages(CAN, frame, left_blindspot=False, right_blindspot=False,
left_blinker=False, right_blinker=False):
side = None
if left_blindspot and not right_blindspot:
side = "left"
elif right_blindspot and not left_blindspot:
side = "right"
elif left_blindspot and right_blindspot:
if left_blinker and not right_blinker:
side = "left"
elif right_blinker and not left_blinker:
side = "right"
if side is None:
return []
ret = []
if frame % 20 == 0:
seq_3b5 = IONIQ_6_CLUSTER_BLINDSPOT_3B5[side]
ret.append((0x3B5, seq_3b5[(frame // 20) % len(seq_3b5)], CAN.ECAN))
if frame % 100 == 0:
seq_31a = IONIQ_6_CLUSTER_BLINDSPOT_31A[side]
ret.append((0x31A, seq_31a[(frame // 100) % len(seq_31a)], CAN.ECAN))
return ret
def create_ioniq_6_cluster_lane_change_messages(CAN, frame, side=None):
if side not in IONIQ_6_CLUSTER_LANE_CHANGE_3C1:
return []
ret = []
frame_phase = IONIQ_6_CLUSTER_LANE_CHANGE_3C1_BURST.get(frame)
if frame_phase is None and frame >= IONIQ_6_CLUSTER_LANE_CHANGE_3C1_STEADY_START and \
(frame - IONIQ_6_CLUSTER_LANE_CHANGE_3C1_STEADY_START) % 20 == 0:
frame_phase = "steady"
if frame_phase is not None:
ret.append((0x3C1, IONIQ_6_CLUSTER_LANE_CHANGE_3C1[side][frame_phase], CAN.ECAN))
if frame >= IONIQ_6_CLUSTER_LANE_CHANGE_3B5_START and (frame - IONIQ_6_CLUSTER_LANE_CHANGE_3B5_START) % 20 == 0:
seq_3b5 = IONIQ_6_CLUSTER_LANE_CHANGE_3B5[side]
ret.append((0x3B5, seq_3b5[((frame - IONIQ_6_CLUSTER_LANE_CHANGE_3B5_START) // 20) % len(seq_3b5)], CAN.ECAN))
if frame >= IONIQ_6_CLUSTER_LANE_CHANGE_31A_START and (frame - IONIQ_6_CLUSTER_LANE_CHANGE_31A_START) % 100 == 0:
seq_31a = IONIQ_6_CLUSTER_LANE_CHANGE_31A[side]
ret.append((0x31A, seq_31a[((frame - IONIQ_6_CLUSTER_LANE_CHANGE_31A_START) // 100) % len(seq_31a)], CAN.ECAN))
return ret
def create_acc_control(packer, CAN, enabled, accel_last, accel, stopping, gas_override, set_speed, hud_control,
main_mode_acc=1, jerk_lower=None, jerk_upper=None, direct_accel=False,
lead_distance=None, lead_rel_speed=None, lead_visible=None, cruise_info=None):
jerk = 5
jn = jerk / 50
if not enabled or gas_override:
a_val, a_raw = 0, 0
elif direct_accel:
a_raw = accel
a_val = accel
else:
a_raw = accel
a_val = np.clip(accel, accel_last - jn, accel_last + jn)
if lead_distance is None and lead_rel_speed is None and lead_visible is None:
acc_obj_dist = 1.0
acc_obj_rel_spd = 0.0
obj_valid = 0
obj_status = 2
else:
lead_visible = bool(lead_visible)
acc_obj_dist = float(np.clip(lead_distance if lead_visible else 0.0, 0.0, 204.7))
acc_obj_rel_spd = float(np.clip(lead_rel_speed if lead_visible else 0.0, -16.4, 34.7))
obj_valid = int(not lead_visible)
obj_status = 0 if not (enabled and lead_visible) else (1 if gas_override else 2)
values = {
"ACCMode": 0 if not enabled else (2 if gas_override else 1),
"MainMode_ACC": 1,
"MainMode_ACC": main_mode_acc,
"StopReq": 1 if stopping else 0,
"aReqValue": a_val,
"aReqRaw": a_raw,
"VSetDis": set_speed,
"JerkLowerLimit": jerk if enabled else 1,
"JerkUpperLimit": 3.0,
"JerkLowerLimit": jerk_lower if jerk_lower is not None else (jerk if enabled else 1),
"JerkUpperLimit": jerk_upper if jerk_upper is not None else 3.0,
"ACC_ObjDist": 1,
"ObjValid": 0,
"OBJ_STATUS": 2,
"ACC_ObjDist": acc_obj_dist,
"ACC_ObjRelSpd": acc_obj_rel_spd,
"ObjValid": obj_valid,
"OBJ_STATUS": obj_status,
"SET_ME_2": 0x4,
"SET_ME_3": 0x3,
"SET_ME_TMP_64": 0x64,
"DISTANCE_SETTING": hud_control.leadDistanceBars,
}
if cruise_info:
values.update({s: cruise_info[s] for s in ("ACC_ObjDist", "ACC_ObjRelSpd")})
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
@@ -320,3 +702,22 @@ def hkg_can_fd_checksum(address: int, sig, d: bytearray) -> int:
elif len(d) == 32:
crc ^= 0x9F5B
return crc
# Ioniq 5/6 / HKG LKA-steering: ADAS_DRV broadcasts ACCELERATOR_BRAKE_ALT (0x100) on bus 0.
# The front radar uses this as its "host alive" heartbeat. When we disable ADAS_DRV the
# radar stops publishing real object tracks. Spoof this message ourselves with valid CRC
# and current pedal state so the radar keeps tracking.
# Length is 24 bytes on Ioniq 6 (DBC declares 32 for ICE Hyundais, but EV firmware uses 24).
# Byte template captured from a real ADAS broadcast; bytes 6-23 appear static / config.
_ACCEL_BRAKE_ALT_TEMPLATE = bytes.fromhex("000000020000fcff000000000020000055ff000068000000")
def create_accelerator_brake_alt_spoof(bus: int, counter: int, brake_pressed: bool, accelerator_pressed: bool) -> CanData:
d = bytearray(_ACCEL_BRAKE_ALT_TEMPLATE)
d[2] = counter & 0xFF # COUNTER (bit 16, 8-bit)
d[4] = (d[4] & ~0x01) | (0x01 if brake_pressed else 0x00) # BRAKE_PRESSED (bit 32)
d[22] = (d[22] & ~0x01) | (0x01 if accelerator_pressed else 0x00) # ACCELERATOR_PEDAL_PRESSED (bit 176)
crc = hkg_can_fd_checksum(0x100, None, d)
d[0] = crc & 0xFF
d[1] = (crc >> 8) & 0xFF
return CanData(0x100, bytes(d), bus)
+99 -32
View File
@@ -1,12 +1,16 @@
import time
from opendbc.car import Bus, get_safety_config, structs, uds
from opendbc.car import get_safety_config, structs, uds
from opendbc.car.hyundai.hyundaicanfd import CanBus
from opendbc.car.hyundai.values import HyundaiFlags, CAR, DBC, \
from opendbc.car.hyundai.values import HyundaiFlags, CAR, CarControllerParams, \
CANFD_UNSUPPORTED_LONGITUDINAL_CAR, \
CANFD_SECURITYACCESS_CAR, \
UNSUPPORTED_LONGITUDINAL_CAR, HyundaiSafetyFlags
from opendbc.car.hyundai.radar_interface import RADAR_START_ADDR
from opendbc.car.interfaces import CarInterfaceBase
CANFD_RADAR_LIVE_LONGITUDINAL_CAR, \
RADAR_LIVE_LONGITUDINAL_CAR, \
UNSUPPORTED_LONGITUDINAL_CAR, HyundaiSafetyFlags, \
HyundaiStarPilotSafetyFlags, \
hyundai_cancel_button_enables_cruise
from opendbc.car.hyundai.radar_interface import get_radar_track_config
from opendbc.car.interfaces import CarInterfaceBase, ACCEL_MIN
from opendbc.car.disable_ecu import disable_ecu, ecu_log
from opendbc.car.hyundai.carcontroller import CarController
from opendbc.car.hyundai.carstate import CarState
@@ -20,6 +24,38 @@ ENABLE_BUTTONS = (ButtonType.accelCruise, ButtonType.decelCruise, ButtonType.can
# Track when ECU disable happened - used to permanently suppress CAN errors from disabled ECU
ECU_DISABLE_TIMESTAMP = 0.0
KONA_NON_SCC_FCA_RADAR_ADDR = 0x602
def apply_platform_longitudinal_params(ret: structs.CarParams) -> None:
if not (ret.flags & HyundaiFlags.CANFD):
return
ret.startingState = True
ret.startAccel = 1.0
ret.longitudinalActuatorDelay = 0.5
ret.vEgoStopping = 0.3
ret.vEgoStarting = 0.1
ret.stoppingDecelRate = 0.4
def apply_ecu_disable_failure_fallback(CP: structs.CarParams, params) -> None:
params.put_bool("EcuDisableFailed", True)
CP.safetyConfigs[-1].safetyParam &= ~HyundaiSafetyFlags.LONG.value
CP.openpilotLongitudinalControl = False
CP.pcmCruise = True
def detect_kona_non_scc_radar_fca(candidate, fingerprint, car_fw) -> bool:
if candidate != CAR.HYUNDAI_KONA_NON_SCC:
return False
if any(fw.ecu == Ecu.fwdRadar for fw in car_fw):
return True
# Some non-SCC Kona trims have FCA radar tracks without SCC. Use PT FCA11
# status on those cars; camera-bus FCA11 is not continuously published.
return KONA_NON_SCC_FCA_RADAR_ADDR in fingerprint[1]
class CarInterface(CarInterfaceBase):
@@ -27,6 +63,10 @@ class CarInterface(CarInterfaceBase):
CarController = CarController
RadarInterface = RadarInterface
@staticmethod
def get_pid_accel_limits(CP, current_speed, cruise_speed):
return ACCEL_MIN, CarControllerParams.ACCEL_MAX
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
ret.brand = "hyundai"
@@ -46,6 +86,9 @@ class CarInterface(CarInterfaceBase):
# this needs to be figured out for cars without an ADAS ECU
# Cars in CANFD_SECURITYACCESS_CAR are known to have ADAS ECUs that work with SecurityAccess
ret.alphaLongitudinalAvailable = False
if lka_steering and ret.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
# Angle-steering LKA platforms still need stock longitudinal validation.
ret.alphaLongitudinalAvailable = False
ret.enableBsm = 0x1ba in fingerprint[CAN.ECAN]
@@ -91,19 +134,23 @@ class CarInterface(CarInterfaceBase):
if ret.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
ret.steerControlType = structs.CarParams.SteerControlType.angle
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CANFD_ANGLE_STEERING.value
if ret.flags & HyundaiFlags.CCNC and not ret.flags & HyundaiFlags.CANFD_LKA_STEERING:
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CCNC.value
else:
# Shared configuration for non CAN-FD cars
ret.alphaLongitudinalAvailable = candidate not in UNSUPPORTED_LONGITUDINAL_CAR
ret.enableBsm = 0x58b in fingerprint[0]
ret.enableBsm = 0x58b in fingerprint[CAN.ECAN]
# Send LFA message on cars with HDA
if 0x485 in fingerprint[2]:
if 0x485 in fingerprint[CAN.CAM]:
ret.flags |= HyundaiFlags.SEND_LFA.value
# These cars use the FCA11 message for the AEB and FCW signals, all others use SCC12
if 0x38d in fingerprint[0] or 0x38d in fingerprint[2]:
if 0x38d in fingerprint[CAN.ECAN] or 0x38d in fingerprint[CAN.CAM]:
ret.flags |= HyundaiFlags.USE_FCA.value
if detect_kona_non_scc_radar_fca(candidate, fingerprint, car_fw):
ret.flags |= HyundaiFlags.NON_SCC_RADAR_FCA.value
if ret.flags & HyundaiFlags.LEGACY:
# these cars require a special panda safety mode due to missing counters and checksums in the messages
@@ -114,15 +161,13 @@ class CarInterface(CarInterfaceBase):
if ret.flags & HyundaiFlags.CAMERA_SCC:
ret.safetyConfigs[0].safetyParam |= HyundaiSafetyFlags.CAMERA_SCC.value
# These cars have the LFA button on the steering wheel
if 0x391 in fingerprint[0]:
ret.flags |= HyundaiFlags.HAS_LDA_BUTTON.value
if candidate == CAR.KIA_FORTE:
has_scc_fw = any(fw.ecu == Ecu.fwdRadar for fw in car_fw)
has_scc_can = any(addr in fingerprint[bus] for bus in (0, 2) for addr in (0x420, 0x421))
if not (has_scc_fw or has_scc_can):
ret.flags |= HyundaiFlags.NON_SCC.value
# These cars expose an LKAS/LFA steering-wheel button that StarPilot can customize.
if 0x391 in fingerprint[0] or ret.flags & HyundaiFlags.CAN_CANFD_BLENDED:
ret.safetyConfigs[-1].safetyParam |= HyundaiStarPilotSafetyFlags.HAS_LDA_BUTTON.value
if ret.flags & HyundaiFlags.CAN_CANFD_BLENDED:
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CAN_CANFD_BLENDED.value
if hyundai_cancel_button_enables_cruise(candidate):
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CANCEL_BTN_ENABLE.value
# Common lateral control setup
@@ -141,21 +186,21 @@ class CarInterface(CarInterfaceBase):
# see https://github.com/commaai/opendbc/pull/1137/
ret.dashcamOnly = True
if ret.flags & HyundaiFlags.NON_SCC:
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.NON_SCC.value
# Common longitudinal control setup
ret.radarUnavailable = RADAR_START_ADDR not in fingerprint[1] or Bus.radar not in DBC[ret.carFingerprint]
radar_config = get_radar_track_config(ret.carFingerprint)
radar_tracks_available = radar_config is not None and radar_config.start_addr in fingerprint[radar_config.bus]
ret.radarUnavailable = not radar_tracks_available
if ret.flags & HyundaiFlags.NON_SCC:
ret.alphaLongitudinalAvailable = False
ret.openpilotLongitudinalControl = alpha_long and ret.alphaLongitudinalAvailable
# When longitudinal is enabled, we disable the ADAS ECU which stops radar messages
# Force radarUnavailable to prevent CAN Error from missing radar messages
if ret.openpilotLongitudinalControl:
if ret.openpilotLongitudinalControl and not (candidate in RADAR_LIVE_LONGITUDINAL_CAR and radar_tracks_available):
ret.radarUnavailable = True
ret.pcmCruise = not ret.openpilotLongitudinalControl
ret.startingState = True
ret.vEgoStarting = 0.1
ret.startAccel = 1.0
ret.longitudinalActuatorDelay = 0.5
apply_platform_longitudinal_params(ret)
if ret.openpilotLongitudinalControl:
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.LONG.value
@@ -168,6 +213,27 @@ class CarInterface(CarInterfaceBase):
# Car specific configuration overrides
if candidate == CAR.GENESIS_G90:
ret.safetyConfigs[-1].safetyParam |= HyundaiStarPilotSafetyFlags.HAS_LDA_BUTTON.value
ret.safetyConfigs[-1].safetyParam |= HyundaiStarPilotSafetyFlags.AOL_LKAS_ON_ENGAGE.value
ret.stoppingDecelRate = 0.55
ret.vEgoStopping = 0.8
if candidate == CAR.HYUNDAI_PALISADE_2023:
ret.startAccel = 1.3
ret.stopAccel = -0.85
ret.stoppingDecelRate = 0.35
ret.vEgoStarting = 0.5
ret.vEgoStopping = 0.35
if candidate == CAR.HYUNDAI_IONIQ_6:
ret.longitudinalActuatorDelay = 0.6
if candidate == CAR.KIA_NIRO_PHEV_2022:
ret.stopAccel = -1.4
ret.stoppingDecelRate = 0.5
ret.vEgoStopping = 0.7
if candidate == CAR.KIA_OPTIMA_G4_FL:
ret.steerActuatorDelay = 0.2
@@ -185,7 +251,7 @@ class CarInterface(CarInterfaceBase):
params = Params()
if communication_control is None:
if CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
if CP.carFingerprint in CANFD_RADAR_LIVE_LONGITUDINAL_CAR:
# Don't use 0x80 suppress bit so we can read the ECU response.
# Use ENABLE_RX_DISABLE_TX (0x01) so the ECU can still receive from rear radars for BSM
# while blocking SCC TX.
@@ -197,7 +263,7 @@ class CarInterface(CarInterfaceBase):
ecu_log(f"=== init() called: opLong={CP.openpilotLongitudinalControl}, flags=0x{CP.flags:x}, safetyParam={CP.safetyConfigs[-1].safetyParam} ===")
if CP.openpilotLongitudinalControl and not (CP.flags & (HyundaiFlags.CANFD_CAMERA_SCC | HyundaiFlags.CAMERA_SCC)):
addr, bus = 0x7d0, CanBus(CP).ECAN if CP.flags & HyundaiFlags.CANFD else 0
addr, bus = 0x7d0, CanBus(CP).ECAN if CP.flags & (HyundaiFlags.CANFD | HyundaiFlags.CAN_CANFD_BLENDED) else 0
if CP.flags & HyundaiFlags.CANFD_LKA_STEERING.value:
addr, bus = 0x730, CanBus(CP).ECAN
@@ -205,7 +271,8 @@ class CarInterface(CarInterfaceBase):
# If it fails (READY mode returns NRC 0x22, or timeout), strip LONG safety flag
# so panda forwards stock SCC messages normally (lateral-only mode).
ecu_log(f"=== ECU DISABLE attempt: addr=0x{addr:x}, bus={bus} ===")
ecu_disabled = disable_ecu(can_recv, can_send, bus=bus, addr=addr, com_cont_req=communication_control)
ecu_disabled = disable_ecu(can_recv, can_send, bus=bus, addr=addr, com_cont_req=communication_control,
reset=bool(CP.flags & HyundaiFlags.CAN_CANFD_BLENDED))
if CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
# Ioniq 6: track success/failure to auto-switch between openpilot long and stock ACC
@@ -215,15 +282,15 @@ class CarInterface(CarInterfaceBase):
params.put_bool("ExperimentalMode", True)
ecu_log("=== ECU DISABLE SUCCESS - Longitudinal + Experimental ENABLED ===")
else:
params.put_bool("EcuDisableFailed", True)
CP.safetyConfigs[-1].safetyParam &= ~HyundaiSafetyFlags.LONG.value
apply_ecu_disable_failure_fallback(CP, params)
ecu_log(f"=== ECU DISABLE FAILED - safetyParam stripped to {CP.safetyConfigs[-1].safetyParam}, lateral-only mode ===")
else:
# Other cars: just log, don't change safety params or params store
if ecu_disabled:
params.put_bool("EcuDisableFailed", False)
ecu_log("=== ECU DISABLE SUCCESS ===")
else:
ecu_log("=== ECU DISABLE FAILED ===")
apply_ecu_disable_failure_fallback(CP, params)
ecu_log(f"=== ECU DISABLE FAILED - safetyParam stripped to {CP.safetyConfigs[-1].safetyParam}, lateral-only mode ===")
# for blinkers
if CP.flags & HyundaiFlags.ENABLE_BLINKERS:
@@ -1,40 +1,121 @@
import math
from dataclasses import dataclass
from opendbc.can import CANParser
from opendbc.can.dbc import DBC as DBCReader
from opendbc.can.parser import get_raw_value
from opendbc.car import Bus, structs
from opendbc.car.interfaces import RadarInterfaceBase
from opendbc.car.hyundai.values import DBC
from opendbc.car.hyundai.values import CAR, DBC, HYUNDAI_MANDO_FRONT_RADAR_DBC, HYUNDAI_MRREVO14F_RADAR_DBC, \
HYUNDAI_MRR30_RADAR_DBC, HYUNDAI_MRR35_RADAR_DBC
from openpilot.common.swaglog import cloudlog
RADAR_START_ADDR = 0x500
RADAR_MSG_COUNT = 32
G90_RADAR_MSG_COUNT = 64
MRREVO14F_RADAR_START_ADDR = 0x602
MRREVO14F_RADAR_MSG_COUNT = 16
MRR30_RADAR_START_ADDR = 0x210
MRR30_RADAR_MSG_COUNT = 16
MRR35_RADAR_START_ADDR = 0x3A5
MRR35_RADAR_MSG_COUNT = 32
@dataclass(frozen=True)
class RadarTrackConfig:
start_addr: int
msg_count: int
radar_type: str
bus: int = 1
frequency: int = 50
parser_msg_count: int | None = None
@property
def can_parser_msg_count(self) -> int:
return self.parser_msg_count if self.parser_msg_count is not None else self.msg_count
RADAR_TRACK_CONFIGS = {
HYUNDAI_MANDO_FRONT_RADAR_DBC: RadarTrackConfig(RADAR_START_ADDR, RADAR_MSG_COUNT, "mando"),
HYUNDAI_MRREVO14F_RADAR_DBC: RadarTrackConfig(MRREVO14F_RADAR_START_ADDR, MRREVO14F_RADAR_MSG_COUNT, "mrrevo14f"),
HYUNDAI_MRR30_RADAR_DBC: RadarTrackConfig(MRR30_RADAR_START_ADDR, MRR30_RADAR_MSG_COUNT, "mrr30", bus=0),
HYUNDAI_MRR35_RADAR_DBC: RadarTrackConfig(MRR35_RADAR_START_ADDR, MRR35_RADAR_MSG_COUNT, "mrr35", bus=0, frequency=20),
}
# POC for parsing corner radars: https://github.com/commaai/openpilot/pull/24221/
def get_radar_can_parser(CP):
if Bus.radar not in DBC[CP.carFingerprint]:
def get_radar_track_config(car_fingerprint) -> RadarTrackConfig | None:
radar_dbc = DBC[car_fingerprint].get(Bus.radar)
if car_fingerprint == CAR.GENESIS_G90 and radar_dbc == HYUNDAI_MANDO_FRONT_RADAR_DBC:
return RadarTrackConfig(RADAR_START_ADDR, G90_RADAR_MSG_COUNT, "mando", parser_msg_count=RADAR_MSG_COUNT)
return RADAR_TRACK_CONFIGS.get(radar_dbc)
def get_radar_can_parser(CP, radar_config):
if radar_config is None:
return None
messages = [(f"RADAR_TRACK_{addr:x}", 50) for addr in range(RADAR_START_ADDR, RADAR_START_ADDR + RADAR_MSG_COUNT)]
return CANParser(DBC[CP.carFingerprint][Bus.radar], messages, 1)
messages = [(f"RADAR_TRACK_{addr:x}", radar_config.frequency)
for addr in range(radar_config.start_addr, radar_config.start_addr + radar_config.can_parser_msg_count)]
return CANParser(DBC[CP.carFingerprint][Bus.radar], messages, radar_config.bus)
class RadarInterface(RadarInterfaceBase):
def __init__(self, CP):
super().__init__(CP)
self.radar_config = get_radar_track_config(CP.carFingerprint)
self.updated_messages = set()
self.trigger_msg = RADAR_START_ADDR + RADAR_MSG_COUNT - 1
self.trigger_msg = (self.radar_config.start_addr + self.radar_config.can_parser_msg_count - 1
if self.radar_config is not None else RADAR_START_ADDR)
self.track_id = 0
self.g90_extended_mando = (CP.carFingerprint == CAR.GENESIS_G90 and self.radar_config is not None and
self.radar_config.msg_count > self.radar_config.can_parser_msg_count)
self.g90_mando_signals = []
if self.g90_extended_mando:
radar_dbc = DBCReader(DBC[CP.carFingerprint][Bus.radar])
self.g90_mando_signals = list(radar_dbc.addr_to_msg[RADAR_START_ADDR].sigs.values())
self.radar_off_can = CP.radarUnavailable
self.rcp = get_radar_can_parser(CP)
# Probe whether radar tracks still exist on the Ioniq 6 while OP long is active,
# without changing planner behavior yet.
self.ioniq_6_radar_probe = CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and CP.openpilotLongitudinalControl and self.radar_off_can
self.ioniq_6_radar_probe_logged = False
self.ioniq_6_radar_probe_updates = 0
self.rcp = get_radar_can_parser(CP, self.radar_config)
# Precompute (addr, "RADAR_TRACK_xxx") pairs once. _update runs on the
# CAN-driven card loop (core 4, shared with controlsd/selfdrived), so avoid
# rebuilding 32 f-strings per radar frame.
self.track_addrs: list[tuple[int, str]] = []
if self.radar_config is not None:
self.track_addrs = [(addr, f"RADAR_TRACK_{addr:x}")
for addr in range(self.radar_config.start_addr,
self.radar_config.start_addr + self.radar_config.can_parser_msg_count)]
def update(self, can_strings):
if self.ioniq_6_radar_probe and self.rcp is not None and not self.ioniq_6_radar_probe_logged:
vls = self.rcp.update(can_strings)
self.updated_messages.update(vls)
self.ioniq_6_radar_probe_updates += 1
if self.trigger_msg in self.updated_messages:
rr = self._update(self.updated_messages)
cloudlog.warning(f"Ioniq 6 radar probe: saw {len(rr.points)} radar tracks with radarUnavailable forced on")
self.updated_messages.clear()
self.ioniq_6_radar_probe_logged = True
elif self.ioniq_6_radar_probe_updates >= 500:
cloudlog.warning("Ioniq 6 radar probe: no radar track frames observed after startup")
self.ioniq_6_radar_probe_logged = True
self.updated_messages.clear()
if self.radar_off_can or (self.rcp is None):
return super().update(None)
vls = self.rcp.update(can_strings)
self.updated_messages.update(vls)
if self.g90_extended_mando:
self._update_g90_extended_mando_tracks(can_strings)
if self.trigger_msg not in self.updated_messages:
return None
@@ -44,6 +125,46 @@ class RadarInterface(RadarInterfaceBase):
return rr
def _decode_g90_mando_values(self, dat: bytes):
vals = {}
for sig in self.g90_mando_signals:
raw = get_raw_value(dat, sig)
if sig.is_signed:
raw -= ((raw >> (sig.size - 1)) & 1) * (1 << sig.size)
vals[sig.name] = raw * sig.factor + sig.offset
return vals
def _update_g90_extended_mando_tracks(self, can_strings):
if self.radar_config is None:
return
start_addr = self.radar_config.start_addr + self.radar_config.can_parser_msg_count
end_addr = self.radar_config.start_addr + self.radar_config.msg_count
for _, frames in can_strings:
for address, dat, src in frames:
if src != self.radar_config.bus or not (start_addr <= address < end_addr) or len(dat) < 8:
continue
self.updated_messages.add(address)
msg = self._decode_g90_mando_values(dat)
valid = msg["STATE"] in (3, 4)
if valid:
if address not in self.pts:
self.pts[address] = structs.RadarData.RadarPoint()
self.pts[address].trackId = self.track_id
self.track_id += 1
azimuth = math.radians(msg["AZIMUTH"])
self.pts[address].measured = True
self.pts[address].dRel = math.cos(azimuth) * msg["LONG_DIST"]
self.pts[address].yRel = 0.5 * -math.sin(azimuth) * msg["LONG_DIST"]
self.pts[address].vRel = msg["REL_SPEED"]
self.pts[address].aRel = msg["REL_ACCEL"]
self.pts[address].yvRel = float("nan")
elif address in self.pts:
del self.pts[address]
def _update(self, updated_messages):
ret = structs.RadarData()
if self.rcp is None:
@@ -52,8 +173,77 @@ class RadarInterface(RadarInterfaceBase):
if not self.rcp.can_valid:
ret.errors.canError = True
for addr in range(RADAR_START_ADDR, RADAR_START_ADDR + RADAR_MSG_COUNT):
msg = self.rcp.vl[f"RADAR_TRACK_{addr:x}"]
if self.radar_config is None:
return ret
radar_type = self.radar_config.radar_type
vl = self.rcp.vl
for addr, track_name in self.track_addrs:
msg = vl[track_name]
if radar_type == "mrr30":
for i in ("1", "2"):
track_key = addr * 2 + int(i) - 1
if track_key not in self.pts:
self.pts[track_key] = structs.RadarData.RadarPoint()
self.pts[track_key].trackId = self.track_id
self.track_id += 1
valid = msg[f"{i}_STATE"] in (3, 4)
if valid:
pt = self.pts[track_key]
pt.measured = True
pt.dRel = msg[f"{i}_LONG_DIST"]
pt.yRel = msg[f"{i}_LAT_DIST"]
pt.vRel = msg[f"{i}_REL_SPEED"]
pt.aRel = float("nan")
pt.yvRel = float("nan")
else:
del self.pts[track_key]
continue
if radar_type == "mrrevo14f":
for i in ("1", "2"):
track_key = addr * 2 + int(i) - 1
valid = msg[f"{i}_DISTANCE"] != 255.75
if valid:
pt = self.pts.get(track_key)
if pt is None:
pt = structs.RadarData.RadarPoint()
pt.trackId = self.track_id
self.track_id += 1
self.pts[track_key] = pt
pt.measured = True
pt.dRel = msg[f"{i}_DISTANCE"]
pt.yRel = msg[f"{i}_LATERAL"]
pt.vRel = msg[f"{i}_SPEED"]
pt.aRel = float("nan")
pt.yvRel = float("nan")
elif track_key in self.pts:
del self.pts[track_key]
continue
if radar_type == "mrr35":
# Most of the 32 channels are empty each frame. Only allocate a point
# when the channel is valid; drop it otherwise. Avoids the per-frame
# alloc-then-delete churn on the ~27 idle channels.
if msg["STATE"] in (3, 4):
pt = self.pts.get(addr)
if pt is None:
pt = structs.RadarData.RadarPoint()
pt.trackId = self.track_id
self.track_id += 1
self.pts[addr] = pt
pt.measured = True
pt.dRel = msg["LONG_DIST"]
pt.yRel = msg["LAT_DIST"]
pt.vRel = msg["REL_SPEED"]
pt.aRel = msg["REL_ACCEL"]
pt.yvRel = float("nan")
elif addr in self.pts:
del self.pts[addr]
continue
if addr not in self.pts:
self.pts[addr] = structs.RadarData.RadarPoint()
File diff suppressed because it is too large Load Diff
+348 -23
View File
@@ -6,16 +6,29 @@ from opendbc.car import ACCELERATION_DUE_TO_GRAVITY, Bus, CarSpecs, DbcDict, Pla
from opendbc.car.lateral import AngleSteeringLimits, ISO_LATERAL_ACCEL, ISO_LATERAL_JERK
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.structs import CarParams
from opendbc.car.docs_definitions import CarHarness, CarDocs, CarParts
from opendbc.car.docs_definitions import CarHarness, CarDocs, CarParts, SupportType
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, p16
Ecu = CarParams.Ecu
AVERAGE_ROAD_ROLL = 0.06 # conservative roll margin used by Hyundai CAN-FD angle steering safety
SPORTAGE_HEV_2026_MAX_LATERAL_ACCEL = 3.6
SPORTAGE_HEV_2026_BASE_LATERAL_JERK = 3.25
SPORTAGE_HEV_2026_LOW_SPEED_JERK_BOOST = 0.55
SPORTAGE_HEV_2026_LOW_SPEED_JERK_SPEED = 11.0
SPORTAGE_HEV_2026_LOW_SPEED_JERK_WIDTH = 5.0
SPORTAGE_HEV_2026_MAX_ANGLE_RATE = 6.5
SPORTAGE_HEV_2026_STEER_ANGLE_MAX = 220.0
HYUNDAI_MANDO_FRONT_RADAR_DBC = "hyundai_kia_mando_front_radar_generated"
HYUNDAI_MRREVO14F_RADAR_DBC = "hyundai_mrrevo14f_radar_generated"
HYUNDAI_MRR30_RADAR_DBC = "hyundai_mrr30_radar_generated"
HYUNDAI_MRR35_RADAR_DBC = "hyundai_mrr35_radar_generated"
GENESIS_G90_STEER_MAX = 461
HYUNDAI_PALISADE_2023_STEER_MAX = 485
class CarControllerParams:
ACCEL_MIN = -3.5 # m/s
ACCEL_MAX = 2.0 # m/s
ACCEL_MAX = 3.5 # m/s
ANGLE_LIMITS: AngleSteeringLimits = AngleSteeringLimits(
180,
([], []),
@@ -53,17 +66,26 @@ class CarControllerParams:
if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
self.STEER_THRESHOLD = 175
# The Sportage angle port is rough at low speed on the higher global jerk limit.
# Keep the branch-wide higher limit for other cars, but restore the older calmer
# jerk ceiling on this port only.
# The Sportage angle port still needs more authority in real turns than the
# fully calmed branch-wide ceiling allows, but the old low-speed jerk boost
# made the 3-20 degree band angry and ping-pongy as the car slowed down.
# Split the difference:
# - keep a calmer low-speed boost that fades out earlier
# - give the car a little more true turn headroom through accel/rate limits
if CP.carFingerprint == CAR.KIA_SPORTAGE_HEV_2026:
sportage_low_speed_weight = min(max((SPORTAGE_HEV_2026_LOW_SPEED_JERK_SPEED - vEgoRaw) / SPORTAGE_HEV_2026_LOW_SPEED_JERK_WIDTH, 0.0), 1.0)
sportage_lateral_jerk = SPORTAGE_HEV_2026_BASE_LATERAL_JERK + (SPORTAGE_HEV_2026_LOW_SPEED_JERK_BOOST * sportage_low_speed_weight)
self.ANGLE_LIMITS = replace(self.ANGLE_LIMITS,
MAX_LATERAL_JERK=3.0 + (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL))
STEER_ANGLE_MAX=SPORTAGE_HEV_2026_STEER_ANGLE_MAX,
MAX_LATERAL_ACCEL=SPORTAGE_HEV_2026_MAX_LATERAL_ACCEL + (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL),
MAX_LATERAL_JERK=sportage_lateral_jerk + (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL),
MAX_ANGLE_RATE=SPORTAGE_HEV_2026_MAX_ANGLE_RATE)
# To determine the limit for your car, find the maximum value that the stock LKAS will request.
# If the max stock LKAS request is <384, add your car to this list.
elif CP.carFingerprint in (CAR.GENESIS_G80, CAR.HYUNDAI_ELANTRA, CAR.HYUNDAI_ELANTRA_GT_I30, CAR.HYUNDAI_IONIQ,
CAR.HYUNDAI_IONIQ_EV_LTD, CAR.HYUNDAI_SANTA_FE_PHEV_2022, CAR.HYUNDAI_SONATA_LF, CAR.KIA_FORTE, CAR.KIA_NIRO_PHEV,
CAR.KIA_FORTE_2019_NON_SCC, CAR.KIA_FORTE_2021_NON_SCC,
CAR.KIA_OPTIMA_H, CAR.KIA_OPTIMA_H_G4_FL, CAR.KIA_SORENTO):
self.STEER_MAX = 255
@@ -78,6 +100,16 @@ class CarControllerParams:
self.STEER_DELTA_UP = 2
self.STEER_DELTA_DOWN = 3
elif CP.flags & HyundaiFlags.CAN_CANFD_BLENDED:
self.STEER_MAX = HYUNDAI_PALISADE_2023_STEER_MAX
self.STEER_DRIVER_ALLOWANCE = 50
self.STEER_THRESHOLD = 150
self.STEER_DELTA_UP = 2
self.STEER_DELTA_DOWN = 3
elif CP.carFingerprint == CAR.GENESIS_G90:
self.STEER_MAX = GENESIS_G90_STEER_MAX
# Default for most HKG
else:
self.STEER_MAX = 384
@@ -95,6 +127,10 @@ class HyundaiSafetyFlags(IntFlag):
FCEV_GAS = 256
ALT_LIMITS_2 = 512
CANFD_ANGLE_STEERING = 1024
NON_SCC = 4096
CAN_CANFD_BLENDED = 8192
CANCEL_BTN_ENABLE = 16384
CCNC = 32768
class HyundaiStarPilotSafetyFlags(IntFlag):
@@ -158,8 +194,6 @@ class HyundaiFlags(IntFlag):
MIN_STEER_32_MPH = 2 ** 23
HAS_LDA_BUTTON = 2 ** 24
FCEV = 2 ** 25
ALT_LIMITS_2 = 2 ** 26
@@ -170,30 +204,71 @@ class HyundaiFlags(IntFlag):
# Hyundai CAN-FD angle-based steering path used on newer ADAS platforms.
CANFD_ANGLE_STEERING = 2 ** 28
# Palisade/Telluride 2023+ uses CAN routing with CAN-FD-style checksums.
CAN_CANFD_BLENDED = 2 ** 29
# Non-SCC platforms do not all share the same FCA source or availability.
NON_SCC_NO_FCA = 2 ** 30
NON_SCC_RADAR_FCA = 2 ** 31
# Connected Car Navigation Cockpit CAN-FD platforms.
CCNC = 2 ** 24
@dataclass
class HyundaiCarDocs(CarDocs):
package: str = "Smart Cruise Control (SCC)"
@dataclass
class HyundaiNonSccCarDocs(CarDocs):
package: str = "No Smart Cruise Control (Non-SCC)"
support_type: SupportType = SupportType.COMMUNITY
support_link: str = "community"
@dataclass
class HyundaiPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "hyundai_kia_generic"})
radar_dbc: str | None = None
def init(self):
if self.flags & HyundaiFlags.MANDO_RADAR:
self.dbc_dict = {Bus.pt: "hyundai_kia_generic", Bus.radar: 'hyundai_kia_mando_front_radar_generated'}
if self.radar_dbc is not None:
self.dbc_dict = {Bus.pt: "hyundai_kia_generic", Bus.radar: self.radar_dbc}
elif self.flags & HyundaiFlags.MANDO_RADAR:
self.dbc_dict = {Bus.pt: "hyundai_kia_generic", Bus.radar: HYUNDAI_MANDO_FRONT_RADAR_DBC}
if self.flags & HyundaiFlags.MIN_STEER_32_MPH:
self.specs = self.specs.override(minSteerSpeed=32 * CV.MPH_TO_MS)
if self.flags & HyundaiFlags.CAN_CANFD_BLENDED:
self.dbc_dict = {Bus.pt: "hyundai_palisade_2023_generated"}
@dataclass
class HyundaiCanFDPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "hyundai_canfd_generated"})
radar_dbc: str | None = None
def init(self):
self.flags |= HyundaiFlags.CANFD
if self.radar_dbc is not None:
self.dbc_dict = {Bus.pt: "hyundai_canfd_generated", Bus.radar: self.radar_dbc}
@dataclass
class HyundaiNonSccPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "hyundai_kia_generic"})
radar_dbc: str | None = None
def init(self):
if self.radar_dbc is not None:
self.dbc_dict = {Bus.pt: "hyundai_kia_generic", Bus.radar: self.radar_dbc}
self.flags |= HyundaiFlags.NON_SCC
if self.flags & HyundaiFlags.MIN_STEER_32_MPH:
self.specs = self.specs.override(minSteerSpeed=32 * CV.MPH_TO_MS)
class CAR(Platforms):
@@ -251,7 +326,7 @@ class CAR(Platforms):
HYUNDAI_IONIQ = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Ioniq Hybrid 2017-19", car_parts=CarParts.common([CarHarness.hyundai_c]))],
CarSpecs(mass=1490, wheelbase=2.7, steerRatio=13.73, tireStiffnessFactor=0.385),
flags=HyundaiFlags.HYBRID | HyundaiFlags.MIN_STEER_32_MPH,
flags=HyundaiFlags.HYBRID | HyundaiFlags.MIN_STEER_32_MPH | HyundaiFlags.MANDO_RADAR,
)
HYUNDAI_IONIQ_HEV_2022 = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Ioniq Hybrid 2020-22", car_parts=CarParts.common([CarHarness.hyundai_h]))],
@@ -288,6 +363,16 @@ class CAR(Platforms):
CarSpecs(mass=1491, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385),
flags=HyundaiFlags.CAMERA_SCC | HyundaiFlags.ALT_LIMITS_2,
)
HYUNDAI_KONA_2ND_GEN = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Kona (without HDA II) 2024-25", car_parts=CarParts.common([CarHarness.hyundai_l]))],
CarSpecs(mass=1590, wheelbase=2.66, steerRatio=13.6, tireStiffnessFactor=0.385),
flags=HyundaiFlags.CCNC,
)
HYUNDAI_KONA_HEV_2ND_GEN = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Kona Hybrid (without HDA II) 2024", car_parts=CarParts.common([CarHarness.hyundai_l]))],
CarSpecs(mass=1590, wheelbase=2.66, steerRatio=13.6, tireStiffnessFactor=0.385),
flags=HyundaiFlags.CCNC,
)
HYUNDAI_KONA_EV = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Kona Electric 2018-21", car_parts=CarParts.common([CarHarness.hyundai_g]))],
CarSpecs(mass=1685, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385),
@@ -297,12 +382,17 @@ class CAR(Platforms):
[HyundaiCarDocs("Hyundai Kona Electric 2022-23", car_parts=CarParts.common([CarHarness.hyundai_o]))],
CarSpecs(mass=1743, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385),
flags=HyundaiFlags.CAMERA_SCC | HyundaiFlags.EV | HyundaiFlags.ALT_LIMITS,
radar_dbc=HYUNDAI_MRREVO14F_RADAR_DBC,
)
HYUNDAI_KONA_EV_2ND_GEN = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Kona Electric (with HDA II, Korea only) 2023", video="https://www.youtube.com/watch?v=U2fOCmcQ8hw",
car_parts=CarParts.common([CarHarness.hyundai_r]))],
[
HyundaiCarDocs("Hyundai Kona Electric (with HDA II, Korea only) 2023", video="https://www.youtube.com/watch?v=U2fOCmcQ8hw",
car_parts=CarParts.common([CarHarness.hyundai_r])),
HyundaiCarDocs("Hyundai Kona Electric (without HDA II) 2024", car_parts=CarParts.common([CarHarness.hyundai_a])),
],
CarSpecs(mass=1740, wheelbase=2.66, steerRatio=13.6, tireStiffnessFactor=0.385),
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_NO_RADAR_DISABLE,
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_NO_RADAR_DISABLE | HyundaiFlags.CCNC,
radar_dbc=HYUNDAI_MRR35_RADAR_DBC,
)
HYUNDAI_KONA_HEV = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Kona Hybrid 2020", car_parts=CarParts.common([CarHarness.hyundai_i]))], # TODO: check packages,
@@ -324,17 +414,27 @@ class CAR(Platforms):
[HyundaiCarDocs("Hyundai Santa Fe 2021-23", "All", video="https://youtu.be/VnHzSTygTS4",
car_parts=CarParts.common([CarHarness.hyundai_l]))],
HYUNDAI_SANTA_FE.specs,
flags=HyundaiFlags.CHECKSUM_CRC8,
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8,
)
HYUNDAI_SANTA_FE_HEV_2022 = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Santa Fe Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l]))],
HYUNDAI_SANTA_FE.specs,
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
)
HYUNDAI_SANTA_FE_PHEV_2022 = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Santa Fe Plug-in Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l]))],
HYUNDAI_SANTA_FE.specs,
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
)
HYUNDAI_SANTA_FE_HEV_5TH_GEN = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Hyundai Santa Fe Hybrid (with HDA II & LFA2) 2024-25", "Highway Driving Assist II & Lane Follow Assist 2",
car_parts=CarParts.common([CarHarness.hyundai_p])),
HyundaiCarDocs("Hyundai Santa Fe Hybrid (without HDA II, LFA2) 2025-26", "Lane Follow Assist 2",
car_parts=CarParts.common([CarHarness.hyundai_l])),
],
CarSpecs(mass=2035, wheelbase=2.81, steerRatio=13.72),
flags=HyundaiFlags.CANFD_ANGLE_STEERING | HyundaiFlags.CCNC,
)
HYUNDAI_SONATA = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Sonata 2020-23", "All", video="https://www.youtube.com/watch?v=ix63r9kE3Fw",
@@ -342,6 +442,11 @@ class CAR(Platforms):
CarSpecs(mass=1513, wheelbase=2.84, steerRatio=13.27 * 1.15, tireStiffnessFactor=0.65), # 15% higher at the center seems reasonable
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8,
)
HYUNDAI_SONATA_2024 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Sonata (without HDA II) 2024-25", car_parts=CarParts.common([CarHarness.hyundai_a]))],
CarSpecs(mass=1556, wheelbase=2.84, steerRatio=12.81),
flags=HyundaiFlags.CCNC,
)
HYUNDAI_SONATA_LF = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Sonata 2018-19", car_parts=CarParts.common([CarHarness.hyundai_e]))],
CarSpecs(mass=1536, wheelbase=2.804, steerRatio=13.27 * 1.15), # 15% higher at the center seems reasonable
@@ -367,6 +472,16 @@ class CAR(Platforms):
CarSpecs(mass=1999, wheelbase=2.9, steerRatio=15.6 * 1.15, tireStiffnessFactor=0.63),
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8,
)
HYUNDAI_PALISADE_2023 = HyundaiPlatformConfig(
[
HyundaiCarDocs("Hyundai Palisade (without HDA II) 2023-25", "Highway Driving Assist",
car_parts=CarParts.common([CarHarness.hyundai_a])),
HyundaiCarDocs("Kia Telluride (without HDA II) 2023-25", "Highway Driving Assist",
car_parts=CarParts.common([CarHarness.hyundai_l])),
],
HYUNDAI_PALISADE.specs,
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.CAN_CANFD_BLENDED | HyundaiFlags.RADAR_SCC,
)
HYUNDAI_VELOSTER = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Veloster 2019-20", min_enable_speed=5. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_e]))],
CarSpecs(mass=2917 * CV.LB_TO_KG, wheelbase=2.8, steerRatio=13.75 * 1.15, tireStiffnessFactor=0.5),
@@ -377,6 +492,11 @@ class CAR(Platforms):
HYUNDAI_SONATA.specs,
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
)
HYUNDAI_SONATA_HEV_2024 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Sonata Hybrid (without HDA II) 2024-25", car_parts=CarParts.common([CarHarness.hyundai_a]))],
CarSpecs(mass=1616, wheelbase=2.84, steerRatio=13.27),
flags=HyundaiFlags.CCNC,
)
HYUNDAI_IONIQ_5 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Hyundai Ioniq 5 (Southeast Asia and Europe only) 2022-24", "All", car_parts=CarParts.common([CarHarness.hyundai_q])),
@@ -385,11 +505,37 @@ class CAR(Platforms):
],
CarSpecs(mass=1948, wheelbase=2.97, steerRatio=14.26, tireStiffnessFactor=0.65),
flags=HyundaiFlags.EV,
radar_dbc=HYUNDAI_MRR30_RADAR_DBC,
)
HYUNDAI_IONIQ_5_PE = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Hyundai Ioniq 5 PE (with HDA II & LFA2) 2025-26", "Highway Driving Assist II & Lane Follow Assist 2",
car_parts=CarParts.common([CarHarness.hyundai_q]))
],
HYUNDAI_IONIQ_5.specs,
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING,
radar_dbc=HYUNDAI_MRR30_RADAR_DBC,
)
HYUNDAI_IONIQ_5_N = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Ioniq 5 N (with HDA II) 2024", car_parts=CarParts.common([CarHarness.hyundai_s]))],
CarSpecs(mass=2205, wheelbase=3.00, steerRatio=14.26, tireStiffnessFactor=1.3),
flags=HyundaiFlags.EV | HyundaiFlags.CCNC,
radar_dbc=HYUNDAI_MRR30_RADAR_DBC,
)
HYUNDAI_IONIQ_6 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Ioniq 6 (with HDA II) 2023-24", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p]))],
HYUNDAI_IONIQ_5.specs,
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT | HyundaiFlags.CANFD_NO_RADAR_DISABLE,
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_NO_RADAR_DISABLE,
radar_dbc=HYUNDAI_MRR35_RADAR_DBC,
)
HYUNDAI_IONIQ_9 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Hyundai Ioniq 9 (with HDA II & LFA2) 2025-26", "Highway Driving Assist II & Lane Follow Assist 2",
car_parts=CarParts.common([CarHarness.hyundai_m]))
],
CarSpecs(mass=2700, wheelbase=3.13, steerRatio=16.02),
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING,
radar_dbc=HYUNDAI_MRR35_RADAR_DBC,
)
HYUNDAI_TUCSON_4TH_GEN = HyundaiCanFDPlatformConfig(
[
@@ -400,11 +546,31 @@ class CAR(Platforms):
],
CarSpecs(mass=1630, wheelbase=2.756, steerRatio=13.7, tireStiffnessFactor=0.385),
)
HYUNDAI_TUCSON_2025 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Tucson (without HDA II) 2025-26", car_parts=CarParts.common([CarHarness.hyundai_n]))],
CarSpecs(mass=1630, wheelbase=2.756, steerRatio=13.7, tireStiffnessFactor=0.385),
flags=HyundaiFlags.CCNC,
)
HYUNDAI_TUCSON_HEV_2025 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Tucson Hybrid (without HDA II) 2025-26", car_parts=CarParts.common([CarHarness.hyundai_n]))],
CarSpecs(mass=1630, wheelbase=2.756, steerRatio=13.7, tireStiffnessFactor=0.385),
flags=HyundaiFlags.CCNC,
)
HYUNDAI_TUCSON_PHEV_2025 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Tucson Plug-in Hybrid (without HDA II) 2025", car_parts=CarParts.common([CarHarness.hyundai_n]))],
CarSpecs(mass=1630, wheelbase=2.756, steerRatio=13.7, tireStiffnessFactor=0.385),
flags=HyundaiFlags.CCNC,
)
HYUNDAI_SANTA_CRUZ_1ST_GEN = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Santa Cruz 2022-24", car_parts=CarParts.common([CarHarness.hyundai_n]))],
# weight from Limited trim - the only supported trim, steering ratio according to Hyundai News https://www.hyundainews.com/assets/documents/original/48035-2022SantaCruzProductGuideSpecsv2081521.pdf
CarSpecs(mass=1870, wheelbase=3, steerRatio=14.2),
)
HYUNDAI_SANTA_CRUZ_2025 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Santa Cruz (without HDA II) 2025", car_parts=CarParts.common([CarHarness.hyundai_n]))],
CarSpecs(mass=1920, wheelbase=3, steerRatio=14.2),
flags=HyundaiFlags.CCNC,
)
HYUNDAI_CUSTIN_1ST_GEN = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Custin 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k]))],
CarSpecs(mass=1690, wheelbase=3.055, steerRatio=17), # mass: from https://www.hyundai-motor.com.tw/clicktobuy/custin#spec_0, steerRatio: from learner
@@ -419,11 +585,24 @@ class CAR(Platforms):
],
CarSpecs(mass=2878 * CV.LB_TO_KG, wheelbase=2.8, steerRatio=13.75, tireStiffnessFactor=0.5)
)
KIA_K4_2025 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia K4 (without HDA II) 2025", car_parts=CarParts.common([CarHarness.hyundai_a])),
HyundaiCarDocs("Kia K4 (with HDA II) 2025", car_parts=CarParts.common([CarHarness.hyundai_r])),
],
CarSpecs(mass=2987 * CV.LB_TO_KG, wheelbase=2.72, steerRatio=13.4),
flags=HyundaiFlags.CCNC,
)
KIA_K5_2021 = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia K5 2021-24", car_parts=CarParts.common([CarHarness.hyundai_a]))],
CarSpecs(mass=3381 * CV.LB_TO_KG, wheelbase=2.85, steerRatio=13.27, tireStiffnessFactor=0.5), # 2021 Kia K5 Steering Ratio (all trims)
flags=HyundaiFlags.CHECKSUM_CRC8,
)
KIA_K5_2025 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Kia K5 (without HDA II) 2025", car_parts=CarParts.common([CarHarness.hyundai_m]))],
CarSpecs(mass=3230 * CV.LB_TO_KG, wheelbase=2.85, steerRatio=13.27),
flags=HyundaiFlags.CCNC,
)
KIA_K5_HEV_2020 = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia K5 Hybrid 2020-22", car_parts=CarParts.common([CarHarness.hyundai_a]))],
KIA_K5_2021.specs,
@@ -516,6 +695,13 @@ class CAR(Platforms):
# weight from SX and above trims, average of FWD and AWD version, steering ratio according to Kia News https://www.kiamedia.com/us/en/models/sportage/2023/specifications
CarSpecs(mass=1725, wheelbase=2.756, steerRatio=13.6),
)
KIA_SPORTAGE_2026 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia Sportage (without HDA II) 2026", car_parts=CarParts.common([CarHarness.hyundai_n])),
],
CarSpecs(mass=1735, wheelbase=2.756, steerRatio=13.7),
flags=HyundaiFlags.CANFD_ANGLE_STEERING | HyundaiFlags.CCNC,
)
KIA_SPORTAGE_HEV_2026 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Kia Sportage Hybrid 2026", car_parts=CarParts.common([CarHarness.hyundai_n]))],
CarSpecs(mass=1812, wheelbase=2.756, steerRatio=13.7),
@@ -535,6 +721,11 @@ class CAR(Platforms):
CarSpecs(mass=3957 * CV.LB_TO_KG, wheelbase=2.81, steerRatio=13.5), # average of the platforms
flags=HyundaiFlags.RADAR_SCC,
)
KIA_SORENTO_2024 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Kia Sorento (without HDA II) 2024-25", car_parts=CarParts.common([CarHarness.hyundai_a]))],
CarSpecs(mass=3957 * CV.LB_TO_KG, wheelbase=2.81, steerRatio=13.5),
flags=HyundaiFlags.CCNC,
)
KIA_SORENTO_HEV_4TH_GEN = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia Sorento Hybrid 2021-23", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
@@ -543,6 +734,13 @@ class CAR(Platforms):
CarSpecs(mass=4395 * CV.LB_TO_KG, wheelbase=2.81, steerRatio=13.5), # average of the platforms
flags=HyundaiFlags.RADAR_SCC,
)
KIA_SORENTO_HEV_4TH_GEN_LFA2 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia Sorento Hybrid 2026", "All", car_parts=CarParts.common([CarHarness.hyundai_q])),
],
CarSpecs(mass=1970, wheelbase=2.814, steerRatio=13.27, tireStiffnessFactor=0.65),
flags=HyundaiFlags.CANFD_ANGLE_STEERING,
)
KIA_STINGER = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia Stinger 2018-20", video="https://www.youtube.com/watch?v=MJ94qoofYw0",
car_parts=CarParts.common([CarHarness.hyundai_c]))],
@@ -565,6 +763,23 @@ class CAR(Platforms):
],
CarSpecs(mass=2055, wheelbase=2.9, steerRatio=16, tireStiffnessFactor=0.65),
flags=HyundaiFlags.EV,
radar_dbc=HYUNDAI_MRR30_RADAR_DBC,
)
KIA_EV6_2025 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia EV6 (with HDA I) 2025", "Highway Driving Assist I", car_parts=CarParts.common([CarHarness.hyundai_p]))
],
CarSpecs(mass=2055, wheelbase=2.9, steerRatio=16, tireStiffnessFactor=0.65),
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING,
radar_dbc=HYUNDAI_MRR30_RADAR_DBC,
)
KIA_EV9 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia EV9 2025-26", car_parts=CarParts.common([CarHarness.hyundai_r]))
],
CarSpecs(mass=2664, wheelbase=3.1, steerRatio=16),
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING,
radar_dbc=HYUNDAI_MRR35_RADAR_DBC,
)
KIA_CARNIVAL_4TH_GEN = HyundaiCanFDPlatformConfig(
[
@@ -583,6 +798,7 @@ class CAR(Platforms):
],
CarSpecs(mass=2205, wheelbase=2.9, steerRatio=17.6),
flags=HyundaiFlags.EV,
radar_dbc=HYUNDAI_MRR30_RADAR_DBC,
)
GENESIS_G70 = HyundaiPlatformConfig(
[HyundaiCarDocs("Genesis G70 2018", "All", car_parts=CarParts.common([CarHarness.hyundai_f]))],
@@ -616,6 +832,14 @@ class CAR(Platforms):
CarSpecs(mass=2260, wheelbase=2.87, steerRatio=17.1),
flags=HyundaiFlags.EV,
)
GENESIS_GV70_ELECTRIFIED_2ND_GEN = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Genesis GV70 Electrified 2026", "All", car_parts=CarParts.common([CarHarness.hyundai_m])),
],
GENESIS_GV70_ELECTRIFIED_1ST_GEN.specs,
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING,
radar_dbc=HYUNDAI_MRR35_RADAR_DBC,
)
GENESIS_G80 = HyundaiPlatformConfig(
[HyundaiCarDocs("Genesis G80 2018-19", "All", car_parts=CarParts.common([CarHarness.hyundai_h]))],
CarSpecs(mass=2060, wheelbase=3.01, steerRatio=16.5),
@@ -628,12 +852,76 @@ class CAR(Platforms):
GENESIS_G90 = HyundaiPlatformConfig(
[HyundaiCarDocs("Genesis G90 2017-20", "All", car_parts=CarParts.common([CarHarness.hyundai_c]))],
CarSpecs(mass=2200, wheelbase=3.15, steerRatio=12.069),
flags=HyundaiFlags.MANDO_RADAR,
)
GENESIS_GV80 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Genesis GV80 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_m]))],
CarSpecs(mass=2258, wheelbase=2.95, steerRatio=14.14),
flags=HyundaiFlags.RADAR_SCC,
)
GENESIS_GV80_2025 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Genesis GV80 (3.5T Prestige Trim, with HDA II & LFA2) 2025", "Highway Driving Assist II & Lane Follow Assist 2",
car_parts=CarParts.common([CarHarness.hyundai_q])),
HyundaiCarDocs("Genesis GV80 Coupe (with HDA II & LFA2) 2025", "Highway Driving Assist II & Lane Follow Assist 2",
car_parts=CarParts.common([CarHarness.hyundai_q])),
],
GENESIS_GV80.specs,
flags=HyundaiFlags.CANFD_ANGLE_STEERING,
radar_dbc=HYUNDAI_MRR35_RADAR_DBC,
)
# Hyundai non-SCC extensions
HYUNDAI_BAYON_1ST_GEN_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Hyundai Bayon Non-SCC 2021", car_parts=CarParts.common([CarHarness.hyundai_n]))],
CarSpecs(mass=1150, wheelbase=2.58, steerRatio=13.27 * 1.15),
flags=HyundaiFlags.CHECKSUM_CRC8,
)
HYUNDAI_ELANTRA_2022_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Hyundai Elantra Non-SCC 2022", car_parts=CarParts.common([CarHarness.hyundai_k]))],
HYUNDAI_ELANTRA_2021.specs,
flags=HyundaiFlags.CHECKSUM_CRC8,
)
HYUNDAI_ELANTRA_HEV_2022_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Hyundai Elantra Hybrid Non-SCC 2022", car_parts=CarParts.common([CarHarness.hyundai_k]))],
HYUNDAI_ELANTRA_HEV_2021.specs,
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
)
HYUNDAI_KONA_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Hyundai Kona Non-SCC 2019", car_parts=CarParts.common([CarHarness.hyundai_b]))],
HYUNDAI_KONA.specs,
flags=HyundaiFlags.ALT_LIMITS,
radar_dbc=HYUNDAI_MRREVO14F_RADAR_DBC,
)
HYUNDAI_KONA_EV_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Hyundai Kona Electric Non-SCC 2019", car_parts=CarParts.common([CarHarness.hyundai_g]))],
HYUNDAI_KONA_EV.specs,
flags=HyundaiFlags.EV | HyundaiFlags.ALT_LIMITS,
)
KIA_CEED_PHEV_2022_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Kia Ceed Plug-in Hybrid Non-SCC 2022", car_parts=CarParts.common([CarHarness.hyundai_i]))],
CarSpecs(mass=1650, wheelbase=2.65, steerRatio=13.75, tireStiffnessFactor=0.5),
flags=HyundaiFlags.HYBRID,
)
KIA_FORTE_2019_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Kia Forte Non-SCC 2019", car_parts=CarParts.common([CarHarness.hyundai_g]))],
KIA_FORTE.specs,
flags=HyundaiFlags.NON_SCC_NO_FCA,
)
KIA_FORTE_2021_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Kia Forte Non-SCC 2021", car_parts=CarParts.common([CarHarness.hyundai_g]))],
KIA_FORTE.specs,
)
KIA_SELTOS_2023_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Kia Seltos Non-SCC 2023-24", car_parts=CarParts.common([CarHarness.hyundai_l]))],
KIA_SELTOS.specs,
flags=HyundaiFlags.CHECKSUM_CRC8,
)
GENESIS_G70_2021_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Genesis G70 Non-SCC 2021", car_parts=CarParts.common([CarHarness.hyundai_f]))],
GENESIS_G70_2020.specs,
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.NON_SCC_RADAR_FCA,
)
class Buttons:
@@ -644,6 +932,31 @@ class Buttons:
CANCEL = 4 # on newer models, this is a pause/resume button
CANCEL_BUTTON_ENABLE_CARS = frozenset({
CAR.HYUNDAI_PALISADE_2023,
})
# These classic HKG platforms publish the LKAS button on CLU13 over the alt bus.
# Keep G90 excluded until its alt-bus path is route-proven without the recent
# engage/disengage regression.
ALT_BUS_LDA_BUTTON_CARS = frozenset({
CAR.HYUNDAI_SONATA,
CAR.HYUNDAI_SONATA_HYBRID,
})
# On these Sonata layouts the alt-bus LKAS button pulses through the CLU13
# steering-wheel-status field instead of the dedicated LKAS bit.
ALT_BUS_LDA_BUTTON_SWL_STAT_CARS = frozenset({
CAR.HYUNDAI_SONATA,
CAR.HYUNDAI_SONATA_HYBRID,
})
def hyundai_cancel_button_enables_cruise(car_fingerprint) -> bool:
return car_fingerprint in CANCEL_BUTTON_ENABLE_CARS
def get_platform_codes(fw_versions: list[bytes]) -> set[tuple[bytes, bytes | None]]:
# Returns unique, platform-specific identification codes for a set of versions
codes = set() # (code-Optional[part], date)
@@ -733,7 +1046,7 @@ PART_NUMBER_FW_PATTERN = re.compile(b'(?<=[0-9][.,][0-9]{2} )([0-9]{5}[-/]?[A-Z]
# We've seen both ICE and hybrid for these platforms, and they have hybrid descriptors (e.g. MQ4 vs MQ4H)
CANFD_FUZZY_WHITELIST = {CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN, CAR.KIA_K8_HEV_1ST_GEN,
# TODO: the hybrid variant is not out yet
CAR.KIA_CARNIVAL_4TH_GEN}
CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN_LFA2}
# List of ECUs expected to have platform codes, camera and radar should exist on all cars
# TODO: use abs, it has the platform code and part number on many platforms
@@ -798,11 +1111,10 @@ FW_QUERY_CONFIG = FwQueryConfig(
# We lose these ECUs without the comma power on these cars.
# Note that we still attempt to match with them when they are present
non_essential_ecus={
# Some Forte trims are lateral-only and omit the SCC radar entirely.
Ecu.fwdRadar: [CAR.KIA_FORTE],
Ecu.abs: [CAR.HYUNDAI_PALISADE, CAR.HYUNDAI_SONATA, CAR.HYUNDAI_SANTA_FE_2022, CAR.KIA_K5_2021, CAR.HYUNDAI_ELANTRA_2021,
CAR.HYUNDAI_SANTA_FE, CAR.HYUNDAI_KONA_EV_2022, CAR.HYUNDAI_KONA_EV, CAR.HYUNDAI_CUSTIN_1ST_GEN, CAR.KIA_SORENTO,
CAR.KIA_CEED, CAR.KIA_SELTOS],
Ecu.fwdRadar: [CAR.HYUNDAI_KONA_NON_SCC],
},
extra_ecus=[
(Ecu.adas, 0x730, None), # ADAS Driving ECU on platforms with LKA steering
@@ -829,9 +1141,20 @@ CAN_GEARS = {
CANFD_CAR = CAR.with_flags(HyundaiFlags.CANFD)
CANFD_RADAR_SCC_CAR = CAR.with_flags(HyundaiFlags.RADAR_SCC) # TODO: merge with UNSUPPORTED_LONGITUDINAL_CAR
# Cars with CANFD_NO_RADAR_DISABLE that now work with SecurityAccess handshake
CANFD_SECURITYACCESS_CAR = {CAR.HYUNDAI_IONIQ_6, CAR.HYUNDAI_KONA_EV_2ND_GEN}
# CAN-FD cars with ADAS ECUs that work with the communication-control path.
CANFD_SECURITYACCESS_CAR = {CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_6, CAR.HYUNDAI_KONA_EV_2ND_GEN}
CANFD_UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.CANFD_NO_RADAR_DISABLE) - CANFD_SECURITYACCESS_CAR # TODO: merge with UNSUPPORTED_LONGITUDINAL_CAR
CANFD_RADAR_LIVE_LONGITUDINAL_CAR = {CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_6, CAR.KIA_EV6, CAR.GENESIS_GV60_EV_1ST_GEN}
RADAR_LIVE_LONGITUDINAL_CAR = CANFD_RADAR_LIVE_LONGITUDINAL_CAR | {
CAR.HYUNDAI_IONIQ,
CAR.HYUNDAI_KONA_EV_2022,
CAR.HYUNDAI_SANTA_FE_2022,
CAR.HYUNDAI_SANTA_FE_HEV_2022,
CAR.HYUNDAI_SANTA_FE_PHEV_2022,
CAR.HYUNDAI_SONATA,
CAR.HYUNDAI_SONATA_HYBRID,
CAR.GENESIS_G90,
}
CAMERA_SCC_CAR = CAR.with_flags(HyundaiFlags.CAMERA_SCC)
@@ -841,6 +1164,8 @@ EV_CAR = CAR.with_flags(HyundaiFlags.EV)
LEGACY_SAFETY_MODE_CAR = CAR.with_flags(HyundaiFlags.LEGACY)
NON_SCC_CAR = CAR.with_flags(HyundaiFlags.NON_SCC)
# TODO: another PR with (HyundaiFlags.LEGACY | HyundaiFlags.UNSUPPORTED_LONGITUDINAL | HyundaiFlags.CAMERA_SCC |
# HyundaiFlags.CANFD_RADAR_SCC | HyundaiFlags.CANFD_NO_RADAR_DISABLE | )
UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.LEGACY) | CAR.with_flags(HyundaiFlags.UNSUPPORTED_LONGITUDINAL)
+33 -9
View File
@@ -18,14 +18,15 @@ from opendbc.car.common.basedir import BASEDIR
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.common.simple_kalman import KF1D, get_kalman_gain
from opendbc.car.gm.values import CAR as GM
from opendbc.car.honda.values import CAR as HONDA, HONDA_BOSCH, HONDA_CAMERA_MESSAGE_CARS, HondaSafetyFlags, HondaStarPilotFlags
from opendbc.car.honda.values import CAR as HONDA, HONDA_BOSCH, HondaFlags, HondaSafetyFlags, HondaStarPilotFlags
from opendbc.car.hyundai.hyundaicanfd import CanBus
from opendbc.car.hyundai.values import CAR as HYUNDAI, CANFD_CAR, HyundaiFlags, HyundaiStarPilotFlags, HyundaiStarPilotSafetyFlags
from opendbc.car.hyundai.values import CAR as HYUNDAI, CANFD_CAR, HyundaiFlags, HyundaiStarPilotFlags, HyundaiStarPilotSafetyFlags, ALT_BUS_LDA_BUTTON_CARS
from opendbc.car.mock.values import CAR as MOCK
from opendbc.car.toyota.values import CAR as TOYOTA, NO_DSU_CAR, TSS2_CAR, UNSUPPORTED_DSU_CAR, ToyotaStarPilotFlags, ToyotaSafetyFlags
from opendbc.car.values import PLATFORMS
from opendbc.can import CANParser
from openpilot.common.params import Params
from openpilot.starpilot.common.testing_grounds import testing_ground
GearShifter = structs.CarState.GearShifter
ButtonType = structs.CarState.ButtonEvent.Type
@@ -182,7 +183,14 @@ class CarInterfaceBase(ABC):
ret.tireStiffnessFront, ret.tireStiffnessRear = scale_tire_stiffness(ret.mass, ret.wheelbase, ret.centerToFront, ret.tireStiffnessFactor)
toggles_to_check = ("force_torque_controller", "nnff", "nnff_lite")
if ret.steerControlType != structs.CarParams.SteerControlType.angle and any(getattr(starpilot_toggles, toggle, False) for toggle in toggles_to_check):
modified_civic_force_torque = (
candidate == HONDA.HONDA_CIVIC_BOSCH and
bool(ret.flags & HondaFlags.EPS_MODIFIED)
)
if ret.steerControlType != structs.CarParams.SteerControlType.angle and (
any(getattr(starpilot_toggles, toggle, False) for toggle in toggles_to_check) or
modified_civic_force_torque
):
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
return ret
@@ -190,6 +198,8 @@ class CarInterfaceBase(ABC):
@classmethod
def get_starpilot_params(cls, candidate: str, fingerprint: dict[int, dict[int, int]], car_fw: list[structs.CarParams.CarFw], CP: structs.CarParams, starpilot_toggles: SimpleNamespace):
fp_ret = custom.StarPilotCarParams.new_message()
fp_ret.pcmCruiseSpeed = True
params = Params(return_defaults=True)
platform = PLATFORMS[candidate]
@@ -207,8 +217,7 @@ class CarInterfaceBase(ABC):
elif platform in HONDA:
fp_ret.canUsePedal = candidate not in HONDA_BOSCH
# Only enable TSR parsing on Hondas confirmed to publish CAMERA_MESSAGES.
if candidate in HONDA_CAMERA_MESSAGE_CARS:
if any(0x35E in bus_fingerprint for bus_fingerprint in fingerprint.values()):
fp_ret.flags |= int(HondaStarPilotFlags.HAS_CAMERA_MESSAGES)
elif platform in HYUNDAI:
@@ -220,11 +229,23 @@ class CarInterfaceBase(ABC):
if 0x1FA in fingerprint[CAN.ECAN]:
fp_ret.flags |= HyundaiStarPilotFlags.SPEED_LIMIT_AVAILABLE.value
if CP.flags & HyundaiFlags.HAS_LDA_BUTTON:
fp_ret.safetyConfigs[-1].safetyParam |= HyundaiStarPilotSafetyFlags.HAS_LDA_BUTTON.value
if starpilot_toggles.always_on_lateral_lkas:
fp_ret.safetyConfigs[-1].safetyParam |= HyundaiStarPilotSafetyFlags.AOL_LKAS_ON_ENGAGE.value
fp_ret.redneckCruiseAvailable = bool(CP.flags & HyundaiFlags.NON_SCC) and not bool(CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS)
if fp_ret.redneckCruiseAvailable and params.get_bool("RedneckCruise") and \
not CP.openpilotLongitudinalControl:
fp_ret.pcmCruiseSpeed = False
hyundai_has_lda_button = (
0x391 in fingerprint[0] or
0x50C in fingerprint[0] or
candidate in ALT_BUS_LDA_BUTTON_CARS or
bool(CP.flags & HyundaiFlags.CAN_CANFD_BLENDED)
)
if hyundai_has_lda_button:
fp_ret.safetyConfigs[-1].safetyParam |= HyundaiStarPilotSafetyFlags.HAS_LDA_BUTTON.value
# LKASButtonControl == 9 means BUTTON_FUNCTIONS["AOL_TOGGLE"] in starpilot_variables.
if params.get_bool("AlwaysOnLateral") and params.get_int("LKASButtonControl") == 9:
fp_ret.safetyConfigs[-1].safetyParam |= HyundaiStarPilotSafetyFlags.AOL_LKAS_ON_ENGAGE.value
elif platform in TOYOTA:
fp_ret.canUsePedal = not CP.autoResumeSng
fp_ret.canUseSDSU = candidate not in UNSUPPORTED_DSU_CAR and candidate not in TSS2_CAR
@@ -239,6 +260,9 @@ class CarInterfaceBase(ABC):
if 0x23 in fingerprint[0]:
fp_ret.flags |= ToyotaStarPilotFlags.ZSS.value
elif platform.config.platform_str == "TESLA_MODEL_S_PREAP":
fp_ret.canUsePedal = True
return fp_ret
@staticmethod
+3 -1
View File
@@ -118,11 +118,13 @@ class CarState(CarStateBase):
fp_ret = custom.StarPilotCarState.new_message()
fp_ret.dashboardStopSign = 1 if cp_cam.vl["CAM_TRAFFIC_SIGNS"]["STOP_SIGN"] == 9 else 0
return ret, fp_ret
@staticmethod
def get_can_parsers(CP):
return {
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 0),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 2),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [("CAM_TRAFFIC_SIGNS", 0)], 2),
}
@@ -4,15 +4,14 @@ from opendbc.car import Bus
from opendbc.car.lateral import apply_steer_angle_limits_vm
from opendbc.car.interfaces import CarControllerBase
from opendbc.car.tesla.teslacan import TeslaCAN
from opendbc.car.tesla.values import CarControllerParams
from opendbc.car.tesla.preap.carcontroller import PreAPLongController, init_preap_can
from opendbc.car.tesla.preap.stock_cc_spoofer import StockCCSpoofer
from opendbc.car.tesla.values import CANBUS, CAR, CarControllerParams
from opendbc.car.vehicle_model import VehicleModel
def get_safety_CP():
# We use the TESLA_MODEL_Y platform for lateral limiting to match safety
# A Model 3 at 40 m/s using the Model Y limits sees a <0.3% difference in max angle (from curvature factor)
from opendbc.car.tesla.interface import CarInterface
return CarInterface.get_non_essential_params("TESLA_MODEL_Y")
return CarInterface.get_non_essential_params(CAR.TESLA_MODEL_Y)
class CarController(CarControllerBase):
@@ -21,11 +20,23 @@ class CarController(CarControllerBase):
self.apply_angle_last = 0
self.packer = CANPacker(dbc_names[Bus.party])
self.tesla_can = TeslaCAN(self.packer)
self.preap_long = None
self.stock_cc = None
# Vehicle model used for lateral limiting
self.VM = VehicleModel(get_safety_CP())
if CP.carFingerprint == CAR.TESLA_MODEL_S_PREAP:
self.tesla_can = init_preap_can(dbc_names)
self.preap_long = PreAPLongController()
self.stock_cc = StockCCSpoofer()
from opendbc.car.tesla.interface import CarInterface
self.VM = VehicleModel(CarInterface.get_non_essential_params(CAR.TESLA_MODEL_S_PREAP))
def update(self, CC, CS, now_nanos, starpilot_toggles):
if self.CP.carFingerprint == CAR.TESLA_MODEL_S_PREAP:
return self._update_preap(CC, CS)
actuators = CC.actuators
can_sends = []
@@ -64,3 +75,45 @@ class CarController(CarControllerBase):
self.frame += 1
return new_actuators, can_sends
def _update_preap(self, CC, CS):
actuators = CC.actuators
can_sends = []
lat_active = CC.latActive and CS.hands_on_level < 3
if CC.cruiseControl.cancel and CS.cruiseEnabled:
CS.cruiseEnabled = False
CS.enableLongControl = False
CS.enableJustCC = False
CS.pedal_speed_kph = 0.0
CS.preap_cc_cancel_needed = True
if hasattr(CS, "engagement"):
CS.engagement.cruiseEnabled = False
CS.engagement.enableLongControl = False
CS.engagement.enableJustCC = False
CS.engagement.pending_enable = False
CS.engagement.pedal_speed_kph = 0.0
if self.frame % 2 == 0:
self.apply_angle_last = apply_steer_angle_limits_vm(
actuators.steeringAngleDeg, self.apply_angle_last, CS.out.vEgoRaw, CS.out.steeringAngleDeg,
lat_active, CarControllerParams, self.VM,
)
cntr = (self.frame // 2) % 16
can_sends.append(self.tesla_can.create_steering_control(cntr, self.apply_angle_last, lat_active))
can_sends.append(self.tesla_can.create_epas_control(cntr, 1))
CS.pccEvent = None
if self.CP.openpilotLongitudinalControl and self.preap_long is not None:
can_sends.extend(self.preap_long.update(CC, CS, self.frame, self.tesla_can, CANBUS.party))
if self.stock_cc is not None:
can_sends.extend(self.stock_cc.update(CS, self.frame, self.tesla_can, CANBUS.party))
if self.stock_cc.pcc_event:
CS.pccEvent = self.stock_cc.pcc_event
new_actuators = actuators.as_builder()
new_actuators.steeringAngleDeg = self.apply_angle_last
self.frame += 1
return new_actuators, can_sends
+35 -1
View File
@@ -5,6 +5,10 @@ from opendbc.car import Bus, structs
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.interfaces import CarStateBase
from opendbc.car.tesla.values import DBC, CANBUS, GEAR_MAP, STEER_THRESHOLD, CAR
from opendbc.car.tesla.preap.carstate import get_preap_can_parsers, update_preap
from opendbc.car.tesla.preap.engagement import PreAPEngagement
from opendbc.car.tesla.preap.nap_conf import nap_conf
from opendbc.car.tesla.preap.pedal_feedback import PedalFeedback
ButtonType = structs.CarState.ButtonEvent.Type
@@ -13,7 +17,8 @@ class CarState(CarStateBase):
def __init__(self, CP, FPCP):
super().__init__(CP, FPCP)
self.can_define = CANDefine(DBC[CP.carFingerprint][Bus.party])
self.shifter_values = self.can_define.dv["DI_systemStatus"]["DI_gear"]
self.shifter_values = self.can_define.dv["DI_systemStatus"]["DI_gear"] if CP.carFingerprint != CAR.TESLA_MODEL_S_PREAP else \
self.can_define.dv["DI_torque2"]["DI_gear"]
self.autopark = False
self.autopark_prev = False
@@ -21,6 +26,25 @@ class CarState(CarStateBase):
self.hands_on_level = 0
self.das_control = None
self.cruise_buttons = 0
self.prev_cruise_buttons = 0
self.msg_stw_actn_req = None
self.speed_units = "MPH"
if CP.carFingerprint == CAR.TESLA_MODEL_S_PREAP:
self.engagement = PreAPEngagement(nap_conf.double_pull_enabled, nap_conf.double_pull_window_ms)
self.cruiseEnabled = False
self.enableLongControl = False
self.enableJustCC = False
self.pedal_speed_kph = 0.0
self.prev_stalk_follow = 0
self.pccEvent = None
self.preap_cc_cancel_needed = False
self.preap_cc_engage_needed = False
self.di_cruise_state = "OFF"
self.pedal = PedalFeedback()
self.pedal_interceptor_value = 0.0
self.pedal_timeout = True
def update_autopark_state(self, autopark_state: str, cruise_enabled: bool):
autopark_now = autopark_state in ("ACTIVE", "COMPLETE", "SELFPARK_STARTED")
@@ -31,7 +55,15 @@ class CarState(CarStateBase):
self.autopark_prev = autopark_now
self.cruise_enabled_prev = cruise_enabled
def update_button_enable(self, buttonEvents: list[structs.CarState.ButtonEvent]):
if self.CP.carFingerprint == CAR.TESLA_MODEL_S_PREAP:
return False
return super().update_button_enable(buttonEvents)
def update(self, can_parsers, starpilot_toggles) -> structs.CarState:
if self.CP.carFingerprint == CAR.TESLA_MODEL_S_PREAP:
return update_preap(self, can_parsers)
cp_party = can_parsers[Bus.party]
cp_ap_party = can_parsers[Bus.ap_party]
ret = structs.CarState()
@@ -124,6 +156,8 @@ class CarState(CarStateBase):
@staticmethod
def get_can_parsers(CP):
if CP.carFingerprint == CAR.TESLA_MODEL_S_PREAP:
return get_preap_can_parsers(CP)
return {
Bus.party: CANParser(DBC[CP.carFingerprint][Bus.party], [], CANBUS.party),
Bus.ap_party: CANParser(DBC[CP.carFingerprint][Bus.party], [], CANBUS.autopilot_party)
@@ -45,3 +45,22 @@ FW_VERSIONS = {
],
},
}
FINGERPRINTS = {
CAR.TESLA_MODEL_S_PREAP: [
{
1: 8, 3: 8, 14: 8, 21: 4, 69: 8, 109: 4, 257: 3, 264: 8, 277: 6, 280: 6, 293: 4, 296: 4,
309: 5, 325: 8, 336: 8, 341: 8, 360: 7, 373: 8, 389: 8, 415: 8, 513: 5, 516: 8, 520: 4,
522: 8, 524: 8, 527: 8, 536: 8, 551: 4, 552: 2, 556: 8, 568: 8, 582: 5, 638: 8, 643: 8,
693: 8, 696: 8, 712: 8, 728: 8, 744: 8, 760: 8, 771: 2, 772: 8, 775: 8, 776: 8, 778: 8,
780: 2, 783: 8, 785: 8, 787: 8, 788: 8, 791: 8, 792: 8, 796: 2, 799: 8, 804: 8, 805: 8,
807: 8, 808: 1, 812: 8, 815: 8, 820: 8, 823: 8, 824: 8, 831: 8, 836: 8, 840: 8, 856: 4,
863: 8, 872: 8, 880: 8, 888: 8, 896: 8, 901: 6, 904: 3, 920: 8, 936: 8, 949: 8, 952: 8,
953: 6, 968: 8, 984: 8, 1000: 8, 1006: 8, 1026: 8, 1028: 8, 1029: 8, 1030: 8, 1032: 1,
1034: 8, 1048: 1, 1064: 8, 1080: 8, 1281: 8, 1285: 8, 1332: 8, 1335: 8, 1362: 6, 1368: 8,
1412: 8, 1436: 8, 1456: 8, 1463: 8, 1476: 8, 1524: 8, 1527: 8, 1601: 8, 1605: 8, 1617: 8,
1621: 8, 1800: 4, 1804: 8, 1812: 8, 1815: 8, 1816: 8, 1828: 8, 1831: 8, 1832: 8, 1840: 8,
1848: 8, 1864: 8, 1880: 8, 1892: 8, 1896: 8, 1912: 8, 1960: 8, 1992: 8, 2008: 3, 2043: 5,
},
],
}
@@ -2,17 +2,29 @@ from opendbc.car import get_safety_config, structs
from opendbc.car.interfaces import CarInterfaceBase
from opendbc.car.tesla.carcontroller import CarController
from opendbc.car.tesla.carstate import CarState
from opendbc.car.tesla.radar_interface import RadarInterface
from opendbc.car.tesla.values import TeslaSafetyFlags, CAR
from opendbc.car.tesla.preap.interface import get_preap_accel_limits, get_preap_params
class CarInterface(CarInterfaceBase):
CarState = CarState
CarController = CarController
RadarInterface = RadarInterface
@staticmethod
def get_pid_accel_limits(CP, current_speed, cruise_speed):
if CP.carFingerprint == CAR.TESLA_MODEL_S_PREAP:
return get_preap_accel_limits(current_speed)
return CarInterfaceBase.get_pid_accel_limits(CP, current_speed, cruise_speed)
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
ret.brand = "tesla"
if candidate == CAR.TESLA_MODEL_S_PREAP:
return get_preap_params(ret)
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.tesla)]
ret.steerLimitTimer = 0.4
@@ -0,0 +1 @@
@@ -0,0 +1,64 @@
from numpy import clip, interp
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.tesla.preap.nap_conf import (
ACCEL_MAX,
PEDAL_BP,
PEDAL_DI_MIN,
PEDAL_DI_ZERO,
PEDAL_MAX_VALUES,
REGEN_MAX,
nap_conf,
)
PEDAL_RAMP_RATE_UP = 5.0
PEDAL_RAMP_RATE_DOWN = 2.5
ACCEL_DEADBAND = 0.15
PEDAL_HYST_GAP = 1.0
TORQUE_LEVEL_ACC = 0.0
TORQUE_LEVEL_DECEL = -30.0
ZERO_TORQUE_MIN_SPEED = 10.0 * CV.MPH_TO_MS
class PedalZeroTorque:
def __init__(self):
self.value = PEDAL_DI_ZERO
self._best_torque = TORQUE_LEVEL_DECEL
def update(self, torque_level: float, current_pedal_di: float, v_ego: float) -> None:
if v_ego < ZERO_TORQUE_MIN_SPEED:
return
if TORQUE_LEVEL_DECEL < torque_level < TORQUE_LEVEL_ACC and abs(torque_level) < abs(self._best_torque):
self.value = current_pedal_di
self._best_torque = torque_level
def get(self, v_ego: float) -> float:
if v_ego < 5.0 * CV.MPH_TO_MS:
return PEDAL_DI_ZERO
return self.value
_zero_torque = PedalZeroTorque()
def get_zero_torque() -> PedalZeroTorque:
return _zero_torque
def compute_pedal_command(accel_request: float, v_ego: float, prev_pedal_di: float) -> tuple[float, float]:
max_pedal_value = float(interp(v_ego, PEDAL_BP, PEDAL_MAX_VALUES))
zero_torque_di = _zero_torque.get(v_ego)
if abs(accel_request) < ACCEL_DEADBAND:
accel_request = 0.0
pedal_di = float(interp(accel_request, [REGEN_MAX, 0.0, ACCEL_MAX], [PEDAL_DI_MIN, zero_torque_di, max_pedal_value]))
pedal_di = float(clip(pedal_di, PEDAL_DI_MIN, max_pedal_value))
pedal_di = float(clip(pedal_di, prev_pedal_di - PEDAL_RAMP_RATE_DOWN, prev_pedal_di + PEDAL_RAMP_RATE_UP))
if abs(pedal_di - prev_pedal_di) < PEDAL_HYST_GAP:
pedal_di = prev_pedal_di
return nap_conf.di_to_pedal(pedal_di), pedal_di
@@ -0,0 +1 @@
@@ -0,0 +1,91 @@
import numpy as np
from opendbc.can import CANPacker
from opendbc.car import Bus
from opendbc.car.carlog import carlog
from opendbc.car.tesla.pedal.controller import get_zero_torque
from opendbc.car.tesla.preap.interface import get_preap_accel_limits
from opendbc.car.tesla.preap.nap_conf import PEDAL_DI_MIN, PEDAL_DI_ZERO, nap_conf
from opendbc.car.tesla.preap.teslacan import TeslaCANPreAP
from opendbc.car.tesla.preap.virtual_das import VirtualDAS
from opendbc.car.tesla.values import CANBUS, CruiseButtons
ENGAGE_GRACE_FRAMES = 50
def init_preap_can(dbc_names):
tesla_can = TeslaCANPreAP(CANPacker(dbc_names[Bus.party]))
tesla_can.pedal_can_bus = nap_conf.pedal_can_bus
return tesla_can
class PreAPLongController:
def __init__(self):
self.prev_pedal_di = 0.0
self.prev_requested_long = False
self.preap_long_engage_frame = -1_000_000
self.engage_a_max = 0.0
self.vdas = VirtualDAS(dt=0.02)
def update(self, CC, CS, frame: int, tesla_can, can_bus_party: int):
can_sends = []
actuators = CC.actuators
requested_long = CS.cruiseEnabled and CS.enableLongControl
long_active = requested_long and CC.longActive
use_pedal = nap_conf.use_pedal and self._pedal_ready()
if (not self.prev_requested_long) and requested_long:
self.preap_long_engage_frame = frame
zero_torque_di = get_zero_torque().get(CS.out.vEgo)
self.prev_pedal_di = max(CS.pedal_interceptor_value, zero_torque_di)
self.vdas.reset(a_init=0.0, pedal_di_init=self.prev_pedal_di)
_, self.engage_a_max = get_preap_accel_limits(CS.out.vEgo)
if use_pedal:
pedal_button_press = CS.cruise_buttons != CS.prev_cruise_buttons and CS.cruise_buttons != CruiseButtons.IDLE
if ((not self.prev_requested_long) and requested_long) or (self.prev_requested_long and not requested_long) or pedal_button_press:
CS.preap_cc_cancel_needed = True
self.prev_requested_long = requested_long
if frame % 2 == 0:
if use_pedal:
get_zero_torque().update(CS.pedal.torque_level, self.prev_pedal_di, CS.out.vEgo)
if requested_long and use_pedal:
try:
if CS.out.gasPressed:
self.prev_pedal_di = max(CS.pedal_interceptor_value, PEDAL_DI_ZERO)
can_sends.append(tesla_can.create_pedal_command(0, enable=0))
elif long_active:
accel_request = float(actuators.accel)
engage_elapsed_frames = frame - self.preap_long_engage_frame
in_engage_grace = engage_elapsed_frames < ENGAGE_GRACE_FRAMES
if in_engage_grace:
accel_request = max(0.0, min(accel_request, (engage_elapsed_frames / ENGAGE_GRACE_FRAMES) * self.engage_a_max))
self.prev_pedal_di = self.vdas.update(
accel_request, CS.out.vEgo, self.prev_pedal_di, a_ego=CS.out.aEgo,
freeze_integrator=in_engage_grace, orientation_ned=list(CC.orientationNED),
)
can_sends.append(tesla_can.create_pedal_command(nap_conf.di_to_pedal(self.prev_pedal_di), enable=1))
if self.prev_pedal_di <= 0.95 * PEDAL_DI_MIN and not in_engage_grace:
CS.pccEvent = "pedalMaxRegen"
else:
zero_torque_di = get_zero_torque().get(CS.out.vEgo)
self.prev_pedal_di = zero_torque_di
can_sends.append(tesla_can.create_pedal_command(nap_conf.di_to_pedal(zero_torque_di), enable=1))
except Exception:
carlog.exception("Pre-AP pedal command failed; sending disabled")
can_sends.append(tesla_can.create_pedal_command(nap_conf.di_to_pedal(PEDAL_DI_ZERO), enable=0))
self.prev_pedal_di = 0.0
else:
if nap_conf.use_pedal:
can_sends.append(tesla_can.create_pedal_command(nap_conf.di_to_pedal(PEDAL_DI_ZERO), enable=0))
self.prev_pedal_di = 0.0
return can_sends
@staticmethod
def _pedal_ready() -> bool:
return nap_conf.pedal_calibrated and abs(nap_conf.pedal_factor) > 1e-6
@@ -0,0 +1,154 @@
import copy
import math
import time
from cereal import custom
from opendbc.can import CANParser
from opendbc.car import Bus, structs
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.tesla.preap.nap_params import NAPParamKeys
from opendbc.car.tesla.preap.engagement import PreAPEngagement
from opendbc.car.tesla.preap.nap_conf import PEDAL_DI_PRESSED, nap_conf
from opendbc.car.tesla.preap.pedal_feedback import PedalFeedback
from opendbc.car.tesla.values import CANBUS, DBC, GEAR_MAP, STEER_THRESHOLD
try:
from openpilot.common.params import Params as _NAPParams
_nap_params = _NAPParams()
except ImportError:
_nap_params = None
_DOORS = ("DOOR_STATE_FL", "DOOR_STATE_FR", "DOOR_STATE_RL", "DOOR_STATE_RR", "DOOR_STATE_FrontTrunk", "BOOT_STATE")
def _current_time_millis() -> int:
return int(round(time.time() * 1000))
def update_preap(cs, can_parsers):
cp_ap_party = can_parsers[Bus.ap_party]
cp_pt = can_parsers[Bus.pt]
cp_chassis = can_parsers[Bus.chassis]
ret = structs.CarState()
fp_ret = custom.StarPilotCarState.new_message()
ret.vEgoRaw = cp_chassis.vl["ESP_B"]["ESP_vehicleSpeed"] * CV.KPH_TO_MS
ret.vEgo, ret.aEgo = cs.update_speed_kf(ret.vEgoRaw)
ret.gasPressed = cp_pt.vl["DI_torque1"]["DI_pedalPos"] > PEDAL_DI_PRESSED
real_brake_pressed = cp_chassis.vl["BrakeMessage"]["driverBrakeStatus"] == 2
ret.brake = 0
ret.brakePressed = real_brake_pressed
epas_status = cp_chassis.vl["EPAS_sysStatus"]
cs.hands_on_level = epas_status["EPAS_handsOnLevel"]
ret.steeringAngleDeg = -epas_status["EPAS_internalSAS"]
ret.steeringRateDeg = -cp_chassis.vl["STW_ANGLHP_STAT"]["StW_AnglHP_Spd"]
ret.steeringTorque = -epas_status["EPAS_torsionBarTorque"]
ret.steeringPressed = cs.update_steering_pressed(abs(ret.steeringTorque) > STEER_THRESHOLD, 5)
eac_status = cs.can_define.dv["EPAS_sysStatus"]["EPAS_eacStatus"].get(int(epas_status["EPAS_eacStatus"]), None)
ret.steerFaultPermanent = eac_status == "EAC_FAULT"
ret.steerFaultTemporary = False
eac_error_code = cs.can_define.dv["EPAS_sysStatus"]["EPAS_eacErrorCode"].get(int(epas_status["EPAS_eacErrorCode"]), None)
ret.steeringDisengage = cs.hands_on_level >= 3 or (eac_status == "EAC_INHIBITED" and eac_error_code in (
"EAC_ERROR_HIGH_ANGLE_REQ", "EAC_ERROR_HIGH_ANGLE_RATE_REQ", "EAC_ERROR_HIGH_ANGLE_SAFETY", "EAC_ERROR_HIGH_ANGLE_RATE_SAFETY",
))
cs.engagement.handle_steering_disengage(ret.steeringDisengage)
cruise_state = cs.can_define.dv["DI_state"]["DI_cruiseState"].get(int(cp_chassis.vl["DI_state"]["DI_cruiseState"]), None)
cs.di_cruise_state = cruise_state or "OFF"
speed_units = cs.can_define.dv["DI_state"]["DI_speedUnits"].get(int(cp_chassis.vl["DI_state"]["DI_speedUnits"]), None)
if speed_units is not None:
cs.speed_units = speed_units
pedal_transform_valid = math.isfinite(nap_conf.pedal_factor) and abs(nap_conf.pedal_factor) > 1e-6
use_pedal = nap_conf.use_pedal and cs.CP.openpilotLongitudinalControl and pedal_transform_valid
pedal_long_allowed = use_pedal
long_control_allowed = True if not use_pedal else pedal_long_allowed
ret.cruiseState.available = True
if cs.enableLongControl and use_pedal:
ret.cruiseState.speed = cs.pedal_speed_kph * CV.KPH_TO_MS
elif speed_units == "KPH":
ret.cruiseState.speed = max(cp_chassis.vl["DI_state"]["DI_digitalSpeed"] * CV.KPH_TO_MS, 1e-3)
elif speed_units == "MPH":
ret.cruiseState.speed = max(cp_chassis.vl["DI_state"]["DI_digitalSpeed"] * CV.MPH_TO_MS, 1e-3)
ret.cruiseState.standstill = False
ret.standstill = cruise_state == "STANDSTILL"
ret.accFaulted = cruise_state == "FAULT"
ret.gearShifter = GEAR_MAP[cs.can_define.dv["DI_torque2"]["DI_gear"].get(int(cp_chassis.vl["DI_torque2"]["DI_gear"]), "DI_GEAR_INVALID")]
ret.doorOpen = any((cs.can_define.dv["GTW_carState"][door].get(int(cp_chassis.vl["GTW_carState"][door]), "OPEN") == "OPEN") for door in _DOORS)
ret.leftBlinker = cp_chassis.vl["GTW_carState"]["BC_indicatorLStatus"] == 1
ret.rightBlinker = cp_chassis.vl["GTW_carState"]["BC_indicatorRStatus"] == 1
ret.seatbeltUnlatched = False
ret.stockAeb = False
ret.stockLkas = False
cs.prev_cruise_buttons = cs.cruise_buttons
cs.cruise_buttons = int(cp_chassis.vl["STW_ACTN_RQ"]["SpdCtrlLvr_Stat"])
cs.msg_stw_actn_req = copy.copy(cp_chassis.vl["STW_ACTN_RQ"])
if _nap_params is not None:
dtr_dist = int(cp_chassis.vl["STW_ACTN_RQ"]["DTR_Dist_Rq"])
if dtr_dist != 255:
stalk_follow = min((dtr_dist // 33) + 1, 7)
if stalk_follow != cs.prev_stalk_follow:
_nap_params.put(NAPParamKeys.FOLLOW_DISTANCE, str(stalk_follow))
cs.prev_stalk_follow = stalk_follow
curr_time_ms = _current_time_millis()
ret.buttonEvents = cs.engagement.process_buttons(
cs.cruise_buttons, cs.prev_cruise_buttons, curr_time_ms, ret.vEgo, cs.speed_units,
use_pedal, pedal_long_allowed, long_control_allowed, real_brake_pressed, cs.di_cruise_state,
)
ret.brakePressed = False
can_engage = cs.engagement.check_can_engage(ret.doorOpen, ret.gearShifter, ret.seatbeltUnlatched)
ret.cruiseState.enabled = cs.engagement.cruiseEnabled and can_engage
cs.cruiseEnabled = cs.engagement.cruiseEnabled
cs.enableLongControl = cs.engagement.enableLongControl
cs.enableJustCC = cs.engagement.enableJustCC
cs.pedal_speed_kph = cs.engagement.pedal_speed_kph
cs.preap_cc_cancel_needed = cs.engagement.preap_cc_cancel_needed
cs.preap_cc_engage_needed = cs.engagement.preap_cc_engage_needed
if nap_conf.use_pedal:
gas_sensor = cp_ap_party.vl.get("GAS_SENSOR", {})
cs.pedal.update(gas_sensor, curr_time_ms)
cs.pedal.update_torque(cp_pt.vl.get("DI_torque1", {}))
cs.pedal_interceptor_value = cs.pedal.interceptor_value
cs.pedal_timeout = cs.pedal.timeout
if use_pedal:
ret.gasPressed = cs.pedal.gas_pressed
cs.das_control = None
cs.cruise_enabled_prev = ret.cruiseState.enabled
ret.pedalMaxRegen = cs.pccEvent == "pedalMaxRegen"
ret.teslaCCEngaged = cs.pccEvent == "teslaCCEngaged"
ret.teslaCCDisengaged = cs.pccEvent == "teslaCCDisengaged"
ret.teslaCCNotArmed = (
not nap_conf.use_pedal and
cs.cruiseEnabled and
cs.enableLongControl and
cs.di_cruise_state not in ("STANDBY", "ENABLED")
)
ret.pedalLongActive = cs.enableLongControl and nap_conf.use_pedal
return ret, fp_ret
def get_preap_can_parsers(CP):
chassis_messages = [
("ESP_B", 0), ("BrakeMessage", 0), ("DI_state", 0), ("DI_torque2", 0),
("GTW_carState", 0), ("GTW_epasControl", 0), ("STW_ANGLHP_STAT", 0), ("EPAS_sysStatus", 0), ("STW_ACTN_RQ", 0),
]
pt_messages = [("DI_torque1", 0)]
pedal_messages = [("GAS_SENSOR", 50)] if nap_conf.use_pedal else []
return {
Bus.party: CANParser(DBC[CP.carFingerprint][Bus.party], [], CANBUS.party),
Bus.ap_party: CANParser(DBC[CP.carFingerprint][Bus.party], pedal_messages, nap_conf.pedal_can_bus),
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], pt_messages, CANBUS.party),
Bus.chassis: CANParser(DBC[CP.carFingerprint][Bus.chassis], chassis_messages, CANBUS.party),
}
@@ -0,0 +1,16 @@
ACCEL_PREAP_BP = [0.0, 1.3, 7.5, 15.0, 25.0, 40.0]
ACCEL_PREAP_FOLLOW = [0.35, 0.55, 0.80, 0.75, 0.65, 0.50]
ACCEL_PREAP_PROFILES = {
0: [0.3, 0.8, 1.1, 1.0, 0.85, 0.7],
1: [0.3, 0.7, 1.0, 0.9, 0.8, 0.65],
2: [0.3, 0.6, 0.9, 0.8, 0.7, 0.55],
}
PEDAL_LONG_K_BP = [0.0, 3.0, 6.0, 35.0]
PEDAL_LONG_KP_V = [0.0, 0.0, 0.0, 0.0]
PEDAL_LONG_KI_V = [0.05, 0.08, 0.10, 0.15]
VDAS_INNER_K_BP = [0.0, 5.0, 35.0]
VDAS_INNER_KP_V = [0.0, 0.0, 0.0]
VDAS_INNER_KI_V = [0.3, 0.2, 0.15]
VDAS_FUTURE_T_BP = [2.0, 5.0]
VDAS_FUTURE_T_V = [0.30, 0.55]
VDAS_AEGO_FILTER_RC = 0.25
@@ -0,0 +1,148 @@
from opendbc.car import structs
from opendbc.car.carlog import carlog
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.tesla.values import CruiseButtons
ButtonType = structs.CarState.ButtonEvent.Type
CANCEL_ECHO_WINDOW_MS = 600
SPOOF_ECHO_WINDOW_MS = 300
class PreAPEngagement:
def __init__(self, double_pull_enabled: bool, double_pull_window_ms: int):
self.enableDoublePull = double_pull_enabled
self.double_pull_window_ms = double_pull_window_ms
self.cruiseEnabled = False
self.enableLongControl = False
self.enableJustCC = False
self.pending_enable = False
self.stalk_pull_time_ms = 0
self.prev_stalk_pull_time_ms = -1000
self.pedal_speed_kph = 0.0
self.preap_cc_cancel_needed = False
self.preap_cc_engage_needed = False
self.preap_last_cc_spoof_ms = 0
self.preap_brake_pressed_prev = False
self.last_stalk_non_cancel_ms = -10000
self.prev_steering_disengage = False
def handle_steering_disengage(self, steering_disengage: bool) -> None:
if steering_disengage and not self.prev_steering_disengage:
self.cruiseEnabled = False
self.enableLongControl = False
self.enableJustCC = False
self.pending_enable = False
self.pedal_speed_kph = 0.0
self.stalk_pull_time_ms = 0
self.prev_stalk_pull_time_ms = -1000
self.prev_steering_disengage = steering_disengage
def process_buttons(self, cruise_buttons: int, prev_cruise_buttons: int, curr_time_ms: int, v_ego: float, speed_units: str,
use_pedal: bool, pedal_long_allowed: bool, long_control_allowed: bool, real_brake_pressed: bool,
di_cruise_state: str = "OFF") -> list[structs.CarState.ButtonEvent]:
self.preap_cc_cancel_needed = False
self.preap_cc_engage_needed = False
button_events: list[structs.CarState.ButtonEvent] = []
if cruise_buttons == CruiseButtons.MAIN and prev_cruise_buttons != CruiseButtons.MAIN:
if self.enableDoublePull:
self._handle_double_pull(curr_time_ms, v_ego, speed_units, use_pedal, pedal_long_allowed, long_control_allowed, di_cruise_state)
else:
self.cruiseEnabled = True
self.enableLongControl = long_control_allowed
self.enableJustCC = not long_control_allowed
self.pedal_speed_kph = self._capture_target_speed(v_ego, speed_units) if pedal_long_allowed else 0.0
if not use_pedal and di_cruise_state == "STANDBY":
self.preap_cc_engage_needed = True
self.preap_last_cc_spoof_ms = curr_time_ms
if cruise_buttons != prev_cruise_buttons:
button_events.append(self._make_button_event(cruise_buttons, prev_cruise_buttons, curr_time_ms, v_ego, speed_units, use_pedal))
if self.pending_enable and (curr_time_ms - self.stalk_pull_time_ms > self.double_pull_window_ms):
self.pending_enable = False
brake_rising_edge = real_brake_pressed and not self.preap_brake_pressed_prev
if use_pedal and brake_rising_edge and self.cruiseEnabled and self.enableLongControl:
self.enableLongControl = False
self.enableJustCC = True
self.pending_enable = False
self.pedal_speed_kph = 0.0
self.preap_brake_pressed_prev = real_brake_pressed
return button_events
def check_can_engage(self, door_open: bool, gear_shifter, seatbelt_unlatched: bool) -> bool:
can_engage = not door_open and gear_shifter == structs.CarState.GearShifter.drive and not seatbelt_unlatched
if not can_engage:
self.cruiseEnabled = False
self.enableLongControl = False
self.enableJustCC = False
self.pending_enable = False
return can_engage
def _handle_double_pull(self, curr_time_ms: int, v_ego: float, speed_units: str, use_pedal: bool,
pedal_long_allowed: bool, long_control_allowed: bool, di_cruise_state: str) -> None:
self.prev_stalk_pull_time_ms = self.stalk_pull_time_ms
self.stalk_pull_time_ms = curr_time_ms
double_pull = (self.stalk_pull_time_ms - self.prev_stalk_pull_time_ms) < self.double_pull_window_ms
self.cruiseEnabled = True
self.pending_enable = False
self.enableLongControl = long_control_allowed if double_pull else False
self.enableJustCC = not self.enableLongControl
self.pedal_speed_kph = self._capture_target_speed(v_ego, speed_units) if pedal_long_allowed and double_pull else 0.0
if double_pull:
if not use_pedal:
self.preap_cc_engage_needed = True
self.preap_last_cc_spoof_ms = curr_time_ms
else:
self.pending_enable = True
if not use_pedal:
self.preap_cc_cancel_needed = True
self.preap_last_cc_spoof_ms = curr_time_ms
def _make_button_event(self, cruise_buttons: int, prev_cruise_buttons: int, curr_time_ms: int,
v_ego: float, speed_units: str, use_pedal: bool) -> structs.CarState.ButtonEvent:
be = structs.CarState.ButtonEvent()
be.pressed = cruise_buttons != CruiseButtons.IDLE
state = cruise_buttons if be.pressed else prev_cruise_buttons
if state == CruiseButtons.MAIN:
be.type = ButtonType.setCruise
if be.pressed:
self.last_stalk_non_cancel_ms = curr_time_ms
elif state == CruiseButtons.CANCEL:
is_echo = (self.cruiseEnabled and (curr_time_ms - self.last_stalk_non_cancel_ms) < CANCEL_ECHO_WINDOW_MS) or \
((curr_time_ms - self.preap_last_cc_spoof_ms) < SPOOF_ECHO_WINDOW_MS)
be.type = ButtonType.unknown if is_echo else ButtonType.cancel
if not is_echo:
self.cruiseEnabled = False
self.enableLongControl = False
self.enableJustCC = False
self.pending_enable = False
self.pedal_speed_kph = 0.0
self.stalk_pull_time_ms = 0
self.prev_stalk_pull_time_ms = -1000
elif CruiseButtons.is_accel(state):
be.type = ButtonType.accelCruise
if be.pressed and use_pedal and self.enableLongControl:
speed_uom_kph = CV.MPH_TO_KPH if speed_units == "MPH" else 1.0
actual_kph = int(v_ego * CV.MS_TO_KPH / speed_uom_kph + 0.5) * speed_uom_kph
self.pedal_speed_kph = min(max(self.pedal_speed_kph, actual_kph) + (5 * speed_uom_kph if state == CruiseButtons.RES_ACCEL_2ND else speed_uom_kph), 270.0)
elif CruiseButtons.is_decel(state):
be.type = ButtonType.decelCruise
if be.pressed and use_pedal and self.enableLongControl:
speed_uom_kph = CV.MPH_TO_KPH if speed_units == "MPH" else 1.0
self.pedal_speed_kph = max(self.pedal_speed_kph - (5 * speed_uom_kph if state == CruiseButtons.DECEL_2ND else speed_uom_kph), 0.0)
else:
be.type = ButtonType.unknown
return be
@staticmethod
def _capture_target_speed(v_ego: float, speed_units: str) -> float:
speed_uom_kph = CV.MPH_TO_KPH if speed_units == "MPH" else 1.0
return max(int(v_ego * CV.MS_TO_KPH / speed_uom_kph + 0.5) * speed_uom_kph, 0.0)
@@ -0,0 +1,11 @@
SPEED_BP = [0.0, 5.0, 12.0, 20.0, 30.0, 40.0]
ACCEL_BP = [-1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0, 2.5]
DEFAULT_TABLE = [
[-5.00, -3.33, -1.67, 0.00, 10.00, 20.00, 30.00, 40.00, 50.00],
[-5.00, -3.33, -1.67, 0.00, 11.60, 23.20, 34.80, 46.40, 58.00],
[-5.00, -3.33, -1.67, 0.00, 13.20, 26.40, 39.60, 52.80, 66.00],
[-5.00, -3.33, -1.67, 0.00, 14.80, 29.60, 44.40, 59.20, 74.00],
[-5.00, -3.33, -1.67, 0.00, 16.40, 32.80, 49.20, 65.60, 82.00],
[-5.00, -3.33, -1.67, 0.00, 18.00, 36.00, 54.00, 72.00, 90.00],
]
@@ -0,0 +1,56 @@
import numpy as np
from opendbc.car import get_safety_config, structs
from opendbc.car.tesla.preap.constants import ACCEL_PREAP_BP, ACCEL_PREAP_PROFILES, PEDAL_LONG_K_BP, PEDAL_LONG_KI_V, PEDAL_LONG_KP_V
from opendbc.car.tesla.preap.nap_conf import nap_conf
PREAP_FLAG_ENABLE_PEDAL = 1
PREAP_FLAG_RADAR_EMULATION = 2
PREAP_FLAG_RADAR_BEHIND_NOSECONE = 4
def get_preap_accel_limits(current_speed: float) -> tuple[float, float]:
try:
from openpilot.common.params import Params
personality = Params().get_int("LongitudinalPersonality")
except Exception:
personality = 1
profile = ACCEL_PREAP_PROFILES.get(personality, ACCEL_PREAP_PROFILES[1])
return -1.5, float(np.interp(current_speed, ACCEL_PREAP_BP, profile))
def get_preap_params(ret: structs.CarParams) -> structs.CarParams:
safety_flags = 0
if nap_conf.use_pedal:
safety_flags |= PREAP_FLAG_ENABLE_PEDAL
if nap_conf.radar_enabled:
safety_flags |= PREAP_FLAG_RADAR_EMULATION
if nap_conf.radar_behind_nosecone:
safety_flags |= PREAP_FLAG_RADAR_BEHIND_NOSECONE
use_pedal = nap_conf.use_pedal and nap_conf.pedal_calibrated
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.teslaPreap, safety_flags)]
ret.radarUnavailable = not nap_conf.radar_enabled
ret.steerControlType = structs.CarParams.SteerControlType.angle
ret.openpilotLongitudinalControl = use_pedal
ret.pcmCruise = not use_pedal
ret.alphaLongitudinalAvailable = False
ret.steerLimitTimer = 0.4
ret.steerActuatorDelay = 0.1
ret.steerAtStandstill = True
ret.vEgoStopping = 0.1
ret.vEgoStarting = 0.1
ret.stoppingDecelRate = 1.0
if use_pedal:
ret.longitudinalTuning.kpBP = PEDAL_LONG_K_BP
ret.longitudinalTuning.kpV = PEDAL_LONG_KP_V
ret.longitudinalTuning.kiBP = PEDAL_LONG_K_BP
ret.longitudinalTuning.kiV = PEDAL_LONG_KI_V
try:
ret.longitudinalTuning.kf = 1.0
except AttributeError:
pass
ret.longitudinalActuatorDelay = 0.4
return ret
@@ -0,0 +1,89 @@
from openpilot.common.params import Params
from opendbc.car.tesla.preap.nap_params import NAPParamKeys
_params = Params()
PEDAL_DI_MIN = -5.0
PEDAL_DI_ZERO = 0.0
PEDAL_DI_PRESSED = 2.0
ACCEL_MAX = 2.5
REGEN_MAX = -1.5
PEDAL_BP = [0.0, 5.0, 12.0, 20.0, 30.0, 40.0]
PEDAL_MAX_VALUES = [50.0, 58.0, 66.0, 74.0, 82.0, 90.0]
def transform_di_to_pedal(val: float, pedal_zero: float, pedal_factor: float) -> float:
return pedal_zero + (val - PEDAL_DI_ZERO) / (pedal_factor or 1.0)
def transform_pedal_to_di(val: float, pedal_zero: float, pedal_factor: float) -> float:
return PEDAL_DI_ZERO + (val - pedal_zero) * (pedal_factor or 1.0)
class NAPConf:
@property
def adaptive_accel(self) -> bool:
return _params.get_bool(NAPParamKeys.ADAPTIVE_ACCEL)
@property
def follow_distance(self) -> int:
return max(1, min(7, _params.get_int(NAPParamKeys.FOLLOW_DISTANCE)))
@property
def use_pedal(self) -> bool:
return _params.get_bool(NAPParamKeys.PEDAL_ENABLED)
@property
def pedal_calibrated(self) -> bool:
if not _params.get_bool(NAPParamKeys.PEDAL_CALIB_DONE):
return False
return abs(self.pedal_factor) > 1e-6
@property
def pedal_can_zero(self) -> bool:
return _params.get_int(NAPParamKeys.PEDAL_CAN_BUS) == 0
@property
def pedal_can_bus(self) -> int:
return 0 if self.pedal_can_zero else 2
@property
def pedal_factor(self) -> float:
return _params.get_float(NAPParamKeys.PEDAL_CALIB_FACTOR)
@property
def pedal_zero(self) -> float:
return _params.get_float(NAPParamKeys.PEDAL_CALIB_ZERO)
@property
def radar_enabled(self) -> bool:
return _params.get_bool(NAPParamKeys.RADAR_ENABLED)
@property
def radar_behind_nosecone(self) -> bool:
return _params.get_bool(NAPParamKeys.RADAR_BEHIND_NOSECONE)
@property
def radar_offset(self) -> float:
return _params.get_float(NAPParamKeys.RADAR_OFFSET)
@property
def double_pull_enabled(self) -> bool:
return True
@property
def double_pull_window_ms(self) -> int:
return 500
def get_pedal_profile_values(self) -> list[float]:
return PEDAL_MAX_VALUES
def di_to_pedal(self, val: float) -> float:
return transform_di_to_pedal(val, self.pedal_zero, self.pedal_factor)
def pedal_to_di(self, val: float) -> float:
return transform_pedal_to_di(val, self.pedal_zero, self.pedal_factor)
nap_conf = NAPConf()
@@ -0,0 +1,15 @@
class NAPParamKeys:
ADAPTIVE_ACCEL = "NAPAdaptiveAccel"
FOLLOW_DISTANCE = "NAPFollowDistance"
FORCE_PRE_AP = "NAPForcePreAP"
PEDAL_ENABLED = "NAPPedalEnabled"
PEDAL_PROFILE = "NAPPedalProfile"
PEDAL_CAN_BUS = "NAPPedalCanBus"
PEDAL_CALIB_DONE = "NAPPedalCalibDone"
PEDAL_CALIB_MIN = "NAPPedalCalibMin"
PEDAL_CALIB_MAX = "NAPPedalCalibMax"
PEDAL_CALIB_FACTOR = "NAPPedalCalibFactor"
PEDAL_CALIB_ZERO = "NAPPedalCalibZero"
RADAR_ENABLED = "NAPRadarEnabled"
RADAR_BEHIND_NOSECONE = "NAPRadarBehindNosecone"
RADAR_OFFSET = "NAPRadarOffset"
@@ -0,0 +1,41 @@
from opendbc.car.tesla.preap.nap_conf import PEDAL_DI_PRESSED, nap_conf
PEDAL_TIMEOUT_MS = 500
class PedalFeedback:
def __init__(self):
self.interceptor_value = 0.0
self.interceptor_value2 = 0.0
self.interceptor_state = 0
self.idx = 0
self.prev_idx = 0
self.last_seen_ms = 0
self.available = False
self.timeout = True
self.torque_level = 0.0
def update(self, gas_sensor_msg, curr_time_ms: int) -> bool:
if not gas_sensor_msg:
return False
self.prev_idx = self.idx
self.interceptor_value = float(nap_conf.pedal_to_di(float(gas_sensor_msg.get("INTERCEPTOR_GAS", 0.0))))
self.interceptor_value2 = float(nap_conf.pedal_to_di(float(gas_sensor_msg.get("INTERCEPTOR_GAS2", 0.0))))
self.interceptor_state = int(gas_sensor_msg.get("STATE", 0))
self.idx = int(gas_sensor_msg.get("IDX", 0))
if self.idx != self.prev_idx:
self.last_seen_ms = curr_time_ms
self.timeout = (curr_time_ms - self.last_seen_ms) > PEDAL_TIMEOUT_MS
self.available = (not self.timeout) and (self.interceptor_state == 0)
return True
def update_torque(self, di_torque1_msg) -> None:
self.torque_level = di_torque1_msg.get("DI_torqueMotor", 0.0)
@property
def gas_pressed(self) -> bool:
return self.interceptor_value > PEDAL_DI_PRESSED
@@ -0,0 +1,62 @@
from opendbc.car.tesla.values import CruiseButtons
_PHASE_IDLE = 0
_PHASE_ENGAGING = 1
CANCEL_DELAY_FRAMES = 10
CC_ENGAGE_TIMEOUT_FRAMES = 50
class StockCCSpoofer:
def __init__(self):
self.cc_engage_phase = _PHASE_IDLE
self.cc_engage_start_frame = 0
self.cancel_pending = False
self.cancel_frame = -1_000_000
self.prev_di_cc_engaged = False
self.pcc_event = None
def update(self, CS, frame: int, tesla_can, can_bus_party: int):
can_sends = []
if getattr(CS, "preap_cc_cancel_needed", False):
self.cancel_pending = True
self.cancel_frame = frame
self.cc_engage_phase = _PHASE_IDLE
CS.preap_cc_cancel_needed = False
if getattr(CS, "preap_cc_engage_needed", False) and self.cc_engage_phase == _PHASE_IDLE:
self.cc_engage_phase = _PHASE_ENGAGING
self.cc_engage_start_frame = frame
CS.preap_cc_engage_needed = False
if self.cancel_pending and (frame - self.cancel_frame) >= CANCEL_DELAY_FRAMES and frame % 10 == 0:
sent = self._send(CS, tesla_can, can_bus_party, CruiseButtons.CANCEL)
if sent is not None:
can_sends.append(sent)
self.cancel_pending = False
elif self.cc_engage_phase == _PHASE_ENGAGING and frame % 10 == 0:
if (frame - self.cc_engage_start_frame) >= CC_ENGAGE_TIMEOUT_FRAMES or getattr(CS, "di_cruise_state", "OFF") == "ENABLED":
self.cc_engage_phase = _PHASE_IDLE
else:
sent = self._send(CS, tesla_can, can_bus_party, CruiseButtons.SET_ACCEL)
if sent is not None:
can_sends.append(sent)
di_cc_engaged = getattr(CS, "di_cruise_state", "OFF") == "ENABLED"
if di_cc_engaged and not self.prev_di_cc_engaged:
self.pcc_event = "teslaCCEngaged"
elif not di_cc_engaged and self.prev_di_cc_engaged:
self.pcc_event = "teslaCCDisengaged"
else:
self.pcc_event = None
self.prev_di_cc_engaged = di_cc_engaged
return can_sends
@staticmethod
def _send(CS, tesla_can, can_bus_party: int, button: int):
msg_stw = getattr(CS, "msg_stw_actn_req", None)
if msg_stw is None:
return None
counter = (int(msg_stw.get("MC_STW_ACTN_RQ", 0)) + 1) % 16
return tesla_can.create_action_request(button, can_bus_party, counter, msg_stw)
@@ -0,0 +1,96 @@
import struct
from ctypes import create_string_buffer
from opendbc.car.tesla.values import CANBUS
PEDAL_M1 = 0.050796813
PEDAL_M2 = 0.101593626
PEDAL_D = -22.85856576
GAS_COMMAND_ID = 0x551
_STW_CRC_POLY = 0x1D
_STW_DEFAULTS = {
"VSL_Enbl_Rq": 1, "DTR_Dist_Rq": 0, "TurnIndLvr_Stat": 0,
"HiBmLvr_Stat": 0, "WprWashSw_Psd": 0, "WprWash_R_Sw_Posn_V2": 0,
"StW_Lvr_Stat": 0, "StW_Cond_Flt": 0, "StW_Cond_Psd": 0,
"HrnSw_Psd": 0, "StW_Sw00_Psd": 0, "StW_Sw01_Psd": 0,
"StW_Sw02_Psd": 0, "StW_Sw03_Psd": 0, "StW_Sw04_Psd": 0,
"StW_Sw05_Psd": 0, "StW_Sw06_Psd": 0,
"WprSw6Posn": 0,
}
def _crc8_stw(data: bytes) -> int:
crc = 0xFF
for b in data:
crc ^= b
for _ in range(8):
crc = ((crc << 1) ^ _STW_CRC_POLY) & 0xFF if (crc & 0x80) else (crc << 1) & 0xFF
return crc ^ 0xFF
class TeslaCANPreAP:
def __init__(self, packer):
self.packer = packer
self.pedal_can_bus = 2
self.pedal_idx = 0
@staticmethod
def checksum(msg_id: int, dat: bytes) -> int:
return ((msg_id & 0xFF) + ((msg_id >> 8) & 0xFF) + sum(dat)) & 0xFF
def create_steering_control(self, counter: int, angle: float, enabled: bool):
values = {
"DAS_steeringControlCounter": counter,
"DAS_steeringAngleRequest": -angle,
"DAS_steeringHapticRequest": 0,
"DAS_steeringControlType": 1 if enabled else 0,
}
data = self.packer.make_can_msg("DAS_steeringControl", CANBUS.party, values)[1]
values["DAS_steeringControlChecksum"] = self.checksum(0x488, data[:3])
return self.packer.make_can_msg("DAS_steeringControl", CANBUS.party, values)
def create_epas_control(self, counter: int, mode: int):
values = {
"EPB_epasEACAllow": mode,
"EPB_epasControlCounter": counter,
"EPB_epasControlChecksum": 0,
}
data = self.packer.make_can_msg("EPB_epasControl", CANBUS.party, values)[1]
values["EPB_epasControlChecksum"] = self.checksum(0x214, data)
return self.packer.make_can_msg("EPB_epasControl", CANBUS.party, values)
def create_pedal_command(self, accel_command: float, enable: int = 1, pedal_can_bus: int | None = None):
if pedal_can_bus is None:
pedal_can_bus = self.pedal_can_bus
idx = self.pedal_idx
self.pedal_idx = (self.pedal_idx + 1) % 16
if enable == 1:
int_cmd1 = max(0, min(65534, int((accel_command - PEDAL_D) / PEDAL_M1)))
int_cmd2 = max(0, min(65534, int((accel_command - PEDAL_D) / PEDAL_M2)))
else:
int_cmd1 = 0
int_cmd2 = 0
msg = create_string_buffer(6)
struct.pack_into("BBBBB", msg, 0,
(int_cmd1 >> 8) & 0xFF, int_cmd1 & 0xFF,
(int_cmd2 >> 8) & 0xFF, int_cmd2 & 0xFF,
((enable << 7) + idx) & 0xFF)
struct.pack_into("B", msg, 5, self.checksum(GAS_COMMAND_ID, msg.raw))
return GAS_COMMAND_ID, bytes(msg.raw), pedal_can_bus
def create_action_request(self, button_to_press: int, bus: int, counter: int, msg_stw=None):
values = {"MC_STW_ACTN_RQ": counter, "CRC_STW_ACTN_RQ": 0, "SpdCtrlLvr_Stat": button_to_press}
if msg_stw is not None:
for key, default in _STW_DEFAULTS.items():
values[key] = msg_stw.get(key, default)
else:
values.update(_STW_DEFAULTS)
# Preserve the live stalk layout, but force VSL enable on engage/resume spoofs.
values["VSL_Enbl_Rq"] = 0 if button_to_press == 1 else 1
data = self.packer.make_can_msg("STW_ACTN_RQ", bus, values)[1]
values["CRC_STW_ACTN_RQ"] = _crc8_stw(data[:7])
return self.packer.make_can_msg("STW_ACTN_RQ", bus, values)
@@ -0,0 +1,139 @@
import math
from numpy import clip, interp
from opendbc.car.common.filter_simple import FirstOrderFilter, HighPassFilter
from opendbc.car.common.pid import PIDController
from opendbc.car.tesla.pedal.controller import PEDAL_RAMP_RATE_DOWN, PEDAL_RAMP_RATE_UP, get_zero_torque
from opendbc.car.tesla.preap.constants import (
VDAS_AEGO_FILTER_RC,
VDAS_FUTURE_T_BP,
VDAS_FUTURE_T_V,
VDAS_INNER_K_BP,
VDAS_INNER_KI_V,
VDAS_INNER_KP_V,
)
from opendbc.car.tesla.preap.ff_table_default import ACCEL_BP, DEFAULT_TABLE, SPEED_BP
from opendbc.car.tesla.preap.nap_conf import ACCEL_MAX, PEDAL_BP, PEDAL_DI_MIN, PEDAL_MAX_VALUES, REGEN_MAX
PID_ERROR_DEADBAND = 0.1
GRAVITY = 9.81
PITCH_LP_RC = 0.5
PITCH_HP_RC1 = 0.1
PITCH_HP_RC2 = 1.0
MAX_PITCH_COMPENSATION = 1.5
class GradeEstimator:
def __init__(self, dt: float = 0.02):
self.pitch_lp = FirstOrderFilter(0.0, PITCH_LP_RC, dt)
self.pitch_hp = HighPassFilter(0.0, PITCH_HP_RC1, PITCH_HP_RC2, dt)
def update(self, orientation_ned: list[float]) -> tuple[float, float]:
if len(orientation_ned) < 2:
return 0.0, 0.0
pitch = orientation_ned[1]
self.pitch_lp.update(pitch)
self.pitch_hp.update(pitch)
grade_accel = math.sin(self.pitch_lp.x) * GRAVITY
pitch_comp = float(clip(math.sin(self.pitch_hp.x) * GRAVITY, -MAX_PITCH_COMPENSATION, MAX_PITCH_COMPENSATION))
return grade_accel, pitch_comp
def reset(self) -> None:
self.pitch_lp.x = 0.0
self.pitch_hp.x = 0.0
self.pitch_hp._f1.x = 0.0
self.pitch_hp._f2.x = 0.0
class JerkLimiter:
def __init__(self, j_max: float = 2.5, dt: float = 0.02):
self.j_max = j_max
self.dt = dt
self.a_limited = 0.0
def update(self, a_cmd: float) -> float:
da_max = self.j_max * self.dt
self.a_limited += float(clip(a_cmd - self.a_limited, -da_max, da_max))
return self.a_limited
def reset(self, a_init: float = 0.0) -> None:
self.a_limited = a_init
class FeedforwardModel:
def __init__(self):
self.speed_bp = list(SPEED_BP)
self.accel_bp = list(ACCEL_BP)
self.table = [list(row) for row in DEFAULT_TABLE]
def get(self, a_cmd: float, v_ego: float, zero_torque_di: float) -> float:
si = float(interp(v_ego, self.speed_bp, range(len(self.speed_bp))))
si_lo = int(si)
si_hi = min(si_lo + 1, len(self.speed_bp) - 1)
sf = si - si_lo
di_lo = float(interp(a_cmd, self.accel_bp, self.table[si_lo]))
di_hi = float(interp(a_cmd, self.accel_bp, self.table[si_hi]))
base_di = di_lo + sf * (di_hi - di_lo)
if a_cmd < 0:
blend = float(clip((a_cmd - REGEN_MAX) / (0.0 - REGEN_MAX), 0.0, 1.0))
else:
blend = float(1.0 - a_cmd / ACCEL_MAX)
return base_di + zero_torque_di * blend
class VirtualDAS:
def __init__(self, dt: float = 0.02):
self.dt = dt
self.jerk_limiter = JerkLimiter(dt=dt)
self.ff_model = FeedforwardModel()
self.grade_estimator = GradeEstimator(dt=dt)
self.inner_pid = PIDController(
k_p=(VDAS_INNER_K_BP, VDAS_INNER_KP_V),
k_i=(VDAS_INNER_K_BP, VDAS_INNER_KI_V),
k_f=0.0,
pos_limit=PEDAL_RAMP_RATE_UP,
neg_limit=-PEDAL_RAMP_RATE_DOWN,
rate=1.0 / dt,
)
self.a_ego_filter = FirstOrderFilter(0.0, VDAS_AEGO_FILTER_RC, dt)
self.prev_a_ego_filtered = 0.0
self.prev_pedal_di = 0.0
def update(self, a_cmd: float, v_ego: float, prev_pedal_di: float, a_ego: float = 0.0,
freeze_integrator: bool = False, orientation_ned: list[float] | None = None) -> float:
a_limited = self.jerk_limiter.update(a_cmd)
grade_accel, pitch_comp = self.grade_estimator.update(orientation_ned or [])
ff_di = self._feedforward(a_limited, v_ego) + pitch_comp
a_ego_corrected = a_ego - grade_accel
a_ego_filtered = self.a_ego_filter.update(a_ego_corrected)
j_ego = (a_ego_filtered - self.prev_a_ego_filtered) / self.dt
self.prev_a_ego_filtered = a_ego_filtered
future_t = float(interp(v_ego, VDAS_FUTURE_T_BP, VDAS_FUTURE_T_V))
error = a_limited - (a_ego_filtered + j_ego * future_t)
if abs(error) < PID_ERROR_DEADBAND:
error = 0.0
pedal_di = ff_di + float(self.inner_pid.update(error, speed=v_ego, freeze_integrator=freeze_integrator))
max_pedal_value = float(interp(v_ego, PEDAL_BP, PEDAL_MAX_VALUES))
pedal_di = float(clip(pedal_di, PEDAL_DI_MIN, max_pedal_value))
pedal_di = float(clip(pedal_di, prev_pedal_di - PEDAL_RAMP_RATE_DOWN, prev_pedal_di + PEDAL_RAMP_RATE_UP))
self.prev_pedal_di = pedal_di
return pedal_di
def reset(self, a_init: float = 0.0, pedal_di_init: float = 0.0) -> None:
self.jerk_limiter.reset(a_init)
self.inner_pid.reset()
self.grade_estimator.reset()
self.a_ego_filter.x = 0.0
self.prev_a_ego_filtered = 0.0
self.prev_pedal_di = pedal_di_init
def _feedforward(self, a_cmd: float, v_ego: float) -> float:
zero_torque_di = get_zero_torque().get(v_ego)
max_pedal_value = float(interp(v_ego, PEDAL_BP, PEDAL_MAX_VALUES))
return float(clip(self.ff_model.get(a_cmd, v_ego, zero_torque_di), PEDAL_DI_MIN, max_pedal_value))
@@ -0,0 +1,87 @@
from opendbc.can import CANParser
from opendbc.car import Bus, structs
from opendbc.car.interfaces import RadarInterfaceBase
from opendbc.car.tesla.preap.nap_conf import nap_conf
from opendbc.car.tesla.values import CANBUS, DBC, CAR
_BOSCH_RADAR_STATUS_MSG = 769
_BOSCH_RADAR_POINT_A_BASE = 784
_BOSCH_RADAR_POINT_B_BASE = 785
_BOSCH_RADAR_POINT_STRIDE = 3
_BOSCH_RADAR_POINTS = 32
_BOSCH_TRIGGER_MSG = _BOSCH_RADAR_POINT_B_BASE + ((_BOSCH_RADAR_POINTS - 1) * _BOSCH_RADAR_POINT_STRIDE)
class RadarInterface(RadarInterfaceBase):
def __init__(self, CP):
super().__init__(CP)
self.radar_off_can = CP.radarUnavailable or CP.carFingerprint != CAR.TESLA_MODEL_S_PREAP
self.updated_messages: set[int] = set()
self.track_id = 0
self.radar_offset = float(nap_conf.radar_offset) if CP.carFingerprint == CAR.TESLA_MODEL_S_PREAP else 0.0
if self.radar_off_can:
self.rcp = None
else:
messages = [(_BOSCH_RADAR_STATUS_MSG, 8)]
for i in range(_BOSCH_RADAR_POINTS):
messages.append((_BOSCH_RADAR_POINT_A_BASE + (i * _BOSCH_RADAR_POINT_STRIDE), 8))
messages.append((_BOSCH_RADAR_POINT_B_BASE + (i * _BOSCH_RADAR_POINT_STRIDE), 8))
self.rcp = CANParser(DBC[CP.carFingerprint][Bus.radar], messages, CANBUS.radar)
self.trigger_msg = _BOSCH_TRIGGER_MSG
def update(self, can_strings):
if self.radar_off_can or self.rcp is None:
return super().update(None)
vls = self.rcp.update(can_strings)
self.updated_messages.update(vls)
if self.trigger_msg not in self.updated_messages:
return None
ret = structs.RadarData()
if not self.rcp.can_valid:
ret.errors.canError = True
radar_status = self.rcp.vl[_BOSCH_RADAR_STATUS_MSG]
ret.errors.radarFault = bool(radar_status["RADC_HWFail"])
ret.errors.radarUnavailableTemporary = False
current_points: set[int] = set()
for i in range(_BOSCH_RADAR_POINTS):
msg_a_id = _BOSCH_RADAR_POINT_A_BASE + (i * _BOSCH_RADAR_POINT_STRIDE)
msg_b_id = _BOSCH_RADAR_POINT_B_BASE + (i * _BOSCH_RADAR_POINT_STRIDE)
msg_a = self.rcp.vl[msg_a_id]
msg_b = self.rcp.vl[msg_b_id]
if msg_a["Index"] != msg_b["Index2"]:
continue
if not msg_a["Tracked"] or msg_a["LongDist"] <= 0.0 or msg_a["LongDist"] > 250.0 or msg_a["ProbExist"] < 50.0:
self.pts.pop(i, None)
continue
current_points.add(i)
if i not in self.pts:
self.pts[i] = structs.RadarData.RadarPoint()
self.pts[i].trackId = self.track_id
self.track_id += 1
point = self.pts[i]
point.dRel = msg_a["LongDist"]
point.yRel = msg_a["LatDist"] + self.radar_offset
point.vRel = msg_a["LongSpeed"]
point.aRel = msg_a["LongAccel"]
point.yvRel = msg_b["LatSpeed"]
point.measured = bool(msg_a["Meas"])
for point_id in list(self.pts.keys()):
if point_id not in current_points:
del self.pts[point_id]
ret.points = list(self.pts.values())
self.updated_messages.clear()
return ret
+31 -1
View File
@@ -60,6 +60,16 @@ class CAR(Platforms):
[TeslaCarDocsHW4("Tesla Model X (with HW4) 2024")],
CarSpecs(mass=2495., wheelbase=2.960, steerRatio=12.0),
)
TESLA_MODEL_S_PREAP = TeslaPlatformConfig(
[CarDocs("Tesla Model S (Pre-AP) 2012-14", "All")],
CarSpecs(mass=2100., wheelbase=2.960, steerRatio=15.0),
{
Bus.party: 'tesla_can',
Bus.pt: 'tesla_can',
Bus.chassis: 'tesla_can',
Bus.radar: 'tesla_radar_bosch_generated',
},
)
FW_QUERY_CONFIG = FwQueryConfig(
@@ -75,7 +85,8 @@ FW_QUERY_CONFIG = FwQueryConfig(
class CANBUS:
party = 0
vehicle = 1
radar = 1
vehicle = radar
autopilot_party = 2
@@ -125,6 +136,25 @@ class TeslaFlags(IntFlag):
LONG_CONTROL = 1
class CruiseButtons:
IDLE = 0
CANCEL = 1
MAIN = 2
RES_ACCEL_2ND = 4
DECEL_2ND = 8
SET_ACCEL = 16
RES_ACCEL = 16
DECEL_SET = 32
@classmethod
def is_accel(cls, btn: int) -> bool:
return btn in (cls.RES_ACCEL, cls.RES_ACCEL_2ND)
@classmethod
def is_decel(cls, btn: int) -> bool:
return btn in (cls.DECEL_SET, cls.DECEL_2ND)
DBC = CAR.create_dbc_map()
STEER_THRESHOLD = 1
+22
View File
@@ -22,9 +22,21 @@ non_tested_cars = [
MOCK.MOCK,
GM.CADILLAC_ATS,
GM.CADILLAC_ESCALADE_ASCM,
GM.CADILLAC_XT5,
GM.HOLDEN_ASTRA,
GM.CHEVROLET_MALIBU,
HYUNDAI.GENESIS_G90,
HYUNDAI.GENESIS_GV70_ELECTRIFIED_2ND_GEN,
HYUNDAI.GENESIS_GV80_2025,
HYUNDAI.HYUNDAI_IONIQ_5_PE,
HYUNDAI.HYUNDAI_IONIQ_5_N,
HYUNDAI.HYUNDAI_SANTA_FE_HEV_5TH_GEN,
HYUNDAI.HYUNDAI_TUCSON_PHEV_2025,
HYUNDAI.KIA_EV6_2025,
HYUNDAI.KIA_EV9,
HYUNDAI.KIA_SPORTAGE_2026,
HYUNDAI.KIA_SORENTO_2024,
HYUNDAI.KIA_SORENTO_HEV_4TH_GEN_LFA2,
VOLKSWAGEN.VOLKSWAGEN_CRAFTER_MK2, # need a route from an ACC-equipped Crafter
SUBARU.SUBARU_FORESTER_HYBRID,
VOLKSWAGEN.PORSCHE_MACAN_MK1,
@@ -143,6 +155,7 @@ routes = [
CarTestRoute("66eaa6c3b6b2afc6/00000009--3a5199aabe", HYUNDAI.GENESIS_G80_2ND_GEN_FL), # LKA steering
CarTestRoute("0bbe367c98fa1538/2023-09-16--00-16-49", HYUNDAI.HYUNDAI_CUSTIN_1ST_GEN),
CarTestRoute("f0709d2bc6ca451f/2022-10-15--08-13-54", HYUNDAI.HYUNDAI_SANTA_CRUZ_1ST_GEN),
CarTestRoute("6e7904b03a4aafc2/00000010--31034184b6", HYUNDAI.HYUNDAI_SANTA_CRUZ_2025),
CarTestRoute("4dbd55df87507948/2022-03-01--09-45-38", HYUNDAI.HYUNDAI_SANTA_FE),
CarTestRoute("bf43d9df2b660eb0/2021-09-23--14-16-37", HYUNDAI.HYUNDAI_SANTA_FE_2022),
CarTestRoute("37398f32561a23ad/2021-11-18--00-11-35", HYUNDAI.HYUNDAI_SANTA_FE_HEV_2022),
@@ -156,11 +169,14 @@ routes = [
CarTestRoute("6a42c1197b2a8179/2023-09-21--10-23-44", HYUNDAI.KIA_OPTIMA_H_G4_FL),
CarTestRoute("c75a59efa0ecd502/2021-03-11--20-52-55", HYUNDAI.KIA_SELTOS),
CarTestRoute("5b7c365c50084530/2020-04-15--16-13-24", HYUNDAI.HYUNDAI_SONATA),
CarTestRoute("4267ea8a353cdb36/00000262--8a427003c7", HYUNDAI.HYUNDAI_SONATA_2024, segment=34),
CarTestRoute("b2a38c712dcf90bd/2020-05-18--18-12-48", HYUNDAI.HYUNDAI_SONATA_LF),
CarTestRoute("c344fd2492c7a9d2/2023-12-11--09-03-23", HYUNDAI.HYUNDAI_STARIA_4TH_GEN),
CarTestRoute("fb3fd42f0baaa2f8/2022-03-30--15-25-05", HYUNDAI.HYUNDAI_TUCSON),
CarTestRoute("db68bbe12250812c/2022-12-05--00-54-12", HYUNDAI.HYUNDAI_TUCSON_4TH_GEN), # 2023
CarTestRoute("36e10531feea61a4/2022-07-25--13-37-42", HYUNDAI.HYUNDAI_TUCSON_4TH_GEN), # hybrid
CarTestRoute("a01ca5d4f394c812/00000000--e98b7e4414", HYUNDAI.HYUNDAI_TUCSON_2025),
CarTestRoute("22f9090014364a87/00000002--4cc739c0b2", HYUNDAI.HYUNDAI_TUCSON_HEV_2025),
CarTestRoute("5875672fc1d4bf57/2020-07-23--21-33-28", HYUNDAI.KIA_SORENTO),
CarTestRoute("1d0d000db3370fd0/2023-01-04--22-28-42", HYUNDAI.KIA_SORENTO_4TH_GEN, segment=5),
CarTestRoute("fc19648042eb6896/2023-08-16--11-43-27", HYUNDAI.KIA_SORENTO_HEV_4TH_GEN, segment=14),
@@ -168,6 +184,7 @@ routes = [
CarTestRoute("9c917ba0d42ffe78/2020-04-17--12-43-19", HYUNDAI.HYUNDAI_PALISADE),
CarTestRoute("05a8f0197fdac372/2022-10-19--14-14-09", HYUNDAI.HYUNDAI_IONIQ_5), # LKA steering
CarTestRoute("eb4eae1476647463/2023-08-26--18-07-04", HYUNDAI.HYUNDAI_IONIQ_6, segment=6), # LKA steering
CarTestRoute("71e4e67d29034771/0000001b--b7b4774ec4", HYUNDAI.HYUNDAI_IONIQ_9),
CarTestRoute("3f29334d6134fcd4/2022-03-30--22-00-50", HYUNDAI.HYUNDAI_IONIQ_PHEV_2019),
CarTestRoute("fa8db5869167f821/2021-06-10--22-50-10", HYUNDAI.HYUNDAI_IONIQ_PHEV),
CarTestRoute("e1107f9d04dfb1e2/2023-09-05--22-32-12", HYUNDAI.HYUNDAI_IONIQ_PHEV), # openpilot longitudinal enabled
@@ -178,11 +195,13 @@ routes = [
CarTestRoute("ab59fe909f626921/2021-10-18--18-34-28", HYUNDAI.HYUNDAI_IONIQ_HEV_2022),
CarTestRoute("22d955b2cd499c22/2020-08-10--19-58-21", HYUNDAI.HYUNDAI_KONA),
CarTestRoute("0099bdb24d82951b/00000005--c38d940b04", HYUNDAI.HYUNDAI_KONA_2022),
CarTestRoute("32025f26789d8fab/00000022--a499e8ffa3", HYUNDAI.HYUNDAI_KONA_2ND_GEN),
CarTestRoute("efc48acf44b1e64d/2021-05-28--21-05-04", HYUNDAI.HYUNDAI_KONA_EV),
CarTestRoute("f90d3cd06caeb6fa/2023-09-06--17-15-47", HYUNDAI.HYUNDAI_KONA_EV), # openpilot longitudinal enabled
CarTestRoute("ff973b941a69366f/2022-07-28--22-01-19", HYUNDAI.HYUNDAI_KONA_EV_2022, segment=11),
CarTestRoute("1618132d68afc876/2023-08-27--09-32-14", HYUNDAI.HYUNDAI_KONA_EV_2ND_GEN, segment=13),
CarTestRoute("49f3c13141b6bc87/2021-07-28--08-05-13", HYUNDAI.HYUNDAI_KONA_HEV),
CarTestRoute("97ca61196eb73e0d/00000052--4555329470", HYUNDAI.HYUNDAI_KONA_HEV_2ND_GEN),
CarTestRoute("a74afe0cf708748f/0000000e--a2885a9a71", HYUNDAI.HYUNDAI_NEXO_1ST_GEN),
CarTestRoute("a74afe0cf708748f/0000000c--b476a8fd00", HYUNDAI.HYUNDAI_NEXO_1ST_GEN), # openpilot longitudinal enabled
CarTestRoute("5dddcbca6eb66c62/2020-07-26--13-24-19", HYUNDAI.KIA_STINGER),
@@ -191,7 +210,9 @@ routes = [
CarTestRoute("d545129f3ca90f28/2022-10-19--09-22-54", HYUNDAI.KIA_EV6), # LKA steering
CarTestRoute("68d6a96e703c00c9/2022-09-10--16-09-39", HYUNDAI.KIA_EV6), # LFA steering
CarTestRoute("9b25e8c1484a1b67/2023-04-13--10-41-45", HYUNDAI.KIA_EV6),
CarTestRoute("baf39eeaba1217ca/00000002--b36e3fa031", HYUNDAI.KIA_K4_2025),
CarTestRoute("007d5e4ad9f86d13/2021-09-30--15-09-23", HYUNDAI.KIA_K5_2021),
CarTestRoute("c4a804b067623789/0000007c--163f831540", HYUNDAI.KIA_K5_2025),
CarTestRoute("c58dfc9fc16590e0/2023-01-14--13-51-48", HYUNDAI.KIA_K5_HEV_2020),
CarTestRoute("78ad5150de133637/2023-09-13--16-15-57", HYUNDAI.KIA_K8_HEV_1ST_GEN),
CarTestRoute("50c6c9b85fd1ff03/2020-10-26--17-56-06", HYUNDAI.KIA_NIRO_EV),
@@ -210,6 +231,7 @@ routes = [
CarTestRoute("82e9cdd3f43bf83e/2021-05-15--02-42-51", HYUNDAI.HYUNDAI_ELANTRA_2021),
CarTestRoute("715ac05b594e9c59/2021-06-20--16-21-07", HYUNDAI.HYUNDAI_ELANTRA_HEV_2021),
CarTestRoute("7120aa90bbc3add7/2021-08-02--07-12-31", HYUNDAI.HYUNDAI_SONATA_HYBRID),
CarTestRoute("bc40c72b728178f2/00000006--ee76ae8c42", HYUNDAI.HYUNDAI_SONATA_HEV_2024),
CarTestRoute("715ac05b594e9c59/2021-10-27--23-24-56", HYUNDAI.GENESIS_G70_2020),
CarTestRoute("6b0d44d22df18134/2023-05-06--10-36-55", HYUNDAI.GENESIS_GV80),
@@ -9,7 +9,7 @@ from typing import Any
from cereal import custom
from opendbc.car import DT_CTRL, CanData, structs
from opendbc.car.car_helpers import interfaces
from opendbc.car.car_helpers import _apply_disable_openpilot_long, interfaces
from opendbc.car.chrysler.carstate import CarState as ChryslerCarState
from opendbc.car.fingerprints import FW_VERSIONS
from opendbc.car.fw_versions import FW_QUERY_CONFIGS
@@ -18,6 +18,7 @@ from opendbc.car.hyundai.values import CAR as HYUNDAI_CAR, HyundaiStarPilotSafet
from opendbc.car.gm.values import CAR as GM_CAR, CanBus as GMCanBus, GMSafetyFlags
from opendbc.car.interfaces import CarInterfaceBase, CarStateBase, get_interface_attr
from opendbc.car.mock.values import CAR as MOCK
from opendbc.car.toyota.values import CAR as TOYOTA_CAR, ToyotaSafetyFlags
from opendbc.car.values import PLATFORMS
DrawType = Callable[[st.SearchStrategy], Any]
@@ -213,6 +214,40 @@ class TestCarInterfaces:
)
assert fp_car_params.safetyConfigs[-1].safetyParam & HyundaiStarPilotSafetyFlags.AOL_LKAS_ON_ENGAGE.value
def test_toyota_disable_openpilot_long_sets_stock_long_safety_flag(self):
CarInterface = interfaces[TOYOTA_CAR.TOYOTA_PRIUS_TSS2]
fingerprint = {bus: {} for bus in range(8)}
toggles = get_test_starpilot_toggles()
car_params = CarInterface.get_params(
TOYOTA_CAR.TOYOTA_PRIUS_TSS2,
fingerprint,
[],
alpha_long=False,
is_release=False,
docs=False,
starpilot_toggles=toggles,
)
assert car_params.openpilotLongitudinalControl
assert not car_params.alphaLongitudinalAvailable
assert not (car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.STOCK_LONGITUDINAL.value)
fp_car_params = CarInterface.get_starpilot_params(
TOYOTA_CAR.TOYOTA_PRIUS_TSS2,
fingerprint,
[],
car_params,
toggles,
)
_apply_disable_openpilot_long(car_params, fp_car_params)
assert not car_params.openpilotLongitudinalControl
assert car_params.pcmCruise
assert fp_car_params.openpilotLongitudinalControlDisabled
assert car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
assert fp_car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
# FIXME: Due to the lists used in carParams, Phase.target is very slow and will cause
# many generated examples to overrun when max_examples > ~20, don't use it
@pytest.mark.parametrize("car_name", sorted(PLATFORMS))
@@ -1,3 +1,4 @@
from opendbc.car.chrysler.values import CAR as CHRYSLER_CAR, pacifica_hybrid_aol_stock_acc_mode
from opendbc.car.values import PLATFORMS
@@ -15,3 +16,13 @@ class TestPlatformConfigs:
assert name == platform.config.platform_str
assert platform.config.specs is not None
def test_pacifica_hybrid_aol_stock_acc_mode_requires_pr_conditions(self):
assert pacifica_hybrid_aol_stock_acc_mode(CHRYSLER_CAR.CHRYSLER_PACIFICA_2019_HYBRID, True, False, True)
assert not pacifica_hybrid_aol_stock_acc_mode(CHRYSLER_CAR.CHRYSLER_PACIFICA_2019_HYBRID, True, True, True)
assert not pacifica_hybrid_aol_stock_acc_mode(CHRYSLER_CAR.CHRYSLER_PACIFICA_2019_HYBRID, True, False, False)
assert not pacifica_hybrid_aol_stock_acc_mode(CHRYSLER_CAR.CHRYSLER_PACIFICA_2019_HYBRID, False, False, True)
def test_pacifica_hybrid_aol_stock_acc_mode_stays_narrow(self):
assert not pacifica_hybrid_aol_stock_acc_mode(CHRYSLER_CAR.CHRYSLER_PACIFICA_2018_HYBRID, True, False, True)
assert not pacifica_hybrid_aol_stock_acc_mode(CHRYSLER_CAR.CHRYSLER_PACIFICA_2020, True, False, True)
@@ -64,13 +64,25 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"VOLKSWAGEN_PASSAT_NMS" = [2.5, 2.5, 0.1]
"VOLKSWAGEN_SHARAN_MK2" = [2.5, 2.5, 0.1]
"HYUNDAI_SANTA_CRUZ_1ST_GEN" = [2.7, 2.7, 0.1]
"HYUNDAI_SANTA_CRUZ_2025" = [2.5, 2.5, 0.1]
"HYUNDAI_SANTA_FE_HEV_5TH_GEN" = [nan, 2.5, nan]
"HYUNDAI_SONATA_2024" = [2.5, 2.5, 0.1]
"HYUNDAI_SONATA_HEV_2024" = [2.5, 2.5, 0.1]
"HYUNDAI_KONA_2ND_GEN" = [2.5, 2.5, 0.1]
"HYUNDAI_KONA_HEV_2ND_GEN" = [2.5, 2.5, 0.1]
"HYUNDAI_TUCSON_2025" = [2.5, 2.5, 0.1]
"HYUNDAI_TUCSON_HEV_2025" = [2.5, 2.5, 0.1]
"HYUNDAI_TUCSON_PHEV_2025" = [2.5, 2.5, 0.1]
"KIA_SPORTAGE_5TH_GEN" = [2.6, 2.6, 0.1]
"KIA_SPORTAGE_2026" = [nan, 2.5, nan]
"KIA_SPORTAGE_HEV_2026" = [nan, 2.5, nan]
"GENESIS_GV70_1ST_GEN" = [2.42, 2.42, 0.1]
"GENESIS_GV60_EV_1ST_GEN" = [2.5, 2.5, 0.1]
"GMC_YUKON" = [1.2, 2.5, 0.26]
"KIA_SORENTO_4TH_GEN" = [2.5, 2.5, 0.1]
"KIA_SORENTO_2024" = [2.5, 2.5, 0.1]
"KIA_SORENTO_HEV_4TH_GEN" = [2.5, 2.5, 0.1]
"KIA_SORENTO_HEV_4TH_GEN_LFA2" = [nan, 2.5, nan]
"KIA_NIRO_HEV_2ND_GEN" = [2.42, 2.5, 0.12]
"KIA_NIRO_EV_2ND_GEN" = [2.05, 2.5, 0.14]
"GENESIS_GV80" = [2.5, 2.5, 0.1]
@@ -78,15 +90,24 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"GMC_ACADIA" = [1.6, 1.6, 0.2]
"LEXUS_IS_TSS2" = [2.0, 2.0, 0.1]
"HYUNDAI_KONA_EV_2ND_GEN" = [2.5, 2.5, 0.1]
"HYUNDAI_IONIQ_5_PE" = [nan, 2.5, nan]
"HYUNDAI_IONIQ_5_N" = [2.5, 2.5, 0.1]
"HYUNDAI_IONIQ_6" = [3.0, 3.0, 0.09]
"HYUNDAI_IONIQ_9" = [nan, 2.5, nan]
"HYUNDAI_AZERA_6TH_GEN" = [1.8, 1.8, 0.1]
"HYUNDAI_AZERA_HEV_6TH_GEN" = [1.8, 1.8, 0.1]
"KIA_K8_HEV_1ST_GEN" = [2.5, 2.5, 0.1]
"KIA_K4_2025" = [2.5, 2.5, 0.1]
"KIA_K5_2025" = [2.5, 2.5, 0.1]
"KIA_EV6_2025" = [nan, 2.5, nan]
"KIA_EV9" = [nan, 2.5, nan]
"HYUNDAI_CUSTIN_1ST_GEN" = [2.5, 2.5, 0.1]
"LEXUS_GS_F" = [2.5, 2.5, 0.08]
"HYUNDAI_STARIA_4TH_GEN" = [1.8, 2.0, 0.15]
"GENESIS_GV70_ELECTRIFIED_1ST_GEN" = [1.9, 1.9, 0.09]
"GENESIS_GV70_ELECTRIFIED_2ND_GEN" = [nan, 2.5, nan]
"GENESIS_G80_2ND_GEN_FL" = [2.5819356441497803, 2.5, 0.11244568973779678]
"GENESIS_GV80_2025" = [nan, 2.5, nan]
# Note that some Rivians achieve significantly less lateral acceleration than this
"RIVIAN_R1_GEN1" = [2.8, 2.5, 0.07]
"HYUNDAI_NEXO_1ST_GEN" = [2.5, 2.5, 0.1]
@@ -33,6 +33,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"HYUNDAI_KONA_2022" = "HYUNDAI_KONA_EV"
"HYUNDAI_KONA_HEV" = "HYUNDAI_KONA_EV"
"HYUNDAI_KONA_EV_2022" = "HYUNDAI_KONA_EV"
"HYUNDAI_PALISADE_2023" = "HYUNDAI_PALISADE"
"HYUNDAI_IONIQ" = "HYUNDAI_IONIQ_PHEV_2019"
"HYUNDAI_IONIQ_HEV_2022" = "HYUNDAI_IONIQ_PHEV_2019"
"HYUNDAI_IONIQ_EV_2020" = "HYUNDAI_IONIQ_PHEV_2019"
@@ -46,13 +47,25 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"GENESIS_G90" = "GENESIS_G70"
"GENESIS_G80" = "GENESIS_G70"
"GENESIS_G70_2020" = "HYUNDAI_SONATA"
"KIA_CEED_PHEV_2022_NON_SCC" = "HYUNDAI_SONATA"
"HYUNDAI_KONA_EV_NON_SCC" = "HYUNDAI_KONA_EV"
"KIA_FORTE_2019_NON_SCC" = "HYUNDAI_SONATA"
"KIA_FORTE_2021_NON_SCC" = "HYUNDAI_SONATA"
"KIA_SELTOS_2023_NON_SCC" = "HYUNDAI_SONATA"
"HYUNDAI_KONA_NON_SCC" = "HYUNDAI_KONA_EV"
"HYUNDAI_ELANTRA_2022_NON_SCC" = "HYUNDAI_ELANTRA_2021"
"HYUNDAI_ELANTRA_HEV_2022_NON_SCC" = "HYUNDAI_ELANTRA_HEV_2021"
"GENESIS_G70_2021_NON_SCC" = "HYUNDAI_SONATA"
"HYUNDAI_BAYON_1ST_GEN_NON_SCC" = "HYUNDAI_SONATA"
"HONDA_FREED" = "HONDA_ODYSSEY"
"HONDA_CLARITY" = "HONDA_ACCORD"
"HONDA_CRV_EU" = "HONDA_CRV"
"HONDA_CIVIC_BOSCH_DIESEL" = "HONDA_CIVIC_BOSCH"
"HONDA_E" = "HONDA_CIVIC_BOSCH"
"BUICK_LACROSSE" = "CHEVROLET_VOLT"
"BUICK_LACROSSE_ASCM" = "CHEVROLET_VOLT"
"BUICK_REGAL" = "CHEVROLET_VOLT"
"CADILLAC_ESCALADE_ASCM" = "CADILLAC_ESCALADE"
"CADILLAC_ESCALADE_ESV" = "CHEVROLET_VOLT"
@@ -87,6 +100,8 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"SUBARU_FORESTER_HYBRID" = "SUBARU_IMPREZA_2020"
"SUBARU_LEGACY" = "SUBARU_OUTBACK"
"TESLA_MODEL_S_PREAP" = "TESLA_MODEL_3"
# Old subarus don't have much data guessing it's like low torque impreza"
"SUBARU_OUTBACK_PREGLOBAL_2018" = "SUBARU_IMPREZA"
"SUBARU_OUTBACK_PREGLOBAL" = "SUBARU_IMPREZA"
@@ -95,10 +110,12 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"SUBARU_ASCENT" = "SUBARU_FORESTER"
"CADILLAC_CT6_CC" = "CHEVROLET_VOLT"
"CADILLAC_XT5" = "GMC_ACADIA"
"CADILLAC_XT5_CC" = "GMC_ACADIA"
"BUICK_BABYENCLAVE" = "GMC_ACADIA"
"CHEVROLET_EQUINOX_CC" = "CHEVROLET_EQUINOX"
"GMC_YUKON_CC" = "CHEVROLET_SILVERADO"
"CHEVROLET_TRAILBLAZER_CC" = "CHEVROLET_TRAILBLAZER"
"CHEVROLET_SILVERADO_CC" = "CHEVROLET_SILVERADO"
"CHEVROLET_TRAX" = "CHEVROLET_VOLT"
+173 -49
View File
@@ -1,6 +1,6 @@
import math
import numpy as np
from opendbc.car import Bus, make_tester_present_msg, rate_limit, structs, ACCELERATION_DUE_TO_GRAVITY, DT_CTRL
from opendbc.car import Bus, create_gas_interceptor_command, make_tester_present_msg, rate_limit, structs, ACCELERATION_DUE_TO_GRAVITY, DT_CTRL
from opendbc.car.lateral import apply_meas_steer_torque_limits, apply_std_steer_angle_limits, common_fault_avoidance
from opendbc.car.can_definitions import CanData
from opendbc.car.carlog import carlog
@@ -9,7 +9,7 @@ from opendbc.car.common.pid import PIDController
from opendbc.car.secoc import add_mac, build_sync_mac
from opendbc.car.interfaces import CarControllerBase
from opendbc.car.toyota import toyotacan
from opendbc.car.toyota.values import CAR, NO_STOP_TIMER_CAR, TSS2_CAR, \
from opendbc.car.toyota.values import CAR, MIN_ACC_SPEED, NO_STOP_TIMER_CAR, PEDAL_TRANSITION, TSS2_CAR, \
CarControllerParams, ToyotaFlags, \
UNSUPPORTED_DSU_CAR
from opendbc.can import CANPacker
@@ -28,6 +28,11 @@ PRIUS_INTEGRAL_MISMATCH_UNWIND = 8.0
PRIUS_POSITIVE_FEEDFORWARD_SCALE = 0.5
MAX_PITCH_COMPENSATION = 1.5 # m/s^2
TOYOTA_COAST_BRAKE_MIN_SPEED = 15.0 # m/s
TOYOTA_COAST_BRAKE_ENABLE_ACCEL = -0.10 # m/s^2
TOYOTA_COAST_BRAKE_DISABLE_ACCEL = -0.06 # m/s^2
TOYOTA_NO_LEAD_COAST_BRAKE_ACCEL = -0.30 # m/s^2
TOYOTA_INTERCEPTOR_COMFORT_TARGET_ACCEL = 2.0 # m/s^2
# LKA limits
# EPS faults if you apply torque while the steering rate is above 100 deg/s for too long
@@ -60,6 +65,83 @@ def get_long_tune(CP, params):
rate=1 / (DT_CTRL * 3))
def update_permit_braking(current: bool, net_acceleration_request_min: float, stopping: bool,
long_active: bool, v_ego: float, lead_visible: bool) -> bool:
if stopping or not long_active:
return True
if not lead_visible and v_ego >= TOYOTA_COAST_BRAKE_MIN_SPEED:
return net_acceleration_request_min <= TOYOTA_NO_LEAD_COAST_BRAKE_ACCEL
# At cruising speeds, some Toyota platforms turn tiny negative accel corrections
# into noticeable brake taps. Keep a small coast-only band so mild follow/cruise
# trims stay off the brakes while still allowing real negative requests through.
if v_ego >= TOYOTA_COAST_BRAKE_MIN_SPEED:
if net_acceleration_request_min <= TOYOTA_COAST_BRAKE_ENABLE_ACCEL:
return True
if net_acceleration_request_min >= TOYOTA_COAST_BRAKE_DISABLE_ACCEL:
return False
return current
if net_acceleration_request_min < 0.2:
return True
if net_acceleration_request_min > 0.3:
return False
return current
def limit_interceptor_pcm_accel(pcm_accel_cmd: float, target_accel: float, stopping: bool, v_ego: float) -> float:
if stopping:
return pcm_accel_cmd
# The Toyota long path should not cross the accel sign against a strong planner
# request on pedal/SDSU cars during ordinary driving.
if target_accel > 0.15 and pcm_accel_cmd < 0.0:
positive_floor = float(np.interp(v_ego, [0.0, 5.0, 15.0], [0.12, 0.08, 0.0]))
pcm_accel_cmd = max(pcm_accel_cmd, positive_floor)
elif target_accel < -0.15 and pcm_accel_cmd > 0.0:
negative_floor = float(np.interp(v_ego, [0.0, 5.0, 15.0], [-0.12, -0.08, 0.0]))
pcm_accel_cmd = min(pcm_accel_cmd, negative_floor)
if target_accel <= -1.6:
return pcm_accel_cmd
if abs(target_accel) > TOYOTA_INTERCEPTOR_COMFORT_TARGET_ACCEL:
return pcm_accel_cmd
lower_delta = float(np.interp(v_ego, [0.0, 5.0, 15.0], [0.20, 0.25, 0.35]))
upper_delta = float(np.interp(v_ego, [0.0, 5.0, 15.0], [0.25, 0.30, 0.40]))
limited = float(np.clip(pcm_accel_cmd, target_accel - lower_delta, target_accel + upper_delta))
# Pedal/SDSU cars can feel especially surgey if the Toyota longitudinal controller
# swings far away from the planner target. Bias comfort-zone requests back toward
# the planner while still allowing some controller correction.
target_weight = float(np.interp(v_ego, [0.0, 5.0, 15.0], [0.8, 0.65, 0.5]))
limited = (target_weight * target_accel) + ((1.0 - target_weight) * limited)
if target_accel > 0.15:
limited = max(limited, float(np.interp(v_ego, [0.0, 5.0, 15.0], [0.10, 0.05, 0.0])))
elif target_accel < -0.15:
limited = min(limited, float(np.interp(v_ego, [0.0, 5.0, 15.0], [-0.10, -0.05, 0.0])))
return limited
def limit_interceptor_stopping_accel(pcm_accel_cmd: float, target_accel: float, stopping: bool, v_ego: float, lead_visible: bool) -> float:
if not stopping or lead_visible or pcm_accel_cmd >= 0.0 or v_ego >= 2.5:
return pcm_accel_cmd
# Pedal/SDSU Toyotas can feel abrupt in the last few feet of a no-lead stop
# because stopping state can hold onto a stale strong negative command even
# after the planner target has already softened. Keep real lead stops
# untouched, but let the last 2-3 mph of a no-lead stop unwind toward the
# current planner target instead of shoving through zero.
stop_floor = float(np.interp(v_ego, [0.0, 0.2, 0.5, 0.9, 1.5, 2.5], [-0.72, -0.76, -0.82, -0.92, -1.05, -1.20]))
target_buffer = float(np.interp(v_ego, [0.0, 0.5, 1.5, 2.5], [0.08, 0.10, 0.15, 0.20]))
planner_floor = float(target_accel) - target_buffer
return max(pcm_accel_cmd, max(stop_floor, planner_floor))
class CarController(CarControllerBase):
def __init__(self, dbc_names, CP):
super().__init__(dbc_names, CP)
@@ -92,6 +174,50 @@ class CarController(CarControllerBase):
self.doors_locked = False
def _compute_interceptor_gas_cmd(self, CC, CS):
if not (self.CP.enableGasInterceptorDEPRECATED and self.CP.openpilotLongitudinalControl and CC.longActive):
return 0.0
if self.CP.minEnableSpeed < 0.0:
return 0.12 if CS.out.standstill and self.accel > 0.0 else 0.0
max_interceptor_gas = 0.5
if self.CP.carFingerprint == CAR.TOYOTA_RAV4:
pedal_scale = float(np.interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.15, 0.3, 0.0]))
elif self.CP.carFingerprint == CAR.TOYOTA_COROLLA:
pedal_scale = float(np.interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.3, 0.4, 0.0]))
else:
pedal_scale = float(np.interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.4, 0.5, 0.0]))
pedal_offset = float(np.interp(CS.out.vEgo, [0.0, 2.3, MIN_ACC_SPEED + PEDAL_TRANSITION], [-0.4, 0.0, 0.2]))
pedal_command = pedal_scale * (self.accel + pedal_offset)
return float(np.clip(pedal_command, 0.0, max_interceptor_gas))
def _update_standstill_request(self, CC, CS, actuators, starpilot_toggles):
# Older TSS-P platforms need a standstill latch pulse, then an explicit release to move again.
if self.CP.carFingerprint not in NO_STOP_TIMER_CAR:
if CS.out.standstill and not self.last_standstill and not starpilot_toggles.sng_hack:
self.standstill_req = True
if CS.pcm_acc_status != 8 or CC.cruiseControl.resume or starpilot_toggles.sng_hack:
# Clear once the PCM has latched the stop, when planner wants to move,
# or when the SNG hack is forcing the latch open.
self.standstill_req = False
else:
# if user engages at a stop with foot on brake, PCM starts in a special cruise standstill mode. on resume press,
# brakes can take a while to ramp up causing a lurch forward. prevent resume press until planner wants to move.
# don't use CC.cruiseControl.resume since it is gated on CS.cruiseState.standstill which goes false for 3s after resume press
# TODO: hybrids do not have this issue and can stay stopped after resume press, whitelist them
should_resume = actuators.accel > 0 or starpilot_toggles.sng_hack
if should_resume:
self.standstill_req = False
if not should_resume and CS.out.cruiseState.standstill:
self.standstill_req = True
self.last_standstill = CS.out.standstill
def update(self, CC, CS, now_nanos, starpilot_toggles):
actuators = CC.actuators
stopping = actuators.longControlState == LongCtrlState.stopping
@@ -184,33 +310,18 @@ class CarController(CarControllerBase):
# *** gas and brake ***
# on entering standstill, send standstill request for older TSS-P cars that aren't designed to stay engaged at a stop
if self.CP.carFingerprint not in NO_STOP_TIMER_CAR:
if CS.out.standstill and not self.last_standstill and not starpilot_toggles.sng_hack:
self.standstill_req = True
if CS.pcm_acc_status != 8:
# pcm entered standstill or it's disabled
self.standstill_req = False
else:
# if user engages at a stop with foot on brake, PCM starts in a special cruise standstill mode. on resume press,
# brakes can take a while to ramp up causing a lurch forward. prevent resume press until planner wants to move.
# don't use CC.cruiseControl.resume since it is gated on CS.cruiseState.standstill which goes false for 3s after resume press
# TODO: hybrids do not have this issue and can stay stopped after resume press, whitelist them
should_resume = actuators.accel > 0 or starpilot_toggles.sng_hack
if should_resume:
self.standstill_req = False
if not should_resume and CS.out.cruiseState.standstill:
self.standstill_req = True
self.last_standstill = CS.out.standstill
self._update_standstill_request(CC, CS, actuators, starpilot_toggles)
interceptor_gas_cmd = self._compute_interceptor_gas_cmd(CC, CS)
# handle UI messages
fcw_alert = hud_control.visualAlert == VisualAlert.fcw
steer_alert = hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw)
lead = hud_control.leadVisible or CS.out.vEgo < 12. # at low speed we always assume the lead is present so ACC can be engaged
if self.frame % 2 == 0 and self.CP.enableGasInterceptorDEPRECATED and self.CP.openpilotLongitudinalControl:
# Send an explicit zero when off so the interceptor does not rescale around a stale command.
can_sends.append(create_gas_interceptor_command(self.packer, interceptor_gas_cmd, self.frame // 2))
if self.CP.openpilotLongitudinalControl:
if self.frame % 3 == 0:
# Press distance button until we are at the correct bar length. Only change while enabled to avoid skipping startup popup
@@ -251,41 +362,54 @@ class CarController(CarControllerBase):
a_ego_future = a_ego_blended + j_ego * future_t
if CC.longActive:
# constantly slowly unwind integral to recover from large temporary errors
unwind_rate = ACCEL_PID_UNWIND
if self.CP.carFingerprint == CAR.TOYOTA_PRIUS and pcm_accel_cmd * self.long_pid.i < 0.0:
unwind_rate *= PRIUS_INTEGRAL_MISMATCH_UNWIND
self.long_pid.i -= unwind_rate * float(np.sign(self.long_pid.i))
if self.CP.enableGasInterceptorDEPRECATED:
# Pedal/SDSU Toyotas have shown better behavior when we trust the planner
# target directly instead of letting the Toyota longitudinal PID swing it
# around. Keep the shared rate limits above, but bypass the extra
# correction layer that causes rev/release oscillation.
self.long_pid.reset()
else:
# constantly slowly unwind integral to recover from large temporary errors
unwind_rate = ACCEL_PID_UNWIND
if self.CP.carFingerprint == CAR.TOYOTA_PRIUS and pcm_accel_cmd * self.long_pid.i < 0.0:
unwind_rate *= PRIUS_INTEGRAL_MISMATCH_UNWIND
self.long_pid.i -= unwind_rate * float(np.sign(self.long_pid.i))
error_future = pcm_accel_cmd - a_ego_future
error_future = pcm_accel_cmd - a_ego_future
if not stopping:
# Toyota's PCM slowly responds to changes in pitch. On change, we amplify our
# acceleration request to compensate for the undershoot and following overshoot
pitch_compensation = float(np.clip(math.sin(self.pitch_hp.x) * ACCELERATION_DUE_TO_GRAVITY,
-MAX_PITCH_COMPENSATION, MAX_PITCH_COMPENSATION))
pcm_accel_cmd += pitch_compensation
if not stopping:
# Toyota's PCM slowly responds to changes in pitch. On change, we amplify our
# acceleration request to compensate for the undershoot and following overshoot
pitch_compensation = float(np.clip(math.sin(self.pitch_hp.x) * ACCELERATION_DUE_TO_GRAVITY,
-MAX_PITCH_COMPENSATION, MAX_PITCH_COMPENSATION))
pcm_accel_cmd += pitch_compensation
feedforward = pcm_accel_cmd
if self.CP.carFingerprint == CAR.TOYOTA_PRIUS:
# Keep Prius positive handoffs softer than the stock tune, while restoring some launch authority.
if feedforward > 0.0:
feedforward *= PRIUS_POSITIVE_FEEDFORWARD_SCALE
feedforward = pcm_accel_cmd
if self.CP.carFingerprint == CAR.TOYOTA_PRIUS:
# Keep Prius positive handoffs softer than the stock tune, while restoring some launch authority.
if feedforward > 0.0:
feedforward *= PRIUS_POSITIVE_FEEDFORWARD_SCALE
pcm_accel_cmd = self.long_pid.update(error_future,
speed=CS.out.vEgo,
feedforward=feedforward,
freeze_integrator=actuators.longControlState != LongCtrlState.pid)
pcm_accel_cmd = self.long_pid.update(error_future,
speed=CS.out.vEgo,
feedforward=feedforward,
freeze_integrator=actuators.longControlState != LongCtrlState.pid)
else:
self.long_pid.reset()
# Along with rate limiting positive jerk above, this greatly improves gas response time
# Consider the net acceleration request that the PCM should be applying (pitch included)
net_acceleration_request_min = min(actuators.accel + accel_due_to_pitch, net_acceleration_request)
if net_acceleration_request_min < 0.2 or stopping or not CC.longActive:
self.permit_braking = True
elif net_acceleration_request_min > 0.3:
self.permit_braking = False
self.permit_braking = update_permit_braking(self.permit_braking,
net_acceleration_request_min,
stopping,
CC.longActive,
CS.out.vEgo,
lead)
if self.CP.enableGasInterceptorDEPRECATED:
pcm_accel_cmd = limit_interceptor_pcm_accel(pcm_accel_cmd, actuators.accel, stopping, CS.out.vEgo)
pcm_accel_cmd = limit_interceptor_stopping_accel(pcm_accel_cmd, actuators.accel, stopping, CS.out.vEgo, bool(hud_control.leadVisible))
pcm_accel_cmd = float(np.clip(pcm_accel_cmd, self.params.ACCEL_MIN, self.params.ACCEL_MAX))
@@ -329,7 +453,7 @@ class CarController(CarControllerBase):
if self.frame % 20 == 0 or send_ui:
can_sends.append(toyotacan.create_ui_command(self.packer, steer_alert, pcm_cancel_cmd, hud_control.leftLaneVisible,
hud_control.rightLaneVisible, hud_control.leftLaneDepart,
hud_control.rightLaneDepart, CC.enabled, CS.lkas_hud))
hud_control.rightLaneDepart, CS.lkas_hud, lat_active))
if (self.frame % 100 == 0 or send_ui) and self.CP.flags & ToyotaFlags.DISABLE_RADAR.value:
can_sends.append(toyotacan.create_fcw_command(self.packer, fcw_alert))
+12 -1
View File
@@ -38,6 +38,11 @@ def calculate_speed_limit(cp_cam):
return 0
def calculate_interceptor_gas_pressed(cp) -> bool:
interceptor_gas = (cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS"] + cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS2"]) / 2
return interceptor_gas > 805
class CarState(CarStateBase):
def __init__(self, CP, FPCP):
super().__init__(CP, FPCP)
@@ -99,7 +104,10 @@ class CarState(CarStateBase):
ret.gasPressed = cp.vl["GAS_PEDAL"]["GAS_PEDAL_USER"] > 0
can_gear = int(cp.vl["GEAR_PACKET_HYBRID"]["GEAR"])
else:
ret.gasPressed = cp.vl["PCM_CRUISE"]["GAS_RELEASED"] == 0 # TODO: these also have GAS_PEDAL, come back and unify
if self.CP.enableGasInterceptorDEPRECATED:
ret.gasPressed = calculate_interceptor_gas_pressed(cp)
else:
ret.gasPressed = cp.vl["PCM_CRUISE"]["GAS_RELEASED"] == 0 # TODO: these also have GAS_PEDAL, come back and unify
can_gear = int(cp.vl["GEAR_PACKET"]["GEAR"])
if not self.CP.flags & ToyotaFlags.DISABLE_RADAR.value:
ret.stockAeb = bool(cp_acc.vl["PRE_COLLISION"]["PRECOLLISION_ACTIVE"] and cp_acc.vl["PRE_COLLISION"]["FORCE"] < -1e-5)
@@ -269,6 +277,9 @@ class CarState(CarStateBase):
("BLINKERS_STATE", float('nan')),
]
if CP.enableGasInterceptorDEPRECATED:
pt_messages.append(("GAS_SENSOR", 50))
return {
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], pt_messages, 0),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 2),
+11 -2
View File
@@ -54,6 +54,9 @@ class CarInterface(CarInterfaceBase):
# openpilot to send it. 0x2AA is sent by a similar device which intercepts
# the radar instead of DSU on NO_DSU_CARs.
use_sdsu = bool(0x2FF in fingerprint[0] or (0x2AA in fingerprint[0] and candidate in NO_DSU_CAR))
if use_sdsu:
# sDSU / radar filter hardware needs the Toyota safety long-filter TX set.
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.LONG_FILTER.value
# In TSS2 cars, the camera does long control
found_ecus = [fw.ecu for fw in car_fw]
@@ -133,15 +136,16 @@ class CarInterface(CarInterfaceBase):
bool(ret.flags & ToyotaFlags.DISABLE_RADAR.value))
ret.autoResumeSng = ret.openpilotLongitudinalControl and candidate in NO_STOP_TIMER_CAR
ret.enableGasInterceptorDEPRECATED = 0x201 in fingerprint[0] and ret.openpilotLongitudinalControl
if not ret.openpilotLongitudinalControl:
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
# min speed to enable ACC. if car can do stop and go, then set enabling speed
# to a negative value, so it won't matter.
ret.minEnableSpeed = -1. if stop_and_go else MIN_ACC_SPEED
ret.minEnableSpeed = -1. if (stop_and_go or ret.enableGasInterceptorDEPRECATED) else MIN_ACC_SPEED
if candidate in TSS2_CAR:
if candidate in TSS2_CAR or ret.enableGasInterceptorDEPRECATED:
ret.flags |= ToyotaFlags.RAISED_ACCEL_LIMIT.value
ret.vEgoStopping = 0.25
@@ -152,6 +156,11 @@ class CarInterface(CarInterfaceBase):
if ret.flags & ToyotaFlags.HYBRID.value:
ret.longitudinalActuatorDelay = 0.05
if ret.enableGasInterceptorDEPRECATED:
# Pedal/SDSU Toyotas feel best with a softer final stop clamp.
ret.longitudinalActuatorDelay = max(ret.longitudinalActuatorDelay, 0.2)
ret.stopAccel = -1.5
return ret
@staticmethod

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