Compare commits

...

298 Commits

Author SHA1 Message Date
firestarsdog 535ae098c6 BigUI WIP: One more 2026-06-29 05:53:57 -04:00
firestarsdog 0b26d1c050 BigUI WIP: Start of Lateral design pass 2026-06-29 05:41:23 -04:00
firestarsdog 7d52c80bf4 BigUI WIP: delete this random blue? 2026-06-29 04:10:55 -04:00
firestarsdog 50630f121c BigUI WIP: System Panel Cleanup 2026-06-29 03:53:12 -04:00
firestarsdog f26c89d6cb SLC: No whammy? 2026-06-29 03:10:59 -04:00
firestar5683 e88854319c PlotWranglerPro 2026-06-28 22:49:08 -05:00
firestar5683 2e53b419d1 sonata hybrid 2026-06-28 22:05:55 -05:00
firestar5683 f4bc8ee6b7 ev9 2026-06-28 20:20:47 -05:00
firestar5683 6daefdc0e1 versioning 2026-06-28 20:04:02 -05:00
firestar5683 96e578fa71 pri pri 2 2026-06-28 16:35:14 -05:00
firestar5683 768a2f324c Mici CSC Icon 2026-06-28 16:22:40 -05:00
firestar5683 f50b8dabbb ev9 2026-06-28 15:52:01 -05:00
firestar5683 d9e0345a2b runtime 2026-06-28 15:51:35 -05:00
firestar5683 8a505e6677 ui 2026-06-28 15:31:42 -05:00
firestar5683 40b5468615 More modeld cleanup 2026-06-28 15:03:09 -05:00
firestar5683 f7418649f8 beeg 2026-06-28 13:30:15 -05:00
firestar5683 5d3b0f4ea9 Update model_compiler.py 2026-06-28 13:17:09 -05:00
firestar5683 7e773e5373 Update model_compiler.py 2026-06-28 13:10:30 -05:00
firestar5683 6d088863a6 no pp 2026-06-28 13:05:58 -05:00
firestar5683 6b515db122 build 2026-06-28 12:50:10 -05:00
firestar5683 bdc1532258 App / Version 2026-06-28 12:44:58 -05:00
firestarsdog afe15a9a80 BigUI WIP: Disablement of more vert scroll 2026-06-28 03:54:02 -04:00
firestarsdog 02060a4b67 BigUI WIP: Hide descriptions 2026-06-28 03:46:39 -04:00
firestarsdog efd25eca90 BigUI WIP: Weird squish fix 2026-06-28 03:01:14 -04:00
firestarsdog b4f17f35f9 BigUI WIP: Toggle tile panel standardization PT55 2026-06-28 02:59:08 -04:00
firestar5683 a0669247e3 ANGEL 2026-06-28 00:56:51 -05:00
firestar5683 be811cbe61 ev9 2026-06-28 00:30:14 -05:00
firestar5683 460b61ebe8 Shenanigans 2026-06-28 00:13:13 -05:00
firestar5683 7411a11f83 pytest 2026-06-27 23:58:18 -05:00
firestar5683 a7ad72489b pri pri 2026-06-27 23:45:56 -05:00
firestar5683 e82a4f034b Konik Tooling 2026-06-27 23:38:32 -05:00
firestar5683 a8ec45ec12 --cem 2026-06-27 22:58:38 -05:00
firestar5683 0df2444e95 mici branch switcher 2026-06-27 22:43:12 -05:00
firestarsdog 32cf065988 BigUI WIP: Nix the Caps 2026-06-27 23:36:11 -04:00
firestarsdog 41c680e60c BigUI WIP: Center Breadcrumb Bar text, was triggering 2026-06-27 23:22:54 -04:00
firestar5683 fac7554cb6 I can be ur angle 2026-06-27 22:17:04 -05:00
firestarsdog 92bc8ee934 BigUI WIP: The Starry Night 2026-06-27 23:12:51 -04:00
firestar5683 9c30fc2387 Plexy 2026-06-27 21:55:44 -05:00
firestar5683 3f5dc27170 FERG 2026-06-27 20:47:54 -05:00
firestarsdog a5fb9a2cff BigUI WIP: Prettier Bread 2026-06-27 21:37:10 -04:00
firestarsdog 201d0c0476 BigUI WIP: Standardize Breadcrumbs 2026-06-27 18:31:33 -04:00
firestar5683 54e722ff00 build 2026-06-27 16:02:53 -05:00
firestar5683 fb1c2ea1c4 spicy 2026-06-27 15:57:38 -05:00
firestar5683 0f43f042fb nope 2026-06-27 15:37:25 -05:00
firestar5683 ee1f0bbe83 nah
pan
2026-06-27 15:23:35 -05:00
firestar5683 b811825b38 slither 2026-06-27 14:36:52 -05:00
firestar5683 ee71b70f24 build 2026-06-27 13:58:30 -05:00
firestar5683 b75b21681d Madlad stuff 2026-06-27 13:51:16 -05:00
firestar5683 562c5eb58d long boi 2026-06-27 13:30:18 -05:00
firestar5683 2798c12c90 build 2026-06-27 13:19:46 -05:00
firestar5683 19d545b22e Operation Bigfoot 2026-06-27 13:06:03 -05:00
firestar5683 36e935c9dc nah 2026-06-27 00:18:18 -05:00
firestar5683 cf89678178 driver monitoring 2026-06-26 21:40:10 -05:00
firestar5683 88e4004c9a Stop The Pain 2026-06-26 16:52:28 -05:00
firestar5683 9f0ae1a495 2019 ascm esv 2026-06-26 16:49:02 -05:00
firestar5683 f5a5b9e141 lat control refactor 2026-06-26 16:32:15 -05:00
firestar5683 057ca874ce Dom's Plan 2026-06-26 16:02:47 -05:00
firestar5683 497eb3f771 build 2026-06-26 15:35:10 -05:00
firestar5683 284bb45c0c that's alot 2026-06-26 15:29:52 -05:00
firestar5683 836b0278eb stuff 2026-06-26 12:53:23 -05:00
whoisdomi 4ce9be2752 Bug Fix: Blinker disregards force stop
Prior commit inteneded for blinker to veto force stop from activating during a turn/curves.
If blinker was on, it was vetoing actual force stops. This tweaks is so the veto only works
on actual turns, or if force stop was already on. So force stop will stop, then allow turn without reactivating force stop.
2026-06-26 06:10:13 -05:00
firestarsdog 24fc6fe4e5 BigUI WIP: More Standarization 2026-06-26 04:16:26 -04:00
firestar5683 297ff1973d Don't Brake Check 2026-06-26 02:18:55 -05:00
firestarsdog 727985697f BigUI WIP: Adaptive Nested Panel 2026-06-26 03:14:11 -04:00
firestar5683 59d1581c0a pedal away from the metal 2026-06-26 01:50:06 -05:00
firestarsdog 3a86dffa17 BigUI WIP: Guess this needs a back button 2026-06-26 01:53:15 -04:00
firestarsdog 9f7dcb68f9 BigUI WIP: Add yeast 2026-06-26 01:27:14 -04:00
firestar5683 6793c1a4f5 build 2026-06-25 23:31:56 -05:00
firestar5683 f351dbac09 Lists A Plenty 2026-06-25 23:26:03 -05:00
firestarsdog 44255d67c2 BigUI WIP: Standarize Breadcrumbs 2026-06-25 22:48:37 -04:00
firestarsdog 1ce49be89b BigUI WIP: Standardize single pane toggle tiles 2026-06-25 18:36:17 -04:00
firestar5683 bdf7bca569 Lite Brite 2026-06-25 10:54:58 -05:00
firestar5683 d8e292dc94 build 2026-06-25 00:00:42 -05:00
firestar5683 f0601e27ec The Plum 2026-06-24 23:54:45 -05:00
firestar5683 cec9f090c4 Bolt Pedal Friction Tune 2026-06-24 22:51:42 -05:00
firestar5683 d08da1ff22 volt opd adjust 2026-06-24 22:31:44 -05:00
firestar5683 58b028cfc8 wumbo 2026-06-24 19:37:49 -05:00
firestar5683 d7f3037c88 build 2026-06-24 14:35:53 -05:00
firestar5683 028ee7b3f5 UpstreamStructs 2026-06-24 14:06:16 -05:00
firestar5683 94aff6faed i6 2026-06-24 13:24:28 -05:00
firestar5683 b63039ce84 i6 2026-06-24 12:55:11 -05:00
firestarsdog aec5590d57 BigUI WIP: New Appearance Icon 2026-06-24 03:07:42 -04:00
firestarsdog abd19df5f4 BigUI WIP: Scale up speaker 2026-06-24 02:33:01 -04:00
firestarsdog b1dae43ea8 BigUI WIP: Bell -> Speaker 2026-06-24 02:25:18 -04:00
firestarsdog 8c32f6d99d BigUI WIP: Add some graphics 2026-06-24 02:13:48 -04:00
firestarsdog f6b51e6742 BigUI WIP: More purples 2026-06-24 01:25:34 -04:00
firestarsdog d3881e6a77 BigUI WIP: Sound Panel Polish 2026-06-24 00:47:06 -04:00
firestarsdog d83b065c53 BigUI WIP: Container Store 2026-06-23 23:50:52 -04:00
firestar5683 16c3d5f48a Tom Bombadil 2026-06-23 21:45:36 -05:00
firestar5683 d6dc906ff8 mica 2026-06-23 20:00:29 -05:00
firestar5683 ea0ef444de The Domi Effect 2026-06-23 18:36:47 -05:00
firestar5683 ea1da70b22 lacrosse test 2026-06-23 18:18:14 -05:00
firestar5683 ad9aa72979 slc 2026-06-23 18:16:06 -05:00
firestar5683 1382200cda plexy and noah 2026-06-23 18:00:13 -05:00
firestar5683 7e8832d672 Pacifica mod support 2026-06-23 13:12:32 -05:00
firestar5683 9cd9a6201c build 2026-06-23 12:09:37 -05:00
firestar5683 6ee00d3f60 Support multipart model artifacts 2026-06-23 12:01:45 -05:00
firestar5683 d97100bd14 tiny my BUTT 2026-06-23 12:01:44 -05:00
firestar5683 bb36fe4287 fartlek 2026-06-23 11:28:17 -05:00
firestar5683 066a0f2520 Tibetan Singing Bowl 2026-06-22 23:37:15 -05:00
firestar5683 c5a6e1da9d Blaze It and Praise It 2026-06-22 19:45:11 -05:00
firestar5683 f8cec6f270 Long Blong 2026-06-22 19:35:51 -05:00
firestar5683 544979d652 EV6 GT 2026-06-22 19:27:43 -05:00
firestar5683 353672fb6a Domi's Steer Max Fix 2026-06-22 17:19:21 -05:00
firestar5683 4101ab9b28 Doms Plan 2026-06-22 17:18:48 -05:00
firestar5683 7fb1d854c6 Revert "Dom(i)'s Steer Max"
This reverts commit ad3bad8b85.
2026-06-22 16:55:53 -05:00
firestar5683 d8587bba31 longy long long 2026-06-22 16:55:49 -05:00
firestar5683 ad3bad8b85 Dom(i)'s Steer Max 2026-06-22 16:42:11 -05:00
firestar5683 c91ac533ef is it hi-yun-dae? 2026-06-21 22:19:04 -05:00
firestar5683 32a6af9ac8 Truckothy 2026-06-21 22:10:37 -05:00
firestar5683 e84c0e44ab The Triple Dipper 2026-06-21 21:19:56 -05:00
firestar5683 d28cd4df8b lil rope a dope 2026-06-21 20:48:27 -05:00
firestarsdog cfd762f0a2 BigUI WIP: Bread 2026-06-21 20:47:11 -04:00
firestarsdog 16d6af490a BigUI WIP: Title Standards 2026-06-21 20:39:13 -04:00
firestar5683 0b37a23c75 new stuf 2026-06-21 17:00:32 -05:00
firestarsdog bf8204e9a2 BigUI WIP: Hänsel und Gretel
BigUI WIP: Hänsel und Gretel
2026-06-21 05:57:30 -04:00
firestar5683 34813dee41 cap'n crunch 2026-06-20 23:04:18 -05:00
firestar5683 d84518b97f AYYYEE TOYOTA 2026-06-20 21:26:46 -05:00
firestar5683 2356823b62 build 2026-06-20 20:52:51 -05:00
firestar5683 ce98b31cbf Tyger Tyger Burning Bright 2026-06-20 20:49:05 -05:00
firestar5683 97881b5c66 The fields speak to me 2026-06-20 18:04:39 -05:00
firestarsdog fc90fcb879 BigUI WIP: Gas/Brake Pass 1 2026-06-20 16:47:05 -04:00
firestar5683 762c54bb2f build 2026-06-20 14:41:37 -05:00
firestar5683 9910651a9d Carl The Witch 2026-06-20 14:38:56 -05:00
firestar5683 42a23508b7 RECOVER 2026-06-20 13:08:23 -05:00
firestar5683 fbb6eb651c twuck 2026-06-20 12:33:29 -05:00
firestar5683 e4809535dc build 2026-06-20 01:26:51 -05:00
firestar5683 63490fe5e6 one pedal update 2026-06-20 01:26:51 -05:00
firestar5683 a7b7148c25 2019 hold 2026-06-20 00:05:25 -05:00
firestar5683 e50874baca dash 2026-06-20 00:00:35 -05:00
firestar5683 ef4f174c8d AHHHH 2026-06-19 23:44:41 -05:00
firestar5683 9c726d99de Revert "THE SMOKING GUN"
This reverts commit 25a50a2ec3.
2026-06-19 23:38:09 -05:00
firestar5683 25a50a2ec3 THE SMOKING GUN 2026-06-19 23:31:38 -05:00
firestar5683 5d3d5a884b a;oiwht 2026-06-19 23:05:28 -05:00
firestar5683 7a3c1ede9a The tone is in the tune 2026-06-19 22:39:05 -05:00
firestar5683 d931d3007b build 2026-06-19 21:13:24 -05:00
firestar5683 05c2a90e16 lacrosse 2026-06-19 21:10:41 -05:00
firestar5683 dc581f6300 Revert "Now Mary's In"
This reverts commit 0bc009f800.
2026-06-19 21:01:51 -05:00
firestar5683 a6c5b43caf Revert "maybe"
This reverts commit d8b715799c.
2026-06-19 21:01:18 -05:00
firestar5683 3ec82de5fc Revert "again"
This reverts commit cd08c87a80.
2026-06-19 21:01:18 -05:00
firestar5683 cd08c87a80 again 2026-06-19 17:33:49 -05:00
firestar5683 d8b715799c maybe 2026-06-19 17:20:32 -05:00
firestar5683 0bc009f800 Now Mary's In 2026-06-19 16:53:59 -05:00
firestarsdog ff49f332b0 BigUI WIP: Shes glowing 2026-06-19 17:25:30 -04:00
firestarsdog f04b561f88 BigUI WIP: Russian Nesting Tiles 2026-06-19 17:16:36 -04:00
firestar5683 1f237019e7 Byte You In The 2026-06-19 14:16:01 -05:00
firestar5683 b47c18426d Steam Your Water 2026-06-19 14:14:07 -05:00
firestar5683 0e5d40fb1b Angler Wish 2026-06-19 13:48:30 -05:00
firestarsdog 496d36f805 BigUI WIP: Font_scale fix 2026-06-19 11:12:39 -04:00
firestar5683 9217bf289c build 2026-06-19 00:44:56 -05:00
firestar5683 2c2d8b86ab Drain The Swamp 2026-06-19 00:39:48 -05:00
firestar5683 c8128d2cde Annie Are You Okay 2026-06-18 23:48:23 -05:00
firestar5683 9c85ebc365 The Triple Dipper 2026-06-18 16:55:42 -05:00
firestar5683 8c510f964c Offroad 2026-06-18 14:04:36 -05:00
firestar5683 35e623c392 Dang Imperials 2026-06-18 13:51:58 -05:00
firestar5683 5eb6fe2dd9 Kachow 2026-06-18 13:36:40 -05:00
firestar5683 64bb95e3cf Applebees 2026-06-18 12:20:17 -05:00
firestar5683 96818d8efc The Time Turner 2026-06-18 10:58:09 -05:00
firestar5683 c23434b947 Is This A Date? 2026-06-18 10:54:25 -05:00
firestar5683 2a0053c046 build 2026-06-18 10:27:12 -05:00
firestar5683 f1a256dac0 Don't Kill Suave 2026-06-18 10:19:06 -05:00
firestar5683 967fb96167 build 2026-06-18 10:09:53 -05:00
firestar5683 bb4f3b74d9 There's Pou in this Tine 2026-06-18 10:07:26 -05:00
firestar5683 7dab3a0ceb build 2026-06-17 23:32:03 -05:00
firestar5683 e6b12d612a Agent Cody Banks 2026-06-17 23:29:00 -05:00
firestarsdog d17a4bd6cb BigUI WIP: Appear 2026-06-17 22:05:45 -04:00
firestar5683 540becea52 Mica's cable shipped 2026-06-17 11:49:14 -05:00
firestar5683 a71be7c47b long plan 2026-06-17 11:24:45 -05:00
firestar5683 7305822e67 leedle leedle leedle lee 2026-06-17 11:16:00 -05:00
firestar5683 6b0e5aed24 I did the backstroke in college 2026-06-16 21:19:57 -05:00
firestar5683 6f907e2060 final plan 2026-06-16 18:58:49 -05:00
firestar5683 29b777f12c fremulon 2026-06-16 18:40:22 -05:00
firestar5683 335d6934fb plimothy 2026-06-16 18:31:36 -05:00
firestar5683 60305edeae niro phev 2026-06-16 10:34:30 -05:00
firestarsdog da305d9da3 BigUI WIP: Lil scrolling cleanup 2026-06-16 03:42:22 -04:00
firestarsdog 624f51aef4 BigUI WIP: Pagination out of bounds dragging fix 2026-06-16 03:10:39 -04:00
firestarsdog ba76832979 BigUI WIP: Pagination/Page Turning UX 2026-06-16 02:54:55 -04:00
firestarsdog 49126f29ae BigUI WIP: Apperance -> Aethergrid start 2026-06-16 02:47:30 -04:00
firestar5683 2ed92c88ae build 2026-06-16 00:00:25 -05:00
firestar5683 a9771a0e85 Supernova 2026-06-15 23:58:00 -05:00
firestar5683 1dcac8cfef Tiny Dancer 2 2026-06-15 22:25:18 -05:00
firestarsdog 18ea1dd170 BigUI WIP : AetherMultiSelect Tile + Dialog 2026-06-15 22:28:44 -04:00
firestarsdog 59336096e7 BigUI WIP: Out with the old 2026-06-15 21:27:06 -04:00
firestarsdog 57d7796edb BigUI WIP: Panel standardization 2026-06-15 21:15:52 -04:00
firestar5683 2dc441d621 build 2026-06-15 16:35:06 -05:00
firestar5683 93ec2a94e6 NudgeMe 2026-06-15 16:32:44 -05:00
firestar5683 56564d55c8 REEEEEE 2026-06-15 16:27:29 -05:00
firestar5683 322364c1ca plan stan 2026-06-15 14:47:21 -05:00
firestar5683 838c7fdc9b fixes 2026-06-15 11:30:41 -05:00
firestar5683 e02d67c153 gaslight2 2026-06-15 01:14:20 -05:00
firestar5683 407e2ea90a refreshing 2026-06-15 01:03:31 -05:00
firestar5683 712256eac4 build 2026-06-15 00:47:13 -05:00
firestar5683 1c2aedcc9a gaslight 2026-06-15 00:45:06 -05:00
firestar5683 9af2969540 My Eyes Are Up Here 2026-06-15 00:40:07 -05:00
firestar5683 8a7946b26c pond 2026-06-15 00:17:25 -05:00
firestar5683 53e0aded4c build 2026-06-15 00:06:07 -05:00
firestar5683 3ecd8be1f1 Raindrops on Roses & Whiskers on Kittens 2026-06-15 00:03:44 -05:00
firestar5683 0262b934c4 dumb 2026-06-14 21:18:13 -05:00
firestar5683 9aeaf593c3 son day I'll get this 2026-06-14 21:02:31 -05:00
firestar5683 00c64ba805 Zikeji Tales 2026-06-14 19:26:56 -05:00
firestar5683 7d939e1c76 planner work 2026-06-14 17:10:32 -05:00
firestar5683 8a4d2b558b radar vs voacc 2026-06-14 17:06:52 -05:00
Jason Jackrel 0508653a29 Volt tune from Thinkpad
Squash merge PR #63.
2026-06-14 16:42:36 -05:00
firestar5683 2e75486d16 planner work 2026-06-14 16:38:10 -05:00
firestar5683 7bd73401cd oopsie 2026-06-13 21:48:35 -05:00
firestar5683 4a107868cf build 2026-06-13 20:50:04 -05:00
firestar5683 53766ff6b7 The Forked Man 2026-06-13 20:47:15 -05:00
firestar5683 4ee6758b9f XCeeding expectations 2026-06-13 20:46:30 -05:00
firestar5683 4c1317e30d Mom's Spaghetti 2026-06-13 20:45:52 -05:00
firestar5683 8a68dec71f Swell Tune 2026-06-13 20:43:51 -05:00
firestar5683 c7efa7fa20 Two is not better than one 2026-06-13 20:42:50 -05:00
firestarsdog bd7394ea33 BigUI WIP: Tile updates 2026-06-13 11:56:21 -04:00
firestar5683 fa7fa6afaf Remove Slow Logic atm 2026-06-12 23:09:31 -05:00
firestar5683 491ec0c0b8 the slow mo guys 2026-06-12 10:50:14 -05:00
firestar5683 87f1334ce9 yes 2026-06-12 10:22:03 -05:00
firestar5683 140b047e7f better raylib 2026-06-12 01:34:58 -05:00
firestar5683 21b1bfd600 THIS DESERVES A BUMP 2026-06-11 23:04:34 -05:00
firestar5683 5da99bc977 build 2026-06-11 22:32:31 -05:00
firestar5683 a30dc40b8a wumbology 2026-06-11 22:30:21 -05:00
firestar5683 96893d9c36 Dingle's got dragonclaw?! 2026-06-11 19:36:18 -05:00
firestarsdog 9be0defc29 The speed of cyan 2026-06-11 18:07:30 -04:00
firestar5683 afc8c6fb4b Mr. Freeze 2026-06-11 11:34:12 -05:00
firestar5683 b559b0f827 Fridge Cigarette 2026-06-11 10:51:05 -05:00
firestar5683 79e31ff241 TRY AGAIN, SPIDERMAN 2026-06-10 22:30:48 -05:00
firestar5683 1e604fd80e loopback 2026-06-10 19:11:02 -05:00
firestar5683 56989a164e update 2026-06-10 19:01:20 -05:00
firestar5683 eaf5d44037 forte he 2026-06-10 15:54:35 -05:00
firestarsdog 93cb562321 BigUI WIP: Larger for the Dads 2026-06-10 15:49:25 -04:00
firestar5683 31d3df673a Kia Xceed 2026-06-10 11:55:20 -05:00
firestar5683 c0aa7a7460 oops 2026-06-10 10:54:28 -05:00
firestar5683 498b7f14a9 build 2026-06-10 10:50:40 -05:00
firestar5683 95b6ecec86 Hello Poppet 2026-06-10 10:48:32 -05:00
firestarsdog 8e6fc9bed3 BigUI WIP: Page flipping is tough... 2026-06-10 02:20:14 -04:00
firestarsdog cf7822180b BigUI WIP: Aethergrid Update 2026-06-10 00:07:00 -04:00
firestar5683 f93c536f7e deeprl3 2026-06-09 22:58:07 -05:00
firestarsdog d555d1f588 BigUP WIP: Style 2026-06-09 23:50:01 -04:00
firestar5683 88e297b183 offsets 2026-06-09 22:21:48 -05:00
firestar5683 37f5c8ca58 Navigation: Breaking Dawn 2026-06-09 21:19:08 -05:00
firestarsdog 7d53bd5af7 BigUI WIP: LED 2026-06-09 21:30:52 -04:00
firestar5683 455a8ec11d XT4 Sigmoid 2026-06-09 17:02:27 -05:00
firestar5683 dac0610a19 build 2026-06-09 16:47:33 -05:00
firestar5683 da5b3f829f black pink in your area2 2026-06-09 16:47:28 -05:00
firestarsdog 02960fc8c2 SLC allow override to persist if within obtained speed 2026-06-09 17:26:25 -04:00
firestar5683 4585938753 build 2026-06-09 15:42:23 -05:00
firestar5683 f777e71c56 Black / Pink in your area 2026-06-09 15:39:56 -05:00
firestar5683 0a698d202b build 2026-06-09 14:13:36 -05:00
firestar5683 63db39082b Mount Wannahockaloogie 2026-06-09 14:11:13 -05:00
firestarsdog 4f48c2ec27 BigUI WIP: Lateral Spateral 2026-06-09 13:13:55 -04:00
firestarsdog 6faf9bd88a BigUI WIP: Weird scrolling 2026-06-09 01:52:23 -04:00
firestarsdog a624cf89bb BigUI WIP: Remove pagenation from system settings 2026-06-09 01:41:27 -04:00
firestarsdog e836ed09c8 BigUI WIP: Optional Pagenation 2026-06-09 01:37:08 -04:00
firestarsdog 8cc9c59330 BigUI WIP: Some panel standarization 2026-06-09 00:00:33 -04:00
firestar5683 a11d064aeb buttan 2026-06-08 22:38:57 -05:00
firestar5683 77c82edb44 sonata hybrid 2026-06-08 20:55:23 -05:00
firestar5683 9a9ac3af77 navstate 2026-06-08 20:30:44 -05:00
firestar5683 ac8511b211 bsm 2026-06-08 20:10:33 -05:00
firestar5683 c3ff2e86db build 2026-06-08 19:57:20 -05:00
firestar5683 98d6a4eac0 chill takeoff 2026-06-08 19:57:20 -05:00
firestar5683 5f413b967e odyss 2026-06-08 19:57:20 -05:00
firestar5683 bdb0da4fa0 suburu sng 2026-06-08 19:57:20 -05:00
firestarsdog cabacda0ad BigUI WIP: mono 2026-06-08 20:48:20 -04:00
firestar5683 d8c79a9712 build 2026-06-08 19:19:19 -05:00
firestar5683 88c259a666 Prioritize Smooth Follow 2026-06-08 19:15:50 -05:00
firestarsdog 796d081fce BigUI WIP: More prints of the finger stuff 2026-06-08 19:16:53 -04:00
firestar5683 6d61feff11 rlonlylat 2026-06-07 21:59:32 -05:00
firestar5683 5d0ab0cfb0 First time in San Juan mi hijo 2026-06-07 21:51:36 -05:00
firestar5683 6c687a9f5c build 2026-06-07 21:16:08 -05:00
firestar5683 7a17c21fab Truck V2 2026-06-07 21:13:52 -05:00
firestar5683 1994a0bd16 build 2026-06-07 20:53:37 -05:00
firestar5683 8a1bfb925c odyss 2026-06-07 20:51:27 -05:00
firestar5683 914411fe06 build 2026-06-07 20:43:19 -05:00
firestar5683 c88bebbc3e Zik is my boss 2026-06-07 20:37:47 -05:00
firestar5683 aba65bf755 build 2026-06-07 20:18:52 -05:00
firestar5683 cf1c99cf78 Haberdashery 2026-06-07 20:16:35 -05:00
firestar5683 9c51a6024e build 2026-06-07 15:21:10 -05:00
firestar5683 bec246c6ef Clear Nav 2026-06-07 15:18:56 -05:00
firestar5683 eeab4b8c85 Build 2026-06-07 15:04:23 -05:00
firestar5683 a6758eb9e6 New stoofs 2026-06-07 15:01:59 -05:00
firestar5683 cdcc8da555 sm 2026-06-07 14:26:14 -05:00
firestar5683 1f9aa83440 buttons 2026-06-07 14:26:14 -05:00
firestar5683 15d218677e build 2026-06-07 14:26:14 -05:00
firestar5683 f8934840da Conditional Chill 2026-06-07 14:26:14 -05:00
firestarsdog a888c5602a BigUI WIP: Prob don't need 2026-06-07 05:04:30 -04:00
firestar5683 4134da76ca volt probz 2026-06-06 20:49:28 -05:00
firestar5683 24b0f37ae7 hda1 2026-06-06 19:31:04 -05:00
firestar5683 559a738fe7 build 2026-06-06 17:52:05 -05:00
firestar5683 f20b1f2a4f mochi mochi mochi 2026-06-06 17:49:41 -05:00
firestar5683 c76b770807 plan takeoff 2026-06-06 14:47:33 -05:00
firestarsdog 3c0dea5747 Raylib UI : Global fingerprint_catalog 2026-06-06 14:53:43 -04:00
firestar5683 e47fbd1cb9 build 2026-06-06 12:08:50 -05:00
firestar5683 efdc48980d defaults 2026-06-06 12:03:35 -05:00
firestarsdog f84f7d4f17 Replay Controls 2026-06-05 23:29:34 -04:00
firestar5683 aa82ae0bd0 build 2026-06-05 22:10:00 -05:00
Michael Tawata 743ff67927 no fun allowed here 2026-06-05 22:06:48 -05:00
firestarsdog 2880189713 BigUI WIP: Pushpop Wires 2026-06-05 18:31:50 -04:00
firestarsdog 2847d0fd8a BigUI WIP: oplong push_widget 2026-06-05 18:20:19 -04:00
firestarsdog c23aeb9fcd BigUI WIP: Remove puke 2026-06-05 15:02:52 -04:00
firestarsdog 980c85cfb1 BigUI WIP: Vehicle Panel state update 2026-06-05 14:58:28 -04:00
1302 changed files with 221734 additions and 222437 deletions
@@ -165,6 +165,10 @@ jobs:
"panda/board/obj/panda_h7.bin.signed"
"panda/board/obj/panda_remote.bin.signed"
"panda/board/obj/panda_h7_remote.bin.signed"
"panda/board/obj/panda_can_ignition_only.bin.signed"
"panda/board/obj/panda_h7_can_ignition_only.bin.signed"
"panda/board/obj/panda_remote_can_ignition_only.bin.signed"
"panda/board/obj/panda_h7_remote_can_ignition_only.bin.signed"
"panda/board/obj/panda_jungle_h7.bin.signed"
"panda/board/obj/body_h7.bin.signed"
)
+1
View File
@@ -68,6 +68,7 @@ cppcheck_report.txt
comma*.sh
selfdrive/modeld/models/*.pkl
!selfdrive/modeld/models/driving_tinygrad.pkl
!selfdrive/modeld/models/driving_vision_tinygrad.pkl
!selfdrive/modeld/models/driving_policy_tinygrad.pkl
!selfdrive/modeld/models/driving_vision_metadata.pkl
+22 -2
View File
@@ -91,6 +91,13 @@ struct StarPilotCarState @0xf35cc4560bbf6ec2 {
cancelPressed @20 :Bool;
cancelLongPressed @21 :Bool;
cancelVeryLongPressed @22 :Bool;
pedalMaxRegen @23 :Bool; # pedal at max regen, driver should use brake for more decel
pedalLongActive @24 :Bool; # Pre-AP pedal longitudinal mode is active (enableLongControl)
teslaCCEngaged @25 :Bool; # rising edge of stock Tesla CC engaging (no-pedal mode)
teslaCCDisengaged @26 :Bool; # falling edge of stock Tesla CC
teslaCCNotArmed @27 :Bool; # lateral engaged but DI_cruiseState != STANDBY/ENABLED
accelHardCruise @28 :Bool; # current/releasing accel cruise button came from GM hard-press signal
decelHardCruise @29 :Bool; # current/releasing decel cruise button came from GM hard-press signal
}
struct StarPilotDeviceState @0xda96579883444c35 {
@@ -108,7 +115,11 @@ struct StarPilotModelDataV2 @0x80ae746ee2596b11 {
}
}
struct StarPilotOnroadEvent @0xa5cd762cd951a455 {
struct StarPilotOnroadEvents @0xa5cd762cd951a455 {
events @0 :List(StarPilotOnroadEvent);
}
struct StarPilotOnroadEvent @0xe344718567f9ce71 {
name @0 :EventName;
enable @1 :Bool;
@@ -158,6 +169,14 @@ struct StarPilotOnroadEvent @0xa5cd762cd951a455 {
switchbackModeInactive @30;
lkasEnable @31;
lkasDisable @32;
lateralManeuver @33;
pedalCruiseEnabled @34;
pedalCruiseDisabled @35;
pedalMaxRegen @36;
teslaCCEngaged @37;
teslaCCDisengaged @38;
teslaCCNotArmed @39;
pedalNotCalibrated @40;
}
}
@@ -259,7 +278,8 @@ struct CustomReserved9 @0xa1680744031fdb2d {
wallTimeNanos @5 :UInt64;
}
struct CustomReserved10 @0xcb9fd56c7057593a {
struct StarPilotLateralManeuverPlanDEPRECATED @0xcb9fd56c7057593a {
desiredCurvature @0 :Float32; # 1/m
}
struct CustomReserved11 @0xc2243c65e0340384 {
Binary file not shown.
+118 -27
View File
@@ -68,12 +68,12 @@ struct OnroadEvent @0xc4fa6047f024e718 {
longitudinalManeuver @30;
steerTempUnavailableSilent @31;
resumeRequired @32;
preDriverDistracted @33;
promptDriverDistracted @34;
driverDistracted @35;
preDriverUnresponsive @36;
promptDriverUnresponsive @37;
driverUnresponsive @38;
driverDistracted1 @33;
driverDistracted2 @34;
driverDistracted3 @35;
driverUnresponsive1 @36;
driverUnresponsive2 @37;
driverUnresponsive3 @38;
belowSteerSpeed @39;
lowBattery @40;
accFaulted @41;
@@ -130,14 +130,6 @@ struct OnroadEvent @0xc4fa6047f024e718 {
userBookmark @95;
excessiveActuation @96;
audioFeedback @97;
lateralManeuver @98;
pedalCruiseEnabled @99;
pedalCruiseDisabled @100;
pedalMaxRegen @101;
teslaCCEngaged @102;
teslaCCDisengaged @103;
teslaCCNotArmed @104;
pedalNotCalibrated @105;
soundsUnavailableDEPRECATED @47;
}
@@ -833,13 +825,30 @@ struct SelfdriveState {
alertStatus @5 :AlertStatus;
alertSize @6 :AlertSize;
alertType @7 :Text;
alertSound @8 :Car.CarControl.HUDControl.AudibleAlert;
alertSound @13 :AudibleAlert;
alertHudVisual @12 :Car.CarControl.HUDControl.VisualAlert;
# configurable driving settings
experimentalMode @10 :Bool;
personality @11 :LongitudinalPersonality;
enum AudibleAlert {
none @0;
engage @1;
disengage @2;
refuse @3;
warningSoft @4;
warningImmediate @5;
prompt @6;
promptRepeat @7;
promptDistracted @8;
preAlert @9;
}
enum OpenpilotState @0xdbe58b96d2d1ac61 {
disabled @0;
preEnabled @1;
@@ -860,6 +869,10 @@ struct SelfdriveState {
mid @2;
full @3;
}
deprecated :group {
alertSound @8 :Car.CarControl.HUDControl.AudibleAlert;
}
}
struct ControlsState @0x97ff69c53601abf1 {
@@ -1095,7 +1108,7 @@ struct ModelDataV2 {
confidence @23: ConfidenceClass;
# Model perceived motion
temporalPose @21 :Pose;
temporalPoseDEPRECATED @21 :Pose;
# e2e lateral planner
action @26: Action;
@@ -2179,7 +2192,6 @@ 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);
@@ -2221,7 +2233,7 @@ struct DriverStateDEPRECATED @0xb83c6cc593ed0a00 {
stdDEPRECATED @2 :Float32;
}
struct DriverMonitoringState @0xb83cda094a1da284 {
struct DriverMonitoringStateDEPRECATED @0xb83cda094a1da284 {
events @18 :List(OnroadEvent);
faceDetected @1 :Bool;
isDistracted @2 :Bool;
@@ -2239,12 +2251,90 @@ struct DriverMonitoringState @0xb83cda094a1da284 {
isActiveMode @16 :Bool;
isRHD @4 :Bool;
uncertainCount @19 :UInt32;
phoneProbOffset @20 :Float32;
phoneProbValidCount @21 :UInt32;
isPreviewDEPRECATED @15 :Bool;
rhdCheckedDEPRECATED @5 :Bool;
eventsDEPRECATED @0 :List(Car.OnroadEventDEPRECATED);
deprecated :group {
phoneProbOffset @20 :Float32;
phoneProbValidCount @21 :UInt32;
isPreview @15 :Bool;
rhdChecked @5 :Bool;
events @0 :List(Car.OnroadEventDEPRECATED);
}
}
struct DriverMonitoringState {
lockout @0 :Bool;
lockoutRecoveryPercent @11 :Int8;
alert3Count @12 :Int8;
noResponseCount @13 :Int8;
noResponseForceDecel @14 :Bool;
alwaysOn @3 :Bool;
alwaysOnLockout @4 :Bool;
alertLevel @5 :AlertLevel;
activePolicy @6 :MonitoringPolicy;
isRHD @7 :Bool;
rhdCalibration @8 :CalibrationState;
visionPolicyState @9 :VisionPolicyState;
wheeltouchPolicyState @10 :WheeltouchPolicyState;
enum AlertLevel {
# ordinal must match the name to prevent bugs
# comparing against the raw ordinal value
none @0;
one @1;
two @2;
three @3;
}
enum MonitoringPolicy {
wheeltouch @0;
vision @1;
}
struct VisionPolicyState {
awarenessPercent @0 :Int8;
awarenessStep @1 :Float32;
isDistracted @2 :Bool;
distractedTypes @3 :DistractedTypes;
faceDetected @4 :Bool;
pose @5 :Pose;
wheeltouchFallbackPercent @6 :Int8;
uncertainOffroadAlertPercent @7 :Int8;
struct DistractedTypes {
pose @0: Bool;
eye @1: Bool;
phone @2: Bool;
}
struct Pose {
pitch @0 :Float32;
yaw @1 :Float32;
pitchCalib @2 :CalibrationState;
yawCalib @3 :CalibrationState;
calibrated @4 :Bool;
uncertainty @5 :Float32;
}
}
struct WheeltouchPolicyState {
awarenessPercent @0 :Int8;
awarenessStep @1 :Float32;
driverInteracting @2 :Bool;
}
struct CalibrationState {
calibratedPercent @0 :Int8;
offset @1 :Float32;
}
deprecated :group {
alertCountLockoutPercent @1 :Int8;
alertTimeLockoutPercent @2 :Int8;
}
}
struct Boot {
@@ -2565,7 +2655,7 @@ struct Event {
thumbnail @66: Thumbnail;
onroadEvents @134: List(OnroadEvent);
carParams @69: Car.CarParams;
driverMonitoringState @71: DriverMonitoringState;
driverMonitoringState @151 :DriverMonitoringState;
livePose @129 :LivePose;
modelV2 @75 :ModelDataV2;
drivingModelData @128 :DrivingModelData;
@@ -2614,8 +2704,8 @@ struct Event {
userBookmark @93 :UserBookmark;
bookmarkButton @148 :UserBookmark;
audioFeedback @149 :AudioFeedback;
lateralManeuverPlan @150 :LateralManeuverPlan;
lateralManeuverPlan @150 :LateralManeuverPlan;
# *********** debug ***********
testJoystick @52 :Joystick;
roadEncodeData @86 :EncodeData;
@@ -2644,12 +2734,12 @@ struct Event {
starpilotCarState @109 :Custom.StarPilotCarState;
starpilotDeviceState @110 :Custom.StarPilotDeviceState;
starpilotModelV2 @111 :Custom.StarPilotModelDataV2;
starpilotOnroadEvents @112 :List(Custom.StarPilotOnroadEvent);
starpilotOnroadEvents @112 :Custom.StarPilotOnroadEvents;
starpilotPlan @113 :Custom.StarPilotPlan;
starpilotRadarState @114 :Custom.StarPilotRadarState;
starpilotSelfdriveState @115 :Custom.StarPilotSelfdriveState;
customReserved9 @116 :Custom.CustomReserved9;
customReserved10 @136 :Custom.CustomReserved10;
starpilotLateralManeuverPlanDEPRECATED @136 :Custom.StarPilotLateralManeuverPlanDEPRECATED;
customReserved11 @137 :Custom.CustomReserved11;
customReserved12 @138 :Custom.CustomReserved12;
customReserved13 @139 :Custom.CustomReserved13;
@@ -2707,5 +2797,6 @@ struct Event {
gyroscope2DEPRECATED @100 :SensorEventData;
accelerometer2DEPRECATED @101 :SensorEventData;
temperatureSensor2DEPRECATED @123 :SensorEventData;
driverMonitoringStateDEPRECATED @71 :DriverMonitoringStateDEPRECATED;
}
}
Binary file not shown.
+41 -12
View File
@@ -42,7 +42,9 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"ExperimentalLongitudinalEnabled", {PERSISTENT, BOOL}},
{"ExperimentalMode", {PERSISTENT, BOOL}},
{"ExperimentalModeConfirmed", {PERSISTENT, BOOL}},
{"PersistChillState", {PERSISTENT, BOOL, "0", "0", 1}},
{"PersistExperimentalState", {PERSISTENT, BOOL, "0", "0", 1}},
{"PersistedCCStatus", {PERSISTENT, INT, "0", "0"}},
{"PersistedCEStatus", {PERSISTENT, INT, "0", "0"}},
{"FirmwareQueryDone", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
{"ForcePowerDown", {PERSISTENT, BOOL}},
@@ -114,6 +116,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"PandaSomResetTriggered", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
{"PandaSignatures", {CLEAR_ON_MANAGER_START, BYTES}},
{"PrimeType", {PERSISTENT, INT}},
{"PriusClusterOffsetMigrated", {PERSISTENT, BOOL, "0", "0"}},
{"RecordAudio", {PERSISTENT, BOOL}},
{"RecordAudioFeedback", {PERSISTENT, BOOL, "0"}},
{"RecordFront", {PERSISTENT, BOOL}},
@@ -160,6 +163,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"AggressiveJerkSpeed", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"AggressiveJerkSpeedDecrease", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
{"AlertVolumeControl", {PERSISTENT, BOOL, "0", "0", 2}},
{"AllowImpossibleAcceleration", {PERSISTENT, BOOL, "0", "0", 3}},
{"AlwaysOnLateral", {PERSISTENT, BOOL, "1", "0", 0}},
{"AlwaysOnLateralLKAS", {PERSISTENT, BOOL, "1", "0", 2}},
{"ApiCache_DriveStats", {PERSISTENT, JSON, "{}", "{}"}},
@@ -168,6 +172,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"AvailableModelNames", {PERSISTENT, STRING, "", "", 1}},
{"AvailableModelSeries", {PERSISTENT, STRING, "", "", 1}},
{"AvailableModels", {PERSISTENT, STRING, "", "", 1}},
{"AvailableModelArtifactFormats", {PERSISTENT, STRING, "", "", 1}},
{"BlacklistedModels", {PERSISTENT, STRING, "", "", 2}},
{"BootLogo", {PERSISTENT, STRING, "starpilot", "stock", 0}},
{"BuildMetadata", {PERSISTENT, STRING, "", "", 0}},
@@ -178,6 +183,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"BorderWidth", {PERSISTENT, FLOAT, "100.0", "100.0", 2}},
{"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}},
{"CancelDownloadMaps", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"DisableWideRoad", {PERSISTENT, BOOL, "0", "0", 3}},
@@ -195,15 +201,22 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"CESlowerLead", {PERSISTENT, BOOL, "1", "0", 1}},
{"CESpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
{"CESpeedLead", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
{"CCMLead", {PERSISTENT, BOOL, "1", "0", 1}},
{"CCMLaunchAssist", {PERSISTENT, BOOL, "0", "0", 1}},
{"CCMSetSpeedMargin", {PERSISTENT, FLOAT, "3.0", "0.0", 1}},
{"CCMSpeed", {PERSISTENT, FLOAT, "45.0", "0.0", 1}},
{"CCMSpeedLead", {PERSISTENT, FLOAT, "35.0", "0.0", 1}},
{"CCStatus", {CLEAR_ON_OFFROAD_TRANSITION, INT, "0", "0"}},
{"CEStatus", {CLEAR_ON_OFFROAD_TRANSITION, INT, "0", "0"}},
{"CEStopLights", {PERSISTENT, BOOL, "1", "0", 1}},
{"CEStoppedLead", {PERSISTENT, BOOL, "0", "0", 1}},
{"ClusterOffset", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
{"ColorScheme", {PERSISTENT, STRING, "frog", "stock", 0}},
{"ColorScheme", {PERSISTENT, STRING, "stock", "stock", 0}},
{"ColorToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"BootLogoToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"Compass", {PERSISTENT, BOOL, "0", "0", 1}},
{"CommunityFavorites", {PERSISTENT, STRING, "", "", 1}},
{"ConditionalChill", {PERSISTENT, BOOL, "0", "0", 1}},
{"ConditionalExperimental", {PERSISTENT, BOOL, "1", "0", 1}},
{"CurvatureData", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
{"CurveSpeedController", {PERSISTENT, BOOL, "1", "0", 1}},
@@ -227,7 +240,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"AggressivePersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
{"StandardPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
{"RelaxedPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
{"CustomThemes", {PERSISTENT, BOOL, "1", "0", 0}},
{"CustomThemes", {PERSISTENT, BOOL, "0", "0", 0}},
{"CustomUI", {PERSISTENT, BOOL, "1", "0", 1}},
{"DebugMode", {CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0", "0"}},
{"DecelerationProfile", {PERSISTENT, INT, "1", "0", 2}},
@@ -274,6 +287,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"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"}},
@@ -301,10 +315,12 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"ForceStandstill", {PERSISTENT, BOOL, "0", "0", 2}},
{"ForceTorqueController", {PERSISTENT, BOOL, "0", "0", 3}},
{"FPSCounter", {PERSISTENT, BOOL, "1", "0", 3}},
{"GalaxyDashboardStats", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
{"StarPilotApiToken", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"StarPilotCarParams", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BYTES, "", ""}},
{"StarPilotCarParamsPersistent", {PERSISTENT, BYTES, "", ""}},
{"StarPilotDongleId", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"StarPilotFavoriteSlots", {PERSISTENT, JSON, "[]", "[]", 1}},
{"StarPilotStats", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
{"StarPilotTogglesUpdated", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"FrogsGoMoosTweak", {PERSISTENT, BOOL, "1", "0", 2}},
@@ -320,12 +336,14 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"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}},
{"IconPack", {PERSISTENT, STRING, "frog-animated", "stock", 0}},
{"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}},
@@ -367,12 +385,14 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"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, "9", "9", 2}},
{"MainCruiseButtonControl", {PERSISTENT, INT, "0", "0", 2}},
{"ManualUpdateInitiated", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
{"AMapKey1", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"AMapKey2", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"ApiCache_NavDestinations", {PERSISTENT, JSON, "[]", "[]"}},
{"FavoriteDestinations", {PERSISTENT, JSON, "[]", "[]"}},
{"MapAcceleration", {PERSISTENT, BOOL, "0", "0", 1}},
{"MapboxPublicKey", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
{"MapBoxRequests", {PERSISTENT, JSON, "{}", "{}"}},
@@ -384,6 +404,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"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}},
{"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, "{}", "{}"}},
@@ -407,6 +428,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"ModelToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"ModelUI", {PERSISTENT, BOOL, "1", "0", 2}},
{"ModelVersions", {PERSISTENT, STRING, "", "", 1}},
{"ModelManifestVersion", {PERSISTENT, STRING, "", "", 1}},
{"NavigationUI", {PERSISTENT, BOOL, "1", "0", 1}},
{"NNFF", {PERSISTENT, BOOL, "0", "0", 2}},
{"NNFFLite", {PERSISTENT, BOOL, "0", "0", 2}},
@@ -415,6 +437,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"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}},
{"Offset1", {PERSISTENT, FLOAT, "5.0", "0.0", 0}},
{"Offset2", {PERSISTENT, FLOAT, "5.0", "0.0", 0}},
@@ -438,8 +461,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"PauseLateralSpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
{"LateralResumeDelay", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
{"PedalsOnUI", {PERSISTENT, BOOL, "0", "0", 1}},
{"PondPaired", {PERSISTENT, BOOL, "0", "0", 0}},
{"PondUploadPending", {PERSISTENT, BOOL, "0", "0", 0}},
{"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}},
@@ -447,7 +470,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"QOLLateral", {PERSISTENT, BOOL, "1", "0", 1}},
{"QOLLongitudinal", {PERSISTENT, BOOL, "1", "0", 1}},
{"QOLVisuals", {PERSISTENT, BOOL, "1", "0", 0}},
{"RadarTakeoffs", {PERSISTENT, BOOL, "1", "0", 2}},
{"RadarTakeoffs", {PERSISTENT, BOOL, "0", "0", 2}},
{"RadarTracksUI", {PERSISTENT, BOOL, "0", "0", 3}},
{"RainbowPath", {PERSISTENT, BOOL, "0", "0", 1}},
{"RandomEvents", {PERSISTENT, BOOL, "0", "0", 1}},
@@ -486,6 +509,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"SetSpeedLimit", {PERSISTENT, BOOL, "0", "0", 1}},
{"SetSpeedOffset", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
{"ShowCEMStatus", {PERSISTENT, BOOL, "1", "0", 2}},
{"ShowCCMStatus", {PERSISTENT, BOOL, "0", "0", 2}},
{"ShowCPU", {PERSISTENT, BOOL, "1", "0", 3}},
{"ShowCSCStatus", {PERSISTENT, BOOL, "1", "0", 2}},
{"ShowGPU", {PERSISTENT, BOOL, "0", "0", 3}},
@@ -502,7 +526,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"ShowStorageUsed", {PERSISTENT, BOOL, "0", "0", 3}},
{"SidebarMetrics", {PERSISTENT, BOOL, "1", "0", 3}},
{"SidebarOpen", {PERSISTENT, BOOL, "0", "0", 0}},
{"SignalAnimation", {PERSISTENT, STRING, "frog", "stock", 0}},
{"SignalAnimation", {PERSISTENT, STRING, "stock", "stock", 0}},
{"SignalMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
{"SignalToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"SimpleMode", {PERSISTENT, BOOL, "0", "0", 0}},
@@ -520,7 +544,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"SLCPriority1", {PERSISTENT, STRING, "Map Data", "Map Data", 2}},
{"SLCPriority2", {PERSISTENT, STRING, "Dashboard", "Dashboard", 2}},
{"SNGHack", {PERSISTENT, BOOL, "1", "0", 2}},
{"SoundPack", {PERSISTENT, STRING, "frog", "stock", 0}},
{"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"}},
@@ -545,8 +569,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"StandbyMode", {PERSISTENT, BOOL, "0", "0", 1}},
{"StartAccel", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"StartAccelStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"StartupMessageBottom", {PERSISTENT, STRING, "Human-tested, frog-approved 🐸", "Always keep hands on wheel and eyes on road", 0}},
{"StartupMessageTop", {PERSISTENT, STRING, "Hop in and buckle up!", "Be ready to take over at any time", 0}},
{"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}},
{"SteerDelay", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"SteerDelayStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
@@ -560,6 +584,7 @@ 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}},
{"StockDongleId", {PERSISTENT, STRING, "", ""}},
{"StopAccel", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"StopAccelStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
@@ -571,6 +596,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"SwitchbackModeCooldown", {PERSISTENT, INT, "5", "0", 2}},
{"SwitchbackModeEnabled", {CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0", "0"}},
{"SubaruSNG", {PERSISTENT, BOOL, "1", "0", 2}},
{"SubaruSNGManualParkingBrake", {PERSISTENT, BOOL, "0", "0", 2}},
{"TacoTune", {PERSISTENT, BOOL, "0", "0", 2}},
{"TestAlert", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
{"TetheringEnabled", {PERSISTENT, INT, "0", "0", 0}},
@@ -579,6 +605,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"Timezone", {PERSISTENT, STRING, "", ""}},
{"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}},
{"TrafficJerkDanger", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
@@ -610,12 +637,14 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"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}},
{"WeatherToken", {PERSISTENT | DONT_LOG, STRING, "", "", 2}},
{"WheelControls", {PERSISTENT, STRING, "", "", 2}},
{"WheelIcon", {PERSISTENT, STRING, "frog", "stock", 0}},
{"WheelIcon", {PERSISTENT, STRING, "stock", "stock", 0}},
{"WheelSpeed", {PERSISTENT, BOOL, "0", "0", 2}},
{"WheelToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
};
Binary file not shown.
+32
View File
@@ -1,8 +1,40 @@
import contextlib
import gc
import os
import platform
import sys
from pathlib import Path
import pytest
def _prepend_host_pytest_runtime() -> None:
if platform.system() != "Darwin" or os.getenv("SP_DISABLE_HOST_PYTEST_REDIRECT") == "1":
return
root_dir = Path(__file__).resolve().parent
work_dir = root_dir / ".host_runtime" / "darwin" / "worktree"
required_extension = work_dir / "msgq_repo" / "msgq" / "ipc_pyx.so"
if not required_extension.exists():
return
extra_paths = [work_dir, work_dir / "starpilot" / "third_party"]
extra_paths.extend(sorted(work_dir.glob("*_repo")))
acados_dir = work_dir / "third_party" / "acados"
if acados_dir.is_dir():
extra_paths.append(acados_dir)
existing = set(sys.path)
insert_at = 0
for path in [str(p) for p in extra_paths if p.exists()]:
if path in existing:
continue
sys.path.insert(insert_at, path)
insert_at += 1
_prepend_host_pytest_runtime()
from openpilot.common.prefix import OpenpilotPrefix
from openpilot.system.manager import manager
from openpilot.system.hardware import TICI, HARDWARE
+2 -1
View File
@@ -4,7 +4,7 @@
A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
# 325 Supported Cars
# 326 Supported Cars
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|<a href="##"><img width=2000></a>Hardware Needed<br>&nbsp;|Video|Setup Video|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
@@ -110,6 +110,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Hyundai|Azera 2022|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Azera 2022">Buy Here</a></sub></details>|||
|Hyundai|Azera Hybrid 2019|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Azera Hybrid 2019">Buy Here</a></sub></details>|||
|Hyundai|Azera Hybrid 2020|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Azera Hybrid 2020">Buy Here</a></sub></details>|||
|Hyundai|Azera Hybrid (with HDA II & LFA2) 2025|Highway Driving Assist II & Lane Follow Assist 2|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai S connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Azera Hybrid (with HDA II & LFA2) 2025">Buy Here</a></sub></details>|||
|Hyundai|Custin 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Custin 2023">Buy Here</a></sub></details>|||
|Hyundai|Elantra 2017-18|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra 2017-18">Buy Here</a></sub></details>|||
|Hyundai|Elantra 2019|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai G connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Elantra 2019">Buy Here</a></sub></details>|||
+152
View File
@@ -0,0 +1,152 @@
# StarPilot Unified Model Rebuild
This workflow rebuilds StarPilot driving and driver-monitoring artifacts for the vendored tinygrad revision. Driving-model behavior versions remain manifest metadata; every runtime driving artifact uses the `tinygrad_single_v1` layout.
## Safety
- The supported build device is `comma@192.168.3.110`.
- Never run these commands against `192.168.3.109`.
- Do not compile normal and big-GPU artifacts together. This workflow builds normal QCOM artifacts only.
- Keep source ONNX files and compiled PKLs on the T5 workspace, not the comma.
## Workspace
The default workspace is:
```text
/Volumes/T5/StarPilot-Model-Rebuild-2026-06-22/
```
Important directories:
- `onnx/<model-id>/`: ID-prefixed source ONNX files.
- `compiled/`: completed unified driving PKLs.
- `driver-monitoring/`: DM ONNX, model PKL, metadata, and camera warps.
- `ready-for-resources/`: flat repository-upload handoff.
- Oversized models are represented by repository-safe `.p00`, `.p01`, and `.sha256` files in `ready-for-resources/`.
- `logs/`: one remote compilation log per model.
- `results/`: source and artifact checksum records.
- `manifests/`: generated `model_names_v22.json`.
## Initialize And Extract
```bash
python3 scripts/model_rebuild_pipeline.py init
python3 scripts/model_rebuild_pipeline.py extract \
--base-manifest /path/to/model_names_v21.json
```
Extraction streams Git blobs directly to disk. LFS pointers are resolved from the local object cache or fetched by object ID, then checked against the pointer SHA-256 and size. Binary ONNX data is never stored in a shell variable.
To retry one source:
```bash
python3 scripts/model_rebuild_pipeline.py extract \
--model pop22 \
--base-manifest /path/to/model_names_v21.json
```
Source commits are defined in `scripts/model_source_map_v22.json`.
## Compile
Compile one model:
```bash
python3 scripts/model_rebuild_pipeline.py compile \
--model pop22 \
--base-manifest /path/to/model_names_v21.json
```
Compile or resume the full catalog:
```bash
python3 scripts/model_rebuild_pipeline.py compile \
--base-manifest /path/to/model_names_v21.json
```
Existing artifacts are skipped unless `--force` is passed. Each model is staged in its own remote input directory, compiled on `.110`, copied back to the T5, hashed, and copied into `ready-for-resources/`. Failures are written to `results/<id>_failure.json`; rerunning the same command resumes incomplete models.
Validate one or all completed artifacts with synthetic camera inputs on QCOM:
```bash
python3 scripts/model_rebuild_pipeline.py validate \
--model pop22 \
--base-manifest /path/to/model_names_v21.json
```
The lower-level device compiler also supports direct use:
```bash
./models --model pop22 --input-format split --version v11
./models --model deeprl3v2 --input-format supercombo --version v15
```
`--version` records behavioral semantics only. It does not change artifact layout.
If the compiled PKL exceeds 100 MiB, `./models` automatically keeps the full
local PKL and creates 95 MiB upload parts beside it:
```text
deeprl3v2_driving_tinygrad.pkl
deeprl3v2_driving_tinygrad.pkl.p00
deeprl3v2_driving_tinygrad.pkl.p01
deeprl3v2_driving_tinygrad.pkl.sha256
```
To split an already compiled artifact:
```bash
./models --split-artifact /path/to/deeprl3v2_driving_tinygrad.pkl \
--output-dir /path/to/upload-ready
```
Upload only the numbered parts and checksum when the full PKL exceeds the
repository limit. The downloader reassembles into a temporary file, verifies
the companion SHA-256, and atomically installs the final PKL. No manifest field
is required for multipart artifacts.
## Driver Monitoring
Stage the current DM ONNX in `uncompiledmodels`, then run:
```bash
./models --dm \
--input-dir /data/openpilot/uncompiledmodels \
--output-dir /tmp/dm_artifacts
```
This builds:
- `dmonitoring_model_tinygrad.pkl`
- `dmonitoring_model_metadata.pkl`
- `dm_warp_1928x1208_tinygrad.pkl`
- `dm_warp_1344x760_tinygrad.pkl`
All four files must be updated together.
## Manifest
Generate v22 after compilation:
```bash
python3 scripts/model_rebuild_pipeline.py manifest \
--base-manifest /path/to/model_names_v21.json
```
The generator preserves existing IDs and behavioral metadata and adds
`deeprl3v2`. Manifest v22 implies the unified single-PKL runtime layout.
Repository-hosted multipart files are discovered by naming convention, so no
size, hash, format, or part-count metadata is required.
## Runtime Verification
Compilation validates JIT capture/replay, pickle round-trip, finite outputs, metadata slices, and both camera warps. Before release:
1. Select representative v8, v11, v12, v15, and supercombo models.
2. Confirm `modeld` stays running.
3. Confirm finite `modelV2` path, lane-line, lead, pose, and action data.
4. Confirm `driverStateV2` on both supported camera resolutions.
5. Test download, selection, deletion, randomization, migration, and fallback in QT, raylib/mici, and Galaxy.
The built-in South Carolina artifact is `selfdrive/modeld/models/driving_tinygrad.pkl`. If migration cannot download the selected v22 artifact, StarPilot switches to that built-in model.
+3 -5
View File
@@ -122,7 +122,6 @@ mods = [
"msgq.ipc_pyx",
"msgq.visionipc.visionipc_pyx",
"openpilot.common.transformations.transformations",
"openpilot.selfdrive.modeld.models.commonmodel_pyx",
"openpilot.selfdrive.pandad.pandad_api_impl",
"openpilot.selfdrive.controls.lib.lateral_mpc_lib.c_generated_code.acados_ocp_solver_pyx",
"openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.c_generated_code.acados_ocp_solver_pyx",
@@ -137,12 +136,11 @@ for mod in mods:
repo_root = Path.cwd().parents[1]
required_files = [
repo_root / "selfdrive/modeld/models/driving_vision_metadata.pkl",
repo_root / "selfdrive/modeld/models/driving_policy_metadata.pkl",
repo_root / "selfdrive/modeld/models/driving_vision_tinygrad.pkl",
repo_root / "selfdrive/modeld/models/driving_policy_tinygrad.pkl",
repo_root / "selfdrive/modeld/models/driving_tinygrad.pkl",
repo_root / "selfdrive/modeld/models/dmonitoring_model_metadata.pkl",
repo_root / "selfdrive/modeld/models/dmonitoring_model_tinygrad.pkl",
repo_root / "selfdrive/modeld/models/dm_warp_1928x1208_tinygrad.pkl",
repo_root / "selfdrive/modeld/models/dm_warp_1344x760_tinygrad.pkl",
repo_root / "selfdrive/pandad/pandad_api_impl.so",
repo_root / "selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/acados_ocp_solver_pyx.so",
repo_root / "selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/libacados_ocp_solver_lat.so",
+1 -1
View File
@@ -21,7 +21,7 @@ fi
export QCOM_PRIORITY=12
if [ -z "$AGNOS_VERSION" ]; then
export AGNOS_VERSION="12.8.16"
export AGNOS_VERSION="12.8.17"
fi
export STAGING_ROOT="/data/safe_staging"
+46 -19
View File
@@ -34,9 +34,11 @@ import requests
import zstandard
from cereal import log
from openpilot.tools.lib.auth_config import DEFAULT_API_HOST, KONIK_API_HOST, get_token, normalize_api_host
API_HOST = os.getenv("COMMA_API_HOST", "https://api.commadotai.com").rstrip("/")
API_HOST = normalize_api_host(os.getenv("COMMA_API_HOST") or os.getenv("API_HOST") or DEFAULT_API_HOST)
API_HOSTS = [API_HOST] if os.getenv("COMMA_API_HOST") or os.getenv("API_HOST") else [DEFAULT_API_HOST, KONIK_API_HOST]
ROUTE_ID_RE = re.compile(r"([0-9a-f]{16})/([^/]+)")
@@ -146,12 +148,12 @@ def parse_route_id(raw: str) -> RouteId:
return RouteId(dongle_id=dongle_id, log_id=log_id)
def route_url(route: RouteId) -> str:
return f"{API_HOST}/v1/route/{quote(route.canonical_name, safe='')}/"
def route_url(route: RouteId, api_host: str) -> str:
return f"{api_host}/v1/route/{quote(route.canonical_name, safe='')}/"
def route_files_url(route: RouteId) -> str:
return f"{API_HOST}/v1/route/{quote(route.canonical_name, safe='')}/files"
def route_files_url(route: RouteId, api_host: str) -> str:
return f"{api_host}/v1/route/{quote(route.canonical_name, safe='')}/files"
def format_segments(segments: list[int]) -> str:
@@ -184,8 +186,13 @@ def relative_posix(path: Path, root: Path) -> str:
return path.relative_to(root).as_posix()
def fetch_json(session: requests.Session, url: str, timeout: float) -> Any:
response = session.get(url, timeout=timeout, allow_redirects=True)
def api_headers(api_host: str) -> dict[str, str] | None:
token = get_token(api_host)
return {"Authorization": f"JWT {token}"} if token else None
def fetch_json(session: requests.Session, url: str, timeout: float, api_host: str | None = None) -> Any:
response = session.get(url, timeout=timeout, allow_redirects=True, headers=api_headers(api_host) if api_host else None)
response.raise_for_status()
return response.json()
@@ -312,24 +319,42 @@ def filename_from_url(url: str) -> str:
def validate_route(route: RouteId, session: requests.Session, timeout: float) -> dict[str, Any]:
try:
route_meta = fetch_json(session, route_url(route), timeout)
except requests.HTTPError as exc:
status_code = exc.response.status_code if exc.response is not None else "unknown"
raise ValidationError(route, [f"route is not publicly accessible from comma connect (HTTP {status_code})."]) from exc
route_meta = None
files_payload = None
api_host = None
not_found_hosts = []
for candidate_host in API_HOSTS:
try:
route_meta = fetch_json(session, route_url(route, candidate_host), timeout, candidate_host)
except requests.HTTPError as exc:
status_code = exc.response.status_code if exc.response is not None else "unknown"
if status_code == 404 and len(API_HOSTS) > 1:
not_found_hosts.append(candidate_host)
continue
raise ValidationError(route, [f"route is not publicly accessible from {candidate_host} (HTTP {status_code})."]) from exc
try:
files_payload = fetch_json(session, route_files_url(route, candidate_host), timeout, candidate_host)
except requests.HTTPError as exc:
status_code = exc.response.status_code if exc.response is not None else "unknown"
if status_code == 404 and len(API_HOSTS) > 1:
not_found_hosts.append(candidate_host)
continue
raise ValidationError(route, [f"public route files could not be fetched from {candidate_host} (HTTP {status_code})."]) from exc
api_host = candidate_host
break
if route_meta is None or files_payload is None or api_host is None:
raise ValidationError(route, [f"route was not found on: {', '.join(not_found_hosts) or ', '.join(API_HOSTS)}."])
if not route_meta.get("is_public", False):
raise ValidationError(route, ["route metadata loaded, but `is_public` was false."])
try:
files_payload = fetch_json(session, route_files_url(route), timeout)
except requests.HTTPError as exc:
status_code = exc.response.status_code if exc.response is not None else "unknown"
raise ValidationError(route, [f"public route files could not be fetched from comma connect (HTTP {status_code})."]) from exc
expected_segments = expected_segment_count(route_meta, files_payload)
if expected_segments <= 0:
raise ValidationError(route, ["could not determine any route segments from comma connect."])
raise ValidationError(route, ["could not determine any route segments from the route API."])
failures: list[str] = []
stream_urls: dict[str, list[Any]] = {}
@@ -384,6 +409,7 @@ def validate_route(route: RouteId, session: requests.Session, timeout: float) ->
raise ValidationError(route, failures)
return {
"api_host": api_host,
"route_meta": route_meta,
"files_payload": files_payload,
"expected_segments": expected_segments,
@@ -532,6 +558,7 @@ def print_validation_summary(route: RouteId, validation: dict[str, Any]) -> None
params = validation["params"]
map_tiles: MapTileSummary = validation["map_tiles"]
print(f"Validated {route.cli_name}")
print(f" route API: {validation['api_host']}")
print(f" public route: yes")
print(f" segments: {validation['expected_segments']}")
print(f" VisionSpeedLimitDetection: {params.get('VisionSpeedLimitDetection', '')}")
+1 -1
View File
@@ -106,7 +106,7 @@ def make_tester_present_msg(addr, bus, subaddr=None, suppress_response=False):
return CanData(addr, bytes(dat), bus)
def get_safety_config(safety_model: structs.CarParams.SafetyModel, safety_param: int = None) -> structs.CarParams.SafetyConfig:
def get_safety_config(safety_model, safety_param: int = None) -> structs.CarParams.SafetyConfig:
ret = structs.CarParams.SafetyConfig()
ret.safetyModel = safety_model
if safety_param is not None:
-6
View File
@@ -205,11 +205,6 @@ struct CarState {
vehicleSensorsInvalid @52 :Bool; # invalid steering angle readings, etc.
lowSpeedAlert @56 :Bool; # lost steering control due to a dynamic min steering speed
blockPcmEnable @60 :Bool; # whether to allow PCM to enable this frame
pedalMaxRegen @61 :Bool; # pedal at max regen, driver should use brake for more decel
pedalLongActive @62 :Bool; # Pre-AP pedal longitudinal mode is active (enableLongControl)
teslaCCEngaged @63 :Bool; # rising edge of stock Tesla CC engaging (no-pedal mode)
teslaCCDisengaged @64 :Bool; # falling edge of stock Tesla CC
teslaCCNotArmed @65 :Bool; # lateral engaged but DI_cruiseState != STANDBY/ENABLED
# cruise state
cruiseState @10 :CruiseState;
@@ -640,7 +635,6 @@ struct CarParams {
fcaGiorgio @32;
rivian @33;
volkswagenMeb @34;
teslaPreap @35;
}
enum SteerControlType {
+3
View File
@@ -334,6 +334,9 @@ def get_car(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multip
CP.carFw = car_fw
CP.fingerprintSource = source
CP.fuzzyFingerprint = not exact_match
post_fingerprint_params = getattr(CarInterface, "apply_post_fingerprint_params", None)
if post_fingerprint_params is not None:
post_fingerprint_params(CP, candidate, fingerprints, car_fw)
FPCP: StarPilotCarParams = CarInterface.get_starpilot_params(candidate, fingerprints, car_fw, CP, starpilot_toggles)
@@ -2,7 +2,7 @@ from opendbc.can import CANPacker
from opendbc.car import Bus, DT_CTRL
from opendbc.car.lateral import apply_meas_steer_torque_limits
from opendbc.car.chrysler import chryslercan
from opendbc.car.chrysler.values import RAM_CARS, RAM_DT, CarControllerParams, ChryslerFlags
from opendbc.car.chrysler.values import RAM_CARS, RAM_DT, CarControllerParams, ChryslerFlags, ChryslerStarPilotFlags
from opendbc.car.interfaces import CarControllerBase
@@ -50,7 +50,9 @@ class CarController(CarControllerBase):
# TODO: can we make this more sane? why is it different for all the cars?
lkas_control_bit = self.lkas_control_bit_prev
if self.CP.carFingerprint in RAM_DT:
if self.FPCP is not None and self.FPCP.flags & ChryslerStarPilotFlags.NO_MIN_STEERING_SPEED:
lkas_control_bit = CC.latActive
elif self.CP.carFingerprint in RAM_DT:
if self.CP.minEnableSpeed <= CS.out.vEgo <= self.CP.minEnableSpeed + 0.5:
lkas_control_bit = True
if (self.CP.minEnableSpeed >= 14.5) and (CS.out.gearShifter != 2):
@@ -21,6 +21,7 @@ class ChryslerFlags(IntFlag):
class ChryslerStarPilotFlags(IntFlag):
RAM_HD_ALT_BUTTONS = 1
NO_MIN_STEERING_SPEED = 2
@dataclass
+455 -73
View File
@@ -10,11 +10,13 @@ from opendbc.car.gm.values import (
CruiseButtons, GMFlags, GMSafetyFlags,
)
from opendbc.car.interfaces import CarControllerBase
from openpilot.common.pid import PIDController
from openpilot.common.params import Params, UnknownKeyName
from openpilot.starpilot.common.testing_grounds import testing_ground
VisualAlert = structs.CarControl.HUDControl.VisualAlert
NetworkLocation = structs.CarParams.NetworkLocation
TransmissionType = structs.CarParams.TransmissionType
LongCtrlState = structs.CarControl.Actuators.LongControlState
GearShifter = structs.CarState.GearShifter
@@ -28,14 +30,42 @@ AUTO_HOLD_VOLT_CARS = {
CAR.CHEVROLET_VOLT_ASCM,
CAR.CHEVROLET_VOLT_CAMERA,
}
AUTO_HOLD_DRIVE_GEARS = {
AUTO_HOLD_DRIVE_GEARS = (
GearShifter.drive,
GearShifter.low,
GearShifter.manumatic,
}
)
AUTO_HOLD_MIN_BRAKE = 80
AUTO_HOLD_MAX_BRAKE = 240
AUTO_HOLD_MIN_DRIVE_TIME_S = 3.0
AUTO_HOLD_STOPPED_SPEED = 0.02
AUTO_HOLD_2019_MIN_BRAKE = 100
BOLT_ACC_PEDAL_FRICTION_RELEASE_FRAMES = 5
BOLT_PEDAL_LONG_ACCEL_LIMIT_BP = [0.0, 1.5, 4.0, 8.0, 15.0, 30.0]
BOLT_PEDAL_LONG_ACCEL_LIMIT_V = [-0.93, -1.28, -1.98, -2.58, -2.86, -2.95]
VOLT_ONE_PEDAL_DECEL_BP = [0.5 * CV.MPH_TO_MS, 6.0 * CV.MPH_TO_MS]
VOLT_ONE_PEDAL_DECEL_V = [-1.0, -1.1]
VOLT_ONE_PEDAL_REGEN_PADDLE_DECEL_V = [-1.5, -1.6]
VOLT_ONE_PEDAL_MAX_DECEL = min((*VOLT_ONE_PEDAL_DECEL_V, *VOLT_ONE_PEDAL_REGEN_PADDLE_DECEL_V)) - 0.5
VOLT_ONE_PEDAL_PID_NEG_LIMIT = -3.5
VOLT_ONE_PEDAL_SPEED_ERROR_FACTOR_BP = [1.5, 20.0]
VOLT_ONE_PEDAL_SPEED_ERROR_FACTOR_V = [0.4, 0.2]
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_SPEED_FACTOR_BP = [0.0, 10.0 * CV.MPH_TO_MS]
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_SPEED_FACTOR_V = [0.2, 1.0]
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_STEER_FACTOR_BP = [20.0, 120.0]
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_STEER_FACTOR_V = [1.0, 0.2]
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_UP = 0.8 * DT_CTRL * 4
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_DOWN = 0.8 * DT_CTRL * 4
VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_BP = [4.0, 8.0]
VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_V = [0.4, 1.0]
VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_INCLINE_V = [0.2, 1.0]
VOLT_ONE_PEDAL_LIFT_BRAKE_BP = [0.0, CarControllerParams.NEAR_STOP_BRAKE_PHASE, 2.0 * CV.MPH_TO_MS]
VOLT_ONE_PEDAL_LIFT_BRAKE_V = [AUTO_HOLD_MIN_BRAKE, AUTO_HOLD_MIN_BRAKE, 20.0]
VOLT_ONE_PEDAL_LIFT_BRAKE_FRAMES = 8
TRUCK_LONG_SMOOTH_CARS = {
CAR.CHEVROLET_SILVERADO,
CAR.CHEVROLET_SILVERADO_CC,
}
def get_stock_cc_active_for_cancel(CP, CS):
@@ -51,7 +81,13 @@ def use_interceptor_sng_launch(CP, CS, maneuver_mode=False):
launch_speed = max(CP.vEgoStarting, 0.3)
if maneuver_mode:
launch_speed = max(launch_speed, 2.0)
return CS.out.cruiseState.standstill and (CS.out.standstill or CS.out.vEgo < launch_speed)
near_stop = CS.out.standstill or CS.out.vEgo < launch_speed
if (
getattr(CP, "carFingerprint", None) == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL and
getattr(CP, "enableGasInterceptorDEPRECATED", False)
):
return near_stop
return CS.out.cruiseState.standstill and near_stop
def should_spoof_dash_speed(CP, starpilot_toggles):
@@ -77,6 +113,13 @@ def should_send_acc_dashboard_status(CP, dash_speed_spoof_active):
return status_car and (dash_speed_spoof_active or volt_camera_no_camera)
def get_acc_dashboard_status_active(CP, CC):
if CC.enabled:
return True
return CP.carFingerprint == CAR.BUICK_LACROSSE_ASCM and CC.latActive
def get_acc_dashboard_fcw_alert(hud_alert, CS):
if hud_alert == VisualAlert.fcw:
return 0x3
@@ -92,29 +135,6 @@ def get_acc_dashboard_fcw_alert(hud_alert, CS):
return 0
def get_acc_dashboard_status_values(enabled, target_speed_kph, hud_control, CS):
if enabled:
return {
"ACCCruiseState": 0,
"ACCLeadCar": int(hud_control.leadVisible) & 0x1,
"ACCResumeButton": 0,
"ACCSpeedSetpoint": target_speed_kph,
"ACCGapLevel": int(hud_control.leadDistanceBars) & 0x3,
"ACCCmdActive": 1,
}
# Replay the stock camera dashboard context when openpilot long is enabled
# but openpilot itself is not actively driving the ACC cluster state.
return {
"ACCCruiseState": int(getattr(CS, "stock_acc_cruise_state", 0)) & 0x7,
"ACCLeadCar": int(getattr(CS, "stock_acc_lead_car", 0)) & 0x1,
"ACCResumeButton": int(getattr(CS, "stock_acc_resume_button", 0)) & 0x1,
"ACCSpeedSetpoint": float(getattr(CS, "stock_acc_speed_setpoint_kph", 0.0)),
"ACCGapLevel": int(getattr(CS, "stock_acc_gap_level", 0)) & 0x3,
"ACCCmdActive": int(getattr(CS, "stock_acc_cmd_active", 0)) & 0x1,
}
ECM_CRUISE_SPOOF_CARS = {
CAR.CHEVROLET_BOLT_CC_2017,
CAR.CHEVROLET_BOLT_CC_2018_2021,
@@ -150,14 +170,56 @@ def get_adas_keepalive_step(CP, is_kaofui_car):
return None
def should_send_adas_status(CP, is_kaofui_car):
if CP.radarUnavailable:
return False
if not is_kaofui_car:
return True
if CP.carFingerprint in ASCM_INT:
return False
return CP.networkLocation != NetworkLocation.fwdCamera and CP.carFingerprint not in SDGM_CAR
def 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:
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]))
if accel <= 0.12:
return accel * low_scale
if accel <= 0.35:
return float(np.interp(accel, [0.12, 0.35], [0.12 * low_scale, 0.35 * mid_scale]))
if accel <= 0.65:
return float(np.interp(accel, [0.35, 0.65], [0.35 * mid_scale, 0.65]))
return accel
def get_lka_steering_cmd_counter(next_counter: int, CS) -> int:
if getattr(CS, "loopback_lka_steering_cmd_updated", False):
return (getattr(CS, "loopback_lka_steering_cmd_counter", next_counter) + 1) % 4
if next_counter < 0 and getattr(CS, "loopback_lka_steering_cmd_ts_nanos", 0) == 0:
return (getattr(CS, "pt_lka_steering_cmd_counter", next_counter) + 1) % 4
return next_counter
def should_send_stock_long_cancel(cancel_counter: int, CS) -> bool:
cs_out = getattr(CS, "out", None)
return cancel_counter > CAMERA_CANCEL_DELAY_FRAMES and not bool(getattr(cs_out, "accFaulted", False))
def supports_volt_auto_hold(CP, auto_hold_enabled: bool):
safety_cfg = getattr(CP, "safetyConfigs", ())
safety_param = safety_cfg[0].safetyParam if safety_cfg else 0
stock_hold_safety_ready = CP.openpilotLongitudinalControl or bool(safety_param & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value)
stock_hold_safety_ready = bool(safety_param & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value)
return (
auto_hold_enabled and
stock_hold_safety_ready and
@@ -165,19 +227,67 @@ def supports_volt_auto_hold(CP, auto_hold_enabled: bool):
)
def estimate_auto_hold_brake(driver_brake: float, op_brake: float) -> int:
def supports_volt_one_pedal(CP, one_pedal_enabled: bool):
safety_cfg = getattr(CP, "safetyConfigs", ())
safety_param = safety_cfg[0].safetyParam if safety_cfg else 0
stock_hold_safety_ready = bool(safety_param & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value)
return (
one_pedal_enabled and
stock_hold_safety_ready and
getattr(CP, "transmissionType", None) == TransmissionType.direct and
CP.carFingerprint in AUTO_HOLD_VOLT_CARS
)
def estimate_auto_hold_brake(driver_brake: float, op_brake: float, CP=None) -> int:
driver_hold = np.interp(float(driver_brake), [8.0, 20.0, 40.0, 80.0], [80.0, 110.0, 150.0, 220.0])
hold_brake = max(float(op_brake), float(driver_hold))
return int(round(np.clip(hold_brake, AUTO_HOLD_MIN_BRAKE, AUTO_HOLD_MAX_BRAKE)))
min_brake = AUTO_HOLD_2019_MIN_BRAKE if getattr(CP, "carFingerprint", None) == CAR.CHEVROLET_VOLT_2019 else AUTO_HOLD_MIN_BRAKE
return int(round(np.clip(hold_brake, min_brake, AUTO_HOLD_MAX_BRAKE)))
def get_auto_hold_stop_threshold(CP, auto_hold_engaged: bool) -> float:
if auto_hold_engaged and getattr(CP, "carFingerprint", None) == CAR.CHEVROLET_VOLT_2019:
return CarControllerParams.NEAR_STOP_BRAKE_PHASE
return AUTO_HOLD_STOPPED_SPEED
def get_volt_one_pedal_target_decel(v_ego: float) -> float:
return float(np.interp(v_ego, VOLT_ONE_PEDAL_DECEL_BP, VOLT_ONE_PEDAL_DECEL_V))
def get_volt_one_pedal_lift_brake(v_ego: float) -> int:
if v_ego > VOLT_ONE_PEDAL_LIFT_BRAKE_BP[-1]:
return 0
return int(round(np.interp(v_ego, VOLT_ONE_PEDAL_LIFT_BRAKE_BP, VOLT_ONE_PEDAL_LIFT_BRAKE_V)))
def should_activate_volt_one_pedal(one_pedal_ready: bool, cruise_main: bool, long_active: bool,
gas_pressed: bool, brake_pressed: bool, regen_braking: bool,
single_pedal_mode: bool, gear_shifter, drive_time_s: float) -> bool:
# Volt rear wheel direction bits can falsely report reverse while stopping in L.
return (
one_pedal_ready and
cruise_main and
single_pedal_mode and
gear_shifter in AUTO_HOLD_DRIVE_GEARS and
drive_time_s >= AUTO_HOLD_MIN_DRIVE_TIME_S and
not long_active and
not gas_pressed and
not brake_pressed and
not regen_braking
)
def should_activate_auto_hold(hold_ready: bool, auto_hold_armed: bool, auto_hold_engaged: bool,
brake_pressed: bool, standstill: bool, long_active: bool,
regen_braking: bool, v_ego: float) -> bool:
stopped = standstill or v_ego < 0.02
brake_pressed: bool, gas_pressed: bool, standstill: bool, long_active: bool,
regen_braking: bool, v_ego: float, stop_speed_threshold: float=AUTO_HOLD_STOPPED_SPEED) -> bool:
stopped = standstill or v_ego < stop_speed_threshold
return (
hold_ready and
(auto_hold_armed or auto_hold_engaged or brake_pressed) and
not gas_pressed and
stopped and
not long_active and
not regen_braking
@@ -201,6 +311,104 @@ def get_friction_brake_bus(CP):
return CanBus.CHASSIS
def supports_bolt_acc_pedal_friction_experiment(CP) -> bool:
return (
CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL and
CP.openpilotLongitudinalControl and
CP.enableGasInterceptorDEPRECATED and
bool(CP.flags & GMFlags.PEDAL_LONG.value)
)
def get_bolt_acc_pedal_friction_brake(apply_brake, full_brake_accel, v_ego, params) -> int:
if apply_brake <= 0:
return 0
full_brake_accel = min(full_brake_accel, -0.1)
legacy_full_scale = max(-params.ACCEL_MIN, 0.1)
corrected_scale = legacy_full_scale / max(-full_brake_accel, 0.1)
speed_gain = float(np.interp(v_ego, [0.0, 8.0, 15.0, 25.0], [1.0, 1.08, 1.2, 1.35]))
onset_gain = float(np.interp(
apply_brake,
[0.0, 5.0, 20.0, 60.0, 120.0, 240.0, params.MAX_BRAKE],
[0.0, 1.8, 1.65, 1.4, 1.22, 1.08, 1.0],
))
shaped_brake = apply_brake * corrected_scale * speed_gain * onset_gain
minimum_brake = float(np.interp(v_ego, [0.0, 6.0, 8.0, 12.0, 18.0, 25.0], [0.0, 0.0, 4.0, 10.0, 20.0, 28.0]))
shaped_brake = max(shaped_brake, minimum_brake)
return int(round(np.clip(shaped_brake, 0, params.MAX_BRAKE)))
def shape_bolt_acc_pedal_low_speed_friction(apply_brake: int, v_ego: float, stopping: bool, active: bool):
if apply_brake <= 0:
return 0, False
engage_threshold = float(np.interp(v_ego, [0.0, 1.5, 3.0, 5.0, 8.0], [40.0, 20.0, 12.0, 10.0, 0.0]))
release_threshold = float(np.interp(v_ego, [0.0, 1.5, 3.0, 5.0, 8.0], [0.0, 8.0, 6.0, 4.0, 0.0]))
if not active:
if apply_brake < engage_threshold:
return 0, False
active = True
elif apply_brake < release_threshold:
return 0, False
if stopping:
stop_fade = float(np.interp(v_ego, [0.0, 0.6, 0.9, 1.2, 1.8, 2.8], [0.0, 0.0, 0.05, 0.12, 0.32, 0.78]))
apply_brake = int(round(apply_brake * stop_fade))
if apply_brake <= 0 or apply_brake < release_threshold:
return 0, False
return apply_brake, active
def get_bolt_pedal_long_accel_limit(v_ego: float) -> float:
return float(np.interp(v_ego, BOLT_PEDAL_LONG_ACCEL_LIMIT_BP, BOLT_PEDAL_LONG_ACCEL_LIMIT_V))
def get_bolt_acc_pedal_planner_brake_switch(v_ego: float, params, tire_radius: float, mass: float,
coeff_drag: float, frontal_area: float, air_density: float) -> int:
planner_accel_limit = get_bolt_pedal_long_accel_limit(v_ego)
aero_drag_force = 0.5 * coeff_drag * frontal_area * air_density * v_ego ** 2
planner_torque = tire_radius * ((mass * planner_accel_limit) + aero_drag_force)
return int(round(planner_torque + params.ZERO_GAS))
def get_bolt_acc_pedal_effective_brake_switch(stock_switch: int, planner_switch: int) -> int:
return max(stock_switch, planner_switch)
def get_bolt_acc_pedal_friction_command_state(apply_brake: int, cruise_main_on: bool, release_frames: int):
command_brake = apply_brake if cruise_main_on else 0
if command_brake > 0:
release_frames = BOLT_ACC_PEDAL_FRICTION_RELEASE_FRAMES
elif release_frames > 0:
release_frames -= 1
should_send = cruise_main_on or release_frames > 0
return command_brake, release_frames, should_send
def get_interceptor_sng_gas_cmd(CP, interceptor_gas_cmd: float, accel: float, params, maneuver_mode: bool) -> float:
if maneuver_mode:
return max(interceptor_gas_cmd, float(np.interp(accel, [0.0, 1.0, 2.0], [params.SNG_INTERCEPTOR_GAS, 0.11, 0.16])))
if supports_bolt_acc_pedal_friction_experiment(CP):
return max(interceptor_gas_cmd, params.SNG_INTERCEPTOR_GAS)
return params.SNG_INTERCEPTOR_GAS
def should_use_fixed_stopping_brake(CP, near_stop: bool, stopping: bool, resume: bool) -> bool:
if not (near_stop and stopping and not resume):
return False
return not supports_bolt_acc_pedal_friction_experiment(CP)
class CarController(CarControllerBase):
def __init__(self, dbc_names, CP):
super().__init__(dbc_names, CP)
@@ -254,10 +462,59 @@ class CarController(CarControllerBase):
self.malibu_button_phase = 0
self.malibu_last_button_ts_nanos = 0
self.auto_hold_brake = 0
self.volt_one_pedal_pid = PIDController(
(CP.longitudinalTuning.kpBP, CP.longitudinalTuning.kpV),
(CP.longitudinalTuning.kiBP, CP.longitudinalTuning.kiV),
rate=1 / (DT_CTRL * 4),
pos_limit=0.0,
neg_limit=VOLT_ONE_PEDAL_PID_NEG_LIMIT,
)
self.volt_one_pedal_decel = 0.0
self.volt_one_pedal_brake = 0
self.volt_one_pedal_lift_frames = 0
self.volt_one_pedal_gas_pressed_last = False
try:
self.gm_auto_hold_enabled = self.params_.get_bool("GMAutoHold")
except UnknownKeyName:
self.gm_auto_hold_enabled = False
self.bolt_acc_pedal_friction_release_frames = 0
self.bolt_acc_pedal_friction_low_speed_active = False
def _reset_volt_one_pedal(self):
self.volt_one_pedal_pid.reset()
self.volt_one_pedal_decel = min(0.0, float(self.aego))
self.volt_one_pedal_brake = 0
self.volt_one_pedal_lift_frames = 0
def _update_volt_one_pedal_brake(self, CC, CS):
pitch_accel = 0.0
if len(CC.orientationNED) == 3 and CS.out.vEgo > self.CP.vEgoStopping:
pitch_accel = math.sin(CC.orientationNED[1]) * ACCELERATION_DUE_TO_GRAVITY
pitch_factor_values = VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_V if pitch_accel <= 0.0 else VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_INCLINE_V
pitch_accel *= float(np.interp(CS.out.vEgo, VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_BP, pitch_factor_values))
target_decel = get_volt_one_pedal_target_decel(CS.out.vEgo)
measured_decel = min(0.0, CS.out.aEgo + pitch_accel)
error_factor = float(np.interp(CS.out.vEgo, VOLT_ONE_PEDAL_SPEED_ERROR_FACTOR_BP, VOLT_ONE_PEDAL_SPEED_ERROR_FACTOR_V))
error = (target_decel - measured_decel) * error_factor
raw_decel = float(self.volt_one_pedal_pid.update(error, speed=CS.out.vEgo, feedforward=target_decel))
rate_limit_factor = min(
float(np.interp(CS.out.vEgo, VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_SPEED_FACTOR_BP, VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_SPEED_FACTOR_V)),
float(np.interp(abs(CS.out.steeringAngleDeg), VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_STEER_FACTOR_BP, VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_STEER_FACTOR_V)),
)
lower = min(self.volt_one_pedal_decel, measured_decel) - VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_UP * rate_limit_factor
upper = max(self.volt_one_pedal_decel, measured_decel) + VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_DOWN + rate_limit_factor
self.volt_one_pedal_decel = float(np.clip(raw_decel, lower, upper))
self.volt_one_pedal_decel = max(self.volt_one_pedal_decel, VOLT_ONE_PEDAL_MAX_DECEL)
self.volt_one_pedal_brake = int(round(np.clip(
np.interp(self.volt_one_pedal_decel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V),
0,
self.params.MAX_BRAKE,
)))
if self.volt_one_pedal_lift_frames > 0:
self.volt_one_pedal_brake = max(self.volt_one_pedal_brake, get_volt_one_pedal_lift_brake(CS.out.vEgo))
self.volt_one_pedal_lift_frames -= 1
def calc_pedal_command(self, accel: float, long_active: bool, v_ego: float):
if not long_active:
@@ -411,7 +668,37 @@ class CarController(CarControllerBase):
accel = actuators.accel
press_regen_paddle = False
auto_hold_enabled = supports_volt_auto_hold(self.CP, self.gm_auto_hold_enabled)
stock_hold_apply_brake = self.apply_brake if self.CP.openpilotLongitudinalControl else 0
volt_one_pedal_supported = supports_volt_one_pedal(
self.CP, bool(getattr(starpilot_toggles, "volt_one_pedal_mode", False))
)
volt_one_pedal_active = should_activate_volt_one_pedal(
volt_one_pedal_supported,
CS.out.cruiseState.available,
CC.longActive,
CS.out.gasPressed,
CS.out.brakePressed,
CS.out.regenBraking,
bool(getattr(CS, "single_pedal_mode", False)),
CS.out.gearShifter,
float(getattr(CS, "one_pedal_drive_time", 0.0)),
)
if volt_one_pedal_active and self.volt_one_pedal_gas_pressed_last and not CS.out.gasPressed:
if CS.out.vEgo < VOLT_ONE_PEDAL_LIFT_BRAKE_BP[-1]:
self.volt_one_pedal_lift_frames = VOLT_ONE_PEDAL_LIFT_BRAKE_FRAMES
elif CS.out.gasPressed or not volt_one_pedal_active:
self.volt_one_pedal_lift_frames = 0
if self.frame % 4 == 0:
if volt_one_pedal_active:
self._update_volt_one_pedal_brake(CC, CS)
else:
self._reset_volt_one_pedal()
if not self.CP.openpilotLongitudinalControl:
self.apply_gas = 0
self.apply_brake = self.volt_one_pedal_brake if volt_one_pedal_active else 0
self.volt_one_pedal_gas_pressed_last = CS.out.gasPressed
stock_hold_apply_brake = max(self.apply_brake if self.CP.openpilotLongitudinalControl else 0, self.volt_one_pedal_brake)
hold_ready = (
auto_hold_enabled and
@@ -421,6 +708,8 @@ class CarController(CarControllerBase):
)
if not hold_ready or CS.out.gasPressed:
CS.auto_hold_armed = False
if CS.out.gasPressed:
CS.auto_hold_engaged = False
elif CS.regen_release_timer > 0.0:
CS.auto_hold_armed = False
elif not CS.auto_hold_armed and (CS.out.vEgo > 0.03 or ((CS.out.standstill or CS.out.vEgo < 0.02) and CS.out.brakePressed)):
@@ -429,7 +718,7 @@ class CarController(CarControllerBase):
if CS.out.vEgo > 0.1 or CS.out.gasPressed or CS.out.gearShifter not in AUTO_HOLD_DRIVE_GEARS:
self.auto_hold_brake = 0
elif CS.out.brakePressed or stock_hold_apply_brake > 0:
self.auto_hold_brake = estimate_auto_hold_brake(CS.out.brake, stock_hold_apply_brake)
self.auto_hold_brake = estimate_auto_hold_brake(CS.out.brake, stock_hold_apply_brake, self.CP)
if self.frame % 25 == 0:
try:
@@ -520,10 +809,23 @@ class CarController(CarControllerBase):
CS.auto_hold_armed,
CS.auto_hold_engaged,
CS.out.brakePressed,
CS.out.gasPressed,
CS.out.standstill,
CC.longActive,
CS.out.regenBraking,
CS.out.vEgo,
get_auto_hold_stop_threshold(self.CP, CS.auto_hold_engaged),
)
bolt_acc_pedal_friction_experiment = supports_bolt_acc_pedal_friction_experiment(self.CP)
bolt_acc_pedal_friction_main_on = bolt_acc_pedal_friction_experiment and CS.out.cruiseState.available
if not bolt_acc_pedal_friction_main_on:
self.bolt_acc_pedal_friction_low_speed_active = False
volt_one_pedal_braking = volt_one_pedal_active and self.volt_one_pedal_brake > 0
volt_one_pedal_hold_active = (
volt_one_pedal_braking and
not auto_hold_active and
CS.one_pedal_drive_time >= AUTO_HOLD_MIN_DRIVE_TIME_S and
(CS.out.standstill or CS.out.vEgo < 0.02)
)
# Steering (Active: 50Hz, inactive: 10Hz)
@@ -578,6 +880,7 @@ class CarController(CarControllerBase):
# ASCM sends max regen when not enabled
self.apply_gas = self.params.INACTIVE_REGEN
self.apply_brake = 0
self.bolt_acc_pedal_friction_low_speed_active = False
self.planner_regen_hold = False
self.regen_paddle_pressed = False
self.regen_paddle_timer = 0
@@ -585,7 +888,7 @@ class CarController(CarControllerBase):
self.regen_release_counter = 0
self.regen_min_on_frames = 0
self.regen_min_off_frames = 0
elif near_stop and stopping and not CC.cruiseControl.resume:
elif should_use_fixed_stopping_brake(self.CP, near_stop, stopping, CC.cruiseControl.resume):
stop_accel = getattr(starpilot_toggles, "stopAccel", self.CP.stopAccel)
self.apply_gas = self.params.INACTIVE_REGEN
self.apply_brake = int(min(-100 * stop_accel, self.params.MAX_BRAKE))
@@ -629,7 +932,16 @@ class CarController(CarControllerBase):
if testing_ground.use_1:
accel_max = min(accel_max, np.interp(CS.out.vEgo, [0.0, 4.0, 12.0], [1.25, 1.6, self.params.ACCEL_MAX]))
accel_cmd = float(np.clip(actuators.accel + accel_due_to_pitch, self.params.ACCEL_MIN, accel_max))
accel_input = actuators.accel + accel_due_to_pitch
if (
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_cmd = float(np.clip(accel_input, self.params.ACCEL_MIN, accel_max))
torque = self.tireRadius * ((self.mass * accel_cmd) + (0.5 * self.coeffDrag * self.frontalArea * self.airDensity * CS.out.vEgo ** 2))
scaled_torque = torque + self.params.ZERO_GAS
apply_gas_torque = np.clip(scaled_torque, self.params.MAX_ACC_REGEN, gas_max)
@@ -637,9 +949,27 @@ class CarController(CarControllerBase):
if testing_ground.use_1:
brake_switch_bias = get_testing_ground_1_brake_switch_bias(CS.out.vEgo)
brake_switch = min(self.params.ZERO_GAS, brake_switch + brake_switch_bias)
if bolt_acc_pedal_friction_main_on:
planner_brake_switch = get_bolt_acc_pedal_planner_brake_switch(
CS.out.vEgo, self.params, self.tireRadius, self.mass, self.coeffDrag, self.frontalArea, self.airDensity,
)
brake_switch = get_bolt_acc_pedal_effective_brake_switch(brake_switch, planner_brake_switch)
brake_accel = min((scaled_torque - brake_switch) / (self.tireRadius * self.mass), 0)
self.apply_gas = int(round(apply_gas_torque))
self.apply_brake = int(round(np.interp(brake_accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
if bolt_acc_pedal_friction_main_on:
if self.apply_brake > 0:
full_brake_accel = min(
self.params.ACCEL_MIN + (0.5 * self.coeffDrag * self.frontalArea * self.airDensity * CS.out.vEgo ** 2) / self.mass +
(self.params.ZERO_GAS - brake_switch) / (self.tireRadius * self.mass),
-0.1,
)
self.apply_brake = get_bolt_acc_pedal_friction_brake(
self.apply_brake, full_brake_accel, CS.out.vEgo, self.params,
)
self.apply_brake, self.bolt_acc_pedal_friction_low_speed_active = shape_bolt_acc_pedal_low_speed_friction(
self.apply_brake, CS.out.vEgo, stopping, self.bolt_acc_pedal_friction_low_speed_active,
)
if self.apply_brake > 0:
self.apply_gas = self.params.INACTIVE_REGEN
@@ -650,15 +980,19 @@ class CarController(CarControllerBase):
# gas interceptor only used for full long control on cars without ACC
interceptor_gas_cmd, press_regen_paddle = self.calc_pedal_command(actuators.accel, CC.longActive, CS.out.vEgo)
if volt_one_pedal_braking:
self.apply_gas = self.params.INACTIVE_REGEN
self.apply_brake = max(self.apply_brake, self.volt_one_pedal_brake)
maneuver_sng_launch = self.longitudinal_maneuver_mode and self.is_volt
if (
self.CP.enableGasInterceptorDEPRECATED and
self.apply_gas > self.params.INACTIVE_REGEN and
use_interceptor_sng_launch(self.CP, CS, maneuver_sng_launch)
):
interceptor_gas_cmd = self.params.SNG_INTERCEPTOR_GAS
if maneuver_sng_launch:
interceptor_gas_cmd = max(interceptor_gas_cmd, float(np.interp(actuators.accel, [0.0, 1.0, 2.0], [self.params.SNG_INTERCEPTOR_GAS, 0.11, 0.16])))
interceptor_gas_cmd = get_interceptor_sng_gas_cmd(
self.CP, interceptor_gas_cmd, actuators.accel, self.params, maneuver_sng_launch,
)
self.apply_brake = 0
self.apply_gas = self.params.INACTIVE_REGEN
@@ -692,6 +1026,27 @@ class CarController(CarControllerBase):
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.DECEL_SET))
if self.CP.enableGasInterceptorDEPRECATED:
can_sends.append(create_gas_interceptor_command(self.packer_pt, interceptor_gas_cmd, idx))
if bolt_acc_pedal_friction_experiment:
friction_brake_bus = get_friction_brake_bus(self.CP)
if self.CP.networkLocation == NetworkLocation.fwdCamera:
at_full_stop = at_full_stop and stopping
experiment_brake, self.bolt_acc_pedal_friction_release_frames, should_send_bolt_acc_pedal_friction = \
get_bolt_acc_pedal_friction_command_state(
self.apply_brake,
bolt_acc_pedal_friction_main_on,
self.bolt_acc_pedal_friction_release_frames,
)
# This fingerprint is routed through the CC-only pedal path, so it
# does not fall through to the normal friction-brake sender below.
# Never apply stock friction with cruise main off, but do send a short
# explicit zero-brake unwind so the last nonzero stock-EBCM command
# cannot linger after a disengage or main-off event.
if should_send_bolt_acc_pedal_friction:
can_sends.append(gmcan.create_friction_brake_command(
self.packer_ch, friction_brake_bus, experiment_brake, idx, bolt_acc_pedal_friction_main_on,
near_stop, at_full_stop, self.CP))
if self.CP.carFingerprint not in CC_ONLY_CAR:
friction_brake_bus = get_friction_brake_bus(self.CP)
# GM Camera exceptions
@@ -709,57 +1064,65 @@ class CarController(CarControllerBase):
acc_engaged = CC.enabled
if auto_hold_active:
hold_brake = self.auto_hold_brake or estimate_auto_hold_brake(CS.out.brake, self.apply_brake)
hold_brake = max(self.volt_one_pedal_brake, self.auto_hold_brake or estimate_auto_hold_brake(CS.out.brake, self.apply_brake, self.CP))
hold_standstill = CS.pcm_acc_status == AccState.STANDSTILL
hold_near_stop = CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE
can_sends.append(gmcan.create_friction_brake_command(
self.packer_ch, friction_brake_bus, hold_brake, idx, False, hold_near_stop, hold_standstill, self.CP))
self.packer_ch, friction_brake_bus, hold_brake, idx, False, hold_near_stop, hold_standstill,
self.CP, allow_near_stop_mode=True))
CS.auto_hold_engaged = True
CS.auto_hold_fault_suppression_timer = 1.0
elif volt_one_pedal_hold_active:
hold_brake = max(self.volt_one_pedal_brake, self.auto_hold_brake or estimate_auto_hold_brake(0.0, self.volt_one_pedal_brake, self.CP))
hold_standstill = CS.pcm_acc_status == AccState.STANDSTILL
hold_near_stop = CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE
can_sends.append(gmcan.create_friction_brake_command(
self.packer_ch, friction_brake_bus, hold_brake, idx, False, hold_near_stop, hold_standstill,
self.CP, allow_near_stop_mode=True))
CS.auto_hold_engaged = True
CS.auto_hold_fault_suppression_timer = 1.0
else:
if volt_one_pedal_braking:
at_full_stop = at_full_stop or CS.pcm_acc_status == AccState.STANDSTILL
near_stop = near_stop or (CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE)
# GasRegenCmdActive needs to be 1 to avoid cruise faults. It describes the ACC state, not actuation
can_sends.append(gmcan.create_gas_regen_command(
self.packer_pt, CanBus.POWERTRAIN, self.apply_gas, idx, acc_engaged, at_full_stop,
include_always_one3=self.CP.carFingerprint in kaofui_cars, use_volt_layout=self.is_volt))
can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, friction_brake_bus, self.apply_brake,
idx, CC.enabled, near_stop, at_full_stop, self.CP))
idx, CC.enabled, near_stop, at_full_stop, self.CP,
allow_near_stop_mode=volt_one_pedal_braking))
CS.auto_hold_engaged = False
if should_send_acc_dashboard_status(self.CP, dash_speed_spoof_active):
acc_dashboard_status = get_acc_dashboard_status_values(CC.enabled, hud_v_cruise * CV.MS_TO_KPH, hud_control, CS)
fcw_alert = get_acc_dashboard_fcw_alert(hud_alert, CS)
can_sends.append(gmcan.create_acc_dashboard_command(self.packer_pt, CanBus.POWERTRAIN,
acc_dashboard_status, fcw_alert))
acc_dashboard_status_active = get_acc_dashboard_status_active(self.CP, CC)
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))
# Radar needs to know current speed and yaw rate (50hz),
# and that ADAS is alive (10hz)
if not self.CP.radarUnavailable:
send_adas = True
if should_send_adas_status(self.CP, self.CP.carFingerprint in kaofui_cars):
tt = self.frame * DT_CTRL
if self.CP.carFingerprint in kaofui_cars:
if self.CP.carFingerprint not in ASCM_INT:
send_adas = (self.CP.networkLocation != NetworkLocation.fwdCamera) and (self.CP.carFingerprint not in SDGM_CAR)
if send_adas:
tt = self.frame * DT_CTRL
if self.CP.carFingerprint in kaofui_cars:
time_and_headlights_step = 10
speed_and_accelerometer_step = 2
if self.frame % time_and_headlights_step == 0:
idx = (self.frame // time_and_headlights_step) % 4
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
if self.frame % speed_and_accelerometer_step == 0:
idx = (self.frame // speed_and_accelerometer_step) % 4
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
else:
time_and_headlights_step = 20
if self.frame % time_and_headlights_step == 0:
idx = (self.frame // time_and_headlights_step) % 4
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
time_and_headlights_step = 10
speed_and_accelerometer_step = 2
if self.frame % time_and_headlights_step == 0:
idx = (self.frame // time_and_headlights_step) % 4
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
if self.frame % speed_and_accelerometer_step == 0:
idx = (self.frame // speed_and_accelerometer_step) % 4
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
else:
time_and_headlights_step = 20
if self.frame % time_and_headlights_step == 0:
idx = (self.frame // time_and_headlights_step) % 4
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
keepalive_step = get_adas_keepalive_step(self.CP, self.CP.carFingerprint in kaofui_cars)
if keepalive_step is not None and self.frame % keepalive_step == 0:
@@ -785,14 +1148,33 @@ class CarController(CarControllerBase):
else:
if self.frame % 4 == 0 and auto_hold_active:
idx = (self.frame // 4) % 4
hold_brake = self.auto_hold_brake or estimate_auto_hold_brake(CS.out.brake, stock_hold_apply_brake)
hold_brake = max(self.volt_one_pedal_brake, self.auto_hold_brake or estimate_auto_hold_brake(CS.out.brake, stock_hold_apply_brake, self.CP))
hold_standstill = CS.pcm_acc_status == AccState.STANDSTILL
hold_near_stop = CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE
can_sends.append(gmcan.create_friction_brake_command(
self.packer_ch, get_friction_brake_bus(self.CP), hold_brake, idx, False, hold_near_stop, hold_standstill, self.CP))
self.packer_ch, get_friction_brake_bus(self.CP), hold_brake, idx, False, hold_near_stop, hold_standstill,
self.CP, allow_near_stop_mode=True))
CS.auto_hold_engaged = True
CS.auto_hold_fault_suppression_timer = 1.0
elif self.frame % 4 == 0 and volt_one_pedal_hold_active:
idx = (self.frame // 4) % 4
hold_brake = max(self.volt_one_pedal_brake, self.auto_hold_brake or estimate_auto_hold_brake(0.0, self.volt_one_pedal_brake, self.CP))
hold_standstill = CS.pcm_acc_status == AccState.STANDSTILL
hold_near_stop = CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE
can_sends.append(gmcan.create_friction_brake_command(
self.packer_ch, get_friction_brake_bus(self.CP), hold_brake, idx, False, hold_near_stop, hold_standstill,
self.CP, allow_near_stop_mode=True))
CS.auto_hold_engaged = True
CS.auto_hold_fault_suppression_timer = 1.0
elif self.frame % 4 == 0 and volt_one_pedal_braking:
idx = (self.frame // 4) % 4
near_stop = CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE
can_sends.append(gmcan.create_friction_brake_command(
self.packer_ch, get_friction_brake_bus(self.CP), self.volt_one_pedal_brake, idx, False, near_stop, False,
self.CP, allow_near_stop_mode=True))
CS.auto_hold_engaged = False
elif self.frame % 4 == 0:
self.apply_brake = 0
CS.auto_hold_engaged = False
# While car is braking, cancel button causes ECM to enter a soft disable state with a fault status.
+63 -30
View File
@@ -33,6 +33,13 @@ AUTO_HOLD_REGEN_RELEASE_COOLDOWN_S = 1.0
BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise,
CruiseButtons.MAIN: ButtonType.mainCruise, CruiseButtons.CANCEL: ButtonType.cancel}
HARD_BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise}
NORMAL_CRUISE_BUTTONS = (CruiseButtons.RES_ACCEL, CruiseButtons.DECEL_SET)
def get_hard_cruise_buttons(steering_button_msg: dict) -> int:
return steering_button_msg.get("ACCButtonsHard", CruiseButtons.INIT)
GearShifter = structs.CarState.GearShifter
BOLT_GEN1_CANCEL_PERSONALITY_CARS = {
@@ -45,6 +52,19 @@ BOLT_CANCEL_BUTTON_CARS = BOLT_GEN1_CANCEL_PERSONALITY_CARS | {
}
def update_auto_hold_drive_timers(in_drive_for_hold: bool, moving_for_hold: bool,
auto_hold_drive_time: float, one_pedal_drive_time: float) -> tuple[float, float]:
if in_drive_for_hold:
if moving_for_hold:
auto_hold_drive_time = min(auto_hold_drive_time + DT_CTRL, AUTO_HOLD_MIN_DRIVE_TIME_S)
one_pedal_drive_time = min(one_pedal_drive_time + DT_CTRL, AUTO_HOLD_MIN_DRIVE_TIME_S)
else:
auto_hold_drive_time = 0.0
one_pedal_drive_time = 0.0
return auto_hold_drive_time, one_pedal_drive_time
class CarState(CarStateBase):
def __init__(self, CP, FPCP):
super().__init__(CP, FPCP)
@@ -64,11 +84,14 @@ class CarState(CarStateBase):
self.prev_distance_button = 0
self.distance_button = 0
self.hard_cruise_buttons = CruiseButtons.INIT
self.force_reset_cruise_buttons = False
self.single_pedal_mode = False
self.auto_hold_armed = False
self.auto_hold_engaged = False
self.auto_hold_drive_time = 0.0
self.one_pedal_drive_time = 0.0
self.auto_hold_fault_suppression_timer = 0.0
self.regen_release_timer = 0.0
self.user_regen_paddle_pressed = False
@@ -81,12 +104,6 @@ class CarState(CarStateBase):
self.lkas_enabled = 0
self.pcm_acc_status = AccState.OFF
self.stock_fcw_alert = 0
self.stock_acc_cruise_state = 0
self.stock_acc_lead_car = 0
self.stock_acc_resume_button = 0
self.stock_acc_speed_setpoint_kph = 0.0
self.stock_acc_gap_level = 0
self.stock_acc_cmd_active = 0
def update_button_enable(self, buttonEvents: list[structs.CarState.ButtonEvent]):
if not self.CP.pcmCruise:
@@ -119,21 +136,36 @@ class CarState(CarStateBase):
sdgm_non_volt = self.CP.carFingerprint in SDGM_CAR and self.CP.carFingerprint not in kaofui_state_cars
prev_cruise_buttons = self.cruise_buttons
prev_hard_cruise_buttons = self.hard_cruise_buttons
prev_distance_button = self.distance_button
if not sdgm_non_volt:
self.cruise_buttons = pt_cp.vl["ASCMSteeringButton"]["ACCButtons"]
self.distance_button = pt_cp.vl["ASCMSteeringButton"]["DistanceButton"]
self.buttons_counter = pt_cp.vl["ASCMSteeringButton"]["RollingCounter"]
self.steering_button_checksum = pt_cp.vl["ASCMSteeringButton"]["SteeringButtonChecksum"]
steering_button_msg = pt_cp.vl["ASCMSteeringButton"]
self.cruise_buttons = steering_button_msg["ACCButtons"]
self.hard_cruise_buttons = get_hard_cruise_buttons(steering_button_msg)
self.distance_button = steering_button_msg["DistanceButton"]
self.buttons_counter = steering_button_msg["RollingCounter"]
self.steering_button_checksum = steering_button_msg["SteeringButtonChecksum"]
self.steering_button_ts_nanos = pt_cp.ts_nanos["ASCMSteeringButton"]["ACCButtons"]
acc_always_one = pt_cp.vl["ASCMSteeringButton"]["ACCAlwaysOne"]
acc_hidden_bit = pt_cp.vl["ASCMSteeringButton"].get("ACCHiddenBit", 0)
acc_always_one = steering_button_msg["ACCAlwaysOne"]
acc_hidden_bit = steering_button_msg.get("ACCHiddenBit", 0)
self.steering_button_prefix = (int(acc_always_one) & 1) | ((int(acc_hidden_bit) & 1) << 6)
else:
self.cruise_buttons = cam_cp.vl["ASCMSteeringButton"]["ACCButtons"]
self.distance_button = cam_cp.vl["ASCMSteeringButton"]["DistanceButton"]
self.buttons_counter = cam_cp.vl["ASCMSteeringButton"]["RollingCounter"]
steering_button_msg = cam_cp.vl["ASCMSteeringButton"]
self.cruise_buttons = steering_button_msg["ACCButtons"]
self.hard_cruise_buttons = get_hard_cruise_buttons(steering_button_msg)
self.distance_button = steering_button_msg["DistanceButton"]
self.buttons_counter = steering_button_msg["RollingCounter"]
self.steering_button_ts_nanos = cam_cp.ts_nanos["ASCMSteeringButton"]["ACCButtons"]
# A GM hard press keeps the normal cruise button signal active too. Suppress
# the normal button until the wheel reports a different normal state.
if self.hard_cruise_buttons != CruiseButtons.INIT and self.cruise_buttons in NORMAL_CRUISE_BUTTONS:
self.force_reset_cruise_buttons = True
if self.force_reset_cruise_buttons and self.cruise_buttons in NORMAL_CRUISE_BUTTONS:
self.cruise_buttons = CruiseButtons.UNPRESS
elif self.force_reset_cruise_buttons and self.cruise_buttons not in NORMAL_CRUISE_BUTTONS:
self.force_reset_cruise_buttons = False
self.pscm_status = copy.copy(pt_cp.vl["PSCMStatus"])
self.moving_backward = (pt_cp.vl["EBCMWheelSpdRear"]["RLWheelDir"] == 2) or (pt_cp.vl["EBCMWheelSpdRear"]["RRWheelDir"] == 2)
@@ -178,7 +210,10 @@ class CarState(CarStateBase):
ret.brakePressed = ret.brake >= VOLT_EBCM_BRAKE_PRESSED_THRESHOLD
elif self.CP.carFingerprint in {CAR.CHEVROLET_MALIBU_CC} or (self.CP.carFingerprint == CAR.CHEVROLET_BLAZER and not no_accel_pos):
ret.brakePressed = ret.brake >= 8
elif (self.CP.flags & GMFlags.FORCE_BRAKE_C9.value) or ((self.CP.networkLocation == NetworkLocation.fwdCamera) and (self.CP.carFingerprint != CAR.CHEVROLET_BLAZER)):
elif (self.CP.flags & GMFlags.FORCE_BRAKE_C9.value) or (
self.CP.networkLocation == NetworkLocation.fwdCamera and
self.CP.carFingerprint not in (SDGM_CAR | ASCM_INT | {CAR.CHEVROLET_BLAZER})
):
ret.brakePressed = pt_cp.vl["ECMEngineStatus"]["BrakePressed"] != 0
else:
# Some Volt 2016-17 have loose brake pedal push rod retainers which causes the ECM to believe
@@ -189,13 +224,10 @@ class CarState(CarStateBase):
ret.brakePressed = ret.brake >= analog_thresh
in_drive_for_hold = ret.gearShifter in (GearShifter.drive, GearShifter.low, GearShifter.manumatic)
if in_drive_for_hold:
if ret.brakePressed:
self.auto_hold_drive_time = AUTO_HOLD_MIN_DRIVE_TIME_S
else:
self.auto_hold_drive_time = min(self.auto_hold_drive_time + DT_CTRL, AUTO_HOLD_MIN_DRIVE_TIME_S)
else:
self.auto_hold_drive_time = 0.0
self.auto_hold_drive_time, self.one_pedal_drive_time = update_auto_hold_drive_timers(
in_drive_for_hold, ret.vEgo > 0.1, self.auto_hold_drive_time, self.one_pedal_drive_time
)
if not in_drive_for_hold:
self.auto_hold_armed = False
self.auto_hold_engaged = False
@@ -280,12 +312,6 @@ class CarState(CarStateBase):
acc_dashboard_status = cam_cp.vl["ASCMActiveCruiseControlStatus"]
if self.CP.carFingerprint not in CC_ONLY_CAR:
ret.cruiseState.speed = acc_dashboard_status["ACCSpeedSetpoint"] * CV.KPH_TO_MS
self.stock_acc_cruise_state = int(acc_dashboard_status["ACCCruiseState"])
self.stock_acc_lead_car = int(acc_dashboard_status["ACCLeadCar"])
self.stock_acc_resume_button = int(acc_dashboard_status["ACCResumeButton"])
self.stock_acc_speed_setpoint_kph = float(acc_dashboard_status["ACCSpeedSetpoint"])
self.stock_acc_gap_level = int(acc_dashboard_status["ACCGapLevel"])
self.stock_acc_cmd_active = int(acc_dashboard_status["ACCCmdActive"])
# Preserve the stock camera FCW level from 0x370 so the controller can
# replay it when that message is blocked and spoofed by openpilot long.
self.stock_fcw_alert = int(acc_dashboard_status["FCWAlert"])
@@ -374,19 +400,26 @@ class CarState(CarStateBase):
lkas_events = [] if (suppress_malibu_side_buttons or suppress_bolt_cancel_lkas) else create_button_events(
self.lkas_enabled, self.lkas_previously_enabled, {1: ButtonType.lkas}
)
hard_cruise_events = create_button_events(
self.hard_cruise_buttons, prev_hard_cruise_buttons, HARD_BUTTONS_DICT, unpressed_btn=CruiseButtons.INIT
)
# Don't add events if transitioning from INIT, unless it's to an actual button.
if self.cruise_buttons != CruiseButtons.UNPRESS or prev_cruise_buttons != CruiseButtons.INIT:
if (self.cruise_buttons != CruiseButtons.UNPRESS or prev_cruise_buttons != CruiseButtons.INIT or
self.hard_cruise_buttons != CruiseButtons.INIT or prev_hard_cruise_buttons != CruiseButtons.INIT):
ret.buttonEvents = [
*cruise_events,
*distance_events,
*lkas_events,
*hard_cruise_events,
]
if ret.vEgo < self.CP.minSteerSpeed:
ret.lowSpeedAlert = True
fp_ret = custom.StarPilotCarState.new_message()
fp_ret.accelHardCruise = self.hard_cruise_buttons == CruiseButtons.RES_ACCEL or prev_hard_cruise_buttons == CruiseButtons.RES_ACCEL
fp_ret.decelHardCruise = self.hard_cruise_buttons == CruiseButtons.DECEL_SET or prev_hard_cruise_buttons == CruiseButtons.DECEL_SET
if bolt_cancel_button and self.cruise_buttons == CruiseButtons.CANCEL:
fp_ret.cancelPressed = True
fp_ret.sportGear = pt_cp.vl["SportMode"]["SportMode"] == 1
@@ -7,6 +7,7 @@ from opendbc.car.gm.values import CAR
CAMERA_DIAGNOSTIC_ADDRESS = 0x24b
CAMERA_DIAGNOSTIC_RX_ADDRESS = 0x64b
SASCM_ADDRESS = 0x2FF
FINGERPRINTS = {
@@ -210,6 +211,7 @@ FINGERPRINTS.update({
CAR.GMC_ACADIA_ASCM: FINGERPRINTS[CAR.GMC_ACADIA],
CAR.CHEVROLET_MALIBU_ASCM: FINGERPRINTS[CAR.CHEVROLET_MALIBU],
CAR.CADILLAC_ESCALADE_ASCM: FINGERPRINTS[CAR.CADILLAC_ESCALADE],
CAR.CADILLAC_ESCALADE_ESV_2019_ASCM: [{**fp, SASCM_ADDRESS: 8} for fp in FINGERPRINTS[CAR.CADILLAC_ESCALADE_ESV_2019]],
CAR.CHEVROLET_SUBURBAN: FINGERPRINTS[CAR.CHEVROLET_SUBURBAN_CC],
CAR.GMC_YUKON_CC: FINGERPRINTS[CAR.GMC_YUKON],
CAR.CADILLAC_XT6: FINGERPRINTS[CAR.CHEVROLET_TRAVERSE],
+18 -13
View File
@@ -179,22 +179,28 @@ def create_ecm_cruise_control_command(packer, bus, enabled, target_speed_kph):
return CanData(0x3D1, bytes(dat), bus)
def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_stop, at_full_stop, CP):
def get_friction_brake_mode(apply_brake, enabled, near_stop, at_full_stop, CP, allow_near_stop_mode=False):
mode = 0x1
# TODO: Understand this better. Volts and ICE Camera ACC cars are 0x1 when enabled with no brake
if enabled and CP.carFingerprint in (CAR.CHEVROLET_BOLT_ACC_2022_2023,):
if enabled and CP.carFingerprint in (CAR.CHEVROLET_BOLT_ACC_2022_2023, CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL):
mode = 0x9
if apply_brake > 0:
mode = 0xa
if at_full_stop:
mode = 0xd
elif allow_near_stop_mode and near_stop:
# Stock Volt auto hold can run with cruise main on but ACC inactive, so
# there is no stock STANDSTILL state to promote 0xa -> 0xd. Restore the
# older near-stop hold mode only for that path.
mode = 0xb
# TODO: this is to have GM bringing the car to complete stop,
# but currently it conflicts with OP controls, so turned off. Not set by all cars
#elif near_stop:
# mode = 0xb
return mode
def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_stop, at_full_stop, CP, allow_near_stop_mode=False):
mode = get_friction_brake_mode(apply_brake, enabled, near_stop, at_full_stop, CP, allow_near_stop_mode)
brake = (0x1000 - apply_brake) & 0xfff
checksum = (0x10000 - (mode << 12) - brake - idx) & 0xffff
@@ -209,18 +215,17 @@ def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_s
return packer.make_can_msg("EBCMFrictionBrakeCmd", bus, values)
def create_acc_dashboard_command(packer, bus, status_values, fcw_alert):
target_speed = min(max(float(status_values.get("ACCSpeedSetpoint", 0.0)), 0.0), 255.0)
def create_acc_dashboard_command(packer, bus, enabled, target_speed_kph, hud_control, fcw_alert):
target_speed = min(target_speed_kph, 255)
values = {
"ACCAlwaysOne": 1,
"ACCCruiseState": int(status_values.get("ACCCruiseState", 0)) & 0x7,
"ACCResumeButton": int(status_values.get("ACCResumeButton", 0)) & 0x1,
"ACCResumeButton": 0,
"ACCSpeedSetpoint": target_speed,
"ACCGapLevel": int(status_values.get("ACCGapLevel", 0)) & 0x3,
"ACCCmdActive": int(status_values.get("ACCCmdActive", 0)) & 0x1,
"ACCGapLevel": hud_control.leadDistanceBars * enabled, # 3 "far", 0 "inactive"
"ACCCmdActive": enabled,
"ACCAlwaysOne2": 1,
"ACCLeadCar": int(status_values.get("ACCLeadCar", 0)) & 0x1,
"ACCLeadCar": hud_control.leadVisible,
"FCWAlert": int(fcw_alert) & 0x3,
}
+53 -13
View File
@@ -62,9 +62,13 @@ NON_LINEAR_TORQUE_PARAMS = {
"left": [3.8, 0.81, 0.24, 0.0465122],
"right": [3.8, 0.81, 0.24, 0.0465122],
},
CAR.CADILLAC_XT4: {
"left": [2.4, 0.95, 0.28, 0.0],
"right": [2.4, 0.95, 0.28, 0.0],
},
CAR.CHEVROLET_VOLT: {
"left": [1.5, 1.0, 0.155, 0.0],
"right": [1.5, 1.0, 0.155, 0.0],
"left": [1.525, 1.05, 0.155, 0.0],
"right": [1.525, 0.95, 0.150, 0.0],
},
}
@@ -129,7 +133,11 @@ class CarInterface(CarInterfaceBase):
@staticmethod
def get_pid_accel_limits(CP, current_speed, cruise_speed):
if CP.enableGasInterceptorDEPRECATED and bool(CP.flags & GMFlags.PEDAL_LONG.value):
if CP.carFingerprint in BOLT_PEDAL_LONG_CARS:
if CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL:
accel_min = CarControllerParams.ACCEL_MIN
accel_max = np.interp(current_speed, [0.0, 1.5, 4.0, 8.0, 15.0],
[0.54, 0.74, 1.03, 1.46, CarControllerParams.ACCEL_MAX])
elif CP.carFingerprint in BOLT_PEDAL_LONG_CARS:
accel_min = np.interp(current_speed, [0.0, 1.5, 4.0, 8.0, 15.0, 30.0],
[-0.93, -1.28, -1.98, -2.58, -2.86, -2.95])
accel_max = np.interp(current_speed, [0.0, 1.5, 4.0, 8.0, 15.0],
@@ -213,6 +221,10 @@ class CarInterface(CarInterfaceBase):
gm_auto_hold = params.get_bool("GMAutoHold")
except UnknownKeyName:
gm_auto_hold = False
try:
volt_one_pedal_mode = params.get_bool("VoltOnePedalMode")
except UnknownKeyName:
volt_one_pedal_mode = False
ret.brand = "gm"
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.gm)]
@@ -420,7 +432,7 @@ class CarInterface(CarInterfaceBase):
elif candidate == CAR.CADILLAC_ESCALADE_ASCM:
CarInterfaceBase.configure_torque_tune(CAR.CADILLAC_ESCALADE, ret.lateralTuning)
elif candidate in (CAR.CADILLAC_ESCALADE_ESV, CAR.CADILLAC_ESCALADE_ESV_2019):
elif candidate in (CAR.CADILLAC_ESCALADE_ESV, CAR.CADILLAC_ESCALADE_ESV_2019, CAR.CADILLAC_ESCALADE_ESV_2019_ASCM):
ret.minEnableSpeed = -1. # engage speed is decided by pcm
if candidate == CAR.CADILLAC_ESCALADE_ESV:
@@ -429,7 +441,8 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.pid.kf = 0.000045
else:
ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
torque_candidate = CAR.CADILLAC_ESCALADE_ESV_2019 if candidate == CAR.CADILLAC_ESCALADE_ESV_2019_ASCM else candidate
CarInterfaceBase.configure_torque_tune(torque_candidate, ret.lateralTuning)
elif candidate in (
CAR.CHEVROLET_BOLT_ACC_2022_2023,
@@ -515,7 +528,19 @@ class CarInterface(CarInterfaceBase):
if not ret.openpilotLongitudinalControl:
ret.minEnableSpeed = -1.
if candidate == CAR.CHEVROLET_BLAZER:
# The Blazer builds brake torque noticeably later than the rest of the GM set.
# A slightly larger planner delay estimate starts the request earlier and keeps
# stopped-lead approaches from turning into a late, harsh max-brake catch-up.
ret.longitudinalActuatorDelay = 0.7
ret.longitudinalTuning.kpBP = [0.0, 4.0, 12.0, 35.0]
ret.longitudinalTuning.kpV = [0.09, 0.075, 0.055, 0.040]
ret.longitudinalTuning.kiBP = [0.0, 4.0, 12.0, 35.0]
ret.longitudinalTuning.kiV = [0.03, 0.04, 0.055, 0.07]
ret.minEnableSpeed = 5 * CV.KPH_TO_MS
ret.stoppingDecelRate = 1.0
ret.vEgoStopping = 0.35
ret.vEgoStarting = 0.35
ret.stopAccel = -0.30
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate == CAR.BUICK_BABYENCLAVE:
@@ -645,7 +670,7 @@ class CarInterface(CarInterfaceBase):
# Exception for flashed cars, or cars whose camera was removed.
missing_camera_msg = CAM_MSG not in fingerprint.get(CanBus.CAMERA, {})
if (ret.networkLocation == NetworkLocation.fwdCamera or candidate in CC_ONLY_CAR) and missing_camera_msg and candidate not in SDGM_CAR:
if (ret.networkLocation == NetworkLocation.fwdCamera or candidate in CC_ONLY_CAR) and missing_camera_msg and candidate not in (ASCM_INT | SDGM_CAR):
ret.flags |= GMFlags.NO_CAMERA.value
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_NO_CAMERA.value
@@ -663,8 +688,8 @@ class CarInterface(CarInterfaceBase):
if remote_start_boots_comma:
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_REMOTE_START_BOOTS_COMMA.value
volt_stock_auto_hold_safety = (
gm_auto_hold and
volt_stock_friction_brake_safety = (
(gm_auto_hold or volt_one_pedal_mode) and
candidate in {
CAR.CHEVROLET_VOLT,
CAR.CHEVROLET_VOLT_2019,
@@ -672,13 +697,28 @@ class CarInterface(CarInterfaceBase):
CAR.CHEVROLET_VOLT_CAMERA,
}
)
if volt_stock_auto_hold_safety:
# Reuse the paddle-scheduler safety bit as a Volt auto-hold marker on
# non-pedal paths. Hold can run while OP longitudinal is configured but
# not currently active, so the bit must be present regardless of the
# current long-control mode.
if volt_stock_friction_brake_safety:
# Reuse the paddle-scheduler safety bit as a Volt stock friction-brake
# marker on non-pedal paths. 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.
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
volt_stock_one_pedal_safety = (
volt_one_pedal_mode and
candidate in {
CAR.CHEVROLET_VOLT,
CAR.CHEVROLET_VOLT_2019,
CAR.CHEVROLET_VOLT_ASCM,
CAR.CHEVROLET_VOLT_CAMERA,
}
)
if volt_stock_one_pedal_safety:
# Reuse the 3D1 scheduler bit as a Volt one-pedal marker on non-pedal
# ACC paths. The bit is ignored by the actual 3D1 scheduler unless the
# car is on a pedal-long CC-only path, so this stays isolated from Bolt.
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_PANDA_3D1_SCHED.value
use_panda_3d1_sched = (
ret.openpilotLongitudinalControl and
ret.enableGasInterceptorDEPRECATED and
@@ -1,6 +1,10 @@
import sys
import types
from types import SimpleNamespace
import numpy as np
import pytest
from opendbc.car import structs
fake_interfaces = types.ModuleType("opendbc.car.interfaces")
@@ -33,21 +37,40 @@ fake_testing_grounds.testing_ground = SimpleNamespace(use_1=False)
sys.modules.setdefault("openpilot.starpilot.common.testing_grounds", fake_testing_grounds)
from opendbc.car.gm.carcontroller import (
AUTO_HOLD_DRIVE_GEARS,
CarController,
estimate_auto_hold_brake,
get_adas_keepalive_step,
get_auto_hold_stop_threshold,
get_bolt_acc_pedal_friction_brake,
get_bolt_acc_pedal_friction_command_state,
get_bolt_acc_pedal_effective_brake_switch,
get_bolt_acc_pedal_planner_brake_switch,
get_bolt_pedal_long_accel_limit,
get_interceptor_sng_gas_cmd,
get_lka_steering_cmd_counter,
get_volt_one_pedal_target_decel,
get_testing_ground_1_brake_switch_bias,
get_acc_dashboard_status_active,
get_stock_cc_active_for_cancel,
shape_bolt_acc_pedal_low_speed_friction,
shape_truck_positive_accel,
should_use_fixed_stopping_brake,
should_activate_auto_hold,
should_activate_volt_one_pedal,
should_send_adas_status,
should_send_stock_long_cancel,
should_spoof_dash_speed,
should_spoof_ecm_cruise_status,
supports_bolt_acc_pedal_friction_experiment,
supports_volt_auto_hold,
supports_volt_one_pedal,
use_interceptor_sng_launch,
)
from opendbc.car.gm.values import AccState, CAR, GMFlags
from opendbc.car.gm.gmcan import get_friction_brake_mode
from opendbc.car.gm.values import AccState, CAR, CarControllerParams, GMFlags
from opendbc.car.structs import CarParams
from opendbc.car.common.conversions import Conversions as CV
def _cs(enabled, pcm_acc_status):
@@ -81,6 +104,7 @@ def _controller(car_fingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021):
controller.pedal_steady = 0.0
controller.aego = 0.0
controller.maneuver_paddle_mode = "auto"
controller.bolt_acc_pedal_friction_low_speed_active = False
return controller
@@ -96,12 +120,201 @@ def test_gen2_bolt_acc_pedal_cancel_uses_enabled_only():
assert not get_stock_cc_active_for_cancel(CP, _cs(False, AccState.ACTIVE))
def test_bolt_acc_pedal_friction_experiment_is_single_fingerprint_only():
assert supports_bolt_acc_pedal_friction_experiment(SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
))
assert not supports_bolt_acc_pedal_friction_experiment(SimpleNamespace(
carFingerprint=CAR.CHEVROLET_MALIBU_HYBRID_CC,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
))
assert not supports_bolt_acc_pedal_friction_experiment(SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
openpilotLongitudinalControl=False,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
))
def test_bolt_acc_pedal_friction_blend_preserves_zero_before_crossover():
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
assert get_bolt_acc_pedal_friction_brake(0, -2.8, 20.0, params) == 0
def test_bolt_acc_pedal_friction_blend_uses_full_brake_range_after_regen():
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
# Legacy mapping tops out early once regen has already consumed part of the
# decel request. The experiment remaps that reduced span back to full scale.
assert get_bolt_acc_pedal_friction_brake(286, -2.86, 20.0, params) == 400
def test_bolt_acc_pedal_friction_blend_biases_small_commands_upward_at_speed():
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
low_speed = get_bolt_acc_pedal_friction_brake(31, -2.86, 0.0, params)
high_speed = get_bolt_acc_pedal_friction_brake(31, -2.86, 20.0, params)
assert low_speed > 31
assert high_speed > low_speed
def test_bolt_acc_pedal_friction_blend_applies_a_minimum_pre_stop_command_at_speed():
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
assert get_bolt_acc_pedal_friction_brake(2, -2.86, 17.0, params) >= 18
def test_bolt_acc_pedal_friction_blend_boosts_midrange_commands_before_stopping_phase():
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
assert get_bolt_acc_pedal_friction_brake(40, -2.86, 15.0, params) >= 80
def test_bolt_acc_pedal_low_speed_friction_ignores_tiny_inactive_brake_requests():
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(9, 5.0, False, False)
assert apply_brake == 0
assert not active
def test_bolt_acc_pedal_low_speed_friction_uses_hysteresis_once_active():
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(24, 3.0, False, False)
assert apply_brake == 24
assert active
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(5, 3.0, False, active)
assert apply_brake == 0
assert not active
def test_bolt_acc_pedal_low_speed_friction_fades_out_at_standstill():
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(80, 0.2, True, True)
assert apply_brake == 0
assert not active
def test_bolt_acc_pedal_low_speed_friction_preserves_rolling_stop_authority():
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(80, 1.2, True, True)
assert 0 < apply_brake < 80
assert active
def test_bolt_acc_pedal_low_speed_friction_drops_out_before_two_clamp():
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(80, 0.9, True, True)
assert apply_brake == 0
assert not active
def test_bolt_pedal_long_accel_limit_matches_planner_regen_envelope():
assert get_bolt_pedal_long_accel_limit(6.66) == pytest.approx(-2.379, abs=1e-3)
assert get_bolt_pedal_long_accel_limit(3.0) == pytest.approx(-1.70, abs=1e-3)
def test_bolt_acc_pedal_planner_brake_switch_is_lower_than_stock_switch():
params = SimpleNamespace(ZERO_GAS=6150, BRAKE_SWITCH_LOOKUP_BP=[0.5, 10.0], BRAKE_SWITCH_LOOKUP_V=[6150, 5500])
v_ego = 6.66
stock_switch = int(round(np.interp(v_ego, params.BRAKE_SWITCH_LOOKUP_BP, params.BRAKE_SWITCH_LOOKUP_V)))
planner_switch = get_bolt_acc_pedal_planner_brake_switch(
v_ego, params, tire_radius=0.336, mass=1832.0, coeff_drag=0.30, frontal_area=2.35, air_density=1.225,
)
assert planner_switch < stock_switch
def test_bolt_acc_pedal_effective_brake_switch_never_suppresses_stock_friction():
params = SimpleNamespace(ZERO_GAS=6150, BRAKE_SWITCH_LOOKUP_BP=[0.5, 10.0], BRAKE_SWITCH_LOOKUP_V=[6150, 5500])
v_ego = 5.434
mass = 1805.0
tire_radius = 0.075 * 2.63779 + 0.1453
frontal_area = 1.05 * 2.63779 + 0.0679
coeff_drag = 0.30
air_density = 1.225
accel_cmd = -1.399
aero_drag_force = 0.5 * coeff_drag * frontal_area * air_density * v_ego ** 2
torque = tire_radius * ((mass * accel_cmd) + aero_drag_force)
scaled_torque = torque + params.ZERO_GAS
stock_switch = int(round(np.interp(v_ego, params.BRAKE_SWITCH_LOOKUP_BP, params.BRAKE_SWITCH_LOOKUP_V)))
planner_switch = get_bolt_acc_pedal_planner_brake_switch(
v_ego, params, tire_radius=tire_radius, mass=mass,
coeff_drag=coeff_drag, frontal_area=frontal_area, air_density=air_density,
)
effective_switch = get_bolt_acc_pedal_effective_brake_switch(stock_switch, planner_switch)
stock_brake_accel = min((scaled_torque - stock_switch) / (tire_radius * mass), 0)
effective_brake_accel = min((scaled_torque - effective_switch) / (tire_radius * mass), 0)
assert planner_switch < stock_switch
assert effective_switch == stock_switch
assert stock_brake_accel < 0
assert effective_brake_accel == stock_brake_accel
def test_bolt_acc_pedal_friction_command_state_requires_cruise_main_for_positive_brake():
command_brake, release_frames, should_send = get_bolt_acc_pedal_friction_command_state(120, False, 0)
assert command_brake == 0
assert release_frames == 0
assert not should_send
def test_bolt_acc_pedal_friction_command_state_sends_zero_unwind_after_main_off():
command_brake, release_frames, should_send = get_bolt_acc_pedal_friction_command_state(120, True, 0)
assert command_brake == 120
assert release_frames > 0
assert should_send
command_brake, release_frames, should_send = get_bolt_acc_pedal_friction_command_state(0, False, release_frames)
assert command_brake == 0
assert release_frames >= 0
assert should_send
def test_fixed_stopping_brake_is_disabled_for_bolt_acc_pedal_experiment():
CP = SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
)
assert not should_use_fixed_stopping_brake(CP, True, True, False)
def test_fixed_stopping_brake_stays_enabled_for_normal_acc_path():
CP = SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=False,
flags=0,
)
assert should_use_fixed_stopping_brake(CP, True, True, False)
assert not should_use_fixed_stopping_brake(CP, False, True, False)
assert not should_use_fixed_stopping_brake(CP, True, False, False)
assert not should_use_fixed_stopping_brake(CP, True, True, True)
def test_stock_cancel_is_suppressed_when_acc_is_faulted():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_CAMERA)
cs = _cs(True, AccState.FAULTED)
cs.out.accFaulted = True
assert not get_stock_cc_active_for_cancel(CP, cs)
assert get_stock_cc_active_for_cancel(CP, cs)
assert not should_send_stock_long_cancel(11, cs)
@@ -143,11 +356,35 @@ def test_live_camera_path_does_not_send_pt_keepalive():
assert get_adas_keepalive_step(cp, is_kaofui_car=True) is None
def test_ascm_int_cars_do_not_send_radar_status():
common = {
"networkLocation": CarParams.NetworkLocation.fwdCamera,
"radarUnavailable": False,
}
assert not should_send_adas_status(SimpleNamespace(carFingerprint=CAR.BUICK_LACROSSE_ASCM, **common), is_kaofui_car=True)
assert not should_send_adas_status(SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM, **common), is_kaofui_car=True)
def test_lacrosse_ascm_marks_acc_dashboard_active_for_aol_only():
cc = SimpleNamespace(enabled=False, latActive=True)
assert get_acc_dashboard_status_active(SimpleNamespace(carFingerprint=CAR.BUICK_LACROSSE_ASCM), cc)
assert not get_acc_dashboard_status_active(SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM), cc)
assert not get_acc_dashboard_status_active(SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023), cc)
def test_acc_dashboard_status_active_for_normal_enabled_cars():
cc = SimpleNamespace(enabled=True, latActive=False)
assert get_acc_dashboard_status_active(SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM), cc)
def test_volt_auto_hold_requires_toggle_supported_non_cc_only_volt_and_stock_safety():
stock_safety = [SimpleNamespace(safetyParam=0x8000)]
no_safety = [SimpleNamespace(safetyParam=0)]
assert supports_volt_auto_hold(
assert not supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
openpilotLongitudinalControl=True,
@@ -165,6 +402,15 @@ def test_volt_auto_hold_requires_toggle_supported_non_cc_only_volt_and_stock_saf
),
True,
)
assert supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
openpilotLongitudinalControl=True,
networkLocation=CarParams.NetworkLocation.fwdCamera,
safetyConfigs=stock_safety,
),
True,
)
assert supports_volt_auto_hold(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT,
@@ -208,6 +454,60 @@ def test_auto_hold_brake_estimate_uses_driver_or_op_brake_and_clamps():
assert estimate_auto_hold_brake(20.0, 40.0) == 110
assert estimate_auto_hold_brake(20.0, 160.0) == 160
assert estimate_auto_hold_brake(100.0, 400.0) == 240
assert estimate_auto_hold_brake(7.0, 0.0, SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_2019)) == 100
def test_volt_one_pedal_requires_toggle_supported_volt_stock_safety_and_ev_transmission():
stock_safety = [SimpleNamespace(safetyParam=0x8000)]
no_safety = [SimpleNamespace(safetyParam=0)]
assert supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
safetyConfigs=stock_safety,
transmissionType=structs.CarParams.TransmissionType.direct,
),
True,
)
assert not supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
safetyConfigs=no_safety,
transmissionType=structs.CarParams.TransmissionType.direct,
),
True,
)
assert not supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CC,
safetyConfigs=stock_safety,
transmissionType=structs.CarParams.TransmissionType.direct,
),
True,
)
assert not supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
safetyConfigs=stock_safety,
transmissionType=structs.CarParams.TransmissionType.automatic,
),
True,
)
assert not supports_volt_one_pedal(
SimpleNamespace(
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
safetyConfigs=stock_safety,
transmissionType=structs.CarParams.TransmissionType.direct,
),
False,
)
def test_auto_hold_drive_gears_accept_capnp_dynamic_enum_membership():
msg = structs.CarState.new_message()
msg.gearShifter = structs.CarState.GearShifter.drive
assert msg.gearShifter in AUTO_HOLD_DRIVE_GEARS
def test_auto_hold_activation_allows_direct_entry_from_stopped_brake_press():
@@ -216,6 +516,7 @@ def test_auto_hold_activation_allows_direct_entry_from_stopped_brake_press():
False,
False,
True,
False,
True,
False,
False,
@@ -229,6 +530,7 @@ def test_auto_hold_activation_stays_latched_after_brake_release():
False,
True,
False,
False,
True,
False,
False,
@@ -236,12 +538,42 @@ def test_auto_hold_activation_stays_latched_after_brake_release():
)
def test_volt_2019_auto_hold_engaged_uses_near_stop_creep_hysteresis():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_2019)
assert get_auto_hold_stop_threshold(CP, True) == CarControllerParams.NEAR_STOP_BRAKE_PHASE
assert should_activate_auto_hold(
True,
True,
True,
False,
False,
False,
False,
False,
0.05,
get_auto_hold_stop_threshold(CP, True),
)
assert not should_activate_auto_hold(
True,
True,
True,
False,
False,
False,
False,
False,
0.05,
)
def test_auto_hold_activation_blocks_when_long_is_active_or_motion_is_above_threshold():
assert not should_activate_auto_hold(
True,
True,
False,
True,
False,
True,
True,
False,
@@ -254,6 +586,8 @@ def test_auto_hold_activation_blocks_when_long_is_active_or_motion_is_above_thre
False,
False,
False,
False,
False,
0.03,
)
@@ -264,6 +598,7 @@ def test_auto_hold_activation_allows_standstill_even_if_speed_filter_is_slightly
True,
False,
False,
False,
True,
False,
False,
@@ -271,6 +606,150 @@ def test_auto_hold_activation_allows_standstill_even_if_speed_filter_is_slightly
)
def test_auto_hold_activation_releases_immediately_on_gas_press():
assert not should_activate_auto_hold(
True,
True,
True,
False,
True,
True,
False,
False,
0.0,
)
def test_volt_one_pedal_activation_requires_main_l_mode_and_no_driver_input():
assert should_activate_volt_one_pedal(
True,
True,
False,
False,
False,
False,
True,
structs.CarState.GearShifter.low,
3.0,
)
assert not should_activate_volt_one_pedal(
True,
False,
False,
False,
False,
False,
True,
structs.CarState.GearShifter.low,
3.0,
)
assert not should_activate_volt_one_pedal(
True,
True,
True,
False,
False,
False,
True,
structs.CarState.GearShifter.low,
3.0,
)
assert not should_activate_volt_one_pedal(
True,
True,
False,
True,
False,
False,
True,
structs.CarState.GearShifter.low,
3.0,
)
assert not should_activate_volt_one_pedal(
True,
True,
False,
False,
False,
True,
True,
structs.CarState.GearShifter.low,
3.0,
)
assert not should_activate_volt_one_pedal(
True,
True,
False,
False,
False,
False,
False,
structs.CarState.GearShifter.drive,
3.0,
)
def test_volt_one_pedal_target_decel_stays_active_above_low_speed_band():
assert get_volt_one_pedal_target_decel(0.5 * CV.MPH_TO_MS) == -1.0
assert get_volt_one_pedal_target_decel(6.0 * CV.MPH_TO_MS) == -1.1
assert get_volt_one_pedal_target_decel(20.0 * CV.MPH_TO_MS) == -1.1
def test_volt_one_pedal_regression_ignores_noisy_wheel_direction_bits():
assert should_activate_volt_one_pedal(
True,
True,
False,
False,
False,
False,
True,
structs.CarState.GearShifter.low,
3.0,
)
def test_volt_one_pedal_requires_time_in_drive_before_arming():
assert not should_activate_volt_one_pedal(
True,
True,
False,
False,
False,
False,
True,
structs.CarState.GearShifter.low,
2.5,
)
def test_friction_brake_mode_keeps_near_stop_disabled_for_regular_long_braking():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM)
assert get_friction_brake_mode(120, False, True, False, CP) == 0xa
def test_friction_brake_mode_uses_near_stop_hold_mode_for_volt_auto_hold():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM)
assert get_friction_brake_mode(120, False, True, False, CP, allow_near_stop_mode=True) == 0xb
assert get_friction_brake_mode(120, False, True, True, CP, allow_near_stop_mode=True) == 0xd
def test_friction_brake_mode_uses_stock_bolt_unwind_for_pedal_print_when_enabled():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL)
assert get_friction_brake_mode(0, False, True, False, CP) == 0x1
assert get_friction_brake_mode(0, True, True, False, CP) == 0x9
def test_friction_brake_mode_keeps_bolt_pedal_braking_mode_unchanged():
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL)
assert get_friction_brake_mode(120, True, False, False, CP) == 0xa
assert get_friction_brake_mode(120, True, True, True, CP) == 0xd
def test_calc_pedal_command_small_accel_deadband_keeps_creep_target_stable():
pos_controller = _controller()
neg_controller = _controller()
@@ -317,6 +796,27 @@ def test_calc_pedal_command_keeps_strong_positive_requests_responsive():
assert pedal_gas - 0.18 > 0.04
def test_shape_truck_positive_accel_softens_small_highway_requests():
shaped = shape_truck_positive_accel(0.12, 26.0, True)
assert 0.09 < shaped < 0.10
def test_shape_truck_positive_accel_keeps_mid_follow_requests_available():
shaped = shape_truck_positive_accel(0.45, 13.5, True)
assert 0.43 < shaped < 0.45
def test_shape_truck_positive_accel_leaves_large_requests_alone():
assert shape_truck_positive_accel(1.0, 26.0, True) == 1.0
def test_shape_truck_positive_accel_is_inactive_when_disabled_or_low_speed():
assert shape_truck_positive_accel(0.12, 26.0, False) == 0.12
assert shape_truck_positive_accel(0.12, 6.0, True) == 0.12
def test_use_interceptor_sng_launch_requires_actual_near_stop():
CP = SimpleNamespace(vEgoStarting=0.25)
@@ -326,6 +826,42 @@ def test_use_interceptor_sng_launch_requires_actual_near_stop():
assert not use_interceptor_sng_launch(CP, _sng_cs(0.0, True, False))
def test_bolt_acc_pedal_sng_launch_uses_physical_standstill_without_stock_acc_bit():
CP = SimpleNamespace(
vEgoStarting=0.25,
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
enableGasInterceptorDEPRECATED=True,
)
assert use_interceptor_sng_launch(CP, _sng_cs(0.0, True, False))
assert use_interceptor_sng_launch(CP, _sng_cs(0.2, False, False))
assert not use_interceptor_sng_launch(CP, _sng_cs(1.2, False, False))
def test_bolt_acc_pedal_sng_launch_preserves_stronger_computed_pedal():
CP = SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
)
params = SimpleNamespace(SNG_INTERCEPTOR_GAS=18. / 255.)
assert get_interceptor_sng_gas_cmd(CP, 0.2, 0.54, params, False) == pytest.approx(0.2)
def test_other_pedal_sng_launch_keeps_fixed_floor_behavior():
CP = SimpleNamespace(
carFingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021,
openpilotLongitudinalControl=True,
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
)
params = SimpleNamespace(SNG_INTERCEPTOR_GAS=18. / 255.)
assert get_interceptor_sng_gas_cmd(CP, 0.2, 0.54, params, False) == pytest.approx(18. / 255.)
def test_use_interceptor_sng_launch_extends_for_maneuver_mode():
CP = SimpleNamespace(vEgoStarting=0.25)
+161 -63
View File
@@ -1,16 +1,18 @@
import pytest
import numpy as np
from types import SimpleNamespace
from parameterized import parameterized
from cereal import custom
from opendbc.can import CANPacker, CANParser
from opendbc.car import Bus, DT_CTRL, structs
from opendbc.car.car_helpers import interfaces
from opendbc.car.gm import gmcan
from opendbc.car.gm.carstate import CarState as GMCarState
from opendbc.car.gm.carstate import CarState as GMCarState, get_hard_cruise_buttons, update_auto_hold_drive_timers
from opendbc.car.gm.carcontroller import (
VisualAlert,
get_acc_dashboard_fcw_alert,
get_acc_dashboard_status_values,
get_volt_one_pedal_lift_brake,
should_send_acc_dashboard_status,
should_send_cc_button_spam,
should_spoof_dash_speed,
@@ -18,7 +20,7 @@ from opendbc.car.gm.carcontroller import (
import opendbc.car.gm.interface as gm_interface
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.gm.fingerprints import FINGERPRINTS
from opendbc.car.gm.values import CAMERA_ACC_CAR, CAR, CC_ONLY_CAR, DBC, GM_RX_OFFSET, GMFlags, GMSafetyFlags
from opendbc.car.gm.values import ASCM_INT, CAMERA_ACC_CAR, CAR, CC_ONLY_CAR, DBC, GM_RX_OFFSET, CarControllerParams, CruiseButtons, GMFlags, GMSafetyFlags
from opendbc.safety import ALTERNATIVE_EXPERIENCE
from openpilot.common.params import Params
@@ -63,6 +65,52 @@ class TestGMFingerprint:
class TestGMInterface:
def test_bolt_acc_pedal_pid_accel_limits_keep_full_negative_authority(self):
cp = SimpleNamespace(
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
)
accel_min, accel_max = gm_interface.CarInterface.get_pid_accel_limits(cp, 4.73, 0.0)
assert accel_min == pytest.approx(CarControllerParams.ACCEL_MIN)
assert accel_max == pytest.approx(np.interp(4.73, [0.0, 1.5, 4.0, 8.0, 15.0],
[0.54, 0.74, 1.03, 1.46, CarControllerParams.ACCEL_MAX]))
def test_bolt_cc_pedal_pid_accel_limits_remain_regen_limited(self):
cp = SimpleNamespace(
enableGasInterceptorDEPRECATED=True,
flags=GMFlags.PEDAL_LONG.value,
carFingerprint=CAR.CHEVROLET_BOLT_CC_2022_2023,
)
accel_min, _ = gm_interface.CarInterface.get_pid_accel_limits(cp, 4.73, 0.0)
assert accel_min == pytest.approx(np.interp(4.73, [0.0, 1.5, 4.0, 8.0, 15.0, 30.0],
[-0.93, -1.28, -1.98, -2.58, -2.86, -2.95]))
def test_missing_hard_cruise_signal_defaults_to_init(self):
assert get_hard_cruise_buttons({"ACCButtons": CruiseButtons.RES_ACCEL}) == CruiseButtons.INIT
assert get_hard_cruise_buttons({"ACCButtonsHard": CruiseButtons.DECEL_SET}) == CruiseButtons.DECEL_SET
def test_volt_auto_hold_drive_timer_requires_motion_before_startup_arming(self):
auto_hold_time, one_pedal_time = update_auto_hold_drive_timers(True, False, 0.0, 0.0)
assert auto_hold_time == 0.0
assert one_pedal_time == 0.0
def test_volt_auto_hold_drive_timer_accumulates_only_while_moving(self):
auto_hold_time, one_pedal_time = update_auto_hold_drive_timers(True, True, 0.0, 0.0)
assert auto_hold_time == pytest.approx(DT_CTRL)
assert one_pedal_time == pytest.approx(DT_CTRL)
auto_hold_time, one_pedal_time = update_auto_hold_drive_timers(True, False, auto_hold_time, one_pedal_time)
assert auto_hold_time == pytest.approx(DT_CTRL)
assert one_pedal_time == pytest.approx(DT_CTRL)
@parameterized.expand(VOLT_CARS)
def test_volt_min_steer_speed_is_7_mph(self, car_model):
CarInterface = interfaces[car_model]
@@ -84,12 +132,15 @@ class TestGMInterface:
old_testing_ground = gm_interface.testing_ground
gm_interface.testing_ground = SimpleNamespace(use_2=True)
params = Params()
params.put_bool("GMPedalLongitudinal", True)
try:
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=False, is_release=False, docs=False,
starpilot_toggles=_test_starpilot_toggles())
finally:
gm_interface.testing_ground = old_testing_ground
params.remove("GMPedalLongitudinal")
if pedal_present:
assert list(car_params.longitudinalTuning.kpV) == pytest.approx([0.10, 0.072, 0.05, 0.04])
@@ -132,6 +183,27 @@ class TestGMInterface:
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])
def test_blazer_uses_softer_low_speed_stop_hold_tune(self):
CarInterface = interfaces[CAR.CHEVROLET_BLAZER]
fingerprint = _empty_fingerprint()
fingerprint[0] = FINGERPRINTS[CAR.CHEVROLET_BLAZER][0].copy()
fingerprint[0][0x2FF] = 8 # SASCM present so alpha-long can enable on this platform
car_params = CarInterface.get_params(CAR.CHEVROLET_BLAZER, fingerprint, [], alpha_long=True, is_release=False,
docs=False, starpilot_toggles=_test_starpilot_toggles())
assert car_params.openpilotLongitudinalControl
assert list(car_params.longitudinalTuning.kpBP) == pytest.approx([0.0, 4.0, 12.0, 35.0])
assert list(car_params.longitudinalTuning.kpV) == pytest.approx([0.09, 0.075, 0.055, 0.04])
assert list(car_params.longitudinalTuning.kiBP) == pytest.approx([0.0, 4.0, 12.0, 35.0])
assert list(car_params.longitudinalTuning.kiV) == pytest.approx([0.03, 0.04, 0.055, 0.07])
assert car_params.longitudinalActuatorDelay == pytest.approx(0.7)
assert car_params.minEnableSpeed == pytest.approx(5 * CV.KPH_TO_MS)
assert car_params.stoppingDecelRate == pytest.approx(1.0)
assert car_params.vEgoStopping == pytest.approx(0.35)
assert car_params.vEgoStarting == pytest.approx(0.35)
assert car_params.stopAccel == pytest.approx(-0.30)
def test_volt_gateway_without_accel_pos_uses_brake_pedal_message(self):
CarInterface = interfaces[CAR.CHEVROLET_VOLT]
fingerprint = _empty_fingerprint()
@@ -163,6 +235,25 @@ class TestGMInterface:
assert car_params.openpilotLongitudinalControl
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
def test_volt_one_pedal_sets_stock_hold_safety_bit_without_auto_hold(self):
CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM]
fingerprint = _empty_fingerprint()
fingerprint[0][0x2FF] = 8
params = Params()
try:
params.put_bool("GMAutoHold", False)
params.put_bool("VoltOnePedalMode", True)
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=True, is_release=False,
docs=False, starpilot_toggles=_test_starpilot_toggles())
finally:
params.remove("GMAutoHold")
params.remove("VoltOnePedalMode")
assert car_params.openpilotLongitudinalControl
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_3D1_SCHED.value
@parameterized.expand(VOLT_CARS)
def test_volt_bsm_is_enabled_without_fingerprint_match(self, car_model):
CarInterface = interfaces[car_model]
@@ -209,14 +300,17 @@ class TestGMInterface:
params = Params()
toggles = _test_starpilot_toggles()
try:
params.put_bool("GMPedalLongitudinal", True)
params.put_bool("RemapCancelToDistance", True)
car_params = CarInterface.get_params(CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL, fingerprint, [], alpha_long=False,
is_release=False, docs=False, starpilot_toggles=toggles)
finally:
params.remove("GMPedalLongitudinal")
params.remove("RemapCancelToDistance")
assert car_params.alternativeExperience & ALTERNATIVE_EXPERIENCE.GM_REMAP_CANCEL_TO_DISTANCE
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_BOLT_2022_PEDAL.value
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
def test_cadillac_xt5_sdgm_sascm_gates_alpha_long(self):
CarInterface = interfaces[CAR.CADILLAC_XT5]
@@ -242,6 +336,49 @@ class TestGMInterface:
assert not sascm_params.pcmCruise
assert sascm_params.safetyConfigs[0].safetyParam & GMSafetyFlags.HW_CAM_LONG.value
def test_cadillac_escalade_esv_2019_ascm_uses_sascm_and_2019_tune(self):
base_fingerprint = FINGERPRINTS[CAR.CADILLAC_ESCALADE_ESV_2019][0]
ascm_fingerprint = FINGERPRINTS[CAR.CADILLAC_ESCALADE_ESV_2019_ASCM][0]
assert CAR.CADILLAC_ESCALADE_ESV_2019_ASCM in ASCM_INT
assert ascm_fingerprint[0x2FF] == 8
assert {addr: length for addr, length in ascm_fingerprint.items() if addr != 0x2FF} == base_fingerprint
CarInterface = interfaces[CAR.CADILLAC_ESCALADE_ESV_2019_ASCM]
fingerprint = _empty_fingerprint()
fingerprint[0] = ascm_fingerprint.copy()
car_params = CarInterface.get_params(CAR.CADILLAC_ESCALADE_ESV_2019_ASCM, fingerprint, [], alpha_long=True, is_release=False,
docs=False, starpilot_toggles=_test_starpilot_toggles())
assert car_params.flags & GMFlags.SASCM.value
assert car_params.networkLocation == structs.CarParams.NetworkLocation.fwdCamera
assert car_params.openpilotLongitudinalControl
assert not car_params.pcmCruise
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.HW_ASCM_INT.value
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.HW_CAM_LONG.value
assert car_params.lateralTuning.torque.latAccelFactor == pytest.approx(1.15)
assert car_params.lateralTuning.torque.friction == pytest.approx(0.2)
def test_cadillac_xt4_uses_nonlinear_torque_curve_with_center_boost(self):
CarInterface = interfaces[CAR.CADILLAC_XT4]
car_params = CarInterface.get_non_essential_params(CAR.CADILLAC_XT4)
ci = CarInterface(car_params, custom.StarPilotCarParams.new_message())
torque_from_lataccel = ci.torque_from_lateral_accel()
low_lataccel = 0.2
high_lataccel = 1.0
low_torque = torque_from_lataccel(low_lataccel, car_params.lateralTuning.torque)
high_torque = torque_from_lataccel(high_lataccel, car_params.lateralTuning.torque)
linear_low_torque = low_lataccel / car_params.lateralTuning.torque.latAccelFactor
linear_high_torque = high_lataccel / car_params.lateralTuning.torque.latAccelFactor
assert low_torque > linear_low_torque * 1.15
assert low_torque < linear_low_torque * 1.30
assert high_torque == pytest.approx(linear_high_torque, rel=0.03)
assert torque_from_lataccel(-low_lataccel, car_params.lateralTuning.torque) == pytest.approx(-low_torque, rel=1e-6)
class TestGMCarController:
def test_dash_speed_spoof_respects_live_stock_acc_toggles(self):
@@ -256,6 +393,11 @@ class TestGMCarController:
assert should_spoof_dash_speed(cp, SimpleNamespace(disable_openpilot_long=False))
def test_volt_one_pedal_lift_brake_seeds_low_speed_braking(self):
assert get_volt_one_pedal_lift_brake(2.1 * CV.MPH_TO_MS) == 0
assert get_volt_one_pedal_lift_brake(2.0 * CV.MPH_TO_MS) == 20
assert get_volt_one_pedal_lift_brake(0.10) == 80
def test_volt_camera_no_camera_sends_acc_dashboard_without_dash_spoof(self):
cp = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_CAMERA, flags=GMFlags.NO_CAMERA.value)
@@ -354,14 +496,9 @@ class TestGMCarController:
msg = gmcan.create_acc_dashboard_command(
packer,
0,
{
"ACCCruiseState": 0,
"ACCLeadCar": 1,
"ACCResumeButton": 0,
"ACCSpeedSetpoint": 100,
"ACCGapLevel": 3,
"ACCCmdActive": 1,
},
True,
100,
SimpleNamespace(leadDistanceBars=3, leadVisible=True),
0x2,
)
@@ -369,23 +506,26 @@ class TestGMCarController:
assert parser.vl["ASCMActiveCruiseControlStatus"]["FCWAlert"] == 2
def test_acc_dashboard_command_can_replay_stock_status_payload(self):
def test_acc_dashboard_command_uses_openpilot_hud_when_disengaged(self):
packer = CANPacker(DBC[CAR.CHEVROLET_VOLT_ASCM][Bus.pt])
parser = CANParser(DBC[CAR.CHEVROLET_VOLT_ASCM][Bus.pt], [("ASCMActiveCruiseControlStatus", 0)], 0)
msg = gmcan.create_acc_dashboard_command(
packer,
0,
{
"ACCCruiseState": 0,
"ACCLeadCar": 1,
"ACCResumeButton": 0,
"ACCSpeedSetpoint": 50,
"ACCGapLevel": 2,
"ACCCmdActive": 0,
},
False,
50,
SimpleNamespace(leadDistanceBars=2, leadVisible=True),
0x3,
)
assert msg[1].hex() == "010023200113"
parser.update([0, [msg]])
values = parser.vl["ASCMActiveCruiseControlStatus"]
assert values["ACCSpeedSetpoint"] == 50
assert values["ACCGapLevel"] == 0
assert values["ACCCmdActive"] == 0
assert values["ACCLeadCar"] == 1
assert values["FCWAlert"] == 3
def test_acc_dashboard_fcw_alert_prefers_openpilot_alert(self):
cs = SimpleNamespace(
@@ -418,45 +558,3 @@ class TestGMCarController:
)
assert get_acc_dashboard_fcw_alert(VisualAlert.none, cs) == 0x3
def test_acc_dashboard_status_values_use_openpilot_hud_when_enabled(self):
cs = SimpleNamespace(
stock_acc_cruise_state=5,
stock_acc_lead_car=0,
stock_acc_resume_button=1,
stock_acc_speed_setpoint_kph=42.0,
stock_acc_gap_level=1,
stock_acc_cmd_active=0,
)
values = get_acc_dashboard_status_values(True, 105.0, SimpleNamespace(leadDistanceBars=3, leadVisible=True), cs)
assert values == {
"ACCCruiseState": 0,
"ACCLeadCar": 1,
"ACCResumeButton": 0,
"ACCSpeedSetpoint": 105.0,
"ACCGapLevel": 3,
"ACCCmdActive": 1,
}
def test_acc_dashboard_status_values_reuse_stock_camera_status_when_disabled(self):
cs = SimpleNamespace(
stock_acc_cruise_state=0,
stock_acc_lead_car=1,
stock_acc_resume_button=0,
stock_acc_speed_setpoint_kph=50.0,
stock_acc_gap_level=2,
stock_acc_cmd_active=0,
)
values = get_acc_dashboard_status_values(False, 0.0, SimpleNamespace(leadDistanceBars=0, leadVisible=False), cs)
assert values == {
"ACCCruiseState": 0,
"ACCLeadCar": 1,
"ACCResumeButton": 0,
"ACCSpeedSetpoint": 50.0,
"ACCGapLevel": 2,
"ACCCmdActive": 0,
}
@@ -1,3 +1,5 @@
from types import SimpleNamespace
from opendbc.can import CANPacker
from opendbc.car.gm import gmcan
from opendbc.car.gm.values import CAR, DBC
@@ -26,6 +28,39 @@ class TestGMCan:
assert dat[1] & 0x1
assert decoded == 8848
def test_prndl2_command_matches_bolt_gen2_regen_paddle_spoof(self):
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL)
addr, dat, bus = gmcan.create_prndl2_command(self.packer, 0, False, CP)
assert addr == 0x1F5
assert bus == 0
assert dat.hex() == "0c0c000600000100"
addr, dat, bus = gmcan.create_prndl2_command(self.packer, 0, True, CP)
assert addr == 0x1F5
assert bus == 0
assert dat.hex() == "0c0c000500020100"
def test_prndl2_command_matches_bolt_gen1_regen_paddle_spoof(self):
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021)
addr, dat, bus = gmcan.create_prndl2_command(self.packer, 0, True, CP)
assert addr == 0x1F5
assert bus == 0
assert dat.hex() == "0c0c000700020100"
def test_regen_paddle_command_matches_bolt_spoof(self):
addr, dat, bus = gmcan.create_regen_paddle_command(self.packer, 0, False)
assert addr == 0xBD
assert bus == 0
assert dat.hex() == "00000000000000"
addr, dat, bus = gmcan.create_regen_paddle_command(self.packer, 0, True)
assert addr == 0xBD
assert bus == 0
assert dat.hex() == "20000000000000"
def test_gas_regen_command_matches_starpilot_volt_2019(self):
packer = CANPacker(DBC[CAR.CHEVROLET_VOLT_2019]["pt"])
@@ -42,11 +77,3 @@ class TestGMCan:
assert addr == 0x2CB
assert bus == 0
assert dat.hex() == "41429c4000bd63bf"
def test_gas_regen_command_matches_opgm_plain_volt_layout(self):
packer = CANPacker("gm_global_a_powertrain_generated")
addr, dat, bus = gmcan.create_gas_regen_command(packer, 0, 5000, 1, True, False, use_generated_layout=True)
assert addr == 0x2CB
assert bus == 0
assert dat.hex() == "41435c7000bca38f"
+12 -1
View File
@@ -303,6 +303,10 @@ class CAR(Platforms):
[GMCarDocs("Cadillac Escalade ESV 2019", "Adaptive Cruise Control (ACC) & LKAS")],
CADILLAC_ESCALADE_ESV.specs,
)
CADILLAC_ESCALADE_ESV_2019_ASCM = GMPlatformConfig(
[GMCarDocs("Cadillac Escalade ESV Platinum 2019 ASCM Harness", "Adaptive Cruise Control (ACC) & LKAS")],
CADILLAC_ESCALADE_ESV_2019.specs,
)
CHEVROLET_BOLT_ACC_2022_2023 = GMPlatformConfig(
[
GMCarDocs("Chevrolet Bolt ACC 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", video="https://youtu.be/xvwzGMUA210"),
@@ -576,7 +580,14 @@ CC_REGEN_PADDLE_CAR = {
CAMERA_ACC_CAR.update(CC_ONLY_CAR)
# ASCM-INT paths are only enabled when SASCM (0x2FF) is detected at runtime
ASCM_INT = {CAR.CHEVROLET_VOLT_ASCM, CAR.GMC_ACADIA_ASCM, CAR.CHEVROLET_MALIBU_ASCM, CAR.CADILLAC_ESCALADE_ASCM, CAR.BUICK_LACROSSE_ASCM}
ASCM_INT = {
CAR.CHEVROLET_VOLT_ASCM,
CAR.GMC_ACADIA_ASCM,
CAR.CHEVROLET_MALIBU_ASCM,
CAR.CADILLAC_ESCALADE_ASCM,
CAR.CADILLAC_ESCALADE_ESV_2019_ASCM,
CAR.BUICK_LACROSSE_ASCM,
}
STEER_THRESHOLD = 1.0
@@ -8,7 +8,8 @@ from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_steer_an
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.hyundai import hyundaicanfd, hyundaican
from opendbc.car.hyundai.hyundaicanfd import CanBus
from opendbc.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CAR, CANFD_RADAR_LIVE_LONGITUDINAL_CAR
from opendbc.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, 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
@@ -63,6 +64,21 @@ REDNECK_BUTTON_COPIES_TIME = 7
REDNECK_BUTTON_COPIES_TIME_IMPERIAL = [REDNECK_BUTTON_COPIES_TIME + 3, 70]
REDNECK_BUTTON_COPIES_TIME_METRIC = [REDNECK_BUTTON_COPIES_TIME, 40]
ANGLE_SAFETY_BASELINE_MODEL = str(CAR.KIA_SPORTAGE_HEV_2026)
DEFAULT_ANGLE_SMOOTHING_VEGO_BP = [5.0, 10.0, 20.0]
DEFAULT_ANGLE_SMOOTHING_ALPHA_V = [0.2, 0.1, 0.0]
EV9_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
def egmp_dynamic_longitudinal_tuning(CP) -> bool:
return CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 or \
kia_ev6_gt_line_longitudinal_tuning(CP.carFingerprint, getattr(CP, "carVin", ""))
def should_reset_ev6_gt_line_longitudinal_tuning(CP, long_control_state: LongCtrlState) -> bool:
return kia_ev6_gt_line_longitudinal_tuning(CP.carFingerprint, getattr(CP, "carVin", "")) and \
long_control_state == LongCtrlState.off
@dataclass
@@ -78,6 +94,13 @@ class Ioniq6LongitudinalTuningState:
long_control_state_last: LongCtrlState = LongCtrlState.off
def reset_ev6_gt_line_longitudinal_tuning(state: Ioniq6LongitudinalTuningState, CP,
long_control_state: LongCtrlState) -> Ioniq6LongitudinalTuningState:
if should_reset_ev6_gt_line_longitudinal_tuning(CP, long_control_state):
return Ioniq6LongitudinalTuningState(long_control_state_last=long_control_state)
return state
@dataclass
class GenesisG90LongitudinalTuningState:
actual_accel: float = 0.0
@@ -202,6 +225,10 @@ def get_baseline_safety_cp():
return CarInterface.get_non_essential_params(ANGLE_SAFETY_BASELINE_MODEL)
def get_angle_smoothing_alpha(CP, v_ego: float) -> float:
return float(np.interp(v_ego, DEFAULT_ANGLE_SMOOTHING_VEGO_BP, DEFAULT_ANGLE_SMOOTHING_ALPHA_V))
def 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])
@@ -219,6 +246,14 @@ 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))
@@ -364,7 +399,7 @@ class CarController(CarControllerBase):
-self.params.ANGLE_LIMITS.STEER_ANGLE_MAX,
self.params.ANGLE_LIMITS.STEER_ANGLE_MAX))
self.angle_filter.update_alpha(float(np.interp(CS.out.vEgo, [5.0, 10.0, 20.0], [0.2, 0.1, 0.0])))
self.angle_filter.update_alpha(get_angle_smoothing_alpha(self.CP, CS.out.vEgo))
desired_angle = self.angle_filter.update(desired_angle)
apply_angle = apply_steer_angle_limits_vm(desired_angle, self.apply_angle_last, v_ego_raw,
@@ -375,6 +410,7 @@ class CarController(CarControllerBase):
CS.out.steeringAngleDeg, CC.latActive, 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
torque_fault = False
@@ -426,22 +462,25 @@ class CarController(CarControllerBase):
# longitudinal messages - stock ECU is still active and these would conflict
self.long_active_ecu = self.CP.openpilotLongitudinalControl and not self.ecu_disable_failed
use_ioniq_6_dynamic_long_tuning = self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and self.long_active_ecu and \
actuators.longControlState in (LongCtrlState.starting, LongCtrlState.pid, LongCtrlState.stopping)
if use_ioniq_6_dynamic_long_tuning and self.frame % 5 == 0:
use_egmp_dynamic_long_tuning = egmp_dynamic_longitudinal_tuning(self.CP) and self.long_active_ecu and \
actuators.longControlState in (LongCtrlState.starting, LongCtrlState.pid, LongCtrlState.stopping)
if should_reset_ev6_gt_line_longitudinal_tuning(self.CP, actuators.longControlState):
self._ioniq_6_long_tuning = reset_ev6_gt_line_longitudinal_tuning(self._ioniq_6_long_tuning, self.CP,
actuators.longControlState)
elif use_egmp_dynamic_long_tuning and self.frame % 5 == 0:
self._ioniq_6_long_tuning = update_ioniq_6_longitudinal_tuning(self._ioniq_6_long_tuning, accel_cmd,
CS.out.vEgo, CS.out.aEgo,
actuators.longControlState, self.long_active_ecu)
use_ioniq_6_smoothed_accel = use_ioniq_6_dynamic_long_tuning and (
use_egmp_smoothed_accel = use_egmp_dynamic_long_tuning and (
accel_cmd >= self._ioniq_6_long_tuning.actual_accel or
self._ioniq_6_long_tuning.launch_active or
self._ioniq_6_long_tuning.stopping
)
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and self.long_active_ecu:
if use_ioniq_6_smoothed_accel:
if use_egmp_dynamic_long_tuning:
if use_egmp_smoothed_accel:
accel = self._ioniq_6_long_tuning.actual_accel
stopping = self._ioniq_6_long_tuning.stopping
elif use_ioniq_6_dynamic_long_tuning:
else:
accel = float(np.clip(accel_cmd,
self.accel_last - IONIQ_6_CANFD_SCC_DECEL_STEP,
self.accel_last + IONIQ_6_CANFD_SCC_ACCEL_STEP))
@@ -566,20 +605,31 @@ class CarController(CarControllerBase):
lka_steering = self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING
lka_steering_long = lka_steering and self.long_active_ecu
ccnc_non_hda2 = self.CP.flags & HyundaiFlags.CCNC and not lka_steering
use_ioniq_6_dynamic_long_tuning = self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and self.long_active_ecu and \
CC.actuators.longControlState == LongCtrlState.pid
use_ioniq_6_smoothed_accel = use_ioniq_6_dynamic_long_tuning and CC.actuators.accel >= self._ioniq_6_long_tuning.actual_accel
use_egmp_dynamic_long_tuning = egmp_dynamic_longitudinal_tuning(self.CP) and self.long_active_ecu and \
CC.actuators.longControlState in (LongCtrlState.starting, LongCtrlState.pid, LongCtrlState.stopping)
use_egmp_smoothed_accel = use_egmp_dynamic_long_tuning and (
CC.actuators.accel >= self._ioniq_6_long_tuning.actual_accel or
self._ioniq_6_long_tuning.launch_active or
self._ioniq_6_long_tuning.stopping
)
# steering control
preserve_stock_lkas = self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and not self.long_active_ecu
preserve_stock_lkas = bool(self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING) and not 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.
# 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,
apply_steer_req, apply_torque, apply_angle,
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))
# prevent LFA from activating on LKA steering cars by sending "no lane lines detected" to ADAS ECU
if self.frame % 5 == 0 and lka_steering:
suppress_lfa = bool(lka_steering)
if 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))
@@ -625,7 +675,8 @@ class CarController(CarControllerBase):
# 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))
can_sends.append(hyundaicanfd.create_accelerator_brake_alt_spoof(0, self.frame // 4, CS.out.brakePressed,
CS.out.gasPressed, self.CP.carFingerprint))
elif not ccnc_non_hda2:
can_sends.extend(hyundaicanfd.create_fca_warning_light(self.packer, self.CAN, self.frame))
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and self.frame % 5 == 0:
@@ -656,8 +707,8 @@ class CarController(CarControllerBase):
"lead_rel_speed": lead_rel_speed,
"lead_visible": lead_visible,
}
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
if use_ioniq_6_smoothed_accel:
if use_egmp_dynamic_long_tuning:
if use_egmp_smoothed_accel:
acc_kwargs["jerk_lower"] = self._ioniq_6_long_tuning.jerk_lower
acc_kwargs["jerk_upper"] = self._ioniq_6_long_tuning.jerk_upper
can_sends.append(hyundaicanfd.create_acc_control(self.packer, self.CAN, CC.enabled, self.accel_last, accel, stopping, CC.cruiseControl.override,
+48 -8
View File
@@ -75,7 +75,14 @@ class CarState(CarStateBase):
self.cruise_buttons: deque = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
self.main_buttons: deque = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
self.lda_button = 0
self.sonata_hybrid_lkas_source = None
self.sonata_hybrid_lkas_sources = {
"bcm": 0,
"clu13": 0,
"swl_stat": 0,
}
self.lda_button_raw = 0
self.lda_button_raw_initialized = False
self.lda_button_last_raw_rise_ts_nanos = 0
self.left_paddle = 0
self.mode_button = 0
@@ -174,15 +181,20 @@ class CarState(CarStateBase):
return False
def create_alt_bus_lda_button_events(self, cp_source: CANParser) -> list[structs.CarState.ButtonEvent]:
def get_alt_bus_lda_button_raw_state(self, cp_source: CANParser) -> tuple[int, int]:
if self.CP.carFingerprint in ALT_BUS_LDA_BUTTON_SWL_STAT_CARS:
raw_lda_button = int(cp_source.vl["CLU13"]["CF_Clu_SWL_Stat"] == 4)
raw_lda_button_ts_nanos = cp_source.ts_nanos["CLU13"]["CF_Clu_SWL_Stat"]
else:
raw_lda_button = int(cp_source.vl["CLU13"]["CF_Clu_LdwsLkasSW"])
raw_lda_button_ts_nanos = cp_source.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"]
return int(cp_source.vl["CLU13"]["CF_Clu_SWL_Stat"] == 4), cp_source.ts_nanos["CLU13"]["CF_Clu_SWL_Stat"]
return int(cp_source.vl["CLU13"]["CF_Clu_LdwsLkasSW"]), cp_source.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"]
def create_alt_bus_lda_button_events(self, cp_source: CANParser) -> list[structs.CarState.ButtonEvent]:
raw_lda_button, raw_lda_button_ts_nanos = self.get_alt_bus_lda_button_raw_state(cp_source)
button_events: list[structs.CarState.ButtonEvent] = []
if not self.lda_button_raw_initialized:
self.lda_button_raw_initialized = True
self.lda_button_raw = raw_lda_button
return button_events
# Some alt-bus LKAS button layouts pulse several times per physical press burst.
# Collapse each burst into a single synthetic press/release pair.
if raw_lda_button and not self.lda_button_raw:
@@ -198,8 +210,10 @@ class CarState(CarStateBase):
return button_events
def create_lkas_button_events(self, cp: CANParser, prev_lda_button: int) -> list[structs.CarState.ButtonEvent]:
if self.CP.carFingerprint == CAR.HYUNDAI_SONATA_HYBRID:
self.lda_button = self.get_sonata_hybrid_lkas_button_state(cp)
# Some classic HKG platforms publish the LKAS button on the cluster bus instead of BCM_PO_11.
if cp.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"] > 0:
elif cp.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"] > 0:
self.lda_button = int(cp.vl["CLU13"]["CF_Clu_LdwsLkasSW"])
elif cp.ts_nanos["BCM_PO_11"]["LDA_BTN"] > 0:
self.lda_button = int(cp.vl["BCM_PO_11"]["LDA_BTN"])
@@ -208,6 +222,32 @@ class CarState(CarStateBase):
return create_button_events(self.lda_button, prev_lda_button, {1: ButtonType.lkas})
def get_sonata_hybrid_lkas_button_state(self, cp: CANParser) -> int:
source_states = {
"bcm": int(cp.vl["BCM_PO_11"]["LDA_BTN"]) if cp.ts_nanos["BCM_PO_11"]["LDA_BTN"] > 0 else 0,
"clu13": int(cp.vl["CLU13"]["CF_Clu_LdwsLkasSW"]) if cp.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"] > 0 else 0,
"swl_stat": int(cp.vl["CLU13"]["CF_Clu_SWL_Stat"] == 4) if cp.ts_nanos["CLU13"]["CF_Clu_SWL_Stat"] > 0 else 0,
}
changed_sources = [source for source, state in source_states.items() if state != self.sonata_hybrid_lkas_sources[source]]
active_sources = [source for source, state in source_states.items() if state]
selected_source = None
if self.sonata_hybrid_lkas_source in changed_sources:
selected_source = self.sonata_hybrid_lkas_source
elif active_sources:
selected_source = active_sources[0]
elif changed_sources:
selected_source = changed_sources[0]
elif self.sonata_hybrid_lkas_source is not None:
selected_source = self.sonata_hybrid_lkas_source
self.sonata_hybrid_lkas_sources.update(source_states)
if selected_source is not None:
self.sonata_hybrid_lkas_source = selected_source
return source_states[selected_source]
return 0
def update(self, can_parsers, starpilot_toggles) -> structs.CarState:
cp = can_parsers[Bus.pt]
cp_cam = can_parsers[Bus.cam]
@@ -348,7 +388,7 @@ class CarState(CarStateBase):
lkas_button_events = []
self.cruise_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwState"])
self.main_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwMain"])
if self.CP.carFingerprint in ALT_BUS_LDA_BUTTON_CARS and cp_alt is not None and cp_alt.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"] > 0:
if self.CP.carFingerprint in ALT_BUS_LDA_BUTTON_CARS and cp_alt is not None and self.get_alt_bus_lda_button_raw_state(cp_alt)[1] > 0:
lkas_button_events = self.create_alt_bus_lda_button_events(cp_alt)
else:
lkas_button_events = self.create_lkas_button_events(cp, prev_lda_button)
@@ -38,6 +38,14 @@ FW_VERSIONS = {
b'\xf1\x00IGhe SCC FHCUP 1.00 1.02 99110-M9000 ',
],
},
CAR.HYUNDAI_AZERA_HEV_7TH_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00GN7HMFC AT KOR LHD 1.00 1.01 99211-N1110 240423',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00GN7_ RDR ----- 1.00 1.00 99110-N1100 ',
],
},
CAR.HYUNDAI_GENESIS: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00DH LKAS 1.1 -150210',
@@ -607,6 +615,17 @@ FW_VERSIONS = {
b'\xf1\x00CD ESC \x0b 101 \x10\x03 58910-J7AC0',
],
},
CAR.KIA_XCEED_PHEV: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00CDph SCC F-CUP 1.00 1.01 99110-CR100 ',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00CDe MDPS C 1.00 1.01 56310-XX000 4CDHC101',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CD2 LKAS AT EUR LHD 1.00 1.01 99211-CR010 621',
],
},
CAR.KIA_FORTE: {
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00BD MDPS C 1.00 1.02 56310-XX000 4BD2C102',
@@ -39,7 +39,7 @@ def create_lkas11(packer, frame, CP, apply_torque, steer_req,
CAR.HYUNDAI_IONIQ_EV_2020, CAR.HYUNDAI_IONIQ_PHEV, CAR.KIA_SELTOS, CAR.HYUNDAI_ELANTRA_2021, CAR.GENESIS_G70_2020,
CAR.HYUNDAI_ELANTRA_HEV_2021, CAR.HYUNDAI_SONATA_HYBRID, CAR.HYUNDAI_KONA_EV, CAR.HYUNDAI_KONA_HEV, CAR.HYUNDAI_KONA_EV_2022,
CAR.HYUNDAI_SANTA_FE_2022, CAR.KIA_K5_2021, CAR.HYUNDAI_IONIQ_HEV_2022, CAR.HYUNDAI_SANTA_FE_HEV_2022,
CAR.HYUNDAI_SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_CEED,
CAR.HYUNDAI_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):
values["CF_Lkas_LdwsActivemode"] = int(left_lane) + (int(right_lane) << 1)
values["CF_Lkas_LdwsOpt_USM"] = 2
@@ -96,6 +96,8 @@ def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque,
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
control_values = {
"LKA_MODE": 2,
@@ -128,6 +130,48 @@ 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 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,
"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,
})
else:
lkas_values.update({
"LKA_MODE": 0,
"LKA_AVAILABLE": 0,
"LKA_WARNING": 0,
"LKA_ICON": lka_icon,
"FCA_SYSWARN": 0,
"TORQUE_REQUEST": 0,
"STEER_REQ": 0,
"LFA_BUTTON": 0,
"LKA_ASSIST": 0,
"DAMP_FACTOR": 100,
"HAS_LANE_SAFETY": 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)
ret = []
if CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
@@ -709,11 +753,15 @@ def hkg_can_fd_checksum(address: int, sig, d: bytearray) -> int:
# radar stops publishing real object tracks. Spoof this message ourselves with valid CRC
# and current pedal state so the radar keeps tracking.
# Length is 24 bytes on Ioniq 6 (DBC declares 32 for ICE Hyundais, but EV firmware uses 24).
# Byte template captured from a real ADAS broadcast; bytes 6-23 appear static / config.
# Byte templates captured from real ADAS broadcasts; only checksum, counter,
# brake, and accelerator bits are updated for the radar heartbeat.
_ACCEL_BRAKE_ALT_TEMPLATE = bytes.fromhex("000000020000fcff000000000020000055ff000068000000")
_KIA_EV9_ACCEL_BRAKE_ALT_TEMPLATE = bytes.fromhex("00000000ff006f00e80400001201030055ffff0000000000")
def create_accelerator_brake_alt_spoof(bus: int, counter: int, brake_pressed: bool, accelerator_pressed: bool) -> CanData:
d = bytearray(_ACCEL_BRAKE_ALT_TEMPLATE)
def create_accelerator_brake_alt_spoof(bus: int, counter: int, brake_pressed: bool, accelerator_pressed: bool,
car_fingerprint=None) -> CanData:
template = _KIA_EV9_ACCEL_BRAKE_ALT_TEMPLATE if str(car_fingerprint) == "KIA_EV9" else _ACCEL_BRAKE_ALT_TEMPLATE
d = bytearray(template)
d[2] = counter & 0xFF # COUNTER (bit 16, 8-bit)
d[4] = (d[4] & ~0x01) | (0x01 if brake_pressed else 0x00) # BRAKE_PRESSED (bit 32)
d[22] = (d[22] & ~0x01) | (0x01 if accelerator_pressed else 0x00) # ACCELERATOR_PEDAL_PRESSED (bit 176)
+23 -9
View File
@@ -4,12 +4,15 @@ from opendbc.car.hyundai.hyundaicanfd import CanBus
from opendbc.car.hyundai.values import HyundaiFlags, CAR, CarControllerParams, \
CANFD_UNSUPPORTED_LONGITUDINAL_CAR, \
CANFD_SECURITYACCESS_CAR, \
CANFD_ANGLE_LONGITUDINAL_CAR, \
CANFD_RADAR_LIVE_LONGITUDINAL_CAR, \
RADAR_LIVE_LONGITUDINAL_CAR, \
UNSUPPORTED_LONGITUDINAL_CAR, HyundaiSafetyFlags, \
LEGACY_LONGITUDINAL_CAR, \
HyundaiStarPilotSafetyFlags, \
hyundai_cancel_button_enables_cruise
from opendbc.car.hyundai.radar_interface import get_radar_track_config
hyundai_cancel_button_enables_cruise, \
kia_ev6_gt_line_longitudinal_tuning
from opendbc.car.hyundai.radar_interface import get_radar_track_config, radar_tracks_available
from opendbc.car.interfaces import CarInterfaceBase, ACCEL_MIN
from opendbc.car.disable_ecu import disable_ecu, ecu_log
from opendbc.car.hyundai.carcontroller import CarController
@@ -39,6 +42,12 @@ def apply_platform_longitudinal_params(ret: structs.CarParams) -> None:
ret.stoppingDecelRate = 0.4
def apply_kia_ev6_gt_line_longitudinal_params(ret: structs.CarParams) -> None:
ret.startAccel = 1.4
ret.longitudinalActuatorDelay = 0.35
ret.vEgoStarting = 0.5
def apply_ecu_disable_failure_fallback(CP: structs.CarParams, params) -> None:
params.put_bool("EcuDisableFailed", True)
CP.safetyConfigs[-1].safetyParam &= ~HyundaiSafetyFlags.LONG.value
@@ -67,6 +76,11 @@ class CarInterface(CarInterfaceBase):
def get_pid_accel_limits(CP, current_speed, cruise_speed):
return ACCEL_MIN, CarControllerParams.ACCEL_MAX
@staticmethod
def apply_post_fingerprint_params(CP: structs.CarParams, candidate, fingerprint, car_fw) -> None:
if kia_ev6_gt_line_longitudinal_tuning(CP.carFingerprint, CP.carVin):
apply_kia_ev6_gt_line_longitudinal_params(CP)
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
ret.brand = "hyundai"
@@ -86,8 +100,8 @@ class CarInterface(CarInterfaceBase):
# this needs to be figured out for cars without an ADAS ECU
# Cars in CANFD_SECURITYACCESS_CAR are known to have ADAS ECUs that work with SecurityAccess
ret.alphaLongitudinalAvailable = False
if lka_steering and ret.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
# Angle-steering LKA platforms still need stock longitudinal validation.
if lka_steering and ret.flags & HyundaiFlags.CANFD_ANGLE_STEERING and candidate not in CANFD_ANGLE_LONGITUDINAL_CAR:
# Most angle-steering LKA platforms still need stock longitudinal validation.
ret.alphaLongitudinalAvailable = False
ret.enableBsm = 0x1ba in fingerprint[CAN.ECAN]
@@ -139,7 +153,7 @@ class CarInterface(CarInterfaceBase):
else:
# Shared configuration for non CAN-FD cars
ret.alphaLongitudinalAvailable = candidate not in UNSUPPORTED_LONGITUDINAL_CAR
ret.alphaLongitudinalAvailable = candidate not in UNSUPPORTED_LONGITUDINAL_CAR or candidate in LEGACY_LONGITUDINAL_CAR
ret.enableBsm = 0x58b in fingerprint[CAN.ECAN]
# Send LFA message on cars with HDA
@@ -191,13 +205,13 @@ class CarInterface(CarInterfaceBase):
# Common longitudinal control setup
radar_config = get_radar_track_config(ret.carFingerprint)
radar_tracks_available = radar_config is not None and radar_config.start_addr in fingerprint[radar_config.bus]
ret.radarUnavailable = not radar_tracks_available
radar_config = get_radar_track_config(ret.carFingerprint, ret.flags)
radar_available = radar_tracks_available(radar_config, fingerprint)
ret.radarUnavailable = not radar_available
if ret.flags & HyundaiFlags.NON_SCC:
ret.alphaLongitudinalAvailable = False
ret.openpilotLongitudinalControl = alpha_long and ret.alphaLongitudinalAvailable
if ret.openpilotLongitudinalControl and not (candidate in RADAR_LIVE_LONGITUDINAL_CAR and radar_tracks_available):
if ret.openpilotLongitudinalControl and not (candidate in RADAR_LIVE_LONGITUDINAL_CAR and radar_available):
ret.radarUnavailable = True
ret.pcmCruise = not ret.openpilotLongitudinalControl
apply_platform_longitudinal_params(ret)
@@ -1,12 +1,12 @@
import math
from dataclasses import dataclass
from dataclasses import dataclass, replace
from opendbc.can import CANParser
from opendbc.can.dbc import DBC as DBCReader
from opendbc.can.parser import get_raw_value
from opendbc.car import Bus, structs
from opendbc.car.interfaces import RadarInterfaceBase
from opendbc.car.hyundai.values import CAR, DBC, HYUNDAI_MANDO_FRONT_RADAR_DBC, HYUNDAI_MRREVO14F_RADAR_DBC, \
from opendbc.car.hyundai.values import CAR, DBC, HyundaiFlags, HYUNDAI_MANDO_FRONT_RADAR_DBC, HYUNDAI_MRREVO14F_RADAR_DBC, \
HYUNDAI_MRR30_RADAR_DBC, HYUNDAI_MRR35_RADAR_DBC
from openpilot.common.swaglog import cloudlog
@@ -29,6 +29,7 @@ class RadarTrackConfig:
bus: int = 1
frequency: int = 50
parser_msg_count: int | None = None
expected_length: int | None = None
@property
def can_parser_msg_count(self) -> int:
@@ -38,18 +39,37 @@ class RadarTrackConfig:
RADAR_TRACK_CONFIGS = {
HYUNDAI_MANDO_FRONT_RADAR_DBC: RadarTrackConfig(RADAR_START_ADDR, RADAR_MSG_COUNT, "mando"),
HYUNDAI_MRREVO14F_RADAR_DBC: RadarTrackConfig(MRREVO14F_RADAR_START_ADDR, MRREVO14F_RADAR_MSG_COUNT, "mrrevo14f"),
HYUNDAI_MRR30_RADAR_DBC: RadarTrackConfig(MRR30_RADAR_START_ADDR, MRR30_RADAR_MSG_COUNT, "mrr30", bus=0),
HYUNDAI_MRR35_RADAR_DBC: RadarTrackConfig(MRR35_RADAR_START_ADDR, MRR35_RADAR_MSG_COUNT, "mrr35", bus=0, frequency=20),
HYUNDAI_MRR30_RADAR_DBC: RadarTrackConfig(MRR30_RADAR_START_ADDR, MRR30_RADAR_MSG_COUNT, "mrr30", bus=0, expected_length=32),
HYUNDAI_MRR35_RADAR_DBC: RadarTrackConfig(MRR35_RADAR_START_ADDR, MRR35_RADAR_MSG_COUNT, "mrr35", bus=0, frequency=20, expected_length=24),
}
# POC for parsing corner radars: https://github.com/commaai/openpilot/pull/24221/
def get_radar_track_config(car_fingerprint) -> RadarTrackConfig | None:
def get_radar_track_config(car_fingerprint, flags: int = 0) -> RadarTrackConfig | None:
radar_dbc = DBC[car_fingerprint].get(Bus.radar)
if car_fingerprint == CAR.GENESIS_G90 and radar_dbc == HYUNDAI_MANDO_FRONT_RADAR_DBC:
return RadarTrackConfig(RADAR_START_ADDR, G90_RADAR_MSG_COUNT, "mando", parser_msg_count=RADAR_MSG_COUNT)
return RADAR_TRACK_CONFIGS.get(radar_dbc)
radar_config = RADAR_TRACK_CONFIGS.get(radar_dbc)
if radar_config is None:
return None
if car_fingerprint == CAR.HYUNDAI_IONIQ_6 and flags & HyundaiFlags.CANFD_CAMERA_SCC:
return replace(radar_config, bus=1)
return radar_config
def radar_tracks_available(radar_config: RadarTrackConfig | None, fingerprint) -> bool:
if radar_config is None:
return False
msg_len = fingerprint[radar_config.bus].get(radar_config.start_addr)
if msg_len is None:
return False
return radar_config.expected_length is None or msg_len == radar_config.expected_length
def get_radar_can_parser(CP, radar_config):
@@ -64,7 +84,7 @@ def get_radar_can_parser(CP, radar_config):
class RadarInterface(RadarInterfaceBase):
def __init__(self, CP):
super().__init__(CP)
self.radar_config = get_radar_track_config(CP.carFingerprint)
self.radar_config = get_radar_track_config(CP.carFingerprint, CP.flags)
self.updated_messages = set()
self.trigger_msg = (self.radar_config.start_addr + self.radar_config.can_parser_msg_count - 1
if self.radar_config is not None else RADAR_START_ADDR)
@@ -9,7 +9,9 @@ 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, \
update_ioniq_6_longitudinal_tuning, \
update_genesis_g90_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
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
@@ -19,8 +21,8 @@ from opendbc.car.hyundai.radar_interface import MRREVO14F_RADAR_START_ADDR, MRR3
from opendbc.car.hyundai.values import CAMERA_SCC_CAR, CANFD_CAR, CAN_GEARS, CAR, CHECKSUM, DATE_FW_ECUS, \
HYBRID_CAR, EV_CAR, FW_QUERY_CONFIG, LEGACY_SAFETY_MODE_CAR, CANFD_FUZZY_WHITELIST, \
UNSUPPORTED_LONGITUDINAL_CAR, PLATFORM_CODE_ECUS, HYUNDAI_VERSION_REQUEST_LONG, \
CarControllerParams, DBC, HyundaiFlags, get_platform_codes, HyundaiSafetyFlags, \
HyundaiStarPilotSafetyFlags, Buttons
LEGACY_LONGITUDINAL_CAR, DBC, HyundaiFlags, get_platform_codes, HyundaiSafetyFlags, \
HyundaiStarPilotSafetyFlags, Buttons, kia_ev6_gt_line_longitudinal_tuning
LongCtrlState = CarControl.Actuators.LongControlState
from opendbc.car.hyundai.fingerprints import FW_VERSIONS
@@ -44,6 +46,7 @@ NO_DATES_PLATFORMS = {
CAR.HYUNDAI_ELANTRA,
CAR.HYUNDAI_ELANTRA_GT_I30,
CAR.KIA_CEED,
CAR.KIA_XCEED_PHEV,
CAR.KIA_FORTE,
CAR.KIA_OPTIMA_G4,
CAR.KIA_OPTIMA_G4_FL,
@@ -91,6 +94,7 @@ CCNC_NON_HDA2_CARS = (
)
ANGLE_STEERING_CARS = (
CAR.HYUNDAI_AZERA_HEV_7TH_GEN,
CAR.HYUNDAI_SANTA_FE_HEV_5TH_GEN,
CAR.HYUNDAI_IONIQ_5_PE,
CAR.HYUNDAI_IONIQ_9,
@@ -130,6 +134,7 @@ class TestHyundaiFingerprint:
CAR.HYUNDAI_SANTA_FE_PHEV_2022,
CAR.HYUNDAI_SONATA,
CAR.HYUNDAI_SONATA_HYBRID,
CAR.KIA_XCEED_PHEV,
CAR.KIA_K5_HEV_2020,
CAR.KIA_NIRO_EV,
CAR.KIA_NIRO_PHEV,
@@ -151,7 +156,7 @@ class TestHyundaiFingerprint:
fingerprint = gen_empty_fingerprint()
fingerprint[1][RADAR_START_ADDR] = 8
for candidate in (CAR.HYUNDAI_SONATA, CAR.HYUNDAI_SONATA_HYBRID, CAR.GENESIS_G90):
for candidate in (CAR.HYUNDAI_SONATA, CAR.HYUNDAI_SONATA_HYBRID, CAR.KIA_XCEED_PHEV, CAR.GENESIS_G90):
CP = CarInterface.get_params(candidate, fingerprint, [], True, False, False, None)
assert CP.openpilotLongitudinalControl
assert not CP.radarUnavailable
@@ -164,37 +169,58 @@ class TestHyundaiFingerprint:
(CAR.KIA_EV6_2025, MRR30_RADAR_START_ADDR),
(CAR.GENESIS_GV60_EV_1ST_GEN, MRR30_RADAR_START_ADDR),
(CAR.HYUNDAI_KONA_EV_2ND_GEN, MRR35_RADAR_START_ADDR),
(CAR.HYUNDAI_IONIQ_6, MRR35_RADAR_START_ADDR),
(CAR.HYUNDAI_IONIQ_9, MRR35_RADAR_START_ADDR),
(CAR.KIA_EV9, MRR35_RADAR_START_ADDR),
):
radar_config = get_radar_track_config(candidate)
assert radar_config.start_addr == radar_addr
for radar in (True, False):
fingerprint = gen_empty_fingerprint()
if radar:
fingerprint[radar_config.bus][radar_addr] = 8
fingerprint[radar_config.bus][radar_addr] = radar_config.expected_length or 8
CP = CarInterface.get_params(candidate, fingerprint, [], False, False, False, None)
assert CP.radarUnavailable != radar
assert get_radar_track_config(CAR.HYUNDAI_KONA_EV_2022).bus == 1
assert get_radar_track_config(CAR.HYUNDAI_IONIQ_5).bus == 0
assert get_radar_track_config(CAR.HYUNDAI_IONIQ_6).start_addr == MRR35_RADAR_START_ADDR
ioniq_6_hda2_radar_config = get_radar_track_config(CAR.HYUNDAI_IONIQ_6)
ioniq_6_hda1_radar_config = get_radar_track_config(CAR.HYUNDAI_IONIQ_6, HyundaiFlags.CANFD_CAMERA_SCC)
assert ioniq_6_hda2_radar_config.start_addr == MRR35_RADAR_START_ADDR
assert ioniq_6_hda2_radar_config.bus == 0
assert ioniq_6_hda1_radar_config.bus == 1
assert ioniq_6_hda1_radar_config.frequency == 20
fingerprint = gen_empty_fingerprint()
fingerprint[1][MRR35_RADAR_START_ADDR] = 24
fingerprint[0][MRR35_RADAR_START_ADDR] = 8
CP = CarInterface.get_params(CAR.HYUNDAI_IONIQ_6, fingerprint, [], False, False, False, None)
assert not CP.openpilotLongitudinalControl
assert CP.radarUnavailable
fingerprint = gen_empty_fingerprint()
fingerprint[ioniq_6_hda1_radar_config.bus][MRR35_RADAR_START_ADDR] = 24
CP = CarInterface.get_params(CAR.HYUNDAI_IONIQ_6, fingerprint, [], False, False, False, None)
assert not CP.openpilotLongitudinalControl
assert CP.flags & HyundaiFlags.CANFD_CAMERA_SCC
assert not CP.radarUnavailable
CP = CarInterface.get_params(CAR.HYUNDAI_IONIQ_6, gen_empty_fingerprint(), [], True, False, False, None)
assert CP.openpilotLongitudinalControl
assert CP.radarUnavailable
fingerprint = gen_empty_fingerprint()
fingerprint[get_radar_track_config(CAR.HYUNDAI_IONIQ_6).bus][MRR35_RADAR_START_ADDR] = 24
fingerprint[ioniq_6_hda1_radar_config.bus][MRR35_RADAR_START_ADDR] = 24
CP = CarInterface.get_params(CAR.HYUNDAI_IONIQ_6, fingerprint, [], True, False, False, None)
assert CP.openpilotLongitudinalControl
assert CP.flags & HyundaiFlags.CANFD_CAMERA_SCC
assert not CP.radarUnavailable
fingerprint = gen_empty_fingerprint()
fingerprint[CanBus(None, fingerprint).CAM][0x50] = 32
fingerprint[ioniq_6_hda2_radar_config.bus][MRR35_RADAR_START_ADDR] = 24
CP = CarInterface.get_params(CAR.HYUNDAI_IONIQ_6, fingerprint, [], True, False, False, None)
assert CP.openpilotLongitudinalControl
assert CP.flags & HyundaiFlags.CANFD_LKA_STEERING
assert not CP.radarUnavailable
assert get_radar_track_config(CAR.HYUNDAI_IONIQ_6).frequency == 20
fingerprint = gen_empty_fingerprint()
fingerprint[CanBus(None, fingerprint).CAM][0x50] = 32
@@ -203,6 +229,22 @@ class TestHyundaiFingerprint:
assert CP.openpilotLongitudinalControl
assert not CP.radarUnavailable
ev9_radar_config = get_radar_track_config(CAR.KIA_EV9)
fingerprint = gen_empty_fingerprint()
fingerprint[CanBus(None, fingerprint).CAM][0x110] = 32
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 not CP.radarUnavailable
assert CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
assert not (CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.LONG)
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CANFD_ANGLE_STEERING
CP = CarInterface.get_params(CAR.KIA_EV9, fingerprint, [], True, False, False, None)
assert not CP.openpilotLongitudinalControl
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)
@@ -237,6 +279,26 @@ class TestHyundaiFingerprint:
assert CP.steerControlType == CarParams.SteerControlType.angle
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CANFD_ANGLE_STEERING
def test_ev9_uses_shared_angle_smoothing(self):
ev9_cp = SimpleNamespace(carFingerprint=CAR.KIA_EV9)
other_cp = SimpleNamespace(carFingerprint=CAR.KIA_SPORTAGE_HEV_2026)
assert get_angle_smoothing_alpha(ev9_cp, 0.0) == pytest.approx(get_angle_smoothing_alpha(other_cp, 0.0))
assert get_angle_smoothing_alpha(ev9_cp, 13.8) == pytest.approx(get_angle_smoothing_alpha(other_cp, 13.8))
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))
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)
def test_ccnc_hda2_lka_layout_does_not_set_ccnc_safety_param(self):
fingerprint = gen_empty_fingerprint()
cam_can = CanBus(None, fingerprint).CAM
@@ -447,6 +509,54 @@ class TestHyundaiFingerprint:
assert CP.pcmCruise
assert not (CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.LONG)
def test_xceed_phev_alpha_long_is_isolated_legacy_experiment(self):
toggles = get_test_toggles()
ceed = CarInterface.get_params(CAR.KIA_CEED, gen_empty_fingerprint(), [], True, False, False, toggles)
assert CAR.KIA_CEED not in LEGACY_LONGITUDINAL_CAR
assert not ceed.alphaLongitudinalAvailable
assert not ceed.openpilotLongitudinalControl
assert ceed.pcmCruise
assert ceed.safetyConfigs[-1].safetyModel == CarParams.SafetyModel.hyundaiLegacy
assert not (ceed.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.LONG)
stock_xceed = CarInterface.get_params(CAR.KIA_XCEED_PHEV, gen_empty_fingerprint(), [], False, False, False, toggles)
assert CAR.KIA_XCEED_PHEV in LEGACY_LONGITUDINAL_CAR
assert stock_xceed.alphaLongitudinalAvailable
assert not stock_xceed.openpilotLongitudinalControl
assert stock_xceed.pcmCruise
assert stock_xceed.safetyConfigs[-1].safetyModel == CarParams.SafetyModel.hyundaiLegacy
assert stock_xceed.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.HYBRID_GAS
assert not (stock_xceed.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.LONG)
long_xceed = CarInterface.get_params(CAR.KIA_XCEED_PHEV, gen_empty_fingerprint(), [], True, False, False, toggles)
assert long_xceed.alphaLongitudinalAvailable
assert long_xceed.openpilotLongitudinalControl
assert not long_xceed.pcmCruise
assert long_xceed.safetyConfigs[-1].safetyModel == CarParams.SafetyModel.hyundaiLegacy
assert long_xceed.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.HYBRID_GAS
assert long_xceed.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.LONG
def test_xceed_phev_disable_failure_falls_back_to_stock_acc(self, monkeypatch):
toggles = get_test_toggles()
CP = CarInterface.get_params(CAR.KIA_XCEED_PHEV, gen_empty_fingerprint(), [], True, False, False, toggles)
called = {}
def fake_disable_ecu(*args, **kwargs):
called.update(kwargs)
return False
monkeypatch.setattr("opendbc.car.hyundai.interface.disable_ecu", fake_disable_ecu)
CarInterface.init(CP, None, None)
assert called["addr"] == 0x7d0
assert called["bus"] == 0
assert called["reset"] is False
assert not CP.openpilotLongitudinalControl
assert CP.pcmCruise
assert not (CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.LONG)
def test_canfd_longitudinal_params_match_family_tune(self):
toggles = get_test_toggles()
CP = CarInterface.get_params(CAR.KIA_EV6, gen_empty_fingerprint(), [], True, False, False, toggles)
@@ -457,6 +567,47 @@ class TestHyundaiFingerprint:
assert CP.longitudinalActuatorDelay == pytest.approx(0.5)
assert CP.startingState
def test_kia_ev6_gt_line_post_fingerprint_longitudinal_params(self):
toggles = get_test_toggles()
CP = CarInterface.get_params(CAR.KIA_EV6, gen_empty_fingerprint(), [], True, False, False, toggles)
CP.carVin = "KNDC4DLC0P0000000"
CarInterface.apply_post_fingerprint_params(CP, CAR.KIA_EV6, gen_empty_fingerprint(), [])
assert CP.startAccel == pytest.approx(1.4)
assert CP.vEgoStarting == pytest.approx(0.5)
assert CP.longitudinalActuatorDelay == pytest.approx(0.35)
assert CP.vEgoStopping == pytest.approx(0.3)
assert CP.stoppingDecelRate == pytest.approx(0.4)
assert kia_ev6_gt_line_longitudinal_tuning(CP.carFingerprint, CP.carVin)
assert egmp_dynamic_longitudinal_tuning(CP)
assert should_reset_ev6_gt_line_longitudinal_tuning(CP, LongCtrlState.off)
assert not should_reset_ev6_gt_line_longitudinal_tuning(CP, LongCtrlState.pid)
stale_state = Ioniq6LongitudinalTuningState(desired_accel=-2.2, actual_accel=-2.2, accel_last=-2.2,
jerk_upper=1.0, jerk_lower=5.0,
long_control_state_last=LongCtrlState.stopping)
reset_state = reset_ev6_gt_line_longitudinal_tuning(stale_state, CP, LongCtrlState.off)
assert reset_state.actual_accel == pytest.approx(0.0)
assert reset_state.accel_last == pytest.approx(0.0)
assert reset_state.long_control_state_last == LongCtrlState.off
def test_kia_ev6_non_gt_line_keeps_family_longitudinal_params(self):
toggles = get_test_toggles()
CP = CarInterface.get_params(CAR.KIA_EV6, gen_empty_fingerprint(), [], True, False, False, toggles)
CP.carVin = "KNDC3DLC0P0000000"
CarInterface.apply_post_fingerprint_params(CP, CAR.KIA_EV6, gen_empty_fingerprint(), [])
assert CP.startAccel == pytest.approx(1.0)
assert CP.vEgoStarting == pytest.approx(0.1)
assert CP.longitudinalActuatorDelay == pytest.approx(0.5)
assert not kia_ev6_gt_line_longitudinal_tuning(CP.carFingerprint, CP.carVin)
assert not egmp_dynamic_longitudinal_tuning(CP)
assert not should_reset_ev6_gt_line_longitudinal_tuning(CP, LongCtrlState.off)
stale_state = Ioniq6LongitudinalTuningState(actual_accel=-2.2, accel_last=-2.2)
assert reset_ev6_gt_line_longitudinal_tuning(stale_state, CP, LongCtrlState.off) is stale_state
def test_genesis_g90_longitudinal_params_bias_toward_earlier_stop_handoff(self):
toggles = get_test_toggles()
CP = CarInterface.get_params(CAR.GENESIS_G90, gen_empty_fingerprint(), [], True, False, False, toggles)
@@ -662,7 +813,7 @@ class TestHyundaiFingerprint:
ret = update(0, 3)
assert any(be.type == ButtonType.lkas and not be.pressed for be in ret.buttonEvents)
def test_sonata_hybrid_uses_alt_bus_lkas_parser(self):
def test_sonata_hybrid_uses_main_bus_lkas_parser(self):
toggles = get_test_toggles()
fingerprint = gen_empty_fingerprint()
fingerprint[0][0x391] = 8
@@ -673,9 +824,9 @@ class TestHyundaiFingerprint:
car_state = CarState(CP, FPCP)
can_parsers = car_state.get_can_parsers(CP)
assert Bus.alt in can_parsers
assert Bus.alt not in can_parsers
def test_sonata_hybrid_alt_bus_clu13_lkas_button_event(self):
def test_sonata_hybrid_bcm_lkas_button_event(self):
toggles = get_test_toggles()
fingerprint = gen_empty_fingerprint()
fingerprint[0][0x391] = 8
@@ -687,6 +838,139 @@ class TestHyundaiFingerprint:
can_parsers = car_state.get_can_parsers(CP)
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
def update(lkas_button: int, frame: int):
msg = packer.make_can_msg("BCM_PO_11", 0, {
"LDA_BTN": lkas_button,
})
can_parsers[Bus.pt].update([(frame, [msg])])
return car_state.update(can_parsers, toggles)[0]
update(0, 1)
ret = update(1, 2)
assert any(be.type == ButtonType.lkas and be.pressed for be in ret.buttonEvents)
ret = update(0, 3)
assert any(be.type == ButtonType.lkas and not be.pressed for be in ret.buttonEvents)
def test_sonata_hybrid_prefers_bcm_lkas_button_over_dead_main_bus_clu13(self):
toggles = get_test_toggles()
fingerprint = gen_empty_fingerprint()
fingerprint[0][0x391] = 8
fingerprint[1][0x50C] = 8
CP = CarInterface.get_params(CAR.HYUNDAI_SONATA_HYBRID, fingerprint, [], False, False, False, toggles)
FPCP = CarInterface.get_starpilot_params(CAR.HYUNDAI_SONATA_HYBRID, fingerprint, [], CP, toggles)
car_state = CarState(CP, FPCP)
can_parsers = car_state.get_can_parsers(CP)
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
def update(clu13_lkas_button: int, bcm_lkas_button: int, frame: int):
msgs = [
packer.make_can_msg("CLU13", 0, {
"CF_Clu_LdwsLkasSW": clu13_lkas_button,
}),
packer.make_can_msg("BCM_PO_11", 0, {
"LDA_BTN": bcm_lkas_button,
}),
]
can_parsers[Bus.pt].update([(frame, msgs)])
return car_state.update(can_parsers, toggles)[0]
update(0, 0, 1)
ret = update(0, 1, 2)
assert any(be.type == ButtonType.lkas and be.pressed for be in ret.buttonEvents)
ret = update(0, 0, 3)
assert any(be.type == ButtonType.lkas and not be.pressed for be in ret.buttonEvents)
def test_sonata_hybrid_falls_back_to_main_bus_clu13_lkas_button_when_bcm_stays_dead(self):
toggles = get_test_toggles()
fingerprint = gen_empty_fingerprint()
fingerprint[0][0x391] = 8
fingerprint[1][0x50C] = 8
CP = CarInterface.get_params(CAR.HYUNDAI_SONATA_HYBRID, fingerprint, [], False, False, False, toggles)
FPCP = CarInterface.get_starpilot_params(CAR.HYUNDAI_SONATA_HYBRID, fingerprint, [], CP, toggles)
car_state = CarState(CP, FPCP)
can_parsers = car_state.get_can_parsers(CP)
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
def update(clu13_lkas_button: int, bcm_lkas_button: int, frame: int):
msgs = [
packer.make_can_msg("CLU13", 0, {
"CF_Clu_LdwsLkasSW": clu13_lkas_button,
}),
packer.make_can_msg("BCM_PO_11", 0, {
"LDA_BTN": bcm_lkas_button,
}),
]
can_parsers[Bus.pt].update([(frame, msgs)])
return car_state.update(can_parsers, toggles)[0]
update(0, 0, 1)
ret = update(1, 0, 2)
assert any(be.type == ButtonType.lkas and be.pressed for be in ret.buttonEvents)
ret = update(0, 0, 3)
assert any(be.type == ButtonType.lkas and not be.pressed for be in ret.buttonEvents)
def test_sonata_hybrid_falls_back_to_main_bus_clu13_swl_stat_lkas_button_when_other_sources_are_dead(self):
toggles = get_test_toggles()
fingerprint = gen_empty_fingerprint()
fingerprint[0][0x391] = 8
fingerprint[1][0x50C] = 8
CP = CarInterface.get_params(CAR.HYUNDAI_SONATA_HYBRID, fingerprint, [], False, False, False, toggles)
FPCP = CarInterface.get_starpilot_params(CAR.HYUNDAI_SONATA_HYBRID, fingerprint, [], CP, toggles)
car_state = CarState(CP, FPCP)
can_parsers = car_state.get_can_parsers(CP)
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
def update(swl_stat: int, frame: int):
msgs = [
packer.make_can_msg("CLU13", 0, {
"CF_Clu_LdwsLkasSW": 0,
"CF_Clu_SWL_Stat": swl_stat,
}),
packer.make_can_msg("BCM_PO_11", 0, {
"LDA_BTN": 0,
}),
]
can_parsers[Bus.pt].update([(frame, msgs)])
return car_state.update(can_parsers, toggles)[0]
update(0, 1)
ret = update(4, 2)
assert any(be.type == ButtonType.lkas and be.pressed for be in ret.buttonEvents)
ret = update(0, 3)
assert any(be.type == ButtonType.lkas and not be.pressed for be in ret.buttonEvents)
def test_sonata_hybrid_ignores_noisy_alt_bus_clu13_lkas_button(self):
toggles = get_test_toggles()
fingerprint = gen_empty_fingerprint()
fingerprint[0][0x391] = 8
fingerprint[1][0x50C] = 8
CP = CarInterface.get_params(CAR.HYUNDAI_SONATA_HYBRID, fingerprint, [], False, False, False, toggles)
FPCP = CarInterface.get_starpilot_params(CAR.HYUNDAI_SONATA_HYBRID, fingerprint, [], CP, toggles)
car_state = CarState(CP, FPCP)
can_parsers = car_state.get_can_parsers(CP)
assert Bus.alt not in can_parsers
def test_sonata_alt_bus_clu13_swl_stat_lkas_button_event(self):
toggles = get_test_toggles()
fingerprint = gen_empty_fingerprint()
fingerprint[0][0x391] = 8
fingerprint[1][0x50C] = 8
CP = CarInterface.get_params(CAR.HYUNDAI_SONATA, fingerprint, [], False, False, False, toggles)
FPCP = CarInterface.get_starpilot_params(CAR.HYUNDAI_SONATA, fingerprint, [], CP, toggles)
car_state = CarState(CP, FPCP)
can_parsers = car_state.get_can_parsers(CP)
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
def update(lkas_button: int, frame: int):
msg = packer.make_can_msg("CLU13", 1, {
"CF_Clu_SWL_Stat": lkas_button,
@@ -1077,6 +1361,167 @@ class TestHyundaiFingerprint:
assert lead_distance == pytest.approx(20.0)
assert lead_rel_speed == pytest.approx(0.0)
def test_ev9_angle_status_stays_active_when_gain_is_zero(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 = 1
can_bus = CanBus(CP)
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("LKAS_ALT", 0)], can_bus.ACAN)
stock_lkas = {
"CHECKSUM": 1234,
"COUNTER": 42,
"LKA_MODE": 2,
"LKA_AVAILABLE": 3,
"LKA_WARNING": 1,
"LKA_ICON": 1,
"FCA_SYSWARN": 1,
"TORQUE_REQUEST": 17,
"STEER_REQ": 1,
"LFA_BUTTON": 1,
"LKA_ASSIST": 1,
"STEER_MODE": 5,
"NEW_SIGNAL_2": 0,
"LKAS_ANGLE_ACTIVE": 1,
"HAS_LANE_SAFETY": 1,
"ADAS_StrAnglReqVal": 12.3,
"ADAS_ACIAnglTqRedcGainVal": 0.42,
"DAMP_FACTOR": 0,
}
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))
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)
lkas_msgs = [msg for msg in msgs if msg[0] == 0x110]
assert len(lkas_msgs) == 1
parser.update([(1, lkas_msgs)])
assert parser.can_valid
assert parser.vl["LKAS_ALT"]["LKAS_ANGLE_ACTIVE"] == 2
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):
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)
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())
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=-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
parser.update([(1, lkas_msgs)])
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
def test_ev9_inactive_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 |
HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT)
CP.openpilotLongitudinalControl = False
controller = CarController(DBC[CP.carFingerprint], CP)
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),
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))
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 len(suppress_msgs) == 1
parser.update([(1, suppress_msgs)])
assert parser.can_valid
assert parser.vl["CAM_0x362"]["LEFT_LANE_LINE"] == 0
assert parser.vl["CAM_0x362"]["RIGHT_LANE_LINE"] == 0
def test_ev9_high_steering_angle_keeps_active_status_and_gain(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
can_bus = CanBus(CP)
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("LKAS_ALT", 0)], can_bus.ACAN)
stock_lkas = {
"CHECKSUM": 1234,
"COUNTER": 42,
"LKA_MODE": 2,
"LKA_AVAILABLE": 3,
"LKA_WARNING": 1,
"LKA_ICON": 1,
"FCA_SYSWARN": 1,
"TORQUE_REQUEST": 17,
"STEER_REQ": 1,
"LFA_BUTTON": 1,
"LKA_ASSIST": 1,
"STEER_MODE": 5,
"NEW_SIGNAL_2": 0,
"LKAS_ANGLE_ACTIVE": 1,
"HAS_LANE_SAFETY": 1,
"ADAS_StrAnglReqVal": 12.3,
"ADAS_ACIAnglTqRedcGainVal": 0.42,
"DAMP_FACTOR": 0,
}
cc = SimpleNamespace(enabled=True, 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=stock_lkas, lfa_block_msg=lfa_block_msg,
out=SimpleNamespace(steeringAngleDeg=120.0))
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)
lkas_msgs = [msg for msg in msgs if msg[0] == 0x110]
suppress_msgs = [msg for msg in msgs if msg[0] == 0x362]
assert len(lkas_msgs) == 1
assert len(suppress_msgs) == 1
parser.update([(1, lkas_msgs)])
assert parser.can_valid
assert parser.vl["LKAS_ALT"]["LKAS_ANGLE_ACTIVE"] == 2
assert parser.vl["LKAS_ALT"]["ADAS_ACIAnglTqRedcGainVal"] == pytest.approx(0.44)
assert parser.vl["LKAS_ALT"]["ADAS_StrAnglReqVal"] == pytest.approx(120.0)
def test_can_acc_commands_use_default_values(self):
CP = CarParams.new_message()
CP.carFingerprint = CAR.GENESIS_G90
@@ -1246,6 +1691,84 @@ class TestHyundaiFingerprint:
assert parser.can_valid
assert parser.vl["LFA"]["LKA_ICON"] == 3
def test_kia_ev6_lfa_helper_preserves_stock_ui_fields_with_stock_long(self):
CP = CarParams.new_message()
CP.carFingerprint = CAR.KIA_EV6
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.CANFD_LKA_STEERING)
CP.openpilotLongitudinalControl = False
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
can_bus = CanBus(CP)
stock_lfa = {
"CHECKSUM": 1234,
"COUNTER": 42,
"LKA_MODE": 6,
"NEW_SIGNAL_1": 3,
"LKA_WARNING": 1,
"LKA_ICON": 1,
"TORQUE_REQUEST": 17,
"STEER_REQ": 0,
"LFA_BUTTON": 1,
"LKA_ASSIST": 1,
"STEER_MODE": 5,
"NEW_SIGNAL_2": 2,
"NEW_SIGNAL_4": 7,
"HAS_LANE_SAFETY": 1,
"DAMP_FACTOR": 0x77,
}
msgs = hyundaicanfd.create_steering_messages(packer, CP, can_bus, True, True, 123, 0.0, stock_lfa)
assert [(packer.dbc.addr_to_msg[addr].name, bus) for addr, _, bus in msgs] == [
("LKAS", can_bus.ACAN),
]
def test_kia_ev6_lkas_helper_preserves_stock_camera_fields_with_stock_long(self):
CP = CarParams.new_message()
CP.carFingerprint = CAR.KIA_EV6
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.CANFD_LKA_STEERING)
CP.openpilotLongitudinalControl = False
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
can_bus = CanBus(CP)
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("LKAS", 0)], can_bus.ACAN)
stock_lkas = {
"CHECKSUM": 1234,
"COUNTER": 42,
"LKA_MODE": 6,
"LKA_AVAILABLE": 3,
"LKA_WARNING": 1,
"LKA_ICON": 1,
"FCA_SYSWARN": 1,
"TORQUE_REQUEST": 17,
"STEER_REQ": 0,
"LFA_BUTTON": 1,
"LKA_ASSIST": 1,
"STEER_MODE": 5,
"NEW_SIGNAL_2": 2,
"HAS_LANE_SAFETY": 1,
"DAMP_FACTOR": 0x70,
}
msgs = hyundaicanfd.create_steering_messages(packer, CP, can_bus, True, True, 123, 0.0,
lkas_base_values=stock_lkas)
lkas_msgs = [msg for msg in msgs if msg[0] == 0x50]
assert len(lkas_msgs) == 1
parser.update([(1, lkas_msgs)])
assert parser.can_valid
assert parser.vl["LKAS"]["LKA_AVAILABLE"] == 3
assert parser.vl["LKAS"]["LKA_WARNING"] == 1
assert parser.vl["LKAS"]["FCA_SYSWARN"] == 1
assert parser.vl["LKAS"]["LFA_BUTTON"] == 1
assert parser.vl["LKAS"]["HAS_LANE_SAFETY"] == 1
assert parser.vl["LKAS"]["DAMP_FACTOR"] == 0x70
assert parser.vl["LKAS"]["TORQUE_REQUEST"] == 123
assert parser.vl["LKAS"]["STEER_REQ"] == 1
assert parser.vl["LKAS"]["LKA_ICON"] == 2
def test_ioniq_6_lkas_alt_helper_preserves_stock_camera_fields(self):
CP = CarParams.new_message()
CP.carFingerprint = CAR.HYUNDAI_IONIQ_6
@@ -1294,6 +1817,121 @@ class TestHyundaiFingerprint:
assert parser.vl["LKAS_ALT"]["STEER_REQ"] == 1
assert parser.vl["LKAS_ALT"]["LKA_ICON"] == 2
def test_ev9_angle_lkas_alt_uses_angle_status_fields(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)
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
can_bus = CanBus(CP)
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("LKAS_ALT", 0)], can_bus.ACAN)
stock_lkas = {
"CHECKSUM": 1234,
"COUNTER": 42,
"LKA_MODE": 2,
"LKA_AVAILABLE": 0,
"LKA_WARNING": 1,
"LKA_ICON": 1,
"FCA_SYSWARN": 1,
"TORQUE_REQUEST": 17,
"STEER_REQ": 1,
"LFA_BUTTON": 1,
"LKA_ASSIST": 1,
"STEER_MODE": 5,
"NEW_SIGNAL_2": 0,
"LKAS_ANGLE_ACTIVE": 1,
"HAS_LANE_SAFETY": 1,
"ADAS_StrAnglReqVal": 12.3,
"ADAS_ACIAnglTqRedcGainVal": 0.42,
"DAMP_FACTOR": 0,
}
msgs = hyundaicanfd.create_steering_messages(packer, CP, can_bus, True, 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
parser.update([(1, lkas_msgs)])
assert parser.can_valid
assert parser.vl["LKAS_ALT"]["LKA_MODE"] == 0
assert parser.vl["LKAS_ALT"]["LKA_AVAILABLE"] == 3
assert parser.vl["LKAS_ALT"]["LKA_WARNING"] == 0
assert parser.vl["LKAS_ALT"]["LKA_ICON"] == 2
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"] == 0
assert parser.vl["LKAS_ALT"]["LKA_ASSIST"] == 0
assert parser.vl["LKAS_ALT"]["DAMP_FACTOR"] == 100
assert parser.vl["LKAS_ALT"]["LKAS_ANGLE_ACTIVE"] == 2
assert parser.vl["LKAS_ALT"]["HAS_LANE_SAFETY"] == 0
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):
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)
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
can_bus = CanBus(CP)
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("LKAS_ALT", 0)], can_bus.ACAN)
stock_lkas = {
"CHECKSUM": 1234,
"COUNTER": 42,
"LKA_MODE": 2,
"LKA_AVAILABLE": 3,
"LKA_WARNING": 1,
"LKA_ICON": 1,
"FCA_SYSWARN": 1,
"TORQUE_REQUEST": 17,
"STEER_REQ": 1,
"LFA_BUTTON": 1,
"LKA_ASSIST": 1,
"STEER_MODE": 5,
"NEW_SIGNAL_2": 0,
"LKAS_ANGLE_ACTIVE": 2,
"HAS_LANE_SAFETY": 1,
"ADAS_StrAnglReqVal": 12.3,
"ADAS_ACIAnglTqRedcGainVal": 0.42,
"DAMP_FACTOR": 0,
}
msgs = hyundaicanfd.create_steering_messages(packer, CP, can_bus, False, False, 0.44, -31.5,
lkas_base_values=stock_lkas, lka_icon=1)
lkas_msgs = [msg for msg in msgs if msg[0] == 0x110]
assert len(lkas_msgs) == 1
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_ICON"] == 1
assert parser.vl["LKAS_ALT"]["FCA_SYSWARN"] == 1
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"]["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"]["ADAS_ACIAnglTqRedcGainVal"] == pytest.approx(0.0)
def test_ev9_accelerator_brake_alt_spoof_matches_route_template(self):
msg = hyundaicanfd.create_accelerator_brake_alt_spoof(0, 0x66, True, False, CAR.KIA_EV9)
assert msg.address == 0x100
assert msg.src == 0
assert msg.dat.hex() == "470c6600ff006f00e80400001201030055ffff0000000000"
def test_ioniq_6_lfahda_cluster_allows_lfa_icon_override(self):
CP = CarParams.new_message()
CP.carFingerprint = CAR.HYUNDAI_IONIQ_6
+32 -5
View File
@@ -54,6 +54,9 @@ class CarControllerParams:
if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
self.STEER_THRESHOLD = 175
elif CP.flags & HyundaiFlags.CANFD:
pass
# To determine the limit for your car, find the maximum value that the stock LKAS will request.
# If the max stock LKAS request is <384, add your car to this list.
elif CP.carFingerprint in (CAR.GENESIS_G80, CAR.HYUNDAI_ELANTRA, CAR.HYUNDAI_ELANTRA_GT_I30, CAR.HYUNDAI_IONIQ,
@@ -255,6 +258,14 @@ class CAR(Platforms):
CarSpecs(mass=1675, wheelbase=2.885, steerRatio=14.5),
flags=HyundaiFlags.HYBRID,
)
HYUNDAI_AZERA_HEV_7TH_GEN = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Hyundai Azera Hybrid (with HDA II & LFA2) 2025", "Highway Driving Assist II & Lane Follow Assist 2",
car_parts=CarParts.common([CarHarness.hyundai_s])),
],
CarSpecs(mass=1720, wheelbase=2.895, steerRatio=13.5),
flags=HyundaiFlags.CANFD_ANGLE_STEERING,
)
HYUNDAI_ELANTRA = HyundaiPlatformConfig(
[
# TODO: 2017-18 could be Hyundai G
@@ -725,13 +736,18 @@ class CAR(Platforms):
CarSpecs(mass=1450, wheelbase=2.65, steerRatio=13.75, tireStiffnessFactor=0.5),
flags=HyundaiFlags.LEGACY,
)
KIA_XCEED_PHEV = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia XCeed Plug-in Hybrid 2021", car_parts=CarParts.common([CarHarness.hyundai_b]))],
CarSpecs(mass=1650, wheelbase=2.65, steerRatio=13.75, tireStiffnessFactor=0.5),
flags=HyundaiFlags.LEGACY | HyundaiFlags.HYBRID | HyundaiFlags.MANDO_RADAR,
)
KIA_EV6 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia EV6 (Southeast Asia only) 2022-24", "All", car_parts=CarParts.common([CarHarness.hyundai_p])),
HyundaiCarDocs("Kia EV6 (without HDA II) 2022-24", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_l])),
HyundaiCarDocs("Kia EV6 (with HDA II) 2022-24", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p]))
],
CarSpecs(mass=2055, wheelbase=2.9, steerRatio=16, tireStiffnessFactor=0.65),
CarSpecs(mass=2055, wheelbase=2.9, steerRatio=14.25, tireStiffnessFactor=0.65),
flags=HyundaiFlags.EV,
radar_dbc=HYUNDAI_MRR30_RADAR_DBC,
)
@@ -739,7 +755,7 @@ class CAR(Platforms):
[
HyundaiCarDocs("Kia EV6 (with HDA I) 2025", "Highway Driving Assist I", car_parts=CarParts.common([CarHarness.hyundai_p]))
],
CarSpecs(mass=2055, wheelbase=2.9, steerRatio=16, tireStiffnessFactor=0.65),
CarSpecs(mass=2055, wheelbase=2.9, steerRatio=14.26, tireStiffnessFactor=0.65),
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING,
radar_dbc=HYUNDAI_MRR30_RADAR_DBC,
)
@@ -906,20 +922,22 @@ CANCEL_BUTTON_ENABLE_CARS = frozenset({
CAR.HYUNDAI_PALISADE_2023,
})
KIA_EV6_GT_LINE_LONG_TUNING_VDS_PREFIXES = frozenset({
"C4DLC",
})
# These classic HKG platforms publish the LKAS button on CLU13 over the alt bus.
# Keep G90 excluded until its alt-bus path is route-proven without the recent
# engage/disengage regression.
ALT_BUS_LDA_BUTTON_CARS = frozenset({
CAR.HYUNDAI_SONATA,
CAR.HYUNDAI_SONATA_HYBRID,
})
# On these Sonata layouts the alt-bus LKAS button pulses through the CLU13
# steering-wheel-status field instead of the dedicated LKAS bit.
ALT_BUS_LDA_BUTTON_SWL_STAT_CARS = frozenset({
CAR.HYUNDAI_SONATA,
CAR.HYUNDAI_SONATA_HYBRID,
})
@@ -927,6 +945,11 @@ def hyundai_cancel_button_enables_cruise(car_fingerprint) -> bool:
return car_fingerprint in CANCEL_BUTTON_ENABLE_CARS
def kia_ev6_gt_line_longitudinal_tuning(car_fingerprint, vin: str) -> bool:
return car_fingerprint == CAR.KIA_EV6 and isinstance(vin, str) and \
len(vin) == 17 and vin[3:8] in KIA_EV6_GT_LINE_LONG_TUNING_VDS_PREFIXES
def get_platform_codes(fw_versions: list[bytes]) -> set[tuple[bytes, bytes | None]]:
# Returns unique, platform-specific identification codes for a set of versions
codes = set() # (code-Optional[part], date)
@@ -1083,7 +1106,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
non_essential_ecus={
Ecu.abs: [CAR.HYUNDAI_PALISADE, CAR.HYUNDAI_SONATA, CAR.HYUNDAI_SANTA_FE_2022, CAR.KIA_K5_2021, CAR.HYUNDAI_ELANTRA_2021,
CAR.HYUNDAI_SANTA_FE, CAR.HYUNDAI_KONA_EV_2022, CAR.HYUNDAI_KONA_EV, CAR.HYUNDAI_CUSTIN_1ST_GEN, CAR.KIA_SORENTO,
CAR.KIA_CEED, CAR.KIA_SELTOS],
CAR.KIA_CEED, CAR.KIA_XCEED_PHEV, CAR.KIA_SELTOS],
Ecu.fwdRadar: [CAR.HYUNDAI_KONA_NON_SCC],
},
extra_ecus=[
@@ -1114,6 +1137,7 @@ CANFD_RADAR_SCC_CAR = CAR.with_flags(HyundaiFlags.RADAR_SCC) # TODO: merge with
# CAN-FD cars with ADAS ECUs that work with the communication-control path.
CANFD_SECURITYACCESS_CAR = {CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_6, CAR.HYUNDAI_KONA_EV_2ND_GEN}
CANFD_UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.CANFD_NO_RADAR_DISABLE) - CANFD_SECURITYACCESS_CAR # TODO: merge with UNSUPPORTED_LONGITUDINAL_CAR
CANFD_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}
RADAR_LIVE_LONGITUDINAL_CAR = CANFD_RADAR_LIVE_LONGITUDINAL_CAR | {
CAR.HYUNDAI_IONIQ,
@@ -1123,6 +1147,7 @@ RADAR_LIVE_LONGITUDINAL_CAR = CANFD_RADAR_LIVE_LONGITUDINAL_CAR | {
CAR.HYUNDAI_SANTA_FE_PHEV_2022,
CAR.HYUNDAI_SONATA,
CAR.HYUNDAI_SONATA_HYBRID,
CAR.KIA_XCEED_PHEV,
CAR.GENESIS_G90,
}
@@ -1140,4 +1165,6 @@ NON_SCC_CAR = CAR.with_flags(HyundaiFlags.NON_SCC)
# HyundaiFlags.CANFD_RADAR_SCC | HyundaiFlags.CANFD_NO_RADAR_DISABLE | )
UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.LEGACY) | CAR.with_flags(HyundaiFlags.UNSUPPORTED_LONGITUDINAL)
LEGACY_LONGITUDINAL_CAR = {CAR.KIA_XCEED_PHEV}
DBC = CAR.create_dbc_map()
+13
View File
@@ -22,6 +22,7 @@ from opendbc.car.honda.values import CAR as HONDA, HONDA_BOSCH, HondaFlags, Hond
from opendbc.car.hyundai.hyundaicanfd import CanBus
from opendbc.car.hyundai.values import CAR as HYUNDAI, CANFD_CAR, HyundaiFlags, HyundaiStarPilotFlags, HyundaiStarPilotSafetyFlags, ALT_BUS_LDA_BUTTON_CARS
from opendbc.car.mock.values import CAR as MOCK
from opendbc.car.subaru.values import CAR as SUBARU, SubaruSafetyFlags
from opendbc.car.toyota.values import CAR as TOYOTA, NO_DSU_CAR, TSS2_CAR, UNSUPPORTED_DSU_CAR, ToyotaStarPilotFlags, ToyotaSafetyFlags
from opendbc.car.values import PLATFORMS
from opendbc.can import CANParser
@@ -133,6 +134,7 @@ class CarInterfaceBase(ABC):
dbc_names = {bus: cp.dbc_name for bus, cp in self.can_parsers.items()}
self.CC: CarControllerBase = self.CarController(dbc_names, CP)
self.CC.FPCP = FPCP
self.FPCP = FPCP
@@ -174,8 +176,11 @@ class CarInterfaceBase(ABC):
ret = cls._get_params(ret, candidate, fingerprint, car_fw, alpha_long, is_release, docs)
trailer_load_kg = float(np.clip(getattr(starpilot_toggles, "trailer_load_kg", 0.0) or 0.0, 0.0, 15000.0 * CV.LB_TO_KG))
# Vehicle mass is published curb weight plus assumed payload such as a human driver; notCars have no assumed payload
if not ret.notCar:
ret.mass = ret.mass + trailer_load_kg
ret.mass = ret.mass + STD_CARGO_KG
# Set params dependent on values set by the car interface
@@ -211,6 +216,9 @@ class CarInterfaceBase(ABC):
if candidate == CHRYSLER.RAM_HD_5TH_GEN:
if 570 not in fingerprint[0]:
fp_ret.flags |= ChryslerStarPilotFlags.RAM_HD_ALT_BUTTONS.value
if 0x4FF in fingerprint[0]:
fp_ret.flags |= ChryslerStarPilotFlags.NO_MIN_STEERING_SPEED.value
CP.minSteerSpeed = 0.
elif platform in GM:
fp_ret.canUsePedal = True
@@ -263,6 +271,10 @@ class CarInterfaceBase(ABC):
elif platform.config.platform_str == "TESLA_MODEL_S_PREAP":
fp_ret.canUsePedal = True
elif platform in SUBARU:
if getattr(starpilot_toggles, "subaru_sng", False):
fp_ret.safetyConfigs[-1].safetyParam |= SubaruSafetyFlags.STOP_AND_GO.value
return fp_ret
@staticmethod
@@ -497,6 +509,7 @@ class CarStateBase(ABC):
class CarControllerBase(ABC):
def __init__(self, dbc_names: dict[StrEnum, str], CP: structs.CarParams):
self.CP = CP
self.FPCP: custom.StarPilotCarParams | None = None
self.frame = 0
self.secoc_key: bytes = b"00" * 16
@@ -1,6 +1,6 @@
import numpy as np
from opendbc.can import CANPacker
from opendbc.car import Bus, make_tester_present_msg
from opendbc.car import Bus, DT_CTRL, make_tester_present_msg
from opendbc.car.lateral import apply_driver_steer_torque_limits, common_fault_avoidance
from opendbc.car.interfaces import CarControllerBase
from opendbc.car.subaru import subarucan
@@ -26,14 +26,9 @@ class CarController(CarControllerBase):
self.p = CarControllerParams(CP)
self.packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
self.manual_hold = False
self.prev_standstill = False
self.sng_acc_resume = False
self.prev_close_distance = 0
self.prev_cruise_state = 0
self.sng_acc_resume_cnt = 0
self.standstill_start = 0
self.epb_resume_frames_remaining = -1
self.last_standstill_frame = 0
def update(self, CC, CS, now_nanos, starpilot_toggles):
actuators = CC.actuators
@@ -70,8 +65,9 @@ class CarController(CarControllerBase):
self.apply_torque_last = apply_torque
# *** stop and go ***
subaru_sng_manual_parking_brake = getattr(starpilot_toggles, "subaru_sng_manual_parking_brake", False)
if starpilot_toggles.subaru_sng:
throttle_cmd, speed_cmd = self.stop_and_go(CC, CS)
throttle_cmd, speed_cmd = self.stop_and_go(CC, CS, subaru_sng_manual_parking_brake)
# *** longitudinal ***
@@ -110,7 +106,11 @@ class CarController(CarControllerBase):
can_sends.append(subarucan.create_preglobal_es_distance(self.packer, cruise_button, CS.es_distance_msg))
if starpilot_toggles.subaru_sng:
can_sends.append(subarucan.create_preglobal_throttle(self.packer, CS.throttle_msg["COUNTER"] + 1, CS.throttle_msg, throttle_cmd))
can_sends.append(subarucan.create_preglobal_throttle(self.packer, CS.throttle_msg["COUNTER"] + 1, CS.throttle_msg,
throttle_cmd))
if self.frame % 2 == 0:
can_sends.append(subarucan.create_preglobal_brake_pedal(self.packer, CS.brake_pedal_msg,
speed_cmd))
else:
if self.frame % 10 == 0:
can_sends.append(subarucan.create_es_dashstatus(self.packer, self.frame // 10, CS.es_dashstatus_msg, CC.enabled,
@@ -124,9 +124,11 @@ class CarController(CarControllerBase):
can_sends.append(subarucan.create_es_infotainment(self.packer, self.frame // 10, CS.es_infotainment_msg, hud_control.visualAlert))
if starpilot_toggles.subaru_sng:
can_sends.append(subarucan.create_throttle(self.packer, CS.throttle_msg["COUNTER"] + 1, CS.throttle_msg, throttle_cmd))
can_sends.append(subarucan.create_throttle(self.packer, CS.throttle_msg["COUNTER"] + 1, CS.throttle_msg,
throttle_cmd))
if self.frame % 2 == 0:
can_sends.append(subarucan.create_brake_pedal(self.packer, self.frame // 2, CS.brake_pedal_msg, speed_cmd, pcm_cancel_cmd))
can_sends.append(subarucan.create_brake_pedal(self.packer, self.frame // 2, CS.brake_pedal_msg,
speed_cmd, pcm_cancel_cmd))
if self.CP.openpilotLongitudinalControl:
if self.frame % 5 == 0:
@@ -166,49 +168,37 @@ class CarController(CarControllerBase):
self.frame += 1
return new_actuators, can_sends
def stop_and_go(self, CC, CS, speed_cmd=False, throttle_cmd=False):
if self.CP.flags & SubaruFlags.PREGLOBAL:
trigger_resume = CC.enabled
trigger_resume &= CS.car_follow == 1
trigger_resume &= CS.close_distance > self.prev_close_distance
trigger_resume &= CS.out.standstill
trigger_resume &= _SNG_ACC_MIN_DIST < CS.close_distance < _SNG_ACC_MAX_DIST
def stop_and_go(self, CC, CS, manual_parking_brake=False):
throttle_cmd = False
speed_cmd = False
if trigger_resume:
self.sng_acc_resume = True
else:
if CS.car_follow == 0 and CS.cruise_state == 3 and CS.out.standstill and self.prev_cruise_state == 1:
self.manual_hold = True
if not CC.enabled or not CC.hudControl.leadVisible:
return throttle_cmd, speed_cmd
if not CS.out.standstill:
self.manual_hold = False
close_distance = CS.close_distance
if not CS.out.standstill:
self.last_standstill_frame = self.frame
trigger_resume = CC.enabled
trigger_resume &= CS.car_follow == 1
trigger_resume &= CS.close_distance > self.prev_close_distance
trigger_resume &= CS.cruise_state == 3
trigger_resume &= not self.manual_hold
trigger_resume &= _SNG_ACC_MIN_DIST < CS.close_distance < _SNG_ACC_MAX_DIST
standstill_timers = (0.75, 0.8) if self.CP.flags & SubaruFlags.PREGLOBAL else (0.5, 0.55)
standstill_duration = (self.frame - self.last_standstill_frame) * DT_CTRL
in_standstill_hold = standstill_duration > standstill_timers[0]
if standstill_duration >= standstill_timers[1]:
self.last_standstill_frame = self.frame
if trigger_resume:
self.sng_acc_resume = True
if manual_parking_brake or not (self.CP.flags & SubaruFlags.PREGLOBAL):
speed_cmd = in_standstill_hold
if CC.enabled and CS.car_follow == 1 and CS.out.standstill and self.frame > self.standstill_start + 50:
speed_cmd = True
should_resume = (
CS.out.standstill and
_SNG_ACC_MIN_DIST < close_distance < _SNG_ACC_MAX_DIST and
close_distance > self.prev_close_distance
)
if should_resume:
self.epb_resume_frames_remaining = 15
if CS.out.standstill and not self.prev_standstill:
self.standstill_start = self.frame
throttle_cmd = self.epb_resume_frames_remaining > 0
if self.epb_resume_frames_remaining > 0:
self.epb_resume_frames_remaining -= 1
self.prev_standstill = CS.out.standstill
self.prev_cruise_state = CS.cruise_state
if self.sng_acc_resume:
if self.sng_acc_resume_cnt < 5:
throttle_cmd = True
self.sng_acc_resume_cnt += 1
else:
self.sng_acc_resume = False
self.sng_acc_resume_cnt = -1
self.prev_close_distance = CS.close_distance
self.prev_close_distance = close_distance
return throttle_cmd, speed_cmd
@@ -358,6 +358,19 @@ def create_brake_pedal(packer, frame, brake_pedal_msg, speed_cmd, brake_cmd):
return packer.make_can_msg("Brake_Pedal", CanBus.camera, values)
def create_preglobal_brake_pedal(packer, brake_pedal_msg, speed_cmd):
values = {s: brake_pedal_msg[s] for s in sorted([
"Brake_Pedal",
"Signal1",
"Speed",
])}
if speed_cmd:
values["Speed"] = 1
return packer.make_can_msg("Brake_Pedal", CanBus.camera, values)
def create_throttle(packer, frame, throttle_msg, throttle_cmd):
values = {s: throttle_msg[s] for s in sorted([
"CHECKSUM",
@@ -1,4 +1,57 @@
from types import SimpleNamespace
from opendbc.car.subaru.carcontroller import CarController
from opendbc.car.subaru.fingerprints import FW_VERSIONS
from opendbc.car.subaru.values import SubaruFlags
def make_sng_controller(flags=0, prev_close_distance=4.0):
controller = object.__new__(CarController)
controller.CP = SimpleNamespace(flags=flags)
controller.frame = 60
controller.last_standstill_frame = 0
controller.prev_close_distance = prev_close_distance
controller.epb_resume_frames_remaining = -1
return controller
def make_sng_state(close_distance=4.0, standstill=True):
cc = SimpleNamespace(enabled=True, hudControl=SimpleNamespace(leadVisible=True))
cs = SimpleNamespace(
close_distance=close_distance,
out=SimpleNamespace(standstill=standstill),
)
return cc, cs
def test_global_sng_keeps_standstill_alive_without_manual_parking_brake_toggle():
controller = make_sng_controller()
cc, cs = make_sng_state()
throttle_cmd, speed_cmd = controller.stop_and_go(cc, cs, manual_parking_brake=False)
assert throttle_cmd is False
assert speed_cmd is True
def test_manual_parking_brake_sng_still_sends_resume_throttle():
controller = make_sng_controller(prev_close_distance=3.9)
cc, cs = make_sng_state(close_distance=4.0)
throttle_cmd, speed_cmd = controller.stop_and_go(cc, cs, manual_parking_brake=True)
assert throttle_cmd is True
assert speed_cmd is True
def test_preglobal_sng_does_not_send_standstill_keepalive_without_manual_toggle():
controller = make_sng_controller(flags=SubaruFlags.PREGLOBAL)
cc, cs = make_sng_state()
throttle_cmd, speed_cmd = controller.stop_and_go(cc, cs, manual_parking_brake=False)
assert throttle_cmd is False
assert speed_cmd is False
class TestSubaruFingerprint:
@@ -59,6 +59,7 @@ class SubaruSafetyFlags(IntFlag):
GEN2 = 1
LONG = 2
PREGLOBAL_REVERSED_DRIVER_TORQUE = 4
STOP_AND_GO = 8
class SubaruFlags(IntFlag):
@@ -126,16 +126,16 @@ def update_preap(cs, can_parsers):
cs.das_control = None
cs.cruise_enabled_prev = ret.cruiseState.enabled
ret.pedalMaxRegen = cs.pccEvent == "pedalMaxRegen"
ret.teslaCCEngaged = cs.pccEvent == "teslaCCEngaged"
ret.teslaCCDisengaged = cs.pccEvent == "teslaCCDisengaged"
ret.teslaCCNotArmed = (
fp_ret.pedalMaxRegen = cs.pccEvent == "pedalMaxRegen"
fp_ret.teslaCCEngaged = cs.pccEvent == "teslaCCEngaged"
fp_ret.teslaCCDisengaged = cs.pccEvent == "teslaCCDisengaged"
fp_ret.teslaCCNotArmed = (
not nap_conf.use_pedal and
cs.cruiseEnabled and
cs.enableLongControl and
cs.di_cruise_state not in ("STANDBY", "ENABLED")
)
ret.pedalLongActive = cs.enableLongControl and nap_conf.use_pedal
fp_ret.pedalLongActive = cs.enableLongControl and nap_conf.use_pedal
return ret, fp_ret
@@ -7,6 +7,7 @@ from opendbc.car.tesla.preap.nap_conf import nap_conf
PREAP_FLAG_ENABLE_PEDAL = 1
PREAP_FLAG_RADAR_EMULATION = 2
PREAP_FLAG_RADAR_BEHIND_NOSECONE = 4
SAFETY_TESLA_PREAP = 35
def get_preap_accel_limits(current_speed: float) -> tuple[float, float]:
@@ -29,7 +30,7 @@ def get_preap_params(ret: structs.CarParams) -> structs.CarParams:
safety_flags |= PREAP_FLAG_RADAR_BEHIND_NOSECONE
use_pedal = nap_conf.use_pedal and nap_conf.pedal_calibrated
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.teslaPreap, safety_flags)]
ret.safetyConfigs = [get_safety_config(SAFETY_TESLA_PREAP, safety_flags)]
ret.radarUnavailable = not nap_conf.radar_enabled
ret.steerControlType = structs.CarParams.SteerControlType.angle
ret.openpilotLongitudinalControl = use_pedal
+3
View File
@@ -22,12 +22,14 @@ non_tested_cars = [
MOCK.MOCK,
GM.CADILLAC_ATS,
GM.CADILLAC_ESCALADE_ASCM,
GM.CADILLAC_ESCALADE_ESV_2019_ASCM,
GM.CADILLAC_XT5,
GM.HOLDEN_ASTRA,
GM.CHEVROLET_MALIBU,
HYUNDAI.GENESIS_G90,
HYUNDAI.GENESIS_GV70_ELECTRIFIED_2ND_GEN,
HYUNDAI.GENESIS_GV80_2025,
HYUNDAI.HYUNDAI_AZERA_HEV_7TH_GEN,
HYUNDAI.HYUNDAI_IONIQ_5_PE,
HYUNDAI.HYUNDAI_IONIQ_5_N,
HYUNDAI.HYUNDAI_SANTA_FE_HEV_5TH_GEN,
@@ -163,6 +165,7 @@ routes = [
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("e0e98335f3ebc58f/2021-03-07--16-38-29", HYUNDAI.KIA_CEED),
CarTestRoute("22703002ddbe2a08/0000000e--083b76c42c", HYUNDAI.KIA_XCEED_PHEV, segment=0),
CarTestRoute("7653b2bce7bcfdaa/2020-03-04--15-34-32", HYUNDAI.KIA_OPTIMA_G4),
CarTestRoute("018654717bc93d7d/2022-09-19--23-11-10", HYUNDAI.KIA_OPTIMA_G4_FL, segment=0),
CarTestRoute("f9716670b2481438/2023-08-23--14-49-50", HYUNDAI.KIA_OPTIMA_H),
@@ -4,9 +4,17 @@ import pytest
from opendbc.car.can_definitions import CanData
from opendbc.car.car_helpers import FRAME_FINGERPRINT, _apply_starpilot_access_policy, _get_gm_stored_candidate_fallback, can_fingerprint
from opendbc.car.fingerprints import _FINGERPRINTS as FINGERPRINTS
from opendbc.car.gm.values import CAR as GM
class TestCanFingerprint:
@staticmethod
def _fingerprint_from_can(fingerprint):
can = [CanData(address=address, dat=b'\x00' * length, src=src)
for address, length in fingerprint.items() for src in (0, 1)]
fingerprint_iter = iter([can])
return can_fingerprint(lambda **kwargs: [next(fingerprint_iter, [])]) # noqa: B023
@pytest.mark.parametrize("car_model, fingerprints", FINGERPRINTS.items())
def test_can_fingerprint(self, car_model, fingerprints):
"""Tests online fingerprinting function on offline fingerprints"""
@@ -23,6 +31,12 @@ class TestCanFingerprint:
assert finger[1] == fingerprint
assert finger[2] == {}
def test_gm_sascm_superset_fingerprint_matches_ascm_variant(self):
car_fingerprint, finger = self._fingerprint_from_can(FINGERPRINTS[GM.CADILLAC_ESCALADE_ESV_2019_ASCM][0])
assert car_fingerprint == GM.CADILLAC_ESCALADE_ESV_2019_ASCM
assert finger[0][0x2FF] == 8
def test_timing(self, subtests):
# just pick any CAN fingerprinting car
car_model = "CHEVROLET_BOLT_ACC_2022_2023"
@@ -8,9 +8,13 @@ from collections.abc import Callable
from typing import Any
from cereal import custom
from opendbc.car import DT_CTRL, CanData, structs
from opendbc.can import CANParser
from opendbc.car import Bus, DT_CTRL, CanData, structs
from opendbc.car.car_helpers import _apply_disable_openpilot_long, interfaces
from opendbc.car.chrysler.carcontroller import CarController as ChryslerCarController
from opendbc.car.chrysler.carstate import CarState as ChryslerCarState
from opendbc.car.chrysler.interface import CarInterface as ChryslerCarInterface
from opendbc.car.chrysler.values import CAR as CHRYSLER_CAR, DBC as CHRYSLER_DBC, ChryslerStarPilotFlags
from opendbc.car.fingerprints import FW_VERSIONS
from opendbc.car.fw_versions import FW_QUERY_CONFIGS
from opendbc.car.hyundai.interface import CarInterface as HyundaiCarInterface
@@ -74,6 +78,7 @@ def get_test_starpilot_toggles() -> SimpleNamespace:
reverse_cruise_increase=False,
sng_hack=False,
subaru_sng=False,
subaru_sng_manual_parking_brake=False,
unlock_doors=False,
vEgoStopping=0.5,
volt_sng=False,
@@ -160,6 +165,53 @@ class TestCarInterfaces:
"Center_Stack_2": {"LKAS_Button": 1},
}, is_ram=True)
def test_chrysler_wd_mod_enables_steer_to_zero(self):
fingerprint = {bus: {} for bus in range(8)}
fingerprint[0][0x4FF] = 8
toggles = get_test_starpilot_toggles()
car_params = ChryslerCarInterface.get_params(
CHRYSLER_CAR.CHRYSLER_PACIFICA_2020,
fingerprint,
[],
alpha_long=False,
is_release=False,
docs=False,
starpilot_toggles=toggles,
)
assert car_params.minSteerSpeed > 0.
fp_car_params = ChryslerCarInterface.get_starpilot_params(
CHRYSLER_CAR.CHRYSLER_PACIFICA_2020,
fingerprint,
[],
car_params,
toggles,
)
assert fp_car_params.flags & ChryslerStarPilotFlags.NO_MIN_STEERING_SPEED
assert car_params.minSteerSpeed == 0.
controller = ChryslerCarController({Bus.pt: CHRYSLER_DBC[car_params.carFingerprint][Bus.pt]}, car_params)
controller.FPCP = fp_car_params
controller.frame = 202
CC = structs.CarControl()
CC.latActive = True
CC.actuators.torque = 0.1
CC = CC.as_reader()
CS = SimpleNamespace(
out=SimpleNamespace(vEgo=0.0, steeringTorqueEps=0.0, gearShifter=structs.CarState.GearShifter.drive),
lkas_car_model=-1,
button_counter=0,
button_message="CRUISE_BUTTONS",
auto_high_beam=0,
)
_, can_sends = controller.update(CC, CS, 0, toggles)
lkas_parser = CANParser(CHRYSLER_DBC[car_params.carFingerprint][Bus.pt], [("LKAS_COMMAND", 50)], 0)
lkas_parser.update([0, can_sends])
assert lkas_parser.vl["LKAS_COMMAND"]["LKAS_CONTROL_BIT"] == 1
def test_gm_bolt_gen2_pedal_safety_flags(self):
CarInterface = interfaces[GM_CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL]
@@ -17,6 +17,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]
# Tesla angle based controllers
"TESLA_MODEL_3" = [nan, 2.5, nan]
@@ -74,6 +75,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"HYUNDAI_TUCSON_HEV_2025" = [2.5, 2.5, 0.1]
"HYUNDAI_TUCSON_PHEV_2025" = [2.5, 2.5, 0.1]
"KIA_SPORTAGE_5TH_GEN" = [2.6, 2.6, 0.1]
"KIA_XCEED_PHEV" = [2.9638737459977467, 2.1259108157250735, 0.07813665616927593]
"KIA_SPORTAGE_2026" = [nan, 2.5, nan]
"KIA_SPORTAGE_HEV_2026" = [nan, 2.5, nan]
"GENESIS_GV70_1ST_GEN" = [2.42, 2.42, 0.1]
@@ -96,6 +98,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"HYUNDAI_IONIQ_9" = [nan, 2.5, nan]
"HYUNDAI_AZERA_6TH_GEN" = [1.8, 1.8, 0.1]
"HYUNDAI_AZERA_HEV_6TH_GEN" = [1.8, 1.8, 0.1]
"HYUNDAI_AZERA_HEV_7TH_GEN" = [nan, 2.5, nan]
"KIA_K8_HEV_1ST_GEN" = [2.5, 2.5, 0.1]
"KIA_K4_2025" = [2.5, 2.5, 0.1]
"KIA_K5_2025" = [2.5, 2.5, 0.1]
@@ -69,6 +69,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"BUICK_REGAL" = "CHEVROLET_VOLT"
"CADILLAC_ESCALADE_ASCM" = "CADILLAC_ESCALADE"
"CADILLAC_ESCALADE_ESV" = "CHEVROLET_VOLT"
"CADILLAC_ESCALADE_ESV_2019_ASCM" = "CADILLAC_ESCALADE_ESV_2019"
"CADILLAC_ATS" = "CHEVROLET_VOLT"
"CHEVROLET_MALIBU" = "CHEVROLET_VOLT"
"CHEVROLET_MALIBU_ASCM" = "CHEVROLET_VOLT"
@@ -26,6 +26,7 @@ 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
MAX_PITCH_COMPENSATION = 1.5 # m/s^2
TOYOTA_COAST_BRAKE_MIN_SPEED = 15.0 # m/s
@@ -43,6 +44,7 @@ MAX_STEER_RATE_FRAMES = 18 # tx control frames needed before torque can be cut
MAX_USER_TORQUE = 500
PARK = structs.CarState.GearShifter.park
REVERSE = structs.CarState.GearShifter.reverse
# Lock / unlock door commands - Credit goes to AlexandreSato!
LOCK_CMD = b"\x40\x05\x30\x11\x00\x80\x00\x00"
@@ -65,6 +67,11 @@ def get_long_tune(CP, params):
rate=1 / (DT_CTRL * 3))
def get_prius_positive_feedforward_scale(v_ego: float) -> float:
return float(np.interp(v_ego, [0.0, 8.0, 20.0],
[PRIUS_POSITIVE_FEEDFORWARD_SCALE, PRIUS_POSITIVE_FEEDFORWARD_SCALE, PRIUS_CRUISE_FEEDFORWARD_SCALE]))
def 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:
@@ -192,18 +199,23 @@ class CarController(CarControllerBase):
self.secoc_prev_reset_counter = 0
self.doors_locked = False
self.auto_brake_hold = bool(self.CP.flags & ToyotaFlags.AUTO_BRAKE_HOLD.value)
self.brake_hold_active = False
self._brake_hold_counter = 0
self._brake_hold_reset = False
self._prev_brake_pressed = False
def _compute_interceptor_gas_cmd(self, CC, CS):
if not (self.CP.enableGasInterceptorDEPRECATED and self.CP.openpilotLongitudinalControl and CC.longActive):
return 0.0
if self.CP.minEnableSpeed < 0.0:
return 0.12 if CS.out.standstill and self.accel > 0.0 else 0.0
if CS.out.standstill:
return 0.12 if self.accel > 0.0 else 0.0
max_interceptor_gas = 0.5
if self.CP.carFingerprint == CAR.TOYOTA_RAV4:
pedal_scale = float(np.interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.15, 0.3, 0.0]))
elif self.CP.carFingerprint == CAR.TOYOTA_COROLLA:
elif self.CP.carFingerprint in (CAR.TOYOTA_COROLLA, CAR.TOYOTA_MATRIX_RETROFIT):
pedal_scale = float(np.interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.3, 0.4, 0.0]))
else:
pedal_scale = float(np.interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.4, 0.5, 0.0]))
@@ -237,6 +249,27 @@ class CarController(CarControllerBase):
self.last_standstill = CS.out.standstill
def create_auto_brake_hold_messages(self, CS: structs.CarState, brake_hold_allowed_timer: int = 100):
can_sends = []
brake_hold_allowed = (CS.out.standstill and CS.out.cruiseState.available and
not CS.out.gasPressed and not CS.out.cruiseState.enabled and
CS.out.gearShifter not in (PARK, REVERSE))
if brake_hold_allowed:
self._brake_hold_counter += 1
self.brake_hold_active = self._brake_hold_counter > brake_hold_allowed_timer and not self._brake_hold_reset
self._brake_hold_reset = not self._prev_brake_pressed and CS.out.brakePressed and not self._brake_hold_reset
else:
self._brake_hold_counter = 0
self.brake_hold_active = False
self._brake_hold_reset = False
self._prev_brake_pressed = CS.out.brakePressed
if self.frame % 2 == 0:
can_sends.append(toyotacan.create_brake_hold_command(self.packer, self.frame, CS.pre_collision_2, self.brake_hold_active))
return can_sends
def update(self, CC, CS, now_nanos, starpilot_toggles):
actuators = CC.actuators
stopping = actuators.longControlState == LongCtrlState.stopping
@@ -330,6 +363,9 @@ class CarController(CarControllerBase):
# *** gas and brake ***
self._update_standstill_request(CC, CS, actuators, starpilot_toggles)
if self.auto_brake_hold:
can_sends.extend(self.create_auto_brake_hold_messages(CS))
interceptor_gas_cmd = self._compute_interceptor_gas_cmd(CC, CS)
# handle UI messages
@@ -405,9 +441,8 @@ class CarController(CarControllerBase):
feedforward = pcm_accel_cmd
if self.CP.carFingerprint == CAR.TOYOTA_PRIUS:
# Keep Prius positive handoffs softer than the stock tune, while restoring some launch authority.
if feedforward > 0.0:
feedforward *= PRIUS_POSITIVE_FEEDFORWARD_SCALE
feedforward *= get_prius_positive_feedforward_scale(CS.out.vEgo)
pcm_accel_cmd = self.long_pid.update(error_future,
speed=CS.out.vEgo,
@@ -80,6 +80,8 @@ class CarState(CarStateBase):
self.has_can_filter = self.FPCP.flags & ToyotaStarPilotFlags.RADAR_CAN_FILTER.value
self.has_SDSU = self.FPCP.flags & ToyotaStarPilotFlags.SMART_DSU.value
self.has_ZSS = self.FPCP.flags & ToyotaStarPilotFlags.ZSS.value
self.auto_brake_hold = bool(self.CP.flags & ToyotaFlags.AUTO_BRAKE_HOLD.value)
self.pre_collision_2 = {}
def update(self, can_parsers, starpilot_toggles) -> structs.CarState:
cp = can_parsers[Bus.pt]
@@ -211,6 +213,9 @@ class CarState(CarStateBase):
if self.CP.carFingerprint != CAR.TOYOTA_PRIUS_V:
self.lkas_hud = copy.copy(cp_cam.vl["LKAS_HUD"])
if self.auto_brake_hold:
self.pre_collision_2 = copy.copy(cp_cam.vl["PRE_COLLISION_2"])
if self.CP.carFingerprint not in UNSUPPORTED_DSU_CAR:
self.pcm_follow_distance = cp.vl["PCM_CRUISE_2"]["PCM_FOLLOW_DISTANCE"]
@@ -4,6 +4,10 @@ from opendbc.car.toyota.values import CAR
Ecu = CarParams.Ecu
FINGERPRINTS = {
CAR.TOYOTA_MATRIX_RETROFIT: [{}],
}
FW_VERSIONS = {
CAR.TOYOTA_AVALON: {
(Ecu.abs, 0x7b0, None): [
+15 -3
View File
@@ -2,11 +2,13 @@ from opendbc.car import Bus, structs, get_safety_config, uds
from opendbc.car.toyota.carstate import CarState
from opendbc.car.toyota.carcontroller import CarController
from opendbc.car.toyota.radar_interface import RadarInterface
from opendbc.car.toyota.values import Ecu, CAR, DBC, ToyotaFlags, CarControllerParams, TSS2_CAR, RADAR_ACC_CAR, NO_DSU_CAR, \
from opendbc.car.toyota.values import Ecu, CAR, DBC, ToyotaFlags, CarControllerParams, TSS2_CAR, RADAR_ACC_CAR, SECOC_CAR, NO_DSU_CAR, \
MIN_ACC_SPEED, EPS_SCALE, NO_STOP_TIMER_CAR, ANGLE_CONTROL_CAR, \
ToyotaSafetyFlags
from opendbc.car.disable_ecu import disable_ecu
from opendbc.car.interfaces import CarInterfaceBase
from opendbc.safety import ALTERNATIVE_EXPERIENCE
from openpilot.common.params import Params
SteerControlType = structs.CarParams.SteerControlType
@@ -66,10 +68,11 @@ class CarInterface(CarInterfaceBase):
if candidate == CAR.TOYOTA_PRIUS:
stop_and_go = True
ret.flags |= ToyotaFlags.HYBRID.value
# Only give steer angle deadzone to for bad angle sensor prius
for fw in car_fw:
if fw.ecu == "eps" and not fw.fwVersion == b'8965B47060\x00\x00\x00\x00\x00\x00':
ret.steerActuatorDelay = 0.25
ret.steerActuatorDelay = 0.14
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg=0.3)
elif candidate in (CAR.LEXUS_RX, CAR.LEXUS_RX_TSS2):
@@ -137,6 +140,13 @@ class CarInterface(CarInterfaceBase):
ret.autoResumeSng = ret.openpilotLongitudinalControl and candidate in NO_STOP_TIMER_CAR
ret.enableGasInterceptorDEPRECATED = 0x201 in fingerprint[0] and ret.openpilotLongitudinalControl
if ret.enableGasInterceptorDEPRECATED:
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.GAS_INTERCEPTOR.value
toyota_auto_hold = Params(return_defaults=True).get_bool("ToyotaAutoHold")
if toyota_auto_hold and candidate in (TSS2_CAR - RADAR_ACC_CAR - SECOC_CAR):
ret.alternativeExperience |= ALTERNATIVE_EXPERIENCE.ALLOW_AEB
ret.flags |= ToyotaFlags.AUTO_BRAKE_HOLD.value
if not ret.openpilotLongitudinalControl:
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
@@ -145,7 +155,9 @@ class CarInterface(CarInterfaceBase):
# to a negative value, so it won't matter.
ret.minEnableSpeed = -1. if (stop_and_go or ret.enableGasInterceptorDEPRECATED) else MIN_ACC_SPEED
if candidate in TSS2_CAR or ret.enableGasInterceptorDEPRECATED:
prius_long_defaults = candidate == CAR.TOYOTA_PRIUS and ret.openpilotLongitudinalControl
if candidate in TSS2_CAR or ret.enableGasInterceptorDEPRECATED or prius_long_defaults:
ret.flags |= ToyotaFlags.RAISED_ACCEL_LIMIT.value
ret.vEgoStopping = 0.25
@@ -2,19 +2,21 @@ from types import SimpleNamespace
from hypothesis import given, settings, strategies as st
from opendbc.car import Bus
from opendbc.car import Bus, structs
from opendbc.can import CANPacker, CANParser
from opendbc.car.structs import CarParams
from opendbc.car.fw_versions import build_fw_dict
from opendbc.car.toyota import toyotacan
from opendbc.car.toyota.carcontroller import CarController, limit_interceptor_pcm_accel, limit_interceptor_stopping_accel, \
limit_prius_stopping_accel, update_permit_braking
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.fingerprints import FW_VERSIONS
from opendbc.car.toyota.interface import CarInterface
from opendbc.car.toyota.values import CAR, DBC, TSS2_CAR, ANGLE_CONTROL_CAR, RADAR_ACC_CAR, SECOC_CAR, \
FW_QUERY_CONFIG, PLATFORM_CODE_ECUS, FUZZY_EXCLUDED_PLATFORMS, \
get_platform_codes
ToyotaFlags, ToyotaSafetyFlags, get_platform_codes
from opendbc.safety import ALTERNATIVE_EXPERIENCE
from openpilot.common.params import Params
Ecu = CarParams.Ecu
@@ -40,6 +42,43 @@ class TestToyotaInterfaces:
if car_model in TSS2_CAR and car_model not in SECOC_CAR:
assert dbc[Bus.pt] == "toyota_nodsu_pt_generated"
def test_auto_hold_sets_flag_on_supported_tss2(self):
params = Params()
try:
params.put_bool("ToyotaAutoHold", True)
car_params = CarInterface.get_params(
CAR.TOYOTA_CAMRY_TSS2,
{bus: {} for bus in range(8)},
[],
alpha_long=False,
is_release=False,
docs=False,
starpilot_toggles=SimpleNamespace(),
)
finally:
params.remove("ToyotaAutoHold")
assert car_params.flags & ToyotaFlags.AUTO_BRAKE_HOLD.value
assert car_params.alternativeExperience & ALTERNATIVE_EXPERIENCE.ALLOW_AEB
def test_prius_openpilot_long_uses_hybrid_long_defaults(self):
car_params = CarInterface.get_params(
CAR.TOYOTA_PRIUS,
{0: {0x2FF: 8}},
[],
alpha_long=False,
is_release=False,
docs=False,
starpilot_toggles=SimpleNamespace(),
)
assert car_params.openpilotLongitudinalControl
assert car_params.flags & ToyotaFlags.HYBRID.value
assert car_params.flags & ToyotaFlags.RAISED_ACCEL_LIMIT.value
assert abs(car_params.longitudinalActuatorDelay - 0.05) < 1e-6
assert abs(car_params.vEgoStopping - 0.25) < 1e-6
assert abs(car_params.vEgoStarting - 0.25) < 1e-6
def test_essential_ecus(self, subtests):
# Asserts standard ECUs exist for each platform
common_ecus = {Ecu.fwdRadar, Ecu.fwdCamera}
@@ -261,6 +300,14 @@ class TestToyotaCarController:
limited = limit_prius_stopping_accel(-3.28, -2.0, True, 0.0, True)
assert limited == -3.28
def test_prius_positive_feedforward_scale_stays_soft_at_launch_speed(self):
assert abs(get_prius_positive_feedforward_scale(0.0) - 0.7) < 1e-6
assert abs(get_prius_positive_feedforward_scale(8.0) - 0.7) < 1e-6
def test_prius_positive_feedforward_scale_restores_cruise_authority(self):
assert get_prius_positive_feedforward_scale(20.0) > get_prius_positive_feedforward_scale(8.0)
assert abs(get_prius_positive_feedforward_scale(20.0) - 0.85) < 1e-6
def test_sng_hack_clears_existing_standstill_latch(self):
controller = self._make_controller(standstill_req=True, last_standstill=True)
@@ -297,6 +344,33 @@ class TestToyotaCarController:
assert parser.vl["LKAS_HUD"]["LEFT_LINE"] == 0
assert parser.vl["LKAS_HUD"]["RIGHT_LINE"] == 0
def test_auto_brake_hold_sends_modified_pre_collision_after_timer(self):
controller = self._make_controller()
controller.packer = CANPacker(DBC[CAR.TOYOTA_CAMRY_TSS2][Bus.pt])
controller.frame = 0
controller.brake_hold_active = False
controller._brake_hold_counter = 0
controller._brake_hold_reset = False
controller._prev_brake_pressed = False
cs = SimpleNamespace(
out=SimpleNamespace(
standstill=True,
cruiseState=SimpleNamespace(available=True, enabled=False),
gasPressed=False,
brakePressed=False,
gearShifter=structs.CarState.GearShifter.drive,
),
pre_collision_2={},
)
can_sends = controller.create_auto_brake_hold_messages(cs, brake_hold_allowed_timer=0)
parser = CANParser(DBC[CAR.TOYOTA_CAMRY_TSS2][Bus.pt], [("PRE_COLLISION_2", 0)], 0)
parser.update([(1, can_sends)])
assert controller.brake_hold_active
assert parser.vl["PRE_COLLISION_2"]["DSS1GDRV"] == -1.0
assert parser.vl["PRE_COLLISION_2"]["PBRTRGR"] == 1
def test_interceptor_stop_and_go_holds_small_launch_at_standstill(self):
controller = self._make_controller()
controller.CP.enableGasInterceptorDEPRECATED = True
@@ -323,6 +397,20 @@ class TestToyotaCarController:
assert 0.0 < gas_cmd <= 0.5
def test_interceptor_corolla_scales_with_accel_request_when_pedal_enables_sng(self):
controller = self._make_controller()
controller.CP.enableGasInterceptorDEPRECATED = True
controller.CP.carFingerprint = CAR.TOYOTA_COROLLA
controller.CP.minEnableSpeed = -1.0
controller.accel = 0.8
gas_cmd = controller._compute_interceptor_gas_cmd(
SimpleNamespace(longActive=True),
SimpleNamespace(out=SimpleNamespace(standstill=False, vEgo=8.0)),
)
assert 0.0 < gas_cmd <= 0.5
def test_interceptor_disabled_returns_zero(self):
controller = self._make_controller()
controller.accel = 1.0
@@ -401,6 +489,7 @@ class TestToyotaCarController:
)
assert CP.enableGasInterceptorDEPRECATED
assert CP.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.GAS_INTERCEPTOR
assert abs(CP.longitudinalActuatorDelay - 0.2) < 1e-6
assert CP.stopAccel == -1.5
@@ -86,6 +86,38 @@ def create_pcs_commands(packer, accel, active, mass):
return [msg1, msg2]
def create_brake_hold_command(packer, frame, pre_collision_2, brake_hold_active):
values = {s: pre_collision_2[s] for s in [
"DSS1GDRV",
"DS1STAT2",
"DS1STBK2",
"PCSWAR",
"PCSALM",
"PCSOPR",
"PCSABK",
"PBATRGR",
"PPTRGR",
"IBTRGR",
"CLEXTRGR",
"IRLT_REQ",
"BRKHLD",
"AVSTRGR",
"VGRSTRGR",
"PREFILL",
"PBRTRGR",
"PCSDIS",
"PBPREPMP",
] if s in pre_collision_2}
if brake_hold_active:
values = {
"DSS1GDRV": 0x3FF,
"PBRTRGR": frame % 730 < 727,
}
return packer.make_can_msg("PRE_COLLISION_2", 0, values)
def create_acc_cancel_command(packer):
values = {
"GAS_RELEASED": 0,
+10 -2
View File
@@ -57,6 +57,7 @@ class ToyotaSafetyFlags(IntFlag):
LTA = (4 << 8)
SECOC = (8 << 8)
LONG_FILTER = (16 << 8)
GAS_INTERCEPTOR = (32 << 8)
class ToyotaFlags(IntFlag):
@@ -75,6 +76,7 @@ class ToyotaFlags(IntFlag):
# these cars can utilize 2.0 m/s^2
RAISED_ACCEL_LIMIT = 1024
SECOC = 2048
AUTO_BRAKE_HOLD = 4096
# deprecated flags
# these cars are speculated to allow stop and go when the DSU is unplugged
@@ -201,6 +203,11 @@ class CAR(Platforms):
CarSpecs(mass=2860. * CV.LB_TO_KG, wheelbase=2.7, steerRatio=18.27, tireStiffnessFactor=0.444),
dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
)
TOYOTA_MATRIX_RETROFIT = PlatformConfig(
[ToyotaCommunityCarDocs("Toyota Matrix 2005 Retrofit", package="Custom retrofit")],
TOYOTA_COROLLA.specs,
dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
)
# LSS2 Lexus UX Hybrid is same as a TSS2 Corolla Hybrid
TOYOTA_COROLLA_TSS2 = ToyotaTSS2PlatformConfig(
[
@@ -548,7 +555,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
],
non_essential_ecus={
# FIXME: On some models, abs can sometimes be missing
Ecu.abs: [CAR.TOYOTA_RAV4, CAR.TOYOTA_COROLLA, CAR.TOYOTA_HIGHLANDER, CAR.TOYOTA_SIENNA, CAR.LEXUS_IS, CAR.TOYOTA_ALPHARD_TSS2],
Ecu.abs: [CAR.TOYOTA_RAV4, CAR.TOYOTA_COROLLA, CAR.TOYOTA_MATRIX_RETROFIT, CAR.TOYOTA_HIGHLANDER, CAR.TOYOTA_SIENNA, CAR.LEXUS_IS, CAR.TOYOTA_ALPHARD_TSS2],
# On some models, the engine can show on two different addresses
Ecu.engine: [CAR.TOYOTA_HIGHLANDER, CAR.TOYOTA_CAMRY, CAR.TOYOTA_COROLLA_TSS2, CAR.TOYOTA_CHR, CAR.TOYOTA_CHR_TSS2, CAR.LEXUS_IS,
CAR.LEXUS_IS_TSS2, CAR.LEXUS_RC, CAR.LEXUS_NX, CAR.LEXUS_NX_TSS2, CAR.LEXUS_RX, CAR.LEXUS_RX_TSS2],
@@ -589,7 +596,8 @@ STEER_THRESHOLD = 100
# These cars have non-standard EPS torque scale factors. All others are 73
EPS_SCALE = defaultdict(lambda: 73,
{CAR.TOYOTA_PRIUS: 66, CAR.TOYOTA_COROLLA: 88, CAR.LEXUS_IS: 77, CAR.LEXUS_RC: 77, CAR.LEXUS_CTH: 100, CAR.TOYOTA_PRIUS_V: 100})
{CAR.TOYOTA_PRIUS: 66, CAR.TOYOTA_COROLLA: 88, CAR.TOYOTA_MATRIX_RETROFIT: 88,
CAR.LEXUS_IS: 77, CAR.LEXUS_RC: 77, CAR.LEXUS_CTH: 100, CAR.TOYOTA_PRIUS_V: 100})
# Toyota/Lexus Safety Sense 2.0 and 2.5
TSS2_CAR = CAR.with_flags(ToyotaFlags.TSS2)
@@ -62,6 +62,12 @@ VAL_TABLE_ HandsOffSWDetectionMode 2 "Failed" 1 "Enabled" 0 "Disabled" ;
BO_ 189 EBCMRegenPaddle: 7 K17_EBCM
SG_ RegenPaddle : 7|4@0+ (1,0) [0|0] "" NEO
SG_ Byte1 : 15|8@0+ (1,0) [0|255] "" NEO
SG_ Byte2 : 23|8@0+ (1,0) [0|255] "" NEO
SG_ Byte3 : 31|8@0+ (1,0) [0|255] "" NEO
SG_ Byte4 : 39|8@0+ (1,0) [0|255] "" NEO
SG_ Byte5 : 47|8@0+ (1,0) [0|255] "" NEO
SG_ Byte6 : 55|8@0+ (1,0) [0|255] "" NEO
BO_ 190 ECMAcceleratorPos: 6 K20_ECM
SG_ BrakePedalPos : 15|8@0+ (1,0) [0|0] "sticky" NEO
@@ -146,6 +152,7 @@ BO_ 481 ASCMSteeringButton: 7 K124_ASCM
SG_ LKAButton : 23|1@0+ (1,0) [0|0] "" NEO
SG_ ACCAlwaysOne : 24|1@0+ (1,0) [0|1] "" XXX
SG_ ACCButtons : 46|3@0+ (1,0) [0|0] "" NEO
SG_ ACCButtonsHard : 26|2@0+ (1,0) [0|3] "" XXX
SG_ DriveModeButton : 39|1@0+ (1,0) [0|1] "" XXX
SG_ RollingCounter : 33|2@0+ (1,0) [0|3] "" NEO
SG_ SteeringButtonChecksum : 43|12@0+ (1,0) [0|255] "" NEO
@@ -173,9 +180,14 @@ BO_ 500 SportMode: 6 XXX
SG_ SportMode : 15|1@0+ (1,0) [0|1] "" XXX
BO_ 501 ECMPRDNL2: 8 K20_ECM
SG_ Byte0 : 7|8@0+ (1,0) [0|255] "" NEO
SG_ Byte1 : 15|8@0+ (1,0) [0|255] "" NEO
SG_ Byte2 : 23|8@0+ (1,0) [0|255] "" NEO
SG_ TransmissionState : 48|4@1+ (1,0) [0|7] "" NEO
SG_ PRNDL2 : 27|4@0+ (1,0) [0|255] "" NEO
SG_ Byte4 : 39|8@0+ (1,0) [0|255] "" NEO
SG_ ManualMode : 41|1@0+ (1,0) [0|1] "" NEO
SG_ Byte7 : 63|8@0+ (1,0) [0|255] "" NEO
BO_ 532 BRAKE_RELATED: 6 XXX
SG_ UserBrakePressure : 0|9@0+ (1,0) [0|511] "" XXX
@@ -37,11 +37,24 @@ BO_ 740 STEERING_LKA: 5 XXX
BO_ 836 PRE_COLLISION_2: 8 DSU
SG_ DSS1GDRV : 7|10@0- (0.1,0) [0|0] "m/s^2" Vector__XXX
SG_ DS1STAT2 : 13|3@0+ (1,0) [0|0] "" Vector__XXX
SG_ DS1STBK2 : 10|3@0+ (1,0) [0|0] "" Vector__XXX
SG_ PCSWAR : 18|1@0+ (1,0) [0|0] "" FCM
SG_ PCSALM : 17|1@0+ (1,0) [0|0] "" FCM
SG_ PCSOPR : 16|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ PCSABK : 31|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ IBTRGR : 27|1@0+ (1,0) [0|0] "" FCM
SG_ PBATRGR : 30|2@0+ (1,0) [0|0] "" Vector__XXX
SG_ PPTRGR : 28|1@0+ (1,0) [0|0] "" FCM
SG_ CLEXTRGR : 26|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ IRLT_REQ : 25|2@0+ (1,0) [0|0] "" Vector__XXX
SG_ BRKHLD : 37|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ PREFILL : 33|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ AVSTRGR : 36|1@0+ (1,0) [0|0] "" SCS
SG_ VGRSTRGR : 35|2@0+ (1,0) [0|0] "" Vector__XXX
SG_ PBRTRGR : 32|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ PCSDIS : 43|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ PBPREPMP : 40|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ CHECKSUM : 63|8@0+ (1,0) [0|0] "" XXX
CM_ SG_ 466 NEUTRAL_FORCE "force in newtons the engine/electric motors are applying without any acceleration commands or user input";
@@ -82,6 +82,12 @@ VAL_TABLE_ HandsOffSWDetectionMode 2 "Failed" 1 "Enabled" 0 "Disabled" ;
BO_ 189 EBCMRegenPaddle: 7 K17_EBCM
SG_ RegenPaddle : 7|4@0+ (1,0) [0|0] "" NEO
SG_ Byte1 : 15|8@0+ (1,0) [0|255] "" NEO
SG_ Byte2 : 23|8@0+ (1,0) [0|255] "" NEO
SG_ Byte3 : 31|8@0+ (1,0) [0|255] "" NEO
SG_ Byte4 : 39|8@0+ (1,0) [0|255] "" NEO
SG_ Byte5 : 47|8@0+ (1,0) [0|255] "" NEO
SG_ Byte6 : 55|8@0+ (1,0) [0|255] "" NEO
BO_ 190 ECMAcceleratorPos: 6 K20_ECM
SG_ BrakePedalPos : 15|8@0+ (1,0) [0|0] "sticky" NEO
@@ -166,6 +172,7 @@ BO_ 481 ASCMSteeringButton: 7 K124_ASCM
SG_ LKAButton : 23|1@0+ (1,0) [0|0] "" NEO
SG_ ACCAlwaysOne : 24|1@0+ (1,0) [0|1] "" XXX
SG_ ACCButtons : 46|3@0+ (1,0) [0|0] "" NEO
SG_ ACCButtonsHard : 26|2@0+ (1,0) [0|3] "" XXX
SG_ DriveModeButton : 39|1@0+ (1,0) [0|1] "" XXX
SG_ RollingCounter : 33|2@0+ (1,0) [0|3] "" NEO
SG_ SteeringButtonChecksum : 43|12@0+ (1,0) [0|255] "" NEO
@@ -193,9 +200,14 @@ BO_ 500 SportMode: 6 XXX
SG_ SportMode : 15|1@0+ (1,0) [0|1] "" XXX
BO_ 501 ECMPRDNL2: 8 K20_ECM
SG_ Byte0 : 7|8@0+ (1,0) [0|255] "" NEO
SG_ Byte1 : 15|8@0+ (1,0) [0|255] "" NEO
SG_ Byte2 : 23|8@0+ (1,0) [0|255] "" NEO
SG_ TransmissionState : 48|4@1+ (1,0) [0|7] "" NEO
SG_ PRNDL2 : 27|4@0+ (1,0) [0|255] "" NEO
SG_ Byte4 : 39|8@0+ (1,0) [0|255] "" NEO
SG_ ManualMode : 41|1@0+ (1,0) [0|1] "" NEO
SG_ Byte7 : 63|8@0+ (1,0) [0|255] "" NEO
BO_ 532 BRAKE_RELATED: 6 XXX
SG_ UserBrakePressure : 0|9@0+ (1,0) [0|511] "" XXX
@@ -650,11 +650,24 @@ BO_ 740 STEERING_LKA: 5 XXX
BO_ 836 PRE_COLLISION_2: 8 DSU
SG_ DSS1GDRV : 7|10@0- (0.1,0) [0|0] "m/s^2" Vector__XXX
SG_ DS1STAT2 : 13|3@0+ (1,0) [0|0] "" Vector__XXX
SG_ DS1STBK2 : 10|3@0+ (1,0) [0|0] "" Vector__XXX
SG_ PCSWAR : 18|1@0+ (1,0) [0|0] "" FCM
SG_ PCSALM : 17|1@0+ (1,0) [0|0] "" FCM
SG_ PCSOPR : 16|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ PCSABK : 31|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ IBTRGR : 27|1@0+ (1,0) [0|0] "" FCM
SG_ PBATRGR : 30|2@0+ (1,0) [0|0] "" Vector__XXX
SG_ PPTRGR : 28|1@0+ (1,0) [0|0] "" FCM
SG_ CLEXTRGR : 26|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ IRLT_REQ : 25|2@0+ (1,0) [0|0] "" Vector__XXX
SG_ BRKHLD : 37|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ PREFILL : 33|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ AVSTRGR : 36|1@0+ (1,0) [0|0] "" SCS
SG_ VGRSTRGR : 35|2@0+ (1,0) [0|0] "" Vector__XXX
SG_ PBRTRGR : 32|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ PCSDIS : 43|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ PBPREPMP : 40|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ CHECKSUM : 63|8@0+ (1,0) [0|0] "" XXX
CM_ SG_ 466 NEUTRAL_FORCE "force in newtons the engine/electric motors are applying without any acceleration commands or user input";
@@ -650,11 +650,24 @@ BO_ 740 STEERING_LKA: 5 XXX
BO_ 836 PRE_COLLISION_2: 8 DSU
SG_ DSS1GDRV : 7|10@0- (0.1,0) [0|0] "m/s^2" Vector__XXX
SG_ DS1STAT2 : 13|3@0+ (1,0) [0|0] "" Vector__XXX
SG_ DS1STBK2 : 10|3@0+ (1,0) [0|0] "" Vector__XXX
SG_ PCSWAR : 18|1@0+ (1,0) [0|0] "" FCM
SG_ PCSALM : 17|1@0+ (1,0) [0|0] "" FCM
SG_ PCSOPR : 16|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ PCSABK : 31|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ IBTRGR : 27|1@0+ (1,0) [0|0] "" FCM
SG_ PBATRGR : 30|2@0+ (1,0) [0|0] "" Vector__XXX
SG_ PPTRGR : 28|1@0+ (1,0) [0|0] "" FCM
SG_ CLEXTRGR : 26|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ IRLT_REQ : 25|2@0+ (1,0) [0|0] "" Vector__XXX
SG_ BRKHLD : 37|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ PREFILL : 33|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ AVSTRGR : 36|1@0+ (1,0) [0|0] "" SCS
SG_ VGRSTRGR : 35|2@0+ (1,0) [0|0] "" Vector__XXX
SG_ PBRTRGR : 32|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ PCSDIS : 43|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ PBPREPMP : 40|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ CHECKSUM : 63|8@0+ (1,0) [0|0] "" XXX
CM_ SG_ 466 NEUTRAL_FORCE "force in newtons the engine/electric motors are applying without any acceleration commands or user input";
@@ -650,11 +650,24 @@ BO_ 740 STEERING_LKA: 5 XXX
BO_ 836 PRE_COLLISION_2: 8 DSU
SG_ DSS1GDRV : 7|10@0- (0.1,0) [0|0] "m/s^2" Vector__XXX
SG_ DS1STAT2 : 13|3@0+ (1,0) [0|0] "" Vector__XXX
SG_ DS1STBK2 : 10|3@0+ (1,0) [0|0] "" Vector__XXX
SG_ PCSWAR : 18|1@0+ (1,0) [0|0] "" FCM
SG_ PCSALM : 17|1@0+ (1,0) [0|0] "" FCM
SG_ PCSOPR : 16|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ PCSABK : 31|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ IBTRGR : 27|1@0+ (1,0) [0|0] "" FCM
SG_ PBATRGR : 30|2@0+ (1,0) [0|0] "" Vector__XXX
SG_ PPTRGR : 28|1@0+ (1,0) [0|0] "" FCM
SG_ CLEXTRGR : 26|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ IRLT_REQ : 25|2@0+ (1,0) [0|0] "" Vector__XXX
SG_ BRKHLD : 37|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ PREFILL : 33|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ AVSTRGR : 36|1@0+ (1,0) [0|0] "" SCS
SG_ VGRSTRGR : 35|2@0+ (1,0) [0|0] "" Vector__XXX
SG_ PBRTRGR : 32|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ PCSDIS : 43|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ PBPREPMP : 40|1@0+ (1,0) [0|0] "" Vector__XXX
SG_ CHECKSUM : 63|8@0+ (1,0) [0|0] "" XXX
CM_ SG_ 466 NEUTRAL_FORCE "force in newtons the engine/electric motors are applying without any acceleration commands or user input";
+35 -6
View File
@@ -61,6 +61,7 @@ static bool gm_panda_paddle_sched = false;
static bool gm_bolt_2022_pedal = false;
static bool gm_alt_brake = false;
static bool gm_volt_auto_hold = false;
static bool gm_volt_one_pedal = false;
static bool gm_cc_long = false;
static bool gm_has_acc = true;
@@ -365,9 +366,10 @@ static bool gm_tx_hook(const CANPacket_t *msg) {
if (msg->addr == 0x315U) {
int brake = ((msg->data[0] & 0xFU) << 8) + msg->data[1];
brake = (0x1000 - brake) & 0xFFF;
bool stock_auto_hold_brake_allowed = gm_volt_auto_hold && !vehicle_moving && !gas_pressed_prev;
bool stock_auto_hold_brake_allowed = gm_volt_auto_hold && acc_main_on && !vehicle_moving && !gas_pressed_prev;
bool stock_one_pedal_brake_allowed = gm_volt_one_pedal && acc_main_on && !gas_pressed_prev && !brake_pressed;
bool violation = false;
violation |= !(get_longitudinal_allowed() || stock_auto_hold_brake_allowed) && (brake != 0);
violation |= !(get_longitudinal_allowed() || stock_auto_hold_brake_allowed || stock_one_pedal_brake_allowed) && (brake != 0);
if (stock_auto_hold_brake_allowed && !get_longitudinal_allowed()) {
violation |= brake > GM_VOLT_AUTO_HOLD_MAX_BRAKE;
} else {
@@ -612,6 +614,11 @@ static safety_config gm_init(uint16_t param) {
{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
static const CanMsg GM_CAM_BOLT_2022_PEDAL_FRICTION_TX_MSGS[] = {{0x180, 0, 4, .check_relay = true}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, {0x315, 0, 5, .check_relay = true}, // pt bus
{0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = true}, // camera bus
{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
static const CanMsg GM_CAM_VOLT_AUTO_HOLD_TX_MSGS[] = {{0x180, 0, 4, .check_relay = true}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, {0x315, 0, 5, .check_relay = true}, // pt bus
{0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = true}, // camera bus
{0x200, 0, 6, .check_relay = false},
@@ -635,6 +642,12 @@ static safety_config gm_init(uint16_t param) {
{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
static const CanMsg GM_CAM_NO_CAMERA_BOLT_2022_PEDAL_FRICTION_TX_MSGS[] = {{0x180, 0, 4, .check_relay = false}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, {0x315, 0, 5, .check_relay = false}, // pt bus
{0x409, 0, 7, .check_relay = false}, {0x40A, 0, 7, .check_relay = false},
{0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = false}, // camera bus
{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
static const CanMsg GM_CAM_NO_CAMERA_VOLT_AUTO_HOLD_TX_MSGS[] = {{0x180, 0, 4, .check_relay = false}, {0x370, 0, 6, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, {0x315, 0, 5, .check_relay = false}, // pt bus
{0x409, 0, 7, .check_relay = false}, {0x40A, 0, 7, .check_relay = false},
{0x1E1, 2, 7, .check_relay = false}, {0x184, 2, 8, .check_relay = false}, // camera bus
@@ -699,6 +712,10 @@ static safety_config gm_init(uint16_t param) {
gm_panda_paddle_sched = GET_FLAG(param, GM_PARAM_PANDA_PADDLE_SCHED) && gm_pedal_long && enable_gas_interceptor;
// Reuse the paddle-scheduler bit as a stock-Volt auto-hold marker on non-pedal ACC paths.
gm_volt_auto_hold = GET_FLAG(param, GM_PARAM_PANDA_PADDLE_SCHED) && !gm_pedal_long && !gm_cc_long && gm_has_acc;
// Reuse the 3D1 scheduler bit as a stock-Volt one-pedal marker on non-pedal
// ACC paths. The actual 3D1 scheduler still requires pedal-long and no-ACC,
// so this stays isolated from the Bolt pedal path.
gm_volt_one_pedal = GET_FLAG(param, GM_PARAM_PANDA_3D1_SCHED) && !gm_pedal_long && !gm_cc_long && gm_has_acc;
gm_alt_brake = GET_FLAG(param, GM_PARAM_NO_CAMERA) && (gm_hw == GM_ASCM) && !gm_sdgm && !gm_ascm_int;
gm_3d1_spoof_valid = false;
@@ -730,6 +747,11 @@ static safety_config gm_init(uint16_t param) {
gm_pcm_cruise = (gm_hw == GM_CAM || gm_sdgm) && !gm_cam_long && !gm_force_ascm && !gm_pedal_long;
const bool gm_ascm_int_stock_cam = gm_ascm_int && (gm_hw == GM_CAM) && gm_pcm_cruise && !gm_cam_long && !gm_pedal_long && !gm_cc_long;
const bool gm_ascm_int_no_accel_pos = gm_ascm_int && (gm_hw == GM_CAM) && gm_force_brake_c9;
// FLAG_GM_BOLT_2022_PEDAL is shared with Malibu Hybrid pedal-long. Requiring
// the paddle scheduler bit narrows this whitelist to the Gen2 Bolt pedal-long
// experiment, which is the only path that should probe chassis friction brake
// while stock ACC remains canceled.
const bool gm_bolt_2022_pedal_friction = gm_bolt_2022_pedal && gm_panda_paddle_sched && !gm_has_acc;
gm_steer_limits = GET_FLAG(param, GM_PARAM_BOLT_2017) ? &GM_BOLT_2017_STEERING_LIMITS : &GM_STEERING_LIMITS;
if ((gm_hw == GM_ASCM && !gm_sdgm) || gm_ascm_int || gm_force_ascm) {
@@ -740,9 +762,10 @@ static safety_config gm_init(uint16_t param) {
safety_config ret;
const bool gm_sdgm_stock = gm_sdgm && !gm_cc_long && !gm_cam_long && !gm_no_camera;
const bool gm_volt_stock_brake = gm_volt_auto_hold || gm_volt_one_pedal;
// SDGM behaves like a forwarding camera path for whitelist/forwarding purposes.
if (gm_sdgm_stock) {
if (gm_volt_auto_hold) {
if (gm_volt_stock_brake) {
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_SDGM_VOLT_AUTO_HOLD_TX_MSGS);
} else {
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_SDGM_TX_MSGS);
@@ -764,16 +787,22 @@ static safety_config gm_init(uint16_t param) {
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_LONG_TX_MSGS);
}
} else {
if (gm_volt_auto_hold && gm_sdgm) {
if (gm_volt_stock_brake && gm_sdgm) {
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_SDGM_VOLT_AUTO_HOLD_TX_MSGS);
} else if (gm_no_camera) {
if (gm_volt_auto_hold) {
if (gm_bolt_2022_pedal_friction) {
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_NO_CAMERA_BOLT_2022_PEDAL_FRICTION_TX_MSGS);
} else if (gm_volt_stock_brake) {
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_NO_CAMERA_VOLT_AUTO_HOLD_TX_MSGS);
} else {
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_NO_CAMERA_TX_MSGS);
}
} else {
if (gm_volt_auto_hold) {
if (gm_bolt_2022_pedal_friction) {
// Experimental Gen2 Bolt pedal-long path: keep stock ACC canceled but
// allow OP to probe chassis friction-brake acceptance through panda.
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_BOLT_2022_PEDAL_FRICTION_TX_MSGS);
} else if (gm_volt_stock_brake) {
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_VOLT_AUTO_HOLD_TX_MSGS);
} else {
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_TX_MSGS);
+9 -9
View File
@@ -70,6 +70,13 @@ static const CanMsg HYUNDAI_TX_MSGS[] = {
HYUNDAI_COMMON_TX_MSGS(0)
};
static const CanMsg HYUNDAI_LONG_TX_MSGS[] = {
HYUNDAI_LONG_COMMON_TX_MSGS(0)
{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 bool hyundai_legacy = false;
static uint8_t hyundai_get_counter(const CANPacket_t *msg) {
@@ -322,13 +329,6 @@ static bool hyundai_tx_hook(const CANPacket_t *msg) {
}
static safety_config hyundai_init(uint16_t param) {
static const CanMsg HYUNDAI_LONG_TX_MSGS[] = {
HYUNDAI_LONG_COMMON_TX_MSGS(0)
{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_CAMERA_SCC_TX_MSGS[] = {
HYUNDAI_COMMON_TX_MSGS(2)
};
@@ -556,9 +556,9 @@ static safety_config hyundai_legacy_init(uint16_t param) {
hyundai_common_init(param);
hyundai_legacy = true;
hyundai_longitudinal = false;
hyundai_camera_scc = false;
return BUILD_SAFETY_CFG(hyundai_legacy_rx_checks, HYUNDAI_TX_MSGS);
return hyundai_longitudinal ? BUILD_SAFETY_CFG(hyundai_legacy_rx_checks, HYUNDAI_LONG_TX_MSGS) :
BUILD_SAFETY_CFG(hyundai_legacy_rx_checks, HYUNDAI_TX_MSGS);
}
const safety_hooks hyundai_hooks = {
@@ -221,7 +221,8 @@ static bool hyundai_canfd_tx_hook(const CANPacket_t *msg) {
}
// steering
const unsigned int steer_addr = (hyundai_canfd_lka_steering && !hyundai_longitudinal) ? hyundai_canfd_get_lka_addr() : 0x12aU;
const unsigned int steer_addr = (hyundai_canfd_lka_steering && (hyundai_canfd_angle_steering || !hyundai_longitudinal)) ?
hyundai_canfd_get_lka_addr() : 0x12aU;
if (msg->addr == steer_addr) {
if (hyundai_canfd_angle_steering) {
const int lkas_angle_active = (msg->data[9] >> 4U) & 0x3U;
@@ -334,6 +335,21 @@ static safety_config hyundai_canfd_init(uint16_t param) {
{0x1DA, 1, 32, .check_relay = false}, // ADRV_0x1da
};
static const CanMsg HYUNDAI_CANFD_LKA_STEERING_ALT_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)
HYUNDAI_CANFD_BLINDSPOT_DASH_TX_MSGS(1)
{0x51, 0, 32, .check_relay = false}, // ADRV_0x51
{0x100, 0, 24, .check_relay = false}, // ACCELERATOR_BRAKE_ALT radar heartbeat spoof
{0x730, 1, 8, .check_relay = false}, // tester present for ADAS ECU disable
{0x160, 1, 16, .check_relay = false}, // ADRV_0x160
{0x1EA, 1, 32, .check_relay = false}, // ADRV_0x1ea
{0x200, 1, 8, .check_relay = false}, // ADRV_0x200
{0x345, 1, 8, .check_relay = false}, // ADRV_0x345
{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)
@@ -382,7 +398,11 @@ static safety_config hyundai_canfd_init(uint16_t param) {
};
SET_RX_CHECKS(hyundai_canfd_lka_steering_long_rx_checks, ret);
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS, ret);
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);
}
} else {
// Longitudinal checks for LFA steering
@@ -24,6 +24,7 @@
#define MSG_SUBARU_Throttle 0x40U
#define MSG_SUBARU_Steering_Torque 0x119U
#define MSG_SUBARU_Wheel_Speeds 0x13aU
#define MSG_SUBARU_Brake_Pedal 0x139U
#define MSG_SUBARU_ES_LKAS 0x122U
#define MSG_SUBARU_ES_Brake 0x220U
@@ -63,6 +64,10 @@
{MSG_SUBARU_ES_STATIC_1, SUBARU_MAIN_BUS, 8, .check_relay = false}, \
{MSG_SUBARU_ES_STATIC_2, SUBARU_MAIN_BUS, 8, .check_relay = false}, \
#define SUBARU_STOP_AND_GO_ADDITIONAL_TX_MSGS() \
{MSG_SUBARU_Throttle, SUBARU_CAM_BUS, 8, .check_relay = true}, \
{MSG_SUBARU_Brake_Pedal, SUBARU_CAM_BUS, 8, .check_relay = true}, \
#define SUBARU_COMMON_RX_CHECKS(alt_bus) \
{.msg = {{MSG_SUBARU_Throttle, SUBARU_MAIN_BUS, 8, 100U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{MSG_SUBARU_Steering_Torque, SUBARU_MAIN_BUS, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
@@ -72,6 +77,7 @@
static bool subaru_gen2 = false;
static bool subaru_longitudinal = false;
static bool subaru_stop_and_go = false;
static uint32_t subaru_get_checksum(const CANPacket_t *msg) {
return (uint8_t)msg->data[0];
@@ -227,6 +233,12 @@ static safety_config subaru_init(uint16_t param) {
SUBARU_GEN2_LONG_ADDITIONAL_TX_MSGS()
};
static const CanMsg SUBARU_STOP_AND_GO_TX_MSGS[] = {
SUBARU_BASE_TX_MSGS(SUBARU_MAIN_BUS, MSG_SUBARU_ES_LKAS)
SUBARU_COMMON_TX_MSGS(SUBARU_MAIN_BUS)
SUBARU_STOP_AND_GO_ADDITIONAL_TX_MSGS()
};
static RxCheck subaru_rx_checks[] = {
SUBARU_COMMON_RX_CHECKS(SUBARU_MAIN_BUS)
};
@@ -239,6 +251,9 @@ static safety_config subaru_init(uint16_t param) {
subaru_gen2 = GET_FLAG(param, SUBARU_PARAM_GEN2);
const uint16_t SUBARU_PARAM_STOP_AND_GO = 8;
subaru_stop_and_go = GET_FLAG(param, SUBARU_PARAM_STOP_AND_GO);
#ifdef ALLOW_DEBUG
const uint16_t SUBARU_PARAM_LONGITUDINAL = 2;
subaru_longitudinal = GET_FLAG(param, SUBARU_PARAM_LONGITUDINAL);
@@ -250,6 +265,7 @@ static safety_config subaru_init(uint16_t param) {
BUILD_SAFETY_CFG(subaru_gen2_rx_checks, SUBARU_GEN2_TX_MSGS);
} else {
ret = subaru_longitudinal ? BUILD_SAFETY_CFG(subaru_rx_checks, SUBARU_LONG_TX_MSGS) : \
subaru_stop_and_go ? BUILD_SAFETY_CFG(subaru_rx_checks, SUBARU_STOP_AND_GO_TX_MSGS) : \
BUILD_SAFETY_CFG(subaru_rx_checks, SUBARU_TX_MSGS);
}
return ret;
@@ -17,7 +17,16 @@
#define SUBARU_PG_MAIN_BUS 0U
#define SUBARU_PG_CAM_BUS 2U
#define SUBARU_PG_COMMON_TX_MSGS() \
{MSG_SUBARU_PG_ES_Distance, SUBARU_PG_MAIN_BUS, 8, .check_relay = true}, \
{MSG_SUBARU_PG_ES_LKAS, SUBARU_PG_MAIN_BUS, 8, .check_relay = true}, \
#define SUBARU_PG_STOP_AND_GO_ADDITIONAL_TX_MSGS() \
{MSG_SUBARU_PG_Throttle, SUBARU_PG_CAM_BUS, 8, .check_relay = false}, \
{MSG_SUBARU_PG_Brake_Pedal, SUBARU_PG_CAM_BUS, 4, .check_relay = false}, \
static bool subaru_pg_reversed_driver_torque = false;
static bool subaru_pg_stop_and_go = false;
static void subaru_preglobal_rx_hook(const CANPacket_t *msg) {
if (msg->bus == SUBARU_PG_MAIN_BUS) {
@@ -81,8 +90,12 @@ static bool subaru_preglobal_tx_hook(const CANPacket_t *msg) {
static safety_config subaru_preglobal_init(uint16_t param) {
static const CanMsg SUBARU_PG_TX_MSGS[] = {
{MSG_SUBARU_PG_ES_Distance, SUBARU_PG_MAIN_BUS, 8, .check_relay = true},
{MSG_SUBARU_PG_ES_LKAS, SUBARU_PG_MAIN_BUS, 8, .check_relay = true}
SUBARU_PG_COMMON_TX_MSGS()
};
static const CanMsg SUBARU_PG_STOP_AND_GO_TX_MSGS[] = {
SUBARU_PG_COMMON_TX_MSGS()
SUBARU_PG_STOP_AND_GO_ADDITIONAL_TX_MSGS()
};
// TODO: do checksum and counter checks after adding the signals to the outback dbc file
@@ -95,9 +108,14 @@ static safety_config subaru_preglobal_init(uint16_t param) {
};
const uint16_t SUBARU_PG_PARAM_REVERSED_DRIVER_TORQUE = 4;
const uint16_t SUBARU_PG_PARAM_STOP_AND_GO = 8;
subaru_pg_reversed_driver_torque = GET_FLAG(param, SUBARU_PG_PARAM_REVERSED_DRIVER_TORQUE);
return BUILD_SAFETY_CFG(subaru_preglobal_rx_checks, SUBARU_PG_TX_MSGS);
subaru_pg_stop_and_go = GET_FLAG(param, SUBARU_PG_PARAM_STOP_AND_GO);
safety_config ret = subaru_pg_stop_and_go ? BUILD_SAFETY_CFG(subaru_preglobal_rx_checks, SUBARU_PG_STOP_AND_GO_TX_MSGS) : \
BUILD_SAFETY_CFG(subaru_preglobal_rx_checks, SUBARU_PG_TX_MSGS);
return ret;
}
const safety_hooks subaru_preglobal_hooks = {
+85 -3
View File
@@ -69,6 +69,9 @@
{.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 }}}, \
#define TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK \
{.msg = {{0x201, 0, 6, 50U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
static bool toyota_secoc = false;
static bool toyota_alt_brake = false;
static bool toyota_stock_longitudinal = false;
@@ -90,6 +93,12 @@ static uint32_t toyota_get_checksum(const CANPacket_t *msg) {
return (uint8_t)(msg->data[checksum_byte]);
}
static int toyota_get_interceptor(const CANPacket_t *msg) {
uint16_t val1 = ((uint16_t)msg->data[0] << 8U) | (uint16_t)msg->data[1];
uint16_t val2 = ((uint16_t)msg->data[2] << 8U) | (uint16_t)msg->data[3];
return (int)((val1 + val2) / 2U);
}
static bool toyota_get_quality_flag_valid(const CANPacket_t *msg) {
bool valid = false;
@@ -150,7 +159,9 @@ static void toyota_rx_hook(const CANPacket_t *msg) {
if (msg->addr == 0x1D2U) {
bool cruise_engaged = GET_BIT(msg, 5U); // PCM_CRUISE.CRUISE_ACTIVE
pcm_cruise_check(cruise_engaged);
gas_pressed = !GET_BIT(msg, 4U); // PCM_CRUISE.GAS_RELEASED
if (!enable_gas_interceptor) {
gas_pressed = !GET_BIT(msg, 4U); // PCM_CRUISE.GAS_RELEASED
}
}
if (!toyota_alt_brake && (msg->addr == 0x226U)) {
brake_pressed = GET_BIT(msg, 37U); // BRAKE_MODULE.BRAKE_PRESSED (toyota_nodsu_pt_generated.dbc)
@@ -181,6 +192,14 @@ static void toyota_rx_hook(const CANPacket_t *msg) {
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;
int gas_interceptor = toyota_get_interceptor(msg);
gas_pressed = gas_interceptor > toyota_gas_interceptor_threshold;
gas_interceptor_prev = gas_interceptor;
}
}
}
@@ -351,6 +370,17 @@ static bool toyota_tx_hook(const CANPacket_t *msg) {
}
}
}
if ((msg->addr == 0x200U) && longitudinal_interceptor_checks(msg)) {
tx = false;
}
// Auto brake hold replaces the camera AEB message only while stopped.
if ((msg->addr == 0x344U) && ((alternative_experience & ALT_EXP_ALLOW_AEB) != 0)) {
if (vehicle_moving || gas_pressed || !acc_main_on) {
tx = false;
}
}
}
// UDS: Only tester present ("\x0F\x02\x3E\x00\x00\x00\x00\x00") allowed on diagnostics address
@@ -384,6 +414,16 @@ static safety_config toyota_init(uint16_t param) {
TOYOTA_COMMON_LONG_TX_MSGS_FILTER
};
static const CanMsg TOYOTA_INTERCEPTOR_TX_MSGS[] = {
TOYOTA_COMMON_LONG_TX_MSGS
{0x200, 0, 6, .check_relay = false},
};
static const CanMsg TOYOTA_INTERCEPTOR_TX_MSGS_FILTER[] = {
TOYOTA_COMMON_LONG_TX_MSGS_FILTER
{0x200, 0, 6, .check_relay = false},
};
static const CanMsg TOYOTA_SECOC_LONG_TX_MSGS[] = {
TOYOTA_COMMON_SECOC_LONG_TX_MSGS
};
@@ -396,6 +436,7 @@ static safety_config toyota_init(uint16_t param) {
const uint32_t TOYOTA_PARAM_STOCK_LONGITUDINAL = 2UL << TOYOTA_PARAM_OFFSET;
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;
#ifdef ALLOW_DEBUG
const uint32_t TOYOTA_PARAM_SECOC = 8UL << TOYOTA_PARAM_OFFSET;
@@ -406,8 +447,13 @@ static safety_config toyota_init(uint16_t param) {
toyota_stock_longitudinal = GET_FLAG(param, TOYOTA_PARAM_STOCK_LONGITUDINAL);
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_dbc_eps_torque_factor = param & TOYOTA_EPS_FACTOR;
if (toyota_stock_longitudinal || toyota_secoc) {
enable_gas_interceptor = false;
}
safety_config ret;
if (toyota_secoc) {
if (toyota_stock_longitudinal) {
@@ -418,6 +464,12 @@ static safety_config toyota_init(uint16_t param) {
} else {
if (toyota_stock_longitudinal) {
SET_TX_MSGS(TOYOTA_TX_MSGS, ret);
} else if (enable_gas_interceptor) {
if (toyota_long_filter) {
SET_TX_MSGS(TOYOTA_INTERCEPTOR_TX_MSGS_FILTER, ret);
} else {
SET_TX_MSGS(TOYOTA_INTERCEPTOR_TX_MSGS, ret);
}
} else {
if (toyota_long_filter) {
SET_TX_MSGS(TOYOTA_LONG_TX_MSGS_FILTER, ret);
@@ -438,8 +490,16 @@ static safety_config toyota_init(uint16_t param) {
static RxCheck toyota_lta_rx_checks[] = {
TOYOTA_RX_CHECKS(true)
};
static RxCheck toyota_lta_interceptor_rx_checks[] = {
TOYOTA_RX_CHECKS(true)
TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK
};
SET_RX_CHECKS(toyota_lta_rx_checks, ret);
if (enable_gas_interceptor) {
SET_RX_CHECKS(toyota_lta_interceptor_rx_checks, ret);
} else {
SET_RX_CHECKS(toyota_lta_rx_checks, ret);
}
} else {
static RxCheck toyota_lka_rx_checks[] = {
TOYOTA_RX_CHECKS(false)
@@ -447,8 +507,20 @@ static safety_config toyota_init(uint16_t param) {
static RxCheck toyota_lka_alt_brake_rx_checks[] = {
TOYOTA_ALT_BRAKE_RX_CHECKS(false)
};
static RxCheck toyota_lka_interceptor_rx_checks[] = {
TOYOTA_RX_CHECKS(false)
TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK
};
static RxCheck toyota_lka_alt_brake_interceptor_rx_checks[] = {
TOYOTA_ALT_BRAKE_RX_CHECKS(false)
TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK
};
if (!toyota_alt_brake) {
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);
} else if (!toyota_alt_brake) {
SET_RX_CHECKS(toyota_lka_rx_checks, ret);
} else {
SET_RX_CHECKS(toyota_lka_alt_brake_rx_checks, ret);
@@ -458,10 +530,20 @@ static safety_config toyota_init(uint16_t param) {
return ret;
}
static bool toyota_fwd_hook(int bus_num, int addr) {
bool block_msg = false;
if (bus_num == 2) {
block_msg = (addr == 0x344) && ((alternative_experience & ALT_EXP_ALLOW_AEB) != 0) &&
!vehicle_moving && !gas_pressed && acc_main_on;
}
return block_msg;
}
const safety_hooks toyota_hooks = {
.init = toyota_init,
.rx = toyota_rx_hook,
.tx = toyota_tx_hook,
.fwd = toyota_fwd_hook,
.get_checksum = toyota_get_checksum,
.compute_checksum = toyota_compute_checksum,
.get_quality_flag_valid = toyota_get_quality_flag_valid,
+6 -4
View File
@@ -973,8 +973,8 @@ class SafetyTest(SafetyTestBase):
continue
if {attr, current_test}.issubset({'TestGmCameraSafety', 'TestGmCameraLongitudinalSafety', 'TestGmAscmSafety',
'TestGmCameraEVSafety', 'TestGmCameraLongitudinalEVSafety', 'TestGmAscmEVSafety',
'TestGmInterceptorSafety', 'TestGmCcLongitudinalSafety',
'TestGmCcLongitudinalPandaSchedSafety'}):
'TestGmInterceptorSafety', 'TestGmBolt2022PedalFrictionSafety',
'TestGmCcLongitudinalSafety', 'TestGmCcLongitudinalPandaSchedSafety'}):
continue
if attr.startswith('TestFord') and current_test.startswith('TestFord'):
continue
@@ -984,7 +984,8 @@ class SafetyTest(SafetyTestBase):
continue
if {attr, current_test}.issubset({'TestHyundaiLongitudinalSafety', 'TestHyundaiLongitudinalSafetyCameraSCC',
'TestHyundaiSafetyFCEVLong', 'TestHyundaiLongitudinalAolLkasOnEngageSafety',
'TestHyundaiCanCanfdBlendedLongitudinalSafety'}):
'TestHyundaiCanCanfdBlendedLongitudinalSafety',
'TestHyundaiLegacyLongitudinalSafetyHEV'}):
continue
volkswagen_shared = ('TestVolkswagenMqb', 'TestVolkswagenMlb')
if attr.startswith(volkswagen_shared) and current_test.startswith(volkswagen_shared):
@@ -1017,7 +1018,8 @@ class SafetyTest(SafetyTestBase):
if attr.startswith('TestHyundaiLongitudinal') or attr in ('TestHyundaiSafetyFCEVLong',
'TestHyundaiLongitudinalAolLkasOnEngageSafety',
'TestHyundaiCanCanfdBlendedLongitudinalSafety'):
'TestHyundaiCanCanfdBlendedLongitudinalSafety',
'TestHyundaiLegacyLongitudinalSafetyHEV'):
# exceptions for common msgs across different Hyundai CAN platforms
tx = list(filter(lambda m: m[0] not in [0x420, 0x50A, 0x389, 0x4A2], tx))
all_tx.append([[m[0], m[1], attr] for m in tx])
+141 -2
View File
@@ -307,6 +307,28 @@ def test_gm_ascm_int_long_no_accel_pos_uses_stock_cam_rx_checks():
assert safety.safety_config_valid()
def test_ascm_int_camera_long_blocks_radar_status_tx():
safety = libsafety_py.libsafety
radar_status_msgs = (
(0xA1, 7),
(0x306, 8),
(0x308, 7),
(0x310, 2),
)
ascm_int_long = GMSafetyFlags.HW_CAM | GMSafetyFlags.HW_CAM_LONG | GMSafetyFlags.HW_ASCM_INT
safety.set_safety_hooks(CarParams.SafetyModel.gm, ascm_int_long)
safety.init_tests()
for addr, length in radar_status_msgs:
assert not safety.safety_tx_hook(common.make_msg(1, addr, length))
volt_sdgm_long = GMSafetyFlags.HW_CAM | GMSafetyFlags.HW_CAM_LONG | GMSafetyFlags.HW_SDGM
safety.set_safety_hooks(CarParams.SafetyModel.gm, volt_sdgm_long)
safety.init_tests()
for addr, length in radar_status_msgs:
assert not safety.safety_tx_hook(common.make_msg(1, addr, length))
class TestGmCameraEVSafety(GmCameraAccEVRegenMixin, TestGmCameraSafety, TestGmEVSafetyBase):
pass
@@ -447,6 +469,71 @@ class TestGmInterceptorSafety(common.GasInterceptorSafetyTest, TestGmCameraSafet
return to_send
class TestGmBolt2022PedalFrictionSafety(TestGmInterceptorSafety):
TX_MSGS = TestGmInterceptorSafety.TX_MSGS + [[0x315, 0]]
FWD_BLACKLISTED_ADDRS = {2: [0x180, 0x370, 0x315], 0: [0x184, 0x3D1]}
RELAY_MALFUNCTION_ADDRS = {0: (0x180, 0x315), 2: (0x184,)}
EXTRA_SAFETY_PARAM = GMSafetyFlags.FLAG_GM_BOLT_2022_PEDAL
def setUp(self):
self.packer = CANPackerPanda("gm_global_a_powertrain_generated")
self.packer_chassis = CANPackerPanda("gm_global_a_chassis")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(
CarParams.SafetyModel.gm,
GMSafetyFlags.HW_CAM |
GMSafetyFlags.FLAG_GM_NO_ACC |
GMSafetyFlags.FLAG_GM_PEDAL_LONG |
GMSafetyFlags.FLAG_GM_GAS_INTERCEPTOR |
GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED |
self.EXTRA_SAFETY_PARAM)
self.safety.init_tests()
def _send_brake_msg(self, brake):
values = {"FrictionBrakeCmd": -brake}
return self.packer_chassis.make_can_msg_safety("EBCMFrictionBrakeCmd", 0, values)
def _engage_longitudinal(self):
self._rx(self.packer.make_can_msg_panda("ASCMSteeringButton", 0, {"ACCButtons": Buttons.DECEL_SET}))
self._rx(self.packer.make_can_msg_panda("ASCMSteeringButton", 0, {"ACCButtons": Buttons.UNPRESS}))
self.assertTrue(self.safety.get_controls_allowed())
self.assertTrue(self.safety.get_longitudinal_allowed())
def test_tx_hook_on_wrong_safety_mode(self):
self.skipTest("Gen2 Bolt pedal friction experiment intentionally shares the interceptor TX set plus 0x315")
def test_buttons(self):
for controls_allowed in (False, True):
self.safety.set_controls_allowed(controls_allowed)
for btn in range(8):
self.assertEqual(btn == Buttons.CANCEL, self._tx(self._button_msg(btn)))
def test_brake_allowed_when_controls_allowed(self):
self._engage_longitudinal()
self.assertTrue(self._tx(self._send_brake_msg(100)))
def test_brake_blocked_without_controls(self):
self.safety.set_controls_allowed(False)
self.assertFalse(self._tx(self._send_brake_msg(100)))
def test_brake_limit_enforced(self):
self._engage_longitudinal()
self.assertTrue(self._tx(self._send_brake_msg(400)))
self.assertFalse(self._tx(self._send_brake_msg(401)))
def test_brake_whitelist_requires_paddle_scheduler_selector(self):
self.safety.set_safety_hooks(
CarParams.SafetyModel.gm,
GMSafetyFlags.HW_CAM |
GMSafetyFlags.FLAG_GM_NO_ACC |
GMSafetyFlags.FLAG_GM_PEDAL_LONG |
GMSafetyFlags.FLAG_GM_GAS_INTERCEPTOR |
self.EXTRA_SAFETY_PARAM)
self.safety.init_tests()
self._engage_longitudinal()
self.assertFalse(self._tx(self._send_brake_msg(100)))
class TestGmCcLongitudinalSafety(TestGmCameraSafety):
TX_MSGS = [[0x180, 0], [0x370, 0], [0x1E1, 0], [0x3D1, 0], [0xBD, 0], [0x1F5, 0], [0x184, 2], [0x1E1, 2]]
FWD_BLACKLISTED_ADDRS = {2: [0x180], 0: [0x184, 0x3D1]} # block LKAS, PSCMStatus, and stock cruise status
@@ -546,18 +633,62 @@ class TestGmVoltAutoHoldCameraSafety(TestGmCameraSafetyBase):
values = {"FrictionBrakeCmd": -brake}
return self.packer_chassis.make_can_msg_safety("EBCMFrictionBrakeCmd", 0, values)
def test_standstill_brake_allowed_without_controls(self):
def test_standstill_brake_allowed_without_controls_when_main_on(self):
self._rx(self._speed_msg(0))
self._rx(self._toggle_aol(True))
self.safety.set_controls_allowed(False)
self.assertTrue(self._tx(self._send_brake_msg(100)))
def test_standstill_brake_blocked_without_main_on(self):
self._rx(self._speed_msg(0))
self._rx(self._toggle_aol(False))
self.safety.set_controls_allowed(False)
self.assertFalse(self._tx(self._send_brake_msg(100)))
def test_moving_brake_blocked_without_controls(self):
self._rx(self._speed_msg(self.STANDSTILL_THRESHOLD + 1))
self._rx(self._toggle_aol(True))
self.safety.set_controls_allowed(False)
self.assertFalse(self._tx(self._send_brake_msg(100)))
def test_gas_blocks_standstill_brake_without_controls(self):
self._rx(self._speed_msg(0))
self._rx(self._toggle_aol(True))
self._rx(self._user_gas_msg(True))
self.safety.set_controls_allowed(False)
self.assertFalse(self._tx(self._send_brake_msg(100)))
class TestGmVoltOnePedalCameraSafety(TestGmCameraSafetyBase):
TX_MSGS = TestGmCameraSafety.TX_MSGS + [[0x315, 0]]
EXTRA_SAFETY_PARAM = GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED | GMSafetyFlags.FLAG_GM_PANDA_3D1_SCHED
def setUp(self):
self.packer = CANPackerSafety("gm_global_a_powertrain_generated")
self.packer_chassis = CANPackerSafety("gm_global_a_chassis")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(CarParams.SafetyModel.gm, GMSafetyFlags.HW_CAM | self.EXTRA_SAFETY_PARAM)
self.safety.init_tests()
def _send_brake_msg(self, brake):
values = {"FrictionBrakeCmd": -brake}
return self.packer_chassis.make_can_msg_safety("EBCMFrictionBrakeCmd", 0, values)
def test_moving_brake_allowed_without_controls_when_main_on(self):
self._rx(self._speed_msg(self.STANDSTILL_THRESHOLD + 1))
self._rx(self._toggle_aol(True))
self.safety.set_controls_allowed(False)
self.assertTrue(self._tx(self._send_brake_msg(100)))
def test_moving_brake_blocked_without_main_on(self):
self._rx(self._speed_msg(self.STANDSTILL_THRESHOLD + 1))
self._rx(self._toggle_aol(False))
self.safety.set_controls_allowed(False)
self.assertFalse(self._tx(self._send_brake_msg(100)))
def test_gas_blocks_moving_brake_without_controls(self):
self._rx(self._speed_msg(self.STANDSTILL_THRESHOLD + 1))
self._rx(self._toggle_aol(True))
self._rx(self._user_gas_msg(True))
self.safety.set_controls_allowed(False)
self.assertFalse(self._tx(self._send_brake_msg(100)))
@@ -578,13 +709,21 @@ class TestGmVoltAutoHoldSdgmSafety(TestGmSafetyBase):
values = {"FrictionBrakeCmd": -brake}
return self.packer_chassis.make_can_msg_safety("EBCMFrictionBrakeCmd", 2, values)
def test_standstill_brake_allowed_without_controls(self):
def test_standstill_brake_allowed_without_controls_when_main_on(self):
self._rx(self._speed_msg(0))
self._rx(self._toggle_aol(True))
self.safety.set_controls_allowed(False)
self.assertTrue(self._tx(self._send_brake_msg(100)))
def test_standstill_brake_blocked_without_main_on(self):
self._rx(self._speed_msg(0))
self._rx(self._toggle_aol(False))
self.safety.set_controls_allowed(False)
self.assertFalse(self._tx(self._send_brake_msg(100)))
def test_moving_brake_blocked_without_controls(self):
self._rx(self._speed_msg(self.STANDSTILL_THRESHOLD + 1))
self._rx(self._toggle_aol(True))
self.safety.set_controls_allowed(False)
self.assertFalse(self._tx(self._send_brake_msg(100)))
@@ -416,6 +416,14 @@ class TestHyundaiSafetyFCEVLong(TestHyundaiLongitudinalSafety, TestHyundaiSafety
self.safety.init_tests()
class TestHyundaiLegacyLongitudinalSafetyHEV(TestHyundaiLongitudinalSafety, TestHyundaiLegacySafetyHEV):
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(CarParams.SafetyModel.hyundaiLegacy, HyundaiSafetyFlags.HYBRID_GAS | HyundaiSafetyFlags.LONG)
self.safety.init_tests()
class TestHyundaiLongitudinalAolLkasOnEngageSafety(HyundaiAolLkasOnEngageBase, TestHyundaiLongitudinalSafety):
def setUp(self):
self.packer = CANPackerSafety("hyundai_kia_generic")
@@ -559,6 +559,61 @@ class TestHyundaiCanfdLKASteeringLongEV(HyundaiLongitudinalBase, TestHyundaiCanf
return self.packer.make_can_msg_safety("SCC_CONTROL", 1, values)
class TestHyundaiCanfdLKASteeringAltAngleLongEV(HyundaiLongitudinalBase, TestHyundaiCanfdAngleSteering):
TX_MSGS = [[0x110, 0], [0x1CF, 1], [0x362, 0], [0x51, 0], [0x100, 0], [0x730, 1], [0x12a, 1], [0x160, 1],
[0x1ba, 1], [0x1e0, 1], [0x1e5, 1], [0x31a, 1], [0x3b5, 1], [0x3c1, 1],
[0x1a0, 1], [0x1ea, 1], [0x200, 1], [0x345, 1], [0x1da, 1]]
RELAY_MALFUNCTION_ADDRS = {0: (0x110, 0x362), 1: (0x1a0,)} # LKAS_ALT, CAM_0x362, SCC_CONTROL
FWD_BLACKLISTED_ADDRS = {0: MRR35_RADAR_TRACK_ADDRS, 2: [0x110, 0x362]}
DISABLED_ECU_UDS_MSG = (0x730, 1)
DISABLED_ECU_ACTUATION_MSG = (0x1a0, 1)
PT_BUS = 1
SCC_BUS = 1
BUTTONS_TX_BUS = 1
STEER_MSG = "LKAS_ALT"
GAS_MSG = ("ACCELERATOR", "ACCELERATOR_PEDAL")
SAFETY_PARAM = HyundaiSafetyFlags.CANFD_LKA_STEERING | HyundaiSafetyFlags.CANFD_LKA_STEERING_ALT | \
HyundaiSafetyFlags.CANFD_ANGLE_STEERING | HyundaiSafetyFlags.LONG | HyundaiSafetyFlags.EV_GAS
def _angle_cmd_msg(self, angle, enabled, increment_timer=True, gain_raw=250):
if increment_timer:
self.safety.set_timer(self.angle_cmd_cnt * int(1e6 / self.LATERAL_FREQUENCY))
self.angle_cmd_cnt += 1
values = {
"LKA_MODE": 0,
"LKA_AVAILABLE": 3 if enabled else 0,
"LKA_WARNING": 0,
"LKA_ICON": 2 if enabled else 1,
"FCA_SYSWARN": 0,
"TORQUE_REQUEST": 0,
"STEER_REQ": 0,
"LFA_BUTTON": 0,
"LKA_ASSIST": 0,
"DAMP_FACTOR": 100,
"LKAS_ANGLE_ACTIVE": 2 if enabled else 1,
"HAS_LANE_SAFETY": 0,
"ADAS_StrAnglReqVal": angle,
"ADAS_ACIAnglTqRedcGainVal": gain_raw * 0.004 if enabled or gain_raw != 250 else 0.0,
}
return self.packer.make_can_msg_safety("LKAS_ALT", 0, values)
def _accel_msg(self, accel, aeb_req=False, aeb_decel=0):
values = {
"aReqRaw": accel,
"aReqValue": accel,
}
return self.packer.make_can_msg_safety("SCC_CONTROL", 1, values)
def _tx_acc_state_msg(self, main_on):
values = {"MainMode_ACC": int(main_on), "ACCMode": 0}
return self.packer.make_can_msg_safety("SCC_CONTROL", 1, values)
# Tests longitudinal for ICE, hybrid, EV cars with LFA steering
class TestHyundaiCanfdLFASteeringLongBase(HyundaiLongitudinalBase, TestHyundaiCanfdLFASteeringBase):
@@ -16,6 +16,7 @@ class SubaruMsg(enum.IntEnum):
Throttle = 0x40
Steering_Torque = 0x119
Wheel_Speeds = 0x13a
Brake_Pedal = 0x139
ES_LKAS = 0x122
ES_LKAS_ANGLE = 0x124
ES_Brake = 0x220
@@ -182,6 +183,12 @@ class TestSubaruGen1TorqueStockLongitudinalSafety(TestSubaruStockLongitudinalSaf
TX_MSGS = lkas_tx_msgs(SUBARU_MAIN_BUS)
class TestSubaruGen1StopAndGoSafety(TestSubaruStockLongitudinalSafetyBase, TestSubaruTorqueSafetyBase):
FLAGS = SubaruSafetyFlags.STOP_AND_GO
TX_MSGS = lkas_tx_msgs(SUBARU_MAIN_BUS) + [[SubaruMsg.Throttle, SUBARU_CAM_BUS],
[SubaruMsg.Brake_Pedal, SUBARU_CAM_BUS]]
class TestSubaruGen2TorqueSafetyBase(TestSubaruTorqueSafetyBase):
ALT_MAIN_BUS = SUBARU_ALT_BUS
ALT_CAM_BUS = SUBARU_ALT_BUS
@@ -70,5 +70,10 @@ class TestSubaruPreglobalReversedDriverTorqueSafety(TestSubaruPreglobalSafety):
DBC = "subaru_outback_2019_generated"
class TestSubaruPreglobalStopAndGoSafety(TestSubaruPreglobalSafety):
FLAGS = SubaruSafetyFlags.STOP_AND_GO
TX_MSGS = [[0x161, 0], [0x164, 0], [0x140, 2], [0xD1, 2]]
if __name__ == "__main__":
unittest.main()
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
import unittest
from opendbc.car.structs import CarParams
from opendbc.car.tesla.preap.interface import SAFETY_TESLA_PREAP
from opendbc.safety.tests.libsafety import libsafety_py
import opendbc.safety.tests.common as common
@@ -14,7 +14,7 @@ class TestTeslaPreAPSafety(common.SafetyTestBase):
self._set_mode(0)
def _set_mode(self, param: int) -> None:
self.safety.set_safety_hooks(CarParams.SafetyModel.teslaPreap, param)
self.safety.set_safety_hooks(SAFETY_TESLA_PREAP, param)
self.safety.init_tests()
@staticmethod
@@ -9,6 +9,7 @@ from opendbc.car.structs import CarParams
from opendbc.safety.tests.libsafety import libsafety_py
import opendbc.safety.tests.common as common
from opendbc.safety.tests.common import CANPackerSafety
from opendbc.safety import ALTERNATIVE_EXPERIENCE
TOYOTA_COMMON_TX_MSGS = [[0x2E4, 0], [0x191, 0], [0x412, 0], [0x343, 0], [0x1D2, 0], [0x1D3, 0]] # LKAS + LTA + ACC & PCM cancel cmds
TOYOTA_SECOC_TX_MSGS = [[0x131, 0], [0x183, 0]] + TOYOTA_COMMON_TX_MSGS
@@ -16,6 +17,8 @@ TOYOTA_COMMON_LONG_TX_MSGS = [[0x283, 0], [0x2E6, 0], [0x2E7, 0], [0x33E, 0], [0
[0x128, 1], [0x141, 1], [0x160, 1], [0x161, 1], [0x470, 1], # DSU bus 1
[0x411, 0], # PCS_HUD
[0x750, 0]] # radar diagnostic address
TOYOTA_COMMON_LONG_TX_MSGS_FILTER = TOYOTA_COMMON_LONG_TX_MSGS[:-1]
GAS_INTERCEPTOR_TX_MSGS = [[0x200, 0]]
class TestToyotaSafetyBase(common.CarSafetyTest, common.LongitudinalAccelSafetyTest):
@@ -94,6 +97,30 @@ class TestToyotaSafetyBase(common.CarSafetyTest, common.LongitudinalAccelSafetyT
msg = libsafety_py.make_CANPacket(0x283, 0, bytes(dat))
self.assertEqual(not bad and not stock_longitudinal, self._tx(msg))
def test_auto_brake_hold_aeb_replacement_only_at_standstill(self):
self.safety.set_alternative_experience(ALTERNATIVE_EXPERIENCE.ALLOW_AEB)
hold_msg = libsafety_py.make_CANPacket(0x344, 0, b"\xfd\x80\x00\x00\x00\x00\x00\xcc")
self._rx(self._speed_msg(0))
self._rx(self._toggle_aol(True))
self._rx(self._user_gas_msg(False))
self.assertTrue(self._tx(hold_msg))
self.assertEqual(-1, self.safety.safety_fwd_hook(2, 0x344))
self._rx(self._speed_msg(1.0))
self.assertFalse(self._tx(hold_msg))
self.assertEqual(0, self.safety.safety_fwd_hook(2, 0x344))
self._rx(self._speed_msg(0))
self._rx(self._user_gas_msg(True))
self.assertFalse(self._tx(hold_msg))
self.assertEqual(0, self.safety.safety_fwd_hook(2, 0x344))
self._rx(self._user_gas_msg(False))
self._rx(self._toggle_aol(False))
self.assertFalse(self._tx(hold_msg))
self.assertEqual(0, self.safety.safety_fwd_hook(2, 0x344))
# Only allow LTA msgs with no actuation
def test_lta_steer_cmd(self):
for engaged, req, req2, torque_wind_down, angle in itertools.product([True, False],
@@ -271,6 +298,47 @@ class TestToyotaAltBrakeSafety(TestToyotaSafetyTorque):
pass
class TestToyotaSafetyGasInterceptorBase(common.GasInterceptorSafetyTest, TestToyotaSafetyBase):
TX_MSGS = TOYOTA_COMMON_TX_MSGS + TOYOTA_COMMON_LONG_TX_MSGS + GAS_INTERCEPTOR_TX_MSGS
INTERCEPTOR_THRESHOLD = 805
DBC = "toyota_nodsu_pt_generated"
SAFETY_PARAM = TestToyotaSafetyBase.EPS_SCALE | ToyotaSafetyFlags.GAS_INTERCEPTOR
def setUp(self):
self.packer = CANPackerSafety(self.DBC)
self.safety = libsafety_py.libsafety
self.safety.set_safety_hooks(CarParams.SafetyModel.toyota, self.SAFETY_PARAM)
self.safety.init_tests()
def _user_gas_msg(self, gas):
return self._interceptor_user_gas(self.INTERCEPTOR_THRESHOLD + 1 if gas else 0)
def test_stock_longitudinal_disables_interceptor(self):
self.safety.set_safety_hooks(CarParams.SafetyModel.toyota,
self.SAFETY_PARAM | ToyotaSafetyFlags.STOCK_LONGITUDINAL)
self.safety.init_tests()
self.safety.set_controls_allowed(True)
self.assertFalse(self._tx(self._interceptor_gas_cmd(100)))
class TestToyotaSafetyTorqueGasInterceptor(TestToyotaSafetyGasInterceptorBase, TestToyotaSafetyTorque):
pass
class TestToyotaAltBrakeLongFilterGasInterceptor(TestToyotaSafetyGasInterceptorBase, TestToyotaAltBrakeSafety):
TX_MSGS = TOYOTA_COMMON_TX_MSGS + TOYOTA_COMMON_LONG_TX_MSGS_FILTER + GAS_INTERCEPTOR_TX_MSGS
RELAY_MALFUNCTION_ADDRS = {0: (0x2E4, 0x191, 0x412)}
FWD_BLACKLISTED_ADDRS = {2: [0x2E4, 0x412, 0x191]}
DBC = "toyota_new_mc_pt_generated"
SAFETY_PARAM = (TestToyotaSafetyBase.EPS_SCALE | ToyotaSafetyFlags.ALT_BRAKE |
ToyotaSafetyFlags.LONG_FILTER | ToyotaSafetyFlags.GAS_INTERCEPTOR)
def test_diagnostics(self):
super().test_diagnostics(ecu_disabled=False)
class TestToyotaStockLongitudinalBase(TestToyotaSafetyBase):
TX_MSGS = TOYOTA_COMMON_TX_MSGS
@@ -398,7 +466,5 @@ class TestToyotaSecOcSafety(TestToyotaSecOcSafetyBase):
self.assertEqual(should_tx, self._tx(self._accel_msg_343(accel)))
self.assertEqual(should_tx, self._tx(self._accel_msg_343(accel, cancel_req=1)))
if __name__ == "__main__":
unittest.main()
+4
View File
@@ -169,6 +169,10 @@ build_project("panda", base_project_f4, "./board/main.c", [])
build_project("panda_h7", base_project_h7, "./board/main.c", [])
build_project("panda_remote", base_project_f4, "./board/main.c", ["-DPANDA_GM_REMOTE_START_C9"])
build_project("panda_h7_remote", base_project_h7, "./board/main.c", ["-DPANDA_GM_REMOTE_START_C9"])
build_project("panda_can_ignition_only", base_project_f4, "./board/main.c", ["-DPANDA_IGNORE_IGNITION_LINE"])
build_project("panda_h7_can_ignition_only", base_project_h7, "./board/main.c", ["-DPANDA_IGNORE_IGNITION_LINE"])
build_project("panda_remote_can_ignition_only", base_project_f4, "./board/main.c", ["-DPANDA_GM_REMOTE_START_C9", "-DPANDA_IGNORE_IGNITION_LINE"])
build_project("panda_h7_remote_can_ignition_only", base_project_h7, "./board/main.c", ["-DPANDA_GM_REMOTE_START_C9", "-DPANDA_IGNORE_IGNITION_LINE"])
# panda jungle fw
flags = [
+13 -1
View File
@@ -27,9 +27,21 @@
#include "board/obj/gitversion.h"
#include "board/can_comms.h"
static bool panda_ignition_line(void);
#include "board/main_comms.h"
static bool panda_ignition_line(void) {
#ifdef PANDA_IGNORE_IGNITION_LINE
return false;
#else
return harness_check_ignition();
#endif
}
// ********************* Serial debugging *********************
void debug_ring_callback(uart_ring *ring) {
@@ -176,7 +188,7 @@ static void tick_handler(void) {
const bool recent_heartbeat = heartbeat_counter == 0U;
// tick drivers at 1Hz
bool started = harness_check_ignition() || ignition_can;
bool started = panda_ignition_line() || ignition_can;
bootkick_tick(started, recent_heartbeat);
// increase heartbeat counter and cap it at the uint32 limit
+1 -1
View File
@@ -12,7 +12,7 @@ static int get_health_pkt(void *dat) {
health->voltage_pkt = current_board->read_voltage_mV();
health->current_pkt = current_board->read_current_mA();
health->ignition_line_pkt = (uint8_t)(harness_check_ignition());
health->ignition_line_pkt = (uint8_t)(panda_ignition_line());
health->ignition_can_pkt = ignition_can;
health->controls_allowed_pkt = controls_allowed;
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1,2 +1,2 @@
extern const uint8_t gitversion[19];
const uint8_t gitversion[19] = "DEV-e5cc7460-DEBUG";
const uint8_t gitversion[19] = "DEV-40b54686-DEBUG";
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

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