Compare commits

...

613 Commits

Author SHA1 Message Date
firestar5683 1a527cb927 rl3 2026-07-22 15:59:31 -05:00
firestar5683 88be0e6387 beeg 2026-07-22 15:41:34 -05:00
firestar5683 cc5c95705d jwarm 2026-07-22 15:32:02 -05:00
firestar5683 d16d72afc6 mici fallback too 2026-07-22 14:16:16 -05:00
firestar5683 6230d08c0f Stutter in raybig? 2026-07-22 13:55:57 -05:00
firestar5683 48af4d2f7f build 2026-07-22 11:41:19 -05:00
firestar5683 d773ad9025 fixes 2026-07-22 11:38:35 -05:00
whoisdomi 894eae7090 Galaxy radar gate fix
Previously showed radartakeoffs and Human like lane changes to
non-radar cars.
2026-07-22 07:36:41 -05:00
firestarsdog 28fcba8471 Big UI : Quick Vehicle Panel Cleanup Pass 2026-07-21 21:14:32 -04:00
firestarsdog 0cff67bda8 Big UI: Try this Domi 2026-07-21 18:29:12 -04:00
firestar5683 f0eeec955f build 2026-07-21 17:18:00 -05:00
firestar5683 9b28c7bd52 Register vision support params 2026-07-21 17:17:11 -05:00
firestar5683 ef400a9b3f Same Subscriber 2026-07-21 17:15:10 -05:00
firestar5683 d4b3c3c3b0 final polish 2026-07-21 16:51:51 -05:00
firestar5683 c57c05886c panda 2026-07-21 15:21:39 -05:00
firestar5683 80f465616a Tone 2026-07-21 15:20:28 -05:00
firestar5683 4e70755895 tunes 2026-07-21 14:29:34 -05:00
firestar5683 5b41566e4e ev9 2026-07-21 13:39:36 -05:00
firestar5683 2141e5a5b1 cleanup 2026-07-21 13:24:01 -05:00
whoisdomi d51dd02d10 Turn Desire Bug 2 + Force Stop 2026-07-21 13:12:42 -05:00
whoisdomi bb3ed894b1 Lane Change Overshoot Edge Case
Latch entry direction instead of comparing to the command's live sign,
which broke once the command crossed zero during a curve's larger
arrest swing. This will prevent overshoot on curves.
2026-07-21 13:12:41 -05:00
whoisdomi d4c911f58c Turn Desire Bug
Blinker turn desire fed the model a turnLeft/turnRight input below lane-change
speed, inflating model_length so the car rolled past stop lines. Gate it in
desire_helper on prior-frame starpilotPlan.redLight/forcingStop/stopSignConfirmed
until standstill, then release so the model still turns through. "Stop first,
then turn."
2026-07-21 13:12:40 -05:00
firestar5683 fc87af4da1 hotplug 2026-07-21 12:22:47 -05:00
firestarsdog 7cd02e0c40 Big UI: Size up simple download manager 2026-07-21 12:53:42 -04:00
firestar5683 db8d38e85f build 2026-07-21 11:47:36 -05:00
firestar5683 2c7e6e72c5 PID Alerts 2026-07-21 11:45:22 -05:00
firestar5683 85dd1cbe7e Tunes 2026-07-21 11:35:29 -05:00
firestar5683 665f538533 test(hyundai): cover EV9 cluster safety message 2026-07-21 11:35:29 -05:00
LowkeyNEXT 28a4de1c9e safety(hyundai): allow EV9 CCNC angle-long messages 2026-07-21 11:35:28 -05:00
LowkeyNEXT 42eef3f21c feat(hyundai): add EV9 alpha longitudinal support 2026-07-21 11:35:28 -05:00
whoisdomi ce86d9d05a Start Accel bug fix 2026-07-21 11:35:28 -05:00
whoisdomi b70d0941c9 Traffic Mode v2 2026-07-21 11:35:28 -05:00
firestarsdog 8d7ba284d7 Big UI : Favorites + Downloaded 2026-07-21 11:57:51 -04:00
firestarsdog 71b577cbc2 Big UI: Default to expanded while I think 2026-07-21 11:29:18 -04:00
firestar5683 cc40a28562 home cleanup 2026-07-20 23:53:20 -05:00
firestar5683 104ae74901 sped away 2026-07-20 23:22:06 -05:00
firestar5683 2e45e57287 Young Cheddar 2026-07-20 22:49:09 -05:00
firestar5683 10843b2f6e log 2026-07-20 21:59:42 -05:00
firestar5683 eb3eb116eb The Fairlife Milk Out of Stock Ya'll 2026-07-20 21:34:47 -05:00
firestar5683 b34e968f91 But Bigger 2026-07-20 20:51:04 -05:00
firestar5683 3701079265 icon 2026-07-20 20:11:18 -05:00
firestarsdog 1a713fe3e2 Big UI : Enlarge Lead Metrics 2026-07-20 18:00:38 -04:00
firestar5683 6d92a8e5ad no pid 2026-07-20 16:48:50 -05:00
firestarsdog 01df304f88 Big UI: Randomizer Smandomizer 2026-07-20 17:34:17 -04:00
firestarsdog 7a76325e67 Big UI : We can sort models now 2026-07-20 17:19:13 -04:00
firestar5683 c3d1f727c0 yas 2026-07-20 16:02:11 -05:00
firestar5683 94e60c5d70 New Raybig Homescreen 2026-07-20 15:34:21 -05:00
firestar5683 43143e7a1f clarified-moonstone 2026-07-20 15:34:14 -05:00
firestarsdog f2b14c7ac0 Big UI : Model Manager cleanup 2026-07-20 15:25:08 -04:00
firestar5683 d5c0fea948 build 2026-07-20 14:22:32 -05:00
firestar5683 a51b1fd78f Omnioculars V1 2026-07-20 14:07:12 -05:00
firestar5683 8b7c2b5f36 oo buddy boy 2026-07-20 13:18:27 -05:00
firestarsdog 0eab89c8d9 Big UI : A lil Domi proofing 2026-07-20 14:13:17 -04:00
firestar5683 7b124faad2 Anti Burn In 2026-07-20 12:33:41 -05:00
firestar5683 4ac7fcaf5c Refactor 2026-07-20 12:23:38 -05:00
firestar5683 f265aa3f7b Japanese BBQ Sauce 2026-07-20 11:59:49 -05:00
firestar5683 11044975c5 build 2026-07-20 11:26:51 -05:00
firestar5683 27f73a6f08 Kirkland Rotisserie 2026-07-20 11:26:07 -05:00
firestarsdog 3e5bb853cb Big UI: Fix Source Bubble 2026-07-20 10:25:15 -04:00
whoisdomi 2f0864e215 Low Speed Turn Assist update
Slightly higher turn in speeds when possible.

This follows model desires, so if the model doesn't request it or is weak not much can be done.
2026-07-20 07:12:15 -05:00
firestarsdog 8376ba7c97 Big UI : Widget Layout Manager : RIP? 2026-07-19 17:57:33 -04:00
firestar5683 c928e27033 Favorites 2026-07-19 15:45:04 -05:00
firestar5683 5f52120d7a personality 2026-07-19 15:27:16 -05:00
firestar5683 cab8457f47 build 2026-07-19 13:59:44 -05:00
firestar5683 8453f6b4eb 2 2026-07-19 13:59:20 -05:00
firestar5683 03cf613e4c mi mi zu zu pri pri 2026-07-19 13:42:37 -05:00
firestarsdog ce44830b22 Big UI: No flicker 2026-07-19 00:49:23 -04:00
firestar5683 3ae3c5361e Burnt Wasabi 2026-07-18 23:10:00 -05:00
firestarsdog d3dd2545a9 Scale up dm preview in raylib 2026-07-18 23:02:55 -04:00
firestar5683 29cb892e1a Toyota 2026-07-18 19:17:25 -05:00
firestar5683 a106e222ca ez mode 2026-07-18 13:53:48 -05:00
firestar5683 6ccc984dab build 2026-07-18 12:15:43 -05:00
firestar5683 cce803e888 happy saturday 2026-07-18 12:15:09 -05:00
whoisdomi 9427041200 More lane change butter please 2026-07-18 07:07:28 -05:00
whoisdomi 6bae55ac8e Low Speed Turn Assist
When using the blinker coming up to a turn and the car near standstill or stops the model goes blind, but before it does comma will now remember what it was trying to do and continue going that direction. Manually moving the wheel or cancelling turn signal will undo this. Turns will initiate at a lower speed.
2026-07-18 07:07:27 -05:00
firestar5683 d8b9bf5529 build 2026-07-17 22:49:46 -05:00
firestar5683 4c07ea96e7 fixes 2026-07-17 22:49:09 -05:00
firestarsdog fcf1bf2f3c Big UI : Dedicated to Dora and all the Explorers 2026-07-17 22:58:03 -04:00
firestar5683 e3c377df34 ev9 angle 2026-07-17 14:02:38 -05:00
firestar5683 28141e3074 jeep 2026-07-17 13:54:42 -05:00
firestar5683 25c5722372 lat smooth 2026-07-17 13:34:20 -05:00
firestar5683 d699f26ded leedle 2026-07-17 11:59:47 -05:00
firestar5683 9ccd61e8cf Mizuzu 2026-07-17 00:17:41 -05:00
firestar5683 fccd46fef5 build 2026-07-16 23:54:08 -05:00
firestar5683 1863259a58 The Night's Watch 2026-07-16 23:51:45 -05:00
firestarsdog c001d2b184 Big UI: Alerts 2026-07-16 23:11:02 -04:00
firestarsdog 03ebfd2c69 Big UI: Sounds/Alerts Fixes 2026-07-16 23:03:26 -04:00
firestarsdog 94141aebb1 Big UI: Screen brightness 2026-07-16 22:52:39 -04:00
firestarsdog 71ee4ab2a3 Big UI: Clean 2026-07-16 20:08:31 -04:00
firestarsdog dfb5acddf5 Big UI: Cleanup 2026-07-16 19:45:32 -04:00
firestarsdog 33d7e977b4 Big UI : Cleanup 2026-07-16 18:57:57 -04:00
firestar5683 c6708e716f Curves 2026-07-16 14:33:52 -05:00
firestar5683 5a7ec6b154 sleepProb 2026-07-16 12:33:27 -05:00
firestar5683 d935514f78 favs 2026-07-16 11:39:24 -05:00
firestar5683 8f3f9cf6c7 yas 2026-07-16 10:33:59 -05:00
firestarsdog 4013a70e9d Ex Tee Four Cee Cee 2026-07-16 10:29:47 -04:00
firestar5683 3e363cf7a3 build 2026-07-15 23:02:20 -05:00
firestar5683 9f13e71a0a carnivallll 2026-07-15 23:02:20 -05:00
firestar5683 28ea8d0c09 dsu bypass 2026-07-15 22:24:04 -05:00
firestar5683 d0c877f591 update 2026-07-15 22:07:25 -05:00
firestar5683 b5ebc6474e Free Galaxy 2026-07-15 21:35:13 -05:00
firestar5683 cb151bb9aa build 2026-07-15 20:53:21 -05:00
firestar5683 e943d2afd2 Stop It. 2026-07-15 20:43:45 -05:00
firestar5683 8556708cd6 plan stan 2026-07-15 19:32:36 -05:00
firestarsdog 6fa509cc88 Big UI: Nuke 2026-07-15 19:02:57 -04:00
firestar5683 8e7ce87be8 owalawattabotta 2026-07-15 15:57:21 -05:00
firestar5683 5d8137b30a O canada 2026-07-15 14:02:15 -05:00
firestar5683 368cb1a6f6 distilled-moonstone v4 2026-07-15 13:48:19 -05:00
firestar5683 ba460bbb7b patch 2026-07-15 11:32:02 -05:00
firestar5683 a6ffaaa781 build 2026-07-15 11:25:27 -05:00
firestar5683 d4cfd34291 food & stuff 2026-07-15 11:20:09 -05:00
firestar5683 b0334c2ae5 nissan 2026-07-15 08:08:08 -05:00
firestarsdog 6a19e82bb2 Big UI : Biggify 2026-07-15 03:48:07 -04:00
firestarsdog 5693de7d8d Big UI: No balls 2026-07-15 03:28:19 -04:00
firestarsdog 20fa52f5df Big UI: Pagination cleanup 2026-07-15 03:20:44 -04:00
firestarsdog cb540160e4 Big UI: Fix pagination indicator overlap 2026-07-15 01:59:28 -04:00
firestarsdog be1fda1504 Big UI: stats to bottom border 2026-07-15 01:39:16 -04:00
firestarsdog 0c3bd608d9 Big UI: Skinny pill 2026-07-15 01:16:44 -04:00
firestarsdog 258b4c6048 Big UI: Widget show/hide cleanup 2026-07-15 00:55:54 -04:00
firestarsdog 8b23fd8c03 Big UI: Border definition 2026-07-15 00:32:26 -04:00
firestar5683 eb7d4212fd cleanups / BUMP 2026-07-14 22:03:41 -05:00
firestar5683 73485ba76c galaxy 2026-07-14 21:13:47 -05:00
firestarsdog 1ea2143e01 Big UI: Country Roads 2026-07-14 21:35:25 -04:00
firestarsdog 20235f027d Big UI: Distance Button text 2026-07-14 19:57:46 -04:00
firestarsdog 55ad1fd0fd BigUI: Purple Rain 2026-07-14 19:43:43 -04:00
firestar5683 5a2117892c toyboy button 2026-07-14 18:27:08 -05:00
firestar5683 d8e4d75656 build 2026-07-14 18:03:52 -05:00
firestar5683 77db3e506c aol 2026-07-14 17:57:12 -05:00
firestar5683 acd15fcba7 c4 2026-07-14 17:30:59 -05:00
firestar5683 00539e1dc2 writeup 2026-07-14 17:12:55 -05:00
firestar5683 fb98b8cb29 Speed Limit Writeup 2026-07-14 17:01:33 -05:00
firestarsdog fc88f70ebd Big UI: Quick Vehicle Panel Cleanup WIP 2026-07-14 17:47:31 -04:00
firestar5683 6233e7b2c0 camera view 2026-07-14 16:33:42 -05:00
firestar5683 0144a691b9 distilled-moonstone v3 2026-07-14 12:23:32 -05:00
firestar5683 37a6c62e6e Reapply "The Final Kachow" 2026-07-14 11:55:45 -05:00
firestar5683 ca09e50081 Revert "The Final Kachow"
This reverts commit 8098b9fa53.
2026-07-14 11:25:31 -05:00
firestar5683 8098b9fa53 The Final Kachow 2026-07-14 11:16:29 -05:00
firestar5683 5564d133b3 HondaDays 2026-07-14 09:36:22 -05:00
firestarsdog 6e3bf42c97 guess we need these too... cleanup l8er 2026-07-14 03:40:59 -04:00
firestarsdog f94babe509 BigUI WIP: Button Map Cleanup 2026-07-14 02:17:37 -04:00
firestar5683 45eead8dcd nighty night 2026-07-14 01:16:15 -05:00
firestarsdog fcd39be875 BigUI WIP: SLC Bug fixes 2026-07-14 02:08:30 -04:00
firestar5683 a609716520 guess we need these 2026-07-14 00:10:28 -05:00
firestar5683 a0ae06022c cleanup errybody errywhere 2026-07-13 23:59:13 -05:00
firestarsdog c9cf8cfdf6 BigUI WIP: Can we see this? 2026-07-14 00:44:58 -04:00
firestar5683 3d4e1f2d48 raybillywig 2026-07-13 22:39:18 -05:00
firestar5683 ca74743eac Raylib stuff 2026-07-13 22:18:15 -05:00
firestar5683 fb031a3c57 slc raylib fix 2026-07-13 22:12:29 -05:00
firestar5683 4bad6f6f79 yas 2026-07-13 21:58:47 -05:00
whoisdomi 7e1b3769d7 Lane Change Smoothing 2.0
1. Reduced jerk
2. Smoother
3. Improved arrest, reducing chances of overshooting if setting is too low
2026-07-13 15:16:57 -05:00
firestar5683 4a98785aa3 FLM 2026-07-13 14:56:23 -05:00
firestar5683 d305071351 build 2026-07-13 14:01:47 -05:00
firestar5683 653009185e elantra and sped 2026-07-13 14:00:50 -05:00
firestarsdog 57bfc691bf BigUI WIP: Vertical toggletile list? 2026-07-13 13:44:11 -04:00
firestar5683 7e4f8d4154 patch1 2026-07-13 12:41:08 -05:00
firestar5683 361f692d53 lagathy 2026-07-13 10:07:16 -05:00
firestar5683 f81099af79 buttonAOl 2026-07-13 09:55:10 -05:00
firestar5683 1d982da86e distilled-moonstone v2 2026-07-13 09:49:45 -05:00
firestarsdog c306fc7cfd BigUI WIP: 4 Jon: His Eyes Are Saved
Pass 1 of collapsing sidebar, adjustor size increases, onroad bar size increase
2026-07-13 01:44:00 -04:00
firestar5683 b49c21e5aa distilled-moonstone v1 2026-07-13 00:14:02 -05:00
firestar5683 44b2df59da Analytical Techniques 2026-07-13 00:03:37 -05:00
firestar5683 c8c3a814ea build 2026-07-12 23:10:36 -05:00
firestar5683 46c3592563 ftm 2026-07-12 23:09:58 -05:00
firestar5683 6534ce356a build 2026-07-12 21:52:05 -05:00
firestar5683 e4dfbd1a60 Warbler 2026-07-12 21:48:37 -05:00
firestar5683 e9a2c925ed mapd wrap 2026-07-12 20:54:47 -05:00
firestar5683 57292f09bf Robocop 2026-07-12 20:10:01 -05:00
firestar5683 e577502f4b VACATION 2026-07-12 17:53:20 -05:00
firestar5683 7b0c0784ed bump 2026-07-12 01:11:18 -05:00
firestar5683 96dcfa4287 milky time 2026-07-12 01:11:13 -05:00
firestar5683 6046a84e95 rare as trees 2026-07-11 21:38:32 -05:00
firestar5683 1884087590 build 2026-07-11 18:21:07 -05:00
Beartech 637c3c820c gm: route SDGM cam-long friction brake to the correct bus
Send EBCMFrictionBrakeCmd (0x315) where the EBCM receives it: SDGM+SASCM cars
off the SASCM's camera-bus (bus2) leg, bare SDGM on the powertrain bus. Whitelist
0x315 on the camera bus in GM_CAM_LONG_TX_MSGS. Requires a panda rebuild.
2026-07-11 18:20:44 -05:00
firestar5683 4f47f8cb0a twuck2 2026-07-11 16:41:29 -05:00
firestar5683 e38668b3cb Steel Creek 2026-07-11 15:42:44 -05:00
firestar5683 9dbf5db9ec FTMv2 2026-07-11 12:19:35 -05:00
firestar5683 e47a836d2d inference 2026-07-11 11:54:46 -05:00
firestar5683 6c0ee59a3e Dom's Plan 2026-07-11 11:54:34 -05:00
firestar5683 a0f4028aaa sped 2026-07-11 11:36:16 -05:00
firestar5683 beb7e5bf6b build 2026-07-11 10:07:27 -05:00
firestar5683 f88ef7ecc2 FTM 2026-07-11 10:07:27 -05:00
firestar5683 6cc8e9b86a AR0231 2026-07-10 23:21:52 -05:00
firestar5683 a999f28270 build 2026-07-10 22:44:34 -05:00
firestar5683 2a3d2744af Update camerad 2026-07-10 22:22:23 -05:00
firestar5683 804316e40c Eden Falls 2026-07-10 17:41:56 -05:00
firestarsdog bdde8907bf BigUI WIP: Mici Mouseface 2026-07-10 05:04:37 -04:00
firestarsdog 85ab7557e9 BigUI WIP: Pad comma panels 2026-07-10 03:42:38 -04:00
firestarsdog 3d2b959db7 BigUI WIP: Grow up 2026-07-10 03:28:57 -04:00
firestarsdog 889cd3db1a BigUI WIP: Alerts Always on Top 2026-07-10 00:01:39 -04:00
firestarsdog 4916143a5d BigUI WIP: Full screen settings exit + bread margins 2026-07-09 23:50:24 -04:00
firestar5683 1153010f85 Hemmed-In Hollow 2026-07-09 20:13:10 -05:00
firestarsdog 0cbd8013b2 BigUI WIP: Hit me baby one more time 2026-07-09 20:21:55 -04:00
firestarsdog a425a9c11a BIgUI WIP: Fix weird ? trunc on nav card 2026-07-09 19:32:17 -04:00
firestar5683 987de36f54 Float The Buffalo 2026-07-09 17:34:56 -05:00
firestarsdog f2fbd3f97c Bucky the Buick 2026-07-09 09:39:34 -04:00
firestarsdog 30245e7d34 Big Pass 2026-07-09 00:41:32 -04:00
firestarsdog 3aa2801ce7 lead indicator mindblender 2026-07-09 00:18:02 -04:00
firestarsdog bc3c245aa5 BigUI WIP: Kiełbasa Polska 2026-07-08 01:15:02 -04:00
whoisdomi cdb266cba2 Weaving Straights 2026-07-07 07:52:10 -05:00
whoisdomi e4e416bfb3 Notchy Highways 2026-07-07 07:50:46 -05:00
firestarsdog 39414ee2e4 BigUI WIP: Pass B 2026-07-07 04:20:07 -04:00
firestar5683 a08fe79cef fixes 2026-07-06 22:32:54 -05:00
Michael Tawata 33508d42ef 19 buick ascm 26 mph minsteerspeed 2026-07-06 16:06:47 -07:00
firestarsdog 110bd8b295 BigUI WIP: Fix boot screen downloads
BigUI WIP: Fix boot screen downloads
2026-07-06 02:47:58 -04:00
firestarsdog 622720f730 BigUI WIP: Vehicle Panel Pass 2026-07-06 02:04:28 -04:00
firestarsdog 4bdf55f6a0 BigUI WIP: We see? 2026-07-06 00:51:45 -04:00
firestarsdog 9e2448b3d5 BigUI WIP: Shrinky Bread that you can see! 2026-07-06 00:08:40 -04:00
firestar5683 2ccd6e35b7 more smol l8ter 2026-07-05 22:51:46 -05:00
firestar5683 71b5cd8594 twuck 2026-07-05 22:48:58 -05:00
firestarsdog 3bcd062eb7 BigUI WIP: Scaling A 2026-07-05 23:08:38 -04:00
firestarsdog 6a4b508864 BigUI WIP: Chilidog 2026-07-05 22:35:16 -04:00
firestar5683 6e48d7dc12 Panic Nik 2026-07-05 21:19:14 -05:00
firestar5683 d849ac120b something tiny is coming 2026-07-05 19:51:47 -05:00
firestar5683 4243a5b055 SMOOOOLLL 2026-07-05 17:31:32 -05:00
firestar5683 8d9e97fdcd hye 2026-07-05 16:03:37 -05:00
firestar5683 1457b902ac Warn of Agnos 2026-07-05 13:17:18 -05:00
firestar5683 8ab1b723ae The Smallest Yard 2026-07-05 11:33:59 -05:00
firestarsdog 2fb5fbf3ad Raylib Rainbow Road Update (All) 2026-07-05 04:39:52 -04:00
firestarsdog cbe6eb077e BigUI WIP: Fast Update Update...Update 2026-07-05 02:50:57 -04:00
firestarsdog 831a8f27e5 BigUI WIP: Oh lol GM riot prevention 2026-07-05 00:20:55 -04:00
firestar5683 46d19999ce debug 2026-07-04 23:20:12 -05:00
firestar5683 e27badccef Sped 2026-07-04 21:05:32 -05:00
firestarsdog 07cf72d573 BigUI WIP: Cull 2026-07-04 19:24:33 -04:00
firestarsdog 8bda3d5be1 BigUI WIP: This seems nice 2026-07-04 18:42:28 -04:00
firestarsdog cdc2b7a3ed BigUI WIP: Mici the Ticis & Tizis 2026-07-04 18:29:56 -04:00
firestarsdog c7bc46ec8e BigUI WIP: Nav card repel from DM icon 2026-07-04 16:09:57 -04:00
firestarsdog dc1b9fc790 BigUI WIP: Collapse Navigation to right side 2026-07-04 15:54:49 -04:00
firestarsdog 275f8966b2 BigUI WIP: Move nav card to bottom for now 2026-07-04 15:25:15 -04:00
firestar5683 5b1445bb5f SLC 2026-07-04 14:19:45 -05:00
firestarsdog 0b1a94914b BigUI WIP: Simple Download Manager Holdover 2026-07-04 15:08:49 -04:00
firestar5683 3317d545be MOAR 2026-07-04 13:47:48 -05:00
firestar5683 a630533b44 build 2026-07-04 13:46:47 -05:00
firestar5683 cb82059d67 GM 2026-07-04 13:46:21 -05:00
firestar5683 e08745d519 Mango Chutney 2026-07-04 10:40:00 -05:00
firestarsdog 7e76fa9b41 BigUI WIP: + SLC 2026-07-04 08:28:10 -04:00
firestar5683 b72570df40 promotion 2026-07-04 00:53:16 -05:00
firestar5683 95ee3a818b nondefault 2026-07-04 00:14:11 -05:00
firestar5683 72992743a4 that's no moon 2026-07-03 23:50:35 -05:00
firestar5683 8eefbacc48 ./build --params/panda/ui/cereal 2026-07-03 20:53:36 -05:00
firestar5683 4531edad1e build 2026-07-03 20:48:32 -05:00
firestar5683 0af6ca7d37 defaults 2026-07-03 20:43:40 -05:00
firestar5683 ac4ffb4a7e visionpulse 2026-07-03 20:24:09 -05:00
firestarsdog 641f1dc238 BigUI WIP: No place like... StarPilot 2026-07-03 20:55:11 -04:00
firestar5683 650cb03b60 NoDashLeak 2026-07-03 19:39:29 -05:00
firestar5683 cefcc41bca Speed 2026-07-03 19:13:53 -05:00
firestar5683 957fe8af75 pedals in galaxy 2026-07-03 13:43:59 -05:00
firestar5683 d98ae2b94b build 2026-07-03 13:38:57 -05:00
firestar5683 12a984b26d Rats In Alani 2026-07-03 13:34:56 -05:00
firestar5683 9cd81e89f6 Update starpilot_boot_logo.jpg 2026-07-03 12:42:50 -05:00
firestar5683 677545cc23 Reapply "The Power Within"
This reverts commit 2f02c93b3b.
2026-07-03 12:28:00 -05:00
firestar5683 2f02c93b3b Revert "The Power Within"
This reverts commit 7f314d9f28.
2026-07-03 12:24:16 -05:00
firestar5683 7f314d9f28 The Power Within 2026-07-03 12:17:10 -05:00
firestarsdog 6ea41a0329 BigUI WIP: CDM? 2026-07-03 00:57:39 -04:00
firestarsdog c33e8094e8 BigUI WIP: Guess we need more bread... 2026-07-02 23:42:48 -04:00
firestarsdog f0b23a40fe BigUI WIP: Conditional Drive Mode 2026-07-02 23:40:11 -04:00
firestarsdog b65158cd97 BigUI WIP: Bozo's Fight Song 2026-07-02 18:40:41 -04:00
firestar5683 b6794d72d3 sorry ya'll 2026-07-02 17:03:29 -05:00
firestar5683 ea2aa532af I AM SCREAMING 2026-07-02 16:38:13 -05:00
firestar5683 5bbe5f1daf RIP BOZO 2026-07-02 15:50:24 -05:00
firestar5683 1afb6b3978 newnew 2026-07-02 15:27:09 -05:00
firestar5683 9e03465520 blankity blankity blank 2026-07-02 14:42:15 -05:00
firestar5683 06e267cef2 Ultraventure 2026-07-02 14:01:21 -05:00
firestarsdog b79527a217 Fingerprint Catalog more chars 2026-07-02 14:13:52 -04:00
firestarsdog fa85c848f6 BigUI WIP: Moar bread 2026-07-02 13:04:38 -04:00
firestarsdog 0e08829188 BigUI WIP: Visual seperation 2026-07-02 12:57:55 -04:00
firestar5683 1363a5b044 fixes 2026-07-02 11:30:44 -05:00
firestar5683 60406cb982 Memory Hither 2026-07-02 10:44:59 -05:00
firestarsdog 152c39044f BigUI WIP: Didn't like these 2026-07-02 11:32:45 -04:00
firestar5683 6c392185be oopsie 2026-07-02 10:10:45 -05:00
firestarsdog c7a5c880cc BigUI WIP: Try this 2026-07-02 11:07:26 -04:00
firestarsdog 0510e87530 BigUI WIP: Standardize Hubtiles 2026-07-02 03:50:52 -04:00
firestarsdog c9734c9b1a BigUI WIP: Vehicle 2026-07-02 03:37:01 -04:00
firestar5683 66e04d4a45 KA THIS CHOW 2026-07-01 22:59:38 -05:00
firestar5683 c1577d2536 build 2026-07-01 15:04:50 -05:00
firestar5683 23b354d47e Auracast 2026-07-01 15:02:27 -05:00
firestarsdog 5c9888a0d7 BigUI WIP: I can see clearly now (MSAA 4× on PC fix) 2026-07-01 15:35:41 -04:00
firestarsdog 74cf048f94 BigUI WIP: Map Panel Cleanup 2026-07-01 15:32:11 -04:00
firestar5683 0eb8d0cfdc kerchew 2026-07-01 13:45:51 -05:00
firestar5683 9ddf5d31b4 kachow 3 2026-07-01 13:41:33 -05:00
firestar5683 7da4359dd0 Kachow 2 2026-07-01 13:27:44 -05:00
firestarsdog 032b2a1fde BigUI WIP: Cleanup 2026-07-01 13:32:46 -04:00
firestar5683 08df283511 kachow 2026-07-01 11:50:57 -05:00
firestar5683 ae3792c683 build 2026-07-01 11:38:22 -05:00
firestar5683 9d2149d495 queue 2026-07-01 11:32:48 -05:00
firestar5683 5234a121f9 Add HKG EV App Start Climate wake
Add a StarPilot vehicle toggle that selects alternate Panda firmware for Hyundai/Kia/Genesis CAN-FD EVs. The firmware keeps the HKG CAN bus active while Panda is in power save and treats the app climate-active frame, bus 1 address 0x384 with byte 3 nonzero, as a Panda boot wake source without publishing it as CAN ignition.

Wire the toggle through StarPilot vehicle settings, Galaxy device settings, parameter definitions, and pandad firmware selection. Disabling the toggle selects the default Panda firmware again.

Tested on a Kia EV9 and 2023 Kia EV6. EV9 remote climate active used 0x384 byte 3 equal to 0x01; EV6 remote climate active used 0x0a. Both stopped/off states observed byte 3 equal to 0x00. Toggle-off negative testing on EV9 saw the remote climate frame on CAN but pandaStates.ignitionCan stayed false. Toggle-on testing verified the active Panda signature matched panda_h7_hkg_remote.bin.signed. Follow-up testing changed the HKG path to wake-only after remote climate caused partial-car fingerprinting and Dashcam Mode; wake-only firmware was built, flashed, and verified on both devices.
2026-07-01 11:32:29 -05:00
firestarsdog 706c65a70d BigUI WIP: Redudant 2026-07-01 12:26:04 -04:00
firestarsdog 04bee88dd2 BigUI WIP: Size bump 2026-07-01 11:38:55 -04:00
firestar5683 072928ec96 tungsten 2026-07-01 10:08:26 -05:00
firestarsdog 03ec0b2c60 BigUI WIP: Too slow 2026-07-01 10:23:11 -04:00
firestar5683 294e301000 widgety gibbit 2026-07-01 08:50:10 -05:00
firestar5683 74e4dd4c44 All Makes 2026-07-01 08:26:02 -05:00
firestarsdog f40881657c BigUI WIP: Meet The Parents 2026-07-01 01:52:11 -04:00
firestarsdog 4bb15dded4 BigUI WIP: fix breadcrumb 2026-07-01 00:27:17 -04:00
firestarsdog 5eb7e8f02b BigUI WIP: Fixed title 2026-07-01 00:10:58 -04:00
firestarsdog ef5ece17eb BigUI WIP: Star Factory 2026-06-30 23:41:23 -04:00
firestar5683 1339ca02c4 build 2026-06-30 22:11:13 -05:00
firestar5683 d5e40d3bdf Oh What A Night! 2026-06-30 22:03:59 -05:00
firestarsdog 97c20744d3 BigUI WIP: K, we'll start simpler 2026-06-30 18:52:31 -04:00
firestar5683 30786472cb i'm 13 and this is 2026-06-30 16:14:18 -05:00
firestar5683 af185f8491 Mariah 2026-06-30 13:14:37 -05:00
firestarsdog 2a5cb66b9a BigUI WIP: Dedup/remove junk 2026-06-30 02:47:18 -04:00
firestarsdog 82eae95add BigUI WIP: Glow Position 2026-06-30 02:17:24 -04:00
firestarsdog 85b33d8819 BigUI WIP: Onroad Bar alignment 2026-06-30 01:03:44 -04:00
firestarsdog e4fc9b6912 BigUI WIP: Meh 2026-06-30 00:22:27 -04:00
firestarsdog a44cde63a5 BigUI WIP: The Bar Borked 2026-06-30 00:19:48 -04:00
Beartech d93039698c Add BUICK_LACROSSE_ASCM_19US variant with 37 mph minSteerSpeed
US 2019 Buick LaCrosse factory LKA operates only 60-180 km/h. Split it
from BUICK_LACROSSE_ASCM into its own variant so minSteerSpeed can be
raised above the EPS LKA-activation threshold (~12.5 m/s), preventing the
"LKAS Fault: Restart the car to engage" on deceleration. Manual
fingerprint selection only; base LaCrosse platforms unchanged.
2026-06-29 22:18:48 -05:00
firestar5683 3a1ca3fb81 ev6 2026-06-29 20:16:31 -05:00
firestar5683 28e51ebfea build 2026-06-29 20:02:59 -05:00
firestar5683 1c2627e1ae Dead Cow Gully 2026-06-29 19:57:24 -05:00
firestarsdog b4dc28a45e BigUP WIP: Ugly lateral 2026-06-29 19:40:36 -04:00
firestar5683 c8a9eb5464 EV9 the final countdown 2026-06-29 12:55:54 -05:00
firestar5683 11be793c3a Revert "ev9"
This reverts commit ce989294ab.
2026-06-29 12:44:56 -05:00
firestar5683 ce989294ab ev9 2026-06-29 12:03:58 -05:00
firestarsdog a21520fa8f BigUI WIP: Breadcrumb Navigation Cleanup 2026-06-29 12:57:53 -04:00
firestarsdog e5751388d4 BigUI WIP: Boop 2026-06-29 12:28:42 -04:00
firestarsdog 535ae098c6 BigUI WIP: One more 2026-06-29 05:53:57 -04:00
firestarsdog 0b26d1c050 BigUI WIP: Start of Lateral design pass 2026-06-29 05:41:23 -04:00
firestarsdog 7d52c80bf4 BigUI WIP: delete this random blue? 2026-06-29 04:10:55 -04:00
firestarsdog 50630f121c BigUI WIP: System Panel Cleanup 2026-06-29 03:53:12 -04:00
firestarsdog f26c89d6cb SLC: No whammy? 2026-06-29 03:10:59 -04:00
firestar5683 e88854319c PlotWranglerPro 2026-06-28 22:49:08 -05:00
firestar5683 2e53b419d1 sonata hybrid 2026-06-28 22:05:55 -05:00
firestar5683 f4bc8ee6b7 ev9 2026-06-28 20:20:47 -05:00
firestar5683 6daefdc0e1 versioning 2026-06-28 20:04:02 -05:00
firestar5683 96e578fa71 pri pri 2 2026-06-28 16:35:14 -05:00
firestar5683 768a2f324c Mici CSC Icon 2026-06-28 16:22:40 -05:00
firestar5683 f50b8dabbb ev9 2026-06-28 15:52:01 -05:00
firestar5683 d9e0345a2b runtime 2026-06-28 15:51:35 -05:00
firestar5683 8a505e6677 ui 2026-06-28 15:31:42 -05:00
firestar5683 40b5468615 More modeld cleanup 2026-06-28 15:03:09 -05:00
firestar5683 f7418649f8 beeg 2026-06-28 13:30:15 -05:00
firestar5683 5d3b0f4ea9 Update model_compiler.py 2026-06-28 13:17:09 -05:00
firestar5683 7e773e5373 Update model_compiler.py 2026-06-28 13:10:30 -05:00
firestar5683 6d088863a6 no pp 2026-06-28 13:05:58 -05:00
firestar5683 6b515db122 build 2026-06-28 12:50:10 -05:00
firestar5683 bdc1532258 App / Version 2026-06-28 12:44:58 -05:00
firestarsdog afe15a9a80 BigUI WIP: Disablement of more vert scroll 2026-06-28 03:54:02 -04:00
firestarsdog 02060a4b67 BigUI WIP: Hide descriptions 2026-06-28 03:46:39 -04:00
firestarsdog efd25eca90 BigUI WIP: Weird squish fix 2026-06-28 03:01:14 -04:00
firestarsdog b4f17f35f9 BigUI WIP: Toggle tile panel standardization PT55 2026-06-28 02:59:08 -04:00
firestar5683 a0669247e3 ANGEL 2026-06-28 00:56:51 -05:00
firestar5683 be811cbe61 ev9 2026-06-28 00:30:14 -05:00
firestar5683 460b61ebe8 Shenanigans 2026-06-28 00:13:13 -05:00
firestar5683 7411a11f83 pytest 2026-06-27 23:58:18 -05:00
firestar5683 a7ad72489b pri pri 2026-06-27 23:45:56 -05:00
firestar5683 e82a4f034b Konik Tooling 2026-06-27 23:38:32 -05:00
firestar5683 a8ec45ec12 --cem 2026-06-27 22:58:38 -05:00
firestar5683 0df2444e95 mici branch switcher 2026-06-27 22:43:12 -05:00
firestarsdog 32cf065988 BigUI WIP: Nix the Caps 2026-06-27 23:36:11 -04:00
firestarsdog 41c680e60c BigUI WIP: Center Breadcrumb Bar text, was triggering 2026-06-27 23:22:54 -04:00
firestar5683 fac7554cb6 I can be ur angle 2026-06-27 22:17:04 -05:00
firestarsdog 92bc8ee934 BigUI WIP: The Starry Night 2026-06-27 23:12:51 -04:00
firestar5683 9c30fc2387 Plexy 2026-06-27 21:55:44 -05:00
firestar5683 3f5dc27170 FERG 2026-06-27 20:47:54 -05:00
firestarsdog a5fb9a2cff BigUI WIP: Prettier Bread 2026-06-27 21:37:10 -04:00
firestarsdog 201d0c0476 BigUI WIP: Standardize Breadcrumbs 2026-06-27 18:31:33 -04:00
firestar5683 54e722ff00 build 2026-06-27 16:02:53 -05:00
firestar5683 fb1c2ea1c4 spicy 2026-06-27 15:57:38 -05:00
firestar5683 0f43f042fb nope 2026-06-27 15:37:25 -05:00
firestar5683 ee1f0bbe83 nah
pan
2026-06-27 15:23:35 -05:00
firestar5683 b811825b38 slither 2026-06-27 14:36:52 -05:00
firestar5683 ee71b70f24 build 2026-06-27 13:58:30 -05:00
firestar5683 b75b21681d Madlad stuff 2026-06-27 13:51:16 -05:00
firestar5683 562c5eb58d long boi 2026-06-27 13:30:18 -05:00
firestar5683 2798c12c90 build 2026-06-27 13:19:46 -05:00
firestar5683 19d545b22e Operation Bigfoot 2026-06-27 13:06:03 -05:00
firestar5683 36e935c9dc nah 2026-06-27 00:18:18 -05:00
firestar5683 cf89678178 driver monitoring 2026-06-26 21:40:10 -05:00
firestar5683 88e4004c9a Stop The Pain 2026-06-26 16:52:28 -05:00
firestar5683 9f0ae1a495 2019 ascm esv 2026-06-26 16:49:02 -05:00
firestar5683 f5a5b9e141 lat control refactor 2026-06-26 16:32:15 -05:00
firestar5683 057ca874ce Dom's Plan 2026-06-26 16:02:47 -05:00
firestar5683 497eb3f771 build 2026-06-26 15:35:10 -05:00
firestar5683 284bb45c0c that's alot 2026-06-26 15:29:52 -05:00
firestar5683 836b0278eb stuff 2026-06-26 12:53:23 -05:00
whoisdomi 4ce9be2752 Bug Fix: Blinker disregards force stop
Prior commit inteneded for blinker to veto force stop from activating during a turn/curves.
If blinker was on, it was vetoing actual force stops. This tweaks is so the veto only works
on actual turns, or if force stop was already on. So force stop will stop, then allow turn without reactivating force stop.
2026-06-26 06:10:13 -05:00
firestarsdog 24fc6fe4e5 BigUI WIP: More Standarization 2026-06-26 04:16:26 -04:00
firestar5683 297ff1973d Don't Brake Check 2026-06-26 02:18:55 -05:00
firestarsdog 727985697f BigUI WIP: Adaptive Nested Panel 2026-06-26 03:14:11 -04:00
firestar5683 59d1581c0a pedal away from the metal 2026-06-26 01:50:06 -05:00
firestarsdog 3a86dffa17 BigUI WIP: Guess this needs a back button 2026-06-26 01:53:15 -04:00
firestarsdog 9f7dcb68f9 BigUI WIP: Add yeast 2026-06-26 01:27:14 -04:00
firestar5683 6793c1a4f5 build 2026-06-25 23:31:56 -05:00
firestar5683 f351dbac09 Lists A Plenty 2026-06-25 23:26:03 -05:00
firestarsdog 44255d67c2 BigUI WIP: Standarize Breadcrumbs 2026-06-25 22:48:37 -04:00
firestarsdog 1ce49be89b BigUI WIP: Standardize single pane toggle tiles 2026-06-25 18:36:17 -04:00
firestar5683 bdf7bca569 Lite Brite 2026-06-25 10:54:58 -05:00
firestar5683 d8e292dc94 build 2026-06-25 00:00:42 -05:00
firestar5683 f0601e27ec The Plum 2026-06-24 23:54:45 -05:00
firestar5683 cec9f090c4 Bolt Pedal Friction Tune 2026-06-24 22:51:42 -05:00
firestar5683 d08da1ff22 volt opd adjust 2026-06-24 22:31:44 -05:00
firestar5683 58b028cfc8 wumbo 2026-06-24 19:37:49 -05:00
firestar5683 d7f3037c88 build 2026-06-24 14:35:53 -05:00
firestar5683 028ee7b3f5 UpstreamStructs 2026-06-24 14:06:16 -05:00
firestar5683 94aff6faed i6 2026-06-24 13:24:28 -05:00
firestar5683 b63039ce84 i6 2026-06-24 12:55:11 -05:00
firestarsdog aec5590d57 BigUI WIP: New Appearance Icon 2026-06-24 03:07:42 -04:00
firestarsdog abd19df5f4 BigUI WIP: Scale up speaker 2026-06-24 02:33:01 -04:00
firestarsdog b1dae43ea8 BigUI WIP: Bell -> Speaker 2026-06-24 02:25:18 -04:00
firestarsdog 8c32f6d99d BigUI WIP: Add some graphics 2026-06-24 02:13:48 -04:00
firestarsdog f6b51e6742 BigUI WIP: More purples 2026-06-24 01:25:34 -04:00
firestarsdog d3881e6a77 BigUI WIP: Sound Panel Polish 2026-06-24 00:47:06 -04:00
firestarsdog d83b065c53 BigUI WIP: Container Store 2026-06-23 23:50:52 -04:00
firestar5683 16c3d5f48a Tom Bombadil 2026-06-23 21:45:36 -05:00
firestar5683 d6dc906ff8 mica 2026-06-23 20:00:29 -05:00
firestar5683 ea0ef444de The Domi Effect 2026-06-23 18:36:47 -05:00
firestar5683 ea1da70b22 lacrosse test 2026-06-23 18:18:14 -05:00
firestar5683 ad9aa72979 slc 2026-06-23 18:16:06 -05:00
firestar5683 1382200cda plexy and noah 2026-06-23 18:00:13 -05:00
firestar5683 7e8832d672 Pacifica mod support 2026-06-23 13:12:32 -05:00
firestar5683 9cd9a6201c build 2026-06-23 12:09:37 -05:00
firestar5683 6ee00d3f60 Support multipart model artifacts 2026-06-23 12:01:45 -05:00
firestar5683 d97100bd14 tiny my BUTT 2026-06-23 12:01:44 -05:00
firestar5683 bb36fe4287 fartlek 2026-06-23 11:28:17 -05:00
firestar5683 066a0f2520 Tibetan Singing Bowl 2026-06-22 23:37:15 -05:00
firestar5683 c5a6e1da9d Blaze It and Praise It 2026-06-22 19:45:11 -05:00
firestar5683 f8cec6f270 Long Blong 2026-06-22 19:35:51 -05:00
firestar5683 544979d652 EV6 GT 2026-06-22 19:27:43 -05:00
firestar5683 353672fb6a Domi's Steer Max Fix 2026-06-22 17:19:21 -05:00
firestar5683 4101ab9b28 Doms Plan 2026-06-22 17:18:48 -05:00
firestar5683 7fb1d854c6 Revert "Dom(i)'s Steer Max"
This reverts commit ad3bad8b85.
2026-06-22 16:55:53 -05:00
firestar5683 d8587bba31 longy long long 2026-06-22 16:55:49 -05:00
firestar5683 ad3bad8b85 Dom(i)'s Steer Max 2026-06-22 16:42:11 -05:00
firestar5683 c91ac533ef is it hi-yun-dae? 2026-06-21 22:19:04 -05:00
firestar5683 32a6af9ac8 Truckothy 2026-06-21 22:10:37 -05:00
firestar5683 e84c0e44ab The Triple Dipper 2026-06-21 21:19:56 -05:00
firestar5683 d28cd4df8b lil rope a dope 2026-06-21 20:48:27 -05:00
firestarsdog cfd762f0a2 BigUI WIP: Bread 2026-06-21 20:47:11 -04:00
firestarsdog 16d6af490a BigUI WIP: Title Standards 2026-06-21 20:39:13 -04:00
firestar5683 0b37a23c75 new stuf 2026-06-21 17:00:32 -05:00
firestarsdog bf8204e9a2 BigUI WIP: Hänsel und Gretel
BigUI WIP: Hänsel und Gretel
2026-06-21 05:57:30 -04:00
firestar5683 34813dee41 cap'n crunch 2026-06-20 23:04:18 -05:00
firestar5683 d84518b97f AYYYEE TOYOTA 2026-06-20 21:26:46 -05:00
firestar5683 2356823b62 build 2026-06-20 20:52:51 -05:00
firestar5683 ce98b31cbf Tyger Tyger Burning Bright 2026-06-20 20:49:05 -05:00
firestar5683 97881b5c66 The fields speak to me 2026-06-20 18:04:39 -05:00
firestarsdog fc90fcb879 BigUI WIP: Gas/Brake Pass 1 2026-06-20 16:47:05 -04:00
firestar5683 762c54bb2f build 2026-06-20 14:41:37 -05:00
firestar5683 9910651a9d Carl The Witch 2026-06-20 14:38:56 -05:00
firestar5683 42a23508b7 RECOVER 2026-06-20 13:08:23 -05:00
firestar5683 fbb6eb651c twuck 2026-06-20 12:33:29 -05:00
firestar5683 e4809535dc build 2026-06-20 01:26:51 -05:00
firestar5683 63490fe5e6 one pedal update 2026-06-20 01:26:51 -05:00
firestar5683 a7b7148c25 2019 hold 2026-06-20 00:05:25 -05:00
firestar5683 e50874baca dash 2026-06-20 00:00:35 -05:00
firestar5683 ef4f174c8d AHHHH 2026-06-19 23:44:41 -05:00
firestar5683 9c726d99de Revert "THE SMOKING GUN"
This reverts commit 25a50a2ec3.
2026-06-19 23:38:09 -05:00
firestar5683 25a50a2ec3 THE SMOKING GUN 2026-06-19 23:31:38 -05:00
firestar5683 5d3d5a884b a;oiwht 2026-06-19 23:05:28 -05:00
firestar5683 7a3c1ede9a The tone is in the tune 2026-06-19 22:39:05 -05:00
firestar5683 d931d3007b build 2026-06-19 21:13:24 -05:00
firestar5683 05c2a90e16 lacrosse 2026-06-19 21:10:41 -05:00
firestar5683 dc581f6300 Revert "Now Mary's In"
This reverts commit 0bc009f800.
2026-06-19 21:01:51 -05:00
firestar5683 a6c5b43caf Revert "maybe"
This reverts commit d8b715799c.
2026-06-19 21:01:18 -05:00
firestar5683 3ec82de5fc Revert "again"
This reverts commit cd08c87a80.
2026-06-19 21:01:18 -05:00
firestar5683 cd08c87a80 again 2026-06-19 17:33:49 -05:00
firestar5683 d8b715799c maybe 2026-06-19 17:20:32 -05:00
firestar5683 0bc009f800 Now Mary's In 2026-06-19 16:53:59 -05:00
firestarsdog ff49f332b0 BigUI WIP: Shes glowing 2026-06-19 17:25:30 -04:00
firestarsdog f04b561f88 BigUI WIP: Russian Nesting Tiles 2026-06-19 17:16:36 -04:00
firestar5683 1f237019e7 Byte You In The 2026-06-19 14:16:01 -05:00
firestar5683 b47c18426d Steam Your Water 2026-06-19 14:14:07 -05:00
firestar5683 0e5d40fb1b Angler Wish 2026-06-19 13:48:30 -05:00
firestarsdog 496d36f805 BigUI WIP: Font_scale fix 2026-06-19 11:12:39 -04:00
firestar5683 9217bf289c build 2026-06-19 00:44:56 -05:00
firestar5683 2c2d8b86ab Drain The Swamp 2026-06-19 00:39:48 -05:00
firestar5683 c8128d2cde Annie Are You Okay 2026-06-18 23:48:23 -05:00
firestar5683 9c85ebc365 The Triple Dipper 2026-06-18 16:55:42 -05:00
firestar5683 8c510f964c Offroad 2026-06-18 14:04:36 -05:00
firestar5683 35e623c392 Dang Imperials 2026-06-18 13:51:58 -05:00
firestar5683 5eb6fe2dd9 Kachow 2026-06-18 13:36:40 -05:00
firestar5683 64bb95e3cf Applebees 2026-06-18 12:20:17 -05:00
firestar5683 96818d8efc The Time Turner 2026-06-18 10:58:09 -05:00
firestar5683 c23434b947 Is This A Date? 2026-06-18 10:54:25 -05:00
firestar5683 2a0053c046 build 2026-06-18 10:27:12 -05:00
firestar5683 f1a256dac0 Don't Kill Suave 2026-06-18 10:19:06 -05:00
firestar5683 967fb96167 build 2026-06-18 10:09:53 -05:00
firestar5683 bb4f3b74d9 There's Pou in this Tine 2026-06-18 10:07:26 -05:00
firestar5683 7dab3a0ceb build 2026-06-17 23:32:03 -05:00
firestar5683 e6b12d612a Agent Cody Banks 2026-06-17 23:29:00 -05:00
firestarsdog d17a4bd6cb BigUI WIP: Appear 2026-06-17 22:05:45 -04:00
firestar5683 540becea52 Mica's cable shipped 2026-06-17 11:49:14 -05:00
firestar5683 a71be7c47b long plan 2026-06-17 11:24:45 -05:00
firestar5683 7305822e67 leedle leedle leedle lee 2026-06-17 11:16:00 -05:00
firestar5683 6b0e5aed24 I did the backstroke in college 2026-06-16 21:19:57 -05:00
firestar5683 6f907e2060 final plan 2026-06-16 18:58:49 -05:00
firestar5683 29b777f12c fremulon 2026-06-16 18:40:22 -05:00
firestar5683 335d6934fb plimothy 2026-06-16 18:31:36 -05:00
firestar5683 60305edeae niro phev 2026-06-16 10:34:30 -05:00
firestarsdog da305d9da3 BigUI WIP: Lil scrolling cleanup 2026-06-16 03:42:22 -04:00
firestarsdog 624f51aef4 BigUI WIP: Pagination out of bounds dragging fix 2026-06-16 03:10:39 -04:00
firestarsdog ba76832979 BigUI WIP: Pagination/Page Turning UX 2026-06-16 02:54:55 -04:00
firestarsdog 49126f29ae BigUI WIP: Apperance -> Aethergrid start 2026-06-16 02:47:30 -04:00
firestar5683 2ed92c88ae build 2026-06-16 00:00:25 -05:00
firestar5683 a9771a0e85 Supernova 2026-06-15 23:58:00 -05:00
firestar5683 1dcac8cfef Tiny Dancer 2 2026-06-15 22:25:18 -05:00
firestarsdog 18ea1dd170 BigUI WIP : AetherMultiSelect Tile + Dialog 2026-06-15 22:28:44 -04:00
firestarsdog 59336096e7 BigUI WIP: Out with the old 2026-06-15 21:27:06 -04:00
firestarsdog 57d7796edb BigUI WIP: Panel standardization 2026-06-15 21:15:52 -04:00
firestar5683 2dc441d621 build 2026-06-15 16:35:06 -05:00
firestar5683 93ec2a94e6 NudgeMe 2026-06-15 16:32:44 -05:00
firestar5683 56564d55c8 REEEEEE 2026-06-15 16:27:29 -05:00
firestar5683 322364c1ca plan stan 2026-06-15 14:47:21 -05:00
firestar5683 838c7fdc9b fixes 2026-06-15 11:30:41 -05:00
firestar5683 e02d67c153 gaslight2 2026-06-15 01:14:20 -05:00
firestar5683 407e2ea90a refreshing 2026-06-15 01:03:31 -05:00
firestar5683 712256eac4 build 2026-06-15 00:47:13 -05:00
firestar5683 1c2aedcc9a gaslight 2026-06-15 00:45:06 -05:00
firestar5683 9af2969540 My Eyes Are Up Here 2026-06-15 00:40:07 -05:00
firestar5683 8a7946b26c pond 2026-06-15 00:17:25 -05:00
firestar5683 53e0aded4c build 2026-06-15 00:06:07 -05:00
firestar5683 3ecd8be1f1 Raindrops on Roses & Whiskers on Kittens 2026-06-15 00:03:44 -05:00
firestar5683 0262b934c4 dumb 2026-06-14 21:18:13 -05:00
firestar5683 9aeaf593c3 son day I'll get this 2026-06-14 21:02:31 -05:00
firestar5683 00c64ba805 Zikeji Tales 2026-06-14 19:26:56 -05:00
firestar5683 7d939e1c76 planner work 2026-06-14 17:10:32 -05:00
firestar5683 8a4d2b558b radar vs voacc 2026-06-14 17:06:52 -05:00
Jason Jackrel 0508653a29 Volt tune from Thinkpad
Squash merge PR #63.
2026-06-14 16:42:36 -05:00
firestar5683 2e75486d16 planner work 2026-06-14 16:38:10 -05:00
firestar5683 7bd73401cd oopsie 2026-06-13 21:48:35 -05:00
firestar5683 4a107868cf build 2026-06-13 20:50:04 -05:00
firestar5683 53766ff6b7 The Forked Man 2026-06-13 20:47:15 -05:00
firestar5683 4ee6758b9f XCeeding expectations 2026-06-13 20:46:30 -05:00
firestar5683 4c1317e30d Mom's Spaghetti 2026-06-13 20:45:52 -05:00
firestar5683 8a68dec71f Swell Tune 2026-06-13 20:43:51 -05:00
firestar5683 c7efa7fa20 Two is not better than one 2026-06-13 20:42:50 -05:00
firestarsdog bd7394ea33 BigUI WIP: Tile updates 2026-06-13 11:56:21 -04:00
firestar5683 fa7fa6afaf Remove Slow Logic atm 2026-06-12 23:09:31 -05:00
firestar5683 491ec0c0b8 the slow mo guys 2026-06-12 10:50:14 -05:00
firestar5683 87f1334ce9 yes 2026-06-12 10:22:03 -05:00
firestar5683 140b047e7f better raylib 2026-06-12 01:34:58 -05:00
firestar5683 21b1bfd600 THIS DESERVES A BUMP 2026-06-11 23:04:34 -05:00
firestar5683 5da99bc977 build 2026-06-11 22:32:31 -05:00
firestar5683 a30dc40b8a wumbology 2026-06-11 22:30:21 -05:00
firestar5683 96893d9c36 Dingle's got dragonclaw?! 2026-06-11 19:36:18 -05:00
firestarsdog 9be0defc29 The speed of cyan 2026-06-11 18:07:30 -04:00
firestar5683 afc8c6fb4b Mr. Freeze 2026-06-11 11:34:12 -05:00
firestar5683 b559b0f827 Fridge Cigarette 2026-06-11 10:51:05 -05:00
firestar5683 79e31ff241 TRY AGAIN, SPIDERMAN 2026-06-10 22:30:48 -05:00
firestar5683 1e604fd80e loopback 2026-06-10 19:11:02 -05:00
firestar5683 56989a164e update 2026-06-10 19:01:20 -05:00
firestar5683 eaf5d44037 forte he 2026-06-10 15:54:35 -05:00
firestarsdog 93cb562321 BigUI WIP: Larger for the Dads 2026-06-10 15:49:25 -04:00
firestar5683 31d3df673a Kia Xceed 2026-06-10 11:55:20 -05:00
firestar5683 c0aa7a7460 oops 2026-06-10 10:54:28 -05:00
firestar5683 498b7f14a9 build 2026-06-10 10:50:40 -05:00
firestar5683 95b6ecec86 Hello Poppet 2026-06-10 10:48:32 -05:00
firestarsdog 8e6fc9bed3 BigUI WIP: Page flipping is tough... 2026-06-10 02:20:14 -04:00
firestarsdog cf7822180b BigUI WIP: Aethergrid Update 2026-06-10 00:07:00 -04:00
firestar5683 f93c536f7e deeprl3 2026-06-09 22:58:07 -05:00
firestarsdog d555d1f588 BigUP WIP: Style 2026-06-09 23:50:01 -04:00
firestar5683 88e297b183 offsets 2026-06-09 22:21:48 -05:00
firestar5683 37f5c8ca58 Navigation: Breaking Dawn 2026-06-09 21:19:08 -05:00
firestarsdog 7d53bd5af7 BigUI WIP: LED 2026-06-09 21:30:52 -04:00
firestar5683 455a8ec11d XT4 Sigmoid 2026-06-09 17:02:27 -05:00
firestar5683 dac0610a19 build 2026-06-09 16:47:33 -05:00
firestar5683 da5b3f829f black pink in your area2 2026-06-09 16:47:28 -05:00
firestarsdog 02960fc8c2 SLC allow override to persist if within obtained speed 2026-06-09 17:26:25 -04:00
firestar5683 4585938753 build 2026-06-09 15:42:23 -05:00
firestar5683 f777e71c56 Black / Pink in your area 2026-06-09 15:39:56 -05:00
firestar5683 0a698d202b build 2026-06-09 14:13:36 -05:00
firestar5683 63db39082b Mount Wannahockaloogie 2026-06-09 14:11:13 -05:00
firestarsdog 4f48c2ec27 BigUI WIP: Lateral Spateral 2026-06-09 13:13:55 -04:00
firestarsdog 6faf9bd88a BigUI WIP: Weird scrolling 2026-06-09 01:52:23 -04:00
firestarsdog a624cf89bb BigUI WIP: Remove pagenation from system settings 2026-06-09 01:41:27 -04:00
firestarsdog e836ed09c8 BigUI WIP: Optional Pagenation 2026-06-09 01:37:08 -04:00
firestarsdog 8cc9c59330 BigUI WIP: Some panel standarization 2026-06-09 00:00:33 -04:00
firestar5683 a11d064aeb buttan 2026-06-08 22:38:57 -05:00
firestar5683 77c82edb44 sonata hybrid 2026-06-08 20:55:23 -05:00
firestar5683 9a9ac3af77 navstate 2026-06-08 20:30:44 -05:00
firestar5683 ac8511b211 bsm 2026-06-08 20:10:33 -05:00
firestar5683 c3ff2e86db build 2026-06-08 19:57:20 -05:00
firestar5683 98d6a4eac0 chill takeoff 2026-06-08 19:57:20 -05:00
firestar5683 5f413b967e odyss 2026-06-08 19:57:20 -05:00
firestar5683 bdb0da4fa0 suburu sng 2026-06-08 19:57:20 -05:00
firestarsdog cabacda0ad BigUI WIP: mono 2026-06-08 20:48:20 -04:00
firestar5683 d8c79a9712 build 2026-06-08 19:19:19 -05:00
firestar5683 88c259a666 Prioritize Smooth Follow 2026-06-08 19:15:50 -05:00
firestarsdog 796d081fce BigUI WIP: More prints of the finger stuff 2026-06-08 19:16:53 -04:00
firestar5683 6d61feff11 rlonlylat 2026-06-07 21:59:32 -05:00
firestar5683 5d0ab0cfb0 First time in San Juan mi hijo 2026-06-07 21:51:36 -05:00
firestar5683 6c687a9f5c build 2026-06-07 21:16:08 -05:00
firestar5683 7a17c21fab Truck V2 2026-06-07 21:13:52 -05:00
firestar5683 1994a0bd16 build 2026-06-07 20:53:37 -05:00
firestar5683 8a1bfb925c odyss 2026-06-07 20:51:27 -05:00
firestar5683 914411fe06 build 2026-06-07 20:43:19 -05:00
firestar5683 c88bebbc3e Zik is my boss 2026-06-07 20:37:47 -05:00
firestar5683 aba65bf755 build 2026-06-07 20:18:52 -05:00
firestar5683 cf1c99cf78 Haberdashery 2026-06-07 20:16:35 -05:00
firestar5683 9c51a6024e build 2026-06-07 15:21:10 -05:00
firestar5683 bec246c6ef Clear Nav 2026-06-07 15:18:56 -05:00
firestar5683 eeab4b8c85 Build 2026-06-07 15:04:23 -05:00
firestar5683 a6758eb9e6 New stoofs 2026-06-07 15:01:59 -05:00
firestar5683 cdcc8da555 sm 2026-06-07 14:26:14 -05:00
firestar5683 1f9aa83440 buttons 2026-06-07 14:26:14 -05:00
firestar5683 15d218677e build 2026-06-07 14:26:14 -05:00
firestar5683 f8934840da Conditional Chill 2026-06-07 14:26:14 -05:00
firestarsdog a888c5602a BigUI WIP: Prob don't need 2026-06-07 05:04:30 -04:00
firestar5683 4134da76ca volt probz 2026-06-06 20:49:28 -05:00
firestar5683 24b0f37ae7 hda1 2026-06-06 19:31:04 -05:00
firestar5683 559a738fe7 build 2026-06-06 17:52:05 -05:00
firestar5683 f20b1f2a4f mochi mochi mochi 2026-06-06 17:49:41 -05:00
firestar5683 c76b770807 plan takeoff 2026-06-06 14:47:33 -05:00
firestarsdog 3c0dea5747 Raylib UI : Global fingerprint_catalog 2026-06-06 14:53:43 -04:00
firestar5683 e47fbd1cb9 build 2026-06-06 12:08:50 -05:00
firestar5683 efdc48980d defaults 2026-06-06 12:03:35 -05:00
firestarsdog f84f7d4f17 Replay Controls 2026-06-05 23:29:34 -04:00
firestar5683 aa82ae0bd0 build 2026-06-05 22:10:00 -05:00
Michael Tawata 743ff67927 no fun allowed here 2026-06-05 22:06:48 -05:00
firestarsdog 2880189713 BigUI WIP: Pushpop Wires 2026-06-05 18:31:50 -04:00
firestarsdog 2847d0fd8a BigUI WIP: oplong push_widget 2026-06-05 18:20:19 -04:00
firestarsdog c23aeb9fcd BigUI WIP: Remove puke 2026-06-05 15:02:52 -04:00
firestarsdog 980c85cfb1 BigUI WIP: Vehicle Panel state update 2026-06-05 14:58:28 -04:00
1558 changed files with 516924 additions and 227923 deletions
@@ -165,6 +165,10 @@ jobs:
"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_can_ignition_only.bin.signed"
"panda/board/obj/panda_h7_can_ignition_only.bin.signed"
"panda/board/obj/panda_remote_can_ignition_only.bin.signed"
"panda/board/obj/panda_h7_remote_can_ignition_only.bin.signed"
"panda/board/obj/panda_jungle_h7.bin.signed"
"panda/board/obj/body_h7.bin.signed"
)
+2
View File
@@ -21,6 +21,7 @@ compiledmodels/
/docs_site/
*.mp4
!docs/assets/speed-limit-vision-demo*.mp4
*.dylib
*.DSYM
*.d
@@ -68,6 +69,7 @@ cppcheck_report.txt
comma*.sh
selfdrive/modeld/models/*.pkl
!selfdrive/modeld/models/driving_tinygrad.pkl
!selfdrive/modeld/models/driving_vision_tinygrad.pkl
!selfdrive/modeld/models/driving_policy_tinygrad.pkl
!selfdrive/modeld/models/driving_vision_metadata.pkl
+107 -1
View File
@@ -3,4 +3,110 @@
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
exec "${ROOT_DIR}/scripts/laptop_device_build.sh" build "$@"
original_args=("$@")
shortcut_targets=()
passthrough_args=()
shortcut_includes_panda=0
panda_generated_files=(
"panda/board/obj/gitversion.h"
"panda/board/obj/version"
)
add_panda_targets() {
local variants=(
panda
panda_h7
panda_remote
panda_h7_remote
panda_hkg_remote
panda_h7_hkg_remote
panda_can_ignition_only
panda_h7_can_ignition_only
panda_remote_can_ignition_only
panda_h7_remote_can_ignition_only
panda_hkg_remote_can_ignition_only
panda_h7_hkg_remote_can_ignition_only
panda_jungle_h7
body_h7
)
local variant
for variant in "${variants[@]}"; do
shortcut_targets+=("panda/board/obj/${variant}.bin.signed")
done
}
while [[ $# -gt 0 ]]; do
case "$1" in
--params|-params|-param)
shortcut_targets+=("common/params_pyx.so")
;;
--panda|-panda)
shortcut_includes_panda=1
add_panda_targets
;;
--ui|-ui)
shortcut_targets+=("selfdrive/ui/ui")
;;
--cereal|-cereal)
shortcut_targets+=(
"cereal/libcereal.a"
"cereal/libsocketmaster.a"
"cereal/messaging/bridge"
"cereal/services.h"
)
;;
*)
if [[ "$1" =~ ^[0-9]+$ ]]; then
passthrough_args+=("-j$1")
else
passthrough_args+=("$1")
fi
;;
esac
shift
done
if [[ "${#shortcut_targets[@]}" -gt 0 ]]; then
scons_args=("--no-scrub")
scons_args+=("${shortcut_targets[@]}")
if [[ "${#passthrough_args[@]}" -gt 0 ]]; then
scons_args+=("${passthrough_args[@]}")
fi
if [[ "${shortcut_includes_panda}" -eq 1 ]]; then
exec "${ROOT_DIR}/scripts/laptop_device_build.sh" scons "${scons_args[@]}"
fi
tmpdir="$(mktemp -d)"
for path in "${panda_generated_files[@]}"; do
if [[ -e "${ROOT_DIR}/${path}" ]]; then
cp -p "${ROOT_DIR}/${path}" "${tmpdir}/${path//\//__}"
else
touch "${tmpdir}/${path//\//__}.missing"
fi
done
if "${ROOT_DIR}/scripts/laptop_device_build.sh" scons "${scons_args[@]}"; then
status=0
else
status=$?
fi
for path in "${panda_generated_files[@]}"; do
if [[ -e "${tmpdir}/${path//\//__}.missing" ]]; then
rm -f "${ROOT_DIR}/${path}"
else
cp -p "${tmpdir}/${path//\//__}" "${ROOT_DIR}/${path}"
fi
done
rm -rf "${tmpdir}"
exit "${status}"
fi
if [[ "${#original_args[@]}" -gt 0 ]]; then
exec "${ROOT_DIR}/scripts/laptop_device_build.sh" build "${original_args[@]}"
fi
exec "${ROOT_DIR}/scripts/laptop_device_build.sh" build
+22 -2
View File
@@ -91,6 +91,13 @@ struct StarPilotCarState @0xf35cc4560bbf6ec2 {
cancelPressed @20 :Bool;
cancelLongPressed @21 :Bool;
cancelVeryLongPressed @22 :Bool;
pedalMaxRegen @23 :Bool; # pedal at max regen, driver should use brake for more decel
pedalLongActive @24 :Bool; # Pre-AP pedal longitudinal mode is active (enableLongControl)
teslaCCEngaged @25 :Bool; # rising edge of stock Tesla CC engaging (no-pedal mode)
teslaCCDisengaged @26 :Bool; # falling edge of stock Tesla CC
teslaCCNotArmed @27 :Bool; # lateral engaged but DI_cruiseState != STANDBY/ENABLED
accelHardCruise @28 :Bool; # current/releasing accel cruise button came from GM hard-press signal
decelHardCruise @29 :Bool; # current/releasing decel cruise button came from GM hard-press signal
}
struct StarPilotDeviceState @0xda96579883444c35 {
@@ -108,7 +115,11 @@ struct StarPilotModelDataV2 @0x80ae746ee2596b11 {
}
}
struct StarPilotOnroadEvent @0xa5cd762cd951a455 {
struct StarPilotOnroadEvents @0xa5cd762cd951a455 {
events @0 :List(StarPilotOnroadEvent);
}
struct StarPilotOnroadEvent @0xe344718567f9ce71 {
name @0 :EventName;
enable @1 :Bool;
@@ -158,6 +169,14 @@ struct StarPilotOnroadEvent @0xa5cd762cd951a455 {
switchbackModeInactive @30;
lkasEnable @31;
lkasDisable @32;
lateralManeuver @33;
pedalCruiseEnabled @34;
pedalCruiseDisabled @35;
pedalMaxRegen @36;
teslaCCEngaged @37;
teslaCCDisengaged @38;
teslaCCNotArmed @39;
pedalNotCalibrated @40;
}
}
@@ -259,7 +278,8 @@ struct CustomReserved9 @0xa1680744031fdb2d {
wallTimeNanos @5 :UInt64;
}
struct CustomReserved10 @0xcb9fd56c7057593a {
struct StarPilotLateralManeuverPlanDEPRECATED @0xcb9fd56c7057593a {
desiredCurvature @0 :Float32; # 1/m
}
struct CustomReserved11 @0xc2243c65e0340384 {
Binary file not shown.
+119 -26
View File
@@ -68,12 +68,12 @@ struct OnroadEvent @0xc4fa6047f024e718 {
longitudinalManeuver @30;
steerTempUnavailableSilent @31;
resumeRequired @32;
preDriverDistracted @33;
promptDriverDistracted @34;
driverDistracted @35;
preDriverUnresponsive @36;
promptDriverUnresponsive @37;
driverUnresponsive @38;
driverDistracted1 @33;
driverDistracted2 @34;
driverDistracted3 @35;
driverUnresponsive1 @36;
driverUnresponsive2 @37;
driverUnresponsive3 @38;
belowSteerSpeed @39;
lowBattery @40;
accFaulted @41;
@@ -130,14 +130,6 @@ struct OnroadEvent @0xc4fa6047f024e718 {
userBookmark @95;
excessiveActuation @96;
audioFeedback @97;
lateralManeuver @98;
pedalCruiseEnabled @99;
pedalCruiseDisabled @100;
pedalMaxRegen @101;
teslaCCEngaged @102;
teslaCCDisengaged @103;
teslaCCNotArmed @104;
pedalNotCalibrated @105;
soundsUnavailableDEPRECATED @47;
}
@@ -833,13 +825,30 @@ struct SelfdriveState {
alertStatus @5 :AlertStatus;
alertSize @6 :AlertSize;
alertType @7 :Text;
alertSound @8 :Car.CarControl.HUDControl.AudibleAlert;
alertSound @13 :AudibleAlert;
alertHudVisual @12 :Car.CarControl.HUDControl.VisualAlert;
# configurable driving settings
experimentalMode @10 :Bool;
personality @11 :LongitudinalPersonality;
enum AudibleAlert {
none @0;
engage @1;
disengage @2;
refuse @3;
warningSoft @4;
warningImmediate @5;
prompt @6;
promptRepeat @7;
promptDistracted @8;
preAlert @9;
}
enum OpenpilotState @0xdbe58b96d2d1ac61 {
disabled @0;
preEnabled @1;
@@ -860,6 +869,10 @@ struct SelfdriveState {
mid @2;
full @3;
}
deprecated :group {
alertSound @8 :Car.CarControl.HUDControl.AudibleAlert;
}
}
struct ControlsState @0x97ff69c53601abf1 {
@@ -1095,7 +1108,7 @@ struct ModelDataV2 {
confidence @23: ConfidenceClass;
# Model perceived motion
temporalPose @21 :Pose;
temporalPoseDEPRECATED @21 :Pose;
# e2e lateral planner
action @26: Action;
@@ -2221,7 +2234,7 @@ struct DriverStateDEPRECATED @0xb83c6cc593ed0a00 {
stdDEPRECATED @2 :Float32;
}
struct DriverMonitoringState @0xb83cda094a1da284 {
struct DriverMonitoringStateDEPRECATED @0xb83cda094a1da284 {
events @18 :List(OnroadEvent);
faceDetected @1 :Bool;
isDistracted @2 :Bool;
@@ -2239,12 +2252,91 @@ struct DriverMonitoringState @0xb83cda094a1da284 {
isActiveMode @16 :Bool;
isRHD @4 :Bool;
uncertainCount @19 :UInt32;
phoneProbOffset @20 :Float32;
phoneProbValidCount @21 :UInt32;
isPreviewDEPRECATED @15 :Bool;
rhdCheckedDEPRECATED @5 :Bool;
eventsDEPRECATED @0 :List(Car.OnroadEventDEPRECATED);
deprecated :group {
phoneProbOffset @20 :Float32;
phoneProbValidCount @21 :UInt32;
isPreview @15 :Bool;
rhdChecked @5 :Bool;
events @0 :List(Car.OnroadEventDEPRECATED);
}
}
struct DriverMonitoringState {
lockout @0 :Bool;
lockoutCount @15 :Int8;
lockoutMinutesRemaining @11 :Int8;
alert3Count @12 :Int8;
noResponseCount @13 :Int8;
noResponseForceDecel @14 :Bool;
alwaysOn @3 :Bool;
alwaysOnLockout @4 :Bool;
alertLevel @5 :AlertLevel;
activePolicy @6 :MonitoringPolicy;
isRHD @7 :Bool;
rhdCalibration @8 :CalibrationState;
visionPolicyState @9 :VisionPolicyState;
wheeltouchPolicyState @10 :WheeltouchPolicyState;
enum AlertLevel {
# ordinal must match the name to prevent bugs
# comparing against the raw ordinal value
none @0;
one @1;
two @2;
three @3;
}
enum MonitoringPolicy {
wheeltouch @0;
vision @1;
}
struct VisionPolicyState {
awarenessPercent @0 :Int8;
awarenessStep @1 :Float32;
isDistracted @2 :Bool;
distractedTypes @3 :DistractedTypes;
faceDetected @4 :Bool;
pose @5 :Pose;
wheeltouchFallbackPercent @6 :Int8;
uncertainOffroadAlertPercent @7 :Int8;
struct DistractedTypes {
pose @0: Bool;
eye @1: Bool;
phone @2: Bool;
}
struct Pose {
pitch @0 :Float32;
yaw @1 :Float32;
pitchCalib @2 :CalibrationState;
yawCalib @3 :CalibrationState;
calibrated @4 :Bool;
uncertainty @5 :Float32;
}
}
struct WheeltouchPolicyState {
awarenessPercent @0 :Int8;
awarenessStep @1 :Float32;
driverInteracting @2 :Bool;
}
struct CalibrationState {
calibratedPercent @0 :Int8;
offset @1 :Float32;
}
deprecated :group {
alertCountLockoutPercent @1 :Int8;
alertTimeLockoutPercent @2 :Int8;
}
}
struct Boot {
@@ -2565,7 +2657,7 @@ struct Event {
thumbnail @66: Thumbnail;
onroadEvents @134: List(OnroadEvent);
carParams @69: Car.CarParams;
driverMonitoringState @71: DriverMonitoringState;
driverMonitoringState @151 :DriverMonitoringState;
livePose @129 :LivePose;
modelV2 @75 :ModelDataV2;
drivingModelData @128 :DrivingModelData;
@@ -2614,8 +2706,8 @@ struct Event {
userBookmark @93 :UserBookmark;
bookmarkButton @148 :UserBookmark;
audioFeedback @149 :AudioFeedback;
lateralManeuverPlan @150 :LateralManeuverPlan;
lateralManeuverPlan @150 :LateralManeuverPlan;
# *********** debug ***********
testJoystick @52 :Joystick;
roadEncodeData @86 :EncodeData;
@@ -2644,12 +2736,12 @@ struct Event {
starpilotCarState @109 :Custom.StarPilotCarState;
starpilotDeviceState @110 :Custom.StarPilotDeviceState;
starpilotModelV2 @111 :Custom.StarPilotModelDataV2;
starpilotOnroadEvents @112 :List(Custom.StarPilotOnroadEvent);
starpilotOnroadEvents @112 :Custom.StarPilotOnroadEvents;
starpilotPlan @113 :Custom.StarPilotPlan;
starpilotRadarState @114 :Custom.StarPilotRadarState;
starpilotSelfdriveState @115 :Custom.StarPilotSelfdriveState;
customReserved9 @116 :Custom.CustomReserved9;
customReserved10 @136 :Custom.CustomReserved10;
starpilotLateralManeuverPlanDEPRECATED @136 :Custom.StarPilotLateralManeuverPlanDEPRECATED;
customReserved11 @137 :Custom.CustomReserved11;
customReserved12 @138 :Custom.CustomReserved12;
customReserved13 @139 :Custom.CustomReserved13;
@@ -2707,5 +2799,6 @@ struct Event {
gyroscope2DEPRECATED @100 :SensorEventData;
accelerometer2DEPRECATED @101 :SensorEventData;
temperatureSensor2DEPRECATED @123 :SensorEventData;
driverMonitoringStateDEPRECATED @71 :DriverMonitoringStateDEPRECATED;
}
}
+61 -1
View File
@@ -1,5 +1,8 @@
#!/usr/bin/env python3
import io
import math
import os
import sys
from pathlib import Path
CHUNK_SIZE = 45 * 1024 * 1024 # 45MB, under GitHub's 50MB limit
@@ -13,9 +16,16 @@ def get_manifest_path(name):
return f"{name}.chunkmanifest"
def _chunk_paths(path, num_chunks):
return [get_manifest_path(path)] + [get_chunk_name(path, i, num_chunks) for i in range(num_chunks)]
def get_chunk_paths(path, file_size):
num_chunks = math.ceil(file_size / CHUNK_SIZE)
return [get_manifest_path(path)] + [get_chunk_name(path, i, num_chunks) for i in range(num_chunks)]
return _chunk_paths(path, num_chunks)
get_chunk_targets = get_chunk_paths
def chunk_file(path, targets):
@@ -31,6 +41,51 @@ def chunk_file(path, targets):
os.remove(path)
def get_existing_chunks(path):
if os.path.isfile(path):
return [path]
manifest_path = get_manifest_path(path)
if os.path.isfile(manifest_path):
num_chunks = int(Path(manifest_path).read_text().strip())
return _chunk_paths(path, num_chunks)
raise FileNotFoundError(path)
def file_chunked_exists(path) -> bool:
return os.path.isfile(path) or os.path.isfile(get_manifest_path(path))
class ChunkStream(io.RawIOBase):
def __init__(self, paths):
self._paths = iter(paths)
self._buffer = memoryview(b"")
def readable(self):
return True
def readinto(self, buffer):
count = 0
while count < len(buffer):
if not self._buffer:
path = next(self._paths, None)
if path is None:
break
self._buffer = memoryview(Path(path).read_bytes())
continue
take = min(len(buffer) - count, len(self._buffer))
buffer[count:count + take] = self._buffer[:take]
self._buffer = self._buffer[take:]
count += take
return count
def open_file_chunked(path):
chunks = get_existing_chunks(path)
if chunks and chunks[0] == get_manifest_path(path):
chunks = chunks[1:]
return io.BufferedReader(ChunkStream(chunks))
def read_file_chunked(path):
manifest_path = get_manifest_path(path)
if os.path.isfile(manifest_path):
@@ -39,3 +94,8 @@ def read_file_chunked(path):
if os.path.isfile(path):
return Path(path).read_bytes()
raise FileNotFoundError(path)
if __name__ == "__main__":
file_path = sys.argv[1]
chunk_file(file_path, get_chunk_targets(file_path, os.path.getsize(file_path)))
Binary file not shown.
+5 -1
View File
@@ -99,7 +99,7 @@ Params::Params(const std::string &path, bool memory) {
if (memory) {
params_folder = Path::shm_path() + "/params";
} else {
cache_path = "/cache/params" + params_prefix + "/";
cache_path = Path::params_cache() + params_prefix + "/";
params_folder = path;
}
params_path = ensure_params_path(params_prefix, params_folder);
@@ -309,6 +309,10 @@ int Params::getTuningLevel(const std::string &key) {
return keys[key].tuning_level;
}
ParamSettingsTier Params::getSettingsTier(const std::string &key) {
return keys[key].settings_tier;
}
std::optional<std::string> Params::getStockValue(const std::string &key) {
ParamKeyAttributes &attributes = keys[key];
if (attributes.stock_value) {
+10
View File
@@ -33,6 +33,11 @@ enum ParamKeyType {
BYTES = 6
};
enum ParamSettingsTier {
SETTINGS_SIMPLE = 0,
SETTINGS_ADVANCED = 1,
};
struct ParamKeyAttributes {
uint32_t flags;
ParamKeyType type;
@@ -42,6 +47,9 @@ struct ParamKeyAttributes {
std::optional<std::string> stock_value = std::nullopt;
int tuning_level = 0;
// Controls settings-page visibility only. It does not gate the param's runtime behavior.
ParamSettingsTier settings_tier = SETTINGS_ADVANCED;
};
class Params {
@@ -112,6 +120,8 @@ public:
int getTuningLevel(const std::string &key);
ParamSettingsTier getSettingsTier(const std::string &key);
std::optional<std::string> getStockValue(const std::string &key);
private:
+30
View File
@@ -4,6 +4,27 @@ from enum import IntEnum, IntFlag
from pathlib import Path
import tempfile
SETTINGS_SIMPLE = 0
SETTINGS_ADVANCED = 1
def _load_settings_tiers() -> dict[str, int]:
params_keys = Path(__file__).with_name("params_keys.h")
if not params_keys.exists():
return {}
tiers = {}
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:
tiers[parts[1]] = SETTINGS_SIMPLE if "SETTINGS_SIMPLE" in line else SETTINGS_ADVANCED
return tiers
_SETTINGS_TIERS = _load_settings_tiers()
try:
from openpilot.common.params_pyx import Params as _Params, ParamKeyFlag, ParamKeyType, UnknownKeyName
except Exception:
@@ -180,6 +201,9 @@ except Exception:
def get_tuning_level(self, key):
return 0
def get_settings_tier(self, key):
return _SETTINGS_TIERS.get(self.check_key(key), SETTINGS_ADVANCED)
else:
assert _Params
assert ParamKeyFlag
@@ -187,6 +211,12 @@ else:
assert UnknownKeyName
class Params(_Params):
def get_settings_tier(self, key):
try:
return super().get_settings_tier(key)
except AttributeError:
return _SETTINGS_TIERS.get(self.check_key(key), SETTINGS_ADVANCED)
def get(self, key, block=False, return_default=False, encoding=None, default=None):
try:
value = super().get(key, block=block, return_default=return_default)
+253 -203
View File
@@ -8,7 +8,7 @@
inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"AccessToken", {CLEAR_ON_MANAGER_START | DONT_LOG, STRING}},
{"AdbEnabled", {PERSISTENT, BOOL}},
{"AlwaysAllowUploads", {PERSISTENT, BOOL, "0"}},
{"AlwaysAllowUploads", {PERSISTENT, BOOL, "0", std::nullopt, 0, SETTINGS_SIMPLE}},
{"AlwaysOnDM", {PERSISTENT, BOOL}},
{"ApiCache_Device", {PERSISTENT, STRING}},
{"AssistNowToken", {PERSISTENT, STRING}},
@@ -36,13 +36,17 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"DoReboot", {CLEAR_ON_MANAGER_START, BOOL}},
{"DoShutdown", {CLEAR_ON_MANAGER_START, BOOL}},
{"DoUninstall", {CLEAR_ON_MANAGER_START, BOOL}},
{"DoUserReboot", {CLEAR_ON_MANAGER_START, BOOL}},
{"DriverTooDistracted", {CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON, BOOL}},
{"DriverLockoutCount", {CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON, INT, "0"}},
{"EcuDisableFailed", {CLEAR_ON_MANAGER_START, BOOL}},
{"AlphaLongitudinalEnabled", {PERSISTENT, BOOL}},
{"ExperimentalLongitudinalEnabled", {PERSISTENT, BOOL}},
{"ExperimentalMode", {PERSISTENT, BOOL}},
{"ExperimentalModeConfirmed", {PERSISTENT, BOOL}},
{"PersistExperimentalState", {PERSISTENT, BOOL, "0", "0", 1}},
{"PersistChillState", {PERSISTENT, BOOL, "0", "0", 1}},
{"PersistExperimentalState", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
{"PersistedCCStatus", {PERSISTENT, INT, "0", "0"}},
{"PersistedCEStatus", {PERSISTENT, INT, "0", "0"}},
{"FirmwareQueryDone", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
{"ForcePowerDown", {PERSISTENT, BOOL}},
@@ -54,11 +58,13 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"GithubUsername", {PERSISTENT, STRING}},
{"GitRemote", {PERSISTENT, STRING}},
{"GsmApn", {PERSISTENT, STRING}},
{"GsmMetered", {PERSISTENT, BOOL, "1"}},
{"GsmMetered", {PERSISTENT, BOOL, "1", std::nullopt, 0, SETTINGS_SIMPLE}},
{"GsmRoaming", {PERSISTENT, BOOL}},
{"HardwareSerial", {PERSISTENT, STRING}},
{"HasAcceptedTerms", {PERSISTENT, STRING, "0"}},
{"HondaGasFactorParams", {PERSISTENT, FLOAT}},
{"HondaLateralPidKiScale", {PERSISTENT, FLOAT, "1.0", "1.0", 3}},
{"HondaLateralPidKpScale", {PERSISTENT, FLOAT, "1.0", "1.0", 3}},
{"HondaWindFactorParams", {PERSISTENT, FLOAT}},
{"InstallDate", {PERSISTENT, TIME}},
{"IsDriverViewEnabled", {CLEAR_ON_MANAGER_START, BOOL}},
@@ -67,7 +73,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"IsMetric", {PERSISTENT, BOOL}},
{"IsOffroad", {CLEAR_ON_MANAGER_START, BOOL}},
{"IsOnroad", {PERSISTENT, BOOL}},
{"IsRHD", {PERSISTENT, BOOL}},
{"IsRHD", {PERSISTENT, BOOL, std::nullopt, std::nullopt, 0, SETTINGS_SIMPLE}},
{"IsRhdDetected", {PERSISTENT, BOOL}},
{"IsRHDOverride", {PERSISTENT, BOOL}},
{"IsReleaseBranch", {CLEAR_ON_MANAGER_START, BOOL}},
@@ -114,6 +120,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"PandaSomResetTriggered", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
{"PandaSignatures", {CLEAR_ON_MANAGER_START, BYTES}},
{"PrimeType", {PERSISTENT, INT}},
{"PriusClusterOffsetMigrated", {PERSISTENT, BOOL, "0", "0"}},
{"RecordAudio", {PERSISTENT, BOOL}},
{"RecordAudioFeedback", {PERSISTENT, BOOL, "0"}},
{"RecordFront", {PERSISTENT, BOOL}},
@@ -121,6 +128,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"SecOCKey", {PERSISTENT | DONT_LOG, STRING}},
{"ShowDebugInfo", {PERSISTENT, BOOL}},
{"ShowAllToggles", {PERSISTENT, BOOL, "0", "0", 3}},
{"TryRaylibUI", {PERSISTENT, BOOL, "1"}},
{"UseOldUI", {PERSISTENT, BOOL, "0", std::nullopt, 0, SETTINGS_SIMPLE}},
{"UsePrebuilt", {PERSISTENT, BOOL, "1"}},
{"RouteCount", {PERSISTENT, INT, "0"}},
{"SnoozeUpdate", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
@@ -141,15 +150,18 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"UpdaterLastFetchTime", {PERSISTENT, TIME}},
{"UptimeOffroad", {PERSISTENT, FLOAT, "0.0"}},
{"UptimeOnroad", {PERSISTENT, FLOAT, "0.0"}},
{"UsbGpuActive", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
{"UsbGpuCompiled", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
{"UsbGpuPresent", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
{"Version", {PERSISTENT, STRING}},
// StarPilot variables
{"AccelerationPath", {PERSISTENT, BOOL, "1", "0", 2}},
{"AccelerationProfile", {PERSISTENT, INT, "0", "0", 0}},
{"AccelerationPath", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
{"AccelerationProfile", {PERSISTENT, INT, "0", "0", 0, SETTINGS_SIMPLE}},
{"AdjacentLeadsUI", {PERSISTENT, BOOL, "1", "0", 3}},
{"AdjacentPath", {PERSISTENT, BOOL, "0", "0", 3}},
{"AdjacentPath", {PERSISTENT, BOOL, "0", "0", 3, SETTINGS_SIMPLE}},
{"AdjacentPathMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
{"AdvancedCustomUI", {PERSISTENT, BOOL, "0", "0", 2}},
{"AdvancedCustomUI", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"AdvancedLateralTune", {PERSISTENT, BOOL, "1", "0", 2}},
{"AdvancedLongitudinalTune", {PERSISTENT, BOOL, "1", "0", 3}},
{"AggressiveFollow", {PERSISTENT, FLOAT, "1.25", "1.25", 2}},
@@ -159,8 +171,9 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"AggressiveJerkDeceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"AggressiveJerkSpeed", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"AggressiveJerkSpeedDecrease", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"AlertVolumeControl", {PERSISTENT, BOOL, "0", "0", 2}},
{"AlwaysOnLateral", {PERSISTENT, BOOL, "1", "0", 0}},
{"AlertVolumeControl", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"AllowImpossibleAcceleration", {PERSISTENT, BOOL, "0", "0", 3}},
{"AlwaysOnLateral", {PERSISTENT, BOOL, "1", "0", 0, SETTINGS_SIMPLE}},
{"AlwaysOnLateralLKAS", {PERSISTENT, BOOL, "1", "0", 2}},
{"ApiCache_DriveStats", {PERSISTENT, JSON, "{}", "{}"}},
{"AutomaticallyDownloadModels", {PERSISTENT, BOOL, "1", "0", 1}},
@@ -168,46 +181,55 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"AvailableModelNames", {PERSISTENT, STRING, "", "", 1}},
{"AvailableModelSeries", {PERSISTENT, STRING, "", "", 1}},
{"AvailableModels", {PERSISTENT, STRING, "", "", 1}},
{"AvailableModelArtifactFormats", {PERSISTENT, STRING, "", "", 1}},
{"BlacklistedModels", {PERSISTENT, STRING, "", "", 2}},
{"BootLogo", {PERSISTENT, STRING, "starpilot", "stock", 0}},
{"BuildMetadata", {PERSISTENT, STRING, "", "", 0}},
{"BlindSpotMetrics", {PERSISTENT, BOOL, "1", "0", 3}},
{"BlindSpotPath", {PERSISTENT, BOOL, "1", "0", 1}},
{"BelowSteerSpeedVolume", {PERSISTENT, INT, "101", "101", 2}},
{"BlindSpotPath", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
{"BelowSteerSpeedVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
{"BorderMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
{"BorderWidth", {PERSISTENT, FLOAT, "100.0", "100.0", 2}},
{"BorderWidth", {PERSISTENT, FLOAT, "100.0", "100.0", 2, SETTINGS_SIMPLE}},
{"CalibratedLateralAcceleration", {PERSISTENT, FLOAT, "2.0", "2.0", 2}},
{"CalibrationProgress", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"CameraView", {PERSISTENT, INT, "3", "0", 2}},
{"CameraOffset", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"CameraView", {PERSISTENT, INT, "3", "0", 2, SETTINGS_SIMPLE}},
{"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}},
{"CarModel", {PERSISTENT, STRING, "MOCK", "MOCK", 0}},
{"CarMake", {PERSISTENT, STRING, "mock", "mock", 0, SETTINGS_SIMPLE}},
{"CarModel", {PERSISTENT, STRING, "MOCK", "MOCK", 0, SETTINGS_SIMPLE}},
{"CarModelName", {PERSISTENT, STRING, "", "", 0}},
{"CECurves", {PERSISTENT, BOOL, "0", "0", 1}},
{"CECurves", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
{"CECurvesLead", {PERSISTENT, BOOL, "0", "0", 1}},
{"CELead", {PERSISTENT, BOOL, "1", "0", 1}},
{"CEModelStopTime", {PERSISTENT, FLOAT, "7.0", "0.0", 2}},
{"CELead", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
{"CEModelStopTime", {PERSISTENT, FLOAT, "7.0", "0.0", 2, SETTINGS_SIMPLE}},
{"CESignalLaneDetection", {PERSISTENT, BOOL, "1", "0", 2}},
{"CESignalSpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
{"CESlowerLead", {PERSISTENT, BOOL, "1", "0", 1}},
{"CESpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
{"CESpeedLead", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
{"CESignalSpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
{"CESlowerLead", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
{"CESpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 1, SETTINGS_SIMPLE}},
{"CESpeedLead", {PERSISTENT, FLOAT, "0.0", "0.0", 1, SETTINGS_SIMPLE}},
{"CCMLead", {PERSISTENT, BOOL, "1", "0", 1}},
{"CCMLaunchAssist", {PERSISTENT, BOOL, "0", "0", 1}},
{"CCMSetSpeedMargin", {PERSISTENT, FLOAT, "3.0", "0.0", 1}},
{"CCMSpeed", {PERSISTENT, FLOAT, "45.0", "0.0", 1}},
{"CCMSpeedLead", {PERSISTENT, FLOAT, "35.0", "0.0", 1}},
{"CCStatus", {CLEAR_ON_OFFROAD_TRANSITION, INT, "0", "0"}},
{"CEStatus", {CLEAR_ON_OFFROAD_TRANSITION, INT, "0", "0"}},
{"CEStopLights", {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}},
{"CEStopLights", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
{"CEStoppedLead", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
{"ClusterOffset", {PERSISTENT, FLOAT, "1.0", "1.0", 2, SETTINGS_SIMPLE}},
{"ColorScheme", {PERSISTENT, STRING, "stock", "stock", 0}},
{"ColorToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"BootLogoToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"Compass", {PERSISTENT, BOOL, "0", "0", 1}},
{"Compass", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
{"CommunityFavorites", {PERSISTENT, STRING, "", "", 1}},
{"ConditionalExperimental", {PERSISTENT, BOOL, "1", "0", 1}},
{"ConditionalChill", {PERSISTENT, BOOL, "0", "0", 1}},
{"ConditionalExperimental", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
{"CurvatureData", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
{"CurveSpeedController", {PERSISTENT, BOOL, "1", "0", 1}},
{"CustomAlerts", {PERSISTENT, BOOL, "0", "0", 0}},
{"CurveSpeedController", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
{"CustomAlerts", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"CustomAccelProfile", {PERSISTENT, BOOL, "0", "0", 3}},
{"CustomAccelProfileInitialized", {PERSISTENT, BOOL, "0", "0", 3}},
{"CustomAccelProfile0MPH", {PERSISTENT, FLOAT, "3.0", "3.0", 3}},
@@ -217,20 +239,20 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"CustomAccelProfile45MPH", {PERSISTENT, FLOAT, "1.0", "1.0", 3}},
{"CustomAccelProfile56MPH", {PERSISTENT, FLOAT, "0.8", "0.8", 3}},
{"CustomAccelProfile89MPH", {PERSISTENT, FLOAT, "0.6", "0.6", 3}},
{"CustomCruise", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
{"CustomCruiseLong", {PERSISTENT, FLOAT, "5.0", "5.0", 2}},
{"CustomCruise", {PERSISTENT, FLOAT, "1.0", "1.0", 2, SETTINGS_SIMPLE}},
{"CustomCruiseLong", {PERSISTENT, FLOAT, "5.0", "5.0", 2, SETTINGS_SIMPLE}},
{"CustomPersonalities", {PERSISTENT, BOOL, "0", "0", 2}},
{"CancelButtonControl", {PERSISTENT, INT, "1", "0", 2}},
{"CancelButtonControl", {PERSISTENT, INT, "1", "0", 2, SETTINGS_SIMPLE}},
{"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}},
{"RelaxedPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
{"CustomThemes", {PERSISTENT, BOOL, "1", "0", 0}},
{"CustomUI", {PERSISTENT, BOOL, "1", "0", 1}},
{"CustomThemes", {PERSISTENT, BOOL, "0", "0", 0}},
{"CustomUI", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
{"DebugMode", {CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0", "0"}},
{"DecelerationProfile", {PERSISTENT, INT, "1", "0", 2}},
{"DecelerationProfile", {PERSISTENT, INT, "1", "0", 2, SETTINGS_SIMPLE}},
{"DeveloperMetrics", {PERSISTENT, BOOL, "1", "0", 3}},
{"DeveloperSidebar", {PERSISTENT, BOOL, "1", "0", 3}},
{"DeveloperSidebarMetric1", {PERSISTENT, INT, "1", "0", 3}},
@@ -241,14 +263,15 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"DeveloperSidebarMetric6", {PERSISTENT, INT, "6", "0", 3}},
{"DeveloperSidebarMetric7", {PERSISTENT, INT, "7", "0", 3}},
{"DeveloperUI", {PERSISTENT, BOOL, "0", "0", 3}},
{"GalaxyDeveloperMode", {PERSISTENT | DONT_LOG, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"DeveloperWidgets", {PERSISTENT, BOOL, "1", "0", 3}},
{"DeviceManagement", {PERSISTENT, BOOL, "1", "0", 1}},
{"DeviceShutdown", {PERSISTENT, INT, "9", "33", 1}},
{"DisableOnroadUploads", {PERSISTENT, BOOL, "0", "0", 2}},
{"DisableOpenpilotLongitudinal", {PERSISTENT, BOOL, "0", "0", 0}},
{"DeviceManagement", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
{"DeviceShutdown", {PERSISTENT, INT, "9", "33", 1, SETTINGS_SIMPLE}},
{"DisableOnroadUploads", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"DisableOpenpilotLongitudinal", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"DiscordUsername", {PERSISTENT, STRING, "", "", 0}},
{"DisengageVolume", {PERSISTENT, INT, "101", "101", 2}},
{"DistanceButtonControl", {PERSISTENT, INT, "1", "0", 2}},
{"DisengageVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
{"DistanceButtonControl", {PERSISTENT, INT, "1", "0", 2, SETTINGS_SIMPLE}},
{"DistanceIconPack", {PERSISTENT, STRING, "stock", "stock", 0}},
{"DistanceIconToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"DownloadableBootLogos", {PERSISTENT, STRING, "", ""}},
@@ -260,130 +283,141 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"DownloadableWheels", {PERSISTENT, STRING, "", ""}},
{"DownloadAllModels", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"DownloadMaps", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"DriverCamera", {PERSISTENT, BOOL, "0", "0", 1}},
{"DriverCamera", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
{"Model", {PERSISTENT, STRING, "sc2", "sc2", 1}},
{"ModelVersion", {PERSISTENT, STRING, "v11", "v11", 1}},
{"DrivingModel", {PERSISTENT, STRING, "sc2", "sc2", 1}},
{"DrivingModelName", {PERSISTENT, STRING, "South Carolina", "South Carolina", 1}},
{"DrivingModelVersion", {PERSISTENT, STRING, "v11", "v11", 1}},
{"DynamicPathWidth", {PERSISTENT, BOOL, "0", "0", 2}},
{"DynamicPedalsOnUI", {PERSISTENT, BOOL, "1", "0", 1}},
{"EngageVolume", {PERSISTENT, INT, "101", "101", 2}},
{"DynamicPathWidth", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"DynamicPedalsOnUI", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
{"EngageVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
{"EVTuning", {PERSISTENT, BOOL, "0", "0", 3}},
{"Fahrenheit", {PERSISTENT, BOOL, "0", "0", 3}},
{"FlashPanda", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"GMDashSpoofOffsets", {PERSISTENT, BOOL, "0", "0", 2}},
{"GMPedalLongitudinal", {PERSISTENT, BOOL, "1", "1", 2}},
{"LongPitch", {PERSISTENT, BOOL, "1", "0", 2}},
{"RemoteStartBootsComma", {PERSISTENT, BOOL, "0", "0"}},
{"RemapCancelToDistance", {PERSISTENT, BOOL, "0", "0"}},
{"NAPAdaptiveAccel", {PERSISTENT, BOOL, "1", "1"}},
{"GMDashSpoofOffsets", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"GMPedalLongitudinal", {PERSISTENT, BOOL, "1", "1", 2, SETTINGS_SIMPLE}},
{"HKGRemoteStartBootsComma", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"IgnoreIgnitionLine", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"LongPitch", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
{"RemoteStartBootsComma", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"RemapCancelToDistance", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"NAPAdaptiveAccel", {PERSISTENT, BOOL, "1", "1", 0, SETTINGS_SIMPLE}},
{"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"}},
{"NAPPedalEnabled", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"NAPPedalCanBus", {PERSISTENT, INT, "2", "2", 0, SETTINGS_SIMPLE}},
{"NAPPedalCalibDone", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"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"}},
{"NAPPedalCalibFactor", {PERSISTENT, FLOAT, "1.0", "1.0", 0, SETTINGS_SIMPLE}},
{"NAPPedalCalibZero", {PERSISTENT, FLOAT, "0.0", "0.0", 0, SETTINGS_SIMPLE}},
{"NAPPedalProfile", {PERSISTENT, INT, "4", "4"}},
{"NAPRadarBehindNosecone", {PERSISTENT, BOOL, "0", "0"}},
{"NAPRadarEnabled", {PERSISTENT, BOOL, "0", "0"}},
{"NAPRadarOffset", {PERSISTENT, FLOAT, "0.0", "0.0"}},
{"NAPRadarBehindNosecone", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"NAPRadarEnabled", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"NAPRadarOffset", {PERSISTENT, FLOAT, "0.0", "0.0", 0, SETTINGS_SIMPLE}},
{"ForceAutoTune", {PERSISTENT, BOOL, "0", "0", 3}},
{"ForceAutoTuneOff", {PERSISTENT, BOOL, "1", "0", 2}},
{"ForceFingerprint", {PERSISTENT, BOOL, "0", "0", 2}},
{"ForceFingerprint", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"ForceOffroad", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"ForceOnroad", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"ForceStops", {PERSISTENT, BOOL, "1", "0", 2}},
{"ForceStopDistanceOffset", {PERSISTENT, INT, "0", "0", 2}},
{"ForceStandstill", {PERSISTENT, BOOL, "0", "0", 2}},
{"ForceStops", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
{"ForceStopDistanceOffset", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
{"ForceStandstill", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"ForceTorqueController", {PERSISTENT, BOOL, "0", "0", 3}},
{"FPSCounter", {PERSISTENT, BOOL, "1", "0", 3}},
{"FLMActiveOverrides", {PERSISTENT, JSON, "{}", "{}", 2}},
{"FLMActiveProfileId", {PERSISTENT, STRING, "", "", 2}},
{"FLMTrialBaseline", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
{"FLMTrialApplied", {PERSISTENT, BOOL, "0", "0", 2}},
{"FPSCounter", {PERSISTENT, BOOL, "0", "0", 3}},
{"GalaxyDashboardStats", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
{"StarPilotApiToken", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"StarPilotCarParams", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BYTES, "", ""}},
{"StarPilotCarParamsPersistent", {PERSISTENT, BYTES, "", ""}},
{"StarPilotDongleId", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"StarPilotFavoriteSlots", {PERSISTENT, JSON, "[]", "[]", 1}},
{"StarPilotStats", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
{"StarPilotTogglesUpdated", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"FrogsGoMoosTweak", {PERSISTENT, BOOL, "1", "0", 2}},
{"GoatScream", {PERSISTENT, BOOL, "0", "0", 1}},
{"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}},
{"GoatScream", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
{"GoatScreamCriticalAlerts", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
{"GreenLightAlert", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"HideAlerts", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"HideChangingLanesBanner", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"HideDistanceProfileBanner", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"HideTurningBanner", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"HideDMIcon", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"HideLeadMarker", {PERSISTENT, BOOL, "0", "0", 2}},
{"HideMaxSpeed", {PERSISTENT, BOOL, "0", "0", 2}},
{"HideSpeed", {PERSISTENT, BOOL, "0", "0", 2}},
{"HideSpeedLimit", {PERSISTENT, BOOL, "0", "0", 2}},
{"HigherBitrate", {PERSISTENT, BOOL, "0", "0", 2}},
{"HolidayThemes", {PERSISTENT, BOOL, "1", "0", 0}},
{"HumanAcceleration", {PERSISTENT, BOOL, "0", "0", 2}},
{"CoastUpToLeads", {PERSISTENT, BOOL, "1", "1", 2}},
{"HumanLaneChanges", {PERSISTENT, BOOL, "0", "0", 2}},
{"IconPack", {PERSISTENT, STRING, "frog-animated", "stock", 0}},
{"HideMaxSpeed", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"HideSpeed", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"HideSpeedLimit", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"HideSteeringWheel", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"HigherBitrate", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"HolidayThemes", {PERSISTENT, BOOL, "1", "0", 0, SETTINGS_SIMPLE}},
{"HumanLaneChanges", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"IconPack", {PERSISTENT, STRING, "stock", "stock", 0}},
{"IconToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"IncreasedStoppedDistance", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
{"IncreasedStoppedDistanceLowVisibility", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
{"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}},
{"IncreaseFollowingSnow", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
{"IncreaseThermalLimits", {PERSISTENT, BOOL, "0", "0", 2}},
{"IncreasedStoppedDistance", {PERSISTENT, FLOAT, "0.0", "0.0", 1, SETTINGS_SIMPLE}},
{"IncreasedStoppedDistanceLowVisibility", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
{"IncreasedStoppedDistanceRain", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
{"IncreasedStoppedDistanceRainStorm", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
{"IncreasedStoppedDistanceSnow", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
{"RedneckCruise", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
{"IncreaseFollowingLowVisibility", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
{"IncreaseFollowingRain", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
{"IncreaseFollowingRainStorm", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
{"IncreaseFollowingSnow", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
{"IncreaseThermalLimits", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"IssueReported", {CLEAR_ON_MANAGER_START, JSON, "{}", "{}"}},
{"KonikDongleId", {PERSISTENT, STRING, "", "", 0}},
{"KonikMinutes", {PERSISTENT, INT, "0", "0", 0}},
{"LaneChanges", {PERSISTENT, BOOL, "1", "1", 0}},
{"LaneChangeSmoothing", {PERSISTENT, INT, "10", "10", 1}},
{"LaneChangeTime", {PERSISTENT, FLOAT, "1.0", "0.0", 1}},
{"LaneDetectionWidth", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
{"LaneLinesColor", {PERSISTENT, STRING, "", "", 2}},
{"LaneLinesWidth", {PERSISTENT, FLOAT, "4.0", "2.0", 2}},
{"LaneChanges", {PERSISTENT, BOOL, "1", "1", 0, SETTINGS_SIMPLE}},
{"LaneChangeSmoothing", {PERSISTENT, INT, "5", "10", 1, SETTINGS_SIMPLE}},
{"LaneChangeTime", {PERSISTENT, FLOAT, "1.0", "0.0", 1, SETTINGS_SIMPLE}},
{"LaneDetectionWidth", {PERSISTENT, FLOAT, "0.0", "0.0", 1, SETTINGS_SIMPLE}},
{"LaneLinesColor", {PERSISTENT, STRING, "", "", 2, SETTINGS_SIMPLE}},
{"LaneLinesWidth", {PERSISTENT, FLOAT, "4.0", "2.0", 2, SETTINGS_SIMPLE}},
{"LastMapsUpdate", {PERSISTENT, STRING, "", ""}},
{"LateralTune", {PERSISTENT, BOOL, "1", "0", 1}},
{"LeadDepartingAlert", {PERSISTENT, BOOL, "0", "0", 0}},
{"LeadDepartingAlert", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"LeadDetectionThreshold", {PERSISTENT, INT, "35", "50", 3}},
{"LeadIndicator", {PERSISTENT, BOOL, "1", "1", 2}},
{"LeadIndicator", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"LeadInfo", {PERSISTENT, BOOL, "1", "0", 3}},
{"LKASButtonControl", {PERSISTENT, INT, "5", "0", 2}},
{"LKASButtonControl", {PERSISTENT, INT, "5", "0", 2, SETTINGS_SIMPLE}},
{"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}},
{"LongCancelButtonControl", {PERSISTENT, INT, "5", "0", 2, SETTINGS_SIMPLE}},
{"LongDistanceButtonControl", {PERSISTENT, INT, "5", "0", 2, SETTINGS_SIMPLE}},
{"LongModeButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
{"LongStarButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
{"LongitudinalActuatorDelay", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"LongitudinalActuatorDelayStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"LateralManeuverStatus", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, JSON, "{}", "{}"}},
{"LongitudinalManeuverPaddleMode", {PERSISTENT, STRING, "auto", "auto"}},
{"LongitudinalManeuverStatus", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, JSON, "{}", "{}"}},
{"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}},
{"LongitudinalTune", {PERSISTENT, BOOL, "1", "0", 0, SETTINGS_SIMPLE}},
{"LoudBlindspotAlert", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"LoudBlindspotAlertWhenDisengaged", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"LowVoltageShutdown", {PERSISTENT, FLOAT, "11.8", "11.8", 3, SETTINGS_SIMPLE}},
{"MainCruiseButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
{"ManualUpdateInitiated", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"AMapKey1", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"AMapKey2", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"ApiCache_NavDestinations", {PERSISTENT, JSON, "[]", "[]"}},
{"FavoriteDestinations", {PERSISTENT, JSON, "[]", "[]"}},
{"MapAcceleration", {PERSISTENT, BOOL, "0", "0", 1}},
{"MapboxPublicKey", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"MapBoxRequests", {PERSISTENT, JSON, "{}", "{}"}},
{"MapboxSecretKey", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"MapDeceleration", {PERSISTENT, BOOL, "0", "0", 1}},
{"MapdSettings", {PERSISTENT, JSON, "{}", "{}"}},
{"MapGears", {PERSISTENT, BOOL, "0", "0", 2}},
{"MapGears", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"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}},
{"ClearNavOnOffroad", {PERSISTENT, BOOL, "1", "1", 2, SETTINGS_SIMPLE}},
{"ClearNavOnOffroadTimeoutMinutes", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
{"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, "{}", "{}"}},
@@ -395,27 +429,31 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"VisionSpeedLimitLastEvent", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"VisionSpeedLimitStatus", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"VisionSpeedLimitStream", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"VisionSpeedLimitSupportCount", {CLEAR_ON_MANAGER_START, INT, "0", "0"}},
{"VisionSpeedLimitSupportSpeed", {CLEAR_ON_MANAGER_START, FLOAT, "0.0", "0.0"}},
{"MaxDesiredAcceleration", {PERSISTENT, FLOAT, "4.0", "2.0", 2}},
{"MinimumBackupSize", {PERSISTENT, INT, "0", "0"}},
{"MinimumLaneChangeSpeed", {PERSISTENT, FLOAT, "20.0", "20.0", 2}},
{"ModeButtonControl", {PERSISTENT, INT, "0", "0", 2}},
{"MinimumLaneChangeSpeed", {PERSISTENT, FLOAT, "20.0", "20.0", 2, SETTINGS_SIMPLE}},
{"ModeButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
{"ModelDownloadProgress", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"ModelDrivesAndScores", {PERSISTENT, JSON, "{}", "{}"}},
{"ModelReleasedDates", {PERSISTENT, STRING, "", "", 1}},
{"ModelRandomizer", {PERSISTENT, BOOL, "0", "0", 2}},
{"ModelSortMode", {PERSISTENT, STRING, "alphabetical", "alphabetical", 1}},
{"ModelToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"ModelUI", {PERSISTENT, BOOL, "1", "0", 2}},
{"ModelUI", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
{"ModelVersions", {PERSISTENT, STRING, "", "", 1}},
{"NavigationUI", {PERSISTENT, BOOL, "1", "0", 1}},
{"ModelManifestVersion", {PERSISTENT, STRING, "", "", 1}},
{"NavigationUI", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
{"NNFF", {PERSISTENT, BOOL, "0", "0", 2}},
{"NNFFLite", {PERSISTENT, BOOL, "0", "0", 2}},
{"NostalgiaMode", {PERSISTENT, BOOL, "0", "0", 2}},
{"NostalgiaMode", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"NNFFModelName", {CLEAR_ON_MANAGER_START, STRING, "", "", 0}},
{"NoLogging", {PERSISTENT, BOOL, "0", "0", 2}},
{"NoUploads", {PERSISTENT, BOOL, "0", "0", 2}},
{"NudgelessLaneChange", {PERSISTENT, BOOL, "0", "0", 0}},
{"NumericalTemp", {PERSISTENT, BOOL, "1", "0", 3}},
{"NoLogging", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"NoUploads", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"NudgelessLaneChange", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"NudgelessLaneChangeOnlyWhenEngaged", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
{"NumericalTemp", {PERSISTENT, BOOL, "0", "0", 3}},
{"Offset1", {PERSISTENT, FLOAT, "5.0", "0.0", 0}},
{"Offset2", {PERSISTENT, FLOAT, "5.0", "0.0", 0}},
{"Offset3", {PERSISTENT, FLOAT, "5.0", "0.0", 0}},
@@ -423,45 +461,47 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"Offset5", {PERSISTENT, FLOAT, "10.0", "0.0", 0}},
{"Offset6", {PERSISTENT, FLOAT, "10.0", "0.0", 0}},
{"Offset7", {PERSISTENT, FLOAT, "10.0", "0.0", 0}},
{"OneLaneChange", {PERSISTENT, BOOL, "1", "0", 2}},
{"OnroadDistanceButton", {PERSISTENT, BOOL, "0", "0", 0}},
{"OneLaneChange", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
{"OnroadDistanceButton", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"OnroadDistanceButtonPressed", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"FavoriteVirtualAccelCruiseCounter", {CLEAR_ON_MANAGER_START, INT, "0", "0"}},
{"FavoriteVirtualDecelCruiseCounter", {CLEAR_ON_MANAGER_START, INT, "0", "0"}},
{"WheelButtonBookmarkCounter", {CLEAR_ON_MANAGER_START, INT, "0", "0"}},
{"openpilotMinutes", {PERSISTENT, INT, "0", "0", 0}},
{"OverpassRequests", {PERSISTENT, JSON, "{}", "{}"}},
{"PathColor", {PERSISTENT, STRING, "", "", 2}},
{"PathEdgesColor", {PERSISTENT, STRING, "", "", 2}},
{"PathEdgeWidth", {PERSISTENT, FLOAT, "20.0", "0.0", 2}},
{"PathWidth", {PERSISTENT, FLOAT, "6.1", "5.9", 2}},
{"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}},
{"PathColor", {PERSISTENT, STRING, "", "", 2, SETTINGS_SIMPLE}},
{"PathEdgesColor", {PERSISTENT, STRING, "", "", 2, SETTINGS_SIMPLE}},
{"PathEdgeWidth", {PERSISTENT, FLOAT, "20.0", "0.0", 2, SETTINGS_SIMPLE}},
{"PathWidth", {PERSISTENT, FLOAT, "6.1", "5.9", 2, SETTINGS_SIMPLE}},
{"PauseAOLOnBrake", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
{"PauseLateralOnSignal", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
{"PauseLateralSpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 1, SETTINGS_SIMPLE}},
{"LateralResumeDelay", {PERSISTENT, FLOAT, "0.0", "0.0", 1, SETTINGS_SIMPLE}},
{"PedalsOnUI", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
{"GalaxyPaired", {PERSISTENT, BOOL, "0", "0", 0}},
{"GalaxyUploadPending", {PERSISTENT, BOOL, "0", "0", 0}},
{"PreferredSchedule", {PERSISTENT, INT, "2", "0", 0}},
{"PreviousSpeedLimit", {PERSISTENT, FLOAT, "0.0", "0.0"}},
{"PromptDistractedVolume", {PERSISTENT, INT, "101", "101", 2}},
{"PromptVolume", {PERSISTENT, INT, "101", "101", 2}},
{"QOLLateral", {PERSISTENT, BOOL, "1", "0", 1}},
{"QOLLongitudinal", {PERSISTENT, BOOL, "1", "0", 1}},
{"QOLVisuals", {PERSISTENT, BOOL, "1", "0", 0}},
{"RadarTakeoffs", {PERSISTENT, BOOL, "1", "0", 2}},
{"PromptDistractedVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
{"PromptVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
{"QOLLateral", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
{"QOLLongitudinal", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
{"QOLVisuals", {PERSISTENT, BOOL, "1", "0", 0, SETTINGS_SIMPLE}},
{"RadarTakeoffs", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"RadarTracksUI", {PERSISTENT, BOOL, "0", "0", 3}},
{"RainbowPath", {PERSISTENT, BOOL, "0", "0", 1}},
{"RainbowPath", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
{"RandomEvents", {PERSISTENT, BOOL, "0", "0", 1}},
{"RandomThemes", {PERSISTENT, BOOL, "0", "0", 1}},
{"RandomThemesHolidays", {PERSISTENT, BOOL, "0", "0", 1}},
{"ReduceAccelerationLowVisibility", {PERSISTENT, INT, "0", "0", 2}},
{"ReduceAccelerationRain", {PERSISTENT, INT, "0", "0", 2}},
{"ReduceAccelerationRainStorm", {PERSISTENT, INT, "0", "0", 2}},
{"ReduceAccelerationSnow", {PERSISTENT, INT, "0", "0", 2}},
{"ReduceLateralAccelerationLowVisibility", {PERSISTENT, INT, "0", "0", 2}},
{"ReduceLateralAccelerationRain", {PERSISTENT, INT, "0", "0", 2}},
{"ReduceLateralAccelerationRainStorm", {PERSISTENT, INT, "0", "0", 2}},
{"ReduceLateralAccelerationSnow", {PERSISTENT, INT, "0", "0", 2}},
{"RefuseVolume", {PERSISTENT, INT, "101", "101", 2}},
{"ReduceAccelerationLowVisibility", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
{"ReduceAccelerationRain", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
{"ReduceAccelerationRainStorm", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
{"ReduceAccelerationSnow", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
{"ReduceLateralAccelerationLowVisibility", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
{"ReduceLateralAccelerationRain", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
{"ReduceLateralAccelerationRainStorm", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
{"ReduceLateralAccelerationSnow", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
{"RefuseVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
{"RelaxedFollow", {PERSISTENT, FLOAT, "1.6", "1.6", 2}},
{"RelaxedFollowHigh", {PERSISTENT, FLOAT, "1.4", "1.4", 2}},
{"RelaxedJerkAcceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
@@ -471,41 +511,42 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"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}},
{"RoadNameUI", {PERSISTENT, BOOL, "1", "0", 1}},
{"RotatingWheel", {PERSISTENT, BOOL, "1", "0", 1}},
{"ScreenBrightness", {PERSISTENT, INT, "101", "101", 2}},
{"ScreenBrightnessOnroad", {PERSISTENT, INT, "101", "101", 2}},
{"ScreenManagement", {PERSISTENT, BOOL, "1", "0", 1}},
{"ScreenRecorder", {PERSISTENT, BOOL, "1", "0", 2}},
{"ScreenTimeout", {PERSISTENT, INT, "30", "30", 2}},
{"ScreenTimeoutOnroad", {PERSISTENT, INT, "30", "10", 2}},
{"RoadEdgesWidth", {PERSISTENT, FLOAT, "2.0", "2.0", 2, SETTINGS_SIMPLE}},
{"RoadNameUI", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
{"RotatingWheel", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
{"ScreenBrightness", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
{"ScreenBrightnessOnroad", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
{"ScreenManagement", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
{"ScreenRecorder", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
{"ScreenTimeout", {PERSISTENT, INT, "30", "30", 2, SETTINGS_SIMPLE}},
{"ScreenTimeoutOnroad", {PERSISTENT, INT, "30", "10", 2, SETTINGS_SIMPLE}},
{"SecOCKeys", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"SafeMode", {PERSISTENT, BOOL, "0", "0", 0}},
{"SafeModeBackup", {PERSISTENT, JSON, "{}", "{}"}},
{"SetSpeedLimit", {PERSISTENT, BOOL, "0", "0", 1}},
{"SetSpeedOffset", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
{"ShowCEMStatus", {PERSISTENT, BOOL, "1", "0", 2}},
{"ShowCPU", {PERSISTENT, BOOL, "1", "0", 3}},
{"ShowCSCStatus", {PERSISTENT, BOOL, "1", "0", 2}},
{"SetSpeedOffset", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
{"ShowCEMStatus", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
{"ShowCCMStatus", {PERSISTENT, BOOL, "0", "0", 2}},
{"ShowCPU", {PERSISTENT, BOOL, "0", "0", 3}},
{"ShowCSCStatus", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
{"ShowGPU", {PERSISTENT, BOOL, "0", "0", 3}},
{"ShowIP", {PERSISTENT, BOOL, "0", "0", 3}},
{"ShowMemoryUsage", {PERSISTENT, BOOL, "1", "0", 3}},
{"ShowModeStatusBanner", {PERSISTENT, BOOL, "1", "0", 2}},
{"ShowMemoryUsage", {PERSISTENT, BOOL, "0", "0", 3}},
{"ShowModeStatusBanner", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
{"ShownToggleDescriptions", {PERSISTENT, JSON, "{}", "{}"}},
{"ShowSLCOffset", {PERSISTENT, BOOL, "1", "0", 0}},
{"ShowSpeedLimits", {PERSISTENT, BOOL, "1", "0", 1}},
{"ShowSpeedLimits", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
{"ShowSteering", {PERSISTENT, BOOL, "0", "0", 3}},
{"ShowStoppingPoint", {PERSISTENT, BOOL, "1", "0", 3}},
{"ShowStoppingPointMetrics", {PERSISTENT, BOOL, "1", "0", 3}},
{"ShowStorageLeft", {PERSISTENT, BOOL, "0", "0", 3}},
{"ShowStorageUsed", {PERSISTENT, BOOL, "0", "0", 3}},
{"SidebarMetrics", {PERSISTENT, BOOL, "1", "0", 3}},
{"SidebarMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
{"SidebarOpen", {PERSISTENT, BOOL, "0", "0", 0}},
{"SignalAnimation", {PERSISTENT, STRING, "frog", "stock", 0}},
{"SignalAnimation", {PERSISTENT, STRING, "stock", "stock", 0}},
{"SignalMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
{"SignalToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"SimpleMode", {PERSISTENT, BOOL, "0", "0", 0}},
{"SimpleMode", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"SLCAbbreviatedSources", {PERSISTENT, BOOL, "0", "0", 3}},
{"SLCActiveSourcesOnly", {PERSISTENT, BOOL, "0", "0", 3}},
{"SLCConfirmation", {PERSISTENT, BOOL, "0", "0", 0}},
@@ -514,18 +555,18 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"SLCFallback", {PERSISTENT, INT, "2", "0", 1}},
{"SLCLookaheadHigher", {PERSISTENT, INT, "0", "0", 2}},
{"SLCLookaheadLower", {PERSISTENT, INT, "0", "0", 2}},
{"SLCMapboxFiller", {PERSISTENT, BOOL, "1", "0", 1}},
{"SLCMapboxFiller", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
{"SLCOverride", {PERSISTENT, INT, "1", "0", 1}},
{"SLCPriority", {PERSISTENT, STRING, "", "", 2}},
{"SLCPriority1", {PERSISTENT, STRING, "Map Data", "Map Data", 2}},
{"SLCPriority2", {PERSISTENT, STRING, "Dashboard", "Dashboard", 2}},
{"SNGHack", {PERSISTENT, BOOL, "1", "0", 2}},
{"SoundPack", {PERSISTENT, STRING, "frog", "stock", 0}},
{"SLCPriority1", {PERSISTENT, STRING, "Vision", "Map Data", 2}},
{"SLCPriority2", {PERSISTENT, STRING, "Map Data", "Dashboard", 2}},
{"SNGHack", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
{"SoundPack", {PERSISTENT, STRING, "stock", "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}},
{"SpeedLimitChangedAlert", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"SpeedLimitController", {PERSISTENT, BOOL, "0", "0", 0}},
{"SpeedLimitFiller", {PERSISTENT, BOOL, "0", "0", 0}},
{"SpeedLimits", {PERSISTENT | DONT_LOG, JSON, "[]", "[]"}},
@@ -533,7 +574,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"SpeedLimitSources", {PERSISTENT, BOOL, "0", "0", 3}},
{"VisionSpeedLimitAutoBookmark", {PERSISTENT, BOOL, "0", "0", 0}},
{"VisionSpeedLimitAutoPreserveSegment", {PERSISTENT, BOOL, "0", "0", 0}},
{"VisionSpeedLimitDetection", {PERSISTENT, BOOL, "0", "0", 0}},
{"VisionSpeedLimitDetection", {PERSISTENT, BOOL, "1", "0", 0}},
{"VisionSpeedLimitTrainingCollector", {PERSISTENT, BOOL, "1", "1", 0}},
{"StandardFollow", {PERSISTENT, FLOAT, "1.45", "1.45", 2}},
{"StandardFollowHigh", {PERSISTENT, FLOAT, "1.2", "1.2", 2}},
@@ -542,13 +583,14 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"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}},
{"StandbyMode", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
{"StartAccel", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"StartAccelStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"StartupMessageBottom", {PERSISTENT, STRING, "Human-tested, frog-approved 🐸", "Always keep hands on wheel and eyes on road", 0}},
{"StartupMessageTop", {PERSISTENT, STRING, "Hop in and buckle up!", "Be ready to take over at any time", 0}},
{"StaticPedalsOnUI", {PERSISTENT, BOOL, "0", "0", 1}},
{"StartupMessageBottom", {PERSISTENT, STRING, "Always keep hands on wheel and eyes on road", "Always keep hands on wheel and eyes on road", 0}},
{"StartupMessageTop", {PERSISTENT, STRING, "Be ready to take over at any time", "Be ready to take over at any time", 0}},
{"StaticPedalsOnUI", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
{"SteerDelay", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"SteerDelayModeMigrated", {PERSISTENT, BOOL}},
{"SteerDelayStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"SteerFriction", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"SteerFrictionStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
@@ -560,18 +602,21 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"SteerOffsetStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"SteerRatio", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"SteerRatioStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"EnableTorqueBarWidget", {PERSISTENT, BOOL, "1", "0", 0}},
{"StockConfidenceBallWidget", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
{"StockDongleId", {PERSISTENT, STRING, "", ""}},
{"StopAccel", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"StopAccelStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"StoppedTimer", {PERSISTENT, BOOL, "0", "0", 1}},
{"StopDistance", {PERSISTENT, FLOAT, "6.0", "6.0", 2}},
{"StoppedTimer", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
{"StoppingDecelRate", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"StoppingDecelRateStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"StarButtonControl", {PERSISTENT, INT, "0", "0", 2}},
{"SwitchbackModeCooldown", {PERSISTENT, INT, "5", "0", 2}},
{"StarButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
{"SwitchbackModeCooldown", {PERSISTENT, INT, "5", "0", 2, SETTINGS_SIMPLE}},
{"SwitchbackModeEnabled", {CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0", "0"}},
{"SubaruSNG", {PERSISTENT, BOOL, "1", "0", 2}},
{"SubaruSNG", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
{"SubaruSNGManualParkingBrake", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"TacoTune", {PERSISTENT, BOOL, "0", "0", 2}},
{"TeslaCoopSteering", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"TestAlert", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"TetheringEnabled", {PERSISTENT, INT, "0", "0", 0}},
{"ThemeDownloadProgress", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
@@ -579,12 +624,13 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"Timezone", {PERSISTENT, STRING, "", ""}},
{"TinygradUpdateAvailable", {PERSISTENT, BOOL, "0", "0", 1}},
{"ToyotaDoors", {PERSISTENT, BOOL, "1", "0", 0}},
{"TrafficFollow", {PERSISTENT, FLOAT, "0.5", "0.5", 2}},
{"TrafficJerkAcceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"TrailerLoad", {PERSISTENT, INT, "0", "0", 2}},
{"TrafficFollow", {PERSISTENT, FLOAT, "0.75", "0.75", 2}},
{"TrafficJerkAcceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
{"TrafficJerkDanger", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
{"TrafficJerkDeceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"TrafficJerkSpeed", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"TrafficJerkSpeedDecrease", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"TrafficJerkDeceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
{"TrafficJerkSpeed", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
{"TrafficJerkSpeedDecrease", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
{"TruckTuning", {PERSISTENT, BOOL, "0", "0", 3}},
{"TuningLevel", {PERSISTENT, INT, "0", "0", 0}},
{"TuningLevelConfirmed", {PERSISTENT, BOOL, "0", "0", 0}},
@@ -596,26 +642,30 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"UpdateTinygrad", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"UpdateWheelImage", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"UseActiveTheme", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"UseKonikServer", {PERSISTENT, BOOL, "0", "0", 2}},
{"UseAutoSteerDelay", {PERSISTENT, BOOL, "1", "1", 3}},
{"UseKonikServer", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"UseSI", {PERSISTENT, BOOL, "1", "1", 3}},
{"UserFavorites", {PERSISTENT, STRING, "", "", 1}},
{"UseVienna", {PERSISTENT, BOOL, "0", "0", 1}},
{"UseVienna", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
{"VEgoStarting", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"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}},
{"VeryLongCancelButtonControl", {PERSISTENT, INT, "6", "0", 2, SETTINGS_SIMPLE}},
{"VeryLongDistanceButtonControl", {PERSISTENT, INT, "6", "0", 2, SETTINGS_SIMPLE}},
{"VeryLongModeButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
{"VeryLongStarButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
{"VoltSNG", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"JeepBrakeHold", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"GMAutoHold", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"VoltOnePedalMode", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"ToyotaAutoHold", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"WarningImmediateVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
{"WarningSoftVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
{"WeatherPresets", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"WeatherToken", {PERSISTENT | DONT_LOG, STRING, "", "", 2}},
{"WheelControls", {PERSISTENT, STRING, "", "", 2}},
{"WheelIcon", {PERSISTENT, STRING, "frog", "stock", 0}},
{"WheelSpeed", {PERSISTENT, BOOL, "0", "0", 2}},
{"WheelIcon", {PERSISTENT, STRING, "stock", "stock", 0}},
{"WheelSpeed", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"WheelToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
};
+1104 -855
View File
File diff suppressed because it is too large Load Diff
+9
View File
@@ -32,6 +32,10 @@ cdef extern from "common/params.h":
JSON
BYTES
cdef enum ParamSettingsTier:
SETTINGS_SIMPLE
SETTINGS_ADVANCED
cdef cppclass c_Params "Params":
c_Params(string, bool) except + nogil
string get(string, bool) nogil
@@ -55,6 +59,8 @@ cdef extern from "common/params.h":
int getTuningLevel(string) nogil
ParamSettingsTier getSettingsTier(string) nogil
PYTHON_2_CPP = {
(str, STRING): lambda v: v,
(builtins.bool, BOOL): lambda v: "1" if v else "0",
@@ -255,3 +261,6 @@ cdef class Params:
cdef string k = self.check_key(key)
cdef optional[int] level = self.p.getTuningLevel(k)
return level.value() if level.has_value() else 0
def get_settings_tier(self, key):
return self.p.getSettingsTier(self.check_key(key))
Binary file not shown.
+16
View File
@@ -0,0 +1,16 @@
from openpilot.common import file_chunker
def test_chunked_stream_round_trip(tmp_path, monkeypatch):
monkeypatch.setattr(file_chunker, "CHUNK_SIZE", 7)
path = tmp_path / "artifact.pkl"
payload = b"a model artifact spanning several chunks"
path.write_bytes(payload)
targets = file_chunker.get_chunk_targets(path, len(payload))
file_chunker.chunk_file(path, targets)
assert file_chunker.file_chunked_exists(path)
assert file_chunker.read_file_chunked(path) == payload
with file_chunker.open_file_chunked(path) as stream:
assert stream.read(9) + stream.read() == payload
+10
View File
@@ -25,3 +25,13 @@ TEST_CASE("params_nonblocking_put") {
REQUIRE(p.get(name) == "1");
}
}
TEST_CASE("settings_tier_is_independent_from_tuning_level") {
Params params;
REQUIRE(params.getSettingsTier("AlwaysOnLateral") == SETTINGS_SIMPLE);
REQUIRE(params.getTuningLevel("AlwaysOnLateral") == 0);
REQUIRE(params.getSettingsTier("HumanLaneChanges") == SETTINGS_SIMPLE);
REQUIRE(params.getTuningLevel("HumanLaneChanges") == 2);
REQUIRE(params.getSettingsTier("AdvancedLateralTune") == SETTINGS_ADVANCED);
}
+32
View File
@@ -1,8 +1,40 @@
import contextlib
import gc
import os
import platform
import sys
from pathlib import Path
import pytest
def _prepend_host_pytest_runtime() -> None:
if platform.system() != "Darwin" or os.getenv("SP_DISABLE_HOST_PYTEST_REDIRECT") == "1":
return
root_dir = Path(__file__).resolve().parent
work_dir = root_dir / ".host_runtime" / "darwin" / "worktree"
required_extension = work_dir / "msgq_repo" / "msgq" / "ipc_pyx.so"
if not required_extension.exists():
return
extra_paths = [work_dir, work_dir / "starpilot" / "third_party"]
extra_paths.extend(sorted(work_dir.glob("*_repo")))
acados_dir = work_dir / "third_party" / "acados"
if acados_dir.is_dir():
extra_paths.append(acados_dir)
existing = set(sys.path)
insert_at = 0
for path in [str(p) for p in extra_paths if p.exists()]:
if path in existing:
continue
sys.path.insert(insert_at, path)
insert_at += 1
_prepend_host_pytest_runtime()
from openpilot.common.prefix import OpenpilotPrefix
from openpilot.system.manager import manager
from openpilot.system.hardware import TICI, HARDWARE
+2 -1
View File
@@ -4,7 +4,7 @@
A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
# 325 Supported Cars
# 326 Supported Cars
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|<a href="##"><img width=2000></a>Hardware Needed<br>&nbsp;|Video|Setup Video|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
@@ -110,6 +110,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Hyundai|Azera 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Azera 2022">Buy Here</a></sub></details>|||
|Hyundai|Azera Hybrid 2019|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Azera Hybrid 2019">Buy Here</a></sub></details>|||
|Hyundai|Azera Hybrid 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Azera Hybrid 2020">Buy Here</a></sub></details>|||
|Hyundai|Azera Hybrid (with HDA II & LFA2) 2025|Highway Driving Assist II & Lane Follow Assist 2|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai S connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Azera Hybrid (with HDA II & LFA2) 2025">Buy Here</a></sub></details>|||
|Hyundai|Custin 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Custin 2023">Buy Here</a></sub></details>|||
|Hyundai|Elantra 2017-18|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra 2017-18">Buy Here</a></sub></details>|||
|Hyundai|Elantra 2019|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra 2019">Buy Here</a></sub></details>|||
+176
View File
@@ -0,0 +1,176 @@
# StarPilot Unified Model Rebuild
This workflow rebuilds StarPilot driving and driver-monitoring artifacts for the vendored tinygrad revision. Driving-model behavior versions remain manifest metadata; every runtime driving artifact uses the `tinygrad_single_v1` layout.
## Safety
- The supported build device is `comma@192.168.3.110`.
- Never run these commands against `192.168.3.109`.
- Normal artifacts target QCOM. External-GPU artifacts must be compiled explicitly and tagged in the manifest.
- Keep source ONNX files and compiled PKLs on the T5 workspace, not the comma.
## Workspace
The default workspace is:
```text
/Volumes/T5/StarPilot-Model-Rebuild-2026-06-22/
```
Important directories:
- `onnx/<model-id>/`: ID-prefixed source ONNX files.
- `compiled/`: completed unified driving PKLs.
- `driver-monitoring/`: DM ONNX, model PKL, metadata, and camera warps.
- `ready-for-resources/`: flat repository-upload handoff.
- Oversized models are represented by repository-safe `.p00`, `.p01`, and `.sha256` files in `ready-for-resources/`.
- `logs/`: one remote compilation log per model.
- `results/`: source and artifact checksum records.
- `manifests/`: generated `model_names_v22.json`.
## Initialize And Extract
```bash
python3 scripts/model_rebuild_pipeline.py init
python3 scripts/model_rebuild_pipeline.py extract \
--base-manifest /path/to/model_names_v21.json
```
Extraction streams Git blobs directly to disk. LFS pointers are resolved from the local object cache or fetched by object ID, then checked against the pointer SHA-256 and size. Binary ONNX data is never stored in a shell variable.
To retry one source:
```bash
python3 scripts/model_rebuild_pipeline.py extract \
--model pop22 \
--base-manifest /path/to/model_names_v21.json
```
Source commits are defined in `scripts/model_source_map_v22.json`.
## Compile
Compile one model:
```bash
python3 scripts/model_rebuild_pipeline.py compile \
--model pop22 \
--base-manifest /path/to/model_names_v21.json
```
Compile or resume the full catalog:
```bash
python3 scripts/model_rebuild_pipeline.py compile \
--base-manifest /path/to/model_names_v21.json
```
Existing artifacts are skipped unless `--force` is passed. Each model is staged in its own remote input directory, compiled on `.110`, copied back to the T5, hashed, and copied into `ready-for-resources/`. Failures are written to `results/<id>_failure.json`; rerunning the same command resumes incomplete models.
Validate one or all completed artifacts with synthetic camera inputs on QCOM:
```bash
python3 scripts/model_rebuild_pipeline.py validate \
--model pop22 \
--base-manifest /path/to/model_names_v21.json
```
The lower-level device compiler also supports direct use:
```bash
./models --model pop22 --input-format split --version v11
./models --model deeprl3v2 --input-format supercombo --version v15
```
For a model that cannot run on the device GPU, compile with the USB AMD GPU attached:
```bash
./models --lebowski --gpu
```
The dynamic flag (`--lebowski` above) sets the output and manifest model ID;
when only one source model is staged, its ONNX filename does not need to match
that ID. Input format and behavior version are inferred. `--external-gpu`
remains available as a compatibility alias for `--gpu`.
This emits a streaming out-of-band pickle and keeps QCOM available for camera warps. Its manifest entry must include:
```json
{
"id": "lebowski",
"uses_external_gpu": true
}
```
Only tagged models activate the external GPU. If the GPU or artifact is unavailable, runtime falls back to the built-in model; all untagged models retain the existing QCOM path.
`--version` records behavioral semantics only. It does not change artifact layout.
If the compiled PKL exceeds 100 MiB, `./models` automatically keeps the full
local PKL and creates 95 MiB upload parts beside it:
```text
deeprl3v2_driving_tinygrad.pkl
deeprl3v2_driving_tinygrad.pkl.p00
deeprl3v2_driving_tinygrad.pkl.p01
deeprl3v2_driving_tinygrad.pkl.sha256
```
To split an already compiled artifact:
```bash
./models --split-artifact /path/to/deeprl3v2_driving_tinygrad.pkl \
--output-dir /path/to/upload-ready
```
Upload only the numbered parts and checksum when the full PKL exceeds the
repository limit. The downloader reassembles into a temporary file, verifies
the companion SHA-256, and atomically installs the final PKL. No manifest field
is required for multipart artifacts.
## Driver Monitoring
Stage the current DM ONNX in `uncompiledmodels`, then run:
```bash
./models --dm \
--input-dir /data/openpilot/uncompiledmodels \
--output-dir /tmp/dm_artifacts
```
This builds:
- `dmonitoring_model_tinygrad.pkl`
- `dmonitoring_model_metadata.pkl`
- `dm_warp_1928x1208_tinygrad.pkl`
- `dm_warp_1344x760_tinygrad.pkl`
All four files must be updated together.
## Manifest
Generate v22 after compilation:
```bash
python3 scripts/model_rebuild_pipeline.py manifest \
--base-manifest /path/to/model_names_v21.json
```
The generator preserves existing IDs and behavioral metadata and adds
`deeprl3v2`. Manifest v22 implies the unified single-PKL runtime layout.
Repository-hosted multipart files are discovered by naming convention, so no
size, hash, format, or part-count metadata is required.
`uses_external_gpu` is optional and defaults to `false`.
## Runtime Verification
Compilation validates JIT capture/replay, pickle round-trip, finite outputs, metadata slices, and both camera warps. Before release:
1. Select representative v8, v11, v12, v15, and supercombo models.
2. Confirm `modeld` stays running.
3. Confirm finite `modelV2` path, lane-line, lead, pose, and action data.
4. Confirm `driverStateV2` on both supported camera resolutions.
5. Test download, selection, deletion, randomization, migration, and fallback in QT, raylib/mici, and Galaxy.
The built-in South Carolina artifact is `selfdrive/modeld/models/driving_tinygrad.pkl`. If migration cannot download the selected v22 artifact, StarPilot switches to that built-in model.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.
+102
View File
@@ -0,0 +1,102 @@
# Contribute to StarPilot
Contributions are welcome. StarPilot is driver-assistance software, so changes should be focused, testable, and safe for vehicles outside the change's intended scope.
## Pull requests
Open all pull requests against the **`Dom` branch**. `Dom` is StarPilot's testing and integration branch; do not target the release branch directly.
A pull request should:
* have one clear purpose and contain only changes needed for that purpose;
* explain what changed, why it changed, and how it was tested;
* link the relevant issue or feedback item when one exists;
* avoid unrelated cleanup, refactors, dependency changes, and formatting churn; and
* pass the existing tests and checks.
Keep commits reviewable and update documentation when behavior or configuration changes. If a change needs a large refactor, separate that work from the behavior change when practical.
### Vehicle-specific changes
Do your best to ensure a vehicle-specific change affects only the intended vehicle, platform, or brand. Prefer the narrowest appropriate condition instead of changing shared behavior for every vehicle.
Tests should prove both sides of that boundary:
* the affected vehicle receives the new or corrected behavior; and
* an unaffected vehicle, platform, brand, or configuration retains the existing behavior.
Include the vehicle and hardware used for on-road or bench testing in the pull request. Hardware testing is valuable, but it does not replace an automated regression test when the behavior can be tested in code.
## Development environment
StarPilot keeps host-native development tools separate from device-target builds. Run the setup and development commands from the repository root.
Install the Python dependencies before starting:
```bash
tools/install_python_dependencies.sh
```
On Ubuntu, `tools/ubuntu_setup.sh` installs both the system and Python dependencies. The development tools also require `uv`.
Use `./dev` for host-native tools. It creates and reuses an isolated environment under `.host_runtime/`, keeping host build artifacts out of the working tree:
```bash
./dev replay
./dev cabana
./dev plotjuggler
./dev shell
```
For desktop UI work, use `./c3`, `./c4`, or `./raybig`. These commands use the same isolated host environment.
Use `./build` when you need comma-compatible device artifacts. This is the expected validation for changes that affect compiled device code or runtime behavior:
```bash
./build
```
Device builds require Docker Desktop or Podman with Linux/aarch64 support and a configured comma sysroot. See the [laptop device-build guide](../how-to/laptop-device-build.md) for setup instructions and the [complete StarPilot development workflow](https://github.com/firestar5683/StarPilot/blob/Dom/tools/STARPILOT_DEVELOPMENT.md) for all host commands and troubleshooting.
## Code formatting
Match the style of the code around your change and do not reformat unrelated files. Python formatting and lint rules are defined in `pyproject.toml`; the project uses two-space indentation and Ruff.
Run Ruff on changed Python files while developing:
```bash
ruff check path/to/changed_file.py
ruff format --check path/to/changed_file.py
```
Before submitting, run the repository lint checks from the project root:
```bash
./scripts/lint/lint.sh
```
## Testing standards
Every behavior change or bug fix should include focused automated tests when practical. Recent StarPilot tests favor small regression cases that construct the relevant state, exercise one behavior, and assert the exact result.
At minimum:
1. Add or update a test that would fail without the change.
2. Cover important boundaries, modes, and disabled states related to the change.
3. For vehicle-specific behavior, add a negative case showing the change does not bleed into an unaffected vehicle or configuration.
4. Run the directly affected test module and the existing tests for the affected subsystem.
5. Ensure the pull request passes all existing CI checks before it is ready to merge.
Run a focused test module with pytest:
```bash
pytest path/to/test_file.py
```
Run the full test suite when your development environment supports it:
```bash
pytest
```
If a test requires special hardware or cannot run in your environment, say so in the pull request and document the closest validation you completed.
+1 -1
View File
@@ -2,7 +2,7 @@
This flow builds **device-target (`larch64`) binaries on your laptop** using a Linux/aarch64 container and a synced comma sysroot.
For the full StarPilot branch workflow, including host-native shorthand tools such as `./dev`, `./c3`, `./c4`, and `./raybig`, see [tools/STARPILOT_DEVELOPMENT.md](../../tools/STARPILOT_DEVELOPMENT.md).
For the full StarPilot branch workflow, including host-native shorthand tools such as `./dev`, `./c3`, `./c4`, and `./raybig`, see the [StarPilot development guide](https://github.com/firestar5683/StarPilot/blob/Dom/tools/STARPILOT_DEVELOPMENT.md).
## Prerequisites
+109
View File
@@ -302,3 +302,112 @@ For temporal behavior on a saved frame directory or route extract, replay the ru
```bash
.venv/bin/python scripts/replay_speed_limit_vision.py .tmp/vision_iter/seg10_5fps --frames-fps 5
```
The detector/classifier runtime is model-only by default. Use `--crop-ocr` with
`evaluate_runtime_manifest.py` or `replay_route_runtime.py` only for an explicit
legacy comparison. A model-only release must match reviewed-manifest accuracy
and pass representative route replays at measured on-device cadence. Evaluate
candidate recognition and temporal publish behavior separately: a correct
single-frame candidate can still be suppressed by the history and speed-change
confirmation policy.
Ignored review rows label the proposed crop, not the entire camera frame.
Consequently, negative-window candidate and publish counts from
`evaluate_reviewed_route_events.py` are an upper bound until the full frame is
audited; another valid sign can be present outside the rejected crop. Use the
per-row output and frame image to audit any regression delta before treating it
as a runtime false positive.
## Promotion Gate
Do not promote a checkpoint from classifier validation accuracy alone. Export it
to an isolated model directory and run the complete runtime pipeline against the
reviewed positive, hard-negative, and failed-drive manifests. A candidate must
preserve exact-value recall, avoid new wrong-value reads, and remain within the
accepted false-positive budget before route replay.
Mine detector proposals that fool an integrated-reject classifier into a new
reject class before retraining:
```bash
.venv/bin/python scripts/speed_limit_vision/mine_classifier_reject_crops.py \
--models-dir /path/to/candidate/models \
--dataset /path/to/versioned/classifier \
--manifest /path/to/reviewed-negative-manifest.csv
```
Keep the resulting dataset version separate from the current training set. If a
hard-negative retrain lowers reviewed recall, reject the checkpoint even when it
improves aggregate validation accuracy or removes a known false positive.
## Active-Learning Review Pass
Keep parallel miners in separate directories and merge them only when their
model and mining fingerprints match:
```bash
.venv/bin/python scripts/speed_limit_vision/merge_manual_review_queues.py \
/path/to/shard0 /path/to/shard1 /path/to/shard2 /path/to/shard3 \
--output-dir /path/to/merged
```
When rescanning with a new model, compare the fingerprinted queues before
selecting another batch. The optional review output retains the full queue
schema so it can be passed directly to the selector and review server:
```bash
.venv/bin/python scripts/speed_limit_vision/compare_manual_review_queues.py \
--before /path/to/baseline/manual_review_queue.csv \
--after /path/to/candidate/manual_review_queue.csv \
--output-csv /path/to/comparison.csv \
--review-output /path/to/disagreements/manual_review_queue.csv
.venv/bin/python scripts/speed_limit_vision/select_manual_review_queue.py \
--input /path/to/disagreements/manual_review_queue.csv \
--output /path/to/review/manual_review_queue.csv \
--max-rows 1200 \
--min-seconds-per-route-speed 3
```
The selector prioritizes value changes and gained/lost reads, balances routes
and speed classes, and removes adjacent same-speed frames from one scene. Start
the reviewer and import its labels without moving route media off the training
volume:
```bash
.venv/bin/python scripts/speed_limit_vision/serve_manual_review_queue.py \
--manifest /path/to/review/manual_review_queue.csv \
--port 8765
.venv/bin/python scripts/speed_limit_vision/import_manual_review_queue.py \
--queue /path/to/review/manual_review_queue.csv
```
## Re-mine the Route Backlog
Re-run the backlog after a candidate passes the reviewed-manifest and route
replay gates. Use a model fingerprinted run so new pseudo-labels are staged next
to, rather than merged into, the original route-mining data:
```bash
.venv/bin/python scripts/speed_limit_vision/mine_route_training_samples.py \
--workspace /Volumes/T5/starpilot_speed_limit/workspace/speed_limit_training_clean \
--models-dir /path/to/promoted/models \
--model-only \
--run-id auto \
--sample-every 2.0 \
--transition-step 0.5 \
--max-frames-per-route 720 \
--max-positives-per-route 120 \
--max-negatives-per-route 200
```
The output is written under
`staging/route_mining/model_<model-fingerprint>_run_<mining-fingerprint>/` with
its own detector images, classifier labels, review manifest, and per-route
completion state. The mining fingerprint includes the model-only mode,
thresholds, sampling configuration, and relevant source code. Review and
deduplicate that staged run before merging it into a training dataset. Never
overwrite the canonical route samples or automatically train on every mined
positive; map agreement and human review remain required because a stronger
model can still reproduce its own mistakes at larger scale.
+278
View File
@@ -0,0 +1,278 @@
# I Taught My Comma to Read Speed Limit Signs
<video controls playsinline preload="metadata" poster="assets/speed-limit-vision-demo-poster.jpg" style="width: 100%; max-width: 1280px;">
<source src="assets/speed-limit-vision-demo.mp4" type="video/mp4">
</video>
[Watch the speed-limit vision demo](assets/speed-limit-vision-demo.mp4)
This whole project started because I was lazy.
If youve driven an HKG or Toyota vehicle in recent years, youve probably seen the dashboard automatically pick up speed limits using the camera. Coming from a car that didn't have this functionality, I was very jealous. Those cars can feed those speed limits directly into openpilot forks speed limit controllers. Mine couldn't.
The “solution” has been to manually add speed limits to Mapbox or OpenStreetMaps. It worked, but it was tedious. I live in rural Kansas, where my commute is almost entirely devoid of nerds contributing open source map data. I began mapping my commute when I first joined the project and within an hour said, “I ain't doin this”
We have an AI model that's good enough to drive our cars running on a smartphone chipset from years ago. How hard could it be to build one that just reads speed limit signs? I wanted StarPilot to look out the windshield, see a speed limit sign, and just know what it said.
That sounded simple enough.
It wasnt.
The first prototype was basically held together with duct tape: I used all the public speed limit datasets I could find (glare and Lisa), some OpenCV, a little OCR, and a lot of wishful thinking. It could occasionally read a sign, but it missed 90% of them and produced enough false positives that you definitely wouldnt want to trust it.
The important part wasnt that it sucked - it was that it worked just well enough to start collecting better training data.
I run a fork of openpilot called StarPilot. We're heavily focused on tuning and testing wild ideas, which made my users the perfect candidates for this project.
Instead of relying on public datasets that barely resembled comma camera footage, StarPilot started collecting its own. Community members submitted routes from all over the country using bookmarks that were generated automatically when the model said "I think this might've been a sign," and every promising detection went through manual review. Eventually that grew into hundreds of gigabytes of real driving footage and thousands of carefully labeled speed limit signs.
From there it became an endless cycle:
- Train a better model.
- Mine more routes.
- Find more missed signs.
- Label them.
- Repeat.
The funny part is that training the neural network wasnt actually the hardest problem.
The hard part was everything around it.
One of the biggest challenges wasnt accuracy, it was speed. Every millisecond spent processing was another frame the model didn't have time to see. The faster it could process frames, the more chances it had to catch a sharp, readable speed limit sign before it disappeared. This was particularly imperative at night, when naturally camera frame rates drop alongside your chances of picking up a clear sign.
Its slowly evolved from a proof of concept into a model thats trained primarily on real comma footage instead of generic traffic sign datasets. It now runs entirely on-device, publishes vision-based speed limits directly into StarPilot, and no longer depends on OCR or manually maintaining map data.
And best of all…
While I'm too lazy to hand write hundreds of speed limit signs into OpenStreetMaps, this tool can now be used to automate the entire process, giving back to the open source mapping community that helps those in larger areas so well.
Give my vision speed limits a try, or help continue to build a better one than mine! All info and training stack is available on the StarPilot repo.
---
*For a full LLM style, more technical breakdown for those interested in the process, please read below:*
## Teaching StarPilot to Read Speed Limit Signs
In March 2026, this project started with a practical question: could StarPilot use the road camera to recognize speed-limit signs, attach them to GPS data, and help fill gaps in OpenStreetMap?
The first answer was "probably." A clean 40 mph sign in recorded comma footage was large enough to detect and read. StarPilot already had access to the live road-camera stream through VisionIPC, ONNX models could run through OpenCV DNN, and the existing Speed Limit Controller already knew how to combine several sources. The pieces existed.
What did not exist was a model trained for this camera, a trustworthy dataset, an honest replay benchmark, or a runtime that could analyze enough frames without interfering with openpilot. Building those became the real project.
This is the story of how we went from a weak imported model that was barely useful for automatic bookmarks to a custom detector and classifier trained primarily from real comma footage, how community routes changed the quality of the data, and why model accuracy on a laptop turned out to be only half of the problem.
### The first prototype
The first live implementation used an imported Ultralytics-style checkpoint called `ayoubsa_best`. Its provenance was thin: the ONNX metadata identified it as a Kaggle-trained detector, but there was no training notebook or source dataset in the repository. More importantly, its classes were a poor fit for American roads. It knew speed limits in 10 mph increments, such as 20, 30, and 40, but not the common 25, 35, 45, 55, or 65 mph signs.
That led to a hybrid design. The model proposed a sign region, then lightweight OpenCV and OCR-like digit logic tried to read the crop. It was enough to prove the complete path:
1. Read the live road-camera stream.
2. Search the right side of the frame for likely signs.
3. Produce a candidate speed and confidence.
4. Confirm it over time.
5. Publish it as a selectable `Vision` source for the Speed Limit Controller.
6. Save debug frames and bookmarks for later training.
It was not yet a good speed-limit reader. It missed most signs, produced false positives, and struggled badly with 5 mph increments. But it could occasionally find real signs, which meant it could help us collect the data needed to replace itself.
That bootstrap capability mattered more than its initial accuracy.
### Public data gave us a starting point
We chose a two-stage architecture early: one model would answer "where is the speed-limit sign and what kind is it?" while another would answer "what number is printed on it?" Detection and reading are related, but they are not the same task. Keeping them separate let the detector learn the general shape and location of a sign without needing a separate object class for every posted speed.
Three public U.S. traffic-sign datasets formed the initial training base:
- [LISA](https://cvrr.ucsd.edu/lisa-traffic-signs-dataset) provided U.S. road scenes, 47 sign types, and 7,855 annotations across 6,610 frames. Its annotations also included useful information such as occlusion and whether a sign belonged to a side road.
- [GLARE](https://arxiv.org/abs/2209.08716) provided 2,157 U.S. traffic-sign images taken from 33 dashcam videos under strong sun glare. The paper itself demonstrated why mixed normal and glare training was important.
- [ARTS](https://swshah.w3.uvm.edu/vail/datasets.php), the Automotive Repository of Traffic Signs, provided additional U.S. signs in easy, challenging, and video-log configurations.
The clean first pass imported 3,078 ARTS images with 3,139 relevant boxes. When a working LISA archive was found, it added 1,577 images and 1,680 boxes, including 15, 25, 35, 45, 55, and 65 mph classes. GLARE was downloaded selectively to avoid pulling a large collection of unrelated checkpoints.
### More data made the first retrain worse
The first clean public-data retrain looked reasonable in conventional validation metrics and failed badly on real comma routes.
The stronger older model found a candidate in 9 of 39 bookmarked route windows. The clean public-data model found only 1 of 39. It also regressed the saved-frame suite. We restored the older model instead of promoting a checkpoint merely because it was newer or had better training curves.
The domain gap was larger than image dimensions suggested. LISA, GLARE, ARTS, and comma footage were all forward-looking road imagery, but they differed in lens distortion, mounting position, exposure, dynamic range, compression, motion blur, sign position, and the number of pixels available when a sign first appeared. Public data often contained a cleaner or more centered sign than the live comma pipeline would see.
This established a rule that guided every later pass: public data was useful for bootstrapping and preservation, but real comma footage had to dominate final training and evaluation.
### Turning StarPilot into a data engine
The next large improvement did not come from a new backbone. It came from collecting better data.
StarPilot gained an automatic training collector, automatic bookmarks, manual bookmarks, debug snapshots, route metadata, and tools to preserve full-resolution video segments. A route-bundling script verified that a contributed route was public, contained the required qlogs, rlogs, and `fcamera.hevc` files, and had the vision collector enabled before packaging it.
That allowed StarPilot users to contribute real driving data from different cars, cameras, roads, states, lighting conditions, and sign styles. Eventually, nearly 200 GB of zipped comma routes landed on disk. This was enormously more useful than another generic sign dataset because it matched the exact production domain:
- the same road camera and encoding path;
- the same wide-angle geometry;
- the same night exposure and motion blur;
- the same right-shoulder sign placement;
- the same live crop errors;
- and the same hard negatives seen by the runtime detector.
The model could then scan old routes, find likely signs and mistakes, and generate another review queue. A better model could rescan the same backlog and find candidates the weaker model had never seen. Each iteration improved not only the deployed model, but also the quality of the next dataset.
### Human review was the quality control
Automatic labels were never trusted blindly. A map transition did not prove that a sign was visible, a bookmark did not prove that the best frame had been selected, and a confident model prediction did not prove that the crop contained the correct sign.
The manual review UI evolved around throughput. Typing a number labeled a regulatory sign. Single-key shortcuts marked advisory (`a`), school-zone (`s`), regulatory (`r`), uncertain (`u`), or ignored/not-a-sign (`i` or `x`) samples. Enter or Space accepted a prediction. Reviewers could also redraw a bad bounding box.
Those details affected model quality directly:
- A readable but slightly imperfect crop was useful.
- A crop that showed only the minimum-speed portion of a 65/40 sign was not a valid 40 mph regulatory label.
- Stop signs and empty crops became hard negatives.
- Advisory signs were labeled explicitly, even though overall sign recall remained the product priority.
- Uncertain night crops were retained separately instead of being treated as equally strong labels.
- Repeated frames from the same sign track were deduplicated so one scene could not dominate the dataset.
- Incorrectly large or shifted boxes were redrawn once the review tool exposed that capability.
One labeling ambiguity forced a larger re-review: earlier sessions had not made regulatory versus advisory state clear enough. Rather than preserve potentially poisoned labels, affected advisory candidates were put back into the queue under the corrected UI contract.
This was active learning in a practical form: spend human time on disagreements, weak reads, false publishes, missed signs, bad boxes, and rare conditions, not on thousands of easy duplicate frames.
By one later OCR-free promotion audit, the backlog covered 80 routes and 366,946 sampled frames. The model under audit produced 34,260 candidates for rescoring, while the canonical review database contained 5,383 unique human-reviewed crops: 593 positive reads and 4,146 usable crop-level rejects, with the remainder reserved for uncertain or otherwise excluded decisions. The raw route collection was large, but those reviewed examples were the part that made it trustworthy.
### The architecture that survived
The current production path is model-only. OCR remains in the source tree for legacy compatibility and offline experiments, but both full-frame OCR and crop OCR are disabled for the active detector/classifier pipeline.
The deployed stack contains:
- A YOLO11n-derived detector exported as a fixed `256x256` ONNX model. It proposes regulatory, advisory, and school-zone speed-limit signs.
- A YOLO11n-classifier-derived `128x128` ONNX model. It predicts 15 through 75 mph in 5 mph increments and includes a learned reject class for crops that are not valid speed-limit reads.
- Runtime logic that evaluates several crop expansions, combines their support, applies geometry and sign-type checks, and confirms lower-confidence changes over multiple frames.
- A right-side region of interest, which reduces wasted work while retaining the part of U.S. road scenes where most applicable signs appear.
The detector is about 9.9 MB and the classifier about 5.9 MB. They remain separate ONNX files intentionally. Joining the graphs into one file would make packaging look tidier, but it would not eliminate the detector or classifier computation. Separate models also let us change detector resolution, classifier training, reject behavior, and crop strategy independently.
A third standalone reject model was tested and discarded. It added another inference pass without measurably improving the reviewed positives or surviving false positive. Folding rejection into the value classifier produced a better cost/accuracy tradeoff.
OCR was removed only after the model-only path could pass the preservation suites on its own. In the decisive audit, model consensus improved the targeted 20 mph events from 1/14 to 11/14 and the broader targeted set from 21/36 to 27/36 without regressing the established legacy suites. At that point, OCR's occasional rescue was no longer worth the latency, extra failure modes, and frames lost while the device waited for it to finish.
Direct-value detectors, smaller input sizes, MobileNet-style classifiers, optical-flow tracking, and multiple crop strategies were also tested. Some looked attractive on desktop metrics and failed where it mattered. In particular, a 224-pixel detector was faster but dropped the hometown 20 mph suite from 14/14 to 9/14. The 256-pixel detector remained the better production choice.
### Frame rate is part of accuracy
The most important evaluation lesson came from a route with seven manually bookmarked signs.
An early offline replay sampled the video at 5 fps and found several signs. The actual comma found almost none. The model files matched and the vision process was running, so the discrepancy initially looked mysterious.
The replay had been landing on lucky frames.
The camera produced 25 frames per second, but the live process only attempted inference every 0.4 seconds. When we replayed all 25 source frames while enforcing that real inference gate, the local result fell to 0 of 7, matching the drive. Halving the interval to 0.2 seconds moved the same honest replay to 5 of 7 candidate windows and 3 of 7 actual publishes.
From then on, a model was not evaluated only as a collection of independent images. Route replay had to include:
- source-frame timing;
- the measured detector and classifier cost;
- steady and follow-up inference intervals;
- temporal confirmation rules;
- CPU backoff;
- and the distinction between a candidate and a value actually published to the UI or controller.
This distinction explained many arguments over numbers. A candidate means the detector and reader noticed something in a sign window. A publish means the temporal and confidence gates accepted it as the live speed limit. A model can read a sign correctly once and still fail to publish if a weak read requires a second frame that the device never processes.
Later-frame mining showed why this mattered. One pass revisited 405 known sign events and extracted 939 clearer frames from later in their tracks. Combined with track-aware classifier training and a reduction from four classifier crops to three, the measured runtime suite improved from 247 to 265 correct publications, including a gain from 211 to 229 in the priority 30-65 mph range. Better frame selection and less per-frame work helped at the same time.
### Making it fit beside openpilot
The comma is already running camera processing, the main driving models, localization, controls, the UI, and vehicle communication. A speed-limit sidecar cannot assume an idle CPU.
The early hybrid pipeline could take roughly 1.5 seconds per frame when the detector, crop reader, and OCR paths all fired. At that cadence, signs could pass through their brief readable window without ever being analyzed. It also added enough CPU pressure to coincide with camera/model frame-sync errors and temporary `locationd` alerts on some night drives.
Changing process priority was not the solution. The daemon was already running at a very low scheduling priority. The useful changes were to remove OCR from production, restrict detection to the right-side ROI, shrink the models carefully, avoid the extra reject-model pass, add device-load backoff, and use a faster follow-up cadence after a candidate appeared.
The current runtime requests a 0.15-second steady interval and a 0.10-second follow-up interval, but inference cost is the real limiter. On two measured routes, the complete process sustained about 1.77 to 1.81 inferences per second. The detector alone took roughly 0.43 to 0.44 seconds, and each classifier crop could add about 0.066 seconds. A sign may receive only 4 to 13 analyzed frames in a seven-second approach window.
This is why "run it at 10 Hz" is not a configuration change. The process can ask for 10 Hz, but it cannot start the next inference until the current one finishes.
### Debugging the system, not just the model
Several apparent model regressions were actually system problems.
These failures led to better observability: model hashes, inference counts, interval reasons, detector and classifier timing, CPU backoff state, candidates, publications, and debug captures all became part of route analysis. We stopped asking only "is the model accurate?" and started asking "which exact model and runtime processed which exact frames, and what did it publish?"
### How we decide whether a model is better
The promotion process now uses several independent gates:
- Reviewed crop accuracy checks whether the classifier can read localized signs.
- Hard-negative sets contain parking signs, traffic signals, truck restrictions, taillights, and other shapes that previously caused false reads.
- A 100-event main route suite tests full candidate and publication behavior at measured comma cadence.
- A separate 34-event held-out suite guards against tuning directly to the main set.
- A hometown 20 mph suite protects a class that earlier models repeatedly missed.
- Legacy suites ensure that old wins do not disappear.
- Targeted night and glare replays test the conditions most likely to break normal validation assumptions.
- On-device timing and live routes remain the final authority.
Many experiments were rejected despite improving one number. A glare-focused model lost broader recall. A traffic-signal hard-negative detector fixed one false positive and lost legitimate 35 mph signs. A more aggressive reject-classifier update reduced errors and then changed enough timing to expose different false publishes. Conservative weight interpolation often produced the safest result because it taught a narrow correction without erasing the representation that already worked.
The drive currently contains 87 detector experiment directories and 72 classifier experiment directories. Those are not 159 successive improvements. They are the record of resolution bake-offs, failed hard-negative passes, direct-value experiments, night/glare runs, integrated-reject models, temporal-crop training, and interpolations that mapped the boundaries of the system.
### Where accuracy is now
The current classifier, internally called `distilled-moonstone v3`, improved the deployed route suite without changing model size or runtime cost:
| Runtime suite | Previous tree | Current model |
| --- | ---: | ---: |
| Main route publishes | 78/100 | **92/100** |
| Main wrong publishes | 0 | **0** |
| Held-out publishes | 24/34 | **26/34** |
| Hometown 20 mph signs | 14/14 | **14/14** |
| Hard sun-glare check | 70 mph published | **70 mph published** |
These denominators are manually confirmed sign events, not arbitrary video frames. `92/100` means the full runtime pipeline published the correct value in 92 of 100 known sign windows while running with measured comma timing. It does not mean the model is 92 percent accurate on every road or every possible sign.
In ordinary daylight scenes, current performance is very good. The model is now reliable when signs are reasonably exposed and readable, while extreme image quality and limited frame opportunities remain the dominant failures.
The current result is also substantially better than the first public-data prototype, which missed nearly every sign on several routes and sometimes produced only a single useful event in an entire drive.
### The next hard barriers
The remaining misses are unusually informative. Of the 18 main-suite events the current model does not publish:
- 6 never produce a correct candidate at measured cadence.
- 4 produce exactly one weak correct read, but cannot satisfy the two-read confirmation rule.
- At an idealized 6.7 Hz cadence, the same model recovers 7 of those 10 publishes, reaching a theoretical 97/100, but also introduces one wrong publish.
- The three stubborn cases are all 45 mph scenes: two remain unreadable even at ideal cadence, and one first reads 40 before seeing 45 too late.
That theoretical 97/100 is not a production accuracy claim. It is a diagnostic result showing that most remaining failures already contain a readable frame that the device does not process.
The next large improvement is therefore likely to come from throughput and temporal reuse, not from simply lowering confidence thresholds:
1. Make the detector materially faster without giving up the small-sign and 20 mph recall lost by the 224- and 192-pixel experiments.
2. After a detector finds a sign, track or cheaply reclassify that crop across nearby frames instead of paying for another full detector pass every time.
3. Investigate a more efficient inference backend or model architecture on comma hardware. Direct model microbenchmarks are useful, but the complete process and onroad CPU contention must remain the acceptance metric.
4. Add targeted, human-reviewed sequences for the remaining 45 mph failures, night ghosting, distant signs, and direct-sun approaches.
5. Improve sign applicability for minimum-speed, truck-only, school-zone, advisory, side-road, and exit-ramp signs without sacrificing the product's primary goal: finding normal regulatory speed limits.
6. Keep model-fingerprinted backlog mining so every promoted model can rescan old routes into an isolated review set without silently replacing canonical labels.
Night remains difficult because the camera often produces fewer sharp frames, motion blur can create double digits, and reflective signs may be detected while they are still too distant to read. Glare is difficult for the opposite reason: contrast collapses and sign edges disappear into the sky. Both problems require sequence-level examples, not just one selected crop.
### What we learned
This was never one long training run. It was the construction of a data and evaluation system.
Public datasets made the first detector possible. A weak model made automatic collection possible. StarPilot users supplied the camera-native diversity that public data could not. Human review turned model proposals into trustworthy labels. Full-cadence replay exposed timing mistakes that image metrics hid. Live device telemetry kept CPU and process failures from being mistaken for neural-network failures. Conservative promotion gates preserved hard-won behavior while narrow experiments improved specific weaknesses.
The result is already useful and, in ordinary scenes, often impressively accurate. The path to the next jump is also clearer than it was at the beginning. We do not mainly need another giant pile of generic sign images. We need to process more of the good frames the comma already sees, teach the remaining hard sequences carefully, and continue judging the complete on-device system rather than the model in isolation.
That is the difference between training a traffic-sign classifier and building a speed-limit reader that works in a car.
### Technical references
- Runtime implementation: [`starpilot/system/speed_limit_vision.py`](../starpilot/system/speed_limit_vision.py)
- Training runbook: [`docs/how-to/train-speed-limit-vision.md`](how-to/train-speed-limit-vision.md)
- Training, mining, review, and evaluation tools: [`scripts/speed_limit_vision`](../scripts/speed_limit_vision)
- Deployed model assets: [`starpilot/assets/vision_models`](../starpilot/assets/vision_models)
+70 -6
View File
@@ -4,7 +4,25 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
source "$DIR/launch_env.sh"
export SP_BOOT_TIMING_LOG="${SP_BOOT_TIMING_LOG:-/tmp/starpilot_boot_timing.log}"
: > "$SP_BOOT_TIMING_LOG" 2>/dev/null || true
SP_LAUNCH_LAST_SECONDS=$SECONDS
function sp_boot_timing_line {
echo "$1"
printf '%s\n' "$1" >> "$SP_BOOT_TIMING_LOG" 2>/dev/null || true
}
function sp_launch_timing {
local now=$SECONDS
local delta=$((now - SP_LAUNCH_LAST_SECONDS))
sp_boot_timing_line "SP_BOOT_TIMING launch $1 +${delta}s total=${now}s"
SP_LAUNCH_LAST_SECONDS=$now
}
function agnos_init {
sp_launch_timing "agnos_init_start"
# TODO: move this to agnos
sudo rm -f /data/etc/NetworkManager/system-connections/*.nmmeta
@@ -36,7 +54,16 @@ function agnos_init {
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
AGNOS_CURRENT_VERSION="$(< /VERSION)"
AGNOS_UPDATE_REQUIRED=1
for accepted_version in $AGNOS_ACCEPTED_VERSIONS; do
if [ "$AGNOS_CURRENT_VERSION" = "$accepted_version" ]; then
AGNOS_UPDATE_REQUIRED=0
break
fi
done
if [ "$AGNOS_UPDATE_REQUIRED" = "1" ]; then
AGNOS_PY="$DIR/system/hardware/tici/agnos.py"
MANIFEST="$DIR/system/hardware/tici/agnos.json"
if $AGNOS_PY --verify $MANIFEST; then
@@ -44,9 +71,13 @@ function agnos_init {
fi
$DIR/system/hardware/tici/updater $AGNOS_PY $MANIFEST
fi
sp_launch_timing "agnos_init_done"
}
function launch {
sp_launch_timing "launch_start"
# Remove orphaned git lock if it exists on boot
[ -f "$DIR/.git/index.lock" ] && rm -f $DIR/.git/index.lock
@@ -83,30 +114,37 @@ function launch {
fi
fi
fi
sp_launch_timing "overlay_check_done"
# handle pythonpath
ln -sfn $(pwd) /data/pythonpath
export BASEDIR="$DIR"
export PYTHONPATH="$DIR/starpilot/third_party:$PWD"
sp_launch_timing "pythonpath_done"
# hardware specific init
if [ -f /AGNOS ]; then
agnos_init
fi
sp_launch_timing "hardware_init_done"
# write tmux scrollback to a file
tmux capture-pane -pq -S-1000 > /tmp/launch_log
sp_launch_timing "capture_launch_log_done"
# start manager
cd system/manager
sp_launch_timing "launch_param_migrations_start"
if ! python3 ./launch_param_migrations.py; then
echo "Launch param migrations failed; continuing boot."
fi
sp_launch_timing "launch_param_migrations_done"
# Bootstrap runtime (e.g. /usr/comma after reset/uninstall) must go straight
# to manager/setup flow. Do not run StarPilot prebuilt checks/builds here.
if [ "$DIR" = "/usr/comma" ] || [ ! -d "$DIR/.git" ]; then
sp_launch_timing "bootstrap_manager_start"
./manager.py
while true; do sleep 1; done
fi
@@ -114,15 +152,35 @@ function launch {
function prebuilt_runtime_compatible {
python3 - <<'PY'
import importlib
import os
from pathlib import Path
import sys
import time
start = time.monotonic()
last = start
log_path = os.environ.get("SP_BOOT_TIMING_LOG")
def emit(line):
print(line, flush=True)
if log_path:
try:
with open(log_path, "a") as f:
f.write(line + "\n")
except OSError:
pass
def log_step(label):
global last
now = time.monotonic()
emit(f"SP_BOOT_TIMING prebuilt_compat {label} +{now - last:.3f}s total={now - start:.3f}s")
last = now
mods = [
"openpilot.common.params_pyx",
"msgq.ipc_pyx",
"msgq.visionipc.visionipc_pyx",
"openpilot.common.transformations.transformations",
"openpilot.selfdrive.modeld.models.commonmodel_pyx",
"openpilot.selfdrive.pandad.pandad_api_impl",
"openpilot.selfdrive.controls.lib.lateral_mpc_lib.c_generated_code.acados_ocp_solver_pyx",
"openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.c_generated_code.acados_ocp_solver_pyx",
@@ -134,15 +192,15 @@ for mod in mods:
except Exception as e:
print(f"Prebuilt compatibility failure in {mod}: {e}", file=sys.stderr)
raise
log_step(f"import:{mod}")
repo_root = Path.cwd().parents[1]
required_files = [
repo_root / "selfdrive/modeld/models/driving_vision_metadata.pkl",
repo_root / "selfdrive/modeld/models/driving_policy_metadata.pkl",
repo_root / "selfdrive/modeld/models/driving_vision_tinygrad.pkl",
repo_root / "selfdrive/modeld/models/driving_policy_tinygrad.pkl",
repo_root / "selfdrive/modeld/models/driving_tinygrad.pkl",
repo_root / "selfdrive/modeld/models/dmonitoring_model_metadata.pkl",
repo_root / "selfdrive/modeld/models/dmonitoring_model_tinygrad.pkl",
repo_root / "selfdrive/modeld/models/dm_warp_1928x1208_tinygrad.pkl",
repo_root / "selfdrive/modeld/models/dm_warp_1344x760_tinygrad.pkl",
repo_root / "selfdrive/pandad/pandad_api_impl.so",
repo_root / "selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/acados_ocp_solver_pyx.so",
repo_root / "selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/libacados_ocp_solver_lat.so",
@@ -154,6 +212,7 @@ required_files = [
for path in required_files:
if not path.is_file():
raise FileNotFoundError(f"Missing prebuilt runtime artifact: {path}")
log_step("required_files")
PY
}
@@ -162,14 +221,19 @@ PY
USE_PREBUILT=$(tr -d '\n' < /data/params/d/UsePrebuilt)
fi
sp_launch_timing "prebuilt_decision_done"
if [ "$USE_PREBUILT" = "1" ] && [ -f $DIR/prebuilt ] && ! prebuilt_runtime_compatible; then
echo "Prebuilt runtime artifacts are incompatible on this device; rebuilding locally."
USE_PREBUILT=0
fi
sp_launch_timing "prebuilt_compat_done"
if [ "$USE_PREBUILT" != "1" ] || [ ! -f $DIR/prebuilt ]; then
sp_launch_timing "build_start"
./build.py
sp_launch_timing "build_done"
fi
sp_launch_timing "manager_start"
./manager.py
# if broken, keep on screen error
+5 -1
View File
@@ -21,7 +21,11 @@ fi
export QCOM_PRIORITY=12
if [ -z "$AGNOS_VERSION" ]; then
export AGNOS_VERSION="12.8.16"
export AGNOS_VERSION="12.8.28"
fi
if [ -z "$AGNOS_ACCEPTED_VERSIONS" ]; then
export AGNOS_ACCEPTED_VERSIONS="$AGNOS_VERSION"
fi
export STAGING_ROOT="/data/safe_staging"
+46 -19
View File
@@ -34,9 +34,11 @@ import requests
import zstandard
from cereal import log
from openpilot.tools.lib.auth_config import DEFAULT_API_HOST, KONIK_API_HOST, get_token, normalize_api_host
API_HOST = os.getenv("COMMA_API_HOST", "https://api.commadotai.com").rstrip("/")
API_HOST = normalize_api_host(os.getenv("COMMA_API_HOST") or os.getenv("API_HOST") or DEFAULT_API_HOST)
API_HOSTS = [API_HOST] if os.getenv("COMMA_API_HOST") or os.getenv("API_HOST") else [DEFAULT_API_HOST, KONIK_API_HOST]
ROUTE_ID_RE = re.compile(r"([0-9a-f]{16})/([^/]+)")
@@ -146,12 +148,12 @@ def parse_route_id(raw: str) -> RouteId:
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_url(route: RouteId, api_host: str) -> 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 route_files_url(route: RouteId, api_host: str) -> str:
return f"{api_host}/v1/route/{quote(route.canonical_name, safe='')}/files"
def format_segments(segments: list[int]) -> str:
@@ -184,8 +186,13 @@ 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)
def api_headers(api_host: str) -> dict[str, str] | None:
token = get_token(api_host)
return {"Authorization": f"JWT {token}"} if token else None
def fetch_json(session: requests.Session, url: str, timeout: float, api_host: str | None = None) -> Any:
response = session.get(url, timeout=timeout, allow_redirects=True, headers=api_headers(api_host) if api_host else None)
response.raise_for_status()
return response.json()
@@ -312,24 +319,42 @@ def filename_from_url(url: str) -> str:
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
route_meta = None
files_payload = None
api_host = None
not_found_hosts = []
for candidate_host in API_HOSTS:
try:
route_meta = fetch_json(session, route_url(route, candidate_host), timeout, candidate_host)
except requests.HTTPError as exc:
status_code = exc.response.status_code if exc.response is not None else "unknown"
if status_code == 404 and len(API_HOSTS) > 1:
not_found_hosts.append(candidate_host)
continue
raise ValidationError(route, [f"route is not publicly accessible from {candidate_host} (HTTP {status_code})."]) from exc
try:
files_payload = fetch_json(session, route_files_url(route, candidate_host), timeout, candidate_host)
except requests.HTTPError as exc:
status_code = exc.response.status_code if exc.response is not None else "unknown"
if status_code == 404 and len(API_HOSTS) > 1:
not_found_hosts.append(candidate_host)
continue
raise ValidationError(route, [f"public route files could not be fetched from {candidate_host} (HTTP {status_code})."]) from exc
api_host = candidate_host
break
if route_meta is None or files_payload is None or api_host is None:
raise ValidationError(route, [f"route was not found on: {', '.join(not_found_hosts) or ', '.join(API_HOSTS)}."])
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."])
raise ValidationError(route, ["could not determine any route segments from the route API."])
failures: list[str] = []
stream_urls: dict[str, list[Any]] = {}
@@ -384,6 +409,7 @@ def validate_route(route: RouteId, session: requests.Session, timeout: float) ->
raise ValidationError(route, failures)
return {
"api_host": api_host,
"route_meta": route_meta,
"files_payload": files_payload,
"expected_segments": expected_segments,
@@ -532,6 +558,7 @@ 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" route API: {validation['api_host']}")
print(f" public route: yes")
print(f" segments: {validation['expected_segments']}")
print(f" VisionSpeedLimitDetection: {params.get('VisionSpeedLimitDetection', '')}")
+2 -2
View File
@@ -32,10 +32,10 @@ nav:
- What is a car port?: car-porting/what-is-a-car-port.md
- Porting a car brand: car-porting/brand-port.md
- Porting a car model: car-porting/model-port.md
- Contributing:
- Contribute:
- Contributing Guide: contributing/contribute.md
- Roadmap: contributing/roadmap.md
#- Architecture: contributing/architecture.md
- Contributing Guide →: https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md
- Links:
- Blog →: https://blog.comma.ai
- Bounties →: https://comma.ai/bounties
@@ -61,6 +61,7 @@ class TestVisionIpc:
recv_buf = self.client.recv()
assert recv_buf is not None
assert recv_buf.data.view('<i4')[0] == 1234
assert recv_buf.frame_id == 1337
assert self.client.frame_id == 1337
del self.client
del self.server
+1
View File
@@ -30,6 +30,7 @@ cdef extern from "msgq/visionipc/visionbuf.h":
size_t idx
cl_mem buf_cl
void set_frame_id(uint64_t id)
uint64_t get_frame_id()
cdef extern from "msgq/visionipc/visionipc.h":
struct VisionIpcBufExtra:
@@ -67,6 +67,10 @@ cdef class VisionBuf:
def fd(self):
return self.buf.fd
@property
def frame_id(self):
return self.buf.get_frame_id()
cdef class VisionIpcServer:
cdef cppVisionIpcServer * server
+1 -1
View File
@@ -106,7 +106,7 @@ def make_tester_present_msg(addr, bus, subaddr=None, suppress_response=False):
return CanData(addr, bytes(dat), bus)
def get_safety_config(safety_model: structs.CarParams.SafetyModel, safety_param: int = None) -> structs.CarParams.SafetyConfig:
def get_safety_config(safety_model, safety_param: int = None) -> structs.CarParams.SafetyConfig:
ret = structs.CarParams.SafetyConfig()
ret.safetyModel = safety_model
if safety_param is not None:
-6
View File
@@ -205,11 +205,6 @@ 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;
@@ -640,7 +635,6 @@ struct CarParams {
fcaGiorgio @32;
rivian @33;
volkswagenMeb @34;
teslaPreap @35;
}
enum SteerControlType {
+3
View File
@@ -334,6 +334,9 @@ def get_car(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multip
CP.carFw = car_fw
CP.fingerprintSource = source
CP.fuzzyFingerprint = not exact_match
post_fingerprint_params = getattr(CarInterface, "apply_post_fingerprint_params", None)
if post_fingerprint_params is not None:
post_fingerprint_params(CP, candidate, fingerprints, car_fw)
FPCP: StarPilotCarParams = CarInterface.get_starpilot_params(candidate, fingerprints, car_fw, CP, starpilot_toggles)
@@ -2,9 +2,28 @@ from opendbc.can import CANPacker
from opendbc.car import Bus, DT_CTRL
from opendbc.car.lateral import apply_meas_steer_torque_limits
from opendbc.car.chrysler import chryslercan
from opendbc.car.chrysler.values import RAM_CARS, RAM_DT, CarControllerParams, ChryslerFlags
from opendbc.car.chrysler.values import JEEPS, RAM_CARS, RAM_DT, CarControllerParams, ChryslerFlags, ChryslerSafetyFlags, ChryslerStarPilotFlags
from opendbc.car.interfaces import CarControllerBase
JEEP_BRAKE_HOLD_DEFAULT_DECEL = -2.0
JEEP_BRAKE_HOLD_MIN_DECEL = -0.5
JEEP_BRAKE_HOLD_MAX_DECEL = -3.0
def clip_jeep_brake_hold_decel(decel: float) -> float:
return max(JEEP_BRAKE_HOLD_MAX_DECEL, min(JEEP_BRAKE_HOLD_MIN_DECEL, float(decel)))
def supports_jeep_brake_hold(CP, brake_hold_enabled: bool) -> bool:
safety_cfg = getattr(CP, "safetyConfigs", ())
safety_param = safety_cfg[0].safetyParam if safety_cfg else 0
return (
brake_hold_enabled and
getattr(CP, "pcmCruise", False) and
CP.carFingerprint in JEEPS and
bool(safety_param & ChryslerSafetyFlags.JEEP_BRAKE_HOLD.value)
)
class CarController(CarControllerBase):
def __init__(self, dbc_names, CP):
@@ -18,6 +37,8 @@ class CarController(CarControllerBase):
self.packer = CANPacker(dbc_names[Bus.pt])
self.params = CarControllerParams(CP)
self.jeep_brake_hold_decel = JEEP_BRAKE_HOLD_DEFAULT_DECEL
self.last_das_3_counter = -1
def update(self, CC, CS, now_nanos, starpilot_toggles):
can_sends = []
@@ -50,7 +71,9 @@ class CarController(CarControllerBase):
# TODO: can we make this more sane? why is it different for all the cars?
lkas_control_bit = self.lkas_control_bit_prev
if self.CP.carFingerprint in RAM_DT:
if self.FPCP is not None and self.FPCP.flags & ChryslerStarPilotFlags.NO_MIN_STEERING_SPEED:
lkas_control_bit = CC.latActive
elif self.CP.carFingerprint in RAM_DT:
if self.CP.minEnableSpeed <= CS.out.vEgo <= self.CP.minEnableSpeed + 0.5:
lkas_control_bit = True
if (self.CP.minEnableSpeed >= 14.5) and (CS.out.gearShifter != 2):
@@ -80,6 +103,11 @@ class CarController(CarControllerBase):
can_sends.append(chryslercan.create_lkas_command(self.packer, self.CP, int(apply_torque), lkas_control_bit))
if supports_jeep_brake_hold(self.CP, getattr(starpilot_toggles, "jeep_brake_hold", False)):
self.update_jeep_brake_hold(CC, CS, can_sends)
elif getattr(CS, "brake_hold", False):
CS.brake_hold = False
self.frame += 1
new_actuators = CC.actuators.as_builder()
@@ -87,3 +115,43 @@ class CarController(CarControllerBase):
new_actuators.torqueOutputCan = self.apply_torque_last
return new_actuators, can_sends
def update_jeep_brake_hold(self, CC, CS, can_sends):
if not getattr(CS, "das_3", None):
CS.brake_hold = False
return
counter_changed = CS.das_3.get("COUNTER") != self.last_das_3_counter
self.last_das_3_counter = CS.das_3.get("COUNTER")
if not CS.brake_hold and CS.cruise_active_actual and CS.acc_decelerating and CS.out.standstill:
CS.brake_hold = True
self.jeep_brake_hold_decel = JEEP_BRAKE_HOLD_DEFAULT_DECEL
driver_intervened = (
CC.cruiseControl.cancel or
CS.out.gasPressed or
CS.out.brakePressed or
not CS.forward_gear or
not CS.out.standstill
)
if CS.brake_hold and driver_intervened:
CS.brake_hold = False
return
if not CS.brake_hold:
return
if CS.cruise_active_actual:
self.jeep_brake_hold_decel = clip_jeep_brake_hold_decel(
min(self.jeep_brake_hold_decel, CS.das_3.get("ACC_DECEL", JEEP_BRAKE_HOLD_DEFAULT_DECEL))
)
return
counter_offset = 2 if counter_changed else 3
can_sends.append(chryslercan.create_das_3_command(self.packer, counter_offset, self.jeep_brake_hold_decel, CS.das_3))
if self.frame % 10 == 0:
can_sends.append(chryslercan.create_cruise_buttons(
self.packer, CS.button_counter + 1, 0, CS.button_message, resume=True
))
+13 -1
View File
@@ -1,7 +1,7 @@
from cereal import custom
from opendbc.can import CANDefine, CANParser
from opendbc.car import Bus, create_button_events, structs
from opendbc.car.chrysler.values import DBC, STEER_THRESHOLD, RAM_CARS, ChryslerStarPilotFlags
from opendbc.car.chrysler.values import DBC, JEEPS, STEER_THRESHOLD, RAM_CARS, ChryslerStarPilotFlags
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.interfaces import CarStateBase
@@ -29,6 +29,11 @@ class CarState(CarStateBase):
self.button_message = "CRUISE_BUTTONS_ALT" if FPCP.flags & ChryslerStarPilotFlags.RAM_HD_ALT_BUTTONS else "CRUISE_BUTTONS"
self.lkas_button = 0
self.brake_hold = False
self.cruise_active_actual = False
self.forward_gear = False
self.acc_decelerating = False
self.das_3 = {}
@staticmethod
def get_lkas_button(pt_signals, is_ram: bool) -> bool:
@@ -93,6 +98,13 @@ class CarState(CarStateBase):
ret.cruiseState.standstill = cp_cruise.vl["DAS_3"]["ACC_STANDSTILL"] == 1
ret.accFaulted = cp_cruise.vl["DAS_3"]["ACC_FAULTED"] != 0
if self.CP.carFingerprint in JEEPS:
self.forward_gear = ret.gearShifter == structs.CarState.GearShifter.drive
self.cruise_active_actual = ret.cruiseState.enabled
self.acc_decelerating = cp_cruise.vl["DAS_3"]["ACC_DECEL"] < -0.5
self.das_3 = dict(cp_cruise.vl["DAS_3"])
ret.brakeHoldActive = self.brake_hold
if self.CP.carFingerprint in RAM_CARS:
# Auto High Beam isn't Located in this message on chrysler or jeep currently located in 729 message
self.auto_high_beam = cp_cam.vl["DAS_6"]['AUTO_HIGH_BEAM_ON']
@@ -73,6 +73,21 @@ def create_cruise_buttons(packer, frame, bus, button_message, cancel=False, resu
return packer.make_can_msg(button_message, bus, values)
def create_das_3_command(packer, counter_offset, brake_decel, das_3):
values = das_3.copy()
values["ACC_AVAILABLE"] = 1
values["ACC_ACTIVE"] = 1
values["ACC_GO"] = 0
values["ACC_STANDSTILL"] = 0
values["ACC_DECEL_REQ"] = 1
values["ACC_DECEL"] = brake_decel
values["ACC_BRK_PREP"] = 0
values["ENGINE_TORQUE_REQUEST_MAX"] = 0
values["GR_MAX_REQ"] = 2
values["COUNTER"] = (das_3["COUNTER"] + counter_offset) % 0x10
return packer.make_can_msg("DAS_3", 0, values)
def chrysler_checksum(address: int, sig, d: bytearray) -> int:
checksum = 0xFF
for j in range(len(d) - 1):
@@ -3,7 +3,7 @@ from opendbc.car import get_safety_config, structs
from opendbc.car.chrysler.carcontroller import CarController
from opendbc.car.chrysler.carstate import CarState
from opendbc.car.chrysler.radar_interface import RadarInterface
from opendbc.car.chrysler.values import CAR, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags, ChryslerSafetyFlags
from opendbc.car.chrysler.values import CAR, JEEPS, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags, ChryslerSafetyFlags
from opendbc.car.interfaces import CarInterfaceBase
@@ -28,6 +28,8 @@ class CarInterface(CarInterfaceBase):
ret.safetyConfigs[0].safetyParam |= ChryslerSafetyFlags.RAM_HD.value
elif candidate in RAM_DT:
ret.safetyConfigs[0].safetyParam |= ChryslerSafetyFlags.RAM_DT.value
elif candidate in JEEPS:
ret.safetyConfigs[0].safetyParam |= ChryslerSafetyFlags.JEEP_BRAKE_HOLD.value
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
if candidate not in RAM_CARS:
@@ -12,6 +12,7 @@ Ecu = CarParams.Ecu
class ChryslerSafetyFlags(IntFlag):
RAM_DT = 1
RAM_HD = 2
JEEP_BRAKE_HOLD = 4
class ChryslerFlags(IntFlag):
@@ -21,6 +22,7 @@ class ChryslerFlags(IntFlag):
class ChryslerStarPilotFlags(IntFlag):
RAM_HD_ALT_BUTTONS = 1
NO_MIN_STEERING_SPEED = 2
@dataclass
@@ -139,6 +141,7 @@ STEER_THRESHOLD = 120
RAM_DT = {CAR.RAM_1500_5TH_GEN, }
RAM_HD = {CAR.RAM_HD_5TH_GEN, }
RAM_CARS = RAM_DT | RAM_HD
JEEPS = {CAR.JEEP_GRAND_CHEROKEE, CAR.JEEP_GRAND_CHEROKEE_2019}
CHRYSLER_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
+2
View File
@@ -258,6 +258,8 @@ MIGRATION = {
"KIA EV6 2025": HYUNDAI.KIA_EV6_2025,
"KIA EV9 2025": HYUNDAI.KIA_EV9,
"KIA CARNIVAL 4TH GEN": HYUNDAI.KIA_CARNIVAL_4TH_GEN,
"KIA CARNIVAL 2025": HYUNDAI.KIA_CARNIVAL_2025,
"KIA CARNIVAL HYBRID 4TH GEN": HYUNDAI.KIA_CARNIVAL_HEV_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,
+59 -27
View File
@@ -16,6 +16,11 @@ AVERAGE_ROAD_ROLL = 0.06 # ~3.4 degrees, 6% superelevation. higher actual roll
MAX_LATERAL_ACCEL = ISO_LATERAL_ACCEL - (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL) # ~2.4 m/s^2
def apply_ford_angle(desired_angle_deg: float, current_angle_deg: float) -> float:
relative_angle = desired_angle_deg - current_angle_deg
return float(np.clip(relative_angle, -5.8, 5.8))
def anti_overshoot(apply_curvature, apply_curvature_last, v_ego):
diff = 0.1
tau = 5 # 5s smooths over the overshoot
@@ -65,6 +70,7 @@ class CarController(CarControllerBase):
self.CAN = fordcan.CanBus(CP)
self.apply_curvature_last = 0
self.apply_angle_last = 0
self.anti_overshoot_curvature_last = 0
self.accel = 0.0
self.gas = 0.0
@@ -98,38 +104,62 @@ class CarController(CarControllerBase):
can_sends.append(fordcan.create_button_msg(self.packer, self.CAN.camera, CS.buttons_stock_values, tja_toggle=True))
### lateral control ###
# send steer msg at 20Hz
if (self.frame % CarControllerParams.STEER_STEP) == 0:
# Bronco and some other cars consistently overshoot curv requests
# Apply some deadzone + smoothing convergence to avoid oscillations
if self.CP.carFingerprint in (CAR.FORD_BRONCO_SPORT_MK1, CAR.FORD_F_150_MK14):
self.anti_overshoot_curvature_last = anti_overshoot(actuators.curvature, self.anti_overshoot_curvature_last, CS.out.vEgoRaw)
apply_curvature = self.anti_overshoot_curvature_last
if self.CP.flags & FordFlags.LKA_STEERING:
lka_active = CC.latActive and CS.lkas_available
if lka_active:
self.apply_angle_last = apply_ford_angle(actuators.steeringAngleDeg, CS.out.steeringAngleDeg)
current_curvature = -CS.out.yawRate / max(CS.out.vEgoRaw, 0.1)
self.apply_curvature_last = apply_ford_curvature_limits(actuators.curvature, self.apply_curvature_last, current_curvature,
CS.out.vEgoRaw, 0., True, self.CP)
else:
apply_curvature = actuators.curvature
self.apply_angle_last = 0.
self.apply_curvature_last = 0.
# apply rate limits, curvature error limit, and clip to signal range
current_curvature = -CS.out.yawRate / max(CS.out.vEgoRaw, 0.1)
# Keep the stock LMC heartbeat present while steering through Lane_Assist_Data1.
if (self.frame % CarControllerParams.STEER_STEP) == 0:
can_sends.append(fordcan.create_lat_ctl_msg(self.packer, self.CAN, False, 0., 0., 0., 0.,
stock_lmc=CS.lateral_motion_control))
self.apply_curvature_last = apply_ford_curvature_limits(apply_curvature, self.apply_curvature_last, current_curvature,
CS.out.vEgoRaw, 0., CC.latActive, self.CP)
if (self.frame % CarControllerParams.LKA_STEP) == 0:
direction = 0
if lka_active:
direction = 2 if CS.out.steeringAngleDeg > 0 else 4
ramp_type = 1 if abs(self.apply_angle_last) >= 5 else 0
can_sends.append(fordcan.create_lka_msg(self.packer, self.CAN, active=lka_active, apply_angle=self.apply_angle_last,
direction=direction, ramp_type=ramp_type, curvature=-self.apply_curvature_last))
else:
# send steer msg at 20Hz
if (self.frame % CarControllerParams.STEER_STEP) == 0:
# Bronco and some other cars consistently overshoot curv requests
# Apply some deadzone + smoothing convergence to avoid oscillations
if self.CP.carFingerprint in (CAR.FORD_BRONCO_SPORT_MK1, CAR.FORD_F_150_MK14):
self.anti_overshoot_curvature_last = anti_overshoot(actuators.curvature, self.anti_overshoot_curvature_last, CS.out.vEgoRaw)
apply_curvature = self.anti_overshoot_curvature_last
else:
apply_curvature = actuators.curvature
if self.CP.flags & FordFlags.CANFD:
# TODO: extended mode
# Ford uses four individual signals to dictate how to drive to the car. Curvature alone (limited to 0.02m/s^2)
# can actuate the steering for a large portion of any lateral movements. However, in order to get further control on
# steer actuation, the other three signals are necessary. Ford controls vehicles differently than most other makes.
# A detailed explanation on ford control can be found here:
# https://www.f150gen14.com/forum/threads/introducing-bluepilot-a-ford-specific-fork-for-comma3x-openpilot.24241/#post-457706
mode = 1 if CC.latActive else 0
counter = (self.frame // CarControllerParams.STEER_STEP) % 0x10
can_sends.append(fordcan.create_lat_ctl2_msg(self.packer, self.CAN, mode, 0., 0., -self.apply_curvature_last, 0., counter))
else:
can_sends.append(fordcan.create_lat_ctl_msg(self.packer, self.CAN, CC.latActive, 0., 0., -self.apply_curvature_last, 0.))
# apply rate limits, curvature error limit, and clip to signal range
current_curvature = -CS.out.yawRate / max(CS.out.vEgoRaw, 0.1)
# send lka msg at 33Hz
if (self.frame % CarControllerParams.LKA_STEP) == 0:
can_sends.append(fordcan.create_lka_msg(self.packer, self.CAN))
self.apply_curvature_last = apply_ford_curvature_limits(apply_curvature, self.apply_curvature_last, current_curvature,
CS.out.vEgoRaw, 0., CC.latActive, self.CP)
if self.CP.flags & FordFlags.CANFD:
# TODO: extended mode
# Ford uses four individual signals to dictate how to drive to the car. Curvature alone (limited to 0.02m/s^2)
# can actuate the steering for a large portion of any lateral movements. However, in order to get further control on
# steer actuation, the other three signals are necessary. Ford controls vehicles differently than most other makes.
# A detailed explanation on ford control can be found here:
# https://www.f150gen14.com/forum/threads/introducing-bluepilot-a-ford-specific-fork-for-comma3x-openpilot.24241/#post-457706
mode = 1 if CC.latActive else 0
counter = (self.frame // CarControllerParams.STEER_STEP) % 0x10
can_sends.append(fordcan.create_lat_ctl2_msg(self.packer, self.CAN, mode, 0., 0., -self.apply_curvature_last, 0., counter))
else:
can_sends.append(fordcan.create_lat_ctl_msg(self.packer, self.CAN, CC.latActive, 0., 0., -self.apply_curvature_last, 0.))
# send lka msg at 33Hz
if (self.frame % CarControllerParams.LKA_STEP) == 0:
can_sends.append(fordcan.create_lka_msg(self.packer, self.CAN))
### longitudinal control ###
# send acc msg at 50Hz
@@ -195,6 +225,8 @@ class CarController(CarControllerBase):
self.lead_distance_bars_last = hud_control.leadDistanceBars
new_actuators = actuators.as_builder()
if self.CP.flags & FordFlags.LKA_STEERING:
new_actuators.steeringAngleDeg = self.apply_angle_last + CS.out.steeringAngleDeg
new_actuators.curvature = self.apply_curvature_last
new_actuators.accel = self.accel
new_actuators.gas = self.gas
+11
View File
@@ -20,6 +20,8 @@ class CarState(CarStateBase):
self.distance_button = 0
self.lc_button = 0
self.lkas_available = False
self.lateral_motion_control = None
def update(self, can_parsers, starpilot_toggles) -> structs.CarState:
cp = can_parsers[Bus.pt]
@@ -108,6 +110,15 @@ class CarState(CarStateBase):
# Stock values from IPMA so that we can retain some stock functionality
self.acc_tja_status_stock_values = cp_cam.vl["ACCDATA_3"]
self.lkas_status_stock_values = cp_cam.vl["IPMA_Data"]
if self.CP.flags & FordFlags.LKA_STEERING:
try:
self.lkas_available = cp.vl["Lane_Assist_Data3_FD1"]["LaActAvail_D_Actl"] == 3
except KeyError:
self.lkas_available = False
try:
self.lateral_motion_control = cp_cam.vl["LateralMotionControl"]
except KeyError:
self.lateral_motion_control = None
ret.buttonEvents = [
*create_button_events(self.distance_button, prev_distance_button, {1: ButtonType.gapAdjustCruise}),
@@ -106,10 +106,12 @@ FW_VERSIONS = {
},
CAR.FORD_F_150_MK14: {
(Ecu.eps, 0x730, None): [
b'ML3V-14D003-BA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'ML3V-14D003-BC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'ML3V-14D003-BD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x760, None): [
b'ML34-2D053-AJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'NL34-2D053-CA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PL34-2D053-CA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PL34-2D053-CC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
@@ -117,6 +119,7 @@ FW_VERSIONS = {
b'PL3V-2D053-BB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x764, None): [
b'ML3T-14D049-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'ML3T-14D049-AK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'ML3T-14D049-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
@@ -124,6 +127,7 @@ FW_VERSIONS = {
b'ML3T-14H102-ABR\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'ML3T-14H102-ABS\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'ML3T-14H102-ABT\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'ML3T-14H102-ACA\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PJ6T-14H102-ABJ\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PJ6T-14H102-ABS\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'RJ6T-14H102-ACJ\x00\x00\x00\x00\x00\x00\x00\x00\x00',
@@ -211,6 +215,7 @@ FW_VERSIONS = {
b'PB3C-2D053-ZD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PB3C-2D053-ZG\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'PB3C-2D053-ZJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
b'RB3C-2D053-AK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x764, None): [
b'ML3T-14D049-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
@@ -220,4 +225,18 @@ FW_VERSIONS = {
b'RJ6T-14H102-BBB\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
},
CAR.FORD_TRANSIT_MK5: {
(Ecu.eps, 0x730, None): [
b'KK21-14D003-AM\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x760, None): [
b'NK41-2D053-DF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x764, None): [
b'PC4T-14D049-AA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x706, None): [
b'NK3T-14F397-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
],
},
}
+54 -19
View File
@@ -1,3 +1,5 @@
import math
from opendbc.car import CanBusBase, structs
HUDControl = structs.CarControl.HUDControl
@@ -33,20 +35,40 @@ def calculate_lat_ctl2_checksum(mode: int, counter: int, dat: bytearray) -> int:
return 0xFF - (checksum & 0xFF)
def create_lka_msg(packer, CAN: CanBus):
def create_lka_msg(packer, CAN: CanBus, active: bool = False, apply_angle: float = 0.0,
direction: int = 0, ramp_type: int = 0, curvature: float = 0.0):
"""
Creates an empty CAN message for the Ford LKA Command.
Creates a CAN message for the Ford LKA Command.
This command can apply "Lane Keeping Aid" maneuvers, which are subject to the PSCM lockout.
On LKA-steering platforms, this command applies Lane Keeping Aid maneuvers through the PSCM.
Frequency is 33Hz.
"""
return packer.make_can_msg("Lane_Assist_Data1", CAN.main, {})
if active:
mrad = math.radians(max(-5.8, min(5.8, apply_angle))) * 1000.0
mrad = max(-102.4, min(102.3, mrad))
curvature = max(-0.01023, min(0.01023, curvature))
else:
mrad = 0.0
direction = 0
ramp_type = 0
curvature = 0.0
values = {
"LkaDrvOvrrd_D_Rq": 0,
"LkaActvStats_D2_Req": direction if active else 0,
"LaRefAng_No_Req": mrad,
"LaRampType_B_Req": ramp_type,
"LaCurvature_No_Calc": curvature,
"LdwActvStats_D_Req": 0,
"LdwActvIntns_D_Req": 3,
}
return packer.make_can_msg("Lane_Assist_Data1", CAN.main, values)
def create_lat_ctl_msg(packer, CAN: CanBus, lat_active: bool, path_offset: float, path_angle: float, curvature: float,
curvature_rate: float):
curvature_rate: float, stock_lmc=None):
"""
Creates a CAN message for the Ford TJA/LCA Command.
@@ -68,20 +90,33 @@ def create_lat_ctl_msg(packer, CAN: CanBus, lat_active: bool, path_offset: float
Frequency is 20Hz.
"""
values = {
"LatCtlRng_L_Max": 0, # Unknown [0|126] meter
"HandsOffCnfm_B_Rq": 0, # Unknown: 0=Inactive, 1=Active [0|1]
"LatCtl_D_Rq": 1 if lat_active else 0, # Mode: 0=None, 1=ContinuousPathFollowing, 2=InterventionLeft,
# 3=InterventionRight, 4-7=NotUsed [0|7]
"LatCtlRampType_D_Rq": 0, # Ramp speed: 0=Slow, 1=Medium, 2=Fast, 3=Immediate [0|3]
# Makes no difference with curvature control
"LatCtlPrecision_D_Rq": 1, # Precision: 0=Comfortable, 1=Precise, 2/3=NotUsed [0|3]
# The stock system always uses comfortable
"LatCtlPathOffst_L_Actl": path_offset, # Path offset [-5.12|5.11] meter
"LatCtlPath_An_Actl": path_angle, # Path angle [-0.5|0.5235] radians
"LatCtlCurv_NoRate_Actl": curvature_rate, # Curvature rate [-0.001024|0.00102375] 1/meter^2
"LatCtlCurv_No_Actl": curvature, # Curvature [-0.02|0.02094] 1/meter
}
if stock_lmc is not None:
values = {
"LatCtlRng_L_Max": stock_lmc["LatCtlRng_L_Max"],
"HandsOffCnfm_B_Rq": stock_lmc["HandsOffCnfm_B_Rq"],
"LatCtl_D_Rq": 0,
"LatCtlRampType_D_Rq": stock_lmc["LatCtlRampType_D_Rq"],
"LatCtlPrecision_D_Rq": stock_lmc["LatCtlPrecision_D_Rq"],
"LatCtlPathOffst_L_Actl": stock_lmc["LatCtlPathOffst_L_Actl"],
"LatCtlPath_An_Actl": stock_lmc["LatCtlPath_An_Actl"],
"LatCtlCurv_NoRate_Actl": stock_lmc["LatCtlCurv_NoRate_Actl"],
"LatCtlCurv_No_Actl": stock_lmc["LatCtlCurv_No_Actl"],
}
else:
values = {
"LatCtlRng_L_Max": 0, # Unknown [0|126] meter
"HandsOffCnfm_B_Rq": 0, # Unknown: 0=Inactive, 1=Active [0|1]
"LatCtl_D_Rq": 1 if lat_active else 0, # Mode: 0=None, 1=ContinuousPathFollowing, 2=InterventionLeft,
# 3=InterventionRight, 4-7=NotUsed [0|7]
"LatCtlRampType_D_Rq": 0, # Ramp speed: 0=Slow, 1=Medium, 2=Fast, 3=Immediate [0|3]
# Makes no difference with curvature control
"LatCtlPrecision_D_Rq": 1, # Precision: 0=Comfortable, 1=Precise, 2/3=NotUsed [0|3]
# The stock system always uses comfortable
"LatCtlPathOffst_L_Actl": path_offset, # Path offset [-5.12|5.11] meter
"LatCtlPath_An_Actl": path_angle, # Path angle [-0.5|0.5235] radians
"LatCtlCurv_NoRate_Actl": curvature_rate, # Curvature rate [-0.001024|0.00102375] 1/meter^2
"LatCtlCurv_No_Actl": curvature, # Curvature [-0.02|0.02094] 1/meter
}
return packer.make_can_msg("LateralMotionControl", CAN.main, values)
+3 -1
View File
@@ -31,7 +31,7 @@ class CarInterface(CarInterfaceBase):
ret.radarUnavailable = Bus.radar not in DBC[candidate]
ret.steerControlType = structs.CarParams.SteerControlType.angle
ret.steerActuatorDelay = 0.2
ret.steerActuatorDelay = 0.05 if ret.flags & FordFlags.LKA_STEERING else 0.2
ret.steerLimitTimer = 1.0
ret.steerAtStandstill = True
@@ -63,6 +63,8 @@ class CarInterface(CarInterfaceBase):
if fingerprint[CAN.camera].get(0x3d6) != 8 or fingerprint[CAN.camera].get(0x186) != 8:
carlog.error('dashcamOnly: SecOC is unsupported')
ret.dashcamOnly = True
elif ret.flags & FordFlags.LKA_STEERING:
ret.safetyConfigs[-1].safetyParam |= FordSafetyFlags.LKA_STEERING.value
else:
# Lock out if the car does not have needed lateral and longitudinal control APIs.
# Note that we also check CAN for adaptive cruise, but no known signal for LCA exists
+13
View File
@@ -46,11 +46,13 @@ class CarControllerParams:
class FordSafetyFlags(IntFlag):
LONG_CONTROL = 1
CANFD = 2
LKA_STEERING = 4
class FordFlags(IntFlag):
# Static flags
CANFD = 1
LKA_STEERING = 2
class RADAR:
@@ -111,6 +113,13 @@ class FordCANFDPlatformConfig(FordPlatformConfig):
self.flags |= FordFlags.CANFD
@dataclass
class FordLKASteeringPlatformConfig(FordPlatformConfig):
def init(self):
super().init()
self.flags |= FordFlags.LKA_STEERING
@dataclass
class FordF150LightningPlatform(FordCANFDPlatformConfig):
def init(self):
@@ -178,6 +187,10 @@ class CAR(Platforms):
[FordCarDocs("Ford Ranger 2024", "Adaptive Cruise Control with Lane Centering", setup_video="https://www.youtube.com/watch?v=2oJlXCKYOy0")],
CarSpecs(mass=2000, wheelbase=3.27, steerRatio=17.0),
)
FORD_TRANSIT_MK5 = FordLKASteeringPlatformConfig(
[FordCarDocs("Ford Transit 2025", "Co-Pilot360 Assist+")],
CarSpecs(mass=2068, wheelbase=3.302, steerRatio=16.7),
)
# FW response contains a combined software and part number
+563 -84
View File
@@ -6,15 +6,17 @@ from opendbc.car.lateral import apply_driver_steer_torque_limits
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,
ASCM_INT, CAMERA_ACC_CAR, CAR, CC_ONLY_CAR, CC_REGEN_PADDLE_CAR, DBC, EV_CAR, SDGM_CAR, AccState, CanBus, CarControllerParams,
CruiseButtons, GMFlags, GMSafetyFlags,
)
from opendbc.car.interfaces import CarControllerBase
from openpilot.common.pid import PIDController
from openpilot.common.params import Params, UnknownKeyName
from openpilot.starpilot.common.testing_grounds import testing_ground
VisualAlert = structs.CarControl.HUDControl.VisualAlert
NetworkLocation = structs.CarParams.NetworkLocation
TransmissionType = structs.CarParams.TransmissionType
LongCtrlState = structs.CarControl.Actuators.LongControlState
GearShifter = structs.CarState.GearShifter
@@ -28,14 +30,52 @@ AUTO_HOLD_VOLT_CARS = {
CAR.CHEVROLET_VOLT_ASCM,
CAR.CHEVROLET_VOLT_CAMERA,
}
AUTO_HOLD_DRIVE_GEARS = {
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
AUTO_HOLD_STOPPED_SPEED = 0.02
AUTO_HOLD_2019_MIN_BRAKE = 100
BOLT_ACC_PEDAL_FRICTION_RELEASE_FRAMES = 5
BOLT_PEDAL_LONG_ACCEL_LIMIT_BP = [0.0, 1.5, 4.0, 8.0, 15.0, 30.0]
BOLT_PEDAL_LONG_ACCEL_LIMIT_V = [-0.93, -1.28, -1.98, -2.58, -2.86, -2.95]
VOLT_ONE_PEDAL_DECEL_BP = [0.5 * CV.MPH_TO_MS, 6.0 * CV.MPH_TO_MS]
VOLT_ONE_PEDAL_DECEL_V = [-1.0, -1.1]
VOLT_ONE_PEDAL_REGEN_PADDLE_DECEL_V = [-1.5, -1.6]
VOLT_ONE_PEDAL_MAX_DECEL = min((*VOLT_ONE_PEDAL_DECEL_V, *VOLT_ONE_PEDAL_REGEN_PADDLE_DECEL_V)) - 0.5
VOLT_ONE_PEDAL_PID_NEG_LIMIT = -3.5
VOLT_ONE_PEDAL_SPEED_ERROR_FACTOR_BP = [1.5, 20.0]
VOLT_ONE_PEDAL_SPEED_ERROR_FACTOR_V = [0.4, 0.2]
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_SPEED_FACTOR_BP = [0.0, 10.0 * CV.MPH_TO_MS]
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_SPEED_FACTOR_V = [0.2, 1.0]
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_STEER_FACTOR_BP = [20.0, 120.0]
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_STEER_FACTOR_V = [1.0, 0.2]
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_UP = 0.8 * DT_CTRL * 4
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_DOWN = 0.8 * DT_CTRL * 4
VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_BP = [4.0, 8.0]
VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_V = [0.4, 1.0]
VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_INCLINE_V = [0.2, 1.0]
VOLT_ONE_PEDAL_LIFT_BRAKE_BP = [0.0, CarControllerParams.NEAR_STOP_BRAKE_PHASE, 2.0 * CV.MPH_TO_MS]
VOLT_ONE_PEDAL_LIFT_BRAKE_V = [AUTO_HOLD_MIN_BRAKE, AUTO_HOLD_MIN_BRAKE, 20.0]
VOLT_ONE_PEDAL_LIFT_BRAKE_FRAMES = 8
TRUCK_LONG_SMOOTH_CARS = {
CAR.CHEVROLET_SILVERADO,
CAR.CHEVROLET_SILVERADO_CC,
}
TRUCK_FRICTION_BRAKE_ENGAGE = 40
TRUCK_FRICTION_BRAKE_RELEASE = 8
TRUCK_FRICTION_BRAKE_IMMEDIATE_ACCEL = -0.85
ACC_DASHBOARD_ZERO_RESERVED_CARS = {
CAR.CHEVROLET_BLAZER,
CAR.CHEVROLET_EQUINOX,
CAR.CHEVROLET_SILVERADO,
CAR.CHEVROLET_TRAILBLAZER,
CAR.CHEVROLET_TRAX,
}
def get_stock_cc_active_for_cancel(CP, CS):
@@ -51,7 +91,13 @@ def use_interceptor_sng_launch(CP, CS, maneuver_mode=False):
launch_speed = max(CP.vEgoStarting, 0.3)
if maneuver_mode:
launch_speed = max(launch_speed, 2.0)
return CS.out.cruiseState.standstill and (CS.out.standstill or CS.out.vEgo < launch_speed)
near_stop = CS.out.standstill or CS.out.vEgo < launch_speed
if (
getattr(CP, "carFingerprint", None) == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL and
getattr(CP, "enableGasInterceptorDEPRECATED", False)
):
return near_stop
return CS.out.cruiseState.standstill and near_stop
def should_spoof_dash_speed(CP, starpilot_toggles):
@@ -77,6 +123,17 @@ 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_status_active(CP, CC):
if CC.enabled:
return True
return CP.carFingerprint == CAR.BUICK_LACROSSE_ASCM and CC.latActive
def get_acc_dashboard_always_one(CP):
return 0 if CP.carFingerprint in ACC_DASHBOARD_ZERO_RESERVED_CARS else 1
def get_acc_dashboard_fcw_alert(hud_alert, CS):
if hud_alert == VisualAlert.fcw:
return 0x3
@@ -92,29 +149,6 @@ def get_acc_dashboard_fcw_alert(hud_alert, CS):
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,
@@ -150,34 +184,162 @@ def get_adas_keepalive_step(CP, is_kaofui_car):
return None
def should_send_adas_status(CP, is_kaofui_car):
if CP.radarUnavailable:
return False
if not is_kaofui_car:
return True
if CP.carFingerprint in ASCM_INT:
return False
return CP.networkLocation != NetworkLocation.fwdCamera and CP.carFingerprint not in SDGM_CAR
def should_send_acc_2cd(CP):
return (
CP.networkLocation == NetworkLocation.fwdCamera and
CP.carFingerprint in CAMERA_ACC_CAR and
CP.carFingerprint not in (CC_ONLY_CAR | SDGM_CAR) and
not bool(getattr(CP, "flags", 0) & GMFlags.NO_CAMERA.value)
)
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 shape_truck_positive_accel(accel: float, v_ego: float, enabled: bool,
lead_visible: bool = False, set_speed_error: float = 0.0) -> float:
if not enabled or accel <= 0.0 or v_ego < 12.0:
return accel
low_scale = float(np.interp(v_ego, [12.0, 18.0, 25.0, 35.0], [0.93, 0.84, 0.76, 0.70]))
mid_scale = float(np.interp(v_ego, [12.0, 18.0, 25.0, 35.0], [0.97, 0.91, 0.85, 0.79]))
if lead_visible and set_speed_error > 0.0:
follow_relief = float(np.interp(set_speed_error, [0.0, 1.0, 2.5, 4.0, 6.0], [0.0, 0.04, 0.10, 0.18, 0.30]))
low_scale += (1.0 - low_scale) * follow_relief
mid_scale += (1.0 - mid_scale) * follow_relief
if accel <= 0.12:
return accel * low_scale
if accel <= 0.35:
return float(np.interp(accel, [0.12, 0.35], [0.12 * low_scale, 0.35 * mid_scale]))
if accel <= 0.65:
return float(np.interp(accel, [0.35, 0.65], [0.35 * mid_scale, 0.65]))
return accel
def shape_truck_friction_brake(apply_brake: int, accel_cmd: float, stopping: bool, active: bool) -> tuple[int, bool]:
if apply_brake <= 0:
return 0, False
# Preserve full brake response for stop control and meaningful deceleration.
if stopping or accel_cmd <= TRUCK_FRICTION_BRAKE_IMMEDIATE_ACCEL:
return apply_brake, True
if active:
if apply_brake <= TRUCK_FRICTION_BRAKE_RELEASE:
return 0, False
return apply_brake, True
if apply_brake >= TRUCK_FRICTION_BRAKE_ENGAGE:
return apply_brake, True
# Keep tiny corrections in the continuous gas/regen torque path. Switching
# to friction also forces max regen, which makes a small request perceptible.
return 0, False
def get_lka_steering_cmd_counter(next_counter: int, CS) -> int:
if getattr(CS, "loopback_lka_steering_cmd_updated", False):
return (getattr(CS, "loopback_lka_steering_cmd_counter", next_counter) + 1) % 4
if next_counter < 0 and getattr(CS, "loopback_lka_steering_cmd_ts_nanos", 0) == 0:
return (getattr(CS, "pt_lka_steering_cmd_counter", next_counter) + 1) % 4
return next_counter
def should_send_stock_long_cancel(cancel_counter: int, CS) -> bool:
cs_out = getattr(CS, "out", None)
return cancel_counter > CAMERA_CANCEL_DELAY_FRAMES and not bool(getattr(cs_out, "accFaulted", False))
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)
stock_hold_safety_ready = bool(safety_param & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value)
return (
auto_hold_enabled and
getattr(CP, "openpilotLongitudinalControl", False) 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:
def supports_volt_one_pedal(CP, one_pedal_enabled: bool):
safety_cfg = getattr(CP, "safetyConfigs", ())
safety_param = safety_cfg[0].safetyParam if safety_cfg else 0
stock_hold_safety_ready = bool(safety_param & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value)
return (
one_pedal_enabled and
getattr(CP, "openpilotLongitudinalControl", False) and
stock_hold_safety_ready and
getattr(CP, "transmissionType", None) == TransmissionType.direct and
CP.carFingerprint in AUTO_HOLD_VOLT_CARS
)
def estimate_auto_hold_brake(driver_brake: float, op_brake: float, CP=None) -> 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)))
min_brake = AUTO_HOLD_2019_MIN_BRAKE if getattr(CP, "carFingerprint", None) == CAR.CHEVROLET_VOLT_2019 else AUTO_HOLD_MIN_BRAKE
return int(round(np.clip(hold_brake, min_brake, AUTO_HOLD_MAX_BRAKE)))
def get_auto_hold_stop_threshold(CP, auto_hold_engaged: bool) -> float:
if auto_hold_engaged and getattr(CP, "carFingerprint", None) == CAR.CHEVROLET_VOLT_2019:
return CarControllerParams.NEAR_STOP_BRAKE_PHASE
return AUTO_HOLD_STOPPED_SPEED
def get_volt_one_pedal_target_decel(v_ego: float) -> float:
return float(np.interp(v_ego, VOLT_ONE_PEDAL_DECEL_BP, VOLT_ONE_PEDAL_DECEL_V))
def get_volt_one_pedal_lift_brake(v_ego: float) -> int:
if v_ego > VOLT_ONE_PEDAL_LIFT_BRAKE_BP[-1]:
return 0
return int(round(np.interp(v_ego, VOLT_ONE_PEDAL_LIFT_BRAKE_BP, VOLT_ONE_PEDAL_LIFT_BRAKE_V)))
def should_activate_volt_one_pedal(one_pedal_ready: bool, cruise_main: bool, long_active: bool,
gas_pressed: bool, brake_pressed: bool, regen_braking: bool,
single_pedal_mode: bool, gear_shifter, drive_time_s: float) -> bool:
# Volt rear wheel direction bits can falsely report reverse while stopping in L.
return (
one_pedal_ready and
cruise_main and
single_pedal_mode and
gear_shifter in AUTO_HOLD_DRIVE_GEARS and
drive_time_s >= AUTO_HOLD_MIN_DRIVE_TIME_S and
not long_active and
not gas_pressed and
not brake_pressed and
not regen_braking
)
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
brake_pressed: bool, gas_pressed: bool, standstill: bool, long_active: bool,
regen_braking: bool, v_ego: float, stop_speed_threshold: float=AUTO_HOLD_STOPPED_SPEED) -> bool:
stopped = standstill or v_ego < stop_speed_threshold
return (
hold_ready and
(auto_hold_armed or auto_hold_engaged or brake_pressed) and
not gas_pressed and
stopped and
not long_active and
not regen_braking
@@ -195,12 +357,118 @@ def get_friction_brake_bus(CP):
if CP.networkLocation == NetworkLocation.fwdCamera:
if CP.carFingerprint in SDGM_CAR:
# cam-long: 0x315 goes where the panda whitelist allows it and the EBCM hears it
safety_cfg = getattr(CP, "safetyConfigs", ())
safety_param = safety_cfg[0].safetyParam if safety_cfg else 0
if safety_param & GMSafetyFlags.HW_CAM_LONG.value:
# SASCM relays 0x315 to the EBCM off its camera-bus (bus2) leg; bare SDGM uses the pt bus
if CP.flags & GMFlags.SASCM.value:
return CanBus.CAMERA
return CanBus.POWERTRAIN
return CanBus.CAMERA
return CanBus.POWERTRAIN
return CanBus.CHASSIS
def supports_bolt_acc_pedal_friction_experiment(CP) -> bool:
return (
CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL and
CP.openpilotLongitudinalControl and
CP.enableGasInterceptorDEPRECATED and
bool(CP.flags & GMFlags.PEDAL_LONG.value)
)
def get_bolt_acc_pedal_friction_brake(apply_brake, full_brake_accel, v_ego, params) -> int:
if apply_brake <= 0:
return 0
full_brake_accel = min(full_brake_accel, -0.1)
legacy_full_scale = max(-params.ACCEL_MIN, 0.1)
corrected_scale = legacy_full_scale / max(-full_brake_accel, 0.1)
speed_gain = float(np.interp(v_ego, [0.0, 8.0, 15.0, 25.0], [1.0, 1.08, 1.2, 1.35]))
onset_gain = float(np.interp(
apply_brake,
[0.0, 5.0, 20.0, 60.0, 120.0, 240.0, params.MAX_BRAKE],
[0.0, 1.8, 1.65, 1.4, 1.22, 1.08, 1.0],
))
shaped_brake = apply_brake * corrected_scale * speed_gain * onset_gain
minimum_brake = float(np.interp(v_ego, [0.0, 6.0, 8.0, 12.0, 18.0, 25.0], [0.0, 0.0, 4.0, 10.0, 20.0, 28.0]))
shaped_brake = max(shaped_brake, minimum_brake)
return int(round(np.clip(shaped_brake, 0, params.MAX_BRAKE)))
def shape_bolt_acc_pedal_low_speed_friction(apply_brake: int, v_ego: float, stopping: bool, active: bool):
if apply_brake <= 0:
return 0, False
engage_threshold = float(np.interp(v_ego, [0.0, 1.5, 3.0, 5.0, 8.0], [40.0, 20.0, 12.0, 10.0, 0.0]))
release_threshold = float(np.interp(v_ego, [0.0, 1.5, 3.0, 5.0, 8.0], [0.0, 8.0, 6.0, 4.0, 0.0]))
if not active:
if apply_brake < engage_threshold:
return 0, False
active = True
elif apply_brake < release_threshold:
return 0, False
if stopping:
stop_fade = float(np.interp(v_ego, [0.0, 0.6, 0.9, 1.2, 1.8, 2.8], [0.0, 0.0, 0.05, 0.12, 0.32, 0.78]))
apply_brake = int(round(apply_brake * stop_fade))
if apply_brake <= 0 or apply_brake < release_threshold:
return 0, False
return apply_brake, active
def get_bolt_pedal_long_accel_limit(v_ego: float) -> float:
return float(np.interp(v_ego, BOLT_PEDAL_LONG_ACCEL_LIMIT_BP, BOLT_PEDAL_LONG_ACCEL_LIMIT_V))
def get_bolt_acc_pedal_planner_brake_switch(v_ego: float, params, tire_radius: float, mass: float,
coeff_drag: float, frontal_area: float, air_density: float) -> int:
planner_accel_limit = get_bolt_pedal_long_accel_limit(v_ego)
aero_drag_force = 0.5 * coeff_drag * frontal_area * air_density * v_ego ** 2
planner_torque = tire_radius * ((mass * planner_accel_limit) + aero_drag_force)
return int(round(planner_torque + params.ZERO_GAS))
def get_bolt_acc_pedal_effective_brake_switch(stock_switch: int, planner_switch: int) -> int:
return max(stock_switch, planner_switch)
def get_bolt_acc_pedal_friction_command_state(apply_brake: int, cruise_main_on: bool, release_frames: int):
command_brake = apply_brake if cruise_main_on else 0
if command_brake > 0:
release_frames = BOLT_ACC_PEDAL_FRICTION_RELEASE_FRAMES
elif release_frames > 0:
release_frames -= 1
should_send = cruise_main_on or release_frames > 0
return command_brake, release_frames, should_send
def get_interceptor_sng_gas_cmd(CP, interceptor_gas_cmd: float, accel: float, params, maneuver_mode: bool) -> float:
if maneuver_mode:
return max(interceptor_gas_cmd, float(np.interp(accel, [0.0, 1.0, 2.0], [params.SNG_INTERCEPTOR_GAS, 0.11, 0.16])))
if supports_bolt_acc_pedal_friction_experiment(CP):
return max(interceptor_gas_cmd, params.SNG_INTERCEPTOR_GAS)
return params.SNG_INTERCEPTOR_GAS
def should_use_fixed_stopping_brake(CP, near_stop: bool, stopping: bool, resume: bool) -> bool:
if not (near_stop and stopping and not resume):
return False
return not supports_bolt_acc_pedal_friction_experiment(CP)
class CarController(CarControllerBase):
def __init__(self, dbc_names, CP):
super().__init__(dbc_names, CP)
@@ -211,6 +479,11 @@ class CarController(CarControllerBase):
self.last_steer_frame = 0
self.last_button_frame = 0
self.cancel_counter = 0
self.xt4_cc_button_burst_remaining = 0
self.xt4_cc_button_burst_button = CruiseButtons.INIT
self.xt4_cc_button_burst_last_counter = -1
self.xt4_cc_button_observed_counter = -1
self.xt4_cc_button_counter_frame = 0
self.lka_steering_cmd_counter = 0
self.lka_icon_status_last = (False, False)
@@ -254,10 +527,60 @@ class CarController(CarControllerBase):
self.malibu_button_phase = 0
self.malibu_last_button_ts_nanos = 0
self.auto_hold_brake = 0
self.volt_one_pedal_pid = PIDController(
(CP.longitudinalTuning.kpBP, CP.longitudinalTuning.kpV),
(CP.longitudinalTuning.kiBP, CP.longitudinalTuning.kiV),
rate=1 / (DT_CTRL * 4),
pos_limit=0.0,
neg_limit=VOLT_ONE_PEDAL_PID_NEG_LIMIT,
)
self.volt_one_pedal_decel = 0.0
self.volt_one_pedal_brake = 0
self.volt_one_pedal_lift_frames = 0
self.volt_one_pedal_gas_pressed_last = False
try:
self.gm_auto_hold_enabled = self.params_.get_bool("GMAutoHold")
except UnknownKeyName:
self.gm_auto_hold_enabled = False
self.bolt_acc_pedal_friction_release_frames = 0
self.bolt_acc_pedal_friction_low_speed_active = False
self.truck_friction_brake_active = False
def _reset_volt_one_pedal(self):
self.volt_one_pedal_pid.reset()
self.volt_one_pedal_decel = min(0.0, float(self.aego))
self.volt_one_pedal_brake = 0
self.volt_one_pedal_lift_frames = 0
def _update_volt_one_pedal_brake(self, CC, CS):
pitch_accel = 0.0
if len(CC.orientationNED) == 3 and CS.out.vEgo > self.CP.vEgoStopping:
pitch_accel = math.sin(CC.orientationNED[1]) * ACCELERATION_DUE_TO_GRAVITY
pitch_factor_values = VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_V if pitch_accel <= 0.0 else VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_INCLINE_V
pitch_accel *= float(np.interp(CS.out.vEgo, VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_BP, pitch_factor_values))
target_decel = get_volt_one_pedal_target_decel(CS.out.vEgo)
measured_decel = min(0.0, CS.out.aEgo + pitch_accel)
error_factor = float(np.interp(CS.out.vEgo, VOLT_ONE_PEDAL_SPEED_ERROR_FACTOR_BP, VOLT_ONE_PEDAL_SPEED_ERROR_FACTOR_V))
error = (target_decel - measured_decel) * error_factor
raw_decel = float(self.volt_one_pedal_pid.update(error, speed=CS.out.vEgo, feedforward=target_decel))
rate_limit_factor = min(
float(np.interp(CS.out.vEgo, VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_SPEED_FACTOR_BP, VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_SPEED_FACTOR_V)),
float(np.interp(abs(CS.out.steeringAngleDeg), VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_STEER_FACTOR_BP, VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_STEER_FACTOR_V)),
)
lower = min(self.volt_one_pedal_decel, measured_decel) - VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_UP * rate_limit_factor
upper = max(self.volt_one_pedal_decel, measured_decel) + VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_DOWN + rate_limit_factor
self.volt_one_pedal_decel = float(np.clip(raw_decel, lower, upper))
self.volt_one_pedal_decel = max(self.volt_one_pedal_decel, VOLT_ONE_PEDAL_MAX_DECEL)
self.volt_one_pedal_brake = int(round(np.clip(
np.interp(self.volt_one_pedal_decel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V),
0,
self.params.MAX_BRAKE,
)))
if self.volt_one_pedal_lift_frames > 0:
self.volt_one_pedal_brake = max(self.volt_one_pedal_brake, get_volt_one_pedal_lift_brake(CS.out.vEgo))
self.volt_one_pedal_lift_frames -= 1
def calc_pedal_command(self, accel: float, long_active: bool, v_ego: float):
if not long_active:
@@ -411,7 +734,37 @@ class CarController(CarControllerBase):
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
volt_one_pedal_supported = supports_volt_one_pedal(
self.CP, bool(getattr(starpilot_toggles, "volt_one_pedal_mode", False))
)
volt_one_pedal_active = should_activate_volt_one_pedal(
volt_one_pedal_supported,
CS.out.cruiseState.available,
CC.longActive,
CS.out.gasPressed,
CS.out.brakePressed,
CS.out.regenBraking,
bool(getattr(CS, "single_pedal_mode", False)),
CS.out.gearShifter,
float(getattr(CS, "one_pedal_drive_time", 0.0)),
)
if volt_one_pedal_active and self.volt_one_pedal_gas_pressed_last and not CS.out.gasPressed:
if CS.out.vEgo < VOLT_ONE_PEDAL_LIFT_BRAKE_BP[-1]:
self.volt_one_pedal_lift_frames = VOLT_ONE_PEDAL_LIFT_BRAKE_FRAMES
elif CS.out.gasPressed or not volt_one_pedal_active:
self.volt_one_pedal_lift_frames = 0
if self.frame % 4 == 0:
if volt_one_pedal_active:
self._update_volt_one_pedal_brake(CC, CS)
else:
self._reset_volt_one_pedal()
if not self.CP.openpilotLongitudinalControl:
self.apply_gas = 0
self.apply_brake = self.volt_one_pedal_brake if volt_one_pedal_active else 0
self.volt_one_pedal_gas_pressed_last = CS.out.gasPressed
stock_hold_apply_brake = max(self.apply_brake if self.CP.openpilotLongitudinalControl else 0, self.volt_one_pedal_brake)
hold_ready = (
auto_hold_enabled and
@@ -421,6 +774,8 @@ class CarController(CarControllerBase):
)
if not hold_ready or CS.out.gasPressed:
CS.auto_hold_armed = False
if CS.out.gasPressed:
CS.auto_hold_engaged = 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)):
@@ -429,7 +784,7 @@ class CarController(CarControllerBase):
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)
self.auto_hold_brake = estimate_auto_hold_brake(CS.out.brake, stock_hold_apply_brake, self.CP)
if self.frame % 25 == 0:
try:
@@ -520,10 +875,23 @@ class CarController(CarControllerBase):
CS.auto_hold_armed,
CS.auto_hold_engaged,
CS.out.brakePressed,
CS.out.gasPressed,
CS.out.standstill,
CC.longActive,
CS.out.regenBraking,
CS.out.vEgo,
get_auto_hold_stop_threshold(self.CP, CS.auto_hold_engaged),
)
bolt_acc_pedal_friction_experiment = supports_bolt_acc_pedal_friction_experiment(self.CP)
bolt_acc_pedal_friction_main_on = bolt_acc_pedal_friction_experiment and CS.out.cruiseState.available
if not bolt_acc_pedal_friction_main_on:
self.bolt_acc_pedal_friction_low_speed_active = False
volt_one_pedal_braking = volt_one_pedal_active and self.volt_one_pedal_brake > 0
volt_one_pedal_hold_active = (
volt_one_pedal_braking and
not auto_hold_active and
CS.one_pedal_drive_time >= AUTO_HOLD_MIN_DRIVE_TIME_S and
(CS.out.standstill or CS.out.vEgo < 0.02)
)
# Steering (Active: 50Hz, inactive: 10Hz)
@@ -567,6 +935,18 @@ class CarController(CarControllerBase):
can_sends.append(gmcan.create_ecm_cruise_control_command(
self.packer_pt, CanBus.POWERTRAIN, True, hud_v_cruise * CV.MS_TO_KPH))
xt4_cc_button_spam = (
self.CP.carFingerprint == CAR.CADILLAC_XT4_CC and
should_send_cc_button_spam(self.CP, CC, CS)
)
if xt4_cc_button_spam:
can_sends.extend(gmcan.create_gm_cc_spam_command(self.packer_pt, self, CS, actuators, starpilot_toggles))
elif self.CP.carFingerprint == CAR.CADILLAC_XT4_CC:
self.xt4_cc_button_burst_remaining = 0
self.xt4_cc_button_burst_button = CruiseButtons.INIT
self.xt4_cc_button_burst_last_counter = -1
self.xt4_cc_button_observed_counter = -1
if self.CP.openpilotLongitudinalControl:
# Gas/regen, brakes, and UI commands - all at 25Hz
if self.frame % 4 == 0:
@@ -578,6 +958,7 @@ class CarController(CarControllerBase):
# ASCM sends max regen when not enabled
self.apply_gas = self.params.INACTIVE_REGEN
self.apply_brake = 0
self.bolt_acc_pedal_friction_low_speed_active = False
self.planner_regen_hold = False
self.regen_paddle_pressed = False
self.regen_paddle_timer = 0
@@ -585,7 +966,7 @@ class CarController(CarControllerBase):
self.regen_release_counter = 0
self.regen_min_on_frames = 0
self.regen_min_off_frames = 0
elif near_stop and stopping and not CC.cruiseControl.resume:
elif should_use_fixed_stopping_brake(self.CP, near_stop, stopping, CC.cruiseControl.resume):
stop_accel = getattr(starpilot_toggles, "stopAccel", self.CP.stopAccel)
self.apply_gas = self.params.INACTIVE_REGEN
self.apply_brake = int(min(-100 * stop_accel, self.params.MAX_BRAKE))
@@ -629,7 +1010,23 @@ class CarController(CarControllerBase):
if testing_ground.use_1:
accel_max = min(accel_max, np.interp(CS.out.vEgo, [0.0, 4.0, 12.0], [1.25, 1.6, self.params.ACCEL_MAX]))
accel_cmd = float(np.clip(actuators.accel + accel_due_to_pitch, self.params.ACCEL_MIN, accel_max))
truck_long_smoothing = (
getattr(starpilot_toggles, "truck_tuning", False) and
self.CP.carFingerprint in TRUCK_LONG_SMOOTH_CARS and
getattr(self.CP, "transmissionType", None) == TransmissionType.automatic and
not self.CP.enableGasInterceptorDEPRECATED
)
accel_input = actuators.accel + accel_due_to_pitch
if truck_long_smoothing:
accel_input = shape_truck_positive_accel(
accel_input,
CS.out.vEgo,
True,
lead_visible=CC.hudControl.leadVisible,
set_speed_error=max(CC.hudControl.setSpeed - CS.out.vEgo, 0.0),
)
accel_cmd = float(np.clip(accel_input, self.params.ACCEL_MIN, accel_max))
torque = self.tireRadius * ((self.mass * accel_cmd) + (0.5 * self.coeffDrag * self.frontalArea * self.airDensity * CS.out.vEgo ** 2))
scaled_torque = torque + self.params.ZERO_GAS
apply_gas_torque = np.clip(scaled_torque, self.params.MAX_ACC_REGEN, gas_max)
@@ -637,9 +1034,33 @@ class CarController(CarControllerBase):
if testing_ground.use_1:
brake_switch_bias = get_testing_ground_1_brake_switch_bias(CS.out.vEgo)
brake_switch = min(self.params.ZERO_GAS, brake_switch + brake_switch_bias)
if bolt_acc_pedal_friction_main_on:
planner_brake_switch = get_bolt_acc_pedal_planner_brake_switch(
CS.out.vEgo, self.params, self.tireRadius, self.mass, self.coeffDrag, self.frontalArea, self.airDensity,
)
brake_switch = get_bolt_acc_pedal_effective_brake_switch(brake_switch, planner_brake_switch)
brake_accel = min((scaled_torque - brake_switch) / (self.tireRadius * self.mass), 0)
self.apply_gas = int(round(apply_gas_torque))
self.apply_brake = int(round(np.interp(brake_accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
if truck_long_smoothing:
self.apply_brake, self.truck_friction_brake_active = shape_truck_friction_brake(
self.apply_brake, accel_cmd, stopping, self.truck_friction_brake_active,
)
else:
self.truck_friction_brake_active = False
if bolt_acc_pedal_friction_main_on:
if self.apply_brake > 0:
full_brake_accel = min(
self.params.ACCEL_MIN + (0.5 * self.coeffDrag * self.frontalArea * self.airDensity * CS.out.vEgo ** 2) / self.mass +
(self.params.ZERO_GAS - brake_switch) / (self.tireRadius * self.mass),
-0.1,
)
self.apply_brake = get_bolt_acc_pedal_friction_brake(
self.apply_brake, full_brake_accel, CS.out.vEgo, self.params,
)
self.apply_brake, self.bolt_acc_pedal_friction_low_speed_active = shape_bolt_acc_pedal_low_speed_friction(
self.apply_brake, CS.out.vEgo, stopping, self.bolt_acc_pedal_friction_low_speed_active,
)
if self.apply_brake > 0:
self.apply_gas = self.params.INACTIVE_REGEN
@@ -650,15 +1071,19 @@ class CarController(CarControllerBase):
# gas interceptor only used for full long control on cars without ACC
interceptor_gas_cmd, press_regen_paddle = self.calc_pedal_command(actuators.accel, CC.longActive, CS.out.vEgo)
if volt_one_pedal_braking:
self.apply_gas = self.params.INACTIVE_REGEN
self.apply_brake = max(self.apply_brake, self.volt_one_pedal_brake)
maneuver_sng_launch = self.longitudinal_maneuver_mode and self.is_volt
if (
self.CP.enableGasInterceptorDEPRECATED and
self.apply_gas > self.params.INACTIVE_REGEN and
use_interceptor_sng_launch(self.CP, CS, maneuver_sng_launch)
):
interceptor_gas_cmd = self.params.SNG_INTERCEPTOR_GAS
if maneuver_sng_launch:
interceptor_gas_cmd = max(interceptor_gas_cmd, float(np.interp(actuators.accel, [0.0, 1.0, 2.0], [self.params.SNG_INTERCEPTOR_GAS, 0.11, 0.16])))
interceptor_gas_cmd = get_interceptor_sng_gas_cmd(
self.CP, interceptor_gas_cmd, actuators.accel, self.params, maneuver_sng_launch,
)
self.apply_brake = 0
self.apply_gas = self.params.INACTIVE_REGEN
@@ -679,25 +1104,50 @@ class CarController(CarControllerBase):
if self.CP.flags & GMFlags.CC_LONG.value:
if should_send_cc_button_spam(self.CP, CC, CS):
# Using extend instead of append since the message is only sent intermittently
can_sends.extend(gmcan.create_gm_cc_spam_command(self.packer_pt, self, CS, actuators, starpilot_toggles))
elif (CS.out.cruiseState.enabled and CC.enabled and self.frame % 52 == 0 and
if self.CP.carFingerprint != CAR.CADILLAC_XT4_CC:
# Using extend instead of append since the message is only sent intermittently
can_sends.extend(gmcan.create_gm_cc_spam_command(self.packer_pt, self, CS, actuators, starpilot_toggles))
else:
if (CS.out.cruiseState.enabled and CC.enabled and self.frame % 52 == 0 and
CS.cruise_buttons == CruiseButtons.UNPRESS and CS.out.gasPressed and CS.out.cruiseState.speed < CS.out.vEgo < hud_v_cruise):
if self.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC:
can_sends.append(gmcan.create_buttons_malibu(
self.packer_pt, CanBus.POWERTRAIN, CruiseButtons.DECEL_SET,
self.malibu_button_phase, CS.steering_button_prefix))
self.malibu_button_phase = (self.malibu_button_phase + 1) % 4
else:
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.DECEL_SET))
if self.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC:
can_sends.append(gmcan.create_buttons_malibu(
self.packer_pt, CanBus.POWERTRAIN, CruiseButtons.DECEL_SET,
self.malibu_button_phase, CS.steering_button_prefix))
self.malibu_button_phase = (self.malibu_button_phase + 1) % 4
else:
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.DECEL_SET))
if self.CP.enableGasInterceptorDEPRECATED:
can_sends.append(create_gas_interceptor_command(self.packer_pt, interceptor_gas_cmd, idx))
if bolt_acc_pedal_friction_experiment:
friction_brake_bus = get_friction_brake_bus(self.CP)
if self.CP.networkLocation == NetworkLocation.fwdCamera:
at_full_stop = at_full_stop and stopping
experiment_brake, self.bolt_acc_pedal_friction_release_frames, should_send_bolt_acc_pedal_friction = \
get_bolt_acc_pedal_friction_command_state(
self.apply_brake,
bolt_acc_pedal_friction_main_on,
self.bolt_acc_pedal_friction_release_frames,
)
# This fingerprint is routed through the CC-only pedal path, so it
# does not fall through to the normal friction-brake sender below.
# Never apply stock friction with cruise main off, but do send a short
# explicit zero-brake unwind so the last nonzero stock-EBCM command
# cannot linger after a disengage or main-off event.
if should_send_bolt_acc_pedal_friction:
can_sends.append(gmcan.create_friction_brake_command(
self.packer_ch, friction_brake_bus, experiment_brake, idx, bolt_acc_pedal_friction_main_on,
near_stop, at_full_stop, self.CP))
if self.CP.carFingerprint not in CC_ONLY_CAR:
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
if should_send_acc_2cd(self.CP):
can_sends.append(gmcan.create_acc_2cd_command(CanBus.POWERTRAIN, idx))
if self.CP.autoResumeSng:
resume = actuators.longControlState != LongCtrlState.starting or CC.cruiseControl.resume
@@ -709,57 +1159,67 @@ class CarController(CarControllerBase):
acc_engaged = CC.enabled
if auto_hold_active:
hold_brake = self.auto_hold_brake or estimate_auto_hold_brake(CS.out.brake, self.apply_brake)
hold_brake = max(self.volt_one_pedal_brake, self.auto_hold_brake or estimate_auto_hold_brake(CS.out.brake, self.apply_brake, self.CP))
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))
self.packer_ch, friction_brake_bus, hold_brake, idx, False, hold_near_stop, hold_standstill,
self.CP, allow_near_stop_mode=True))
CS.auto_hold_engaged = True
CS.auto_hold_fault_suppression_timer = 1.0
elif volt_one_pedal_hold_active:
hold_brake = max(self.volt_one_pedal_brake, self.auto_hold_brake or estimate_auto_hold_brake(0.0, self.volt_one_pedal_brake, self.CP))
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, allow_near_stop_mode=True))
CS.auto_hold_engaged = True
CS.auto_hold_fault_suppression_timer = 1.0
else:
if volt_one_pedal_braking:
at_full_stop = at_full_stop or CS.pcm_acc_status == AccState.STANDSTILL
near_stop = near_stop or (CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE)
# 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))
idx, CC.enabled, near_stop, at_full_stop, self.CP,
allow_near_stop_mode=volt_one_pedal_braking))
CS.auto_hold_engaged = False
if should_send_acc_dashboard_status(self.CP, dash_speed_spoof_active):
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))
acc_dashboard_status_active = get_acc_dashboard_status_active(self.CP, CC)
acc_dashboard_always_one = get_acc_dashboard_always_one(self.CP)
can_sends.append(gmcan.create_acc_dashboard_command(self.packer_pt, CanBus.POWERTRAIN, acc_dashboard_status_active,
hud_v_cruise * CV.MS_TO_KPH, hud_control, fcw_alert,
acc_dashboard_always_one))
# Radar needs to know current speed and yaw rate (50hz),
# and that ADAS is alive (10hz)
if not self.CP.radarUnavailable:
send_adas = True
if should_send_adas_status(self.CP, self.CP.carFingerprint in kaofui_cars):
tt = self.frame * DT_CTRL
if self.CP.carFingerprint in kaofui_cars:
if self.CP.carFingerprint not in ASCM_INT:
send_adas = (self.CP.networkLocation != NetworkLocation.fwdCamera) and (self.CP.carFingerprint not in SDGM_CAR)
if send_adas:
tt = self.frame * DT_CTRL
if self.CP.carFingerprint in kaofui_cars:
time_and_headlights_step = 10
speed_and_accelerometer_step = 2
if self.frame % time_and_headlights_step == 0:
idx = (self.frame // time_and_headlights_step) % 4
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
if self.frame % speed_and_accelerometer_step == 0:
idx = (self.frame // speed_and_accelerometer_step) % 4
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
else:
time_and_headlights_step = 20
if self.frame % time_and_headlights_step == 0:
idx = (self.frame // time_and_headlights_step) % 4
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
time_and_headlights_step = 10
speed_and_accelerometer_step = 2
if self.frame % time_and_headlights_step == 0:
idx = (self.frame // time_and_headlights_step) % 4
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
if self.frame % speed_and_accelerometer_step == 0:
idx = (self.frame // speed_and_accelerometer_step) % 4
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
else:
time_and_headlights_step = 20
if self.frame % time_and_headlights_step == 0:
idx = (self.frame // time_and_headlights_step) % 4
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
keepalive_step = get_adas_keepalive_step(self.CP, self.CP.carFingerprint in kaofui_cars)
if keepalive_step is not None and self.frame % keepalive_step == 0:
@@ -785,14 +1245,33 @@ class CarController(CarControllerBase):
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_brake = max(self.volt_one_pedal_brake, self.auto_hold_brake or estimate_auto_hold_brake(CS.out.brake, stock_hold_apply_brake, self.CP))
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))
self.packer_ch, get_friction_brake_bus(self.CP), hold_brake, idx, False, hold_near_stop, hold_standstill,
self.CP, allow_near_stop_mode=True))
CS.auto_hold_engaged = True
CS.auto_hold_fault_suppression_timer = 1.0
elif self.frame % 4 == 0 and volt_one_pedal_hold_active:
idx = (self.frame // 4) % 4
hold_brake = max(self.volt_one_pedal_brake, self.auto_hold_brake or estimate_auto_hold_brake(0.0, self.volt_one_pedal_brake, self.CP))
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, allow_near_stop_mode=True))
CS.auto_hold_engaged = True
CS.auto_hold_fault_suppression_timer = 1.0
elif self.frame % 4 == 0 and volt_one_pedal_braking:
idx = (self.frame // 4) % 4
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), self.volt_one_pedal_brake, idx, False, near_stop, False,
self.CP, allow_near_stop_mode=True))
CS.auto_hold_engaged = False
elif self.frame % 4 == 0:
self.apply_brake = 0
CS.auto_hold_engaged = False
# While car is braking, cancel button causes ECM to enter a soft disable state with a fault status.
+63 -30
View File
@@ -33,6 +33,13 @@ 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}
HARD_BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise}
NORMAL_CRUISE_BUTTONS = (CruiseButtons.RES_ACCEL, CruiseButtons.DECEL_SET)
def get_hard_cruise_buttons(steering_button_msg: dict) -> int:
return steering_button_msg.get("ACCButtonsHard", CruiseButtons.INIT)
GearShifter = structs.CarState.GearShifter
BOLT_GEN1_CANCEL_PERSONALITY_CARS = {
@@ -45,6 +52,19 @@ BOLT_CANCEL_BUTTON_CARS = BOLT_GEN1_CANCEL_PERSONALITY_CARS | {
}
def update_auto_hold_drive_timers(in_drive_for_hold: bool, moving_for_hold: bool,
auto_hold_drive_time: float, one_pedal_drive_time: float) -> tuple[float, float]:
if in_drive_for_hold:
if moving_for_hold:
auto_hold_drive_time = min(auto_hold_drive_time + DT_CTRL, AUTO_HOLD_MIN_DRIVE_TIME_S)
one_pedal_drive_time = min(one_pedal_drive_time + DT_CTRL, AUTO_HOLD_MIN_DRIVE_TIME_S)
else:
auto_hold_drive_time = 0.0
one_pedal_drive_time = 0.0
return auto_hold_drive_time, one_pedal_drive_time
class CarState(CarStateBase):
def __init__(self, CP, FPCP):
super().__init__(CP, FPCP)
@@ -64,11 +84,14 @@ class CarState(CarStateBase):
self.prev_distance_button = 0
self.distance_button = 0
self.hard_cruise_buttons = CruiseButtons.INIT
self.force_reset_cruise_buttons = False
self.single_pedal_mode = False
self.auto_hold_armed = False
self.auto_hold_engaged = False
self.auto_hold_drive_time = 0.0
self.one_pedal_drive_time = 0.0
self.auto_hold_fault_suppression_timer = 0.0
self.regen_release_timer = 0.0
self.user_regen_paddle_pressed = False
@@ -81,12 +104,6 @@ class CarState(CarStateBase):
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:
@@ -119,21 +136,36 @@ class CarState(CarStateBase):
sdgm_non_volt = self.CP.carFingerprint in SDGM_CAR and self.CP.carFingerprint not in kaofui_state_cars
prev_cruise_buttons = self.cruise_buttons
prev_hard_cruise_buttons = self.hard_cruise_buttons
prev_distance_button = self.distance_button
if not sdgm_non_volt:
self.cruise_buttons = pt_cp.vl["ASCMSteeringButton"]["ACCButtons"]
self.distance_button = pt_cp.vl["ASCMSteeringButton"]["DistanceButton"]
self.buttons_counter = pt_cp.vl["ASCMSteeringButton"]["RollingCounter"]
self.steering_button_checksum = pt_cp.vl["ASCMSteeringButton"]["SteeringButtonChecksum"]
steering_button_msg = pt_cp.vl["ASCMSteeringButton"]
self.cruise_buttons = steering_button_msg["ACCButtons"]
self.hard_cruise_buttons = get_hard_cruise_buttons(steering_button_msg)
self.distance_button = steering_button_msg["DistanceButton"]
self.buttons_counter = steering_button_msg["RollingCounter"]
self.steering_button_checksum = steering_button_msg["SteeringButtonChecksum"]
self.steering_button_ts_nanos = pt_cp.ts_nanos["ASCMSteeringButton"]["ACCButtons"]
acc_always_one = pt_cp.vl["ASCMSteeringButton"]["ACCAlwaysOne"]
acc_hidden_bit = pt_cp.vl["ASCMSteeringButton"].get("ACCHiddenBit", 0)
acc_always_one = steering_button_msg["ACCAlwaysOne"]
acc_hidden_bit = steering_button_msg.get("ACCHiddenBit", 0)
self.steering_button_prefix = (int(acc_always_one) & 1) | ((int(acc_hidden_bit) & 1) << 6)
else:
self.cruise_buttons = cam_cp.vl["ASCMSteeringButton"]["ACCButtons"]
self.distance_button = cam_cp.vl["ASCMSteeringButton"]["DistanceButton"]
self.buttons_counter = cam_cp.vl["ASCMSteeringButton"]["RollingCounter"]
steering_button_msg = cam_cp.vl["ASCMSteeringButton"]
self.cruise_buttons = steering_button_msg["ACCButtons"]
self.hard_cruise_buttons = get_hard_cruise_buttons(steering_button_msg)
self.distance_button = steering_button_msg["DistanceButton"]
self.buttons_counter = steering_button_msg["RollingCounter"]
self.steering_button_ts_nanos = cam_cp.ts_nanos["ASCMSteeringButton"]["ACCButtons"]
# A GM hard press keeps the normal cruise button signal active too. Suppress
# the normal button until the wheel reports a different normal state.
if self.hard_cruise_buttons != CruiseButtons.INIT and self.cruise_buttons in NORMAL_CRUISE_BUTTONS:
self.force_reset_cruise_buttons = True
if self.force_reset_cruise_buttons and self.cruise_buttons in NORMAL_CRUISE_BUTTONS:
self.cruise_buttons = CruiseButtons.UNPRESS
elif self.force_reset_cruise_buttons and self.cruise_buttons not in NORMAL_CRUISE_BUTTONS:
self.force_reset_cruise_buttons = False
self.pscm_status = copy.copy(pt_cp.vl["PSCMStatus"])
self.moving_backward = (pt_cp.vl["EBCMWheelSpdRear"]["RLWheelDir"] == 2) or (pt_cp.vl["EBCMWheelSpdRear"]["RRWheelDir"] == 2)
@@ -178,7 +210,10 @@ class CarState(CarStateBase):
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)):
elif (self.CP.flags & GMFlags.FORCE_BRAKE_C9.value) or (
self.CP.networkLocation == NetworkLocation.fwdCamera and
self.CP.carFingerprint not in (SDGM_CAR | ASCM_INT | {CAR.CHEVROLET_BLAZER})
):
ret.brakePressed = pt_cp.vl["ECMEngineStatus"]["BrakePressed"] != 0
else:
# Some Volt 2016-17 have loose brake pedal push rod retainers which causes the ECM to believe
@@ -189,13 +224,10 @@ class CarState(CarStateBase):
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_drive_time, self.one_pedal_drive_time = update_auto_hold_drive_timers(
in_drive_for_hold, ret.vEgo > 0.1, self.auto_hold_drive_time, self.one_pedal_drive_time
)
if not in_drive_for_hold:
self.auto_hold_armed = False
self.auto_hold_engaged = False
@@ -280,12 +312,6 @@ class CarState(CarStateBase):
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"])
@@ -374,19 +400,26 @@ class CarState(CarStateBase):
lkas_events = [] if (suppress_malibu_side_buttons or suppress_bolt_cancel_lkas) else create_button_events(
self.lkas_enabled, self.lkas_previously_enabled, {1: ButtonType.lkas}
)
hard_cruise_events = create_button_events(
self.hard_cruise_buttons, prev_hard_cruise_buttons, HARD_BUTTONS_DICT, unpressed_btn=CruiseButtons.INIT
)
# Don't add events if transitioning from INIT, unless it's to an actual button.
if self.cruise_buttons != CruiseButtons.UNPRESS or prev_cruise_buttons != CruiseButtons.INIT:
if (self.cruise_buttons != CruiseButtons.UNPRESS or prev_cruise_buttons != CruiseButtons.INIT or
self.hard_cruise_buttons != CruiseButtons.INIT or prev_hard_cruise_buttons != CruiseButtons.INIT):
ret.buttonEvents = [
*cruise_events,
*distance_events,
*lkas_events,
*hard_cruise_events,
]
if ret.vEgo < self.CP.minSteerSpeed:
ret.lowSpeedAlert = True
fp_ret = custom.StarPilotCarState.new_message()
fp_ret.accelHardCruise = self.hard_cruise_buttons == CruiseButtons.RES_ACCEL or prev_hard_cruise_buttons == CruiseButtons.RES_ACCEL
fp_ret.decelHardCruise = self.hard_cruise_buttons == CruiseButtons.DECEL_SET or prev_hard_cruise_buttons == CruiseButtons.DECEL_SET
if bolt_cancel_button and self.cruise_buttons == CruiseButtons.CANCEL:
fp_ret.cancelPressed = True
fp_ret.sportGear = pt_cp.vl["SportMode"]["SportMode"] == 1
@@ -7,6 +7,7 @@ from opendbc.car.gm.values import CAR
CAMERA_DIAGNOSTIC_ADDRESS = 0x24b
CAMERA_DIAGNOSTIC_RX_ADDRESS = 0x64b
SASCM_ADDRESS = 0x2FF
FINGERPRINTS = {
@@ -210,6 +211,7 @@ FINGERPRINTS.update({
CAR.GMC_ACADIA_ASCM: FINGERPRINTS[CAR.GMC_ACADIA],
CAR.CHEVROLET_MALIBU_ASCM: FINGERPRINTS[CAR.CHEVROLET_MALIBU],
CAR.CADILLAC_ESCALADE_ASCM: FINGERPRINTS[CAR.CADILLAC_ESCALADE],
CAR.CADILLAC_ESCALADE_ESV_2019_ASCM: [{**fp, SASCM_ADDRESS: 8} for fp in FINGERPRINTS[CAR.CADILLAC_ESCALADE_ESV_2019]],
CAR.CHEVROLET_SUBURBAN: FINGERPRINTS[CAR.CHEVROLET_SUBURBAN_CC],
CAR.GMC_YUKON_CC: FINGERPRINTS[CAR.GMC_YUKON],
CAR.CADILLAC_XT6: FINGERPRINTS[CAR.CHEVROLET_TRAVERSE],
@@ -218,6 +220,7 @@ FINGERPRINTS.update({
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.CADILLAC_XT4_CC: FINGERPRINTS[CAR.CADILLAC_XT4],
CAR.BUICK_LACROSSE_ASCM: FINGERPRINTS[CAR.BUICK_LACROSSE],
})
+68 -15
View File
@@ -17,6 +17,10 @@ MALIBU_BUTTON_MAP = {
CruiseButtons.CANCEL: 5,
}
ACC_CRUISE_STATE_ADAPTIVE = 2
XT4_CC_BUTTON_BURST_FRAMES = 6
XT4_CC_BUTTON_COUNTER_DELAY_FRAMES = 1
def malibu_phase_map_for_button(button):
key = MALIBU_BUTTON_MAP.get(button)
@@ -179,22 +183,28 @@ def create_ecm_cruise_control_command(packer, bus, enabled, target_speed_kph):
return CanData(0x3D1, bytes(dat), bus)
def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_stop, at_full_stop, CP):
def get_friction_brake_mode(apply_brake, enabled, near_stop, at_full_stop, CP, allow_near_stop_mode=False):
mode = 0x1
# TODO: Understand this better. Volts and ICE Camera ACC cars are 0x1 when enabled with no brake
if enabled and CP.carFingerprint in (CAR.CHEVROLET_BOLT_ACC_2022_2023,):
if enabled and CP.carFingerprint in (CAR.CHEVROLET_BOLT_ACC_2022_2023, CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL):
mode = 0x9
if apply_brake > 0:
mode = 0xa
if at_full_stop:
mode = 0xd
elif allow_near_stop_mode and near_stop:
# Stock Volt auto hold can run with cruise main on but ACC inactive, so
# there is no stock STANDSTILL state to promote 0xa -> 0xd. Restore the
# older near-stop hold mode only for that path.
mode = 0xb
# TODO: this is to have GM bringing the car to complete stop,
# but currently it conflicts with OP controls, so turned off. Not set by all cars
#elif near_stop:
# mode = 0xb
return mode
def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_stop, at_full_stop, CP, allow_near_stop_mode=False):
mode = get_friction_brake_mode(apply_brake, enabled, near_stop, at_full_stop, CP, allow_near_stop_mode)
brake = (0x1000 - apply_brake) & 0xfff
checksum = (0x10000 - (mode << 12) - brake - idx) & 0xffff
@@ -209,18 +219,25 @@ 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, status_values, fcw_alert):
target_speed = min(max(float(status_values.get("ACCSpeedSetpoint", 0.0)), 0.0), 255.0)
def create_acc_2cd_command(bus, idx):
dat = bytearray([0x00, 0x2c, 0x03, 0xd3, 0x00])
dat[0] = (idx & 0x3) << 6
dat[4] = (0xfd - (idx & 0x3)) & 0xff
return CanData(0x2CD, bytes(dat), bus)
def create_acc_dashboard_command(packer, bus, enabled, target_speed_kph, hud_control, fcw_alert, acc_always_one=1):
target_speed = min(target_speed_kph, 255)
values = {
"ACCAlwaysOne": 1,
"ACCCruiseState": int(status_values.get("ACCCruiseState", 0)) & 0x7,
"ACCResumeButton": int(status_values.get("ACCResumeButton", 0)) & 0x1,
"ACCAlwaysOne": acc_always_one,
"ACCCruiseState": ACC_CRUISE_STATE_ADAPTIVE,
"ACCResumeButton": 0,
"ACCSpeedSetpoint": target_speed,
"ACCGapLevel": int(status_values.get("ACCGapLevel", 0)) & 0x3,
"ACCCmdActive": int(status_values.get("ACCCmdActive", 0)) & 0x1,
"ACCAlwaysOne2": 1,
"ACCLeadCar": int(status_values.get("ACCLeadCar", 0)) & 0x1,
"ACCGapLevel": hud_control.leadDistanceBars * enabled, # 3 "far", 0 "inactive"
"ACCCmdActive": enabled,
"ACCAlwaysOne2": acc_always_one,
"ACCLeadCar": hud_control.leadVisible,
"FCWAlert": int(fcw_alert) & 0x3,
}
@@ -303,6 +320,42 @@ def create_gm_cc_spam_command(packer, controller, CS, actuators, starpilot_toggl
cruise_btn = CruiseButtons.INIT
controller.apply_speed = speed_setpoint
if CS.CP.carFingerprint == CAR.CADILLAC_XT4_CC:
if controller.xt4_cc_button_observed_counter != CS.buttons_counter:
controller.xt4_cc_button_observed_counter = CS.buttons_counter
controller.xt4_cc_button_counter_frame = controller.frame
if cruise_btn == CruiseButtons.INIT:
controller.xt4_cc_button_burst_remaining = 0
controller.xt4_cc_button_burst_button = CruiseButtons.INIT
return []
if (controller.xt4_cc_button_burst_remaining > 0 and
controller.xt4_cc_button_burst_button != cruise_btn):
controller.xt4_cc_button_burst_remaining = 0
if controller.xt4_cc_button_burst_remaining == 0:
if (controller.frame - controller.last_button_frame) * DT_CTRL <= rate:
return []
controller.last_button_frame = controller.frame
controller.xt4_cc_button_burst_button = cruise_btn
controller.xt4_cc_button_burst_remaining = XT4_CC_BUTTON_BURST_FRAMES
controller.xt4_cc_button_burst_last_counter = -1
# XT4 physical taps hold the button for 5-7 consecutive 33 Hz frames.
# Sending immediately after the stock frame is too early for the receiving ECU.
if controller.frame - controller.xt4_cc_button_counter_frame < XT4_CC_BUTTON_COUNTER_DELAY_FRAMES:
return []
# Send once per observed stock counter so the injected sequence has the same cadence.
if controller.xt4_cc_button_burst_last_counter == CS.buttons_counter:
return []
controller.xt4_cc_button_burst_last_counter = CS.buttons_counter
controller.xt4_cc_button_burst_remaining -= 1
idx = (CS.buttons_counter + 1) % 4
return [create_buttons(packer, CanBus.POWERTRAIN, idx, controller.xt4_cc_button_burst_button)]
# Check rlogs closely - our message shouldn't show up on the pt bus for us
# Or bus 2, since we're forwarding... but I think it does
if (cruise_btn != CruiseButtons.INIT) and ((controller.frame - controller.last_button_frame) * DT_CTRL > rate):
+80 -19
View File
@@ -62,12 +62,28 @@ NON_LINEAR_TORQUE_PARAMS = {
"left": [3.8, 0.81, 0.24, 0.0465122],
"right": [3.8, 0.81, 0.24, 0.0465122],
},
CAR.CADILLAC_XT4: {
"left": [2.4, 0.95, 0.28, 0.0],
"right": [2.4, 0.95, 0.28, 0.0],
},
CAR.CHEVROLET_VOLT: {
"left": [1.5, 1.0, 0.155, 0.0],
"right": [1.5, 1.0, 0.155, 0.0],
"left": [1.525, 1.05, 0.155, 0.0],
"right": [1.525, 0.95, 0.150, 0.0],
},
}
NON_LINEAR_TORQUE_PARAM_ALIASES = {
CAR.CHEVROLET_VOLT_ASCM: CAR.CHEVROLET_VOLT,
CAR.CHEVROLET_VOLT_CAMERA: CAR.CHEVROLET_VOLT,
CAR.CHEVROLET_VOLT_CC: CAR.CHEVROLET_VOLT,
CAR.CHEVROLET_VOLT_2019: CAR.CHEVROLET_VOLT,
}
def get_nonlinear_torque_params(car_fingerprint):
source_fingerprint = NON_LINEAR_TORQUE_PARAM_ALIASES.get(car_fingerprint, car_fingerprint)
return NON_LINEAR_TORQUE_PARAMS.get(source_fingerprint)
PEDAL_MSG = 0x201
CAM_MSG = 0x320
ACCELERATOR_POS_MSG = 0xBE
@@ -129,7 +145,11 @@ class CarInterface(CarInterfaceBase):
@staticmethod
def get_pid_accel_limits(CP, current_speed, cruise_speed):
if CP.enableGasInterceptorDEPRECATED and bool(CP.flags & GMFlags.PEDAL_LONG.value):
if CP.carFingerprint in BOLT_PEDAL_LONG_CARS:
if CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL:
accel_min = CarControllerParams.ACCEL_MIN
accel_max = np.interp(current_speed, [0.0, 1.5, 4.0, 8.0, 15.0],
[0.54, 0.74, 1.03, 1.46, CarControllerParams.ACCEL_MAX])
elif CP.carFingerprint in BOLT_PEDAL_LONG_CARS:
accel_min = np.interp(current_speed, [0.0, 1.5, 4.0, 8.0, 15.0, 30.0],
[-0.93, -1.28, -1.98, -2.58, -2.86, -2.95])
accel_max = np.interp(current_speed, [0.0, 1.5, 4.0, 8.0, 15.0],
@@ -161,7 +181,7 @@ class CarInterface(CarInterfaceBase):
# The "lat_accel vs torque" relationship is assumed to be the sum of "sigmoid + linear" curves
# An important thing to consider is that the slope at 0 should be > 0 (ideally >1)
# This has big effect on the stability about 0 (noise when going straight)
non_linear_torque_params = NON_LINEAR_TORQUE_PARAMS.get(self.CP.carFingerprint)
non_linear_torque_params = get_nonlinear_torque_params(self.CP.carFingerprint)
assert non_linear_torque_params, "The params are not defined"
if isinstance(non_linear_torque_params, dict):
side_key = "left" if lateral_acceleration >= 0 else "right"
@@ -179,7 +199,7 @@ class CarInterface(CarInterfaceBase):
return torque_values, lataccel_values
def torque_from_lateral_accel(self) -> TorqueFromLateralAccelCallbackType:
if self.CP.carFingerprint in NON_LINEAR_TORQUE_PARAMS:
if get_nonlinear_torque_params(self.CP.carFingerprint) is not None:
torque_values, lataccel_values = self.get_lataccel_torque_siglin()
def torque_from_lateral_accel_siglin(lateral_acceleration: float, torque_params: structs.CarParams.LateralTorqueTuning):
@@ -189,7 +209,7 @@ class CarInterface(CarInterfaceBase):
return self.torque_from_lateral_accel_linear
def lateral_accel_from_torque(self) -> LateralAccelFromTorqueCallbackType:
if self.CP.carFingerprint in NON_LINEAR_TORQUE_PARAMS:
if get_nonlinear_torque_params(self.CP.carFingerprint) is not None:
torque_values, lataccel_values = self.get_lataccel_torque_siglin()
def lateral_accel_from_torque_siglin(torque: float, torque_params: structs.CarParams.LateralTorqueTuning):
@@ -213,6 +233,10 @@ class CarInterface(CarInterfaceBase):
gm_auto_hold = params.get_bool("GMAutoHold")
except UnknownKeyName:
gm_auto_hold = False
try:
volt_one_pedal_mode = params.get_bool("VoltOnePedalMode")
except UnknownKeyName:
volt_one_pedal_mode = False
ret.brand = "gm"
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.gm)]
@@ -410,8 +434,10 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate in (CAR.BUICK_LACROSSE, CAR.BUICK_LACROSSE_ASCM):
elif candidate in (CAR.BUICK_LACROSSE, CAR.BUICK_LACROSSE_ASCM, CAR.BUICK_LACROSSE_ASCM_19US):
CarInterfaceBase.configure_torque_tune(CAR.BUICK_LACROSSE, ret.lateralTuning)
if candidate == CAR.BUICK_LACROSSE_ASCM_19US:
ret.minSteerSpeed = 27 * CV.MPH_TO_MS
elif candidate == CAR.CADILLAC_ESCALADE:
ret.minEnableSpeed = -1. # engage speed is decided by pcm
@@ -420,7 +446,7 @@ class CarInterface(CarInterfaceBase):
elif candidate == CAR.CADILLAC_ESCALADE_ASCM:
CarInterfaceBase.configure_torque_tune(CAR.CADILLAC_ESCALADE, ret.lateralTuning)
elif candidate in (CAR.CADILLAC_ESCALADE_ESV, CAR.CADILLAC_ESCALADE_ESV_2019):
elif candidate in (CAR.CADILLAC_ESCALADE_ESV, CAR.CADILLAC_ESCALADE_ESV_2019, CAR.CADILLAC_ESCALADE_ESV_2019_ASCM):
ret.minEnableSpeed = -1. # engage speed is decided by pcm
if candidate == CAR.CADILLAC_ESCALADE_ESV:
@@ -429,7 +455,8 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.pid.kf = 0.000045
else:
ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
torque_candidate = CAR.CADILLAC_ESCALADE_ESV_2019 if candidate == CAR.CADILLAC_ESCALADE_ESV_2019_ASCM else candidate
CarInterfaceBase.configure_torque_tune(torque_candidate, ret.lateralTuning)
elif candidate in (
CAR.CHEVROLET_BOLT_ACC_2022_2023,
@@ -482,7 +509,7 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.CADILLAC_XT4:
elif candidate in (CAR.CADILLAC_XT4, CAR.CADILLAC_XT4_CC):
ret.steerActuatorDelay = 0.2
if not ret.openpilotLongitudinalControl:
ret.minEnableSpeed = -1.
@@ -515,7 +542,19 @@ class CarInterface(CarInterfaceBase):
if not ret.openpilotLongitudinalControl:
ret.minEnableSpeed = -1.
if candidate == CAR.CHEVROLET_BLAZER:
# The Blazer builds brake torque noticeably later than the rest of the GM set.
# A slightly larger planner delay estimate starts the request earlier and keeps
# stopped-lead approaches from turning into a late, harsh max-brake catch-up.
ret.longitudinalActuatorDelay = 0.7
ret.longitudinalTuning.kpBP = [0.0, 4.0, 12.0, 35.0]
ret.longitudinalTuning.kpV = [0.09, 0.075, 0.055, 0.040]
ret.longitudinalTuning.kiBP = [0.0, 4.0, 12.0, 35.0]
ret.longitudinalTuning.kiV = [0.03, 0.04, 0.055, 0.07]
ret.minEnableSpeed = 5 * CV.KPH_TO_MS
ret.stoppingDecelRate = 1.0
ret.vEgoStopping = 0.35
ret.vEgoStarting = 0.35
ret.stopAccel = -0.30
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.BUICK_BABYENCLAVE:
@@ -570,6 +609,9 @@ class CarInterface(CarInterfaceBase):
if is_bolt_2022_2023_pedal:
# Gen2 Bolt pedal-long should follow the no-ACC panda path.
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_NO_ACC.value
ret.startingState = True
ret.startAccel = 0.55
ret.vEgoStarting = max(ret.vEgoStarting, 0.35)
if candidate in (CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL, CAR.CHEVROLET_MALIBU_HYBRID_CC):
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_BOLT_2022_PEDAL.value
@@ -613,7 +655,7 @@ class CarInterface(CarInterfaceBase):
ret.longitudinalTuning.kpBP = [0.0, 5.0, 15.0, 35.0]
ret.longitudinalTuning.kpV = [0.02, 0.03, 0.028, 0.022]
ret.longitudinalTuning.kiBP = [0.0, 5.0, 15.0, 35.0]
ret.longitudinalTuning.kiV = [0.28, 0.26, 0.20, 0.16]
ret.longitudinalTuning.kiV = [0.20, 0.18, 0.13, 0.08]
elif candidate in CC_ONLY_CAR and not ret.enableGasInterceptorDEPRECATED:
ret.flags |= GMFlags.CC_LONG.value
@@ -645,7 +687,7 @@ class CarInterface(CarInterfaceBase):
# Exception for flashed cars, or cars whose camera was removed.
missing_camera_msg = CAM_MSG not in fingerprint.get(CanBus.CAMERA, {})
if (ret.networkLocation == NetworkLocation.fwdCamera or candidate in CC_ONLY_CAR) and missing_camera_msg and candidate not in SDGM_CAR:
if (ret.networkLocation == NetworkLocation.fwdCamera or candidate in CC_ONLY_CAR) and missing_camera_msg and candidate not in (ASCM_INT | SDGM_CAR):
ret.flags |= GMFlags.NO_CAMERA.value
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_NO_CAMERA.value
@@ -663,8 +705,9 @@ 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
volt_stock_friction_brake_safety = (
ret.openpilotLongitudinalControl and
(gm_auto_hold or volt_one_pedal_mode) and
candidate in {
CAR.CHEVROLET_VOLT,
CAR.CHEVROLET_VOLT_2019,
@@ -672,13 +715,31 @@ class CarInterface(CarInterfaceBase):
CAR.CHEVROLET_VOLT_CAMERA,
}
)
if volt_stock_auto_hold_safety:
# Reuse the paddle-scheduler safety bit as a Volt auto-hold marker on
# non-pedal paths. Hold can run while OP longitudinal is configured but
# not currently active, so the bit must be present regardless of the
# current long-control mode.
if volt_stock_friction_brake_safety:
# Reuse the paddle-scheduler safety bit as a Volt stock friction-brake
# marker on non-pedal paths. Auto hold and one-pedal can run while OP
# longitudinal is configured but not currently active, so the bit must
# be present regardless of the current long-control mode. Do not expose
# the path at all when OP long is disabled in CarParams.
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
volt_stock_one_pedal_safety = (
ret.openpilotLongitudinalControl and
volt_one_pedal_mode and
candidate in {
CAR.CHEVROLET_VOLT,
CAR.CHEVROLET_VOLT_2019,
CAR.CHEVROLET_VOLT_ASCM,
CAR.CHEVROLET_VOLT_CAMERA,
}
)
if volt_stock_one_pedal_safety:
# Reuse the 3D1 scheduler bit as a Volt one-pedal marker on non-pedal
# ACC paths. The bit is ignored by the actual 3D1 scheduler unless the
# car is on a pedal-long CC-only path, so this stays isolated from Bolt.
# Do not expose the path at all when OP long is disabled in CarParams.
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_PANDA_3D1_SCHED.value
use_panda_3d1_sched = (
ret.openpilotLongitudinalControl and
ret.enableGasInterceptorDEPRECATED and
@@ -1,6 +1,10 @@
import sys
import types
from types import SimpleNamespace
import numpy as np
import pytest
from opendbc.car import structs
fake_interfaces = types.ModuleType("opendbc.car.interfaces")
@@ -33,21 +37,42 @@ 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 (
AUTO_HOLD_DRIVE_GEARS,
CarController,
estimate_auto_hold_brake,
get_adas_keepalive_step,
get_auto_hold_stop_threshold,
get_bolt_acc_pedal_friction_brake,
get_bolt_acc_pedal_friction_command_state,
get_bolt_acc_pedal_effective_brake_switch,
get_bolt_acc_pedal_planner_brake_switch,
get_bolt_pedal_long_accel_limit,
get_interceptor_sng_gas_cmd,
get_lka_steering_cmd_counter,
get_volt_one_pedal_target_decel,
get_testing_ground_1_brake_switch_bias,
get_acc_dashboard_status_active,
get_stock_cc_active_for_cancel,
shape_bolt_acc_pedal_low_speed_friction,
shape_truck_friction_brake,
shape_truck_positive_accel,
should_use_fixed_stopping_brake,
should_activate_auto_hold,
should_activate_volt_one_pedal,
should_send_acc_2cd,
should_send_adas_status,
should_send_stock_long_cancel,
should_spoof_dash_speed,
should_spoof_ecm_cruise_status,
supports_bolt_acc_pedal_friction_experiment,
supports_volt_auto_hold,
supports_volt_one_pedal,
use_interceptor_sng_launch,
)
from opendbc.car.gm.values import AccState, CAR, GMFlags
from opendbc.car.gm.gmcan import get_friction_brake_mode
from opendbc.car.gm.values import AccState, CAR, CarControllerParams, GMFlags
from opendbc.car.structs import CarParams
from opendbc.car.common.conversions import Conversions as CV
def _cs(enabled, pcm_acc_status):
@@ -81,6 +106,7 @@ def _controller(car_fingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021):
controller.pedal_steady = 0.0
controller.aego = 0.0
controller.maneuver_paddle_mode = "auto"
controller.bolt_acc_pedal_friction_low_speed_active = False
return controller
@@ -96,12 +122,201 @@ def test_gen2_bolt_acc_pedal_cancel_uses_enabled_only():
assert not get_stock_cc_active_for_cancel(CP, _cs(False, AccState.ACTIVE))
def test_bolt_acc_pedal_friction_experiment_is_single_fingerprint_only():
assert supports_bolt_acc_pedal_friction_experiment(SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
))
assert not supports_bolt_acc_pedal_friction_experiment(SimpleNamespace(
carFingerprint=CAR.CHEVROLET_MALIBU_HYBRID_CC,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
))
assert not supports_bolt_acc_pedal_friction_experiment(SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
openpilotLongitudinalControl=False,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
))
def test_bolt_acc_pedal_friction_blend_preserves_zero_before_crossover():
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
assert get_bolt_acc_pedal_friction_brake(0, -2.8, 20.0, params) == 0
def test_bolt_acc_pedal_friction_blend_uses_full_brake_range_after_regen():
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
# Legacy mapping tops out early once regen has already consumed part of the
# decel request. The experiment remaps that reduced span back to full scale.
assert get_bolt_acc_pedal_friction_brake(286, -2.86, 20.0, params) == 400
def test_bolt_acc_pedal_friction_blend_biases_small_commands_upward_at_speed():
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
low_speed = get_bolt_acc_pedal_friction_brake(31, -2.86, 0.0, params)
high_speed = get_bolt_acc_pedal_friction_brake(31, -2.86, 20.0, params)
assert low_speed > 31
assert high_speed > low_speed
def test_bolt_acc_pedal_friction_blend_applies_a_minimum_pre_stop_command_at_speed():
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
assert get_bolt_acc_pedal_friction_brake(2, -2.86, 17.0, params) >= 18
def test_bolt_acc_pedal_friction_blend_boosts_midrange_commands_before_stopping_phase():
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
assert get_bolt_acc_pedal_friction_brake(40, -2.86, 15.0, params) >= 80
def test_bolt_acc_pedal_low_speed_friction_ignores_tiny_inactive_brake_requests():
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(9, 5.0, False, False)
assert apply_brake == 0
assert not active
def test_bolt_acc_pedal_low_speed_friction_uses_hysteresis_once_active():
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(24, 3.0, False, False)
assert apply_brake == 24
assert active
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(5, 3.0, False, active)
assert apply_brake == 0
assert not active
def test_bolt_acc_pedal_low_speed_friction_fades_out_at_standstill():
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(80, 0.2, True, True)
assert apply_brake == 0
assert not active
def test_bolt_acc_pedal_low_speed_friction_preserves_rolling_stop_authority():
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(80, 1.2, True, True)
assert 0 < apply_brake < 80
assert active
def test_bolt_acc_pedal_low_speed_friction_drops_out_before_two_clamp():
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(80, 0.9, True, True)
assert apply_brake == 0
assert not active
def test_bolt_pedal_long_accel_limit_matches_planner_regen_envelope():
assert get_bolt_pedal_long_accel_limit(6.66) == pytest.approx(-2.379, abs=1e-3)
assert get_bolt_pedal_long_accel_limit(3.0) == pytest.approx(-1.70, abs=1e-3)
def test_bolt_acc_pedal_planner_brake_switch_is_lower_than_stock_switch():
params = SimpleNamespace(ZERO_GAS=6150, BRAKE_SWITCH_LOOKUP_BP=[0.5, 10.0], BRAKE_SWITCH_LOOKUP_V=[6150, 5500])
v_ego = 6.66
stock_switch = int(round(np.interp(v_ego, params.BRAKE_SWITCH_LOOKUP_BP, params.BRAKE_SWITCH_LOOKUP_V)))
planner_switch = get_bolt_acc_pedal_planner_brake_switch(
v_ego, params, tire_radius=0.336, mass=1832.0, coeff_drag=0.30, frontal_area=2.35, air_density=1.225,
)
assert planner_switch < stock_switch
def test_bolt_acc_pedal_effective_brake_switch_never_suppresses_stock_friction():
params = SimpleNamespace(ZERO_GAS=6150, BRAKE_SWITCH_LOOKUP_BP=[0.5, 10.0], BRAKE_SWITCH_LOOKUP_V=[6150, 5500])
v_ego = 5.434
mass = 1805.0
tire_radius = 0.075 * 2.63779 + 0.1453
frontal_area = 1.05 * 2.63779 + 0.0679
coeff_drag = 0.30
air_density = 1.225
accel_cmd = -1.399
aero_drag_force = 0.5 * coeff_drag * frontal_area * air_density * v_ego ** 2
torque = tire_radius * ((mass * accel_cmd) + aero_drag_force)
scaled_torque = torque + params.ZERO_GAS
stock_switch = int(round(np.interp(v_ego, params.BRAKE_SWITCH_LOOKUP_BP, params.BRAKE_SWITCH_LOOKUP_V)))
planner_switch = get_bolt_acc_pedal_planner_brake_switch(
v_ego, params, tire_radius=tire_radius, mass=mass,
coeff_drag=coeff_drag, frontal_area=frontal_area, air_density=air_density,
)
effective_switch = get_bolt_acc_pedal_effective_brake_switch(stock_switch, planner_switch)
stock_brake_accel = min((scaled_torque - stock_switch) / (tire_radius * mass), 0)
effective_brake_accel = min((scaled_torque - effective_switch) / (tire_radius * mass), 0)
assert planner_switch < stock_switch
assert effective_switch == stock_switch
assert stock_brake_accel < 0
assert effective_brake_accel == stock_brake_accel
def test_bolt_acc_pedal_friction_command_state_requires_cruise_main_for_positive_brake():
command_brake, release_frames, should_send = get_bolt_acc_pedal_friction_command_state(120, False, 0)
assert command_brake == 0
assert release_frames == 0
assert not should_send
def test_bolt_acc_pedal_friction_command_state_sends_zero_unwind_after_main_off():
command_brake, release_frames, should_send = get_bolt_acc_pedal_friction_command_state(120, True, 0)
assert command_brake == 120
assert release_frames > 0
assert should_send
command_brake, release_frames, should_send = get_bolt_acc_pedal_friction_command_state(0, False, release_frames)
assert command_brake == 0
assert release_frames >= 0
assert should_send
def test_fixed_stopping_brake_is_disabled_for_bolt_acc_pedal_experiment():
CP = SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
)
assert not should_use_fixed_stopping_brake(CP, True, True, False)
def test_fixed_stopping_brake_stays_enabled_for_normal_acc_path():
CP = SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=False,
flags=0,
)
assert should_use_fixed_stopping_brake(CP, True, True, False)
assert not should_use_fixed_stopping_brake(CP, False, True, False)
assert not should_use_fixed_stopping_brake(CP, True, False, False)
assert not should_use_fixed_stopping_brake(CP, True, True, True)
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 get_stock_cc_active_for_cancel(CP, cs)
assert not should_send_stock_long_cancel(11, cs)
@@ -143,11 +358,48 @@ def test_live_camera_path_does_not_send_pt_keepalive():
assert get_adas_keepalive_step(cp, is_kaofui_car=True) is None
def test_acc_2cd_replacement_only_used_with_live_camera_path():
assert should_send_acc_2cd(SimpleNamespace(
carFingerprint=CAR.CHEVROLET_TRAILBLAZER, networkLocation=CarParams.NetworkLocation.fwdCamera, flags=0))
assert not should_send_acc_2cd(SimpleNamespace(
carFingerprint=CAR.CHEVROLET_TRAILBLAZER, networkLocation=CarParams.NetworkLocation.fwdCamera, flags=GMFlags.NO_CAMERA.value))
assert not should_send_acc_2cd(SimpleNamespace(
carFingerprint=CAR.CHEVROLET_TRAILBLAZER, networkLocation=CarParams.NetworkLocation.gateway, flags=0))
assert not should_send_acc_2cd(SimpleNamespace(
carFingerprint=CAR.CHEVROLET_TRAILBLAZER_CC, networkLocation=CarParams.NetworkLocation.fwdCamera, flags=0))
assert not should_send_acc_2cd(SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BLAZER, networkLocation=CarParams.NetworkLocation.fwdCamera, flags=0))
def test_ascm_int_cars_do_not_send_radar_status():
common = {
"networkLocation": CarParams.NetworkLocation.fwdCamera,
"radarUnavailable": False,
}
assert not should_send_adas_status(SimpleNamespace(carFingerprint=CAR.BUICK_LACROSSE_ASCM, **common), is_kaofui_car=True)
assert not should_send_adas_status(SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM, **common), is_kaofui_car=True)
def test_lacrosse_ascm_marks_acc_dashboard_active_for_aol_only():
cc = SimpleNamespace(enabled=False, latActive=True)
assert get_acc_dashboard_status_active(SimpleNamespace(carFingerprint=CAR.BUICK_LACROSSE_ASCM), cc)
assert not get_acc_dashboard_status_active(SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM), cc)
assert not get_acc_dashboard_status_active(SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023), cc)
def test_acc_dashboard_status_active_for_normal_enabled_cars():
cc = SimpleNamespace(enabled=True, latActive=False)
assert get_acc_dashboard_status_active(SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM), cc)
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(
assert not supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
openpilotLongitudinalControl=True,
@@ -166,6 +418,15 @@ def test_volt_auto_hold_requires_toggle_supported_non_cc_only_volt_and_stock_saf
True,
)
assert supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
openpilotLongitudinalControl=True,
networkLocation=CarParams.NetworkLocation.fwdCamera,
safetyConfigs=stock_safety,
),
True,
)
assert not supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT,
openpilotLongitudinalControl=False,
@@ -174,7 +435,7 @@ def test_volt_auto_hold_requires_toggle_supported_non_cc_only_volt_and_stock_saf
),
True,
)
assert supports_volt_auto_hold(
assert not supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_2019,
openpilotLongitudinalControl=False,
@@ -208,6 +469,74 @@ def test_auto_hold_brake_estimate_uses_driver_or_op_brake_and_clamps():
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
assert estimate_auto_hold_brake(7.0, 0.0, SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_2019)) == 100
def test_volt_one_pedal_requires_toggle_supported_volt_stock_safety_and_ev_transmission():
stock_safety = [SimpleNamespace(safetyParam=0x8000)]
no_safety = [SimpleNamespace(safetyParam=0)]
assert supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
openpilotLongitudinalControl=True,
safetyConfigs=stock_safety,
transmissionType=structs.CarParams.TransmissionType.direct,
),
True,
)
assert not supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
openpilotLongitudinalControl=True,
safetyConfigs=no_safety,
transmissionType=structs.CarParams.TransmissionType.direct,
),
True,
)
assert not supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CC,
openpilotLongitudinalControl=True,
safetyConfigs=stock_safety,
transmissionType=structs.CarParams.TransmissionType.direct,
),
True,
)
assert not supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
openpilotLongitudinalControl=True,
safetyConfigs=stock_safety,
transmissionType=structs.CarParams.TransmissionType.automatic,
),
True,
)
assert not supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
openpilotLongitudinalControl=False,
safetyConfigs=stock_safety,
transmissionType=structs.CarParams.TransmissionType.direct,
),
True,
)
assert not supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
openpilotLongitudinalControl=True,
safetyConfigs=stock_safety,
transmissionType=structs.CarParams.TransmissionType.direct,
),
False,
)
def test_auto_hold_drive_gears_accept_capnp_dynamic_enum_membership():
msg = structs.CarState.new_message()
msg.gearShifter = structs.CarState.GearShifter.drive
assert msg.gearShifter in AUTO_HOLD_DRIVE_GEARS
def test_auto_hold_activation_allows_direct_entry_from_stopped_brake_press():
@@ -216,6 +545,7 @@ def test_auto_hold_activation_allows_direct_entry_from_stopped_brake_press():
False,
False,
True,
False,
True,
False,
False,
@@ -229,6 +559,7 @@ def test_auto_hold_activation_stays_latched_after_brake_release():
False,
True,
False,
False,
True,
False,
False,
@@ -236,12 +567,42 @@ def test_auto_hold_activation_stays_latched_after_brake_release():
)
def test_volt_2019_auto_hold_engaged_uses_near_stop_creep_hysteresis():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_2019)
assert get_auto_hold_stop_threshold(CP, True) == CarControllerParams.NEAR_STOP_BRAKE_PHASE
assert should_activate_auto_hold(
True,
True,
True,
False,
False,
False,
False,
False,
0.05,
get_auto_hold_stop_threshold(CP, True),
)
assert not should_activate_auto_hold(
True,
True,
True,
False,
False,
False,
False,
False,
0.05,
)
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,
False,
True,
True,
False,
@@ -254,6 +615,8 @@ def test_auto_hold_activation_blocks_when_long_is_active_or_motion_is_above_thre
False,
False,
False,
False,
False,
0.03,
)
@@ -264,6 +627,7 @@ def test_auto_hold_activation_allows_standstill_even_if_speed_filter_is_slightly
True,
False,
False,
False,
True,
False,
False,
@@ -271,6 +635,150 @@ def test_auto_hold_activation_allows_standstill_even_if_speed_filter_is_slightly
)
def test_auto_hold_activation_releases_immediately_on_gas_press():
assert not should_activate_auto_hold(
True,
True,
True,
False,
True,
True,
False,
False,
0.0,
)
def test_volt_one_pedal_activation_requires_main_l_mode_and_no_driver_input():
assert should_activate_volt_one_pedal(
True,
True,
False,
False,
False,
False,
True,
structs.CarState.GearShifter.low,
3.0,
)
assert not should_activate_volt_one_pedal(
True,
False,
False,
False,
False,
False,
True,
structs.CarState.GearShifter.low,
3.0,
)
assert not should_activate_volt_one_pedal(
True,
True,
True,
False,
False,
False,
True,
structs.CarState.GearShifter.low,
3.0,
)
assert not should_activate_volt_one_pedal(
True,
True,
False,
True,
False,
False,
True,
structs.CarState.GearShifter.low,
3.0,
)
assert not should_activate_volt_one_pedal(
True,
True,
False,
False,
False,
True,
True,
structs.CarState.GearShifter.low,
3.0,
)
assert not should_activate_volt_one_pedal(
True,
True,
False,
False,
False,
False,
False,
structs.CarState.GearShifter.drive,
3.0,
)
def test_volt_one_pedal_target_decel_stays_active_above_low_speed_band():
assert get_volt_one_pedal_target_decel(0.5 * CV.MPH_TO_MS) == -1.0
assert get_volt_one_pedal_target_decel(6.0 * CV.MPH_TO_MS) == -1.1
assert get_volt_one_pedal_target_decel(20.0 * CV.MPH_TO_MS) == -1.1
def test_volt_one_pedal_regression_ignores_noisy_wheel_direction_bits():
assert should_activate_volt_one_pedal(
True,
True,
False,
False,
False,
False,
True,
structs.CarState.GearShifter.low,
3.0,
)
def test_volt_one_pedal_requires_time_in_drive_before_arming():
assert not should_activate_volt_one_pedal(
True,
True,
False,
False,
False,
False,
True,
structs.CarState.GearShifter.low,
2.5,
)
def test_friction_brake_mode_keeps_near_stop_disabled_for_regular_long_braking():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM)
assert get_friction_brake_mode(120, False, True, False, CP) == 0xa
def test_friction_brake_mode_uses_near_stop_hold_mode_for_volt_auto_hold():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM)
assert get_friction_brake_mode(120, False, True, False, CP, allow_near_stop_mode=True) == 0xb
assert get_friction_brake_mode(120, False, True, True, CP, allow_near_stop_mode=True) == 0xd
def test_friction_brake_mode_uses_stock_bolt_unwind_for_pedal_print_when_enabled():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL)
assert get_friction_brake_mode(0, False, True, False, CP) == 0x1
assert get_friction_brake_mode(0, True, True, False, CP) == 0x9
def test_friction_brake_mode_keeps_bolt_pedal_braking_mode_unchanged():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL)
assert get_friction_brake_mode(120, True, False, False, CP) == 0xa
assert get_friction_brake_mode(120, True, True, True, CP) == 0xd
def test_calc_pedal_command_small_accel_deadband_keeps_creep_target_stable():
pos_controller = _controller()
neg_controller = _controller()
@@ -317,6 +825,58 @@ def test_calc_pedal_command_keeps_strong_positive_requests_responsive():
assert pedal_gas - 0.18 > 0.04
def test_shape_truck_positive_accel_softens_small_highway_requests():
shaped = shape_truck_positive_accel(0.12, 26.0, True)
assert 0.08 < shaped < 0.095
def test_shape_truck_positive_accel_keeps_mid_follow_requests_available():
shaped = shape_truck_positive_accel(0.45, 13.5, True)
assert 0.43 < shaped < 0.45
def test_shape_truck_positive_accel_leaves_large_requests_alone():
assert shape_truck_positive_accel(1.0, 26.0, True) == 1.0
def test_shape_truck_positive_accel_is_inactive_when_disabled_or_low_speed():
assert shape_truck_positive_accel(0.12, 26.0, False) == 0.12
assert shape_truck_positive_accel(0.12, 6.0, True) == 0.12
def test_shape_truck_positive_accel_preserves_more_follow_authority_with_lead():
base = shape_truck_positive_accel(0.28, 26.0, True)
relieved = shape_truck_positive_accel(0.28, 26.0, True, lead_visible=True, set_speed_error=6.0)
assert relieved > base
assert relieved < 0.28
def test_shape_truck_positive_accel_does_not_relax_without_speed_error():
base = shape_truck_positive_accel(0.28, 26.0, True)
no_error = shape_truck_positive_accel(0.28, 26.0, True, lead_visible=True, set_speed_error=0.0)
assert no_error == base
def test_shape_truck_friction_brake_suppresses_boundary_chatter():
assert shape_truck_friction_brake(14, -0.3, False, False) == (0, False)
def test_shape_truck_friction_brake_uses_hysteresis_once_engaged():
assert shape_truck_friction_brake(39, -0.3, False, False) == (0, False)
assert shape_truck_friction_brake(40, -0.3, False, False) == (40, True)
assert shape_truck_friction_brake(14, -0.3, False, True) == (14, True)
assert shape_truck_friction_brake(8, -0.3, False, True) == (0, False)
def test_shape_truck_friction_brake_never_delays_meaningful_braking():
assert shape_truck_friction_brake(5, -0.85, False, False) == (5, True)
assert shape_truck_friction_brake(5, -0.2, True, False) == (5, True)
def test_use_interceptor_sng_launch_requires_actual_near_stop():
CP = SimpleNamespace(vEgoStarting=0.25)
@@ -326,6 +886,42 @@ def test_use_interceptor_sng_launch_requires_actual_near_stop():
assert not use_interceptor_sng_launch(CP, _sng_cs(0.0, True, False))
def test_bolt_acc_pedal_sng_launch_uses_physical_standstill_without_stock_acc_bit():
CP = SimpleNamespace(
vEgoStarting=0.25,
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
enableGasInterceptorDEPRECATED=True,
)
assert use_interceptor_sng_launch(CP, _sng_cs(0.0, True, False))
assert use_interceptor_sng_launch(CP, _sng_cs(0.2, False, False))
assert not use_interceptor_sng_launch(CP, _sng_cs(1.2, False, False))
def test_bolt_acc_pedal_sng_launch_preserves_stronger_computed_pedal():
CP = SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
)
params = SimpleNamespace(SNG_INTERCEPTOR_GAS=18. / 255.)
assert get_interceptor_sng_gas_cmd(CP, 0.2, 0.54, params, False) == pytest.approx(0.2)
def test_other_pedal_sng_launch_keeps_fixed_floor_behavior():
CP = SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
)
params = SimpleNamespace(SNG_INTERCEPTOR_GAS=18. / 255.)
assert get_interceptor_sng_gas_cmd(CP, 0.2, 0.54, params, False) == pytest.approx(18. / 255.)
def test_use_interceptor_sng_launch_extends_for_maneuver_mode():
CP = SimpleNamespace(vEgoStarting=0.25)
+311 -66
View File
@@ -1,16 +1,19 @@
import pytest
import numpy as np
from types import SimpleNamespace
from parameterized import parameterized
from cereal import custom
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.carstate import CarState as GMCarState
from opendbc.car.gm.carstate import CarState as GMCarState, get_hard_cruise_buttons, update_auto_hold_drive_timers
from opendbc.car.gm.carcontroller import (
VisualAlert,
get_acc_dashboard_always_one,
get_acc_dashboard_fcw_alert,
get_acc_dashboard_status_values,
get_volt_one_pedal_lift_brake,
should_send_acc_dashboard_status,
should_send_cc_button_spam,
should_spoof_dash_speed,
@@ -18,7 +21,7 @@ from opendbc.car.gm.carcontroller import (
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.car.gm.values import ASCM_INT, CAMERA_ACC_CAR, CAR, CC_ONLY_CAR, DBC, GM_RX_OFFSET, CarControllerParams, CruiseButtons, GMFlags, GMSafetyFlags
from opendbc.safety import ALTERNATIVE_EXPERIENCE
from openpilot.common.params import Params
@@ -63,6 +66,52 @@ class TestGMFingerprint:
class TestGMInterface:
def test_bolt_acc_pedal_pid_accel_limits_keep_full_negative_authority(self):
cp = SimpleNamespace(
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
)
accel_min, accel_max = gm_interface.CarInterface.get_pid_accel_limits(cp, 4.73, 0.0)
assert accel_min == pytest.approx(CarControllerParams.ACCEL_MIN)
assert accel_max == pytest.approx(np.interp(4.73, [0.0, 1.5, 4.0, 8.0, 15.0],
[0.54, 0.74, 1.03, 1.46, CarControllerParams.ACCEL_MAX]))
def test_bolt_cc_pedal_pid_accel_limits_remain_regen_limited(self):
cp = SimpleNamespace(
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
carFingerprint=CAR.CHEVROLET_BOLT_CC_2022_2023,
)
accel_min, _ = gm_interface.CarInterface.get_pid_accel_limits(cp, 4.73, 0.0)
assert accel_min == pytest.approx(np.interp(4.73, [0.0, 1.5, 4.0, 8.0, 15.0, 30.0],
[-0.93, -1.28, -1.98, -2.58, -2.86, -2.95]))
def test_missing_hard_cruise_signal_defaults_to_init(self):
assert get_hard_cruise_buttons({"ACCButtons": CruiseButtons.RES_ACCEL}) == CruiseButtons.INIT
assert get_hard_cruise_buttons({"ACCButtonsHard": CruiseButtons.DECEL_SET}) == CruiseButtons.DECEL_SET
def test_volt_auto_hold_drive_timer_requires_motion_before_startup_arming(self):
auto_hold_time, one_pedal_time = update_auto_hold_drive_timers(True, False, 0.0, 0.0)
assert auto_hold_time == 0.0
assert one_pedal_time == 0.0
def test_volt_auto_hold_drive_timer_accumulates_only_while_moving(self):
auto_hold_time, one_pedal_time = update_auto_hold_drive_timers(True, True, 0.0, 0.0)
assert auto_hold_time == pytest.approx(DT_CTRL)
assert one_pedal_time == pytest.approx(DT_CTRL)
auto_hold_time, one_pedal_time = update_auto_hold_drive_timers(True, False, auto_hold_time, one_pedal_time)
assert auto_hold_time == pytest.approx(DT_CTRL)
assert one_pedal_time == pytest.approx(DT_CTRL)
@parameterized.expand(VOLT_CARS)
def test_volt_min_steer_speed_is_7_mph(self, car_model):
CarInterface = interfaces[car_model]
@@ -84,12 +133,15 @@ class TestGMInterface:
old_testing_ground = gm_interface.testing_ground
gm_interface.testing_ground = SimpleNamespace(use_2=True)
params = Params()
params.put_bool("GMPedalLongitudinal", True)
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
params.remove("GMPedalLongitudinal")
if pedal_present:
assert list(car_params.longitudinalTuning.kpV) == pytest.approx([0.10, 0.072, 0.05, 0.04])
@@ -117,6 +169,21 @@ class TestGMInterface:
assert car_params.flags & GMFlags.NO_CAMERA.value
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_NO_CAMERA.value
def test_volt_ascm_sparse_fingerprint_without_camera_does_not_set_no_camera(self):
CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM]
fingerprint = {
0: FINGERPRINTS[CAR.CHEVROLET_VOLT][0].copy(),
1: {},
}
fingerprint[0][0x2FF] = 8 # SASCM detected
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=False, is_release=False,
docs=False, starpilot_toggles=_test_starpilot_toggles())
assert not (car_params.flags & GMFlags.NO_CAMERA.value)
assert not (car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_NO_CAMERA.value)
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.HW_ASCM_INT.value
def test_silverado_alpha_long_uses_trimmed_longitudinal_tune(self):
CarInterface = interfaces[CAR.CHEVROLET_SILVERADO]
fingerprint = _empty_fingerprint()
@@ -130,7 +197,28 @@ class TestGMInterface:
assert list(car_params.longitudinalTuning.kpBP) == pytest.approx([0.0, 5.0, 15.0, 35.0])
assert list(car_params.longitudinalTuning.kpV) == pytest.approx([0.02, 0.03, 0.028, 0.022])
assert list(car_params.longitudinalTuning.kiBP) == pytest.approx([0.0, 5.0, 15.0, 35.0])
assert list(car_params.longitudinalTuning.kiV) == pytest.approx([0.28, 0.26, 0.20, 0.16])
assert list(car_params.longitudinalTuning.kiV) == pytest.approx([0.20, 0.18, 0.13, 0.08])
def test_blazer_uses_softer_low_speed_stop_hold_tune(self):
CarInterface = interfaces[CAR.CHEVROLET_BLAZER]
fingerprint = _empty_fingerprint()
fingerprint[0] = FINGERPRINTS[CAR.CHEVROLET_BLAZER][0].copy()
fingerprint[0][0x2FF] = 8 # SASCM present so alpha-long can enable on this platform
car_params = CarInterface.get_params(CAR.CHEVROLET_BLAZER, fingerprint, [], alpha_long=True, is_release=False,
docs=False, starpilot_toggles=_test_starpilot_toggles())
assert car_params.openpilotLongitudinalControl
assert list(car_params.longitudinalTuning.kpBP) == pytest.approx([0.0, 4.0, 12.0, 35.0])
assert list(car_params.longitudinalTuning.kpV) == pytest.approx([0.09, 0.075, 0.055, 0.04])
assert list(car_params.longitudinalTuning.kiBP) == pytest.approx([0.0, 4.0, 12.0, 35.0])
assert list(car_params.longitudinalTuning.kiV) == pytest.approx([0.03, 0.04, 0.055, 0.07])
assert car_params.longitudinalActuatorDelay == pytest.approx(0.7)
assert car_params.minEnableSpeed == pytest.approx(5 * CV.KPH_TO_MS)
assert car_params.stoppingDecelRate == pytest.approx(1.0)
assert car_params.vEgoStopping == pytest.approx(0.35)
assert car_params.vEgoStarting == pytest.approx(0.35)
assert car_params.stopAccel == pytest.approx(-0.30)
def test_volt_gateway_without_accel_pos_uses_brake_pedal_message(self):
CarInterface = interfaces[CAR.CHEVROLET_VOLT]
@@ -163,6 +251,60 @@ class TestGMInterface:
assert car_params.openpilotLongitudinalControl
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
def test_volt_auto_hold_does_not_set_stock_hold_safety_bit_with_op_long_disabled(self):
CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM]
fingerprint = _empty_fingerprint()
fingerprint[0][0x2FF] = 8
params = Params()
try:
params.put_bool("GMAutoHold", True)
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=False, is_release=False,
docs=False, starpilot_toggles=_test_starpilot_toggles())
finally:
params.remove("GMAutoHold")
assert not car_params.openpilotLongitudinalControl
assert not (car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value)
def test_volt_one_pedal_sets_stock_hold_safety_bit_without_auto_hold(self):
CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM]
fingerprint = _empty_fingerprint()
fingerprint[0][0x2FF] = 8
params = Params()
try:
params.put_bool("GMAutoHold", False)
params.put_bool("VoltOnePedalMode", True)
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=True, is_release=False,
docs=False, starpilot_toggles=_test_starpilot_toggles())
finally:
params.remove("GMAutoHold")
params.remove("VoltOnePedalMode")
assert car_params.openpilotLongitudinalControl
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_3D1_SCHED.value
def test_volt_one_pedal_does_not_set_stock_hold_safety_bits_with_op_long_disabled(self):
CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM]
fingerprint = _empty_fingerprint()
fingerprint[0][0x2FF] = 8
params = Params()
try:
params.put_bool("GMAutoHold", False)
params.put_bool("VoltOnePedalMode", True)
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=False, is_release=False,
docs=False, starpilot_toggles=_test_starpilot_toggles())
finally:
params.remove("GMAutoHold")
params.remove("VoltOnePedalMode")
assert not car_params.openpilotLongitudinalControl
assert not (car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value)
assert not (car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_3D1_SCHED.value)
@parameterized.expand(VOLT_CARS)
def test_volt_bsm_is_enabled_without_fingerprint_match(self, car_model):
CarInterface = interfaces[car_model]
@@ -209,14 +351,20 @@ class TestGMInterface:
params = Params()
toggles = _test_starpilot_toggles()
try:
params.put_bool("GMPedalLongitudinal", True)
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("GMPedalLongitudinal")
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
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
assert car_params.startingState
assert car_params.startAccel == pytest.approx(0.55)
assert car_params.vEgoStarting == pytest.approx(0.35)
def test_cadillac_xt5_sdgm_sascm_gates_alpha_long(self):
CarInterface = interfaces[CAR.CADILLAC_XT5]
@@ -242,6 +390,62 @@ class TestGMInterface:
assert not sascm_params.pcmCruise
assert sascm_params.safetyConfigs[0].safetyParam & GMSafetyFlags.HW_CAM_LONG.value
def test_cadillac_escalade_esv_2019_ascm_uses_sascm_and_2019_tune(self):
base_fingerprint = FINGERPRINTS[CAR.CADILLAC_ESCALADE_ESV_2019][0]
ascm_fingerprint = FINGERPRINTS[CAR.CADILLAC_ESCALADE_ESV_2019_ASCM][0]
assert CAR.CADILLAC_ESCALADE_ESV_2019_ASCM in ASCM_INT
assert ascm_fingerprint[0x2FF] == 8
assert {addr: length for addr, length in ascm_fingerprint.items() if addr != 0x2FF} == base_fingerprint
CarInterface = interfaces[CAR.CADILLAC_ESCALADE_ESV_2019_ASCM]
fingerprint = _empty_fingerprint()
fingerprint[0] = ascm_fingerprint.copy()
car_params = CarInterface.get_params(CAR.CADILLAC_ESCALADE_ESV_2019_ASCM, fingerprint, [], alpha_long=True, is_release=False,
docs=False, starpilot_toggles=_test_starpilot_toggles())
assert car_params.flags & GMFlags.SASCM.value
assert car_params.networkLocation == structs.CarParams.NetworkLocation.fwdCamera
assert car_params.openpilotLongitudinalControl
assert not car_params.pcmCruise
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.HW_ASCM_INT.value
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.HW_CAM_LONG.value
assert car_params.lateralTuning.torque.latAccelFactor == pytest.approx(1.15)
assert car_params.lateralTuning.torque.friction == pytest.approx(0.2)
def test_cadillac_xt4_uses_nonlinear_torque_curve_with_center_boost(self):
CarInterface = interfaces[CAR.CADILLAC_XT4]
car_params = CarInterface.get_non_essential_params(CAR.CADILLAC_XT4)
ci = CarInterface(car_params, custom.StarPilotCarParams.new_message())
torque_from_lataccel = ci.torque_from_lateral_accel()
low_lataccel = 0.2
high_lataccel = 1.0
low_torque = torque_from_lataccel(low_lataccel, car_params.lateralTuning.torque)
high_torque = torque_from_lataccel(high_lataccel, car_params.lateralTuning.torque)
linear_low_torque = low_lataccel / car_params.lateralTuning.torque.latAccelFactor
linear_high_torque = high_lataccel / car_params.lateralTuning.torque.latAccelFactor
assert low_torque > linear_low_torque * 1.15
assert low_torque < linear_low_torque * 1.30
assert high_torque == pytest.approx(linear_high_torque, rel=0.03)
assert torque_from_lataccel(-low_lataccel, car_params.lateralTuning.torque) == pytest.approx(-low_torque, rel=1e-6)
@parameterized.expand((CAR.CHEVROLET_VOLT_ASCM, CAR.CHEVROLET_VOLT_CAMERA, CAR.CHEVROLET_VOLT_CC, CAR.CHEVROLET_VOLT_2019))
def test_volt_integration_variants_share_nonlinear_torque_curve(self, candidate):
assert gm_interface.get_nonlinear_torque_params(candidate) == gm_interface.NON_LINEAR_TORQUE_PARAMS[CAR.CHEVROLET_VOLT]
CarInterface = interfaces[candidate]
car_params = CarInterface.get_non_essential_params(candidate)
ci = CarInterface(car_params, custom.StarPilotCarParams.new_message())
torque_from_lataccel = ci.torque_from_lateral_accel()
left_torque = torque_from_lataccel(0.5, car_params.lateralTuning.torque)
right_torque = torque_from_lataccel(-0.5, car_params.lateralTuning.torque)
assert left_torque > abs(right_torque)
class TestGMCarController:
def test_dash_speed_spoof_respects_live_stock_acc_toggles(self):
@@ -256,6 +460,11 @@ class TestGMCarController:
assert should_spoof_dash_speed(cp, SimpleNamespace(disable_openpilot_long=False))
def test_volt_one_pedal_lift_brake_seeds_low_speed_braking(self):
assert get_volt_one_pedal_lift_brake(2.1 * CV.MPH_TO_MS) == 0
assert get_volt_one_pedal_lift_brake(2.0 * CV.MPH_TO_MS) == 20
assert get_volt_one_pedal_lift_brake(0.10) == 80
def test_volt_camera_no_camera_sends_acc_dashboard_without_dash_spoof(self):
cp = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_CAMERA, flags=GMFlags.NO_CAMERA.value)
@@ -348,44 +557,122 @@ class TestGMCarController:
assert [msg[2] for msg in msgs] == [0]
def test_xt4_cc_redneck_spam_matches_physical_button_burst(self):
packer = CANPacker(DBC[CAR.CADILLAC_XT4_CC][Bus.pt])
controller = SimpleNamespace(
frame=int(0.3 / DT_CTRL),
last_button_frame=0,
apply_speed=0,
malibu_button_phase=0,
xt4_cc_button_burst_remaining=0,
xt4_cc_button_burst_button=CruiseButtons.INIT,
xt4_cc_button_burst_last_counter=-1,
xt4_cc_button_observed_counter=-1,
xt4_cc_button_counter_frame=0,
)
cs = SimpleNamespace(
CP=SimpleNamespace(
carFingerprint=CAR.CADILLAC_XT4_CC,
flags=GMFlags.CC_LONG.value,
minEnableSpeed=24 * CV.MPH_TO_MS,
),
buttons_counter=0,
out=SimpleNamespace(
vEgo=25.0,
cruiseState=SimpleNamespace(speed=20.0),
),
)
actuators = SimpleNamespace(accel=1.0)
dats = []
send_counts = []
for counter in (0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 0, 0, 0, 1, 1, 1):
cs.buttons_counter = counter
msgs = gmcan.create_gm_cc_spam_command(packer, controller, cs, actuators, SimpleNamespace(is_metric=False))
send_counts.append(len(msgs))
dats.extend(bytes(msg[1]).hex() for msg in msgs)
controller.frame += 1
assert send_counts == [0, 1, 0] * gmcan.XT4_CC_BUTTON_BURST_FRAMES
assert dats == [
"000000010125de",
"00000001022acd",
"00000001032fbc",
"000000010020ef",
"000000010125de",
"00000001022acd",
]
assert controller.xt4_cc_button_burst_remaining == 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,
},
True,
100,
SimpleNamespace(leadDistanceBars=3, leadVisible=True),
0x2,
)
parser.update([0, [msg]])
assert parser.vl["ASCMActiveCruiseControlStatus"]["FCWAlert"] == 2
values = parser.vl["ASCMActiveCruiseControlStatus"]
def test_acc_dashboard_command_can_replay_stock_status_payload(self):
packer = CANPacker(DBC[CAR.CHEVROLET_VOLT_ASCM][Bus.pt])
assert values["ACCAlwaysOne"] == 1
assert values["ACCAlwaysOne2"] == 1
assert values["ACCCruiseState"] == 2
assert values["ACCCmdActive"] == 1
assert values["FCWAlert"] == 2
def test_acc_dashboard_command_allows_camera_acc_zero_reserved_bits(self):
packer = CANPacker(DBC[CAR.CHEVROLET_TRAILBLAZER][Bus.pt])
parser = CANParser(DBC[CAR.CHEVROLET_TRAILBLAZER][Bus.pt], [("ASCMActiveCruiseControlStatus", 0)], 0)
msg = gmcan.create_acc_dashboard_command(
packer,
0,
{
"ACCCruiseState": 0,
"ACCLeadCar": 1,
"ACCResumeButton": 0,
"ACCSpeedSetpoint": 50,
"ACCGapLevel": 2,
"ACCCmdActive": 0,
},
True,
67.1875,
SimpleNamespace(leadDistanceBars=1, leadVisible=False),
0,
acc_always_one=0,
)
parser.update([0, [msg]])
values = parser.vl["ASCMActiveCruiseControlStatus"]
assert msg[1] == b"\x00\x02\x94\x33\x00\x00"
assert values["ACCAlwaysOne"] == 0
assert values["ACCAlwaysOne2"] == 0
assert values["ACCCruiseState"] == 2
assert values["ACCCmdActive"] == 1
def test_acc_dashboard_always_one_matches_camera_acc_platforms(self):
assert get_acc_dashboard_always_one(SimpleNamespace(carFingerprint=CAR.CHEVROLET_TRAILBLAZER)) == 0
assert get_acc_dashboard_always_one(SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023)) == 1
def test_acc_dashboard_command_uses_openpilot_hud_when_disengaged(self):
packer = CANPacker(DBC[CAR.CHEVROLET_VOLT_ASCM][Bus.pt])
parser = CANParser(DBC[CAR.CHEVROLET_VOLT_ASCM][Bus.pt], [("ASCMActiveCruiseControlStatus", 0)], 0)
msg = gmcan.create_acc_dashboard_command(
packer,
0,
False,
50,
SimpleNamespace(leadDistanceBars=2, leadVisible=True),
0x3,
)
assert msg[1].hex() == "010023200113"
parser.update([0, [msg]])
values = parser.vl["ASCMActiveCruiseControlStatus"]
assert values["ACCSpeedSetpoint"] == 50
assert values["ACCCruiseState"] == 2
assert values["ACCGapLevel"] == 0
assert values["ACCCmdActive"] == 0
assert values["ACCLeadCar"] == 1
assert values["FCWAlert"] == 3
def test_acc_dashboard_fcw_alert_prefers_openpilot_alert(self):
cs = SimpleNamespace(
@@ -418,45 +705,3 @@ class TestGMCarController:
)
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,
}
@@ -1,3 +1,5 @@
from types import SimpleNamespace
from opendbc.can import CANPacker
from opendbc.car.gm import gmcan
from opendbc.car.gm.values import CAR, DBC
@@ -26,6 +28,47 @@ class TestGMCan:
assert dat[1] & 0x1
assert decoded == 8848
def test_acc_2cd_command_matches_stock_camera_counter_layout(self):
assert [gmcan.create_acc_2cd_command(0, idx)[1].hex() for idx in range(4)] == [
"002c03d3fd",
"402c03d3fc",
"802c03d3fb",
"c02c03d3fa",
]
def test_prndl2_command_matches_bolt_gen2_regen_paddle_spoof(self):
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL)
addr, dat, bus = gmcan.create_prndl2_command(self.packer, 0, False, CP)
assert addr == 0x1F5
assert bus == 0
assert dat.hex() == "0c0c000600000100"
addr, dat, bus = gmcan.create_prndl2_command(self.packer, 0, True, CP)
assert addr == 0x1F5
assert bus == 0
assert dat.hex() == "0c0c000500020100"
def test_prndl2_command_matches_bolt_gen1_regen_paddle_spoof(self):
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021)
addr, dat, bus = gmcan.create_prndl2_command(self.packer, 0, True, CP)
assert addr == 0x1F5
assert bus == 0
assert dat.hex() == "0c0c000700020100"
def test_regen_paddle_command_matches_bolt_spoof(self):
addr, dat, bus = gmcan.create_regen_paddle_command(self.packer, 0, False)
assert addr == 0xBD
assert bus == 0
assert dat.hex() == "00000000000000"
addr, dat, bus = gmcan.create_regen_paddle_command(self.packer, 0, True)
assert addr == 0xBD
assert bus == 0
assert dat.hex() == "20000000000000"
def test_gas_regen_command_matches_starpilot_volt_2019(self):
packer = CANPacker(DBC[CAR.CHEVROLET_VOLT_2019]["pt"])
@@ -42,11 +85,3 @@ class TestGMCan:
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"
+22 -1
View File
@@ -283,6 +283,10 @@ class CAR(Platforms):
[GMCarDocs("Buick LaCrosse 2017-19 ASCM Harness")],
BUICK_LACROSSE.specs,
)
BUICK_LACROSSE_ASCM_19US = GMPlatformConfig(
[GMCarDocs("Buick LaCrosse 2019 US 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),
@@ -303,6 +307,10 @@ class CAR(Platforms):
[GMCarDocs("Cadillac Escalade ESV 2019", "Adaptive Cruise Control (ACC) & LKAS")],
CADILLAC_ESCALADE_ESV.specs,
)
CADILLAC_ESCALADE_ESV_2019_ASCM = GMPlatformConfig(
[GMCarDocs("Cadillac Escalade ESV Platinum 2019 ASCM Harness", "Adaptive Cruise Control (ACC) & LKAS")],
CADILLAC_ESCALADE_ESV_2019.specs,
)
CHEVROLET_BOLT_ACC_2022_2023 = GMPlatformConfig(
[
GMCarDocs("Chevrolet Bolt ACC 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", video="https://youtu.be/xvwzGMUA210"),
@@ -364,6 +372,10 @@ class CAR(Platforms):
[GMCarDocs("Cadillac XT4 2023", "Driver Assist Package")],
GMCarSpecs(mass=1660, wheelbase=2.78, steerRatio=14.4, centerToFrontRatio=0.4),
)
CADILLAC_XT4_CC = GMPlatformConfig(
[GMCarDocs("Cadillac XT4 - No-ACC")],
CADILLAC_XT4.specs,
)
CADILLAC_XT5 = GMSDGMPlatformConfig(
[GMCarDocs("Cadillac XT5 2022", "Driver Assist Package")],
CarSpecs(mass=1810, wheelbase=2.86, steerRatio=16.34, centerToFrontRatio=0.5),
@@ -566,6 +578,7 @@ CC_ONLY_CAR = {
CAR.CHEVROLET_MALIBU_CC,
CAR.CHEVROLET_MALIBU_HYBRID_CC,
CAR.CHEVROLET_SILVERADO_CC,
CAR.CADILLAC_XT4_CC,
}
CC_REGEN_PADDLE_CAR = {
CAR.CHEVROLET_BOLT_CC_2018_2021,
@@ -576,7 +589,15 @@ 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, CAR.BUICK_LACROSSE_ASCM}
ASCM_INT = {
CAR.CHEVROLET_VOLT_ASCM,
CAR.GMC_ACADIA_ASCM,
CAR.CHEVROLET_MALIBU_ASCM,
CAR.CADILLAC_ESCALADE_ASCM,
CAR.CADILLAC_ESCALADE_ESV_2019_ASCM,
CAR.BUICK_LACROSSE_ASCM,
CAR.BUICK_LACROSSE_ASCM_19US,
}
STEER_THRESHOLD = 1.0
+313 -44
View File
@@ -4,11 +4,12 @@ import numpy as np
from opendbc.can import CANPacker
from opendbc.car import Bus, DT_CTRL, make_tester_present_msg, rate_limit, structs
from opendbc.car.common.filter_simple import FirstOrderFilter
from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_steer_angle_limits_vm, common_fault_avoidance
from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_steer_angle_limits_vm, common_fault_avoidance, get_max_angle_delta_vm, get_max_angle_vm
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, CANFD_RADAR_LIVE_LONGITUDINAL_CAR
from opendbc.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CAR, CANFD_ANGLE_LONGITUDINAL_CAR, \
CANFD_RADAR_LIVE_LONGITUDINAL_CAR, kia_ev6_gt_line_longitudinal_tuning
from opendbc.car.interfaces import CarControllerBase
from opendbc.car.vehicle_model import VehicleModel
from openpilot.common.params import Params
@@ -52,6 +53,7 @@ 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]
EV6_GT_LINE_STOP_BRAKE_CAP_MAX_SPEED = 1.2
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]
@@ -63,6 +65,24 @@ 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]
ANGLE_SAFETY_BASELINE_MODEL = str(CAR.KIA_SPORTAGE_HEV_2026)
DEFAULT_ANGLE_SMOOTHING_VEGO_BP = [5.0, 10.0, 20.0]
DEFAULT_ANGLE_SMOOTHING_ALPHA_V = [0.2, 0.1, 0.0]
EV9_STOP_REQUEST_SPEED = 0.47
EV9_STANDSTILL_DELAY_FRAMES = 178
EV9_STOP_RELEASE_DELAY_FRAMES = 6
BLINDSPOT_WARNING_FLASH_SAMPLES = 20
BLINDSPOT_WARNING_FLASH_ON_SAMPLES = 16
BLINDSPOT_WARNING_SOUND_SAMPLES = 36
def egmp_dynamic_longitudinal_tuning(CP) -> bool:
return CP.carFingerprint in (CAR.HYUNDAI_IONIQ_6, CAR.KIA_EV9) or \
kia_ev6_gt_line_longitudinal_tuning(CP.carFingerprint, getattr(CP, "carVin", ""))
def should_reset_ev6_gt_line_longitudinal_tuning(CP, long_control_state: LongCtrlState) -> bool:
return kia_ev6_gt_line_longitudinal_tuning(CP.carFingerprint, getattr(CP, "carVin", "")) and \
long_control_state == LongCtrlState.off
@dataclass
@@ -78,6 +98,13 @@ class Ioniq6LongitudinalTuningState:
long_control_state_last: LongCtrlState = LongCtrlState.off
def reset_ev6_gt_line_longitudinal_tuning(state: Ioniq6LongitudinalTuningState, CP,
long_control_state: LongCtrlState) -> Ioniq6LongitudinalTuningState:
if should_reset_ev6_gt_line_longitudinal_tuning(CP, long_control_state):
return Ioniq6LongitudinalTuningState(long_control_state_last=long_control_state)
return state
@dataclass
class GenesisG90LongitudinalTuningState:
actual_accel: float = 0.0
@@ -85,6 +112,29 @@ class GenesisG90LongitudinalTuningState:
long_control_state_last: LongCtrlState = LongCtrlState.off
@dataclass(frozen=True)
class EV9LongitudinalTuningState:
stop_request: bool = False
cruise_standstill: bool = False
stop_request_frames: int = 0
release_frames: int = 0
@dataclass(frozen=True)
class BlindspotWarningOutput:
mirror_lamp_active: bool = False
sound_active: bool = False
@dataclass
class BlindspotWarningState:
flash_phase: int = 0
mirror_warning_active: bool = False
escalated_prev: bool = False
sound_remaining: int = 0
sound_armed: bool = True
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))
@@ -97,8 +147,93 @@ def _calculate_ioniq_6_dynamic_lower_jerk(accel_error: float) -> float:
return IONIQ_6_LONG_MIN_JERK
def should_track_stop_accel_directly(stopping: bool, v_ego: float,
accel_cmd: float, actual_accel: float) -> bool:
return bool(stopping and v_ego > EV6_GT_LINE_STOP_BRAKE_CAP_MAX_SPEED and accel_cmd < actual_accel)
def should_use_ev6_gt_line_stop_direct_tracking(ev6_gt_line: bool, stopping: bool, v_ego: float,
accel_cmd: float, actual_accel: float) -> bool:
return bool(ev6_gt_line and stopping and v_ego > EV6_GT_LINE_STOP_BRAKE_CAP_MAX_SPEED and accel_cmd < actual_accel)
def update_ev9_longitudinal_tuning(state: EV9LongitudinalTuningState, enabled: bool,
stopping: bool, v_ego: float) -> EV9LongitudinalTuningState:
if not enabled:
return EV9LongitudinalTuningState()
if stopping:
if not state.stop_request and v_ego > EV9_STOP_REQUEST_SPEED:
return EV9LongitudinalTuningState()
frames = state.stop_request_frames + 1 if state.stop_request else 0
return EV9LongitudinalTuningState(
stop_request=True,
cruise_standstill=frames >= EV9_STANDSTILL_DELAY_FRAMES,
stop_request_frames=frames,
)
if state.stop_request:
release_frames = state.release_frames + 1
if release_frames <= EV9_STOP_RELEASE_DELAY_FRAMES:
return EV9LongitudinalTuningState(
stop_request=True,
cruise_standstill=False,
stop_request_frames=state.stop_request_frames,
release_frames=release_frames,
)
return EV9LongitudinalTuningState()
def update_blindspot_warning(state: BlindspotWarningState, escalated: bool,
blinker: bool) -> BlindspotWarningOutput:
if not blinker:
state.flash_phase = 0
state.mirror_warning_active = False
state.escalated_prev = False
state.sound_remaining = 0
state.sound_armed = True
return BlindspotWarningOutput()
rising = escalated and not state.escalated_prev
if rising:
state.flash_phase = 0
state.mirror_warning_active = True
if state.sound_armed:
state.sound_remaining = BLINDSPOT_WARNING_SOUND_SAMPLES
state.sound_armed = False
elif escalated:
state.flash_phase = (state.flash_phase + 1) % BLINDSPOT_WARNING_FLASH_SAMPLES
state.mirror_warning_active = True
elif state.mirror_warning_active and state.flash_phase < BLINDSPOT_WARNING_FLASH_ON_SAMPLES - 1:
state.flash_phase += 1
else:
state.flash_phase = 0
state.mirror_warning_active = False
state.escalated_prev = escalated
sound_active = state.sound_remaining > 0
if state.sound_remaining > 0:
state.sound_remaining -= 1
return BlindspotWarningOutput(
mirror_lamp_active=state.mirror_warning_active and state.flash_phase < BLINDSPOT_WARNING_FLASH_ON_SAMPLES,
sound_active=sound_active,
)
def reset_egmp_longitudinal_tuning(state: Ioniq6LongitudinalTuningState) -> Ioniq6LongitudinalTuningState:
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
return state
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:
long_control_state: LongCtrlState, long_active: bool,
ev6_gt_line: bool = False, low_speed_stop_brake_cap: bool = False) -> 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 \
@@ -140,7 +275,9 @@ def update_ioniq_6_longitudinal_tuning(state: Ioniq6LongitudinalTuningState, acc
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_max_speed = EV6_GT_LINE_STOP_BRAKE_CAP_MAX_SPEED if ev6_gt_line or low_speed_stop_brake_cap else \
IONIQ_6_STOP_BRAKE_CAP_MAX_SPEED
if v_ego <= 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)
@@ -202,6 +339,16 @@ def get_baseline_safety_cp():
return CarInterface.get_non_essential_params(ANGLE_SAFETY_BASELINE_MODEL)
def get_angle_smoothing_alpha(CP, v_ego: float) -> float:
return float(np.interp(v_ego, DEFAULT_ANGLE_SMOOTHING_VEGO_BP, DEFAULT_ANGLE_SMOOTHING_ALPHA_V))
def direct_angle_request_allowed(v_ego_raw, measured_angle, last_angle, drive_gear, VM, params):
safety_v_ego = max(v_ego_raw - 1.0, 1.0)
max_safety_angle = get_max_angle_vm(safety_v_ego, VM, params)
return drive_gear and abs(measured_angle) <= max_safety_angle and abs(last_angle) <= max_safety_angle
def compute_torque_reduction_gain(steering_torque, v_ego, lat_active, last_gain):
if lat_active:
ceiling = np.interp(v_ego, [0.5, 1.5], [1.0, 0.85])
@@ -253,6 +400,7 @@ class CarController(CarControllerBase):
self.VM = VehicleModel(CP)
self.BASELINE_VM = VehicleModel(get_baseline_safety_cp()) if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING else self.VM
self.angle_filter = FirstOrderFilter(0.0, 0.2, DT_CTRL)
self.direct_angle_request_allowed = True
self.accel_last = 0
self.apply_torque_last = 0
@@ -263,6 +411,10 @@ class CarController(CarControllerBase):
self.ecu_disable_failed = False
self._ecu_disable_checked = False
self._params = Params()
if CP.carFingerprint == CAR.KIA_EV9:
self._ev9_long_tuning = EV9LongitudinalTuningState()
self._left_blindspot_warning = BlindspotWarningState()
self._right_blindspot_warning = BlindspotWarningState()
self.long_active_ecu = self.CP.openpilotLongitudinalControl
self._ioniq_6_lane_change_ui_side = None
self._ioniq_6_lane_change_ui_frames = 0
@@ -356,36 +508,52 @@ class CarController(CarControllerBase):
if not self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
self.params = CarControllerParams(self.CP, CS.out.vEgoRaw)
apply_angle = CS.out.steeringAngleDeg
direct_angle_control = self.CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR and self.long_active_ecu
measured_steering_angle = CS.angle_steering_angle if direct_angle_control else CS.out.steeringAngleDeg
angle_lat_active = CC.latActive
if direct_angle_control and CC.latActive:
drive_gear = CS.out.gearShifter == structs.CarState.GearShifter.drive
angle_lat_active = direct_angle_request_allowed(CS.out.vEgoRaw, measured_steering_angle, self.apply_angle_last,
drive_gear, self.BASELINE_VM, self.params) and not CS.angle_steering_fault
self.direct_angle_request_allowed = angle_lat_active
apply_angle = measured_steering_angle
if self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
v_ego_raw = CS.out.vEgoRaw
desired_angle = float(np.clip(actuators.steeringAngleDeg,
-self.params.ANGLE_LIMITS.STEER_ANGLE_MAX,
self.params.ANGLE_LIMITS.STEER_ANGLE_MAX))
self.angle_filter.update_alpha(float(np.interp(CS.out.vEgo, [5.0, 10.0, 20.0], [0.2, 0.1, 0.0])))
self.angle_filter.update_alpha(get_angle_smoothing_alpha(self.CP, CS.out.vEgo))
desired_angle = self.angle_filter.update(desired_angle)
apply_angle = apply_steer_angle_limits_vm(desired_angle, self.apply_angle_last, v_ego_raw,
CS.out.steeringAngleDeg, CC.latActive, self.params, self.VM)
measured_steering_angle, angle_lat_active, self.params, self.VM)
if str(self.CP.carFingerprint) != ANGLE_SAFETY_BASELINE_MODEL:
apply_angle = apply_steer_angle_limits_vm(apply_angle or desired_angle, self.apply_angle_last, v_ego_raw,
CS.out.steeringAngleDeg, CC.latActive, self.params, self.BASELINE_VM)
measured_steering_angle, angle_lat_active, self.params, self.BASELINE_VM)
apply_torque = compute_torque_reduction_gain(CS.out.steeringTorque, v_ego_raw, CC.latActive, self.apply_torque_last)
apply_steer_req = CC.latActive and apply_torque != 0.0
if direct_angle_control and angle_lat_active:
# Match Panda's 1 m/s speed tolerance so a shrinking absolute limit stays inside its jerk envelope.
safety_v_ego = max(v_ego_raw - 1.0, 1.0)
max_angle_delta = min(get_max_angle_delta_vm(safety_v_ego, self.BASELINE_VM, self.params),
self.params.ANGLE_LIMITS.MAX_ANGLE_RATE)
apply_angle = float(np.clip(apply_angle,
self.apply_angle_last - max_angle_delta,
self.apply_angle_last + max_angle_delta))
apply_torque = compute_torque_reduction_gain(CS.out.steeringTorque, v_ego_raw, angle_lat_active, self.apply_torque_last)
apply_steer_req = angle_lat_active and apply_torque != 0.0
torque_fault = False
if apply_angle is None:
apply_torque = 0
apply_angle = CS.out.steeringAngleDeg
apply_angle = measured_steering_angle
apply_steer_req = False
self.apply_angle_last = apply_angle
if not CC.latActive:
self.apply_angle_last = float(np.clip(CS.out.steeringAngleDeg,
if not angle_lat_active:
self.apply_angle_last = float(np.clip(measured_steering_angle,
-self.params.ANGLE_LIMITS.STEER_ANGLE_MAX,
self.params.ANGLE_LIMITS.STEER_ANGLE_MAX))
self.angle_filter.x = self.apply_angle_last
@@ -426,22 +594,37 @@ 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:
use_egmp_dynamic_long_tuning = egmp_dynamic_longitudinal_tuning(self.CP) and self.long_active_ecu and \
actuators.longControlState in (LongCtrlState.starting, LongCtrlState.pid, LongCtrlState.stopping)
is_ev6_gt_line = kia_ev6_gt_line_longitudinal_tuning(self.CP.carFingerprint, getattr(self.CP, "carVin", ""))
is_ev9 = self.CP.carFingerprint == CAR.KIA_EV9
if is_ev9 and (self._ev9_long_tuning.stop_request or not CC.enabled or CC.cruiseControl.override):
self._ioniq_6_long_tuning = reset_egmp_longitudinal_tuning(self._ioniq_6_long_tuning)
if should_reset_ev6_gt_line_longitudinal_tuning(self.CP, actuators.longControlState):
self._ioniq_6_long_tuning = reset_ev6_gt_line_longitudinal_tuning(self._ioniq_6_long_tuning, self.CP,
actuators.longControlState)
elif use_egmp_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 (
actuators.longControlState, self.long_active_ecu,
ev6_gt_line=is_ev6_gt_line,
low_speed_stop_brake_cap=is_ev9)
use_egmp_smoothed_accel = use_egmp_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:
if should_use_ev6_gt_line_stop_direct_tracking(is_ev6_gt_line, self._ioniq_6_long_tuning.stopping,
CS.out.vEgo, accel_cmd, self._ioniq_6_long_tuning.actual_accel):
use_egmp_smoothed_accel = False
if is_ev9 and should_track_stop_accel_directly(self._ioniq_6_long_tuning.stopping, CS.out.vEgo,
accel_cmd, self._ioniq_6_long_tuning.actual_accel):
use_egmp_smoothed_accel = False
if use_egmp_dynamic_long_tuning:
if use_egmp_smoothed_accel:
accel = self._ioniq_6_long_tuning.actual_accel
stopping = self._ioniq_6_long_tuning.stopping
elif use_ioniq_6_dynamic_long_tuning:
else:
accel = float(np.clip(accel_cmd,
self.accel_last - IONIQ_6_CANFD_SCC_DECEL_STEP,
self.accel_last + IONIQ_6_CANFD_SCC_ACCEL_STEP))
@@ -566,25 +749,64 @@ class CarController(CarControllerBase):
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
use_egmp_dynamic_long_tuning = egmp_dynamic_longitudinal_tuning(self.CP) and self.long_active_ecu and \
CC.actuators.longControlState in (LongCtrlState.starting, LongCtrlState.pid, LongCtrlState.stopping)
use_egmp_smoothed_accel = use_egmp_dynamic_long_tuning and (
CC.actuators.accel >= self._ioniq_6_long_tuning.actual_accel or
self._ioniq_6_long_tuning.launch_active or
self._ioniq_6_long_tuning.stopping
)
# 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,
CS.stock_lfa_msg,
CS.stock_lkas_msg if preserve_stock_lkas else None,
lka_icon=lka_icon))
preserve_stock_lkas = bool(self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING) and not self.long_active_ecu
angle_lkas_alt = bool(self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING and
self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT)
ccnc_angle_long = self.CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR and \
self.CP.flags & HyundaiFlags.CCNC and angle_lkas_alt and self.long_active_ecu
steering_msg_active = apply_steer_req
if angle_lkas_alt:
# Angle LKAS_ALT cars fault if the angle-steering status drops inactive during torque limiting.
# Hold the angle status active while lateral is active; VM/safety limits handle actuation.
steering_msg_active = CC.latActive
gear = getattr(getattr(CS, "out", None), "gearShifter", None)
drive_gear = gear == structs.CarState.GearShifter.drive
if angle_lkas_alt:
steering_msg_active = bool(steering_msg_active and drive_gear)
angle_lkas_alt_standstill_handoff = bool(getattr(CS.out, "standstill", False) and not CC.latActive)
forward_stock_lkas = angle_lkas_alt and (
angle_lkas_alt_standstill_handoff or not (drive_gear and (CC.latActive or CC.enabled))
)
if not forward_stock_lkas and not ccnc_angle_long:
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled,
steering_msg_active, apply_torque, apply_angle,
CS.stock_lfa_msg,
CS.stock_lkas_msg if preserve_stock_lkas else None,
lka_icon=lka_icon))
direct_steering_active = ccnc_angle_long and drive_gear and CC.latActive and self.direct_angle_request_allowed and not CS.angle_steering_fault
inactive_steering_angle = float(np.clip(CS.angle_steering_angle,
-self.params.ANGLE_LIMITS.STEER_ANGLE_MAX,
self.params.ANGLE_LIMITS.STEER_ANGLE_MAX)) if ccnc_angle_long else 0.0
if ccnc_angle_long and drive_gear:
can_sends.append(hyundaicanfd.create_angle_adas_cmd(
self.packer, self.CAN,
apply_angle if direct_steering_active else inactive_steering_angle,
direct_steering_active, apply_torque if direct_steering_active else 0.0,
))
if ccnc_angle_long and not drive_gear:
can_sends.extend(hyundaicanfd.create_inactive_angle_steering_messages(self.packer, self.CAN,
inactive_steering_angle))
# 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:
suppress_lfa = bool(lka_steering)
if angle_lkas_alt:
suppress_lfa = bool(lka_steering and drive_gear and (CC.latActive or (ccnc_angle_long and CC.enabled)))
if self.frame % 5 == 0 and suppress_lfa:
can_sends.append(hyundaicanfd.create_suppress_lfa(self.packer, self.CAN, CS.lfa_block_msg,
self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT))
# LFA and HDA icons
if self.frame % 5 == 0 and (not lka_steering or lka_steering_long):
if self.frame % 5 == 0 and (not lka_steering or lka_steering_long) and not ccnc_angle_long:
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,
@@ -620,12 +842,41 @@ class CarController(CarControllerBase):
if self.long_active_ecu:
if lka_steering:
can_sends.extend(hyundaicanfd.create_adrv_messages(self.packer, self.CAN, self.frame))
# 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))
if ccnc_angle_long:
left_escalated = CS.left_blindspot_from_radar and CC.leftBlinker and not CC.rightBlinker
right_escalated = CS.right_blindspot_from_radar and CC.rightBlinker and not CC.leftBlinker
left_warning = BlindspotWarningOutput()
right_warning = BlindspotWarningOutput()
if self.frame % 5 == 0:
left_warning = update_blindspot_warning(
self._left_blindspot_warning, left_escalated, CC.leftBlinker,
)
right_warning = update_blindspot_warning(
self._right_blindspot_warning, right_escalated, CC.rightBlinker,
)
steering_available = CC.latActive or CC.enabled
steering_active = direct_steering_active and apply_steer_req and not CS.out.steeringPressed
adrv_messages = hyundaicanfd.create_ccnc_adrv_messages(
self.packer, self.CP, self.CAN, self.frame, CC.enabled, CS.out.cruiseState.available, CC.hudControl,
CS.out, CS.is_metric, steering_available, steering_active,
CS.left_blindspot_from_radar, CS.right_blindspot_from_radar,
drive_gear=drive_gear,
hba_icon=CS.hba_icon,
left_escalated=left_escalated, right_escalated=right_escalated,
left_warning_lamp=left_warning.mirror_lamp_active,
right_warning_lamp=right_warning.mirror_lamp_active,
left_sound_active=left_warning.sound_active, right_sound_active=right_warning.sound_active,
)
else:
adrv_messages = hyundaicanfd.create_adrv_messages(self.packer, self.CAN, self.frame)
can_sends.extend(adrv_messages)
# The front radar treats ADAS_DRV's 0x100 broadcast as its host heartbeat
# and stops publishing object tracks when it disappears.
radar_heartbeat_step = 1 if ccnc_angle_long else 4
if self.CP.carFingerprint in CANFD_RADAR_LIVE_LONGITUDINAL_CAR and self.frame % radar_heartbeat_step == 0:
can_sends.append(hyundaicanfd.create_accelerator_brake_alt_spoof(0, self.frame // radar_heartbeat_step,
CS.out.brakePressed, CS.out.gasPressed,
self.CP.carFingerprint))
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:
@@ -656,13 +907,31 @@ class CarController(CarControllerBase):
"lead_rel_speed": lead_rel_speed,
"lead_visible": lead_visible,
}
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
if use_ioniq_6_smoothed_accel:
if use_egmp_dynamic_long_tuning:
if use_egmp_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, cruise_info=CS.cruise_info if ccnc_non_hda2 else None,
**acc_kwargs))
if ccnc_angle_long:
self._ev9_long_tuning = update_ev9_longitudinal_tuning(
self._ev9_long_tuning, CC.enabled and not CC.cruiseControl.override,
CC.actuators.longControlState == LongCtrlState.stopping, float(CS.out.vEgo),
)
if self._ev9_long_tuning.stop_request or not CC.enabled or CC.cruiseControl.override:
self._ioniq_6_long_tuning = reset_egmp_longitudinal_tuning(self._ioniq_6_long_tuning)
accel = 0.0
can_sends.append(hyundaicanfd.create_ccnc_acc_control(
self.packer, self.CAN, CC.enabled, accel,
self._ev9_long_tuning.stop_request, self._ev9_long_tuning.cruise_standstill, CC.cruiseControl.override,
set_speed_in_units, int(CS.out.cruiseState.available), lead_distance, lead_rel_speed, lead_visible,
float(CS.out.vEgo),
jerk_lower=acc_kwargs["jerk_lower"],
jerk_upper=acc_kwargs["jerk_upper"],
))
else:
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, cruise_info=CS.cruise_info if ccnc_non_hda2 else None, **acc_kwargs,
))
self.accel_last = accel
else:
# button presses
+78 -10
View File
@@ -8,6 +8,7 @@ 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, HyundaiStarPilotSafetyFlags, CAR, DBC, Buttons, CarControllerParams, \
CANFD_ANGLE_LONGITUDINAL_CAR, CANFD_CORNER_RADAR_BSM_CAR, \
hyundai_cancel_button_enables_cruise, ALT_BUS_LDA_BUTTON_CARS, ALT_BUS_LDA_BUTTON_SWL_STAT_CARS
from opendbc.car.interfaces import CarStateBase
@@ -75,7 +76,14 @@ 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.sonata_hybrid_lkas_source = None
self.sonata_hybrid_lkas_sources = {
"bcm": 0,
"clu13": 0,
"swl_stat": 0,
}
self.lda_button_raw = 0
self.lda_button_raw_initialized = False
self.lda_button_last_raw_rise_ts_nanos = 0
self.left_paddle = 0
self.mode_button = 0
@@ -129,6 +137,11 @@ class CarState(CarStateBase):
self.blindspots_front_corner_1_ts = 0
self.left_blindspot_from_radar = False
self.right_blindspot_from_radar = False
if CP.carFingerprint == CAR.KIA_EV9:
self.hba_icon = 0
self.main_cruise_on = False
self.angle_steering_angle = 0.0
self.angle_steering_fault = False
# On some cars, CLU15->CF_Clu_VehicleSpeed can oscillate faster than the dash updates. Sample at 5 Hz
self.cluster_speed = 0
@@ -142,6 +155,12 @@ 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 update_main_cruise(self, ret: structs.CarState) -> bool:
if any(be.type == ButtonType.mainCruise and be.pressed for be in ret.buttonEvents):
self.main_cruise_on = not self.main_cruise_on
return bool(ret.cruiseState.available and self.main_cruise_on)
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 = (
@@ -174,15 +193,20 @@ class CarState(CarStateBase):
return False
def create_alt_bus_lda_button_events(self, cp_source: CANParser) -> list[structs.CarState.ButtonEvent]:
def get_alt_bus_lda_button_raw_state(self, cp_source: CANParser) -> tuple[int, int]:
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"]
return int(cp_source.vl["CLU13"]["CF_Clu_SWL_Stat"] == 4), cp_source.ts_nanos["CLU13"]["CF_Clu_SWL_Stat"]
return int(cp_source.vl["CLU13"]["CF_Clu_LdwsLkasSW"]), cp_source.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"]
def create_alt_bus_lda_button_events(self, cp_source: CANParser) -> list[structs.CarState.ButtonEvent]:
raw_lda_button, raw_lda_button_ts_nanos = self.get_alt_bus_lda_button_raw_state(cp_source)
button_events: list[structs.CarState.ButtonEvent] = []
if not self.lda_button_raw_initialized:
self.lda_button_raw_initialized = True
self.lda_button_raw = raw_lda_button
return button_events
# 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:
@@ -198,8 +222,10 @@ class CarState(CarStateBase):
return button_events
def create_lkas_button_events(self, cp: CANParser, prev_lda_button: int) -> list[structs.CarState.ButtonEvent]:
if self.CP.carFingerprint == CAR.HYUNDAI_SONATA_HYBRID:
self.lda_button = self.get_sonata_hybrid_lkas_button_state(cp)
# 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:
elif 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"])
@@ -208,6 +234,32 @@ class CarState(CarStateBase):
return create_button_events(self.lda_button, prev_lda_button, {1: ButtonType.lkas})
def get_sonata_hybrid_lkas_button_state(self, cp: CANParser) -> int:
source_states = {
"bcm": int(cp.vl["BCM_PO_11"]["LDA_BTN"]) if cp.ts_nanos["BCM_PO_11"]["LDA_BTN"] > 0 else 0,
"clu13": int(cp.vl["CLU13"]["CF_Clu_LdwsLkasSW"]) if cp.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"] > 0 else 0,
"swl_stat": int(cp.vl["CLU13"]["CF_Clu_SWL_Stat"] == 4) if cp.ts_nanos["CLU13"]["CF_Clu_SWL_Stat"] > 0 else 0,
}
changed_sources = [source for source, state in source_states.items() if state != self.sonata_hybrid_lkas_sources[source]]
active_sources = [source for source, state in source_states.items() if state]
selected_source = None
if self.sonata_hybrid_lkas_source in changed_sources:
selected_source = self.sonata_hybrid_lkas_source
elif active_sources:
selected_source = active_sources[0]
elif changed_sources:
selected_source = changed_sources[0]
elif self.sonata_hybrid_lkas_source is not None:
selected_source = self.sonata_hybrid_lkas_source
self.sonata_hybrid_lkas_sources.update(source_states)
if selected_source is not None:
self.sonata_hybrid_lkas_source = selected_source
return source_states[selected_source]
return 0
def update(self, can_parsers, starpilot_toggles) -> structs.CarState:
cp = can_parsers[Bus.pt]
cp_cam = can_parsers[Bus.cam]
@@ -348,7 +400,7 @@ class CarState(CarStateBase):
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.carFingerprint in ALT_BUS_LDA_BUTTON_CARS and cp_alt is not None and cp_alt.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"] > 0:
if self.CP.carFingerprint in ALT_BUS_LDA_BUTTON_CARS and cp_alt is not None and self.get_alt_bus_lda_button_raw_state(cp_alt)[1] > 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)
@@ -408,6 +460,10 @@ class CarState(CarStateBase):
ret.steeringTorqueEps = cp.vl["MDPS"]["STEERING_OUT_TORQUE"]
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > self.params.STEER_THRESHOLD, 5)
ret.steerFaultTemporary = cp.vl["MDPS"]["LKA_FAULT"] != 0
if self.CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR:
self.angle_steering_angle = cp.vl["MDPS"]["STEERING_ANGLE_2"]
self.angle_steering_fault = cp.vl["MDPS"]["LKA_ANGLE_FAULT"] != 0
ret.steerFaultTemporary = ret.steerFaultTemporary or self.angle_steering_fault
ccnc_non_hda2 = self.CP.flags & HyundaiFlags.CCNC and not self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING
if ccnc_non_hda2:
@@ -423,11 +479,12 @@ class CarState(CarStateBase):
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:
corner_radar_bsm = self.CP.carFingerprint in CANFD_CORNER_RADAR_BSM_CAR
if corner_radar_bsm:
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:
if corner_radar_bsm:
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
@@ -497,6 +554,9 @@ class CarState(CarStateBase):
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 self.CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR and cp.ts_nanos["FR_CMR_01_10ms"]["FR_CMR_Crc1Val"] > 0:
hba_icon = int(cp.vl["FR_CMR_01_10ms"]["HBA_IndLmpReq"])
self.hba_icon = hba_icon if hba_icon in (1, 2) else 0
if cp.ts_nanos["BLINKER_STALKS"]["CHECKSUM_MAYBE"] > 0:
self.stock_blinker_stalks_ts = cp.ts_nanos["BLINKER_STALKS"]["CHECKSUM_MAYBE"]
@@ -504,6 +564,8 @@ class CarState(CarStateBase):
*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})]
if self.CP.openpilotLongitudinalControl and self.CP.carFingerprint == CAR.KIA_EV9:
ret.cruiseState.available = self.update_main_cruise(ret)
ret.blockPcmEnable = not self.recent_button_interaction()
@@ -552,6 +614,12 @@ class CarState(CarStateBase):
("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.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR and CP.enableBsm:
# Keep the suppressed ADAS BSM output optional.
msgs.append(("BLINDSPOTS_REAR_CORNERS", 0))
if CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR:
msgs.append(("BLINDSPOTS_FRONT_CORNER_2", 0))
msgs.append(("FR_CMR_01_10ms", 0))
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
@@ -38,6 +38,14 @@ FW_VERSIONS = {
b'\xf1\x00IGhe SCC FHCUP 1.00 1.02 99110-M9000 ',
],
},
CAR.HYUNDAI_AZERA_HEV_7TH_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00GN7HMFC AT KOR LHD 1.00 1.01 99211-N1110 240423',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00GN7_ RDR ----- 1.00 1.00 99110-N1100 ',
],
},
CAR.HYUNDAI_GENESIS: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00DH LKAS 1.1 -150210',
@@ -607,6 +615,17 @@ FW_VERSIONS = {
b'\xf1\x00CD ESC \x0b 101 \x10\x03 58910-J7AC0',
],
},
CAR.KIA_XCEED_PHEV: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00CDph SCC F-CUP 1.00 1.01 99110-CR100 ',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00CDe MDPS C 1.00 1.01 56310-XX000 4CDHC101',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CD2 LKAS AT EUR LHD 1.00 1.01 99211-CR010 621',
],
},
CAR.KIA_FORTE: {
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00BD MDPS C 1.00 1.02 56310-XX000 4BD2C102',
@@ -980,6 +999,21 @@ FW_VERSIONS = {
b'\xf1\x00CN ESC \t 105 \x10\x03 58910-AA800',
],
},
CAR.HYUNDAI_ELANTRA_2024: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00CN7_ RDR ----- 1.00 1.01 99110-AA500 ',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00CN7 MDPS C 1.00 1.02 56300AA670\x00 4CSDC102',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.02 99210-AA500 230420',
b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.03 99210-AA500 230918',
],
(Ecu.abs, 0x7d1, None): [
b'\xf1\x00CN ESC \t 104#\x07\x03 58910-AA850',
],
},
CAR.HYUNDAI_ELANTRA_HEV_2021: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.03 99210-AA000 200819',
@@ -999,6 +1033,22 @@ FW_VERSIONS = {
b'\xf1\x00CN7 MDPS C 1.00 1.04 56310BY050\x00 4CNHC104',
],
},
CAR.HYUNDAI_ELANTRA_HEV_2024: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CN7HMFC AT AUS RHD 1.00 1.02 99210-AA500 230420',
b'\xf1\x00CN7HMFC AT CAN LHD 1.00 1.05 99210-AA510 240509',
b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.03 99210-AA500 230918',
b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.05 99210-AA510 240509',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00CN7_ RDR ----- 1.00 1.01 99110-AA500 ',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00CN7 MDPS C 1.00 1.00 56300BY670\x00 4CSHC100',
b'\xf1\x00CN7 MDPS C 1.00 1.00 56300BY680\x00 4CSHC100',
b'\xf1\x00CN7 MDPS C 1.00 1.03 56300BY670\x00 4CSHC103',
],
},
CAR.HYUNDAI_KONA_HEV: {
(Ecu.abs, 0x7d1, None): [
b'\xf1\x00OS IEB \x01 104 \x11 58520-CM000',
@@ -1462,6 +1512,26 @@ FW_VERSIONS = {
b'\xf1\x00KA4c SCC FHCUP 1.00 1.01 99110-I4000 ',
],
},
CAR.KIA_CARNIVAL_2025: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00KA4 MFC AT CAN LHD 1.00 1.00 99210-R0700 250324',
b'\xf1\x00KA4 MFC AT USA LHD 1.00 1.05 99210-R0500 240305',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00KA4_ SCC FHCUP 1.00 1.01 99110-R0510 ',
b'\xf1\x00KA4_ RDR ----- 1.00 1.01 99110-R0510 ',
],
},
CAR.KIA_CARNIVAL_HEV_4TH_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00KA4HMFC AT USA LHD 1.00 1.05 99210-R0500 240305',
b'\xf1\x00KA4HMFC AT KOR LHD 1.00 1.00 99210-R0600 240924',
b'\xf1\x00KA4HMFC AT USA LHD 1.00 1.00 99210-R0700 250324',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00KAhe RDR ----- 1.00 1.01 99110-ES500 ',
],
},
CAR.KIA_K8_HEV_1ST_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00GL3HMFC AT KOR LHD 1.00 1.03 99211-L8000 210907',
@@ -39,8 +39,9 @@ def create_lkas11(packer, frame, CP, apply_torque, steer_req,
CAR.HYUNDAI_IONIQ_EV_2020, CAR.HYUNDAI_IONIQ_PHEV, CAR.KIA_SELTOS, CAR.HYUNDAI_ELANTRA_2021, CAR.GENESIS_G70_2020,
CAR.HYUNDAI_ELANTRA_HEV_2021, CAR.HYUNDAI_SONATA_HYBRID, CAR.HYUNDAI_KONA_EV, CAR.HYUNDAI_KONA_HEV, CAR.HYUNDAI_KONA_EV_2022,
CAR.HYUNDAI_SANTA_FE_2022, CAR.KIA_K5_2021, CAR.HYUNDAI_IONIQ_HEV_2022, CAR.HYUNDAI_SANTA_FE_HEV_2022,
CAR.HYUNDAI_SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_CEED,
CAR.HYUNDAI_AZERA_6TH_GEN, CAR.HYUNDAI_AZERA_HEV_6TH_GEN, CAR.HYUNDAI_CUSTIN_1ST_GEN, CAR.HYUNDAI_KONA_2022):
CAR.HYUNDAI_SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_CEED, CAR.KIA_XCEED_PHEV,
CAR.HYUNDAI_AZERA_6TH_GEN, CAR.HYUNDAI_AZERA_HEV_6TH_GEN, CAR.HYUNDAI_CUSTIN_1ST_GEN, CAR.HYUNDAI_KONA_2022,
CAR.HYUNDAI_ELANTRA_2024, CAR.HYUNDAI_ELANTRA_HEV_2024):
values["CF_Lkas_LdwsActivemode"] = int(left_lane) + (int(right_lane) << 1)
values["CF_Lkas_LdwsOpt_USM"] = 2
@@ -3,7 +3,7 @@ import numpy as np
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
from opendbc.car.hyundai.values import HyundaiFlags, CAR
def _set_value(msg: bytearray, sig, ival: int) -> None:
@@ -92,10 +92,15 @@ 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_angle_adas_cmd(packer, CAN, apply_angle: float, lat_active: bool, torque_reduction_gain: float):
return _create_angle_adas_cmd_msg(packer, CAN, apply_angle, lat_active, torque_reduction_gain)
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
angle_lkas_alt = CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING and CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
control_values = {
"LKA_MODE": 2,
@@ -128,6 +133,59 @@ def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque,
lkas_values["ADAS_StrAnglReqVal"] = apply_angle
lkas_values["LKAS_ANGLE_ACTIVE"] = 2 if lat_active else 1
lkas_values["ADAS_ACIAnglTqRedcGainVal"] = apply_torque if lat_active else 0.0
if angle_lkas_alt:
if lat_active:
lkas_values = {
"LKA_OptUsmSta": 0,
"LKA_RcgSta": 3,
"LKA_LHLnWrnSta": 0,
"LKA_RHLnWrnSta": 0,
"LKA_HndsoffSnd": 0,
"LKA_StrSnd": 0,
"LKA_SysIndReq": 2,
"StrTqReqVal": 0,
"ActToiSta": 0,
"ToiFltSta": 0,
"LFA_BUTTON": 0,
"LKA_SysWrn": 0,
"Damping_Gain": 100,
"LKAS_ANGLE_ACTIVE": 2,
"LKA_UsmMod": 0,
"ADAS_StrAnglReqVal": apply_angle,
"ADAS_ACIAnglTqRedcGainVal": apply_torque,
}
else:
lkas_values.update({
"LKA_OptUsmSta": 0,
"LKA_MODE": 0,
"LKA_RcgSta": 0,
"LKA_AVAILABLE": 0,
"LKA_LHLnWrnSta": 0,
"LKA_RHLnWrnSta": 0,
"LKA_WARNING": 0,
"LKA_HndsoffSnd": 0,
"LKA_StrSnd": 2,
"LKA_SysIndReq": 1,
"LKA_ICON": 1,
"FCA_SYSWARN": 0,
"StrTqReqVal": 0,
"TORQUE_REQUEST": 0,
"ActToiSta": 0,
"STEER_REQ": 0,
"ToiFltSta": 0,
"LFA_BUTTON": 0,
"LKA_SysWrn": 0,
"LKA_ASSIST": 0,
"Damping_Gain": 0,
"STEER_MODE": 0,
"NEW_SIGNAL_2": 0,
"LKAS_ANGLE_ACTIVE": 1,
"LKA_UsmMod": 0,
"HAS_LANE_SAFETY": 0,
"ADAS_ACIAnglTqRedcGainVal": 0.0,
"DAMP_FACTOR": 0,
})
lkas_values["ADAS_StrAnglReqVal"] = lkas_base_values.get("ADAS_StrAnglReqVal", apply_angle) if lkas_base_values else apply_angle
ret = []
if CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
@@ -150,6 +208,25 @@ def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque,
return ret
def create_inactive_angle_steering_messages(packer, CAN, steering_angle: float):
lfa_values = {
"LKA_MODE": 2,
"LKA_ICON": 1,
"TORQUE_REQUEST": 0,
"LKA_ASSIST": 0,
"STEER_REQ": 0,
"STEER_MODE": 0,
"HAS_LANE_SAFETY": 0,
"NEW_SIGNAL_1": 0,
"NEW_SIGNAL_2": 0,
"DAMP_FACTOR": 100,
}
return [
packer.make_can_msg("LFA", CAN.ECAN, lfa_values),
create_angle_adas_cmd(packer, CAN, steering_angle, False, 0.0),
]
def create_suppress_lfa(packer, CAN, lfa_block_msg, lka_steering_alt):
suppress_msg = "CAM_0x362" if lka_steering_alt else "CAM_0x2a4"
msg_bytes = 32 if lka_steering_alt else 24
@@ -343,6 +420,35 @@ def create_blindspot_status_messages(packer, CAN, rear_values, front_corner_valu
]
def create_ccnc_blindspot_status_messages(packer, CP, CAN, counter, left_blindspot=False, right_blindspot=False,
left_escalated=False, right_escalated=False, drive_gear=False,
left_warning_lamp=False, right_warning_lamp=False,
left_sound_active=False, right_sound_active=False):
left_state = 2 if left_blindspot and left_escalated else (1 if left_blindspot else 0)
right_state = 2 if right_blindspot and right_escalated else (1 if right_blindspot else 0)
left_osm_state = 2 if left_warning_lamp else 1 if left_state == 1 else 0
right_osm_state = 2 if right_warning_lamp else 1 if right_state == 1 else 0
desired_fields = {
"BCW_IndSta": 1,
"BCA_OnOffEquip2Sta": 2,
"BCA_Sta": int(drive_gear),
"BCW_LtIndSta": left_state,
"BCW_RtIndSta": right_state,
"BCW_LtSndWrngSta": int(left_sound_active),
"BCW_RtSndWrngSta": int(right_sound_active),
"OSMrrLamp_LtIndSta": left_osm_state,
"OSMrrLamp_RtIndSta": right_osm_state,
}
return [
_create_ccnc_adrv_message_with_signals(
packer, CP, CAN, 0x1BA, counter, "BLINDSPOTS_REAR_CORNERS", desired_fields,
),
# No retained radar input reproduces the stock RCTA target decision across routes.
_create_ccnc_adrv_message(CP.carFingerprint, 0x1E5, CAN.ECAN, counter),
]
IONIQ_6_CLUSTER_BLINDSPOT_31A = {
"right": (
bytes.fromhex("fa7c10f0f0ffff03898aff0b0a8678ff000000007e0055550000000000000000"),
@@ -687,6 +793,30 @@ def create_adrv_messages(packer, CAN, frame):
return ret
def create_ccnc_adrv_messages(packer, CP, CAN, frame, enabled, main_cruise_enabled, hud, out, is_metric,
steering_available, steering_active, left_blindspot, right_blindspot,
drive_gear=False,
hba_icon=0,
left_escalated=False, right_escalated=False,
left_warning_lamp=False, right_warning_lamp=False,
left_sound_active=False, right_sound_active=False):
ret = [
_create_ccnc_adrv_message(CP.carFingerprint, address, CAN.ECAN, frame // period)
for address, period in _CCNC_ADRV_PERIODS[CP.carFingerprint].items() if frame % period == 0
]
if frame % 5 == 0:
ret.extend(create_ccnc_angle_long_status_messages(
packer, CP, CAN, frame // 5, enabled, main_cruise_enabled, hud, out, is_metric,
steering_available, steering_active, hba_icon,
))
ret.extend(create_ccnc_blindspot_status_messages(
packer, CP, CAN, frame // 5, left_blindspot, right_blindspot, left_escalated, right_escalated,
drive_gear,
left_warning_lamp, right_warning_lamp, left_sound_active, right_sound_active,
))
return ret
def hkg_can_fd_checksum(address: int, sig, d: bytearray) -> int:
crc = 0
for i in range(2, len(d)):
@@ -709,11 +839,44 @@ def hkg_can_fd_checksum(address: int, sig, d: bytearray) -> int:
# 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.
# Byte templates captured from real ADAS broadcasts; only checksum, counter,
# brake, and accelerator bits are updated for the radar heartbeat.
_ACCEL_BRAKE_ALT_TEMPLATE = bytes.fromhex("000000020000fcff000000000020000055ff000068000000")
_KIA_EV9_ACCEL_BRAKE_ALT_TEMPLATE = bytes.fromhex("00000000ff006f00e80400001201030055ffff0000000000")
# Neutral bodies verified across stock and successful suppression routes. Only
# rolling integrity fields and the decoded state above are changed at runtime.
_CCNC_ADRV_TEMPLATES = {
CAR.KIA_EV9: {
0x160: bytes.fromhex("0000000100000000fffc0100a8001000"),
0x1DA: bytes.fromhex("0000002200110000000000000000000000000000000000000000000000000000"),
0x1EA: bytes.fromhex("000000080000000000000000000000ff000000000000000000000000000f0f00"),
0x200: bytes.fromhex("00000014801a0000"),
0x345: bytes.fromhex("0000001500560000"),
0x161: bytes.fromhex("0000000000000000c0fff0c003000040000000000000000000ff000000000000"),
0x162: bytes.fromhex("0000002700000000000000000000000000000000000000000000000000000000"),
0x1BA: bytes.fromhex("00000000000000880200000000000000000100000000000f"),
0x1E5: bytes.fromhex("00000000000000000000220300000080"),
0x1E0: bytes.fromhex("00000002000000000000000000000000"),
0x38C: bytes.fromhex("000000f71f000000000000000000000000000000000000000000000000000000"),
},
}
_CCNC_ADRV_PERIODS = {
CAR.KIA_EV9: {
0x160: 2,
0x1DA: 100,
0x1EA: 5,
0x200: 5,
0x345: 20,
0x1E0: 5,
0x38C: 20,
},
}
def create_accelerator_brake_alt_spoof(bus: int, counter: int, brake_pressed: bool, accelerator_pressed: bool) -> CanData:
d = bytearray(_ACCEL_BRAKE_ALT_TEMPLATE)
def create_accelerator_brake_alt_spoof(bus: int, counter: int, brake_pressed: bool, accelerator_pressed: bool,
car_fingerprint=None) -> CanData:
template = _KIA_EV9_ACCEL_BRAKE_ALT_TEMPLATE if car_fingerprint == CAR.KIA_EV9 else _ACCEL_BRAKE_ALT_TEMPLATE
d = bytearray(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)
@@ -721,3 +884,111 @@ def create_accelerator_brake_alt_spoof(bus: int, counter: int, brake_pressed: bo
d[0] = crc & 0xFF
d[1] = (crc >> 8) & 0xFF
return CanData(0x100, bytes(d), bus)
def _create_ccnc_adrv_message(car_fingerprint, address: int, bus: int, counter: int) -> CanData:
d = bytearray(_CCNC_ADRV_TEMPLATES[car_fingerprint][address])
d[2] = counter & 0xFF
crc = hkg_can_fd_checksum(address, None, d)
d[0] = crc & 0xFF
d[1] = (crc >> 8) & 0xFF
return CanData(address, bytes(d), bus)
def _set_ccnc_message_signals(packer, message_name: str, dat: bytearray, values: dict) -> None:
dbc_msg = packer.dbc.name_to_msg[message_name]
for name, value in values.items():
sig = dbc_msg.sigs[name]
ival = int(np.floor((value - sig.offset) / sig.factor + 0.5))
if ival < 0:
ival = (1 << sig.size) + ival
_set_value(dat, sig, ival)
def _create_ccnc_adrv_message_with_signals(packer, CP, CAN, address: int, counter: int,
message_name: str, values: dict) -> CanData:
msg = _create_ccnc_adrv_message(CP.carFingerprint, address, CAN.ECAN, counter)
dat = bytearray(msg.dat)
# Update decoded fields in the verified neutral payload.
_set_ccnc_message_signals(packer, message_name, dat, values)
crc = hkg_can_fd_checksum(address, None, dat)
dat[0] = crc & 0xFF
dat[1] = (crc >> 8) & 0xFF
return CanData(address, bytes(dat), CAN.ECAN)
def create_ccnc_acc_control(packer, CAN, enabled: bool, accel: float,
stop_request: bool, cruise_standstill: bool, gas_override: bool, set_speed: float,
main_mode_acc: int, lead_distance: float, lead_rel_speed: float, lead_visible: bool,
v_ego: float, jerk_lower: float = 0.7, jerk_upper: float = 0.7):
if not enabled or gas_override or stop_request:
accel = 0.0
lead_visible = bool(enabled and lead_visible)
desired_headway = min(max(round(1.625 * max(v_ego, 0.0), 1), 3.5), 204.6) if enabled else 204.6
values = {
"ACCMode": 0 if not enabled else (2 if gas_override else 1),
"MainMode_ACC": int(bool(main_mode_acc)),
"StopReq": 1 if stop_request and enabled else 0,
"CRUISE_STANDSTILL": 1 if cruise_standstill and stop_request and enabled else 0,
"aReqValue": accel,
"aReqRaw": accel,
"VSetDis": set_speed,
"JerkLowerLimit": jerk_lower if enabled else 1.0,
"JerkUpperLimit": jerk_upper if enabled else 3.0,
"ACC_ObjDist": float(np.clip(lead_distance, 0.0, 204.7)) if lead_visible else 204.6,
"ACC_ObjRelSpd": float(np.clip(lead_rel_speed, -16.4, 34.7)) if lead_visible else 34.6,
"ObjValid": 0 if lead_visible else 1,
"OBJ_STATUS": 2 if enabled and lead_visible else 0,
"NEW_SIGNAL_3": 2 if lead_visible else 0,
"NEW_SIGNAL_15": desired_headway,
"SET_ME_2": 4,
"SET_ME_3": 3,
"SET_ME_TMP_64": 0x64,
# Stock CCNC LKA-long routes use raw 7. The DBC's physical range is stale.
"DISTANCE_SETTING": 7 if enabled else 0,
}
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
def create_ccnc_angle_long_status_messages(packer, CP, CAN, counter: int, enabled: bool = False,
main_cruise_enabled: bool = False, hud=None, out=None,
is_metric: bool = True, steering_available: bool = False,
steering_active: bool = False, hba_icon: int = 0) -> list[CanData]:
cruise_speed = round(out.vCruiseCluster * (1 if is_metric else CV.KPH_TO_MPH)) if out is not None else 0
display_speed = (40 if is_metric else 25) if cruise_speed > (145 if is_metric else 90) else max(cruise_speed, 0)
main_standby = bool(main_cruise_enabled and not enabled)
values_161 = {
"FCA_ICON": 1, # orange: FCA unavailable
"FCA_ALT_ICON": 0,
"FCA_IMAGE": 0,
"ALERTS_1": 0,
"ALERTS_2": 0,
"ALERTS_3": 0,
"ALERTS_4": 0,
"ALERTS_5": 0,
"SOUNDS_1": 0,
"SOUNDS_2": 0,
"SOUNDS_3": 0,
"SOUNDS_4": 0,
"LFA_ICON": (2 if steering_active else 1) if steering_available else 0,
"HBA_ICON": hba_icon if hba_icon in (1, 2) else 0,
"HDA_ICON": 2 if enabled else 1 if main_standby else 0,
"TARGET": 3 if enabled else 0,
"SETSPEED": 3 if enabled else 1 if main_standby else 0,
"SETSPEED_HUD": 2 if enabled else 1 if main_standby else 0,
"SETSPEED_SPEED": display_speed if enabled or main_standby else 255,
"DISTANCE": hud.leadDistanceBars if enabled and hud is not None else 0,
"DISTANCE_SPACING": 3 if enabled or main_standby else 0,
"DISTANCE_CAR": 2 if enabled else 1 if main_standby else 0,
}
values_162 = {fault: 0 for fault in (
"FAULT_FSS", "FAULT_FCA", "FAULT_LSS", "FAULT_SLA", "FAULT_HDA", "FAULT_DAS", "FAULT_LFA", "FAULT_DAW",
"FAULT_HBA", "FAULT_ESS",
)}
values_162["VIBRATE"] = 0
return [
_create_ccnc_adrv_message_with_signals(packer, CP, CAN, 0x161, counter, "CCNC_0x161", values_161),
_create_ccnc_adrv_message_with_signals(packer, CP, CAN, 0x162, counter, "CCNC_0x162", values_162),
]
+58 -12
View File
@@ -4,12 +4,15 @@ from opendbc.car.hyundai.hyundaicanfd import CanBus
from opendbc.car.hyundai.values import HyundaiFlags, CAR, CarControllerParams, \
CANFD_UNSUPPORTED_LONGITUDINAL_CAR, \
CANFD_SECURITYACCESS_CAR, \
CANFD_ANGLE_LONGITUDINAL_CAR, \
CANFD_RADAR_LIVE_LONGITUDINAL_CAR, \
RADAR_LIVE_LONGITUDINAL_CAR, \
UNSUPPORTED_LONGITUDINAL_CAR, HyundaiSafetyFlags, \
LEGACY_LONGITUDINAL_CAR, \
HyundaiStarPilotSafetyFlags, \
hyundai_cancel_button_enables_cruise
from opendbc.car.hyundai.radar_interface import get_radar_track_config
hyundai_cancel_button_enables_cruise, \
kia_ev6_gt_line_longitudinal_tuning
from opendbc.car.hyundai.radar_interface import get_radar_track_config, radar_tracks_available
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
@@ -39,6 +42,18 @@ def apply_platform_longitudinal_params(ret: structs.CarParams) -> None:
ret.stoppingDecelRate = 0.4
def apply_kia_ev6_gt_line_longitudinal_params(ret: structs.CarParams) -> None:
ret.startAccel = 1.4
ret.longitudinalActuatorDelay = 0.35
ret.vEgoStarting = 0.5
def apply_kia_ev9_longitudinal_params(ret: structs.CarParams) -> None:
ret.startAccel = 0.2
ret.longitudinalActuatorDelay = 0.3
ret.vEgoStarting = 0.5
def apply_ecu_disable_failure_fallback(CP: structs.CarParams, params) -> None:
params.put_bool("EcuDisableFailed", True)
CP.safetyConfigs[-1].safetyParam &= ~HyundaiSafetyFlags.LONG.value
@@ -67,6 +82,11 @@ class CarInterface(CarInterfaceBase):
def get_pid_accel_limits(CP, current_speed, cruise_speed):
return ACCEL_MIN, CarControllerParams.ACCEL_MAX
@staticmethod
def apply_post_fingerprint_params(CP: structs.CarParams, candidate, fingerprint, car_fw) -> None:
if kia_ev6_gt_line_longitudinal_tuning(CP.carFingerprint, CP.carVin):
apply_kia_ev6_gt_line_longitudinal_params(CP)
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
ret.brand = "hyundai"
@@ -86,14 +106,14 @@ 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.
if lka_steering and ret.flags & HyundaiFlags.CANFD_ANGLE_STEERING and candidate not in CANFD_ANGLE_LONGITUDINAL_CAR:
# Most angle-steering LKA platforms still need stock longitudinal validation.
ret.alphaLongitudinalAvailable = False
ret.enableBsm = 0x1ba in fingerprint[CAN.ECAN]
ret.enableBsm = 0x1ba in fingerprint[CAN.ECAN] or candidate == CAR.KIA_EV9
# Check if the car is hybrid. Only HEV/PHEV cars have 0xFA on E-CAN.
if 0xFA in fingerprint[CAN.ECAN]:
# Carnival HEV can fingerprint with too little E-CAN traffic to see 0xFA.
if 0xFA in fingerprint[CAN.ECAN] or candidate == CAR.KIA_CARNIVAL_HEV_4TH_GEN:
ret.flags |= HyundaiFlags.HYBRID.value
if lka_steering:
@@ -101,6 +121,10 @@ class CarInterface(CarInterfaceBase):
ret.flags |= HyundaiFlags.CANFD_LKA_STEERING.value
if 0x110 in fingerprint[CAN.CAM]:
ret.flags |= HyundaiFlags.CANFD_LKA_STEERING_ALT.value
# This HDA II Carnival uses the alternate 0x1AA cruise-button frame even
# though other LKA-steering platforms use 0x1CF.
if candidate == CAR.KIA_CARNIVAL_2025 and 0x1aa in fingerprint[CAN.ECAN] and 0x1cf not in fingerprint[CAN.ECAN]:
ret.flags |= HyundaiFlags.CANFD_ALT_BUTTONS.value
else:
# no LKA steering
if 0x1cf not in fingerprint[CAN.ECAN]:
@@ -134,12 +158,19 @@ 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 candidate == CAR.HYUNDAI_IONIQ_6:
# Keep lateral active through stops: zeroing torque at standstill dropped the
# stop-turn hold and forced a rate-limit re-ramp from zero on every pull-away
# (turn1/turn2 rlogs 2026-07-14). Torque steering has no standstill gate in the
# panda safety or the carcontroller; the MDPS tolerating held torque at 0 speed
# is being validated on-road.
ret.steerAtStandstill = True
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.alphaLongitudinalAvailable = candidate not in UNSUPPORTED_LONGITUDINAL_CAR or candidate in LEGACY_LONGITUDINAL_CAR
ret.enableBsm = 0x58b in fingerprint[CAN.ECAN]
# Send LFA message on cars with HDA
@@ -160,6 +191,8 @@ class CarInterface(CarInterfaceBase):
if ret.flags & HyundaiFlags.CAMERA_SCC:
ret.safetyConfigs[0].safetyParam |= HyundaiSafetyFlags.CAMERA_SCC.value
if candidate in (CAR.HYUNDAI_ELANTRA_2024, CAR.HYUNDAI_ELANTRA_HEV_2024):
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CAN_REFRESH_MSGS.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:
@@ -191,19 +224,21 @@ class CarInterface(CarInterfaceBase):
# Common longitudinal control setup
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
radar_config = get_radar_track_config(ret.carFingerprint, ret.flags)
radar_available = radar_tracks_available(radar_config, fingerprint)
ret.radarUnavailable = not radar_available
if ret.flags & HyundaiFlags.NON_SCC:
ret.alphaLongitudinalAvailable = False
ret.openpilotLongitudinalControl = alpha_long and ret.alphaLongitudinalAvailable
if ret.openpilotLongitudinalControl and not (candidate in RADAR_LIVE_LONGITUDINAL_CAR and radar_tracks_available):
if ret.openpilotLongitudinalControl and not (candidate in RADAR_LIVE_LONGITUDINAL_CAR and radar_available):
ret.radarUnavailable = True
ret.pcmCruise = not ret.openpilotLongitudinalControl
apply_platform_longitudinal_params(ret)
if ret.openpilotLongitudinalControl:
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.LONG.value
if candidate in CANFD_ANGLE_LONGITUDINAL_CAR and ret.flags & HyundaiFlags.CCNC:
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CCNC.value
if ret.flags & HyundaiFlags.HYBRID:
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.HYBRID_GAS.value
elif ret.flags & HyundaiFlags.EV:
@@ -224,9 +259,20 @@ class CarInterface(CarInterfaceBase):
ret.vEgoStarting = 0.5
ret.vEgoStopping = 0.35
if candidate == CAR.HYUNDAI_ELANTRA_2021:
ret.longitudinalActuatorDelay = 0.22
ret.stopAccel = -1.5
ret.stoppingDecelRate = 0.5
if candidate == CAR.HYUNDAI_ELANTRA_HEV_2024:
ret.longitudinalActuatorDelay = 0.22
if candidate == CAR.HYUNDAI_IONIQ_6:
ret.longitudinalActuatorDelay = 0.6
if candidate == CAR.KIA_EV9 and ret.openpilotLongitudinalControl:
apply_kia_ev9_longitudinal_params(ret)
if candidate == CAR.KIA_NIRO_PHEV_2022:
ret.stopAccel = -1.4
ret.stoppingDecelRate = 0.5
@@ -1,12 +1,12 @@
import math
from dataclasses import dataclass
from dataclasses import dataclass, replace
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 CAR, DBC, HYUNDAI_MANDO_FRONT_RADAR_DBC, HYUNDAI_MRREVO14F_RADAR_DBC, \
from opendbc.car.hyundai.values import CAR, DBC, HyundaiFlags, HYUNDAI_MANDO_FRONT_RADAR_DBC, HYUNDAI_MRREVO14F_RADAR_DBC, \
HYUNDAI_MRR30_RADAR_DBC, HYUNDAI_MRR35_RADAR_DBC
from openpilot.common.swaglog import cloudlog
@@ -29,6 +29,7 @@ class RadarTrackConfig:
bus: int = 1
frequency: int = 50
parser_msg_count: int | None = None
expected_length: int | None = None
@property
def can_parser_msg_count(self) -> int:
@@ -38,18 +39,37 @@ class RadarTrackConfig:
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),
HYUNDAI_MRR30_RADAR_DBC: RadarTrackConfig(MRR30_RADAR_START_ADDR, MRR30_RADAR_MSG_COUNT, "mrr30", bus=0, expected_length=32),
HYUNDAI_MRR35_RADAR_DBC: RadarTrackConfig(MRR35_RADAR_START_ADDR, MRR35_RADAR_MSG_COUNT, "mrr35", bus=0, frequency=20, expected_length=24),
}
# POC for parsing corner radars: https://github.com/commaai/openpilot/pull/24221/
def get_radar_track_config(car_fingerprint) -> RadarTrackConfig | None:
def get_radar_track_config(car_fingerprint, flags: int = 0) -> 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)
radar_config = RADAR_TRACK_CONFIGS.get(radar_dbc)
if radar_config is None:
return None
if car_fingerprint == CAR.HYUNDAI_IONIQ_6 and flags & HyundaiFlags.CANFD_CAMERA_SCC:
return replace(radar_config, bus=1)
return radar_config
def radar_tracks_available(radar_config: RadarTrackConfig | None, fingerprint) -> bool:
if radar_config is None:
return False
msg_len = fingerprint[radar_config.bus].get(radar_config.start_addr)
if msg_len is None:
return False
return radar_config.expected_length is None or msg_len == radar_config.expected_length
def get_radar_can_parser(CP, radar_config):
@@ -64,7 +84,7 @@ def get_radar_can_parser(CP, radar_config):
class RadarInterface(RadarInterfaceBase):
def __init__(self, CP):
super().__init__(CP)
self.radar_config = get_radar_track_config(CP.carFingerprint)
self.radar_config = get_radar_track_config(CP.carFingerprint, CP.flags)
self.updated_messages = set()
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)
File diff suppressed because it is too large Load Diff
+79 -10
View File
@@ -54,6 +54,9 @@ class CarControllerParams:
if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
self.STEER_THRESHOLD = 175
elif CP.flags & HyundaiFlags.CANFD:
pass
# 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,
@@ -100,6 +103,7 @@ class HyundaiSafetyFlags(IntFlag):
NON_SCC = 4096
CAN_CANFD_BLENDED = 8192
CANCEL_BTN_ENABLE = 16384
CAN_REFRESH_MSGS = 32768
CCNC = 32768
@@ -215,6 +219,11 @@ class HyundaiPlatformConfig(PlatformConfig):
self.dbc_dict = {Bus.pt: "hyundai_palisade_2023_generated"}
@dataclass
class HyundaiRefreshPlatformConfig(HyundaiPlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "hyundai_can_refresh_generated"})
@dataclass
class HyundaiCanFDPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "hyundai_canfd_generated"})
@@ -255,6 +264,14 @@ class CAR(Platforms):
CarSpecs(mass=1675, wheelbase=2.885, steerRatio=14.5),
flags=HyundaiFlags.HYBRID,
)
HYUNDAI_AZERA_HEV_7TH_GEN = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Hyundai Azera Hybrid (with HDA II & LFA2) 2025", "Highway Driving Assist II & Lane Follow Assist 2",
car_parts=CarParts.common([CarHarness.hyundai_s])),
],
CarSpecs(mass=1720, wheelbase=2.895, steerRatio=13.5),
flags=HyundaiFlags.CANFD_ANGLE_STEERING,
)
HYUNDAI_ELANTRA = HyundaiPlatformConfig(
[
# TODO: 2017-18 could be Hyundai G
@@ -278,12 +295,25 @@ class CAR(Platforms):
CarSpecs(mass=2800 * CV.LB_TO_KG, wheelbase=2.72, steerRatio=12.9, tireStiffnessFactor=0.65),
flags=HyundaiFlags.CHECKSUM_CRC8,
)
HYUNDAI_ELANTRA_2024 = HyundaiRefreshPlatformConfig(
[HyundaiCarDocs("Hyundai Elantra 2024-25", car_parts=CarParts.common([CarHarness.hyundai_k]))],
CarSpecs(mass=2797 * CV.LB_TO_KG, wheelbase=2.72, steerRatio=12.9, tireStiffnessFactor=0.65),
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.CAMERA_SCC,
)
HYUNDAI_ELANTRA_HEV_2021 = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Elantra Hybrid 2021-23", video="https://youtu.be/_EdYQtV52-c",
car_parts=CarParts.common([CarHarness.hyundai_k]))],
CarSpecs(mass=3017 * CV.LB_TO_KG, wheelbase=2.72, steerRatio=12.9, tireStiffnessFactor=0.65),
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
)
HYUNDAI_ELANTRA_HEV_2024 = HyundaiRefreshPlatformConfig(
[
HyundaiCarDocs("Hyundai Elantra Hybrid 2024-26", car_parts=CarParts.common([CarHarness.hyundai_k])),
HyundaiCarDocs("Hyundai i30 Hybrid 2024", car_parts=CarParts.common([CarHarness.hyundai_k])),
],
HYUNDAI_ELANTRA_HEV_2021.specs,
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.CAMERA_SCC | HyundaiFlags.HYBRID,
)
HYUNDAI_GENESIS = HyundaiPlatformConfig(
[
# TODO: check 2015 packages
@@ -725,13 +755,18 @@ class CAR(Platforms):
CarSpecs(mass=1450, wheelbase=2.65, steerRatio=13.75, tireStiffnessFactor=0.5),
flags=HyundaiFlags.LEGACY,
)
KIA_XCEED_PHEV = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia XCeed Plug-in Hybrid 2021", car_parts=CarParts.common([CarHarness.hyundai_b]))],
CarSpecs(mass=1650, wheelbase=2.65, steerRatio=13.75, tireStiffnessFactor=0.5),
flags=HyundaiFlags.LEGACY | HyundaiFlags.HYBRID | HyundaiFlags.MANDO_RADAR,
)
KIA_EV6 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia EV6 (Southeast Asia only) 2022-24", "All", car_parts=CarParts.common([CarHarness.hyundai_p])),
HyundaiCarDocs("Kia EV6 (without HDA II) 2022-24", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_l])),
HyundaiCarDocs("Kia EV6 (with HDA II) 2022-24", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p]))
],
CarSpecs(mass=2055, wheelbase=2.9, steerRatio=16, tireStiffnessFactor=0.65),
CarSpecs(mass=2055, wheelbase=2.9, steerRatio=14.25, tireStiffnessFactor=0.65),
flags=HyundaiFlags.EV,
radar_dbc=HYUNDAI_MRR30_RADAR_DBC,
)
@@ -739,7 +774,7 @@ class CAR(Platforms):
[
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),
CarSpecs(mass=2055, wheelbase=2.9, steerRatio=14.26, tireStiffnessFactor=0.65),
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING,
radar_dbc=HYUNDAI_MRR30_RADAR_DBC,
)
@@ -748,7 +783,7 @@ class CAR(Platforms):
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,
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING | HyundaiFlags.CCNC,
radar_dbc=HYUNDAI_MRR35_RADAR_DBC,
)
KIA_CARNIVAL_4TH_GEN = HyundaiCanFDPlatformConfig(
@@ -759,6 +794,24 @@ class CAR(Platforms):
CarSpecs(mass=2087, wheelbase=3.09, steerRatio=14.23),
flags=HyundaiFlags.RADAR_SCC,
)
KIA_CARNIVAL_2025 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia Carnival 2025", car_parts=CarParts.common([CarHarness.hyundai_k])),
HyundaiCarDocs("Kia Carnival (with HDA II) 2025", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])),
],
KIA_CARNIVAL_4TH_GEN.specs,
flags=HyundaiFlags.CCNC,
)
KIA_CARNIVAL_HEV_4TH_GEN = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia Carnival Hybrid 2025", car_parts=CarParts.common([CarHarness.hyundai_k])),
HyundaiCarDocs("Kia Carnival Hybrid 2026", car_parts=CarParts.common([CarHarness.hyundai_a])),
HyundaiCarDocs("Kia Carnival Hybrid (with HDA II) 2025-26", "Highway Driving Assist II",
car_parts=CarParts.common([CarHarness.hyundai_q])),
],
CarSpecs(mass=2253, wheelbase=3.09, steerRatio=14.23),
flags=HyundaiFlags.CCNC,
)
# Genesis
GENESIS_GV60_EV_1ST_GEN = HyundaiCanFDPlatformConfig(
@@ -906,20 +959,22 @@ CANCEL_BUTTON_ENABLE_CARS = frozenset({
CAR.HYUNDAI_PALISADE_2023,
})
KIA_EV6_GT_LINE_LONG_TUNING_VDS_PREFIXES = frozenset({
"C4DLC",
})
# 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,
})
@@ -927,6 +982,11 @@ def hyundai_cancel_button_enables_cruise(car_fingerprint) -> bool:
return car_fingerprint in CANCEL_BUTTON_ENABLE_CARS
def kia_ev6_gt_line_longitudinal_tuning(car_fingerprint, vin: str) -> bool:
return car_fingerprint == CAR.KIA_EV6 and isinstance(vin, str) and \
len(vin) == 17 and vin[3:8] in KIA_EV6_GT_LINE_LONG_TUNING_VDS_PREFIXES
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)
@@ -1015,8 +1075,8 @@ 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_SORENTO_HEV_4TH_GEN_LFA2}
CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_CARNIVAL_2025, CAR.KIA_CARNIVAL_HEV_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
@@ -1083,7 +1143,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
non_essential_ecus={
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],
CAR.KIA_CEED, CAR.KIA_XCEED_PHEV, CAR.KIA_SELTOS],
Ecu.fwdRadar: [CAR.HYUNDAI_KONA_NON_SCC],
},
extra_ecus=[
@@ -1112,9 +1172,15 @@ CANFD_CAR = CAR.with_flags(HyundaiFlags.CANFD)
CANFD_RADAR_SCC_CAR = CAR.with_flags(HyundaiFlags.RADAR_SCC) # TODO: merge with UNSUPPORTED_LONGITUDINAL_CAR
# 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_SECURITYACCESS_CAR = {
CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_6, CAR.HYUNDAI_KONA_EV_2ND_GEN, CAR.KIA_EV9,
}
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}
CANFD_ANGLE_LONGITUDINAL_CAR = {CAR.KIA_EV9}
CANFD_CORNER_RADAR_BSM_CAR = {CAR.HYUNDAI_IONIQ_6, CAR.KIA_EV9}
CANFD_RADAR_LIVE_LONGITUDINAL_CAR = {
CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_6, CAR.KIA_EV6, CAR.KIA_EV9, CAR.GENESIS_GV60_EV_1ST_GEN,
}
RADAR_LIVE_LONGITUDINAL_CAR = CANFD_RADAR_LIVE_LONGITUDINAL_CAR | {
CAR.HYUNDAI_IONIQ,
CAR.HYUNDAI_KONA_EV_2022,
@@ -1123,6 +1189,7 @@ RADAR_LIVE_LONGITUDINAL_CAR = CANFD_RADAR_LIVE_LONGITUDINAL_CAR | {
CAR.HYUNDAI_SANTA_FE_PHEV_2022,
CAR.HYUNDAI_SONATA,
CAR.HYUNDAI_SONATA_HYBRID,
CAR.KIA_XCEED_PHEV,
CAR.GENESIS_G90,
}
@@ -1140,4 +1207,6 @@ NON_SCC_CAR = CAR.with_flags(HyundaiFlags.NON_SCC)
# HyundaiFlags.CANFD_RADAR_SCC | HyundaiFlags.CANFD_NO_RADAR_DISABLE | )
UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.LEGACY) | CAR.with_flags(HyundaiFlags.UNSUPPORTED_LONGITUDINAL)
LEGACY_LONGITUDINAL_CAR = {CAR.KIA_XCEED_PHEV}
DBC = CAR.create_dbc_map()
+13
View File
@@ -22,6 +22,7 @@ from opendbc.car.honda.values import CAR as HONDA, HONDA_BOSCH, HondaFlags, Hond
from opendbc.car.hyundai.hyundaicanfd import CanBus
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.subaru.values import CAR as SUBARU, SubaruSafetyFlags
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
@@ -133,6 +134,7 @@ class CarInterfaceBase(ABC):
dbc_names = {bus: cp.dbc_name for bus, cp in self.can_parsers.items()}
self.CC: CarControllerBase = self.CarController(dbc_names, CP)
self.CC.FPCP = FPCP
self.FPCP = FPCP
@@ -174,8 +176,11 @@ class CarInterfaceBase(ABC):
ret = cls._get_params(ret, candidate, fingerprint, car_fw, alpha_long, is_release, docs)
trailer_load_kg = float(np.clip(getattr(starpilot_toggles, "trailer_load_kg", 0.0) or 0.0, 0.0, 15000.0 * CV.LB_TO_KG))
# Vehicle mass is published curb weight plus assumed payload such as a human driver; notCars have no assumed payload
if not ret.notCar:
ret.mass = ret.mass + trailer_load_kg
ret.mass = ret.mass + STD_CARGO_KG
# Set params dependent on values set by the car interface
@@ -211,6 +216,9 @@ class CarInterfaceBase(ABC):
if candidate == CHRYSLER.RAM_HD_5TH_GEN:
if 570 not in fingerprint[0]:
fp_ret.flags |= ChryslerStarPilotFlags.RAM_HD_ALT_BUTTONS.value
if 0x4FF in fingerprint[0]:
fp_ret.flags |= ChryslerStarPilotFlags.NO_MIN_STEERING_SPEED.value
CP.minSteerSpeed = 0.
elif platform in GM:
fp_ret.canUsePedal = True
@@ -263,6 +271,10 @@ class CarInterfaceBase(ABC):
elif platform.config.platform_str == "TESLA_MODEL_S_PREAP":
fp_ret.canUsePedal = True
elif platform in SUBARU:
if getattr(starpilot_toggles, "subaru_sng", False):
fp_ret.safetyConfigs[-1].safetyParam |= SubaruSafetyFlags.STOP_AND_GO.value
return fp_ret
@staticmethod
@@ -497,6 +509,7 @@ class CarStateBase(ABC):
class CarControllerBase(ABC):
def __init__(self, dbc_names: dict[StrEnum, str], CP: structs.CarParams):
self.CP = CP
self.FPCP: custom.StarPilotCarParams | None = None
self.frame = 0
self.secoc_key: bytes = b"00" * 16
@@ -1,5 +1,8 @@
import numpy as np
from opendbc.can import CANPacker
from opendbc.car import Bus, structs
from opendbc.car import Bus, DT_CTRL, structs
from opendbc.car.common.filter_simple import FirstOrderFilter
from opendbc.car.lateral import apply_std_steer_angle_limits
from opendbc.car.interfaces import CarControllerBase
from opendbc.car.nissan import nissancan
@@ -13,6 +16,7 @@ class CarController(CarControllerBase):
super().__init__(dbc_names, CP)
self.car_fingerprint = CP.carFingerprint
self.angle_filter = FirstOrderFilter(0.0, 0.1, DT_CTRL)
self.apply_angle_last = 0
self.packer = CANPacker(dbc_names[Bus.pt])
@@ -27,8 +31,15 @@ class CarController(CarControllerBase):
### STEER ###
steer_hud_alert = 1 if hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw) else 0
# Nissan EPS is sensitive to jitter in angle requests at low speed and high steering angles.
if CC.latActive:
self.angle_filter.update_alpha(float(np.interp(CS.out.vEgo, [5, 10, 20], [0.2, 0.1, 0.0])))
self.angle_filter.update(actuators.steeringAngleDeg)
else:
self.angle_filter.x = actuators.steeringAngleDeg
# windup slower
self.apply_angle_last = apply_std_steer_angle_limits(actuators.steeringAngleDeg, self.apply_angle_last, CS.out.vEgoRaw,
self.apply_angle_last = apply_std_steer_angle_limits(self.angle_filter.x, self.apply_angle_last, CS.out.vEgoRaw,
CS.out.steeringAngleDeg, CC.latActive, CarControllerParams.ANGLE_LIMITS)
lkas_max_torque = 0
@@ -1,6 +1,6 @@
import numpy as np
from opendbc.can import CANPacker
from opendbc.car import Bus, make_tester_present_msg
from opendbc.car import Bus, DT_CTRL, make_tester_present_msg
from opendbc.car.lateral import apply_driver_steer_torque_limits, common_fault_avoidance
from opendbc.car.interfaces import CarControllerBase
from opendbc.car.subaru import subarucan
@@ -26,14 +26,9 @@ class CarController(CarControllerBase):
self.p = CarControllerParams(CP)
self.packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
self.manual_hold = False
self.prev_standstill = False
self.sng_acc_resume = False
self.prev_close_distance = 0
self.prev_cruise_state = 0
self.sng_acc_resume_cnt = 0
self.standstill_start = 0
self.epb_resume_frames_remaining = -1
self.last_standstill_frame = 0
def update(self, CC, CS, now_nanos, starpilot_toggles):
actuators = CC.actuators
@@ -70,8 +65,9 @@ class CarController(CarControllerBase):
self.apply_torque_last = apply_torque
# *** stop and go ***
subaru_sng_manual_parking_brake = getattr(starpilot_toggles, "subaru_sng_manual_parking_brake", False)
if starpilot_toggles.subaru_sng:
throttle_cmd, speed_cmd = self.stop_and_go(CC, CS)
throttle_cmd, speed_cmd = self.stop_and_go(CC, CS, subaru_sng_manual_parking_brake)
# *** longitudinal ***
@@ -110,7 +106,11 @@ class CarController(CarControllerBase):
can_sends.append(subarucan.create_preglobal_es_distance(self.packer, cruise_button, CS.es_distance_msg))
if starpilot_toggles.subaru_sng:
can_sends.append(subarucan.create_preglobal_throttle(self.packer, CS.throttle_msg["COUNTER"] + 1, CS.throttle_msg, throttle_cmd))
can_sends.append(subarucan.create_preglobal_throttle(self.packer, CS.throttle_msg["COUNTER"] + 1, CS.throttle_msg,
throttle_cmd))
if self.frame % 2 == 0:
can_sends.append(subarucan.create_preglobal_brake_pedal(self.packer, CS.brake_pedal_msg,
speed_cmd))
else:
if self.frame % 10 == 0:
can_sends.append(subarucan.create_es_dashstatus(self.packer, self.frame // 10, CS.es_dashstatus_msg, CC.enabled,
@@ -124,9 +124,11 @@ class CarController(CarControllerBase):
can_sends.append(subarucan.create_es_infotainment(self.packer, self.frame // 10, CS.es_infotainment_msg, hud_control.visualAlert))
if starpilot_toggles.subaru_sng:
can_sends.append(subarucan.create_throttle(self.packer, CS.throttle_msg["COUNTER"] + 1, CS.throttle_msg, throttle_cmd))
can_sends.append(subarucan.create_throttle(self.packer, CS.throttle_msg["COUNTER"] + 1, CS.throttle_msg,
throttle_cmd))
if self.frame % 2 == 0:
can_sends.append(subarucan.create_brake_pedal(self.packer, self.frame // 2, CS.brake_pedal_msg, speed_cmd, pcm_cancel_cmd))
can_sends.append(subarucan.create_brake_pedal(self.packer, self.frame // 2, CS.brake_pedal_msg,
speed_cmd, pcm_cancel_cmd))
if self.CP.openpilotLongitudinalControl:
if self.frame % 5 == 0:
@@ -166,49 +168,37 @@ class CarController(CarControllerBase):
self.frame += 1
return new_actuators, can_sends
def stop_and_go(self, CC, CS, speed_cmd=False, throttle_cmd=False):
if self.CP.flags & SubaruFlags.PREGLOBAL:
trigger_resume = CC.enabled
trigger_resume &= CS.car_follow == 1
trigger_resume &= CS.close_distance > self.prev_close_distance
trigger_resume &= CS.out.standstill
trigger_resume &= _SNG_ACC_MIN_DIST < CS.close_distance < _SNG_ACC_MAX_DIST
def stop_and_go(self, CC, CS, manual_parking_brake=False):
throttle_cmd = False
speed_cmd = False
if trigger_resume:
self.sng_acc_resume = True
else:
if CS.car_follow == 0 and CS.cruise_state == 3 and CS.out.standstill and self.prev_cruise_state == 1:
self.manual_hold = True
if not CC.enabled or not CC.hudControl.leadVisible:
return throttle_cmd, speed_cmd
if not CS.out.standstill:
self.manual_hold = False
close_distance = CS.close_distance
if not CS.out.standstill:
self.last_standstill_frame = self.frame
trigger_resume = CC.enabled
trigger_resume &= CS.car_follow == 1
trigger_resume &= CS.close_distance > self.prev_close_distance
trigger_resume &= CS.cruise_state == 3
trigger_resume &= not self.manual_hold
trigger_resume &= _SNG_ACC_MIN_DIST < CS.close_distance < _SNG_ACC_MAX_DIST
standstill_timers = (0.75, 0.8) if self.CP.flags & SubaruFlags.PREGLOBAL else (0.5, 0.55)
standstill_duration = (self.frame - self.last_standstill_frame) * DT_CTRL
in_standstill_hold = standstill_duration > standstill_timers[0]
if standstill_duration >= standstill_timers[1]:
self.last_standstill_frame = self.frame
if trigger_resume:
self.sng_acc_resume = True
if manual_parking_brake or not (self.CP.flags & SubaruFlags.PREGLOBAL):
speed_cmd = in_standstill_hold
if CC.enabled and CS.car_follow == 1 and CS.out.standstill and self.frame > self.standstill_start + 50:
speed_cmd = True
should_resume = (
CS.out.standstill and
_SNG_ACC_MIN_DIST < close_distance < _SNG_ACC_MAX_DIST and
close_distance > self.prev_close_distance
)
if should_resume:
self.epb_resume_frames_remaining = 15
if CS.out.standstill and not self.prev_standstill:
self.standstill_start = self.frame
throttle_cmd = self.epb_resume_frames_remaining > 0
if self.epb_resume_frames_remaining > 0:
self.epb_resume_frames_remaining -= 1
self.prev_standstill = CS.out.standstill
self.prev_cruise_state = CS.cruise_state
if self.sng_acc_resume:
if self.sng_acc_resume_cnt < 5:
throttle_cmd = True
self.sng_acc_resume_cnt += 1
else:
self.sng_acc_resume = False
self.sng_acc_resume_cnt = -1
self.prev_close_distance = CS.close_distance
self.prev_close_distance = close_distance
return throttle_cmd, speed_cmd
@@ -358,6 +358,19 @@ def create_brake_pedal(packer, frame, brake_pedal_msg, speed_cmd, brake_cmd):
return packer.make_can_msg("Brake_Pedal", CanBus.camera, values)
def create_preglobal_brake_pedal(packer, brake_pedal_msg, speed_cmd):
values = {s: brake_pedal_msg[s] for s in sorted([
"Brake_Pedal",
"Signal1",
"Speed",
])}
if speed_cmd:
values["Speed"] = 1
return packer.make_can_msg("Brake_Pedal", CanBus.camera, values)
def create_throttle(packer, frame, throttle_msg, throttle_cmd):
values = {s: throttle_msg[s] for s in sorted([
"CHECKSUM",
@@ -1,4 +1,57 @@
from types import SimpleNamespace
from opendbc.car.subaru.carcontroller import CarController
from opendbc.car.subaru.fingerprints import FW_VERSIONS
from opendbc.car.subaru.values import SubaruFlags
def make_sng_controller(flags=0, prev_close_distance=4.0):
controller = object.__new__(CarController)
controller.CP = SimpleNamespace(flags=flags)
controller.frame = 60
controller.last_standstill_frame = 0
controller.prev_close_distance = prev_close_distance
controller.epb_resume_frames_remaining = -1
return controller
def make_sng_state(close_distance=4.0, standstill=True):
cc = SimpleNamespace(enabled=True, hudControl=SimpleNamespace(leadVisible=True))
cs = SimpleNamespace(
close_distance=close_distance,
out=SimpleNamespace(standstill=standstill),
)
return cc, cs
def test_global_sng_keeps_standstill_alive_without_manual_parking_brake_toggle():
controller = make_sng_controller()
cc, cs = make_sng_state()
throttle_cmd, speed_cmd = controller.stop_and_go(cc, cs, manual_parking_brake=False)
assert throttle_cmd is False
assert speed_cmd is True
def test_manual_parking_brake_sng_still_sends_resume_throttle():
controller = make_sng_controller(prev_close_distance=3.9)
cc, cs = make_sng_state(close_distance=4.0)
throttle_cmd, speed_cmd = controller.stop_and_go(cc, cs, manual_parking_brake=True)
assert throttle_cmd is True
assert speed_cmd is True
def test_preglobal_sng_does_not_send_standstill_keepalive_without_manual_toggle():
controller = make_sng_controller(flags=SubaruFlags.PREGLOBAL)
cc, cs = make_sng_state()
throttle_cmd, speed_cmd = controller.stop_and_go(cc, cs, manual_parking_brake=False)
assert throttle_cmd is False
assert speed_cmd is False
class TestSubaruFingerprint:
@@ -59,6 +59,7 @@ class SubaruSafetyFlags(IntFlag):
GEN2 = 1
LONG = 2
PREGLOBAL_REVERSED_DRIVER_TORQUE = 4
STOP_AND_GO = 8
class SubaruFlags(IntFlag):
@@ -3,10 +3,11 @@ from opendbc.can import CANPacker
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.coop_steering import CooperativeSteeringController
from opendbc.car.tesla.teslacan import TeslaCAN
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.tesla.values import CANBUS, CAR, CarControllerParams, TeslaSafetyFlags
from opendbc.car.vehicle_model import VehicleModel
def get_safety_CP():
@@ -18,6 +19,11 @@ class CarController(CarControllerBase):
def __init__(self, dbc_names, CP):
super().__init__(dbc_names, CP)
self.apply_angle_last = 0
self.apply_angle_command_last = 0
self.coop_steer = CooperativeSteeringController()
self.coop_enabled = CP.carFingerprint == CAR.TESLA_MODEL_3 and any(
config.safetyParam & TeslaSafetyFlags.COOP_STEERING.value for config in CP.safetyConfigs
)
self.packer = CANPacker(dbc_names[Bus.party])
self.tesla_can = TeslaCAN(self.packer)
self.preap_long = None
@@ -40,17 +46,18 @@ class CarController(CarControllerBase):
actuators = CC.actuators
can_sends = []
# Tesla EPS enforces disabling steering on heavy lateral override force.
# When enabling in a tight curve, we wait until user reduces steering force to start steering.
# Canceling is done on rising edge and is handled generically with CC.cruiseControl.cancel
lat_active = CC.latActive and CS.hands_on_level < 3
# Preserve the stock controller path unless cooperative steering is explicitly enabled.
lat_active = CC.latActive and (not CS.out.steeringDisengage if self.coop_enabled else CS.hands_on_level < 3)
if self.frame % 2 == 0:
# Angular rate limit based on speed
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)
can_sends.append(self.tesla_can.create_steering_control(self.apply_angle_last, lat_active))
self.apply_angle_command_last, lat_active = self.coop_steer.update(
self.apply_angle_last, lat_active, self.coop_enabled, CS, self.VM,
)
can_sends.append(self.tesla_can.create_steering_control(self.apply_angle_command_last, lat_active))
if self.frame % 10 == 0:
can_sends.append(self.tesla_can.create_steering_allowed())
@@ -71,7 +78,7 @@ class CarController(CarControllerBase):
# TODO: HUD control
new_actuators = actuators.as_builder()
new_actuators.steeringAngleDeg = self.apply_angle_last
new_actuators.steeringAngleDeg = self.apply_angle_command_last
self.frame += 1
return new_actuators, can_sends
+9 -3
View File
@@ -4,7 +4,7 @@ from opendbc.can import CANDefine, CANParser
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.values import DBC, CANBUS, GEAR_MAP, STEER_DISENGAGE_THRESHOLD, STEER_THRESHOLD, TeslaSafetyFlags, 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
@@ -30,6 +30,9 @@ class CarState(CarStateBase):
self.prev_cruise_buttons = 0
self.msg_stw_actn_req = None
self.speed_units = "MPH"
self.cooperative_steering = any(
config.safetyParam & TeslaSafetyFlags.COOP_STEERING.value for config in CP.safetyConfigs
)
if CP.carFingerprint == CAR.TESLA_MODEL_S_PREAP:
self.engagement = PreAPEngagement(nap_conf.double_pull_enabled, nap_conf.double_pull_window_ms)
@@ -95,8 +98,11 @@ class CarState(CarStateBase):
# FSD disengages using union of handsOnLevel (slow overrides) and high angle rate faults (fast overrides, high speed)
eac_error_code = self.can_define.dv["EPAS3S_sysStatus"]["EPAS3S_eacErrorCode"].get(int(epas_status["EPAS3S_eacErrorCode"]), None)
ret.steeringDisengage = self.hands_on_level >= 3 or (eac_status == "EAC_INHIBITED" and
eac_error_code == "EAC_ERROR_HIGH_ANGLE_RATE_SAFETY")
ret.steeringDisengage = (
self.hands_on_level >= 3 or
(eac_status == "EAC_INHIBITED" and eac_error_code == "EAC_ERROR_HIGH_ANGLE_RATE_SAFETY") or
(self.cooperative_steering and abs(ret.steeringTorque) > STEER_DISENGAGE_THRESHOLD)
)
# Cruise state
cruise_state = self.can_define.dv["DI_state"]["DI_cruiseState"].get(int(cp_party.vl["DI_state"]["DI_cruiseState"]), None)
@@ -0,0 +1,133 @@
import math
import numpy as np
from opendbc.car import DT_CTRL, rate_limit
from opendbc.car.lateral import apply_steer_angle_limits_vm
from opendbc.car.tesla.values import CarControllerParams
from opendbc.car.vehicle_model import VehicleModel
DT_LAT_CTRL = DT_CTRL * CarControllerParams.STEER_STEP
STEER_RESUME_RATE_LIMIT_RAMP_RATE = 300.0 # deg/s^2
STEER_OVERRIDE_MIN_TORQUE = 0.5 # Nm
STEER_OVERRIDE_MAX_TORQUE = 2.5 # Nm
STEER_OVERRIDE_TORQUE_RANGE = STEER_OVERRIDE_MAX_TORQUE - STEER_OVERRIDE_MIN_TORQUE
STEER_OVERRIDE_MAX_LAT_ACCEL = 2.0 # m/s^2
STEER_OVERRIDE_DELTA_GAIN_LIMIT = 125.0 # deg/s/Nm
def apply_bounds(signal: float, limit: float) -> float:
return float(np.clip(signal, -limit, limit))
def apply_deadzone(signal: float, deadzone: float) -> float:
return signal - apply_bounds(signal, deadzone)
def get_steer_from_lat_accel(lat_accel: float, v_ego: float, VM: VehicleModel) -> float:
curvature = lat_accel / max(1.0, v_ego) ** 2
return math.degrees(VM.get_steer_from_curvature(curvature, v_ego, 0.0))
def get_override_torque_to_angle(v_ego: float, VM: VehicleModel) -> float:
max_angle = CarControllerParams.ANGLE_LIMITS.STEER_ANGLE_MAX
steer_from_lat_accel = apply_bounds(get_steer_from_lat_accel(STEER_OVERRIDE_MAX_LAT_ACCEL, v_ego, VM), max_angle)
return steer_from_lat_accel / STEER_OVERRIDE_TORQUE_RANGE
def calc_override_angle_delta_limit(torque: float) -> float:
max_gain = CarControllerParams.ANGLE_LIMITS.MAX_ANGLE_RATE / DT_LAT_CTRL / STEER_OVERRIDE_TORQUE_RANGE
return torque * min(STEER_OVERRIDE_DELTA_GAIN_LIMIT, max_gain) * DT_LAT_CTRL
class SteerRateLimiter:
def __init__(self):
self.last = 0.0
def reset(self, angle: float) -> None:
self.last = angle
def update(self, angle: float, angle_delta_limit: float) -> float:
limited = rate_limit(angle, self.last, -angle_delta_limit, angle_delta_limit)
self.last = limited
return limited
class CooperativeSteeringController:
def __init__(self):
self.apply_angle_last = 0.0
self.coop_apply_angle_last = 0.0
self.angle_override = 0.0
self.resume_rate_limiter_delta = SteerRateLimiter()
self.resume_rate_limiter = SteerRateLimiter()
def reset_override_state(self, apply_angle: float) -> None:
self.apply_angle_last = apply_angle
self.angle_override = 0.0
self.coop_apply_angle_last = apply_angle
def reset_resume_state(self, apply_angle: float) -> None:
self.resume_rate_limiter_delta.reset(0.0)
self.resume_rate_limiter.reset(apply_angle)
def update_override_angle(self, apply_angle_delta: float, driver_torque: float, v_ego: float, VM: VehicleModel) -> float:
driver_torque = apply_deadzone(driver_torque, STEER_OVERRIDE_MIN_TORQUE)
torque_to_angle = get_override_torque_to_angle(v_ego, VM)
target_angle = driver_torque * torque_to_angle
holding_torque = self.angle_override / torque_to_angle if abs(v_ego) > 0.1 else 0.0
torque_delta = driver_torque - holding_torque
angle_delta_limit = calc_override_angle_delta_limit(abs(torque_delta))
angle_override_delta = float(np.clip(target_angle - self.angle_override, -angle_delta_limit, angle_delta_limit))
# Avoid counting model-requested motion and driver-requested motion twice.
if angle_override_delta * apply_angle_delta > 0.0:
angle_override_delta -= apply_bounds(apply_angle_delta, abs(angle_override_delta))
self.angle_override += angle_override_delta
return self.angle_override
def unwind_override_angle(self, saturation_error: float) -> None:
if self.angle_override * saturation_error > 0.0:
self.angle_override -= apply_bounds(saturation_error, abs(self.angle_override))
def apply_resume_rate_limit(self, lat_active: bool, apply_angle: float) -> float:
if not lat_active:
self.reset_resume_state(apply_angle)
return apply_angle
angle_rate_delta = self.resume_rate_limiter_delta.update(
CarControllerParams.ANGLE_LIMITS.MAX_ANGLE_RATE,
STEER_RESUME_RATE_LIMIT_RAMP_RATE * DT_LAT_CTRL ** 2,
)
return self.resume_rate_limiter.update(apply_angle, angle_rate_delta)
def update(self, apply_angle: float, lat_active: bool, enabled: bool, CS, VM: VehicleModel) -> tuple[float, bool]:
if not enabled:
self.reset_resume_state(apply_angle)
self.reset_override_state(apply_angle)
return apply_angle, lat_active
apply_angle = self.apply_resume_rate_limit(lat_active, apply_angle)
if not lat_active:
self.reset_override_state(apply_angle)
return apply_angle, False
apply_angle_delta = apply_angle - self.apply_angle_last
self.apply_angle_last = apply_angle
apply_angle += self.update_override_angle(apply_angle_delta, CS.out.steeringTorque, CS.out.vEgo, VM)
limited_angle = apply_steer_angle_limits_vm(
apply_angle,
self.coop_apply_angle_last,
CS.out.vEgoRaw,
CS.out.steeringAngleDeg,
True,
CarControllerParams,
VM,
)
self.coop_apply_angle_last = limited_angle
self.unwind_override_angle(apply_angle - limited_angle)
return limited_angle, True
@@ -9,6 +9,7 @@ FW_VERSIONS = {
(Ecu.eps, 0x730, None): [
b'TeM3_E014p10_0.0.0 (16),E014.17.00',
b'TeM3_E014p10_0.0.0 (16),EL014.17.00',
b'TeM3_E014p10_0.0.0 (24),E014.20.2',
b'TeM3_ES014p11_0.0.0 (25),ES014.19.0',
b'TeMYG4_DCS_Update_0.0.0 (13),E4014.28.1',
b'TeMYG4_DCS_Update_0.0.0 (9),E4014.26.0',
@@ -18,6 +18,13 @@ class CarInterface(CarInterfaceBase):
return get_preap_accel_limits(current_speed)
return CarInterfaceBase.get_pid_accel_limits(CP, current_speed, cruise_speed)
@classmethod
def get_params(cls, candidate, fingerprint, car_fw, alpha_long, is_release, docs, starpilot_toggles):
ret = super().get_params(candidate, fingerprint, car_fw, alpha_long, is_release, docs, starpilot_toggles)
if candidate == CAR.TESLA_MODEL_3 and getattr(starpilot_toggles, "tesla_cooperative_steering", False):
ret.safetyConfigs[0].safetyParam |= TeslaSafetyFlags.COOP_STEERING.value
return ret
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
ret.brand = "tesla"
@@ -126,16 +126,16 @@ def update_preap(cs, can_parsers):
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 = (
fp_ret.pedalMaxRegen = cs.pccEvent == "pedalMaxRegen"
fp_ret.teslaCCEngaged = cs.pccEvent == "teslaCCEngaged"
fp_ret.teslaCCDisengaged = cs.pccEvent == "teslaCCDisengaged"
fp_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
fp_ret.pedalLongActive = cs.enableLongControl and nap_conf.use_pedal
return ret, fp_ret
@@ -7,6 +7,7 @@ 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
SAFETY_TESLA_PREAP = 35
def get_preap_accel_limits(current_speed: float) -> tuple[float, float]:
@@ -29,7 +30,7 @@ def get_preap_params(ret: structs.CarParams) -> structs.CarParams:
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.safetyConfigs = [get_safety_config(SAFETY_TESLA_PREAP, safety_flags)]
ret.radarUnavailable = not nap_conf.radar_enabled
ret.steerControlType = structs.CarParams.SteerControlType.angle
ret.openpilotLongitudinalControl = use_pedal
@@ -0,0 +1,75 @@
from types import SimpleNamespace
import pytest
from opendbc.car import gen_empty_fingerprint
from opendbc.car.tesla.carcontroller import CarController, get_safety_CP
from opendbc.car.tesla.coop_steering import CooperativeSteeringController
from opendbc.car.tesla.interface import CarInterface
from opendbc.car.tesla.values import CAR, DBC, CarControllerParams, TeslaSafetyFlags
from opendbc.car.vehicle_model import VehicleModel
def make_car_state(torque=0.0, speed=15.0, angle=0.0):
return SimpleNamespace(out=SimpleNamespace(
steeringTorque=torque,
steeringAngleDeg=angle,
vEgo=speed,
vEgoRaw=speed,
))
@pytest.fixture
def vehicle_model():
return VehicleModel(get_safety_CP())
def test_disabled_preserves_angle_command(vehicle_model):
controller = CooperativeSteeringController()
angle, lat_active = controller.update(12.5, True, False, make_car_state(torque=2.0), vehicle_model)
assert angle == 12.5
assert lat_active
def test_light_driver_torque_adjusts_angle(vehicle_model):
controller = CooperativeSteeringController()
angle = 0.0
for _ in range(10):
angle, lat_active = controller.update(0.0, True, True, make_car_state(torque=1.5), vehicle_model)
assert lat_active
assert angle > 0.0
assert angle <= CarControllerParams.ANGLE_LIMITS.STEER_ANGLE_MAX
def test_inactive_lateral_resets_override(vehicle_model):
controller = CooperativeSteeringController()
for _ in range(10):
controller.update(0.0, True, True, make_car_state(torque=1.5), vehicle_model)
angle, lat_active = controller.update(8.0, False, True, make_car_state(torque=1.5, angle=8.0), vehicle_model)
assert angle == 8.0
assert not lat_active
angle, lat_active = controller.update(8.0, True, True, make_car_state(angle=8.0), vehicle_model)
assert angle == 8.0
assert lat_active
@pytest.mark.parametrize(("candidate", "enabled", "expected"), (
(CAR.TESLA_MODEL_3, False, False),
(CAR.TESLA_MODEL_3, True, True),
(CAR.TESLA_MODEL_Y, True, False),
(CAR.TESLA_MODEL_S_PREAP, True, False),
))
def test_safety_flag_is_model_3_only(candidate, enabled, expected):
toggles = SimpleNamespace(tesla_cooperative_steering=enabled, trailer_load_kg=0.0)
params = CarInterface.get_params(candidate, gen_empty_fingerprint(), [], False, False, False, toggles)
has_flag = any(config.safetyParam & TeslaSafetyFlags.COOP_STEERING.value for config in params.safetyConfigs)
assert has_flag is expected
if candidate != CAR.TESLA_MODEL_S_PREAP:
assert CarController(DBC[candidate], params).coop_enabled is expected
+2
View File
@@ -130,6 +130,7 @@ class CarControllerParams:
class TeslaSafetyFlags(IntFlag):
LONG_CONTROL = 1
COOP_STEERING = 256
class TeslaFlags(IntFlag):
@@ -158,3 +159,4 @@ class CruiseButtons:
DBC = CAR.create_dbc_map()
STEER_THRESHOLD = 1
STEER_DISENGAGE_THRESHOLD = 5.0
+9
View File
@@ -22,18 +22,21 @@ non_tested_cars = [
MOCK.MOCK,
GM.CADILLAC_ATS,
GM.CADILLAC_ESCALADE_ASCM,
GM.CADILLAC_ESCALADE_ESV_2019_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_AZERA_HEV_7TH_GEN,
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_CARNIVAL_2025,
HYUNDAI.KIA_SPORTAGE_2026,
HYUNDAI.KIA_SORENTO_2024,
HYUNDAI.KIA_SORENTO_HEV_4TH_GEN_LFA2,
@@ -162,7 +165,9 @@ routes = [
CarTestRoute("656ac0d830792fcc/2021-12-28--14-45-56", HYUNDAI.HYUNDAI_SANTA_FE_PHEV_2022, segment=1),
CarTestRoute("de59124955b921d8/2023-06-24--00-12-50", HYUNDAI.KIA_CARNIVAL_4TH_GEN),
CarTestRoute("409c9409979a8abc/2023-07-11--09-06-44", HYUNDAI.KIA_CARNIVAL_4TH_GEN), # Chinese model
CarTestRoute("6c0069dcd5bbb6c1/00000020--6b95507969", HYUNDAI.KIA_CARNIVAL_HEV_4TH_GEN), # HDA II
CarTestRoute("e0e98335f3ebc58f/2021-03-07--16-38-29", HYUNDAI.KIA_CEED),
CarTestRoute("22703002ddbe2a08/0000000e--083b76c42c", HYUNDAI.KIA_XCEED_PHEV, segment=0),
CarTestRoute("7653b2bce7bcfdaa/2020-03-04--15-34-32", HYUNDAI.KIA_OPTIMA_G4),
CarTestRoute("018654717bc93d7d/2022-09-19--23-11-10", HYUNDAI.KIA_OPTIMA_G4_FL, segment=0),
CarTestRoute("f9716670b2481438/2023-08-23--14-49-50", HYUNDAI.KIA_OPTIMA_H),
@@ -229,7 +234,11 @@ routes = [
CarTestRoute("c5ac319aa9583f83/2021-06-01--18-18-31", HYUNDAI.HYUNDAI_ELANTRA),
CarTestRoute("734ef96182ddf940/2022-10-02--16-41-44", HYUNDAI.HYUNDAI_ELANTRA_GT_I30),
CarTestRoute("82e9cdd3f43bf83e/2021-05-15--02-42-51", HYUNDAI.HYUNDAI_ELANTRA_2021),
CarTestRoute("c2fd040a5e34f3ad/00000013--9211a52a3d", HYUNDAI.HYUNDAI_ELANTRA_2024),
CarTestRoute("715ac05b594e9c59/2021-06-20--16-21-07", HYUNDAI.HYUNDAI_ELANTRA_HEV_2021),
CarTestRoute("65ef8b49f9b0dd24/00000141--5c8720a01c", HYUNDAI.HYUNDAI_ELANTRA_HEV_2024),
CarTestRoute("07a48901db7b2503/000001a1--ad07872c4f", HYUNDAI.HYUNDAI_ELANTRA_HEV_2024),
CarTestRoute("07a48901db7b2503/0000000f--697d5906e8", HYUNDAI.HYUNDAI_ELANTRA_HEV_2024), # Hyundai i30 Hybrid 2024
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),
@@ -4,9 +4,17 @@ import pytest
from opendbc.car.can_definitions import CanData
from opendbc.car.car_helpers import FRAME_FINGERPRINT, _apply_starpilot_access_policy, _get_gm_stored_candidate_fallback, can_fingerprint
from opendbc.car.fingerprints import _FINGERPRINTS as FINGERPRINTS
from opendbc.car.gm.values import CAR as GM
class TestCanFingerprint:
@staticmethod
def _fingerprint_from_can(fingerprint):
can = [CanData(address=address, dat=b'\x00' * length, src=src)
for address, length in fingerprint.items() for src in (0, 1)]
fingerprint_iter = iter([can])
return can_fingerprint(lambda **kwargs: [next(fingerprint_iter, [])]) # noqa: B023
@pytest.mark.parametrize("car_model, fingerprints", FINGERPRINTS.items())
def test_can_fingerprint(self, car_model, fingerprints):
"""Tests online fingerprinting function on offline fingerprints"""
@@ -23,6 +31,12 @@ class TestCanFingerprint:
assert finger[1] == fingerprint
assert finger[2] == {}
def test_gm_sascm_superset_fingerprint_matches_ascm_variant(self):
car_fingerprint, finger = self._fingerprint_from_can(FINGERPRINTS[GM.CADILLAC_ESCALADE_ESV_2019_ASCM][0])
assert car_fingerprint == GM.CADILLAC_ESCALADE_ESV_2019_ASCM
assert finger[0][0x2FF] == 8
def test_timing(self, subtests):
# just pick any CAN fingerprinting car
car_model = "CHEVROLET_BOLT_ACC_2022_2023"
@@ -8,9 +8,13 @@ from collections.abc import Callable
from typing import Any
from cereal import custom
from opendbc.car import DT_CTRL, CanData, structs
from opendbc.can import CANParser
from opendbc.car import Bus, DT_CTRL, CanData, structs
from opendbc.car.car_helpers import _apply_disable_openpilot_long, interfaces
from opendbc.car.chrysler.carcontroller import CarController as ChryslerCarController
from opendbc.car.chrysler.carstate import CarState as ChryslerCarState
from opendbc.car.chrysler.interface import CarInterface as ChryslerCarInterface
from opendbc.car.chrysler.values import CAR as CHRYSLER_CAR, DBC as CHRYSLER_DBC, ChryslerSafetyFlags, ChryslerStarPilotFlags
from opendbc.car.fingerprints import FW_VERSIONS
from opendbc.car.fw_versions import FW_QUERY_CONFIGS
from opendbc.car.hyundai.interface import CarInterface as HyundaiCarInterface
@@ -74,6 +78,8 @@ def get_test_starpilot_toggles() -> SimpleNamespace:
reverse_cruise_increase=False,
sng_hack=False,
subaru_sng=False,
subaru_sng_manual_parking_brake=False,
tesla_cooperative_steering=False,
unlock_doors=False,
vEgoStopping=0.5,
volt_sng=False,
@@ -160,6 +166,80 @@ class TestCarInterfaces:
"Center_Stack_2": {"LKAS_Button": 1},
}, is_ram=True)
def test_chrysler_wd_mod_enables_steer_to_zero(self):
fingerprint = {bus: {} for bus in range(8)}
fingerprint[0][0x4FF] = 8
toggles = get_test_starpilot_toggles()
car_params = ChryslerCarInterface.get_params(
CHRYSLER_CAR.CHRYSLER_PACIFICA_2020,
fingerprint,
[],
alpha_long=False,
is_release=False,
docs=False,
starpilot_toggles=toggles,
)
assert car_params.minSteerSpeed > 0.
fp_car_params = ChryslerCarInterface.get_starpilot_params(
CHRYSLER_CAR.CHRYSLER_PACIFICA_2020,
fingerprint,
[],
car_params,
toggles,
)
assert fp_car_params.flags & ChryslerStarPilotFlags.NO_MIN_STEERING_SPEED
assert car_params.minSteerSpeed == 0.
controller = ChryslerCarController({Bus.pt: CHRYSLER_DBC[car_params.carFingerprint][Bus.pt]}, car_params)
controller.FPCP = fp_car_params
controller.frame = 202
CC = structs.CarControl()
CC.latActive = True
CC.actuators.torque = 0.1
CC = CC.as_reader()
CS = SimpleNamespace(
out=SimpleNamespace(vEgo=0.0, steeringTorqueEps=0.0, gearShifter=structs.CarState.GearShifter.drive),
lkas_car_model=-1,
button_counter=0,
button_message="CRUISE_BUTTONS",
auto_high_beam=0,
)
_, can_sends = controller.update(CC, CS, 0, toggles)
lkas_parser = CANParser(CHRYSLER_DBC[car_params.carFingerprint][Bus.pt], [("LKAS_COMMAND", 50)], 0)
lkas_parser.update([0, can_sends])
assert lkas_parser.vl["LKAS_COMMAND"]["LKAS_CONTROL_BIT"] == 1
@pytest.mark.parametrize("candidate", (CHRYSLER_CAR.JEEP_GRAND_CHEROKEE, CHRYSLER_CAR.JEEP_GRAND_CHEROKEE_2019))
def test_jeep_brake_hold_safety_capability_is_provisioned(self, candidate):
car_params = ChryslerCarInterface.get_params(
candidate,
{bus: {} for bus in range(8)},
[],
alpha_long=False,
is_release=False,
docs=False,
starpilot_toggles=get_test_starpilot_toggles(),
)
assert car_params.safetyConfigs[0].safetyParam & ChryslerSafetyFlags.JEEP_BRAKE_HOLD.value
def test_jeep_brake_hold_safety_capability_is_not_provisioned_for_non_jeep(self):
car_params = ChryslerCarInterface.get_params(
CHRYSLER_CAR.CHRYSLER_PACIFICA_2020,
{bus: {} for bus in range(8)},
[],
alpha_long=False,
is_release=False,
docs=False,
starpilot_toggles=get_test_starpilot_toggles(),
)
assert not car_params.safetyConfigs[0].safetyParam & ChryslerSafetyFlags.JEEP_BRAKE_HOLD.value
def test_gm_bolt_gen2_pedal_safety_flags(self):
CarInterface = interfaces[GM_CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL]
@@ -0,0 +1,49 @@
from types import SimpleNamespace
from opendbc.car.chrysler.carcontroller import (
JEEP_BRAKE_HOLD_DEFAULT_DECEL,
clip_jeep_brake_hold_decel,
supports_jeep_brake_hold,
)
from opendbc.car.chrysler.values import CAR, ChryslerSafetyFlags, JEEPS
def test_jeep_group_is_grand_cherokee_only():
assert JEEPS == {CAR.JEEP_GRAND_CHEROKEE, CAR.JEEP_GRAND_CHEROKEE_2019}
def test_jeep_brake_hold_support_requires_toggle_jeep_pcm_and_safety_flag():
safety = [SimpleNamespace(safetyParam=ChryslerSafetyFlags.JEEP_BRAKE_HOLD.value)]
no_safety = [SimpleNamespace(safetyParam=0)]
assert supports_jeep_brake_hold(SimpleNamespace(
carFingerprint=CAR.JEEP_GRAND_CHEROKEE,
pcmCruise=True,
safetyConfigs=safety,
), True)
assert not supports_jeep_brake_hold(SimpleNamespace(
carFingerprint=CAR.JEEP_GRAND_CHEROKEE,
pcmCruise=True,
safetyConfigs=no_safety,
), True)
assert not supports_jeep_brake_hold(SimpleNamespace(
carFingerprint=CAR.JEEP_GRAND_CHEROKEE,
pcmCruise=False,
safetyConfigs=safety,
), True)
assert not supports_jeep_brake_hold(SimpleNamespace(
carFingerprint=CAR.CHRYSLER_PACIFICA_2018,
pcmCruise=True,
safetyConfigs=safety,
), True)
assert not supports_jeep_brake_hold(SimpleNamespace(
carFingerprint=CAR.JEEP_GRAND_CHEROKEE,
pcmCruise=True,
safetyConfigs=safety,
), False)
def test_jeep_brake_hold_decel_is_bounded():
assert clip_jeep_brake_hold_decel(JEEP_BRAKE_HOLD_DEFAULT_DECEL) == JEEP_BRAKE_HOLD_DEFAULT_DECEL
assert clip_jeep_brake_hold_decel(-8.0) == -3.0
assert clip_jeep_brake_hold_decel(0.5) == -0.5
@@ -17,6 +17,8 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
# Toyota LTA also has torque
"TOYOTA_RAV4_TSS2_2023" = [nan, 3.0, nan]
"TOYOTA_MATRIX_RETROFIT" = [4.05, 1.84, 0.10]
"TOYOTA_RAV4_PRIME" = [1.7, 2.0, 0.14]
# Tesla angle based controllers
"TESLA_MODEL_3" = [nan, 2.5, nan]
@@ -35,6 +37,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"FORD_F_150_LIGHTNING_MK1" = [nan, 1.5, nan]
"FORD_MUSTANG_MACH_E_MK1" = [nan, 1.5, nan]
"FORD_RANGER_MK2" = [nan, 1.5, nan]
"FORD_TRANSIT_MK5" = [nan, 1.5, nan]
###
# No steering wheel
@@ -74,6 +77,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"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_XCEED_PHEV" = [2.9638737459977467, 2.1259108157250735, 0.07813665616927593]
"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]
@@ -87,6 +91,8 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"KIA_NIRO_EV_2ND_GEN" = [2.05, 2.5, 0.14]
"GENESIS_GV80" = [2.5, 2.5, 0.1]
"KIA_CARNIVAL_4TH_GEN" = [1.75, 1.75, 0.15]
"KIA_CARNIVAL_2025" = [1.75, 1.75, 0.15]
"KIA_CARNIVAL_HEV_4TH_GEN" = [1.75, 1.75, 0.15]
"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]
@@ -96,6 +102,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"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]
"HYUNDAI_AZERA_HEV_7TH_GEN" = [nan, 2.5, nan]
"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]
@@ -9,7 +9,6 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"TOYOTA_ALPHARD_TSS2" = "TOYOTA_SIENNA"
"TOYOTA_PRIUS_V" = "TOYOTA_PRIUS"
"TOYOTA_RAV4_PRIME" = "TOYOTA_RAV4_TSS2"
"TOYOTA_SIENNA_4TH_GEN" = "TOYOTA_RAV4_TSS2"
"LEXUS_IS" = "LEXUS_NX"
"LEXUS_CTH" = "LEXUS_NX"
@@ -38,8 +37,10 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"HYUNDAI_IONIQ_HEV_2022" = "HYUNDAI_IONIQ_PHEV_2019"
"HYUNDAI_IONIQ_EV_2020" = "HYUNDAI_IONIQ_PHEV_2019"
"HYUNDAI_ELANTRA" = "HYUNDAI_SONATA_LF"
"HYUNDAI_ELANTRA_2024" = "HYUNDAI_ELANTRA_2021"
"HYUNDAI_ELANTRA_GT_I30" = "HYUNDAI_SONATA_LF"
"HYUNDAI_ELANTRA_HEV_2021" = "HYUNDAI_SONATA"
"HYUNDAI_ELANTRA_HEV_2024" = "HYUNDAI_SONATA"
"HYUNDAI_TUCSON" = "HYUNDAI_SANTA_FE"
"HYUNDAI_SANTA_FE_2022" = "HYUNDAI_SANTA_FE_HEV_2022"
"KIA_K5_HEV_2020" = "KIA_K5_2021"
@@ -66,9 +67,11 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"BUICK_LACROSSE" = "CHEVROLET_VOLT"
"BUICK_LACROSSE_ASCM" = "CHEVROLET_VOLT"
"BUICK_LACROSSE_ASCM_19US" = "CHEVROLET_VOLT"
"BUICK_REGAL" = "CHEVROLET_VOLT"
"CADILLAC_ESCALADE_ASCM" = "CADILLAC_ESCALADE"
"CADILLAC_ESCALADE_ESV" = "CHEVROLET_VOLT"
"CADILLAC_ESCALADE_ESV_2019_ASCM" = "CADILLAC_ESCALADE_ESV_2019"
"CADILLAC_ATS" = "CHEVROLET_VOLT"
"CHEVROLET_MALIBU" = "CHEVROLET_VOLT"
"CHEVROLET_MALIBU_ASCM" = "CHEVROLET_VOLT"
@@ -117,5 +120,6 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"GMC_YUKON_CC" = "CHEVROLET_SILVERADO"
"CHEVROLET_TRAILBLAZER_CC" = "CHEVROLET_TRAILBLAZER"
"CHEVROLET_SILVERADO_CC" = "CHEVROLET_SILVERADO"
"CADILLAC_XT4_CC" = "CADILLAC_XT4"
"CHEVROLET_TRAX" = "CHEVROLET_VOLT"
@@ -26,6 +26,9 @@ ACCEL_WINDDOWN_LIMIT = -4.0 * DT_CTRL * 3 # m/s^2 / frame
ACCEL_PID_UNWIND = 0.03 * DT_CTRL * 3 # m/s^2 / frame
PRIUS_INTEGRAL_MISMATCH_UNWIND = 8.0
PRIUS_POSITIVE_FEEDFORWARD_SCALE = 0.7
PRIUS_CRUISE_FEEDFORWARD_SCALE = 1.0
PRIUS_NEGATIVE_FEEDFORWARD_SCALE = 1.125
CAMRY_HYBRID_POSITIVE_FEEDFORWARD_SCALE = 0.8
MAX_PITCH_COMPENSATION = 1.5 # m/s^2
TOYOTA_COAST_BRAKE_MIN_SPEED = 15.0 # m/s
@@ -33,6 +36,7 @@ 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
TOYOTA_NO_LEAD_CRUISE_SIGN_FLIP_MIN_SET_SPEED_ERROR = 0.35 # m/s
# LKA limits
# EPS faults if you apply torque while the steering rate is above 100 deg/s for too long
@@ -43,19 +47,28 @@ MAX_STEER_RATE_FRAMES = 18 # tx control frames needed before torque can be cut
MAX_USER_TORQUE = 500
PARK = structs.CarState.GearShifter.park
REVERSE = structs.CarState.GearShifter.reverse
# Lock / unlock door commands - Credit goes to AlexandreSato!
LOCK_CMD = b"\x40\x05\x30\x11\x00\x80\x00\x00"
UNLOCK_CMD = b"\x40\x05\x30\x11\x00\x40\x00\x00"
def is_camry_hybrid(CP) -> bool:
return CP.carFingerprint == CAR.TOYOTA_CAMRY and bool(CP.flags & ToyotaFlags.HYBRID.value)
def is_ths_hybrid(CP) -> bool:
return CP.carFingerprint == CAR.TOYOTA_PRIUS or is_camry_hybrid(CP)
def get_long_tune(CP, params):
kiBP = [2., 5.]
kiV = [0.5, 0.25]
k_f = 1.0
if CP.carFingerprint == CAR.TOYOTA_PRIUS:
k_f = 0.8
if is_ths_hybrid(CP):
k_f = 0.8 if CP.carFingerprint == CAR.TOYOTA_PRIUS else 1.0
elif CP.carFingerprint not in TSS2_CAR:
kiBP = [0., 5., 35.]
kiV = [3.6, 2.4, 1.5]
@@ -65,6 +78,21 @@ def get_long_tune(CP, params):
rate=1 / (DT_CTRL * 3))
def get_prius_positive_feedforward_scale(v_ego: float) -> float:
return float(np.interp(v_ego, [0.0, 8.0, 20.0],
[PRIUS_POSITIVE_FEEDFORWARD_SCALE, PRIUS_POSITIVE_FEEDFORWARD_SCALE, PRIUS_CRUISE_FEEDFORWARD_SCALE]))
def get_prius_feedforward(accel: float, v_ego: float) -> float:
if accel > 0.0:
return accel * get_prius_positive_feedforward_scale(v_ego)
return accel * PRIUS_NEGATIVE_FEEDFORWARD_SCALE
def get_camry_hybrid_feedforward(accel: float) -> float:
return accel * CAMRY_HYBRID_POSITIVE_FEEDFORWARD_SCALE if accel > 0.0 else accel
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:
@@ -161,6 +189,19 @@ def limit_prius_stopping_accel(pcm_accel_cmd: float, target_accel: float, stoppi
return max(pcm_accel_cmd, max(stop_floor, planner_floor))
def limit_no_lead_cruise_sign_flip(pcm_accel_cmd: float, target_accel: float, stopping: bool, v_ego: float,
set_speed: float, lead_visible: bool) -> float:
if stopping or lead_visible or pcm_accel_cmd >= 0.0 or v_ego < TOYOTA_COAST_BRAKE_MIN_SPEED:
return pcm_accel_cmd
if target_accel < -0.02 or set_speed <= 0.0:
return pcm_accel_cmd
if float(set_speed) - float(v_ego) >= TOYOTA_NO_LEAD_CRUISE_SIGN_FLIP_MIN_SET_SPEED_ERROR:
return max(pcm_accel_cmd, 0.0)
return pcm_accel_cmd
class CarController(CarControllerBase):
def __init__(self, dbc_names, CP):
super().__init__(dbc_names, CP)
@@ -192,18 +233,23 @@ class CarController(CarControllerBase):
self.secoc_prev_reset_counter = 0
self.doors_locked = False
self.auto_brake_hold = bool(self.CP.flags & ToyotaFlags.AUTO_BRAKE_HOLD.value)
self.brake_hold_active = False
self._brake_hold_counter = 0
self._brake_hold_reset = False
self._prev_brake_pressed = 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
if CS.out.standstill:
return 0.12 if 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:
elif self.CP.carFingerprint in (CAR.TOYOTA_COROLLA, CAR.TOYOTA_MATRIX_RETROFIT):
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]))
@@ -237,6 +283,27 @@ class CarController(CarControllerBase):
self.last_standstill = CS.out.standstill
def create_auto_brake_hold_messages(self, CS: structs.CarState, brake_hold_allowed_timer: int = 100):
can_sends = []
brake_hold_allowed = (CS.out.standstill and CS.out.cruiseState.available and
not CS.out.gasPressed and not CS.out.cruiseState.enabled and
CS.out.gearShifter not in (PARK, REVERSE))
if brake_hold_allowed:
self._brake_hold_counter += 1
self.brake_hold_active = self._brake_hold_counter > brake_hold_allowed_timer and not self._brake_hold_reset
self._brake_hold_reset = not self._prev_brake_pressed and CS.out.brakePressed and not self._brake_hold_reset
else:
self._brake_hold_counter = 0
self.brake_hold_active = False
self._brake_hold_reset = False
self._prev_brake_pressed = CS.out.brakePressed
if self.frame % 2 == 0:
can_sends.append(toyotacan.create_brake_hold_command(self.packer, self.frame, CS.pre_collision_2, self.brake_hold_active))
return can_sends
def update(self, CC, CS, now_nanos, starpilot_toggles):
actuators = CC.actuators
stopping = actuators.longControlState == LongCtrlState.stopping
@@ -330,6 +397,9 @@ class CarController(CarControllerBase):
# *** gas and brake ***
self._update_standstill_request(CC, CS, actuators, starpilot_toggles)
if self.auto_brake_hold:
can_sends.extend(self.create_auto_brake_hold_messages(CS))
interceptor_gas_cmd = self._compute_interceptor_gas_cmd(CC, CS)
# handle UI messages
@@ -390,7 +460,7 @@ class CarController(CarControllerBase):
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:
if is_ths_hybrid(self.CP) 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))
@@ -405,9 +475,11 @@ class CarController(CarControllerBase):
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 = get_prius_feedforward(feedforward, CS.out.vEgo)
elif is_camry_hybrid(self.CP) and feedforward > 0.0:
# Preserve the established Camry Hybrid acceleration response while
# allowing negative requests to track the planner at full scale.
feedforward = get_camry_hybrid_feedforward(feedforward)
pcm_accel_cmd = self.long_pid.update(error_future,
speed=CS.out.vEgo,
@@ -429,14 +501,22 @@ class CarController(CarControllerBase):
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))
elif self.CP.carFingerprint == CAR.TOYOTA_PRIUS:
pcm_accel_cmd = limit_prius_stopping_accel(pcm_accel_cmd, actuators.accel, stopping, CS.out.vEgo, lead)
else:
pcm_accel_cmd = limit_no_lead_cruise_sign_flip(pcm_accel_cmd, actuators.accel, stopping, CS.out.vEgo,
CS.out.cruiseState.speed, bool(hud_control.leadVisible))
if self.CP.carFingerprint == CAR.TOYOTA_PRIUS:
pcm_accel_cmd = limit_prius_stopping_accel(pcm_accel_cmd, actuators.accel, stopping, CS.out.vEgo, lead)
pcm_accel_cmd = float(np.clip(pcm_accel_cmd, self.params.ACCEL_MIN, self.params.ACCEL_MAX))
main_accel_cmd = 0. if self.CP.flags & ToyotaFlags.SECOC.value else pcm_accel_cmd
# Toyota's physical distance-button hold can collide with StarPilot's wheel-button
# actions and trip a temporary EPS fault. Suppress native long-press handling while
# the physical gap button is held so ACC only sees the hold as a plain button press.
allow_long_press = 0 if bool(getattr(CS, "distance_button", False)) else None
can_sends.append(toyotacan.create_accel_command(self.packer, main_accel_cmd, pcm_cancel_cmd, self.permit_braking, self.standstill_req, lead,
CS.acc_type, fcw_alert, self.distance_button, starpilot_toggles.reverse_cruise_increase))
CS.acc_type, fcw_alert, self.distance_button, starpilot_toggles.reverse_cruise_increase,
allow_long_press))
if self.CP.flags & ToyotaFlags.SECOC.value:
acc_cmd_2 = toyotacan.create_accel_command_2(self.packer, pcm_accel_cmd)
acc_cmd_2 = add_mac(self.secoc_key,
@@ -455,7 +535,9 @@ class CarController(CarControllerBase):
if self.CP.carFingerprint in UNSUPPORTED_DSU_CAR:
can_sends.append(toyotacan.create_acc_cancel_command(self.packer))
else:
can_sends.append(toyotacan.create_accel_command(self.packer, 0, pcm_cancel_cmd, True, False, lead, CS.acc_type, False, self.distance_button, starpilot_toggles.reverse_cruise_increase))
allow_long_press = 0 if bool(getattr(CS, "distance_button", False)) else None
can_sends.append(toyotacan.create_accel_command(self.packer, 0, pcm_cancel_cmd, True, False, lead, CS.acc_type, False,
self.distance_button, starpilot_toggles.reverse_cruise_increase, allow_long_press))
# *** hud ui ***
if self.CP.carFingerprint != CAR.TOYOTA_PRIUS_V:
+19 -9
View File
@@ -23,6 +23,7 @@ TEMP_STEER_FAULTS = (0, 9, 11, 21, 25)
# - lka/lta msg drop out: 3 (recoverable)
# - prolonged high driver torque: 17 (permanent)
PERM_STEER_FAULTS = (3, 17)
LKAS_BUTTON_CAR = TSS2_CAR | {CAR.TOYOTA_PRIUS}
# Traffic signals for Speed Limit Controller - Credit goes to the DragonPilot team!
@@ -43,6 +44,13 @@ def calculate_interceptor_gas_pressed(cp) -> bool:
return interceptor_gas > 805
def create_lkas_button_events(lkas_button: int, prev_lkas_button: int) -> list[structs.CarState.ButtonEvent]:
if lkas_button != 0 and lkas_button != prev_lkas_button:
return (create_button_events(1, 0, {1: ButtonType.lkas}) +
create_button_events(0, 1, {1: ButtonType.lkas}))
return []
class CarState(CarStateBase):
def __init__(self, CP, FPCP):
super().__init__(CP, FPCP)
@@ -80,13 +88,16 @@ class CarState(CarStateBase):
self.has_can_filter = self.FPCP.flags & ToyotaStarPilotFlags.RADAR_CAN_FILTER.value
self.has_SDSU = self.FPCP.flags & ToyotaStarPilotFlags.SMART_DSU.value
self.has_ZSS = self.FPCP.flags & ToyotaStarPilotFlags.ZSS.value
self.auto_brake_hold = bool(self.CP.flags & ToyotaFlags.AUTO_BRAKE_HOLD.value)
self.pre_collision_2 = {}
def update(self, can_parsers, starpilot_toggles) -> structs.CarState:
cp = can_parsers[Bus.pt]
cp_cam = can_parsers[Bus.cam]
ret = structs.CarState()
cp_acc = cp_cam if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR) else cp
dsu_bypass = bool(self.CP.flags & ToyotaFlags.DSU_BYPASS.value)
cp_acc = cp_cam if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR) or dsu_bypass else cp
if not self.CP.flags & ToyotaFlags.SECOC.value:
self.gvc = cp.vl["VSC1S07"]["GVC"]
@@ -178,7 +189,7 @@ class CarState(CarStateBase):
conversion_factor = CV.KPH_TO_MS if is_metric else CV.MPH_TO_MS
ret.cruiseState.speedCluster = cluster_set_speed * conversion_factor
if self.CP.carFingerprint in TSS2_CAR and not self.CP.flags & ToyotaFlags.DISABLE_RADAR.value:
if dsu_bypass or (self.CP.carFingerprint in TSS2_CAR and not self.CP.flags & ToyotaFlags.DISABLE_RADAR.value):
# smartDSU can intercept ACC_CONTROL, so don't require it when it's no
# longer forwarded on the PT bus.
if not self.has_SDSU:
@@ -211,20 +222,19 @@ class CarState(CarStateBase):
if self.CP.carFingerprint != CAR.TOYOTA_PRIUS_V:
self.lkas_hud = copy.copy(cp_cam.vl["LKAS_HUD"])
if self.auto_brake_hold:
self.pre_collision_2 = copy.copy(cp_cam.vl["PRE_COLLISION_2"])
if self.CP.carFingerprint not in UNSUPPORTED_DSU_CAR:
self.pcm_follow_distance = cp.vl["PCM_CRUISE_2"]["PCM_FOLLOW_DISTANCE"]
buttonEvents = []
if self.CP.carFingerprint in TSS2_CAR:
# lkas button is wired to the camera
if self.CP.carFingerprint in LKAS_BUTTON_CAR:
prev_lkas_button = self.lkas_button
self.lkas_button = cp_cam.vl["LKAS_HUD"]["LDA_ON_MESSAGE"]
buttonEvents += create_lkas_button_events(self.lkas_button, prev_lkas_button)
# Cycles between 1 and 2 when pressing the button, then rests back at 0 after ~3s
if self.lkas_button != 0 and self.lkas_button != prev_lkas_button:
buttonEvents.extend(create_button_events(1, 0, {1: ButtonType.lkas}) +
create_button_events(0, 1, {1: ButtonType.lkas}))
if self.CP.carFingerprint in TSS2_CAR:
if self.CP.carFingerprint not in (RADAR_ACC_CAR | SECOC_CAR):
# distance button is wired to the ACC module (camera or radar)
prev_distance_button = self.distance_button
@@ -4,6 +4,10 @@ from opendbc.car.toyota.values import CAR
Ecu = CarParams.Ecu
FINGERPRINTS = {
CAR.TOYOTA_MATRIX_RETROFIT: [{}],
}
FW_VERSIONS = {
CAR.TOYOTA_AVALON: {
(Ecu.abs, 0x7b0, None): [
+42 -4
View File
@@ -2,11 +2,13 @@ from opendbc.car import Bus, structs, get_safety_config, uds
from opendbc.car.toyota.carstate import CarState
from opendbc.car.toyota.carcontroller import CarController
from opendbc.car.toyota.radar_interface import RadarInterface
from opendbc.car.toyota.values import Ecu, CAR, DBC, ToyotaFlags, CarControllerParams, TSS2_CAR, RADAR_ACC_CAR, NO_DSU_CAR, \
from opendbc.car.toyota.values import Ecu, CAR, DBC, ToyotaFlags, CarControllerParams, TSS2_CAR, RADAR_ACC_CAR, SECOC_CAR, NO_DSU_CAR, \
MIN_ACC_SPEED, EPS_SCALE, NO_STOP_TIMER_CAR, ANGLE_CONTROL_CAR, \
ToyotaSafetyFlags
from opendbc.car.disable_ecu import disable_ecu
from opendbc.car.interfaces import CarInterfaceBase
from opendbc.safety import ALTERNATIVE_EXPERIENCE
from openpilot.common.params import Params
SteerControlType = structs.CarParams.SteerControlType
@@ -26,6 +28,9 @@ class CarInterface(CarInterfaceBase):
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.toyota)]
ret.safetyConfigs[0].safetyParam = EPS_SCALE[candidate]
if candidate == CAR.LEXUS_IS:
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.ALT_CRUISE.value
# BRAKE_MODULE is on a different address for these cars
if DBC[candidate][Bus.pt] == "toyota_new_mc_pt_generated":
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.ALT_BRAKE.value
@@ -58,6 +63,17 @@ class CarInterface(CarInterfaceBase):
# sDSU / radar filter hardware needs the Toyota safety long-filter TX set.
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.LONG_FILTER.value
# A DSU bypass adapter reroutes the stock DSU messages to the camera bus.
# These messages are normally absent there on pre-TSS2 platforms.
camera_fingerprint = fingerprint.get(2, {})
has_dsu_bypass = 0x343 in camera_fingerprint or 0x4CB in camera_fingerprint
if candidate == CAR.LEXUS_IS:
# The IS mirrors its native buses onto camera bus during startup without a bypass adapter.
has_dsu_bypass = ((0x343 in camera_fingerprint and 0x343 not in fingerprint.get(1, {})) or
(0x4CB in camera_fingerprint and 0x4CB not in fingerprint.get(0, {})))
if not use_sdsu and candidate not in TSS2_CAR and has_dsu_bypass:
ret.flags |= ToyotaFlags.DSU_BYPASS.value
# In TSS2 cars, the camera does long control
found_ecus = [fw.ecu for fw in car_fw]
@@ -66,10 +82,11 @@ class CarInterface(CarInterfaceBase):
if candidate == CAR.TOYOTA_PRIUS:
stop_and_go = True
ret.flags |= ToyotaFlags.HYBRID.value
# Only give steer angle deadzone to for bad angle sensor prius
for fw in car_fw:
if fw.ecu == "eps" and not fw.fwVersion == b'8965B47060\x00\x00\x00\x00\x00\x00':
ret.steerActuatorDelay = 0.25
ret.steerActuatorDelay = 0.14
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg=0.3)
elif candidate in (CAR.LEXUS_RX, CAR.LEXUS_RX_TSS2):
@@ -110,7 +127,9 @@ class CarInterface(CarInterfaceBase):
# No radar dbc for cars without DSU which are not TSS 2.0
# TODO: make an adas dbc file for dsu-less models
ret.radarUnavailable = Bus.radar not in DBC[candidate] or candidate in (NO_DSU_CAR - TSS2_CAR)
ret.radarUnavailable = Bus.radar not in DBC[candidate] or candidate in (NO_DSU_CAR - TSS2_CAR - {CAR.TOYOTA_CAMRY})
if candidate == CAR.TOYOTA_CAMRY:
ret.radarTimeStepDEPRECATED = 0.1
# Since we don't yet parse radar on TSS2/TSS-P radar-based ACC cars, gate
# longitudinal behind the alpha-long toggle.
@@ -132,11 +151,19 @@ class CarInterface(CarInterfaceBase):
# - TSS2 radar ACC cars (disables radar)
ret.openpilotLongitudinalControl = (use_sdsu or
bool(ret.flags & ToyotaFlags.DSU_BYPASS.value) or
candidate in (TSS2_CAR - RADAR_ACC_CAR) or
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 ret.enableGasInterceptorDEPRECATED:
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.GAS_INTERCEPTOR.value
toyota_auto_hold = Params(return_defaults=True).get_bool("ToyotaAutoHold")
if toyota_auto_hold and candidate in (TSS2_CAR - RADAR_ACC_CAR - SECOC_CAR):
ret.alternativeExperience |= ALTERNATIVE_EXPERIENCE.ALLOW_AEB
ret.flags |= ToyotaFlags.AUTO_BRAKE_HOLD.value
if not ret.openpilotLongitudinalControl:
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
@@ -145,7 +172,11 @@ class CarInterface(CarInterfaceBase):
# to a negative value, so it won't matter.
ret.minEnableSpeed = -1. if (stop_and_go or ret.enableGasInterceptorDEPRECATED) else MIN_ACC_SPEED
if candidate in TSS2_CAR or ret.enableGasInterceptorDEPRECATED:
prius_long_defaults = candidate == CAR.TOYOTA_PRIUS and ret.openpilotLongitudinalControl
camry_hybrid_long_defaults = (candidate == CAR.TOYOTA_CAMRY and ret.openpilotLongitudinalControl and
bool(ret.flags & ToyotaFlags.HYBRID.value))
if candidate in TSS2_CAR or ret.enableGasInterceptorDEPRECATED or prius_long_defaults:
ret.flags |= ToyotaFlags.RAISED_ACCEL_LIMIT.value
ret.vEgoStopping = 0.25
@@ -156,6 +187,13 @@ class CarInterface(CarInterfaceBase):
if ret.flags & ToyotaFlags.HYBRID.value:
ret.longitudinalActuatorDelay = 0.05
if camry_hybrid_long_defaults:
# The THS eCVT responds much faster than the legacy non-TSS2 ICE tune.
ret.longitudinalActuatorDelay = 0.05
ret.vEgoStopping = 0.25
ret.vEgoStarting = 0.25
ret.stoppingDecelRate = 0.3
if ret.enableGasInterceptorDEPRECATED:
# Pedal/SDSU Toyotas feel best with a softer final stop clamp.
ret.longitudinalActuatorDelay = max(ret.longitudinalActuatorDelay, 0.2)
@@ -2,9 +2,14 @@
from opendbc.can import CANParser
from opendbc.car import Bus
from opendbc.car.structs import RadarData
from opendbc.car.toyota.values import DBC, TSS2_CAR
from opendbc.car.toyota.values import CAR, DBC, TSS2_CAR
from opendbc.car.interfaces import RadarInterfaceBase
RADAR_ACC_TSSP_CAR = {CAR.TOYOTA_CAMRY}
TSSP_CLUSTER_MSGS = list(range(0x680, 0x686))
KPH_TO_MS = 1. / 3.6
TSSP_RADAR_EGO_SPEED_SCALE = 0.922
def _create_radar_can_parser(car_fingerprint):
if car_fingerprint in TSS2_CAR:
@@ -21,22 +26,37 @@ def _create_radar_can_parser(car_fingerprint):
return CANParser(DBC[car_fingerprint][Bus.radar], messages, 1)
def _create_tssp_radar_can_parser(car_fingerprint):
return CANParser(DBC[car_fingerprint][Bus.radar], [(addr, 10) for addr in TSSP_CLUSTER_MSGS], 1)
def _create_wheel_speed_can_parser(car_fingerprint):
return CANParser(DBC[car_fingerprint][Bus.pt], [("WHEEL_SPEEDS", 80)], 0)
class RadarInterface(RadarInterfaceBase):
def __init__(self, CP):
super().__init__(CP)
self.track_id = 0
self.radar_acc_tssp = CP.carFingerprint in RADAR_ACC_TSSP_CAR
if CP.carFingerprint in TSS2_CAR:
self.RADAR_A_MSGS = list(range(0x180, 0x190))
self.RADAR_B_MSGS = list(range(0x190, 0x1a0))
if self.radar_acc_tssp:
self.RADAR_MSGS = TSSP_CLUSTER_MSGS
self.rcp = None if CP.radarUnavailable else _create_tssp_radar_can_parser(CP.carFingerprint)
self.pt_cp = None if CP.radarUnavailable else _create_wheel_speed_can_parser(CP.carFingerprint)
self.trigger_msg = self.RADAR_MSGS[-1]
else:
self.RADAR_A_MSGS = list(range(0x210, 0x220))
self.RADAR_B_MSGS = list(range(0x220, 0x230))
if CP.carFingerprint in TSS2_CAR:
self.RADAR_A_MSGS = list(range(0x180, 0x190))
self.RADAR_B_MSGS = list(range(0x190, 0x1a0))
else:
self.RADAR_A_MSGS = list(range(0x210, 0x220))
self.RADAR_B_MSGS = list(range(0x220, 0x230))
self.valid_cnt = {key: 0 for key in self.RADAR_A_MSGS}
self.rcp = None if CP.radarUnavailable else _create_radar_can_parser(CP.carFingerprint)
self.pt_cp = None
self.trigger_msg = self.RADAR_B_MSGS[-1]
self.valid_cnt = {key: 0 for key in self.RADAR_A_MSGS}
self.rcp = None if CP.radarUnavailable else _create_radar_can_parser(CP.carFingerprint)
self.trigger_msg = self.RADAR_B_MSGS[-1]
self.updated_messages = set()
def update(self, can_strings):
@@ -45,16 +65,72 @@ class RadarInterface(RadarInterfaceBase):
vls = self.rcp.update(can_strings)
self.updated_messages.update(vls)
if self.pt_cp is not None:
self.pt_cp.update(can_strings)
if self.trigger_msg not in self.updated_messages:
return None
if self.pt_cp is not None and not self.pt_cp.can_valid:
self.updated_messages.clear()
ret = RadarData()
ret.errors.canError = True
return ret
rr = self._update(self.updated_messages)
self.updated_messages.clear()
return rr
def _get_v_ego(self):
ws = self.pt_cp.vl["WHEEL_SPEEDS"]
wheel_speed = (ws["WHEEL_SPEED_FL"] + ws["WHEEL_SPEED_FR"] +
ws["WHEEL_SPEED_RL"] + ws["WHEEL_SPEED_RR"]) / 4.
return wheel_speed * KPH_TO_MS * self.CP.wheelSpeedFactor
def _update_tssp(self, updated_messages):
ret = RadarData()
if not self.rcp.can_valid:
ret.errors.canError = True
v_ego = self._get_v_ego()
updated_ids = set()
for ii in sorted(updated_messages):
if ii not in self.RADAR_MSGS:
continue
cpt = self.rcp.vl[ii]
track_id = int(cpt["ID"])
if track_id == 0x3f or cpt["LONG_DIST"] <= 0:
continue
updated_ids.add(track_id)
if track_id not in self.pts:
self.pts[track_id] = RadarData.RadarPoint()
self.pts[track_id].trackId = self.track_id
self.track_id += 1
self.pts[track_id].dRel = float(cpt["LONG_DIST"])
self.pts[track_id].yRel = -float(cpt["LAT_DIST"])
self.pts[track_id].vRel = float(cpt["SPEED"]) - v_ego * TSSP_RADAR_EGO_SPEED_SCALE
self.pts[track_id].aRel = float("nan")
self.pts[track_id].yvRel = float(cpt["LAT_SPEED"])
self.pts[track_id].measured = True
for track_id in list(self.pts):
if track_id not in updated_ids:
del self.pts[track_id]
ret.points = list(self.pts.values())
return ret
def _update(self, updated_messages):
if self.radar_acc_tssp:
return self._update_tssp(updated_messages)
return self._update_denso(updated_messages)
def _update_denso(self, updated_messages):
ret = RadarData()
if not self.rcp.can_valid:
ret.errors.canError = True
@@ -1,20 +1,27 @@
from types import SimpleNamespace
import pytest
from hypothesis import given, settings, strategies as st
from opendbc.car import Bus
from opendbc.car import Bus, structs
from opendbc.can import CANPacker, CANParser
from opendbc.car.structs import CarParams
from opendbc.car.fw_versions import build_fw_dict
from opendbc.car.toyota import toyotacan
from opendbc.car.toyota.carcontroller import CarController, limit_interceptor_pcm_accel, limit_interceptor_stopping_accel, \
from opendbc.car.toyota.carcontroller import CarController, get_camry_hybrid_feedforward, get_long_tune, get_prius_feedforward, \
get_prius_positive_feedforward_scale, \
limit_interceptor_pcm_accel, \
limit_interceptor_stopping_accel, limit_no_lead_cruise_sign_flip, \
limit_prius_stopping_accel, update_permit_braking
from opendbc.car.toyota.carstate import calculate_interceptor_gas_pressed
from opendbc.car.toyota.carstate import CarState, LKAS_BUTTON_CAR, calculate_interceptor_gas_pressed, create_lkas_button_events
from opendbc.car.toyota.fingerprints import FW_VERSIONS
from opendbc.car.toyota.interface import CarInterface
from opendbc.car.toyota.radar_interface import RadarInterface, TSSP_RADAR_EGO_SPEED_SCALE
from opendbc.car.toyota.values import CAR, DBC, TSS2_CAR, ANGLE_CONTROL_CAR, RADAR_ACC_CAR, SECOC_CAR, \
FW_QUERY_CONFIG, PLATFORM_CODE_ECUS, FUZZY_EXCLUDED_PLATFORMS, \
get_platform_codes
ToyotaFlags, ToyotaSafetyFlags, get_platform_codes
from opendbc.safety import ALTERNATIVE_EXPERIENCE
from openpilot.common.params import Params
Ecu = CarParams.Ecu
@@ -33,6 +40,23 @@ class TestToyotaInterfaces:
# At this time, only RAV4 2023 is expected to use LTA/angle control
assert ANGLE_CONTROL_CAR == {CAR.TOYOTA_RAV4_TSS2_2023}
def test_rav4_prime_force_torque_controller(self):
fingerprint = {bus: {} for bus in range(8)}
default_params = CarInterface.get_params(
CAR.TOYOTA_RAV4_PRIME, fingerprint, [], False, False, False,
SimpleNamespace(force_torque_controller=False, nnff=False, nnff_lite=False),
)
forced_params = CarInterface.get_params(
CAR.TOYOTA_RAV4_PRIME, fingerprint, [], False, False, False,
SimpleNamespace(force_torque_controller=True, nnff=False, nnff_lite=False),
)
assert default_params.lateralTuning.which() == "pid"
assert forced_params.lateralTuning.which() == "torque"
assert forced_params.lateralTuning.torque.latAccelFactor == pytest.approx(1.7)
assert forced_params.lateralTuning.torque.friction == pytest.approx(0.14)
def test_tss2_dbc(self):
# We make some assumptions about TSS2 platforms,
# like looking up certain signals only in this DBC
@@ -40,6 +64,226 @@ class TestToyotaInterfaces:
if car_model in TSS2_CAR and car_model not in SECOC_CAR:
assert dbc[Bus.pt] == "toyota_nodsu_pt_generated"
def test_auto_hold_sets_flag_on_supported_tss2(self):
params = Params()
try:
params.put_bool("ToyotaAutoHold", True)
car_params = CarInterface.get_params(
CAR.TOYOTA_CAMRY_TSS2,
{bus: {} for bus in range(8)},
[],
alpha_long=False,
is_release=False,
docs=False,
starpilot_toggles=SimpleNamespace(),
)
finally:
params.remove("ToyotaAutoHold")
assert car_params.flags & ToyotaFlags.AUTO_BRAKE_HOLD.value
assert car_params.alternativeExperience & ALTERNATIVE_EXPERIENCE.ALLOW_AEB
def test_prius_openpilot_long_uses_hybrid_long_defaults(self):
car_params = CarInterface.get_params(
CAR.TOYOTA_PRIUS,
{0: {0x2FF: 8}},
[],
alpha_long=False,
is_release=False,
docs=False,
starpilot_toggles=SimpleNamespace(),
)
assert car_params.openpilotLongitudinalControl
assert car_params.flags & ToyotaFlags.HYBRID.value
assert car_params.flags & ToyotaFlags.RAISED_ACCEL_LIMIT.value
assert abs(car_params.longitudinalActuatorDelay - 0.05) < 1e-6
assert abs(car_params.vEgoStopping - 0.25) < 1e-6
assert abs(car_params.vEgoStarting - 0.25) < 1e-6
@pytest.mark.parametrize("camera_message", [0x343, 0x4CB])
def test_dsu_bypass_enables_longitudinal(self, camera_message):
fingerprint = {bus: {} for bus in range(8)}
fingerprint[2][camera_message] = 8
car_params = CarInterface.get_params(
CAR.TOYOTA_COROLLA,
fingerprint,
[],
alpha_long=False,
is_release=False,
docs=False,
starpilot_toggles=SimpleNamespace(),
)
assert car_params.flags & ToyotaFlags.DSU_BYPASS.value
assert car_params.openpilotLongitudinalControl
assert not car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
starpilot_params = CarInterface.get_starpilot_params(
CAR.TOYOTA_COROLLA, fingerprint, [], car_params, SimpleNamespace(),
)
car_state = CarState(car_params, starpilot_params)
can_parsers = car_state.get_can_parsers(car_params)
car_state.update(can_parsers, SimpleNamespace(cluster_offset=1.0))
for message in ("ACC_CONTROL", "PRE_COLLISION", "PCS_HUD"):
assert message not in can_parsers[Bus.pt].vl
assert message in can_parsers[Bus.cam].vl
@pytest.mark.parametrize(("native_bus", "message"), [(1, 0x343), (0, 0x4CB)])
def test_dsu_bypass_ignores_startup_bus_mirror(self, native_bus, message):
fingerprint = {bus: {} for bus in range(8)}
fingerprint[native_bus][message] = 8
fingerprint[2][message] = 8
car_params = CarInterface.get_params(
CAR.LEXUS_IS,
fingerprint,
[],
alpha_long=False,
is_release=False,
docs=False,
starpilot_toggles=SimpleNamespace(),
)
assert not car_params.flags & ToyotaFlags.DSU_BYPASS.value
assert not car_params.openpilotLongitudinalControl
assert car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
assert car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.ALT_CRUISE.value
@pytest.mark.parametrize(("native_bus", "message"), [(1, 0x343), (0, 0x4CB)])
def test_prius_dsu_bypass_allows_native_bus_message(self, native_bus, message):
fingerprint = {bus: {} for bus in range(8)}
fingerprint[native_bus][message] = 8
fingerprint[2][message] = 8
car_params = CarInterface.get_params(
CAR.TOYOTA_PRIUS,
fingerprint,
[],
alpha_long=False,
is_release=False,
docs=False,
starpilot_toggles=SimpleNamespace(),
)
assert car_params.flags & ToyotaFlags.DSU_BYPASS.value
assert car_params.openpilotLongitudinalControl
assert not car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
assert not car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.ALT_CRUISE.value
def test_dsu_bypass_does_not_change_tss2_or_smart_dsu(self):
fingerprint = {bus: {} for bus in range(8)}
fingerprint[0][0x2FF] = 8
fingerprint[2][0x343] = 8
smart_dsu_params = CarInterface.get_params(
CAR.TOYOTA_COROLLA,
fingerprint,
[],
alpha_long=False,
is_release=False,
docs=False,
starpilot_toggles=SimpleNamespace(),
)
tss2_params = CarInterface.get_params(
CAR.TOYOTA_CAMRY_TSS2,
fingerprint,
[],
alpha_long=False,
is_release=False,
docs=False,
starpilot_toggles=SimpleNamespace(),
)
assert not smart_dsu_params.flags & ToyotaFlags.DSU_BYPASS.value
assert not tss2_params.flags & ToyotaFlags.DSU_BYPASS.value
def test_camry_hybrid_continental_radar_uses_ths_longitudinal_tune(self):
fingerprint = {bus: ({0x2FF: 8} if bus == 0 else {}) for bus in range(8)}
hybrid_fw = [CarParams.CarFw(ecu=Ecu.hybrid, address=0x7D2, fwVersion=b"test")]
car_params = CarInterface.get_params(
CAR.TOYOTA_CAMRY,
fingerprint,
hybrid_fw,
alpha_long=True,
is_release=False,
docs=False,
starpilot_toggles=SimpleNamespace(),
)
assert car_params.openpilotLongitudinalControl
assert not car_params.radarUnavailable
assert abs(car_params.radarTimeStepDEPRECATED - 0.1) < 1e-6
assert abs(car_params.longitudinalActuatorDelay - 0.05) < 1e-6
assert abs(car_params.vEgoStopping - 0.25) < 1e-6
assert abs(car_params.vEgoStarting - 0.25) < 1e-6
assert abs(car_params.stoppingDecelRate - 0.3) < 1e-6
assert not car_params.flags & ToyotaFlags.NO_STOP_TIMER.value
controller = get_long_tune(car_params, SimpleNamespace(ACCEL_MIN=-3.5, ACCEL_MAX=2.0))
controller.speed = 0.0
assert controller.k_i == pytest.approx(0.5)
assert controller.k_f == pytest.approx(1.0)
radar_interface = RadarInterface(car_params)
assert radar_interface.radar_acc_tssp
assert radar_interface.rcp is not None
assert radar_interface.pt_cp is not None
def test_camry_ice_keeps_legacy_longitudinal_tune(self):
fingerprint = {bus: ({0x2FF: 8} if bus == 0 else {}) for bus in range(8)}
car_params = CarInterface.get_params(
CAR.TOYOTA_CAMRY,
fingerprint,
[],
alpha_long=True,
is_release=False,
docs=False,
starpilot_toggles=SimpleNamespace(),
)
assert not car_params.flags & ToyotaFlags.HYBRID.value
assert car_params.longitudinalActuatorDelay == pytest.approx(0.15)
assert car_params.vEgoStopping == pytest.approx(0.5)
assert car_params.stoppingDecelRate == pytest.approx(0.8)
controller = get_long_tune(car_params, SimpleNamespace(ACCEL_MIN=-3.5, ACCEL_MAX=2.0))
controller.speed = 0.0
assert controller.k_i == pytest.approx(3.6)
assert controller.k_f == pytest.approx(1.0)
def test_camry_continental_radar_converts_absolute_target_speed(self):
radar_interface = RadarInterface.__new__(RadarInterface)
radar_interface.CP = SimpleNamespace(wheelSpeedFactor=1.0)
radar_interface.pts = {}
radar_interface.track_id = 0
radar_interface.RADAR_MSGS = [0x680]
radar_interface.pt_cp = SimpleNamespace(vl={
"WHEEL_SPEEDS": {
"WHEEL_SPEED_FL": 36.0,
"WHEEL_SPEED_FR": 36.0,
"WHEEL_SPEED_RL": 36.0,
"WHEEL_SPEED_RR": 36.0,
},
})
radar_interface.rcp = SimpleNamespace(can_valid=True, vl={
0x680: {
"ID": 7,
"LONG_DIST": 40.0,
"LAT_DIST": -0.2,
"SPEED": 11.0,
"LAT_SPEED": 0.1,
},
})
radar_data = radar_interface._update_tssp({0x680})
assert len(radar_data.points) == 1
assert radar_data.points[0].dRel == 40.0
assert radar_data.points[0].vRel == pytest.approx(11.0 - 10.0 * TSSP_RADAR_EGO_SPEED_SCALE)
def test_essential_ecus(self, subtests):
# Asserts standard ECUs exist for each platform
common_ecus = {Ecu.fwdRadar, Ecu.fwdCamera}
@@ -253,6 +497,18 @@ class TestToyotaCarController:
assert update_permit_braking(False, 0.10, True, True, 25.0, False) is True
assert update_permit_braking(False, 0.10, False, False, 25.0, False) is True
def test_no_lead_cruise_sign_flip_clamps_negative_pulse_when_set_speed_is_ahead(self):
limited = limit_no_lead_cruise_sign_flip(-0.44, 0.0, False, 23.3, 25.0, False)
assert limited == 0.0
def test_no_lead_cruise_sign_flip_keeps_real_decel_requests(self):
limited = limit_no_lead_cruise_sign_flip(-0.44, -0.15, False, 23.3, 25.0, False)
assert limited == -0.44
def test_no_lead_cruise_sign_flip_keeps_lead_follow_brake(self):
limited = limit_no_lead_cruise_sign_flip(-0.44, 0.0, False, 23.3, 25.0, True)
assert limited == -0.44
def test_prius_stopping_accel_unwinds_stale_stop_hold(self):
limited = limit_prius_stopping_accel(-3.28, -0.05, True, 0.0, True)
assert -1.5 < limited < 0.0
@@ -261,6 +517,22 @@ class TestToyotaCarController:
limited = limit_prius_stopping_accel(-3.28, -2.0, True, 0.0, True)
assert limited == -3.28
def test_prius_positive_feedforward_scale_stays_soft_at_launch_speed(self):
assert abs(get_prius_positive_feedforward_scale(0.0) - 0.7) < 1e-6
assert abs(get_prius_positive_feedforward_scale(8.0) - 0.7) < 1e-6
def test_prius_positive_feedforward_scale_restores_cruise_authority(self):
assert get_prius_positive_feedforward_scale(20.0) > get_prius_positive_feedforward_scale(8.0)
assert abs(get_prius_positive_feedforward_scale(20.0) - 1.0) < 1e-6
def test_prius_feedforward_adds_braking_authority_without_changing_acceleration(self):
assert get_prius_feedforward(-2.0, 8.0) == pytest.approx(-2.25)
assert get_prius_feedforward(1.0, 8.0) == pytest.approx(0.7)
def test_camry_hybrid_feedforward_only_softens_acceleration(self):
assert get_camry_hybrid_feedforward(1.0) == pytest.approx(0.8)
assert get_camry_hybrid_feedforward(-2.0) == pytest.approx(-2.0)
def test_sng_hack_clears_existing_standstill_latch(self):
controller = self._make_controller(standstill_req=True, last_standstill=True)
@@ -297,6 +569,49 @@ class TestToyotaCarController:
assert parser.vl["LKAS_HUD"]["LEFT_LINE"] == 0
assert parser.vl["LKAS_HUD"]["RIGHT_LINE"] == 0
def test_acc_control_can_suppress_long_press_behavior_while_gap_button_is_held(self):
packer = CANPacker(DBC[CAR.TOYOTA_HIGHLANDER_TSS2][Bus.pt])
parser = CANParser(DBC[CAR.TOYOTA_HIGHLANDER_TSS2][Bus.pt], [("ACC_CONTROL", 0)], 0)
default_msg = toyotacan.create_accel_command(
packer, 0.0, False, True, False, False, 1, False, 0, False,
)
parser.update([(1, [default_msg])])
assert parser.vl["ACC_CONTROL"]["ALLOW_LONG_PRESS"] == 1
suppressed_msg = toyotacan.create_accel_command(
packer, 0.0, False, True, False, False, 1, False, 0, False, allow_long_press=0,
)
parser.update([(1, [suppressed_msg])])
assert parser.vl["ACC_CONTROL"]["ALLOW_LONG_PRESS"] == 0
def test_auto_brake_hold_sends_modified_pre_collision_after_timer(self):
controller = self._make_controller()
controller.packer = CANPacker(DBC[CAR.TOYOTA_CAMRY_TSS2][Bus.pt])
controller.frame = 0
controller.brake_hold_active = False
controller._brake_hold_counter = 0
controller._brake_hold_reset = False
controller._prev_brake_pressed = False
cs = SimpleNamespace(
out=SimpleNamespace(
standstill=True,
cruiseState=SimpleNamespace(available=True, enabled=False),
gasPressed=False,
brakePressed=False,
gearShifter=structs.CarState.GearShifter.drive,
),
pre_collision_2={},
)
can_sends = controller.create_auto_brake_hold_messages(cs, brake_hold_allowed_timer=0)
parser = CANParser(DBC[CAR.TOYOTA_CAMRY_TSS2][Bus.pt], [("PRE_COLLISION_2", 0)], 0)
parser.update([(1, can_sends)])
assert controller.brake_hold_active
assert parser.vl["PRE_COLLISION_2"]["DSS1GDRV"] == -1.0
assert parser.vl["PRE_COLLISION_2"]["PBRTRGR"] == 1
def test_interceptor_stop_and_go_holds_small_launch_at_standstill(self):
controller = self._make_controller()
controller.CP.enableGasInterceptorDEPRECATED = True
@@ -323,6 +638,20 @@ class TestToyotaCarController:
assert 0.0 < gas_cmd <= 0.5
def test_interceptor_corolla_scales_with_accel_request_when_pedal_enables_sng(self):
controller = self._make_controller()
controller.CP.enableGasInterceptorDEPRECATED = True
controller.CP.carFingerprint = CAR.TOYOTA_COROLLA
controller.CP.minEnableSpeed = -1.0
controller.accel = 0.8
gas_cmd = controller._compute_interceptor_gas_cmd(
SimpleNamespace(longActive=True),
SimpleNamespace(out=SimpleNamespace(standstill=False, vEgo=8.0)),
)
assert 0.0 < gas_cmd <= 0.5
def test_interceptor_disabled_returns_zero(self):
controller = self._make_controller()
controller.accel = 1.0
@@ -401,11 +730,35 @@ class TestToyotaCarController:
)
assert CP.enableGasInterceptorDEPRECATED
assert CP.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.GAS_INTERCEPTOR
assert abs(CP.longitudinalActuatorDelay - 0.2) < 1e-6
assert CP.stopAccel == -1.5
class TestToyotaCarState:
def test_lkas_button_platforms(self):
assert CAR.TOYOTA_PRIUS in LKAS_BUTTON_CAR
assert TSS2_CAR <= LKAS_BUTTON_CAR
assert CAR.TOYOTA_CAMRY not in LKAS_BUTTON_CAR
assert CAR.LEXUS_RX not in LKAS_BUTTON_CAR
@pytest.mark.parametrize("lkas_button,prev_lkas_button,event_count", [
(0, 0, 0),
(1, 0, 2),
(1, 1, 0),
(0, 1, 0),
(2, 1, 2),
])
def test_lkas_button_events(self, lkas_button, prev_lkas_button, event_count):
events = create_lkas_button_events(lkas_button, prev_lkas_button)
assert len(events) == event_count
if events:
assert [(event.type, event.pressed) for event in events] == [
(structs.CarState.ButtonEvent.Type.lkas, True),
(structs.CarState.ButtonEvent.Type.lkas, False),
]
def test_interceptor_gas_pressed_threshold(self):
cp = SimpleNamespace(vl={
"GAS_SENSOR": {
+38 -2
View File
@@ -40,8 +40,12 @@ def create_lta_steer_command_2(packer, frame):
return packer.make_can_msg("STEERING_LTA_2", 0, values)
def create_accel_command(packer, accel, pcm_cancel, permit_braking, standstill_req, lead, acc_type, fcw_alert, distance, reverse_cruise_active):
def create_accel_command(packer, accel, pcm_cancel, permit_braking, standstill_req, lead, acc_type, fcw_alert,
distance, reverse_cruise_active, allow_long_press=None):
# TODO: find the exact canceling bit that does not create a chime
if allow_long_press is None:
allow_long_press = 2 if reverse_cruise_active else 1
values = {
"ACCEL_CMD": accel,
"ACC_TYPE": acc_type,
@@ -50,7 +54,7 @@ def create_accel_command(packer, accel, pcm_cancel, permit_braking, standstill_r
"PERMIT_BRAKING": permit_braking,
"RELEASE_STANDSTILL": not standstill_req,
"CANCEL_REQ": pcm_cancel,
"ALLOW_LONG_PRESS": 2 if reverse_cruise_active else 1,
"ALLOW_LONG_PRESS": allow_long_press,
"ACC_CUT_IN": fcw_alert, # only shown when ACC enabled
}
return packer.make_can_msg("ACC_CONTROL", 0, values)
@@ -86,6 +90,38 @@ def create_pcs_commands(packer, accel, active, mass):
return [msg1, msg2]
def create_brake_hold_command(packer, frame, pre_collision_2, brake_hold_active):
values = {s: pre_collision_2[s] for s in [
"DSS1GDRV",
"DS1STAT2",
"DS1STBK2",
"PCSWAR",
"PCSALM",
"PCSOPR",
"PCSABK",
"PBATRGR",
"PPTRGR",
"IBTRGR",
"CLEXTRGR",
"IRLT_REQ",
"BRKHLD",
"AVSTRGR",
"VGRSTRGR",
"PREFILL",
"PBRTRGR",
"PCSDIS",
"PBPREPMP",
] if s in pre_collision_2}
if brake_hold_active:
values = {
"DSS1GDRV": 0x3FF,
"PBRTRGR": frame % 730 < 727,
}
return packer.make_can_msg("PRE_COLLISION_2", 0, values)
def create_acc_cancel_command(packer):
values = {
"GAS_RELEASED": 0,
+14 -3
View File
@@ -57,12 +57,16 @@ class ToyotaSafetyFlags(IntFlag):
LTA = (4 << 8)
SECOC = (8 << 8)
LONG_FILTER = (16 << 8)
GAS_INTERCEPTOR = (32 << 8)
ALT_CRUISE = (64 << 8)
class ToyotaFlags(IntFlag):
# Detected flags
HYBRID = 1
DISABLE_RADAR = 4
# The DSU's ACC messages are rerouted through the camera bus by an adapter.
DSU_BYPASS = 8192
# Static flags
TSS2 = 8
@@ -75,6 +79,7 @@ class ToyotaFlags(IntFlag):
# these cars can utilize 2.0 m/s^2
RAISED_ACCEL_LIMIT = 1024
SECOC = 2048
AUTO_BRAKE_HOLD = 4096
# deprecated flags
# these cars are speculated to allow stop and go when the DSU is unplugged
@@ -169,7 +174,7 @@ class CAR(Platforms):
ToyotaCarDocs("Toyota Camry Hybrid 2018-20", video="https://www.youtube.com/watch?v=Q2DYY0AWKgk"),
],
CarSpecs(mass=3400. * CV.LB_TO_KG, wheelbase=2.82448, steerRatio=13.7, tireStiffnessFactor=0.7933),
dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
dbc_dict('toyota_nodsu_pt_generated', 'toyota_radar_dsu_tssp'),
flags=ToyotaFlags.NO_DSU,
)
TOYOTA_CAMRY_TSS2 = ToyotaTSS2PlatformConfig( # TSS 2.5
@@ -201,6 +206,11 @@ class CAR(Platforms):
CarSpecs(mass=2860. * CV.LB_TO_KG, wheelbase=2.7, steerRatio=18.27, tireStiffnessFactor=0.444),
dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
)
TOYOTA_MATRIX_RETROFIT = PlatformConfig(
[ToyotaCommunityCarDocs("Toyota Matrix 2005 Retrofit", package="Custom retrofit")],
TOYOTA_COROLLA.specs,
dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
)
# LSS2 Lexus UX Hybrid is same as a TSS2 Corolla Hybrid
TOYOTA_COROLLA_TSS2 = ToyotaTSS2PlatformConfig(
[
@@ -548,7 +558,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
],
non_essential_ecus={
# FIXME: On some models, abs can sometimes be missing
Ecu.abs: [CAR.TOYOTA_RAV4, CAR.TOYOTA_COROLLA, CAR.TOYOTA_HIGHLANDER, CAR.TOYOTA_SIENNA, CAR.LEXUS_IS, CAR.TOYOTA_ALPHARD_TSS2],
Ecu.abs: [CAR.TOYOTA_RAV4, CAR.TOYOTA_COROLLA, CAR.TOYOTA_MATRIX_RETROFIT, CAR.TOYOTA_HIGHLANDER, CAR.TOYOTA_SIENNA, CAR.LEXUS_IS, CAR.TOYOTA_ALPHARD_TSS2],
# On some models, the engine can show on two different addresses
Ecu.engine: [CAR.TOYOTA_HIGHLANDER, CAR.TOYOTA_CAMRY, CAR.TOYOTA_COROLLA_TSS2, CAR.TOYOTA_CHR, CAR.TOYOTA_CHR_TSS2, CAR.LEXUS_IS,
CAR.LEXUS_IS_TSS2, CAR.LEXUS_RC, CAR.LEXUS_NX, CAR.LEXUS_NX_TSS2, CAR.LEXUS_RX, CAR.LEXUS_RX_TSS2],
@@ -589,7 +599,8 @@ STEER_THRESHOLD = 100
# These cars have non-standard EPS torque scale factors. All others are 73
EPS_SCALE = defaultdict(lambda: 73,
{CAR.TOYOTA_PRIUS: 66, CAR.TOYOTA_COROLLA: 88, CAR.LEXUS_IS: 77, CAR.LEXUS_RC: 77, CAR.LEXUS_CTH: 100, CAR.TOYOTA_PRIUS_V: 100})
{CAR.TOYOTA_PRIUS: 66, CAR.TOYOTA_COROLLA: 88, CAR.TOYOTA_MATRIX_RETROFIT: 88,
CAR.LEXUS_IS: 77, CAR.LEXUS_RC: 77, CAR.LEXUS_CTH: 100, CAR.TOYOTA_PRIUS_V: 100})
# Toyota/Lexus Safety Sense 2.0 and 2.5
TSS2_CAR = CAR.with_flags(ToyotaFlags.TSS2)
@@ -94,6 +94,9 @@ class CarInterface(CarInterfaceBase):
if candidate == CAR.PORSCHE_MACAN_MK1:
ret.steerActuatorDelay = 0.07
elif candidate == CAR.VOLKSWAGEN_TAOS_MK1:
# Logged Taos braking response aligns about 0.1 s later than the MQB default.
ret.longitudinalActuatorDelay = 0.25
ret.pcmCruise = not ret.openpilotLongitudinalControl
ret.stopAccel = -0.55
@@ -2,6 +2,7 @@ import random
import re
from opendbc.car.structs import CarParams
from opendbc.car.volkswagen.interface import CarInterface
from opendbc.car.volkswagen.values import CAR, FW_QUERY_CONFIG, WMI
from opendbc.car.volkswagen.fingerprints import FW_VERSIONS
@@ -13,6 +14,13 @@ SPARE_PART_FW_PATTERN = re.compile(b'\xf1\x87(?P<gateway>[0-9][0-9A-Z]{2})(?P<un
class TestVolkswagenPlatformConfigs:
def test_taos_longitudinal_actuator_delay(self):
taos_cp = CarInterface.get_non_essential_params(CAR.VOLKSWAGEN_TAOS_MK1)
golf_cp = CarInterface.get_non_essential_params(CAR.VOLKSWAGEN_GOLF_MK7)
assert abs(taos_cp.longitudinalActuatorDelay - 0.25) < 1e-6
assert abs(golf_cp.longitudinalActuatorDelay - 0.15) < 1e-6
def test_spare_part_fw_pattern(self, subtests):
# Relied on for determining if a FW is likely VW
for platform, ecus in FW_VERSIONS.items():
+1
View File
@@ -10,6 +10,7 @@ source_files = [
for f in Path("generator").rglob("*")
if f.is_file() and f.suffix in {".py", ".dbc"}
]
source_files.append(File("hyundai_kia_generic.dbc"))
output_files = [
f.name.replace(".dbc", "_generated.dbc")

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