Compare commits

...

315 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
494 changed files with 299493 additions and 9789 deletions
+1
View File
@@ -21,6 +21,7 @@ compiledmodels/
/docs_site/
*.mp4
!docs/assets/speed-limit-vision-demo*.mp4
*.dylib
*.DSYM
*.d
+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
Binary file not shown.
+3 -1
View File
@@ -2192,6 +2192,7 @@ struct DriverStateV2 {
rightBlinkProb @8 :Float32;
sunglassesProb @9 :Float32;
phoneProb @13 :Float32;
sleepProb @14 :Float32;
notReadyProbDEPRECATED @12 :List(Float32);
occludedProbDEPRECATED @10 :Float32;
readyProbDEPRECATED @11 :List(Float32);
@@ -2263,7 +2264,8 @@ struct DriverMonitoringStateDEPRECATED @0xb83cda094a1da284 {
struct DriverMonitoringState {
lockout @0 :Bool;
lockoutRecoveryPercent @11 :Int8;
lockoutCount @15 :Int8;
lockoutMinutesRemaining @11 :Int8;
alert3Count @12 :Int8;
noResponseCount @13 :Int8;
noResponseForceDecel @14 :Bool;
+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)
+224 -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,14 +36,16 @@ 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}},
{"PersistChillState", {PERSISTENT, BOOL, "0", "0", 1}},
{"PersistExperimentalState", {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}},
@@ -56,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}},
@@ -69,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}},
@@ -124,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}},
@@ -144,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}},
@@ -162,9 +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}},
{"AlertVolumeControl", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
{"AllowImpossibleAcceleration", {PERSISTENT, BOOL, "0", "0", 3}},
{"AlwaysOnLateral", {PERSISTENT, BOOL, "1", "0", 0}},
{"AlwaysOnLateral", {PERSISTENT, BOOL, "1", "0", 0, SETTINGS_SIMPLE}},
{"AlwaysOnLateralLKAS", {PERSISTENT, BOOL, "1", "0", 2}},
{"ApiCache_DriveStats", {PERSISTENT, JSON, "{}", "{}"}},
{"AutomaticallyDownloadModels", {PERSISTENT, BOOL, "1", "0", 1}},
@@ -177,30 +186,30 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"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}},
{"CameraOffset", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"CameraView", {PERSISTENT, INT, "3", "0", 2}},
{"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}},
@@ -208,19 +217,19 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"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}},
{"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}},
{"ConditionalChill", {PERSISTENT, BOOL, "0", "0", 1}},
{"ConditionalExperimental", {PERSISTENT, BOOL, "1", "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}},
@@ -230,10 +239,10 @@ 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}},
@@ -241,9 +250,9 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"StandardPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
{"RelaxedPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
{"CustomThemes", {PERSISTENT, BOOL, "0", "0", 0}},
{"CustomUI", {PERSISTENT, BOOL, "1", "0", 1}},
{"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}},
@@ -254,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, "", ""}},
@@ -273,48 +283,53 @@ 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}},
{"IgnoreIgnitionLine", {PERSISTENT, BOOL, "0", "0"}},
{"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, "", ""}},
@@ -324,70 +339,67 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"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}},
{"HideSteeringWheel", {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}},
{"PrioritizeSmoothFollowing", {PERSISTENT, BOOL, "0", "0", 2}},
{"HumanLaneChanges", {PERSISTENT, BOOL, "0", "0", 2}},
{"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}},
{"LoudBlindspotAlertWhenDisengaged", {PERSISTENT, BOOL, "0", "0", 0}},
{"LowVoltageShutdown", {PERSISTENT, FLOAT, "11.8", "11.8", 3}},
{"MainCruiseButtonControl", {PERSISTENT, INT, "0", "0", 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}},
@@ -399,12 +411,13 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"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}},
{"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, "{}", "{}"}},
@@ -416,29 +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}},
{"ModelManifestVersion", {PERSISTENT, STRING, "", "", 1}},
{"NavigationUI", {PERSISTENT, BOOL, "1", "0", 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}},
{"NudgelessLaneChangeOnlyWhenEngaged", {PERSISTENT, BOOL, "0", "0", 1}},
{"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}},
@@ -446,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}},
{"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, "0", "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}},
@@ -494,42 +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}},
{"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, "1", "0", 3}},
{"ShowCSCStatus", {PERSISTENT, BOOL, "1", "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, "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}},
@@ -538,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}},
{"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, "[]", "[]"}},
@@ -557,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}},
@@ -566,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, "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}},
{"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}},
@@ -584,20 +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}},
{"StockConfidenceBallWidget", {PERSISTENT, BOOL, "0", "0", 0}},
{"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}},
{"SubaruSNGManualParkingBrake", {PERSISTENT, BOOL, "0", "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, "", ""}},
@@ -606,12 +625,12 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"TinygradUpdateAvailable", {PERSISTENT, BOOL, "0", "0", 1}},
{"ToyotaDoors", {PERSISTENT, BOOL, "1", "0", 0}},
{"TrailerLoad", {PERSISTENT, INT, "0", "0", 2}},
{"TrafficFollow", {PERSISTENT, FLOAT, "0.5", "0.5", 2}},
{"TrafficJerkAcceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"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}},
@@ -623,28 +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}},
{"VoltOnePedalMode", {PERSISTENT, BOOL, "0", "0", 2}},
{"ToyotaAutoHold", {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, "stock", "stock", 0}},
{"WheelSpeed", {PERSISTENT, BOOL, "0", "0", 2}},
{"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);
}
+25 -1
View File
@@ -6,7 +6,7 @@ This workflow rebuilds StarPilot driving and driver-monitoring artifacts for the
- The supported build device is `comma@192.168.3.110`.
- Never run these commands against `192.168.3.109`.
- Do not compile normal and big-GPU artifacts together. This workflow builds normal QCOM artifacts only.
- 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
@@ -82,6 +82,28 @@ The lower-level device compiler also supports direct use:
./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
@@ -139,6 +161,8 @@ The generator preserves existing IDs and behavioral metadata and adds
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:
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)
+67 -1
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,8 +152,29 @@ 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",
@@ -133,6 +192,7 @@ 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 = [
@@ -152,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
}
@@ -160,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.17"
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"
+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
@@ -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, ChryslerStarPilotFlags
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 = []
@@ -82,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()
@@ -89,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):
@@ -140,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
+116 -19
View File
@@ -6,7 +6,7 @@ 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
@@ -66,6 +66,16 @@ 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):
@@ -120,6 +130,10 @@ def get_acc_dashboard_status_active(CP, CC):
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
@@ -183,16 +197,31 @@ def should_send_adas_status(CP, is_kaofui_car):
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) -> float:
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.95, 0.88, 0.82, 0.76]))
mid_scale = float(np.interp(v_ego, [12.0, 18.0, 25.0, 35.0], [0.98, 0.94, 0.89, 0.84]))
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
@@ -203,6 +232,27 @@ def shape_truck_positive_accel(accel: float, v_ego: float, enabled: bool) -> flo
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
@@ -222,6 +272,7 @@ def supports_volt_auto_hold(CP, auto_hold_enabled: bool):
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
)
@@ -233,6 +284,7 @@ def supports_volt_one_pedal(CP, one_pedal_enabled: bool):
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
@@ -305,6 +357,14 @@ 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
@@ -419,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)
@@ -479,6 +544,7 @@ class CarController(CarControllerBase):
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()
@@ -869,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:
@@ -932,14 +1010,21 @@ 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_input = actuators.accel + accel_due_to_pitch
if (
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 = shape_truck_positive_accel(accel_input, CS.out.vEgo, True)
)
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))
@@ -957,6 +1042,12 @@ class CarController(CarControllerBase):
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(
@@ -1013,17 +1104,19 @@ 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:
@@ -1053,6 +1146,8 @@ class CarController(CarControllerBase):
# 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
@@ -1097,8 +1192,10 @@ class CarController(CarControllerBase):
if should_send_acc_dashboard_status(self.CP, dash_speed_spoof_active):
fcw_alert = get_acc_dashboard_fcw_alert(hud_alert, CS)
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))
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)
@@ -220,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],
})
+51 -3
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)
@@ -215,16 +219,24 @@ def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_s
return packer.make_can_msg("EBCMFrictionBrakeCmd", bus, values)
def create_acc_dashboard_command(packer, bus, enabled, target_speed_kph, hud_control, fcw_alert):
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,
"ACCAlwaysOne": acc_always_one,
"ACCCruiseState": ACC_CRUISE_STATE_ADAPTIVE,
"ACCResumeButton": 0,
"ACCSpeedSetpoint": target_speed,
"ACCGapLevel": hud_control.leadDistanceBars * enabled, # 3 "far", 0 "inactive"
"ACCCmdActive": enabled,
"ACCAlwaysOne2": 1,
"ACCAlwaysOne2": acc_always_one,
"ACCLeadCar": hud_control.leadVisible,
"FCWAlert": int(fcw_alert) & 0x3,
}
@@ -308,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):
+30 -9
View File
@@ -72,6 +72,18 @@ NON_LINEAR_TORQUE_PARAMS = {
},
}
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
@@ -169,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"
@@ -187,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):
@@ -197,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):
@@ -422,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
@@ -495,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.
@@ -595,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
@@ -638,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
@@ -689,6 +706,7 @@ class CarInterface(CarInterfaceBase):
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_REMOTE_START_BOOTS_COMMA.value
volt_stock_friction_brake_safety = (
ret.openpilotLongitudinalControl and
(gm_auto_hold or volt_one_pedal_mode) and
candidate in {
CAR.CHEVROLET_VOLT,
@@ -699,12 +717,14 @@ class CarInterface(CarInterfaceBase):
)
if volt_stock_friction_brake_safety:
# Reuse the paddle-scheduler safety bit as a Volt stock friction-brake
# marker on non-pedal paths. Both 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.
# 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,
@@ -717,6 +737,7 @@ class CarInterface(CarInterfaceBase):
# 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 = (
@@ -54,10 +54,12 @@ from opendbc.car.gm.carcontroller import (
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,
@@ -356,6 +358,19 @@ 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,
@@ -411,7 +426,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,
openpilotLongitudinalControl=False,
@@ -420,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,
@@ -464,6 +479,7 @@ def test_volt_one_pedal_requires_toggle_supported_volt_stock_safety_and_ev_trans
assert supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
openpilotLongitudinalControl=True,
safetyConfigs=stock_safety,
transmissionType=structs.CarParams.TransmissionType.direct,
),
@@ -472,6 +488,7 @@ def test_volt_one_pedal_requires_toggle_supported_volt_stock_safety_and_ev_trans
assert not supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
openpilotLongitudinalControl=True,
safetyConfigs=no_safety,
transmissionType=structs.CarParams.TransmissionType.direct,
),
@@ -480,6 +497,7 @@ def test_volt_one_pedal_requires_toggle_supported_volt_stock_safety_and_ev_trans
assert not supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CC,
openpilotLongitudinalControl=True,
safetyConfigs=stock_safety,
transmissionType=structs.CarParams.TransmissionType.direct,
),
@@ -488,6 +506,7 @@ def test_volt_one_pedal_requires_toggle_supported_volt_stock_safety_and_ev_trans
assert not supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
openpilotLongitudinalControl=True,
safetyConfigs=stock_safety,
transmissionType=structs.CarParams.TransmissionType.automatic,
),
@@ -496,6 +515,16 @@ def test_volt_one_pedal_requires_toggle_supported_volt_stock_safety_and_ev_trans
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,
),
@@ -799,7 +828,7 @@ def test_calc_pedal_command_keeps_strong_positive_requests_responsive():
def test_shape_truck_positive_accel_softens_small_highway_requests():
shaped = shape_truck_positive_accel(0.12, 26.0, True)
assert 0.09 < shaped < 0.10
assert 0.08 < shaped < 0.095
def test_shape_truck_positive_accel_keeps_mid_follow_requests_available():
@@ -817,6 +846,37 @@ def test_shape_truck_positive_accel_is_inactive_when_disabled_or_low_speed():
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)
+149 -2
View File
@@ -11,6 +11,7 @@ from opendbc.car.gm import gmcan
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_volt_one_pedal_lift_brake,
should_send_acc_dashboard_status,
@@ -168,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()
@@ -181,7 +197,7 @@ 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]
@@ -235,6 +251,22 @@ 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()
@@ -254,6 +286,25 @@ class TestGMInterface:
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]
@@ -311,6 +362,9 @@ class TestGMInterface:
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]
@@ -379,6 +433,19 @@ class TestGMInterface:
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):
@@ -490,6 +557,53 @@ 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)
@@ -504,7 +618,39 @@ class TestGMCarController:
parser.update([0, [msg]])
assert parser.vl["ASCMActiveCruiseControlStatus"]["FCWAlert"] == 2
values = parser.vl["ASCMActiveCruiseControlStatus"]
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,
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])
@@ -522,6 +668,7 @@ class TestGMCarController:
values = parser.vl["ASCMActiveCruiseControlStatus"]
assert values["ACCSpeedSetpoint"] == 50
assert values["ACCCruiseState"] == 2
assert values["ACCGapLevel"] == 0
assert values["ACCCmdActive"] == 0
assert values["ACCLeadCar"] == 1
@@ -28,6 +28,14 @@ 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)
+10
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),
@@ -368,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),
@@ -570,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,
@@ -587,6 +596,7 @@ ASCM_INT = {
CAR.CADILLAC_ESCALADE_ASCM,
CAR.CADILLAC_ESCALADE_ESV_2019_ASCM,
CAR.BUICK_LACROSSE_ASCM,
CAR.BUICK_LACROSSE_ASCM_19US,
}
STEER_THRESHOLD = 1.0
+264 -46
View File
@@ -4,12 +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, \
kia_ev6_gt_line_longitudinal_tuning
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
@@ -53,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]
@@ -66,13 +67,16 @@ 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_HIGH_ANGLE_GAIN_BP = [70.0, 120.0, 220.0, 320.0]
EV9_HIGH_ANGLE_GAIN_CAP_V = [0.85, 0.55, 0.30, 0.16]
EV9_HIGH_ANGLE_GAIN_MIN = 0.004
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 == CAR.HYUNDAI_IONIQ_6 or \
return CP.carFingerprint in (CAR.HYUNDAI_IONIQ_6, CAR.KIA_EV9) or \
kia_ev6_gt_line_longitudinal_tuning(CP.carFingerprint, getattr(CP, "carVin", ""))
@@ -108,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))
@@ -120,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 \
@@ -163,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)
@@ -229,6 +343,12 @@ 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])
@@ -246,14 +366,6 @@ def compute_torque_reduction_gain(steering_torque, v_ego, lat_active, last_gain)
return round(gain / 0.004) * 0.004
def apply_ev9_high_angle_gain_cap(CP, gain: float, steering_angle_deg: float, lat_active: bool) -> float:
if CP.carFingerprint != CAR.KIA_EV9 or not CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING or not lat_active:
return gain
cap = float(np.interp(abs(steering_angle_deg), EV9_HIGH_ANGLE_GAIN_BP, EV9_HIGH_ANGLE_GAIN_CAP_V))
return max(EV9_HIGH_ANGLE_GAIN_MIN, min(gain, cap))
def process_hud_alert(enabled, fingerprint, hud_control):
sys_warning = (hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw))
@@ -288,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
@@ -298,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
@@ -391,37 +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(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_torque = apply_ev9_high_angle_gain_cap(self.CP, apply_torque, CS.out.steeringAngleDeg, CC.latActive)
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
@@ -464,18 +596,30 @@ class CarController(CarControllerBase):
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)
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 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
@@ -615,26 +759,54 @@ class CarController(CarControllerBase):
# steering control
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 self.CP.carFingerprint == CAR.KIA_EV9 and self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
# EV9 faults if the angle-steering status drops inactive during torque limiting.
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
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))
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
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,
@@ -670,13 +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, self.CP.carFingerprint))
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:
@@ -711,9 +911,27 @@ class CarController(CarControllerBase):
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
+30 -2
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
@@ -136,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
@@ -149,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 = (
@@ -448,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:
@@ -463,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
@@ -537,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"]
@@ -544,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()
@@ -592,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
@@ -999,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',
@@ -1018,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',
@@ -1481,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',
@@ -40,7 +40,8 @@ def create_lkas11(packer, frame, CP, apply_torque, steer_req,
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.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_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
+255 -32
View File
@@ -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,12 +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
ev9_angle_lkas_alt = str(CP.carFingerprint) == "KIA_EV9" and CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING and \
CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
angle_lkas_alt = CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING and CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
control_values = {
"LKA_MODE": 2,
@@ -130,48 +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 ev9_angle_lkas_alt:
if angle_lkas_alt:
if lat_active:
lkas_values.update({
"LKA_MODE": 0,
"LKA_AVAILABLE": 3,
"LKA_WARNING": 0,
"LKA_ICON": lka_icon,
"FCA_SYSWARN": 0,
"TORQUE_REQUEST": 0,
"STEER_REQ": 0,
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_ASSIST": 0,
"DAMP_FACTOR": 100,
"HAS_LANE_SAFETY": 0,
})
elif lkas_base_values:
for signal in ("LKA_MODE", "LKA_AVAILABLE", "LKA_WARNING", "LKA_ICON", "FCA_SYSWARN",
"LFA_BUTTON", "LKA_ASSIST", "DAMP_FACTOR", "HAS_LANE_SAFETY"):
if signal in lkas_base_values:
lkas_values[signal] = lkas_base_values[signal]
lkas_values.update({
"TORQUE_REQUEST": 0,
"STEER_REQ": 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_ICON": lka_icon,
"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,
"DAMP_FACTOR": 100,
"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,
})
# These signals overlap DAMP_FACTOR in the local DBC naming; omitting them
# preserves the stock angle-steering damping byte expected by the ADAS ECU.
lkas_values.pop("STEER_MODE", None)
lkas_values.pop("NEW_SIGNAL_2", None)
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:
@@ -194,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
@@ -387,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"),
@@ -731,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)):
@@ -757,10 +843,39 @@ def hkg_can_fd_checksum(address: int, sig, d: bytearray) -> int:
# 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,
car_fingerprint=None) -> CanData:
template = _KIA_EV9_ACCEL_BRAKE_ALT_TEMPLATE if str(car_fingerprint) == "KIA_EV9" else _ACCEL_BRAKE_ALT_TEMPLATE
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)
@@ -769,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),
]
+35 -3
View File
@@ -48,6 +48,12 @@ def apply_kia_ev6_gt_line_longitudinal_params(ret: structs.CarParams) -> None:
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
@@ -104,10 +110,10 @@ class CarInterface(CarInterfaceBase):
# 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:
@@ -115,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]:
@@ -148,6 +158,13 @@ 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
@@ -174,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:
@@ -218,6 +237,8 @@ class CarInterface(CarInterfaceBase):
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:
@@ -238,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
@@ -8,10 +8,14 @@ from opendbc.car import Bus, ButtonType, gen_empty_fingerprint, structs
from opendbc.car.structs import CarControl, CarParams
from opendbc.car.fw_versions import build_fw_dict, match_fw_to_car
from opendbc.car.hyundai.carcontroller import CarController, Ioniq6LongitudinalTuningState, GenesisG90LongitudinalTuningState, \
EV9LongitudinalTuningState, update_ev9_longitudinal_tuning, \
BlindspotWarningState, update_blindspot_warning, \
reset_egmp_longitudinal_tuning, \
update_ioniq_6_longitudinal_tuning, \
update_genesis_g90_longitudinal_tuning, egmp_dynamic_longitudinal_tuning, \
should_reset_ev6_gt_line_longitudinal_tuning, reset_ev6_gt_line_longitudinal_tuning, \
get_angle_smoothing_alpha, apply_ev9_high_angle_gain_cap
direct_angle_request_allowed, get_angle_smoothing_alpha, \
should_use_ev6_gt_line_stop_direct_tracking
from opendbc.car.hyundai.carstate import CarState, decode_canfd_camera_lead, decode_ioniq_6_blindspot_radar_state
from opendbc.car.hyundai.interface import CarInterface
from opendbc.car.hyundai import hyundaican, hyundaicanfd
@@ -89,6 +93,8 @@ CCNC_NON_HDA2_CARS = (
CAR.HYUNDAI_SANTA_CRUZ_2025,
CAR.KIA_K4_2025,
CAR.KIA_K5_2025,
CAR.KIA_CARNIVAL_2025,
CAR.KIA_CARNIVAL_HEV_4TH_GEN,
CAR.KIA_SPORTAGE_2026,
CAR.KIA_SORENTO_2024,
)
@@ -235,16 +241,21 @@ class TestHyundaiFingerprint:
fingerprint[ev9_radar_config.bus][ev9_radar_config.start_addr] = ev9_radar_config.expected_length
ev9_car_fw = [CarParams.CarFw(ecu=Ecu.adas, fwVersion=b"", address=0x730, brand="hyundai")]
CP = CarInterface.get_params(CAR.KIA_EV9, fingerprint, ev9_car_fw, True, False, False, None)
assert not CP.alphaLongitudinalAvailable
assert not CP.openpilotLongitudinalControl
assert CP.alphaLongitudinalAvailable
assert CP.openpilotLongitudinalControl
assert not CP.radarUnavailable
assert CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
assert not (CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.LONG)
assert CP.flags & HyundaiFlags.CCNC
assert egmp_dynamic_longitudinal_tuning(CP)
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.LONG
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CANFD_ANGLE_STEERING
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CCNC
CP = CarInterface.get_params(CAR.KIA_EV9, fingerprint, [], True, False, False, None)
assert not CP.openpilotLongitudinalControl
assert CP.openpilotLongitudinalControl
CP = CarInterface.get_params(CAR.KIA_EV9, fingerprint, ev9_car_fw, False, False, False, None)
assert not CP.openpilotLongitudinalControl
assert not (CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CCNC)
for candidate in HYUNDAI_NON_SCC_CARS:
CP = CarInterface.get_params(candidate, gen_empty_fingerprint(), [], True, False, False, None)
assert bool(CP.flags & HyundaiFlags.NON_SCC)
@@ -254,6 +265,7 @@ class TestHyundaiFingerprint:
CP = CarInterface.get_params(CAR.KIA_SPORTAGE_HEV_2026, gen_empty_fingerprint(), [], False, False, False, None)
assert CP.steerControlType == CarParams.SteerControlType.angle
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CANFD_ANGLE_STEERING
assert not (CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CCNC)
fingerprint = gen_empty_fingerprint()
cam_can = CanBus(None, fingerprint).CAM
@@ -288,28 +300,59 @@ class TestHyundaiFingerprint:
assert get_angle_smoothing_alpha(ev9_cp, 20.0) == pytest.approx(get_angle_smoothing_alpha(other_cp, 20.0))
assert get_angle_smoothing_alpha(other_cp, 20.0) == pytest.approx(0.0)
def test_ev9_high_angle_gain_cap_is_ev9_only_and_nonzero(self):
ev9_cp = SimpleNamespace(carFingerprint=CAR.KIA_EV9, flags=int(HyundaiFlags.CANFD_ANGLE_STEERING))
sportage_cp = SimpleNamespace(carFingerprint=CAR.KIA_SPORTAGE_HEV_2026, flags=int(HyundaiFlags.CANFD_ANGLE_STEERING))
def test_ev9_direct_angle_waits_for_safety_envelope(self):
CP = CarInterface.get_params(CAR.KIA_EV9, gen_empty_fingerprint(), [], True, False, False, None)
controller = CarController(DBC[CP.carFingerprint], CP)
assert apply_ev9_high_angle_gain_cap(ev9_cp, 0.70, 60.0, True) == pytest.approx(0.70)
assert apply_ev9_high_angle_gain_cap(ev9_cp, 0.70, 120.0, True) == pytest.approx(0.55)
assert apply_ev9_high_angle_gain_cap(ev9_cp, 0.70, 320.0, True) == pytest.approx(0.16)
assert apply_ev9_high_angle_gain_cap(ev9_cp, 0.0, 320.0, True) > 0.0
assert apply_ev9_high_angle_gain_cap(ev9_cp, 0.70, 320.0, False) == pytest.approx(0.70)
assert apply_ev9_high_angle_gain_cap(sportage_cp, 0.70, 320.0, True) == pytest.approx(0.70)
assert not direct_angle_request_allowed(8.47, 155.5, 155.6, True, controller.BASELINE_VM, controller.params)
assert direct_angle_request_allowed(8.47, 140.0, 140.0, True, controller.BASELINE_VM, controller.params)
assert not direct_angle_request_allowed(8.47, 140.0, 140.0, False, controller.BASELINE_VM, controller.params)
def test_ccnc_hda2_lka_layout_does_not_set_ccnc_safety_param(self):
def test_angle_platforms_disable_standstill_steering(self):
ev9_cp = CarInterface.get_params(CAR.KIA_EV9, gen_empty_fingerprint(), [], False, False, False, None)
ioniq_5_pe_cp = CarInterface.get_params(CAR.HYUNDAI_IONIQ_5_PE, gen_empty_fingerprint(), [], False, False, False, None)
sportage_cp = CarInterface.get_params(CAR.KIA_SPORTAGE_HEV_2026, gen_empty_fingerprint(), [], False, False, False, None)
assert not ev9_cp.steerAtStandstill
assert not ioniq_5_pe_cp.steerAtStandstill
assert not sportage_cp.steerAtStandstill
@pytest.mark.parametrize("candidate", (CAR.KIA_K4_2025, CAR.KIA_CARNIVAL_2025, CAR.KIA_CARNIVAL_HEV_4TH_GEN))
def test_ccnc_hda2_lka_layout_does_not_set_ccnc_safety_param(self, candidate):
fingerprint = gen_empty_fingerprint()
cam_can = CanBus(None, fingerprint).CAM
fingerprint[cam_can] = {0x50: 16}
CP = CarInterface.get_params(CAR.KIA_K4_2025, fingerprint, [], False, False, False, None)
CP = CarInterface.get_params(candidate, fingerprint, [], False, False, False, None)
assert CP.flags & HyundaiFlags.CCNC
assert CP.flags & HyundaiFlags.CANFD_LKA_STEERING
assert not (CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CCNC)
def test_carnival_hev_sets_hybrid_gas_safety(self):
CP = CarInterface.get_params(CAR.KIA_CARNIVAL_HEV_4TH_GEN, gen_empty_fingerprint(), [], False, False, False, None)
assert CP.flags & HyundaiFlags.HYBRID
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.HYBRID_GAS
def test_carnival_2025_hda2_detects_alternate_buttons(self):
fingerprint = gen_empty_fingerprint()
CAN = CanBus(None, fingerprint)
fingerprint[CAN.CAM] = {0x110: 32}
fingerprint[1] = {0x1aa: 16}
carnival_cp = CarInterface.get_params(CAR.KIA_CARNIVAL_2025, fingerprint, [], False, False, False, None)
assert carnival_cp.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
assert carnival_cp.flags & HyundaiFlags.CANFD_ALT_BUTTONS
assert carnival_cp.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CANFD_ALT_BUTTONS
carnival_state = CarState(carnival_cp, None)
pt_states = {state.name for state in carnival_state.get_can_parsers(carnival_cp)[Bus.pt].message_states.values()}
assert "CRUISE_BUTTONS" not in pt_states
k4_cp = CarInterface.get_params(CAR.KIA_K4_2025, fingerprint, [], False, False, False, None)
assert not (k4_cp.flags & HyundaiFlags.CANFD_ALT_BUTTONS)
def test_ioniq_6_hda1_layout_stays_non_lka(self):
fingerprint = gen_empty_fingerprint()
fingerprint[1] = {0x100: 8, 0x110: 8}
@@ -326,6 +369,13 @@ class TestHyundaiFingerprint:
assert palisade_2023.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CAN_CANFD_BLENDED
assert palisade_2023.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CANCEL_BTN_ENABLE
@pytest.mark.parametrize("candidate", (CAR.HYUNDAI_ELANTRA_2024, CAR.HYUNDAI_ELANTRA_HEV_2024))
def test_hyundai_can_refresh_platforms_use_refresh_dbc_and_safety_param(self, candidate):
CP = CarInterface.get_params(candidate, gen_empty_fingerprint(), [], False, False, False, None)
assert DBC[CP.carFingerprint][Bus.pt] == "hyundai_can_refresh_generated"
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CAN_REFRESH_MSGS
def test_hyundai_lkas_button_sets_starpilot_safety_flag(self):
fingerprint = gen_empty_fingerprint()
fingerprint[0][0x391] = 8
@@ -478,6 +528,20 @@ class TestHyundaiFingerprint:
events = car_state.create_cruise_button_events(Buttons.CANCEL, Buttons.NONE)
assert [(be.type, be.pressed) for be in events] == [(ButtonType.cancel, True)]
def test_ccnc_angle_long_main_cruise_toggle(self):
car_state = SimpleNamespace(main_cruise_on=False)
ret = SimpleNamespace(
cruiseState=SimpleNamespace(available=True),
buttonEvents=[structs.CarState.ButtonEvent(pressed=True, type=ButtonType.mainCruise)],
)
assert CarState.update_main_cruise(car_state, ret)
ret.buttonEvents = [structs.CarState.ButtonEvent(pressed=False, type=ButtonType.mainCruise)]
assert CarState.update_main_cruise(car_state, ret)
ret.buttonEvents = [structs.CarState.ButtonEvent(pressed=True, type=ButtonType.mainCruise)]
assert not CarState.update_main_cruise(car_state, ret)
def test_palisade_2023_cancel_release_enables_from_standby(self):
toggles = get_test_toggles()
CP = CarInterface.get_params(CAR.HYUNDAI_PALISADE_2023, gen_empty_fingerprint(), [], True, False, False, toggles)
@@ -625,6 +689,20 @@ class TestHyundaiFingerprint:
assert CP.vEgoStopping == pytest.approx(0.35)
assert CP.stoppingDecelRate == pytest.approx(0.35)
def test_elantra_2021_longitudinal_params_match_observed_response(self):
toggles = get_test_toggles()
CP = CarInterface.get_params(CAR.HYUNDAI_ELANTRA_2021, gen_empty_fingerprint(), [], True, False, False, toggles)
assert CP.longitudinalActuatorDelay == pytest.approx(0.22)
assert CP.stopAccel == pytest.approx(-1.5)
assert CP.stoppingDecelRate == pytest.approx(0.5)
def test_elantra_hev_2024_longitudinal_delay_matches_observed_response(self):
toggles = get_test_toggles()
CP = CarInterface.get_params(CAR.HYUNDAI_ELANTRA_HEV_2024, gen_empty_fingerprint(), [], True, False, False, toggles)
assert CP.longitudinalActuatorDelay == pytest.approx(0.22)
def test_kia_niro_phev_2022_longitudinal_params_soften_final_stop_hold(self):
toggles = get_test_toggles()
CP = CarInterface.get_params(CAR.KIA_NIRO_PHEV_2022, gen_empty_fingerprint(), [], True, False, False, toggles)
@@ -653,6 +731,35 @@ class TestHyundaiFingerprint:
def test_kona_ev_non_scc_has_no_dedicated_fw_coverage(self):
assert CAR.HYUNDAI_KONA_EV_NON_SCC not in FW_VERSIONS
def test_elantra_hev_2026_route_fw_exact_matches_2024_platform(self):
route_fw = {
(Ecu.fwdCamera, 0x7c4): b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.05 99210-AA510 240509',
(Ecu.fwdRadar, 0x7d0): b'\xf1\x00CN7_ RDR ----- 1.00 1.01 99110-AA500 ',
(Ecu.eps, 0x7d4): b'\xf1\x00CN7 MDPS C 1.00 1.03 56300BY670\x00 4CSHC103',
}
car_fw = [
CarParams.CarFw(ecu=ecu, fwVersion=version, address=address, subAddress=0, brand="hyundai")
for (ecu, address), version in route_fw.items()
]
exact, matches = match_fw_to_car(car_fw, "", allow_exact=True, allow_fuzzy=False, log=False)
assert exact
assert matches == {CAR.HYUNDAI_ELANTRA_HEV_2024}
def test_kia_carnival_2025_canadian_route_fw_exact_matches(self):
route_fw = {
(Ecu.fwdCamera, 0x7c4): b'\xf1\x00KA4 MFC AT CAN LHD 1.00 1.00 99210-R0700 250324',
(Ecu.fwdRadar, 0x7d0): b'\xf1\x00KA4_ RDR ----- 1.00 1.01 99110-R0510 ',
}
car_fw = [
CarParams.CarFw(ecu=ecu, fwVersion=version, address=address, subAddress=0, brand="hyundai")
for (ecu, address), version in route_fw.items()
]
exact, matches = match_fw_to_car(car_fw, "", allow_exact=True, allow_fuzzy=False, log=False)
assert exact
assert matches == {CAR.KIA_CARNIVAL_2025}
def test_kona_non_scc_fca_radar_fw_is_optional(self):
fw_versions = FW_VERSIONS[CAR.HYUNDAI_KONA_NON_SCC]
car_fw = [
@@ -1003,6 +1110,14 @@ class TestHyundaiFingerprint:
assert CP.longitudinalActuatorDelay == pytest.approx(0.6)
assert CP.startingState
def test_ev9_longitudinal_params_match_observed_response(self):
toggles = get_test_toggles()
CP = CarInterface.get_params(CAR.KIA_EV9, gen_empty_fingerprint(), [], True, False, False, toggles)
assert CP.startAccel == pytest.approx(0.2)
assert CP.vEgoStarting == pytest.approx(0.5)
assert CP.longitudinalActuatorDelay == pytest.approx(0.3)
def test_ioniq_6_longitudinal_tuning_helper_matches_dynamic_profile(self):
state = Ioniq6LongitudinalTuningState()
@@ -1094,6 +1209,70 @@ class TestHyundaiFingerprint:
assert state.desired_accel == pytest.approx(-2.82)
assert state.actual_accel < -1.8
def test_kia_ev6_gt_line_longitudinal_tuning_helper_delays_final_stop_cap(self):
state = Ioniq6LongitudinalTuningState(actual_accel=-2.82, accel_last=-2.82,
long_control_state_last=LongCtrlState.pid)
state = update_ioniq_6_longitudinal_tuning(state, accel_cmd=-2.82, v_ego=1.8, a_ego=-2.4,
long_control_state=LongCtrlState.stopping, long_active=True,
ev6_gt_line=True)
assert state.stopping
assert state.desired_accel == pytest.approx(-2.82)
assert state.actual_accel == pytest.approx(-2.82)
def test_kia_ev6_gt_line_prefers_direct_stop_tracking_above_final_band(self):
assert should_use_ev6_gt_line_stop_direct_tracking(True, True, 1.8, -2.05, -1.29)
assert not should_use_ev6_gt_line_stop_direct_tracking(True, True, 1.0, -2.05, -1.29)
assert not should_use_ev6_gt_line_stop_direct_tracking(True, False, 1.8, -2.05, -1.29)
assert not should_use_ev6_gt_line_stop_direct_tracking(False, True, 1.8, -2.05, -1.29)
assert not should_use_ev6_gt_line_stop_direct_tracking(True, True, 1.8, -1.0, -1.29)
def test_ev9_longitudinal_tuning_matches_stock_stop_hold_release_timing(self):
state = update_ev9_longitudinal_tuning(EV9LongitudinalTuningState(), True, True, 1.0)
assert not state.stop_request
state = update_ev9_longitudinal_tuning(state, True, True, 0.4)
assert state.stop_request
assert not state.cruise_standstill
for _ in range(178):
state = update_ev9_longitudinal_tuning(state, True, True, 0.0)
assert state.cruise_standstill
for _ in range(6):
state = update_ev9_longitudinal_tuning(state, True, False, 0.0)
assert state.stop_request
assert not state.cruise_standstill
assert update_ev9_longitudinal_tuning(state, True, False, 0.0) == EV9LongitudinalTuningState()
def test_ev9_blindspot_warning_matches_stock_envelope(self):
state = BlindspotWarningState()
outputs = [update_blindspot_warning(state, True, True) for _ in range(40)]
assert [output.sound_active for output in outputs[:36]] == [True] * 36
assert not any(output.sound_active for output in outputs[36:])
assert [output.mirror_lamp_active for output in outputs[:20]] == [True] * 16 + [False] * 4
assert [output.mirror_lamp_active for output in outputs[20:]] == [True] * 16 + [False] * 4
assert not update_blindspot_warning(state, False, True).sound_active
assert update_blindspot_warning(state, True, True).sound_active is False
update_blindspot_warning(state, False, False)
assert update_blindspot_warning(state, True, True).sound_active
def test_ev9_longitudinal_tuning_resets_accel_before_stop_release(self):
accel_state = Ioniq6LongitudinalTuningState()
for _ in range(4):
accel_state = update_ioniq_6_longitudinal_tuning(
accel_state, accel_cmd=0.2, v_ego=0.0, a_ego=0.0,
long_control_state=LongCtrlState.starting, long_active=True,
low_speed_stop_brake_cap=True,
)
assert accel_state.actual_accel == pytest.approx(0.75)
accel_state = reset_egmp_longitudinal_tuning(accel_state)
assert accel_state.actual_accel == 0.0
assert not accel_state.launch_active
def test_genesis_g90_longitudinal_tuning_softens_final_stop_hold(self):
state = GenesisG90LongitudinalTuningState()
@@ -1395,7 +1574,7 @@ class TestHyundaiFingerprint:
cc = SimpleNamespace(enabled=True, latActive=True, actuators=SimpleNamespace(longControlState=LongCtrlState.off),
leftBlinker=False, rightBlinker=False, hudControl=SimpleNamespace())
cs = SimpleNamespace(stock_lfa_msg=None, stock_lkas_msg=stock_lkas,
out=SimpleNamespace(steeringAngleDeg=0.0))
out=SimpleNamespace(steeringAngleDeg=0.0, gearShifter=structs.CarState.GearShifter.drive))
msgs = controller.create_canfd_msgs(0, False, 0.0, 8.5, 0.0, 0.0, False, cc.hudControl, cs, cc,
get_test_toggles(), lka_icon=2, lfa_icon=2)
@@ -1409,7 +1588,7 @@ class TestHyundaiFingerprint:
assert parser.vl["LKAS_ALT"]["ADAS_ACIAnglTqRedcGainVal"] == pytest.approx(0.0)
assert parser.vl["LKAS_ALT"]["ADAS_StrAnglReqVal"] == pytest.approx(8.5)
def test_ev9_inactive_angle_status_keeps_standby_damping_without_stock_lkas(self):
def test_ev9_inactive_angle_steering_lets_safety_forward_stock_lkas(self):
CP = CarParams.new_message()
CP.carFingerprint = CAR.KIA_EV9
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING |
@@ -1417,31 +1596,98 @@ class TestHyundaiFingerprint:
CP.openpilotLongitudinalControl = False
controller = CarController(DBC[CP.carFingerprint], CP)
can_bus = CanBus(CP)
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("LKAS_ALT", 0)], can_bus.ACAN)
cc = SimpleNamespace(enabled=False, latActive=False, actuators=SimpleNamespace(longControlState=LongCtrlState.off),
leftBlinker=False, rightBlinker=False, hudControl=SimpleNamespace())
stock_lkas = {
"CHECKSUM": 1234,
"COUNTER": 42,
"LKA_OptUsmSta": 2,
"LKA_MODE": 2,
"LKA_RcgSta": 3,
"LKA_AVAILABLE": 3,
"LKA_LHLnWrnSta": 3,
"LKA_RHLnWrnSta": 3,
"LKA_WARNING": 1,
"LKA_HndsoffSnd": 1,
"LKA_StrSnd": 1,
"LKA_SysIndReq": 4,
"LKA_ICON": 0,
"FCA_SYSWARN": 1,
"StrTqReqVal": 17,
"TORQUE_REQUEST": 17,
"ActToiSta": 3,
"STEER_REQ": 1,
"ToiFltSta": 3,
"LFA_BUTTON": 1,
"LKA_SysWrn": 15,
"LKA_ASSIST": 1,
"Damping_Gain": 0,
"STEER_MODE": 5,
"NEW_SIGNAL_2": 0,
"LKAS_ANGLE_ACTIVE": 2,
"LKA_UsmMod": 3,
"HAS_LANE_SAFETY": 1,
"ADAS_StrAnglReqVal": 12.3,
"ADAS_ACIAnglTqRedcGainVal": 0.42,
"DAMP_FACTOR": 0,
}
lfa_block_msg = {f"BYTE{i}": 0 for i in range(3, 32) if i != 7}
lfa_block_msg["COUNTER"] = 0
cs = SimpleNamespace(stock_lfa_msg=None, stock_lkas_msg={}, lfa_block_msg=lfa_block_msg,
cs = SimpleNamespace(stock_lfa_msg=None, stock_lkas_msg=stock_lkas, lfa_block_msg=lfa_block_msg,
out=SimpleNamespace(steeringAngleDeg=-201.0))
msgs = controller.create_canfd_msgs(0, False, 0.0, -201.0, 0.0, 0.0, False, cc.hudControl, cs, cc,
get_test_toggles(), lka_icon=1, lfa_icon=1)
lkas_msgs = [msg for msg in msgs if msg[0] == 0x110]
assert len(lkas_msgs) == 1
assert len(lkas_msgs) == 0
parser.update([(1, lkas_msgs)])
@pytest.mark.parametrize(("standstill", "expected_lkas_msgs"), [(False, 1), (True, 0)])
def test_ioniq_5_pe_standstill_lets_safety_forward_stock_lkas(self, standstill, expected_lkas_msgs):
CP = CarParams.new_message()
CP.carFingerprint = CAR.HYUNDAI_IONIQ_5_PE
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING |
HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT)
CP.openpilotLongitudinalControl = False
assert parser.can_valid
assert parser.vl["LKAS_ALT"]["LKAS_ANGLE_ACTIVE"] == 1
assert parser.vl["LKAS_ALT"]["ADAS_ACIAnglTqRedcGainVal"] == pytest.approx(0.0)
assert parser.vl["LKAS_ALT"]["ADAS_StrAnglReqVal"] == pytest.approx(-201.0)
assert parser.vl["LKAS_ALT"]["DAMP_FACTOR"] == pytest.approx(100.0)
assert parser.vl["LKAS_ALT"]["STEER_MODE"] == 2
assert parser.vl["LKAS_ALT"]["NEW_SIGNAL_2"] == 3
controller = CarController(DBC[CP.carFingerprint], CP)
cc = SimpleNamespace(enabled=True, latActive=False, actuators=SimpleNamespace(longControlState=LongCtrlState.off),
leftBlinker=False, rightBlinker=False, hudControl=SimpleNamespace())
cs = SimpleNamespace(
stock_lfa_msg=None,
stock_lkas_msg={},
out=SimpleNamespace(
standstill=standstill,
steeringAngleDeg=0.0,
gearShifter=structs.CarState.GearShifter.drive,
),
)
def test_ev9_inactive_angle_steering_still_suppresses_stock_lfa(self):
msgs = controller.create_canfd_msgs(0, False, 0.0, 0.0, 0.0, 0.0, False, cc.hudControl, cs, cc,
get_test_toggles(), lka_icon=1, lfa_icon=1)
assert len([msg for msg in msgs if msg[0] == 0x110]) == expected_lkas_msgs
def test_ev9_inactive_angle_steering_does_not_suppress_stock_lfa(self):
CP = CarParams.new_message()
CP.carFingerprint = CAR.KIA_EV9
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING |
HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT)
CP.openpilotLongitudinalControl = False
controller = CarController(DBC[CP.carFingerprint], CP)
controller.frame = 5
cc = SimpleNamespace(enabled=False, latActive=False, actuators=SimpleNamespace(longControlState=LongCtrlState.off),
leftBlinker=False, rightBlinker=False, hudControl=SimpleNamespace())
lfa_block_msg = {f"BYTE{i}": 0 for i in range(3, 32) if i != 7}
lfa_block_msg["COUNTER"] = 0
cs = SimpleNamespace(stock_lfa_msg=None, stock_lkas_msg={}, lfa_block_msg=lfa_block_msg,
out=SimpleNamespace(steeringAngleDeg=0.0, gearShifter=structs.CarState.GearShifter.drive))
msgs = controller.create_canfd_msgs(0, False, 0.0, 0.0, 0.0, 0.0, False, cc.hudControl, cs, cc,
get_test_toggles(), lka_icon=1, lfa_icon=1)
suppress_msgs = [msg for msg in msgs if msg[0] == 0x362]
assert not suppress_msgs
def test_ev9_active_angle_steering_still_suppresses_stock_lfa(self):
CP = CarParams.new_message()
CP.carFingerprint = CAR.KIA_EV9
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING |
@@ -1452,15 +1698,15 @@ class TestHyundaiFingerprint:
controller.frame = 5
can_bus = CanBus(CP)
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("CAM_0x362", 0)], can_bus.ECAN)
cc = SimpleNamespace(enabled=False, latActive=False, actuators=SimpleNamespace(longControlState=LongCtrlState.off),
cc = SimpleNamespace(enabled=False, latActive=True, actuators=SimpleNamespace(longControlState=LongCtrlState.off),
leftBlinker=False, rightBlinker=False, hudControl=SimpleNamespace())
lfa_block_msg = {f"BYTE{i}": 0 for i in range(3, 32) if i != 7}
lfa_block_msg["COUNTER"] = 0
cs = SimpleNamespace(stock_lfa_msg=None, stock_lkas_msg={}, lfa_block_msg=lfa_block_msg,
out=SimpleNamespace(steeringAngleDeg=0.0))
out=SimpleNamespace(steeringAngleDeg=0.0, gearShifter=structs.CarState.GearShifter.drive))
msgs = controller.create_canfd_msgs(0, False, 0.0, 0.0, 0.0, 0.0, False, cc.hudControl, cs, cc,
get_test_toggles(), lka_icon=1, lfa_icon=1)
get_test_toggles(), lka_icon=2, lfa_icon=2)
suppress_msgs = [msg for msg in msgs if msg[0] == 0x362]
assert len(suppress_msgs) == 1
@@ -1506,7 +1752,7 @@ class TestHyundaiFingerprint:
lfa_block_msg = {f"BYTE{i}": 0 for i in range(3, 32) if i != 7}
lfa_block_msg["COUNTER"] = 0
cs = SimpleNamespace(stock_lfa_msg=None, stock_lkas_msg=stock_lkas, lfa_block_msg=lfa_block_msg,
out=SimpleNamespace(steeringAngleDeg=120.0))
out=SimpleNamespace(steeringAngleDeg=120.0, gearShifter=structs.CarState.GearShifter.drive))
msgs = controller.create_canfd_msgs(0, True, 0.44, 120.0, 0.0, 0.0, False, cc.hudControl, cs, cc,
get_test_toggles(), lka_icon=2, lfa_icon=2)
@@ -1848,7 +2094,7 @@ class TestHyundaiFingerprint:
"DAMP_FACTOR": 0,
}
msgs = hyundaicanfd.create_steering_messages(packer, CP, can_bus, True, True, 0.44, -31.5,
msgs = hyundaicanfd.create_steering_messages(packer, CP, can_bus, False, True, 0.44, -31.5,
lkas_base_values=stock_lkas, lka_icon=2)
lkas_msgs = [msg for msg in msgs if msg[0] == 0x110]
assert len(lkas_msgs) == 1
@@ -1856,6 +2102,19 @@ class TestHyundaiFingerprint:
parser.update([(1, lkas_msgs)])
assert parser.can_valid
assert parser.vl["LKAS_ALT"]["LKA_OptUsmSta"] == 0
assert parser.vl["LKAS_ALT"]["LKA_RcgSta"] == 3
assert parser.vl["LKAS_ALT"]["LKA_LHLnWrnSta"] == 0
assert parser.vl["LKAS_ALT"]["LKA_RHLnWrnSta"] == 0
assert parser.vl["LKAS_ALT"]["LKA_HndsoffSnd"] == 0
assert parser.vl["LKAS_ALT"]["LKA_StrSnd"] == 0
assert parser.vl["LKAS_ALT"]["LKA_SysIndReq"] == 2
assert parser.vl["LKAS_ALT"]["StrTqReqVal"] == 0
assert parser.vl["LKAS_ALT"]["ActToiSta"] == 0
assert parser.vl["LKAS_ALT"]["ToiFltSta"] == 0
assert parser.vl["LKAS_ALT"]["LKA_SysWrn"] == 0
assert parser.vl["LKAS_ALT"]["Damping_Gain"] == 100
assert parser.vl["LKAS_ALT"]["LKA_UsmMod"] == 0
assert parser.vl["LKAS_ALT"]["LKA_MODE"] == 0
assert parser.vl["LKAS_ALT"]["LKA_AVAILABLE"] == 3
assert parser.vl["LKAS_ALT"]["LKA_WARNING"] == 0
@@ -1871,7 +2130,7 @@ class TestHyundaiFingerprint:
assert parser.vl["LKAS_ALT"]["ADAS_StrAnglReqVal"] == pytest.approx(-31.5)
assert parser.vl["LKAS_ALT"]["ADAS_ACIAnglTqRedcGainVal"] == pytest.approx(0.44)
def test_ev9_angle_lkas_alt_preserves_stock_status_when_inactive(self):
def test_ev9_angle_lkas_alt_clears_stock_status_when_inactive(self):
CP = CarParams.new_message()
CP.carFingerprint = CAR.KIA_EV9
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING |
@@ -1884,18 +2143,31 @@ class TestHyundaiFingerprint:
stock_lkas = {
"CHECKSUM": 1234,
"COUNTER": 42,
"LKA_OptUsmSta": 6,
"LKA_MODE": 2,
"LKA_RcgSta": 7,
"LKA_AVAILABLE": 3,
"LKA_LHLnWrnSta": 3,
"LKA_RHLnWrnSta": 3,
"LKA_WARNING": 1,
"LKA_HndsoffSnd": 1,
"LKA_StrSnd": 1,
"LKA_SysIndReq": 4,
"LKA_ICON": 1,
"FCA_SYSWARN": 1,
"StrTqReqVal": 17,
"TORQUE_REQUEST": 17,
"ActToiSta": 3,
"STEER_REQ": 1,
"ToiFltSta": 3,
"LFA_BUTTON": 1,
"LKA_SysWrn": 15,
"LKA_ASSIST": 1,
"Damping_Gain": 0,
"STEER_MODE": 5,
"NEW_SIGNAL_2": 0,
"LKAS_ANGLE_ACTIVE": 2,
"LKA_UsmMod": 3,
"HAS_LANE_SAFETY": 1,
"ADAS_StrAnglReqVal": 12.3,
"ADAS_ACIAnglTqRedcGainVal": 0.42,
@@ -1910,19 +2182,32 @@ class TestHyundaiFingerprint:
parser.update([(1, lkas_msgs)])
assert parser.can_valid
assert parser.vl["LKAS_ALT"]["LKA_MODE"] == 2
assert parser.vl["LKAS_ALT"]["LKA_AVAILABLE"] == 3
assert parser.vl["LKAS_ALT"]["LKA_WARNING"] == 1
assert parser.vl["LKAS_ALT"]["LKA_OptUsmSta"] == 0
assert parser.vl["LKAS_ALT"]["LKA_RcgSta"] == 0
assert parser.vl["LKAS_ALT"]["LKA_LHLnWrnSta"] == 0
assert parser.vl["LKAS_ALT"]["LKA_RHLnWrnSta"] == 0
assert parser.vl["LKAS_ALT"]["LKA_HndsoffSnd"] == 0
assert parser.vl["LKAS_ALT"]["LKA_StrSnd"] == 2
assert parser.vl["LKAS_ALT"]["LKA_SysIndReq"] == 1
assert parser.vl["LKAS_ALT"]["StrTqReqVal"] == 0
assert parser.vl["LKAS_ALT"]["ActToiSta"] == 0
assert parser.vl["LKAS_ALT"]["ToiFltSta"] == 0
assert parser.vl["LKAS_ALT"]["LKA_SysWrn"] == 0
assert parser.vl["LKAS_ALT"]["Damping_Gain"] == 0
assert parser.vl["LKAS_ALT"]["LKA_UsmMod"] == 0
assert parser.vl["LKAS_ALT"]["LKA_MODE"] == 0
assert parser.vl["LKAS_ALT"]["LKA_AVAILABLE"] == 0
assert parser.vl["LKAS_ALT"]["LKA_WARNING"] == 0
assert parser.vl["LKAS_ALT"]["LKA_ICON"] == 1
assert parser.vl["LKAS_ALT"]["FCA_SYSWARN"] == 1
assert parser.vl["LKAS_ALT"]["FCA_SYSWARN"] == 0
assert parser.vl["LKAS_ALT"]["TORQUE_REQUEST"] == 0
assert parser.vl["LKAS_ALT"]["STEER_REQ"] == 0
assert parser.vl["LKAS_ALT"]["LFA_BUTTON"] == 1
assert parser.vl["LKAS_ALT"]["LKA_ASSIST"] == 1
assert parser.vl["LKAS_ALT"]["LFA_BUTTON"] == 0
assert parser.vl["LKAS_ALT"]["LKA_ASSIST"] == 0
assert parser.vl["LKAS_ALT"]["DAMP_FACTOR"] == 0
assert parser.vl["LKAS_ALT"]["LKAS_ANGLE_ACTIVE"] == 1
assert parser.vl["LKAS_ALT"]["HAS_LANE_SAFETY"] == 1
assert parser.vl["LKAS_ALT"]["ADAS_StrAnglReqVal"] == pytest.approx(-31.5)
assert parser.vl["LKAS_ALT"]["HAS_LANE_SAFETY"] == 0
assert parser.vl["LKAS_ALT"]["ADAS_StrAnglReqVal"] == pytest.approx(12.3)
assert parser.vl["LKAS_ALT"]["ADAS_ACIAnglTqRedcGainVal"] == pytest.approx(0.0)
def test_ev9_accelerator_brake_alt_spoof_matches_route_template(self):
@@ -2036,6 +2321,151 @@ class TestHyundaiFingerprint:
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_LtIndSta"] == 2
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["OSMrrLamp_LtIndSta"] == 2
def test_ev9_blindspot_status_uses_stock_warning_fields(self):
CP = CarParams.new_message()
CP.carFingerprint = CAR.KIA_EV9
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT)
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
can_bus = CanBus(CP)
parser = CANParser(DBC[CP.carFingerprint][Bus.pt],
[("BLINDSPOTS_REAR_CORNERS", 0), ("BLINDSPOTS_FRONT_CORNER_1", 0)], can_bus.ECAN)
msgs = hyundaicanfd.create_ccnc_blindspot_status_messages(
packer, CP, can_bus, 7, left_blindspot=True, left_escalated=True,
drive_gear=True, left_warning_lamp=True, left_sound_active=True,
)
parser.update([(1, msgs)])
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_LtIndSta"] == 2
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["OSMrrLamp_LtIndSta"] == 2
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_LtSndWrngSta"] == 1
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_Sta"] == 0
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["FL_INDICATOR"] == 0
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_IndSta"] == 1
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["BCA_OnOffEquip2Sta"] == 2
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["BCA_Sta"] == 1
assert parser.vl["BLINDSPOTS_FRONT_CORNER_1"]["NEW_SIGNAL_7"] == 0
def test_ev9_ccnc_status_clears_faults_and_tracks_control_state(self):
CP = CarParams.new_message()
CP.carFingerprint = CAR.KIA_EV9
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT)
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
can_bus = CanBus(CP)
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("CCNC_0x161", 0), ("CCNC_0x162", 0)], can_bus.ECAN)
parser.update([(1, hyundaicanfd.create_ccnc_angle_long_status_messages(packer, CP, can_bus, 12, hba_icon=2))])
assert parser.vl["CCNC_0x161"]["FCA_ICON"] == 1
assert parser.vl["CCNC_0x161"]["FCA_ALT_ICON"] == 0
assert parser.vl["CCNC_0x161"]["FCA_IMAGE"] == 0
assert parser.vl["CCNC_0x161"]["HBA_ICON"] == 2
assert all(parser.vl["CCNC_0x161"][sound] == 0 for sound in ("SOUNDS_1", "SOUNDS_2", "SOUNDS_3", "SOUNDS_4"))
assert parser.vl["CCNC_0x162"]["VIBRATE"] == 0
assert all(parser.vl["CCNC_0x162"][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",
))
parser.update([(1, hyundaicanfd.create_ccnc_angle_long_status_messages(
packer, CP, can_bus, 13, main_cruise_enabled=True, steering_available=True, steering_active=False,
))])
assert parser.vl["CCNC_0x161"]["HDA_ICON"] == 1
assert parser.vl["CCNC_0x161"]["LFA_ICON"] == 1
parser.update([(1, hyundaicanfd.create_ccnc_angle_long_status_messages(
packer, CP, can_bus, 14, enabled=True, main_cruise_enabled=True,
steering_available=True, steering_active=True,
))])
assert parser.vl["CCNC_0x161"]["HDA_ICON"] == 2
assert parser.vl["CCNC_0x161"]["LFA_ICON"] == 2
parser.update([(1, hyundaicanfd.create_ccnc_angle_long_status_messages(
packer, CP, can_bus, 15, enabled=True, main_cruise_enabled=True,
steering_available=True, steering_active=False,
))])
assert parser.vl["CCNC_0x161"]["HDA_ICON"] == 2
assert parser.vl["CCNC_0x161"]["LFA_ICON"] == 1
def test_ev9_ccnc_acc_control_uses_packer_counter(self):
CP = CarParams.new_message()
CP.carFingerprint = CAR.KIA_EV9
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.CCNC | HyundaiFlags.CANFD_ANGLE_STEERING |
HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT)
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
can_bus = CanBus(CP)
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("SCC_CONTROL", 0)], can_bus.ECAN)
messages = [
hyundaicanfd.create_ccnc_acc_control(
packer, can_bus, True, 0.2, False, False, False, 50.0,
1, 27.5, -1.2, True, 10.0,
)
for _ in range(2)
]
parser.update([(1, [messages[0]])])
parser.update([(2, [messages[1]])])
assert parser.can_valid
assert messages[0][1][2] == 0
assert messages[1][1][2] == 1
assert parser.vl["SCC_CONTROL"]["ACC_ObjDist"] == pytest.approx(27.5)
assert parser.vl["SCC_CONTROL"]["ACC_ObjRelSpd"] == pytest.approx(-1.2)
@pytest.mark.parametrize(("steering_pressed", "steering_active"), ((False, True), (True, False)))
def test_ev9_ccnc_steering_icon_tracks_controller_authority(self, monkeypatch, steering_pressed, steering_active):
CP = CarParams.new_message()
CP.carFingerprint = CAR.KIA_EV9
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.EV | HyundaiFlags.CCNC |
HyundaiFlags.CANFD_ANGLE_STEERING | HyundaiFlags.CANFD_LKA_STEERING |
HyundaiFlags.CANFD_LKA_STEERING_ALT)
CP.openpilotLongitudinalControl = True
controller = CarController(DBC[CP.carFingerprint], CP)
controller.frame = 5
captured = {}
def capture_ccnc_adrv_messages(*args, **kwargs):
captured["steering_available"] = args[9]
captured["steering_active"] = args[10]
return []
monkeypatch.setattr(hyundaicanfd, "create_ccnc_adrv_messages", capture_ccnc_adrv_messages)
lfa_block_msg = {f"BYTE{i}": 0 for i in range(3, 32) if i != 7}
lfa_block_msg["COUNTER"] = 0
cc = SimpleNamespace(
enabled=True,
latActive=True,
actuators=SimpleNamespace(longControlState=LongCtrlState.pid, accel=0.0),
leftBlinker=False,
rightBlinker=False,
hudControl=SimpleNamespace(),
)
cs = SimpleNamespace(
angle_steering_fault=False,
angle_steering_angle=0.0,
hba_icon=0,
is_metric=True,
left_blindspot_from_radar=False,
right_blindspot_from_radar=False,
lfa_block_msg=lfa_block_msg,
out=SimpleNamespace(
brakePressed=False,
cruiseState=SimpleNamespace(available=True),
gasPressed=False,
gearShifter=structs.CarState.GearShifter.drive,
steeringPressed=steering_pressed,
),
)
controller.create_canfd_msgs(0, True, 0.44, 0.0, 0.0, 0.0, False, cc.hudControl, cs, cc,
get_test_toggles(), lka_icon=2, lfa_icon=2)
assert captured == {"steering_available": True, "steering_active": steering_active}
def test_ioniq_6_blindspot_radar_state_decode(self):
assert decode_ioniq_6_blindspot_radar_state(0x02) == (False, False)
assert decode_ioniq_6_blindspot_radar_state(0x0A) == (False, True)
+48 -6
View File
@@ -103,6 +103,7 @@ class HyundaiSafetyFlags(IntFlag):
NON_SCC = 4096
CAN_CANFD_BLENDED = 8192
CANCEL_BTN_ENABLE = 16384
CAN_REFRESH_MSGS = 32768
CCNC = 32768
@@ -218,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"})
@@ -289,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
@@ -764,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(
@@ -775,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(
@@ -1038,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
@@ -1135,10 +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_ANGLE_LONGITUDINAL_CAR = set()
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,
@@ -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
@@ -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"
@@ -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
+6
View File
@@ -36,6 +36,7 @@ non_tested_cars = [
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,
@@ -164,6 +165,7 @@ 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),
@@ -232,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),
@@ -14,7 +14,7 @@ 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, ChryslerStarPilotFlags
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
@@ -79,6 +79,7 @@ def get_test_starpilot_toggles() -> SimpleNamespace:
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,
@@ -212,6 +213,33 @@ class TestCarInterfaces:
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
@@ -18,6 +18,7 @@ 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]
@@ -36,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
@@ -89,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]
@@ -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,6 +67,7 @@ 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"
@@ -118,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,7 +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 = 0.85
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
@@ -34,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
@@ -51,13 +54,21 @@ 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]
@@ -72,6 +83,16 @@ def get_prius_positive_feedforward_scale(v_ego: float) -> float:
[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:
@@ -168,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)
@@ -426,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))
@@ -441,8 +475,11 @@ class CarController(CarControllerBase):
feedforward = pcm_accel_cmd
if self.CP.carFingerprint == CAR.TOYOTA_PRIUS:
if feedforward > 0.0:
feedforward *= get_prius_positive_feedforward_scale(CS.out.vEgo)
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,
@@ -464,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,
@@ -490,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:
+14 -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)
@@ -88,7 +96,8 @@ class CarState(CarStateBase):
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"]
@@ -180,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:
@@ -220,16 +229,12 @@ class CarState(CarStateBase):
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
+27 -1
View File
@@ -28,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
@@ -60,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]
@@ -113,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.
@@ -135,6 +151,7 @@ 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))
@@ -156,6 +173,8 @@ class CarInterface(CarInterfaceBase):
ret.minEnableSpeed = -1. if (stop_and_go or ret.enableGasInterceptorDEPRECATED) else MIN_ACC_SPEED
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
@@ -168,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,5 +1,6 @@
from types import SimpleNamespace
import pytest
from hypothesis import given, settings, strategies as st
from opendbc.car import Bus, structs
@@ -7,11 +8,15 @@ 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, get_prius_positive_feedforward_scale, limit_interceptor_pcm_accel, \
limit_interceptor_stopping_accel, limit_prius_stopping_accel, update_permit_braking
from opendbc.car.toyota.carstate import calculate_interceptor_gas_pressed
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 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, \
ToyotaFlags, ToyotaSafetyFlags, get_platform_codes
@@ -35,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
@@ -79,6 +101,189 @@ class TestToyotaInterfaces:
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}
@@ -292,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
@@ -306,7 +523,15 @@ class TestToyotaCarController:
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) - 0.85) < 1e-6
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)
@@ -344,6 +569,22 @@ 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])
@@ -495,6 +736,29 @@ class TestToyotaCarController:
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": {
+6 -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)
+4 -1
View File
@@ -58,12 +58,15 @@ class ToyotaSafetyFlags(IntFlag):
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
@@ -171,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
@@ -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")
@@ -0,0 +1,22 @@
#!/usr/bin/env python3
from pathlib import Path
GENERIC_LFAHDA = "BO_ 1157 LFAHDA_MFC: 4 XXX"
REFRESH_LFAHDA = "BO_ 1157 LFAHDA_MFC: 8 XXX"
def main() -> None:
dbc_dir = Path(__file__).resolve().parents[2]
source = dbc_dir / "hyundai_kia_generic.dbc"
target = dbc_dir / "hyundai_can_refresh_generated.dbc"
dbc = source.read_text(encoding="utf-8")
if dbc.count(GENERIC_LFAHDA) != 1:
raise RuntimeError(f"expected exactly one {GENERIC_LFAHDA!r} definition")
target.write_text(dbc.replace(GENERIC_LFAHDA, REFRESH_LFAHDA, 1), encoding="utf-8")
if __name__ == "__main__":
main()
@@ -82,18 +82,31 @@ BO_ 261 ACCELERATOR_ALT: 32 XXX
BO_ 272 LKAS_ALT: 32 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ LKA_OptUsmSta : 24|3@1+ (1,0) [0|7] "" ADAS_DRV
SG_ LKA_MODE : 24|3@1+ (1,0) [0|7] "" XXX
SG_ LKA_RcgSta : 27|3@1+ (1,0) [0|7] "" ADAS_DRV
SG_ LKA_AVAILABLE : 27|2@1+ (1,0) [0|255] "" XXX
SG_ LKA_LHLnWrnSta : 30|2@1+ (1,0) [0|3] "" ADAS_DRV
SG_ LKA_RHLnWrnSta : 32|2@1+ (1,0) [0|3] "" ADAS_DRV
SG_ LKA_WARNING : 32|1@1+ (1,0) [0|1] "" XXX
SG_ LKA_HndsoffSnd : 34|2@1+ (1,0) [0|3] "" ADAS_DRV
SG_ LKA_StrSnd : 36|2@1+ (1,0) [0|3] "" ADAS_DRV
SG_ LKA_SysIndReq : 38|3@1+ (1,0) [0|7] "" ADAS_DRV
SG_ LKA_ICON : 38|2@1+ (1,0) [0|255] "" XXX
SG_ FCA_SYSWARN : 40|1@0+ (1,0) [0|1] "" XXX
SG_ StrTqReqVal : 41|11@1+ (1,-1024) [0|4095] "" XXX
SG_ TORQUE_REQUEST : 41|11@1+ (1,-1024) [0|4095] "" XXX
SG_ ActToiSta : 52|2@1+ (1,0) [0|3] "" ADAS_DRV
SG_ STEER_REQ : 52|1@1+ (1,0) [0|1] "" XXX
SG_ ToiFltSta : 54|2@1+ (1,0) [0|3] "" ADAS_DRV
SG_ LFA_BUTTON : 56|1@1+ (1,0) [0|255] "" XXX
SG_ LKA_SysWrn : 60|4@1+ (1,0) [0|15] "" ADAS_DRV
SG_ LKA_ASSIST : 62|1@1+ (1,0) [0|1] "" XXX
SG_ Damping_Gain : 64|8@1+ (1,0) [0|255] "" CGW
SG_ STEER_MODE : 65|3@1+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_2 : 70|2@0+ (1,0) [0|3] "" XXX
SG_ LKAS_ANGLE_ACTIVE : 77|2@0+ (1,0) [0|3] "" XXX
SG_ LKA_UsmMod : 80|2@1+ (1,0) [0|3] "" ADAS_DRV
SG_ HAS_LANE_SAFETY : 80|1@0+ (1,0) [0|1] "" XXX
SG_ ADAS_StrAnglReqVal : 82|14@1- (0.1,0) [0|176.7] "Deg" GW_RGW,MDPS,SFA
SG_ ADAS_ACIAnglTqRedcGainVal : 96|8@1+ (0.004,0) [0|1] "" GW_RGW,MDPS,SFA
File diff suppressed because it is too large Load Diff
@@ -319,18 +319,31 @@ BO_ 261 ACCELERATOR_ALT: 32 XXX
BO_ 272 LKAS_ALT: 32 XXX
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
SG_ LKA_OptUsmSta : 24|3@1+ (1,0) [0|7] "" ADAS_DRV
SG_ LKA_MODE : 24|3@1+ (1,0) [0|7] "" XXX
SG_ LKA_RcgSta : 27|3@1+ (1,0) [0|7] "" ADAS_DRV
SG_ LKA_AVAILABLE : 27|2@1+ (1,0) [0|255] "" XXX
SG_ LKA_LHLnWrnSta : 30|2@1+ (1,0) [0|3] "" ADAS_DRV
SG_ LKA_RHLnWrnSta : 32|2@1+ (1,0) [0|3] "" ADAS_DRV
SG_ LKA_WARNING : 32|1@1+ (1,0) [0|1] "" XXX
SG_ LKA_HndsoffSnd : 34|2@1+ (1,0) [0|3] "" ADAS_DRV
SG_ LKA_StrSnd : 36|2@1+ (1,0) [0|3] "" ADAS_DRV
SG_ LKA_SysIndReq : 38|3@1+ (1,0) [0|7] "" ADAS_DRV
SG_ LKA_ICON : 38|2@1+ (1,0) [0|255] "" XXX
SG_ FCA_SYSWARN : 40|1@0+ (1,0) [0|1] "" XXX
SG_ StrTqReqVal : 41|11@1+ (1,-1024) [0|4095] "" XXX
SG_ TORQUE_REQUEST : 41|11@1+ (1,-1024) [0|4095] "" XXX
SG_ ActToiSta : 52|2@1+ (1,0) [0|3] "" ADAS_DRV
SG_ STEER_REQ : 52|1@1+ (1,0) [0|1] "" XXX
SG_ ToiFltSta : 54|2@1+ (1,0) [0|3] "" ADAS_DRV
SG_ LFA_BUTTON : 56|1@1+ (1,0) [0|255] "" XXX
SG_ LKA_SysWrn : 60|4@1+ (1,0) [0|15] "" ADAS_DRV
SG_ LKA_ASSIST : 62|1@1+ (1,0) [0|1] "" XXX
SG_ Damping_Gain : 64|8@1+ (1,0) [0|255] "" CGW
SG_ STEER_MODE : 65|3@1+ (1,0) [0|1] "" XXX
SG_ NEW_SIGNAL_2 : 70|2@0+ (1,0) [0|3] "" XXX
SG_ LKAS_ANGLE_ACTIVE : 77|2@0+ (1,0) [0|3] "" XXX
SG_ LKA_UsmMod : 80|2@1+ (1,0) [0|3] "" ADAS_DRV
SG_ HAS_LANE_SAFETY : 80|1@0+ (1,0) [0|1] "" XXX
SG_ ADAS_StrAnglReqVal : 82|14@1- (0.1,0) [0|176.7] "Deg" GW_RGW,MDPS,SFA
SG_ ADAS_ACIAnglTqRedcGainVal : 96|8@1+ (0.004,0) [0|1] "" GW_RGW,MDPS,SFA
+13 -1
View File
@@ -23,6 +23,7 @@ typedef enum {
} ChryslerPlatform;
static ChryslerPlatform chrysler_platform;
static const ChryslerAddrs *chrysler_addrs;
static bool chrysler_jeep_brake_hold;
static uint32_t chrysler_get_checksum(const CANPacket_t *msg) {
int checksum_byte = GET_LEN(msg) - 1U;
@@ -153,18 +154,27 @@ static bool chrysler_tx_hook(const CANPacket_t *msg) {
if (msg->addr == chrysler_addrs->CRUISE_BUTTONS || msg->addr == chrysler_addrs->CRUISE_BUTTONS_ALT) {
const bool is_cancel = msg->data[0] == 1U;
const bool is_resume = msg->data[0] == 0x10U;
const bool allowed = is_cancel || (is_resume && controls_allowed);
const bool allow_resume_standstill = chrysler_jeep_brake_hold && is_resume && acc_main_on &&
!vehicle_moving && !gas_pressed && !brake_pressed;
const bool allowed = is_cancel || (is_resume && (controls_allowed || allow_resume_standstill));
if (!allowed) {
tx = false;
}
}
if (msg->addr == chrysler_addrs->DAS_3) {
if (!chrysler_jeep_brake_hold || !acc_main_on || vehicle_moving || gas_pressed || brake_pressed) {
tx = false;
}
}
return tx;
}
static safety_config chrysler_init(uint16_t param) {
const uint32_t CHRYSLER_PARAM_RAM_DT = 1U; // set for Ram DT platform
const uint32_t CHRYSLER_PARAM_JEEP_BRAKE_HOLD = 4U;
// CAN messages for Chrysler/Jeep platforms
static const ChryslerAddrs CHRYSLER_ADDRS = {
@@ -217,6 +227,7 @@ static safety_config chrysler_init(uint16_t param) {
{CHRYSLER_ADDRS.CRUISE_BUTTONS, 0, 3, .check_relay = false},
{CHRYSLER_ADDRS.LKAS_COMMAND, 0, 6, .check_relay = true},
{CHRYSLER_ADDRS.DAS_6, 0, 8, .check_relay = true},
{CHRYSLER_ADDRS.DAS_3, 0, 8, .check_relay = false},
// RealFast variables
{CHRYSLER_ADDRS.CRUISE_BUTTONS_ALT, 2, 3, .check_relay = false},
@@ -287,6 +298,7 @@ static safety_config chrysler_init(uint16_t param) {
chrysler_addrs = &CHRYSLER_ADDRS;
ret = BUILD_SAFETY_CFG(chrysler_rx_checks, CHRYSLER_TX_MSGS);
}
chrysler_jeep_brake_hold = GET_FLAG(param, CHRYSLER_PARAM_JEEP_BRAKE_HOLD) && (chrysler_platform == CHRYSLER_PACIFICA);
return ret;
}
+7 -5
View File
@@ -86,6 +86,8 @@ static bool ford_get_quality_flag_valid(const CANPacket_t *msg) {
#define FORD_CANFD_INACTIVE_CURVATURE_RATE 1024U
static bool ford_lka_steering = false;
// Curvature rate limits
#define FORD_LIMITS(limit_lateral_acceleration) { \
.max_angle = 1000, /* 0.02 curvature */ \
@@ -227,12 +229,10 @@ static bool ford_tx_hook(const CANPacket_t *msg) {
// Safety check for Lane_Assist_Data1 action
if (msg->addr == FORD_Lane_Assist_Data1) {
// Do not allow steering using Lane_Assist_Data1 (Lane-Departure Aid).
// This message must be sent for Lane Centering to work, and can include
// values such as the steering angle or lane curvature for debugging,
// but the action (LkaActvStats_D2_Req) must be set to zero.
unsigned int action = msg->data[0] >> 5;
if (action != 0U) {
bool valid_lka_action = action == 0U;
valid_lka_action |= ford_lka_steering && controls_allowed && ((action == 2U) || (action == 4U));
if (!valid_lka_action) {
tx = false;
}
}
@@ -330,7 +330,9 @@ static safety_config ford_init(uint16_t param) {
};
const uint16_t FORD_PARAM_CANFD = 2;
const uint16_t FORD_PARAM_LKA_STEERING = 4;
const bool ford_canfd = GET_FLAG(param, FORD_PARAM_CANFD);
ford_lka_steering = GET_FLAG(param, FORD_PARAM_LKA_STEERING);
bool ford_longitudinal = false;
+4 -3
View File
@@ -517,13 +517,14 @@ static bool gm_fwd_hook(int bus_num, int addr) {
bool is_lkas_msg = addr == 0x180U;
bool is_acc_status_msg = addr == 0x370U;
bool is_acc_actuation_msg = (addr == 0x315U) || (addr == 0x2CBU);
bool is_acc_counter_msg = addr == 0x2CDU;
block_msg = is_lkas_msg;
if (gm_cam_long || gm_pedal_long) {
block_msg |= is_acc_status_msg;
}
if (gm_cam_long) {
block_msg |= is_acc_actuation_msg;
block_msg |= is_acc_actuation_msg || is_acc_counter_msg;
}
}
}
@@ -579,8 +580,8 @@ static safety_config gm_init(uint16_t param) {
};
// block PSCMStatus (0x184); forwarded through openpilot to hide an alert from the camera
static const CanMsg GM_CAM_LONG_TX_MSGS[] = {{0x180, 0, 4, .check_relay = true}, {0x315, 0, 5, .check_relay = true}, {0x2CB, 0, 8, .check_relay = true}, {0x370, 0, 6, .check_relay = true}, {0x3D1, 0, 8, .check_relay = false}, // pt bus
{0x184, 2, 8, .check_relay = true}, // camera bus
static const CanMsg GM_CAM_LONG_TX_MSGS[] = {{0x180, 0, 4, .check_relay = true}, {0x315, 0, 5, .check_relay = true}, {0x2CB, 0, 8, .check_relay = true}, {0x2CD, 0, 5, .check_relay = true}, {0x370, 0, 6, .check_relay = true}, {0x3D1, 0, 8, .check_relay = false}, // pt bus
{0x184, 2, 8, .check_relay = true}, {0x315, 2, 5, .check_relay = false}, // camera bus (SASCM brake relay)
{0x200, 0, 6, .check_relay = false}, {0x1E1, 0, 7, .check_relay = false},
{0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus
+52 -15
View File
@@ -25,13 +25,13 @@ const LongitudinalLimits HYUNDAI_LONG_LIMITS = {
.min_accel = -350, // 1/100 m/s2
};
#define HYUNDAI_COMMON_TX_MSGS(scc_bus) \
{0x340, 0, 8, .check_relay = true}, /* LKAS11 Bus 0 */ \
{0x4F1, scc_bus, 4, .check_relay = false}, /* CLU11 Bus 0 (radar-SCC) or 2 (camera-SCC) */ \
{0x485, 0, 4, .check_relay = true}, /* LFAHDA_MFC Bus 0 */ \
#define HYUNDAI_COMMON_TX_MSGS(scc_bus, can_refresh) \
{0x340, 0, 8, .check_relay = true}, /* LKAS11 Bus 0 */ \
{0x4F1, scc_bus, 4, .check_relay = false}, /* CLU11 Bus 0 (radar-SCC) or 2 (camera-SCC) */ \
{0x485, 0, (can_refresh) ? 8 : 4, .check_relay = true}, /* LFAHDA_MFC Bus 0 */ \
#define HYUNDAI_LONG_COMMON_TX_MSGS(scc_bus) \
HYUNDAI_COMMON_TX_MSGS(scc_bus) \
#define HYUNDAI_LONG_COMMON_TX_MSGS(scc_bus, can_refresh) \
HYUNDAI_COMMON_TX_MSGS(scc_bus, can_refresh) \
{0x420, 0, 8, .check_relay = true}, /* SCC11 Bus 0 */ \
{0x421, 0, 8, .check_relay = true}, /* SCC12 Bus 0 */ \
{0x50A, 0, 8, .check_relay = true}, /* SCC13 Bus 0 */ \
@@ -67,11 +67,22 @@ const LongitudinalLimits HYUNDAI_LONG_LIMITS = {
{.msg = {{0x592U, 0, 8, 10U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
static const CanMsg HYUNDAI_TX_MSGS[] = {
HYUNDAI_COMMON_TX_MSGS(0)
HYUNDAI_COMMON_TX_MSGS(0, false)
};
static const CanMsg HYUNDAI_REFRESH_TX_MSGS[] = {
HYUNDAI_COMMON_TX_MSGS(0, true)
};
static const CanMsg HYUNDAI_LONG_TX_MSGS[] = {
HYUNDAI_LONG_COMMON_TX_MSGS(0)
HYUNDAI_LONG_COMMON_TX_MSGS(0, false)
{0x38D, 0, 8, .check_relay = false}, // FCA11 Bus 0
{0x483, 0, 8, .check_relay = false}, // FCA12 Bus 0
{0x7D0, 0, 8, .check_relay = false}, // radar UDS TX addr Bus 0 (for radar disable)
};
static const CanMsg HYUNDAI_LONG_REFRESH_TX_MSGS[] = {
HYUNDAI_LONG_COMMON_TX_MSGS(0, true)
{0x38D, 0, 8, .check_relay = false}, // FCA11 Bus 0
{0x483, 0, 8, .check_relay = false}, // FCA12 Bus 0
{0x7D0, 0, 8, .check_relay = false}, // radar UDS TX addr Bus 0 (for radar disable)
@@ -330,11 +341,19 @@ static bool hyundai_tx_hook(const CANPacket_t *msg) {
static safety_config hyundai_init(uint16_t param) {
static const CanMsg HYUNDAI_CAMERA_SCC_TX_MSGS[] = {
HYUNDAI_COMMON_TX_MSGS(2)
HYUNDAI_COMMON_TX_MSGS(2, false)
};
static const CanMsg HYUNDAI_CAMERA_SCC_REFRESH_TX_MSGS[] = {
HYUNDAI_COMMON_TX_MSGS(2, true)
};
static const CanMsg HYUNDAI_CAMERA_SCC_LONG_TX_MSGS[] = {
HYUNDAI_LONG_COMMON_TX_MSGS(2)
HYUNDAI_LONG_COMMON_TX_MSGS(2, false)
};
static const CanMsg HYUNDAI_CAMERA_SCC_LONG_REFRESH_TX_MSGS[] = {
HYUNDAI_LONG_COMMON_TX_MSGS(2, true)
};
static const CanMsg HYUNDAI_CAN_CANFD_BLENDED_TX_MSGS[] = {
@@ -403,11 +422,19 @@ static safety_config hyundai_init(uint16_t param) {
}
}
if (hyundai_camera_scc) {
SET_TX_MSGS(HYUNDAI_CAMERA_SCC_LONG_TX_MSGS, ret);
if (hyundai_can_refresh_msgs) {
SET_TX_MSGS(HYUNDAI_CAMERA_SCC_LONG_REFRESH_TX_MSGS, ret);
} else {
SET_TX_MSGS(HYUNDAI_CAMERA_SCC_LONG_TX_MSGS, ret);
}
} else if (hyundai_can_canfd_blended) {
SET_TX_MSGS(HYUNDAI_CAN_CANFD_BLENDED_LONG_TX_MSGS, ret);
} else {
SET_TX_MSGS(HYUNDAI_LONG_TX_MSGS, ret);
if (hyundai_can_refresh_msgs) {
SET_TX_MSGS(HYUNDAI_LONG_REFRESH_TX_MSGS, ret);
} else {
SET_TX_MSGS(HYUNDAI_LONG_TX_MSGS, ret);
}
}
} else if (hyundai_camera_scc) {
@@ -425,9 +452,14 @@ static safety_config hyundai_init(uint16_t param) {
};
if (hyundai_has_lda_button) {
ret = BUILD_SAFETY_CFG(hyundai_cam_scc_rx_checks_lda, HYUNDAI_CAMERA_SCC_TX_MSGS);
SET_RX_CHECKS(hyundai_cam_scc_rx_checks_lda, ret);
} else {
ret = BUILD_SAFETY_CFG(hyundai_cam_scc_rx_checks, HYUNDAI_CAMERA_SCC_TX_MSGS);
SET_RX_CHECKS(hyundai_cam_scc_rx_checks, ret);
}
if (hyundai_can_refresh_msgs) {
SET_TX_MSGS(HYUNDAI_CAMERA_SCC_REFRESH_TX_MSGS, ret);
} else {
SET_TX_MSGS(HYUNDAI_CAMERA_SCC_TX_MSGS, ret);
}
} else if (hyundai_can_canfd_blended) {
static RxCheck hyundai_can_canfd_blended_rx_checks[] = {
@@ -509,7 +541,11 @@ static safety_config hyundai_init(uint16_t param) {
HYUNDAI_LDA_BUTTON_ADDR_CHECK
};
SET_TX_MSGS(HYUNDAI_TX_MSGS, ret);
if (hyundai_can_refresh_msgs) {
SET_TX_MSGS(HYUNDAI_REFRESH_TX_MSGS, ret);
} else {
SET_TX_MSGS(HYUNDAI_TX_MSGS, ret);
}
if (hyundai_fcev_gas_signal) {
if (hyundai_has_lda_button) {
SET_RX_CHECKS(hyundai_fcev_rx_checks_lda, ret);
@@ -557,6 +593,7 @@ static safety_config hyundai_legacy_init(uint16_t param) {
hyundai_common_init(param);
hyundai_legacy = true;
hyundai_camera_scc = false;
hyundai_can_refresh_msgs = false;
return hyundai_longitudinal ? BUILD_SAFETY_CFG(hyundai_legacy_rx_checks, HYUNDAI_LONG_TX_MSGS) :
BUILD_SAFETY_CFG(hyundai_legacy_rx_checks, HYUNDAI_TX_MSGS);
}
+117 -10
View File
@@ -13,8 +13,8 @@
#define HYUNDAI_CANFD_LKA_STEERING_ALT_COMMON_TX_MSGS(a_can, e_can) \
HYUNDAI_CANFD_CRUISE_BUTTON_TX_MSGS(e_can) \
{0x110, a_can, 32, .check_relay = (a_can) == 0}, /* LKAS_ALT */ \
{0x362, a_can, 32, .check_relay = (a_can) == 0}, /* CAM_0x362 */ \
{0x110, a_can, 32, .check_relay = (a_can) == 0, .disable_static_blocking = true}, /* LKAS_ALT */ \
{0x362, a_can, 32, .check_relay = (a_can) == 0, .disable_static_blocking = true}, /* CAM_0x362 */ \
#define HYUNDAI_CANFD_LFA_STEERING_COMMON_TX_MSGS(e_can) \
{0x12A, e_can, 16, .check_relay = (e_can) == 0}, /* LFA */ \
@@ -26,6 +26,7 @@
#define HYUNDAI_CANFD_MRR35_RADAR_TRACK_START 0x3A5
#define HYUNDAI_CANFD_MRR35_RADAR_TRACK_END 0x3C4
#define HYUNDAI_CANFD_INACTIVE_ACCEL_TX_THRESHOLD 10U
#define HYUNDAI_CANFD_BLINDSPOT_DASH_TX_MSGS(e_can) \
{0x1BA, e_can, 24, .check_relay = false}, /* BLINDSPOTS_REAR_CORNERS */ \
@@ -60,6 +61,9 @@ static bool hyundai_canfd_alt_buttons = false;
static bool hyundai_canfd_lka_steering_alt = false;
static bool hyundai_canfd_angle_steering = false;
static bool hyundai_ccnc = false;
static bool hyundai_canfd_ccnc_angle_long = false;
static bool hyundai_canfd_lka_alt_drive_gear = false;
static uint8_t hyundai_canfd_inactive_accel_tx_count = 0U;
static unsigned int hyundai_canfd_get_lka_addr(void) {
return hyundai_canfd_lka_steering_alt ? 0x110U : 0x50U;
@@ -80,6 +84,20 @@ static uint32_t hyundai_canfd_get_checksum(const CANPacket_t *msg) {
return chksum;
}
static bool hyundai_canfd_lka_alt_forward_addr(int addr) {
return (addr == 0x110) || (addr == 0x362);
}
static bool hyundai_canfd_lka_alt_openpilot_allowed(void) {
const bool angle_steering_allowed = !hyundai_canfd_angle_steering || vehicle_moving;
return (aol_allowed || controls_allowed) && angle_steering_allowed &&
(!hyundai_ev_gas_signal || hyundai_canfd_lka_alt_drive_gear);
}
static bool hyundai_canfd_lka_alt_stock_forwarding(void) {
return hyundai_canfd_lka_steering_alt && hyundai_canfd_angle_steering && !hyundai_canfd_lka_alt_openpilot_allowed();
}
static void hyundai_canfd_rx_all_hook(const CANPacket_t *msg) {
SAFETY_UNUSED(msg);
}
@@ -87,6 +105,10 @@ static void hyundai_canfd_rx_all_hook(const CANPacket_t *msg) {
static bool hyundai_canfd_fwd_hook(int bus_num, int addr) {
const bool mrr35_radar_track = (addr >= HYUNDAI_CANFD_MRR35_RADAR_TRACK_START) && (addr <= HYUNDAI_CANFD_MRR35_RADAR_TRACK_END);
if ((bus_num == 2) && hyundai_canfd_lka_steering_alt && hyundai_canfd_lka_alt_forward_addr(addr)) {
return !hyundai_canfd_lka_alt_stock_forwarding();
}
// On LKA-steering long-control cars using live MRR35 radar tracks, openpilot parses
// the tracks directly from bus 0. Forwarding them to bus 2 creates a returned TX copy
// of every object frame on the logged CAN stream without adding planner data.
@@ -105,7 +127,9 @@ static void hyundai_canfd_rx_hook(const CANPacket_t *msg) {
torque_driver_new -= 4095;
update_sample(&torque_driver, torque_driver_new);
int angle_meas_new = (msg->data[13] << 8U) | msg->data[12];
// CCNC angle-long platforms publish the usable angle in STEERING_ANGLE_2.
const unsigned int angle_offset = hyundai_canfd_ccnc_angle_long ? 16U : 12U;
int angle_meas_new = (msg->data[angle_offset + 1U] << 8U) | msg->data[angle_offset];
angle_meas_new = to_signed(angle_meas_new, 16);
update_sample(&angle_meas, angle_meas_new);
}
@@ -113,6 +137,7 @@ static void hyundai_canfd_rx_hook(const CANPacket_t *msg) {
// cruise buttons
const unsigned int button_addr = hyundai_canfd_alt_buttons ? 0x1aaU : 0x1cfU;
if (msg->addr == button_addr) {
const bool controls_allowed_prev = controls_allowed;
bool main_button = false;
int cruise_button = 0;
if (msg->addr == 0x1cfU) {
@@ -127,11 +152,15 @@ static void hyundai_canfd_rx_hook(const CANPacket_t *msg) {
hyundai_lkas_button_check(GET_BIT(msg, 39U));
}
hyundai_common_cruise_buttons_check(cruise_button, main_button);
if (!controls_allowed_prev && controls_allowed) {
hyundai_canfd_inactive_accel_tx_count = 0U;
}
}
// gas press, different for EV, hybrid, and ICE models
if ((msg->addr == 0x35U) && hyundai_ev_gas_signal) {
gas_pressed = msg->data[5] != 0U;
hyundai_canfd_lka_alt_drive_gear = (msg->data[24] & 0x7U) == 5U;
} else if ((msg->addr == 0x105U) && hyundai_hybrid_gas_signal) {
gas_pressed = GET_BIT(msg, 103U) || (msg->data[13] != 0U) || GET_BIT(msg, 112U);
} else if ((msg->addr == 0x100U) && !hyundai_ev_gas_signal && !hyundai_hybrid_gas_signal) {
@@ -202,6 +231,10 @@ static bool hyundai_canfd_tx_hook(const CANPacket_t *msg) {
bool tx = true;
if ((msg->bus == 0U) && hyundai_canfd_lka_alt_forward_addr(msg->addr) && hyundai_canfd_lka_alt_stock_forwarding()) {
tx = false;
}
if (msg->addr == 0xCBU) {
if (!hyundai_canfd_angle_steering) {
tx = false;
@@ -209,6 +242,10 @@ static bool hyundai_canfd_tx_hook(const CANPacket_t *msg) {
const int lfa_angle_active = (msg->data[3] >> 4U) & 0xFU;
const bool steer_angle_req = lfa_angle_active == 2;
if (steer_angle_req && hyundai_canfd_ccnc_angle_long && !hyundai_canfd_lka_alt_openpilot_allowed()) {
tx = false;
}
int desired_angle = (((uint32_t)(msg->data[5] & 0x3FU)) << 8) | (uint32_t)msg->data[4];
desired_angle = to_signed(desired_angle, 14);
@@ -281,6 +318,18 @@ static bool hyundai_canfd_tx_hook(const CANPacket_t *msg) {
bool violation = false;
if (hyundai_longitudinal) {
const int acc_mode = (msg->data[8] >> 4) & 0x7U;
const bool inactive_accel = (acc_mode == 0) && (desired_accel_raw == 0) && (desired_accel_val == 0);
if (inactive_accel) {
hyundai_canfd_inactive_accel_tx_count = SAFETY_MIN(hyundai_canfd_inactive_accel_tx_count + 1U,
HYUNDAI_CANFD_INACTIVE_ACCEL_TX_THRESHOLD);
if (hyundai_canfd_inactive_accel_tx_count >= HYUNDAI_CANFD_INACTIVE_ACCEL_TX_THRESHOLD) {
controls_allowed = false;
}
} else {
hyundai_canfd_inactive_accel_tx_count = 0U;
}
violation |= longitudinal_accel_checks(desired_accel_raw, HYUNDAI_LONG_LIMITS);
violation |= longitudinal_accel_checks(desired_accel_val, HYUNDAI_LONG_LIMITS);
} else {
@@ -320,6 +369,16 @@ static safety_config hyundai_canfd_init(uint16_t param) {
HYUNDAI_CANFD_LKA_STEERING_ALT_COMMON_TX_MSGS(0, 1)
};
static const CanMsg HYUNDAI_CANFD_LKA_STEERING_ALT_BUTTONS_TX_MSGS[] = {
HYUNDAI_CANFD_LKA_STEERING_COMMON_TX_MSGS(0, 1)
HYUNDAI_CANFD_SCC_CONTROL_COMMON_TX_MSGS(1, false)
};
static const CanMsg HYUNDAI_CANFD_LKA_STEERING_ALT_ALT_BUTTONS_TX_MSGS[] = {
HYUNDAI_CANFD_LKA_STEERING_ALT_COMMON_TX_MSGS(0, 1)
HYUNDAI_CANFD_SCC_CONTROL_COMMON_TX_MSGS(1, false)
};
static const CanMsg HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS[] = {
HYUNDAI_CANFD_LKA_STEERING_COMMON_TX_MSGS(0, 1)
HYUNDAI_CANFD_LFA_STEERING_COMMON_TX_MSGS(1)
@@ -350,6 +409,24 @@ static safety_config hyundai_canfd_init(uint16_t param) {
{0x1DA, 1, 32, .check_relay = false}, // ADRV_0x1da
};
static const CanMsg HYUNDAI_CANFD_CCNC_ANGLE_LONG_TX_MSGS[] = {
HYUNDAI_CANFD_LKA_STEERING_ALT_COMMON_TX_MSGS(0, 1)
HYUNDAI_CANFD_LFA_STEERING_COMMON_TX_MSGS(1)
HYUNDAI_CANFD_SCC_CONTROL_COMMON_TX_MSGS(1, true)
{0x1BA, 1, 24, .check_relay = false}, // BLINDSPOTS_REAR_CORNERS
{0x1E5, 1, 16, .check_relay = false}, // BLINDSPOTS_FRONT_CORNER_1
{0x100, 0, 24, .check_relay = false}, // ACCELERATOR_BRAKE_ALT radar heartbeat
{0x730, 1, 8, .check_relay = false}, // tester present for ADAS ECU disable
{0x160, 1, 16, .check_relay = false}, // ADRV_0x160
{0x161, 1, 32, .check_relay = false}, // CCNC_0x161
{0x162, 1, 32, .check_relay = false}, // CCNC_0x162
{0x1EA, 1, 32, .check_relay = false}, // ADRV_0x1ea
{0x200, 1, 8, .check_relay = false}, // ADRV_0x200
{0x345, 1, 8, .check_relay = false}, // ADRV_0x345
{0x38C, 1, 32, .check_relay = false}, // CCNC support frame
{0x1DA, 1, 32, .check_relay = false}, // ADRV_0x1da
};
static const CanMsg HYUNDAI_CANFD_LFA_STEERING_TX_MSGS[] = {
HYUNDAI_CANFD_CRUISE_BUTTON_TX_MSGS(2)
HYUNDAI_CANFD_LFA_STEERING_COMMON_TX_MSGS(0)
@@ -389,6 +466,10 @@ static safety_config hyundai_canfd_init(uint16_t param) {
hyundai_canfd_lka_steering_alt = GET_FLAG(param, HYUNDAI_PARAM_CANFD_LKA_STEERING_ALT);
hyundai_canfd_angle_steering = GET_FLAG(param, HYUNDAI_PARAM_CANFD_ANGLE_STEERING);
hyundai_ccnc = GET_FLAG(param, HYUNDAI_PARAM_CCNC);
hyundai_canfd_ccnc_angle_long = hyundai_longitudinal && hyundai_canfd_lka_steering &&
hyundai_canfd_lka_steering_alt && hyundai_canfd_angle_steering && hyundai_ccnc;
hyundai_canfd_lka_alt_drive_gear = false;
hyundai_canfd_inactive_accel_tx_count = 0U;
safety_config ret;
if (hyundai_longitudinal) {
@@ -397,8 +478,18 @@ static safety_config hyundai_canfd_init(uint16_t param) {
HYUNDAI_CANFD_STD_BUTTONS_RX_CHECKS(1)
};
SET_RX_CHECKS(hyundai_canfd_lka_steering_long_rx_checks, ret);
if (hyundai_canfd_lka_steering_alt) {
static RxCheck hyundai_canfd_lka_steering_alt_buttons_long_rx_checks[] = {
HYUNDAI_CANFD_ALT_BUTTONS_RX_CHECKS(1)
};
if (hyundai_canfd_alt_buttons) {
SET_RX_CHECKS(hyundai_canfd_lka_steering_alt_buttons_long_rx_checks, ret);
} else {
SET_RX_CHECKS(hyundai_canfd_lka_steering_long_rx_checks, ret);
}
if (hyundai_canfd_ccnc_angle_long) {
SET_TX_MSGS(HYUNDAI_CANFD_CCNC_ANGLE_LONG_TX_MSGS, ret);
} else if (hyundai_canfd_lka_steering_alt) {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_LONG_TX_MSGS, ret);
} else {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS, ret);
@@ -444,17 +535,33 @@ static safety_config hyundai_canfd_init(uint16_t param) {
if (hyundai_canfd_lka_steering) {
// *** LKA steering checks ***
// E-CAN is on bus 1, SCC messages are sent on cars with ADRV ECU.
// Does not use the alt buttons message
static RxCheck hyundai_canfd_lka_steering_rx_checks[] = {
HYUNDAI_CANFD_STD_BUTTONS_RX_CHECKS(1)
HYUNDAI_CANFD_SCC_ADDR_CHECK(1)
};
SET_RX_CHECKS(hyundai_canfd_lka_steering_rx_checks, ret);
if (hyundai_canfd_lka_steering_alt) {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_TX_MSGS, ret);
static RxCheck hyundai_canfd_lka_steering_alt_buttons_rx_checks[] = {
HYUNDAI_CANFD_ALT_BUTTONS_RX_CHECKS(1)
HYUNDAI_CANFD_SCC_ADDR_CHECK(1)
};
if (hyundai_canfd_alt_buttons) {
SET_RX_CHECKS(hyundai_canfd_lka_steering_alt_buttons_rx_checks, ret);
} else {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_TX_MSGS, ret);
SET_RX_CHECKS(hyundai_canfd_lka_steering_rx_checks, ret);
}
if (hyundai_canfd_lka_steering_alt) {
if (hyundai_canfd_alt_buttons) {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_ALT_BUTTONS_TX_MSGS, ret);
} else {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_TX_MSGS, ret);
}
} else {
if (hyundai_canfd_alt_buttons) {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_BUTTONS_TX_MSGS, ret);
} else {
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_TX_MSGS, ret);
}
}
} else if (!hyundai_camera_scc) {
@@ -57,6 +57,9 @@ bool hyundai_non_scc = false;
extern bool hyundai_cancel_button_enable;
bool hyundai_cancel_button_enable = false;
extern bool hyundai_can_refresh_msgs;
bool hyundai_can_refresh_msgs = false;
static uint8_t hyundai_last_button_interaction; // button messages since the user pressed an enable button
static bool acc_main_on_prev;
static bool acc_main_on_tx;
@@ -76,6 +79,7 @@ void hyundai_common_init(uint16_t param) {
const uint16_t HYUNDAI_PARAM_NON_SCC = 4096;
const uint16_t HYUNDAI_PARAM_CAN_CANFD_BLENDED = 8192;
const uint16_t HYUNDAI_PARAM_CANCEL_BTN_ENABLE = 16384;
const uint16_t HYUNDAI_PARAM_CAN_REFRESH_MSGS = 32768;
hyundai_ev_gas_signal = GET_FLAG(param, HYUNDAI_PARAM_EV_GAS);
hyundai_hybrid_gas_signal = !hyundai_ev_gas_signal && GET_FLAG(param, HYUNDAI_PARAM_HYBRID_GAS);
@@ -90,6 +94,7 @@ void hyundai_common_init(uint16_t param) {
hyundai_aol_lkas_on_engage = GET_FLAG(param, HYUNDAI_PARAM_AOL_LKAS_ON_ENGAGE);
hyundai_non_scc = GET_FLAG(param, HYUNDAI_PARAM_NON_SCC);
hyundai_cancel_button_enable = GET_FLAG(param, HYUNDAI_PARAM_CANCEL_BTN_ENABLE);
hyundai_can_refresh_msgs = GET_FLAG(param, HYUNDAI_PARAM_CAN_REFRESH_MSGS);
hyundai_last_button_interaction = HYUNDAI_PREV_BUTTON_SAMPLES;
acc_main_on_prev = false;
@@ -199,7 +204,6 @@ void hyundai_common_acc_main_on_sync(void) {
if (acc_main_on_mismatches >= 3U) {
acc_main_on = false;
lkas_on = false;
}
} else {
acc_main_on_mismatches = 0U;
+7 -4
View File
@@ -4,6 +4,12 @@
static bool nissan_alt_eps = false;
static void nissan_rx_all_hook(const CANPacket_t *msg) {
if ((msg->addr == 0x1B6U) && (msg->bus == (nissan_alt_eps ? 2U : 1U))) {
acc_main_on = GET_BIT(msg, 36U);
}
}
static void nissan_rx_hook(const CANPacket_t *msg) {
if (msg->bus == (nissan_alt_eps ? 1U : 0U)) {
@@ -51,10 +57,6 @@ static void nissan_rx_hook(const CANPacket_t *msg) {
pcm_cruise_check(cruise_engaged);
}
if ((msg->addr == 0x1B6U) && (msg->bus == (nissan_alt_eps ? 2U : 1U))) {
acc_main_on = GET_BIT(msg, 36U);
}
if ((msg->addr == 0x239U) && (msg->bus == 0U)) {
acc_main_on = GET_BIT(msg, 17U);
}
@@ -140,6 +142,7 @@ static safety_config nissan_init(uint16_t param) {
const safety_hooks nissan_hooks = {
.init = nissan_init,
.rx_all = nissan_rx_all_hook,
.rx = nissan_rx_hook,
.tx = nissan_tx_hook,
};
+11 -1
View File
@@ -3,8 +3,11 @@
#include "opendbc/safety/declarations.h"
static bool tesla_longitudinal = false;
static bool tesla_coop_steering = false;
static bool tesla_stock_aeb = false;
#define TESLA_STEERING_DISENGAGE_TORQUE 500 // cNm
// Only rising edges while controls are not allowed are considered for these systems:
// TODO: Only LKAS (non-emergency) is currently supported since we've only seen it
static bool tesla_stock_lkas = false;
@@ -101,11 +104,14 @@ static void tesla_rx_hook(const CANPacket_t *msg) {
update_sample(&angle_meas, angle_meas_new);
const int hands_on_level = msg->data[4] >> 6; // EPAS3S_handsOnLevel
const int torsion_bar_torque = (((msg->data[2] & 0x0FU) << 8) | msg->data[3]) - 2050; // 0.01 Nm
const int eac_status = msg->data[6] >> 5; // EPAS3S_eacStatus
const int eac_error_code = msg->data[2] >> 4; // EPAS3S_eacErrorCode
// Disengage on normal user override, or if high angle rate fault from user overriding extremely quickly
steering_disengage = (hands_on_level >= 3) || ((eac_status == 0) && (eac_error_code == 9));
steering_disengage = (hands_on_level >= 3) ||
(tesla_coop_steering && (SAFETY_ABS(torsion_bar_torque) > TESLA_STEERING_DISENGAGE_TORQUE)) ||
((eac_status == 0) && (eac_error_code == 9));
}
// Vehicle speed (DI_speed)
@@ -333,7 +339,11 @@ static safety_config tesla_init(uint16_t param) {
SAFETY_UNUSED(param);
#ifdef ALLOW_DEBUG
const uint16_t TESLA_FLAG_LONGITUDINAL_CONTROL = 1;
const uint16_t TESLA_FLAG_COOP_STEERING = 256;
tesla_longitudinal = GET_FLAG(param, TESLA_FLAG_LONGITUDINAL_CONTROL);
tesla_coop_steering = GET_FLAG(param, TESLA_FLAG_COOP_STEERING);
#else
tesla_coop_steering = false;
#endif
tesla_stock_aeb = false;
+39 -7
View File
@@ -50,21 +50,36 @@
#define TOYOTA_COMMON_RX_CHECKS(lta) \
{.msg = {{ 0xaa, 0, 8, 83U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{0x260, 0, 8, 50U, .ignore_counter = true, .ignore_quality_flag=!(lta)}, { 0 }, { 0 }}}, \
/* StarPilot Variables */ \
#define TOYOTA_CRUISE_RX_CHECK \
{.msg = {{0x1D3, 0, 8, 33U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
#define TOYOTA_ALT_CRUISE_RX_CHECK \
{.msg = {{0x1D3, 0, 8, 33U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, \
{0x1D3, 0, 5, 33U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, \
{0x365, 0, 7, 10U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}}}, \
#define TOYOTA_RX_CHECKS(lta) \
TOYOTA_COMMON_RX_CHECKS(lta) \
TOYOTA_CRUISE_RX_CHECK \
{.msg = {{0x1D2, 0, 8, 33U, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{0x226, 0, 8, 40U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
#define TOYOTA_ALT_CRUISE_RX_CHECKS(lta) \
TOYOTA_COMMON_RX_CHECKS(lta) \
TOYOTA_ALT_CRUISE_RX_CHECK \
{.msg = {{0x1D2, 0, 8, 33U, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{0x226, 0, 8, 40U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
#define TOYOTA_ALT_BRAKE_RX_CHECKS(lta) \
TOYOTA_COMMON_RX_CHECKS(lta) \
TOYOTA_CRUISE_RX_CHECK \
{.msg = {{0x1D2, 0, 8, 33U, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{0x224, 0, 8, 40U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
#define TOYOTA_SECOC_RX_CHECKS \
TOYOTA_COMMON_RX_CHECKS(false) \
TOYOTA_CRUISE_RX_CHECK \
{.msg = {{0x176, 0, 8, 32U, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{0x116, 0, 8, 42U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{0x101, 0, 8, 50U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
@@ -78,6 +93,7 @@ static bool toyota_stock_longitudinal = false;
static bool toyota_lta = false;
static int toyota_dbc_eps_torque_factor = 100; // conversion factor for STEER_TORQUE_EPS in %: see dbc file
static bool toyota_long_filter = false;
static bool toyota_alt_cruise = false;
static uint32_t toyota_compute_checksum(const CANPacket_t *msg) {
int len = GET_LEN(msg);
@@ -108,6 +124,12 @@ static bool toyota_get_quality_flag_valid(const CANPacket_t *msg) {
return valid;
}
static void toyota_rx_all_hook(const CANPacket_t *msg) {
if (toyota_alt_cruise && (msg->bus == 0U) && (msg->addr == 0x365U) && (GET_LEN(msg) == 7U)) {
acc_main_on = GET_BIT(msg, 0U); // DSU_CRUISE.MAIN_ON
}
}
static void toyota_rx_hook(const CANPacket_t *msg) {
if (msg->bus == 0U) {
@@ -185,14 +207,10 @@ static void toyota_rx_hook(const CANPacket_t *msg) {
UPDATE_VEHICLE_SPEED(speed / 4.0 * 0.01 * KPH_TO_MS);
}
if (msg->addr == 0x1D3U) {
if ((msg->addr == 0x1D3U) && (GET_LEN(msg) == 8U)) {
acc_main_on = GET_BIT(msg, 15U);
}
if (msg->addr == 0x365U) {
acc_main_on = GET_BIT(msg, 0U);
}
if (enable_gas_interceptor && (msg->addr == 0x201U)) {
// Match the DBC's physical pedal threshold to avoid controls state mismatches.
const int toyota_gas_interceptor_threshold = 805;
@@ -437,6 +455,7 @@ static safety_config toyota_init(uint16_t param) {
const uint32_t TOYOTA_PARAM_LTA = 4UL << TOYOTA_PARAM_OFFSET;
const uint32_t TOYOTA_PARAM_LONG_FILTER = 16UL << TOYOTA_PARAM_OFFSET;
const uint32_t TOYOTA_PARAM_GAS_INTERCEPTOR = 32UL << TOYOTA_PARAM_OFFSET;
const uint32_t TOYOTA_PARAM_ALT_CRUISE = 64UL << TOYOTA_PARAM_OFFSET;
#ifdef ALLOW_DEBUG
const uint32_t TOYOTA_PARAM_SECOC = 8UL << TOYOTA_PARAM_OFFSET;
@@ -448,6 +467,7 @@ static safety_config toyota_init(uint16_t param) {
toyota_lta = GET_FLAG(param, TOYOTA_PARAM_LTA);
toyota_long_filter = GET_FLAG(param, TOYOTA_PARAM_LONG_FILTER);
enable_gas_interceptor = GET_FLAG(param, TOYOTA_PARAM_GAS_INTERCEPTOR);
toyota_alt_cruise = GET_FLAG(param, TOYOTA_PARAM_ALT_CRUISE);
toyota_dbc_eps_torque_factor = param & TOYOTA_EPS_FACTOR;
if (toyota_stock_longitudinal || toyota_secoc) {
@@ -515,8 +535,19 @@ static safety_config toyota_init(uint16_t param) {
TOYOTA_ALT_BRAKE_RX_CHECKS(false)
TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK
};
static RxCheck toyota_lka_alt_cruise_rx_checks[] = {
TOYOTA_ALT_CRUISE_RX_CHECKS(false)
};
static RxCheck toyota_lka_alt_cruise_interceptor_rx_checks[] = {
TOYOTA_ALT_CRUISE_RX_CHECKS(false)
TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK
};
if (enable_gas_interceptor && !toyota_alt_brake) {
if (toyota_alt_cruise && enable_gas_interceptor) {
SET_RX_CHECKS(toyota_lka_alt_cruise_interceptor_rx_checks, ret);
} else if (toyota_alt_cruise) {
SET_RX_CHECKS(toyota_lka_alt_cruise_rx_checks, ret);
} else if (enable_gas_interceptor && !toyota_alt_brake) {
SET_RX_CHECKS(toyota_lka_interceptor_rx_checks, ret);
} else if (enable_gas_interceptor) {
SET_RX_CHECKS(toyota_lka_alt_brake_interceptor_rx_checks, ret);
@@ -542,6 +573,7 @@ static bool toyota_fwd_hook(int bus_num, int addr) {
const safety_hooks toyota_hooks = {
.init = toyota_init,
.rx = toyota_rx_hook,
.rx_all = toyota_rx_all_hook,
.tx = toyota_tx_hook,
.fwd = toyota_fwd_hook,
.get_checksum = toyota_get_checksum,
@@ -984,6 +984,7 @@ class SafetyTest(SafetyTestBase):
continue
if {attr, current_test}.issubset({'TestHyundaiLongitudinalSafety', 'TestHyundaiLongitudinalSafetyCameraSCC',
'TestHyundaiSafetyFCEVLong', 'TestHyundaiLongitudinalAolLkasOnEngageSafety',
'TestHyundaiSafetyCanRefreshLong', 'TestHyundaiSafetyCanRefreshLongCameraSCC',
'TestHyundaiCanCanfdBlendedLongitudinalSafety',
'TestHyundaiLegacyLongitudinalSafetyHEV'}):
continue
@@ -208,6 +208,28 @@ class HyundaiLongitudinalBase(common.LongitudinalAccelSafetyTest):
class HyundaiAolLkasOnEngageBase:
def test_acc_main_sync_does_not_clear_aol_lkas_latch(self):
try:
tx_acc_state_msg = self._tx_acc_state_msg(False)
except NotImplementedError as err:
raise unittest.SkipTest("ACC main TX state message not implemented") from err
self.safety.set_alternative_experience(ALTERNATIVE_EXPERIENCE.ALWAYS_ON_LATERAL)
self._rx(self._button_msg(Buttons.NONE, main_button=0))
self._rx(self._button_msg(Buttons.NONE, main_button=1))
self._rx(self._button_msg(Buttons.NONE, main_button=0))
self._rx(self._button_msg(Buttons.SET))
self._rx(self._button_msg(Buttons.NONE))
self.safety.set_controls_allowed(False)
for _ in range(3):
self._tx(tx_acc_state_msg)
self.assertFalse(self.safety.get_acc_main_on())
self._set_prev_torque(0)
self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_RATE_UP)))
def test_aol_lkas_auto_enables_on_set_engagement(self):
torque_cmd = self.MAX_RATE_UP
@@ -23,15 +23,20 @@ def is_steering_msg(mode, param, addr):
elif mode in (CarParams.SafetyModel.hyundai, CarParams.SafetyModel.hyundaiLegacy):
ret = addr == 832
elif mode == CarParams.SafetyModel.hyundaiCanfd:
ret = addr == (0x110 if param & HyundaiSafetyFlags.CANFD_LKA_STEERING_ALT else
0x50 if param & HyundaiSafetyFlags.CANFD_LKA_STEERING else
0x12A)
if param & HyundaiSafetyFlags.CCNC and param & HyundaiSafetyFlags.LONG and param & HyundaiSafetyFlags.CANFD_ANGLE_STEERING:
ret = addr == 0xCB
else:
ret = addr == (0x110 if param & HyundaiSafetyFlags.CANFD_LKA_STEERING_ALT else
0x50 if param & HyundaiSafetyFlags.CANFD_LKA_STEERING else
0x12A)
elif mode == CarParams.SafetyModel.chrysler:
ret = addr == 0x292
elif mode == CarParams.SafetyModel.subaru:
ret = addr == 0x122
elif mode == CarParams.SafetyModel.ford:
ret = addr == 0x3d6 if param & FordSafetyFlags.CANFD else addr == 0x3d3
ret = addr == (0x3ca if param & FordSafetyFlags.LKA_STEERING else
0x3d6 if param & FordSafetyFlags.CANFD else
0x3d3)
elif mode == CarParams.SafetyModel.nissan:
ret = addr == 0x169
elif mode == CarParams.SafetyModel.rivian:
@@ -60,14 +65,24 @@ def get_steer_value(mode, param, msg):
elif mode in (CarParams.SafetyModel.hyundai, CarParams.SafetyModel.hyundaiLegacy):
torque = (((msg.data[3] & 0x7) << 8) | msg.data[2]) - 1024
elif mode == CarParams.SafetyModel.hyundaiCanfd:
torque = ((msg.data[5] >> 1) | (msg.data[6] & 0xF) << 7) - 1024
if param & HyundaiSafetyFlags.CANFD_ANGLE_STEERING:
if param & HyundaiSafetyFlags.CCNC and param & HyundaiSafetyFlags.LONG:
angle = ((msg.data[5] & 0x3F) << 8) | msg.data[4]
else:
angle = (msg.data[11] << 6) | (msg.data[10] >> 2)
angle = to_signed(angle, 14)
else:
torque = ((msg.data[5] >> 1) | (msg.data[6] & 0xF) << 7) - 1024
elif mode == CarParams.SafetyModel.chrysler:
torque = (((msg.data[0] & 0x7) << 8) | msg.data[1]) - 1024
elif mode == CarParams.SafetyModel.subaru:
torque = ((msg.data[3] & 0x1F) << 8) | msg.data[2]
torque = -to_signed(torque, 13)
elif mode == CarParams.SafetyModel.ford:
if param & FordSafetyFlags.CANFD:
if param & FordSafetyFlags.LKA_STEERING:
action = msg.data[0] >> 5
angle = 1 if action in (2, 4) else 0
elif param & FordSafetyFlags.CANFD:
angle = ((msg.data[2] << 3) | (msg.data[3] >> 5)) - 1000
else:
angle = ((msg.data[0] << 3) | (msg.data[1] >> 5)) - 1000
@@ -9,7 +9,7 @@ from opendbc.safety.tests.common import CANPackerSafety
class TestChryslerSafety(common.CarSafetyTest, common.MotorTorqueSteeringSafetyTest):
TX_MSGS = [[0x23B, 0], [0x292, 0], [0x2A6, 0]]
TX_MSGS = [[0x23B, 0], [0x292, 0], [0x2A6, 0], [0x1F4, 0]]
RELAY_MALFUNCTION_ADDRS = {0: (0x292, 0x2A6)}
FWD_BLACKLISTED_ADDRS = {2: [0x292, 0x2A6]}
@@ -37,6 +37,14 @@ class TestChryslerSafety(common.CarSafetyTest, common.MotorTorqueSteeringSafetyT
values = {"ACC_ACTIVE": enable}
return self.packer.make_can_msg_safety("DAS_3", self.DAS_BUS, values)
def _main_on_msg(self, available=True, active=False):
values = {"ACC_AVAILABLE": available, "ACC_ACTIVE": active}
return self.packer.make_can_msg_panda("DAS_3", self.DAS_BUS, values)
def _das_3_msg(self):
values = {"ACC_AVAILABLE": 1, "ACC_ACTIVE": 1, "ACC_DECEL_REQ": 1, "ACC_DECEL": -2.0}
return self.packer.make_can_msg_safety("DAS_3", self.DAS_BUS, values)
def _speed_msg(self, speed):
values = {"SPEED_LEFT": speed, "SPEED_RIGHT": speed}
return self.packer.make_can_msg_safety("SPEED_1", 0, values)
@@ -71,6 +79,58 @@ class TestChryslerSafety(common.CarSafetyTest, common.MotorTorqueSteeringSafetyT
self.assertFalse(self._tx(self._button_msg(cancel=True, resume=True)))
self.assertFalse(self._tx(self._button_msg(cancel=False, resume=False)))
def test_jeep_brake_hold_resume_at_standstill_requires_safety_flag_and_main_on(self):
if self.__class__ is not TestChryslerSafety:
self.skipTest("Jeep brake hold applies to base Chrysler/Jeep safety only")
self._rx(self._speed_msg(0))
self._rx(self._main_on_msg(True))
self.safety.set_controls_allowed(False)
self.assertFalse(self._tx(self._button_msg(resume=True)))
self.safety.set_safety_hooks(CarParams.SafetyModel.chrysler, ChryslerSafetyFlags.JEEP_BRAKE_HOLD)
self.safety.init_tests()
self._rx(self._speed_msg(0))
self._rx(self._main_on_msg(True))
self.assertTrue(self._tx(self._button_msg(resume=True)))
self._rx(self._user_gas_msg(1))
self.assertFalse(self._tx(self._button_msg(resume=True)))
self._rx(self._user_gas_msg(0))
self._rx(self._user_brake_msg(True))
self.assertFalse(self._tx(self._button_msg(resume=True)))
self._rx(self._user_brake_msg(False))
self._rx(self._speed_msg(1))
self.assertFalse(self._tx(self._button_msg(resume=True)))
def test_jeep_brake_hold_das_3_requires_safety_flag_main_on_and_standstill(self):
if self.__class__ is not TestChryslerSafety:
self.skipTest("Jeep brake hold applies to base Chrysler/Jeep safety only")
self.assertFalse(self._tx(self._das_3_msg()))
self.safety.set_safety_hooks(CarParams.SafetyModel.chrysler, ChryslerSafetyFlags.JEEP_BRAKE_HOLD)
self.safety.init_tests()
self._rx(self._speed_msg(0))
self.assertFalse(self._tx(self._das_3_msg()))
self._rx(self._main_on_msg(True))
self.assertTrue(self._tx(self._das_3_msg()))
self._rx(self._user_gas_msg(1))
self.assertFalse(self._tx(self._das_3_msg()))
self._rx(self._user_gas_msg(0))
self._rx(self._user_brake_msg(True))
self.assertFalse(self._tx(self._das_3_msg()))
self._rx(self._user_brake_msg(False))
self._rx(self._speed_msg(1))
self.assertFalse(self._tx(self._das_3_msg()))
def _toggle_aol(self, toggle_on):
# DAS_3, bit 20 is ACC_AVAILABLE
values = {"ACC_AVAILABLE": 1 if toggle_on else 0}
+18 -6
View File
@@ -76,6 +76,7 @@ class TestFordSafetyBase(common.CarSafetyTest):
STEER_MESSAGE = 0
# Curvature control limits
LKA_STEERING = False
DEG_TO_CAN = 50000 # 1 / (2e-5) rad to can
MAX_CURVATURE = 0.02
MAX_CURVATURE_ERROR = 0.002
@@ -354,12 +355,13 @@ class TestFordSafetyBase(common.CarSafetyTest):
self._set_prev_desired_angle(sign * (curvature_offset + initial_curvature))
self.assertEqual(should_tx, self._tx(self._lat_ctl_msg(True, 0, 0, sign * (curvature_offset + desired_curvature), 0)))
def test_prevent_lkas_action(self):
self.safety.set_controls_allowed(1)
self.assertFalse(self._tx(self._lkas_command_msg(1)))
self.safety.set_controls_allowed(0)
self.assertFalse(self._tx(self._lkas_command_msg(1)))
def test_lkas_action(self):
for controls_allowed in (0, 1):
self.safety.set_controls_allowed(controls_allowed)
for action in range(8):
should_tx = action == 0
should_tx |= self.LKA_STEERING and controls_allowed and action in (2, 4)
self.assertEqual(should_tx, self._tx(self._lkas_command_msg(action)))
def test_acc_buttons(self):
for allowed in (0, 1):
@@ -484,6 +486,16 @@ class TestFordLongitudinalSafety(TestFordLongitudinalSafetyBase):
pass
class TestFordLKASteeringSafety(TestFordLongitudinalSafety):
LKA_STEERING = True
def setUp(self):
self.packer = CANPackerSafety("ford_lincoln_base_pt")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(CarParams.SafetyModel.ford, FordSafetyFlags.LKA_STEERING)
self.safety.init_tests()
class TestFordCANFDLongitudinalSafety(TestFordLongitudinalSafetyBase):
STEER_MESSAGE = MSG_LateralMotionControl2
+3 -3
View File
@@ -346,10 +346,10 @@ class TestGmCameraNoCameraSafety(TestGmCameraSafety):
class TestGmCameraLongitudinalSafety(GmLongitudinalBase, TestGmCameraSafetyBase):
TX_MSGS = [[0x180, 0], [0x315, 0], [0x2CB, 0], [0x370, 0], [0x200, 0], [0x1E1, 0], [0x3D1, 0], [0xBD, 0], [0x1F5, 0], # pt bus
TX_MSGS = [[0x180, 0], [0x315, 0], [0x2CB, 0], [0x2CD, 0], [0x370, 0], [0x200, 0], [0x1E1, 0], [0x3D1, 0], [0xBD, 0], [0x1F5, 0], # pt bus
[0x184, 2]] # camera bus
FWD_BLACKLISTED_ADDRS = {2: [0x180, 0x2CB, 0x370, 0x315], 0: [0x184]} # block LKAS, ACC messages and PSCMStatus
RELAY_MALFUNCTION_ADDRS = {0: (0x180, 0x2CB, 0x370, 0x315), 2: (0x184,)}
FWD_BLACKLISTED_ADDRS = {2: [0x180, 0x2CB, 0x2CD, 0x370, 0x315], 0: [0x184]} # block LKAS, ACC messages and PSCMStatus
RELAY_MALFUNCTION_ADDRS = {0: (0x180, 0x2CB, 0x2CD, 0x370, 0x315), 2: (0x184,)}
BUTTONS_BUS = 0 # rx only
MAX_GAS = 2698

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