mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-24 01:33:46 +08:00
Compare commits
613 Commits
standstill-ui
...
rl3
| Author | SHA1 | Date | |
|---|---|---|---|
| 1a527cb927 | |||
| 88be0e6387 | |||
| cc5c95705d | |||
| d16d72afc6 | |||
| 6230d08c0f | |||
| 48af4d2f7f | |||
| d773ad9025 | |||
| 894eae7090 | |||
| 28fcba8471 | |||
| 0cff67bda8 | |||
| f0eeec955f | |||
| 9b28c7bd52 | |||
| ef400a9b3f | |||
| d4b3c3c3b0 | |||
| c57c05886c | |||
| 80f465616a | |||
| 4e70755895 | |||
| 5b41566e4e | |||
| 2141e5a5b1 | |||
| d51dd02d10 | |||
| bb3ed894b1 | |||
| d4c911f58c | |||
| fc87af4da1 | |||
| 7cd02e0c40 | |||
| db8d38e85f | |||
| 2c7e6e72c5 | |||
| 85dd1cbe7e | |||
| 665f538533 | |||
| 28a4de1c9e | |||
| 42eef3f21c | |||
| ce86d9d05a | |||
| b70d0941c9 | |||
| 8d7ba284d7 | |||
| 71b577cbc2 | |||
| cc40a28562 | |||
| 104ae74901 | |||
| 2e45e57287 | |||
| 10843b2f6e | |||
| eb3eb116eb | |||
| b34e968f91 | |||
| 3701079265 | |||
| 1a713fe3e2 | |||
| 6d92a8e5ad | |||
| 01df304f88 | |||
| 7a76325e67 | |||
| c3d1f727c0 | |||
| 94e60c5d70 | |||
| 43143e7a1f | |||
| f2b14c7ac0 | |||
| d5c0fea948 | |||
| a51b1fd78f | |||
| 8b7c2b5f36 | |||
| 0eab89c8d9 | |||
| 7b124faad2 | |||
| 4ac7fcaf5c | |||
| f265aa3f7b | |||
| 11044975c5 | |||
| 27f73a6f08 | |||
| 3e5bb853cb | |||
| 2f0864e215 | |||
| 8376ba7c97 | |||
| c928e27033 | |||
| 5f52120d7a | |||
| cab8457f47 | |||
| 8453f6b4eb | |||
| 03cf613e4c | |||
| ce44830b22 | |||
| 3ae3c5361e | |||
| d3dd2545a9 | |||
| 29cb892e1a | |||
| a106e222ca | |||
| 6ccc984dab | |||
| cce803e888 | |||
| 9427041200 | |||
| 6bae55ac8e | |||
| d8b9bf5529 | |||
| 4c07ea96e7 | |||
| fcf1bf2f3c | |||
| e3c377df34 | |||
| 28141e3074 | |||
| 25c5722372 | |||
| d699f26ded | |||
| 9ccd61e8cf | |||
| fccd46fef5 | |||
| 1863259a58 | |||
| c001d2b184 | |||
| 03ebfd2c69 | |||
| 94141aebb1 | |||
| 71ee4ab2a3 | |||
| dfb5acddf5 | |||
| 33d7e977b4 | |||
| c6708e716f | |||
| 5a7ec6b154 | |||
| d935514f78 | |||
| 8f3f9cf6c7 | |||
| 4013a70e9d | |||
| 3e363cf7a3 | |||
| 9f13e71a0a | |||
| 28ea8d0c09 | |||
| d0c877f591 | |||
| b5ebc6474e | |||
| cb151bb9aa | |||
| e943d2afd2 | |||
| 8556708cd6 | |||
| 6fa509cc88 | |||
| 8e7ce87be8 | |||
| 5d8137b30a | |||
| 368cb1a6f6 | |||
| ba460bbb7b | |||
| a6ffaaa781 | |||
| d4cfd34291 | |||
| b0334c2ae5 | |||
| 6a19e82bb2 | |||
| 5693de7d8d | |||
| 20fa52f5df | |||
| cb540160e4 | |||
| be1fda1504 | |||
| 0c3bd608d9 | |||
| 258b4c6048 | |||
| 8b23fd8c03 | |||
| eb7d4212fd | |||
| 73485ba76c | |||
| 1ea2143e01 | |||
| 20235f027d | |||
| 55ad1fd0fd | |||
| 5a2117892c | |||
| d8e4d75656 | |||
| 77db3e506c | |||
| acd15fcba7 | |||
| 00539e1dc2 | |||
| fb98b8cb29 | |||
| fc88f70ebd | |||
| 6233e7b2c0 | |||
| 0144a691b9 | |||
| 37a6c62e6e | |||
| ca09e50081 | |||
| 8098b9fa53 | |||
| 5564d133b3 | |||
| 6e3bf42c97 | |||
| f94babe509 | |||
| 45eead8dcd | |||
| fcd39be875 | |||
| a609716520 | |||
| a0ae06022c | |||
| c9cf8cfdf6 | |||
| 3d4e1f2d48 | |||
| ca74743eac | |||
| fb031a3c57 | |||
| 4bad6f6f79 | |||
| 7e1b3769d7 | |||
| 4a98785aa3 | |||
| d305071351 | |||
| 653009185e | |||
| 57bfc691bf | |||
| 7e4f8d4154 | |||
| 361f692d53 | |||
| f81099af79 | |||
| 1d982da86e | |||
| c306fc7cfd | |||
| b49c21e5aa | |||
| 44b2df59da | |||
| c8c3a814ea | |||
| 46c3592563 | |||
| 6534ce356a | |||
| e4dfbd1a60 | |||
| e9a2c925ed | |||
| 57292f09bf | |||
| e577502f4b | |||
| 7b0c0784ed | |||
| 96dcfa4287 | |||
| 6046a84e95 | |||
| 1884087590 | |||
| 637c3c820c | |||
| 4f47f8cb0a | |||
| e38668b3cb | |||
| 9dbf5db9ec | |||
| e47a836d2d | |||
| 6c0ee59a3e | |||
| a0f4028aaa | |||
| beb7e5bf6b | |||
| f88ef7ecc2 | |||
| 6cc8e9b86a | |||
| a999f28270 | |||
| 2a3d2744af | |||
| 804316e40c | |||
| bdde8907bf | |||
| 85ab7557e9 | |||
| 3d2b959db7 | |||
| 889cd3db1a | |||
| 4916143a5d | |||
| 1153010f85 | |||
| 0cbd8013b2 | |||
| a425a9c11a | |||
| 987de36f54 | |||
| f2fbd3f97c | |||
| 30245e7d34 | |||
| 3aa2801ce7 | |||
| bc3c245aa5 | |||
| cdb266cba2 | |||
| e4e416bfb3 | |||
| 39414ee2e4 | |||
| a08fe79cef | |||
| 33508d42ef | |||
| 110bd8b295 | |||
| 622720f730 | |||
| 4bdf55f6a0 | |||
| 9e2448b3d5 | |||
| 2ccd6e35b7 | |||
| 71b5cd8594 | |||
| 3bcd062eb7 | |||
| 6a4b508864 | |||
| 6e48d7dc12 | |||
| d849ac120b | |||
| 4243a5b055 | |||
| 8d9e97fdcd | |||
| 1457b902ac | |||
| 8ab1b723ae | |||
| 2fb5fbf3ad | |||
| cbe6eb077e | |||
| 831a8f27e5 | |||
| 46d19999ce | |||
| e27badccef | |||
| 07cf72d573 | |||
| 8bda3d5be1 | |||
| cdc2b7a3ed | |||
| c7bc46ec8e | |||
| dc1b9fc790 | |||
| 275f8966b2 | |||
| 5b1445bb5f | |||
| 0b1a94914b | |||
| 3317d545be | |||
| a630533b44 | |||
| cb82059d67 | |||
| e08745d519 | |||
| 7e76fa9b41 | |||
| b72570df40 | |||
| 95ee3a818b | |||
| 72992743a4 | |||
| 8eefbacc48 | |||
| 4531edad1e | |||
| 0af6ca7d37 | |||
| ac4ffb4a7e | |||
| 641f1dc238 | |||
| 650cb03b60 | |||
| cefcc41bca | |||
| 957fe8af75 | |||
| d98ae2b94b | |||
| 12a984b26d | |||
| 9cd81e89f6 | |||
| 677545cc23 | |||
| 2f02c93b3b | |||
| 7f314d9f28 | |||
| 6ea41a0329 | |||
| c33e8094e8 | |||
| f0b23a40fe | |||
| b65158cd97 | |||
| b6794d72d3 | |||
| ea2aa532af | |||
| 5bbe5f1daf | |||
| 1afb6b3978 | |||
| 9e03465520 | |||
| 06e267cef2 | |||
| b79527a217 | |||
| fa85c848f6 | |||
| 0e08829188 | |||
| 1363a5b044 | |||
| 60406cb982 | |||
| 152c39044f | |||
| 6c392185be | |||
| c7a5c880cc | |||
| 0510e87530 | |||
| c9734c9b1a | |||
| 66e04d4a45 | |||
| c1577d2536 | |||
| 23b354d47e | |||
| 5c9888a0d7 | |||
| 74cf048f94 | |||
| 0eb8d0cfdc | |||
| 9ddf5d31b4 | |||
| 7da4359dd0 | |||
| 032b2a1fde | |||
| 08df283511 | |||
| ae3792c683 | |||
| 9d2149d495 | |||
| 5234a121f9 | |||
| 706c65a70d | |||
| 04bee88dd2 | |||
| 072928ec96 | |||
| 03ec0b2c60 | |||
| 294e301000 | |||
| 74e4dd4c44 | |||
| f40881657c | |||
| 4bb15dded4 | |||
| 5eb7e8f02b | |||
| ef5ece17eb | |||
| 1339ca02c4 | |||
| d5e40d3bdf | |||
| 97c20744d3 | |||
| 30786472cb | |||
| af185f8491 | |||
| 2a5cb66b9a | |||
| 82eae95add | |||
| 85b33d8819 | |||
| e4fc9b6912 | |||
| a44cde63a5 | |||
| d93039698c | |||
| 3a1ca3fb81 | |||
| 28e51ebfea | |||
| 1c2627e1ae | |||
| b4dc28a45e | |||
| c8a9eb5464 | |||
| 11be793c3a | |||
| ce989294ab | |||
| a21520fa8f | |||
| e5751388d4 | |||
| 535ae098c6 | |||
| 0b26d1c050 | |||
| 7d52c80bf4 | |||
| 50630f121c | |||
| f26c89d6cb | |||
| e88854319c | |||
| 2e53b419d1 | |||
| f4bc8ee6b7 | |||
| 6daefdc0e1 | |||
| 96e578fa71 | |||
| 768a2f324c | |||
| f50b8dabbb | |||
| d9e0345a2b | |||
| 8a505e6677 | |||
| 40b5468615 | |||
| f7418649f8 | |||
| 5d3b0f4ea9 | |||
| 7e773e5373 | |||
| 6d088863a6 | |||
| 6b515db122 | |||
| bdc1532258 | |||
| afe15a9a80 | |||
| 02060a4b67 | |||
| efd25eca90 | |||
| b4f17f35f9 | |||
| a0669247e3 | |||
| be811cbe61 | |||
| 460b61ebe8 | |||
| 7411a11f83 | |||
| a7ad72489b | |||
| e82a4f034b | |||
| a8ec45ec12 | |||
| 0df2444e95 | |||
| 32cf065988 | |||
| 41c680e60c | |||
| fac7554cb6 | |||
| 92bc8ee934 | |||
| 9c30fc2387 | |||
| 3f5dc27170 | |||
| a5fb9a2cff | |||
| 201d0c0476 | |||
| 54e722ff00 | |||
| fb1c2ea1c4 | |||
| 0f43f042fb | |||
| ee1f0bbe83 | |||
| b811825b38 | |||
| ee71b70f24 | |||
| b75b21681d | |||
| 562c5eb58d | |||
| 2798c12c90 | |||
| 19d545b22e | |||
| 36e935c9dc | |||
| cf89678178 | |||
| 88e4004c9a | |||
| 9f0ae1a495 | |||
| f5a5b9e141 | |||
| 057ca874ce | |||
| 497eb3f771 | |||
| 284bb45c0c | |||
| 836b0278eb | |||
| 4ce9be2752 | |||
| 24fc6fe4e5 | |||
| 297ff1973d | |||
| 727985697f | |||
| 59d1581c0a | |||
| 3a86dffa17 | |||
| 9f7dcb68f9 | |||
| 6793c1a4f5 | |||
| f351dbac09 | |||
| 44255d67c2 | |||
| 1ce49be89b | |||
| bdf7bca569 | |||
| d8e292dc94 | |||
| f0601e27ec | |||
| cec9f090c4 | |||
| d08da1ff22 | |||
| 58b028cfc8 | |||
| d7f3037c88 | |||
| 028ee7b3f5 | |||
| 94aff6faed | |||
| b63039ce84 | |||
| aec5590d57 | |||
| abd19df5f4 | |||
| b1dae43ea8 | |||
| 8c32f6d99d | |||
| f6b51e6742 | |||
| d3881e6a77 | |||
| d83b065c53 | |||
| 16c3d5f48a | |||
| d6dc906ff8 | |||
| ea0ef444de | |||
| ea1da70b22 | |||
| ad9aa72979 | |||
| 1382200cda | |||
| 7e8832d672 | |||
| 9cd9a6201c | |||
| 6ee00d3f60 | |||
| d97100bd14 | |||
| bb36fe4287 | |||
| 066a0f2520 | |||
| c5a6e1da9d | |||
| f8cec6f270 | |||
| 544979d652 | |||
| 353672fb6a | |||
| 4101ab9b28 | |||
| 7fb1d854c6 | |||
| d8587bba31 | |||
| ad3bad8b85 | |||
| c91ac533ef | |||
| 32a6af9ac8 | |||
| e84c0e44ab | |||
| d28cd4df8b | |||
| cfd762f0a2 | |||
| 16d6af490a | |||
| 0b37a23c75 | |||
| bf8204e9a2 | |||
| 34813dee41 | |||
| d84518b97f | |||
| 2356823b62 | |||
| ce98b31cbf | |||
| 97881b5c66 | |||
| fc90fcb879 | |||
| 762c54bb2f | |||
| 9910651a9d | |||
| 42a23508b7 | |||
| fbb6eb651c | |||
| e4809535dc | |||
| 63490fe5e6 | |||
| a7b7148c25 | |||
| e50874baca | |||
| ef4f174c8d | |||
| 9c726d99de | |||
| 25a50a2ec3 | |||
| 5d3d5a884b | |||
| 7a3c1ede9a | |||
| d931d3007b | |||
| 05c2a90e16 | |||
| dc581f6300 | |||
| a6c5b43caf | |||
| 3ec82de5fc | |||
| cd08c87a80 | |||
| d8b715799c | |||
| 0bc009f800 | |||
| ff49f332b0 | |||
| f04b561f88 | |||
| 1f237019e7 | |||
| b47c18426d | |||
| 0e5d40fb1b | |||
| 496d36f805 | |||
| 9217bf289c | |||
| 2c2d8b86ab | |||
| c8128d2cde | |||
| 9c85ebc365 | |||
| 8c510f964c | |||
| 35e623c392 | |||
| 5eb6fe2dd9 | |||
| 64bb95e3cf | |||
| 96818d8efc | |||
| c23434b947 | |||
| 2a0053c046 | |||
| f1a256dac0 | |||
| 967fb96167 | |||
| bb4f3b74d9 | |||
| 7dab3a0ceb | |||
| e6b12d612a | |||
| d17a4bd6cb | |||
| 540becea52 | |||
| a71be7c47b | |||
| 7305822e67 | |||
| 6b0e5aed24 | |||
| 6f907e2060 | |||
| 29b777f12c | |||
| 335d6934fb | |||
| 60305edeae | |||
| da305d9da3 | |||
| 624f51aef4 | |||
| ba76832979 | |||
| 49126f29ae | |||
| 2ed92c88ae | |||
| a9771a0e85 | |||
| 1dcac8cfef | |||
| 18ea1dd170 | |||
| 59336096e7 | |||
| 57d7796edb | |||
| 2dc441d621 | |||
| 93ec2a94e6 | |||
| 56564d55c8 | |||
| 322364c1ca | |||
| 838c7fdc9b | |||
| e02d67c153 | |||
| 407e2ea90a | |||
| 712256eac4 | |||
| 1c2aedcc9a | |||
| 9af2969540 | |||
| 8a7946b26c | |||
| 53e0aded4c | |||
| 3ecd8be1f1 | |||
| 0262b934c4 | |||
| 9aeaf593c3 | |||
| 00c64ba805 | |||
| 7d939e1c76 | |||
| 8a4d2b558b | |||
| 0508653a29 | |||
| 2e75486d16 | |||
| 7bd73401cd | |||
| 4a107868cf | |||
| 53766ff6b7 | |||
| 4ee6758b9f | |||
| 4c1317e30d | |||
| 8a68dec71f | |||
| c7efa7fa20 | |||
| bd7394ea33 | |||
| fa7fa6afaf | |||
| 491ec0c0b8 | |||
| 87f1334ce9 | |||
| 140b047e7f | |||
| 21b1bfd600 | |||
| 5da99bc977 | |||
| a30dc40b8a | |||
| 96893d9c36 | |||
| 9be0defc29 | |||
| afc8c6fb4b | |||
| b559b0f827 | |||
| 79e31ff241 | |||
| 1e604fd80e | |||
| 56989a164e | |||
| eaf5d44037 | |||
| 93cb562321 | |||
| 31d3df673a | |||
| c0aa7a7460 | |||
| 498b7f14a9 | |||
| 95b6ecec86 | |||
| 8e6fc9bed3 | |||
| cf7822180b | |||
| f93c536f7e | |||
| d555d1f588 | |||
| 88e297b183 | |||
| 37f5c8ca58 | |||
| 7d53bd5af7 | |||
| 455a8ec11d | |||
| dac0610a19 | |||
| da5b3f829f | |||
| 02960fc8c2 | |||
| 4585938753 | |||
| f777e71c56 | |||
| 0a698d202b | |||
| 63db39082b | |||
| 4f48c2ec27 | |||
| 6faf9bd88a | |||
| a624cf89bb | |||
| e836ed09c8 | |||
| 8cc9c59330 | |||
| a11d064aeb | |||
| 77c82edb44 | |||
| 9a9ac3af77 | |||
| ac8511b211 | |||
| c3ff2e86db | |||
| 98d6a4eac0 | |||
| 5f413b967e | |||
| bdb0da4fa0 | |||
| cabacda0ad | |||
| d8c79a9712 | |||
| 88c259a666 | |||
| 796d081fce | |||
| 6d61feff11 | |||
| 5d0ab0cfb0 | |||
| 6c687a9f5c | |||
| 7a17c21fab | |||
| 1994a0bd16 | |||
| 8a1bfb925c | |||
| 914411fe06 | |||
| c88bebbc3e | |||
| aba65bf755 | |||
| cf1c99cf78 | |||
| 9c51a6024e | |||
| bec246c6ef | |||
| eeab4b8c85 | |||
| a6758eb9e6 | |||
| cdcc8da555 | |||
| 1f9aa83440 | |||
| 15d218677e | |||
| f8934840da | |||
| a888c5602a | |||
| 4134da76ca | |||
| 24b0f37ae7 | |||
| 559a738fe7 | |||
| f20b1f2a4f | |||
| c76b770807 | |||
| 3c0dea5747 | |||
| e47fbd1cb9 | |||
| efdc48980d | |||
| f84f7d4f17 | |||
| aa82ae0bd0 | |||
| 743ff67927 | |||
| 2880189713 | |||
| 2847d0fd8a | |||
| c23aeb9fcd | |||
| 980c85cfb1 |
@@ -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"
|
||||
)
|
||||
|
||||
@@ -21,6 +21,7 @@ compiledmodels/
|
||||
/docs_site/
|
||||
|
||||
*.mp4
|
||||
!docs/assets/speed-limit-vision-demo*.mp4
|
||||
*.dylib
|
||||
*.DSYM
|
||||
*.d
|
||||
@@ -68,6 +69,7 @@ cppcheck_report.txt
|
||||
comma*.sh
|
||||
|
||||
selfdrive/modeld/models/*.pkl
|
||||
!selfdrive/modeld/models/driving_tinygrad.pkl
|
||||
!selfdrive/modeld/models/driving_vision_tinygrad.pkl
|
||||
!selfdrive/modeld/models/driving_policy_tinygrad.pkl
|
||||
!selfdrive/modeld/models/driving_vision_metadata.pkl
|
||||
|
||||
@@ -3,4 +3,110 @@
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
exec "${ROOT_DIR}/scripts/laptop_device_build.sh" build "$@"
|
||||
original_args=("$@")
|
||||
|
||||
shortcut_targets=()
|
||||
passthrough_args=()
|
||||
shortcut_includes_panda=0
|
||||
panda_generated_files=(
|
||||
"panda/board/obj/gitversion.h"
|
||||
"panda/board/obj/version"
|
||||
)
|
||||
|
||||
add_panda_targets() {
|
||||
local variants=(
|
||||
panda
|
||||
panda_h7
|
||||
panda_remote
|
||||
panda_h7_remote
|
||||
panda_hkg_remote
|
||||
panda_h7_hkg_remote
|
||||
panda_can_ignition_only
|
||||
panda_h7_can_ignition_only
|
||||
panda_remote_can_ignition_only
|
||||
panda_h7_remote_can_ignition_only
|
||||
panda_hkg_remote_can_ignition_only
|
||||
panda_h7_hkg_remote_can_ignition_only
|
||||
panda_jungle_h7
|
||||
body_h7
|
||||
)
|
||||
|
||||
local variant
|
||||
for variant in "${variants[@]}"; do
|
||||
shortcut_targets+=("panda/board/obj/${variant}.bin.signed")
|
||||
done
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--params|-params|-param)
|
||||
shortcut_targets+=("common/params_pyx.so")
|
||||
;;
|
||||
--panda|-panda)
|
||||
shortcut_includes_panda=1
|
||||
add_panda_targets
|
||||
;;
|
||||
--ui|-ui)
|
||||
shortcut_targets+=("selfdrive/ui/ui")
|
||||
;;
|
||||
--cereal|-cereal)
|
||||
shortcut_targets+=(
|
||||
"cereal/libcereal.a"
|
||||
"cereal/libsocketmaster.a"
|
||||
"cereal/messaging/bridge"
|
||||
"cereal/services.h"
|
||||
)
|
||||
;;
|
||||
*)
|
||||
if [[ "$1" =~ ^[0-9]+$ ]]; then
|
||||
passthrough_args+=("-j$1")
|
||||
else
|
||||
passthrough_args+=("$1")
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ "${#shortcut_targets[@]}" -gt 0 ]]; then
|
||||
scons_args=("--no-scrub")
|
||||
scons_args+=("${shortcut_targets[@]}")
|
||||
if [[ "${#passthrough_args[@]}" -gt 0 ]]; then
|
||||
scons_args+=("${passthrough_args[@]}")
|
||||
fi
|
||||
|
||||
if [[ "${shortcut_includes_panda}" -eq 1 ]]; then
|
||||
exec "${ROOT_DIR}/scripts/laptop_device_build.sh" scons "${scons_args[@]}"
|
||||
fi
|
||||
|
||||
tmpdir="$(mktemp -d)"
|
||||
for path in "${panda_generated_files[@]}"; do
|
||||
if [[ -e "${ROOT_DIR}/${path}" ]]; then
|
||||
cp -p "${ROOT_DIR}/${path}" "${tmpdir}/${path//\//__}"
|
||||
else
|
||||
touch "${tmpdir}/${path//\//__}.missing"
|
||||
fi
|
||||
done
|
||||
|
||||
if "${ROOT_DIR}/scripts/laptop_device_build.sh" scons "${scons_args[@]}"; then
|
||||
status=0
|
||||
else
|
||||
status=$?
|
||||
fi
|
||||
|
||||
for path in "${panda_generated_files[@]}"; do
|
||||
if [[ -e "${tmpdir}/${path//\//__}.missing" ]]; then
|
||||
rm -f "${ROOT_DIR}/${path}"
|
||||
else
|
||||
cp -p "${tmpdir}/${path//\//__}" "${ROOT_DIR}/${path}"
|
||||
fi
|
||||
done
|
||||
rm -rf "${tmpdir}"
|
||||
exit "${status}"
|
||||
fi
|
||||
|
||||
if [[ "${#original_args[@]}" -gt 0 ]]; then
|
||||
exec "${ROOT_DIR}/scripts/laptop_device_build.sh" build "${original_args[@]}"
|
||||
fi
|
||||
|
||||
exec "${ROOT_DIR}/scripts/laptop_device_build.sh" build
|
||||
|
||||
+22
-2
@@ -91,6 +91,13 @@ struct StarPilotCarState @0xf35cc4560bbf6ec2 {
|
||||
cancelPressed @20 :Bool;
|
||||
cancelLongPressed @21 :Bool;
|
||||
cancelVeryLongPressed @22 :Bool;
|
||||
pedalMaxRegen @23 :Bool; # pedal at max regen, driver should use brake for more decel
|
||||
pedalLongActive @24 :Bool; # Pre-AP pedal longitudinal mode is active (enableLongControl)
|
||||
teslaCCEngaged @25 :Bool; # rising edge of stock Tesla CC engaging (no-pedal mode)
|
||||
teslaCCDisengaged @26 :Bool; # falling edge of stock Tesla CC
|
||||
teslaCCNotArmed @27 :Bool; # lateral engaged but DI_cruiseState != STANDBY/ENABLED
|
||||
accelHardCruise @28 :Bool; # current/releasing accel cruise button came from GM hard-press signal
|
||||
decelHardCruise @29 :Bool; # current/releasing decel cruise button came from GM hard-press signal
|
||||
}
|
||||
|
||||
struct StarPilotDeviceState @0xda96579883444c35 {
|
||||
@@ -108,7 +115,11 @@ struct StarPilotModelDataV2 @0x80ae746ee2596b11 {
|
||||
}
|
||||
}
|
||||
|
||||
struct StarPilotOnroadEvent @0xa5cd762cd951a455 {
|
||||
struct StarPilotOnroadEvents @0xa5cd762cd951a455 {
|
||||
events @0 :List(StarPilotOnroadEvent);
|
||||
}
|
||||
|
||||
struct StarPilotOnroadEvent @0xe344718567f9ce71 {
|
||||
name @0 :EventName;
|
||||
|
||||
enable @1 :Bool;
|
||||
@@ -158,6 +169,14 @@ struct StarPilotOnroadEvent @0xa5cd762cd951a455 {
|
||||
switchbackModeInactive @30;
|
||||
lkasEnable @31;
|
||||
lkasDisable @32;
|
||||
lateralManeuver @33;
|
||||
pedalCruiseEnabled @34;
|
||||
pedalCruiseDisabled @35;
|
||||
pedalMaxRegen @36;
|
||||
teslaCCEngaged @37;
|
||||
teslaCCDisengaged @38;
|
||||
teslaCCNotArmed @39;
|
||||
pedalNotCalibrated @40;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,7 +278,8 @@ struct CustomReserved9 @0xa1680744031fdb2d {
|
||||
wallTimeNanos @5 :UInt64;
|
||||
}
|
||||
|
||||
struct CustomReserved10 @0xcb9fd56c7057593a {
|
||||
struct StarPilotLateralManeuverPlanDEPRECATED @0xcb9fd56c7057593a {
|
||||
desiredCurvature @0 :Float32; # 1/m
|
||||
}
|
||||
|
||||
struct CustomReserved11 @0xc2243c65e0340384 {
|
||||
|
||||
Binary file not shown.
+119
-26
@@ -68,12 +68,12 @@ struct OnroadEvent @0xc4fa6047f024e718 {
|
||||
longitudinalManeuver @30;
|
||||
steerTempUnavailableSilent @31;
|
||||
resumeRequired @32;
|
||||
preDriverDistracted @33;
|
||||
promptDriverDistracted @34;
|
||||
driverDistracted @35;
|
||||
preDriverUnresponsive @36;
|
||||
promptDriverUnresponsive @37;
|
||||
driverUnresponsive @38;
|
||||
driverDistracted1 @33;
|
||||
driverDistracted2 @34;
|
||||
driverDistracted3 @35;
|
||||
driverUnresponsive1 @36;
|
||||
driverUnresponsive2 @37;
|
||||
driverUnresponsive3 @38;
|
||||
belowSteerSpeed @39;
|
||||
lowBattery @40;
|
||||
accFaulted @41;
|
||||
@@ -130,14 +130,6 @@ struct OnroadEvent @0xc4fa6047f024e718 {
|
||||
userBookmark @95;
|
||||
excessiveActuation @96;
|
||||
audioFeedback @97;
|
||||
lateralManeuver @98;
|
||||
pedalCruiseEnabled @99;
|
||||
pedalCruiseDisabled @100;
|
||||
pedalMaxRegen @101;
|
||||
teslaCCEngaged @102;
|
||||
teslaCCDisengaged @103;
|
||||
teslaCCNotArmed @104;
|
||||
pedalNotCalibrated @105;
|
||||
|
||||
soundsUnavailableDEPRECATED @47;
|
||||
}
|
||||
@@ -833,13 +825,30 @@ struct SelfdriveState {
|
||||
alertStatus @5 :AlertStatus;
|
||||
alertSize @6 :AlertSize;
|
||||
alertType @7 :Text;
|
||||
alertSound @8 :Car.CarControl.HUDControl.AudibleAlert;
|
||||
alertSound @13 :AudibleAlert;
|
||||
alertHudVisual @12 :Car.CarControl.HUDControl.VisualAlert;
|
||||
|
||||
# configurable driving settings
|
||||
experimentalMode @10 :Bool;
|
||||
personality @11 :LongitudinalPersonality;
|
||||
|
||||
enum AudibleAlert {
|
||||
none @0;
|
||||
|
||||
engage @1;
|
||||
disengage @2;
|
||||
refuse @3;
|
||||
|
||||
warningSoft @4;
|
||||
warningImmediate @5;
|
||||
|
||||
prompt @6;
|
||||
promptRepeat @7;
|
||||
promptDistracted @8;
|
||||
|
||||
preAlert @9;
|
||||
}
|
||||
|
||||
enum OpenpilotState @0xdbe58b96d2d1ac61 {
|
||||
disabled @0;
|
||||
preEnabled @1;
|
||||
@@ -860,6 +869,10 @@ struct SelfdriveState {
|
||||
mid @2;
|
||||
full @3;
|
||||
}
|
||||
|
||||
deprecated :group {
|
||||
alertSound @8 :Car.CarControl.HUDControl.AudibleAlert;
|
||||
}
|
||||
}
|
||||
|
||||
struct ControlsState @0x97ff69c53601abf1 {
|
||||
@@ -1095,7 +1108,7 @@ struct ModelDataV2 {
|
||||
confidence @23: ConfidenceClass;
|
||||
|
||||
# Model perceived motion
|
||||
temporalPose @21 :Pose;
|
||||
temporalPoseDEPRECATED @21 :Pose;
|
||||
|
||||
# e2e lateral planner
|
||||
action @26: Action;
|
||||
@@ -2221,7 +2234,7 @@ struct DriverStateDEPRECATED @0xb83c6cc593ed0a00 {
|
||||
stdDEPRECATED @2 :Float32;
|
||||
}
|
||||
|
||||
struct DriverMonitoringState @0xb83cda094a1da284 {
|
||||
struct DriverMonitoringStateDEPRECATED @0xb83cda094a1da284 {
|
||||
events @18 :List(OnroadEvent);
|
||||
faceDetected @1 :Bool;
|
||||
isDistracted @2 :Bool;
|
||||
@@ -2239,12 +2252,91 @@ struct DriverMonitoringState @0xb83cda094a1da284 {
|
||||
isActiveMode @16 :Bool;
|
||||
isRHD @4 :Bool;
|
||||
uncertainCount @19 :UInt32;
|
||||
phoneProbOffset @20 :Float32;
|
||||
phoneProbValidCount @21 :UInt32;
|
||||
|
||||
isPreviewDEPRECATED @15 :Bool;
|
||||
rhdCheckedDEPRECATED @5 :Bool;
|
||||
eventsDEPRECATED @0 :List(Car.OnroadEventDEPRECATED);
|
||||
deprecated :group {
|
||||
phoneProbOffset @20 :Float32;
|
||||
phoneProbValidCount @21 :UInt32;
|
||||
isPreview @15 :Bool;
|
||||
rhdChecked @5 :Bool;
|
||||
events @0 :List(Car.OnroadEventDEPRECATED);
|
||||
}
|
||||
}
|
||||
|
||||
struct DriverMonitoringState {
|
||||
lockout @0 :Bool;
|
||||
lockoutCount @15 :Int8;
|
||||
lockoutMinutesRemaining @11 :Int8;
|
||||
alert3Count @12 :Int8;
|
||||
noResponseCount @13 :Int8;
|
||||
noResponseForceDecel @14 :Bool;
|
||||
|
||||
alwaysOn @3 :Bool;
|
||||
alwaysOnLockout @4 :Bool;
|
||||
|
||||
alertLevel @5 :AlertLevel;
|
||||
activePolicy @6 :MonitoringPolicy;
|
||||
isRHD @7 :Bool;
|
||||
rhdCalibration @8 :CalibrationState;
|
||||
|
||||
visionPolicyState @9 :VisionPolicyState;
|
||||
wheeltouchPolicyState @10 :WheeltouchPolicyState;
|
||||
|
||||
enum AlertLevel {
|
||||
# ordinal must match the name to prevent bugs
|
||||
# comparing against the raw ordinal value
|
||||
none @0;
|
||||
one @1;
|
||||
two @2;
|
||||
three @3;
|
||||
}
|
||||
|
||||
enum MonitoringPolicy {
|
||||
wheeltouch @0;
|
||||
vision @1;
|
||||
}
|
||||
|
||||
struct VisionPolicyState {
|
||||
awarenessPercent @0 :Int8;
|
||||
awarenessStep @1 :Float32;
|
||||
isDistracted @2 :Bool;
|
||||
distractedTypes @3 :DistractedTypes;
|
||||
|
||||
faceDetected @4 :Bool;
|
||||
pose @5 :Pose;
|
||||
wheeltouchFallbackPercent @6 :Int8;
|
||||
uncertainOffroadAlertPercent @7 :Int8;
|
||||
|
||||
struct DistractedTypes {
|
||||
pose @0: Bool;
|
||||
eye @1: Bool;
|
||||
phone @2: Bool;
|
||||
}
|
||||
|
||||
struct Pose {
|
||||
pitch @0 :Float32;
|
||||
yaw @1 :Float32;
|
||||
pitchCalib @2 :CalibrationState;
|
||||
yawCalib @3 :CalibrationState;
|
||||
calibrated @4 :Bool;
|
||||
uncertainty @5 :Float32;
|
||||
}
|
||||
}
|
||||
|
||||
struct WheeltouchPolicyState {
|
||||
awarenessPercent @0 :Int8;
|
||||
awarenessStep @1 :Float32;
|
||||
driverInteracting @2 :Bool;
|
||||
}
|
||||
|
||||
struct CalibrationState {
|
||||
calibratedPercent @0 :Int8;
|
||||
offset @1 :Float32;
|
||||
}
|
||||
|
||||
deprecated :group {
|
||||
alertCountLockoutPercent @1 :Int8;
|
||||
alertTimeLockoutPercent @2 :Int8;
|
||||
}
|
||||
}
|
||||
|
||||
struct Boot {
|
||||
@@ -2565,7 +2657,7 @@ struct Event {
|
||||
thumbnail @66: Thumbnail;
|
||||
onroadEvents @134: List(OnroadEvent);
|
||||
carParams @69: Car.CarParams;
|
||||
driverMonitoringState @71: DriverMonitoringState;
|
||||
driverMonitoringState @151 :DriverMonitoringState;
|
||||
livePose @129 :LivePose;
|
||||
modelV2 @75 :ModelDataV2;
|
||||
drivingModelData @128 :DrivingModelData;
|
||||
@@ -2614,8 +2706,8 @@ struct Event {
|
||||
userBookmark @93 :UserBookmark;
|
||||
bookmarkButton @148 :UserBookmark;
|
||||
audioFeedback @149 :AudioFeedback;
|
||||
lateralManeuverPlan @150 :LateralManeuverPlan;
|
||||
|
||||
lateralManeuverPlan @150 :LateralManeuverPlan;
|
||||
# *********** debug ***********
|
||||
testJoystick @52 :Joystick;
|
||||
roadEncodeData @86 :EncodeData;
|
||||
@@ -2644,12 +2736,12 @@ struct Event {
|
||||
starpilotCarState @109 :Custom.StarPilotCarState;
|
||||
starpilotDeviceState @110 :Custom.StarPilotDeviceState;
|
||||
starpilotModelV2 @111 :Custom.StarPilotModelDataV2;
|
||||
starpilotOnroadEvents @112 :List(Custom.StarPilotOnroadEvent);
|
||||
starpilotOnroadEvents @112 :Custom.StarPilotOnroadEvents;
|
||||
starpilotPlan @113 :Custom.StarPilotPlan;
|
||||
starpilotRadarState @114 :Custom.StarPilotRadarState;
|
||||
starpilotSelfdriveState @115 :Custom.StarPilotSelfdriveState;
|
||||
customReserved9 @116 :Custom.CustomReserved9;
|
||||
customReserved10 @136 :Custom.CustomReserved10;
|
||||
starpilotLateralManeuverPlanDEPRECATED @136 :Custom.StarPilotLateralManeuverPlanDEPRECATED;
|
||||
customReserved11 @137 :Custom.CustomReserved11;
|
||||
customReserved12 @138 :Custom.CustomReserved12;
|
||||
customReserved13 @139 :Custom.CustomReserved13;
|
||||
@@ -2707,5 +2799,6 @@ struct Event {
|
||||
gyroscope2DEPRECATED @100 :SensorEventData;
|
||||
accelerometer2DEPRECATED @101 :SensorEventData;
|
||||
temperatureSensor2DEPRECATED @123 :SensorEventData;
|
||||
driverMonitoringStateDEPRECATED @71 :DriverMonitoringStateDEPRECATED;
|
||||
}
|
||||
}
|
||||
|
||||
+61
-1
@@ -1,5 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
import io
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
CHUNK_SIZE = 45 * 1024 * 1024 # 45MB, under GitHub's 50MB limit
|
||||
@@ -13,9 +16,16 @@ def get_manifest_path(name):
|
||||
return f"{name}.chunkmanifest"
|
||||
|
||||
|
||||
def _chunk_paths(path, num_chunks):
|
||||
return [get_manifest_path(path)] + [get_chunk_name(path, i, num_chunks) for i in range(num_chunks)]
|
||||
|
||||
|
||||
def get_chunk_paths(path, file_size):
|
||||
num_chunks = math.ceil(file_size / CHUNK_SIZE)
|
||||
return [get_manifest_path(path)] + [get_chunk_name(path, i, num_chunks) for i in range(num_chunks)]
|
||||
return _chunk_paths(path, num_chunks)
|
||||
|
||||
|
||||
get_chunk_targets = get_chunk_paths
|
||||
|
||||
|
||||
def chunk_file(path, targets):
|
||||
@@ -31,6 +41,51 @@ def chunk_file(path, targets):
|
||||
os.remove(path)
|
||||
|
||||
|
||||
def get_existing_chunks(path):
|
||||
if os.path.isfile(path):
|
||||
return [path]
|
||||
manifest_path = get_manifest_path(path)
|
||||
if os.path.isfile(manifest_path):
|
||||
num_chunks = int(Path(manifest_path).read_text().strip())
|
||||
return _chunk_paths(path, num_chunks)
|
||||
raise FileNotFoundError(path)
|
||||
|
||||
|
||||
def file_chunked_exists(path) -> bool:
|
||||
return os.path.isfile(path) or os.path.isfile(get_manifest_path(path))
|
||||
|
||||
|
||||
class ChunkStream(io.RawIOBase):
|
||||
def __init__(self, paths):
|
||||
self._paths = iter(paths)
|
||||
self._buffer = memoryview(b"")
|
||||
|
||||
def readable(self):
|
||||
return True
|
||||
|
||||
def readinto(self, buffer):
|
||||
count = 0
|
||||
while count < len(buffer):
|
||||
if not self._buffer:
|
||||
path = next(self._paths, None)
|
||||
if path is None:
|
||||
break
|
||||
self._buffer = memoryview(Path(path).read_bytes())
|
||||
continue
|
||||
take = min(len(buffer) - count, len(self._buffer))
|
||||
buffer[count:count + take] = self._buffer[:take]
|
||||
self._buffer = self._buffer[take:]
|
||||
count += take
|
||||
return count
|
||||
|
||||
|
||||
def open_file_chunked(path):
|
||||
chunks = get_existing_chunks(path)
|
||||
if chunks and chunks[0] == get_manifest_path(path):
|
||||
chunks = chunks[1:]
|
||||
return io.BufferedReader(ChunkStream(chunks))
|
||||
|
||||
|
||||
def read_file_chunked(path):
|
||||
manifest_path = get_manifest_path(path)
|
||||
if os.path.isfile(manifest_path):
|
||||
@@ -39,3 +94,8 @@ def read_file_chunked(path):
|
||||
if os.path.isfile(path):
|
||||
return Path(path).read_bytes()
|
||||
raise FileNotFoundError(path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
file_path = sys.argv[1]
|
||||
chunk_file(file_path, get_chunk_targets(file_path, os.path.getsize(file_path)))
|
||||
|
||||
Binary file not shown.
+5
-1
@@ -99,7 +99,7 @@ Params::Params(const std::string &path, bool memory) {
|
||||
if (memory) {
|
||||
params_folder = Path::shm_path() + "/params";
|
||||
} else {
|
||||
cache_path = "/cache/params" + params_prefix + "/";
|
||||
cache_path = Path::params_cache() + params_prefix + "/";
|
||||
params_folder = path;
|
||||
}
|
||||
params_path = ensure_params_path(params_prefix, params_folder);
|
||||
@@ -309,6 +309,10 @@ int Params::getTuningLevel(const std::string &key) {
|
||||
return keys[key].tuning_level;
|
||||
}
|
||||
|
||||
ParamSettingsTier Params::getSettingsTier(const std::string &key) {
|
||||
return keys[key].settings_tier;
|
||||
}
|
||||
|
||||
std::optional<std::string> Params::getStockValue(const std::string &key) {
|
||||
ParamKeyAttributes &attributes = keys[key];
|
||||
if (attributes.stock_value) {
|
||||
|
||||
@@ -33,6 +33,11 @@ enum ParamKeyType {
|
||||
BYTES = 6
|
||||
};
|
||||
|
||||
enum ParamSettingsTier {
|
||||
SETTINGS_SIMPLE = 0,
|
||||
SETTINGS_ADVANCED = 1,
|
||||
};
|
||||
|
||||
struct ParamKeyAttributes {
|
||||
uint32_t flags;
|
||||
ParamKeyType type;
|
||||
@@ -42,6 +47,9 @@ struct ParamKeyAttributes {
|
||||
std::optional<std::string> stock_value = std::nullopt;
|
||||
|
||||
int tuning_level = 0;
|
||||
|
||||
// Controls settings-page visibility only. It does not gate the param's runtime behavior.
|
||||
ParamSettingsTier settings_tier = SETTINGS_ADVANCED;
|
||||
};
|
||||
|
||||
class Params {
|
||||
@@ -112,6 +120,8 @@ public:
|
||||
|
||||
int getTuningLevel(const std::string &key);
|
||||
|
||||
ParamSettingsTier getSettingsTier(const std::string &key);
|
||||
|
||||
std::optional<std::string> getStockValue(const std::string &key);
|
||||
|
||||
private:
|
||||
|
||||
@@ -4,6 +4,27 @@ from enum import IntEnum, IntFlag
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
|
||||
SETTINGS_SIMPLE = 0
|
||||
SETTINGS_ADVANCED = 1
|
||||
|
||||
|
||||
def _load_settings_tiers() -> dict[str, int]:
|
||||
params_keys = Path(__file__).with_name("params_keys.h")
|
||||
if not params_keys.exists():
|
||||
return {}
|
||||
|
||||
tiers = {}
|
||||
for line in params_keys.read_text(encoding="utf-8", errors="ignore").splitlines():
|
||||
if not line.lstrip().startswith('{"'):
|
||||
continue
|
||||
parts = line.split('"')
|
||||
if len(parts) >= 2:
|
||||
tiers[parts[1]] = SETTINGS_SIMPLE if "SETTINGS_SIMPLE" in line else SETTINGS_ADVANCED
|
||||
return tiers
|
||||
|
||||
|
||||
_SETTINGS_TIERS = _load_settings_tiers()
|
||||
|
||||
try:
|
||||
from openpilot.common.params_pyx import Params as _Params, ParamKeyFlag, ParamKeyType, UnknownKeyName
|
||||
except Exception:
|
||||
@@ -180,6 +201,9 @@ except Exception:
|
||||
def get_tuning_level(self, key):
|
||||
return 0
|
||||
|
||||
def get_settings_tier(self, key):
|
||||
return _SETTINGS_TIERS.get(self.check_key(key), SETTINGS_ADVANCED)
|
||||
|
||||
else:
|
||||
assert _Params
|
||||
assert ParamKeyFlag
|
||||
@@ -187,6 +211,12 @@ else:
|
||||
assert UnknownKeyName
|
||||
|
||||
class Params(_Params):
|
||||
def get_settings_tier(self, key):
|
||||
try:
|
||||
return super().get_settings_tier(key)
|
||||
except AttributeError:
|
||||
return _SETTINGS_TIERS.get(self.check_key(key), SETTINGS_ADVANCED)
|
||||
|
||||
def get(self, key, block=False, return_default=False, encoding=None, default=None):
|
||||
try:
|
||||
value = super().get(key, block=block, return_default=return_default)
|
||||
|
||||
+253
-203
@@ -8,7 +8,7 @@
|
||||
inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"AccessToken", {CLEAR_ON_MANAGER_START | DONT_LOG, STRING}},
|
||||
{"AdbEnabled", {PERSISTENT, BOOL}},
|
||||
{"AlwaysAllowUploads", {PERSISTENT, BOOL, "0"}},
|
||||
{"AlwaysAllowUploads", {PERSISTENT, BOOL, "0", std::nullopt, 0, SETTINGS_SIMPLE}},
|
||||
{"AlwaysOnDM", {PERSISTENT, BOOL}},
|
||||
{"ApiCache_Device", {PERSISTENT, STRING}},
|
||||
{"AssistNowToken", {PERSISTENT, STRING}},
|
||||
@@ -36,13 +36,17 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"DoReboot", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"DoShutdown", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"DoUninstall", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"DoUserReboot", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"DriverTooDistracted", {CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON, BOOL}},
|
||||
{"DriverLockoutCount", {CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON, INT, "0"}},
|
||||
{"EcuDisableFailed", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"AlphaLongitudinalEnabled", {PERSISTENT, BOOL}},
|
||||
{"ExperimentalLongitudinalEnabled", {PERSISTENT, BOOL}},
|
||||
{"ExperimentalMode", {PERSISTENT, BOOL}},
|
||||
{"ExperimentalModeConfirmed", {PERSISTENT, BOOL}},
|
||||
{"PersistExperimentalState", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"PersistChillState", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"PersistExperimentalState", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"PersistedCCStatus", {PERSISTENT, INT, "0", "0"}},
|
||||
{"PersistedCEStatus", {PERSISTENT, INT, "0", "0"}},
|
||||
{"FirmwareQueryDone", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
||||
{"ForcePowerDown", {PERSISTENT, BOOL}},
|
||||
@@ -54,11 +58,13 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"GithubUsername", {PERSISTENT, STRING}},
|
||||
{"GitRemote", {PERSISTENT, STRING}},
|
||||
{"GsmApn", {PERSISTENT, STRING}},
|
||||
{"GsmMetered", {PERSISTENT, BOOL, "1"}},
|
||||
{"GsmMetered", {PERSISTENT, BOOL, "1", std::nullopt, 0, SETTINGS_SIMPLE}},
|
||||
{"GsmRoaming", {PERSISTENT, BOOL}},
|
||||
{"HardwareSerial", {PERSISTENT, STRING}},
|
||||
{"HasAcceptedTerms", {PERSISTENT, STRING, "0"}},
|
||||
{"HondaGasFactorParams", {PERSISTENT, FLOAT}},
|
||||
{"HondaLateralPidKiScale", {PERSISTENT, FLOAT, "1.0", "1.0", 3}},
|
||||
{"HondaLateralPidKpScale", {PERSISTENT, FLOAT, "1.0", "1.0", 3}},
|
||||
{"HondaWindFactorParams", {PERSISTENT, FLOAT}},
|
||||
{"InstallDate", {PERSISTENT, TIME}},
|
||||
{"IsDriverViewEnabled", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
@@ -67,7 +73,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"IsMetric", {PERSISTENT, BOOL}},
|
||||
{"IsOffroad", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"IsOnroad", {PERSISTENT, BOOL}},
|
||||
{"IsRHD", {PERSISTENT, BOOL}},
|
||||
{"IsRHD", {PERSISTENT, BOOL, std::nullopt, std::nullopt, 0, SETTINGS_SIMPLE}},
|
||||
{"IsRhdDetected", {PERSISTENT, BOOL}},
|
||||
{"IsRHDOverride", {PERSISTENT, BOOL}},
|
||||
{"IsReleaseBranch", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
@@ -114,6 +120,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"PandaSomResetTriggered", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||
{"PandaSignatures", {CLEAR_ON_MANAGER_START, BYTES}},
|
||||
{"PrimeType", {PERSISTENT, INT}},
|
||||
{"PriusClusterOffsetMigrated", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"RecordAudio", {PERSISTENT, BOOL}},
|
||||
{"RecordAudioFeedback", {PERSISTENT, BOOL, "0"}},
|
||||
{"RecordFront", {PERSISTENT, BOOL}},
|
||||
@@ -121,6 +128,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"SecOCKey", {PERSISTENT | DONT_LOG, STRING}},
|
||||
{"ShowDebugInfo", {PERSISTENT, BOOL}},
|
||||
{"ShowAllToggles", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"TryRaylibUI", {PERSISTENT, BOOL, "1"}},
|
||||
{"UseOldUI", {PERSISTENT, BOOL, "0", std::nullopt, 0, SETTINGS_SIMPLE}},
|
||||
{"UsePrebuilt", {PERSISTENT, BOOL, "1"}},
|
||||
{"RouteCount", {PERSISTENT, INT, "0"}},
|
||||
{"SnoozeUpdate", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||
@@ -141,15 +150,18 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"UpdaterLastFetchTime", {PERSISTENT, TIME}},
|
||||
{"UptimeOffroad", {PERSISTENT, FLOAT, "0.0"}},
|
||||
{"UptimeOnroad", {PERSISTENT, FLOAT, "0.0"}},
|
||||
{"UsbGpuActive", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||
{"UsbGpuCompiled", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||
{"UsbGpuPresent", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||
{"Version", {PERSISTENT, STRING}},
|
||||
|
||||
// StarPilot variables
|
||||
{"AccelerationPath", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"AccelerationProfile", {PERSISTENT, INT, "0", "0", 0}},
|
||||
{"AccelerationPath", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"AccelerationProfile", {PERSISTENT, INT, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"AdjacentLeadsUI", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"AdjacentPath", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"AdjacentPath", {PERSISTENT, BOOL, "0", "0", 3, SETTINGS_SIMPLE}},
|
||||
{"AdjacentPathMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"AdvancedCustomUI", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"AdvancedCustomUI", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"AdvancedLateralTune", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"AdvancedLongitudinalTune", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"AggressiveFollow", {PERSISTENT, FLOAT, "1.25", "1.25", 2}},
|
||||
@@ -159,8 +171,9 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"AggressiveJerkDeceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"AggressiveJerkSpeed", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"AggressiveJerkSpeedDecrease", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"AlertVolumeControl", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"AlwaysOnLateral", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"AlertVolumeControl", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"AllowImpossibleAcceleration", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"AlwaysOnLateral", {PERSISTENT, BOOL, "1", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"AlwaysOnLateralLKAS", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"ApiCache_DriveStats", {PERSISTENT, JSON, "{}", "{}"}},
|
||||
{"AutomaticallyDownloadModels", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
@@ -168,46 +181,55 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"AvailableModelNames", {PERSISTENT, STRING, "", "", 1}},
|
||||
{"AvailableModelSeries", {PERSISTENT, STRING, "", "", 1}},
|
||||
{"AvailableModels", {PERSISTENT, STRING, "", "", 1}},
|
||||
{"AvailableModelArtifactFormats", {PERSISTENT, STRING, "", "", 1}},
|
||||
{"BlacklistedModels", {PERSISTENT, STRING, "", "", 2}},
|
||||
{"BootLogo", {PERSISTENT, STRING, "starpilot", "stock", 0}},
|
||||
{"BuildMetadata", {PERSISTENT, STRING, "", "", 0}},
|
||||
{"BlindSpotMetrics", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"BlindSpotPath", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"BelowSteerSpeedVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"BlindSpotPath", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"BelowSteerSpeedVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"BorderMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"BorderWidth", {PERSISTENT, FLOAT, "100.0", "100.0", 2}},
|
||||
{"BorderWidth", {PERSISTENT, FLOAT, "100.0", "100.0", 2, SETTINGS_SIMPLE}},
|
||||
{"CalibratedLateralAcceleration", {PERSISTENT, FLOAT, "2.0", "2.0", 2}},
|
||||
{"CalibrationProgress", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"CameraView", {PERSISTENT, INT, "3", "0", 2}},
|
||||
{"CameraOffset", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"CameraView", {PERSISTENT, INT, "3", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"CancelDownloadMaps", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"DisableWideRoad", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"CancelModelDownload", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"CancelThemeDownload", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"CarMake", {PERSISTENT, STRING, "mock", "mock", 0}},
|
||||
{"CarModel", {PERSISTENT, STRING, "MOCK", "MOCK", 0}},
|
||||
{"CarMake", {PERSISTENT, STRING, "mock", "mock", 0, SETTINGS_SIMPLE}},
|
||||
{"CarModel", {PERSISTENT, STRING, "MOCK", "MOCK", 0, SETTINGS_SIMPLE}},
|
||||
{"CarModelName", {PERSISTENT, STRING, "", "", 0}},
|
||||
{"CECurves", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"CECurves", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"CECurvesLead", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"CELead", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"CEModelStopTime", {PERSISTENT, FLOAT, "7.0", "0.0", 2}},
|
||||
{"CELead", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"CEModelStopTime", {PERSISTENT, FLOAT, "7.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"CESignalLaneDetection", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"CESignalSpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"CESlowerLead", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"CESpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
|
||||
{"CESpeedLead", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
|
||||
{"CESignalSpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"CESlowerLead", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"CESpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 1, SETTINGS_SIMPLE}},
|
||||
{"CESpeedLead", {PERSISTENT, FLOAT, "0.0", "0.0", 1, SETTINGS_SIMPLE}},
|
||||
{"CCMLead", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"CCMLaunchAssist", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"CCMSetSpeedMargin", {PERSISTENT, FLOAT, "3.0", "0.0", 1}},
|
||||
{"CCMSpeed", {PERSISTENT, FLOAT, "45.0", "0.0", 1}},
|
||||
{"CCMSpeedLead", {PERSISTENT, FLOAT, "35.0", "0.0", 1}},
|
||||
{"CCStatus", {CLEAR_ON_OFFROAD_TRANSITION, INT, "0", "0"}},
|
||||
{"CEStatus", {CLEAR_ON_OFFROAD_TRANSITION, INT, "0", "0"}},
|
||||
{"CEStopLights", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"CEStoppedLead", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"ClusterOffset", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
|
||||
{"ColorScheme", {PERSISTENT, STRING, "frog", "stock", 0}},
|
||||
{"CEStopLights", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"CEStoppedLead", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"ClusterOffset", {PERSISTENT, FLOAT, "1.0", "1.0", 2, SETTINGS_SIMPLE}},
|
||||
{"ColorScheme", {PERSISTENT, STRING, "stock", "stock", 0}},
|
||||
{"ColorToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"BootLogoToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"Compass", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"Compass", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"CommunityFavorites", {PERSISTENT, STRING, "", "", 1}},
|
||||
{"ConditionalExperimental", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"ConditionalChill", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"ConditionalExperimental", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"CurvatureData", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
|
||||
{"CurveSpeedController", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"CustomAlerts", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"CurveSpeedController", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"CustomAlerts", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"CustomAccelProfile", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"CustomAccelProfileInitialized", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"CustomAccelProfile0MPH", {PERSISTENT, FLOAT, "3.0", "3.0", 3}},
|
||||
@@ -217,20 +239,20 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"CustomAccelProfile45MPH", {PERSISTENT, FLOAT, "1.0", "1.0", 3}},
|
||||
{"CustomAccelProfile56MPH", {PERSISTENT, FLOAT, "0.8", "0.8", 3}},
|
||||
{"CustomAccelProfile89MPH", {PERSISTENT, FLOAT, "0.6", "0.6", 3}},
|
||||
{"CustomCruise", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
|
||||
{"CustomCruiseLong", {PERSISTENT, FLOAT, "5.0", "5.0", 2}},
|
||||
{"CustomCruise", {PERSISTENT, FLOAT, "1.0", "1.0", 2, SETTINGS_SIMPLE}},
|
||||
{"CustomCruiseLong", {PERSISTENT, FLOAT, "5.0", "5.0", 2, SETTINGS_SIMPLE}},
|
||||
{"CustomPersonalities", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"CancelButtonControl", {PERSISTENT, INT, "1", "0", 2}},
|
||||
{"CancelButtonControl", {PERSISTENT, INT, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"CancelButtonControlsMigrated", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"AOLLKASMigratedToButtonControl", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"TrafficPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
{"AggressivePersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
{"StandardPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
{"RelaxedPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
{"CustomThemes", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"CustomUI", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"CustomThemes", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"CustomUI", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"DebugMode", {CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0", "0"}},
|
||||
{"DecelerationProfile", {PERSISTENT, INT, "1", "0", 2}},
|
||||
{"DecelerationProfile", {PERSISTENT, INT, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"DeveloperMetrics", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"DeveloperSidebar", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"DeveloperSidebarMetric1", {PERSISTENT, INT, "1", "0", 3}},
|
||||
@@ -241,14 +263,15 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"DeveloperSidebarMetric6", {PERSISTENT, INT, "6", "0", 3}},
|
||||
{"DeveloperSidebarMetric7", {PERSISTENT, INT, "7", "0", 3}},
|
||||
{"DeveloperUI", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"GalaxyDeveloperMode", {PERSISTENT | DONT_LOG, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"DeveloperWidgets", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"DeviceManagement", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"DeviceShutdown", {PERSISTENT, INT, "9", "33", 1}},
|
||||
{"DisableOnroadUploads", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"DisableOpenpilotLongitudinal", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"DeviceManagement", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"DeviceShutdown", {PERSISTENT, INT, "9", "33", 1, SETTINGS_SIMPLE}},
|
||||
{"DisableOnroadUploads", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"DisableOpenpilotLongitudinal", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"DiscordUsername", {PERSISTENT, STRING, "", "", 0}},
|
||||
{"DisengageVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"DistanceButtonControl", {PERSISTENT, INT, "1", "0", 2}},
|
||||
{"DisengageVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"DistanceButtonControl", {PERSISTENT, INT, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"DistanceIconPack", {PERSISTENT, STRING, "stock", "stock", 0}},
|
||||
{"DistanceIconToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"DownloadableBootLogos", {PERSISTENT, STRING, "", ""}},
|
||||
@@ -260,130 +283,141 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"DownloadableWheels", {PERSISTENT, STRING, "", ""}},
|
||||
{"DownloadAllModels", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"DownloadMaps", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"DriverCamera", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"DriverCamera", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"Model", {PERSISTENT, STRING, "sc2", "sc2", 1}},
|
||||
{"ModelVersion", {PERSISTENT, STRING, "v11", "v11", 1}},
|
||||
{"DrivingModel", {PERSISTENT, STRING, "sc2", "sc2", 1}},
|
||||
{"DrivingModelName", {PERSISTENT, STRING, "South Carolina", "South Carolina", 1}},
|
||||
{"DrivingModelVersion", {PERSISTENT, STRING, "v11", "v11", 1}},
|
||||
{"DynamicPathWidth", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"DynamicPedalsOnUI", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"EngageVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"DynamicPathWidth", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"DynamicPedalsOnUI", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"EngageVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"EVTuning", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"Fahrenheit", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"FlashPanda", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"GMDashSpoofOffsets", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"GMPedalLongitudinal", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
{"LongPitch", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"RemoteStartBootsComma", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"RemapCancelToDistance", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"NAPAdaptiveAccel", {PERSISTENT, BOOL, "1", "1"}},
|
||||
{"GMDashSpoofOffsets", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"GMPedalLongitudinal", {PERSISTENT, BOOL, "1", "1", 2, SETTINGS_SIMPLE}},
|
||||
{"HKGRemoteStartBootsComma", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"IgnoreIgnitionLine", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"LongPitch", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"RemoteStartBootsComma", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"RemapCancelToDistance", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"NAPAdaptiveAccel", {PERSISTENT, BOOL, "1", "1", 0, SETTINGS_SIMPLE}},
|
||||
{"NAPFollowDistance", {PERSISTENT, INT, "4", "4"}},
|
||||
{"NAPForcePreAP", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"NAPPedalEnabled", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"NAPPedalCanBus", {PERSISTENT, INT, "2", "2"}},
|
||||
{"NAPPedalCalibDone", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"NAPPedalEnabled", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"NAPPedalCanBus", {PERSISTENT, INT, "2", "2", 0, SETTINGS_SIMPLE}},
|
||||
{"NAPPedalCalibDone", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"NAPPedalCalibMin", {PERSISTENT, FLOAT, "-3.0", "-3.0"}},
|
||||
{"NAPPedalCalibMax", {PERSISTENT, FLOAT, "99.6", "99.6"}},
|
||||
{"NAPPedalCalibFactor", {PERSISTENT, FLOAT, "1.0", "1.0"}},
|
||||
{"NAPPedalCalibZero", {PERSISTENT, FLOAT, "0.0", "0.0"}},
|
||||
{"NAPPedalCalibFactor", {PERSISTENT, FLOAT, "1.0", "1.0", 0, SETTINGS_SIMPLE}},
|
||||
{"NAPPedalCalibZero", {PERSISTENT, FLOAT, "0.0", "0.0", 0, SETTINGS_SIMPLE}},
|
||||
{"NAPPedalProfile", {PERSISTENT, INT, "4", "4"}},
|
||||
{"NAPRadarBehindNosecone", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"NAPRadarEnabled", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"NAPRadarOffset", {PERSISTENT, FLOAT, "0.0", "0.0"}},
|
||||
{"NAPRadarBehindNosecone", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"NAPRadarEnabled", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"NAPRadarOffset", {PERSISTENT, FLOAT, "0.0", "0.0", 0, SETTINGS_SIMPLE}},
|
||||
{"ForceAutoTune", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"ForceAutoTuneOff", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"ForceFingerprint", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"ForceFingerprint", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ForceOffroad", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"ForceOnroad", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"ForceStops", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"ForceStopDistanceOffset", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"ForceStandstill", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"ForceStops", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ForceStopDistanceOffset", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ForceStandstill", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ForceTorqueController", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"FPSCounter", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"FLMActiveOverrides", {PERSISTENT, JSON, "{}", "{}", 2}},
|
||||
{"FLMActiveProfileId", {PERSISTENT, STRING, "", "", 2}},
|
||||
{"FLMTrialBaseline", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
|
||||
{"FLMTrialApplied", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"FPSCounter", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"GalaxyDashboardStats", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
|
||||
{"StarPilotApiToken", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
|
||||
{"StarPilotCarParams", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BYTES, "", ""}},
|
||||
{"StarPilotCarParamsPersistent", {PERSISTENT, BYTES, "", ""}},
|
||||
{"StarPilotDongleId", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
|
||||
{"StarPilotFavoriteSlots", {PERSISTENT, JSON, "[]", "[]", 1}},
|
||||
{"StarPilotStats", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
|
||||
{"StarPilotTogglesUpdated", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"FrogsGoMoosTweak", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"GoatScream", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"GoatScreamCriticalAlerts", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"GreenLightAlert", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"HideAlerts", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideChangingLanesBanner", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideDistanceProfileBanner", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideTurningBanner", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideDMIcon", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"GoatScream", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"GoatScreamCriticalAlerts", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"GreenLightAlert", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"HideAlerts", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HideChangingLanesBanner", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HideDistanceProfileBanner", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HideTurningBanner", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HideDMIcon", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HideLeadMarker", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideMaxSpeed", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideSpeed", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideSpeedLimit", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HigherBitrate", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HolidayThemes", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"HumanAcceleration", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"CoastUpToLeads", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
{"HumanLaneChanges", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"IconPack", {PERSISTENT, STRING, "frog-animated", "stock", 0}},
|
||||
{"HideMaxSpeed", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HideSpeed", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HideSpeedLimit", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HideSteeringWheel", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HigherBitrate", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HolidayThemes", {PERSISTENT, BOOL, "1", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"HumanLaneChanges", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"IconPack", {PERSISTENT, STRING, "stock", "stock", 0}},
|
||||
{"IconToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"IncreasedStoppedDistance", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
|
||||
{"IncreasedStoppedDistanceLowVisibility", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"IncreasedStoppedDistanceRain", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"IncreasedStoppedDistanceRainStorm", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"IncreasedStoppedDistanceSnow", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"RedneckCruise", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"IncreaseFollowingLowVisibility", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"IncreaseFollowingRain", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"IncreaseFollowingRainStorm", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"IncreaseFollowingSnow", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"IncreaseThermalLimits", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"IncreasedStoppedDistance", {PERSISTENT, FLOAT, "0.0", "0.0", 1, SETTINGS_SIMPLE}},
|
||||
{"IncreasedStoppedDistanceLowVisibility", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"IncreasedStoppedDistanceRain", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"IncreasedStoppedDistanceRainStorm", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"IncreasedStoppedDistanceSnow", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"RedneckCruise", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"IncreaseFollowingLowVisibility", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"IncreaseFollowingRain", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"IncreaseFollowingRainStorm", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"IncreaseFollowingSnow", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"IncreaseThermalLimits", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"IssueReported", {CLEAR_ON_MANAGER_START, JSON, "{}", "{}"}},
|
||||
{"KonikDongleId", {PERSISTENT, STRING, "", "", 0}},
|
||||
{"KonikMinutes", {PERSISTENT, INT, "0", "0", 0}},
|
||||
{"LaneChanges", {PERSISTENT, BOOL, "1", "1", 0}},
|
||||
{"LaneChangeSmoothing", {PERSISTENT, INT, "10", "10", 1}},
|
||||
{"LaneChangeTime", {PERSISTENT, FLOAT, "1.0", "0.0", 1}},
|
||||
{"LaneDetectionWidth", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
|
||||
{"LaneLinesColor", {PERSISTENT, STRING, "", "", 2}},
|
||||
{"LaneLinesWidth", {PERSISTENT, FLOAT, "4.0", "2.0", 2}},
|
||||
{"LaneChanges", {PERSISTENT, BOOL, "1", "1", 0, SETTINGS_SIMPLE}},
|
||||
{"LaneChangeSmoothing", {PERSISTENT, INT, "5", "10", 1, SETTINGS_SIMPLE}},
|
||||
{"LaneChangeTime", {PERSISTENT, FLOAT, "1.0", "0.0", 1, SETTINGS_SIMPLE}},
|
||||
{"LaneDetectionWidth", {PERSISTENT, FLOAT, "0.0", "0.0", 1, SETTINGS_SIMPLE}},
|
||||
{"LaneLinesColor", {PERSISTENT, STRING, "", "", 2, SETTINGS_SIMPLE}},
|
||||
{"LaneLinesWidth", {PERSISTENT, FLOAT, "4.0", "2.0", 2, SETTINGS_SIMPLE}},
|
||||
{"LastMapsUpdate", {PERSISTENT, STRING, "", ""}},
|
||||
{"LateralTune", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"LeadDepartingAlert", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"LeadDepartingAlert", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"LeadDetectionThreshold", {PERSISTENT, INT, "35", "50", 3}},
|
||||
{"LeadIndicator", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
{"LeadIndicator", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"LeadInfo", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"LKASButtonControl", {PERSISTENT, INT, "5", "0", 2}},
|
||||
{"LKASButtonControl", {PERSISTENT, INT, "5", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"LockDoors", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"LockDoorsTimer", {PERSISTENT, INT, "0", "0", 0}},
|
||||
{"LongCancelButtonControl", {PERSISTENT, INT, "5", "0", 2}},
|
||||
{"LongDistanceButtonControl", {PERSISTENT, INT, "5", "0", 2}},
|
||||
{"LongModeButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"LongStarButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"LongCancelButtonControl", {PERSISTENT, INT, "5", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"LongDistanceButtonControl", {PERSISTENT, INT, "5", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"LongModeButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"LongStarButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"LongitudinalActuatorDelay", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"LongitudinalActuatorDelayStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"LateralManeuverStatus", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, JSON, "{}", "{}"}},
|
||||
{"LongitudinalManeuverPaddleMode", {PERSISTENT, STRING, "auto", "auto"}},
|
||||
{"LongitudinalManeuverStatus", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, JSON, "{}", "{}"}},
|
||||
{"LongitudinalTune", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"LoudBlindspotAlert", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"LowVoltageShutdown", {PERSISTENT, FLOAT, "11.8", "11.8", 3}},
|
||||
{"MainCruiseButtonControl", {PERSISTENT, INT, "9", "9", 2}},
|
||||
{"LongitudinalTune", {PERSISTENT, BOOL, "1", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"LoudBlindspotAlert", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"LoudBlindspotAlertWhenDisengaged", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"LowVoltageShutdown", {PERSISTENT, FLOAT, "11.8", "11.8", 3, SETTINGS_SIMPLE}},
|
||||
{"MainCruiseButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ManualUpdateInitiated", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"AMapKey1", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
|
||||
{"AMapKey2", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
|
||||
{"ApiCache_NavDestinations", {PERSISTENT, JSON, "[]", "[]"}},
|
||||
{"FavoriteDestinations", {PERSISTENT, JSON, "[]", "[]"}},
|
||||
{"MapAcceleration", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"MapboxPublicKey", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
|
||||
{"MapBoxRequests", {PERSISTENT, JSON, "{}", "{}"}},
|
||||
{"MapboxSecretKey", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
|
||||
{"MapDeceleration", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"MapdSettings", {PERSISTENT, JSON, "{}", "{}"}},
|
||||
{"MapGears", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"MapGears", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"MapsSelected", {PERSISTENT, STRING, "", "", 0}},
|
||||
{"MapSpeedLimit", {CLEAR_ON_MANAGER_START, FLOAT, "0.0", "0.0"}},
|
||||
{"NavDesiresAllowed", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"NavLongitudinalAllowed", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"ClearNavOnOffroad", {PERSISTENT, BOOL, "1", "1", 2, SETTINGS_SIMPLE}},
|
||||
{"ClearNavOnOffroadTimeoutMinutes", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"NavDestination", {PERSISTENT | CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"NavInstructionCollapsed", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0", "0"}},
|
||||
{"NavInstructionState", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, JSON, "{}", "{}"}},
|
||||
@@ -395,27 +429,31 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"VisionSpeedLimitLastEvent", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"VisionSpeedLimitStatus", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"VisionSpeedLimitStream", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"VisionSpeedLimitSupportCount", {CLEAR_ON_MANAGER_START, INT, "0", "0"}},
|
||||
{"VisionSpeedLimitSupportSpeed", {CLEAR_ON_MANAGER_START, FLOAT, "0.0", "0.0"}},
|
||||
{"MaxDesiredAcceleration", {PERSISTENT, FLOAT, "4.0", "2.0", 2}},
|
||||
{"MinimumBackupSize", {PERSISTENT, INT, "0", "0"}},
|
||||
{"MinimumLaneChangeSpeed", {PERSISTENT, FLOAT, "20.0", "20.0", 2}},
|
||||
{"ModeButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"MinimumLaneChangeSpeed", {PERSISTENT, FLOAT, "20.0", "20.0", 2, SETTINGS_SIMPLE}},
|
||||
{"ModeButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ModelDownloadProgress", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"ModelDrivesAndScores", {PERSISTENT, JSON, "{}", "{}"}},
|
||||
{"ModelReleasedDates", {PERSISTENT, STRING, "", "", 1}},
|
||||
{"ModelRandomizer", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"ModelSortMode", {PERSISTENT, STRING, "alphabetical", "alphabetical", 1}},
|
||||
{"ModelToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"ModelUI", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"ModelUI", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ModelVersions", {PERSISTENT, STRING, "", "", 1}},
|
||||
{"NavigationUI", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"ModelManifestVersion", {PERSISTENT, STRING, "", "", 1}},
|
||||
{"NavigationUI", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"NNFF", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"NNFFLite", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"NostalgiaMode", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"NostalgiaMode", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"NNFFModelName", {CLEAR_ON_MANAGER_START, STRING, "", "", 0}},
|
||||
{"NoLogging", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"NoUploads", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"NudgelessLaneChange", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"NumericalTemp", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"NoLogging", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"NoUploads", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"NudgelessLaneChange", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"NudgelessLaneChangeOnlyWhenEngaged", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"NumericalTemp", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"Offset1", {PERSISTENT, FLOAT, "5.0", "0.0", 0}},
|
||||
{"Offset2", {PERSISTENT, FLOAT, "5.0", "0.0", 0}},
|
||||
{"Offset3", {PERSISTENT, FLOAT, "5.0", "0.0", 0}},
|
||||
@@ -423,45 +461,47 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"Offset5", {PERSISTENT, FLOAT, "10.0", "0.0", 0}},
|
||||
{"Offset6", {PERSISTENT, FLOAT, "10.0", "0.0", 0}},
|
||||
{"Offset7", {PERSISTENT, FLOAT, "10.0", "0.0", 0}},
|
||||
{"OneLaneChange", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"OnroadDistanceButton", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"OneLaneChange", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"OnroadDistanceButton", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"OnroadDistanceButtonPressed", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"FavoriteVirtualAccelCruiseCounter", {CLEAR_ON_MANAGER_START, INT, "0", "0"}},
|
||||
{"FavoriteVirtualDecelCruiseCounter", {CLEAR_ON_MANAGER_START, INT, "0", "0"}},
|
||||
{"WheelButtonBookmarkCounter", {CLEAR_ON_MANAGER_START, INT, "0", "0"}},
|
||||
{"openpilotMinutes", {PERSISTENT, INT, "0", "0", 0}},
|
||||
{"OverpassRequests", {PERSISTENT, JSON, "{}", "{}"}},
|
||||
{"PathColor", {PERSISTENT, STRING, "", "", 2}},
|
||||
{"PathEdgesColor", {PERSISTENT, STRING, "", "", 2}},
|
||||
{"PathEdgeWidth", {PERSISTENT, FLOAT, "20.0", "0.0", 2}},
|
||||
{"PathWidth", {PERSISTENT, FLOAT, "6.1", "5.9", 2}},
|
||||
{"PauseAOLOnBrake", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"PauseLateralOnSignal", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"PauseLateralSpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
|
||||
{"LateralResumeDelay", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
|
||||
{"PedalsOnUI", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"PondPaired", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"PondUploadPending", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"PathColor", {PERSISTENT, STRING, "", "", 2, SETTINGS_SIMPLE}},
|
||||
{"PathEdgesColor", {PERSISTENT, STRING, "", "", 2, SETTINGS_SIMPLE}},
|
||||
{"PathEdgeWidth", {PERSISTENT, FLOAT, "20.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"PathWidth", {PERSISTENT, FLOAT, "6.1", "5.9", 2, SETTINGS_SIMPLE}},
|
||||
{"PauseAOLOnBrake", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"PauseLateralOnSignal", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"PauseLateralSpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 1, SETTINGS_SIMPLE}},
|
||||
{"LateralResumeDelay", {PERSISTENT, FLOAT, "0.0", "0.0", 1, SETTINGS_SIMPLE}},
|
||||
{"PedalsOnUI", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"GalaxyPaired", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"GalaxyUploadPending", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"PreferredSchedule", {PERSISTENT, INT, "2", "0", 0}},
|
||||
{"PreviousSpeedLimit", {PERSISTENT, FLOAT, "0.0", "0.0"}},
|
||||
{"PromptDistractedVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"PromptVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"QOLLateral", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"QOLLongitudinal", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"QOLVisuals", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"RadarTakeoffs", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"PromptDistractedVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"PromptVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"QOLLateral", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"QOLLongitudinal", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"QOLVisuals", {PERSISTENT, BOOL, "1", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"RadarTakeoffs", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"RadarTracksUI", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"RainbowPath", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"RainbowPath", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"RandomEvents", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"RandomThemes", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"RandomThemesHolidays", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"ReduceAccelerationLowVisibility", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"ReduceAccelerationRain", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"ReduceAccelerationRainStorm", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"ReduceAccelerationSnow", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"ReduceLateralAccelerationLowVisibility", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"ReduceLateralAccelerationRain", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"ReduceLateralAccelerationRainStorm", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"ReduceLateralAccelerationSnow", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"RefuseVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"ReduceAccelerationLowVisibility", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ReduceAccelerationRain", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ReduceAccelerationRainStorm", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ReduceAccelerationSnow", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ReduceLateralAccelerationLowVisibility", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ReduceLateralAccelerationRain", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ReduceLateralAccelerationRainStorm", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ReduceLateralAccelerationSnow", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"RefuseVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"RelaxedFollow", {PERSISTENT, FLOAT, "1.6", "1.6", 2}},
|
||||
{"RelaxedFollowHigh", {PERSISTENT, FLOAT, "1.4", "1.4", 2}},
|
||||
{"RelaxedJerkAcceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
@@ -471,41 +511,42 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"RelaxedJerkSpeedDecrease", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"ReverseCruise", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"RecoveryPower", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
|
||||
{"RoadEdgesWidth", {PERSISTENT, FLOAT, "2.0", "2.0", 2}},
|
||||
{"RoadNameUI", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"RotatingWheel", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"ScreenBrightness", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"ScreenBrightnessOnroad", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"ScreenManagement", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"ScreenRecorder", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"ScreenTimeout", {PERSISTENT, INT, "30", "30", 2}},
|
||||
{"ScreenTimeoutOnroad", {PERSISTENT, INT, "30", "10", 2}},
|
||||
{"RoadEdgesWidth", {PERSISTENT, FLOAT, "2.0", "2.0", 2, SETTINGS_SIMPLE}},
|
||||
{"RoadNameUI", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"RotatingWheel", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"ScreenBrightness", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"ScreenBrightnessOnroad", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"ScreenManagement", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"ScreenRecorder", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ScreenTimeout", {PERSISTENT, INT, "30", "30", 2, SETTINGS_SIMPLE}},
|
||||
{"ScreenTimeoutOnroad", {PERSISTENT, INT, "30", "10", 2, SETTINGS_SIMPLE}},
|
||||
{"SecOCKeys", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
|
||||
{"SafeMode", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"SafeModeBackup", {PERSISTENT, JSON, "{}", "{}"}},
|
||||
{"SetSpeedLimit", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"SetSpeedOffset", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"ShowCEMStatus", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"ShowCPU", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"ShowCSCStatus", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"SetSpeedOffset", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"ShowCEMStatus", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ShowCCMStatus", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"ShowCPU", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"ShowCSCStatus", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ShowGPU", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"ShowIP", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"ShowMemoryUsage", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"ShowModeStatusBanner", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"ShowMemoryUsage", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"ShowModeStatusBanner", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ShownToggleDescriptions", {PERSISTENT, JSON, "{}", "{}"}},
|
||||
{"ShowSLCOffset", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"ShowSpeedLimits", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"ShowSpeedLimits", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"ShowSteering", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"ShowStoppingPoint", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"ShowStoppingPointMetrics", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"ShowStorageLeft", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"ShowStorageUsed", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"SidebarMetrics", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"SidebarMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"SidebarOpen", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"SignalAnimation", {PERSISTENT, STRING, "frog", "stock", 0}},
|
||||
{"SignalAnimation", {PERSISTENT, STRING, "stock", "stock", 0}},
|
||||
{"SignalMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"SignalToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"SimpleMode", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"SimpleMode", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"SLCAbbreviatedSources", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"SLCActiveSourcesOnly", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"SLCConfirmation", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
@@ -514,18 +555,18 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"SLCFallback", {PERSISTENT, INT, "2", "0", 1}},
|
||||
{"SLCLookaheadHigher", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"SLCLookaheadLower", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"SLCMapboxFiller", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"SLCMapboxFiller", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"SLCOverride", {PERSISTENT, INT, "1", "0", 1}},
|
||||
{"SLCPriority", {PERSISTENT, STRING, "", "", 2}},
|
||||
{"SLCPriority1", {PERSISTENT, STRING, "Map Data", "Map Data", 2}},
|
||||
{"SLCPriority2", {PERSISTENT, STRING, "Dashboard", "Dashboard", 2}},
|
||||
{"SNGHack", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"SoundPack", {PERSISTENT, STRING, "frog", "stock", 0}},
|
||||
{"SLCPriority1", {PERSISTENT, STRING, "Vision", "Map Data", 2}},
|
||||
{"SLCPriority2", {PERSISTENT, STRING, "Map Data", "Dashboard", 2}},
|
||||
{"SNGHack", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"SoundPack", {PERSISTENT, STRING, "stock", "stock", 0}},
|
||||
{"SoundToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"SLCAdoptSpeedLimit", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"SLCForceCruiseSpeed", {CLEAR_ON_MANAGER_START, FLOAT, "0.0", "0.0"}},
|
||||
{"SpeedLimitAccepted", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"SpeedLimitChangedAlert", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"SpeedLimitChangedAlert", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"SpeedLimitController", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"SpeedLimitFiller", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"SpeedLimits", {PERSISTENT | DONT_LOG, JSON, "[]", "[]"}},
|
||||
@@ -533,7 +574,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"SpeedLimitSources", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"VisionSpeedLimitAutoBookmark", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"VisionSpeedLimitAutoPreserveSegment", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"VisionSpeedLimitDetection", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"VisionSpeedLimitDetection", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"VisionSpeedLimitTrainingCollector", {PERSISTENT, BOOL, "1", "1", 0}},
|
||||
{"StandardFollow", {PERSISTENT, FLOAT, "1.45", "1.45", 2}},
|
||||
{"StandardFollowHigh", {PERSISTENT, FLOAT, "1.2", "1.2", 2}},
|
||||
@@ -542,13 +583,14 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"StandardJerkDeceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"StandardJerkSpeed", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"StandardJerkSpeedDecrease", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"StandbyMode", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"StandbyMode", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"StartAccel", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"StartAccelStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"StartupMessageBottom", {PERSISTENT, STRING, "Human-tested, frog-approved 🐸", "Always keep hands on wheel and eyes on road", 0}},
|
||||
{"StartupMessageTop", {PERSISTENT, STRING, "Hop in and buckle up!", "Be ready to take over at any time", 0}},
|
||||
{"StaticPedalsOnUI", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"StartupMessageBottom", {PERSISTENT, STRING, "Always keep hands on wheel and eyes on road", "Always keep hands on wheel and eyes on road", 0}},
|
||||
{"StartupMessageTop", {PERSISTENT, STRING, "Be ready to take over at any time", "Be ready to take over at any time", 0}},
|
||||
{"StaticPedalsOnUI", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"SteerDelay", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"SteerDelayModeMigrated", {PERSISTENT, BOOL}},
|
||||
{"SteerDelayStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"SteerFriction", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"SteerFrictionStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
@@ -560,18 +602,21 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"SteerOffsetStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"SteerRatio", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"SteerRatioStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"EnableTorqueBarWidget", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"StockConfidenceBallWidget", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"StockDongleId", {PERSISTENT, STRING, "", ""}},
|
||||
{"StopAccel", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"StopAccelStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"StoppedTimer", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"StopDistance", {PERSISTENT, FLOAT, "6.0", "6.0", 2}},
|
||||
{"StoppedTimer", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"StoppingDecelRate", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"StoppingDecelRateStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"StarButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"SwitchbackModeCooldown", {PERSISTENT, INT, "5", "0", 2}},
|
||||
{"StarButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"SwitchbackModeCooldown", {PERSISTENT, INT, "5", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"SwitchbackModeEnabled", {CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0", "0"}},
|
||||
{"SubaruSNG", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"SubaruSNG", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"SubaruSNGManualParkingBrake", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"TacoTune", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"TeslaCoopSteering", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"TestAlert", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"TetheringEnabled", {PERSISTENT, INT, "0", "0", 0}},
|
||||
{"ThemeDownloadProgress", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
@@ -579,12 +624,13 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"Timezone", {PERSISTENT, STRING, "", ""}},
|
||||
{"TinygradUpdateAvailable", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"ToyotaDoors", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"TrafficFollow", {PERSISTENT, FLOAT, "0.5", "0.5", 2}},
|
||||
{"TrafficJerkAcceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"TrailerLoad", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"TrafficFollow", {PERSISTENT, FLOAT, "0.75", "0.75", 2}},
|
||||
{"TrafficJerkAcceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"TrafficJerkDanger", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"TrafficJerkDeceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"TrafficJerkSpeed", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"TrafficJerkSpeedDecrease", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"TrafficJerkDeceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"TrafficJerkSpeed", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"TrafficJerkSpeedDecrease", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"TruckTuning", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"TuningLevel", {PERSISTENT, INT, "0", "0", 0}},
|
||||
{"TuningLevelConfirmed", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
@@ -596,26 +642,30 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"UpdateTinygrad", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"UpdateWheelImage", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"UseActiveTheme", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"UseKonikServer", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"UseAutoSteerDelay", {PERSISTENT, BOOL, "1", "1", 3}},
|
||||
{"UseKonikServer", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"UseSI", {PERSISTENT, BOOL, "1", "1", 3}},
|
||||
{"UserFavorites", {PERSISTENT, STRING, "", "", 1}},
|
||||
{"UseVienna", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"UseVienna", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"VEgoStarting", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"VEgoStartingStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"VEgoStopping", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"VEgoStoppingStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"VeryLongCancelButtonControl", {PERSISTENT, INT, "6", "0", 2}},
|
||||
{"VeryLongDistanceButtonControl", {PERSISTENT, INT, "6", "0", 2}},
|
||||
{"VeryLongModeButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"VeryLongStarButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"VoltSNG", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"GMAutoHold", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"WarningImmediateVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"WarningSoftVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"WeatherPresets", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"VeryLongCancelButtonControl", {PERSISTENT, INT, "6", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"VeryLongDistanceButtonControl", {PERSISTENT, INT, "6", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"VeryLongModeButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"VeryLongStarButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"VoltSNG", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"JeepBrakeHold", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"GMAutoHold", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"VoltOnePedalMode", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ToyotaAutoHold", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"WarningImmediateVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"WarningSoftVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"WeatherPresets", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"WeatherToken", {PERSISTENT | DONT_LOG, STRING, "", "", 2}},
|
||||
{"WheelControls", {PERSISTENT, STRING, "", "", 2}},
|
||||
{"WheelIcon", {PERSISTENT, STRING, "frog", "stock", 0}},
|
||||
{"WheelSpeed", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"WheelIcon", {PERSISTENT, STRING, "stock", "stock", 0}},
|
||||
{"WheelSpeed", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"WheelToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
};
|
||||
|
||||
+1104
-855
File diff suppressed because it is too large
Load Diff
@@ -32,6 +32,10 @@ cdef extern from "common/params.h":
|
||||
JSON
|
||||
BYTES
|
||||
|
||||
cdef enum ParamSettingsTier:
|
||||
SETTINGS_SIMPLE
|
||||
SETTINGS_ADVANCED
|
||||
|
||||
cdef cppclass c_Params "Params":
|
||||
c_Params(string, bool) except + nogil
|
||||
string get(string, bool) nogil
|
||||
@@ -55,6 +59,8 @@ cdef extern from "common/params.h":
|
||||
|
||||
int getTuningLevel(string) nogil
|
||||
|
||||
ParamSettingsTier getSettingsTier(string) nogil
|
||||
|
||||
PYTHON_2_CPP = {
|
||||
(str, STRING): lambda v: v,
|
||||
(builtins.bool, BOOL): lambda v: "1" if v else "0",
|
||||
@@ -255,3 +261,6 @@ cdef class Params:
|
||||
cdef string k = self.check_key(key)
|
||||
cdef optional[int] level = self.p.getTuningLevel(k)
|
||||
return level.value() if level.has_value() else 0
|
||||
|
||||
def get_settings_tier(self, key):
|
||||
return self.p.getSettingsTier(self.check_key(key))
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,16 @@
|
||||
from openpilot.common import file_chunker
|
||||
|
||||
|
||||
def test_chunked_stream_round_trip(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(file_chunker, "CHUNK_SIZE", 7)
|
||||
path = tmp_path / "artifact.pkl"
|
||||
payload = b"a model artifact spanning several chunks"
|
||||
path.write_bytes(payload)
|
||||
|
||||
targets = file_chunker.get_chunk_targets(path, len(payload))
|
||||
file_chunker.chunk_file(path, targets)
|
||||
|
||||
assert file_chunker.file_chunked_exists(path)
|
||||
assert file_chunker.read_file_chunked(path) == payload
|
||||
with file_chunker.open_file_chunked(path) as stream:
|
||||
assert stream.read(9) + stream.read() == payload
|
||||
@@ -25,3 +25,13 @@ TEST_CASE("params_nonblocking_put") {
|
||||
REQUIRE(p.get(name) == "1");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("settings_tier_is_independent_from_tuning_level") {
|
||||
Params params;
|
||||
|
||||
REQUIRE(params.getSettingsTier("AlwaysOnLateral") == SETTINGS_SIMPLE);
|
||||
REQUIRE(params.getTuningLevel("AlwaysOnLateral") == 0);
|
||||
REQUIRE(params.getSettingsTier("HumanLaneChanges") == SETTINGS_SIMPLE);
|
||||
REQUIRE(params.getTuningLevel("HumanLaneChanges") == 2);
|
||||
REQUIRE(params.getSettingsTier("AdvancedLateralTune") == SETTINGS_ADVANCED);
|
||||
}
|
||||
|
||||
+32
@@ -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
@@ -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> |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|[](##)|[](##)|<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|[](##)|[](##)|<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|[](##)|[](##)|<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|[](##)|[](##)|<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|[](##)|[](##)|<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|[](##)|[](##)|<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|[](##)|[](##)|<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>|||
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
# StarPilot Unified Model Rebuild
|
||||
|
||||
This workflow rebuilds StarPilot driving and driver-monitoring artifacts for the vendored tinygrad revision. Driving-model behavior versions remain manifest metadata; every runtime driving artifact uses the `tinygrad_single_v1` layout.
|
||||
|
||||
## Safety
|
||||
|
||||
- The supported build device is `comma@192.168.3.110`.
|
||||
- Never run these commands against `192.168.3.109`.
|
||||
- Normal artifacts target QCOM. External-GPU artifacts must be compiled explicitly and tagged in the manifest.
|
||||
- Keep source ONNX files and compiled PKLs on the T5 workspace, not the comma.
|
||||
|
||||
## Workspace
|
||||
|
||||
The default workspace is:
|
||||
|
||||
```text
|
||||
/Volumes/T5/StarPilot-Model-Rebuild-2026-06-22/
|
||||
```
|
||||
|
||||
Important directories:
|
||||
|
||||
- `onnx/<model-id>/`: ID-prefixed source ONNX files.
|
||||
- `compiled/`: completed unified driving PKLs.
|
||||
- `driver-monitoring/`: DM ONNX, model PKL, metadata, and camera warps.
|
||||
- `ready-for-resources/`: flat repository-upload handoff.
|
||||
- Oversized models are represented by repository-safe `.p00`, `.p01`, and `.sha256` files in `ready-for-resources/`.
|
||||
- `logs/`: one remote compilation log per model.
|
||||
- `results/`: source and artifact checksum records.
|
||||
- `manifests/`: generated `model_names_v22.json`.
|
||||
|
||||
## Initialize And Extract
|
||||
|
||||
```bash
|
||||
python3 scripts/model_rebuild_pipeline.py init
|
||||
python3 scripts/model_rebuild_pipeline.py extract \
|
||||
--base-manifest /path/to/model_names_v21.json
|
||||
```
|
||||
|
||||
Extraction streams Git blobs directly to disk. LFS pointers are resolved from the local object cache or fetched by object ID, then checked against the pointer SHA-256 and size. Binary ONNX data is never stored in a shell variable.
|
||||
|
||||
To retry one source:
|
||||
|
||||
```bash
|
||||
python3 scripts/model_rebuild_pipeline.py extract \
|
||||
--model pop22 \
|
||||
--base-manifest /path/to/model_names_v21.json
|
||||
```
|
||||
|
||||
Source commits are defined in `scripts/model_source_map_v22.json`.
|
||||
|
||||
## Compile
|
||||
|
||||
Compile one model:
|
||||
|
||||
```bash
|
||||
python3 scripts/model_rebuild_pipeline.py compile \
|
||||
--model pop22 \
|
||||
--base-manifest /path/to/model_names_v21.json
|
||||
```
|
||||
|
||||
Compile or resume the full catalog:
|
||||
|
||||
```bash
|
||||
python3 scripts/model_rebuild_pipeline.py compile \
|
||||
--base-manifest /path/to/model_names_v21.json
|
||||
```
|
||||
|
||||
Existing artifacts are skipped unless `--force` is passed. Each model is staged in its own remote input directory, compiled on `.110`, copied back to the T5, hashed, and copied into `ready-for-resources/`. Failures are written to `results/<id>_failure.json`; rerunning the same command resumes incomplete models.
|
||||
|
||||
Validate one or all completed artifacts with synthetic camera inputs on QCOM:
|
||||
|
||||
```bash
|
||||
python3 scripts/model_rebuild_pipeline.py validate \
|
||||
--model pop22 \
|
||||
--base-manifest /path/to/model_names_v21.json
|
||||
```
|
||||
|
||||
The lower-level device compiler also supports direct use:
|
||||
|
||||
```bash
|
||||
./models --model pop22 --input-format split --version v11
|
||||
./models --model deeprl3v2 --input-format supercombo --version v15
|
||||
```
|
||||
|
||||
For a model that cannot run on the device GPU, compile with the USB AMD GPU attached:
|
||||
|
||||
```bash
|
||||
./models --lebowski --gpu
|
||||
```
|
||||
|
||||
The dynamic flag (`--lebowski` above) sets the output and manifest model ID;
|
||||
when only one source model is staged, its ONNX filename does not need to match
|
||||
that ID. Input format and behavior version are inferred. `--external-gpu`
|
||||
remains available as a compatibility alias for `--gpu`.
|
||||
|
||||
This emits a streaming out-of-band pickle and keeps QCOM available for camera warps. Its manifest entry must include:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "lebowski",
|
||||
"uses_external_gpu": true
|
||||
}
|
||||
```
|
||||
|
||||
Only tagged models activate the external GPU. If the GPU or artifact is unavailable, runtime falls back to the built-in model; all untagged models retain the existing QCOM path.
|
||||
|
||||
`--version` records behavioral semantics only. It does not change artifact layout.
|
||||
|
||||
If the compiled PKL exceeds 100 MiB, `./models` automatically keeps the full
|
||||
local PKL and creates 95 MiB upload parts beside it:
|
||||
|
||||
```text
|
||||
deeprl3v2_driving_tinygrad.pkl
|
||||
deeprl3v2_driving_tinygrad.pkl.p00
|
||||
deeprl3v2_driving_tinygrad.pkl.p01
|
||||
deeprl3v2_driving_tinygrad.pkl.sha256
|
||||
```
|
||||
|
||||
To split an already compiled artifact:
|
||||
|
||||
```bash
|
||||
./models --split-artifact /path/to/deeprl3v2_driving_tinygrad.pkl \
|
||||
--output-dir /path/to/upload-ready
|
||||
```
|
||||
|
||||
Upload only the numbered parts and checksum when the full PKL exceeds the
|
||||
repository limit. The downloader reassembles into a temporary file, verifies
|
||||
the companion SHA-256, and atomically installs the final PKL. No manifest field
|
||||
is required for multipart artifacts.
|
||||
|
||||
## Driver Monitoring
|
||||
|
||||
Stage the current DM ONNX in `uncompiledmodels`, then run:
|
||||
|
||||
```bash
|
||||
./models --dm \
|
||||
--input-dir /data/openpilot/uncompiledmodels \
|
||||
--output-dir /tmp/dm_artifacts
|
||||
```
|
||||
|
||||
This builds:
|
||||
|
||||
- `dmonitoring_model_tinygrad.pkl`
|
||||
- `dmonitoring_model_metadata.pkl`
|
||||
- `dm_warp_1928x1208_tinygrad.pkl`
|
||||
- `dm_warp_1344x760_tinygrad.pkl`
|
||||
|
||||
All four files must be updated together.
|
||||
|
||||
## Manifest
|
||||
|
||||
Generate v22 after compilation:
|
||||
|
||||
```bash
|
||||
python3 scripts/model_rebuild_pipeline.py manifest \
|
||||
--base-manifest /path/to/model_names_v21.json
|
||||
```
|
||||
|
||||
The generator preserves existing IDs and behavioral metadata and adds
|
||||
`deeprl3v2`. Manifest v22 implies the unified single-PKL runtime layout.
|
||||
Repository-hosted multipart files are discovered by naming convention, so no
|
||||
size, hash, format, or part-count metadata is required.
|
||||
|
||||
`uses_external_gpu` is optional and defaults to `false`.
|
||||
|
||||
## Runtime Verification
|
||||
|
||||
Compilation validates JIT capture/replay, pickle round-trip, finite outputs, metadata slices, and both camera warps. Before release:
|
||||
|
||||
1. Select representative v8, v11, v12, v15, and supercombo models.
|
||||
2. Confirm `modeld` stays running.
|
||||
3. Confirm finite `modelV2` path, lane-line, lead, pose, and action data.
|
||||
4. Confirm `driverStateV2` on both supported camera resolutions.
|
||||
5. Test download, selection, deletion, randomization, migration, and fallback in QT, raylib/mici, and Galaxy.
|
||||
|
||||
The built-in South Carolina artifact is `selfdrive/modeld/models/driving_tinygrad.pkl`. If migration cannot download the selected v22 artifact, StarPilot switches to that built-in model.
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 88 KiB |
Binary file not shown.
@@ -0,0 +1,102 @@
|
||||
# Contribute to StarPilot
|
||||
|
||||
Contributions are welcome. StarPilot is driver-assistance software, so changes should be focused, testable, and safe for vehicles outside the change's intended scope.
|
||||
|
||||
## Pull requests
|
||||
|
||||
Open all pull requests against the **`Dom` branch**. `Dom` is StarPilot's testing and integration branch; do not target the release branch directly.
|
||||
|
||||
A pull request should:
|
||||
|
||||
* have one clear purpose and contain only changes needed for that purpose;
|
||||
* explain what changed, why it changed, and how it was tested;
|
||||
* link the relevant issue or feedback item when one exists;
|
||||
* avoid unrelated cleanup, refactors, dependency changes, and formatting churn; and
|
||||
* pass the existing tests and checks.
|
||||
|
||||
Keep commits reviewable and update documentation when behavior or configuration changes. If a change needs a large refactor, separate that work from the behavior change when practical.
|
||||
|
||||
### Vehicle-specific changes
|
||||
|
||||
Do your best to ensure a vehicle-specific change affects only the intended vehicle, platform, or brand. Prefer the narrowest appropriate condition instead of changing shared behavior for every vehicle.
|
||||
|
||||
Tests should prove both sides of that boundary:
|
||||
|
||||
* the affected vehicle receives the new or corrected behavior; and
|
||||
* an unaffected vehicle, platform, brand, or configuration retains the existing behavior.
|
||||
|
||||
Include the vehicle and hardware used for on-road or bench testing in the pull request. Hardware testing is valuable, but it does not replace an automated regression test when the behavior can be tested in code.
|
||||
|
||||
## Development environment
|
||||
|
||||
StarPilot keeps host-native development tools separate from device-target builds. Run the setup and development commands from the repository root.
|
||||
|
||||
Install the Python dependencies before starting:
|
||||
|
||||
```bash
|
||||
tools/install_python_dependencies.sh
|
||||
```
|
||||
|
||||
On Ubuntu, `tools/ubuntu_setup.sh` installs both the system and Python dependencies. The development tools also require `uv`.
|
||||
|
||||
Use `./dev` for host-native tools. It creates and reuses an isolated environment under `.host_runtime/`, keeping host build artifacts out of the working tree:
|
||||
|
||||
```bash
|
||||
./dev replay
|
||||
./dev cabana
|
||||
./dev plotjuggler
|
||||
./dev shell
|
||||
```
|
||||
|
||||
For desktop UI work, use `./c3`, `./c4`, or `./raybig`. These commands use the same isolated host environment.
|
||||
|
||||
Use `./build` when you need comma-compatible device artifacts. This is the expected validation for changes that affect compiled device code or runtime behavior:
|
||||
|
||||
```bash
|
||||
./build
|
||||
```
|
||||
|
||||
Device builds require Docker Desktop or Podman with Linux/aarch64 support and a configured comma sysroot. See the [laptop device-build guide](../how-to/laptop-device-build.md) for setup instructions and the [complete StarPilot development workflow](https://github.com/firestar5683/StarPilot/blob/Dom/tools/STARPILOT_DEVELOPMENT.md) for all host commands and troubleshooting.
|
||||
|
||||
## Code formatting
|
||||
|
||||
Match the style of the code around your change and do not reformat unrelated files. Python formatting and lint rules are defined in `pyproject.toml`; the project uses two-space indentation and Ruff.
|
||||
|
||||
Run Ruff on changed Python files while developing:
|
||||
|
||||
```bash
|
||||
ruff check path/to/changed_file.py
|
||||
ruff format --check path/to/changed_file.py
|
||||
```
|
||||
|
||||
Before submitting, run the repository lint checks from the project root:
|
||||
|
||||
```bash
|
||||
./scripts/lint/lint.sh
|
||||
```
|
||||
|
||||
## Testing standards
|
||||
|
||||
Every behavior change or bug fix should include focused automated tests when practical. Recent StarPilot tests favor small regression cases that construct the relevant state, exercise one behavior, and assert the exact result.
|
||||
|
||||
At minimum:
|
||||
|
||||
1. Add or update a test that would fail without the change.
|
||||
2. Cover important boundaries, modes, and disabled states related to the change.
|
||||
3. For vehicle-specific behavior, add a negative case showing the change does not bleed into an unaffected vehicle or configuration.
|
||||
4. Run the directly affected test module and the existing tests for the affected subsystem.
|
||||
5. Ensure the pull request passes all existing CI checks before it is ready to merge.
|
||||
|
||||
Run a focused test module with pytest:
|
||||
|
||||
```bash
|
||||
pytest path/to/test_file.py
|
||||
```
|
||||
|
||||
Run the full test suite when your development environment supports it:
|
||||
|
||||
```bash
|
||||
pytest
|
||||
```
|
||||
|
||||
If a test requires special hardware or cannot run in your environment, say so in the pull request and document the closest validation you completed.
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
This flow builds **device-target (`larch64`) binaries on your laptop** using a Linux/aarch64 container and a synced comma sysroot.
|
||||
|
||||
For the full StarPilot branch workflow, including host-native shorthand tools such as `./dev`, `./c3`, `./c4`, and `./raybig`, see [tools/STARPILOT_DEVELOPMENT.md](../../tools/STARPILOT_DEVELOPMENT.md).
|
||||
For the full StarPilot branch workflow, including host-native shorthand tools such as `./dev`, `./c3`, `./c4`, and `./raybig`, see the [StarPilot development guide](https://github.com/firestar5683/StarPilot/blob/Dom/tools/STARPILOT_DEVELOPMENT.md).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
|
||||
@@ -302,3 +302,112 @@ For temporal behavior on a saved frame directory or route extract, replay the ru
|
||||
```bash
|
||||
.venv/bin/python scripts/replay_speed_limit_vision.py .tmp/vision_iter/seg10_5fps --frames-fps 5
|
||||
```
|
||||
|
||||
The detector/classifier runtime is model-only by default. Use `--crop-ocr` with
|
||||
`evaluate_runtime_manifest.py` or `replay_route_runtime.py` only for an explicit
|
||||
legacy comparison. A model-only release must match reviewed-manifest accuracy
|
||||
and pass representative route replays at measured on-device cadence. Evaluate
|
||||
candidate recognition and temporal publish behavior separately: a correct
|
||||
single-frame candidate can still be suppressed by the history and speed-change
|
||||
confirmation policy.
|
||||
|
||||
Ignored review rows label the proposed crop, not the entire camera frame.
|
||||
Consequently, negative-window candidate and publish counts from
|
||||
`evaluate_reviewed_route_events.py` are an upper bound until the full frame is
|
||||
audited; another valid sign can be present outside the rejected crop. Use the
|
||||
per-row output and frame image to audit any regression delta before treating it
|
||||
as a runtime false positive.
|
||||
|
||||
## Promotion Gate
|
||||
|
||||
Do not promote a checkpoint from classifier validation accuracy alone. Export it
|
||||
to an isolated model directory and run the complete runtime pipeline against the
|
||||
reviewed positive, hard-negative, and failed-drive manifests. A candidate must
|
||||
preserve exact-value recall, avoid new wrong-value reads, and remain within the
|
||||
accepted false-positive budget before route replay.
|
||||
|
||||
Mine detector proposals that fool an integrated-reject classifier into a new
|
||||
reject class before retraining:
|
||||
|
||||
```bash
|
||||
.venv/bin/python scripts/speed_limit_vision/mine_classifier_reject_crops.py \
|
||||
--models-dir /path/to/candidate/models \
|
||||
--dataset /path/to/versioned/classifier \
|
||||
--manifest /path/to/reviewed-negative-manifest.csv
|
||||
```
|
||||
|
||||
Keep the resulting dataset version separate from the current training set. If a
|
||||
hard-negative retrain lowers reviewed recall, reject the checkpoint even when it
|
||||
improves aggregate validation accuracy or removes a known false positive.
|
||||
|
||||
## Active-Learning Review Pass
|
||||
|
||||
Keep parallel miners in separate directories and merge them only when their
|
||||
model and mining fingerprints match:
|
||||
|
||||
```bash
|
||||
.venv/bin/python scripts/speed_limit_vision/merge_manual_review_queues.py \
|
||||
/path/to/shard0 /path/to/shard1 /path/to/shard2 /path/to/shard3 \
|
||||
--output-dir /path/to/merged
|
||||
```
|
||||
|
||||
When rescanning with a new model, compare the fingerprinted queues before
|
||||
selecting another batch. The optional review output retains the full queue
|
||||
schema so it can be passed directly to the selector and review server:
|
||||
|
||||
```bash
|
||||
.venv/bin/python scripts/speed_limit_vision/compare_manual_review_queues.py \
|
||||
--before /path/to/baseline/manual_review_queue.csv \
|
||||
--after /path/to/candidate/manual_review_queue.csv \
|
||||
--output-csv /path/to/comparison.csv \
|
||||
--review-output /path/to/disagreements/manual_review_queue.csv
|
||||
|
||||
.venv/bin/python scripts/speed_limit_vision/select_manual_review_queue.py \
|
||||
--input /path/to/disagreements/manual_review_queue.csv \
|
||||
--output /path/to/review/manual_review_queue.csv \
|
||||
--max-rows 1200 \
|
||||
--min-seconds-per-route-speed 3
|
||||
```
|
||||
|
||||
The selector prioritizes value changes and gained/lost reads, balances routes
|
||||
and speed classes, and removes adjacent same-speed frames from one scene. Start
|
||||
the reviewer and import its labels without moving route media off the training
|
||||
volume:
|
||||
|
||||
```bash
|
||||
.venv/bin/python scripts/speed_limit_vision/serve_manual_review_queue.py \
|
||||
--manifest /path/to/review/manual_review_queue.csv \
|
||||
--port 8765
|
||||
|
||||
.venv/bin/python scripts/speed_limit_vision/import_manual_review_queue.py \
|
||||
--queue /path/to/review/manual_review_queue.csv
|
||||
```
|
||||
|
||||
## Re-mine the Route Backlog
|
||||
|
||||
Re-run the backlog after a candidate passes the reviewed-manifest and route
|
||||
replay gates. Use a model fingerprinted run so new pseudo-labels are staged next
|
||||
to, rather than merged into, the original route-mining data:
|
||||
|
||||
```bash
|
||||
.venv/bin/python scripts/speed_limit_vision/mine_route_training_samples.py \
|
||||
--workspace /Volumes/T5/starpilot_speed_limit/workspace/speed_limit_training_clean \
|
||||
--models-dir /path/to/promoted/models \
|
||||
--model-only \
|
||||
--run-id auto \
|
||||
--sample-every 2.0 \
|
||||
--transition-step 0.5 \
|
||||
--max-frames-per-route 720 \
|
||||
--max-positives-per-route 120 \
|
||||
--max-negatives-per-route 200
|
||||
```
|
||||
|
||||
The output is written under
|
||||
`staging/route_mining/model_<model-fingerprint>_run_<mining-fingerprint>/` with
|
||||
its own detector images, classifier labels, review manifest, and per-route
|
||||
completion state. The mining fingerprint includes the model-only mode,
|
||||
thresholds, sampling configuration, and relevant source code. Review and
|
||||
deduplicate that staged run before merging it into a training dataset. Never
|
||||
overwrite the canonical route samples or automatically train on every mined
|
||||
positive; map agreement and human review remain required because a stronger
|
||||
model can still reproduce its own mistakes at larger scale.
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
# I Taught My Comma to Read Speed Limit Signs
|
||||
|
||||
<video controls playsinline preload="metadata" poster="assets/speed-limit-vision-demo-poster.jpg" style="width: 100%; max-width: 1280px;">
|
||||
<source src="assets/speed-limit-vision-demo.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
[Watch the speed-limit vision demo](assets/speed-limit-vision-demo.mp4)
|
||||
|
||||
This whole project started because I was lazy.
|
||||
|
||||
If you’ve driven an HKG or Toyota vehicle in recent years, you’ve probably seen the dashboard automatically pick up speed limits using the camera. Coming from a car that didn't have this functionality, I was very jealous. Those cars can feed those speed limits directly into openpilot forks speed limit controllers. Mine couldn't.
|
||||
|
||||
The “solution” has been to manually add speed limits to Mapbox or OpenStreetMaps. It worked, but it was tedious. I live in rural Kansas, where my commute is almost entirely devoid of nerds contributing open source map data. I began mapping my commute when I first joined the project and within an hour said, “I ain't doin this”
|
||||
|
||||
We have an AI model that's good enough to drive our cars running on a smartphone chipset from years ago. How hard could it be to build one that just reads speed limit signs? I wanted StarPilot to look out the windshield, see a speed limit sign, and just know what it said.
|
||||
|
||||
That sounded simple enough.
|
||||
|
||||
It wasn’t.
|
||||
|
||||
The first prototype was basically held together with duct tape: I used all the public speed limit datasets I could find (glare and Lisa), some OpenCV, a little OCR, and a lot of wishful thinking. It could occasionally read a sign, but it missed 90% of them and produced enough false positives that you definitely wouldn’t want to trust it.
|
||||
|
||||
The important part wasn’t that it sucked - it was that it worked just well enough to start collecting better training data.
|
||||
|
||||
I run a fork of openpilot called StarPilot. We're heavily focused on tuning and testing wild ideas, which made my users the perfect candidates for this project.
|
||||
|
||||
Instead of relying on public datasets that barely resembled comma camera footage, StarPilot started collecting its own. Community members submitted routes from all over the country using bookmarks that were generated automatically when the model said "I think this might've been a sign," and every promising detection went through manual review. Eventually that grew into hundreds of gigabytes of real driving footage and thousands of carefully labeled speed limit signs.
|
||||
|
||||
From there it became an endless cycle:
|
||||
|
||||
- Train a better model.
|
||||
- Mine more routes.
|
||||
- Find more missed signs.
|
||||
- Label them.
|
||||
- Repeat.
|
||||
|
||||
The funny part is that training the neural network wasn’t actually the hardest problem.
|
||||
|
||||
The hard part was everything around it.
|
||||
|
||||
One of the biggest challenges wasn’t accuracy, it was speed. Every millisecond spent processing was another frame the model didn't have time to see. The faster it could process frames, the more chances it had to catch a sharp, readable speed limit sign before it disappeared. This was particularly imperative at night, when naturally camera frame rates drop alongside your chances of picking up a clear sign.
|
||||
|
||||
It’s slowly evolved from a proof of concept into a model that’s trained primarily on real comma footage instead of generic traffic sign datasets. It now runs entirely on-device, publishes vision-based speed limits directly into StarPilot, and no longer depends on OCR or manually maintaining map data.
|
||||
|
||||
And best of all…
|
||||
|
||||
While I'm too lazy to hand write hundreds of speed limit signs into OpenStreetMaps, this tool can now be used to automate the entire process, giving back to the open source mapping community that helps those in larger areas so well.
|
||||
|
||||
Give my vision speed limits a try, or help continue to build a better one than mine! All info and training stack is available on the StarPilot repo.
|
||||
|
||||
---
|
||||
|
||||
*For a full LLM style, more technical breakdown for those interested in the process, please read below:*
|
||||
|
||||
|
||||
## Teaching StarPilot to Read Speed Limit Signs
|
||||
|
||||
In March 2026, this project started with a practical question: could StarPilot use the road camera to recognize speed-limit signs, attach them to GPS data, and help fill gaps in OpenStreetMap?
|
||||
|
||||
The first answer was "probably." A clean 40 mph sign in recorded comma footage was large enough to detect and read. StarPilot already had access to the live road-camera stream through VisionIPC, ONNX models could run through OpenCV DNN, and the existing Speed Limit Controller already knew how to combine several sources. The pieces existed.
|
||||
|
||||
What did not exist was a model trained for this camera, a trustworthy dataset, an honest replay benchmark, or a runtime that could analyze enough frames without interfering with openpilot. Building those became the real project.
|
||||
|
||||
This is the story of how we went from a weak imported model that was barely useful for automatic bookmarks to a custom detector and classifier trained primarily from real comma footage, how community routes changed the quality of the data, and why model accuracy on a laptop turned out to be only half of the problem.
|
||||
|
||||
### The first prototype
|
||||
|
||||
The first live implementation used an imported Ultralytics-style checkpoint called `ayoubsa_best`. Its provenance was thin: the ONNX metadata identified it as a Kaggle-trained detector, but there was no training notebook or source dataset in the repository. More importantly, its classes were a poor fit for American roads. It knew speed limits in 10 mph increments, such as 20, 30, and 40, but not the common 25, 35, 45, 55, or 65 mph signs.
|
||||
|
||||
That led to a hybrid design. The model proposed a sign region, then lightweight OpenCV and OCR-like digit logic tried to read the crop. It was enough to prove the complete path:
|
||||
|
||||
1. Read the live road-camera stream.
|
||||
2. Search the right side of the frame for likely signs.
|
||||
3. Produce a candidate speed and confidence.
|
||||
4. Confirm it over time.
|
||||
5. Publish it as a selectable `Vision` source for the Speed Limit Controller.
|
||||
6. Save debug frames and bookmarks for later training.
|
||||
|
||||
It was not yet a good speed-limit reader. It missed most signs, produced false positives, and struggled badly with 5 mph increments. But it could occasionally find real signs, which meant it could help us collect the data needed to replace itself.
|
||||
|
||||
That bootstrap capability mattered more than its initial accuracy.
|
||||
|
||||
### Public data gave us a starting point
|
||||
|
||||
We chose a two-stage architecture early: one model would answer "where is the speed-limit sign and what kind is it?" while another would answer "what number is printed on it?" Detection and reading are related, but they are not the same task. Keeping them separate let the detector learn the general shape and location of a sign without needing a separate object class for every posted speed.
|
||||
|
||||
Three public U.S. traffic-sign datasets formed the initial training base:
|
||||
|
||||
- [LISA](https://cvrr.ucsd.edu/lisa-traffic-signs-dataset) provided U.S. road scenes, 47 sign types, and 7,855 annotations across 6,610 frames. Its annotations also included useful information such as occlusion and whether a sign belonged to a side road.
|
||||
- [GLARE](https://arxiv.org/abs/2209.08716) provided 2,157 U.S. traffic-sign images taken from 33 dashcam videos under strong sun glare. The paper itself demonstrated why mixed normal and glare training was important.
|
||||
- [ARTS](https://swshah.w3.uvm.edu/vail/datasets.php), the Automotive Repository of Traffic Signs, provided additional U.S. signs in easy, challenging, and video-log configurations.
|
||||
|
||||
The clean first pass imported 3,078 ARTS images with 3,139 relevant boxes. When a working LISA archive was found, it added 1,577 images and 1,680 boxes, including 15, 25, 35, 45, 55, and 65 mph classes. GLARE was downloaded selectively to avoid pulling a large collection of unrelated checkpoints.
|
||||
|
||||
|
||||
### More data made the first retrain worse
|
||||
|
||||
The first clean public-data retrain looked reasonable in conventional validation metrics and failed badly on real comma routes.
|
||||
|
||||
The stronger older model found a candidate in 9 of 39 bookmarked route windows. The clean public-data model found only 1 of 39. It also regressed the saved-frame suite. We restored the older model instead of promoting a checkpoint merely because it was newer or had better training curves.
|
||||
|
||||
The domain gap was larger than image dimensions suggested. LISA, GLARE, ARTS, and comma footage were all forward-looking road imagery, but they differed in lens distortion, mounting position, exposure, dynamic range, compression, motion blur, sign position, and the number of pixels available when a sign first appeared. Public data often contained a cleaner or more centered sign than the live comma pipeline would see.
|
||||
|
||||
This established a rule that guided every later pass: public data was useful for bootstrapping and preservation, but real comma footage had to dominate final training and evaluation.
|
||||
|
||||
### Turning StarPilot into a data engine
|
||||
|
||||
The next large improvement did not come from a new backbone. It came from collecting better data.
|
||||
|
||||
StarPilot gained an automatic training collector, automatic bookmarks, manual bookmarks, debug snapshots, route metadata, and tools to preserve full-resolution video segments. A route-bundling script verified that a contributed route was public, contained the required qlogs, rlogs, and `fcamera.hevc` files, and had the vision collector enabled before packaging it.
|
||||
|
||||
That allowed StarPilot users to contribute real driving data from different cars, cameras, roads, states, lighting conditions, and sign styles. Eventually, nearly 200 GB of zipped comma routes landed on disk. This was enormously more useful than another generic sign dataset because it matched the exact production domain:
|
||||
|
||||
- the same road camera and encoding path;
|
||||
- the same wide-angle geometry;
|
||||
- the same night exposure and motion blur;
|
||||
- the same right-shoulder sign placement;
|
||||
- the same live crop errors;
|
||||
- and the same hard negatives seen by the runtime detector.
|
||||
|
||||
The model could then scan old routes, find likely signs and mistakes, and generate another review queue. A better model could rescan the same backlog and find candidates the weaker model had never seen. Each iteration improved not only the deployed model, but also the quality of the next dataset.
|
||||
|
||||
### Human review was the quality control
|
||||
|
||||
Automatic labels were never trusted blindly. A map transition did not prove that a sign was visible, a bookmark did not prove that the best frame had been selected, and a confident model prediction did not prove that the crop contained the correct sign.
|
||||
|
||||
The manual review UI evolved around throughput. Typing a number labeled a regulatory sign. Single-key shortcuts marked advisory (`a`), school-zone (`s`), regulatory (`r`), uncertain (`u`), or ignored/not-a-sign (`i` or `x`) samples. Enter or Space accepted a prediction. Reviewers could also redraw a bad bounding box.
|
||||
|
||||
Those details affected model quality directly:
|
||||
|
||||
- A readable but slightly imperfect crop was useful.
|
||||
- A crop that showed only the minimum-speed portion of a 65/40 sign was not a valid 40 mph regulatory label.
|
||||
- Stop signs and empty crops became hard negatives.
|
||||
- Advisory signs were labeled explicitly, even though overall sign recall remained the product priority.
|
||||
- Uncertain night crops were retained separately instead of being treated as equally strong labels.
|
||||
- Repeated frames from the same sign track were deduplicated so one scene could not dominate the dataset.
|
||||
- Incorrectly large or shifted boxes were redrawn once the review tool exposed that capability.
|
||||
|
||||
One labeling ambiguity forced a larger re-review: earlier sessions had not made regulatory versus advisory state clear enough. Rather than preserve potentially poisoned labels, affected advisory candidates were put back into the queue under the corrected UI contract.
|
||||
|
||||
This was active learning in a practical form: spend human time on disagreements, weak reads, false publishes, missed signs, bad boxes, and rare conditions, not on thousands of easy duplicate frames.
|
||||
|
||||
By one later OCR-free promotion audit, the backlog covered 80 routes and 366,946 sampled frames. The model under audit produced 34,260 candidates for rescoring, while the canonical review database contained 5,383 unique human-reviewed crops: 593 positive reads and 4,146 usable crop-level rejects, with the remainder reserved for uncertain or otherwise excluded decisions. The raw route collection was large, but those reviewed examples were the part that made it trustworthy.
|
||||
|
||||
### The architecture that survived
|
||||
|
||||
The current production path is model-only. OCR remains in the source tree for legacy compatibility and offline experiments, but both full-frame OCR and crop OCR are disabled for the active detector/classifier pipeline.
|
||||
|
||||
The deployed stack contains:
|
||||
|
||||
- A YOLO11n-derived detector exported as a fixed `256x256` ONNX model. It proposes regulatory, advisory, and school-zone speed-limit signs.
|
||||
- A YOLO11n-classifier-derived `128x128` ONNX model. It predicts 15 through 75 mph in 5 mph increments and includes a learned reject class for crops that are not valid speed-limit reads.
|
||||
- Runtime logic that evaluates several crop expansions, combines their support, applies geometry and sign-type checks, and confirms lower-confidence changes over multiple frames.
|
||||
- A right-side region of interest, which reduces wasted work while retaining the part of U.S. road scenes where most applicable signs appear.
|
||||
|
||||
The detector is about 9.9 MB and the classifier about 5.9 MB. They remain separate ONNX files intentionally. Joining the graphs into one file would make packaging look tidier, but it would not eliminate the detector or classifier computation. Separate models also let us change detector resolution, classifier training, reject behavior, and crop strategy independently.
|
||||
|
||||
A third standalone reject model was tested and discarded. It added another inference pass without measurably improving the reviewed positives or surviving false positive. Folding rejection into the value classifier produced a better cost/accuracy tradeoff.
|
||||
|
||||
OCR was removed only after the model-only path could pass the preservation suites on its own. In the decisive audit, model consensus improved the targeted 20 mph events from 1/14 to 11/14 and the broader targeted set from 21/36 to 27/36 without regressing the established legacy suites. At that point, OCR's occasional rescue was no longer worth the latency, extra failure modes, and frames lost while the device waited for it to finish.
|
||||
|
||||
Direct-value detectors, smaller input sizes, MobileNet-style classifiers, optical-flow tracking, and multiple crop strategies were also tested. Some looked attractive on desktop metrics and failed where it mattered. In particular, a 224-pixel detector was faster but dropped the hometown 20 mph suite from 14/14 to 9/14. The 256-pixel detector remained the better production choice.
|
||||
|
||||
### Frame rate is part of accuracy
|
||||
|
||||
The most important evaluation lesson came from a route with seven manually bookmarked signs.
|
||||
|
||||
An early offline replay sampled the video at 5 fps and found several signs. The actual comma found almost none. The model files matched and the vision process was running, so the discrepancy initially looked mysterious.
|
||||
|
||||
The replay had been landing on lucky frames.
|
||||
|
||||
The camera produced 25 frames per second, but the live process only attempted inference every 0.4 seconds. When we replayed all 25 source frames while enforcing that real inference gate, the local result fell to 0 of 7, matching the drive. Halving the interval to 0.2 seconds moved the same honest replay to 5 of 7 candidate windows and 3 of 7 actual publishes.
|
||||
|
||||
From then on, a model was not evaluated only as a collection of independent images. Route replay had to include:
|
||||
|
||||
- source-frame timing;
|
||||
- the measured detector and classifier cost;
|
||||
- steady and follow-up inference intervals;
|
||||
- temporal confirmation rules;
|
||||
- CPU backoff;
|
||||
- and the distinction between a candidate and a value actually published to the UI or controller.
|
||||
|
||||
This distinction explained many arguments over numbers. A candidate means the detector and reader noticed something in a sign window. A publish means the temporal and confidence gates accepted it as the live speed limit. A model can read a sign correctly once and still fail to publish if a weak read requires a second frame that the device never processes.
|
||||
|
||||
Later-frame mining showed why this mattered. One pass revisited 405 known sign events and extracted 939 clearer frames from later in their tracks. Combined with track-aware classifier training and a reduction from four classifier crops to three, the measured runtime suite improved from 247 to 265 correct publications, including a gain from 211 to 229 in the priority 30-65 mph range. Better frame selection and less per-frame work helped at the same time.
|
||||
|
||||
### Making it fit beside openpilot
|
||||
|
||||
The comma is already running camera processing, the main driving models, localization, controls, the UI, and vehicle communication. A speed-limit sidecar cannot assume an idle CPU.
|
||||
|
||||
The early hybrid pipeline could take roughly 1.5 seconds per frame when the detector, crop reader, and OCR paths all fired. At that cadence, signs could pass through their brief readable window without ever being analyzed. It also added enough CPU pressure to coincide with camera/model frame-sync errors and temporary `locationd` alerts on some night drives.
|
||||
|
||||
Changing process priority was not the solution. The daemon was already running at a very low scheduling priority. The useful changes were to remove OCR from production, restrict detection to the right-side ROI, shrink the models carefully, avoid the extra reject-model pass, add device-load backoff, and use a faster follow-up cadence after a candidate appeared.
|
||||
|
||||
The current runtime requests a 0.15-second steady interval and a 0.10-second follow-up interval, but inference cost is the real limiter. On two measured routes, the complete process sustained about 1.77 to 1.81 inferences per second. The detector alone took roughly 0.43 to 0.44 seconds, and each classifier crop could add about 0.066 seconds. A sign may receive only 4 to 13 analyzed frames in a seven-second approach window.
|
||||
|
||||
This is why "run it at 10 Hz" is not a configuration change. The process can ask for 10 Hz, but it cannot start the next inference until the current one finishes.
|
||||
|
||||
### Debugging the system, not just the model
|
||||
|
||||
Several apparent model regressions were actually system problems.
|
||||
|
||||
|
||||
These failures led to better observability: model hashes, inference counts, interval reasons, detector and classifier timing, CPU backoff state, candidates, publications, and debug captures all became part of route analysis. We stopped asking only "is the model accurate?" and started asking "which exact model and runtime processed which exact frames, and what did it publish?"
|
||||
|
||||
### How we decide whether a model is better
|
||||
|
||||
The promotion process now uses several independent gates:
|
||||
|
||||
- Reviewed crop accuracy checks whether the classifier can read localized signs.
|
||||
- Hard-negative sets contain parking signs, traffic signals, truck restrictions, taillights, and other shapes that previously caused false reads.
|
||||
- A 100-event main route suite tests full candidate and publication behavior at measured comma cadence.
|
||||
- A separate 34-event held-out suite guards against tuning directly to the main set.
|
||||
- A hometown 20 mph suite protects a class that earlier models repeatedly missed.
|
||||
- Legacy suites ensure that old wins do not disappear.
|
||||
- Targeted night and glare replays test the conditions most likely to break normal validation assumptions.
|
||||
- On-device timing and live routes remain the final authority.
|
||||
|
||||
Many experiments were rejected despite improving one number. A glare-focused model lost broader recall. A traffic-signal hard-negative detector fixed one false positive and lost legitimate 35 mph signs. A more aggressive reject-classifier update reduced errors and then changed enough timing to expose different false publishes. Conservative weight interpolation often produced the safest result because it taught a narrow correction without erasing the representation that already worked.
|
||||
|
||||
The drive currently contains 87 detector experiment directories and 72 classifier experiment directories. Those are not 159 successive improvements. They are the record of resolution bake-offs, failed hard-negative passes, direct-value experiments, night/glare runs, integrated-reject models, temporal-crop training, and interpolations that mapped the boundaries of the system.
|
||||
|
||||
### Where accuracy is now
|
||||
|
||||
The current classifier, internally called `distilled-moonstone v3`, improved the deployed route suite without changing model size or runtime cost:
|
||||
|
||||
| Runtime suite | Previous tree | Current model |
|
||||
| --- | ---: | ---: |
|
||||
| Main route publishes | 78/100 | **92/100** |
|
||||
| Main wrong publishes | 0 | **0** |
|
||||
| Held-out publishes | 24/34 | **26/34** |
|
||||
| Hometown 20 mph signs | 14/14 | **14/14** |
|
||||
| Hard sun-glare check | 70 mph published | **70 mph published** |
|
||||
|
||||
These denominators are manually confirmed sign events, not arbitrary video frames. `92/100` means the full runtime pipeline published the correct value in 92 of 100 known sign windows while running with measured comma timing. It does not mean the model is 92 percent accurate on every road or every possible sign.
|
||||
|
||||
In ordinary daylight scenes, current performance is very good. The model is now reliable when signs are reasonably exposed and readable, while extreme image quality and limited frame opportunities remain the dominant failures.
|
||||
|
||||
The current result is also substantially better than the first public-data prototype, which missed nearly every sign on several routes and sometimes produced only a single useful event in an entire drive.
|
||||
|
||||
### The next hard barriers
|
||||
|
||||
The remaining misses are unusually informative. Of the 18 main-suite events the current model does not publish:
|
||||
|
||||
- 6 never produce a correct candidate at measured cadence.
|
||||
- 4 produce exactly one weak correct read, but cannot satisfy the two-read confirmation rule.
|
||||
- At an idealized 6.7 Hz cadence, the same model recovers 7 of those 10 publishes, reaching a theoretical 97/100, but also introduces one wrong publish.
|
||||
- The three stubborn cases are all 45 mph scenes: two remain unreadable even at ideal cadence, and one first reads 40 before seeing 45 too late.
|
||||
|
||||
That theoretical 97/100 is not a production accuracy claim. It is a diagnostic result showing that most remaining failures already contain a readable frame that the device does not process.
|
||||
|
||||
The next large improvement is therefore likely to come from throughput and temporal reuse, not from simply lowering confidence thresholds:
|
||||
|
||||
1. Make the detector materially faster without giving up the small-sign and 20 mph recall lost by the 224- and 192-pixel experiments.
|
||||
2. After a detector finds a sign, track or cheaply reclassify that crop across nearby frames instead of paying for another full detector pass every time.
|
||||
3. Investigate a more efficient inference backend or model architecture on comma hardware. Direct model microbenchmarks are useful, but the complete process and onroad CPU contention must remain the acceptance metric.
|
||||
4. Add targeted, human-reviewed sequences for the remaining 45 mph failures, night ghosting, distant signs, and direct-sun approaches.
|
||||
5. Improve sign applicability for minimum-speed, truck-only, school-zone, advisory, side-road, and exit-ramp signs without sacrificing the product's primary goal: finding normal regulatory speed limits.
|
||||
6. Keep model-fingerprinted backlog mining so every promoted model can rescan old routes into an isolated review set without silently replacing canonical labels.
|
||||
|
||||
Night remains difficult because the camera often produces fewer sharp frames, motion blur can create double digits, and reflective signs may be detected while they are still too distant to read. Glare is difficult for the opposite reason: contrast collapses and sign edges disappear into the sky. Both problems require sequence-level examples, not just one selected crop.
|
||||
|
||||
### What we learned
|
||||
|
||||
This was never one long training run. It was the construction of a data and evaluation system.
|
||||
|
||||
Public datasets made the first detector possible. A weak model made automatic collection possible. StarPilot users supplied the camera-native diversity that public data could not. Human review turned model proposals into trustworthy labels. Full-cadence replay exposed timing mistakes that image metrics hid. Live device telemetry kept CPU and process failures from being mistaken for neural-network failures. Conservative promotion gates preserved hard-won behavior while narrow experiments improved specific weaknesses.
|
||||
|
||||
The result is already useful and, in ordinary scenes, often impressively accurate. The path to the next jump is also clearer than it was at the beginning. We do not mainly need another giant pile of generic sign images. We need to process more of the good frames the comma already sees, teach the remaining hard sequences carefully, and continue judging the complete on-device system rather than the model in isolation.
|
||||
|
||||
That is the difference between training a traffic-sign classifier and building a speed-limit reader that works in a car.
|
||||
|
||||
### Technical references
|
||||
|
||||
- Runtime implementation: [`starpilot/system/speed_limit_vision.py`](../starpilot/system/speed_limit_vision.py)
|
||||
- Training runbook: [`docs/how-to/train-speed-limit-vision.md`](how-to/train-speed-limit-vision.md)
|
||||
- Training, mining, review, and evaluation tools: [`scripts/speed_limit_vision`](../scripts/speed_limit_vision)
|
||||
- Deployed model assets: [`starpilot/assets/vision_models`](../starpilot/assets/vision_models)
|
||||
+70
-6
@@ -4,7 +4,25 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
|
||||
|
||||
source "$DIR/launch_env.sh"
|
||||
|
||||
export SP_BOOT_TIMING_LOG="${SP_BOOT_TIMING_LOG:-/tmp/starpilot_boot_timing.log}"
|
||||
: > "$SP_BOOT_TIMING_LOG" 2>/dev/null || true
|
||||
SP_LAUNCH_LAST_SECONDS=$SECONDS
|
||||
|
||||
function sp_boot_timing_line {
|
||||
echo "$1"
|
||||
printf '%s\n' "$1" >> "$SP_BOOT_TIMING_LOG" 2>/dev/null || true
|
||||
}
|
||||
|
||||
function sp_launch_timing {
|
||||
local now=$SECONDS
|
||||
local delta=$((now - SP_LAUNCH_LAST_SECONDS))
|
||||
sp_boot_timing_line "SP_BOOT_TIMING launch $1 +${delta}s total=${now}s"
|
||||
SP_LAUNCH_LAST_SECONDS=$now
|
||||
}
|
||||
|
||||
function agnos_init {
|
||||
sp_launch_timing "agnos_init_start"
|
||||
|
||||
# TODO: move this to agnos
|
||||
sudo rm -f /data/etc/NetworkManager/system-connections/*.nmmeta
|
||||
|
||||
@@ -36,7 +54,16 @@ function agnos_init {
|
||||
sudo rm -f /data/misc/display/color_cal/color_cal /data/misc/display/color_cal/source.sha256
|
||||
|
||||
# Check if AGNOS update is required
|
||||
if [ $(< /VERSION) != "$AGNOS_VERSION" ]; then
|
||||
AGNOS_CURRENT_VERSION="$(< /VERSION)"
|
||||
AGNOS_UPDATE_REQUIRED=1
|
||||
for accepted_version in $AGNOS_ACCEPTED_VERSIONS; do
|
||||
if [ "$AGNOS_CURRENT_VERSION" = "$accepted_version" ]; then
|
||||
AGNOS_UPDATE_REQUIRED=0
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$AGNOS_UPDATE_REQUIRED" = "1" ]; then
|
||||
AGNOS_PY="$DIR/system/hardware/tici/agnos.py"
|
||||
MANIFEST="$DIR/system/hardware/tici/agnos.json"
|
||||
if $AGNOS_PY --verify $MANIFEST; then
|
||||
@@ -44,9 +71,13 @@ function agnos_init {
|
||||
fi
|
||||
$DIR/system/hardware/tici/updater $AGNOS_PY $MANIFEST
|
||||
fi
|
||||
|
||||
sp_launch_timing "agnos_init_done"
|
||||
}
|
||||
|
||||
function launch {
|
||||
sp_launch_timing "launch_start"
|
||||
|
||||
# Remove orphaned git lock if it exists on boot
|
||||
[ -f "$DIR/.git/index.lock" ] && rm -f $DIR/.git/index.lock
|
||||
|
||||
@@ -83,30 +114,37 @@ function launch {
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
sp_launch_timing "overlay_check_done"
|
||||
|
||||
# handle pythonpath
|
||||
ln -sfn $(pwd) /data/pythonpath
|
||||
export BASEDIR="$DIR"
|
||||
export PYTHONPATH="$DIR/starpilot/third_party:$PWD"
|
||||
sp_launch_timing "pythonpath_done"
|
||||
|
||||
# hardware specific init
|
||||
if [ -f /AGNOS ]; then
|
||||
agnos_init
|
||||
fi
|
||||
sp_launch_timing "hardware_init_done"
|
||||
|
||||
# write tmux scrollback to a file
|
||||
tmux capture-pane -pq -S-1000 > /tmp/launch_log
|
||||
sp_launch_timing "capture_launch_log_done"
|
||||
|
||||
# start manager
|
||||
cd system/manager
|
||||
|
||||
sp_launch_timing "launch_param_migrations_start"
|
||||
if ! python3 ./launch_param_migrations.py; then
|
||||
echo "Launch param migrations failed; continuing boot."
|
||||
fi
|
||||
sp_launch_timing "launch_param_migrations_done"
|
||||
|
||||
# Bootstrap runtime (e.g. /usr/comma after reset/uninstall) must go straight
|
||||
# to manager/setup flow. Do not run StarPilot prebuilt checks/builds here.
|
||||
if [ "$DIR" = "/usr/comma" ] || [ ! -d "$DIR/.git" ]; then
|
||||
sp_launch_timing "bootstrap_manager_start"
|
||||
./manager.py
|
||||
while true; do sleep 1; done
|
||||
fi
|
||||
@@ -114,15 +152,35 @@ function launch {
|
||||
function prebuilt_runtime_compatible {
|
||||
python3 - <<'PY'
|
||||
import importlib
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import time
|
||||
|
||||
start = time.monotonic()
|
||||
last = start
|
||||
log_path = os.environ.get("SP_BOOT_TIMING_LOG")
|
||||
|
||||
def emit(line):
|
||||
print(line, flush=True)
|
||||
if log_path:
|
||||
try:
|
||||
with open(log_path, "a") as f:
|
||||
f.write(line + "\n")
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def log_step(label):
|
||||
global last
|
||||
now = time.monotonic()
|
||||
emit(f"SP_BOOT_TIMING prebuilt_compat {label} +{now - last:.3f}s total={now - start:.3f}s")
|
||||
last = now
|
||||
|
||||
mods = [
|
||||
"openpilot.common.params_pyx",
|
||||
"msgq.ipc_pyx",
|
||||
"msgq.visionipc.visionipc_pyx",
|
||||
"openpilot.common.transformations.transformations",
|
||||
"openpilot.selfdrive.modeld.models.commonmodel_pyx",
|
||||
"openpilot.selfdrive.pandad.pandad_api_impl",
|
||||
"openpilot.selfdrive.controls.lib.lateral_mpc_lib.c_generated_code.acados_ocp_solver_pyx",
|
||||
"openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.c_generated_code.acados_ocp_solver_pyx",
|
||||
@@ -134,15 +192,15 @@ for mod in mods:
|
||||
except Exception as e:
|
||||
print(f"Prebuilt compatibility failure in {mod}: {e}", file=sys.stderr)
|
||||
raise
|
||||
log_step(f"import:{mod}")
|
||||
|
||||
repo_root = Path.cwd().parents[1]
|
||||
required_files = [
|
||||
repo_root / "selfdrive/modeld/models/driving_vision_metadata.pkl",
|
||||
repo_root / "selfdrive/modeld/models/driving_policy_metadata.pkl",
|
||||
repo_root / "selfdrive/modeld/models/driving_vision_tinygrad.pkl",
|
||||
repo_root / "selfdrive/modeld/models/driving_policy_tinygrad.pkl",
|
||||
repo_root / "selfdrive/modeld/models/driving_tinygrad.pkl",
|
||||
repo_root / "selfdrive/modeld/models/dmonitoring_model_metadata.pkl",
|
||||
repo_root / "selfdrive/modeld/models/dmonitoring_model_tinygrad.pkl",
|
||||
repo_root / "selfdrive/modeld/models/dm_warp_1928x1208_tinygrad.pkl",
|
||||
repo_root / "selfdrive/modeld/models/dm_warp_1344x760_tinygrad.pkl",
|
||||
repo_root / "selfdrive/pandad/pandad_api_impl.so",
|
||||
repo_root / "selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/acados_ocp_solver_pyx.so",
|
||||
repo_root / "selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/libacados_ocp_solver_lat.so",
|
||||
@@ -154,6 +212,7 @@ required_files = [
|
||||
for path in required_files:
|
||||
if not path.is_file():
|
||||
raise FileNotFoundError(f"Missing prebuilt runtime artifact: {path}")
|
||||
log_step("required_files")
|
||||
PY
|
||||
}
|
||||
|
||||
@@ -162,14 +221,19 @@ PY
|
||||
USE_PREBUILT=$(tr -d '\n' < /data/params/d/UsePrebuilt)
|
||||
fi
|
||||
|
||||
sp_launch_timing "prebuilt_decision_done"
|
||||
if [ "$USE_PREBUILT" = "1" ] && [ -f $DIR/prebuilt ] && ! prebuilt_runtime_compatible; then
|
||||
echo "Prebuilt runtime artifacts are incompatible on this device; rebuilding locally."
|
||||
USE_PREBUILT=0
|
||||
fi
|
||||
sp_launch_timing "prebuilt_compat_done"
|
||||
|
||||
if [ "$USE_PREBUILT" != "1" ] || [ ! -f $DIR/prebuilt ]; then
|
||||
sp_launch_timing "build_start"
|
||||
./build.py
|
||||
sp_launch_timing "build_done"
|
||||
fi
|
||||
sp_launch_timing "manager_start"
|
||||
./manager.py
|
||||
|
||||
# if broken, keep on screen error
|
||||
|
||||
+5
-1
@@ -21,7 +21,11 @@ fi
|
||||
export QCOM_PRIORITY=12
|
||||
|
||||
if [ -z "$AGNOS_VERSION" ]; then
|
||||
export AGNOS_VERSION="12.8.16"
|
||||
export AGNOS_VERSION="12.8.28"
|
||||
fi
|
||||
|
||||
if [ -z "$AGNOS_ACCEPTED_VERSIONS" ]; then
|
||||
export AGNOS_ACCEPTED_VERSIONS="$AGNOS_VERSION"
|
||||
fi
|
||||
|
||||
export STAGING_ROOT="/data/safe_staging"
|
||||
|
||||
@@ -34,9 +34,11 @@ import requests
|
||||
import zstandard
|
||||
|
||||
from cereal import log
|
||||
from openpilot.tools.lib.auth_config import DEFAULT_API_HOST, KONIK_API_HOST, get_token, normalize_api_host
|
||||
|
||||
|
||||
API_HOST = os.getenv("COMMA_API_HOST", "https://api.commadotai.com").rstrip("/")
|
||||
API_HOST = normalize_api_host(os.getenv("COMMA_API_HOST") or os.getenv("API_HOST") or DEFAULT_API_HOST)
|
||||
API_HOSTS = [API_HOST] if os.getenv("COMMA_API_HOST") or os.getenv("API_HOST") else [DEFAULT_API_HOST, KONIK_API_HOST]
|
||||
ROUTE_ID_RE = re.compile(r"([0-9a-f]{16})/([^/]+)")
|
||||
|
||||
|
||||
@@ -146,12 +148,12 @@ def parse_route_id(raw: str) -> RouteId:
|
||||
return RouteId(dongle_id=dongle_id, log_id=log_id)
|
||||
|
||||
|
||||
def route_url(route: RouteId) -> str:
|
||||
return f"{API_HOST}/v1/route/{quote(route.canonical_name, safe='')}/"
|
||||
def route_url(route: RouteId, api_host: str) -> str:
|
||||
return f"{api_host}/v1/route/{quote(route.canonical_name, safe='')}/"
|
||||
|
||||
|
||||
def route_files_url(route: RouteId) -> str:
|
||||
return f"{API_HOST}/v1/route/{quote(route.canonical_name, safe='')}/files"
|
||||
def route_files_url(route: RouteId, api_host: str) -> str:
|
||||
return f"{api_host}/v1/route/{quote(route.canonical_name, safe='')}/files"
|
||||
|
||||
|
||||
def format_segments(segments: list[int]) -> str:
|
||||
@@ -184,8 +186,13 @@ def relative_posix(path: Path, root: Path) -> str:
|
||||
return path.relative_to(root).as_posix()
|
||||
|
||||
|
||||
def fetch_json(session: requests.Session, url: str, timeout: float) -> Any:
|
||||
response = session.get(url, timeout=timeout, allow_redirects=True)
|
||||
def api_headers(api_host: str) -> dict[str, str] | None:
|
||||
token = get_token(api_host)
|
||||
return {"Authorization": f"JWT {token}"} if token else None
|
||||
|
||||
|
||||
def fetch_json(session: requests.Session, url: str, timeout: float, api_host: str | None = None) -> Any:
|
||||
response = session.get(url, timeout=timeout, allow_redirects=True, headers=api_headers(api_host) if api_host else None)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
@@ -312,24 +319,42 @@ def filename_from_url(url: str) -> str:
|
||||
|
||||
|
||||
def validate_route(route: RouteId, session: requests.Session, timeout: float) -> dict[str, Any]:
|
||||
try:
|
||||
route_meta = fetch_json(session, route_url(route), timeout)
|
||||
except requests.HTTPError as exc:
|
||||
status_code = exc.response.status_code if exc.response is not None else "unknown"
|
||||
raise ValidationError(route, [f"route is not publicly accessible from comma connect (HTTP {status_code})."]) from exc
|
||||
route_meta = None
|
||||
files_payload = None
|
||||
api_host = None
|
||||
not_found_hosts = []
|
||||
|
||||
for candidate_host in API_HOSTS:
|
||||
try:
|
||||
route_meta = fetch_json(session, route_url(route, candidate_host), timeout, candidate_host)
|
||||
except requests.HTTPError as exc:
|
||||
status_code = exc.response.status_code if exc.response is not None else "unknown"
|
||||
if status_code == 404 and len(API_HOSTS) > 1:
|
||||
not_found_hosts.append(candidate_host)
|
||||
continue
|
||||
raise ValidationError(route, [f"route is not publicly accessible from {candidate_host} (HTTP {status_code})."]) from exc
|
||||
|
||||
try:
|
||||
files_payload = fetch_json(session, route_files_url(route, candidate_host), timeout, candidate_host)
|
||||
except requests.HTTPError as exc:
|
||||
status_code = exc.response.status_code if exc.response is not None else "unknown"
|
||||
if status_code == 404 and len(API_HOSTS) > 1:
|
||||
not_found_hosts.append(candidate_host)
|
||||
continue
|
||||
raise ValidationError(route, [f"public route files could not be fetched from {candidate_host} (HTTP {status_code})."]) from exc
|
||||
|
||||
api_host = candidate_host
|
||||
break
|
||||
|
||||
if route_meta is None or files_payload is None or api_host is None:
|
||||
raise ValidationError(route, [f"route was not found on: {', '.join(not_found_hosts) or ', '.join(API_HOSTS)}."])
|
||||
|
||||
if not route_meta.get("is_public", False):
|
||||
raise ValidationError(route, ["route metadata loaded, but `is_public` was false."])
|
||||
|
||||
try:
|
||||
files_payload = fetch_json(session, route_files_url(route), timeout)
|
||||
except requests.HTTPError as exc:
|
||||
status_code = exc.response.status_code if exc.response is not None else "unknown"
|
||||
raise ValidationError(route, [f"public route files could not be fetched from comma connect (HTTP {status_code})."]) from exc
|
||||
|
||||
expected_segments = expected_segment_count(route_meta, files_payload)
|
||||
if expected_segments <= 0:
|
||||
raise ValidationError(route, ["could not determine any route segments from comma connect."])
|
||||
raise ValidationError(route, ["could not determine any route segments from the route API."])
|
||||
|
||||
failures: list[str] = []
|
||||
stream_urls: dict[str, list[Any]] = {}
|
||||
@@ -384,6 +409,7 @@ def validate_route(route: RouteId, session: requests.Session, timeout: float) ->
|
||||
raise ValidationError(route, failures)
|
||||
|
||||
return {
|
||||
"api_host": api_host,
|
||||
"route_meta": route_meta,
|
||||
"files_payload": files_payload,
|
||||
"expected_segments": expected_segments,
|
||||
@@ -532,6 +558,7 @@ def print_validation_summary(route: RouteId, validation: dict[str, Any]) -> None
|
||||
params = validation["params"]
|
||||
map_tiles: MapTileSummary = validation["map_tiles"]
|
||||
print(f"Validated {route.cli_name}")
|
||||
print(f" route API: {validation['api_host']}")
|
||||
print(f" public route: yes")
|
||||
print(f" segments: {validation['expected_segments']}")
|
||||
print(f" VisionSpeedLimitDetection: {params.get('VisionSpeedLimitDetection', '')}")
|
||||
|
||||
+2
-2
@@ -32,10 +32,10 @@ nav:
|
||||
- What is a car port?: car-porting/what-is-a-car-port.md
|
||||
- Porting a car brand: car-porting/brand-port.md
|
||||
- Porting a car model: car-porting/model-port.md
|
||||
- Contributing:
|
||||
- Contribute:
|
||||
- Contributing Guide: contributing/contribute.md
|
||||
- Roadmap: contributing/roadmap.md
|
||||
#- Architecture: contributing/architecture.md
|
||||
- Contributing Guide →: https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md
|
||||
- Links:
|
||||
- Blog →: https://blog.comma.ai
|
||||
- Bounties →: https://comma.ai/bounties
|
||||
|
||||
@@ -61,6 +61,7 @@ class TestVisionIpc:
|
||||
recv_buf = self.client.recv()
|
||||
assert recv_buf is not None
|
||||
assert recv_buf.data.view('<i4')[0] == 1234
|
||||
assert recv_buf.frame_id == 1337
|
||||
assert self.client.frame_id == 1337
|
||||
del self.client
|
||||
del self.server
|
||||
|
||||
@@ -30,6 +30,7 @@ cdef extern from "msgq/visionipc/visionbuf.h":
|
||||
size_t idx
|
||||
cl_mem buf_cl
|
||||
void set_frame_id(uint64_t id)
|
||||
uint64_t get_frame_id()
|
||||
|
||||
cdef extern from "msgq/visionipc/visionipc.h":
|
||||
struct VisionIpcBufExtra:
|
||||
|
||||
@@ -67,6 +67,10 @@ cdef class VisionBuf:
|
||||
def fd(self):
|
||||
return self.buf.fd
|
||||
|
||||
@property
|
||||
def frame_id(self):
|
||||
return self.buf.get_frame_id()
|
||||
|
||||
|
||||
cdef class VisionIpcServer:
|
||||
cdef cppVisionIpcServer * server
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -334,6 +334,9 @@ def get_car(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multip
|
||||
CP.carFw = car_fw
|
||||
CP.fingerprintSource = source
|
||||
CP.fuzzyFingerprint = not exact_match
|
||||
post_fingerprint_params = getattr(CarInterface, "apply_post_fingerprint_params", None)
|
||||
if post_fingerprint_params is not None:
|
||||
post_fingerprint_params(CP, candidate, fingerprints, car_fw)
|
||||
|
||||
FPCP: StarPilotCarParams = CarInterface.get_starpilot_params(candidate, fingerprints, car_fw, CP, starpilot_toggles)
|
||||
|
||||
|
||||
@@ -2,9 +2,28 @@ from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus, DT_CTRL
|
||||
from opendbc.car.lateral import apply_meas_steer_torque_limits
|
||||
from opendbc.car.chrysler import chryslercan
|
||||
from opendbc.car.chrysler.values import RAM_CARS, RAM_DT, CarControllerParams, ChryslerFlags
|
||||
from opendbc.car.chrysler.values import JEEPS, RAM_CARS, RAM_DT, CarControllerParams, ChryslerFlags, ChryslerSafetyFlags, ChryslerStarPilotFlags
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
|
||||
JEEP_BRAKE_HOLD_DEFAULT_DECEL = -2.0
|
||||
JEEP_BRAKE_HOLD_MIN_DECEL = -0.5
|
||||
JEEP_BRAKE_HOLD_MAX_DECEL = -3.0
|
||||
|
||||
|
||||
def clip_jeep_brake_hold_decel(decel: float) -> float:
|
||||
return max(JEEP_BRAKE_HOLD_MAX_DECEL, min(JEEP_BRAKE_HOLD_MIN_DECEL, float(decel)))
|
||||
|
||||
|
||||
def supports_jeep_brake_hold(CP, brake_hold_enabled: bool) -> bool:
|
||||
safety_cfg = getattr(CP, "safetyConfigs", ())
|
||||
safety_param = safety_cfg[0].safetyParam if safety_cfg else 0
|
||||
return (
|
||||
brake_hold_enabled and
|
||||
getattr(CP, "pcmCruise", False) and
|
||||
CP.carFingerprint in JEEPS and
|
||||
bool(safety_param & ChryslerSafetyFlags.JEEP_BRAKE_HOLD.value)
|
||||
)
|
||||
|
||||
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_names, CP):
|
||||
@@ -18,6 +37,8 @@ class CarController(CarControllerBase):
|
||||
|
||||
self.packer = CANPacker(dbc_names[Bus.pt])
|
||||
self.params = CarControllerParams(CP)
|
||||
self.jeep_brake_hold_decel = JEEP_BRAKE_HOLD_DEFAULT_DECEL
|
||||
self.last_das_3_counter = -1
|
||||
|
||||
def update(self, CC, CS, now_nanos, starpilot_toggles):
|
||||
can_sends = []
|
||||
@@ -50,7 +71,9 @@ class CarController(CarControllerBase):
|
||||
|
||||
# TODO: can we make this more sane? why is it different for all the cars?
|
||||
lkas_control_bit = self.lkas_control_bit_prev
|
||||
if self.CP.carFingerprint in RAM_DT:
|
||||
if self.FPCP is not None and self.FPCP.flags & ChryslerStarPilotFlags.NO_MIN_STEERING_SPEED:
|
||||
lkas_control_bit = CC.latActive
|
||||
elif self.CP.carFingerprint in RAM_DT:
|
||||
if self.CP.minEnableSpeed <= CS.out.vEgo <= self.CP.minEnableSpeed + 0.5:
|
||||
lkas_control_bit = True
|
||||
if (self.CP.minEnableSpeed >= 14.5) and (CS.out.gearShifter != 2):
|
||||
@@ -80,6 +103,11 @@ class CarController(CarControllerBase):
|
||||
|
||||
can_sends.append(chryslercan.create_lkas_command(self.packer, self.CP, int(apply_torque), lkas_control_bit))
|
||||
|
||||
if supports_jeep_brake_hold(self.CP, getattr(starpilot_toggles, "jeep_brake_hold", False)):
|
||||
self.update_jeep_brake_hold(CC, CS, can_sends)
|
||||
elif getattr(CS, "brake_hold", False):
|
||||
CS.brake_hold = False
|
||||
|
||||
self.frame += 1
|
||||
|
||||
new_actuators = CC.actuators.as_builder()
|
||||
@@ -87,3 +115,43 @@ class CarController(CarControllerBase):
|
||||
new_actuators.torqueOutputCan = self.apply_torque_last
|
||||
|
||||
return new_actuators, can_sends
|
||||
|
||||
def update_jeep_brake_hold(self, CC, CS, can_sends):
|
||||
if not getattr(CS, "das_3", None):
|
||||
CS.brake_hold = False
|
||||
return
|
||||
|
||||
counter_changed = CS.das_3.get("COUNTER") != self.last_das_3_counter
|
||||
self.last_das_3_counter = CS.das_3.get("COUNTER")
|
||||
|
||||
if not CS.brake_hold and CS.cruise_active_actual and CS.acc_decelerating and CS.out.standstill:
|
||||
CS.brake_hold = True
|
||||
self.jeep_brake_hold_decel = JEEP_BRAKE_HOLD_DEFAULT_DECEL
|
||||
|
||||
driver_intervened = (
|
||||
CC.cruiseControl.cancel or
|
||||
CS.out.gasPressed or
|
||||
CS.out.brakePressed or
|
||||
not CS.forward_gear or
|
||||
not CS.out.standstill
|
||||
)
|
||||
if CS.brake_hold and driver_intervened:
|
||||
CS.brake_hold = False
|
||||
return
|
||||
|
||||
if not CS.brake_hold:
|
||||
return
|
||||
|
||||
if CS.cruise_active_actual:
|
||||
self.jeep_brake_hold_decel = clip_jeep_brake_hold_decel(
|
||||
min(self.jeep_brake_hold_decel, CS.das_3.get("ACC_DECEL", JEEP_BRAKE_HOLD_DEFAULT_DECEL))
|
||||
)
|
||||
return
|
||||
|
||||
counter_offset = 2 if counter_changed else 3
|
||||
can_sends.append(chryslercan.create_das_3_command(self.packer, counter_offset, self.jeep_brake_hold_decel, CS.das_3))
|
||||
|
||||
if self.frame % 10 == 0:
|
||||
can_sends.append(chryslercan.create_cruise_buttons(
|
||||
self.packer, CS.button_counter + 1, 0, CS.button_message, resume=True
|
||||
))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from cereal import custom
|
||||
from opendbc.can import CANDefine, CANParser
|
||||
from opendbc.car import Bus, create_button_events, structs
|
||||
from opendbc.car.chrysler.values import DBC, STEER_THRESHOLD, RAM_CARS, ChryslerStarPilotFlags
|
||||
from opendbc.car.chrysler.values import DBC, JEEPS, STEER_THRESHOLD, RAM_CARS, ChryslerStarPilotFlags
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
|
||||
@@ -29,6 +29,11 @@ class CarState(CarStateBase):
|
||||
self.button_message = "CRUISE_BUTTONS_ALT" if FPCP.flags & ChryslerStarPilotFlags.RAM_HD_ALT_BUTTONS else "CRUISE_BUTTONS"
|
||||
|
||||
self.lkas_button = 0
|
||||
self.brake_hold = False
|
||||
self.cruise_active_actual = False
|
||||
self.forward_gear = False
|
||||
self.acc_decelerating = False
|
||||
self.das_3 = {}
|
||||
|
||||
@staticmethod
|
||||
def get_lkas_button(pt_signals, is_ram: bool) -> bool:
|
||||
@@ -93,6 +98,13 @@ class CarState(CarStateBase):
|
||||
ret.cruiseState.standstill = cp_cruise.vl["DAS_3"]["ACC_STANDSTILL"] == 1
|
||||
ret.accFaulted = cp_cruise.vl["DAS_3"]["ACC_FAULTED"] != 0
|
||||
|
||||
if self.CP.carFingerprint in JEEPS:
|
||||
self.forward_gear = ret.gearShifter == structs.CarState.GearShifter.drive
|
||||
self.cruise_active_actual = ret.cruiseState.enabled
|
||||
self.acc_decelerating = cp_cruise.vl["DAS_3"]["ACC_DECEL"] < -0.5
|
||||
self.das_3 = dict(cp_cruise.vl["DAS_3"])
|
||||
ret.brakeHoldActive = self.brake_hold
|
||||
|
||||
if self.CP.carFingerprint in RAM_CARS:
|
||||
# Auto High Beam isn't Located in this message on chrysler or jeep currently located in 729 message
|
||||
self.auto_high_beam = cp_cam.vl["DAS_6"]['AUTO_HIGH_BEAM_ON']
|
||||
|
||||
@@ -73,6 +73,21 @@ def create_cruise_buttons(packer, frame, bus, button_message, cancel=False, resu
|
||||
return packer.make_can_msg(button_message, bus, values)
|
||||
|
||||
|
||||
def create_das_3_command(packer, counter_offset, brake_decel, das_3):
|
||||
values = das_3.copy()
|
||||
values["ACC_AVAILABLE"] = 1
|
||||
values["ACC_ACTIVE"] = 1
|
||||
values["ACC_GO"] = 0
|
||||
values["ACC_STANDSTILL"] = 0
|
||||
values["ACC_DECEL_REQ"] = 1
|
||||
values["ACC_DECEL"] = brake_decel
|
||||
values["ACC_BRK_PREP"] = 0
|
||||
values["ENGINE_TORQUE_REQUEST_MAX"] = 0
|
||||
values["GR_MAX_REQ"] = 2
|
||||
values["COUNTER"] = (das_3["COUNTER"] + counter_offset) % 0x10
|
||||
return packer.make_can_msg("DAS_3", 0, values)
|
||||
|
||||
|
||||
def chrysler_checksum(address: int, sig, d: bytearray) -> int:
|
||||
checksum = 0xFF
|
||||
for j in range(len(d) - 1):
|
||||
|
||||
@@ -3,7 +3,7 @@ from opendbc.car import get_safety_config, structs
|
||||
from opendbc.car.chrysler.carcontroller import CarController
|
||||
from opendbc.car.chrysler.carstate import CarState
|
||||
from opendbc.car.chrysler.radar_interface import RadarInterface
|
||||
from opendbc.car.chrysler.values import CAR, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags, ChryslerSafetyFlags
|
||||
from opendbc.car.chrysler.values import CAR, JEEPS, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags, ChryslerSafetyFlags
|
||||
from opendbc.car.interfaces import CarInterfaceBase
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.safetyConfigs[0].safetyParam |= ChryslerSafetyFlags.RAM_HD.value
|
||||
elif candidate in RAM_DT:
|
||||
ret.safetyConfigs[0].safetyParam |= ChryslerSafetyFlags.RAM_DT.value
|
||||
elif candidate in JEEPS:
|
||||
ret.safetyConfigs[0].safetyParam |= ChryslerSafetyFlags.JEEP_BRAKE_HOLD.value
|
||||
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
if candidate not in RAM_CARS:
|
||||
|
||||
@@ -12,6 +12,7 @@ Ecu = CarParams.Ecu
|
||||
class ChryslerSafetyFlags(IntFlag):
|
||||
RAM_DT = 1
|
||||
RAM_HD = 2
|
||||
JEEP_BRAKE_HOLD = 4
|
||||
|
||||
|
||||
class ChryslerFlags(IntFlag):
|
||||
@@ -21,6 +22,7 @@ class ChryslerFlags(IntFlag):
|
||||
|
||||
class ChryslerStarPilotFlags(IntFlag):
|
||||
RAM_HD_ALT_BUTTONS = 1
|
||||
NO_MIN_STEERING_SPEED = 2
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -139,6 +141,7 @@ STEER_THRESHOLD = 120
|
||||
RAM_DT = {CAR.RAM_1500_5TH_GEN, }
|
||||
RAM_HD = {CAR.RAM_HD_5TH_GEN, }
|
||||
RAM_CARS = RAM_DT | RAM_HD
|
||||
JEEPS = {CAR.JEEP_GRAND_CHEROKEE, CAR.JEEP_GRAND_CHEROKEE_2019}
|
||||
|
||||
|
||||
CHRYSLER_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
|
||||
|
||||
@@ -258,6 +258,8 @@ MIGRATION = {
|
||||
"KIA EV6 2025": HYUNDAI.KIA_EV6_2025,
|
||||
"KIA EV9 2025": HYUNDAI.KIA_EV9,
|
||||
"KIA CARNIVAL 4TH GEN": HYUNDAI.KIA_CARNIVAL_4TH_GEN,
|
||||
"KIA CARNIVAL 2025": HYUNDAI.KIA_CARNIVAL_2025,
|
||||
"KIA CARNIVAL HYBRID 4TH GEN": HYUNDAI.KIA_CARNIVAL_HEV_4TH_GEN,
|
||||
"GENESIS GV60 ELECTRIC 1ST GEN": HYUNDAI.GENESIS_GV60_EV_1ST_GEN,
|
||||
"GENESIS G70 2018": HYUNDAI.GENESIS_G70,
|
||||
"GENESIS G70 2020": HYUNDAI.GENESIS_G70_2020,
|
||||
|
||||
@@ -16,6 +16,11 @@ AVERAGE_ROAD_ROLL = 0.06 # ~3.4 degrees, 6% superelevation. higher actual roll
|
||||
MAX_LATERAL_ACCEL = ISO_LATERAL_ACCEL - (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL) # ~2.4 m/s^2
|
||||
|
||||
|
||||
def apply_ford_angle(desired_angle_deg: float, current_angle_deg: float) -> float:
|
||||
relative_angle = desired_angle_deg - current_angle_deg
|
||||
return float(np.clip(relative_angle, -5.8, 5.8))
|
||||
|
||||
|
||||
def anti_overshoot(apply_curvature, apply_curvature_last, v_ego):
|
||||
diff = 0.1
|
||||
tau = 5 # 5s smooths over the overshoot
|
||||
@@ -65,6 +70,7 @@ class CarController(CarControllerBase):
|
||||
self.CAN = fordcan.CanBus(CP)
|
||||
|
||||
self.apply_curvature_last = 0
|
||||
self.apply_angle_last = 0
|
||||
self.anti_overshoot_curvature_last = 0
|
||||
self.accel = 0.0
|
||||
self.gas = 0.0
|
||||
@@ -98,38 +104,62 @@ class CarController(CarControllerBase):
|
||||
can_sends.append(fordcan.create_button_msg(self.packer, self.CAN.camera, CS.buttons_stock_values, tja_toggle=True))
|
||||
|
||||
### lateral control ###
|
||||
# send steer msg at 20Hz
|
||||
if (self.frame % CarControllerParams.STEER_STEP) == 0:
|
||||
# Bronco and some other cars consistently overshoot curv requests
|
||||
# Apply some deadzone + smoothing convergence to avoid oscillations
|
||||
if self.CP.carFingerprint in (CAR.FORD_BRONCO_SPORT_MK1, CAR.FORD_F_150_MK14):
|
||||
self.anti_overshoot_curvature_last = anti_overshoot(actuators.curvature, self.anti_overshoot_curvature_last, CS.out.vEgoRaw)
|
||||
apply_curvature = self.anti_overshoot_curvature_last
|
||||
if self.CP.flags & FordFlags.LKA_STEERING:
|
||||
lka_active = CC.latActive and CS.lkas_available
|
||||
if lka_active:
|
||||
self.apply_angle_last = apply_ford_angle(actuators.steeringAngleDeg, CS.out.steeringAngleDeg)
|
||||
current_curvature = -CS.out.yawRate / max(CS.out.vEgoRaw, 0.1)
|
||||
self.apply_curvature_last = apply_ford_curvature_limits(actuators.curvature, self.apply_curvature_last, current_curvature,
|
||||
CS.out.vEgoRaw, 0., True, self.CP)
|
||||
else:
|
||||
apply_curvature = actuators.curvature
|
||||
self.apply_angle_last = 0.
|
||||
self.apply_curvature_last = 0.
|
||||
|
||||
# apply rate limits, curvature error limit, and clip to signal range
|
||||
current_curvature = -CS.out.yawRate / max(CS.out.vEgoRaw, 0.1)
|
||||
# Keep the stock LMC heartbeat present while steering through Lane_Assist_Data1.
|
||||
if (self.frame % CarControllerParams.STEER_STEP) == 0:
|
||||
can_sends.append(fordcan.create_lat_ctl_msg(self.packer, self.CAN, False, 0., 0., 0., 0.,
|
||||
stock_lmc=CS.lateral_motion_control))
|
||||
|
||||
self.apply_curvature_last = apply_ford_curvature_limits(apply_curvature, self.apply_curvature_last, current_curvature,
|
||||
CS.out.vEgoRaw, 0., CC.latActive, self.CP)
|
||||
if (self.frame % CarControllerParams.LKA_STEP) == 0:
|
||||
direction = 0
|
||||
if lka_active:
|
||||
direction = 2 if CS.out.steeringAngleDeg > 0 else 4
|
||||
ramp_type = 1 if abs(self.apply_angle_last) >= 5 else 0
|
||||
can_sends.append(fordcan.create_lka_msg(self.packer, self.CAN, active=lka_active, apply_angle=self.apply_angle_last,
|
||||
direction=direction, ramp_type=ramp_type, curvature=-self.apply_curvature_last))
|
||||
else:
|
||||
# send steer msg at 20Hz
|
||||
if (self.frame % CarControllerParams.STEER_STEP) == 0:
|
||||
# Bronco and some other cars consistently overshoot curv requests
|
||||
# Apply some deadzone + smoothing convergence to avoid oscillations
|
||||
if self.CP.carFingerprint in (CAR.FORD_BRONCO_SPORT_MK1, CAR.FORD_F_150_MK14):
|
||||
self.anti_overshoot_curvature_last = anti_overshoot(actuators.curvature, self.anti_overshoot_curvature_last, CS.out.vEgoRaw)
|
||||
apply_curvature = self.anti_overshoot_curvature_last
|
||||
else:
|
||||
apply_curvature = actuators.curvature
|
||||
|
||||
if self.CP.flags & FordFlags.CANFD:
|
||||
# TODO: extended mode
|
||||
# Ford uses four individual signals to dictate how to drive to the car. Curvature alone (limited to 0.02m/s^2)
|
||||
# can actuate the steering for a large portion of any lateral movements. However, in order to get further control on
|
||||
# steer actuation, the other three signals are necessary. Ford controls vehicles differently than most other makes.
|
||||
# A detailed explanation on ford control can be found here:
|
||||
# https://www.f150gen14.com/forum/threads/introducing-bluepilot-a-ford-specific-fork-for-comma3x-openpilot.24241/#post-457706
|
||||
mode = 1 if CC.latActive else 0
|
||||
counter = (self.frame // CarControllerParams.STEER_STEP) % 0x10
|
||||
can_sends.append(fordcan.create_lat_ctl2_msg(self.packer, self.CAN, mode, 0., 0., -self.apply_curvature_last, 0., counter))
|
||||
else:
|
||||
can_sends.append(fordcan.create_lat_ctl_msg(self.packer, self.CAN, CC.latActive, 0., 0., -self.apply_curvature_last, 0.))
|
||||
# apply rate limits, curvature error limit, and clip to signal range
|
||||
current_curvature = -CS.out.yawRate / max(CS.out.vEgoRaw, 0.1)
|
||||
|
||||
# send lka msg at 33Hz
|
||||
if (self.frame % CarControllerParams.LKA_STEP) == 0:
|
||||
can_sends.append(fordcan.create_lka_msg(self.packer, self.CAN))
|
||||
self.apply_curvature_last = apply_ford_curvature_limits(apply_curvature, self.apply_curvature_last, current_curvature,
|
||||
CS.out.vEgoRaw, 0., CC.latActive, self.CP)
|
||||
|
||||
if self.CP.flags & FordFlags.CANFD:
|
||||
# TODO: extended mode
|
||||
# Ford uses four individual signals to dictate how to drive to the car. Curvature alone (limited to 0.02m/s^2)
|
||||
# can actuate the steering for a large portion of any lateral movements. However, in order to get further control on
|
||||
# steer actuation, the other three signals are necessary. Ford controls vehicles differently than most other makes.
|
||||
# A detailed explanation on ford control can be found here:
|
||||
# https://www.f150gen14.com/forum/threads/introducing-bluepilot-a-ford-specific-fork-for-comma3x-openpilot.24241/#post-457706
|
||||
mode = 1 if CC.latActive else 0
|
||||
counter = (self.frame // CarControllerParams.STEER_STEP) % 0x10
|
||||
can_sends.append(fordcan.create_lat_ctl2_msg(self.packer, self.CAN, mode, 0., 0., -self.apply_curvature_last, 0., counter))
|
||||
else:
|
||||
can_sends.append(fordcan.create_lat_ctl_msg(self.packer, self.CAN, CC.latActive, 0., 0., -self.apply_curvature_last, 0.))
|
||||
|
||||
# send lka msg at 33Hz
|
||||
if (self.frame % CarControllerParams.LKA_STEP) == 0:
|
||||
can_sends.append(fordcan.create_lka_msg(self.packer, self.CAN))
|
||||
|
||||
### longitudinal control ###
|
||||
# send acc msg at 50Hz
|
||||
@@ -195,6 +225,8 @@ class CarController(CarControllerBase):
|
||||
self.lead_distance_bars_last = hud_control.leadDistanceBars
|
||||
|
||||
new_actuators = actuators.as_builder()
|
||||
if self.CP.flags & FordFlags.LKA_STEERING:
|
||||
new_actuators.steeringAngleDeg = self.apply_angle_last + CS.out.steeringAngleDeg
|
||||
new_actuators.curvature = self.apply_curvature_last
|
||||
new_actuators.accel = self.accel
|
||||
new_actuators.gas = self.gas
|
||||
|
||||
@@ -20,6 +20,8 @@ class CarState(CarStateBase):
|
||||
|
||||
self.distance_button = 0
|
||||
self.lc_button = 0
|
||||
self.lkas_available = False
|
||||
self.lateral_motion_control = None
|
||||
|
||||
def update(self, can_parsers, starpilot_toggles) -> structs.CarState:
|
||||
cp = can_parsers[Bus.pt]
|
||||
@@ -108,6 +110,15 @@ class CarState(CarStateBase):
|
||||
# Stock values from IPMA so that we can retain some stock functionality
|
||||
self.acc_tja_status_stock_values = cp_cam.vl["ACCDATA_3"]
|
||||
self.lkas_status_stock_values = cp_cam.vl["IPMA_Data"]
|
||||
if self.CP.flags & FordFlags.LKA_STEERING:
|
||||
try:
|
||||
self.lkas_available = cp.vl["Lane_Assist_Data3_FD1"]["LaActAvail_D_Actl"] == 3
|
||||
except KeyError:
|
||||
self.lkas_available = False
|
||||
try:
|
||||
self.lateral_motion_control = cp_cam.vl["LateralMotionControl"]
|
||||
except KeyError:
|
||||
self.lateral_motion_control = None
|
||||
|
||||
ret.buttonEvents = [
|
||||
*create_button_events(self.distance_button, prev_distance_button, {1: ButtonType.gapAdjustCruise}),
|
||||
|
||||
@@ -106,10 +106,12 @@ FW_VERSIONS = {
|
||||
},
|
||||
CAR.FORD_F_150_MK14: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'ML3V-14D003-BA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'ML3V-14D003-BC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'ML3V-14D003-BD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'ML34-2D053-AJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'NL34-2D053-CA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PL34-2D053-CA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PL34-2D053-CC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
@@ -117,6 +119,7 @@ FW_VERSIONS = {
|
||||
b'PL3V-2D053-BB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'ML3T-14D049-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'ML3T-14D049-AK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'ML3T-14D049-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
@@ -124,6 +127,7 @@ FW_VERSIONS = {
|
||||
b'ML3T-14H102-ABR\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'ML3T-14H102-ABS\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'ML3T-14H102-ABT\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'ML3T-14H102-ACA\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PJ6T-14H102-ABJ\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PJ6T-14H102-ABS\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'RJ6T-14H102-ACJ\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
@@ -211,6 +215,7 @@ FW_VERSIONS = {
|
||||
b'PB3C-2D053-ZD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PB3C-2D053-ZG\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PB3C-2D053-ZJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'RB3C-2D053-AK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'ML3T-14D049-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
@@ -220,4 +225,18 @@ FW_VERSIONS = {
|
||||
b'RJ6T-14H102-BBB\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.FORD_TRANSIT_MK5: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'KK21-14D003-AM\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'NK41-2D053-DF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'PC4T-14D049-AA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'NK3T-14F397-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import math
|
||||
|
||||
from opendbc.car import CanBusBase, structs
|
||||
|
||||
HUDControl = structs.CarControl.HUDControl
|
||||
@@ -33,20 +35,40 @@ def calculate_lat_ctl2_checksum(mode: int, counter: int, dat: bytearray) -> int:
|
||||
return 0xFF - (checksum & 0xFF)
|
||||
|
||||
|
||||
def create_lka_msg(packer, CAN: CanBus):
|
||||
def create_lka_msg(packer, CAN: CanBus, active: bool = False, apply_angle: float = 0.0,
|
||||
direction: int = 0, ramp_type: int = 0, curvature: float = 0.0):
|
||||
"""
|
||||
Creates an empty CAN message for the Ford LKA Command.
|
||||
Creates a CAN message for the Ford LKA Command.
|
||||
|
||||
This command can apply "Lane Keeping Aid" maneuvers, which are subject to the PSCM lockout.
|
||||
On LKA-steering platforms, this command applies Lane Keeping Aid maneuvers through the PSCM.
|
||||
|
||||
Frequency is 33Hz.
|
||||
"""
|
||||
|
||||
return packer.make_can_msg("Lane_Assist_Data1", CAN.main, {})
|
||||
if active:
|
||||
mrad = math.radians(max(-5.8, min(5.8, apply_angle))) * 1000.0
|
||||
mrad = max(-102.4, min(102.3, mrad))
|
||||
curvature = max(-0.01023, min(0.01023, curvature))
|
||||
else:
|
||||
mrad = 0.0
|
||||
direction = 0
|
||||
ramp_type = 0
|
||||
curvature = 0.0
|
||||
|
||||
values = {
|
||||
"LkaDrvOvrrd_D_Rq": 0,
|
||||
"LkaActvStats_D2_Req": direction if active else 0,
|
||||
"LaRefAng_No_Req": mrad,
|
||||
"LaRampType_B_Req": ramp_type,
|
||||
"LaCurvature_No_Calc": curvature,
|
||||
"LdwActvStats_D_Req": 0,
|
||||
"LdwActvIntns_D_Req": 3,
|
||||
}
|
||||
return packer.make_can_msg("Lane_Assist_Data1", CAN.main, values)
|
||||
|
||||
|
||||
def create_lat_ctl_msg(packer, CAN: CanBus, lat_active: bool, path_offset: float, path_angle: float, curvature: float,
|
||||
curvature_rate: float):
|
||||
curvature_rate: float, stock_lmc=None):
|
||||
"""
|
||||
Creates a CAN message for the Ford TJA/LCA Command.
|
||||
|
||||
@@ -68,20 +90,33 @@ def create_lat_ctl_msg(packer, CAN: CanBus, lat_active: bool, path_offset: float
|
||||
Frequency is 20Hz.
|
||||
"""
|
||||
|
||||
values = {
|
||||
"LatCtlRng_L_Max": 0, # Unknown [0|126] meter
|
||||
"HandsOffCnfm_B_Rq": 0, # Unknown: 0=Inactive, 1=Active [0|1]
|
||||
"LatCtl_D_Rq": 1 if lat_active else 0, # Mode: 0=None, 1=ContinuousPathFollowing, 2=InterventionLeft,
|
||||
# 3=InterventionRight, 4-7=NotUsed [0|7]
|
||||
"LatCtlRampType_D_Rq": 0, # Ramp speed: 0=Slow, 1=Medium, 2=Fast, 3=Immediate [0|3]
|
||||
# Makes no difference with curvature control
|
||||
"LatCtlPrecision_D_Rq": 1, # Precision: 0=Comfortable, 1=Precise, 2/3=NotUsed [0|3]
|
||||
# The stock system always uses comfortable
|
||||
"LatCtlPathOffst_L_Actl": path_offset, # Path offset [-5.12|5.11] meter
|
||||
"LatCtlPath_An_Actl": path_angle, # Path angle [-0.5|0.5235] radians
|
||||
"LatCtlCurv_NoRate_Actl": curvature_rate, # Curvature rate [-0.001024|0.00102375] 1/meter^2
|
||||
"LatCtlCurv_No_Actl": curvature, # Curvature [-0.02|0.02094] 1/meter
|
||||
}
|
||||
if stock_lmc is not None:
|
||||
values = {
|
||||
"LatCtlRng_L_Max": stock_lmc["LatCtlRng_L_Max"],
|
||||
"HandsOffCnfm_B_Rq": stock_lmc["HandsOffCnfm_B_Rq"],
|
||||
"LatCtl_D_Rq": 0,
|
||||
"LatCtlRampType_D_Rq": stock_lmc["LatCtlRampType_D_Rq"],
|
||||
"LatCtlPrecision_D_Rq": stock_lmc["LatCtlPrecision_D_Rq"],
|
||||
"LatCtlPathOffst_L_Actl": stock_lmc["LatCtlPathOffst_L_Actl"],
|
||||
"LatCtlPath_An_Actl": stock_lmc["LatCtlPath_An_Actl"],
|
||||
"LatCtlCurv_NoRate_Actl": stock_lmc["LatCtlCurv_NoRate_Actl"],
|
||||
"LatCtlCurv_No_Actl": stock_lmc["LatCtlCurv_No_Actl"],
|
||||
}
|
||||
else:
|
||||
values = {
|
||||
"LatCtlRng_L_Max": 0, # Unknown [0|126] meter
|
||||
"HandsOffCnfm_B_Rq": 0, # Unknown: 0=Inactive, 1=Active [0|1]
|
||||
"LatCtl_D_Rq": 1 if lat_active else 0, # Mode: 0=None, 1=ContinuousPathFollowing, 2=InterventionLeft,
|
||||
# 3=InterventionRight, 4-7=NotUsed [0|7]
|
||||
"LatCtlRampType_D_Rq": 0, # Ramp speed: 0=Slow, 1=Medium, 2=Fast, 3=Immediate [0|3]
|
||||
# Makes no difference with curvature control
|
||||
"LatCtlPrecision_D_Rq": 1, # Precision: 0=Comfortable, 1=Precise, 2/3=NotUsed [0|3]
|
||||
# The stock system always uses comfortable
|
||||
"LatCtlPathOffst_L_Actl": path_offset, # Path offset [-5.12|5.11] meter
|
||||
"LatCtlPath_An_Actl": path_angle, # Path angle [-0.5|0.5235] radians
|
||||
"LatCtlCurv_NoRate_Actl": curvature_rate, # Curvature rate [-0.001024|0.00102375] 1/meter^2
|
||||
"LatCtlCurv_No_Actl": curvature, # Curvature [-0.02|0.02094] 1/meter
|
||||
}
|
||||
return packer.make_can_msg("LateralMotionControl", CAN.main, values)
|
||||
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
ret.radarUnavailable = Bus.radar not in DBC[candidate]
|
||||
ret.steerControlType = structs.CarParams.SteerControlType.angle
|
||||
ret.steerActuatorDelay = 0.2
|
||||
ret.steerActuatorDelay = 0.05 if ret.flags & FordFlags.LKA_STEERING else 0.2
|
||||
ret.steerLimitTimer = 1.0
|
||||
ret.steerAtStandstill = True
|
||||
|
||||
@@ -63,6 +63,8 @@ class CarInterface(CarInterfaceBase):
|
||||
if fingerprint[CAN.camera].get(0x3d6) != 8 or fingerprint[CAN.camera].get(0x186) != 8:
|
||||
carlog.error('dashcamOnly: SecOC is unsupported')
|
||||
ret.dashcamOnly = True
|
||||
elif ret.flags & FordFlags.LKA_STEERING:
|
||||
ret.safetyConfigs[-1].safetyParam |= FordSafetyFlags.LKA_STEERING.value
|
||||
else:
|
||||
# Lock out if the car does not have needed lateral and longitudinal control APIs.
|
||||
# Note that we also check CAN for adaptive cruise, but no known signal for LCA exists
|
||||
|
||||
@@ -46,11 +46,13 @@ class CarControllerParams:
|
||||
class FordSafetyFlags(IntFlag):
|
||||
LONG_CONTROL = 1
|
||||
CANFD = 2
|
||||
LKA_STEERING = 4
|
||||
|
||||
|
||||
class FordFlags(IntFlag):
|
||||
# Static flags
|
||||
CANFD = 1
|
||||
LKA_STEERING = 2
|
||||
|
||||
|
||||
class RADAR:
|
||||
@@ -111,6 +113,13 @@ class FordCANFDPlatformConfig(FordPlatformConfig):
|
||||
self.flags |= FordFlags.CANFD
|
||||
|
||||
|
||||
@dataclass
|
||||
class FordLKASteeringPlatformConfig(FordPlatformConfig):
|
||||
def init(self):
|
||||
super().init()
|
||||
self.flags |= FordFlags.LKA_STEERING
|
||||
|
||||
|
||||
@dataclass
|
||||
class FordF150LightningPlatform(FordCANFDPlatformConfig):
|
||||
def init(self):
|
||||
@@ -178,6 +187,10 @@ class CAR(Platforms):
|
||||
[FordCarDocs("Ford Ranger 2024", "Adaptive Cruise Control with Lane Centering", setup_video="https://www.youtube.com/watch?v=2oJlXCKYOy0")],
|
||||
CarSpecs(mass=2000, wheelbase=3.27, steerRatio=17.0),
|
||||
)
|
||||
FORD_TRANSIT_MK5 = FordLKASteeringPlatformConfig(
|
||||
[FordCarDocs("Ford Transit 2025", "Co-Pilot360 Assist+")],
|
||||
CarSpecs(mass=2068, wheelbase=3.302, steerRatio=16.7),
|
||||
)
|
||||
|
||||
|
||||
# FW response contains a combined software and part number
|
||||
|
||||
@@ -6,15 +6,17 @@ from opendbc.car.lateral import apply_driver_steer_torque_limits
|
||||
from opendbc.car.gm import gmcan
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.gm.values import (
|
||||
ASCM_INT, CAR, CC_ONLY_CAR, CC_REGEN_PADDLE_CAR, DBC, EV_CAR, SDGM_CAR, AccState, CanBus, CarControllerParams,
|
||||
ASCM_INT, CAMERA_ACC_CAR, CAR, CC_ONLY_CAR, CC_REGEN_PADDLE_CAR, DBC, EV_CAR, SDGM_CAR, AccState, CanBus, CarControllerParams,
|
||||
CruiseButtons, GMFlags, GMSafetyFlags,
|
||||
)
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
from openpilot.common.pid import PIDController
|
||||
from openpilot.common.params import Params, UnknownKeyName
|
||||
from openpilot.starpilot.common.testing_grounds import testing_ground
|
||||
|
||||
VisualAlert = structs.CarControl.HUDControl.VisualAlert
|
||||
NetworkLocation = structs.CarParams.NetworkLocation
|
||||
TransmissionType = structs.CarParams.TransmissionType
|
||||
LongCtrlState = structs.CarControl.Actuators.LongControlState
|
||||
GearShifter = structs.CarState.GearShifter
|
||||
|
||||
@@ -28,14 +30,52 @@ AUTO_HOLD_VOLT_CARS = {
|
||||
CAR.CHEVROLET_VOLT_ASCM,
|
||||
CAR.CHEVROLET_VOLT_CAMERA,
|
||||
}
|
||||
AUTO_HOLD_DRIVE_GEARS = {
|
||||
AUTO_HOLD_DRIVE_GEARS = (
|
||||
GearShifter.drive,
|
||||
GearShifter.low,
|
||||
GearShifter.manumatic,
|
||||
}
|
||||
)
|
||||
AUTO_HOLD_MIN_BRAKE = 80
|
||||
AUTO_HOLD_MAX_BRAKE = 240
|
||||
AUTO_HOLD_MIN_DRIVE_TIME_S = 3.0
|
||||
AUTO_HOLD_STOPPED_SPEED = 0.02
|
||||
AUTO_HOLD_2019_MIN_BRAKE = 100
|
||||
BOLT_ACC_PEDAL_FRICTION_RELEASE_FRAMES = 5
|
||||
BOLT_PEDAL_LONG_ACCEL_LIMIT_BP = [0.0, 1.5, 4.0, 8.0, 15.0, 30.0]
|
||||
BOLT_PEDAL_LONG_ACCEL_LIMIT_V = [-0.93, -1.28, -1.98, -2.58, -2.86, -2.95]
|
||||
VOLT_ONE_PEDAL_DECEL_BP = [0.5 * CV.MPH_TO_MS, 6.0 * CV.MPH_TO_MS]
|
||||
VOLT_ONE_PEDAL_DECEL_V = [-1.0, -1.1]
|
||||
VOLT_ONE_PEDAL_REGEN_PADDLE_DECEL_V = [-1.5, -1.6]
|
||||
VOLT_ONE_PEDAL_MAX_DECEL = min((*VOLT_ONE_PEDAL_DECEL_V, *VOLT_ONE_PEDAL_REGEN_PADDLE_DECEL_V)) - 0.5
|
||||
VOLT_ONE_PEDAL_PID_NEG_LIMIT = -3.5
|
||||
VOLT_ONE_PEDAL_SPEED_ERROR_FACTOR_BP = [1.5, 20.0]
|
||||
VOLT_ONE_PEDAL_SPEED_ERROR_FACTOR_V = [0.4, 0.2]
|
||||
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_SPEED_FACTOR_BP = [0.0, 10.0 * CV.MPH_TO_MS]
|
||||
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_SPEED_FACTOR_V = [0.2, 1.0]
|
||||
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_STEER_FACTOR_BP = [20.0, 120.0]
|
||||
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_STEER_FACTOR_V = [1.0, 0.2]
|
||||
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_UP = 0.8 * DT_CTRL * 4
|
||||
VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_DOWN = 0.8 * DT_CTRL * 4
|
||||
VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_BP = [4.0, 8.0]
|
||||
VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_V = [0.4, 1.0]
|
||||
VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_INCLINE_V = [0.2, 1.0]
|
||||
VOLT_ONE_PEDAL_LIFT_BRAKE_BP = [0.0, CarControllerParams.NEAR_STOP_BRAKE_PHASE, 2.0 * CV.MPH_TO_MS]
|
||||
VOLT_ONE_PEDAL_LIFT_BRAKE_V = [AUTO_HOLD_MIN_BRAKE, AUTO_HOLD_MIN_BRAKE, 20.0]
|
||||
VOLT_ONE_PEDAL_LIFT_BRAKE_FRAMES = 8
|
||||
TRUCK_LONG_SMOOTH_CARS = {
|
||||
CAR.CHEVROLET_SILVERADO,
|
||||
CAR.CHEVROLET_SILVERADO_CC,
|
||||
}
|
||||
TRUCK_FRICTION_BRAKE_ENGAGE = 40
|
||||
TRUCK_FRICTION_BRAKE_RELEASE = 8
|
||||
TRUCK_FRICTION_BRAKE_IMMEDIATE_ACCEL = -0.85
|
||||
ACC_DASHBOARD_ZERO_RESERVED_CARS = {
|
||||
CAR.CHEVROLET_BLAZER,
|
||||
CAR.CHEVROLET_EQUINOX,
|
||||
CAR.CHEVROLET_SILVERADO,
|
||||
CAR.CHEVROLET_TRAILBLAZER,
|
||||
CAR.CHEVROLET_TRAX,
|
||||
}
|
||||
|
||||
|
||||
def get_stock_cc_active_for_cancel(CP, CS):
|
||||
@@ -51,7 +91,13 @@ def use_interceptor_sng_launch(CP, CS, maneuver_mode=False):
|
||||
launch_speed = max(CP.vEgoStarting, 0.3)
|
||||
if maneuver_mode:
|
||||
launch_speed = max(launch_speed, 2.0)
|
||||
return CS.out.cruiseState.standstill and (CS.out.standstill or CS.out.vEgo < launch_speed)
|
||||
near_stop = CS.out.standstill or CS.out.vEgo < launch_speed
|
||||
if (
|
||||
getattr(CP, "carFingerprint", None) == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL and
|
||||
getattr(CP, "enableGasInterceptorDEPRECATED", False)
|
||||
):
|
||||
return near_stop
|
||||
return CS.out.cruiseState.standstill and near_stop
|
||||
|
||||
|
||||
def should_spoof_dash_speed(CP, starpilot_toggles):
|
||||
@@ -77,6 +123,17 @@ def should_send_acc_dashboard_status(CP, dash_speed_spoof_active):
|
||||
return status_car and (dash_speed_spoof_active or volt_camera_no_camera)
|
||||
|
||||
|
||||
def get_acc_dashboard_status_active(CP, CC):
|
||||
if CC.enabled:
|
||||
return True
|
||||
|
||||
return CP.carFingerprint == CAR.BUICK_LACROSSE_ASCM and CC.latActive
|
||||
|
||||
|
||||
def get_acc_dashboard_always_one(CP):
|
||||
return 0 if CP.carFingerprint in ACC_DASHBOARD_ZERO_RESERVED_CARS else 1
|
||||
|
||||
|
||||
def get_acc_dashboard_fcw_alert(hud_alert, CS):
|
||||
if hud_alert == VisualAlert.fcw:
|
||||
return 0x3
|
||||
@@ -92,29 +149,6 @@ def get_acc_dashboard_fcw_alert(hud_alert, CS):
|
||||
return 0
|
||||
|
||||
|
||||
def get_acc_dashboard_status_values(enabled, target_speed_kph, hud_control, CS):
|
||||
if enabled:
|
||||
return {
|
||||
"ACCCruiseState": 0,
|
||||
"ACCLeadCar": int(hud_control.leadVisible) & 0x1,
|
||||
"ACCResumeButton": 0,
|
||||
"ACCSpeedSetpoint": target_speed_kph,
|
||||
"ACCGapLevel": int(hud_control.leadDistanceBars) & 0x3,
|
||||
"ACCCmdActive": 1,
|
||||
}
|
||||
|
||||
# Replay the stock camera dashboard context when openpilot long is enabled
|
||||
# but openpilot itself is not actively driving the ACC cluster state.
|
||||
return {
|
||||
"ACCCruiseState": int(getattr(CS, "stock_acc_cruise_state", 0)) & 0x7,
|
||||
"ACCLeadCar": int(getattr(CS, "stock_acc_lead_car", 0)) & 0x1,
|
||||
"ACCResumeButton": int(getattr(CS, "stock_acc_resume_button", 0)) & 0x1,
|
||||
"ACCSpeedSetpoint": float(getattr(CS, "stock_acc_speed_setpoint_kph", 0.0)),
|
||||
"ACCGapLevel": int(getattr(CS, "stock_acc_gap_level", 0)) & 0x3,
|
||||
"ACCCmdActive": int(getattr(CS, "stock_acc_cmd_active", 0)) & 0x1,
|
||||
}
|
||||
|
||||
|
||||
ECM_CRUISE_SPOOF_CARS = {
|
||||
CAR.CHEVROLET_BOLT_CC_2017,
|
||||
CAR.CHEVROLET_BOLT_CC_2018_2021,
|
||||
@@ -150,34 +184,162 @@ def get_adas_keepalive_step(CP, is_kaofui_car):
|
||||
return None
|
||||
|
||||
|
||||
def should_send_adas_status(CP, is_kaofui_car):
|
||||
if CP.radarUnavailable:
|
||||
return False
|
||||
|
||||
if not is_kaofui_car:
|
||||
return True
|
||||
|
||||
if CP.carFingerprint in ASCM_INT:
|
||||
return False
|
||||
|
||||
return CP.networkLocation != NetworkLocation.fwdCamera and CP.carFingerprint not in SDGM_CAR
|
||||
|
||||
|
||||
def should_send_acc_2cd(CP):
|
||||
return (
|
||||
CP.networkLocation == NetworkLocation.fwdCamera and
|
||||
CP.carFingerprint in CAMERA_ACC_CAR and
|
||||
CP.carFingerprint not in (CC_ONLY_CAR | SDGM_CAR) and
|
||||
not bool(getattr(CP, "flags", 0) & GMFlags.NO_CAMERA.value)
|
||||
)
|
||||
|
||||
|
||||
def get_testing_ground_1_brake_switch_bias(v_ego: float) -> int:
|
||||
return int(round(np.interp(v_ego, [0.0, 6.0, 15.0, 30.0], [40.0, 85.0, 130.0, 170.0])))
|
||||
|
||||
|
||||
def shape_truck_positive_accel(accel: float, v_ego: float, enabled: bool,
|
||||
lead_visible: bool = False, set_speed_error: float = 0.0) -> float:
|
||||
if not enabled or accel <= 0.0 or v_ego < 12.0:
|
||||
return accel
|
||||
|
||||
low_scale = float(np.interp(v_ego, [12.0, 18.0, 25.0, 35.0], [0.93, 0.84, 0.76, 0.70]))
|
||||
mid_scale = float(np.interp(v_ego, [12.0, 18.0, 25.0, 35.0], [0.97, 0.91, 0.85, 0.79]))
|
||||
|
||||
if lead_visible and set_speed_error > 0.0:
|
||||
follow_relief = float(np.interp(set_speed_error, [0.0, 1.0, 2.5, 4.0, 6.0], [0.0, 0.04, 0.10, 0.18, 0.30]))
|
||||
low_scale += (1.0 - low_scale) * follow_relief
|
||||
mid_scale += (1.0 - mid_scale) * follow_relief
|
||||
|
||||
if accel <= 0.12:
|
||||
return accel * low_scale
|
||||
if accel <= 0.35:
|
||||
return float(np.interp(accel, [0.12, 0.35], [0.12 * low_scale, 0.35 * mid_scale]))
|
||||
if accel <= 0.65:
|
||||
return float(np.interp(accel, [0.35, 0.65], [0.35 * mid_scale, 0.65]))
|
||||
return accel
|
||||
|
||||
|
||||
def shape_truck_friction_brake(apply_brake: int, accel_cmd: float, stopping: bool, active: bool) -> tuple[int, bool]:
|
||||
if apply_brake <= 0:
|
||||
return 0, False
|
||||
|
||||
# Preserve full brake response for stop control and meaningful deceleration.
|
||||
if stopping or accel_cmd <= TRUCK_FRICTION_BRAKE_IMMEDIATE_ACCEL:
|
||||
return apply_brake, True
|
||||
|
||||
if active:
|
||||
if apply_brake <= TRUCK_FRICTION_BRAKE_RELEASE:
|
||||
return 0, False
|
||||
return apply_brake, True
|
||||
|
||||
if apply_brake >= TRUCK_FRICTION_BRAKE_ENGAGE:
|
||||
return apply_brake, True
|
||||
|
||||
# Keep tiny corrections in the continuous gas/regen torque path. Switching
|
||||
# to friction also forces max regen, which makes a small request perceptible.
|
||||
return 0, False
|
||||
|
||||
|
||||
def get_lka_steering_cmd_counter(next_counter: int, CS) -> int:
|
||||
if getattr(CS, "loopback_lka_steering_cmd_updated", False):
|
||||
return (getattr(CS, "loopback_lka_steering_cmd_counter", next_counter) + 1) % 4
|
||||
if next_counter < 0 and getattr(CS, "loopback_lka_steering_cmd_ts_nanos", 0) == 0:
|
||||
return (getattr(CS, "pt_lka_steering_cmd_counter", next_counter) + 1) % 4
|
||||
return next_counter
|
||||
|
||||
|
||||
def should_send_stock_long_cancel(cancel_counter: int, CS) -> bool:
|
||||
cs_out = getattr(CS, "out", None)
|
||||
return cancel_counter > CAMERA_CANCEL_DELAY_FRAMES and not bool(getattr(cs_out, "accFaulted", False))
|
||||
|
||||
|
||||
def supports_volt_auto_hold(CP, auto_hold_enabled: bool):
|
||||
safety_cfg = getattr(CP, "safetyConfigs", ())
|
||||
safety_param = safety_cfg[0].safetyParam if safety_cfg else 0
|
||||
stock_hold_safety_ready = CP.openpilotLongitudinalControl or bool(safety_param & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value)
|
||||
stock_hold_safety_ready = bool(safety_param & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value)
|
||||
return (
|
||||
auto_hold_enabled and
|
||||
getattr(CP, "openpilotLongitudinalControl", False) and
|
||||
stock_hold_safety_ready and
|
||||
CP.carFingerprint in AUTO_HOLD_VOLT_CARS
|
||||
)
|
||||
|
||||
|
||||
def estimate_auto_hold_brake(driver_brake: float, op_brake: float) -> int:
|
||||
def supports_volt_one_pedal(CP, one_pedal_enabled: bool):
|
||||
safety_cfg = getattr(CP, "safetyConfigs", ())
|
||||
safety_param = safety_cfg[0].safetyParam if safety_cfg else 0
|
||||
stock_hold_safety_ready = bool(safety_param & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value)
|
||||
return (
|
||||
one_pedal_enabled and
|
||||
getattr(CP, "openpilotLongitudinalControl", False) and
|
||||
stock_hold_safety_ready and
|
||||
getattr(CP, "transmissionType", None) == TransmissionType.direct and
|
||||
CP.carFingerprint in AUTO_HOLD_VOLT_CARS
|
||||
)
|
||||
|
||||
|
||||
def estimate_auto_hold_brake(driver_brake: float, op_brake: float, CP=None) -> int:
|
||||
driver_hold = np.interp(float(driver_brake), [8.0, 20.0, 40.0, 80.0], [80.0, 110.0, 150.0, 220.0])
|
||||
hold_brake = max(float(op_brake), float(driver_hold))
|
||||
return int(round(np.clip(hold_brake, AUTO_HOLD_MIN_BRAKE, AUTO_HOLD_MAX_BRAKE)))
|
||||
min_brake = AUTO_HOLD_2019_MIN_BRAKE if getattr(CP, "carFingerprint", None) == CAR.CHEVROLET_VOLT_2019 else AUTO_HOLD_MIN_BRAKE
|
||||
return int(round(np.clip(hold_brake, min_brake, AUTO_HOLD_MAX_BRAKE)))
|
||||
|
||||
|
||||
def get_auto_hold_stop_threshold(CP, auto_hold_engaged: bool) -> float:
|
||||
if auto_hold_engaged and getattr(CP, "carFingerprint", None) == CAR.CHEVROLET_VOLT_2019:
|
||||
return CarControllerParams.NEAR_STOP_BRAKE_PHASE
|
||||
return AUTO_HOLD_STOPPED_SPEED
|
||||
|
||||
|
||||
def get_volt_one_pedal_target_decel(v_ego: float) -> float:
|
||||
return float(np.interp(v_ego, VOLT_ONE_PEDAL_DECEL_BP, VOLT_ONE_PEDAL_DECEL_V))
|
||||
|
||||
|
||||
def get_volt_one_pedal_lift_brake(v_ego: float) -> int:
|
||||
if v_ego > VOLT_ONE_PEDAL_LIFT_BRAKE_BP[-1]:
|
||||
return 0
|
||||
|
||||
return int(round(np.interp(v_ego, VOLT_ONE_PEDAL_LIFT_BRAKE_BP, VOLT_ONE_PEDAL_LIFT_BRAKE_V)))
|
||||
|
||||
|
||||
def should_activate_volt_one_pedal(one_pedal_ready: bool, cruise_main: bool, long_active: bool,
|
||||
gas_pressed: bool, brake_pressed: bool, regen_braking: bool,
|
||||
single_pedal_mode: bool, gear_shifter, drive_time_s: float) -> bool:
|
||||
# Volt rear wheel direction bits can falsely report reverse while stopping in L.
|
||||
return (
|
||||
one_pedal_ready and
|
||||
cruise_main and
|
||||
single_pedal_mode and
|
||||
gear_shifter in AUTO_HOLD_DRIVE_GEARS and
|
||||
drive_time_s >= AUTO_HOLD_MIN_DRIVE_TIME_S and
|
||||
not long_active and
|
||||
not gas_pressed and
|
||||
not brake_pressed and
|
||||
not regen_braking
|
||||
)
|
||||
|
||||
|
||||
def should_activate_auto_hold(hold_ready: bool, auto_hold_armed: bool, auto_hold_engaged: bool,
|
||||
brake_pressed: bool, standstill: bool, long_active: bool,
|
||||
regen_braking: bool, v_ego: float) -> bool:
|
||||
stopped = standstill or v_ego < 0.02
|
||||
brake_pressed: bool, gas_pressed: bool, standstill: bool, long_active: bool,
|
||||
regen_braking: bool, v_ego: float, stop_speed_threshold: float=AUTO_HOLD_STOPPED_SPEED) -> bool:
|
||||
stopped = standstill or v_ego < stop_speed_threshold
|
||||
return (
|
||||
hold_ready and
|
||||
(auto_hold_armed or auto_hold_engaged or brake_pressed) and
|
||||
not gas_pressed and
|
||||
stopped and
|
||||
not long_active and
|
||||
not regen_braking
|
||||
@@ -195,12 +357,118 @@ def get_friction_brake_bus(CP):
|
||||
|
||||
if CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
if CP.carFingerprint in SDGM_CAR:
|
||||
# cam-long: 0x315 goes where the panda whitelist allows it and the EBCM hears it
|
||||
safety_cfg = getattr(CP, "safetyConfigs", ())
|
||||
safety_param = safety_cfg[0].safetyParam if safety_cfg else 0
|
||||
if safety_param & GMSafetyFlags.HW_CAM_LONG.value:
|
||||
# SASCM relays 0x315 to the EBCM off its camera-bus (bus2) leg; bare SDGM uses the pt bus
|
||||
if CP.flags & GMFlags.SASCM.value:
|
||||
return CanBus.CAMERA
|
||||
return CanBus.POWERTRAIN
|
||||
return CanBus.CAMERA
|
||||
return CanBus.POWERTRAIN
|
||||
|
||||
return CanBus.CHASSIS
|
||||
|
||||
|
||||
def supports_bolt_acc_pedal_friction_experiment(CP) -> bool:
|
||||
return (
|
||||
CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL and
|
||||
CP.openpilotLongitudinalControl and
|
||||
CP.enableGasInterceptorDEPRECATED and
|
||||
bool(CP.flags & GMFlags.PEDAL_LONG.value)
|
||||
)
|
||||
|
||||
|
||||
def get_bolt_acc_pedal_friction_brake(apply_brake, full_brake_accel, v_ego, params) -> int:
|
||||
if apply_brake <= 0:
|
||||
return 0
|
||||
|
||||
full_brake_accel = min(full_brake_accel, -0.1)
|
||||
legacy_full_scale = max(-params.ACCEL_MIN, 0.1)
|
||||
corrected_scale = legacy_full_scale / max(-full_brake_accel, 0.1)
|
||||
speed_gain = float(np.interp(v_ego, [0.0, 8.0, 15.0, 25.0], [1.0, 1.08, 1.2, 1.35]))
|
||||
onset_gain = float(np.interp(
|
||||
apply_brake,
|
||||
[0.0, 5.0, 20.0, 60.0, 120.0, 240.0, params.MAX_BRAKE],
|
||||
[0.0, 1.8, 1.65, 1.4, 1.22, 1.08, 1.0],
|
||||
))
|
||||
|
||||
shaped_brake = apply_brake * corrected_scale * speed_gain * onset_gain
|
||||
minimum_brake = float(np.interp(v_ego, [0.0, 6.0, 8.0, 12.0, 18.0, 25.0], [0.0, 0.0, 4.0, 10.0, 20.0, 28.0]))
|
||||
shaped_brake = max(shaped_brake, minimum_brake)
|
||||
|
||||
return int(round(np.clip(shaped_brake, 0, params.MAX_BRAKE)))
|
||||
|
||||
|
||||
def shape_bolt_acc_pedal_low_speed_friction(apply_brake: int, v_ego: float, stopping: bool, active: bool):
|
||||
if apply_brake <= 0:
|
||||
return 0, False
|
||||
|
||||
engage_threshold = float(np.interp(v_ego, [0.0, 1.5, 3.0, 5.0, 8.0], [40.0, 20.0, 12.0, 10.0, 0.0]))
|
||||
release_threshold = float(np.interp(v_ego, [0.0, 1.5, 3.0, 5.0, 8.0], [0.0, 8.0, 6.0, 4.0, 0.0]))
|
||||
|
||||
if not active:
|
||||
if apply_brake < engage_threshold:
|
||||
return 0, False
|
||||
active = True
|
||||
elif apply_brake < release_threshold:
|
||||
return 0, False
|
||||
|
||||
if stopping:
|
||||
stop_fade = float(np.interp(v_ego, [0.0, 0.6, 0.9, 1.2, 1.8, 2.8], [0.0, 0.0, 0.05, 0.12, 0.32, 0.78]))
|
||||
apply_brake = int(round(apply_brake * stop_fade))
|
||||
if apply_brake <= 0 or apply_brake < release_threshold:
|
||||
return 0, False
|
||||
|
||||
return apply_brake, active
|
||||
|
||||
|
||||
def get_bolt_pedal_long_accel_limit(v_ego: float) -> float:
|
||||
return float(np.interp(v_ego, BOLT_PEDAL_LONG_ACCEL_LIMIT_BP, BOLT_PEDAL_LONG_ACCEL_LIMIT_V))
|
||||
|
||||
|
||||
def get_bolt_acc_pedal_planner_brake_switch(v_ego: float, params, tire_radius: float, mass: float,
|
||||
coeff_drag: float, frontal_area: float, air_density: float) -> int:
|
||||
planner_accel_limit = get_bolt_pedal_long_accel_limit(v_ego)
|
||||
aero_drag_force = 0.5 * coeff_drag * frontal_area * air_density * v_ego ** 2
|
||||
planner_torque = tire_radius * ((mass * planner_accel_limit) + aero_drag_force)
|
||||
return int(round(planner_torque + params.ZERO_GAS))
|
||||
|
||||
|
||||
def get_bolt_acc_pedal_effective_brake_switch(stock_switch: int, planner_switch: int) -> int:
|
||||
return max(stock_switch, planner_switch)
|
||||
|
||||
|
||||
def get_bolt_acc_pedal_friction_command_state(apply_brake: int, cruise_main_on: bool, release_frames: int):
|
||||
command_brake = apply_brake if cruise_main_on else 0
|
||||
|
||||
if command_brake > 0:
|
||||
release_frames = BOLT_ACC_PEDAL_FRICTION_RELEASE_FRAMES
|
||||
elif release_frames > 0:
|
||||
release_frames -= 1
|
||||
|
||||
should_send = cruise_main_on or release_frames > 0
|
||||
return command_brake, release_frames, should_send
|
||||
|
||||
|
||||
def get_interceptor_sng_gas_cmd(CP, interceptor_gas_cmd: float, accel: float, params, maneuver_mode: bool) -> float:
|
||||
if maneuver_mode:
|
||||
return max(interceptor_gas_cmd, float(np.interp(accel, [0.0, 1.0, 2.0], [params.SNG_INTERCEPTOR_GAS, 0.11, 0.16])))
|
||||
|
||||
if supports_bolt_acc_pedal_friction_experiment(CP):
|
||||
return max(interceptor_gas_cmd, params.SNG_INTERCEPTOR_GAS)
|
||||
|
||||
return params.SNG_INTERCEPTOR_GAS
|
||||
|
||||
|
||||
def should_use_fixed_stopping_brake(CP, near_stop: bool, stopping: bool, resume: bool) -> bool:
|
||||
if not (near_stop and stopping and not resume):
|
||||
return False
|
||||
|
||||
return not supports_bolt_acc_pedal_friction_experiment(CP)
|
||||
|
||||
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_names, CP):
|
||||
super().__init__(dbc_names, CP)
|
||||
@@ -211,6 +479,11 @@ class CarController(CarControllerBase):
|
||||
self.last_steer_frame = 0
|
||||
self.last_button_frame = 0
|
||||
self.cancel_counter = 0
|
||||
self.xt4_cc_button_burst_remaining = 0
|
||||
self.xt4_cc_button_burst_button = CruiseButtons.INIT
|
||||
self.xt4_cc_button_burst_last_counter = -1
|
||||
self.xt4_cc_button_observed_counter = -1
|
||||
self.xt4_cc_button_counter_frame = 0
|
||||
|
||||
self.lka_steering_cmd_counter = 0
|
||||
self.lka_icon_status_last = (False, False)
|
||||
@@ -254,10 +527,60 @@ class CarController(CarControllerBase):
|
||||
self.malibu_button_phase = 0
|
||||
self.malibu_last_button_ts_nanos = 0
|
||||
self.auto_hold_brake = 0
|
||||
self.volt_one_pedal_pid = PIDController(
|
||||
(CP.longitudinalTuning.kpBP, CP.longitudinalTuning.kpV),
|
||||
(CP.longitudinalTuning.kiBP, CP.longitudinalTuning.kiV),
|
||||
rate=1 / (DT_CTRL * 4),
|
||||
pos_limit=0.0,
|
||||
neg_limit=VOLT_ONE_PEDAL_PID_NEG_LIMIT,
|
||||
)
|
||||
self.volt_one_pedal_decel = 0.0
|
||||
self.volt_one_pedal_brake = 0
|
||||
self.volt_one_pedal_lift_frames = 0
|
||||
self.volt_one_pedal_gas_pressed_last = False
|
||||
try:
|
||||
self.gm_auto_hold_enabled = self.params_.get_bool("GMAutoHold")
|
||||
except UnknownKeyName:
|
||||
self.gm_auto_hold_enabled = False
|
||||
self.bolt_acc_pedal_friction_release_frames = 0
|
||||
self.bolt_acc_pedal_friction_low_speed_active = False
|
||||
self.truck_friction_brake_active = False
|
||||
|
||||
def _reset_volt_one_pedal(self):
|
||||
self.volt_one_pedal_pid.reset()
|
||||
self.volt_one_pedal_decel = min(0.0, float(self.aego))
|
||||
self.volt_one_pedal_brake = 0
|
||||
self.volt_one_pedal_lift_frames = 0
|
||||
|
||||
def _update_volt_one_pedal_brake(self, CC, CS):
|
||||
pitch_accel = 0.0
|
||||
if len(CC.orientationNED) == 3 and CS.out.vEgo > self.CP.vEgoStopping:
|
||||
pitch_accel = math.sin(CC.orientationNED[1]) * ACCELERATION_DUE_TO_GRAVITY
|
||||
pitch_factor_values = VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_V if pitch_accel <= 0.0 else VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_INCLINE_V
|
||||
pitch_accel *= float(np.interp(CS.out.vEgo, VOLT_ONE_PEDAL_ACCEL_PITCH_FACTOR_BP, pitch_factor_values))
|
||||
|
||||
target_decel = get_volt_one_pedal_target_decel(CS.out.vEgo)
|
||||
measured_decel = min(0.0, CS.out.aEgo + pitch_accel)
|
||||
error_factor = float(np.interp(CS.out.vEgo, VOLT_ONE_PEDAL_SPEED_ERROR_FACTOR_BP, VOLT_ONE_PEDAL_SPEED_ERROR_FACTOR_V))
|
||||
error = (target_decel - measured_decel) * error_factor
|
||||
|
||||
raw_decel = float(self.volt_one_pedal_pid.update(error, speed=CS.out.vEgo, feedforward=target_decel))
|
||||
rate_limit_factor = min(
|
||||
float(np.interp(CS.out.vEgo, VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_SPEED_FACTOR_BP, VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_SPEED_FACTOR_V)),
|
||||
float(np.interp(abs(CS.out.steeringAngleDeg), VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_STEER_FACTOR_BP, VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_STEER_FACTOR_V)),
|
||||
)
|
||||
lower = min(self.volt_one_pedal_decel, measured_decel) - VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_UP * rate_limit_factor
|
||||
upper = max(self.volt_one_pedal_decel, measured_decel) + VOLT_ONE_PEDAL_DECEL_RATE_LIMIT_DOWN + rate_limit_factor
|
||||
self.volt_one_pedal_decel = float(np.clip(raw_decel, lower, upper))
|
||||
self.volt_one_pedal_decel = max(self.volt_one_pedal_decel, VOLT_ONE_PEDAL_MAX_DECEL)
|
||||
self.volt_one_pedal_brake = int(round(np.clip(
|
||||
np.interp(self.volt_one_pedal_decel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V),
|
||||
0,
|
||||
self.params.MAX_BRAKE,
|
||||
)))
|
||||
if self.volt_one_pedal_lift_frames > 0:
|
||||
self.volt_one_pedal_brake = max(self.volt_one_pedal_brake, get_volt_one_pedal_lift_brake(CS.out.vEgo))
|
||||
self.volt_one_pedal_lift_frames -= 1
|
||||
|
||||
def calc_pedal_command(self, accel: float, long_active: bool, v_ego: float):
|
||||
if not long_active:
|
||||
@@ -411,7 +734,37 @@ class CarController(CarControllerBase):
|
||||
accel = actuators.accel
|
||||
press_regen_paddle = False
|
||||
auto_hold_enabled = supports_volt_auto_hold(self.CP, self.gm_auto_hold_enabled)
|
||||
stock_hold_apply_brake = self.apply_brake if self.CP.openpilotLongitudinalControl else 0
|
||||
volt_one_pedal_supported = supports_volt_one_pedal(
|
||||
self.CP, bool(getattr(starpilot_toggles, "volt_one_pedal_mode", False))
|
||||
)
|
||||
volt_one_pedal_active = should_activate_volt_one_pedal(
|
||||
volt_one_pedal_supported,
|
||||
CS.out.cruiseState.available,
|
||||
CC.longActive,
|
||||
CS.out.gasPressed,
|
||||
CS.out.brakePressed,
|
||||
CS.out.regenBraking,
|
||||
bool(getattr(CS, "single_pedal_mode", False)),
|
||||
CS.out.gearShifter,
|
||||
float(getattr(CS, "one_pedal_drive_time", 0.0)),
|
||||
)
|
||||
if volt_one_pedal_active and self.volt_one_pedal_gas_pressed_last and not CS.out.gasPressed:
|
||||
if CS.out.vEgo < VOLT_ONE_PEDAL_LIFT_BRAKE_BP[-1]:
|
||||
self.volt_one_pedal_lift_frames = VOLT_ONE_PEDAL_LIFT_BRAKE_FRAMES
|
||||
elif CS.out.gasPressed or not volt_one_pedal_active:
|
||||
self.volt_one_pedal_lift_frames = 0
|
||||
|
||||
if self.frame % 4 == 0:
|
||||
if volt_one_pedal_active:
|
||||
self._update_volt_one_pedal_brake(CC, CS)
|
||||
else:
|
||||
self._reset_volt_one_pedal()
|
||||
if not self.CP.openpilotLongitudinalControl:
|
||||
self.apply_gas = 0
|
||||
self.apply_brake = self.volt_one_pedal_brake if volt_one_pedal_active else 0
|
||||
self.volt_one_pedal_gas_pressed_last = CS.out.gasPressed
|
||||
|
||||
stock_hold_apply_brake = max(self.apply_brake if self.CP.openpilotLongitudinalControl else 0, self.volt_one_pedal_brake)
|
||||
|
||||
hold_ready = (
|
||||
auto_hold_enabled and
|
||||
@@ -421,6 +774,8 @@ class CarController(CarControllerBase):
|
||||
)
|
||||
if not hold_ready or CS.out.gasPressed:
|
||||
CS.auto_hold_armed = False
|
||||
if CS.out.gasPressed:
|
||||
CS.auto_hold_engaged = False
|
||||
elif CS.regen_release_timer > 0.0:
|
||||
CS.auto_hold_armed = False
|
||||
elif not CS.auto_hold_armed and (CS.out.vEgo > 0.03 or ((CS.out.standstill or CS.out.vEgo < 0.02) and CS.out.brakePressed)):
|
||||
@@ -429,7 +784,7 @@ class CarController(CarControllerBase):
|
||||
if CS.out.vEgo > 0.1 or CS.out.gasPressed or CS.out.gearShifter not in AUTO_HOLD_DRIVE_GEARS:
|
||||
self.auto_hold_brake = 0
|
||||
elif CS.out.brakePressed or stock_hold_apply_brake > 0:
|
||||
self.auto_hold_brake = estimate_auto_hold_brake(CS.out.brake, stock_hold_apply_brake)
|
||||
self.auto_hold_brake = estimate_auto_hold_brake(CS.out.brake, stock_hold_apply_brake, self.CP)
|
||||
|
||||
if self.frame % 25 == 0:
|
||||
try:
|
||||
@@ -520,10 +875,23 @@ class CarController(CarControllerBase):
|
||||
CS.auto_hold_armed,
|
||||
CS.auto_hold_engaged,
|
||||
CS.out.brakePressed,
|
||||
CS.out.gasPressed,
|
||||
CS.out.standstill,
|
||||
CC.longActive,
|
||||
CS.out.regenBraking,
|
||||
CS.out.vEgo,
|
||||
get_auto_hold_stop_threshold(self.CP, CS.auto_hold_engaged),
|
||||
)
|
||||
bolt_acc_pedal_friction_experiment = supports_bolt_acc_pedal_friction_experiment(self.CP)
|
||||
bolt_acc_pedal_friction_main_on = bolt_acc_pedal_friction_experiment and CS.out.cruiseState.available
|
||||
if not bolt_acc_pedal_friction_main_on:
|
||||
self.bolt_acc_pedal_friction_low_speed_active = False
|
||||
volt_one_pedal_braking = volt_one_pedal_active and self.volt_one_pedal_brake > 0
|
||||
volt_one_pedal_hold_active = (
|
||||
volt_one_pedal_braking and
|
||||
not auto_hold_active and
|
||||
CS.one_pedal_drive_time >= AUTO_HOLD_MIN_DRIVE_TIME_S and
|
||||
(CS.out.standstill or CS.out.vEgo < 0.02)
|
||||
)
|
||||
|
||||
# Steering (Active: 50Hz, inactive: 10Hz)
|
||||
@@ -567,6 +935,18 @@ class CarController(CarControllerBase):
|
||||
can_sends.append(gmcan.create_ecm_cruise_control_command(
|
||||
self.packer_pt, CanBus.POWERTRAIN, True, hud_v_cruise * CV.MS_TO_KPH))
|
||||
|
||||
xt4_cc_button_spam = (
|
||||
self.CP.carFingerprint == CAR.CADILLAC_XT4_CC and
|
||||
should_send_cc_button_spam(self.CP, CC, CS)
|
||||
)
|
||||
if xt4_cc_button_spam:
|
||||
can_sends.extend(gmcan.create_gm_cc_spam_command(self.packer_pt, self, CS, actuators, starpilot_toggles))
|
||||
elif self.CP.carFingerprint == CAR.CADILLAC_XT4_CC:
|
||||
self.xt4_cc_button_burst_remaining = 0
|
||||
self.xt4_cc_button_burst_button = CruiseButtons.INIT
|
||||
self.xt4_cc_button_burst_last_counter = -1
|
||||
self.xt4_cc_button_observed_counter = -1
|
||||
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
# Gas/regen, brakes, and UI commands - all at 25Hz
|
||||
if self.frame % 4 == 0:
|
||||
@@ -578,6 +958,7 @@ class CarController(CarControllerBase):
|
||||
# ASCM sends max regen when not enabled
|
||||
self.apply_gas = self.params.INACTIVE_REGEN
|
||||
self.apply_brake = 0
|
||||
self.bolt_acc_pedal_friction_low_speed_active = False
|
||||
self.planner_regen_hold = False
|
||||
self.regen_paddle_pressed = False
|
||||
self.regen_paddle_timer = 0
|
||||
@@ -585,7 +966,7 @@ class CarController(CarControllerBase):
|
||||
self.regen_release_counter = 0
|
||||
self.regen_min_on_frames = 0
|
||||
self.regen_min_off_frames = 0
|
||||
elif near_stop and stopping and not CC.cruiseControl.resume:
|
||||
elif should_use_fixed_stopping_brake(self.CP, near_stop, stopping, CC.cruiseControl.resume):
|
||||
stop_accel = getattr(starpilot_toggles, "stopAccel", self.CP.stopAccel)
|
||||
self.apply_gas = self.params.INACTIVE_REGEN
|
||||
self.apply_brake = int(min(-100 * stop_accel, self.params.MAX_BRAKE))
|
||||
@@ -629,7 +1010,23 @@ class CarController(CarControllerBase):
|
||||
if testing_ground.use_1:
|
||||
accel_max = min(accel_max, np.interp(CS.out.vEgo, [0.0, 4.0, 12.0], [1.25, 1.6, self.params.ACCEL_MAX]))
|
||||
|
||||
accel_cmd = float(np.clip(actuators.accel + accel_due_to_pitch, self.params.ACCEL_MIN, accel_max))
|
||||
truck_long_smoothing = (
|
||||
getattr(starpilot_toggles, "truck_tuning", False) and
|
||||
self.CP.carFingerprint in TRUCK_LONG_SMOOTH_CARS and
|
||||
getattr(self.CP, "transmissionType", None) == TransmissionType.automatic and
|
||||
not self.CP.enableGasInterceptorDEPRECATED
|
||||
)
|
||||
accel_input = actuators.accel + accel_due_to_pitch
|
||||
if truck_long_smoothing:
|
||||
accel_input = shape_truck_positive_accel(
|
||||
accel_input,
|
||||
CS.out.vEgo,
|
||||
True,
|
||||
lead_visible=CC.hudControl.leadVisible,
|
||||
set_speed_error=max(CC.hudControl.setSpeed - CS.out.vEgo, 0.0),
|
||||
)
|
||||
|
||||
accel_cmd = float(np.clip(accel_input, self.params.ACCEL_MIN, accel_max))
|
||||
torque = self.tireRadius * ((self.mass * accel_cmd) + (0.5 * self.coeffDrag * self.frontalArea * self.airDensity * CS.out.vEgo ** 2))
|
||||
scaled_torque = torque + self.params.ZERO_GAS
|
||||
apply_gas_torque = np.clip(scaled_torque, self.params.MAX_ACC_REGEN, gas_max)
|
||||
@@ -637,9 +1034,33 @@ class CarController(CarControllerBase):
|
||||
if testing_ground.use_1:
|
||||
brake_switch_bias = get_testing_ground_1_brake_switch_bias(CS.out.vEgo)
|
||||
brake_switch = min(self.params.ZERO_GAS, brake_switch + brake_switch_bias)
|
||||
if bolt_acc_pedal_friction_main_on:
|
||||
planner_brake_switch = get_bolt_acc_pedal_planner_brake_switch(
|
||||
CS.out.vEgo, self.params, self.tireRadius, self.mass, self.coeffDrag, self.frontalArea, self.airDensity,
|
||||
)
|
||||
brake_switch = get_bolt_acc_pedal_effective_brake_switch(brake_switch, planner_brake_switch)
|
||||
brake_accel = min((scaled_torque - brake_switch) / (self.tireRadius * self.mass), 0)
|
||||
self.apply_gas = int(round(apply_gas_torque))
|
||||
self.apply_brake = int(round(np.interp(brake_accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
|
||||
if truck_long_smoothing:
|
||||
self.apply_brake, self.truck_friction_brake_active = shape_truck_friction_brake(
|
||||
self.apply_brake, accel_cmd, stopping, self.truck_friction_brake_active,
|
||||
)
|
||||
else:
|
||||
self.truck_friction_brake_active = False
|
||||
if bolt_acc_pedal_friction_main_on:
|
||||
if self.apply_brake > 0:
|
||||
full_brake_accel = min(
|
||||
self.params.ACCEL_MIN + (0.5 * self.coeffDrag * self.frontalArea * self.airDensity * CS.out.vEgo ** 2) / self.mass +
|
||||
(self.params.ZERO_GAS - brake_switch) / (self.tireRadius * self.mass),
|
||||
-0.1,
|
||||
)
|
||||
self.apply_brake = get_bolt_acc_pedal_friction_brake(
|
||||
self.apply_brake, full_brake_accel, CS.out.vEgo, self.params,
|
||||
)
|
||||
self.apply_brake, self.bolt_acc_pedal_friction_low_speed_active = shape_bolt_acc_pedal_low_speed_friction(
|
||||
self.apply_brake, CS.out.vEgo, stopping, self.bolt_acc_pedal_friction_low_speed_active,
|
||||
)
|
||||
if self.apply_brake > 0:
|
||||
self.apply_gas = self.params.INACTIVE_REGEN
|
||||
|
||||
@@ -650,15 +1071,19 @@ class CarController(CarControllerBase):
|
||||
# gas interceptor only used for full long control on cars without ACC
|
||||
interceptor_gas_cmd, press_regen_paddle = self.calc_pedal_command(actuators.accel, CC.longActive, CS.out.vEgo)
|
||||
|
||||
if volt_one_pedal_braking:
|
||||
self.apply_gas = self.params.INACTIVE_REGEN
|
||||
self.apply_brake = max(self.apply_brake, self.volt_one_pedal_brake)
|
||||
|
||||
maneuver_sng_launch = self.longitudinal_maneuver_mode and self.is_volt
|
||||
if (
|
||||
self.CP.enableGasInterceptorDEPRECATED and
|
||||
self.apply_gas > self.params.INACTIVE_REGEN and
|
||||
use_interceptor_sng_launch(self.CP, CS, maneuver_sng_launch)
|
||||
):
|
||||
interceptor_gas_cmd = self.params.SNG_INTERCEPTOR_GAS
|
||||
if maneuver_sng_launch:
|
||||
interceptor_gas_cmd = max(interceptor_gas_cmd, float(np.interp(actuators.accel, [0.0, 1.0, 2.0], [self.params.SNG_INTERCEPTOR_GAS, 0.11, 0.16])))
|
||||
interceptor_gas_cmd = get_interceptor_sng_gas_cmd(
|
||||
self.CP, interceptor_gas_cmd, actuators.accel, self.params, maneuver_sng_launch,
|
||||
)
|
||||
self.apply_brake = 0
|
||||
self.apply_gas = self.params.INACTIVE_REGEN
|
||||
|
||||
@@ -679,25 +1104,50 @@ class CarController(CarControllerBase):
|
||||
|
||||
if self.CP.flags & GMFlags.CC_LONG.value:
|
||||
if should_send_cc_button_spam(self.CP, CC, CS):
|
||||
# Using extend instead of append since the message is only sent intermittently
|
||||
can_sends.extend(gmcan.create_gm_cc_spam_command(self.packer_pt, self, CS, actuators, starpilot_toggles))
|
||||
elif (CS.out.cruiseState.enabled and CC.enabled and self.frame % 52 == 0 and
|
||||
if self.CP.carFingerprint != CAR.CADILLAC_XT4_CC:
|
||||
# Using extend instead of append since the message is only sent intermittently
|
||||
can_sends.extend(gmcan.create_gm_cc_spam_command(self.packer_pt, self, CS, actuators, starpilot_toggles))
|
||||
else:
|
||||
if (CS.out.cruiseState.enabled and CC.enabled and self.frame % 52 == 0 and
|
||||
CS.cruise_buttons == CruiseButtons.UNPRESS and CS.out.gasPressed and CS.out.cruiseState.speed < CS.out.vEgo < hud_v_cruise):
|
||||
if self.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC:
|
||||
can_sends.append(gmcan.create_buttons_malibu(
|
||||
self.packer_pt, CanBus.POWERTRAIN, CruiseButtons.DECEL_SET,
|
||||
self.malibu_button_phase, CS.steering_button_prefix))
|
||||
self.malibu_button_phase = (self.malibu_button_phase + 1) % 4
|
||||
else:
|
||||
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.DECEL_SET))
|
||||
if self.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC:
|
||||
can_sends.append(gmcan.create_buttons_malibu(
|
||||
self.packer_pt, CanBus.POWERTRAIN, CruiseButtons.DECEL_SET,
|
||||
self.malibu_button_phase, CS.steering_button_prefix))
|
||||
self.malibu_button_phase = (self.malibu_button_phase + 1) % 4
|
||||
else:
|
||||
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.DECEL_SET))
|
||||
if self.CP.enableGasInterceptorDEPRECATED:
|
||||
can_sends.append(create_gas_interceptor_command(self.packer_pt, interceptor_gas_cmd, idx))
|
||||
if bolt_acc_pedal_friction_experiment:
|
||||
friction_brake_bus = get_friction_brake_bus(self.CP)
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
at_full_stop = at_full_stop and stopping
|
||||
|
||||
experiment_brake, self.bolt_acc_pedal_friction_release_frames, should_send_bolt_acc_pedal_friction = \
|
||||
get_bolt_acc_pedal_friction_command_state(
|
||||
self.apply_brake,
|
||||
bolt_acc_pedal_friction_main_on,
|
||||
self.bolt_acc_pedal_friction_release_frames,
|
||||
)
|
||||
|
||||
# This fingerprint is routed through the CC-only pedal path, so it
|
||||
# does not fall through to the normal friction-brake sender below.
|
||||
# Never apply stock friction with cruise main off, but do send a short
|
||||
# explicit zero-brake unwind so the last nonzero stock-EBCM command
|
||||
# cannot linger after a disengage or main-off event.
|
||||
if should_send_bolt_acc_pedal_friction:
|
||||
can_sends.append(gmcan.create_friction_brake_command(
|
||||
self.packer_ch, friction_brake_bus, experiment_brake, idx, bolt_acc_pedal_friction_main_on,
|
||||
near_stop, at_full_stop, self.CP))
|
||||
if self.CP.carFingerprint not in CC_ONLY_CAR:
|
||||
friction_brake_bus = get_friction_brake_bus(self.CP)
|
||||
# GM Camera exceptions
|
||||
# TODO: can we always check the longControlState?
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
at_full_stop = at_full_stop and stopping
|
||||
if should_send_acc_2cd(self.CP):
|
||||
can_sends.append(gmcan.create_acc_2cd_command(CanBus.POWERTRAIN, idx))
|
||||
|
||||
if self.CP.autoResumeSng:
|
||||
resume = actuators.longControlState != LongCtrlState.starting or CC.cruiseControl.resume
|
||||
@@ -709,57 +1159,67 @@ class CarController(CarControllerBase):
|
||||
acc_engaged = CC.enabled
|
||||
|
||||
if auto_hold_active:
|
||||
hold_brake = self.auto_hold_brake or estimate_auto_hold_brake(CS.out.brake, self.apply_brake)
|
||||
hold_brake = max(self.volt_one_pedal_brake, self.auto_hold_brake or estimate_auto_hold_brake(CS.out.brake, self.apply_brake, self.CP))
|
||||
hold_standstill = CS.pcm_acc_status == AccState.STANDSTILL
|
||||
hold_near_stop = CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE
|
||||
can_sends.append(gmcan.create_friction_brake_command(
|
||||
self.packer_ch, friction_brake_bus, hold_brake, idx, False, hold_near_stop, hold_standstill, self.CP))
|
||||
self.packer_ch, friction_brake_bus, hold_brake, idx, False, hold_near_stop, hold_standstill,
|
||||
self.CP, allow_near_stop_mode=True))
|
||||
CS.auto_hold_engaged = True
|
||||
CS.auto_hold_fault_suppression_timer = 1.0
|
||||
elif volt_one_pedal_hold_active:
|
||||
hold_brake = max(self.volt_one_pedal_brake, self.auto_hold_brake or estimate_auto_hold_brake(0.0, self.volt_one_pedal_brake, self.CP))
|
||||
hold_standstill = CS.pcm_acc_status == AccState.STANDSTILL
|
||||
hold_near_stop = CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE
|
||||
can_sends.append(gmcan.create_friction_brake_command(
|
||||
self.packer_ch, friction_brake_bus, hold_brake, idx, False, hold_near_stop, hold_standstill,
|
||||
self.CP, allow_near_stop_mode=True))
|
||||
CS.auto_hold_engaged = True
|
||||
CS.auto_hold_fault_suppression_timer = 1.0
|
||||
else:
|
||||
if volt_one_pedal_braking:
|
||||
at_full_stop = at_full_stop or CS.pcm_acc_status == AccState.STANDSTILL
|
||||
near_stop = near_stop or (CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE)
|
||||
# GasRegenCmdActive needs to be 1 to avoid cruise faults. It describes the ACC state, not actuation
|
||||
can_sends.append(gmcan.create_gas_regen_command(
|
||||
self.packer_pt, CanBus.POWERTRAIN, self.apply_gas, idx, acc_engaged, at_full_stop,
|
||||
include_always_one3=self.CP.carFingerprint in kaofui_cars, use_volt_layout=self.is_volt))
|
||||
can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, friction_brake_bus, self.apply_brake,
|
||||
idx, CC.enabled, near_stop, at_full_stop, self.CP))
|
||||
idx, CC.enabled, near_stop, at_full_stop, self.CP,
|
||||
allow_near_stop_mode=volt_one_pedal_braking))
|
||||
CS.auto_hold_engaged = False
|
||||
|
||||
if should_send_acc_dashboard_status(self.CP, dash_speed_spoof_active):
|
||||
acc_dashboard_status = get_acc_dashboard_status_values(CC.enabled, hud_v_cruise * CV.MS_TO_KPH, hud_control, CS)
|
||||
fcw_alert = get_acc_dashboard_fcw_alert(hud_alert, CS)
|
||||
can_sends.append(gmcan.create_acc_dashboard_command(self.packer_pt, CanBus.POWERTRAIN,
|
||||
acc_dashboard_status, fcw_alert))
|
||||
acc_dashboard_status_active = get_acc_dashboard_status_active(self.CP, CC)
|
||||
acc_dashboard_always_one = get_acc_dashboard_always_one(self.CP)
|
||||
can_sends.append(gmcan.create_acc_dashboard_command(self.packer_pt, CanBus.POWERTRAIN, acc_dashboard_status_active,
|
||||
hud_v_cruise * CV.MS_TO_KPH, hud_control, fcw_alert,
|
||||
acc_dashboard_always_one))
|
||||
|
||||
# Radar needs to know current speed and yaw rate (50hz),
|
||||
# and that ADAS is alive (10hz)
|
||||
if not self.CP.radarUnavailable:
|
||||
send_adas = True
|
||||
if should_send_adas_status(self.CP, self.CP.carFingerprint in kaofui_cars):
|
||||
tt = self.frame * DT_CTRL
|
||||
if self.CP.carFingerprint in kaofui_cars:
|
||||
if self.CP.carFingerprint not in ASCM_INT:
|
||||
send_adas = (self.CP.networkLocation != NetworkLocation.fwdCamera) and (self.CP.carFingerprint not in SDGM_CAR)
|
||||
|
||||
if send_adas:
|
||||
tt = self.frame * DT_CTRL
|
||||
if self.CP.carFingerprint in kaofui_cars:
|
||||
time_and_headlights_step = 10
|
||||
speed_and_accelerometer_step = 2
|
||||
if self.frame % time_and_headlights_step == 0:
|
||||
idx = (self.frame // time_and_headlights_step) % 4
|
||||
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
|
||||
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
|
||||
if self.frame % speed_and_accelerometer_step == 0:
|
||||
idx = (self.frame // speed_and_accelerometer_step) % 4
|
||||
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
|
||||
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
|
||||
else:
|
||||
time_and_headlights_step = 20
|
||||
if self.frame % time_and_headlights_step == 0:
|
||||
idx = (self.frame // time_and_headlights_step) % 4
|
||||
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
|
||||
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
|
||||
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
|
||||
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
|
||||
time_and_headlights_step = 10
|
||||
speed_and_accelerometer_step = 2
|
||||
if self.frame % time_and_headlights_step == 0:
|
||||
idx = (self.frame // time_and_headlights_step) % 4
|
||||
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
|
||||
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
|
||||
if self.frame % speed_and_accelerometer_step == 0:
|
||||
idx = (self.frame // speed_and_accelerometer_step) % 4
|
||||
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
|
||||
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
|
||||
else:
|
||||
time_and_headlights_step = 20
|
||||
if self.frame % time_and_headlights_step == 0:
|
||||
idx = (self.frame // time_and_headlights_step) % 4
|
||||
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
|
||||
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
|
||||
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
|
||||
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
|
||||
|
||||
keepalive_step = get_adas_keepalive_step(self.CP, self.CP.carFingerprint in kaofui_cars)
|
||||
if keepalive_step is not None and self.frame % keepalive_step == 0:
|
||||
@@ -785,14 +1245,33 @@ class CarController(CarControllerBase):
|
||||
else:
|
||||
if self.frame % 4 == 0 and auto_hold_active:
|
||||
idx = (self.frame // 4) % 4
|
||||
hold_brake = self.auto_hold_brake or estimate_auto_hold_brake(CS.out.brake, stock_hold_apply_brake)
|
||||
hold_brake = max(self.volt_one_pedal_brake, self.auto_hold_brake or estimate_auto_hold_brake(CS.out.brake, stock_hold_apply_brake, self.CP))
|
||||
hold_standstill = CS.pcm_acc_status == AccState.STANDSTILL
|
||||
hold_near_stop = CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE
|
||||
can_sends.append(gmcan.create_friction_brake_command(
|
||||
self.packer_ch, get_friction_brake_bus(self.CP), hold_brake, idx, False, hold_near_stop, hold_standstill, self.CP))
|
||||
self.packer_ch, get_friction_brake_bus(self.CP), hold_brake, idx, False, hold_near_stop, hold_standstill,
|
||||
self.CP, allow_near_stop_mode=True))
|
||||
CS.auto_hold_engaged = True
|
||||
CS.auto_hold_fault_suppression_timer = 1.0
|
||||
elif self.frame % 4 == 0 and volt_one_pedal_hold_active:
|
||||
idx = (self.frame // 4) % 4
|
||||
hold_brake = max(self.volt_one_pedal_brake, self.auto_hold_brake or estimate_auto_hold_brake(0.0, self.volt_one_pedal_brake, self.CP))
|
||||
hold_standstill = CS.pcm_acc_status == AccState.STANDSTILL
|
||||
hold_near_stop = CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE
|
||||
can_sends.append(gmcan.create_friction_brake_command(
|
||||
self.packer_ch, get_friction_brake_bus(self.CP), hold_brake, idx, False, hold_near_stop, hold_standstill,
|
||||
self.CP, allow_near_stop_mode=True))
|
||||
CS.auto_hold_engaged = True
|
||||
CS.auto_hold_fault_suppression_timer = 1.0
|
||||
elif self.frame % 4 == 0 and volt_one_pedal_braking:
|
||||
idx = (self.frame // 4) % 4
|
||||
near_stop = CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE
|
||||
can_sends.append(gmcan.create_friction_brake_command(
|
||||
self.packer_ch, get_friction_brake_bus(self.CP), self.volt_one_pedal_brake, idx, False, near_stop, False,
|
||||
self.CP, allow_near_stop_mode=True))
|
||||
CS.auto_hold_engaged = False
|
||||
elif self.frame % 4 == 0:
|
||||
self.apply_brake = 0
|
||||
CS.auto_hold_engaged = False
|
||||
|
||||
# While car is braking, cancel button causes ECM to enter a soft disable state with a fault status.
|
||||
|
||||
@@ -33,6 +33,13 @@ AUTO_HOLD_REGEN_RELEASE_COOLDOWN_S = 1.0
|
||||
|
||||
BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise,
|
||||
CruiseButtons.MAIN: ButtonType.mainCruise, CruiseButtons.CANCEL: ButtonType.cancel}
|
||||
HARD_BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.DECEL_SET: ButtonType.decelCruise}
|
||||
NORMAL_CRUISE_BUTTONS = (CruiseButtons.RES_ACCEL, CruiseButtons.DECEL_SET)
|
||||
|
||||
|
||||
def get_hard_cruise_buttons(steering_button_msg: dict) -> int:
|
||||
return steering_button_msg.get("ACCButtonsHard", CruiseButtons.INIT)
|
||||
|
||||
|
||||
GearShifter = structs.CarState.GearShifter
|
||||
BOLT_GEN1_CANCEL_PERSONALITY_CARS = {
|
||||
@@ -45,6 +52,19 @@ BOLT_CANCEL_BUTTON_CARS = BOLT_GEN1_CANCEL_PERSONALITY_CARS | {
|
||||
}
|
||||
|
||||
|
||||
def update_auto_hold_drive_timers(in_drive_for_hold: bool, moving_for_hold: bool,
|
||||
auto_hold_drive_time: float, one_pedal_drive_time: float) -> tuple[float, float]:
|
||||
if in_drive_for_hold:
|
||||
if moving_for_hold:
|
||||
auto_hold_drive_time = min(auto_hold_drive_time + DT_CTRL, AUTO_HOLD_MIN_DRIVE_TIME_S)
|
||||
one_pedal_drive_time = min(one_pedal_drive_time + DT_CTRL, AUTO_HOLD_MIN_DRIVE_TIME_S)
|
||||
else:
|
||||
auto_hold_drive_time = 0.0
|
||||
one_pedal_drive_time = 0.0
|
||||
|
||||
return auto_hold_drive_time, one_pedal_drive_time
|
||||
|
||||
|
||||
class CarState(CarStateBase):
|
||||
def __init__(self, CP, FPCP):
|
||||
super().__init__(CP, FPCP)
|
||||
@@ -64,11 +84,14 @@ class CarState(CarStateBase):
|
||||
|
||||
self.prev_distance_button = 0
|
||||
self.distance_button = 0
|
||||
self.hard_cruise_buttons = CruiseButtons.INIT
|
||||
self.force_reset_cruise_buttons = False
|
||||
|
||||
self.single_pedal_mode = False
|
||||
self.auto_hold_armed = False
|
||||
self.auto_hold_engaged = False
|
||||
self.auto_hold_drive_time = 0.0
|
||||
self.one_pedal_drive_time = 0.0
|
||||
self.auto_hold_fault_suppression_timer = 0.0
|
||||
self.regen_release_timer = 0.0
|
||||
self.user_regen_paddle_pressed = False
|
||||
@@ -81,12 +104,6 @@ class CarState(CarStateBase):
|
||||
self.lkas_enabled = 0
|
||||
self.pcm_acc_status = AccState.OFF
|
||||
self.stock_fcw_alert = 0
|
||||
self.stock_acc_cruise_state = 0
|
||||
self.stock_acc_lead_car = 0
|
||||
self.stock_acc_resume_button = 0
|
||||
self.stock_acc_speed_setpoint_kph = 0.0
|
||||
self.stock_acc_gap_level = 0
|
||||
self.stock_acc_cmd_active = 0
|
||||
|
||||
def update_button_enable(self, buttonEvents: list[structs.CarState.ButtonEvent]):
|
||||
if not self.CP.pcmCruise:
|
||||
@@ -119,21 +136,36 @@ class CarState(CarStateBase):
|
||||
sdgm_non_volt = self.CP.carFingerprint in SDGM_CAR and self.CP.carFingerprint not in kaofui_state_cars
|
||||
|
||||
prev_cruise_buttons = self.cruise_buttons
|
||||
prev_hard_cruise_buttons = self.hard_cruise_buttons
|
||||
prev_distance_button = self.distance_button
|
||||
if not sdgm_non_volt:
|
||||
self.cruise_buttons = pt_cp.vl["ASCMSteeringButton"]["ACCButtons"]
|
||||
self.distance_button = pt_cp.vl["ASCMSteeringButton"]["DistanceButton"]
|
||||
self.buttons_counter = pt_cp.vl["ASCMSteeringButton"]["RollingCounter"]
|
||||
self.steering_button_checksum = pt_cp.vl["ASCMSteeringButton"]["SteeringButtonChecksum"]
|
||||
steering_button_msg = pt_cp.vl["ASCMSteeringButton"]
|
||||
self.cruise_buttons = steering_button_msg["ACCButtons"]
|
||||
self.hard_cruise_buttons = get_hard_cruise_buttons(steering_button_msg)
|
||||
self.distance_button = steering_button_msg["DistanceButton"]
|
||||
self.buttons_counter = steering_button_msg["RollingCounter"]
|
||||
self.steering_button_checksum = steering_button_msg["SteeringButtonChecksum"]
|
||||
self.steering_button_ts_nanos = pt_cp.ts_nanos["ASCMSteeringButton"]["ACCButtons"]
|
||||
acc_always_one = pt_cp.vl["ASCMSteeringButton"]["ACCAlwaysOne"]
|
||||
acc_hidden_bit = pt_cp.vl["ASCMSteeringButton"].get("ACCHiddenBit", 0)
|
||||
acc_always_one = steering_button_msg["ACCAlwaysOne"]
|
||||
acc_hidden_bit = steering_button_msg.get("ACCHiddenBit", 0)
|
||||
self.steering_button_prefix = (int(acc_always_one) & 1) | ((int(acc_hidden_bit) & 1) << 6)
|
||||
else:
|
||||
self.cruise_buttons = cam_cp.vl["ASCMSteeringButton"]["ACCButtons"]
|
||||
self.distance_button = cam_cp.vl["ASCMSteeringButton"]["DistanceButton"]
|
||||
self.buttons_counter = cam_cp.vl["ASCMSteeringButton"]["RollingCounter"]
|
||||
steering_button_msg = cam_cp.vl["ASCMSteeringButton"]
|
||||
self.cruise_buttons = steering_button_msg["ACCButtons"]
|
||||
self.hard_cruise_buttons = get_hard_cruise_buttons(steering_button_msg)
|
||||
self.distance_button = steering_button_msg["DistanceButton"]
|
||||
self.buttons_counter = steering_button_msg["RollingCounter"]
|
||||
self.steering_button_ts_nanos = cam_cp.ts_nanos["ASCMSteeringButton"]["ACCButtons"]
|
||||
|
||||
# A GM hard press keeps the normal cruise button signal active too. Suppress
|
||||
# the normal button until the wheel reports a different normal state.
|
||||
if self.hard_cruise_buttons != CruiseButtons.INIT and self.cruise_buttons in NORMAL_CRUISE_BUTTONS:
|
||||
self.force_reset_cruise_buttons = True
|
||||
if self.force_reset_cruise_buttons and self.cruise_buttons in NORMAL_CRUISE_BUTTONS:
|
||||
self.cruise_buttons = CruiseButtons.UNPRESS
|
||||
elif self.force_reset_cruise_buttons and self.cruise_buttons not in NORMAL_CRUISE_BUTTONS:
|
||||
self.force_reset_cruise_buttons = False
|
||||
|
||||
self.pscm_status = copy.copy(pt_cp.vl["PSCMStatus"])
|
||||
self.moving_backward = (pt_cp.vl["EBCMWheelSpdRear"]["RLWheelDir"] == 2) or (pt_cp.vl["EBCMWheelSpdRear"]["RRWheelDir"] == 2)
|
||||
|
||||
@@ -178,7 +210,10 @@ class CarState(CarStateBase):
|
||||
ret.brakePressed = ret.brake >= VOLT_EBCM_BRAKE_PRESSED_THRESHOLD
|
||||
elif self.CP.carFingerprint in {CAR.CHEVROLET_MALIBU_CC} or (self.CP.carFingerprint == CAR.CHEVROLET_BLAZER and not no_accel_pos):
|
||||
ret.brakePressed = ret.brake >= 8
|
||||
elif (self.CP.flags & GMFlags.FORCE_BRAKE_C9.value) or ((self.CP.networkLocation == NetworkLocation.fwdCamera) and (self.CP.carFingerprint != CAR.CHEVROLET_BLAZER)):
|
||||
elif (self.CP.flags & GMFlags.FORCE_BRAKE_C9.value) or (
|
||||
self.CP.networkLocation == NetworkLocation.fwdCamera and
|
||||
self.CP.carFingerprint not in (SDGM_CAR | ASCM_INT | {CAR.CHEVROLET_BLAZER})
|
||||
):
|
||||
ret.brakePressed = pt_cp.vl["ECMEngineStatus"]["BrakePressed"] != 0
|
||||
else:
|
||||
# Some Volt 2016-17 have loose brake pedal push rod retainers which causes the ECM to believe
|
||||
@@ -189,13 +224,10 @@ class CarState(CarStateBase):
|
||||
ret.brakePressed = ret.brake >= analog_thresh
|
||||
|
||||
in_drive_for_hold = ret.gearShifter in (GearShifter.drive, GearShifter.low, GearShifter.manumatic)
|
||||
if in_drive_for_hold:
|
||||
if ret.brakePressed:
|
||||
self.auto_hold_drive_time = AUTO_HOLD_MIN_DRIVE_TIME_S
|
||||
else:
|
||||
self.auto_hold_drive_time = min(self.auto_hold_drive_time + DT_CTRL, AUTO_HOLD_MIN_DRIVE_TIME_S)
|
||||
else:
|
||||
self.auto_hold_drive_time = 0.0
|
||||
self.auto_hold_drive_time, self.one_pedal_drive_time = update_auto_hold_drive_timers(
|
||||
in_drive_for_hold, ret.vEgo > 0.1, self.auto_hold_drive_time, self.one_pedal_drive_time
|
||||
)
|
||||
if not in_drive_for_hold:
|
||||
self.auto_hold_armed = False
|
||||
self.auto_hold_engaged = False
|
||||
|
||||
@@ -280,12 +312,6 @@ class CarState(CarStateBase):
|
||||
acc_dashboard_status = cam_cp.vl["ASCMActiveCruiseControlStatus"]
|
||||
if self.CP.carFingerprint not in CC_ONLY_CAR:
|
||||
ret.cruiseState.speed = acc_dashboard_status["ACCSpeedSetpoint"] * CV.KPH_TO_MS
|
||||
self.stock_acc_cruise_state = int(acc_dashboard_status["ACCCruiseState"])
|
||||
self.stock_acc_lead_car = int(acc_dashboard_status["ACCLeadCar"])
|
||||
self.stock_acc_resume_button = int(acc_dashboard_status["ACCResumeButton"])
|
||||
self.stock_acc_speed_setpoint_kph = float(acc_dashboard_status["ACCSpeedSetpoint"])
|
||||
self.stock_acc_gap_level = int(acc_dashboard_status["ACCGapLevel"])
|
||||
self.stock_acc_cmd_active = int(acc_dashboard_status["ACCCmdActive"])
|
||||
# Preserve the stock camera FCW level from 0x370 so the controller can
|
||||
# replay it when that message is blocked and spoofed by openpilot long.
|
||||
self.stock_fcw_alert = int(acc_dashboard_status["FCWAlert"])
|
||||
@@ -374,19 +400,26 @@ class CarState(CarStateBase):
|
||||
lkas_events = [] if (suppress_malibu_side_buttons or suppress_bolt_cancel_lkas) else create_button_events(
|
||||
self.lkas_enabled, self.lkas_previously_enabled, {1: ButtonType.lkas}
|
||||
)
|
||||
hard_cruise_events = create_button_events(
|
||||
self.hard_cruise_buttons, prev_hard_cruise_buttons, HARD_BUTTONS_DICT, unpressed_btn=CruiseButtons.INIT
|
||||
)
|
||||
|
||||
# Don't add events if transitioning from INIT, unless it's to an actual button.
|
||||
if self.cruise_buttons != CruiseButtons.UNPRESS or prev_cruise_buttons != CruiseButtons.INIT:
|
||||
if (self.cruise_buttons != CruiseButtons.UNPRESS or prev_cruise_buttons != CruiseButtons.INIT or
|
||||
self.hard_cruise_buttons != CruiseButtons.INIT or prev_hard_cruise_buttons != CruiseButtons.INIT):
|
||||
ret.buttonEvents = [
|
||||
*cruise_events,
|
||||
*distance_events,
|
||||
*lkas_events,
|
||||
*hard_cruise_events,
|
||||
]
|
||||
|
||||
if ret.vEgo < self.CP.minSteerSpeed:
|
||||
ret.lowSpeedAlert = True
|
||||
|
||||
fp_ret = custom.StarPilotCarState.new_message()
|
||||
fp_ret.accelHardCruise = self.hard_cruise_buttons == CruiseButtons.RES_ACCEL or prev_hard_cruise_buttons == CruiseButtons.RES_ACCEL
|
||||
fp_ret.decelHardCruise = self.hard_cruise_buttons == CruiseButtons.DECEL_SET or prev_hard_cruise_buttons == CruiseButtons.DECEL_SET
|
||||
if bolt_cancel_button and self.cruise_buttons == CruiseButtons.CANCEL:
|
||||
fp_ret.cancelPressed = True
|
||||
fp_ret.sportGear = pt_cp.vl["SportMode"]["SportMode"] == 1
|
||||
|
||||
@@ -7,6 +7,7 @@ from opendbc.car.gm.values import CAR
|
||||
|
||||
CAMERA_DIAGNOSTIC_ADDRESS = 0x24b
|
||||
CAMERA_DIAGNOSTIC_RX_ADDRESS = 0x64b
|
||||
SASCM_ADDRESS = 0x2FF
|
||||
|
||||
|
||||
FINGERPRINTS = {
|
||||
@@ -210,6 +211,7 @@ FINGERPRINTS.update({
|
||||
CAR.GMC_ACADIA_ASCM: FINGERPRINTS[CAR.GMC_ACADIA],
|
||||
CAR.CHEVROLET_MALIBU_ASCM: FINGERPRINTS[CAR.CHEVROLET_MALIBU],
|
||||
CAR.CADILLAC_ESCALADE_ASCM: FINGERPRINTS[CAR.CADILLAC_ESCALADE],
|
||||
CAR.CADILLAC_ESCALADE_ESV_2019_ASCM: [{**fp, SASCM_ADDRESS: 8} for fp in FINGERPRINTS[CAR.CADILLAC_ESCALADE_ESV_2019]],
|
||||
CAR.CHEVROLET_SUBURBAN: FINGERPRINTS[CAR.CHEVROLET_SUBURBAN_CC],
|
||||
CAR.GMC_YUKON_CC: FINGERPRINTS[CAR.GMC_YUKON],
|
||||
CAR.CADILLAC_XT6: FINGERPRINTS[CAR.CHEVROLET_TRAVERSE],
|
||||
@@ -218,6 +220,7 @@ FINGERPRINTS.update({
|
||||
CAR.CHEVROLET_MALIBU_SDGM: FINGERPRINTS[CAR.CHEVROLET_MALIBU_CC],
|
||||
CAR.BUICK_BABYENCLAVE: FINGERPRINTS[CAR.CHEVROLET_TRAVERSE],
|
||||
CAR.CHEVROLET_SILVERADO_CC: FINGERPRINTS[CAR.CHEVROLET_SILVERADO],
|
||||
CAR.CADILLAC_XT4_CC: FINGERPRINTS[CAR.CADILLAC_XT4],
|
||||
CAR.BUICK_LACROSSE_ASCM: FINGERPRINTS[CAR.BUICK_LACROSSE],
|
||||
})
|
||||
|
||||
|
||||
@@ -17,6 +17,10 @@ MALIBU_BUTTON_MAP = {
|
||||
CruiseButtons.CANCEL: 5,
|
||||
}
|
||||
|
||||
ACC_CRUISE_STATE_ADAPTIVE = 2
|
||||
XT4_CC_BUTTON_BURST_FRAMES = 6
|
||||
XT4_CC_BUTTON_COUNTER_DELAY_FRAMES = 1
|
||||
|
||||
|
||||
def malibu_phase_map_for_button(button):
|
||||
key = MALIBU_BUTTON_MAP.get(button)
|
||||
@@ -179,22 +183,28 @@ def create_ecm_cruise_control_command(packer, bus, enabled, target_speed_kph):
|
||||
return CanData(0x3D1, bytes(dat), bus)
|
||||
|
||||
|
||||
def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_stop, at_full_stop, CP):
|
||||
def get_friction_brake_mode(apply_brake, enabled, near_stop, at_full_stop, CP, allow_near_stop_mode=False):
|
||||
mode = 0x1
|
||||
|
||||
# TODO: Understand this better. Volts and ICE Camera ACC cars are 0x1 when enabled with no brake
|
||||
if enabled and CP.carFingerprint in (CAR.CHEVROLET_BOLT_ACC_2022_2023,):
|
||||
if enabled and CP.carFingerprint in (CAR.CHEVROLET_BOLT_ACC_2022_2023, CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL):
|
||||
mode = 0x9
|
||||
|
||||
if apply_brake > 0:
|
||||
mode = 0xa
|
||||
if at_full_stop:
|
||||
mode = 0xd
|
||||
elif allow_near_stop_mode and near_stop:
|
||||
# Stock Volt auto hold can run with cruise main on but ACC inactive, so
|
||||
# there is no stock STANDSTILL state to promote 0xa -> 0xd. Restore the
|
||||
# older near-stop hold mode only for that path.
|
||||
mode = 0xb
|
||||
|
||||
# TODO: this is to have GM bringing the car to complete stop,
|
||||
# but currently it conflicts with OP controls, so turned off. Not set by all cars
|
||||
#elif near_stop:
|
||||
# mode = 0xb
|
||||
return mode
|
||||
|
||||
|
||||
def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_stop, at_full_stop, CP, allow_near_stop_mode=False):
|
||||
mode = get_friction_brake_mode(apply_brake, enabled, near_stop, at_full_stop, CP, allow_near_stop_mode)
|
||||
|
||||
brake = (0x1000 - apply_brake) & 0xfff
|
||||
checksum = (0x10000 - (mode << 12) - brake - idx) & 0xffff
|
||||
@@ -209,18 +219,25 @@ def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_s
|
||||
return packer.make_can_msg("EBCMFrictionBrakeCmd", bus, values)
|
||||
|
||||
|
||||
def create_acc_dashboard_command(packer, bus, status_values, fcw_alert):
|
||||
target_speed = min(max(float(status_values.get("ACCSpeedSetpoint", 0.0)), 0.0), 255.0)
|
||||
def create_acc_2cd_command(bus, idx):
|
||||
dat = bytearray([0x00, 0x2c, 0x03, 0xd3, 0x00])
|
||||
dat[0] = (idx & 0x3) << 6
|
||||
dat[4] = (0xfd - (idx & 0x3)) & 0xff
|
||||
return CanData(0x2CD, bytes(dat), bus)
|
||||
|
||||
|
||||
def create_acc_dashboard_command(packer, bus, enabled, target_speed_kph, hud_control, fcw_alert, acc_always_one=1):
|
||||
target_speed = min(target_speed_kph, 255)
|
||||
|
||||
values = {
|
||||
"ACCAlwaysOne": 1,
|
||||
"ACCCruiseState": int(status_values.get("ACCCruiseState", 0)) & 0x7,
|
||||
"ACCResumeButton": int(status_values.get("ACCResumeButton", 0)) & 0x1,
|
||||
"ACCAlwaysOne": acc_always_one,
|
||||
"ACCCruiseState": ACC_CRUISE_STATE_ADAPTIVE,
|
||||
"ACCResumeButton": 0,
|
||||
"ACCSpeedSetpoint": target_speed,
|
||||
"ACCGapLevel": int(status_values.get("ACCGapLevel", 0)) & 0x3,
|
||||
"ACCCmdActive": int(status_values.get("ACCCmdActive", 0)) & 0x1,
|
||||
"ACCAlwaysOne2": 1,
|
||||
"ACCLeadCar": int(status_values.get("ACCLeadCar", 0)) & 0x1,
|
||||
"ACCGapLevel": hud_control.leadDistanceBars * enabled, # 3 "far", 0 "inactive"
|
||||
"ACCCmdActive": enabled,
|
||||
"ACCAlwaysOne2": acc_always_one,
|
||||
"ACCLeadCar": hud_control.leadVisible,
|
||||
"FCWAlert": int(fcw_alert) & 0x3,
|
||||
}
|
||||
|
||||
@@ -303,6 +320,42 @@ def create_gm_cc_spam_command(packer, controller, CS, actuators, starpilot_toggl
|
||||
cruise_btn = CruiseButtons.INIT
|
||||
controller.apply_speed = speed_setpoint
|
||||
|
||||
if CS.CP.carFingerprint == CAR.CADILLAC_XT4_CC:
|
||||
if controller.xt4_cc_button_observed_counter != CS.buttons_counter:
|
||||
controller.xt4_cc_button_observed_counter = CS.buttons_counter
|
||||
controller.xt4_cc_button_counter_frame = controller.frame
|
||||
|
||||
if cruise_btn == CruiseButtons.INIT:
|
||||
controller.xt4_cc_button_burst_remaining = 0
|
||||
controller.xt4_cc_button_burst_button = CruiseButtons.INIT
|
||||
return []
|
||||
|
||||
if (controller.xt4_cc_button_burst_remaining > 0 and
|
||||
controller.xt4_cc_button_burst_button != cruise_btn):
|
||||
controller.xt4_cc_button_burst_remaining = 0
|
||||
|
||||
if controller.xt4_cc_button_burst_remaining == 0:
|
||||
if (controller.frame - controller.last_button_frame) * DT_CTRL <= rate:
|
||||
return []
|
||||
controller.last_button_frame = controller.frame
|
||||
controller.xt4_cc_button_burst_button = cruise_btn
|
||||
controller.xt4_cc_button_burst_remaining = XT4_CC_BUTTON_BURST_FRAMES
|
||||
controller.xt4_cc_button_burst_last_counter = -1
|
||||
|
||||
# XT4 physical taps hold the button for 5-7 consecutive 33 Hz frames.
|
||||
# Sending immediately after the stock frame is too early for the receiving ECU.
|
||||
if controller.frame - controller.xt4_cc_button_counter_frame < XT4_CC_BUTTON_COUNTER_DELAY_FRAMES:
|
||||
return []
|
||||
|
||||
# Send once per observed stock counter so the injected sequence has the same cadence.
|
||||
if controller.xt4_cc_button_burst_last_counter == CS.buttons_counter:
|
||||
return []
|
||||
|
||||
controller.xt4_cc_button_burst_last_counter = CS.buttons_counter
|
||||
controller.xt4_cc_button_burst_remaining -= 1
|
||||
idx = (CS.buttons_counter + 1) % 4
|
||||
return [create_buttons(packer, CanBus.POWERTRAIN, idx, controller.xt4_cc_button_burst_button)]
|
||||
|
||||
# Check rlogs closely - our message shouldn't show up on the pt bus for us
|
||||
# Or bus 2, since we're forwarding... but I think it does
|
||||
if (cruise_btn != CruiseButtons.INIT) and ((controller.frame - controller.last_button_frame) * DT_CTRL > rate):
|
||||
|
||||
@@ -62,12 +62,28 @@ NON_LINEAR_TORQUE_PARAMS = {
|
||||
"left": [3.8, 0.81, 0.24, 0.0465122],
|
||||
"right": [3.8, 0.81, 0.24, 0.0465122],
|
||||
},
|
||||
CAR.CADILLAC_XT4: {
|
||||
"left": [2.4, 0.95, 0.28, 0.0],
|
||||
"right": [2.4, 0.95, 0.28, 0.0],
|
||||
},
|
||||
CAR.CHEVROLET_VOLT: {
|
||||
"left": [1.5, 1.0, 0.155, 0.0],
|
||||
"right": [1.5, 1.0, 0.155, 0.0],
|
||||
"left": [1.525, 1.05, 0.155, 0.0],
|
||||
"right": [1.525, 0.95, 0.150, 0.0],
|
||||
},
|
||||
}
|
||||
|
||||
NON_LINEAR_TORQUE_PARAM_ALIASES = {
|
||||
CAR.CHEVROLET_VOLT_ASCM: CAR.CHEVROLET_VOLT,
|
||||
CAR.CHEVROLET_VOLT_CAMERA: CAR.CHEVROLET_VOLT,
|
||||
CAR.CHEVROLET_VOLT_CC: CAR.CHEVROLET_VOLT,
|
||||
CAR.CHEVROLET_VOLT_2019: CAR.CHEVROLET_VOLT,
|
||||
}
|
||||
|
||||
|
||||
def get_nonlinear_torque_params(car_fingerprint):
|
||||
source_fingerprint = NON_LINEAR_TORQUE_PARAM_ALIASES.get(car_fingerprint, car_fingerprint)
|
||||
return NON_LINEAR_TORQUE_PARAMS.get(source_fingerprint)
|
||||
|
||||
PEDAL_MSG = 0x201
|
||||
CAM_MSG = 0x320
|
||||
ACCELERATOR_POS_MSG = 0xBE
|
||||
@@ -129,7 +145,11 @@ class CarInterface(CarInterfaceBase):
|
||||
@staticmethod
|
||||
def get_pid_accel_limits(CP, current_speed, cruise_speed):
|
||||
if CP.enableGasInterceptorDEPRECATED and bool(CP.flags & GMFlags.PEDAL_LONG.value):
|
||||
if CP.carFingerprint in BOLT_PEDAL_LONG_CARS:
|
||||
if CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL:
|
||||
accel_min = CarControllerParams.ACCEL_MIN
|
||||
accel_max = np.interp(current_speed, [0.0, 1.5, 4.0, 8.0, 15.0],
|
||||
[0.54, 0.74, 1.03, 1.46, CarControllerParams.ACCEL_MAX])
|
||||
elif CP.carFingerprint in BOLT_PEDAL_LONG_CARS:
|
||||
accel_min = np.interp(current_speed, [0.0, 1.5, 4.0, 8.0, 15.0, 30.0],
|
||||
[-0.93, -1.28, -1.98, -2.58, -2.86, -2.95])
|
||||
accel_max = np.interp(current_speed, [0.0, 1.5, 4.0, 8.0, 15.0],
|
||||
@@ -161,7 +181,7 @@ class CarInterface(CarInterfaceBase):
|
||||
# The "lat_accel vs torque" relationship is assumed to be the sum of "sigmoid + linear" curves
|
||||
# An important thing to consider is that the slope at 0 should be > 0 (ideally >1)
|
||||
# This has big effect on the stability about 0 (noise when going straight)
|
||||
non_linear_torque_params = NON_LINEAR_TORQUE_PARAMS.get(self.CP.carFingerprint)
|
||||
non_linear_torque_params = get_nonlinear_torque_params(self.CP.carFingerprint)
|
||||
assert non_linear_torque_params, "The params are not defined"
|
||||
if isinstance(non_linear_torque_params, dict):
|
||||
side_key = "left" if lateral_acceleration >= 0 else "right"
|
||||
@@ -179,7 +199,7 @@ class CarInterface(CarInterfaceBase):
|
||||
return torque_values, lataccel_values
|
||||
|
||||
def torque_from_lateral_accel(self) -> TorqueFromLateralAccelCallbackType:
|
||||
if self.CP.carFingerprint in NON_LINEAR_TORQUE_PARAMS:
|
||||
if get_nonlinear_torque_params(self.CP.carFingerprint) is not None:
|
||||
torque_values, lataccel_values = self.get_lataccel_torque_siglin()
|
||||
|
||||
def torque_from_lateral_accel_siglin(lateral_acceleration: float, torque_params: structs.CarParams.LateralTorqueTuning):
|
||||
@@ -189,7 +209,7 @@ class CarInterface(CarInterfaceBase):
|
||||
return self.torque_from_lateral_accel_linear
|
||||
|
||||
def lateral_accel_from_torque(self) -> LateralAccelFromTorqueCallbackType:
|
||||
if self.CP.carFingerprint in NON_LINEAR_TORQUE_PARAMS:
|
||||
if get_nonlinear_torque_params(self.CP.carFingerprint) is not None:
|
||||
torque_values, lataccel_values = self.get_lataccel_torque_siglin()
|
||||
|
||||
def lateral_accel_from_torque_siglin(torque: float, torque_params: structs.CarParams.LateralTorqueTuning):
|
||||
@@ -213,6 +233,10 @@ class CarInterface(CarInterfaceBase):
|
||||
gm_auto_hold = params.get_bool("GMAutoHold")
|
||||
except UnknownKeyName:
|
||||
gm_auto_hold = False
|
||||
try:
|
||||
volt_one_pedal_mode = params.get_bool("VoltOnePedalMode")
|
||||
except UnknownKeyName:
|
||||
volt_one_pedal_mode = False
|
||||
|
||||
ret.brand = "gm"
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.gm)]
|
||||
@@ -410,8 +434,10 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate in (CAR.BUICK_LACROSSE, CAR.BUICK_LACROSSE_ASCM):
|
||||
elif candidate in (CAR.BUICK_LACROSSE, CAR.BUICK_LACROSSE_ASCM, CAR.BUICK_LACROSSE_ASCM_19US):
|
||||
CarInterfaceBase.configure_torque_tune(CAR.BUICK_LACROSSE, ret.lateralTuning)
|
||||
if candidate == CAR.BUICK_LACROSSE_ASCM_19US:
|
||||
ret.minSteerSpeed = 27 * CV.MPH_TO_MS
|
||||
|
||||
elif candidate == CAR.CADILLAC_ESCALADE:
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
@@ -420,7 +446,7 @@ class CarInterface(CarInterfaceBase):
|
||||
elif candidate == CAR.CADILLAC_ESCALADE_ASCM:
|
||||
CarInterfaceBase.configure_torque_tune(CAR.CADILLAC_ESCALADE, ret.lateralTuning)
|
||||
|
||||
elif candidate in (CAR.CADILLAC_ESCALADE_ESV, CAR.CADILLAC_ESCALADE_ESV_2019):
|
||||
elif candidate in (CAR.CADILLAC_ESCALADE_ESV, CAR.CADILLAC_ESCALADE_ESV_2019, CAR.CADILLAC_ESCALADE_ESV_2019_ASCM):
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
|
||||
if candidate == CAR.CADILLAC_ESCALADE_ESV:
|
||||
@@ -429,7 +455,8 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.lateralTuning.pid.kf = 0.000045
|
||||
else:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
torque_candidate = CAR.CADILLAC_ESCALADE_ESV_2019 if candidate == CAR.CADILLAC_ESCALADE_ESV_2019_ASCM else candidate
|
||||
CarInterfaceBase.configure_torque_tune(torque_candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate in (
|
||||
CAR.CHEVROLET_BOLT_ACC_2022_2023,
|
||||
@@ -482,7 +509,7 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.CADILLAC_XT4:
|
||||
elif candidate in (CAR.CADILLAC_XT4, CAR.CADILLAC_XT4_CC):
|
||||
ret.steerActuatorDelay = 0.2
|
||||
if not ret.openpilotLongitudinalControl:
|
||||
ret.minEnableSpeed = -1.
|
||||
@@ -515,7 +542,19 @@ class CarInterface(CarInterfaceBase):
|
||||
if not ret.openpilotLongitudinalControl:
|
||||
ret.minEnableSpeed = -1.
|
||||
if candidate == CAR.CHEVROLET_BLAZER:
|
||||
# The Blazer builds brake torque noticeably later than the rest of the GM set.
|
||||
# A slightly larger planner delay estimate starts the request earlier and keeps
|
||||
# stopped-lead approaches from turning into a late, harsh max-brake catch-up.
|
||||
ret.longitudinalActuatorDelay = 0.7
|
||||
ret.longitudinalTuning.kpBP = [0.0, 4.0, 12.0, 35.0]
|
||||
ret.longitudinalTuning.kpV = [0.09, 0.075, 0.055, 0.040]
|
||||
ret.longitudinalTuning.kiBP = [0.0, 4.0, 12.0, 35.0]
|
||||
ret.longitudinalTuning.kiV = [0.03, 0.04, 0.055, 0.07]
|
||||
ret.minEnableSpeed = 5 * CV.KPH_TO_MS
|
||||
ret.stoppingDecelRate = 1.0
|
||||
ret.vEgoStopping = 0.35
|
||||
ret.vEgoStarting = 0.35
|
||||
ret.stopAccel = -0.30
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.BUICK_BABYENCLAVE:
|
||||
@@ -570,6 +609,9 @@ class CarInterface(CarInterfaceBase):
|
||||
if is_bolt_2022_2023_pedal:
|
||||
# Gen2 Bolt pedal-long should follow the no-ACC panda path.
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_NO_ACC.value
|
||||
ret.startingState = True
|
||||
ret.startAccel = 0.55
|
||||
ret.vEgoStarting = max(ret.vEgoStarting, 0.35)
|
||||
|
||||
if candidate in (CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL, CAR.CHEVROLET_MALIBU_HYBRID_CC):
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_BOLT_2022_PEDAL.value
|
||||
@@ -613,7 +655,7 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.longitudinalTuning.kpBP = [0.0, 5.0, 15.0, 35.0]
|
||||
ret.longitudinalTuning.kpV = [0.02, 0.03, 0.028, 0.022]
|
||||
ret.longitudinalTuning.kiBP = [0.0, 5.0, 15.0, 35.0]
|
||||
ret.longitudinalTuning.kiV = [0.28, 0.26, 0.20, 0.16]
|
||||
ret.longitudinalTuning.kiV = [0.20, 0.18, 0.13, 0.08]
|
||||
|
||||
elif candidate in CC_ONLY_CAR and not ret.enableGasInterceptorDEPRECATED:
|
||||
ret.flags |= GMFlags.CC_LONG.value
|
||||
@@ -645,7 +687,7 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
# Exception for flashed cars, or cars whose camera was removed.
|
||||
missing_camera_msg = CAM_MSG not in fingerprint.get(CanBus.CAMERA, {})
|
||||
if (ret.networkLocation == NetworkLocation.fwdCamera or candidate in CC_ONLY_CAR) and missing_camera_msg and candidate not in SDGM_CAR:
|
||||
if (ret.networkLocation == NetworkLocation.fwdCamera or candidate in CC_ONLY_CAR) and missing_camera_msg and candidate not in (ASCM_INT | SDGM_CAR):
|
||||
ret.flags |= GMFlags.NO_CAMERA.value
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_NO_CAMERA.value
|
||||
|
||||
@@ -663,8 +705,9 @@ class CarInterface(CarInterfaceBase):
|
||||
if remote_start_boots_comma:
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_REMOTE_START_BOOTS_COMMA.value
|
||||
|
||||
volt_stock_auto_hold_safety = (
|
||||
gm_auto_hold and
|
||||
volt_stock_friction_brake_safety = (
|
||||
ret.openpilotLongitudinalControl and
|
||||
(gm_auto_hold or volt_one_pedal_mode) and
|
||||
candidate in {
|
||||
CAR.CHEVROLET_VOLT,
|
||||
CAR.CHEVROLET_VOLT_2019,
|
||||
@@ -672,13 +715,31 @@ class CarInterface(CarInterfaceBase):
|
||||
CAR.CHEVROLET_VOLT_CAMERA,
|
||||
}
|
||||
)
|
||||
if volt_stock_auto_hold_safety:
|
||||
# Reuse the paddle-scheduler safety bit as a Volt auto-hold marker on
|
||||
# non-pedal paths. Hold can run while OP longitudinal is configured but
|
||||
# not currently active, so the bit must be present regardless of the
|
||||
# current long-control mode.
|
||||
if volt_stock_friction_brake_safety:
|
||||
# Reuse the paddle-scheduler safety bit as a Volt stock friction-brake
|
||||
# marker on non-pedal paths. Auto hold and one-pedal can run while OP
|
||||
# longitudinal is configured but not currently active, so the bit must
|
||||
# be present regardless of the current long-control mode. Do not expose
|
||||
# the path at all when OP long is disabled in CarParams.
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
|
||||
|
||||
volt_stock_one_pedal_safety = (
|
||||
ret.openpilotLongitudinalControl and
|
||||
volt_one_pedal_mode and
|
||||
candidate in {
|
||||
CAR.CHEVROLET_VOLT,
|
||||
CAR.CHEVROLET_VOLT_2019,
|
||||
CAR.CHEVROLET_VOLT_ASCM,
|
||||
CAR.CHEVROLET_VOLT_CAMERA,
|
||||
}
|
||||
)
|
||||
if volt_stock_one_pedal_safety:
|
||||
# Reuse the 3D1 scheduler bit as a Volt one-pedal marker on non-pedal
|
||||
# ACC paths. The bit is ignored by the actual 3D1 scheduler unless the
|
||||
# car is on a pedal-long CC-only path, so this stays isolated from Bolt.
|
||||
# Do not expose the path at all when OP long is disabled in CarParams.
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_PANDA_3D1_SCHED.value
|
||||
|
||||
use_panda_3d1_sched = (
|
||||
ret.openpilotLongitudinalControl and
|
||||
ret.enableGasInterceptorDEPRECATED and
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import sys
|
||||
import types
|
||||
from types import SimpleNamespace
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from opendbc.car import structs
|
||||
|
||||
fake_interfaces = types.ModuleType("opendbc.car.interfaces")
|
||||
|
||||
@@ -33,21 +37,42 @@ fake_testing_grounds.testing_ground = SimpleNamespace(use_1=False)
|
||||
sys.modules.setdefault("openpilot.starpilot.common.testing_grounds", fake_testing_grounds)
|
||||
|
||||
from opendbc.car.gm.carcontroller import (
|
||||
AUTO_HOLD_DRIVE_GEARS,
|
||||
CarController,
|
||||
estimate_auto_hold_brake,
|
||||
get_adas_keepalive_step,
|
||||
get_auto_hold_stop_threshold,
|
||||
get_bolt_acc_pedal_friction_brake,
|
||||
get_bolt_acc_pedal_friction_command_state,
|
||||
get_bolt_acc_pedal_effective_brake_switch,
|
||||
get_bolt_acc_pedal_planner_brake_switch,
|
||||
get_bolt_pedal_long_accel_limit,
|
||||
get_interceptor_sng_gas_cmd,
|
||||
get_lka_steering_cmd_counter,
|
||||
get_volt_one_pedal_target_decel,
|
||||
get_testing_ground_1_brake_switch_bias,
|
||||
get_acc_dashboard_status_active,
|
||||
get_stock_cc_active_for_cancel,
|
||||
shape_bolt_acc_pedal_low_speed_friction,
|
||||
shape_truck_friction_brake,
|
||||
shape_truck_positive_accel,
|
||||
should_use_fixed_stopping_brake,
|
||||
should_activate_auto_hold,
|
||||
should_activate_volt_one_pedal,
|
||||
should_send_acc_2cd,
|
||||
should_send_adas_status,
|
||||
should_send_stock_long_cancel,
|
||||
should_spoof_dash_speed,
|
||||
should_spoof_ecm_cruise_status,
|
||||
supports_bolt_acc_pedal_friction_experiment,
|
||||
supports_volt_auto_hold,
|
||||
supports_volt_one_pedal,
|
||||
use_interceptor_sng_launch,
|
||||
)
|
||||
from opendbc.car.gm.values import AccState, CAR, GMFlags
|
||||
from opendbc.car.gm.gmcan import get_friction_brake_mode
|
||||
from opendbc.car.gm.values import AccState, CAR, CarControllerParams, GMFlags
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
|
||||
|
||||
def _cs(enabled, pcm_acc_status):
|
||||
@@ -81,6 +106,7 @@ def _controller(car_fingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021):
|
||||
controller.pedal_steady = 0.0
|
||||
controller.aego = 0.0
|
||||
controller.maneuver_paddle_mode = "auto"
|
||||
controller.bolt_acc_pedal_friction_low_speed_active = False
|
||||
return controller
|
||||
|
||||
|
||||
@@ -96,12 +122,201 @@ def test_gen2_bolt_acc_pedal_cancel_uses_enabled_only():
|
||||
assert not get_stock_cc_active_for_cancel(CP, _cs(False, AccState.ACTIVE))
|
||||
|
||||
|
||||
def test_bolt_acc_pedal_friction_experiment_is_single_fingerprint_only():
|
||||
assert supports_bolt_acc_pedal_friction_experiment(SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
openpilotLongitudinalControl=True,
|
||||
enableGasInterceptorDEPRECATED=True,
|
||||
flags=GMFlags.PEDAL_LONG.value,
|
||||
))
|
||||
assert not supports_bolt_acc_pedal_friction_experiment(SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_MALIBU_HYBRID_CC,
|
||||
openpilotLongitudinalControl=True,
|
||||
enableGasInterceptorDEPRECATED=True,
|
||||
flags=GMFlags.PEDAL_LONG.value,
|
||||
))
|
||||
assert not supports_bolt_acc_pedal_friction_experiment(SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
openpilotLongitudinalControl=False,
|
||||
enableGasInterceptorDEPRECATED=True,
|
||||
flags=GMFlags.PEDAL_LONG.value,
|
||||
))
|
||||
|
||||
|
||||
def test_bolt_acc_pedal_friction_blend_preserves_zero_before_crossover():
|
||||
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
|
||||
|
||||
assert get_bolt_acc_pedal_friction_brake(0, -2.8, 20.0, params) == 0
|
||||
|
||||
|
||||
def test_bolt_acc_pedal_friction_blend_uses_full_brake_range_after_regen():
|
||||
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
|
||||
|
||||
# Legacy mapping tops out early once regen has already consumed part of the
|
||||
# decel request. The experiment remaps that reduced span back to full scale.
|
||||
assert get_bolt_acc_pedal_friction_brake(286, -2.86, 20.0, params) == 400
|
||||
|
||||
|
||||
def test_bolt_acc_pedal_friction_blend_biases_small_commands_upward_at_speed():
|
||||
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
|
||||
|
||||
low_speed = get_bolt_acc_pedal_friction_brake(31, -2.86, 0.0, params)
|
||||
high_speed = get_bolt_acc_pedal_friction_brake(31, -2.86, 20.0, params)
|
||||
|
||||
assert low_speed > 31
|
||||
assert high_speed > low_speed
|
||||
|
||||
|
||||
def test_bolt_acc_pedal_friction_blend_applies_a_minimum_pre_stop_command_at_speed():
|
||||
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
|
||||
|
||||
assert get_bolt_acc_pedal_friction_brake(2, -2.86, 17.0, params) >= 18
|
||||
|
||||
|
||||
def test_bolt_acc_pedal_friction_blend_boosts_midrange_commands_before_stopping_phase():
|
||||
params = SimpleNamespace(ACCEL_MIN=-4.0, MAX_BRAKE=400)
|
||||
|
||||
assert get_bolt_acc_pedal_friction_brake(40, -2.86, 15.0, params) >= 80
|
||||
|
||||
|
||||
def test_bolt_acc_pedal_low_speed_friction_ignores_tiny_inactive_brake_requests():
|
||||
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(9, 5.0, False, False)
|
||||
|
||||
assert apply_brake == 0
|
||||
assert not active
|
||||
|
||||
|
||||
def test_bolt_acc_pedal_low_speed_friction_uses_hysteresis_once_active():
|
||||
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(24, 3.0, False, False)
|
||||
assert apply_brake == 24
|
||||
assert active
|
||||
|
||||
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(5, 3.0, False, active)
|
||||
assert apply_brake == 0
|
||||
assert not active
|
||||
|
||||
|
||||
def test_bolt_acc_pedal_low_speed_friction_fades_out_at_standstill():
|
||||
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(80, 0.2, True, True)
|
||||
|
||||
assert apply_brake == 0
|
||||
assert not active
|
||||
|
||||
|
||||
def test_bolt_acc_pedal_low_speed_friction_preserves_rolling_stop_authority():
|
||||
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(80, 1.2, True, True)
|
||||
|
||||
assert 0 < apply_brake < 80
|
||||
assert active
|
||||
|
||||
|
||||
def test_bolt_acc_pedal_low_speed_friction_drops_out_before_two_clamp():
|
||||
apply_brake, active = shape_bolt_acc_pedal_low_speed_friction(80, 0.9, True, True)
|
||||
|
||||
assert apply_brake == 0
|
||||
assert not active
|
||||
|
||||
|
||||
def test_bolt_pedal_long_accel_limit_matches_planner_regen_envelope():
|
||||
assert get_bolt_pedal_long_accel_limit(6.66) == pytest.approx(-2.379, abs=1e-3)
|
||||
assert get_bolt_pedal_long_accel_limit(3.0) == pytest.approx(-1.70, abs=1e-3)
|
||||
|
||||
|
||||
def test_bolt_acc_pedal_planner_brake_switch_is_lower_than_stock_switch():
|
||||
params = SimpleNamespace(ZERO_GAS=6150, BRAKE_SWITCH_LOOKUP_BP=[0.5, 10.0], BRAKE_SWITCH_LOOKUP_V=[6150, 5500])
|
||||
v_ego = 6.66
|
||||
|
||||
stock_switch = int(round(np.interp(v_ego, params.BRAKE_SWITCH_LOOKUP_BP, params.BRAKE_SWITCH_LOOKUP_V)))
|
||||
planner_switch = get_bolt_acc_pedal_planner_brake_switch(
|
||||
v_ego, params, tire_radius=0.336, mass=1832.0, coeff_drag=0.30, frontal_area=2.35, air_density=1.225,
|
||||
)
|
||||
|
||||
assert planner_switch < stock_switch
|
||||
|
||||
|
||||
def test_bolt_acc_pedal_effective_brake_switch_never_suppresses_stock_friction():
|
||||
params = SimpleNamespace(ZERO_GAS=6150, BRAKE_SWITCH_LOOKUP_BP=[0.5, 10.0], BRAKE_SWITCH_LOOKUP_V=[6150, 5500])
|
||||
v_ego = 5.434
|
||||
mass = 1805.0
|
||||
tire_radius = 0.075 * 2.63779 + 0.1453
|
||||
frontal_area = 1.05 * 2.63779 + 0.0679
|
||||
coeff_drag = 0.30
|
||||
air_density = 1.225
|
||||
accel_cmd = -1.399
|
||||
|
||||
aero_drag_force = 0.5 * coeff_drag * frontal_area * air_density * v_ego ** 2
|
||||
torque = tire_radius * ((mass * accel_cmd) + aero_drag_force)
|
||||
scaled_torque = torque + params.ZERO_GAS
|
||||
|
||||
stock_switch = int(round(np.interp(v_ego, params.BRAKE_SWITCH_LOOKUP_BP, params.BRAKE_SWITCH_LOOKUP_V)))
|
||||
planner_switch = get_bolt_acc_pedal_planner_brake_switch(
|
||||
v_ego, params, tire_radius=tire_radius, mass=mass,
|
||||
coeff_drag=coeff_drag, frontal_area=frontal_area, air_density=air_density,
|
||||
)
|
||||
effective_switch = get_bolt_acc_pedal_effective_brake_switch(stock_switch, planner_switch)
|
||||
|
||||
stock_brake_accel = min((scaled_torque - stock_switch) / (tire_radius * mass), 0)
|
||||
effective_brake_accel = min((scaled_torque - effective_switch) / (tire_radius * mass), 0)
|
||||
|
||||
assert planner_switch < stock_switch
|
||||
assert effective_switch == stock_switch
|
||||
assert stock_brake_accel < 0
|
||||
assert effective_brake_accel == stock_brake_accel
|
||||
|
||||
|
||||
def test_bolt_acc_pedal_friction_command_state_requires_cruise_main_for_positive_brake():
|
||||
command_brake, release_frames, should_send = get_bolt_acc_pedal_friction_command_state(120, False, 0)
|
||||
|
||||
assert command_brake == 0
|
||||
assert release_frames == 0
|
||||
assert not should_send
|
||||
|
||||
|
||||
def test_bolt_acc_pedal_friction_command_state_sends_zero_unwind_after_main_off():
|
||||
command_brake, release_frames, should_send = get_bolt_acc_pedal_friction_command_state(120, True, 0)
|
||||
|
||||
assert command_brake == 120
|
||||
assert release_frames > 0
|
||||
assert should_send
|
||||
|
||||
command_brake, release_frames, should_send = get_bolt_acc_pedal_friction_command_state(0, False, release_frames)
|
||||
|
||||
assert command_brake == 0
|
||||
assert release_frames >= 0
|
||||
assert should_send
|
||||
|
||||
|
||||
def test_fixed_stopping_brake_is_disabled_for_bolt_acc_pedal_experiment():
|
||||
CP = SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
openpilotLongitudinalControl=True,
|
||||
enableGasInterceptorDEPRECATED=True,
|
||||
flags=GMFlags.PEDAL_LONG.value,
|
||||
)
|
||||
|
||||
assert not should_use_fixed_stopping_brake(CP, True, True, False)
|
||||
|
||||
|
||||
def test_fixed_stopping_brake_stays_enabled_for_normal_acc_path():
|
||||
CP = SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023,
|
||||
openpilotLongitudinalControl=True,
|
||||
enableGasInterceptorDEPRECATED=False,
|
||||
flags=0,
|
||||
)
|
||||
|
||||
assert should_use_fixed_stopping_brake(CP, True, True, False)
|
||||
assert not should_use_fixed_stopping_brake(CP, False, True, False)
|
||||
assert not should_use_fixed_stopping_brake(CP, True, False, False)
|
||||
assert not should_use_fixed_stopping_brake(CP, True, True, True)
|
||||
|
||||
|
||||
def test_stock_cancel_is_suppressed_when_acc_is_faulted():
|
||||
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_CAMERA)
|
||||
cs = _cs(True, AccState.FAULTED)
|
||||
cs.out.accFaulted = True
|
||||
|
||||
assert not get_stock_cc_active_for_cancel(CP, cs)
|
||||
assert get_stock_cc_active_for_cancel(CP, cs)
|
||||
assert not should_send_stock_long_cancel(11, cs)
|
||||
|
||||
|
||||
@@ -143,11 +358,48 @@ def test_live_camera_path_does_not_send_pt_keepalive():
|
||||
assert get_adas_keepalive_step(cp, is_kaofui_car=True) is None
|
||||
|
||||
|
||||
def test_acc_2cd_replacement_only_used_with_live_camera_path():
|
||||
assert should_send_acc_2cd(SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_TRAILBLAZER, networkLocation=CarParams.NetworkLocation.fwdCamera, flags=0))
|
||||
assert not should_send_acc_2cd(SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_TRAILBLAZER, networkLocation=CarParams.NetworkLocation.fwdCamera, flags=GMFlags.NO_CAMERA.value))
|
||||
assert not should_send_acc_2cd(SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_TRAILBLAZER, networkLocation=CarParams.NetworkLocation.gateway, flags=0))
|
||||
assert not should_send_acc_2cd(SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_TRAILBLAZER_CC, networkLocation=CarParams.NetworkLocation.fwdCamera, flags=0))
|
||||
assert not should_send_acc_2cd(SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_BLAZER, networkLocation=CarParams.NetworkLocation.fwdCamera, flags=0))
|
||||
|
||||
|
||||
def test_ascm_int_cars_do_not_send_radar_status():
|
||||
common = {
|
||||
"networkLocation": CarParams.NetworkLocation.fwdCamera,
|
||||
"radarUnavailable": False,
|
||||
}
|
||||
|
||||
assert not should_send_adas_status(SimpleNamespace(carFingerprint=CAR.BUICK_LACROSSE_ASCM, **common), is_kaofui_car=True)
|
||||
assert not should_send_adas_status(SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM, **common), is_kaofui_car=True)
|
||||
|
||||
|
||||
def test_lacrosse_ascm_marks_acc_dashboard_active_for_aol_only():
|
||||
cc = SimpleNamespace(enabled=False, latActive=True)
|
||||
|
||||
assert get_acc_dashboard_status_active(SimpleNamespace(carFingerprint=CAR.BUICK_LACROSSE_ASCM), cc)
|
||||
assert not get_acc_dashboard_status_active(SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM), cc)
|
||||
assert not get_acc_dashboard_status_active(SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023), cc)
|
||||
|
||||
|
||||
def test_acc_dashboard_status_active_for_normal_enabled_cars():
|
||||
cc = SimpleNamespace(enabled=True, latActive=False)
|
||||
|
||||
assert get_acc_dashboard_status_active(SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM), cc)
|
||||
|
||||
|
||||
def test_volt_auto_hold_requires_toggle_supported_non_cc_only_volt_and_stock_safety():
|
||||
stock_safety = [SimpleNamespace(safetyParam=0x8000)]
|
||||
no_safety = [SimpleNamespace(safetyParam=0)]
|
||||
|
||||
assert supports_volt_auto_hold(
|
||||
assert not supports_volt_auto_hold(
|
||||
SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
|
||||
openpilotLongitudinalControl=True,
|
||||
@@ -166,6 +418,15 @@ def test_volt_auto_hold_requires_toggle_supported_non_cc_only_volt_and_stock_saf
|
||||
True,
|
||||
)
|
||||
assert supports_volt_auto_hold(
|
||||
SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
|
||||
openpilotLongitudinalControl=True,
|
||||
networkLocation=CarParams.NetworkLocation.fwdCamera,
|
||||
safetyConfigs=stock_safety,
|
||||
),
|
||||
True,
|
||||
)
|
||||
assert not supports_volt_auto_hold(
|
||||
SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_VOLT,
|
||||
openpilotLongitudinalControl=False,
|
||||
@@ -174,7 +435,7 @@ def test_volt_auto_hold_requires_toggle_supported_non_cc_only_volt_and_stock_saf
|
||||
),
|
||||
True,
|
||||
)
|
||||
assert supports_volt_auto_hold(
|
||||
assert not supports_volt_auto_hold(
|
||||
SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_VOLT_2019,
|
||||
openpilotLongitudinalControl=False,
|
||||
@@ -208,6 +469,74 @@ def test_auto_hold_brake_estimate_uses_driver_or_op_brake_and_clamps():
|
||||
assert estimate_auto_hold_brake(20.0, 40.0) == 110
|
||||
assert estimate_auto_hold_brake(20.0, 160.0) == 160
|
||||
assert estimate_auto_hold_brake(100.0, 400.0) == 240
|
||||
assert estimate_auto_hold_brake(7.0, 0.0, SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_2019)) == 100
|
||||
|
||||
|
||||
def test_volt_one_pedal_requires_toggle_supported_volt_stock_safety_and_ev_transmission():
|
||||
stock_safety = [SimpleNamespace(safetyParam=0x8000)]
|
||||
no_safety = [SimpleNamespace(safetyParam=0)]
|
||||
|
||||
assert supports_volt_one_pedal(
|
||||
SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
|
||||
openpilotLongitudinalControl=True,
|
||||
safetyConfigs=stock_safety,
|
||||
transmissionType=structs.CarParams.TransmissionType.direct,
|
||||
),
|
||||
True,
|
||||
)
|
||||
assert not supports_volt_one_pedal(
|
||||
SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
|
||||
openpilotLongitudinalControl=True,
|
||||
safetyConfigs=no_safety,
|
||||
transmissionType=structs.CarParams.TransmissionType.direct,
|
||||
),
|
||||
True,
|
||||
)
|
||||
assert not supports_volt_one_pedal(
|
||||
SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_VOLT_CC,
|
||||
openpilotLongitudinalControl=True,
|
||||
safetyConfigs=stock_safety,
|
||||
transmissionType=structs.CarParams.TransmissionType.direct,
|
||||
),
|
||||
True,
|
||||
)
|
||||
assert not supports_volt_one_pedal(
|
||||
SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
|
||||
openpilotLongitudinalControl=True,
|
||||
safetyConfigs=stock_safety,
|
||||
transmissionType=structs.CarParams.TransmissionType.automatic,
|
||||
),
|
||||
True,
|
||||
)
|
||||
assert not supports_volt_one_pedal(
|
||||
SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
|
||||
openpilotLongitudinalControl=False,
|
||||
safetyConfigs=stock_safety,
|
||||
transmissionType=structs.CarParams.TransmissionType.direct,
|
||||
),
|
||||
True,
|
||||
)
|
||||
assert not supports_volt_one_pedal(
|
||||
SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
|
||||
openpilotLongitudinalControl=True,
|
||||
safetyConfigs=stock_safety,
|
||||
transmissionType=structs.CarParams.TransmissionType.direct,
|
||||
),
|
||||
False,
|
||||
)
|
||||
|
||||
|
||||
def test_auto_hold_drive_gears_accept_capnp_dynamic_enum_membership():
|
||||
msg = structs.CarState.new_message()
|
||||
msg.gearShifter = structs.CarState.GearShifter.drive
|
||||
|
||||
assert msg.gearShifter in AUTO_HOLD_DRIVE_GEARS
|
||||
|
||||
|
||||
def test_auto_hold_activation_allows_direct_entry_from_stopped_brake_press():
|
||||
@@ -216,6 +545,7 @@ def test_auto_hold_activation_allows_direct_entry_from_stopped_brake_press():
|
||||
False,
|
||||
False,
|
||||
True,
|
||||
False,
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
@@ -229,6 +559,7 @@ def test_auto_hold_activation_stays_latched_after_brake_release():
|
||||
False,
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
@@ -236,12 +567,42 @@ def test_auto_hold_activation_stays_latched_after_brake_release():
|
||||
)
|
||||
|
||||
|
||||
def test_volt_2019_auto_hold_engaged_uses_near_stop_creep_hysteresis():
|
||||
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_2019)
|
||||
|
||||
assert get_auto_hold_stop_threshold(CP, True) == CarControllerParams.NEAR_STOP_BRAKE_PHASE
|
||||
assert should_activate_auto_hold(
|
||||
True,
|
||||
True,
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
0.05,
|
||||
get_auto_hold_stop_threshold(CP, True),
|
||||
)
|
||||
assert not should_activate_auto_hold(
|
||||
True,
|
||||
True,
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
0.05,
|
||||
)
|
||||
|
||||
|
||||
def test_auto_hold_activation_blocks_when_long_is_active_or_motion_is_above_threshold():
|
||||
assert not should_activate_auto_hold(
|
||||
True,
|
||||
True,
|
||||
False,
|
||||
True,
|
||||
False,
|
||||
True,
|
||||
True,
|
||||
False,
|
||||
@@ -254,6 +615,8 @@ def test_auto_hold_activation_blocks_when_long_is_active_or_motion_is_above_thre
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
0.03,
|
||||
)
|
||||
|
||||
@@ -264,6 +627,7 @@ def test_auto_hold_activation_allows_standstill_even_if_speed_filter_is_slightly
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
@@ -271,6 +635,150 @@ def test_auto_hold_activation_allows_standstill_even_if_speed_filter_is_slightly
|
||||
)
|
||||
|
||||
|
||||
def test_auto_hold_activation_releases_immediately_on_gas_press():
|
||||
assert not should_activate_auto_hold(
|
||||
True,
|
||||
True,
|
||||
True,
|
||||
False,
|
||||
True,
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
0.0,
|
||||
)
|
||||
|
||||
|
||||
def test_volt_one_pedal_activation_requires_main_l_mode_and_no_driver_input():
|
||||
assert should_activate_volt_one_pedal(
|
||||
True,
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
True,
|
||||
structs.CarState.GearShifter.low,
|
||||
3.0,
|
||||
)
|
||||
assert not should_activate_volt_one_pedal(
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
True,
|
||||
structs.CarState.GearShifter.low,
|
||||
3.0,
|
||||
)
|
||||
assert not should_activate_volt_one_pedal(
|
||||
True,
|
||||
True,
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
True,
|
||||
structs.CarState.GearShifter.low,
|
||||
3.0,
|
||||
)
|
||||
assert not should_activate_volt_one_pedal(
|
||||
True,
|
||||
True,
|
||||
False,
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
True,
|
||||
structs.CarState.GearShifter.low,
|
||||
3.0,
|
||||
)
|
||||
assert not should_activate_volt_one_pedal(
|
||||
True,
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
True,
|
||||
True,
|
||||
structs.CarState.GearShifter.low,
|
||||
3.0,
|
||||
)
|
||||
assert not should_activate_volt_one_pedal(
|
||||
True,
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
structs.CarState.GearShifter.drive,
|
||||
3.0,
|
||||
)
|
||||
|
||||
|
||||
def test_volt_one_pedal_target_decel_stays_active_above_low_speed_band():
|
||||
assert get_volt_one_pedal_target_decel(0.5 * CV.MPH_TO_MS) == -1.0
|
||||
assert get_volt_one_pedal_target_decel(6.0 * CV.MPH_TO_MS) == -1.1
|
||||
assert get_volt_one_pedal_target_decel(20.0 * CV.MPH_TO_MS) == -1.1
|
||||
|
||||
|
||||
def test_volt_one_pedal_regression_ignores_noisy_wheel_direction_bits():
|
||||
assert should_activate_volt_one_pedal(
|
||||
True,
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
True,
|
||||
structs.CarState.GearShifter.low,
|
||||
3.0,
|
||||
)
|
||||
|
||||
|
||||
def test_volt_one_pedal_requires_time_in_drive_before_arming():
|
||||
assert not should_activate_volt_one_pedal(
|
||||
True,
|
||||
True,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
False,
|
||||
True,
|
||||
structs.CarState.GearShifter.low,
|
||||
2.5,
|
||||
)
|
||||
|
||||
|
||||
def test_friction_brake_mode_keeps_near_stop_disabled_for_regular_long_braking():
|
||||
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM)
|
||||
|
||||
assert get_friction_brake_mode(120, False, True, False, CP) == 0xa
|
||||
|
||||
|
||||
def test_friction_brake_mode_uses_near_stop_hold_mode_for_volt_auto_hold():
|
||||
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_ASCM)
|
||||
|
||||
assert get_friction_brake_mode(120, False, True, False, CP, allow_near_stop_mode=True) == 0xb
|
||||
assert get_friction_brake_mode(120, False, True, True, CP, allow_near_stop_mode=True) == 0xd
|
||||
|
||||
|
||||
def test_friction_brake_mode_uses_stock_bolt_unwind_for_pedal_print_when_enabled():
|
||||
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL)
|
||||
|
||||
assert get_friction_brake_mode(0, False, True, False, CP) == 0x1
|
||||
assert get_friction_brake_mode(0, True, True, False, CP) == 0x9
|
||||
|
||||
|
||||
def test_friction_brake_mode_keeps_bolt_pedal_braking_mode_unchanged():
|
||||
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL)
|
||||
|
||||
assert get_friction_brake_mode(120, True, False, False, CP) == 0xa
|
||||
assert get_friction_brake_mode(120, True, True, True, CP) == 0xd
|
||||
|
||||
|
||||
def test_calc_pedal_command_small_accel_deadband_keeps_creep_target_stable():
|
||||
pos_controller = _controller()
|
||||
neg_controller = _controller()
|
||||
@@ -317,6 +825,58 @@ def test_calc_pedal_command_keeps_strong_positive_requests_responsive():
|
||||
assert pedal_gas - 0.18 > 0.04
|
||||
|
||||
|
||||
def test_shape_truck_positive_accel_softens_small_highway_requests():
|
||||
shaped = shape_truck_positive_accel(0.12, 26.0, True)
|
||||
|
||||
assert 0.08 < shaped < 0.095
|
||||
|
||||
|
||||
def test_shape_truck_positive_accel_keeps_mid_follow_requests_available():
|
||||
shaped = shape_truck_positive_accel(0.45, 13.5, True)
|
||||
|
||||
assert 0.43 < shaped < 0.45
|
||||
|
||||
|
||||
def test_shape_truck_positive_accel_leaves_large_requests_alone():
|
||||
assert shape_truck_positive_accel(1.0, 26.0, True) == 1.0
|
||||
|
||||
|
||||
def test_shape_truck_positive_accel_is_inactive_when_disabled_or_low_speed():
|
||||
assert shape_truck_positive_accel(0.12, 26.0, False) == 0.12
|
||||
assert shape_truck_positive_accel(0.12, 6.0, True) == 0.12
|
||||
|
||||
|
||||
def test_shape_truck_positive_accel_preserves_more_follow_authority_with_lead():
|
||||
base = shape_truck_positive_accel(0.28, 26.0, True)
|
||||
relieved = shape_truck_positive_accel(0.28, 26.0, True, lead_visible=True, set_speed_error=6.0)
|
||||
|
||||
assert relieved > base
|
||||
assert relieved < 0.28
|
||||
|
||||
|
||||
def test_shape_truck_positive_accel_does_not_relax_without_speed_error():
|
||||
base = shape_truck_positive_accel(0.28, 26.0, True)
|
||||
no_error = shape_truck_positive_accel(0.28, 26.0, True, lead_visible=True, set_speed_error=0.0)
|
||||
|
||||
assert no_error == base
|
||||
|
||||
|
||||
def test_shape_truck_friction_brake_suppresses_boundary_chatter():
|
||||
assert shape_truck_friction_brake(14, -0.3, False, False) == (0, False)
|
||||
|
||||
|
||||
def test_shape_truck_friction_brake_uses_hysteresis_once_engaged():
|
||||
assert shape_truck_friction_brake(39, -0.3, False, False) == (0, False)
|
||||
assert shape_truck_friction_brake(40, -0.3, False, False) == (40, True)
|
||||
assert shape_truck_friction_brake(14, -0.3, False, True) == (14, True)
|
||||
assert shape_truck_friction_brake(8, -0.3, False, True) == (0, False)
|
||||
|
||||
|
||||
def test_shape_truck_friction_brake_never_delays_meaningful_braking():
|
||||
assert shape_truck_friction_brake(5, -0.85, False, False) == (5, True)
|
||||
assert shape_truck_friction_brake(5, -0.2, True, False) == (5, True)
|
||||
|
||||
|
||||
def test_use_interceptor_sng_launch_requires_actual_near_stop():
|
||||
CP = SimpleNamespace(vEgoStarting=0.25)
|
||||
|
||||
@@ -326,6 +886,42 @@ def test_use_interceptor_sng_launch_requires_actual_near_stop():
|
||||
assert not use_interceptor_sng_launch(CP, _sng_cs(0.0, True, False))
|
||||
|
||||
|
||||
def test_bolt_acc_pedal_sng_launch_uses_physical_standstill_without_stock_acc_bit():
|
||||
CP = SimpleNamespace(
|
||||
vEgoStarting=0.25,
|
||||
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
enableGasInterceptorDEPRECATED=True,
|
||||
)
|
||||
|
||||
assert use_interceptor_sng_launch(CP, _sng_cs(0.0, True, False))
|
||||
assert use_interceptor_sng_launch(CP, _sng_cs(0.2, False, False))
|
||||
assert not use_interceptor_sng_launch(CP, _sng_cs(1.2, False, False))
|
||||
|
||||
|
||||
def test_bolt_acc_pedal_sng_launch_preserves_stronger_computed_pedal():
|
||||
CP = SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
openpilotLongitudinalControl=True,
|
||||
enableGasInterceptorDEPRECATED=True,
|
||||
flags=GMFlags.PEDAL_LONG.value,
|
||||
)
|
||||
params = SimpleNamespace(SNG_INTERCEPTOR_GAS=18. / 255.)
|
||||
|
||||
assert get_interceptor_sng_gas_cmd(CP, 0.2, 0.54, params, False) == pytest.approx(0.2)
|
||||
|
||||
|
||||
def test_other_pedal_sng_launch_keeps_fixed_floor_behavior():
|
||||
CP = SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021,
|
||||
openpilotLongitudinalControl=True,
|
||||
enableGasInterceptorDEPRECATED=True,
|
||||
flags=GMFlags.PEDAL_LONG.value,
|
||||
)
|
||||
params = SimpleNamespace(SNG_INTERCEPTOR_GAS=18. / 255.)
|
||||
|
||||
assert get_interceptor_sng_gas_cmd(CP, 0.2, 0.54, params, False) == pytest.approx(18. / 255.)
|
||||
|
||||
|
||||
def test_use_interceptor_sng_launch_extends_for_maneuver_mode():
|
||||
CP = SimpleNamespace(vEgoStarting=0.25)
|
||||
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import pytest
|
||||
import numpy as np
|
||||
from types import SimpleNamespace
|
||||
from parameterized import parameterized
|
||||
|
||||
from cereal import custom
|
||||
from opendbc.can import CANPacker, CANParser
|
||||
from opendbc.car import Bus, DT_CTRL, structs
|
||||
from opendbc.car.car_helpers import interfaces
|
||||
from opendbc.car.gm import gmcan
|
||||
from opendbc.car.gm.carstate import CarState as GMCarState
|
||||
from opendbc.car.gm.carstate import CarState as GMCarState, get_hard_cruise_buttons, update_auto_hold_drive_timers
|
||||
from opendbc.car.gm.carcontroller import (
|
||||
VisualAlert,
|
||||
get_acc_dashboard_always_one,
|
||||
get_acc_dashboard_fcw_alert,
|
||||
get_acc_dashboard_status_values,
|
||||
get_volt_one_pedal_lift_brake,
|
||||
should_send_acc_dashboard_status,
|
||||
should_send_cc_button_spam,
|
||||
should_spoof_dash_speed,
|
||||
@@ -18,7 +21,7 @@ from opendbc.car.gm.carcontroller import (
|
||||
import opendbc.car.gm.interface as gm_interface
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.gm.fingerprints import FINGERPRINTS
|
||||
from opendbc.car.gm.values import CAMERA_ACC_CAR, CAR, CC_ONLY_CAR, DBC, GM_RX_OFFSET, GMFlags, GMSafetyFlags
|
||||
from opendbc.car.gm.values import ASCM_INT, CAMERA_ACC_CAR, CAR, CC_ONLY_CAR, DBC, GM_RX_OFFSET, CarControllerParams, CruiseButtons, GMFlags, GMSafetyFlags
|
||||
from opendbc.safety import ALTERNATIVE_EXPERIENCE
|
||||
from openpilot.common.params import Params
|
||||
|
||||
@@ -63,6 +66,52 @@ class TestGMFingerprint:
|
||||
|
||||
|
||||
class TestGMInterface:
|
||||
def test_bolt_acc_pedal_pid_accel_limits_keep_full_negative_authority(self):
|
||||
cp = SimpleNamespace(
|
||||
enableGasInterceptorDEPRECATED=True,
|
||||
flags=GMFlags.PEDAL_LONG.value,
|
||||
carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
)
|
||||
|
||||
accel_min, accel_max = gm_interface.CarInterface.get_pid_accel_limits(cp, 4.73, 0.0)
|
||||
|
||||
assert accel_min == pytest.approx(CarControllerParams.ACCEL_MIN)
|
||||
assert accel_max == pytest.approx(np.interp(4.73, [0.0, 1.5, 4.0, 8.0, 15.0],
|
||||
[0.54, 0.74, 1.03, 1.46, CarControllerParams.ACCEL_MAX]))
|
||||
|
||||
def test_bolt_cc_pedal_pid_accel_limits_remain_regen_limited(self):
|
||||
cp = SimpleNamespace(
|
||||
enableGasInterceptorDEPRECATED=True,
|
||||
flags=GMFlags.PEDAL_LONG.value,
|
||||
carFingerprint=CAR.CHEVROLET_BOLT_CC_2022_2023,
|
||||
)
|
||||
|
||||
accel_min, _ = gm_interface.CarInterface.get_pid_accel_limits(cp, 4.73, 0.0)
|
||||
|
||||
assert accel_min == pytest.approx(np.interp(4.73, [0.0, 1.5, 4.0, 8.0, 15.0, 30.0],
|
||||
[-0.93, -1.28, -1.98, -2.58, -2.86, -2.95]))
|
||||
|
||||
def test_missing_hard_cruise_signal_defaults_to_init(self):
|
||||
assert get_hard_cruise_buttons({"ACCButtons": CruiseButtons.RES_ACCEL}) == CruiseButtons.INIT
|
||||
assert get_hard_cruise_buttons({"ACCButtonsHard": CruiseButtons.DECEL_SET}) == CruiseButtons.DECEL_SET
|
||||
|
||||
def test_volt_auto_hold_drive_timer_requires_motion_before_startup_arming(self):
|
||||
auto_hold_time, one_pedal_time = update_auto_hold_drive_timers(True, False, 0.0, 0.0)
|
||||
|
||||
assert auto_hold_time == 0.0
|
||||
assert one_pedal_time == 0.0
|
||||
|
||||
def test_volt_auto_hold_drive_timer_accumulates_only_while_moving(self):
|
||||
auto_hold_time, one_pedal_time = update_auto_hold_drive_timers(True, True, 0.0, 0.0)
|
||||
|
||||
assert auto_hold_time == pytest.approx(DT_CTRL)
|
||||
assert one_pedal_time == pytest.approx(DT_CTRL)
|
||||
|
||||
auto_hold_time, one_pedal_time = update_auto_hold_drive_timers(True, False, auto_hold_time, one_pedal_time)
|
||||
|
||||
assert auto_hold_time == pytest.approx(DT_CTRL)
|
||||
assert one_pedal_time == pytest.approx(DT_CTRL)
|
||||
|
||||
@parameterized.expand(VOLT_CARS)
|
||||
def test_volt_min_steer_speed_is_7_mph(self, car_model):
|
||||
CarInterface = interfaces[car_model]
|
||||
@@ -84,12 +133,15 @@ class TestGMInterface:
|
||||
|
||||
old_testing_ground = gm_interface.testing_ground
|
||||
gm_interface.testing_ground = SimpleNamespace(use_2=True)
|
||||
params = Params()
|
||||
params.put_bool("GMPedalLongitudinal", True)
|
||||
|
||||
try:
|
||||
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=False, is_release=False, docs=False,
|
||||
starpilot_toggles=_test_starpilot_toggles())
|
||||
finally:
|
||||
gm_interface.testing_ground = old_testing_ground
|
||||
params.remove("GMPedalLongitudinal")
|
||||
|
||||
if pedal_present:
|
||||
assert list(car_params.longitudinalTuning.kpV) == pytest.approx([0.10, 0.072, 0.05, 0.04])
|
||||
@@ -117,6 +169,21 @@ class TestGMInterface:
|
||||
assert car_params.flags & GMFlags.NO_CAMERA.value
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_NO_CAMERA.value
|
||||
|
||||
def test_volt_ascm_sparse_fingerprint_without_camera_does_not_set_no_camera(self):
|
||||
CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM]
|
||||
fingerprint = {
|
||||
0: FINGERPRINTS[CAR.CHEVROLET_VOLT][0].copy(),
|
||||
1: {},
|
||||
}
|
||||
fingerprint[0][0x2FF] = 8 # SASCM detected
|
||||
|
||||
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=False, is_release=False,
|
||||
docs=False, starpilot_toggles=_test_starpilot_toggles())
|
||||
|
||||
assert not (car_params.flags & GMFlags.NO_CAMERA.value)
|
||||
assert not (car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_NO_CAMERA.value)
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.HW_ASCM_INT.value
|
||||
|
||||
def test_silverado_alpha_long_uses_trimmed_longitudinal_tune(self):
|
||||
CarInterface = interfaces[CAR.CHEVROLET_SILVERADO]
|
||||
fingerprint = _empty_fingerprint()
|
||||
@@ -130,7 +197,28 @@ class TestGMInterface:
|
||||
assert list(car_params.longitudinalTuning.kpBP) == pytest.approx([0.0, 5.0, 15.0, 35.0])
|
||||
assert list(car_params.longitudinalTuning.kpV) == pytest.approx([0.02, 0.03, 0.028, 0.022])
|
||||
assert list(car_params.longitudinalTuning.kiBP) == pytest.approx([0.0, 5.0, 15.0, 35.0])
|
||||
assert list(car_params.longitudinalTuning.kiV) == pytest.approx([0.28, 0.26, 0.20, 0.16])
|
||||
assert list(car_params.longitudinalTuning.kiV) == pytest.approx([0.20, 0.18, 0.13, 0.08])
|
||||
|
||||
def test_blazer_uses_softer_low_speed_stop_hold_tune(self):
|
||||
CarInterface = interfaces[CAR.CHEVROLET_BLAZER]
|
||||
fingerprint = _empty_fingerprint()
|
||||
fingerprint[0] = FINGERPRINTS[CAR.CHEVROLET_BLAZER][0].copy()
|
||||
fingerprint[0][0x2FF] = 8 # SASCM present so alpha-long can enable on this platform
|
||||
|
||||
car_params = CarInterface.get_params(CAR.CHEVROLET_BLAZER, fingerprint, [], alpha_long=True, is_release=False,
|
||||
docs=False, starpilot_toggles=_test_starpilot_toggles())
|
||||
|
||||
assert car_params.openpilotLongitudinalControl
|
||||
assert list(car_params.longitudinalTuning.kpBP) == pytest.approx([0.0, 4.0, 12.0, 35.0])
|
||||
assert list(car_params.longitudinalTuning.kpV) == pytest.approx([0.09, 0.075, 0.055, 0.04])
|
||||
assert list(car_params.longitudinalTuning.kiBP) == pytest.approx([0.0, 4.0, 12.0, 35.0])
|
||||
assert list(car_params.longitudinalTuning.kiV) == pytest.approx([0.03, 0.04, 0.055, 0.07])
|
||||
assert car_params.longitudinalActuatorDelay == pytest.approx(0.7)
|
||||
assert car_params.minEnableSpeed == pytest.approx(5 * CV.KPH_TO_MS)
|
||||
assert car_params.stoppingDecelRate == pytest.approx(1.0)
|
||||
assert car_params.vEgoStopping == pytest.approx(0.35)
|
||||
assert car_params.vEgoStarting == pytest.approx(0.35)
|
||||
assert car_params.stopAccel == pytest.approx(-0.30)
|
||||
|
||||
def test_volt_gateway_without_accel_pos_uses_brake_pedal_message(self):
|
||||
CarInterface = interfaces[CAR.CHEVROLET_VOLT]
|
||||
@@ -163,6 +251,60 @@ class TestGMInterface:
|
||||
assert car_params.openpilotLongitudinalControl
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
|
||||
|
||||
def test_volt_auto_hold_does_not_set_stock_hold_safety_bit_with_op_long_disabled(self):
|
||||
CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM]
|
||||
fingerprint = _empty_fingerprint()
|
||||
fingerprint[0][0x2FF] = 8
|
||||
|
||||
params = Params()
|
||||
try:
|
||||
params.put_bool("GMAutoHold", True)
|
||||
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=False, is_release=False,
|
||||
docs=False, starpilot_toggles=_test_starpilot_toggles())
|
||||
finally:
|
||||
params.remove("GMAutoHold")
|
||||
|
||||
assert not car_params.openpilotLongitudinalControl
|
||||
assert not (car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value)
|
||||
|
||||
def test_volt_one_pedal_sets_stock_hold_safety_bit_without_auto_hold(self):
|
||||
CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM]
|
||||
fingerprint = _empty_fingerprint()
|
||||
fingerprint[0][0x2FF] = 8
|
||||
|
||||
params = Params()
|
||||
try:
|
||||
params.put_bool("GMAutoHold", False)
|
||||
params.put_bool("VoltOnePedalMode", True)
|
||||
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=True, is_release=False,
|
||||
docs=False, starpilot_toggles=_test_starpilot_toggles())
|
||||
finally:
|
||||
params.remove("GMAutoHold")
|
||||
params.remove("VoltOnePedalMode")
|
||||
|
||||
assert car_params.openpilotLongitudinalControl
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_3D1_SCHED.value
|
||||
|
||||
def test_volt_one_pedal_does_not_set_stock_hold_safety_bits_with_op_long_disabled(self):
|
||||
CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM]
|
||||
fingerprint = _empty_fingerprint()
|
||||
fingerprint[0][0x2FF] = 8
|
||||
|
||||
params = Params()
|
||||
try:
|
||||
params.put_bool("GMAutoHold", False)
|
||||
params.put_bool("VoltOnePedalMode", True)
|
||||
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=False, is_release=False,
|
||||
docs=False, starpilot_toggles=_test_starpilot_toggles())
|
||||
finally:
|
||||
params.remove("GMAutoHold")
|
||||
params.remove("VoltOnePedalMode")
|
||||
|
||||
assert not car_params.openpilotLongitudinalControl
|
||||
assert not (car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value)
|
||||
assert not (car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_3D1_SCHED.value)
|
||||
|
||||
@parameterized.expand(VOLT_CARS)
|
||||
def test_volt_bsm_is_enabled_without_fingerprint_match(self, car_model):
|
||||
CarInterface = interfaces[car_model]
|
||||
@@ -209,14 +351,20 @@ class TestGMInterface:
|
||||
params = Params()
|
||||
toggles = _test_starpilot_toggles()
|
||||
try:
|
||||
params.put_bool("GMPedalLongitudinal", True)
|
||||
params.put_bool("RemapCancelToDistance", True)
|
||||
car_params = CarInterface.get_params(CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL, fingerprint, [], alpha_long=False,
|
||||
is_release=False, docs=False, starpilot_toggles=toggles)
|
||||
finally:
|
||||
params.remove("GMPedalLongitudinal")
|
||||
params.remove("RemapCancelToDistance")
|
||||
|
||||
assert car_params.alternativeExperience & ALTERNATIVE_EXPERIENCE.GM_REMAP_CANCEL_TO_DISTANCE
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_BOLT_2022_PEDAL.value
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
|
||||
assert car_params.startingState
|
||||
assert car_params.startAccel == pytest.approx(0.55)
|
||||
assert car_params.vEgoStarting == pytest.approx(0.35)
|
||||
|
||||
def test_cadillac_xt5_sdgm_sascm_gates_alpha_long(self):
|
||||
CarInterface = interfaces[CAR.CADILLAC_XT5]
|
||||
@@ -242,6 +390,62 @@ class TestGMInterface:
|
||||
assert not sascm_params.pcmCruise
|
||||
assert sascm_params.safetyConfigs[0].safetyParam & GMSafetyFlags.HW_CAM_LONG.value
|
||||
|
||||
def test_cadillac_escalade_esv_2019_ascm_uses_sascm_and_2019_tune(self):
|
||||
base_fingerprint = FINGERPRINTS[CAR.CADILLAC_ESCALADE_ESV_2019][0]
|
||||
ascm_fingerprint = FINGERPRINTS[CAR.CADILLAC_ESCALADE_ESV_2019_ASCM][0]
|
||||
|
||||
assert CAR.CADILLAC_ESCALADE_ESV_2019_ASCM in ASCM_INT
|
||||
assert ascm_fingerprint[0x2FF] == 8
|
||||
assert {addr: length for addr, length in ascm_fingerprint.items() if addr != 0x2FF} == base_fingerprint
|
||||
|
||||
CarInterface = interfaces[CAR.CADILLAC_ESCALADE_ESV_2019_ASCM]
|
||||
fingerprint = _empty_fingerprint()
|
||||
fingerprint[0] = ascm_fingerprint.copy()
|
||||
|
||||
car_params = CarInterface.get_params(CAR.CADILLAC_ESCALADE_ESV_2019_ASCM, fingerprint, [], alpha_long=True, is_release=False,
|
||||
docs=False, starpilot_toggles=_test_starpilot_toggles())
|
||||
|
||||
assert car_params.flags & GMFlags.SASCM.value
|
||||
assert car_params.networkLocation == structs.CarParams.NetworkLocation.fwdCamera
|
||||
assert car_params.openpilotLongitudinalControl
|
||||
assert not car_params.pcmCruise
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.HW_ASCM_INT.value
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.HW_CAM_LONG.value
|
||||
assert car_params.lateralTuning.torque.latAccelFactor == pytest.approx(1.15)
|
||||
assert car_params.lateralTuning.torque.friction == pytest.approx(0.2)
|
||||
|
||||
def test_cadillac_xt4_uses_nonlinear_torque_curve_with_center_boost(self):
|
||||
CarInterface = interfaces[CAR.CADILLAC_XT4]
|
||||
car_params = CarInterface.get_non_essential_params(CAR.CADILLAC_XT4)
|
||||
ci = CarInterface(car_params, custom.StarPilotCarParams.new_message())
|
||||
torque_from_lataccel = ci.torque_from_lateral_accel()
|
||||
|
||||
low_lataccel = 0.2
|
||||
high_lataccel = 1.0
|
||||
low_torque = torque_from_lataccel(low_lataccel, car_params.lateralTuning.torque)
|
||||
high_torque = torque_from_lataccel(high_lataccel, car_params.lateralTuning.torque)
|
||||
|
||||
linear_low_torque = low_lataccel / car_params.lateralTuning.torque.latAccelFactor
|
||||
linear_high_torque = high_lataccel / car_params.lateralTuning.torque.latAccelFactor
|
||||
|
||||
assert low_torque > linear_low_torque * 1.15
|
||||
assert low_torque < linear_low_torque * 1.30
|
||||
assert high_torque == pytest.approx(linear_high_torque, rel=0.03)
|
||||
assert torque_from_lataccel(-low_lataccel, car_params.lateralTuning.torque) == pytest.approx(-low_torque, rel=1e-6)
|
||||
|
||||
@parameterized.expand((CAR.CHEVROLET_VOLT_ASCM, CAR.CHEVROLET_VOLT_CAMERA, CAR.CHEVROLET_VOLT_CC, CAR.CHEVROLET_VOLT_2019))
|
||||
def test_volt_integration_variants_share_nonlinear_torque_curve(self, candidate):
|
||||
assert gm_interface.get_nonlinear_torque_params(candidate) == gm_interface.NON_LINEAR_TORQUE_PARAMS[CAR.CHEVROLET_VOLT]
|
||||
|
||||
CarInterface = interfaces[candidate]
|
||||
car_params = CarInterface.get_non_essential_params(candidate)
|
||||
ci = CarInterface(car_params, custom.StarPilotCarParams.new_message())
|
||||
torque_from_lataccel = ci.torque_from_lateral_accel()
|
||||
|
||||
left_torque = torque_from_lataccel(0.5, car_params.lateralTuning.torque)
|
||||
right_torque = torque_from_lataccel(-0.5, car_params.lateralTuning.torque)
|
||||
assert left_torque > abs(right_torque)
|
||||
|
||||
|
||||
class TestGMCarController:
|
||||
def test_dash_speed_spoof_respects_live_stock_acc_toggles(self):
|
||||
@@ -256,6 +460,11 @@ class TestGMCarController:
|
||||
|
||||
assert should_spoof_dash_speed(cp, SimpleNamespace(disable_openpilot_long=False))
|
||||
|
||||
def test_volt_one_pedal_lift_brake_seeds_low_speed_braking(self):
|
||||
assert get_volt_one_pedal_lift_brake(2.1 * CV.MPH_TO_MS) == 0
|
||||
assert get_volt_one_pedal_lift_brake(2.0 * CV.MPH_TO_MS) == 20
|
||||
assert get_volt_one_pedal_lift_brake(0.10) == 80
|
||||
|
||||
def test_volt_camera_no_camera_sends_acc_dashboard_without_dash_spoof(self):
|
||||
cp = SimpleNamespace(carFingerprint=CAR.CHEVROLET_VOLT_CAMERA, flags=GMFlags.NO_CAMERA.value)
|
||||
|
||||
@@ -348,44 +557,122 @@ class TestGMCarController:
|
||||
|
||||
assert [msg[2] for msg in msgs] == [0]
|
||||
|
||||
def test_xt4_cc_redneck_spam_matches_physical_button_burst(self):
|
||||
packer = CANPacker(DBC[CAR.CADILLAC_XT4_CC][Bus.pt])
|
||||
controller = SimpleNamespace(
|
||||
frame=int(0.3 / DT_CTRL),
|
||||
last_button_frame=0,
|
||||
apply_speed=0,
|
||||
malibu_button_phase=0,
|
||||
xt4_cc_button_burst_remaining=0,
|
||||
xt4_cc_button_burst_button=CruiseButtons.INIT,
|
||||
xt4_cc_button_burst_last_counter=-1,
|
||||
xt4_cc_button_observed_counter=-1,
|
||||
xt4_cc_button_counter_frame=0,
|
||||
)
|
||||
cs = SimpleNamespace(
|
||||
CP=SimpleNamespace(
|
||||
carFingerprint=CAR.CADILLAC_XT4_CC,
|
||||
flags=GMFlags.CC_LONG.value,
|
||||
minEnableSpeed=24 * CV.MPH_TO_MS,
|
||||
),
|
||||
buttons_counter=0,
|
||||
out=SimpleNamespace(
|
||||
vEgo=25.0,
|
||||
cruiseState=SimpleNamespace(speed=20.0),
|
||||
),
|
||||
)
|
||||
actuators = SimpleNamespace(accel=1.0)
|
||||
|
||||
dats = []
|
||||
send_counts = []
|
||||
for counter in (0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 0, 0, 0, 1, 1, 1):
|
||||
cs.buttons_counter = counter
|
||||
msgs = gmcan.create_gm_cc_spam_command(packer, controller, cs, actuators, SimpleNamespace(is_metric=False))
|
||||
send_counts.append(len(msgs))
|
||||
dats.extend(bytes(msg[1]).hex() for msg in msgs)
|
||||
controller.frame += 1
|
||||
|
||||
assert send_counts == [0, 1, 0] * gmcan.XT4_CC_BUTTON_BURST_FRAMES
|
||||
assert dats == [
|
||||
"000000010125de",
|
||||
"00000001022acd",
|
||||
"00000001032fbc",
|
||||
"000000010020ef",
|
||||
"000000010125de",
|
||||
"00000001022acd",
|
||||
]
|
||||
assert controller.xt4_cc_button_burst_remaining == 0
|
||||
|
||||
def test_acc_dashboard_command_preserves_raw_fcw_alert_level(self):
|
||||
packer = CANPacker(DBC[CAR.CHEVROLET_BOLT_ACC_2022_2023][Bus.pt])
|
||||
parser = CANParser(DBC[CAR.CHEVROLET_BOLT_ACC_2022_2023][Bus.pt], [("ASCMActiveCruiseControlStatus", 0)], 0)
|
||||
msg = gmcan.create_acc_dashboard_command(
|
||||
packer,
|
||||
0,
|
||||
{
|
||||
"ACCCruiseState": 0,
|
||||
"ACCLeadCar": 1,
|
||||
"ACCResumeButton": 0,
|
||||
"ACCSpeedSetpoint": 100,
|
||||
"ACCGapLevel": 3,
|
||||
"ACCCmdActive": 1,
|
||||
},
|
||||
True,
|
||||
100,
|
||||
SimpleNamespace(leadDistanceBars=3, leadVisible=True),
|
||||
0x2,
|
||||
)
|
||||
|
||||
parser.update([0, [msg]])
|
||||
|
||||
assert parser.vl["ASCMActiveCruiseControlStatus"]["FCWAlert"] == 2
|
||||
values = parser.vl["ASCMActiveCruiseControlStatus"]
|
||||
|
||||
def test_acc_dashboard_command_can_replay_stock_status_payload(self):
|
||||
packer = CANPacker(DBC[CAR.CHEVROLET_VOLT_ASCM][Bus.pt])
|
||||
assert values["ACCAlwaysOne"] == 1
|
||||
assert values["ACCAlwaysOne2"] == 1
|
||||
assert values["ACCCruiseState"] == 2
|
||||
assert values["ACCCmdActive"] == 1
|
||||
assert values["FCWAlert"] == 2
|
||||
|
||||
def test_acc_dashboard_command_allows_camera_acc_zero_reserved_bits(self):
|
||||
packer = CANPacker(DBC[CAR.CHEVROLET_TRAILBLAZER][Bus.pt])
|
||||
parser = CANParser(DBC[CAR.CHEVROLET_TRAILBLAZER][Bus.pt], [("ASCMActiveCruiseControlStatus", 0)], 0)
|
||||
msg = gmcan.create_acc_dashboard_command(
|
||||
packer,
|
||||
0,
|
||||
{
|
||||
"ACCCruiseState": 0,
|
||||
"ACCLeadCar": 1,
|
||||
"ACCResumeButton": 0,
|
||||
"ACCSpeedSetpoint": 50,
|
||||
"ACCGapLevel": 2,
|
||||
"ACCCmdActive": 0,
|
||||
},
|
||||
True,
|
||||
67.1875,
|
||||
SimpleNamespace(leadDistanceBars=1, leadVisible=False),
|
||||
0,
|
||||
acc_always_one=0,
|
||||
)
|
||||
|
||||
parser.update([0, [msg]])
|
||||
values = parser.vl["ASCMActiveCruiseControlStatus"]
|
||||
|
||||
assert msg[1] == b"\x00\x02\x94\x33\x00\x00"
|
||||
assert values["ACCAlwaysOne"] == 0
|
||||
assert values["ACCAlwaysOne2"] == 0
|
||||
assert values["ACCCruiseState"] == 2
|
||||
assert values["ACCCmdActive"] == 1
|
||||
|
||||
def test_acc_dashboard_always_one_matches_camera_acc_platforms(self):
|
||||
assert get_acc_dashboard_always_one(SimpleNamespace(carFingerprint=CAR.CHEVROLET_TRAILBLAZER)) == 0
|
||||
assert get_acc_dashboard_always_one(SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023)) == 1
|
||||
|
||||
def test_acc_dashboard_command_uses_openpilot_hud_when_disengaged(self):
|
||||
packer = CANPacker(DBC[CAR.CHEVROLET_VOLT_ASCM][Bus.pt])
|
||||
parser = CANParser(DBC[CAR.CHEVROLET_VOLT_ASCM][Bus.pt], [("ASCMActiveCruiseControlStatus", 0)], 0)
|
||||
msg = gmcan.create_acc_dashboard_command(
|
||||
packer,
|
||||
0,
|
||||
False,
|
||||
50,
|
||||
SimpleNamespace(leadDistanceBars=2, leadVisible=True),
|
||||
0x3,
|
||||
)
|
||||
|
||||
assert msg[1].hex() == "010023200113"
|
||||
parser.update([0, [msg]])
|
||||
values = parser.vl["ASCMActiveCruiseControlStatus"]
|
||||
|
||||
assert values["ACCSpeedSetpoint"] == 50
|
||||
assert values["ACCCruiseState"] == 2
|
||||
assert values["ACCGapLevel"] == 0
|
||||
assert values["ACCCmdActive"] == 0
|
||||
assert values["ACCLeadCar"] == 1
|
||||
assert values["FCWAlert"] == 3
|
||||
|
||||
def test_acc_dashboard_fcw_alert_prefers_openpilot_alert(self):
|
||||
cs = SimpleNamespace(
|
||||
@@ -418,45 +705,3 @@ class TestGMCarController:
|
||||
)
|
||||
|
||||
assert get_acc_dashboard_fcw_alert(VisualAlert.none, cs) == 0x3
|
||||
|
||||
def test_acc_dashboard_status_values_use_openpilot_hud_when_enabled(self):
|
||||
cs = SimpleNamespace(
|
||||
stock_acc_cruise_state=5,
|
||||
stock_acc_lead_car=0,
|
||||
stock_acc_resume_button=1,
|
||||
stock_acc_speed_setpoint_kph=42.0,
|
||||
stock_acc_gap_level=1,
|
||||
stock_acc_cmd_active=0,
|
||||
)
|
||||
|
||||
values = get_acc_dashboard_status_values(True, 105.0, SimpleNamespace(leadDistanceBars=3, leadVisible=True), cs)
|
||||
|
||||
assert values == {
|
||||
"ACCCruiseState": 0,
|
||||
"ACCLeadCar": 1,
|
||||
"ACCResumeButton": 0,
|
||||
"ACCSpeedSetpoint": 105.0,
|
||||
"ACCGapLevel": 3,
|
||||
"ACCCmdActive": 1,
|
||||
}
|
||||
|
||||
def test_acc_dashboard_status_values_reuse_stock_camera_status_when_disabled(self):
|
||||
cs = SimpleNamespace(
|
||||
stock_acc_cruise_state=0,
|
||||
stock_acc_lead_car=1,
|
||||
stock_acc_resume_button=0,
|
||||
stock_acc_speed_setpoint_kph=50.0,
|
||||
stock_acc_gap_level=2,
|
||||
stock_acc_cmd_active=0,
|
||||
)
|
||||
|
||||
values = get_acc_dashboard_status_values(False, 0.0, SimpleNamespace(leadDistanceBars=0, leadVisible=False), cs)
|
||||
|
||||
assert values == {
|
||||
"ACCCruiseState": 0,
|
||||
"ACCLeadCar": 1,
|
||||
"ACCResumeButton": 0,
|
||||
"ACCSpeedSetpoint": 50.0,
|
||||
"ACCGapLevel": 2,
|
||||
"ACCCmdActive": 0,
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from types import SimpleNamespace
|
||||
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car.gm import gmcan
|
||||
from opendbc.car.gm.values import CAR, DBC
|
||||
@@ -26,6 +28,47 @@ class TestGMCan:
|
||||
assert dat[1] & 0x1
|
||||
assert decoded == 8848
|
||||
|
||||
def test_acc_2cd_command_matches_stock_camera_counter_layout(self):
|
||||
assert [gmcan.create_acc_2cd_command(0, idx)[1].hex() for idx in range(4)] == [
|
||||
"002c03d3fd",
|
||||
"402c03d3fc",
|
||||
"802c03d3fb",
|
||||
"c02c03d3fa",
|
||||
]
|
||||
|
||||
def test_prndl2_command_matches_bolt_gen2_regen_paddle_spoof(self):
|
||||
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL)
|
||||
|
||||
addr, dat, bus = gmcan.create_prndl2_command(self.packer, 0, False, CP)
|
||||
assert addr == 0x1F5
|
||||
assert bus == 0
|
||||
assert dat.hex() == "0c0c000600000100"
|
||||
|
||||
addr, dat, bus = gmcan.create_prndl2_command(self.packer, 0, True, CP)
|
||||
assert addr == 0x1F5
|
||||
assert bus == 0
|
||||
assert dat.hex() == "0c0c000500020100"
|
||||
|
||||
def test_prndl2_command_matches_bolt_gen1_regen_paddle_spoof(self):
|
||||
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021)
|
||||
|
||||
addr, dat, bus = gmcan.create_prndl2_command(self.packer, 0, True, CP)
|
||||
|
||||
assert addr == 0x1F5
|
||||
assert bus == 0
|
||||
assert dat.hex() == "0c0c000700020100"
|
||||
|
||||
def test_regen_paddle_command_matches_bolt_spoof(self):
|
||||
addr, dat, bus = gmcan.create_regen_paddle_command(self.packer, 0, False)
|
||||
assert addr == 0xBD
|
||||
assert bus == 0
|
||||
assert dat.hex() == "00000000000000"
|
||||
|
||||
addr, dat, bus = gmcan.create_regen_paddle_command(self.packer, 0, True)
|
||||
assert addr == 0xBD
|
||||
assert bus == 0
|
||||
assert dat.hex() == "20000000000000"
|
||||
|
||||
|
||||
def test_gas_regen_command_matches_starpilot_volt_2019(self):
|
||||
packer = CANPacker(DBC[CAR.CHEVROLET_VOLT_2019]["pt"])
|
||||
@@ -42,11 +85,3 @@ class TestGMCan:
|
||||
assert addr == 0x2CB
|
||||
assert bus == 0
|
||||
assert dat.hex() == "41429c4000bd63bf"
|
||||
|
||||
def test_gas_regen_command_matches_opgm_plain_volt_layout(self):
|
||||
packer = CANPacker("gm_global_a_powertrain_generated")
|
||||
addr, dat, bus = gmcan.create_gas_regen_command(packer, 0, 5000, 1, True, False, use_generated_layout=True)
|
||||
|
||||
assert addr == 0x2CB
|
||||
assert bus == 0
|
||||
assert dat.hex() == "41435c7000bca38f"
|
||||
|
||||
@@ -283,6 +283,10 @@ class CAR(Platforms):
|
||||
[GMCarDocs("Buick LaCrosse 2017-19 ASCM Harness")],
|
||||
BUICK_LACROSSE.specs,
|
||||
)
|
||||
BUICK_LACROSSE_ASCM_19US = GMPlatformConfig(
|
||||
[GMCarDocs("Buick LaCrosse 2019 US ASCM Harness")],
|
||||
BUICK_LACROSSE.specs,
|
||||
)
|
||||
BUICK_REGAL = GMASCMPlatformConfig(
|
||||
[GMCarDocs("Buick Regal Essence 2018")],
|
||||
GMCarSpecs(mass=1714, wheelbase=2.83, steerRatio=14.4, centerToFrontRatio=0.4),
|
||||
@@ -303,6 +307,10 @@ class CAR(Platforms):
|
||||
[GMCarDocs("Cadillac Escalade ESV 2019", "Adaptive Cruise Control (ACC) & LKAS")],
|
||||
CADILLAC_ESCALADE_ESV.specs,
|
||||
)
|
||||
CADILLAC_ESCALADE_ESV_2019_ASCM = GMPlatformConfig(
|
||||
[GMCarDocs("Cadillac Escalade ESV Platinum 2019 ASCM Harness", "Adaptive Cruise Control (ACC) & LKAS")],
|
||||
CADILLAC_ESCALADE_ESV_2019.specs,
|
||||
)
|
||||
CHEVROLET_BOLT_ACC_2022_2023 = GMPlatformConfig(
|
||||
[
|
||||
GMCarDocs("Chevrolet Bolt ACC 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", video="https://youtu.be/xvwzGMUA210"),
|
||||
@@ -364,6 +372,10 @@ class CAR(Platforms):
|
||||
[GMCarDocs("Cadillac XT4 2023", "Driver Assist Package")],
|
||||
GMCarSpecs(mass=1660, wheelbase=2.78, steerRatio=14.4, centerToFrontRatio=0.4),
|
||||
)
|
||||
CADILLAC_XT4_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Cadillac XT4 - No-ACC")],
|
||||
CADILLAC_XT4.specs,
|
||||
)
|
||||
CADILLAC_XT5 = GMSDGMPlatformConfig(
|
||||
[GMCarDocs("Cadillac XT5 2022", "Driver Assist Package")],
|
||||
CarSpecs(mass=1810, wheelbase=2.86, steerRatio=16.34, centerToFrontRatio=0.5),
|
||||
@@ -566,6 +578,7 @@ CC_ONLY_CAR = {
|
||||
CAR.CHEVROLET_MALIBU_CC,
|
||||
CAR.CHEVROLET_MALIBU_HYBRID_CC,
|
||||
CAR.CHEVROLET_SILVERADO_CC,
|
||||
CAR.CADILLAC_XT4_CC,
|
||||
}
|
||||
CC_REGEN_PADDLE_CAR = {
|
||||
CAR.CHEVROLET_BOLT_CC_2018_2021,
|
||||
@@ -576,7 +589,15 @@ CC_REGEN_PADDLE_CAR = {
|
||||
CAMERA_ACC_CAR.update(CC_ONLY_CAR)
|
||||
|
||||
# ASCM-INT paths are only enabled when SASCM (0x2FF) is detected at runtime
|
||||
ASCM_INT = {CAR.CHEVROLET_VOLT_ASCM, CAR.GMC_ACADIA_ASCM, CAR.CHEVROLET_MALIBU_ASCM, CAR.CADILLAC_ESCALADE_ASCM, CAR.BUICK_LACROSSE_ASCM}
|
||||
ASCM_INT = {
|
||||
CAR.CHEVROLET_VOLT_ASCM,
|
||||
CAR.GMC_ACADIA_ASCM,
|
||||
CAR.CHEVROLET_MALIBU_ASCM,
|
||||
CAR.CADILLAC_ESCALADE_ASCM,
|
||||
CAR.CADILLAC_ESCALADE_ESV_2019_ASCM,
|
||||
CAR.BUICK_LACROSSE_ASCM,
|
||||
CAR.BUICK_LACROSSE_ASCM_19US,
|
||||
}
|
||||
|
||||
STEER_THRESHOLD = 1.0
|
||||
|
||||
|
||||
@@ -4,11 +4,12 @@ import numpy as np
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus, DT_CTRL, make_tester_present_msg, rate_limit, structs
|
||||
from opendbc.car.common.filter_simple import FirstOrderFilter
|
||||
from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_steer_angle_limits_vm, common_fault_avoidance
|
||||
from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_steer_angle_limits_vm, common_fault_avoidance, get_max_angle_delta_vm, get_max_angle_vm
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.hyundai import hyundaicanfd, hyundaican
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CAR, CANFD_RADAR_LIVE_LONGITUDINAL_CAR
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CAR, CANFD_ANGLE_LONGITUDINAL_CAR, \
|
||||
CANFD_RADAR_LIVE_LONGITUDINAL_CAR, kia_ev6_gt_line_longitudinal_tuning
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
from openpilot.common.params import Params
|
||||
@@ -52,6 +53,7 @@ IONIQ_6_LAUNCH_HOLD_SPEED_V = [0.75, 0.6, 0.4, 0.0]
|
||||
IONIQ_6_STOP_BRAKE_CAP_MAX_SPEED = 2.0
|
||||
IONIQ_6_STOP_BRAKE_CAP_SPEED_BP = [0.0, 0.08, 0.25, 0.6, 1.2, 2.0, 3.0]
|
||||
IONIQ_6_STOP_BRAKE_CAP_ACCEL_V = [-0.15, -0.16, -0.22, -0.42, -0.78, -1.15, -1.40]
|
||||
EV6_GT_LINE_STOP_BRAKE_CAP_MAX_SPEED = 1.2
|
||||
IONIQ_6_STOP_HOLD_JERK_BP = [0.0, 0.15, 0.6, 1.2, 2.0, 3.0]
|
||||
IONIQ_6_STOP_HOLD_JERK_V = [0.35, 0.40, 0.48, 0.65, 0.85, 1.10]
|
||||
IONIQ_6_STOP_RELEASE_JERK_BP = [0.0, 0.15, 0.5]
|
||||
@@ -63,6 +65,24 @@ REDNECK_BUTTON_COPIES_TIME = 7
|
||||
REDNECK_BUTTON_COPIES_TIME_IMPERIAL = [REDNECK_BUTTON_COPIES_TIME + 3, 70]
|
||||
REDNECK_BUTTON_COPIES_TIME_METRIC = [REDNECK_BUTTON_COPIES_TIME, 40]
|
||||
ANGLE_SAFETY_BASELINE_MODEL = str(CAR.KIA_SPORTAGE_HEV_2026)
|
||||
DEFAULT_ANGLE_SMOOTHING_VEGO_BP = [5.0, 10.0, 20.0]
|
||||
DEFAULT_ANGLE_SMOOTHING_ALPHA_V = [0.2, 0.1, 0.0]
|
||||
EV9_STOP_REQUEST_SPEED = 0.47
|
||||
EV9_STANDSTILL_DELAY_FRAMES = 178
|
||||
EV9_STOP_RELEASE_DELAY_FRAMES = 6
|
||||
BLINDSPOT_WARNING_FLASH_SAMPLES = 20
|
||||
BLINDSPOT_WARNING_FLASH_ON_SAMPLES = 16
|
||||
BLINDSPOT_WARNING_SOUND_SAMPLES = 36
|
||||
|
||||
|
||||
def egmp_dynamic_longitudinal_tuning(CP) -> bool:
|
||||
return CP.carFingerprint in (CAR.HYUNDAI_IONIQ_6, CAR.KIA_EV9) or \
|
||||
kia_ev6_gt_line_longitudinal_tuning(CP.carFingerprint, getattr(CP, "carVin", ""))
|
||||
|
||||
|
||||
def should_reset_ev6_gt_line_longitudinal_tuning(CP, long_control_state: LongCtrlState) -> bool:
|
||||
return kia_ev6_gt_line_longitudinal_tuning(CP.carFingerprint, getattr(CP, "carVin", "")) and \
|
||||
long_control_state == LongCtrlState.off
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -78,6 +98,13 @@ class Ioniq6LongitudinalTuningState:
|
||||
long_control_state_last: LongCtrlState = LongCtrlState.off
|
||||
|
||||
|
||||
def reset_ev6_gt_line_longitudinal_tuning(state: Ioniq6LongitudinalTuningState, CP,
|
||||
long_control_state: LongCtrlState) -> Ioniq6LongitudinalTuningState:
|
||||
if should_reset_ev6_gt_line_longitudinal_tuning(CP, long_control_state):
|
||||
return Ioniq6LongitudinalTuningState(long_control_state_last=long_control_state)
|
||||
return state
|
||||
|
||||
|
||||
@dataclass
|
||||
class GenesisG90LongitudinalTuningState:
|
||||
actual_accel: float = 0.0
|
||||
@@ -85,6 +112,29 @@ class GenesisG90LongitudinalTuningState:
|
||||
long_control_state_last: LongCtrlState = LongCtrlState.off
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class EV9LongitudinalTuningState:
|
||||
stop_request: bool = False
|
||||
cruise_standstill: bool = False
|
||||
stop_request_frames: int = 0
|
||||
release_frames: int = 0
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class BlindspotWarningOutput:
|
||||
mirror_lamp_active: bool = False
|
||||
sound_active: bool = False
|
||||
|
||||
|
||||
@dataclass
|
||||
class BlindspotWarningState:
|
||||
flash_phase: int = 0
|
||||
mirror_warning_active: bool = False
|
||||
escalated_prev: bool = False
|
||||
sound_remaining: int = 0
|
||||
sound_armed: bool = True
|
||||
|
||||
|
||||
def _jerk_limited_integrator(desired_accel: float, last_accel: float, jerk_upper: float, jerk_lower: float) -> float:
|
||||
step = (jerk_upper if desired_accel >= last_accel else jerk_lower) * DT_CTRL * 5.0
|
||||
return float(np.clip(desired_accel, last_accel - step, last_accel + step))
|
||||
@@ -97,8 +147,93 @@ def _calculate_ioniq_6_dynamic_lower_jerk(accel_error: float) -> float:
|
||||
return IONIQ_6_LONG_MIN_JERK
|
||||
|
||||
|
||||
def should_track_stop_accel_directly(stopping: bool, v_ego: float,
|
||||
accel_cmd: float, actual_accel: float) -> bool:
|
||||
return bool(stopping and v_ego > EV6_GT_LINE_STOP_BRAKE_CAP_MAX_SPEED and accel_cmd < actual_accel)
|
||||
|
||||
|
||||
def should_use_ev6_gt_line_stop_direct_tracking(ev6_gt_line: bool, stopping: bool, v_ego: float,
|
||||
accel_cmd: float, actual_accel: float) -> bool:
|
||||
return bool(ev6_gt_line and stopping and v_ego > EV6_GT_LINE_STOP_BRAKE_CAP_MAX_SPEED and accel_cmd < actual_accel)
|
||||
|
||||
|
||||
def update_ev9_longitudinal_tuning(state: EV9LongitudinalTuningState, enabled: bool,
|
||||
stopping: bool, v_ego: float) -> EV9LongitudinalTuningState:
|
||||
if not enabled:
|
||||
return EV9LongitudinalTuningState()
|
||||
|
||||
if stopping:
|
||||
if not state.stop_request and v_ego > EV9_STOP_REQUEST_SPEED:
|
||||
return EV9LongitudinalTuningState()
|
||||
frames = state.stop_request_frames + 1 if state.stop_request else 0
|
||||
return EV9LongitudinalTuningState(
|
||||
stop_request=True,
|
||||
cruise_standstill=frames >= EV9_STANDSTILL_DELAY_FRAMES,
|
||||
stop_request_frames=frames,
|
||||
)
|
||||
|
||||
if state.stop_request:
|
||||
release_frames = state.release_frames + 1
|
||||
if release_frames <= EV9_STOP_RELEASE_DELAY_FRAMES:
|
||||
return EV9LongitudinalTuningState(
|
||||
stop_request=True,
|
||||
cruise_standstill=False,
|
||||
stop_request_frames=state.stop_request_frames,
|
||||
release_frames=release_frames,
|
||||
)
|
||||
|
||||
return EV9LongitudinalTuningState()
|
||||
|
||||
|
||||
def update_blindspot_warning(state: BlindspotWarningState, escalated: bool,
|
||||
blinker: bool) -> BlindspotWarningOutput:
|
||||
if not blinker:
|
||||
state.flash_phase = 0
|
||||
state.mirror_warning_active = False
|
||||
state.escalated_prev = False
|
||||
state.sound_remaining = 0
|
||||
state.sound_armed = True
|
||||
return BlindspotWarningOutput()
|
||||
|
||||
rising = escalated and not state.escalated_prev
|
||||
if rising:
|
||||
state.flash_phase = 0
|
||||
state.mirror_warning_active = True
|
||||
if state.sound_armed:
|
||||
state.sound_remaining = BLINDSPOT_WARNING_SOUND_SAMPLES
|
||||
state.sound_armed = False
|
||||
elif escalated:
|
||||
state.flash_phase = (state.flash_phase + 1) % BLINDSPOT_WARNING_FLASH_SAMPLES
|
||||
state.mirror_warning_active = True
|
||||
elif state.mirror_warning_active and state.flash_phase < BLINDSPOT_WARNING_FLASH_ON_SAMPLES - 1:
|
||||
state.flash_phase += 1
|
||||
else:
|
||||
state.flash_phase = 0
|
||||
state.mirror_warning_active = False
|
||||
|
||||
state.escalated_prev = escalated
|
||||
sound_active = state.sound_remaining > 0
|
||||
if state.sound_remaining > 0:
|
||||
state.sound_remaining -= 1
|
||||
return BlindspotWarningOutput(
|
||||
mirror_lamp_active=state.mirror_warning_active and state.flash_phase < BLINDSPOT_WARNING_FLASH_ON_SAMPLES,
|
||||
sound_active=sound_active,
|
||||
)
|
||||
|
||||
|
||||
def reset_egmp_longitudinal_tuning(state: Ioniq6LongitudinalTuningState) -> Ioniq6LongitudinalTuningState:
|
||||
state.desired_accel = 0.0
|
||||
state.actual_accel = 0.0
|
||||
state.accel_last = 0.0
|
||||
state.jerk_upper = 0.0
|
||||
state.jerk_lower = 0.0
|
||||
state.launch_active = False
|
||||
return state
|
||||
|
||||
|
||||
def update_ioniq_6_longitudinal_tuning(state: Ioniq6LongitudinalTuningState, accel_cmd: float, v_ego: float, a_ego: float,
|
||||
long_control_state: LongCtrlState, long_active: bool) -> Ioniq6LongitudinalTuningState:
|
||||
long_control_state: LongCtrlState, long_active: bool,
|
||||
ev6_gt_line: bool = False, low_speed_stop_brake_cap: bool = False) -> Ioniq6LongitudinalTuningState:
|
||||
starting = long_control_state == LongCtrlState.starting
|
||||
stopping = long_control_state == LongCtrlState.stopping
|
||||
restart_from_stop = state.long_control_state_last in (LongCtrlState.stopping, LongCtrlState.starting) and \
|
||||
@@ -140,7 +275,9 @@ def update_ioniq_6_longitudinal_tuning(state: Ioniq6LongitudinalTuningState, acc
|
||||
state.jerk_lower = min(dynamic_lower_jerk, lower_speed_limit)
|
||||
|
||||
if state.stopping:
|
||||
if v_ego <= IONIQ_6_STOP_BRAKE_CAP_MAX_SPEED:
|
||||
stop_brake_cap_max_speed = EV6_GT_LINE_STOP_BRAKE_CAP_MAX_SPEED if ev6_gt_line or low_speed_stop_brake_cap else \
|
||||
IONIQ_6_STOP_BRAKE_CAP_MAX_SPEED
|
||||
if v_ego <= stop_brake_cap_max_speed:
|
||||
stop_brake_cap = float(np.interp(v_ego, IONIQ_6_STOP_BRAKE_CAP_SPEED_BP, IONIQ_6_STOP_BRAKE_CAP_ACCEL_V))
|
||||
state.desired_accel = min(0.0, max(accel_cmd, stop_brake_cap))
|
||||
state.jerk_upper = min(state.jerk_upper, float(np.interp(v_ego, IONIQ_6_STOP_HOLD_JERK_BP, IONIQ_6_STOP_HOLD_JERK_V)) * IONIQ_6_RESPONSE_MULTIPLIER)
|
||||
@@ -202,6 +339,16 @@ def get_baseline_safety_cp():
|
||||
return CarInterface.get_non_essential_params(ANGLE_SAFETY_BASELINE_MODEL)
|
||||
|
||||
|
||||
def get_angle_smoothing_alpha(CP, v_ego: float) -> float:
|
||||
return float(np.interp(v_ego, DEFAULT_ANGLE_SMOOTHING_VEGO_BP, DEFAULT_ANGLE_SMOOTHING_ALPHA_V))
|
||||
|
||||
|
||||
def direct_angle_request_allowed(v_ego_raw, measured_angle, last_angle, drive_gear, VM, params):
|
||||
safety_v_ego = max(v_ego_raw - 1.0, 1.0)
|
||||
max_safety_angle = get_max_angle_vm(safety_v_ego, VM, params)
|
||||
return drive_gear and abs(measured_angle) <= max_safety_angle and abs(last_angle) <= max_safety_angle
|
||||
|
||||
|
||||
def compute_torque_reduction_gain(steering_torque, v_ego, lat_active, last_gain):
|
||||
if lat_active:
|
||||
ceiling = np.interp(v_ego, [0.5, 1.5], [1.0, 0.85])
|
||||
@@ -253,6 +400,7 @@ class CarController(CarControllerBase):
|
||||
self.VM = VehicleModel(CP)
|
||||
self.BASELINE_VM = VehicleModel(get_baseline_safety_cp()) if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING else self.VM
|
||||
self.angle_filter = FirstOrderFilter(0.0, 0.2, DT_CTRL)
|
||||
self.direct_angle_request_allowed = True
|
||||
|
||||
self.accel_last = 0
|
||||
self.apply_torque_last = 0
|
||||
@@ -263,6 +411,10 @@ class CarController(CarControllerBase):
|
||||
self.ecu_disable_failed = False
|
||||
self._ecu_disable_checked = False
|
||||
self._params = Params()
|
||||
if CP.carFingerprint == CAR.KIA_EV9:
|
||||
self._ev9_long_tuning = EV9LongitudinalTuningState()
|
||||
self._left_blindspot_warning = BlindspotWarningState()
|
||||
self._right_blindspot_warning = BlindspotWarningState()
|
||||
self.long_active_ecu = self.CP.openpilotLongitudinalControl
|
||||
self._ioniq_6_lane_change_ui_side = None
|
||||
self._ioniq_6_lane_change_ui_frames = 0
|
||||
@@ -356,36 +508,52 @@ class CarController(CarControllerBase):
|
||||
|
||||
if not self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
|
||||
self.params = CarControllerParams(self.CP, CS.out.vEgoRaw)
|
||||
apply_angle = CS.out.steeringAngleDeg
|
||||
direct_angle_control = self.CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR and self.long_active_ecu
|
||||
measured_steering_angle = CS.angle_steering_angle if direct_angle_control else CS.out.steeringAngleDeg
|
||||
angle_lat_active = CC.latActive
|
||||
if direct_angle_control and CC.latActive:
|
||||
drive_gear = CS.out.gearShifter == structs.CarState.GearShifter.drive
|
||||
angle_lat_active = direct_angle_request_allowed(CS.out.vEgoRaw, measured_steering_angle, self.apply_angle_last,
|
||||
drive_gear, self.BASELINE_VM, self.params) and not CS.angle_steering_fault
|
||||
self.direct_angle_request_allowed = angle_lat_active
|
||||
apply_angle = measured_steering_angle
|
||||
|
||||
if self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
|
||||
v_ego_raw = CS.out.vEgoRaw
|
||||
desired_angle = float(np.clip(actuators.steeringAngleDeg,
|
||||
-self.params.ANGLE_LIMITS.STEER_ANGLE_MAX,
|
||||
self.params.ANGLE_LIMITS.STEER_ANGLE_MAX))
|
||||
|
||||
self.angle_filter.update_alpha(float(np.interp(CS.out.vEgo, [5.0, 10.0, 20.0], [0.2, 0.1, 0.0])))
|
||||
self.angle_filter.update_alpha(get_angle_smoothing_alpha(self.CP, CS.out.vEgo))
|
||||
desired_angle = self.angle_filter.update(desired_angle)
|
||||
|
||||
apply_angle = apply_steer_angle_limits_vm(desired_angle, self.apply_angle_last, v_ego_raw,
|
||||
CS.out.steeringAngleDeg, CC.latActive, self.params, self.VM)
|
||||
measured_steering_angle, angle_lat_active, self.params, self.VM)
|
||||
|
||||
if str(self.CP.carFingerprint) != ANGLE_SAFETY_BASELINE_MODEL:
|
||||
apply_angle = apply_steer_angle_limits_vm(apply_angle or desired_angle, self.apply_angle_last, v_ego_raw,
|
||||
CS.out.steeringAngleDeg, CC.latActive, self.params, self.BASELINE_VM)
|
||||
measured_steering_angle, angle_lat_active, self.params, self.BASELINE_VM)
|
||||
|
||||
apply_torque = compute_torque_reduction_gain(CS.out.steeringTorque, v_ego_raw, CC.latActive, self.apply_torque_last)
|
||||
apply_steer_req = CC.latActive and apply_torque != 0.0
|
||||
if direct_angle_control and angle_lat_active:
|
||||
# Match Panda's 1 m/s speed tolerance so a shrinking absolute limit stays inside its jerk envelope.
|
||||
safety_v_ego = max(v_ego_raw - 1.0, 1.0)
|
||||
max_angle_delta = min(get_max_angle_delta_vm(safety_v_ego, self.BASELINE_VM, self.params),
|
||||
self.params.ANGLE_LIMITS.MAX_ANGLE_RATE)
|
||||
apply_angle = float(np.clip(apply_angle,
|
||||
self.apply_angle_last - max_angle_delta,
|
||||
self.apply_angle_last + max_angle_delta))
|
||||
|
||||
apply_torque = compute_torque_reduction_gain(CS.out.steeringTorque, v_ego_raw, angle_lat_active, self.apply_torque_last)
|
||||
apply_steer_req = angle_lat_active and apply_torque != 0.0
|
||||
torque_fault = False
|
||||
|
||||
if apply_angle is None:
|
||||
apply_torque = 0
|
||||
apply_angle = CS.out.steeringAngleDeg
|
||||
apply_angle = measured_steering_angle
|
||||
apply_steer_req = False
|
||||
|
||||
self.apply_angle_last = apply_angle
|
||||
if not CC.latActive:
|
||||
self.apply_angle_last = float(np.clip(CS.out.steeringAngleDeg,
|
||||
if not angle_lat_active:
|
||||
self.apply_angle_last = float(np.clip(measured_steering_angle,
|
||||
-self.params.ANGLE_LIMITS.STEER_ANGLE_MAX,
|
||||
self.params.ANGLE_LIMITS.STEER_ANGLE_MAX))
|
||||
self.angle_filter.x = self.apply_angle_last
|
||||
@@ -426,22 +594,37 @@ class CarController(CarControllerBase):
|
||||
# longitudinal messages - stock ECU is still active and these would conflict
|
||||
self.long_active_ecu = self.CP.openpilotLongitudinalControl and not self.ecu_disable_failed
|
||||
|
||||
use_ioniq_6_dynamic_long_tuning = self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and self.long_active_ecu and \
|
||||
actuators.longControlState in (LongCtrlState.starting, LongCtrlState.pid, LongCtrlState.stopping)
|
||||
if use_ioniq_6_dynamic_long_tuning and self.frame % 5 == 0:
|
||||
use_egmp_dynamic_long_tuning = egmp_dynamic_longitudinal_tuning(self.CP) and self.long_active_ecu and \
|
||||
actuators.longControlState in (LongCtrlState.starting, LongCtrlState.pid, LongCtrlState.stopping)
|
||||
is_ev6_gt_line = kia_ev6_gt_line_longitudinal_tuning(self.CP.carFingerprint, getattr(self.CP, "carVin", ""))
|
||||
is_ev9 = self.CP.carFingerprint == CAR.KIA_EV9
|
||||
if is_ev9 and (self._ev9_long_tuning.stop_request or not CC.enabled or CC.cruiseControl.override):
|
||||
self._ioniq_6_long_tuning = reset_egmp_longitudinal_tuning(self._ioniq_6_long_tuning)
|
||||
if should_reset_ev6_gt_line_longitudinal_tuning(self.CP, actuators.longControlState):
|
||||
self._ioniq_6_long_tuning = reset_ev6_gt_line_longitudinal_tuning(self._ioniq_6_long_tuning, self.CP,
|
||||
actuators.longControlState)
|
||||
elif use_egmp_dynamic_long_tuning and self.frame % 5 == 0:
|
||||
self._ioniq_6_long_tuning = update_ioniq_6_longitudinal_tuning(self._ioniq_6_long_tuning, accel_cmd,
|
||||
CS.out.vEgo, CS.out.aEgo,
|
||||
actuators.longControlState, self.long_active_ecu)
|
||||
use_ioniq_6_smoothed_accel = use_ioniq_6_dynamic_long_tuning and (
|
||||
actuators.longControlState, self.long_active_ecu,
|
||||
ev6_gt_line=is_ev6_gt_line,
|
||||
low_speed_stop_brake_cap=is_ev9)
|
||||
use_egmp_smoothed_accel = use_egmp_dynamic_long_tuning and (
|
||||
accel_cmd >= self._ioniq_6_long_tuning.actual_accel or
|
||||
self._ioniq_6_long_tuning.launch_active or
|
||||
self._ioniq_6_long_tuning.stopping
|
||||
)
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and self.long_active_ecu:
|
||||
if use_ioniq_6_smoothed_accel:
|
||||
if should_use_ev6_gt_line_stop_direct_tracking(is_ev6_gt_line, self._ioniq_6_long_tuning.stopping,
|
||||
CS.out.vEgo, accel_cmd, self._ioniq_6_long_tuning.actual_accel):
|
||||
use_egmp_smoothed_accel = False
|
||||
if is_ev9 and should_track_stop_accel_directly(self._ioniq_6_long_tuning.stopping, CS.out.vEgo,
|
||||
accel_cmd, self._ioniq_6_long_tuning.actual_accel):
|
||||
use_egmp_smoothed_accel = False
|
||||
if use_egmp_dynamic_long_tuning:
|
||||
if use_egmp_smoothed_accel:
|
||||
accel = self._ioniq_6_long_tuning.actual_accel
|
||||
stopping = self._ioniq_6_long_tuning.stopping
|
||||
elif use_ioniq_6_dynamic_long_tuning:
|
||||
else:
|
||||
accel = float(np.clip(accel_cmd,
|
||||
self.accel_last - IONIQ_6_CANFD_SCC_DECEL_STEP,
|
||||
self.accel_last + IONIQ_6_CANFD_SCC_ACCEL_STEP))
|
||||
@@ -566,25 +749,64 @@ class CarController(CarControllerBase):
|
||||
lka_steering = self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING
|
||||
lka_steering_long = lka_steering and self.long_active_ecu
|
||||
ccnc_non_hda2 = self.CP.flags & HyundaiFlags.CCNC and not lka_steering
|
||||
use_ioniq_6_dynamic_long_tuning = self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and self.long_active_ecu and \
|
||||
CC.actuators.longControlState == LongCtrlState.pid
|
||||
use_ioniq_6_smoothed_accel = use_ioniq_6_dynamic_long_tuning and CC.actuators.accel >= self._ioniq_6_long_tuning.actual_accel
|
||||
use_egmp_dynamic_long_tuning = egmp_dynamic_longitudinal_tuning(self.CP) and self.long_active_ecu and \
|
||||
CC.actuators.longControlState in (LongCtrlState.starting, LongCtrlState.pid, LongCtrlState.stopping)
|
||||
use_egmp_smoothed_accel = use_egmp_dynamic_long_tuning and (
|
||||
CC.actuators.accel >= self._ioniq_6_long_tuning.actual_accel or
|
||||
self._ioniq_6_long_tuning.launch_active or
|
||||
self._ioniq_6_long_tuning.stopping
|
||||
)
|
||||
|
||||
# steering control
|
||||
preserve_stock_lkas = self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and not self.long_active_ecu
|
||||
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled,
|
||||
apply_steer_req, apply_torque, apply_angle,
|
||||
CS.stock_lfa_msg,
|
||||
CS.stock_lkas_msg if preserve_stock_lkas else None,
|
||||
lka_icon=lka_icon))
|
||||
preserve_stock_lkas = bool(self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING) and not self.long_active_ecu
|
||||
angle_lkas_alt = bool(self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING and
|
||||
self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT)
|
||||
ccnc_angle_long = self.CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR and \
|
||||
self.CP.flags & HyundaiFlags.CCNC and angle_lkas_alt and self.long_active_ecu
|
||||
steering_msg_active = apply_steer_req
|
||||
if angle_lkas_alt:
|
||||
# Angle LKAS_ALT cars fault if the angle-steering status drops inactive during torque limiting.
|
||||
# Hold the angle status active while lateral is active; VM/safety limits handle actuation.
|
||||
steering_msg_active = CC.latActive
|
||||
|
||||
gear = getattr(getattr(CS, "out", None), "gearShifter", None)
|
||||
drive_gear = gear == structs.CarState.GearShifter.drive
|
||||
if angle_lkas_alt:
|
||||
steering_msg_active = bool(steering_msg_active and drive_gear)
|
||||
angle_lkas_alt_standstill_handoff = bool(getattr(CS.out, "standstill", False) and not CC.latActive)
|
||||
forward_stock_lkas = angle_lkas_alt and (
|
||||
angle_lkas_alt_standstill_handoff or not (drive_gear and (CC.latActive or CC.enabled))
|
||||
)
|
||||
if not forward_stock_lkas and not ccnc_angle_long:
|
||||
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled,
|
||||
steering_msg_active, apply_torque, apply_angle,
|
||||
CS.stock_lfa_msg,
|
||||
CS.stock_lkas_msg if preserve_stock_lkas else None,
|
||||
lka_icon=lka_icon))
|
||||
direct_steering_active = ccnc_angle_long and drive_gear and CC.latActive and self.direct_angle_request_allowed and not CS.angle_steering_fault
|
||||
inactive_steering_angle = float(np.clip(CS.angle_steering_angle,
|
||||
-self.params.ANGLE_LIMITS.STEER_ANGLE_MAX,
|
||||
self.params.ANGLE_LIMITS.STEER_ANGLE_MAX)) if ccnc_angle_long else 0.0
|
||||
if ccnc_angle_long and drive_gear:
|
||||
can_sends.append(hyundaicanfd.create_angle_adas_cmd(
|
||||
self.packer, self.CAN,
|
||||
apply_angle if direct_steering_active else inactive_steering_angle,
|
||||
direct_steering_active, apply_torque if direct_steering_active else 0.0,
|
||||
))
|
||||
if ccnc_angle_long and not drive_gear:
|
||||
can_sends.extend(hyundaicanfd.create_inactive_angle_steering_messages(self.packer, self.CAN,
|
||||
inactive_steering_angle))
|
||||
|
||||
# prevent LFA from activating on LKA steering cars by sending "no lane lines detected" to ADAS ECU
|
||||
if self.frame % 5 == 0 and lka_steering:
|
||||
suppress_lfa = bool(lka_steering)
|
||||
if angle_lkas_alt:
|
||||
suppress_lfa = bool(lka_steering and drive_gear and (CC.latActive or (ccnc_angle_long and CC.enabled)))
|
||||
if self.frame % 5 == 0 and suppress_lfa:
|
||||
can_sends.append(hyundaicanfd.create_suppress_lfa(self.packer, self.CAN, CS.lfa_block_msg,
|
||||
self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT))
|
||||
|
||||
# LFA and HDA icons
|
||||
if self.frame % 5 == 0 and (not lka_steering or lka_steering_long):
|
||||
if self.frame % 5 == 0 and (not lka_steering or lka_steering_long) and not ccnc_angle_long:
|
||||
if ccnc_non_hda2:
|
||||
can_sends.extend(hyundaicanfd.create_ccnc(self.packer, self.CAN, self.long_active_ecu, CC.enabled, CC.hudControl,
|
||||
CC.leftBlinker, CC.rightBlinker, CS.msg_161, CS.msg_162, CS.msg_1b5,
|
||||
@@ -620,12 +842,41 @@ class CarController(CarControllerBase):
|
||||
|
||||
if self.long_active_ecu:
|
||||
if lka_steering:
|
||||
can_sends.extend(hyundaicanfd.create_adrv_messages(self.packer, self.CAN, self.frame))
|
||||
# Ioniq 5/6: front radar treats ADAS_DRV's 0x100 broadcast as its host heartbeat
|
||||
# and stops publishing object tracks when it disappears. Spoof it periodically on
|
||||
# PT bus so the radar keeps tracking.
|
||||
if self.CP.carFingerprint in CANFD_RADAR_LIVE_LONGITUDINAL_CAR and self.frame % 4 == 0:
|
||||
can_sends.append(hyundaicanfd.create_accelerator_brake_alt_spoof(0, self.frame // 4, CS.out.brakePressed, CS.out.gasPressed))
|
||||
if ccnc_angle_long:
|
||||
left_escalated = CS.left_blindspot_from_radar and CC.leftBlinker and not CC.rightBlinker
|
||||
right_escalated = CS.right_blindspot_from_radar and CC.rightBlinker and not CC.leftBlinker
|
||||
left_warning = BlindspotWarningOutput()
|
||||
right_warning = BlindspotWarningOutput()
|
||||
if self.frame % 5 == 0:
|
||||
left_warning = update_blindspot_warning(
|
||||
self._left_blindspot_warning, left_escalated, CC.leftBlinker,
|
||||
)
|
||||
right_warning = update_blindspot_warning(
|
||||
self._right_blindspot_warning, right_escalated, CC.rightBlinker,
|
||||
)
|
||||
steering_available = CC.latActive or CC.enabled
|
||||
steering_active = direct_steering_active and apply_steer_req and not CS.out.steeringPressed
|
||||
adrv_messages = hyundaicanfd.create_ccnc_adrv_messages(
|
||||
self.packer, self.CP, self.CAN, self.frame, CC.enabled, CS.out.cruiseState.available, CC.hudControl,
|
||||
CS.out, CS.is_metric, steering_available, steering_active,
|
||||
CS.left_blindspot_from_radar, CS.right_blindspot_from_radar,
|
||||
drive_gear=drive_gear,
|
||||
hba_icon=CS.hba_icon,
|
||||
left_escalated=left_escalated, right_escalated=right_escalated,
|
||||
left_warning_lamp=left_warning.mirror_lamp_active,
|
||||
right_warning_lamp=right_warning.mirror_lamp_active,
|
||||
left_sound_active=left_warning.sound_active, right_sound_active=right_warning.sound_active,
|
||||
)
|
||||
else:
|
||||
adrv_messages = hyundaicanfd.create_adrv_messages(self.packer, self.CAN, self.frame)
|
||||
can_sends.extend(adrv_messages)
|
||||
# The front radar treats ADAS_DRV's 0x100 broadcast as its host heartbeat
|
||||
# and stops publishing object tracks when it disappears.
|
||||
radar_heartbeat_step = 1 if ccnc_angle_long else 4
|
||||
if self.CP.carFingerprint in CANFD_RADAR_LIVE_LONGITUDINAL_CAR and self.frame % radar_heartbeat_step == 0:
|
||||
can_sends.append(hyundaicanfd.create_accelerator_brake_alt_spoof(0, self.frame // radar_heartbeat_step,
|
||||
CS.out.brakePressed, CS.out.gasPressed,
|
||||
self.CP.carFingerprint))
|
||||
elif not ccnc_non_hda2:
|
||||
can_sends.extend(hyundaicanfd.create_fca_warning_light(self.packer, self.CAN, self.frame))
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and self.frame % 5 == 0:
|
||||
@@ -656,13 +907,31 @@ class CarController(CarControllerBase):
|
||||
"lead_rel_speed": lead_rel_speed,
|
||||
"lead_visible": lead_visible,
|
||||
}
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
|
||||
if use_ioniq_6_smoothed_accel:
|
||||
if use_egmp_dynamic_long_tuning:
|
||||
if use_egmp_smoothed_accel:
|
||||
acc_kwargs["jerk_lower"] = self._ioniq_6_long_tuning.jerk_lower
|
||||
acc_kwargs["jerk_upper"] = self._ioniq_6_long_tuning.jerk_upper
|
||||
can_sends.append(hyundaicanfd.create_acc_control(self.packer, self.CAN, CC.enabled, self.accel_last, accel, stopping, CC.cruiseControl.override,
|
||||
set_speed_in_units, hud_control, cruise_info=CS.cruise_info if ccnc_non_hda2 else None,
|
||||
**acc_kwargs))
|
||||
if ccnc_angle_long:
|
||||
self._ev9_long_tuning = update_ev9_longitudinal_tuning(
|
||||
self._ev9_long_tuning, CC.enabled and not CC.cruiseControl.override,
|
||||
CC.actuators.longControlState == LongCtrlState.stopping, float(CS.out.vEgo),
|
||||
)
|
||||
if self._ev9_long_tuning.stop_request or not CC.enabled or CC.cruiseControl.override:
|
||||
self._ioniq_6_long_tuning = reset_egmp_longitudinal_tuning(self._ioniq_6_long_tuning)
|
||||
accel = 0.0
|
||||
can_sends.append(hyundaicanfd.create_ccnc_acc_control(
|
||||
self.packer, self.CAN, CC.enabled, accel,
|
||||
self._ev9_long_tuning.stop_request, self._ev9_long_tuning.cruise_standstill, CC.cruiseControl.override,
|
||||
set_speed_in_units, int(CS.out.cruiseState.available), lead_distance, lead_rel_speed, lead_visible,
|
||||
float(CS.out.vEgo),
|
||||
jerk_lower=acc_kwargs["jerk_lower"],
|
||||
jerk_upper=acc_kwargs["jerk_upper"],
|
||||
))
|
||||
else:
|
||||
can_sends.append(hyundaicanfd.create_acc_control(
|
||||
self.packer, self.CAN, CC.enabled, self.accel_last, accel, stopping, CC.cruiseControl.override,
|
||||
set_speed_in_units, hud_control, cruise_info=CS.cruise_info if ccnc_non_hda2 else None, **acc_kwargs,
|
||||
))
|
||||
self.accel_last = accel
|
||||
else:
|
||||
# button presses
|
||||
|
||||
@@ -8,6 +8,7 @@ from opendbc.car import Bus, create_button_events, structs
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, HyundaiStarPilotFlags, HyundaiStarPilotSafetyFlags, CAR, DBC, Buttons, CarControllerParams, \
|
||||
CANFD_ANGLE_LONGITUDINAL_CAR, CANFD_CORNER_RADAR_BSM_CAR, \
|
||||
hyundai_cancel_button_enables_cruise, ALT_BUS_LDA_BUTTON_CARS, ALT_BUS_LDA_BUTTON_SWL_STAT_CARS
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
|
||||
@@ -75,7 +76,14 @@ class CarState(CarStateBase):
|
||||
self.cruise_buttons: deque = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
|
||||
self.main_buttons: deque = deque([Buttons.NONE] * PREV_BUTTON_SAMPLES, maxlen=PREV_BUTTON_SAMPLES)
|
||||
self.lda_button = 0
|
||||
self.sonata_hybrid_lkas_source = None
|
||||
self.sonata_hybrid_lkas_sources = {
|
||||
"bcm": 0,
|
||||
"clu13": 0,
|
||||
"swl_stat": 0,
|
||||
}
|
||||
self.lda_button_raw = 0
|
||||
self.lda_button_raw_initialized = False
|
||||
self.lda_button_last_raw_rise_ts_nanos = 0
|
||||
self.left_paddle = 0
|
||||
self.mode_button = 0
|
||||
@@ -129,6 +137,11 @@ class CarState(CarStateBase):
|
||||
self.blindspots_front_corner_1_ts = 0
|
||||
self.left_blindspot_from_radar = False
|
||||
self.right_blindspot_from_radar = False
|
||||
if CP.carFingerprint == CAR.KIA_EV9:
|
||||
self.hba_icon = 0
|
||||
self.main_cruise_on = False
|
||||
self.angle_steering_angle = 0.0
|
||||
self.angle_steering_fault = False
|
||||
|
||||
# On some cars, CLU15->CF_Clu_VehicleSpeed can oscillate faster than the dash updates. Sample at 5 Hz
|
||||
self.cluster_speed = 0
|
||||
@@ -142,6 +155,12 @@ class CarState(CarStateBase):
|
||||
# Main button also can trigger an engagement on these cars
|
||||
return any(btn in ENABLE_BUTTONS for btn in self.cruise_buttons) or any(self.main_buttons)
|
||||
|
||||
def update_main_cruise(self, ret: structs.CarState) -> bool:
|
||||
if any(be.type == ButtonType.mainCruise and be.pressed for be in ret.buttonEvents):
|
||||
self.main_cruise_on = not self.main_cruise_on
|
||||
|
||||
return bool(ret.cruiseState.available and self.main_cruise_on)
|
||||
|
||||
def create_cruise_button_events(self, cur_button: int, prev_button: int) -> list[structs.CarState.ButtonEvent]:
|
||||
if cur_button != prev_button and prev_button != Buttons.CANCEL and cur_button == Buttons.CANCEL:
|
||||
self.cancel_button_enable_in_progress = (
|
||||
@@ -174,15 +193,20 @@ class CarState(CarStateBase):
|
||||
|
||||
return False
|
||||
|
||||
def create_alt_bus_lda_button_events(self, cp_source: CANParser) -> list[structs.CarState.ButtonEvent]:
|
||||
def get_alt_bus_lda_button_raw_state(self, cp_source: CANParser) -> tuple[int, int]:
|
||||
if self.CP.carFingerprint in ALT_BUS_LDA_BUTTON_SWL_STAT_CARS:
|
||||
raw_lda_button = int(cp_source.vl["CLU13"]["CF_Clu_SWL_Stat"] == 4)
|
||||
raw_lda_button_ts_nanos = cp_source.ts_nanos["CLU13"]["CF_Clu_SWL_Stat"]
|
||||
else:
|
||||
raw_lda_button = int(cp_source.vl["CLU13"]["CF_Clu_LdwsLkasSW"])
|
||||
raw_lda_button_ts_nanos = cp_source.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"]
|
||||
return int(cp_source.vl["CLU13"]["CF_Clu_SWL_Stat"] == 4), cp_source.ts_nanos["CLU13"]["CF_Clu_SWL_Stat"]
|
||||
return int(cp_source.vl["CLU13"]["CF_Clu_LdwsLkasSW"]), cp_source.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"]
|
||||
|
||||
def create_alt_bus_lda_button_events(self, cp_source: CANParser) -> list[structs.CarState.ButtonEvent]:
|
||||
raw_lda_button, raw_lda_button_ts_nanos = self.get_alt_bus_lda_button_raw_state(cp_source)
|
||||
button_events: list[structs.CarState.ButtonEvent] = []
|
||||
|
||||
if not self.lda_button_raw_initialized:
|
||||
self.lda_button_raw_initialized = True
|
||||
self.lda_button_raw = raw_lda_button
|
||||
return button_events
|
||||
|
||||
# Some alt-bus LKAS button layouts pulse several times per physical press burst.
|
||||
# Collapse each burst into a single synthetic press/release pair.
|
||||
if raw_lda_button and not self.lda_button_raw:
|
||||
@@ -198,8 +222,10 @@ class CarState(CarStateBase):
|
||||
return button_events
|
||||
|
||||
def create_lkas_button_events(self, cp: CANParser, prev_lda_button: int) -> list[structs.CarState.ButtonEvent]:
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_SONATA_HYBRID:
|
||||
self.lda_button = self.get_sonata_hybrid_lkas_button_state(cp)
|
||||
# Some classic HKG platforms publish the LKAS button on the cluster bus instead of BCM_PO_11.
|
||||
if cp.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"] > 0:
|
||||
elif cp.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"] > 0:
|
||||
self.lda_button = int(cp.vl["CLU13"]["CF_Clu_LdwsLkasSW"])
|
||||
elif cp.ts_nanos["BCM_PO_11"]["LDA_BTN"] > 0:
|
||||
self.lda_button = int(cp.vl["BCM_PO_11"]["LDA_BTN"])
|
||||
@@ -208,6 +234,32 @@ class CarState(CarStateBase):
|
||||
|
||||
return create_button_events(self.lda_button, prev_lda_button, {1: ButtonType.lkas})
|
||||
|
||||
def get_sonata_hybrid_lkas_button_state(self, cp: CANParser) -> int:
|
||||
source_states = {
|
||||
"bcm": int(cp.vl["BCM_PO_11"]["LDA_BTN"]) if cp.ts_nanos["BCM_PO_11"]["LDA_BTN"] > 0 else 0,
|
||||
"clu13": int(cp.vl["CLU13"]["CF_Clu_LdwsLkasSW"]) if cp.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"] > 0 else 0,
|
||||
"swl_stat": int(cp.vl["CLU13"]["CF_Clu_SWL_Stat"] == 4) if cp.ts_nanos["CLU13"]["CF_Clu_SWL_Stat"] > 0 else 0,
|
||||
}
|
||||
|
||||
changed_sources = [source for source, state in source_states.items() if state != self.sonata_hybrid_lkas_sources[source]]
|
||||
active_sources = [source for source, state in source_states.items() if state]
|
||||
|
||||
selected_source = None
|
||||
if self.sonata_hybrid_lkas_source in changed_sources:
|
||||
selected_source = self.sonata_hybrid_lkas_source
|
||||
elif active_sources:
|
||||
selected_source = active_sources[0]
|
||||
elif changed_sources:
|
||||
selected_source = changed_sources[0]
|
||||
elif self.sonata_hybrid_lkas_source is not None:
|
||||
selected_source = self.sonata_hybrid_lkas_source
|
||||
|
||||
self.sonata_hybrid_lkas_sources.update(source_states)
|
||||
if selected_source is not None:
|
||||
self.sonata_hybrid_lkas_source = selected_source
|
||||
return source_states[selected_source]
|
||||
return 0
|
||||
|
||||
def update(self, can_parsers, starpilot_toggles) -> structs.CarState:
|
||||
cp = can_parsers[Bus.pt]
|
||||
cp_cam = can_parsers[Bus.cam]
|
||||
@@ -348,7 +400,7 @@ class CarState(CarStateBase):
|
||||
lkas_button_events = []
|
||||
self.cruise_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwState"])
|
||||
self.main_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwMain"])
|
||||
if self.CP.carFingerprint in ALT_BUS_LDA_BUTTON_CARS and cp_alt is not None and cp_alt.ts_nanos["CLU13"]["CF_Clu_LdwsLkasSW"] > 0:
|
||||
if self.CP.carFingerprint in ALT_BUS_LDA_BUTTON_CARS and cp_alt is not None and self.get_alt_bus_lda_button_raw_state(cp_alt)[1] > 0:
|
||||
lkas_button_events = self.create_alt_bus_lda_button_events(cp_alt)
|
||||
else:
|
||||
lkas_button_events = self.create_lkas_button_events(cp, prev_lda_button)
|
||||
@@ -408,6 +460,10 @@ class CarState(CarStateBase):
|
||||
ret.steeringTorqueEps = cp.vl["MDPS"]["STEERING_OUT_TORQUE"]
|
||||
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > self.params.STEER_THRESHOLD, 5)
|
||||
ret.steerFaultTemporary = cp.vl["MDPS"]["LKA_FAULT"] != 0
|
||||
if self.CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR:
|
||||
self.angle_steering_angle = cp.vl["MDPS"]["STEERING_ANGLE_2"]
|
||||
self.angle_steering_fault = cp.vl["MDPS"]["LKA_ANGLE_FAULT"] != 0
|
||||
ret.steerFaultTemporary = ret.steerFaultTemporary or self.angle_steering_fault
|
||||
|
||||
ccnc_non_hda2 = self.CP.flags & HyundaiFlags.CCNC and not self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING
|
||||
if ccnc_non_hda2:
|
||||
@@ -423,11 +479,12 @@ class CarState(CarStateBase):
|
||||
cp.vl["BLINKERS"][right_blinker_sig])
|
||||
self.left_blindspot_from_radar = False
|
||||
self.right_blindspot_from_radar = False
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
|
||||
corner_radar_bsm = self.CP.carFingerprint in CANFD_CORNER_RADAR_BSM_CAR
|
||||
if corner_radar_bsm:
|
||||
self.left_blindspot_from_radar, self.right_blindspot_from_radar = decode_ioniq_6_blindspot_radar_state(
|
||||
cp.vl["BLINDSPOTS_FRONT_CORNER_2"]["SIDE_DETECT_STATE"])
|
||||
if self.CP.enableBsm:
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
|
||||
if corner_radar_bsm:
|
||||
ret.leftBlindspot = (bool(cp.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_LtIndSta"]) or
|
||||
self.left_blindspot_from_radar)
|
||||
ret.rightBlindspot = (bool(cp.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_RtIndSta"]) or
|
||||
@@ -497,6 +554,9 @@ class CarState(CarStateBase):
|
||||
self.stock_lfa_msg = copy.copy(cp.vl["LFA"])
|
||||
if cp.ts_nanos["LFAHDA_CLUSTER"]["CHECKSUM"] > 0:
|
||||
self.stock_lfahda_cluster_msg = copy.copy(cp.vl["LFAHDA_CLUSTER"])
|
||||
if self.CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR and cp.ts_nanos["FR_CMR_01_10ms"]["FR_CMR_Crc1Val"] > 0:
|
||||
hba_icon = int(cp.vl["FR_CMR_01_10ms"]["HBA_IndLmpReq"])
|
||||
self.hba_icon = hba_icon if hba_icon in (1, 2) else 0
|
||||
if cp.ts_nanos["BLINKER_STALKS"]["CHECKSUM_MAYBE"] > 0:
|
||||
self.stock_blinker_stalks_ts = cp.ts_nanos["BLINKER_STALKS"]["CHECKSUM_MAYBE"]
|
||||
|
||||
@@ -504,6 +564,8 @@ class CarState(CarStateBase):
|
||||
*create_button_events(self.main_buttons[-1], prev_main_buttons, {1: ButtonType.mainCruise}),
|
||||
*create_button_events(self.lda_button, prev_lda_button, {1: ButtonType.lkas}),
|
||||
*create_button_events(self.left_paddle, prev_left_paddle, {1: ButtonType.altButton2})]
|
||||
if self.CP.openpilotLongitudinalControl and self.CP.carFingerprint == CAR.KIA_EV9:
|
||||
ret.cruiseState.available = self.update_main_cruise(ret)
|
||||
|
||||
ret.blockPcmEnable = not self.recent_button_interaction()
|
||||
|
||||
@@ -552,6 +614,12 @@ class CarState(CarStateBase):
|
||||
("LFAHDA_CLUSTER", 0), # optional: carries cluster icon state on some variants
|
||||
("BLINKER_STALKS", 0), # optional: some trims publish live stalk/light state on ECAN during turn camera events
|
||||
]
|
||||
if CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR and CP.enableBsm:
|
||||
# Keep the suppressed ADAS BSM output optional.
|
||||
msgs.append(("BLINDSPOTS_REAR_CORNERS", 0))
|
||||
if CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR:
|
||||
msgs.append(("BLINDSPOTS_FRONT_CORNER_2", 0))
|
||||
msgs.append(("FR_CMR_01_10ms", 0))
|
||||
if CP.flags & HyundaiFlags.EV:
|
||||
msgs.append(("DRIVE_MODE_EV", 0)) # optional: not all CAN-FD EV variants publish drive mode
|
||||
msgs.append(("MANUAL_SPEED_LIMIT_ASSIST", 0)) # optional: used for non-adaptive cruise state and Ioniq 6 i-Pedal latch detection
|
||||
|
||||
@@ -38,6 +38,14 @@ FW_VERSIONS = {
|
||||
b'\xf1\x00IGhe SCC FHCUP 1.00 1.02 99110-M9000 ',
|
||||
],
|
||||
},
|
||||
CAR.HYUNDAI_AZERA_HEV_7TH_GEN: {
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00GN7HMFC AT KOR LHD 1.00 1.01 99211-N1110 240423',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x7d0, None): [
|
||||
b'\xf1\x00GN7_ RDR ----- 1.00 1.00 99110-N1100 ',
|
||||
],
|
||||
},
|
||||
CAR.HYUNDAI_GENESIS: {
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00DH LKAS 1.1 -150210',
|
||||
@@ -607,6 +615,17 @@ FW_VERSIONS = {
|
||||
b'\xf1\x00CD ESC \x0b 101 \x10\x03 58910-J7AC0',
|
||||
],
|
||||
},
|
||||
CAR.KIA_XCEED_PHEV: {
|
||||
(Ecu.fwdRadar, 0x7d0, None): [
|
||||
b'\xf1\x00CDph SCC F-CUP 1.00 1.01 99110-CR100 ',
|
||||
],
|
||||
(Ecu.eps, 0x7d4, None): [
|
||||
b'\xf1\x00CDe MDPS C 1.00 1.01 56310-XX000 4CDHC101',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00CD2 LKAS AT EUR LHD 1.00 1.01 99211-CR010 621',
|
||||
],
|
||||
},
|
||||
CAR.KIA_FORTE: {
|
||||
(Ecu.eps, 0x7d4, None): [
|
||||
b'\xf1\x00BD MDPS C 1.00 1.02 56310-XX000 4BD2C102',
|
||||
@@ -980,6 +999,21 @@ FW_VERSIONS = {
|
||||
b'\xf1\x00CN ESC \t 105 \x10\x03 58910-AA800',
|
||||
],
|
||||
},
|
||||
CAR.HYUNDAI_ELANTRA_2024: {
|
||||
(Ecu.fwdRadar, 0x7d0, None): [
|
||||
b'\xf1\x00CN7_ RDR ----- 1.00 1.01 99110-AA500 ',
|
||||
],
|
||||
(Ecu.eps, 0x7d4, None): [
|
||||
b'\xf1\x00CN7 MDPS C 1.00 1.02 56300AA670\x00 4CSDC102',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.02 99210-AA500 230420',
|
||||
b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.03 99210-AA500 230918',
|
||||
],
|
||||
(Ecu.abs, 0x7d1, None): [
|
||||
b'\xf1\x00CN ESC \t 104#\x07\x03 58910-AA850',
|
||||
],
|
||||
},
|
||||
CAR.HYUNDAI_ELANTRA_HEV_2021: {
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.03 99210-AA000 200819',
|
||||
@@ -999,6 +1033,22 @@ FW_VERSIONS = {
|
||||
b'\xf1\x00CN7 MDPS C 1.00 1.04 56310BY050\x00 4CNHC104',
|
||||
],
|
||||
},
|
||||
CAR.HYUNDAI_ELANTRA_HEV_2024: {
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00CN7HMFC AT AUS RHD 1.00 1.02 99210-AA500 230420',
|
||||
b'\xf1\x00CN7HMFC AT CAN LHD 1.00 1.05 99210-AA510 240509',
|
||||
b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.03 99210-AA500 230918',
|
||||
b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.05 99210-AA510 240509',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x7d0, None): [
|
||||
b'\xf1\x00CN7_ RDR ----- 1.00 1.01 99110-AA500 ',
|
||||
],
|
||||
(Ecu.eps, 0x7d4, None): [
|
||||
b'\xf1\x00CN7 MDPS C 1.00 1.00 56300BY670\x00 4CSHC100',
|
||||
b'\xf1\x00CN7 MDPS C 1.00 1.00 56300BY680\x00 4CSHC100',
|
||||
b'\xf1\x00CN7 MDPS C 1.00 1.03 56300BY670\x00 4CSHC103',
|
||||
],
|
||||
},
|
||||
CAR.HYUNDAI_KONA_HEV: {
|
||||
(Ecu.abs, 0x7d1, None): [
|
||||
b'\xf1\x00OS IEB \x01 104 \x11 58520-CM000',
|
||||
@@ -1462,6 +1512,26 @@ FW_VERSIONS = {
|
||||
b'\xf1\x00KA4c SCC FHCUP 1.00 1.01 99110-I4000 ',
|
||||
],
|
||||
},
|
||||
CAR.KIA_CARNIVAL_2025: {
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00KA4 MFC AT CAN LHD 1.00 1.00 99210-R0700 250324',
|
||||
b'\xf1\x00KA4 MFC AT USA LHD 1.00 1.05 99210-R0500 240305',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x7d0, None): [
|
||||
b'\xf1\x00KA4_ SCC FHCUP 1.00 1.01 99110-R0510 ',
|
||||
b'\xf1\x00KA4_ RDR ----- 1.00 1.01 99110-R0510 ',
|
||||
],
|
||||
},
|
||||
CAR.KIA_CARNIVAL_HEV_4TH_GEN: {
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00KA4HMFC AT USA LHD 1.00 1.05 99210-R0500 240305',
|
||||
b'\xf1\x00KA4HMFC AT KOR LHD 1.00 1.00 99210-R0600 240924',
|
||||
b'\xf1\x00KA4HMFC AT USA LHD 1.00 1.00 99210-R0700 250324',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x7d0, None): [
|
||||
b'\xf1\x00KAhe RDR ----- 1.00 1.01 99110-ES500 ',
|
||||
],
|
||||
},
|
||||
CAR.KIA_K8_HEV_1ST_GEN: {
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00GL3HMFC AT KOR LHD 1.00 1.03 99211-L8000 210907',
|
||||
|
||||
@@ -39,8 +39,9 @@ def create_lkas11(packer, frame, CP, apply_torque, steer_req,
|
||||
CAR.HYUNDAI_IONIQ_EV_2020, CAR.HYUNDAI_IONIQ_PHEV, CAR.KIA_SELTOS, CAR.HYUNDAI_ELANTRA_2021, CAR.GENESIS_G70_2020,
|
||||
CAR.HYUNDAI_ELANTRA_HEV_2021, CAR.HYUNDAI_SONATA_HYBRID, CAR.HYUNDAI_KONA_EV, CAR.HYUNDAI_KONA_HEV, CAR.HYUNDAI_KONA_EV_2022,
|
||||
CAR.HYUNDAI_SANTA_FE_2022, CAR.KIA_K5_2021, CAR.HYUNDAI_IONIQ_HEV_2022, CAR.HYUNDAI_SANTA_FE_HEV_2022,
|
||||
CAR.HYUNDAI_SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_CEED,
|
||||
CAR.HYUNDAI_AZERA_6TH_GEN, CAR.HYUNDAI_AZERA_HEV_6TH_GEN, CAR.HYUNDAI_CUSTIN_1ST_GEN, CAR.HYUNDAI_KONA_2022):
|
||||
CAR.HYUNDAI_SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_CEED, CAR.KIA_XCEED_PHEV,
|
||||
CAR.HYUNDAI_AZERA_6TH_GEN, CAR.HYUNDAI_AZERA_HEV_6TH_GEN, CAR.HYUNDAI_CUSTIN_1ST_GEN, CAR.HYUNDAI_KONA_2022,
|
||||
CAR.HYUNDAI_ELANTRA_2024, CAR.HYUNDAI_ELANTRA_HEV_2024):
|
||||
values["CF_Lkas_LdwsActivemode"] = int(left_lane) + (int(right_lane) << 1)
|
||||
values["CF_Lkas_LdwsOpt_USM"] = 2
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import numpy as np
|
||||
from opendbc.car import CanBusBase, CanData
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.crc import CRC16_XMODEM
|
||||
from opendbc.car.hyundai.values import HyundaiFlags
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, CAR
|
||||
|
||||
|
||||
def _set_value(msg: bytearray, sig, ival: int) -> None:
|
||||
@@ -92,10 +92,15 @@ def _create_angle_adas_cmd_msg(packer, CAN, apply_angle: float, lat_active: bool
|
||||
return packer.make_can_msg("ADAS_CMD_35_10ms", CAN.ECAN, values)
|
||||
|
||||
|
||||
def create_angle_adas_cmd(packer, CAN, apply_angle: float, lat_active: bool, torque_reduction_gain: float):
|
||||
return _create_angle_adas_cmd_msg(packer, CAN, apply_angle, lat_active, torque_reduction_gain)
|
||||
|
||||
|
||||
def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque, apply_angle,
|
||||
lfa_base_values=None, lkas_base_values=None, lka_icon=None):
|
||||
if lka_icon is None:
|
||||
lka_icon = 2 if enabled else 1
|
||||
angle_lkas_alt = CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING and CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
|
||||
|
||||
control_values = {
|
||||
"LKA_MODE": 2,
|
||||
@@ -128,6 +133,59 @@ def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque,
|
||||
lkas_values["ADAS_StrAnglReqVal"] = apply_angle
|
||||
lkas_values["LKAS_ANGLE_ACTIVE"] = 2 if lat_active else 1
|
||||
lkas_values["ADAS_ACIAnglTqRedcGainVal"] = apply_torque if lat_active else 0.0
|
||||
if angle_lkas_alt:
|
||||
if lat_active:
|
||||
lkas_values = {
|
||||
"LKA_OptUsmSta": 0,
|
||||
"LKA_RcgSta": 3,
|
||||
"LKA_LHLnWrnSta": 0,
|
||||
"LKA_RHLnWrnSta": 0,
|
||||
"LKA_HndsoffSnd": 0,
|
||||
"LKA_StrSnd": 0,
|
||||
"LKA_SysIndReq": 2,
|
||||
"StrTqReqVal": 0,
|
||||
"ActToiSta": 0,
|
||||
"ToiFltSta": 0,
|
||||
"LFA_BUTTON": 0,
|
||||
"LKA_SysWrn": 0,
|
||||
"Damping_Gain": 100,
|
||||
"LKAS_ANGLE_ACTIVE": 2,
|
||||
"LKA_UsmMod": 0,
|
||||
"ADAS_StrAnglReqVal": apply_angle,
|
||||
"ADAS_ACIAnglTqRedcGainVal": apply_torque,
|
||||
}
|
||||
else:
|
||||
lkas_values.update({
|
||||
"LKA_OptUsmSta": 0,
|
||||
"LKA_MODE": 0,
|
||||
"LKA_RcgSta": 0,
|
||||
"LKA_AVAILABLE": 0,
|
||||
"LKA_LHLnWrnSta": 0,
|
||||
"LKA_RHLnWrnSta": 0,
|
||||
"LKA_WARNING": 0,
|
||||
"LKA_HndsoffSnd": 0,
|
||||
"LKA_StrSnd": 2,
|
||||
"LKA_SysIndReq": 1,
|
||||
"LKA_ICON": 1,
|
||||
"FCA_SYSWARN": 0,
|
||||
"StrTqReqVal": 0,
|
||||
"TORQUE_REQUEST": 0,
|
||||
"ActToiSta": 0,
|
||||
"STEER_REQ": 0,
|
||||
"ToiFltSta": 0,
|
||||
"LFA_BUTTON": 0,
|
||||
"LKA_SysWrn": 0,
|
||||
"LKA_ASSIST": 0,
|
||||
"Damping_Gain": 0,
|
||||
"STEER_MODE": 0,
|
||||
"NEW_SIGNAL_2": 0,
|
||||
"LKAS_ANGLE_ACTIVE": 1,
|
||||
"LKA_UsmMod": 0,
|
||||
"HAS_LANE_SAFETY": 0,
|
||||
"ADAS_ACIAnglTqRedcGainVal": 0.0,
|
||||
"DAMP_FACTOR": 0,
|
||||
})
|
||||
lkas_values["ADAS_StrAnglReqVal"] = lkas_base_values.get("ADAS_StrAnglReqVal", apply_angle) if lkas_base_values else apply_angle
|
||||
|
||||
ret = []
|
||||
if CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
|
||||
@@ -150,6 +208,25 @@ def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque,
|
||||
return ret
|
||||
|
||||
|
||||
def create_inactive_angle_steering_messages(packer, CAN, steering_angle: float):
|
||||
lfa_values = {
|
||||
"LKA_MODE": 2,
|
||||
"LKA_ICON": 1,
|
||||
"TORQUE_REQUEST": 0,
|
||||
"LKA_ASSIST": 0,
|
||||
"STEER_REQ": 0,
|
||||
"STEER_MODE": 0,
|
||||
"HAS_LANE_SAFETY": 0,
|
||||
"NEW_SIGNAL_1": 0,
|
||||
"NEW_SIGNAL_2": 0,
|
||||
"DAMP_FACTOR": 100,
|
||||
}
|
||||
return [
|
||||
packer.make_can_msg("LFA", CAN.ECAN, lfa_values),
|
||||
create_angle_adas_cmd(packer, CAN, steering_angle, False, 0.0),
|
||||
]
|
||||
|
||||
|
||||
def create_suppress_lfa(packer, CAN, lfa_block_msg, lka_steering_alt):
|
||||
suppress_msg = "CAM_0x362" if lka_steering_alt else "CAM_0x2a4"
|
||||
msg_bytes = 32 if lka_steering_alt else 24
|
||||
@@ -343,6 +420,35 @@ def create_blindspot_status_messages(packer, CAN, rear_values, front_corner_valu
|
||||
]
|
||||
|
||||
|
||||
def create_ccnc_blindspot_status_messages(packer, CP, CAN, counter, left_blindspot=False, right_blindspot=False,
|
||||
left_escalated=False, right_escalated=False, drive_gear=False,
|
||||
left_warning_lamp=False, right_warning_lamp=False,
|
||||
left_sound_active=False, right_sound_active=False):
|
||||
left_state = 2 if left_blindspot and left_escalated else (1 if left_blindspot else 0)
|
||||
right_state = 2 if right_blindspot and right_escalated else (1 if right_blindspot else 0)
|
||||
left_osm_state = 2 if left_warning_lamp else 1 if left_state == 1 else 0
|
||||
right_osm_state = 2 if right_warning_lamp else 1 if right_state == 1 else 0
|
||||
desired_fields = {
|
||||
"BCW_IndSta": 1,
|
||||
"BCA_OnOffEquip2Sta": 2,
|
||||
"BCA_Sta": int(drive_gear),
|
||||
"BCW_LtIndSta": left_state,
|
||||
"BCW_RtIndSta": right_state,
|
||||
"BCW_LtSndWrngSta": int(left_sound_active),
|
||||
"BCW_RtSndWrngSta": int(right_sound_active),
|
||||
"OSMrrLamp_LtIndSta": left_osm_state,
|
||||
"OSMrrLamp_RtIndSta": right_osm_state,
|
||||
}
|
||||
|
||||
return [
|
||||
_create_ccnc_adrv_message_with_signals(
|
||||
packer, CP, CAN, 0x1BA, counter, "BLINDSPOTS_REAR_CORNERS", desired_fields,
|
||||
),
|
||||
# No retained radar input reproduces the stock RCTA target decision across routes.
|
||||
_create_ccnc_adrv_message(CP.carFingerprint, 0x1E5, CAN.ECAN, counter),
|
||||
]
|
||||
|
||||
|
||||
IONIQ_6_CLUSTER_BLINDSPOT_31A = {
|
||||
"right": (
|
||||
bytes.fromhex("fa7c10f0f0ffff03898aff0b0a8678ff000000007e0055550000000000000000"),
|
||||
@@ -687,6 +793,30 @@ def create_adrv_messages(packer, CAN, frame):
|
||||
return ret
|
||||
|
||||
|
||||
def create_ccnc_adrv_messages(packer, CP, CAN, frame, enabled, main_cruise_enabled, hud, out, is_metric,
|
||||
steering_available, steering_active, left_blindspot, right_blindspot,
|
||||
drive_gear=False,
|
||||
hba_icon=0,
|
||||
left_escalated=False, right_escalated=False,
|
||||
left_warning_lamp=False, right_warning_lamp=False,
|
||||
left_sound_active=False, right_sound_active=False):
|
||||
ret = [
|
||||
_create_ccnc_adrv_message(CP.carFingerprint, address, CAN.ECAN, frame // period)
|
||||
for address, period in _CCNC_ADRV_PERIODS[CP.carFingerprint].items() if frame % period == 0
|
||||
]
|
||||
if frame % 5 == 0:
|
||||
ret.extend(create_ccnc_angle_long_status_messages(
|
||||
packer, CP, CAN, frame // 5, enabled, main_cruise_enabled, hud, out, is_metric,
|
||||
steering_available, steering_active, hba_icon,
|
||||
))
|
||||
ret.extend(create_ccnc_blindspot_status_messages(
|
||||
packer, CP, CAN, frame // 5, left_blindspot, right_blindspot, left_escalated, right_escalated,
|
||||
drive_gear,
|
||||
left_warning_lamp, right_warning_lamp, left_sound_active, right_sound_active,
|
||||
))
|
||||
return ret
|
||||
|
||||
|
||||
def hkg_can_fd_checksum(address: int, sig, d: bytearray) -> int:
|
||||
crc = 0
|
||||
for i in range(2, len(d)):
|
||||
@@ -709,11 +839,44 @@ def hkg_can_fd_checksum(address: int, sig, d: bytearray) -> int:
|
||||
# radar stops publishing real object tracks. Spoof this message ourselves with valid CRC
|
||||
# and current pedal state so the radar keeps tracking.
|
||||
# Length is 24 bytes on Ioniq 6 (DBC declares 32 for ICE Hyundais, but EV firmware uses 24).
|
||||
# Byte template captured from a real ADAS broadcast; bytes 6-23 appear static / config.
|
||||
# Byte templates captured from real ADAS broadcasts; only checksum, counter,
|
||||
# brake, and accelerator bits are updated for the radar heartbeat.
|
||||
_ACCEL_BRAKE_ALT_TEMPLATE = bytes.fromhex("000000020000fcff000000000020000055ff000068000000")
|
||||
_KIA_EV9_ACCEL_BRAKE_ALT_TEMPLATE = bytes.fromhex("00000000ff006f00e80400001201030055ffff0000000000")
|
||||
# Neutral bodies verified across stock and successful suppression routes. Only
|
||||
# rolling integrity fields and the decoded state above are changed at runtime.
|
||||
_CCNC_ADRV_TEMPLATES = {
|
||||
CAR.KIA_EV9: {
|
||||
0x160: bytes.fromhex("0000000100000000fffc0100a8001000"),
|
||||
0x1DA: bytes.fromhex("0000002200110000000000000000000000000000000000000000000000000000"),
|
||||
0x1EA: bytes.fromhex("000000080000000000000000000000ff000000000000000000000000000f0f00"),
|
||||
0x200: bytes.fromhex("00000014801a0000"),
|
||||
0x345: bytes.fromhex("0000001500560000"),
|
||||
0x161: bytes.fromhex("0000000000000000c0fff0c003000040000000000000000000ff000000000000"),
|
||||
0x162: bytes.fromhex("0000002700000000000000000000000000000000000000000000000000000000"),
|
||||
0x1BA: bytes.fromhex("00000000000000880200000000000000000100000000000f"),
|
||||
0x1E5: bytes.fromhex("00000000000000000000220300000080"),
|
||||
0x1E0: bytes.fromhex("00000002000000000000000000000000"),
|
||||
0x38C: bytes.fromhex("000000f71f000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
}
|
||||
_CCNC_ADRV_PERIODS = {
|
||||
CAR.KIA_EV9: {
|
||||
0x160: 2,
|
||||
0x1DA: 100,
|
||||
0x1EA: 5,
|
||||
0x200: 5,
|
||||
0x345: 20,
|
||||
0x1E0: 5,
|
||||
0x38C: 20,
|
||||
},
|
||||
}
|
||||
|
||||
def create_accelerator_brake_alt_spoof(bus: int, counter: int, brake_pressed: bool, accelerator_pressed: bool) -> CanData:
|
||||
d = bytearray(_ACCEL_BRAKE_ALT_TEMPLATE)
|
||||
|
||||
def create_accelerator_brake_alt_spoof(bus: int, counter: int, brake_pressed: bool, accelerator_pressed: bool,
|
||||
car_fingerprint=None) -> CanData:
|
||||
template = _KIA_EV9_ACCEL_BRAKE_ALT_TEMPLATE if car_fingerprint == CAR.KIA_EV9 else _ACCEL_BRAKE_ALT_TEMPLATE
|
||||
d = bytearray(template)
|
||||
d[2] = counter & 0xFF # COUNTER (bit 16, 8-bit)
|
||||
d[4] = (d[4] & ~0x01) | (0x01 if brake_pressed else 0x00) # BRAKE_PRESSED (bit 32)
|
||||
d[22] = (d[22] & ~0x01) | (0x01 if accelerator_pressed else 0x00) # ACCELERATOR_PEDAL_PRESSED (bit 176)
|
||||
@@ -721,3 +884,111 @@ def create_accelerator_brake_alt_spoof(bus: int, counter: int, brake_pressed: bo
|
||||
d[0] = crc & 0xFF
|
||||
d[1] = (crc >> 8) & 0xFF
|
||||
return CanData(0x100, bytes(d), bus)
|
||||
|
||||
|
||||
def _create_ccnc_adrv_message(car_fingerprint, address: int, bus: int, counter: int) -> CanData:
|
||||
d = bytearray(_CCNC_ADRV_TEMPLATES[car_fingerprint][address])
|
||||
d[2] = counter & 0xFF
|
||||
crc = hkg_can_fd_checksum(address, None, d)
|
||||
d[0] = crc & 0xFF
|
||||
d[1] = (crc >> 8) & 0xFF
|
||||
return CanData(address, bytes(d), bus)
|
||||
|
||||
|
||||
def _set_ccnc_message_signals(packer, message_name: str, dat: bytearray, values: dict) -> None:
|
||||
dbc_msg = packer.dbc.name_to_msg[message_name]
|
||||
for name, value in values.items():
|
||||
sig = dbc_msg.sigs[name]
|
||||
ival = int(np.floor((value - sig.offset) / sig.factor + 0.5))
|
||||
if ival < 0:
|
||||
ival = (1 << sig.size) + ival
|
||||
_set_value(dat, sig, ival)
|
||||
|
||||
|
||||
def _create_ccnc_adrv_message_with_signals(packer, CP, CAN, address: int, counter: int,
|
||||
message_name: str, values: dict) -> CanData:
|
||||
msg = _create_ccnc_adrv_message(CP.carFingerprint, address, CAN.ECAN, counter)
|
||||
dat = bytearray(msg.dat)
|
||||
# Update decoded fields in the verified neutral payload.
|
||||
_set_ccnc_message_signals(packer, message_name, dat, values)
|
||||
crc = hkg_can_fd_checksum(address, None, dat)
|
||||
dat[0] = crc & 0xFF
|
||||
dat[1] = (crc >> 8) & 0xFF
|
||||
return CanData(address, bytes(dat), CAN.ECAN)
|
||||
|
||||
|
||||
def create_ccnc_acc_control(packer, CAN, enabled: bool, accel: float,
|
||||
stop_request: bool, cruise_standstill: bool, gas_override: bool, set_speed: float,
|
||||
main_mode_acc: int, lead_distance: float, lead_rel_speed: float, lead_visible: bool,
|
||||
v_ego: float, jerk_lower: float = 0.7, jerk_upper: float = 0.7):
|
||||
if not enabled or gas_override or stop_request:
|
||||
accel = 0.0
|
||||
|
||||
lead_visible = bool(enabled and lead_visible)
|
||||
desired_headway = min(max(round(1.625 * max(v_ego, 0.0), 1), 3.5), 204.6) if enabled else 204.6
|
||||
values = {
|
||||
"ACCMode": 0 if not enabled else (2 if gas_override else 1),
|
||||
"MainMode_ACC": int(bool(main_mode_acc)),
|
||||
"StopReq": 1 if stop_request and enabled else 0,
|
||||
"CRUISE_STANDSTILL": 1 if cruise_standstill and stop_request and enabled else 0,
|
||||
"aReqValue": accel,
|
||||
"aReqRaw": accel,
|
||||
"VSetDis": set_speed,
|
||||
"JerkLowerLimit": jerk_lower if enabled else 1.0,
|
||||
"JerkUpperLimit": jerk_upper if enabled else 3.0,
|
||||
"ACC_ObjDist": float(np.clip(lead_distance, 0.0, 204.7)) if lead_visible else 204.6,
|
||||
"ACC_ObjRelSpd": float(np.clip(lead_rel_speed, -16.4, 34.7)) if lead_visible else 34.6,
|
||||
"ObjValid": 0 if lead_visible else 1,
|
||||
"OBJ_STATUS": 2 if enabled and lead_visible else 0,
|
||||
"NEW_SIGNAL_3": 2 if lead_visible else 0,
|
||||
"NEW_SIGNAL_15": desired_headway,
|
||||
"SET_ME_2": 4,
|
||||
"SET_ME_3": 3,
|
||||
"SET_ME_TMP_64": 0x64,
|
||||
# Stock CCNC LKA-long routes use raw 7. The DBC's physical range is stale.
|
||||
"DISTANCE_SETTING": 7 if enabled else 0,
|
||||
}
|
||||
|
||||
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
|
||||
|
||||
|
||||
def create_ccnc_angle_long_status_messages(packer, CP, CAN, counter: int, enabled: bool = False,
|
||||
main_cruise_enabled: bool = False, hud=None, out=None,
|
||||
is_metric: bool = True, steering_available: bool = False,
|
||||
steering_active: bool = False, hba_icon: int = 0) -> list[CanData]:
|
||||
cruise_speed = round(out.vCruiseCluster * (1 if is_metric else CV.KPH_TO_MPH)) if out is not None else 0
|
||||
display_speed = (40 if is_metric else 25) if cruise_speed > (145 if is_metric else 90) else max(cruise_speed, 0)
|
||||
main_standby = bool(main_cruise_enabled and not enabled)
|
||||
values_161 = {
|
||||
"FCA_ICON": 1, # orange: FCA unavailable
|
||||
"FCA_ALT_ICON": 0,
|
||||
"FCA_IMAGE": 0,
|
||||
"ALERTS_1": 0,
|
||||
"ALERTS_2": 0,
|
||||
"ALERTS_3": 0,
|
||||
"ALERTS_4": 0,
|
||||
"ALERTS_5": 0,
|
||||
"SOUNDS_1": 0,
|
||||
"SOUNDS_2": 0,
|
||||
"SOUNDS_3": 0,
|
||||
"SOUNDS_4": 0,
|
||||
"LFA_ICON": (2 if steering_active else 1) if steering_available else 0,
|
||||
"HBA_ICON": hba_icon if hba_icon in (1, 2) else 0,
|
||||
"HDA_ICON": 2 if enabled else 1 if main_standby else 0,
|
||||
"TARGET": 3 if enabled else 0,
|
||||
"SETSPEED": 3 if enabled else 1 if main_standby else 0,
|
||||
"SETSPEED_HUD": 2 if enabled else 1 if main_standby else 0,
|
||||
"SETSPEED_SPEED": display_speed if enabled or main_standby else 255,
|
||||
"DISTANCE": hud.leadDistanceBars if enabled and hud is not None else 0,
|
||||
"DISTANCE_SPACING": 3 if enabled or main_standby else 0,
|
||||
"DISTANCE_CAR": 2 if enabled else 1 if main_standby else 0,
|
||||
}
|
||||
values_162 = {fault: 0 for fault in (
|
||||
"FAULT_FSS", "FAULT_FCA", "FAULT_LSS", "FAULT_SLA", "FAULT_HDA", "FAULT_DAS", "FAULT_LFA", "FAULT_DAW",
|
||||
"FAULT_HBA", "FAULT_ESS",
|
||||
)}
|
||||
values_162["VIBRATE"] = 0
|
||||
return [
|
||||
_create_ccnc_adrv_message_with_signals(packer, CP, CAN, 0x161, counter, "CCNC_0x161", values_161),
|
||||
_create_ccnc_adrv_message_with_signals(packer, CP, CAN, 0x162, counter, "CCNC_0x162", values_162),
|
||||
]
|
||||
|
||||
@@ -4,12 +4,15 @@ from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, CAR, CarControllerParams, \
|
||||
CANFD_UNSUPPORTED_LONGITUDINAL_CAR, \
|
||||
CANFD_SECURITYACCESS_CAR, \
|
||||
CANFD_ANGLE_LONGITUDINAL_CAR, \
|
||||
CANFD_RADAR_LIVE_LONGITUDINAL_CAR, \
|
||||
RADAR_LIVE_LONGITUDINAL_CAR, \
|
||||
UNSUPPORTED_LONGITUDINAL_CAR, HyundaiSafetyFlags, \
|
||||
LEGACY_LONGITUDINAL_CAR, \
|
||||
HyundaiStarPilotSafetyFlags, \
|
||||
hyundai_cancel_button_enables_cruise
|
||||
from opendbc.car.hyundai.radar_interface import get_radar_track_config
|
||||
hyundai_cancel_button_enables_cruise, \
|
||||
kia_ev6_gt_line_longitudinal_tuning
|
||||
from opendbc.car.hyundai.radar_interface import get_radar_track_config, radar_tracks_available
|
||||
from opendbc.car.interfaces import CarInterfaceBase, ACCEL_MIN
|
||||
from opendbc.car.disable_ecu import disable_ecu, ecu_log
|
||||
from opendbc.car.hyundai.carcontroller import CarController
|
||||
@@ -39,6 +42,18 @@ def apply_platform_longitudinal_params(ret: structs.CarParams) -> None:
|
||||
ret.stoppingDecelRate = 0.4
|
||||
|
||||
|
||||
def apply_kia_ev6_gt_line_longitudinal_params(ret: structs.CarParams) -> None:
|
||||
ret.startAccel = 1.4
|
||||
ret.longitudinalActuatorDelay = 0.35
|
||||
ret.vEgoStarting = 0.5
|
||||
|
||||
|
||||
def apply_kia_ev9_longitudinal_params(ret: structs.CarParams) -> None:
|
||||
ret.startAccel = 0.2
|
||||
ret.longitudinalActuatorDelay = 0.3
|
||||
ret.vEgoStarting = 0.5
|
||||
|
||||
|
||||
def apply_ecu_disable_failure_fallback(CP: structs.CarParams, params) -> None:
|
||||
params.put_bool("EcuDisableFailed", True)
|
||||
CP.safetyConfigs[-1].safetyParam &= ~HyundaiSafetyFlags.LONG.value
|
||||
@@ -67,6 +82,11 @@ class CarInterface(CarInterfaceBase):
|
||||
def get_pid_accel_limits(CP, current_speed, cruise_speed):
|
||||
return ACCEL_MIN, CarControllerParams.ACCEL_MAX
|
||||
|
||||
@staticmethod
|
||||
def apply_post_fingerprint_params(CP: structs.CarParams, candidate, fingerprint, car_fw) -> None:
|
||||
if kia_ev6_gt_line_longitudinal_tuning(CP.carFingerprint, CP.carVin):
|
||||
apply_kia_ev6_gt_line_longitudinal_params(CP)
|
||||
|
||||
@staticmethod
|
||||
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
|
||||
ret.brand = "hyundai"
|
||||
@@ -86,14 +106,14 @@ class CarInterface(CarInterfaceBase):
|
||||
# this needs to be figured out for cars without an ADAS ECU
|
||||
# Cars in CANFD_SECURITYACCESS_CAR are known to have ADAS ECUs that work with SecurityAccess
|
||||
ret.alphaLongitudinalAvailable = False
|
||||
if lka_steering and ret.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
|
||||
# Angle-steering LKA platforms still need stock longitudinal validation.
|
||||
if lka_steering and ret.flags & HyundaiFlags.CANFD_ANGLE_STEERING and candidate not in CANFD_ANGLE_LONGITUDINAL_CAR:
|
||||
# Most angle-steering LKA platforms still need stock longitudinal validation.
|
||||
ret.alphaLongitudinalAvailable = False
|
||||
|
||||
ret.enableBsm = 0x1ba in fingerprint[CAN.ECAN]
|
||||
ret.enableBsm = 0x1ba in fingerprint[CAN.ECAN] or candidate == CAR.KIA_EV9
|
||||
|
||||
# Check if the car is hybrid. Only HEV/PHEV cars have 0xFA on E-CAN.
|
||||
if 0xFA in fingerprint[CAN.ECAN]:
|
||||
# Carnival HEV can fingerprint with too little E-CAN traffic to see 0xFA.
|
||||
if 0xFA in fingerprint[CAN.ECAN] or candidate == CAR.KIA_CARNIVAL_HEV_4TH_GEN:
|
||||
ret.flags |= HyundaiFlags.HYBRID.value
|
||||
|
||||
if lka_steering:
|
||||
@@ -101,6 +121,10 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.flags |= HyundaiFlags.CANFD_LKA_STEERING.value
|
||||
if 0x110 in fingerprint[CAN.CAM]:
|
||||
ret.flags |= HyundaiFlags.CANFD_LKA_STEERING_ALT.value
|
||||
# This HDA II Carnival uses the alternate 0x1AA cruise-button frame even
|
||||
# though other LKA-steering platforms use 0x1CF.
|
||||
if candidate == CAR.KIA_CARNIVAL_2025 and 0x1aa in fingerprint[CAN.ECAN] and 0x1cf not in fingerprint[CAN.ECAN]:
|
||||
ret.flags |= HyundaiFlags.CANFD_ALT_BUTTONS.value
|
||||
else:
|
||||
# no LKA steering
|
||||
if 0x1cf not in fingerprint[CAN.ECAN]:
|
||||
@@ -134,12 +158,19 @@ class CarInterface(CarInterfaceBase):
|
||||
if ret.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
|
||||
ret.steerControlType = structs.CarParams.SteerControlType.angle
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CANFD_ANGLE_STEERING.value
|
||||
if candidate == CAR.HYUNDAI_IONIQ_6:
|
||||
# Keep lateral active through stops: zeroing torque at standstill dropped the
|
||||
# stop-turn hold and forced a rate-limit re-ramp from zero on every pull-away
|
||||
# (turn1/turn2 rlogs 2026-07-14). Torque steering has no standstill gate in the
|
||||
# panda safety or the carcontroller; the MDPS tolerating held torque at 0 speed
|
||||
# is being validated on-road.
|
||||
ret.steerAtStandstill = True
|
||||
if ret.flags & HyundaiFlags.CCNC and not ret.flags & HyundaiFlags.CANFD_LKA_STEERING:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CCNC.value
|
||||
|
||||
else:
|
||||
# Shared configuration for non CAN-FD cars
|
||||
ret.alphaLongitudinalAvailable = candidate not in UNSUPPORTED_LONGITUDINAL_CAR
|
||||
ret.alphaLongitudinalAvailable = candidate not in UNSUPPORTED_LONGITUDINAL_CAR or candidate in LEGACY_LONGITUDINAL_CAR
|
||||
ret.enableBsm = 0x58b in fingerprint[CAN.ECAN]
|
||||
|
||||
# Send LFA message on cars with HDA
|
||||
@@ -160,6 +191,8 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
if ret.flags & HyundaiFlags.CAMERA_SCC:
|
||||
ret.safetyConfigs[0].safetyParam |= HyundaiSafetyFlags.CAMERA_SCC.value
|
||||
if candidate in (CAR.HYUNDAI_ELANTRA_2024, CAR.HYUNDAI_ELANTRA_HEV_2024):
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CAN_REFRESH_MSGS.value
|
||||
|
||||
# These cars expose an LKAS/LFA steering-wheel button that StarPilot can customize.
|
||||
if 0x391 in fingerprint[0] or ret.flags & HyundaiFlags.CAN_CANFD_BLENDED:
|
||||
@@ -191,19 +224,21 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
# Common longitudinal control setup
|
||||
|
||||
radar_config = get_radar_track_config(ret.carFingerprint)
|
||||
radar_tracks_available = radar_config is not None and radar_config.start_addr in fingerprint[radar_config.bus]
|
||||
ret.radarUnavailable = not radar_tracks_available
|
||||
radar_config = get_radar_track_config(ret.carFingerprint, ret.flags)
|
||||
radar_available = radar_tracks_available(radar_config, fingerprint)
|
||||
ret.radarUnavailable = not radar_available
|
||||
if ret.flags & HyundaiFlags.NON_SCC:
|
||||
ret.alphaLongitudinalAvailable = False
|
||||
ret.openpilotLongitudinalControl = alpha_long and ret.alphaLongitudinalAvailable
|
||||
if ret.openpilotLongitudinalControl and not (candidate in RADAR_LIVE_LONGITUDINAL_CAR and radar_tracks_available):
|
||||
if ret.openpilotLongitudinalControl and not (candidate in RADAR_LIVE_LONGITUDINAL_CAR and radar_available):
|
||||
ret.radarUnavailable = True
|
||||
ret.pcmCruise = not ret.openpilotLongitudinalControl
|
||||
apply_platform_longitudinal_params(ret)
|
||||
|
||||
if ret.openpilotLongitudinalControl:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.LONG.value
|
||||
if candidate in CANFD_ANGLE_LONGITUDINAL_CAR and ret.flags & HyundaiFlags.CCNC:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CCNC.value
|
||||
if ret.flags & HyundaiFlags.HYBRID:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.HYBRID_GAS.value
|
||||
elif ret.flags & HyundaiFlags.EV:
|
||||
@@ -224,9 +259,20 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.vEgoStarting = 0.5
|
||||
ret.vEgoStopping = 0.35
|
||||
|
||||
if candidate == CAR.HYUNDAI_ELANTRA_2021:
|
||||
ret.longitudinalActuatorDelay = 0.22
|
||||
ret.stopAccel = -1.5
|
||||
ret.stoppingDecelRate = 0.5
|
||||
|
||||
if candidate == CAR.HYUNDAI_ELANTRA_HEV_2024:
|
||||
ret.longitudinalActuatorDelay = 0.22
|
||||
|
||||
if candidate == CAR.HYUNDAI_IONIQ_6:
|
||||
ret.longitudinalActuatorDelay = 0.6
|
||||
|
||||
if candidate == CAR.KIA_EV9 and ret.openpilotLongitudinalControl:
|
||||
apply_kia_ev9_longitudinal_params(ret)
|
||||
|
||||
if candidate == CAR.KIA_NIRO_PHEV_2022:
|
||||
ret.stopAccel = -1.4
|
||||
ret.stoppingDecelRate = 0.5
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, replace
|
||||
|
||||
from opendbc.can import CANParser
|
||||
from opendbc.can.dbc import DBC as DBCReader
|
||||
from opendbc.can.parser import get_raw_value
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car.interfaces import RadarInterfaceBase
|
||||
from opendbc.car.hyundai.values import CAR, DBC, HYUNDAI_MANDO_FRONT_RADAR_DBC, HYUNDAI_MRREVO14F_RADAR_DBC, \
|
||||
from opendbc.car.hyundai.values import CAR, DBC, HyundaiFlags, HYUNDAI_MANDO_FRONT_RADAR_DBC, HYUNDAI_MRREVO14F_RADAR_DBC, \
|
||||
HYUNDAI_MRR30_RADAR_DBC, HYUNDAI_MRR35_RADAR_DBC
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
@@ -29,6 +29,7 @@ class RadarTrackConfig:
|
||||
bus: int = 1
|
||||
frequency: int = 50
|
||||
parser_msg_count: int | None = None
|
||||
expected_length: int | None = None
|
||||
|
||||
@property
|
||||
def can_parser_msg_count(self) -> int:
|
||||
@@ -38,18 +39,37 @@ class RadarTrackConfig:
|
||||
RADAR_TRACK_CONFIGS = {
|
||||
HYUNDAI_MANDO_FRONT_RADAR_DBC: RadarTrackConfig(RADAR_START_ADDR, RADAR_MSG_COUNT, "mando"),
|
||||
HYUNDAI_MRREVO14F_RADAR_DBC: RadarTrackConfig(MRREVO14F_RADAR_START_ADDR, MRREVO14F_RADAR_MSG_COUNT, "mrrevo14f"),
|
||||
HYUNDAI_MRR30_RADAR_DBC: RadarTrackConfig(MRR30_RADAR_START_ADDR, MRR30_RADAR_MSG_COUNT, "mrr30", bus=0),
|
||||
HYUNDAI_MRR35_RADAR_DBC: RadarTrackConfig(MRR35_RADAR_START_ADDR, MRR35_RADAR_MSG_COUNT, "mrr35", bus=0, frequency=20),
|
||||
HYUNDAI_MRR30_RADAR_DBC: RadarTrackConfig(MRR30_RADAR_START_ADDR, MRR30_RADAR_MSG_COUNT, "mrr30", bus=0, expected_length=32),
|
||||
HYUNDAI_MRR35_RADAR_DBC: RadarTrackConfig(MRR35_RADAR_START_ADDR, MRR35_RADAR_MSG_COUNT, "mrr35", bus=0, frequency=20, expected_length=24),
|
||||
}
|
||||
|
||||
# POC for parsing corner radars: https://github.com/commaai/openpilot/pull/24221/
|
||||
|
||||
|
||||
def get_radar_track_config(car_fingerprint) -> RadarTrackConfig | None:
|
||||
def get_radar_track_config(car_fingerprint, flags: int = 0) -> RadarTrackConfig | None:
|
||||
radar_dbc = DBC[car_fingerprint].get(Bus.radar)
|
||||
if car_fingerprint == CAR.GENESIS_G90 and radar_dbc == HYUNDAI_MANDO_FRONT_RADAR_DBC:
|
||||
return RadarTrackConfig(RADAR_START_ADDR, G90_RADAR_MSG_COUNT, "mando", parser_msg_count=RADAR_MSG_COUNT)
|
||||
return RADAR_TRACK_CONFIGS.get(radar_dbc)
|
||||
|
||||
radar_config = RADAR_TRACK_CONFIGS.get(radar_dbc)
|
||||
if radar_config is None:
|
||||
return None
|
||||
|
||||
if car_fingerprint == CAR.HYUNDAI_IONIQ_6 and flags & HyundaiFlags.CANFD_CAMERA_SCC:
|
||||
return replace(radar_config, bus=1)
|
||||
|
||||
return radar_config
|
||||
|
||||
|
||||
def radar_tracks_available(radar_config: RadarTrackConfig | None, fingerprint) -> bool:
|
||||
if radar_config is None:
|
||||
return False
|
||||
|
||||
msg_len = fingerprint[radar_config.bus].get(radar_config.start_addr)
|
||||
if msg_len is None:
|
||||
return False
|
||||
|
||||
return radar_config.expected_length is None or msg_len == radar_config.expected_length
|
||||
|
||||
|
||||
def get_radar_can_parser(CP, radar_config):
|
||||
@@ -64,7 +84,7 @@ def get_radar_can_parser(CP, radar_config):
|
||||
class RadarInterface(RadarInterfaceBase):
|
||||
def __init__(self, CP):
|
||||
super().__init__(CP)
|
||||
self.radar_config = get_radar_track_config(CP.carFingerprint)
|
||||
self.radar_config = get_radar_track_config(CP.carFingerprint, CP.flags)
|
||||
self.updated_messages = set()
|
||||
self.trigger_msg = (self.radar_config.start_addr + self.radar_config.can_parser_msg_count - 1
|
||||
if self.radar_config is not None else RADAR_START_ADDR)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -54,6 +54,9 @@ class CarControllerParams:
|
||||
if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
|
||||
self.STEER_THRESHOLD = 175
|
||||
|
||||
elif CP.flags & HyundaiFlags.CANFD:
|
||||
pass
|
||||
|
||||
# To determine the limit for your car, find the maximum value that the stock LKAS will request.
|
||||
# If the max stock LKAS request is <384, add your car to this list.
|
||||
elif CP.carFingerprint in (CAR.GENESIS_G80, CAR.HYUNDAI_ELANTRA, CAR.HYUNDAI_ELANTRA_GT_I30, CAR.HYUNDAI_IONIQ,
|
||||
@@ -100,6 +103,7 @@ class HyundaiSafetyFlags(IntFlag):
|
||||
NON_SCC = 4096
|
||||
CAN_CANFD_BLENDED = 8192
|
||||
CANCEL_BTN_ENABLE = 16384
|
||||
CAN_REFRESH_MSGS = 32768
|
||||
CCNC = 32768
|
||||
|
||||
|
||||
@@ -215,6 +219,11 @@ class HyundaiPlatformConfig(PlatformConfig):
|
||||
self.dbc_dict = {Bus.pt: "hyundai_palisade_2023_generated"}
|
||||
|
||||
|
||||
@dataclass
|
||||
class HyundaiRefreshPlatformConfig(HyundaiPlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "hyundai_can_refresh_generated"})
|
||||
|
||||
|
||||
@dataclass
|
||||
class HyundaiCanFDPlatformConfig(PlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "hyundai_canfd_generated"})
|
||||
@@ -255,6 +264,14 @@ class CAR(Platforms):
|
||||
CarSpecs(mass=1675, wheelbase=2.885, steerRatio=14.5),
|
||||
flags=HyundaiFlags.HYBRID,
|
||||
)
|
||||
HYUNDAI_AZERA_HEV_7TH_GEN = HyundaiCanFDPlatformConfig(
|
||||
[
|
||||
HyundaiCarDocs("Hyundai Azera Hybrid (with HDA II & LFA2) 2025", "Highway Driving Assist II & Lane Follow Assist 2",
|
||||
car_parts=CarParts.common([CarHarness.hyundai_s])),
|
||||
],
|
||||
CarSpecs(mass=1720, wheelbase=2.895, steerRatio=13.5),
|
||||
flags=HyundaiFlags.CANFD_ANGLE_STEERING,
|
||||
)
|
||||
HYUNDAI_ELANTRA = HyundaiPlatformConfig(
|
||||
[
|
||||
# TODO: 2017-18 could be Hyundai G
|
||||
@@ -278,12 +295,25 @@ class CAR(Platforms):
|
||||
CarSpecs(mass=2800 * CV.LB_TO_KG, wheelbase=2.72, steerRatio=12.9, tireStiffnessFactor=0.65),
|
||||
flags=HyundaiFlags.CHECKSUM_CRC8,
|
||||
)
|
||||
HYUNDAI_ELANTRA_2024 = HyundaiRefreshPlatformConfig(
|
||||
[HyundaiCarDocs("Hyundai Elantra 2024-25", car_parts=CarParts.common([CarHarness.hyundai_k]))],
|
||||
CarSpecs(mass=2797 * CV.LB_TO_KG, wheelbase=2.72, steerRatio=12.9, tireStiffnessFactor=0.65),
|
||||
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.CAMERA_SCC,
|
||||
)
|
||||
HYUNDAI_ELANTRA_HEV_2021 = HyundaiPlatformConfig(
|
||||
[HyundaiCarDocs("Hyundai Elantra Hybrid 2021-23", video="https://youtu.be/_EdYQtV52-c",
|
||||
car_parts=CarParts.common([CarHarness.hyundai_k]))],
|
||||
CarSpecs(mass=3017 * CV.LB_TO_KG, wheelbase=2.72, steerRatio=12.9, tireStiffnessFactor=0.65),
|
||||
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
|
||||
)
|
||||
HYUNDAI_ELANTRA_HEV_2024 = HyundaiRefreshPlatformConfig(
|
||||
[
|
||||
HyundaiCarDocs("Hyundai Elantra Hybrid 2024-26", car_parts=CarParts.common([CarHarness.hyundai_k])),
|
||||
HyundaiCarDocs("Hyundai i30 Hybrid 2024", car_parts=CarParts.common([CarHarness.hyundai_k])),
|
||||
],
|
||||
HYUNDAI_ELANTRA_HEV_2021.specs,
|
||||
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.CAMERA_SCC | HyundaiFlags.HYBRID,
|
||||
)
|
||||
HYUNDAI_GENESIS = HyundaiPlatformConfig(
|
||||
[
|
||||
# TODO: check 2015 packages
|
||||
@@ -725,13 +755,18 @@ class CAR(Platforms):
|
||||
CarSpecs(mass=1450, wheelbase=2.65, steerRatio=13.75, tireStiffnessFactor=0.5),
|
||||
flags=HyundaiFlags.LEGACY,
|
||||
)
|
||||
KIA_XCEED_PHEV = HyundaiPlatformConfig(
|
||||
[HyundaiCarDocs("Kia XCeed Plug-in Hybrid 2021", car_parts=CarParts.common([CarHarness.hyundai_b]))],
|
||||
CarSpecs(mass=1650, wheelbase=2.65, steerRatio=13.75, tireStiffnessFactor=0.5),
|
||||
flags=HyundaiFlags.LEGACY | HyundaiFlags.HYBRID | HyundaiFlags.MANDO_RADAR,
|
||||
)
|
||||
KIA_EV6 = HyundaiCanFDPlatformConfig(
|
||||
[
|
||||
HyundaiCarDocs("Kia EV6 (Southeast Asia only) 2022-24", "All", car_parts=CarParts.common([CarHarness.hyundai_p])),
|
||||
HyundaiCarDocs("Kia EV6 (without HDA II) 2022-24", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_l])),
|
||||
HyundaiCarDocs("Kia EV6 (with HDA II) 2022-24", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p]))
|
||||
],
|
||||
CarSpecs(mass=2055, wheelbase=2.9, steerRatio=16, tireStiffnessFactor=0.65),
|
||||
CarSpecs(mass=2055, wheelbase=2.9, steerRatio=14.25, tireStiffnessFactor=0.65),
|
||||
flags=HyundaiFlags.EV,
|
||||
radar_dbc=HYUNDAI_MRR30_RADAR_DBC,
|
||||
)
|
||||
@@ -739,7 +774,7 @@ class CAR(Platforms):
|
||||
[
|
||||
HyundaiCarDocs("Kia EV6 (with HDA I) 2025", "Highway Driving Assist I", car_parts=CarParts.common([CarHarness.hyundai_p]))
|
||||
],
|
||||
CarSpecs(mass=2055, wheelbase=2.9, steerRatio=16, tireStiffnessFactor=0.65),
|
||||
CarSpecs(mass=2055, wheelbase=2.9, steerRatio=14.26, tireStiffnessFactor=0.65),
|
||||
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING,
|
||||
radar_dbc=HYUNDAI_MRR30_RADAR_DBC,
|
||||
)
|
||||
@@ -748,7 +783,7 @@ class CAR(Platforms):
|
||||
HyundaiCarDocs("Kia EV9 2025-26", car_parts=CarParts.common([CarHarness.hyundai_r]))
|
||||
],
|
||||
CarSpecs(mass=2664, wheelbase=3.1, steerRatio=16),
|
||||
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING,
|
||||
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING | HyundaiFlags.CCNC,
|
||||
radar_dbc=HYUNDAI_MRR35_RADAR_DBC,
|
||||
)
|
||||
KIA_CARNIVAL_4TH_GEN = HyundaiCanFDPlatformConfig(
|
||||
@@ -759,6 +794,24 @@ class CAR(Platforms):
|
||||
CarSpecs(mass=2087, wheelbase=3.09, steerRatio=14.23),
|
||||
flags=HyundaiFlags.RADAR_SCC,
|
||||
)
|
||||
KIA_CARNIVAL_2025 = HyundaiCanFDPlatformConfig(
|
||||
[
|
||||
HyundaiCarDocs("Kia Carnival 2025", car_parts=CarParts.common([CarHarness.hyundai_k])),
|
||||
HyundaiCarDocs("Kia Carnival (with HDA II) 2025", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])),
|
||||
],
|
||||
KIA_CARNIVAL_4TH_GEN.specs,
|
||||
flags=HyundaiFlags.CCNC,
|
||||
)
|
||||
KIA_CARNIVAL_HEV_4TH_GEN = HyundaiCanFDPlatformConfig(
|
||||
[
|
||||
HyundaiCarDocs("Kia Carnival Hybrid 2025", car_parts=CarParts.common([CarHarness.hyundai_k])),
|
||||
HyundaiCarDocs("Kia Carnival Hybrid 2026", car_parts=CarParts.common([CarHarness.hyundai_a])),
|
||||
HyundaiCarDocs("Kia Carnival Hybrid (with HDA II) 2025-26", "Highway Driving Assist II",
|
||||
car_parts=CarParts.common([CarHarness.hyundai_q])),
|
||||
],
|
||||
CarSpecs(mass=2253, wheelbase=3.09, steerRatio=14.23),
|
||||
flags=HyundaiFlags.CCNC,
|
||||
)
|
||||
|
||||
# Genesis
|
||||
GENESIS_GV60_EV_1ST_GEN = HyundaiCanFDPlatformConfig(
|
||||
@@ -906,20 +959,22 @@ CANCEL_BUTTON_ENABLE_CARS = frozenset({
|
||||
CAR.HYUNDAI_PALISADE_2023,
|
||||
})
|
||||
|
||||
KIA_EV6_GT_LINE_LONG_TUNING_VDS_PREFIXES = frozenset({
|
||||
"C4DLC",
|
||||
})
|
||||
|
||||
|
||||
# These classic HKG platforms publish the LKAS button on CLU13 over the alt bus.
|
||||
# Keep G90 excluded until its alt-bus path is route-proven without the recent
|
||||
# engage/disengage regression.
|
||||
ALT_BUS_LDA_BUTTON_CARS = frozenset({
|
||||
CAR.HYUNDAI_SONATA,
|
||||
CAR.HYUNDAI_SONATA_HYBRID,
|
||||
})
|
||||
|
||||
# On these Sonata layouts the alt-bus LKAS button pulses through the CLU13
|
||||
# steering-wheel-status field instead of the dedicated LKAS bit.
|
||||
ALT_BUS_LDA_BUTTON_SWL_STAT_CARS = frozenset({
|
||||
CAR.HYUNDAI_SONATA,
|
||||
CAR.HYUNDAI_SONATA_HYBRID,
|
||||
})
|
||||
|
||||
|
||||
@@ -927,6 +982,11 @@ def hyundai_cancel_button_enables_cruise(car_fingerprint) -> bool:
|
||||
return car_fingerprint in CANCEL_BUTTON_ENABLE_CARS
|
||||
|
||||
|
||||
def kia_ev6_gt_line_longitudinal_tuning(car_fingerprint, vin: str) -> bool:
|
||||
return car_fingerprint == CAR.KIA_EV6 and isinstance(vin, str) and \
|
||||
len(vin) == 17 and vin[3:8] in KIA_EV6_GT_LINE_LONG_TUNING_VDS_PREFIXES
|
||||
|
||||
|
||||
def get_platform_codes(fw_versions: list[bytes]) -> set[tuple[bytes, bytes | None]]:
|
||||
# Returns unique, platform-specific identification codes for a set of versions
|
||||
codes = set() # (code-Optional[part], date)
|
||||
@@ -1015,8 +1075,8 @@ PART_NUMBER_FW_PATTERN = re.compile(b'(?<=[0-9][.,][0-9]{2} )([0-9]{5}[-/]?[A-Z]
|
||||
|
||||
# We've seen both ICE and hybrid for these platforms, and they have hybrid descriptors (e.g. MQ4 vs MQ4H)
|
||||
CANFD_FUZZY_WHITELIST = {CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN, CAR.KIA_K8_HEV_1ST_GEN,
|
||||
# TODO: the hybrid variant is not out yet
|
||||
CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN_LFA2}
|
||||
CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_CARNIVAL_2025, CAR.KIA_CARNIVAL_HEV_4TH_GEN,
|
||||
CAR.KIA_SORENTO_HEV_4TH_GEN_LFA2}
|
||||
|
||||
# List of ECUs expected to have platform codes, camera and radar should exist on all cars
|
||||
# TODO: use abs, it has the platform code and part number on many platforms
|
||||
@@ -1083,7 +1143,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
non_essential_ecus={
|
||||
Ecu.abs: [CAR.HYUNDAI_PALISADE, CAR.HYUNDAI_SONATA, CAR.HYUNDAI_SANTA_FE_2022, CAR.KIA_K5_2021, CAR.HYUNDAI_ELANTRA_2021,
|
||||
CAR.HYUNDAI_SANTA_FE, CAR.HYUNDAI_KONA_EV_2022, CAR.HYUNDAI_KONA_EV, CAR.HYUNDAI_CUSTIN_1ST_GEN, CAR.KIA_SORENTO,
|
||||
CAR.KIA_CEED, CAR.KIA_SELTOS],
|
||||
CAR.KIA_CEED, CAR.KIA_XCEED_PHEV, CAR.KIA_SELTOS],
|
||||
Ecu.fwdRadar: [CAR.HYUNDAI_KONA_NON_SCC],
|
||||
},
|
||||
extra_ecus=[
|
||||
@@ -1112,9 +1172,15 @@ CANFD_CAR = CAR.with_flags(HyundaiFlags.CANFD)
|
||||
CANFD_RADAR_SCC_CAR = CAR.with_flags(HyundaiFlags.RADAR_SCC) # TODO: merge with UNSUPPORTED_LONGITUDINAL_CAR
|
||||
|
||||
# CAN-FD cars with ADAS ECUs that work with the communication-control path.
|
||||
CANFD_SECURITYACCESS_CAR = {CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_6, CAR.HYUNDAI_KONA_EV_2ND_GEN}
|
||||
CANFD_SECURITYACCESS_CAR = {
|
||||
CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_6, CAR.HYUNDAI_KONA_EV_2ND_GEN, CAR.KIA_EV9,
|
||||
}
|
||||
CANFD_UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.CANFD_NO_RADAR_DISABLE) - CANFD_SECURITYACCESS_CAR # TODO: merge with UNSUPPORTED_LONGITUDINAL_CAR
|
||||
CANFD_RADAR_LIVE_LONGITUDINAL_CAR = {CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_6, CAR.KIA_EV6, CAR.GENESIS_GV60_EV_1ST_GEN}
|
||||
CANFD_ANGLE_LONGITUDINAL_CAR = {CAR.KIA_EV9}
|
||||
CANFD_CORNER_RADAR_BSM_CAR = {CAR.HYUNDAI_IONIQ_6, CAR.KIA_EV9}
|
||||
CANFD_RADAR_LIVE_LONGITUDINAL_CAR = {
|
||||
CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_6, CAR.KIA_EV6, CAR.KIA_EV9, CAR.GENESIS_GV60_EV_1ST_GEN,
|
||||
}
|
||||
RADAR_LIVE_LONGITUDINAL_CAR = CANFD_RADAR_LIVE_LONGITUDINAL_CAR | {
|
||||
CAR.HYUNDAI_IONIQ,
|
||||
CAR.HYUNDAI_KONA_EV_2022,
|
||||
@@ -1123,6 +1189,7 @@ RADAR_LIVE_LONGITUDINAL_CAR = CANFD_RADAR_LIVE_LONGITUDINAL_CAR | {
|
||||
CAR.HYUNDAI_SANTA_FE_PHEV_2022,
|
||||
CAR.HYUNDAI_SONATA,
|
||||
CAR.HYUNDAI_SONATA_HYBRID,
|
||||
CAR.KIA_XCEED_PHEV,
|
||||
CAR.GENESIS_G90,
|
||||
}
|
||||
|
||||
@@ -1140,4 +1207,6 @@ NON_SCC_CAR = CAR.with_flags(HyundaiFlags.NON_SCC)
|
||||
# HyundaiFlags.CANFD_RADAR_SCC | HyundaiFlags.CANFD_NO_RADAR_DISABLE | )
|
||||
UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.LEGACY) | CAR.with_flags(HyundaiFlags.UNSUPPORTED_LONGITUDINAL)
|
||||
|
||||
LEGACY_LONGITUDINAL_CAR = {CAR.KIA_XCEED_PHEV}
|
||||
|
||||
DBC = CAR.create_dbc_map()
|
||||
|
||||
@@ -22,6 +22,7 @@ from opendbc.car.honda.values import CAR as HONDA, HONDA_BOSCH, HondaFlags, Hond
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.values import CAR as HYUNDAI, CANFD_CAR, HyundaiFlags, HyundaiStarPilotFlags, HyundaiStarPilotSafetyFlags, ALT_BUS_LDA_BUTTON_CARS
|
||||
from opendbc.car.mock.values import CAR as MOCK
|
||||
from opendbc.car.subaru.values import CAR as SUBARU, SubaruSafetyFlags
|
||||
from opendbc.car.toyota.values import CAR as TOYOTA, NO_DSU_CAR, TSS2_CAR, UNSUPPORTED_DSU_CAR, ToyotaStarPilotFlags, ToyotaSafetyFlags
|
||||
from opendbc.car.values import PLATFORMS
|
||||
from opendbc.can import CANParser
|
||||
@@ -133,6 +134,7 @@ class CarInterfaceBase(ABC):
|
||||
|
||||
dbc_names = {bus: cp.dbc_name for bus, cp in self.can_parsers.items()}
|
||||
self.CC: CarControllerBase = self.CarController(dbc_names, CP)
|
||||
self.CC.FPCP = FPCP
|
||||
|
||||
self.FPCP = FPCP
|
||||
|
||||
@@ -174,8 +176,11 @@ class CarInterfaceBase(ABC):
|
||||
|
||||
ret = cls._get_params(ret, candidate, fingerprint, car_fw, alpha_long, is_release, docs)
|
||||
|
||||
trailer_load_kg = float(np.clip(getattr(starpilot_toggles, "trailer_load_kg", 0.0) or 0.0, 0.0, 15000.0 * CV.LB_TO_KG))
|
||||
|
||||
# Vehicle mass is published curb weight plus assumed payload such as a human driver; notCars have no assumed payload
|
||||
if not ret.notCar:
|
||||
ret.mass = ret.mass + trailer_load_kg
|
||||
ret.mass = ret.mass + STD_CARGO_KG
|
||||
|
||||
# Set params dependent on values set by the car interface
|
||||
@@ -211,6 +216,9 @@ class CarInterfaceBase(ABC):
|
||||
if candidate == CHRYSLER.RAM_HD_5TH_GEN:
|
||||
if 570 not in fingerprint[0]:
|
||||
fp_ret.flags |= ChryslerStarPilotFlags.RAM_HD_ALT_BUTTONS.value
|
||||
if 0x4FF in fingerprint[0]:
|
||||
fp_ret.flags |= ChryslerStarPilotFlags.NO_MIN_STEERING_SPEED.value
|
||||
CP.minSteerSpeed = 0.
|
||||
|
||||
elif platform in GM:
|
||||
fp_ret.canUsePedal = True
|
||||
@@ -263,6 +271,10 @@ class CarInterfaceBase(ABC):
|
||||
elif platform.config.platform_str == "TESLA_MODEL_S_PREAP":
|
||||
fp_ret.canUsePedal = True
|
||||
|
||||
elif platform in SUBARU:
|
||||
if getattr(starpilot_toggles, "subaru_sng", False):
|
||||
fp_ret.safetyConfigs[-1].safetyParam |= SubaruSafetyFlags.STOP_AND_GO.value
|
||||
|
||||
return fp_ret
|
||||
|
||||
@staticmethod
|
||||
@@ -497,6 +509,7 @@ class CarStateBase(ABC):
|
||||
class CarControllerBase(ABC):
|
||||
def __init__(self, dbc_names: dict[StrEnum, str], CP: structs.CarParams):
|
||||
self.CP = CP
|
||||
self.FPCP: custom.StarPilotCarParams | None = None
|
||||
self.frame = 0
|
||||
self.secoc_key: bytes = b"00" * 16
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import numpy as np
|
||||
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car import Bus, DT_CTRL, structs
|
||||
from opendbc.car.common.filter_simple import FirstOrderFilter
|
||||
from opendbc.car.lateral import apply_std_steer_angle_limits
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
from opendbc.car.nissan import nissancan
|
||||
@@ -13,6 +16,7 @@ class CarController(CarControllerBase):
|
||||
super().__init__(dbc_names, CP)
|
||||
self.car_fingerprint = CP.carFingerprint
|
||||
|
||||
self.angle_filter = FirstOrderFilter(0.0, 0.1, DT_CTRL)
|
||||
self.apply_angle_last = 0
|
||||
|
||||
self.packer = CANPacker(dbc_names[Bus.pt])
|
||||
@@ -27,8 +31,15 @@ class CarController(CarControllerBase):
|
||||
### STEER ###
|
||||
steer_hud_alert = 1 if hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw) else 0
|
||||
|
||||
# Nissan EPS is sensitive to jitter in angle requests at low speed and high steering angles.
|
||||
if CC.latActive:
|
||||
self.angle_filter.update_alpha(float(np.interp(CS.out.vEgo, [5, 10, 20], [0.2, 0.1, 0.0])))
|
||||
self.angle_filter.update(actuators.steeringAngleDeg)
|
||||
else:
|
||||
self.angle_filter.x = actuators.steeringAngleDeg
|
||||
|
||||
# windup slower
|
||||
self.apply_angle_last = apply_std_steer_angle_limits(actuators.steeringAngleDeg, self.apply_angle_last, CS.out.vEgoRaw,
|
||||
self.apply_angle_last = apply_std_steer_angle_limits(self.angle_filter.x, self.apply_angle_last, CS.out.vEgoRaw,
|
||||
CS.out.steeringAngleDeg, CC.latActive, CarControllerParams.ANGLE_LIMITS)
|
||||
|
||||
lkas_max_torque = 0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import numpy as np
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus, make_tester_present_msg
|
||||
from opendbc.car import Bus, DT_CTRL, make_tester_present_msg
|
||||
from opendbc.car.lateral import apply_driver_steer_torque_limits, common_fault_avoidance
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
from opendbc.car.subaru import subarucan
|
||||
@@ -26,14 +26,9 @@ class CarController(CarControllerBase):
|
||||
self.p = CarControllerParams(CP)
|
||||
self.packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
|
||||
|
||||
self.manual_hold = False
|
||||
self.prev_standstill = False
|
||||
self.sng_acc_resume = False
|
||||
|
||||
self.prev_close_distance = 0
|
||||
self.prev_cruise_state = 0
|
||||
self.sng_acc_resume_cnt = 0
|
||||
self.standstill_start = 0
|
||||
self.epb_resume_frames_remaining = -1
|
||||
self.last_standstill_frame = 0
|
||||
|
||||
def update(self, CC, CS, now_nanos, starpilot_toggles):
|
||||
actuators = CC.actuators
|
||||
@@ -70,8 +65,9 @@ class CarController(CarControllerBase):
|
||||
self.apply_torque_last = apply_torque
|
||||
|
||||
# *** stop and go ***
|
||||
subaru_sng_manual_parking_brake = getattr(starpilot_toggles, "subaru_sng_manual_parking_brake", False)
|
||||
if starpilot_toggles.subaru_sng:
|
||||
throttle_cmd, speed_cmd = self.stop_and_go(CC, CS)
|
||||
throttle_cmd, speed_cmd = self.stop_and_go(CC, CS, subaru_sng_manual_parking_brake)
|
||||
|
||||
# *** longitudinal ***
|
||||
|
||||
@@ -110,7 +106,11 @@ class CarController(CarControllerBase):
|
||||
can_sends.append(subarucan.create_preglobal_es_distance(self.packer, cruise_button, CS.es_distance_msg))
|
||||
|
||||
if starpilot_toggles.subaru_sng:
|
||||
can_sends.append(subarucan.create_preglobal_throttle(self.packer, CS.throttle_msg["COUNTER"] + 1, CS.throttle_msg, throttle_cmd))
|
||||
can_sends.append(subarucan.create_preglobal_throttle(self.packer, CS.throttle_msg["COUNTER"] + 1, CS.throttle_msg,
|
||||
throttle_cmd))
|
||||
if self.frame % 2 == 0:
|
||||
can_sends.append(subarucan.create_preglobal_brake_pedal(self.packer, CS.brake_pedal_msg,
|
||||
speed_cmd))
|
||||
else:
|
||||
if self.frame % 10 == 0:
|
||||
can_sends.append(subarucan.create_es_dashstatus(self.packer, self.frame // 10, CS.es_dashstatus_msg, CC.enabled,
|
||||
@@ -124,9 +124,11 @@ class CarController(CarControllerBase):
|
||||
can_sends.append(subarucan.create_es_infotainment(self.packer, self.frame // 10, CS.es_infotainment_msg, hud_control.visualAlert))
|
||||
|
||||
if starpilot_toggles.subaru_sng:
|
||||
can_sends.append(subarucan.create_throttle(self.packer, CS.throttle_msg["COUNTER"] + 1, CS.throttle_msg, throttle_cmd))
|
||||
can_sends.append(subarucan.create_throttle(self.packer, CS.throttle_msg["COUNTER"] + 1, CS.throttle_msg,
|
||||
throttle_cmd))
|
||||
if self.frame % 2 == 0:
|
||||
can_sends.append(subarucan.create_brake_pedal(self.packer, self.frame // 2, CS.brake_pedal_msg, speed_cmd, pcm_cancel_cmd))
|
||||
can_sends.append(subarucan.create_brake_pedal(self.packer, self.frame // 2, CS.brake_pedal_msg,
|
||||
speed_cmd, pcm_cancel_cmd))
|
||||
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
if self.frame % 5 == 0:
|
||||
@@ -166,49 +168,37 @@ class CarController(CarControllerBase):
|
||||
self.frame += 1
|
||||
return new_actuators, can_sends
|
||||
|
||||
def stop_and_go(self, CC, CS, speed_cmd=False, throttle_cmd=False):
|
||||
if self.CP.flags & SubaruFlags.PREGLOBAL:
|
||||
trigger_resume = CC.enabled
|
||||
trigger_resume &= CS.car_follow == 1
|
||||
trigger_resume &= CS.close_distance > self.prev_close_distance
|
||||
trigger_resume &= CS.out.standstill
|
||||
trigger_resume &= _SNG_ACC_MIN_DIST < CS.close_distance < _SNG_ACC_MAX_DIST
|
||||
def stop_and_go(self, CC, CS, manual_parking_brake=False):
|
||||
throttle_cmd = False
|
||||
speed_cmd = False
|
||||
|
||||
if trigger_resume:
|
||||
self.sng_acc_resume = True
|
||||
else:
|
||||
if CS.car_follow == 0 and CS.cruise_state == 3 and CS.out.standstill and self.prev_cruise_state == 1:
|
||||
self.manual_hold = True
|
||||
if not CC.enabled or not CC.hudControl.leadVisible:
|
||||
return throttle_cmd, speed_cmd
|
||||
|
||||
if not CS.out.standstill:
|
||||
self.manual_hold = False
|
||||
close_distance = CS.close_distance
|
||||
if not CS.out.standstill:
|
||||
self.last_standstill_frame = self.frame
|
||||
|
||||
trigger_resume = CC.enabled
|
||||
trigger_resume &= CS.car_follow == 1
|
||||
trigger_resume &= CS.close_distance > self.prev_close_distance
|
||||
trigger_resume &= CS.cruise_state == 3
|
||||
trigger_resume &= not self.manual_hold
|
||||
trigger_resume &= _SNG_ACC_MIN_DIST < CS.close_distance < _SNG_ACC_MAX_DIST
|
||||
standstill_timers = (0.75, 0.8) if self.CP.flags & SubaruFlags.PREGLOBAL else (0.5, 0.55)
|
||||
standstill_duration = (self.frame - self.last_standstill_frame) * DT_CTRL
|
||||
in_standstill_hold = standstill_duration > standstill_timers[0]
|
||||
if standstill_duration >= standstill_timers[1]:
|
||||
self.last_standstill_frame = self.frame
|
||||
|
||||
if trigger_resume:
|
||||
self.sng_acc_resume = True
|
||||
if manual_parking_brake or not (self.CP.flags & SubaruFlags.PREGLOBAL):
|
||||
speed_cmd = in_standstill_hold
|
||||
|
||||
if CC.enabled and CS.car_follow == 1 and CS.out.standstill and self.frame > self.standstill_start + 50:
|
||||
speed_cmd = True
|
||||
should_resume = (
|
||||
CS.out.standstill and
|
||||
_SNG_ACC_MIN_DIST < close_distance < _SNG_ACC_MAX_DIST and
|
||||
close_distance > self.prev_close_distance
|
||||
)
|
||||
if should_resume:
|
||||
self.epb_resume_frames_remaining = 15
|
||||
|
||||
if CS.out.standstill and not self.prev_standstill:
|
||||
self.standstill_start = self.frame
|
||||
throttle_cmd = self.epb_resume_frames_remaining > 0
|
||||
if self.epb_resume_frames_remaining > 0:
|
||||
self.epb_resume_frames_remaining -= 1
|
||||
|
||||
self.prev_standstill = CS.out.standstill
|
||||
self.prev_cruise_state = CS.cruise_state
|
||||
|
||||
if self.sng_acc_resume:
|
||||
if self.sng_acc_resume_cnt < 5:
|
||||
throttle_cmd = True
|
||||
self.sng_acc_resume_cnt += 1
|
||||
else:
|
||||
self.sng_acc_resume = False
|
||||
self.sng_acc_resume_cnt = -1
|
||||
|
||||
self.prev_close_distance = CS.close_distance
|
||||
self.prev_close_distance = close_distance
|
||||
return throttle_cmd, speed_cmd
|
||||
|
||||
@@ -358,6 +358,19 @@ def create_brake_pedal(packer, frame, brake_pedal_msg, speed_cmd, brake_cmd):
|
||||
return packer.make_can_msg("Brake_Pedal", CanBus.camera, values)
|
||||
|
||||
|
||||
def create_preglobal_brake_pedal(packer, brake_pedal_msg, speed_cmd):
|
||||
values = {s: brake_pedal_msg[s] for s in sorted([
|
||||
"Brake_Pedal",
|
||||
"Signal1",
|
||||
"Speed",
|
||||
])}
|
||||
|
||||
if speed_cmd:
|
||||
values["Speed"] = 1
|
||||
|
||||
return packer.make_can_msg("Brake_Pedal", CanBus.camera, values)
|
||||
|
||||
|
||||
def create_throttle(packer, frame, throttle_msg, throttle_cmd):
|
||||
values = {s: throttle_msg[s] for s in sorted([
|
||||
"CHECKSUM",
|
||||
|
||||
@@ -1,4 +1,57 @@
|
||||
from types import SimpleNamespace
|
||||
|
||||
from opendbc.car.subaru.carcontroller import CarController
|
||||
from opendbc.car.subaru.fingerprints import FW_VERSIONS
|
||||
from opendbc.car.subaru.values import SubaruFlags
|
||||
|
||||
|
||||
def make_sng_controller(flags=0, prev_close_distance=4.0):
|
||||
controller = object.__new__(CarController)
|
||||
controller.CP = SimpleNamespace(flags=flags)
|
||||
controller.frame = 60
|
||||
controller.last_standstill_frame = 0
|
||||
controller.prev_close_distance = prev_close_distance
|
||||
controller.epb_resume_frames_remaining = -1
|
||||
return controller
|
||||
|
||||
|
||||
def make_sng_state(close_distance=4.0, standstill=True):
|
||||
cc = SimpleNamespace(enabled=True, hudControl=SimpleNamespace(leadVisible=True))
|
||||
cs = SimpleNamespace(
|
||||
close_distance=close_distance,
|
||||
out=SimpleNamespace(standstill=standstill),
|
||||
)
|
||||
return cc, cs
|
||||
|
||||
|
||||
def test_global_sng_keeps_standstill_alive_without_manual_parking_brake_toggle():
|
||||
controller = make_sng_controller()
|
||||
cc, cs = make_sng_state()
|
||||
|
||||
throttle_cmd, speed_cmd = controller.stop_and_go(cc, cs, manual_parking_brake=False)
|
||||
|
||||
assert throttle_cmd is False
|
||||
assert speed_cmd is True
|
||||
|
||||
|
||||
def test_manual_parking_brake_sng_still_sends_resume_throttle():
|
||||
controller = make_sng_controller(prev_close_distance=3.9)
|
||||
cc, cs = make_sng_state(close_distance=4.0)
|
||||
|
||||
throttle_cmd, speed_cmd = controller.stop_and_go(cc, cs, manual_parking_brake=True)
|
||||
|
||||
assert throttle_cmd is True
|
||||
assert speed_cmd is True
|
||||
|
||||
|
||||
def test_preglobal_sng_does_not_send_standstill_keepalive_without_manual_toggle():
|
||||
controller = make_sng_controller(flags=SubaruFlags.PREGLOBAL)
|
||||
cc, cs = make_sng_state()
|
||||
|
||||
throttle_cmd, speed_cmd = controller.stop_and_go(cc, cs, manual_parking_brake=False)
|
||||
|
||||
assert throttle_cmd is False
|
||||
assert speed_cmd is False
|
||||
|
||||
|
||||
class TestSubaruFingerprint:
|
||||
|
||||
@@ -59,6 +59,7 @@ class SubaruSafetyFlags(IntFlag):
|
||||
GEN2 = 1
|
||||
LONG = 2
|
||||
PREGLOBAL_REVERSED_DRIVER_TORQUE = 4
|
||||
STOP_AND_GO = 8
|
||||
|
||||
|
||||
class SubaruFlags(IntFlag):
|
||||
|
||||
@@ -3,10 +3,11 @@ from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus
|
||||
from opendbc.car.lateral import apply_steer_angle_limits_vm
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
from opendbc.car.tesla.coop_steering import CooperativeSteeringController
|
||||
from opendbc.car.tesla.teslacan import TeslaCAN
|
||||
from opendbc.car.tesla.preap.carcontroller import PreAPLongController, init_preap_can
|
||||
from opendbc.car.tesla.preap.stock_cc_spoofer import StockCCSpoofer
|
||||
from opendbc.car.tesla.values import CANBUS, CAR, CarControllerParams
|
||||
from opendbc.car.tesla.values import CANBUS, CAR, CarControllerParams, TeslaSafetyFlags
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
|
||||
def get_safety_CP():
|
||||
@@ -18,6 +19,11 @@ class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_names, CP):
|
||||
super().__init__(dbc_names, CP)
|
||||
self.apply_angle_last = 0
|
||||
self.apply_angle_command_last = 0
|
||||
self.coop_steer = CooperativeSteeringController()
|
||||
self.coop_enabled = CP.carFingerprint == CAR.TESLA_MODEL_3 and any(
|
||||
config.safetyParam & TeslaSafetyFlags.COOP_STEERING.value for config in CP.safetyConfigs
|
||||
)
|
||||
self.packer = CANPacker(dbc_names[Bus.party])
|
||||
self.tesla_can = TeslaCAN(self.packer)
|
||||
self.preap_long = None
|
||||
@@ -40,17 +46,18 @@ class CarController(CarControllerBase):
|
||||
actuators = CC.actuators
|
||||
can_sends = []
|
||||
|
||||
# Tesla EPS enforces disabling steering on heavy lateral override force.
|
||||
# When enabling in a tight curve, we wait until user reduces steering force to start steering.
|
||||
# Canceling is done on rising edge and is handled generically with CC.cruiseControl.cancel
|
||||
lat_active = CC.latActive and CS.hands_on_level < 3
|
||||
# Preserve the stock controller path unless cooperative steering is explicitly enabled.
|
||||
lat_active = CC.latActive and (not CS.out.steeringDisengage if self.coop_enabled else CS.hands_on_level < 3)
|
||||
|
||||
if self.frame % 2 == 0:
|
||||
# Angular rate limit based on speed
|
||||
self.apply_angle_last = apply_steer_angle_limits_vm(actuators.steeringAngleDeg, self.apply_angle_last, CS.out.vEgoRaw, CS.out.steeringAngleDeg,
|
||||
lat_active, CarControllerParams, self.VM)
|
||||
|
||||
can_sends.append(self.tesla_can.create_steering_control(self.apply_angle_last, lat_active))
|
||||
self.apply_angle_command_last, lat_active = self.coop_steer.update(
|
||||
self.apply_angle_last, lat_active, self.coop_enabled, CS, self.VM,
|
||||
)
|
||||
can_sends.append(self.tesla_can.create_steering_control(self.apply_angle_command_last, lat_active))
|
||||
|
||||
if self.frame % 10 == 0:
|
||||
can_sends.append(self.tesla_can.create_steering_allowed())
|
||||
@@ -71,7 +78,7 @@ class CarController(CarControllerBase):
|
||||
|
||||
# TODO: HUD control
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.steeringAngleDeg = self.apply_angle_last
|
||||
new_actuators.steeringAngleDeg = self.apply_angle_command_last
|
||||
|
||||
self.frame += 1
|
||||
return new_actuators, can_sends
|
||||
|
||||
@@ -4,7 +4,7 @@ from opendbc.can import CANDefine, CANParser
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
from opendbc.car.tesla.values import DBC, CANBUS, GEAR_MAP, STEER_THRESHOLD, CAR
|
||||
from opendbc.car.tesla.values import DBC, CANBUS, GEAR_MAP, STEER_DISENGAGE_THRESHOLD, STEER_THRESHOLD, TeslaSafetyFlags, CAR
|
||||
from opendbc.car.tesla.preap.carstate import get_preap_can_parsers, update_preap
|
||||
from opendbc.car.tesla.preap.engagement import PreAPEngagement
|
||||
from opendbc.car.tesla.preap.nap_conf import nap_conf
|
||||
@@ -30,6 +30,9 @@ class CarState(CarStateBase):
|
||||
self.prev_cruise_buttons = 0
|
||||
self.msg_stw_actn_req = None
|
||||
self.speed_units = "MPH"
|
||||
self.cooperative_steering = any(
|
||||
config.safetyParam & TeslaSafetyFlags.COOP_STEERING.value for config in CP.safetyConfigs
|
||||
)
|
||||
|
||||
if CP.carFingerprint == CAR.TESLA_MODEL_S_PREAP:
|
||||
self.engagement = PreAPEngagement(nap_conf.double_pull_enabled, nap_conf.double_pull_window_ms)
|
||||
@@ -95,8 +98,11 @@ class CarState(CarStateBase):
|
||||
|
||||
# FSD disengages using union of handsOnLevel (slow overrides) and high angle rate faults (fast overrides, high speed)
|
||||
eac_error_code = self.can_define.dv["EPAS3S_sysStatus"]["EPAS3S_eacErrorCode"].get(int(epas_status["EPAS3S_eacErrorCode"]), None)
|
||||
ret.steeringDisengage = self.hands_on_level >= 3 or (eac_status == "EAC_INHIBITED" and
|
||||
eac_error_code == "EAC_ERROR_HIGH_ANGLE_RATE_SAFETY")
|
||||
ret.steeringDisengage = (
|
||||
self.hands_on_level >= 3 or
|
||||
(eac_status == "EAC_INHIBITED" and eac_error_code == "EAC_ERROR_HIGH_ANGLE_RATE_SAFETY") or
|
||||
(self.cooperative_steering and abs(ret.steeringTorque) > STEER_DISENGAGE_THRESHOLD)
|
||||
)
|
||||
|
||||
# Cruise state
|
||||
cruise_state = self.can_define.dv["DI_state"]["DI_cruiseState"].get(int(cp_party.vl["DI_state"]["DI_cruiseState"]), None)
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
import math
|
||||
|
||||
import numpy as np
|
||||
|
||||
from opendbc.car import DT_CTRL, rate_limit
|
||||
from opendbc.car.lateral import apply_steer_angle_limits_vm
|
||||
from opendbc.car.tesla.values import CarControllerParams
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
|
||||
|
||||
DT_LAT_CTRL = DT_CTRL * CarControllerParams.STEER_STEP
|
||||
STEER_RESUME_RATE_LIMIT_RAMP_RATE = 300.0 # deg/s^2
|
||||
|
||||
STEER_OVERRIDE_MIN_TORQUE = 0.5 # Nm
|
||||
STEER_OVERRIDE_MAX_TORQUE = 2.5 # Nm
|
||||
STEER_OVERRIDE_TORQUE_RANGE = STEER_OVERRIDE_MAX_TORQUE - STEER_OVERRIDE_MIN_TORQUE
|
||||
STEER_OVERRIDE_MAX_LAT_ACCEL = 2.0 # m/s^2
|
||||
STEER_OVERRIDE_DELTA_GAIN_LIMIT = 125.0 # deg/s/Nm
|
||||
|
||||
|
||||
def apply_bounds(signal: float, limit: float) -> float:
|
||||
return float(np.clip(signal, -limit, limit))
|
||||
|
||||
|
||||
def apply_deadzone(signal: float, deadzone: float) -> float:
|
||||
return signal - apply_bounds(signal, deadzone)
|
||||
|
||||
|
||||
def get_steer_from_lat_accel(lat_accel: float, v_ego: float, VM: VehicleModel) -> float:
|
||||
curvature = lat_accel / max(1.0, v_ego) ** 2
|
||||
return math.degrees(VM.get_steer_from_curvature(curvature, v_ego, 0.0))
|
||||
|
||||
|
||||
def get_override_torque_to_angle(v_ego: float, VM: VehicleModel) -> float:
|
||||
max_angle = CarControllerParams.ANGLE_LIMITS.STEER_ANGLE_MAX
|
||||
steer_from_lat_accel = apply_bounds(get_steer_from_lat_accel(STEER_OVERRIDE_MAX_LAT_ACCEL, v_ego, VM), max_angle)
|
||||
return steer_from_lat_accel / STEER_OVERRIDE_TORQUE_RANGE
|
||||
|
||||
|
||||
def calc_override_angle_delta_limit(torque: float) -> float:
|
||||
max_gain = CarControllerParams.ANGLE_LIMITS.MAX_ANGLE_RATE / DT_LAT_CTRL / STEER_OVERRIDE_TORQUE_RANGE
|
||||
return torque * min(STEER_OVERRIDE_DELTA_GAIN_LIMIT, max_gain) * DT_LAT_CTRL
|
||||
|
||||
|
||||
class SteerRateLimiter:
|
||||
def __init__(self):
|
||||
self.last = 0.0
|
||||
|
||||
def reset(self, angle: float) -> None:
|
||||
self.last = angle
|
||||
|
||||
def update(self, angle: float, angle_delta_limit: float) -> float:
|
||||
limited = rate_limit(angle, self.last, -angle_delta_limit, angle_delta_limit)
|
||||
self.last = limited
|
||||
return limited
|
||||
|
||||
|
||||
class CooperativeSteeringController:
|
||||
def __init__(self):
|
||||
self.apply_angle_last = 0.0
|
||||
self.coop_apply_angle_last = 0.0
|
||||
self.angle_override = 0.0
|
||||
self.resume_rate_limiter_delta = SteerRateLimiter()
|
||||
self.resume_rate_limiter = SteerRateLimiter()
|
||||
|
||||
def reset_override_state(self, apply_angle: float) -> None:
|
||||
self.apply_angle_last = apply_angle
|
||||
self.angle_override = 0.0
|
||||
self.coop_apply_angle_last = apply_angle
|
||||
|
||||
def reset_resume_state(self, apply_angle: float) -> None:
|
||||
self.resume_rate_limiter_delta.reset(0.0)
|
||||
self.resume_rate_limiter.reset(apply_angle)
|
||||
|
||||
def update_override_angle(self, apply_angle_delta: float, driver_torque: float, v_ego: float, VM: VehicleModel) -> float:
|
||||
driver_torque = apply_deadzone(driver_torque, STEER_OVERRIDE_MIN_TORQUE)
|
||||
torque_to_angle = get_override_torque_to_angle(v_ego, VM)
|
||||
target_angle = driver_torque * torque_to_angle
|
||||
|
||||
holding_torque = self.angle_override / torque_to_angle if abs(v_ego) > 0.1 else 0.0
|
||||
torque_delta = driver_torque - holding_torque
|
||||
angle_delta_limit = calc_override_angle_delta_limit(abs(torque_delta))
|
||||
angle_override_delta = float(np.clip(target_angle - self.angle_override, -angle_delta_limit, angle_delta_limit))
|
||||
|
||||
# Avoid counting model-requested motion and driver-requested motion twice.
|
||||
if angle_override_delta * apply_angle_delta > 0.0:
|
||||
angle_override_delta -= apply_bounds(apply_angle_delta, abs(angle_override_delta))
|
||||
|
||||
self.angle_override += angle_override_delta
|
||||
return self.angle_override
|
||||
|
||||
def unwind_override_angle(self, saturation_error: float) -> None:
|
||||
if self.angle_override * saturation_error > 0.0:
|
||||
self.angle_override -= apply_bounds(saturation_error, abs(self.angle_override))
|
||||
|
||||
def apply_resume_rate_limit(self, lat_active: bool, apply_angle: float) -> float:
|
||||
if not lat_active:
|
||||
self.reset_resume_state(apply_angle)
|
||||
return apply_angle
|
||||
|
||||
angle_rate_delta = self.resume_rate_limiter_delta.update(
|
||||
CarControllerParams.ANGLE_LIMITS.MAX_ANGLE_RATE,
|
||||
STEER_RESUME_RATE_LIMIT_RAMP_RATE * DT_LAT_CTRL ** 2,
|
||||
)
|
||||
return self.resume_rate_limiter.update(apply_angle, angle_rate_delta)
|
||||
|
||||
def update(self, apply_angle: float, lat_active: bool, enabled: bool, CS, VM: VehicleModel) -> tuple[float, bool]:
|
||||
if not enabled:
|
||||
self.reset_resume_state(apply_angle)
|
||||
self.reset_override_state(apply_angle)
|
||||
return apply_angle, lat_active
|
||||
|
||||
apply_angle = self.apply_resume_rate_limit(lat_active, apply_angle)
|
||||
if not lat_active:
|
||||
self.reset_override_state(apply_angle)
|
||||
return apply_angle, False
|
||||
|
||||
apply_angle_delta = apply_angle - self.apply_angle_last
|
||||
self.apply_angle_last = apply_angle
|
||||
apply_angle += self.update_override_angle(apply_angle_delta, CS.out.steeringTorque, CS.out.vEgo, VM)
|
||||
|
||||
limited_angle = apply_steer_angle_limits_vm(
|
||||
apply_angle,
|
||||
self.coop_apply_angle_last,
|
||||
CS.out.vEgoRaw,
|
||||
CS.out.steeringAngleDeg,
|
||||
True,
|
||||
CarControllerParams,
|
||||
VM,
|
||||
)
|
||||
self.coop_apply_angle_last = limited_angle
|
||||
self.unwind_override_angle(apply_angle - limited_angle)
|
||||
return limited_angle, True
|
||||
@@ -9,6 +9,7 @@ FW_VERSIONS = {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'TeM3_E014p10_0.0.0 (16),E014.17.00',
|
||||
b'TeM3_E014p10_0.0.0 (16),EL014.17.00',
|
||||
b'TeM3_E014p10_0.0.0 (24),E014.20.2',
|
||||
b'TeM3_ES014p11_0.0.0 (25),ES014.19.0',
|
||||
b'TeMYG4_DCS_Update_0.0.0 (13),E4014.28.1',
|
||||
b'TeMYG4_DCS_Update_0.0.0 (9),E4014.26.0',
|
||||
|
||||
@@ -18,6 +18,13 @@ class CarInterface(CarInterfaceBase):
|
||||
return get_preap_accel_limits(current_speed)
|
||||
return CarInterfaceBase.get_pid_accel_limits(CP, current_speed, cruise_speed)
|
||||
|
||||
@classmethod
|
||||
def get_params(cls, candidate, fingerprint, car_fw, alpha_long, is_release, docs, starpilot_toggles):
|
||||
ret = super().get_params(candidate, fingerprint, car_fw, alpha_long, is_release, docs, starpilot_toggles)
|
||||
if candidate == CAR.TESLA_MODEL_3 and getattr(starpilot_toggles, "tesla_cooperative_steering", False):
|
||||
ret.safetyConfigs[0].safetyParam |= TeslaSafetyFlags.COOP_STEERING.value
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
|
||||
ret.brand = "tesla"
|
||||
|
||||
@@ -126,16 +126,16 @@ def update_preap(cs, can_parsers):
|
||||
|
||||
cs.das_control = None
|
||||
cs.cruise_enabled_prev = ret.cruiseState.enabled
|
||||
ret.pedalMaxRegen = cs.pccEvent == "pedalMaxRegen"
|
||||
ret.teslaCCEngaged = cs.pccEvent == "teslaCCEngaged"
|
||||
ret.teslaCCDisengaged = cs.pccEvent == "teslaCCDisengaged"
|
||||
ret.teslaCCNotArmed = (
|
||||
fp_ret.pedalMaxRegen = cs.pccEvent == "pedalMaxRegen"
|
||||
fp_ret.teslaCCEngaged = cs.pccEvent == "teslaCCEngaged"
|
||||
fp_ret.teslaCCDisengaged = cs.pccEvent == "teslaCCDisengaged"
|
||||
fp_ret.teslaCCNotArmed = (
|
||||
not nap_conf.use_pedal and
|
||||
cs.cruiseEnabled and
|
||||
cs.enableLongControl and
|
||||
cs.di_cruise_state not in ("STANDBY", "ENABLED")
|
||||
)
|
||||
ret.pedalLongActive = cs.enableLongControl and nap_conf.use_pedal
|
||||
fp_ret.pedalLongActive = cs.enableLongControl and nap_conf.use_pedal
|
||||
return ret, fp_ret
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ from opendbc.car.tesla.preap.nap_conf import nap_conf
|
||||
PREAP_FLAG_ENABLE_PEDAL = 1
|
||||
PREAP_FLAG_RADAR_EMULATION = 2
|
||||
PREAP_FLAG_RADAR_BEHIND_NOSECONE = 4
|
||||
SAFETY_TESLA_PREAP = 35
|
||||
|
||||
|
||||
def get_preap_accel_limits(current_speed: float) -> tuple[float, float]:
|
||||
@@ -29,7 +30,7 @@ def get_preap_params(ret: structs.CarParams) -> structs.CarParams:
|
||||
safety_flags |= PREAP_FLAG_RADAR_BEHIND_NOSECONE
|
||||
|
||||
use_pedal = nap_conf.use_pedal and nap_conf.pedal_calibrated
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.teslaPreap, safety_flags)]
|
||||
ret.safetyConfigs = [get_safety_config(SAFETY_TESLA_PREAP, safety_flags)]
|
||||
ret.radarUnavailable = not nap_conf.radar_enabled
|
||||
ret.steerControlType = structs.CarParams.SteerControlType.angle
|
||||
ret.openpilotLongitudinalControl = use_pedal
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from opendbc.car import gen_empty_fingerprint
|
||||
from opendbc.car.tesla.carcontroller import CarController, get_safety_CP
|
||||
from opendbc.car.tesla.coop_steering import CooperativeSteeringController
|
||||
from opendbc.car.tesla.interface import CarInterface
|
||||
from opendbc.car.tesla.values import CAR, DBC, CarControllerParams, TeslaSafetyFlags
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
|
||||
|
||||
def make_car_state(torque=0.0, speed=15.0, angle=0.0):
|
||||
return SimpleNamespace(out=SimpleNamespace(
|
||||
steeringTorque=torque,
|
||||
steeringAngleDeg=angle,
|
||||
vEgo=speed,
|
||||
vEgoRaw=speed,
|
||||
))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def vehicle_model():
|
||||
return VehicleModel(get_safety_CP())
|
||||
|
||||
|
||||
def test_disabled_preserves_angle_command(vehicle_model):
|
||||
controller = CooperativeSteeringController()
|
||||
angle, lat_active = controller.update(12.5, True, False, make_car_state(torque=2.0), vehicle_model)
|
||||
|
||||
assert angle == 12.5
|
||||
assert lat_active
|
||||
|
||||
|
||||
def test_light_driver_torque_adjusts_angle(vehicle_model):
|
||||
controller = CooperativeSteeringController()
|
||||
angle = 0.0
|
||||
|
||||
for _ in range(10):
|
||||
angle, lat_active = controller.update(0.0, True, True, make_car_state(torque=1.5), vehicle_model)
|
||||
|
||||
assert lat_active
|
||||
assert angle > 0.0
|
||||
assert angle <= CarControllerParams.ANGLE_LIMITS.STEER_ANGLE_MAX
|
||||
|
||||
|
||||
def test_inactive_lateral_resets_override(vehicle_model):
|
||||
controller = CooperativeSteeringController()
|
||||
for _ in range(10):
|
||||
controller.update(0.0, True, True, make_car_state(torque=1.5), vehicle_model)
|
||||
|
||||
angle, lat_active = controller.update(8.0, False, True, make_car_state(torque=1.5, angle=8.0), vehicle_model)
|
||||
assert angle == 8.0
|
||||
assert not lat_active
|
||||
|
||||
angle, lat_active = controller.update(8.0, True, True, make_car_state(angle=8.0), vehicle_model)
|
||||
assert angle == 8.0
|
||||
assert lat_active
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("candidate", "enabled", "expected"), (
|
||||
(CAR.TESLA_MODEL_3, False, False),
|
||||
(CAR.TESLA_MODEL_3, True, True),
|
||||
(CAR.TESLA_MODEL_Y, True, False),
|
||||
(CAR.TESLA_MODEL_S_PREAP, True, False),
|
||||
))
|
||||
def test_safety_flag_is_model_3_only(candidate, enabled, expected):
|
||||
toggles = SimpleNamespace(tesla_cooperative_steering=enabled, trailer_load_kg=0.0)
|
||||
params = CarInterface.get_params(candidate, gen_empty_fingerprint(), [], False, False, False, toggles)
|
||||
|
||||
has_flag = any(config.safetyParam & TeslaSafetyFlags.COOP_STEERING.value for config in params.safetyConfigs)
|
||||
assert has_flag is expected
|
||||
|
||||
if candidate != CAR.TESLA_MODEL_S_PREAP:
|
||||
assert CarController(DBC[candidate], params).coop_enabled is expected
|
||||
@@ -130,6 +130,7 @@ class CarControllerParams:
|
||||
|
||||
class TeslaSafetyFlags(IntFlag):
|
||||
LONG_CONTROL = 1
|
||||
COOP_STEERING = 256
|
||||
|
||||
|
||||
class TeslaFlags(IntFlag):
|
||||
@@ -158,3 +159,4 @@ class CruiseButtons:
|
||||
DBC = CAR.create_dbc_map()
|
||||
|
||||
STEER_THRESHOLD = 1
|
||||
STEER_DISENGAGE_THRESHOLD = 5.0
|
||||
|
||||
@@ -22,18 +22,21 @@ non_tested_cars = [
|
||||
MOCK.MOCK,
|
||||
GM.CADILLAC_ATS,
|
||||
GM.CADILLAC_ESCALADE_ASCM,
|
||||
GM.CADILLAC_ESCALADE_ESV_2019_ASCM,
|
||||
GM.CADILLAC_XT5,
|
||||
GM.HOLDEN_ASTRA,
|
||||
GM.CHEVROLET_MALIBU,
|
||||
HYUNDAI.GENESIS_G90,
|
||||
HYUNDAI.GENESIS_GV70_ELECTRIFIED_2ND_GEN,
|
||||
HYUNDAI.GENESIS_GV80_2025,
|
||||
HYUNDAI.HYUNDAI_AZERA_HEV_7TH_GEN,
|
||||
HYUNDAI.HYUNDAI_IONIQ_5_PE,
|
||||
HYUNDAI.HYUNDAI_IONIQ_5_N,
|
||||
HYUNDAI.HYUNDAI_SANTA_FE_HEV_5TH_GEN,
|
||||
HYUNDAI.HYUNDAI_TUCSON_PHEV_2025,
|
||||
HYUNDAI.KIA_EV6_2025,
|
||||
HYUNDAI.KIA_EV9,
|
||||
HYUNDAI.KIA_CARNIVAL_2025,
|
||||
HYUNDAI.KIA_SPORTAGE_2026,
|
||||
HYUNDAI.KIA_SORENTO_2024,
|
||||
HYUNDAI.KIA_SORENTO_HEV_4TH_GEN_LFA2,
|
||||
@@ -162,7 +165,9 @@ routes = [
|
||||
CarTestRoute("656ac0d830792fcc/2021-12-28--14-45-56", HYUNDAI.HYUNDAI_SANTA_FE_PHEV_2022, segment=1),
|
||||
CarTestRoute("de59124955b921d8/2023-06-24--00-12-50", HYUNDAI.KIA_CARNIVAL_4TH_GEN),
|
||||
CarTestRoute("409c9409979a8abc/2023-07-11--09-06-44", HYUNDAI.KIA_CARNIVAL_4TH_GEN), # Chinese model
|
||||
CarTestRoute("6c0069dcd5bbb6c1/00000020--6b95507969", HYUNDAI.KIA_CARNIVAL_HEV_4TH_GEN), # HDA II
|
||||
CarTestRoute("e0e98335f3ebc58f/2021-03-07--16-38-29", HYUNDAI.KIA_CEED),
|
||||
CarTestRoute("22703002ddbe2a08/0000000e--083b76c42c", HYUNDAI.KIA_XCEED_PHEV, segment=0),
|
||||
CarTestRoute("7653b2bce7bcfdaa/2020-03-04--15-34-32", HYUNDAI.KIA_OPTIMA_G4),
|
||||
CarTestRoute("018654717bc93d7d/2022-09-19--23-11-10", HYUNDAI.KIA_OPTIMA_G4_FL, segment=0),
|
||||
CarTestRoute("f9716670b2481438/2023-08-23--14-49-50", HYUNDAI.KIA_OPTIMA_H),
|
||||
@@ -229,7 +234,11 @@ routes = [
|
||||
CarTestRoute("c5ac319aa9583f83/2021-06-01--18-18-31", HYUNDAI.HYUNDAI_ELANTRA),
|
||||
CarTestRoute("734ef96182ddf940/2022-10-02--16-41-44", HYUNDAI.HYUNDAI_ELANTRA_GT_I30),
|
||||
CarTestRoute("82e9cdd3f43bf83e/2021-05-15--02-42-51", HYUNDAI.HYUNDAI_ELANTRA_2021),
|
||||
CarTestRoute("c2fd040a5e34f3ad/00000013--9211a52a3d", HYUNDAI.HYUNDAI_ELANTRA_2024),
|
||||
CarTestRoute("715ac05b594e9c59/2021-06-20--16-21-07", HYUNDAI.HYUNDAI_ELANTRA_HEV_2021),
|
||||
CarTestRoute("65ef8b49f9b0dd24/00000141--5c8720a01c", HYUNDAI.HYUNDAI_ELANTRA_HEV_2024),
|
||||
CarTestRoute("07a48901db7b2503/000001a1--ad07872c4f", HYUNDAI.HYUNDAI_ELANTRA_HEV_2024),
|
||||
CarTestRoute("07a48901db7b2503/0000000f--697d5906e8", HYUNDAI.HYUNDAI_ELANTRA_HEV_2024), # Hyundai i30 Hybrid 2024
|
||||
CarTestRoute("7120aa90bbc3add7/2021-08-02--07-12-31", HYUNDAI.HYUNDAI_SONATA_HYBRID),
|
||||
CarTestRoute("bc40c72b728178f2/00000006--ee76ae8c42", HYUNDAI.HYUNDAI_SONATA_HEV_2024),
|
||||
CarTestRoute("715ac05b594e9c59/2021-10-27--23-24-56", HYUNDAI.GENESIS_G70_2020),
|
||||
|
||||
@@ -4,9 +4,17 @@ import pytest
|
||||
from opendbc.car.can_definitions import CanData
|
||||
from opendbc.car.car_helpers import FRAME_FINGERPRINT, _apply_starpilot_access_policy, _get_gm_stored_candidate_fallback, can_fingerprint
|
||||
from opendbc.car.fingerprints import _FINGERPRINTS as FINGERPRINTS
|
||||
from opendbc.car.gm.values import CAR as GM
|
||||
|
||||
|
||||
class TestCanFingerprint:
|
||||
@staticmethod
|
||||
def _fingerprint_from_can(fingerprint):
|
||||
can = [CanData(address=address, dat=b'\x00' * length, src=src)
|
||||
for address, length in fingerprint.items() for src in (0, 1)]
|
||||
fingerprint_iter = iter([can])
|
||||
return can_fingerprint(lambda **kwargs: [next(fingerprint_iter, [])]) # noqa: B023
|
||||
|
||||
@pytest.mark.parametrize("car_model, fingerprints", FINGERPRINTS.items())
|
||||
def test_can_fingerprint(self, car_model, fingerprints):
|
||||
"""Tests online fingerprinting function on offline fingerprints"""
|
||||
@@ -23,6 +31,12 @@ class TestCanFingerprint:
|
||||
assert finger[1] == fingerprint
|
||||
assert finger[2] == {}
|
||||
|
||||
def test_gm_sascm_superset_fingerprint_matches_ascm_variant(self):
|
||||
car_fingerprint, finger = self._fingerprint_from_can(FINGERPRINTS[GM.CADILLAC_ESCALADE_ESV_2019_ASCM][0])
|
||||
|
||||
assert car_fingerprint == GM.CADILLAC_ESCALADE_ESV_2019_ASCM
|
||||
assert finger[0][0x2FF] == 8
|
||||
|
||||
def test_timing(self, subtests):
|
||||
# just pick any CAN fingerprinting car
|
||||
car_model = "CHEVROLET_BOLT_ACC_2022_2023"
|
||||
|
||||
@@ -8,9 +8,13 @@ from collections.abc import Callable
|
||||
from typing import Any
|
||||
|
||||
from cereal import custom
|
||||
from opendbc.car import DT_CTRL, CanData, structs
|
||||
from opendbc.can import CANParser
|
||||
from opendbc.car import Bus, DT_CTRL, CanData, structs
|
||||
from opendbc.car.car_helpers import _apply_disable_openpilot_long, interfaces
|
||||
from opendbc.car.chrysler.carcontroller import CarController as ChryslerCarController
|
||||
from opendbc.car.chrysler.carstate import CarState as ChryslerCarState
|
||||
from opendbc.car.chrysler.interface import CarInterface as ChryslerCarInterface
|
||||
from opendbc.car.chrysler.values import CAR as CHRYSLER_CAR, DBC as CHRYSLER_DBC, ChryslerSafetyFlags, ChryslerStarPilotFlags
|
||||
from opendbc.car.fingerprints import FW_VERSIONS
|
||||
from opendbc.car.fw_versions import FW_QUERY_CONFIGS
|
||||
from opendbc.car.hyundai.interface import CarInterface as HyundaiCarInterface
|
||||
@@ -74,6 +78,8 @@ def get_test_starpilot_toggles() -> SimpleNamespace:
|
||||
reverse_cruise_increase=False,
|
||||
sng_hack=False,
|
||||
subaru_sng=False,
|
||||
subaru_sng_manual_parking_brake=False,
|
||||
tesla_cooperative_steering=False,
|
||||
unlock_doors=False,
|
||||
vEgoStopping=0.5,
|
||||
volt_sng=False,
|
||||
@@ -160,6 +166,80 @@ class TestCarInterfaces:
|
||||
"Center_Stack_2": {"LKAS_Button": 1},
|
||||
}, is_ram=True)
|
||||
|
||||
def test_chrysler_wd_mod_enables_steer_to_zero(self):
|
||||
fingerprint = {bus: {} for bus in range(8)}
|
||||
fingerprint[0][0x4FF] = 8
|
||||
toggles = get_test_starpilot_toggles()
|
||||
|
||||
car_params = ChryslerCarInterface.get_params(
|
||||
CHRYSLER_CAR.CHRYSLER_PACIFICA_2020,
|
||||
fingerprint,
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=toggles,
|
||||
)
|
||||
assert car_params.minSteerSpeed > 0.
|
||||
|
||||
fp_car_params = ChryslerCarInterface.get_starpilot_params(
|
||||
CHRYSLER_CAR.CHRYSLER_PACIFICA_2020,
|
||||
fingerprint,
|
||||
[],
|
||||
car_params,
|
||||
toggles,
|
||||
)
|
||||
assert fp_car_params.flags & ChryslerStarPilotFlags.NO_MIN_STEERING_SPEED
|
||||
assert car_params.minSteerSpeed == 0.
|
||||
|
||||
controller = ChryslerCarController({Bus.pt: CHRYSLER_DBC[car_params.carFingerprint][Bus.pt]}, car_params)
|
||||
controller.FPCP = fp_car_params
|
||||
controller.frame = 202
|
||||
|
||||
CC = structs.CarControl()
|
||||
CC.latActive = True
|
||||
CC.actuators.torque = 0.1
|
||||
CC = CC.as_reader()
|
||||
CS = SimpleNamespace(
|
||||
out=SimpleNamespace(vEgo=0.0, steeringTorqueEps=0.0, gearShifter=structs.CarState.GearShifter.drive),
|
||||
lkas_car_model=-1,
|
||||
button_counter=0,
|
||||
button_message="CRUISE_BUTTONS",
|
||||
auto_high_beam=0,
|
||||
)
|
||||
_, can_sends = controller.update(CC, CS, 0, toggles)
|
||||
|
||||
lkas_parser = CANParser(CHRYSLER_DBC[car_params.carFingerprint][Bus.pt], [("LKAS_COMMAND", 50)], 0)
|
||||
lkas_parser.update([0, can_sends])
|
||||
assert lkas_parser.vl["LKAS_COMMAND"]["LKAS_CONTROL_BIT"] == 1
|
||||
|
||||
@pytest.mark.parametrize("candidate", (CHRYSLER_CAR.JEEP_GRAND_CHEROKEE, CHRYSLER_CAR.JEEP_GRAND_CHEROKEE_2019))
|
||||
def test_jeep_brake_hold_safety_capability_is_provisioned(self, candidate):
|
||||
car_params = ChryslerCarInterface.get_params(
|
||||
candidate,
|
||||
{bus: {} for bus in range(8)},
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=get_test_starpilot_toggles(),
|
||||
)
|
||||
|
||||
assert car_params.safetyConfigs[0].safetyParam & ChryslerSafetyFlags.JEEP_BRAKE_HOLD.value
|
||||
|
||||
def test_jeep_brake_hold_safety_capability_is_not_provisioned_for_non_jeep(self):
|
||||
car_params = ChryslerCarInterface.get_params(
|
||||
CHRYSLER_CAR.CHRYSLER_PACIFICA_2020,
|
||||
{bus: {} for bus in range(8)},
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=get_test_starpilot_toggles(),
|
||||
)
|
||||
|
||||
assert not car_params.safetyConfigs[0].safetyParam & ChryslerSafetyFlags.JEEP_BRAKE_HOLD.value
|
||||
|
||||
def test_gm_bolt_gen2_pedal_safety_flags(self):
|
||||
CarInterface = interfaces[GM_CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL]
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
from types import SimpleNamespace
|
||||
|
||||
from opendbc.car.chrysler.carcontroller import (
|
||||
JEEP_BRAKE_HOLD_DEFAULT_DECEL,
|
||||
clip_jeep_brake_hold_decel,
|
||||
supports_jeep_brake_hold,
|
||||
)
|
||||
from opendbc.car.chrysler.values import CAR, ChryslerSafetyFlags, JEEPS
|
||||
|
||||
|
||||
def test_jeep_group_is_grand_cherokee_only():
|
||||
assert JEEPS == {CAR.JEEP_GRAND_CHEROKEE, CAR.JEEP_GRAND_CHEROKEE_2019}
|
||||
|
||||
|
||||
def test_jeep_brake_hold_support_requires_toggle_jeep_pcm_and_safety_flag():
|
||||
safety = [SimpleNamespace(safetyParam=ChryslerSafetyFlags.JEEP_BRAKE_HOLD.value)]
|
||||
no_safety = [SimpleNamespace(safetyParam=0)]
|
||||
|
||||
assert supports_jeep_brake_hold(SimpleNamespace(
|
||||
carFingerprint=CAR.JEEP_GRAND_CHEROKEE,
|
||||
pcmCruise=True,
|
||||
safetyConfigs=safety,
|
||||
), True)
|
||||
assert not supports_jeep_brake_hold(SimpleNamespace(
|
||||
carFingerprint=CAR.JEEP_GRAND_CHEROKEE,
|
||||
pcmCruise=True,
|
||||
safetyConfigs=no_safety,
|
||||
), True)
|
||||
assert not supports_jeep_brake_hold(SimpleNamespace(
|
||||
carFingerprint=CAR.JEEP_GRAND_CHEROKEE,
|
||||
pcmCruise=False,
|
||||
safetyConfigs=safety,
|
||||
), True)
|
||||
assert not supports_jeep_brake_hold(SimpleNamespace(
|
||||
carFingerprint=CAR.CHRYSLER_PACIFICA_2018,
|
||||
pcmCruise=True,
|
||||
safetyConfigs=safety,
|
||||
), True)
|
||||
assert not supports_jeep_brake_hold(SimpleNamespace(
|
||||
carFingerprint=CAR.JEEP_GRAND_CHEROKEE,
|
||||
pcmCruise=True,
|
||||
safetyConfigs=safety,
|
||||
), False)
|
||||
|
||||
|
||||
def test_jeep_brake_hold_decel_is_bounded():
|
||||
assert clip_jeep_brake_hold_decel(JEEP_BRAKE_HOLD_DEFAULT_DECEL) == JEEP_BRAKE_HOLD_DEFAULT_DECEL
|
||||
assert clip_jeep_brake_hold_decel(-8.0) == -3.0
|
||||
assert clip_jeep_brake_hold_decel(0.5) == -0.5
|
||||
@@ -17,6 +17,8 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
|
||||
# Toyota LTA also has torque
|
||||
"TOYOTA_RAV4_TSS2_2023" = [nan, 3.0, nan]
|
||||
"TOYOTA_MATRIX_RETROFIT" = [4.05, 1.84, 0.10]
|
||||
"TOYOTA_RAV4_PRIME" = [1.7, 2.0, 0.14]
|
||||
|
||||
# Tesla angle based controllers
|
||||
"TESLA_MODEL_3" = [nan, 2.5, nan]
|
||||
@@ -35,6 +37,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"FORD_F_150_LIGHTNING_MK1" = [nan, 1.5, nan]
|
||||
"FORD_MUSTANG_MACH_E_MK1" = [nan, 1.5, nan]
|
||||
"FORD_RANGER_MK2" = [nan, 1.5, nan]
|
||||
"FORD_TRANSIT_MK5" = [nan, 1.5, nan]
|
||||
###
|
||||
|
||||
# No steering wheel
|
||||
@@ -74,6 +77,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"HYUNDAI_TUCSON_HEV_2025" = [2.5, 2.5, 0.1]
|
||||
"HYUNDAI_TUCSON_PHEV_2025" = [2.5, 2.5, 0.1]
|
||||
"KIA_SPORTAGE_5TH_GEN" = [2.6, 2.6, 0.1]
|
||||
"KIA_XCEED_PHEV" = [2.9638737459977467, 2.1259108157250735, 0.07813665616927593]
|
||||
"KIA_SPORTAGE_2026" = [nan, 2.5, nan]
|
||||
"KIA_SPORTAGE_HEV_2026" = [nan, 2.5, nan]
|
||||
"GENESIS_GV70_1ST_GEN" = [2.42, 2.42, 0.1]
|
||||
@@ -87,6 +91,8 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"KIA_NIRO_EV_2ND_GEN" = [2.05, 2.5, 0.14]
|
||||
"GENESIS_GV80" = [2.5, 2.5, 0.1]
|
||||
"KIA_CARNIVAL_4TH_GEN" = [1.75, 1.75, 0.15]
|
||||
"KIA_CARNIVAL_2025" = [1.75, 1.75, 0.15]
|
||||
"KIA_CARNIVAL_HEV_4TH_GEN" = [1.75, 1.75, 0.15]
|
||||
"GMC_ACADIA" = [1.6, 1.6, 0.2]
|
||||
"LEXUS_IS_TSS2" = [2.0, 2.0, 0.1]
|
||||
"HYUNDAI_KONA_EV_2ND_GEN" = [2.5, 2.5, 0.1]
|
||||
@@ -96,6 +102,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"HYUNDAI_IONIQ_9" = [nan, 2.5, nan]
|
||||
"HYUNDAI_AZERA_6TH_GEN" = [1.8, 1.8, 0.1]
|
||||
"HYUNDAI_AZERA_HEV_6TH_GEN" = [1.8, 1.8, 0.1]
|
||||
"HYUNDAI_AZERA_HEV_7TH_GEN" = [nan, 2.5, nan]
|
||||
"KIA_K8_HEV_1ST_GEN" = [2.5, 2.5, 0.1]
|
||||
"KIA_K4_2025" = [2.5, 2.5, 0.1]
|
||||
"KIA_K5_2025" = [2.5, 2.5, 0.1]
|
||||
|
||||
@@ -9,7 +9,6 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
|
||||
"TOYOTA_ALPHARD_TSS2" = "TOYOTA_SIENNA"
|
||||
"TOYOTA_PRIUS_V" = "TOYOTA_PRIUS"
|
||||
"TOYOTA_RAV4_PRIME" = "TOYOTA_RAV4_TSS2"
|
||||
"TOYOTA_SIENNA_4TH_GEN" = "TOYOTA_RAV4_TSS2"
|
||||
"LEXUS_IS" = "LEXUS_NX"
|
||||
"LEXUS_CTH" = "LEXUS_NX"
|
||||
@@ -38,8 +37,10 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"HYUNDAI_IONIQ_HEV_2022" = "HYUNDAI_IONIQ_PHEV_2019"
|
||||
"HYUNDAI_IONIQ_EV_2020" = "HYUNDAI_IONIQ_PHEV_2019"
|
||||
"HYUNDAI_ELANTRA" = "HYUNDAI_SONATA_LF"
|
||||
"HYUNDAI_ELANTRA_2024" = "HYUNDAI_ELANTRA_2021"
|
||||
"HYUNDAI_ELANTRA_GT_I30" = "HYUNDAI_SONATA_LF"
|
||||
"HYUNDAI_ELANTRA_HEV_2021" = "HYUNDAI_SONATA"
|
||||
"HYUNDAI_ELANTRA_HEV_2024" = "HYUNDAI_SONATA"
|
||||
"HYUNDAI_TUCSON" = "HYUNDAI_SANTA_FE"
|
||||
"HYUNDAI_SANTA_FE_2022" = "HYUNDAI_SANTA_FE_HEV_2022"
|
||||
"KIA_K5_HEV_2020" = "KIA_K5_2021"
|
||||
@@ -66,9 +67,11 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
|
||||
"BUICK_LACROSSE" = "CHEVROLET_VOLT"
|
||||
"BUICK_LACROSSE_ASCM" = "CHEVROLET_VOLT"
|
||||
"BUICK_LACROSSE_ASCM_19US" = "CHEVROLET_VOLT"
|
||||
"BUICK_REGAL" = "CHEVROLET_VOLT"
|
||||
"CADILLAC_ESCALADE_ASCM" = "CADILLAC_ESCALADE"
|
||||
"CADILLAC_ESCALADE_ESV" = "CHEVROLET_VOLT"
|
||||
"CADILLAC_ESCALADE_ESV_2019_ASCM" = "CADILLAC_ESCALADE_ESV_2019"
|
||||
"CADILLAC_ATS" = "CHEVROLET_VOLT"
|
||||
"CHEVROLET_MALIBU" = "CHEVROLET_VOLT"
|
||||
"CHEVROLET_MALIBU_ASCM" = "CHEVROLET_VOLT"
|
||||
@@ -117,5 +120,6 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"GMC_YUKON_CC" = "CHEVROLET_SILVERADO"
|
||||
"CHEVROLET_TRAILBLAZER_CC" = "CHEVROLET_TRAILBLAZER"
|
||||
"CHEVROLET_SILVERADO_CC" = "CHEVROLET_SILVERADO"
|
||||
"CADILLAC_XT4_CC" = "CADILLAC_XT4"
|
||||
|
||||
"CHEVROLET_TRAX" = "CHEVROLET_VOLT"
|
||||
|
||||
@@ -26,6 +26,9 @@ ACCEL_WINDDOWN_LIMIT = -4.0 * DT_CTRL * 3 # m/s^2 / frame
|
||||
ACCEL_PID_UNWIND = 0.03 * DT_CTRL * 3 # m/s^2 / frame
|
||||
PRIUS_INTEGRAL_MISMATCH_UNWIND = 8.0
|
||||
PRIUS_POSITIVE_FEEDFORWARD_SCALE = 0.7
|
||||
PRIUS_CRUISE_FEEDFORWARD_SCALE = 1.0
|
||||
PRIUS_NEGATIVE_FEEDFORWARD_SCALE = 1.125
|
||||
CAMRY_HYBRID_POSITIVE_FEEDFORWARD_SCALE = 0.8
|
||||
|
||||
MAX_PITCH_COMPENSATION = 1.5 # m/s^2
|
||||
TOYOTA_COAST_BRAKE_MIN_SPEED = 15.0 # m/s
|
||||
@@ -33,6 +36,7 @@ TOYOTA_COAST_BRAKE_ENABLE_ACCEL = -0.10 # m/s^2
|
||||
TOYOTA_COAST_BRAKE_DISABLE_ACCEL = -0.06 # m/s^2
|
||||
TOYOTA_NO_LEAD_COAST_BRAKE_ACCEL = -0.30 # m/s^2
|
||||
TOYOTA_INTERCEPTOR_COMFORT_TARGET_ACCEL = 2.0 # m/s^2
|
||||
TOYOTA_NO_LEAD_CRUISE_SIGN_FLIP_MIN_SET_SPEED_ERROR = 0.35 # m/s
|
||||
|
||||
# LKA limits
|
||||
# EPS faults if you apply torque while the steering rate is above 100 deg/s for too long
|
||||
@@ -43,19 +47,28 @@ MAX_STEER_RATE_FRAMES = 18 # tx control frames needed before torque can be cut
|
||||
MAX_USER_TORQUE = 500
|
||||
|
||||
PARK = structs.CarState.GearShifter.park
|
||||
REVERSE = structs.CarState.GearShifter.reverse
|
||||
|
||||
# Lock / unlock door commands - Credit goes to AlexandreSato!
|
||||
LOCK_CMD = b"\x40\x05\x30\x11\x00\x80\x00\x00"
|
||||
UNLOCK_CMD = b"\x40\x05\x30\x11\x00\x40\x00\x00"
|
||||
|
||||
|
||||
def is_camry_hybrid(CP) -> bool:
|
||||
return CP.carFingerprint == CAR.TOYOTA_CAMRY and bool(CP.flags & ToyotaFlags.HYBRID.value)
|
||||
|
||||
|
||||
def is_ths_hybrid(CP) -> bool:
|
||||
return CP.carFingerprint == CAR.TOYOTA_PRIUS or is_camry_hybrid(CP)
|
||||
|
||||
|
||||
def get_long_tune(CP, params):
|
||||
kiBP = [2., 5.]
|
||||
kiV = [0.5, 0.25]
|
||||
k_f = 1.0
|
||||
|
||||
if CP.carFingerprint == CAR.TOYOTA_PRIUS:
|
||||
k_f = 0.8
|
||||
if is_ths_hybrid(CP):
|
||||
k_f = 0.8 if CP.carFingerprint == CAR.TOYOTA_PRIUS else 1.0
|
||||
elif CP.carFingerprint not in TSS2_CAR:
|
||||
kiBP = [0., 5., 35.]
|
||||
kiV = [3.6, 2.4, 1.5]
|
||||
@@ -65,6 +78,21 @@ def get_long_tune(CP, params):
|
||||
rate=1 / (DT_CTRL * 3))
|
||||
|
||||
|
||||
def get_prius_positive_feedforward_scale(v_ego: float) -> float:
|
||||
return float(np.interp(v_ego, [0.0, 8.0, 20.0],
|
||||
[PRIUS_POSITIVE_FEEDFORWARD_SCALE, PRIUS_POSITIVE_FEEDFORWARD_SCALE, PRIUS_CRUISE_FEEDFORWARD_SCALE]))
|
||||
|
||||
|
||||
def get_prius_feedforward(accel: float, v_ego: float) -> float:
|
||||
if accel > 0.0:
|
||||
return accel * get_prius_positive_feedforward_scale(v_ego)
|
||||
return accel * PRIUS_NEGATIVE_FEEDFORWARD_SCALE
|
||||
|
||||
|
||||
def get_camry_hybrid_feedforward(accel: float) -> float:
|
||||
return accel * CAMRY_HYBRID_POSITIVE_FEEDFORWARD_SCALE if accel > 0.0 else accel
|
||||
|
||||
|
||||
def update_permit_braking(current: bool, net_acceleration_request_min: float, stopping: bool,
|
||||
long_active: bool, v_ego: float, lead_visible: bool) -> bool:
|
||||
if stopping or not long_active:
|
||||
@@ -161,6 +189,19 @@ def limit_prius_stopping_accel(pcm_accel_cmd: float, target_accel: float, stoppi
|
||||
return max(pcm_accel_cmd, max(stop_floor, planner_floor))
|
||||
|
||||
|
||||
def limit_no_lead_cruise_sign_flip(pcm_accel_cmd: float, target_accel: float, stopping: bool, v_ego: float,
|
||||
set_speed: float, lead_visible: bool) -> float:
|
||||
if stopping or lead_visible or pcm_accel_cmd >= 0.0 or v_ego < TOYOTA_COAST_BRAKE_MIN_SPEED:
|
||||
return pcm_accel_cmd
|
||||
if target_accel < -0.02 or set_speed <= 0.0:
|
||||
return pcm_accel_cmd
|
||||
|
||||
if float(set_speed) - float(v_ego) >= TOYOTA_NO_LEAD_CRUISE_SIGN_FLIP_MIN_SET_SPEED_ERROR:
|
||||
return max(pcm_accel_cmd, 0.0)
|
||||
|
||||
return pcm_accel_cmd
|
||||
|
||||
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_names, CP):
|
||||
super().__init__(dbc_names, CP)
|
||||
@@ -192,18 +233,23 @@ class CarController(CarControllerBase):
|
||||
self.secoc_prev_reset_counter = 0
|
||||
|
||||
self.doors_locked = False
|
||||
self.auto_brake_hold = bool(self.CP.flags & ToyotaFlags.AUTO_BRAKE_HOLD.value)
|
||||
self.brake_hold_active = False
|
||||
self._brake_hold_counter = 0
|
||||
self._brake_hold_reset = False
|
||||
self._prev_brake_pressed = False
|
||||
|
||||
def _compute_interceptor_gas_cmd(self, CC, CS):
|
||||
if not (self.CP.enableGasInterceptorDEPRECATED and self.CP.openpilotLongitudinalControl and CC.longActive):
|
||||
return 0.0
|
||||
|
||||
if self.CP.minEnableSpeed < 0.0:
|
||||
return 0.12 if CS.out.standstill and self.accel > 0.0 else 0.0
|
||||
if CS.out.standstill:
|
||||
return 0.12 if self.accel > 0.0 else 0.0
|
||||
|
||||
max_interceptor_gas = 0.5
|
||||
if self.CP.carFingerprint == CAR.TOYOTA_RAV4:
|
||||
pedal_scale = float(np.interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.15, 0.3, 0.0]))
|
||||
elif self.CP.carFingerprint == CAR.TOYOTA_COROLLA:
|
||||
elif self.CP.carFingerprint in (CAR.TOYOTA_COROLLA, CAR.TOYOTA_MATRIX_RETROFIT):
|
||||
pedal_scale = float(np.interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.3, 0.4, 0.0]))
|
||||
else:
|
||||
pedal_scale = float(np.interp(CS.out.vEgo, [0.0, MIN_ACC_SPEED, MIN_ACC_SPEED + PEDAL_TRANSITION], [0.4, 0.5, 0.0]))
|
||||
@@ -237,6 +283,27 @@ class CarController(CarControllerBase):
|
||||
|
||||
self.last_standstill = CS.out.standstill
|
||||
|
||||
def create_auto_brake_hold_messages(self, CS: structs.CarState, brake_hold_allowed_timer: int = 100):
|
||||
can_sends = []
|
||||
brake_hold_allowed = (CS.out.standstill and CS.out.cruiseState.available and
|
||||
not CS.out.gasPressed and not CS.out.cruiseState.enabled and
|
||||
CS.out.gearShifter not in (PARK, REVERSE))
|
||||
|
||||
if brake_hold_allowed:
|
||||
self._brake_hold_counter += 1
|
||||
self.brake_hold_active = self._brake_hold_counter > brake_hold_allowed_timer and not self._brake_hold_reset
|
||||
self._brake_hold_reset = not self._prev_brake_pressed and CS.out.brakePressed and not self._brake_hold_reset
|
||||
else:
|
||||
self._brake_hold_counter = 0
|
||||
self.brake_hold_active = False
|
||||
self._brake_hold_reset = False
|
||||
self._prev_brake_pressed = CS.out.brakePressed
|
||||
|
||||
if self.frame % 2 == 0:
|
||||
can_sends.append(toyotacan.create_brake_hold_command(self.packer, self.frame, CS.pre_collision_2, self.brake_hold_active))
|
||||
|
||||
return can_sends
|
||||
|
||||
def update(self, CC, CS, now_nanos, starpilot_toggles):
|
||||
actuators = CC.actuators
|
||||
stopping = actuators.longControlState == LongCtrlState.stopping
|
||||
@@ -330,6 +397,9 @@ class CarController(CarControllerBase):
|
||||
# *** gas and brake ***
|
||||
|
||||
self._update_standstill_request(CC, CS, actuators, starpilot_toggles)
|
||||
if self.auto_brake_hold:
|
||||
can_sends.extend(self.create_auto_brake_hold_messages(CS))
|
||||
|
||||
interceptor_gas_cmd = self._compute_interceptor_gas_cmd(CC, CS)
|
||||
|
||||
# handle UI messages
|
||||
@@ -390,7 +460,7 @@ class CarController(CarControllerBase):
|
||||
else:
|
||||
# constantly slowly unwind integral to recover from large temporary errors
|
||||
unwind_rate = ACCEL_PID_UNWIND
|
||||
if self.CP.carFingerprint == CAR.TOYOTA_PRIUS and pcm_accel_cmd * self.long_pid.i < 0.0:
|
||||
if is_ths_hybrid(self.CP) and pcm_accel_cmd * self.long_pid.i < 0.0:
|
||||
unwind_rate *= PRIUS_INTEGRAL_MISMATCH_UNWIND
|
||||
self.long_pid.i -= unwind_rate * float(np.sign(self.long_pid.i))
|
||||
|
||||
@@ -405,9 +475,11 @@ class CarController(CarControllerBase):
|
||||
|
||||
feedforward = pcm_accel_cmd
|
||||
if self.CP.carFingerprint == CAR.TOYOTA_PRIUS:
|
||||
# Keep Prius positive handoffs softer than the stock tune, while restoring some launch authority.
|
||||
if feedforward > 0.0:
|
||||
feedforward *= PRIUS_POSITIVE_FEEDFORWARD_SCALE
|
||||
feedforward = get_prius_feedforward(feedforward, CS.out.vEgo)
|
||||
elif is_camry_hybrid(self.CP) and feedforward > 0.0:
|
||||
# Preserve the established Camry Hybrid acceleration response while
|
||||
# allowing negative requests to track the planner at full scale.
|
||||
feedforward = get_camry_hybrid_feedforward(feedforward)
|
||||
|
||||
pcm_accel_cmd = self.long_pid.update(error_future,
|
||||
speed=CS.out.vEgo,
|
||||
@@ -429,14 +501,22 @@ class CarController(CarControllerBase):
|
||||
if self.CP.enableGasInterceptorDEPRECATED:
|
||||
pcm_accel_cmd = limit_interceptor_pcm_accel(pcm_accel_cmd, actuators.accel, stopping, CS.out.vEgo)
|
||||
pcm_accel_cmd = limit_interceptor_stopping_accel(pcm_accel_cmd, actuators.accel, stopping, CS.out.vEgo, bool(hud_control.leadVisible))
|
||||
elif self.CP.carFingerprint == CAR.TOYOTA_PRIUS:
|
||||
pcm_accel_cmd = limit_prius_stopping_accel(pcm_accel_cmd, actuators.accel, stopping, CS.out.vEgo, lead)
|
||||
else:
|
||||
pcm_accel_cmd = limit_no_lead_cruise_sign_flip(pcm_accel_cmd, actuators.accel, stopping, CS.out.vEgo,
|
||||
CS.out.cruiseState.speed, bool(hud_control.leadVisible))
|
||||
if self.CP.carFingerprint == CAR.TOYOTA_PRIUS:
|
||||
pcm_accel_cmd = limit_prius_stopping_accel(pcm_accel_cmd, actuators.accel, stopping, CS.out.vEgo, lead)
|
||||
|
||||
pcm_accel_cmd = float(np.clip(pcm_accel_cmd, self.params.ACCEL_MIN, self.params.ACCEL_MAX))
|
||||
|
||||
main_accel_cmd = 0. if self.CP.flags & ToyotaFlags.SECOC.value else pcm_accel_cmd
|
||||
# Toyota's physical distance-button hold can collide with StarPilot's wheel-button
|
||||
# actions and trip a temporary EPS fault. Suppress native long-press handling while
|
||||
# the physical gap button is held so ACC only sees the hold as a plain button press.
|
||||
allow_long_press = 0 if bool(getattr(CS, "distance_button", False)) else None
|
||||
can_sends.append(toyotacan.create_accel_command(self.packer, main_accel_cmd, pcm_cancel_cmd, self.permit_braking, self.standstill_req, lead,
|
||||
CS.acc_type, fcw_alert, self.distance_button, starpilot_toggles.reverse_cruise_increase))
|
||||
CS.acc_type, fcw_alert, self.distance_button, starpilot_toggles.reverse_cruise_increase,
|
||||
allow_long_press))
|
||||
if self.CP.flags & ToyotaFlags.SECOC.value:
|
||||
acc_cmd_2 = toyotacan.create_accel_command_2(self.packer, pcm_accel_cmd)
|
||||
acc_cmd_2 = add_mac(self.secoc_key,
|
||||
@@ -455,7 +535,9 @@ class CarController(CarControllerBase):
|
||||
if self.CP.carFingerprint in UNSUPPORTED_DSU_CAR:
|
||||
can_sends.append(toyotacan.create_acc_cancel_command(self.packer))
|
||||
else:
|
||||
can_sends.append(toyotacan.create_accel_command(self.packer, 0, pcm_cancel_cmd, True, False, lead, CS.acc_type, False, self.distance_button, starpilot_toggles.reverse_cruise_increase))
|
||||
allow_long_press = 0 if bool(getattr(CS, "distance_button", False)) else None
|
||||
can_sends.append(toyotacan.create_accel_command(self.packer, 0, pcm_cancel_cmd, True, False, lead, CS.acc_type, False,
|
||||
self.distance_button, starpilot_toggles.reverse_cruise_increase, allow_long_press))
|
||||
|
||||
# *** hud ui ***
|
||||
if self.CP.carFingerprint != CAR.TOYOTA_PRIUS_V:
|
||||
|
||||
@@ -23,6 +23,7 @@ TEMP_STEER_FAULTS = (0, 9, 11, 21, 25)
|
||||
# - lka/lta msg drop out: 3 (recoverable)
|
||||
# - prolonged high driver torque: 17 (permanent)
|
||||
PERM_STEER_FAULTS = (3, 17)
|
||||
LKAS_BUTTON_CAR = TSS2_CAR | {CAR.TOYOTA_PRIUS}
|
||||
|
||||
|
||||
# Traffic signals for Speed Limit Controller - Credit goes to the DragonPilot team!
|
||||
@@ -43,6 +44,13 @@ def calculate_interceptor_gas_pressed(cp) -> bool:
|
||||
return interceptor_gas > 805
|
||||
|
||||
|
||||
def create_lkas_button_events(lkas_button: int, prev_lkas_button: int) -> list[structs.CarState.ButtonEvent]:
|
||||
if lkas_button != 0 and lkas_button != prev_lkas_button:
|
||||
return (create_button_events(1, 0, {1: ButtonType.lkas}) +
|
||||
create_button_events(0, 1, {1: ButtonType.lkas}))
|
||||
return []
|
||||
|
||||
|
||||
class CarState(CarStateBase):
|
||||
def __init__(self, CP, FPCP):
|
||||
super().__init__(CP, FPCP)
|
||||
@@ -80,13 +88,16 @@ class CarState(CarStateBase):
|
||||
self.has_can_filter = self.FPCP.flags & ToyotaStarPilotFlags.RADAR_CAN_FILTER.value
|
||||
self.has_SDSU = self.FPCP.flags & ToyotaStarPilotFlags.SMART_DSU.value
|
||||
self.has_ZSS = self.FPCP.flags & ToyotaStarPilotFlags.ZSS.value
|
||||
self.auto_brake_hold = bool(self.CP.flags & ToyotaFlags.AUTO_BRAKE_HOLD.value)
|
||||
self.pre_collision_2 = {}
|
||||
|
||||
def update(self, can_parsers, starpilot_toggles) -> structs.CarState:
|
||||
cp = can_parsers[Bus.pt]
|
||||
cp_cam = can_parsers[Bus.cam]
|
||||
|
||||
ret = structs.CarState()
|
||||
cp_acc = cp_cam if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR) else cp
|
||||
dsu_bypass = bool(self.CP.flags & ToyotaFlags.DSU_BYPASS.value)
|
||||
cp_acc = cp_cam if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR) or dsu_bypass else cp
|
||||
|
||||
if not self.CP.flags & ToyotaFlags.SECOC.value:
|
||||
self.gvc = cp.vl["VSC1S07"]["GVC"]
|
||||
@@ -178,7 +189,7 @@ class CarState(CarStateBase):
|
||||
conversion_factor = CV.KPH_TO_MS if is_metric else CV.MPH_TO_MS
|
||||
ret.cruiseState.speedCluster = cluster_set_speed * conversion_factor
|
||||
|
||||
if self.CP.carFingerprint in TSS2_CAR and not self.CP.flags & ToyotaFlags.DISABLE_RADAR.value:
|
||||
if dsu_bypass or (self.CP.carFingerprint in TSS2_CAR and not self.CP.flags & ToyotaFlags.DISABLE_RADAR.value):
|
||||
# smartDSU can intercept ACC_CONTROL, so don't require it when it's no
|
||||
# longer forwarded on the PT bus.
|
||||
if not self.has_SDSU:
|
||||
@@ -211,20 +222,19 @@ class CarState(CarStateBase):
|
||||
if self.CP.carFingerprint != CAR.TOYOTA_PRIUS_V:
|
||||
self.lkas_hud = copy.copy(cp_cam.vl["LKAS_HUD"])
|
||||
|
||||
if self.auto_brake_hold:
|
||||
self.pre_collision_2 = copy.copy(cp_cam.vl["PRE_COLLISION_2"])
|
||||
|
||||
if self.CP.carFingerprint not in UNSUPPORTED_DSU_CAR:
|
||||
self.pcm_follow_distance = cp.vl["PCM_CRUISE_2"]["PCM_FOLLOW_DISTANCE"]
|
||||
|
||||
buttonEvents = []
|
||||
if self.CP.carFingerprint in TSS2_CAR:
|
||||
# lkas button is wired to the camera
|
||||
if self.CP.carFingerprint in LKAS_BUTTON_CAR:
|
||||
prev_lkas_button = self.lkas_button
|
||||
self.lkas_button = cp_cam.vl["LKAS_HUD"]["LDA_ON_MESSAGE"]
|
||||
buttonEvents += create_lkas_button_events(self.lkas_button, prev_lkas_button)
|
||||
|
||||
# Cycles between 1 and 2 when pressing the button, then rests back at 0 after ~3s
|
||||
if self.lkas_button != 0 and self.lkas_button != prev_lkas_button:
|
||||
buttonEvents.extend(create_button_events(1, 0, {1: ButtonType.lkas}) +
|
||||
create_button_events(0, 1, {1: ButtonType.lkas}))
|
||||
|
||||
if self.CP.carFingerprint in TSS2_CAR:
|
||||
if self.CP.carFingerprint not in (RADAR_ACC_CAR | SECOC_CAR):
|
||||
# distance button is wired to the ACC module (camera or radar)
|
||||
prev_distance_button = self.distance_button
|
||||
|
||||
@@ -4,6 +4,10 @@ from opendbc.car.toyota.values import CAR
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
FINGERPRINTS = {
|
||||
CAR.TOYOTA_MATRIX_RETROFIT: [{}],
|
||||
}
|
||||
|
||||
FW_VERSIONS = {
|
||||
CAR.TOYOTA_AVALON: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
|
||||
@@ -2,11 +2,13 @@ from opendbc.car import Bus, structs, get_safety_config, uds
|
||||
from opendbc.car.toyota.carstate import CarState
|
||||
from opendbc.car.toyota.carcontroller import CarController
|
||||
from opendbc.car.toyota.radar_interface import RadarInterface
|
||||
from opendbc.car.toyota.values import Ecu, CAR, DBC, ToyotaFlags, CarControllerParams, TSS2_CAR, RADAR_ACC_CAR, NO_DSU_CAR, \
|
||||
from opendbc.car.toyota.values import Ecu, CAR, DBC, ToyotaFlags, CarControllerParams, TSS2_CAR, RADAR_ACC_CAR, SECOC_CAR, NO_DSU_CAR, \
|
||||
MIN_ACC_SPEED, EPS_SCALE, NO_STOP_TIMER_CAR, ANGLE_CONTROL_CAR, \
|
||||
ToyotaSafetyFlags
|
||||
from opendbc.car.disable_ecu import disable_ecu
|
||||
from opendbc.car.interfaces import CarInterfaceBase
|
||||
from opendbc.safety import ALTERNATIVE_EXPERIENCE
|
||||
from openpilot.common.params import Params
|
||||
|
||||
SteerControlType = structs.CarParams.SteerControlType
|
||||
|
||||
@@ -26,6 +28,9 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.toyota)]
|
||||
ret.safetyConfigs[0].safetyParam = EPS_SCALE[candidate]
|
||||
|
||||
if candidate == CAR.LEXUS_IS:
|
||||
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.ALT_CRUISE.value
|
||||
|
||||
# BRAKE_MODULE is on a different address for these cars
|
||||
if DBC[candidate][Bus.pt] == "toyota_new_mc_pt_generated":
|
||||
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.ALT_BRAKE.value
|
||||
@@ -58,6 +63,17 @@ class CarInterface(CarInterfaceBase):
|
||||
# sDSU / radar filter hardware needs the Toyota safety long-filter TX set.
|
||||
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.LONG_FILTER.value
|
||||
|
||||
# A DSU bypass adapter reroutes the stock DSU messages to the camera bus.
|
||||
# These messages are normally absent there on pre-TSS2 platforms.
|
||||
camera_fingerprint = fingerprint.get(2, {})
|
||||
has_dsu_bypass = 0x343 in camera_fingerprint or 0x4CB in camera_fingerprint
|
||||
if candidate == CAR.LEXUS_IS:
|
||||
# The IS mirrors its native buses onto camera bus during startup without a bypass adapter.
|
||||
has_dsu_bypass = ((0x343 in camera_fingerprint and 0x343 not in fingerprint.get(1, {})) or
|
||||
(0x4CB in camera_fingerprint and 0x4CB not in fingerprint.get(0, {})))
|
||||
if not use_sdsu and candidate not in TSS2_CAR and has_dsu_bypass:
|
||||
ret.flags |= ToyotaFlags.DSU_BYPASS.value
|
||||
|
||||
# In TSS2 cars, the camera does long control
|
||||
found_ecus = [fw.ecu for fw in car_fw]
|
||||
|
||||
@@ -66,10 +82,11 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
if candidate == CAR.TOYOTA_PRIUS:
|
||||
stop_and_go = True
|
||||
ret.flags |= ToyotaFlags.HYBRID.value
|
||||
# Only give steer angle deadzone to for bad angle sensor prius
|
||||
for fw in car_fw:
|
||||
if fw.ecu == "eps" and not fw.fwVersion == b'8965B47060\x00\x00\x00\x00\x00\x00':
|
||||
ret.steerActuatorDelay = 0.25
|
||||
ret.steerActuatorDelay = 0.14
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning, steering_angle_deadzone_deg=0.3)
|
||||
|
||||
elif candidate in (CAR.LEXUS_RX, CAR.LEXUS_RX_TSS2):
|
||||
@@ -110,7 +127,9 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
# No radar dbc for cars without DSU which are not TSS 2.0
|
||||
# TODO: make an adas dbc file for dsu-less models
|
||||
ret.radarUnavailable = Bus.radar not in DBC[candidate] or candidate in (NO_DSU_CAR - TSS2_CAR)
|
||||
ret.radarUnavailable = Bus.radar not in DBC[candidate] or candidate in (NO_DSU_CAR - TSS2_CAR - {CAR.TOYOTA_CAMRY})
|
||||
if candidate == CAR.TOYOTA_CAMRY:
|
||||
ret.radarTimeStepDEPRECATED = 0.1
|
||||
|
||||
# Since we don't yet parse radar on TSS2/TSS-P radar-based ACC cars, gate
|
||||
# longitudinal behind the alpha-long toggle.
|
||||
@@ -132,11 +151,19 @@ class CarInterface(CarInterfaceBase):
|
||||
# - TSS2 radar ACC cars (disables radar)
|
||||
|
||||
ret.openpilotLongitudinalControl = (use_sdsu or
|
||||
bool(ret.flags & ToyotaFlags.DSU_BYPASS.value) or
|
||||
candidate in (TSS2_CAR - RADAR_ACC_CAR) or
|
||||
bool(ret.flags & ToyotaFlags.DISABLE_RADAR.value))
|
||||
|
||||
ret.autoResumeSng = ret.openpilotLongitudinalControl and candidate in NO_STOP_TIMER_CAR
|
||||
ret.enableGasInterceptorDEPRECATED = 0x201 in fingerprint[0] and ret.openpilotLongitudinalControl
|
||||
if ret.enableGasInterceptorDEPRECATED:
|
||||
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.GAS_INTERCEPTOR.value
|
||||
|
||||
toyota_auto_hold = Params(return_defaults=True).get_bool("ToyotaAutoHold")
|
||||
if toyota_auto_hold and candidate in (TSS2_CAR - RADAR_ACC_CAR - SECOC_CAR):
|
||||
ret.alternativeExperience |= ALTERNATIVE_EXPERIENCE.ALLOW_AEB
|
||||
ret.flags |= ToyotaFlags.AUTO_BRAKE_HOLD.value
|
||||
|
||||
if not ret.openpilotLongitudinalControl:
|
||||
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
|
||||
@@ -145,7 +172,11 @@ class CarInterface(CarInterfaceBase):
|
||||
# to a negative value, so it won't matter.
|
||||
ret.minEnableSpeed = -1. if (stop_and_go or ret.enableGasInterceptorDEPRECATED) else MIN_ACC_SPEED
|
||||
|
||||
if candidate in TSS2_CAR or ret.enableGasInterceptorDEPRECATED:
|
||||
prius_long_defaults = candidate == CAR.TOYOTA_PRIUS and ret.openpilotLongitudinalControl
|
||||
camry_hybrid_long_defaults = (candidate == CAR.TOYOTA_CAMRY and ret.openpilotLongitudinalControl and
|
||||
bool(ret.flags & ToyotaFlags.HYBRID.value))
|
||||
|
||||
if candidate in TSS2_CAR or ret.enableGasInterceptorDEPRECATED or prius_long_defaults:
|
||||
ret.flags |= ToyotaFlags.RAISED_ACCEL_LIMIT.value
|
||||
|
||||
ret.vEgoStopping = 0.25
|
||||
@@ -156,6 +187,13 @@ class CarInterface(CarInterfaceBase):
|
||||
if ret.flags & ToyotaFlags.HYBRID.value:
|
||||
ret.longitudinalActuatorDelay = 0.05
|
||||
|
||||
if camry_hybrid_long_defaults:
|
||||
# The THS eCVT responds much faster than the legacy non-TSS2 ICE tune.
|
||||
ret.longitudinalActuatorDelay = 0.05
|
||||
ret.vEgoStopping = 0.25
|
||||
ret.vEgoStarting = 0.25
|
||||
ret.stoppingDecelRate = 0.3
|
||||
|
||||
if ret.enableGasInterceptorDEPRECATED:
|
||||
# Pedal/SDSU Toyotas feel best with a softer final stop clamp.
|
||||
ret.longitudinalActuatorDelay = max(ret.longitudinalActuatorDelay, 0.2)
|
||||
|
||||
@@ -2,9 +2,14 @@
|
||||
from opendbc.can import CANParser
|
||||
from opendbc.car import Bus
|
||||
from opendbc.car.structs import RadarData
|
||||
from opendbc.car.toyota.values import DBC, TSS2_CAR
|
||||
from opendbc.car.toyota.values import CAR, DBC, TSS2_CAR
|
||||
from opendbc.car.interfaces import RadarInterfaceBase
|
||||
|
||||
RADAR_ACC_TSSP_CAR = {CAR.TOYOTA_CAMRY}
|
||||
TSSP_CLUSTER_MSGS = list(range(0x680, 0x686))
|
||||
KPH_TO_MS = 1. / 3.6
|
||||
TSSP_RADAR_EGO_SPEED_SCALE = 0.922
|
||||
|
||||
|
||||
def _create_radar_can_parser(car_fingerprint):
|
||||
if car_fingerprint in TSS2_CAR:
|
||||
@@ -21,22 +26,37 @@ def _create_radar_can_parser(car_fingerprint):
|
||||
return CANParser(DBC[car_fingerprint][Bus.radar], messages, 1)
|
||||
|
||||
|
||||
def _create_tssp_radar_can_parser(car_fingerprint):
|
||||
return CANParser(DBC[car_fingerprint][Bus.radar], [(addr, 10) for addr in TSSP_CLUSTER_MSGS], 1)
|
||||
|
||||
|
||||
def _create_wheel_speed_can_parser(car_fingerprint):
|
||||
return CANParser(DBC[car_fingerprint][Bus.pt], [("WHEEL_SPEEDS", 80)], 0)
|
||||
|
||||
|
||||
class RadarInterface(RadarInterfaceBase):
|
||||
def __init__(self, CP):
|
||||
super().__init__(CP)
|
||||
self.track_id = 0
|
||||
self.radar_acc_tssp = CP.carFingerprint in RADAR_ACC_TSSP_CAR
|
||||
|
||||
if CP.carFingerprint in TSS2_CAR:
|
||||
self.RADAR_A_MSGS = list(range(0x180, 0x190))
|
||||
self.RADAR_B_MSGS = list(range(0x190, 0x1a0))
|
||||
if self.radar_acc_tssp:
|
||||
self.RADAR_MSGS = TSSP_CLUSTER_MSGS
|
||||
self.rcp = None if CP.radarUnavailable else _create_tssp_radar_can_parser(CP.carFingerprint)
|
||||
self.pt_cp = None if CP.radarUnavailable else _create_wheel_speed_can_parser(CP.carFingerprint)
|
||||
self.trigger_msg = self.RADAR_MSGS[-1]
|
||||
else:
|
||||
self.RADAR_A_MSGS = list(range(0x210, 0x220))
|
||||
self.RADAR_B_MSGS = list(range(0x220, 0x230))
|
||||
if CP.carFingerprint in TSS2_CAR:
|
||||
self.RADAR_A_MSGS = list(range(0x180, 0x190))
|
||||
self.RADAR_B_MSGS = list(range(0x190, 0x1a0))
|
||||
else:
|
||||
self.RADAR_A_MSGS = list(range(0x210, 0x220))
|
||||
self.RADAR_B_MSGS = list(range(0x220, 0x230))
|
||||
self.valid_cnt = {key: 0 for key in self.RADAR_A_MSGS}
|
||||
self.rcp = None if CP.radarUnavailable else _create_radar_can_parser(CP.carFingerprint)
|
||||
self.pt_cp = None
|
||||
self.trigger_msg = self.RADAR_B_MSGS[-1]
|
||||
|
||||
self.valid_cnt = {key: 0 for key in self.RADAR_A_MSGS}
|
||||
|
||||
self.rcp = None if CP.radarUnavailable else _create_radar_can_parser(CP.carFingerprint)
|
||||
self.trigger_msg = self.RADAR_B_MSGS[-1]
|
||||
self.updated_messages = set()
|
||||
|
||||
def update(self, can_strings):
|
||||
@@ -45,16 +65,72 @@ class RadarInterface(RadarInterfaceBase):
|
||||
|
||||
vls = self.rcp.update(can_strings)
|
||||
self.updated_messages.update(vls)
|
||||
if self.pt_cp is not None:
|
||||
self.pt_cp.update(can_strings)
|
||||
|
||||
if self.trigger_msg not in self.updated_messages:
|
||||
return None
|
||||
|
||||
if self.pt_cp is not None and not self.pt_cp.can_valid:
|
||||
self.updated_messages.clear()
|
||||
ret = RadarData()
|
||||
ret.errors.canError = True
|
||||
return ret
|
||||
|
||||
rr = self._update(self.updated_messages)
|
||||
self.updated_messages.clear()
|
||||
|
||||
return rr
|
||||
|
||||
def _get_v_ego(self):
|
||||
ws = self.pt_cp.vl["WHEEL_SPEEDS"]
|
||||
wheel_speed = (ws["WHEEL_SPEED_FL"] + ws["WHEEL_SPEED_FR"] +
|
||||
ws["WHEEL_SPEED_RL"] + ws["WHEEL_SPEED_RR"]) / 4.
|
||||
return wheel_speed * KPH_TO_MS * self.CP.wheelSpeedFactor
|
||||
|
||||
def _update_tssp(self, updated_messages):
|
||||
ret = RadarData()
|
||||
if not self.rcp.can_valid:
|
||||
ret.errors.canError = True
|
||||
|
||||
v_ego = self._get_v_ego()
|
||||
updated_ids = set()
|
||||
for ii in sorted(updated_messages):
|
||||
if ii not in self.RADAR_MSGS:
|
||||
continue
|
||||
|
||||
cpt = self.rcp.vl[ii]
|
||||
track_id = int(cpt["ID"])
|
||||
if track_id == 0x3f or cpt["LONG_DIST"] <= 0:
|
||||
continue
|
||||
|
||||
updated_ids.add(track_id)
|
||||
if track_id not in self.pts:
|
||||
self.pts[track_id] = RadarData.RadarPoint()
|
||||
self.pts[track_id].trackId = self.track_id
|
||||
self.track_id += 1
|
||||
|
||||
self.pts[track_id].dRel = float(cpt["LONG_DIST"])
|
||||
self.pts[track_id].yRel = -float(cpt["LAT_DIST"])
|
||||
self.pts[track_id].vRel = float(cpt["SPEED"]) - v_ego * TSSP_RADAR_EGO_SPEED_SCALE
|
||||
self.pts[track_id].aRel = float("nan")
|
||||
self.pts[track_id].yvRel = float(cpt["LAT_SPEED"])
|
||||
self.pts[track_id].measured = True
|
||||
|
||||
for track_id in list(self.pts):
|
||||
if track_id not in updated_ids:
|
||||
del self.pts[track_id]
|
||||
|
||||
ret.points = list(self.pts.values())
|
||||
return ret
|
||||
|
||||
def _update(self, updated_messages):
|
||||
if self.radar_acc_tssp:
|
||||
return self._update_tssp(updated_messages)
|
||||
|
||||
return self._update_denso(updated_messages)
|
||||
|
||||
def _update_denso(self, updated_messages):
|
||||
ret = RadarData()
|
||||
if not self.rcp.can_valid:
|
||||
ret.errors.canError = True
|
||||
|
||||
@@ -1,20 +1,27 @@
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
from hypothesis import given, settings, strategies as st
|
||||
|
||||
from opendbc.car import Bus
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.can import CANPacker, CANParser
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.fw_versions import build_fw_dict
|
||||
from opendbc.car.toyota import toyotacan
|
||||
from opendbc.car.toyota.carcontroller import CarController, limit_interceptor_pcm_accel, limit_interceptor_stopping_accel, \
|
||||
from opendbc.car.toyota.carcontroller import CarController, get_camry_hybrid_feedforward, get_long_tune, get_prius_feedforward, \
|
||||
get_prius_positive_feedforward_scale, \
|
||||
limit_interceptor_pcm_accel, \
|
||||
limit_interceptor_stopping_accel, limit_no_lead_cruise_sign_flip, \
|
||||
limit_prius_stopping_accel, update_permit_braking
|
||||
from opendbc.car.toyota.carstate import calculate_interceptor_gas_pressed
|
||||
from opendbc.car.toyota.carstate import CarState, LKAS_BUTTON_CAR, calculate_interceptor_gas_pressed, create_lkas_button_events
|
||||
from opendbc.car.toyota.fingerprints import FW_VERSIONS
|
||||
from opendbc.car.toyota.interface import CarInterface
|
||||
from opendbc.car.toyota.radar_interface import RadarInterface, TSSP_RADAR_EGO_SPEED_SCALE
|
||||
from opendbc.car.toyota.values import CAR, DBC, TSS2_CAR, ANGLE_CONTROL_CAR, RADAR_ACC_CAR, SECOC_CAR, \
|
||||
FW_QUERY_CONFIG, PLATFORM_CODE_ECUS, FUZZY_EXCLUDED_PLATFORMS, \
|
||||
get_platform_codes
|
||||
ToyotaFlags, ToyotaSafetyFlags, get_platform_codes
|
||||
from opendbc.safety import ALTERNATIVE_EXPERIENCE
|
||||
from openpilot.common.params import Params
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
@@ -33,6 +40,23 @@ class TestToyotaInterfaces:
|
||||
# At this time, only RAV4 2023 is expected to use LTA/angle control
|
||||
assert ANGLE_CONTROL_CAR == {CAR.TOYOTA_RAV4_TSS2_2023}
|
||||
|
||||
def test_rav4_prime_force_torque_controller(self):
|
||||
fingerprint = {bus: {} for bus in range(8)}
|
||||
|
||||
default_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_RAV4_PRIME, fingerprint, [], False, False, False,
|
||||
SimpleNamespace(force_torque_controller=False, nnff=False, nnff_lite=False),
|
||||
)
|
||||
forced_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_RAV4_PRIME, fingerprint, [], False, False, False,
|
||||
SimpleNamespace(force_torque_controller=True, nnff=False, nnff_lite=False),
|
||||
)
|
||||
|
||||
assert default_params.lateralTuning.which() == "pid"
|
||||
assert forced_params.lateralTuning.which() == "torque"
|
||||
assert forced_params.lateralTuning.torque.latAccelFactor == pytest.approx(1.7)
|
||||
assert forced_params.lateralTuning.torque.friction == pytest.approx(0.14)
|
||||
|
||||
def test_tss2_dbc(self):
|
||||
# We make some assumptions about TSS2 platforms,
|
||||
# like looking up certain signals only in this DBC
|
||||
@@ -40,6 +64,226 @@ class TestToyotaInterfaces:
|
||||
if car_model in TSS2_CAR and car_model not in SECOC_CAR:
|
||||
assert dbc[Bus.pt] == "toyota_nodsu_pt_generated"
|
||||
|
||||
def test_auto_hold_sets_flag_on_supported_tss2(self):
|
||||
params = Params()
|
||||
try:
|
||||
params.put_bool("ToyotaAutoHold", True)
|
||||
car_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_CAMRY_TSS2,
|
||||
{bus: {} for bus in range(8)},
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
finally:
|
||||
params.remove("ToyotaAutoHold")
|
||||
|
||||
assert car_params.flags & ToyotaFlags.AUTO_BRAKE_HOLD.value
|
||||
assert car_params.alternativeExperience & ALTERNATIVE_EXPERIENCE.ALLOW_AEB
|
||||
|
||||
def test_prius_openpilot_long_uses_hybrid_long_defaults(self):
|
||||
car_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_PRIUS,
|
||||
{0: {0x2FF: 8}},
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
|
||||
assert car_params.openpilotLongitudinalControl
|
||||
assert car_params.flags & ToyotaFlags.HYBRID.value
|
||||
assert car_params.flags & ToyotaFlags.RAISED_ACCEL_LIMIT.value
|
||||
assert abs(car_params.longitudinalActuatorDelay - 0.05) < 1e-6
|
||||
assert abs(car_params.vEgoStopping - 0.25) < 1e-6
|
||||
assert abs(car_params.vEgoStarting - 0.25) < 1e-6
|
||||
|
||||
@pytest.mark.parametrize("camera_message", [0x343, 0x4CB])
|
||||
def test_dsu_bypass_enables_longitudinal(self, camera_message):
|
||||
fingerprint = {bus: {} for bus in range(8)}
|
||||
fingerprint[2][camera_message] = 8
|
||||
|
||||
car_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_COROLLA,
|
||||
fingerprint,
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
|
||||
assert car_params.flags & ToyotaFlags.DSU_BYPASS.value
|
||||
assert car_params.openpilotLongitudinalControl
|
||||
assert not car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
|
||||
|
||||
starpilot_params = CarInterface.get_starpilot_params(
|
||||
CAR.TOYOTA_COROLLA, fingerprint, [], car_params, SimpleNamespace(),
|
||||
)
|
||||
car_state = CarState(car_params, starpilot_params)
|
||||
can_parsers = car_state.get_can_parsers(car_params)
|
||||
car_state.update(can_parsers, SimpleNamespace(cluster_offset=1.0))
|
||||
|
||||
for message in ("ACC_CONTROL", "PRE_COLLISION", "PCS_HUD"):
|
||||
assert message not in can_parsers[Bus.pt].vl
|
||||
assert message in can_parsers[Bus.cam].vl
|
||||
|
||||
@pytest.mark.parametrize(("native_bus", "message"), [(1, 0x343), (0, 0x4CB)])
|
||||
def test_dsu_bypass_ignores_startup_bus_mirror(self, native_bus, message):
|
||||
fingerprint = {bus: {} for bus in range(8)}
|
||||
fingerprint[native_bus][message] = 8
|
||||
fingerprint[2][message] = 8
|
||||
|
||||
car_params = CarInterface.get_params(
|
||||
CAR.LEXUS_IS,
|
||||
fingerprint,
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
|
||||
assert not car_params.flags & ToyotaFlags.DSU_BYPASS.value
|
||||
assert not car_params.openpilotLongitudinalControl
|
||||
assert car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
|
||||
assert car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.ALT_CRUISE.value
|
||||
|
||||
@pytest.mark.parametrize(("native_bus", "message"), [(1, 0x343), (0, 0x4CB)])
|
||||
def test_prius_dsu_bypass_allows_native_bus_message(self, native_bus, message):
|
||||
fingerprint = {bus: {} for bus in range(8)}
|
||||
fingerprint[native_bus][message] = 8
|
||||
fingerprint[2][message] = 8
|
||||
|
||||
car_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_PRIUS,
|
||||
fingerprint,
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
|
||||
assert car_params.flags & ToyotaFlags.DSU_BYPASS.value
|
||||
assert car_params.openpilotLongitudinalControl
|
||||
assert not car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
|
||||
assert not car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.ALT_CRUISE.value
|
||||
|
||||
def test_dsu_bypass_does_not_change_tss2_or_smart_dsu(self):
|
||||
fingerprint = {bus: {} for bus in range(8)}
|
||||
fingerprint[0][0x2FF] = 8
|
||||
fingerprint[2][0x343] = 8
|
||||
|
||||
smart_dsu_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_COROLLA,
|
||||
fingerprint,
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
tss2_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_CAMRY_TSS2,
|
||||
fingerprint,
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
|
||||
assert not smart_dsu_params.flags & ToyotaFlags.DSU_BYPASS.value
|
||||
assert not tss2_params.flags & ToyotaFlags.DSU_BYPASS.value
|
||||
|
||||
def test_camry_hybrid_continental_radar_uses_ths_longitudinal_tune(self):
|
||||
fingerprint = {bus: ({0x2FF: 8} if bus == 0 else {}) for bus in range(8)}
|
||||
hybrid_fw = [CarParams.CarFw(ecu=Ecu.hybrid, address=0x7D2, fwVersion=b"test")]
|
||||
car_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_CAMRY,
|
||||
fingerprint,
|
||||
hybrid_fw,
|
||||
alpha_long=True,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
|
||||
assert car_params.openpilotLongitudinalControl
|
||||
assert not car_params.radarUnavailable
|
||||
assert abs(car_params.radarTimeStepDEPRECATED - 0.1) < 1e-6
|
||||
assert abs(car_params.longitudinalActuatorDelay - 0.05) < 1e-6
|
||||
assert abs(car_params.vEgoStopping - 0.25) < 1e-6
|
||||
assert abs(car_params.vEgoStarting - 0.25) < 1e-6
|
||||
assert abs(car_params.stoppingDecelRate - 0.3) < 1e-6
|
||||
assert not car_params.flags & ToyotaFlags.NO_STOP_TIMER.value
|
||||
|
||||
controller = get_long_tune(car_params, SimpleNamespace(ACCEL_MIN=-3.5, ACCEL_MAX=2.0))
|
||||
controller.speed = 0.0
|
||||
assert controller.k_i == pytest.approx(0.5)
|
||||
assert controller.k_f == pytest.approx(1.0)
|
||||
|
||||
radar_interface = RadarInterface(car_params)
|
||||
assert radar_interface.radar_acc_tssp
|
||||
assert radar_interface.rcp is not None
|
||||
assert radar_interface.pt_cp is not None
|
||||
|
||||
def test_camry_ice_keeps_legacy_longitudinal_tune(self):
|
||||
fingerprint = {bus: ({0x2FF: 8} if bus == 0 else {}) for bus in range(8)}
|
||||
car_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_CAMRY,
|
||||
fingerprint,
|
||||
[],
|
||||
alpha_long=True,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
|
||||
assert not car_params.flags & ToyotaFlags.HYBRID.value
|
||||
assert car_params.longitudinalActuatorDelay == pytest.approx(0.15)
|
||||
assert car_params.vEgoStopping == pytest.approx(0.5)
|
||||
assert car_params.stoppingDecelRate == pytest.approx(0.8)
|
||||
|
||||
controller = get_long_tune(car_params, SimpleNamespace(ACCEL_MIN=-3.5, ACCEL_MAX=2.0))
|
||||
controller.speed = 0.0
|
||||
assert controller.k_i == pytest.approx(3.6)
|
||||
assert controller.k_f == pytest.approx(1.0)
|
||||
|
||||
def test_camry_continental_radar_converts_absolute_target_speed(self):
|
||||
radar_interface = RadarInterface.__new__(RadarInterface)
|
||||
radar_interface.CP = SimpleNamespace(wheelSpeedFactor=1.0)
|
||||
radar_interface.pts = {}
|
||||
radar_interface.track_id = 0
|
||||
radar_interface.RADAR_MSGS = [0x680]
|
||||
radar_interface.pt_cp = SimpleNamespace(vl={
|
||||
"WHEEL_SPEEDS": {
|
||||
"WHEEL_SPEED_FL": 36.0,
|
||||
"WHEEL_SPEED_FR": 36.0,
|
||||
"WHEEL_SPEED_RL": 36.0,
|
||||
"WHEEL_SPEED_RR": 36.0,
|
||||
},
|
||||
})
|
||||
radar_interface.rcp = SimpleNamespace(can_valid=True, vl={
|
||||
0x680: {
|
||||
"ID": 7,
|
||||
"LONG_DIST": 40.0,
|
||||
"LAT_DIST": -0.2,
|
||||
"SPEED": 11.0,
|
||||
"LAT_SPEED": 0.1,
|
||||
},
|
||||
})
|
||||
|
||||
radar_data = radar_interface._update_tssp({0x680})
|
||||
|
||||
assert len(radar_data.points) == 1
|
||||
assert radar_data.points[0].dRel == 40.0
|
||||
assert radar_data.points[0].vRel == pytest.approx(11.0 - 10.0 * TSSP_RADAR_EGO_SPEED_SCALE)
|
||||
|
||||
def test_essential_ecus(self, subtests):
|
||||
# Asserts standard ECUs exist for each platform
|
||||
common_ecus = {Ecu.fwdRadar, Ecu.fwdCamera}
|
||||
@@ -253,6 +497,18 @@ class TestToyotaCarController:
|
||||
assert update_permit_braking(False, 0.10, True, True, 25.0, False) is True
|
||||
assert update_permit_braking(False, 0.10, False, False, 25.0, False) is True
|
||||
|
||||
def test_no_lead_cruise_sign_flip_clamps_negative_pulse_when_set_speed_is_ahead(self):
|
||||
limited = limit_no_lead_cruise_sign_flip(-0.44, 0.0, False, 23.3, 25.0, False)
|
||||
assert limited == 0.0
|
||||
|
||||
def test_no_lead_cruise_sign_flip_keeps_real_decel_requests(self):
|
||||
limited = limit_no_lead_cruise_sign_flip(-0.44, -0.15, False, 23.3, 25.0, False)
|
||||
assert limited == -0.44
|
||||
|
||||
def test_no_lead_cruise_sign_flip_keeps_lead_follow_brake(self):
|
||||
limited = limit_no_lead_cruise_sign_flip(-0.44, 0.0, False, 23.3, 25.0, True)
|
||||
assert limited == -0.44
|
||||
|
||||
def test_prius_stopping_accel_unwinds_stale_stop_hold(self):
|
||||
limited = limit_prius_stopping_accel(-3.28, -0.05, True, 0.0, True)
|
||||
assert -1.5 < limited < 0.0
|
||||
@@ -261,6 +517,22 @@ class TestToyotaCarController:
|
||||
limited = limit_prius_stopping_accel(-3.28, -2.0, True, 0.0, True)
|
||||
assert limited == -3.28
|
||||
|
||||
def test_prius_positive_feedforward_scale_stays_soft_at_launch_speed(self):
|
||||
assert abs(get_prius_positive_feedforward_scale(0.0) - 0.7) < 1e-6
|
||||
assert abs(get_prius_positive_feedforward_scale(8.0) - 0.7) < 1e-6
|
||||
|
||||
def test_prius_positive_feedforward_scale_restores_cruise_authority(self):
|
||||
assert get_prius_positive_feedforward_scale(20.0) > get_prius_positive_feedforward_scale(8.0)
|
||||
assert abs(get_prius_positive_feedforward_scale(20.0) - 1.0) < 1e-6
|
||||
|
||||
def test_prius_feedforward_adds_braking_authority_without_changing_acceleration(self):
|
||||
assert get_prius_feedforward(-2.0, 8.0) == pytest.approx(-2.25)
|
||||
assert get_prius_feedforward(1.0, 8.0) == pytest.approx(0.7)
|
||||
|
||||
def test_camry_hybrid_feedforward_only_softens_acceleration(self):
|
||||
assert get_camry_hybrid_feedforward(1.0) == pytest.approx(0.8)
|
||||
assert get_camry_hybrid_feedforward(-2.0) == pytest.approx(-2.0)
|
||||
|
||||
def test_sng_hack_clears_existing_standstill_latch(self):
|
||||
controller = self._make_controller(standstill_req=True, last_standstill=True)
|
||||
|
||||
@@ -297,6 +569,49 @@ class TestToyotaCarController:
|
||||
assert parser.vl["LKAS_HUD"]["LEFT_LINE"] == 0
|
||||
assert parser.vl["LKAS_HUD"]["RIGHT_LINE"] == 0
|
||||
|
||||
def test_acc_control_can_suppress_long_press_behavior_while_gap_button_is_held(self):
|
||||
packer = CANPacker(DBC[CAR.TOYOTA_HIGHLANDER_TSS2][Bus.pt])
|
||||
parser = CANParser(DBC[CAR.TOYOTA_HIGHLANDER_TSS2][Bus.pt], [("ACC_CONTROL", 0)], 0)
|
||||
|
||||
default_msg = toyotacan.create_accel_command(
|
||||
packer, 0.0, False, True, False, False, 1, False, 0, False,
|
||||
)
|
||||
parser.update([(1, [default_msg])])
|
||||
assert parser.vl["ACC_CONTROL"]["ALLOW_LONG_PRESS"] == 1
|
||||
|
||||
suppressed_msg = toyotacan.create_accel_command(
|
||||
packer, 0.0, False, True, False, False, 1, False, 0, False, allow_long_press=0,
|
||||
)
|
||||
parser.update([(1, [suppressed_msg])])
|
||||
assert parser.vl["ACC_CONTROL"]["ALLOW_LONG_PRESS"] == 0
|
||||
|
||||
def test_auto_brake_hold_sends_modified_pre_collision_after_timer(self):
|
||||
controller = self._make_controller()
|
||||
controller.packer = CANPacker(DBC[CAR.TOYOTA_CAMRY_TSS2][Bus.pt])
|
||||
controller.frame = 0
|
||||
controller.brake_hold_active = False
|
||||
controller._brake_hold_counter = 0
|
||||
controller._brake_hold_reset = False
|
||||
controller._prev_brake_pressed = False
|
||||
cs = SimpleNamespace(
|
||||
out=SimpleNamespace(
|
||||
standstill=True,
|
||||
cruiseState=SimpleNamespace(available=True, enabled=False),
|
||||
gasPressed=False,
|
||||
brakePressed=False,
|
||||
gearShifter=structs.CarState.GearShifter.drive,
|
||||
),
|
||||
pre_collision_2={},
|
||||
)
|
||||
|
||||
can_sends = controller.create_auto_brake_hold_messages(cs, brake_hold_allowed_timer=0)
|
||||
|
||||
parser = CANParser(DBC[CAR.TOYOTA_CAMRY_TSS2][Bus.pt], [("PRE_COLLISION_2", 0)], 0)
|
||||
parser.update([(1, can_sends)])
|
||||
assert controller.brake_hold_active
|
||||
assert parser.vl["PRE_COLLISION_2"]["DSS1GDRV"] == -1.0
|
||||
assert parser.vl["PRE_COLLISION_2"]["PBRTRGR"] == 1
|
||||
|
||||
def test_interceptor_stop_and_go_holds_small_launch_at_standstill(self):
|
||||
controller = self._make_controller()
|
||||
controller.CP.enableGasInterceptorDEPRECATED = True
|
||||
@@ -323,6 +638,20 @@ class TestToyotaCarController:
|
||||
|
||||
assert 0.0 < gas_cmd <= 0.5
|
||||
|
||||
def test_interceptor_corolla_scales_with_accel_request_when_pedal_enables_sng(self):
|
||||
controller = self._make_controller()
|
||||
controller.CP.enableGasInterceptorDEPRECATED = True
|
||||
controller.CP.carFingerprint = CAR.TOYOTA_COROLLA
|
||||
controller.CP.minEnableSpeed = -1.0
|
||||
controller.accel = 0.8
|
||||
|
||||
gas_cmd = controller._compute_interceptor_gas_cmd(
|
||||
SimpleNamespace(longActive=True),
|
||||
SimpleNamespace(out=SimpleNamespace(standstill=False, vEgo=8.0)),
|
||||
)
|
||||
|
||||
assert 0.0 < gas_cmd <= 0.5
|
||||
|
||||
def test_interceptor_disabled_returns_zero(self):
|
||||
controller = self._make_controller()
|
||||
controller.accel = 1.0
|
||||
@@ -401,11 +730,35 @@ class TestToyotaCarController:
|
||||
)
|
||||
|
||||
assert CP.enableGasInterceptorDEPRECATED
|
||||
assert CP.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.GAS_INTERCEPTOR
|
||||
assert abs(CP.longitudinalActuatorDelay - 0.2) < 1e-6
|
||||
assert CP.stopAccel == -1.5
|
||||
|
||||
|
||||
class TestToyotaCarState:
|
||||
def test_lkas_button_platforms(self):
|
||||
assert CAR.TOYOTA_PRIUS in LKAS_BUTTON_CAR
|
||||
assert TSS2_CAR <= LKAS_BUTTON_CAR
|
||||
assert CAR.TOYOTA_CAMRY not in LKAS_BUTTON_CAR
|
||||
assert CAR.LEXUS_RX not in LKAS_BUTTON_CAR
|
||||
|
||||
@pytest.mark.parametrize("lkas_button,prev_lkas_button,event_count", [
|
||||
(0, 0, 0),
|
||||
(1, 0, 2),
|
||||
(1, 1, 0),
|
||||
(0, 1, 0),
|
||||
(2, 1, 2),
|
||||
])
|
||||
def test_lkas_button_events(self, lkas_button, prev_lkas_button, event_count):
|
||||
events = create_lkas_button_events(lkas_button, prev_lkas_button)
|
||||
|
||||
assert len(events) == event_count
|
||||
if events:
|
||||
assert [(event.type, event.pressed) for event in events] == [
|
||||
(structs.CarState.ButtonEvent.Type.lkas, True),
|
||||
(structs.CarState.ButtonEvent.Type.lkas, False),
|
||||
]
|
||||
|
||||
def test_interceptor_gas_pressed_threshold(self):
|
||||
cp = SimpleNamespace(vl={
|
||||
"GAS_SENSOR": {
|
||||
|
||||
@@ -40,8 +40,12 @@ def create_lta_steer_command_2(packer, frame):
|
||||
return packer.make_can_msg("STEERING_LTA_2", 0, values)
|
||||
|
||||
|
||||
def create_accel_command(packer, accel, pcm_cancel, permit_braking, standstill_req, lead, acc_type, fcw_alert, distance, reverse_cruise_active):
|
||||
def create_accel_command(packer, accel, pcm_cancel, permit_braking, standstill_req, lead, acc_type, fcw_alert,
|
||||
distance, reverse_cruise_active, allow_long_press=None):
|
||||
# TODO: find the exact canceling bit that does not create a chime
|
||||
if allow_long_press is None:
|
||||
allow_long_press = 2 if reverse_cruise_active else 1
|
||||
|
||||
values = {
|
||||
"ACCEL_CMD": accel,
|
||||
"ACC_TYPE": acc_type,
|
||||
@@ -50,7 +54,7 @@ def create_accel_command(packer, accel, pcm_cancel, permit_braking, standstill_r
|
||||
"PERMIT_BRAKING": permit_braking,
|
||||
"RELEASE_STANDSTILL": not standstill_req,
|
||||
"CANCEL_REQ": pcm_cancel,
|
||||
"ALLOW_LONG_PRESS": 2 if reverse_cruise_active else 1,
|
||||
"ALLOW_LONG_PRESS": allow_long_press,
|
||||
"ACC_CUT_IN": fcw_alert, # only shown when ACC enabled
|
||||
}
|
||||
return packer.make_can_msg("ACC_CONTROL", 0, values)
|
||||
@@ -86,6 +90,38 @@ def create_pcs_commands(packer, accel, active, mass):
|
||||
return [msg1, msg2]
|
||||
|
||||
|
||||
def create_brake_hold_command(packer, frame, pre_collision_2, brake_hold_active):
|
||||
values = {s: pre_collision_2[s] for s in [
|
||||
"DSS1GDRV",
|
||||
"DS1STAT2",
|
||||
"DS1STBK2",
|
||||
"PCSWAR",
|
||||
"PCSALM",
|
||||
"PCSOPR",
|
||||
"PCSABK",
|
||||
"PBATRGR",
|
||||
"PPTRGR",
|
||||
"IBTRGR",
|
||||
"CLEXTRGR",
|
||||
"IRLT_REQ",
|
||||
"BRKHLD",
|
||||
"AVSTRGR",
|
||||
"VGRSTRGR",
|
||||
"PREFILL",
|
||||
"PBRTRGR",
|
||||
"PCSDIS",
|
||||
"PBPREPMP",
|
||||
] if s in pre_collision_2}
|
||||
|
||||
if brake_hold_active:
|
||||
values = {
|
||||
"DSS1GDRV": 0x3FF,
|
||||
"PBRTRGR": frame % 730 < 727,
|
||||
}
|
||||
|
||||
return packer.make_can_msg("PRE_COLLISION_2", 0, values)
|
||||
|
||||
|
||||
def create_acc_cancel_command(packer):
|
||||
values = {
|
||||
"GAS_RELEASED": 0,
|
||||
|
||||
@@ -57,12 +57,16 @@ class ToyotaSafetyFlags(IntFlag):
|
||||
LTA = (4 << 8)
|
||||
SECOC = (8 << 8)
|
||||
LONG_FILTER = (16 << 8)
|
||||
GAS_INTERCEPTOR = (32 << 8)
|
||||
ALT_CRUISE = (64 << 8)
|
||||
|
||||
|
||||
class ToyotaFlags(IntFlag):
|
||||
# Detected flags
|
||||
HYBRID = 1
|
||||
DISABLE_RADAR = 4
|
||||
# The DSU's ACC messages are rerouted through the camera bus by an adapter.
|
||||
DSU_BYPASS = 8192
|
||||
|
||||
# Static flags
|
||||
TSS2 = 8
|
||||
@@ -75,6 +79,7 @@ class ToyotaFlags(IntFlag):
|
||||
# these cars can utilize 2.0 m/s^2
|
||||
RAISED_ACCEL_LIMIT = 1024
|
||||
SECOC = 2048
|
||||
AUTO_BRAKE_HOLD = 4096
|
||||
|
||||
# deprecated flags
|
||||
# these cars are speculated to allow stop and go when the DSU is unplugged
|
||||
@@ -169,7 +174,7 @@ class CAR(Platforms):
|
||||
ToyotaCarDocs("Toyota Camry Hybrid 2018-20", video="https://www.youtube.com/watch?v=Q2DYY0AWKgk"),
|
||||
],
|
||||
CarSpecs(mass=3400. * CV.LB_TO_KG, wheelbase=2.82448, steerRatio=13.7, tireStiffnessFactor=0.7933),
|
||||
dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
|
||||
dbc_dict('toyota_nodsu_pt_generated', 'toyota_radar_dsu_tssp'),
|
||||
flags=ToyotaFlags.NO_DSU,
|
||||
)
|
||||
TOYOTA_CAMRY_TSS2 = ToyotaTSS2PlatformConfig( # TSS 2.5
|
||||
@@ -201,6 +206,11 @@ class CAR(Platforms):
|
||||
CarSpecs(mass=2860. * CV.LB_TO_KG, wheelbase=2.7, steerRatio=18.27, tireStiffnessFactor=0.444),
|
||||
dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
|
||||
)
|
||||
TOYOTA_MATRIX_RETROFIT = PlatformConfig(
|
||||
[ToyotaCommunityCarDocs("Toyota Matrix 2005 Retrofit", package="Custom retrofit")],
|
||||
TOYOTA_COROLLA.specs,
|
||||
dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'),
|
||||
)
|
||||
# LSS2 Lexus UX Hybrid is same as a TSS2 Corolla Hybrid
|
||||
TOYOTA_COROLLA_TSS2 = ToyotaTSS2PlatformConfig(
|
||||
[
|
||||
@@ -548,7 +558,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
],
|
||||
non_essential_ecus={
|
||||
# FIXME: On some models, abs can sometimes be missing
|
||||
Ecu.abs: [CAR.TOYOTA_RAV4, CAR.TOYOTA_COROLLA, CAR.TOYOTA_HIGHLANDER, CAR.TOYOTA_SIENNA, CAR.LEXUS_IS, CAR.TOYOTA_ALPHARD_TSS2],
|
||||
Ecu.abs: [CAR.TOYOTA_RAV4, CAR.TOYOTA_COROLLA, CAR.TOYOTA_MATRIX_RETROFIT, CAR.TOYOTA_HIGHLANDER, CAR.TOYOTA_SIENNA, CAR.LEXUS_IS, CAR.TOYOTA_ALPHARD_TSS2],
|
||||
# On some models, the engine can show on two different addresses
|
||||
Ecu.engine: [CAR.TOYOTA_HIGHLANDER, CAR.TOYOTA_CAMRY, CAR.TOYOTA_COROLLA_TSS2, CAR.TOYOTA_CHR, CAR.TOYOTA_CHR_TSS2, CAR.LEXUS_IS,
|
||||
CAR.LEXUS_IS_TSS2, CAR.LEXUS_RC, CAR.LEXUS_NX, CAR.LEXUS_NX_TSS2, CAR.LEXUS_RX, CAR.LEXUS_RX_TSS2],
|
||||
@@ -589,7 +599,8 @@ STEER_THRESHOLD = 100
|
||||
|
||||
# These cars have non-standard EPS torque scale factors. All others are 73
|
||||
EPS_SCALE = defaultdict(lambda: 73,
|
||||
{CAR.TOYOTA_PRIUS: 66, CAR.TOYOTA_COROLLA: 88, CAR.LEXUS_IS: 77, CAR.LEXUS_RC: 77, CAR.LEXUS_CTH: 100, CAR.TOYOTA_PRIUS_V: 100})
|
||||
{CAR.TOYOTA_PRIUS: 66, CAR.TOYOTA_COROLLA: 88, CAR.TOYOTA_MATRIX_RETROFIT: 88,
|
||||
CAR.LEXUS_IS: 77, CAR.LEXUS_RC: 77, CAR.LEXUS_CTH: 100, CAR.TOYOTA_PRIUS_V: 100})
|
||||
|
||||
# Toyota/Lexus Safety Sense 2.0 and 2.5
|
||||
TSS2_CAR = CAR.with_flags(ToyotaFlags.TSS2)
|
||||
|
||||
@@ -94,6 +94,9 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
if candidate == CAR.PORSCHE_MACAN_MK1:
|
||||
ret.steerActuatorDelay = 0.07
|
||||
elif candidate == CAR.VOLKSWAGEN_TAOS_MK1:
|
||||
# Logged Taos braking response aligns about 0.1 s later than the MQB default.
|
||||
ret.longitudinalActuatorDelay = 0.25
|
||||
|
||||
ret.pcmCruise = not ret.openpilotLongitudinalControl
|
||||
ret.stopAccel = -0.55
|
||||
|
||||
@@ -2,6 +2,7 @@ import random
|
||||
import re
|
||||
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.volkswagen.interface import CarInterface
|
||||
from opendbc.car.volkswagen.values import CAR, FW_QUERY_CONFIG, WMI
|
||||
from opendbc.car.volkswagen.fingerprints import FW_VERSIONS
|
||||
|
||||
@@ -13,6 +14,13 @@ SPARE_PART_FW_PATTERN = re.compile(b'\xf1\x87(?P<gateway>[0-9][0-9A-Z]{2})(?P<un
|
||||
|
||||
|
||||
class TestVolkswagenPlatformConfigs:
|
||||
def test_taos_longitudinal_actuator_delay(self):
|
||||
taos_cp = CarInterface.get_non_essential_params(CAR.VOLKSWAGEN_TAOS_MK1)
|
||||
golf_cp = CarInterface.get_non_essential_params(CAR.VOLKSWAGEN_GOLF_MK7)
|
||||
|
||||
assert abs(taos_cp.longitudinalActuatorDelay - 0.25) < 1e-6
|
||||
assert abs(golf_cp.longitudinalActuatorDelay - 0.15) < 1e-6
|
||||
|
||||
def test_spare_part_fw_pattern(self, subtests):
|
||||
# Relied on for determining if a FW is likely VW
|
||||
for platform, ecus in FW_VERSIONS.items():
|
||||
|
||||
@@ -10,6 +10,7 @@ source_files = [
|
||||
for f in Path("generator").rglob("*")
|
||||
if f.is_file() and f.suffix in {".py", ".dbc"}
|
||||
]
|
||||
source_files.append(File("hyundai_kia_generic.dbc"))
|
||||
|
||||
output_files = [
|
||||
f.name.replace(".dbc", "_generated.dbc")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user