IQ.Pilot Release Commit @ 2b39aa6

This commit is contained in:
IQ.Lvbs CI [bot]
2026-07-29 00:19:10 -05:00
parent 8f052b6f93
commit c7908ad2e0
226 changed files with 11978 additions and 11349 deletions
@@ -16,27 +16,27 @@
},
"python/iqpilot_private/konn3kt/backups/archive_codec.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "0d09e647c74e2fe7711d86f03ed46b8dbcea1cc481db79831f524052bfe3df9a",
"sha256": "1ca35a437eaf1c50ce6f58eb38cdfb56eacab6f7d5201eef446495a68d90d69b",
"size": 135536
},
"python/iqpilot_private/konn3kt/backups/backup_keys.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "5433b48250397a598883e373b62dec1e196ca0252a834e4df951714f737225a0",
"sha256": "98039925b5104eb48b69d2ea6a34600daab8251d926fecab666ac51e565d1571",
"size": 67664
},
"python/iqpilot_private/konn3kt/backups/backup_orchestrator.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "1ff757bd5846b8643757b6c9b19e469023ebc40abe9c1cc81843e213c9fca36c",
"sha256": "85edb71bacde7958772f85a07e56506e974214756198b0fc8b91a21188d336e6",
"size": 204624
},
"python/iqpilot_private/konn3kt/backups/cbc_vault.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "10820bac096f6d19c2c2730da7b514d39740071aa2e06ef0aff62d29dd038f79",
"sha256": "1c8a4bf98aa378c3a4d924725a6f8f42c4de13452a18064bc76539354659d84e",
"size": 69752
},
"python/iqpilot_private/konn3kt/backups/imahelper.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "53869c7ac607b3df9b2582a7a696c78f240b65aa912adaa5bbf821cb2d6e43be",
"sha256": "a5ce7828c559a5841317d79e01a7e571debbce4d4992827aedab27d6d2b2eb15",
"size": 203072
},
"python/iqpilot_private/konn3kt/flockd/__init__.py": {
@@ -46,12 +46,12 @@
},
"python/iqpilot_private/konn3kt/flockd/flockd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "16af7e2135b778b8c00b37913ff2789382fcf4bdf752677c112646444ff4128a",
"sha256": "2fe267d00d04e9f94ef16885cb88b4b28ad7f6e1b1ca41caf23883d46ab03961",
"size": 202168
},
"python/iqpilot_private/konn3kt/flockd/signatures.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "ea3cefbd825298085ff45dd9c8a6dfceac2f59ae159897288190c6e1916f9914",
"sha256": "d3ff0a6d4d9f1653418de8fc3fa52baed0067c48c02438b30e1d27cf92e5aeb4",
"size": 68080
},
"python/iqpilot_private/konn3kt/hephaestus/__init__.py": {
@@ -61,67 +61,67 @@
},
"python/iqpilot_private/konn3kt/hephaestus/_vendor/localapi_runtime.zip": {
"mode": 420,
"sha256": "addff42a1545df02047da7e44aaef7ac86299d18559beb6889ce0e59281d2989",
"sha256": "1854ddf5a9e969ce210acf55fd4e53cc158e0117c67f65132e9c21fb49d1b53e",
"size": 2100238
},
"python/iqpilot_private/konn3kt/hephaestus/ble_auth.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "93afb33797a4a0808eb0f07f8a1507a933aff5e46c88611be73c5fb6ea25db03",
"sha256": "983a5825aaf5178081c5f44f1ba7e950b117774fcf751881c013dd05796b70d4",
"size": 268064
},
"python/iqpilot_private/konn3kt/hephaestus/ble_gatt.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "cba46bd69c7680542c9bd81e351152d9e74d02870706211e440f14f60e124e5a",
"sha256": "87586aec493893ccae8c67fc89fc71006762f87100d3d396d9735d6603b86cb9",
"size": 340584
},
"python/iqpilot_private/konn3kt/hephaestus/ble_rpc_dispatch.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "aee81fc9495d0d57885be7680a5c8b5b909acb1cc3dbbdcbd000ce006b7b8db9",
"sha256": "cff0554eebc045f8d46d5153d88949123ddc26cc251dec1fe6303c1911b9c8ad",
"size": 68048
},
"python/iqpilot_private/konn3kt/hephaestus/ble_transportd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "268b6dcef5769fcdf55014fd23c8fe6cd782f5ca2b13c376fe7f6832370394af",
"sha256": "a1f10df44d7a49066b2383ad06dde00337f9dc2126f5eeeeca5a7d6c4689da07",
"size": 335728
},
"python/iqpilot_private/konn3kt/hephaestus/bt_gamepad.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "fffd2076ec445350819197f4e976df5eaa2f75091f12b5fdd5e3c8173a50f45c",
"sha256": "d0f12e27574b34033a86e29e186d4a551cd8c9f03c2e478ac656003ce164cbc9",
"size": 271680
},
"python/iqpilot_private/konn3kt/hephaestus/cloud_routes.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "d216f9a9778494f15681deb712d804dea1b16efcf39c64aa5b8dac2f69d1e18d",
"sha256": "ab76c8af88512b3839150810f9083f26f0238ac644585a1cb9d4e462a2ea92b2",
"size": 134408
},
"python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "98f4eedb594fda692a9d8df3d629f140b9be141b9ebf249f4b9b1fd6f08f7ab2",
"size": 3514416
"sha256": "80183a42c221b9ac60681ad585d629fe22313f655e8dcb955dc05128bcf16147",
"size": 3647464
},
"python/iqpilot_private/konn3kt/hephaestus/kwp2000.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "0497c9f642821c7d5a751cb017d267b05a0351f7132739d4cfe4045725c9c288",
"sha256": "05bac25363b7dc2b1cab18fbf54511c98fc8806034eba759f56008451efd1c64",
"size": 136448
},
"python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "f2213863c2ac3dca7adbeb1aa8f4e3aa826f314e00242f57cc226e182cb1a827",
"sha256": "0e3104b80f8eb480caa673fee837f9b8159e479f8761c443caa3e88f6d65fc82",
"size": 68136
},
"python/iqpilot_private/konn3kt/hephaestus/motd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "56bd84482c5608a24566f397fcae1a8e3cfdf056d4b8cfbafc4e3940df7ff7a4",
"sha256": "3ce3d559312252ab44fd4b2d68b25393751d74e14d0f5142ab2d14e5dd2a466b",
"size": 67840
},
"python/iqpilot_private/konn3kt/hephaestus/tp20.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "d072ae1e45b6af63bd53d9347c0abd5349e7ac4a38f2771ed90cbab23f53fa0e",
"sha256": "b85ce7db3fbd4879067c068175d0c40f4c1eb9aa54811396519ce2f8fff7ad3c",
"size": 135648
},
"python/iqpilot_private/konn3kt/hephaestus/vw_pq_flasher.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "de65e895460391aaa375829c6ecb32f4afdb38f788eb8b08037da92e45612819",
"sha256": "2e7b7f1b4ed526c924c06aa63ecfaffc1ed726e3a31ba267712a1502e0f30020",
"size": 269568
},
"python/iqpilot_private/konn3kt/uploaderd/__init__.py": {
@@ -131,7 +131,7 @@
},
"python/iqpilot_private/konn3kt/uploaderd/iquploaderd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "cf6a5657d7df10401bc9c0a8beb0cfd66f17647a8ab15b533973470754434cf2",
"sha256": "66cec9701718c377178142f92912d8093dd62a58bb00fa2f865b76457db959db",
"size": 204184
},
"runtime": {
@@ -171,25 +171,25 @@
}
},
"signatures": {
"python/iqpilot_private/konn3kt/backups/archive_codec.cpython-312-aarch64-linux-gnu.so": "MhygD4Nh+eJC3m2vDtF641GcCMQwyRq3shJFQMv2SA+152534qnm/FnmG31lqFq08SNxJSUpbYs5d56dzVJoAw==",
"python/iqpilot_private/konn3kt/backups/backup_keys.cpython-312-aarch64-linux-gnu.so": "URQ2U1GvLsYG+Nur/06oweZqpcyPeniGu0fmRgIrYmvrMETgJkTVjERAb18jnUEUb2SLT6IDzpVJYQwvancEDA==",
"python/iqpilot_private/konn3kt/backups/backup_orchestrator.cpython-312-aarch64-linux-gnu.so": "N0G4N2rA3BwaCJxYGDYwk/HJzIMqdVNH/MVKtnit3zt8f1j/Cx742cFD+K+8AiLbBAKk5m1Hu1+h30P8IIOKAw==",
"python/iqpilot_private/konn3kt/backups/cbc_vault.cpython-312-aarch64-linux-gnu.so": "BtRiu8ovXJ/34exEQl5B3rytuRAabbfu5ivDlTpVjE8g1B/Gtuq4+WnnyY3MxceuRPDDwdh6H+wEs86JQgIeAg==",
"python/iqpilot_private/konn3kt/backups/imahelper.cpython-312-aarch64-linux-gnu.so": "Zx2W7qGJeeNQm4Rh83jbwN8CoBLRiBxHOaCBuhPLs6eH9yGTWChReVUlfSO6ERLjwDdj7ZZxJrAwejyYlAr6CA==",
"python/iqpilot_private/konn3kt/flockd/flockd.cpython-312-aarch64-linux-gnu.so": "eaV/SCWv/7CGkqcP+9aEPGRqDgLtjd4pwLbu/XdHWj/gtK4yvlFP7Es/klwBGIrEK9D7Go9ld6XKfHhOPYmfAQ==",
"python/iqpilot_private/konn3kt/flockd/signatures.cpython-312-aarch64-linux-gnu.so": "I+/gJLOtUIPx3BfBfLynJZz79SX/TtEqhc9awsk+xI6+HPHjvU5+2DD5HRWlgVWO7bdJKf5m24hP4QHO8sfzCw==",
"python/iqpilot_private/konn3kt/hephaestus/ble_auth.cpython-312-aarch64-linux-gnu.so": "4dPhClIrA3dBR9/JO5sgcluwzZc8FJREIOSNQ40n3A7vw3CXw6ImVwhmjE/yVQ5wIjndhKb+KLy21V37lzGfAw==",
"python/iqpilot_private/konn3kt/hephaestus/ble_gatt.cpython-312-aarch64-linux-gnu.so": "wGbVY1IQZaD2u9Ied+zcBM2XjIOoRxSectUKRKry7D6yuxB7Mqtoa8LLq9GOZCRFZDHPQ9rk2f9QSgBZfT5VAg==",
"python/iqpilot_private/konn3kt/hephaestus/ble_rpc_dispatch.cpython-312-aarch64-linux-gnu.so": "YqAqfi21Vy0kyOb2FQONlS6F8gW8Cvi8/CjYsaxiItGS8E6y6bzhYe5/AlMyd385NYoD4upXfbMGXHXDqblsCQ==",
"python/iqpilot_private/konn3kt/hephaestus/ble_transportd.cpython-312-aarch64-linux-gnu.so": "gWd6Vfq+80Z4uxFmDfvaYkp1Wzw18kEzA/mG0Xd54EbDJ4GefnGkAHuvssMYGNE+TCEpqaVt85Jf8QWflirzAw==",
"python/iqpilot_private/konn3kt/hephaestus/bt_gamepad.cpython-312-aarch64-linux-gnu.so": "xXbkx1zJrM5NPlBeVhengh56WJ6CXXb4V8aswT4zasmr3DcxrpV7Y7Wbk13UEwV8ix+huvs9wVlojOfdZqiIAA==",
"python/iqpilot_private/konn3kt/hephaestus/cloud_routes.cpython-312-aarch64-linux-gnu.so": "chkeeRV4erIsiTmJ51G7/qFJ8fPACpkuO8sveQv5okaopUar+O2/m+lDLBnYkR4Es/TFnCGRv4N0RwvzT49rCg==",
"python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so": "p40DN5yfj4tyY6kzcA7VEmbMxhjQ77QwiNR3vMGEuoKh7mcK3jmbBdGPa4gANN30n/UyLEG2+sg4dhM0RVm4BA==",
"python/iqpilot_private/konn3kt/hephaestus/kwp2000.cpython-312-aarch64-linux-gnu.so": "0ZIlxKWPjaZGQFKWhKp07Rt62fwr6QJYUxtY8bQ3mNYsWV72Ix36Q2TfALEVVlE/zSHwP34eV4mSFA8Q1qyhBw==",
"python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so": "F7nzMGhcWJ7daMQgd6zpPtzD1q5Up8sJyztfGo30md7aeiSMGX8BONlW6FglTR+WZoNei7p9HA1YWGelGuYzDA==",
"python/iqpilot_private/konn3kt/hephaestus/motd.cpython-312-aarch64-linux-gnu.so": "rECWieVZcSBvEoDvGdg0+hpR7EkzmZtsuS6b/f1RAQPk8k96sLsUOXO4myL0xlnuzY7dim3UL9g8BqqW8qutAg==",
"python/iqpilot_private/konn3kt/hephaestus/tp20.cpython-312-aarch64-linux-gnu.so": "CtyIPJuuLXHQ9VaMtLsZVHD18UWlw1JfNTYqlvJtwsTZK88ct+9S99n6ROkdqIr5qn3LQjMr7QxvEGe457IFBA==",
"python/iqpilot_private/konn3kt/hephaestus/vw_pq_flasher.cpython-312-aarch64-linux-gnu.so": "u4+khM3qZF78JaJKBVdnSHb4VARUJXg/BS+1SuX/DydJuEmziX4FTCi+LiO1X6GjEi1ciJP9jz6bWEC39b7xAw==",
"python/iqpilot_private/konn3kt/uploaderd/iquploaderd.cpython-312-aarch64-linux-gnu.so": "65hNz0AqEUYmvWaVGGFSp7y0BtVRT8KG/3B1lil/eg6p9BCDvPbH6vmku7/Zm7T7bQjYmjvofzRjiqi1hbLcBA=="
"python/iqpilot_private/konn3kt/backups/archive_codec.cpython-312-aarch64-linux-gnu.so": "S6pnKIiETehDkX4x+40+Q+fBQ04N9x1OI7NQS5+j6JVNNIE0PA4MuMB+Vl9rt3o+fJrFoyIL1g+tMWT2gMluDA==",
"python/iqpilot_private/konn3kt/backups/backup_keys.cpython-312-aarch64-linux-gnu.so": "NckVEdkvdS5EmHJKOIe1EakeyNUbYFxDG2tohT0MMzKM3Tk47f0k4cs/QOqFRNsb612WwPH9PaPdhOmNXUKLCQ==",
"python/iqpilot_private/konn3kt/backups/backup_orchestrator.cpython-312-aarch64-linux-gnu.so": "3byD6wJefryFq1S6jV0TT+Ui/Yn+pyj3Iu+RI2vxIuue9qIknyIy0zc156kXSotiYkip0XOuzwB1HNWo4qTPCg==",
"python/iqpilot_private/konn3kt/backups/cbc_vault.cpython-312-aarch64-linux-gnu.so": "0S7cE4HK9ZoATxXvrWcQF9Hf+iy8JKdBd/OAQJt6IHrmgDeExkn5NQD5VjMpOejsGBTCQEgL/xx7JpYxlr5wCQ==",
"python/iqpilot_private/konn3kt/backups/imahelper.cpython-312-aarch64-linux-gnu.so": "3adGsr3oPmcldMROx5pjkCxa2Vrbaj2lRhFjNgz151VHRULAOTuxyX7nAZQmxNuDEumyji5TW5d1zTqAoMFnBg==",
"python/iqpilot_private/konn3kt/flockd/flockd.cpython-312-aarch64-linux-gnu.so": "gIbpR3VZKr2n0sXnV6PJhHiF6spttO6PFPgxxGoo7ml8tmYFx7SoUhUiEEhNQXGpcN27iW/5IT7RGIE4m8kPCg==",
"python/iqpilot_private/konn3kt/flockd/signatures.cpython-312-aarch64-linux-gnu.so": "iLZnAzgx87+P3adKMeOOJswOKmCdt4gqG4EJ/lEHjjWZC+GZlnTr/qQ3fiQ1R1qp6Jvb3MrFI4FyFnsbURfPCQ==",
"python/iqpilot_private/konn3kt/hephaestus/ble_auth.cpython-312-aarch64-linux-gnu.so": "QTiHED4yIIVzPcU6BapHNU/nlX1g6cP8AaxteooJDR/DqCnUbW/3YUsVSmqvwTLNTF6YWNOLa2dgvybyYRBSAQ==",
"python/iqpilot_private/konn3kt/hephaestus/ble_gatt.cpython-312-aarch64-linux-gnu.so": "MPoy7t15DrxRSJBi+ZFRqHU/ahIc03h8XxuOiho/zaU4I6LIy4QwP18duc+YkeJ5R6jkPduoWgLyHKbAXPTYCg==",
"python/iqpilot_private/konn3kt/hephaestus/ble_rpc_dispatch.cpython-312-aarch64-linux-gnu.so": "I9uohiZn6lrerZFWee6cf2yXkeDxZTSBxKVQWSGKq5woFAK5fZ94FMf8qKfow8dOw8etLXC8XOYA2oH5TfK+AQ==",
"python/iqpilot_private/konn3kt/hephaestus/ble_transportd.cpython-312-aarch64-linux-gnu.so": "smEWcWBel8zd78q5YvTnVKlM3EMh15FxywK/Dlx6lCHJ5eLAPsoYo+YimWd9qUFBOF/rmkO5k/9TlqsKE31vBw==",
"python/iqpilot_private/konn3kt/hephaestus/bt_gamepad.cpython-312-aarch64-linux-gnu.so": "wrvw7RvBfiEt0dz2TKMuDb03FIIaPLEclAYYmWObzwF1NAmyiBRqup0F0y3k7vtmwbubjns7+j2ZvA4j+k18AQ==",
"python/iqpilot_private/konn3kt/hephaestus/cloud_routes.cpython-312-aarch64-linux-gnu.so": "/IBbuQ0E7ym7EGLV9EYDqcWLaSgBa6IImVgapC50F7LhNXmuUhOZJc9sVyV50Xa/e+60toBSPsJETud6sSeTCA==",
"python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so": "iqiqKn2zwfgBtsiqKtNTMgfnCfek9F41ZLVi3GTOzJucXsyKMCCLfyn4zImGf1w9zG+XWPWwudDBYEYWKr8AAQ==",
"python/iqpilot_private/konn3kt/hephaestus/kwp2000.cpython-312-aarch64-linux-gnu.so": "uzsNWnyFDbCTEYVdDEF2dS+Hk/WnB/Je4bT5Htge8TX/1O/pEpgCKpLE1j1H5Nt+/M5HDvgSXJScQxmlWJBaDQ==",
"python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so": "TwgqVE8NrG59tVrr9z1Lvo7yh1gVyI8548cP7PT44F4bnDVx9oJvqaZOqF1vEV4xWdoYKSIBPVA3YnINnwEICw==",
"python/iqpilot_private/konn3kt/hephaestus/motd.cpython-312-aarch64-linux-gnu.so": "uBZA3ZSrCNfpy/vWbtYApt9UOStxuBBe0LP6AhEZEHWfebrM0CeCK1qimb+kfGDoQyTXLmie6v8PiKWYeTmODQ==",
"python/iqpilot_private/konn3kt/hephaestus/tp20.cpython-312-aarch64-linux-gnu.so": "OwU3WbDPCT0ggbPBWjuYvTiPuPW+j557MyciaBFq1C5+xElg94DHWH6DpV/WzUFkRwjb4226epgYjSCQHva/Ag==",
"python/iqpilot_private/konn3kt/hephaestus/vw_pq_flasher.cpython-312-aarch64-linux-gnu.so": "j7lzNRlIQ0kr0Pw4C8Oo/fw1i/ihgmNTXP+cro9J3ivRm1xpe5aPPY22I5cw763tGHo2h9YCAO5EBAHz+FSsCg==",
"python/iqpilot_private/konn3kt/uploaderd/iquploaderd.cpython-312-aarch64-linux-gnu.so": "Z3vtFdwz1cLaTACNhZoaO5UXG4HCzEOB/2/RCdii1epsXE9amtWEbwx2miS2qlN03gC28Ieak9SECjxMyGafAg=="
}
}
+1
View File
@@ -332,6 +332,7 @@ struct IQCarParams @0xd4189b5c8aca9f78 {
iqLateralNet @4 :LateralNet;
longitudinalStoppingSpeedOverride @5 :Float32; # m/s; zero keeps the upstream default
stoppingDecelRateOverride @6 :Float32; # m/s^3; zero keeps the upstream default
struct LateralNet {
fuzzyFingerprint @0 :Bool;
+13 -1
View File
@@ -3,6 +3,7 @@ import os
import requests
import unicodedata
from datetime import datetime, timedelta, UTC
from functools import lru_cache
from openpilot.system.hardware.hw import Paths
from openpilot.system.version import get_version
@@ -11,6 +12,16 @@ KEYS = {"id_rsa": "RS256",
"id_ecdsa": "ES256"}
@lru_cache(maxsize=4)
def load_signing_key(private_key: str):
# PyJWT re-parses a PEM string on every encode; an RSA parse is ~40ms, so cache the key object
try:
from cryptography.hazmat.primitives.serialization import load_pem_private_key
return load_pem_private_key(private_key.encode(), password=None)
except Exception:
return private_key
class BaseApi:
def __init__(self, dongle_id, api_host, user_agent="openpilot-"):
self.dongle_id = dongle_id
@@ -38,7 +49,8 @@ class BaseApi:
}
if payload_extra is not None:
payload.update(payload_extra)
token = jwt.encode(payload, self.private_key, algorithm=self.jwt_algorithm)
key = load_signing_key(self.private_key) if self.private_key else self.private_key
token = jwt.encode(payload, key, algorithm=self.jwt_algorithm)
if isinstance(token, bytes):
token = token.decode('utf8')
return token
+85
View File
@@ -167,6 +167,91 @@ def managed_proc(cmd: list[str], env: dict[str, str]):
proc.kill()
def tabulate(tabular_data, headers=(), tablefmt="simple", floatfmt="g", stralign="left", numalign=None):
rows = [list(row) for row in tabular_data]
def fmt(val):
if isinstance(val, str):
return val
if isinstance(val, (bool, int)):
return str(val)
try:
return format(val, floatfmt)
except (TypeError, ValueError):
return str(val)
formatted = [[fmt(c) for c in row] for row in rows]
hdrs = [str(h) for h in headers] if headers else None
ncols = max((len(r) for r in formatted), default=0)
if hdrs:
ncols = max(ncols, len(hdrs))
if ncols == 0:
return ""
for r in formatted:
r.extend([""] * (ncols - len(r)))
if hdrs:
hdrs.extend([""] * (ncols - len(hdrs)))
widths = [0] * ncols
if hdrs:
for i in range(ncols):
widths[i] = len(hdrs[i])
for row in formatted:
for i in range(ncols):
widths[i] = max(widths[i], max(len(ln) for ln in row[i].split('\n')))
def _align(s, w):
if stralign == "center":
return s.center(w)
return s.ljust(w)
if tablefmt == "html":
parts = ["<table>"]
if hdrs:
parts.append("<thead>")
parts.append("<tr>" + "".join(f"<th>{h}</th>" for h in hdrs) + "</tr>")
parts.append("</thead>")
parts.append("<tbody>")
for row in formatted:
parts.append("<tr>" + "".join(f"<td>{c}</td>" for c in row) + "</tr>")
parts.append("</tbody>")
parts.append("</table>")
return "\n".join(parts)
if tablefmt == "simple_grid":
def _sep(left, mid, right):
return left + mid.join("" * (w + 2) for w in widths) + right
top, mid_sep, bot = _sep("", "", ""), _sep("", "", ""), _sep("", "", "")
def _fmt_row(cells):
split = [c.split('\n') for c in cells]
nlines = max(len(s) for s in split)
for s in split:
s.extend([""] * (nlines - len(s)))
return ["" + "".join(f" {_align(split[i][li], widths[i])} " for i in range(ncols)) + "" for li in range(nlines)]
lines = [top]
if hdrs:
lines.extend(_fmt_row(hdrs))
lines.append(mid_sep)
for ri, row in enumerate(formatted):
lines.extend(_fmt_row(row))
lines.append(mid_sep if ri < len(formatted) - 1 else bot)
return "\n".join(lines)
gap = " "
lines = []
if hdrs:
lines.append(gap.join(h.ljust(w) for h, w in zip(hdrs, widths, strict=True)))
lines.append(gap.join("-" * w for w in widths))
for row in formatted:
lines.append(gap.join(_align(row[i], widths[i]) for i in range(ncols)))
return "\n".join(lines)
def retry(attempts=3, delay=1.0, ignore_failure=False):
def decorator(func):
@functools.wraps(func)
-1
View File
@@ -1 +0,0 @@
Wen
+99
View File
@@ -1,3 +1,102 @@
IQ.Lvbs License v0.1a
Copyright (c) 2026 IQ.Lvbs LLC, a part of Project Teal Lvbs Inc. All Rights Reserved.
DEFINITIONS
"Software" refers to IQ.Pilot, konn3kt, and all associated source code,
documentation, and assets owned by the Copyright Holder.
"Open Components" refers to portions of the Software explicitly marked as
open source.
"Proprietary Components" refers to all portions of the Software not made
available to the public in source form.
"Copyright Holder" refers to IQ.Lvbs LLC, a part of Project Teal Lvbs Inc.
GRANT OF LICENSE
Subject to the terms of this license, you are granted a limited,
non-exclusive, revocable license to:
1. View, study, and learn from the Open Components
2. Modify the Open Components for personal, internal, or open-source public use
3. Run the Software for personal, non-commercial purposes
RESTRICTIONS
You may NOT:
1. Claim ownership of any part of the Software, excluding your own
modifications that do not incorporate Proprietary Components.
2. Reverse engineer, decompile, disassemble, or in any way attempt to
circumvent the obfuscation of the Proprietary Components.
3. Use the Software or any derivative for commercial purposes without
explicit written permission from the Copyright Holder.
4. Remove or alter any copyright notices or this license.
5. Sublicense, sell, or transfer rights to the Software.
6. Use the Software and/or its source code to compete with or create a
substantially similar product.
7. Use the Software in closed source software not licensed by IQ.Lvbs LLC.
CONSEQUENCES OF VIOLATION
In the event any Restriction is violated, any product created using inspiration from, or source code from, IQ.Pilot or Konn3kt shall be subject to a licensing fee determined solely by the Copyright Holder. Additionally, the violating party hereby grants IQ.Lvbs LLC an exclusive, irrevocable, worldwide, royalty-free license to use any and all assets from the infringing product on IQ.Lvbs webpages, advertising materials, and in any other manner IQ.Lvbs sees fit.
OWNERSHIP
All rights, title, and interest in the Software remain exclusively with the
Copyright Holder. Any modifications, improvements, or derivative works you
create based on the Software are owned by the Copyright Holder. By
contributing modifications, you irrevocably assign all rights to the
Copyright Holder.
PROPRIETARY COMPONENTS
The Proprietary Components are provided in binary or obfuscated form only.
Reverse engineering, decompilation, or disassembly of Proprietary Components
is strictly prohibited. Violation of this provision entitles IQ.Lvbs LLC to
pursue all available legal remedies to protect its intellectual property and
trade secrets.
NO WARRANTY
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT
HOLDER DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
NON-INFRINGEMENT.
LIMITATION OF LIABILITY
IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES, OR
OTHER LIABILITY ARISING FROM THE USE OF THE SOFTWARE. THE USER ACCEPTS FULL
RESPONSIBILITY FOR ANY AND ALL LIABILITIES WHEN USING IQ.LVBS SOFTWARE.
TERMINATION
This license terminates automatically if you violate any of its terms. Upon
termination, you must destroy all copies of the Software in your possession.
The Copyright Holder reserves the right to revoke this license at any time
for any reason.
GOVERNING LAW
This license shall be governed by the laws of the State of Illinois, United
States of America. Any disputes arising under this license shall be subject
to the exclusive jurisdiction of the courts located in Henry County, Illinois.
---
For commercial licensing inquiries, contact: support@iqlvbs.com
Copyright (c) 2020, Comma.ai, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+8
View File
@@ -96,3 +96,11 @@ to the exclusive jurisdiction of the courts located in Henry County, Illinois.
---
For commercial licensing inquiries, contact: support@iqlvbs.com
Copyright (c) 2020, Comma.ai, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-3
View File
@@ -1,3 +0,0 @@
include iqdbc/car/car.capnp
include iqdbc/car/include/c++.capnp
recursive-include iqdbc/safety *.h
+55
View File
@@ -0,0 +1,55 @@
## IQ.Pilot Car-Port Credits
This file is intended to track per-platform and per-tuning attribution inside the car-port-side included inside of IQ.Pilot.
It does not replace the repository-wide license files for IQ.Pilot itself or any other components not listed here.
## Credits:
### Hyundai / Kia / Genesis (HKG) - carrotpilot
#### Contributors
Full credit for the Hyundai / Kia / Genesis port and its HKG-specific tuning belongs to:
- carrotpilot
- ajouatom
#### Attribution
This repository carries the carrotpilot HKG implementation from `ajouatom/openpilot`, imported from commit `b54d44efd514bc61f86f08b01c23a6c190885c07`. IQ.Lvbs claims no copyright, credit, or authorship over that implementation.
#### Upstream License Notice
The HKG work attributed above is acknowledged here under LICENSE notice:
```text
Copyright (c) 2018, Comma.ai, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
```
#### Scope
This licensing is limited to the Hyundai/Kia/Genesis car, DBC, and safety implementations and the supporting schema and compatibility changes required by that port.
### Honda
#### Contributors
Full credit for the Honda-specific tuning lineage carried in this tree belongs to:
- MVL-Boston on GitHub
- mvl3c on Discord
#### Attribution
This repository carries Honda tuning work derived from MVL's tuning effort's, that originated from, and/or is verbatim their work, and are accredited as such, IQ.Lvbs claims no copyright, credit, or authorship of the components listed below.
#### Scope
This licensing is limited to `iqdbc_repo/iqdbc/car/honda` and `iqdbc_repo/iqdbc/iqpilot/car/honda`.
-441
View File
@@ -1,441 +0,0 @@
<!--- AUTOGENERATED FROM selfdrive/car/CARS_template.md, DO NOT EDIT. --->
# Support Information for 410 Known Cars
|Make|Model|Package|Support Level|
|---|---|---|:---:|
|Acura|ADX 2025-26|All|[Community](#community)|
|Acura|ILX 2016-18|Technology Plus Package or AcuraWatch Plus|[Upstream](#upstream)|
|Acura|ILX 2019|All|[Upstream](#upstream)|
|Acura|Integra 2023-25|All|[Community](#community)|
|Acura|MDX 2015-16|Advance Package|[Community](#community)|
|Acura|MDX 2017-20|All|[Community](#community)|
|Acura|MDX 2022-24|All|[Community](#community)|
|Acura|MDX 2025-26|All except Type S|[Upstream](#upstream)|
|Acura|MDX Hybrid 2017-20|All|[Community](#community)|
|Acura|RDX 2016-18|AcuraWatch Plus or Advance Package|[Upstream](#upstream)|
|Acura|RDX 2019-21|All|[Upstream](#upstream)|
|Acura|RDX 2022-25|All|[Community](#community)|
|Acura|RLX 2017|Advance Package or Technology Package|[Community](#community)|
|Acura|TLX 2015-17|Advance Package|[Community](#community)|
|Acura|TLX 2018-20|All|[Community](#community)|
|Acura|TLX 2021|All|[Upstream](#upstream)|
|Acura|TLX 2022-23|All|[Community](#community)|
|Acura|TLX 2025|All|[Upstream](#upstream)|
|Acura|ZDX 2024|All|[Not compatible](#can-bus-security)|
|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Audi|A4 2016-24|All|[Not compatible](#flexray)|
|Audi|A5 2016-24|All|[Not compatible](#flexray)|
|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Audi|Q3 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Audi|Q5 2017-24|All|[Not compatible](#flexray)|
|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Cadillac|CT6 Non-ACC 2017-18|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Cadillac|XT5 Non-ACC 2018|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim, without Super Cruise Package|[Upstream](#upstream)|
|Chevrolet|Bolt EUV LT Non-ACC 2022-23|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|[Upstream](#upstream)|
|Chevrolet|Bolt EV LT Non-ACC 2022-23|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Chevrolet|Bolt EV Non-ACC 2017|No Adaptive Cruise Control (Non-ACC)|[Community](community)|
|Chevrolet|Bolt EV Non-ACC 2018-21|No Adaptive Cruise Control (Non-ACC)|[Community](community)|
|Chevrolet|Equinox 2019-22|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Equinox Non-ACC 2019-22|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Chevrolet|Malibu Non-ACC 2016-23|No Adaptive Cruise Control (Non-ACC)|[Community](community)|
|Chevrolet|Silverado 1500 2020-21|Safety Package II|[Upstream](#upstream)|
|Chevrolet|Suburban Non-ACC 2016-20|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Chevrolet|Trailblazer 2021-22|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Trailblazer Non-ACC 2021-22|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chrysler|Pacifica 2021-23|All|[Upstream](#upstream)|
|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chrysler|Pacifica Hybrid 2019-25|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|comma|body|All|[Upstream](#upstream)|
|CUPRA|Ateca 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Dodge|Durango 2020-21|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Ford|Bronco Sport 2021-24|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Escape 2020-22|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Escape 2023-24|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Escape Hybrid 2020-22|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Escape Hybrid 2023-24|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Escape Plug-in Hybrid 2020-22|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Escape Plug-in Hybrid 2023-24|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Expedition 2022-24|Co-Pilot360 Assist 2.0|[Upstream](#upstream)|
|Ford|Explorer 2020-24|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|Explorer Hybrid 2020-24|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Ford|F-150 2021-23|Co-Pilot360 Assist 2.0|[Upstream](#upstream)|
|Ford|F-150 Hybrid 2021-23|Co-Pilot360 Assist 2.0|[Upstream](#upstream)|
|Ford|Focus 2018|Adaptive Cruise Control with Lane Centering|[Upstream](#upstream)|
|Ford|Focus Hybrid 2018|Adaptive Cruise Control with Lane Centering|[Upstream](#upstream)|
|Ford|Kuga 2020-23|Adaptive Cruise Control with Lane Centering|[Upstream](#upstream)|
|Ford|Kuga Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|[Upstream](#upstream)|
|Ford|Kuga Hybrid 2024|All|[Upstream](#upstream)|
|Ford|Kuga Plug-in Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|[Upstream](#upstream)|
|Ford|Kuga Plug-in Hybrid 2024|All|[Upstream](#upstream)|
|Ford|Maverick 2022|LARIAT Luxury|[Upstream](#upstream)|
|Ford|Maverick 2023-24|Co-Pilot360 Assist|[Upstream](#upstream)|
|Ford|Maverick Hybrid 2022|LARIAT Luxury|[Upstream](#upstream)|
|Ford|Maverick Hybrid 2023-24|Co-Pilot360 Assist|[Upstream](#upstream)|
|Ford|Mustang Mach-E 2021-24|All|[Upstream](#upstream)|
|Ford|Ranger 2024|Adaptive Cruise Control with Lane Centering|[Upstream](#upstream)|
|Genesis|G70 2018|All|[Upstream](#upstream)|
|Genesis|G70 2019-21|All|[Upstream](#upstream)|
|Genesis|G70 2022-23|All|[Upstream](#upstream)|
|Genesis|G70 Non-SCC 2021|No Smart Cruise Control (Non-SCC)|[Dashcam mode](#dashcam)|
|Genesis|G80 2017|All|[Upstream](#upstream)|
|Genesis|G80 2018-19|All|[Upstream](#upstream)|
|Genesis|G80 (2.5T Advanced Trim, with HDA II) 2024|Highway Driving Assist II|[Upstream](#upstream)|
|Genesis|G90 2017-20|All|[Upstream](#upstream)|
|Genesis|GV60 (Advanced Trim) 2023|All|[Upstream](#upstream)|
|Genesis|GV60 (Performance Trim) 2022-23|All|[Upstream](#upstream)|
|Genesis|GV70 (2.5T Trim, without HDA II) 2022-24|All|[Upstream](#upstream)|
|Genesis|GV70 (3.5T Trim, without HDA II) 2022-23|All|[Upstream](#upstream)|
|Genesis|GV70 Electrified (Australia Only) 2022|All|[Upstream](#upstream)|
|Genesis|GV70 Electrified (with HDA II) 2023-24|Highway Driving Assist II|[Upstream](#upstream)|
|Genesis|GV80 2023|All|[Upstream](#upstream)|
|GMC|Sierra 1500 2020-21|Driver Alert Package II|[Upstream](#upstream)|
|GMC|Yukon 2019-20|Adaptive Cruise Control (ACC) & LKAS|[Dashcam mode](#dashcam)|
|Honda|Accord 2016-17|Honda Sensing|[Community](#community)|
|Honda|Accord 2018-22|All|[Upstream](#upstream)|
|Honda|Accord 2023-25|All|[Upstream](#upstream)|
|Honda|Accord Hybrid 2017|All|[Community](#community)|
|Honda|Accord Hybrid 2018-22|All|[Upstream](#upstream)|
|Honda|Accord Hybrid 2023-25|All|[Upstream](#upstream)|
|Honda|City (Brazil only) 2023|All|[Upstream](#upstream)|
|Honda|Civic 2016-18|Honda Sensing|[Upstream](#upstream)|
|Honda|Civic 2019-21|All|[Upstream](#upstream)|
|Honda|Civic 2022-24|All|[Upstream](#upstream)|
|Honda|Civic Hatchback 2017-18|Honda Sensing|[Upstream](#upstream)|
|Honda|Civic Hatchback 2019-21|All|[Upstream](#upstream)|
|Honda|Civic Hatchback 2022-24|All|[Upstream](#upstream)|
|Honda|Civic Hatchback Hybrid 2025-26|All|[Upstream](#upstream)|
|Honda|Civic Hatchback Hybrid (Europe only) 2023|All|[Upstream](#upstream)|
|Honda|Civic Hybrid 2025-26|All|[Upstream](#upstream)|
|Honda|Clarity 2018-21|Honda Sensing|[Community](community)|
|Honda|Clarity 2018-21|All|[Community](#community)|
|Honda|CR-V 2015-16|Touring Trim|[Upstream](#upstream)|
|Honda|CR-V 2017-22|Honda Sensing|[Upstream](#upstream)|
|Honda|CR-V 2023-26|All|[Upstream](#upstream)|
|Honda|CR-V Hybrid 2017-22|Honda Sensing|[Upstream](#upstream)|
|Honda|CR-V Hybrid 2023-26|All|[Upstream](#upstream)|
|Honda|e 2020|All|[Upstream](#upstream)|
|Honda|Fit 2018-20|Honda Sensing|[Upstream](#upstream)|
|Honda|Freed 2020|Honda Sensing|[Upstream](#upstream)|
|Honda|HR-V 2019-22|Honda Sensing|[Upstream](#upstream)|
|Honda|HR-V 2023-25|All|[Upstream](#upstream)|
|Honda|Insight 2019-22|All|[Upstream](#upstream)|
|Honda|Inspire 2018|All|[Upstream](#upstream)|
|Honda|N-Box 2018|All|[Upstream](#upstream)|
|Honda|Odyssey 2018-20|Honda Sensing|[Upstream](#upstream)|
|Honda|Odyssey 2021-26|All|[Upstream](#upstream)|
|Honda|Odyssey (Taiwan) 2018-19|Honda Sensing|[Upstream](#upstream)|
|Honda|Passport 2019-25|All|[Upstream](#upstream)|
|Honda|Passport 2026|All|[Upstream](#upstream)|
|Honda|Pilot 2016-22|Honda Sensing|[Upstream](#upstream)|
|Honda|Pilot 2023-25|All|[Upstream](#upstream)|
|Honda|Prologue 2024-25|All|[Not compatible](#can-bus-security)|
|Honda|Ridgeline 2017-25|Honda Sensing|[Upstream](#upstream)|
|Hyundai|Azera 2022|All|[Upstream](#upstream)|
|Hyundai|Azera Hybrid 2019|All|[Upstream](#upstream)|
|Hyundai|Azera Hybrid 2020|All|[Upstream](#upstream)|
|Hyundai|Bayon Non-SCC 2021|No Smart Cruise Control (Non-SCC)|[Dashcam mode](#dashcam)|
|Hyundai|Custin 2023|All|[Upstream](#upstream)|
|Hyundai|Elantra 2017-18|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Elantra 2019|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Elantra GT 2017-20|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Elantra Non-SCC 2022|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Ioniq 5 (Southeast Asia and Europe only) 2022-24|All|[Upstream](#upstream)|
|Hyundai|Ioniq 5 (with HDA II) 2022-24|Highway Driving Assist II|[Upstream](#upstream)|
|Hyundai|Ioniq 5 (without HDA II) 2022-24|Highway Driving Assist|[Upstream](#upstream)|
|Hyundai|Ioniq 6 (with HDA II) 2023-24|Highway Driving Assist II|[Upstream](#upstream)|
|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Ioniq Electric 2020|All|[Upstream](#upstream)|
|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Ioniq Plug-in Hybrid 2020-22|All|[Upstream](#upstream)|
|Hyundai|Kona 2020|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona 2022-23|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona Electric 2022-23|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona Electric (with HDA II, Korea only) 2023|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona Electric Non-SCC 2019|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona Non-SCC 2019|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Hyundai|Nexo 2021|All|[Upstream](#upstream)|
|Hyundai|Palisade 2020-22|All|[Upstream](#upstream)|
|Hyundai|Palisade 2023-24|Highway Driving Assist II|[Community](#community)|
|Hyundai|Santa Cruz 2022-24|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Santa Fe 2019-20|All|[Upstream](#upstream)|
|Hyundai|Santa Fe 2021-23|All|[Upstream](#upstream)|
|Hyundai|Santa Fe Hybrid 2022-23|All|[Upstream](#upstream)|
|Hyundai|Santa Fe Plug-in Hybrid 2022-23|All|[Upstream](#upstream)|
|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Sonata 2020-23|All|[Upstream](#upstream)|
|Hyundai|Sonata Hybrid 2020-23|All|[Upstream](#upstream)|
|Hyundai|Staria 2023|All|[Upstream](#upstream)|
|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Tucson 2022|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Tucson 2023-24|All|[Upstream](#upstream)|
|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Tucson Hybrid 2022-24|All|[Upstream](#upstream)|
|Hyundai|Tucson Plug-in Hybrid 2024|All|[Upstream](#upstream)|
|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Kia|Carnival 2022-24|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Carnival (China only) 2023|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Ceed 2019-21|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Ceed Plug-in Hybrid Non-SCC 2022|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Kia|EV6 (Southeast Asia only) 2022-24|All|[Upstream](#upstream)|
|Kia|EV6 (with HDA II) 2022-24|Highway Driving Assist II|[Upstream](#upstream)|
|Kia|EV6 (without HDA II) 2022-24|Highway Driving Assist|[Upstream](#upstream)|
|Kia|Forte 2019-21|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Forte 2022-23|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Forte Non-SCC 2019|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Kia|Forte Non-SCC 2021|No Smart Cruise Control (Non-SCC)|[Dashcam mode](#dashcam)|
|Kia|K5 2021-24|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|K5 Hybrid 2020-22|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|K8 Hybrid (with HDA II) 2023|Highway Driving Assist II|[Upstream](#upstream)|
|Kia|Niro EV 2019|All|[Upstream](#upstream)|
|Kia|Niro EV 2020|All|[Upstream](#upstream)|
|Kia|Niro EV 2021|All|[Upstream](#upstream)|
|Kia|Niro EV 2022|All|[Upstream](#upstream)|
|Kia|Niro EV (with HDA II) 2025|Highway Driving Assist II|[Upstream](#upstream)|
|Kia|Niro EV (without HDA II) 2023-25|All|[Upstream](#upstream)|
|Kia|Niro Hybrid 2018|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Niro Hybrid 2021|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Niro Hybrid 2022|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Niro Hybrid 2023|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Niro Plug-in Hybrid 2018-19|All|[Upstream](#upstream)|
|Kia|Niro Plug-in Hybrid 2020|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Niro Plug-in Hybrid 2021|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Niro Plug-in Hybrid 2022|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Optima 2017|Advanced Smart Cruise Control|[Upstream](#upstream)|
|Kia|Optima 2019-20|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Optima Hybrid 2017|Advanced Smart Cruise Control|[Dashcam mode](#dashcam)|
|Kia|Optima Hybrid 2019|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Seltos 2021|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Seltos Non-SCC 2023-24|No Smart Cruise Control (Non-SCC)|[Dashcam mode](#dashcam)|
|Kia|Sorento 2018|Advanced Smart Cruise Control & LKAS|[Upstream](#upstream)|
|Kia|Sorento 2019|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Sorento 2021-23|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Sorento Hybrid 2021-23|All|[Upstream](#upstream)|
|Kia|Sorento Plug-in Hybrid 2022-23|All|[Upstream](#upstream)|
|Kia|Sportage 2023-24|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Sportage Hybrid 2023|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Stinger 2022-23|All|[Upstream](#upstream)|
|Kia|Telluride 2020-22|All|[Upstream](#upstream)|
|Kia|Telluride 2023-24|Highway Driving Assist II|[Community](#community)|
|Lexus|CT Hybrid 2017-18|Lexus Safety System+|[Upstream](#upstream)|
|Lexus|ES 2017-18|All|[Upstream](#upstream)|
|Lexus|ES 2019-25|All|[Upstream](#upstream)|
|Lexus|ES Hybrid 2017-18|All|[Upstream](#upstream)|
|Lexus|ES Hybrid 2019-25|All|[Upstream](#upstream)|
|Lexus|GS F 2016|All|[Upstream](#upstream)|
|Lexus|IS 2017-19|All|[Upstream](#upstream)|
|Lexus|IS 2022-24|All|[Upstream](#upstream)|
|Lexus|LC 2024-25|All|[Upstream](#upstream)|
|Lexus|LS 2018|All except Lexus Safety System+ A|[Upstream](#upstream)|
|Lexus|NS 2022-25|All|[Not compatible](#can-bus-security)|
|Lexus|NX 2018-19|All|[Upstream](#upstream)|
|Lexus|NX 2020-21|All|[Upstream](#upstream)|
|Lexus|NX Hybrid 2018-19|All|[Upstream](#upstream)|
|Lexus|NX Hybrid 2020-21|All|[Upstream](#upstream)|
|Lexus|RC 2018-20|All|[Upstream](#upstream)|
|Lexus|RC 2023|All|[Upstream](#upstream)|
|Lexus|RX 2016|Lexus Safety System+|[Upstream](#upstream)|
|Lexus|RX 2017-19|All|[Upstream](#upstream)|
|Lexus|RX 2020-22|All|[Upstream](#upstream)|
|Lexus|RX Hybrid 2016|Lexus Safety System+|[Upstream](#upstream)|
|Lexus|RX Hybrid 2017-19|All|[Upstream](#upstream)|
|Lexus|RX Hybrid 2020-22|All|[Upstream](#upstream)|
|Lexus|UX Hybrid 2019-24|All|[Upstream](#upstream)|
|Lincoln|Aviator 2020-24|Co-Pilot360 Plus|[Upstream](#upstream)|
|Lincoln|Aviator Plug-in Hybrid 2020-24|Co-Pilot360 Plus|[Upstream](#upstream)|
|MAN|eTGE 2020-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|MAN|TGE 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Mazda|3 2017-18|All|[Dashcam mode](#dashcam)|
|Mazda|6 2017-20|All|[Dashcam mode](#dashcam)|
|Mazda|CX-5 2017-21|All|[Dashcam mode](#dashcam)|
|Mazda|CX-5 2022-25|All|[Upstream](#upstream)|
|Mazda|CX-9 2016-20|All|[Dashcam mode](#dashcam)|
|Mazda|CX-9 2021-23|All|[Upstream](#upstream)|
|Nissan|Altima 2019-20, 2024|ProPILOT Assist|[Upstream](#upstream)|
|Nissan|Leaf 2018-23|ProPILOT Assist|[Upstream](#upstream)|
|Nissan|Rogue 2018-20|ProPILOT Assist|[Upstream](#upstream)|
|Nissan|X-Trail 2017|ProPILOT Assist|[Upstream](#upstream)|
|Peugeot|208 2019-25|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|Porsche|Macan 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|Ram|1500 2019-24|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Ram|2500 2020-24|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Ram|3500 2019-22|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Rivian|R1S 2022-24|All|[Upstream](#upstream)|
|Rivian|R1T 2022-24|All|[Upstream](#upstream)|
|SEAT|Alhambra 2018-20|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Subaru|Ascent 2019-21|All|[Upstream](#upstream)|
|Subaru|Ascent 2023|All|[Dashcam mode](#dashcam)|
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Crosstrek Hybrid 2020|EyeSight Driver Assistance|[Dashcam mode](#dashcam)|
|Subaru|Forester 2017-18|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Forester 2019-21|All|[Upstream](#upstream)|
|Subaru|Forester 2022-24|All|[Dashcam mode](#dashcam)|
|Subaru|Forester Hybrid 2020|EyeSight Driver Assistance|[Dashcam mode](#dashcam)|
|Subaru|Impreza 2017-19|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Impreza 2020-22|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Legacy 2015-18|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Legacy 2020-22|All|[Upstream](#upstream)|
|Subaru|Outback 2015-17|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Outback 2018-19|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Outback 2020-22|All|[Upstream](#upstream)|
|Subaru|Outback 2023|All|[Dashcam mode](#dashcam)|
|Subaru|Solterra 2023-25|All|[Not compatible](#can-bus-security)|
|Subaru|XV 2018-19|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|XV 2020-21|EyeSight Driver Assistance|[Upstream](#upstream)|
|Škoda|Fabia 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Škoda|Kamiq 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Škoda|Karoq 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Škoda|Kodiaq 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Škoda|Octavia 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Škoda|Octavia RS 2016|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Škoda|Octavia Scout 2017-19|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Škoda|Scala 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Škoda|Superb 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Tesla|Model 3 (with HW3) 2019-23|All|[Upstream](#upstream)|
|Tesla|Model 3 (with HW4) 2024-25|All|[Upstream](#upstream)|
|Tesla|Model X (with HW4) 2024|All|[Community](community)|
|Tesla|Model Y (with HW3) 2020-23|All|[Upstream](#upstream)|
|Tesla|Model Y (with HW4) 2024-25|All|[Upstream](#upstream)|
|Toyota|Alphard 2019-20|All|[Upstream](#upstream)|
|Toyota|Alphard Hybrid 2021|All|[Upstream](#upstream)|
|Toyota|Avalon 2016|Toyota Safety Sense P|[Upstream](#upstream)|
|Toyota|Avalon 2017-18|All|[Upstream](#upstream)|
|Toyota|Avalon 2019-21|All|[Upstream](#upstream)|
|Toyota|Avalon 2022|All|[Upstream](#upstream)|
|Toyota|Avalon Hybrid 2019-21|All|[Upstream](#upstream)|
|Toyota|Avalon Hybrid 2022|All|[Upstream](#upstream)|
|Toyota|bZ4x 2023-25|All|[Not compatible](#can-bus-security)|
|Toyota|C-HR 2017-20|All|[Upstream](#upstream)|
|Toyota|C-HR 2021|All|[Upstream](#upstream)|
|Toyota|C-HR Hybrid 2017-20|All|[Upstream](#upstream)|
|Toyota|C-HR Hybrid 2021-22|All|[Upstream](#upstream)|
|Toyota|Camry 2018-20|All|[Upstream](#upstream)|
|Toyota|Camry 2021-24|All|[Upstream](#upstream)|
|Toyota|Camry 2025|All|[Not compatible](#can-bus-security)|
|Toyota|Camry Hybrid 2018-20|All|[Upstream](#upstream)|
|Toyota|Camry Hybrid 2021-24|All|[Upstream](#upstream)|
|Toyota|Corolla 2017-19|All|[Upstream](#upstream)|
|Toyota|Corolla 2020-22|All|[Upstream](#upstream)|
|Toyota|Corolla Cross 2022-25|All|[Not compatible](#can-bus-security)|
|Toyota|Corolla Cross (Non-US only) 2020-23|All|[Upstream](#upstream)|
|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|[Upstream](#upstream)|
|Toyota|Corolla Hatchback 2019-22|All|[Upstream](#upstream)|
|Toyota|Corolla Hybrid 2020-22|All|[Upstream](#upstream)|
|Toyota|Corolla Hybrid (South America only) 2020-23|All|[Upstream](#upstream)|
|Toyota|Highlander 2017-19|All|[Upstream](#upstream)|
|Toyota|Highlander 2020-23|All|[Upstream](#upstream)|
|Toyota|Highlander 2025|All|[Not compatible](#can-bus-security)|
|Toyota|Highlander Hybrid 2017-19|All|[Upstream](#upstream)|
|Toyota|Highlander Hybrid 2020-23|All|[Upstream](#upstream)|
|Toyota|Mirai 2021|All|[Upstream](#upstream)|
|Toyota|Prius 2016|Toyota Safety Sense P|[Upstream](#upstream)|
|Toyota|Prius 2017-20|All|[Upstream](#upstream)|
|Toyota|Prius 2021-22|All|[Upstream](#upstream)|
|Toyota|Prius Prime 2017-20|All|[Upstream](#upstream)|
|Toyota|Prius Prime 2021-22|All|[Upstream](#upstream)|
|Toyota|Prius v 2017|Toyota Safety Sense P|[Upstream](#upstream)|
|Toyota|RAV4 2016|Toyota Safety Sense P|[Upstream](#upstream)|
|Toyota|RAV4 2017-18|All|[Upstream](#upstream)|
|Toyota|RAV4 2019-21|All|[Upstream](#upstream)|
|Toyota|RAV4 2022|All|[Upstream](#upstream)|
|Toyota|RAV4 2023-25|All|[Upstream](#upstream)|
|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|[Upstream](#upstream)|
|Toyota|RAV4 Hybrid 2017-18|All|[Upstream](#upstream)|
|Toyota|RAV4 Hybrid 2019-21|All|[Upstream](#upstream)|
|Toyota|RAV4 Hybrid 2022|All|[Upstream](#upstream)|
|Toyota|RAV4 Hybrid 2023-25|All|[Upstream](#upstream)|
|Toyota|RAV4 Prime 2021-23|All|[Custom](#secoc-cars-with-recoverable-keys)|
|Toyota|RAV4 Prime 2024-25|All|[Not compatible](#can-bus-security)|
|Toyota|Sequoia 2023-25|All|[Not compatible](#can-bus-security)|
|Toyota|Sienna 2018-20|All|[Upstream](#upstream)|
|Toyota|Sienna 2021-23|All|[Custom](#secoc-cars-with-recoverable-keys)|
|Toyota|Sienna 2024-25|All|[Not compatible](#can-bus-security)|
|Toyota|Tundra 2022-25|All|[Not compatible](#can-bus-security)|
|Toyota|Venza 2021-25|All|[Not compatible](#can-bus-security)|
|Toyota|Yaris (Non-US only) 2020, 2023|All|[Custom](#secoc-cars-with-recoverable-keys)|
|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Arteon Shooting Brake 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Atlas Cross Sport 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Caddy 2019|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|Volkswagen|Caddy Maxi 2019|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|Volkswagen|California 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Crafter 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|e-Crafter 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Grand California 2019-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Jetta 2015-18|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|Volkswagen|Jetta 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Jetta GLI 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Passat 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Passat NMS 2017-22|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Sharan 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|T-Roc 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Taos 2022-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Tiguan 2018-24|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
# Types of Support
**iqdbc can support many more cars than it currently does.** There are a few reasons your car may not be supported.
If your car doesn't fit into any of the incompatibility criteria here, then there's a good chance it can be supported!
We're adding support for new cars all the time. **We don't have a roadmap for car support**, and in fact, most car
support comes from users like you!
## Upstream
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.
## Custom
Vehicles in this category are not considered plug-and-play. Software support is included in upstream openpilot, but
these vehicles might not have a harness in the comma store, or the physical install might be at an unusual or cumbersome
location, or they might need unusual configuration after install. These vehicles will not work with release builds of
openpilot, but depending on the situation, development builds or custom forks may allow their use.
### SecOC cars with recoverable keys
For a small subset of SecOC-protected vehicles, tools may be available in the community to recover the SecOC keys. These
tools, and the recovery process, are not part of openpilot. If supplied with a valid SecOC key, development builds or
custom forks may work with these vehicles. Release builds of openpilot don't support SecOC.
-117
View File
@@ -1,117 +0,0 @@
#!/usr/bin/env python3
import time
import threading
import argparse
import numpy as np
from pprint import pprint
from inputs import get_gamepad
from kbhit import KBHit
from iqdbc.car.structs import CarControl
from iqdbc.car.panda_runner import PandaRunner
class Keyboard:
def __init__(self):
self.kb = KBHit()
self.axis_increment = 0.05 # 5% of full actuation each key press
self.axes_map = {'w': 'gb', 's': 'gb',
'a': 'steer', 'd': 'steer'}
self.axes_values = {'gb': 0., 'steer': 0.}
self.axes_order = ['gb', 'steer']
self.cancel = False
def update(self):
key = self.kb.getch().lower()
print(key)
self.cancel = False
if key == 'r':
self.axes_values = {ax: 0. for ax in self.axes_values}
elif key == 'c':
self.cancel = True
elif key in self.axes_map:
axis = self.axes_map[key]
incr = self.axis_increment if key in ['w', 'a'] else -self.axis_increment
self.axes_values[axis] = float(np.clip(self.axes_values[axis] + incr, -1, 1))
else:
return False
return True
class Joystick:
def __init__(self, gamepad=False):
# TODO: find a way to get this from API, perhaps "inputs" doesn't support it
if gamepad:
self.cancel_button = 'BTN_NORTH' # (BTN_NORTH=X, ABS_RZ=Right Trigger)
accel_axis = 'ABS_Y'
steer_axis = 'ABS_RX'
else:
self.cancel_button = 'BTN_TRIGGER'
accel_axis = 'ABS_Y'
steer_axis = 'ABS_RX'
self.min_axis_value = {accel_axis: 0., steer_axis: 0.}
self.max_axis_value = {accel_axis: 255., steer_axis: 255.}
self.axes_values = {accel_axis: 0., steer_axis: 0.}
self.axes_order = [accel_axis, steer_axis]
self.cancel = False
def update(self):
joystick_event = get_gamepad()[0]
event = (joystick_event.code, joystick_event.state)
if event[0] == self.cancel_button:
if event[1] == 1:
self.cancel = True
elif event[1] == 0: # state 0 is falling edge
self.cancel = False
elif event[0] in self.axes_values:
self.max_axis_value[event[0]] = max(event[1], self.max_axis_value[event[0]])
self.min_axis_value[event[0]] = min(event[1], self.min_axis_value[event[0]])
norm = -float(np.interp(event[1], [self.min_axis_value[event[0]], self.max_axis_value[event[0]]], [-1., 1.]))
self.axes_values[event[0]] = norm if abs(norm) > 0.05 else 0. # center can be noisy, deadzone of 5%
else:
return False
return True
def joystick_thread(joystick):
while True:
joystick.update()
def main(joystick):
threading.Thread(target=joystick_thread, args=(joystick,), daemon=True).start()
with PandaRunner() as p:
CC = CarControl(enabled=False)
while True:
CC.actuators.accel = float(4.0*np.clip(joystick.axes_values['gb'], -1, 1))
CC.actuators.torque = float(np.clip(joystick.axes_values['steer'], -1, 1))
pprint(CC)
p.read()
p.write(CC)
# 100Hz
time.sleep(0.01)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Test the car interface with a joystick. Uses keyboard by default.',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--mode', choices=['keyboard', 'gamepad', 'joystick'], default='keyboard')
args = parser.parse_args()
print()
joystick: Keyboard | Joystick
if args.mode == 'keyboard':
print('Gas/brake control: `W` and `S` keys')
print('Steering control: `A` and `D` keys')
print('Buttons')
print('- `R`: Resets axes')
print('- `C`: Cancel cruise control')
joystick = Keyboard()
else:
joystick = Joystick(gamepad=(args.mode == 'gamepad'))
main(joystick)
-60
View File
@@ -1,60 +0,0 @@
#!/usr/bin/env python3
import sys
import termios
import atexit
from select import select
STDIN_FD = sys.stdin.fileno()
class KBHit:
def __init__(self) -> None:
self.set_kbhit_terminal()
def set_kbhit_terminal(self) -> None:
# Save the terminal settings
self.old_term = termios.tcgetattr(STDIN_FD)
self.new_term = self.old_term.copy()
# New terminal setting unbuffered
self.new_term[3] &= ~(termios.ICANON | termios.ECHO)
termios.tcsetattr(STDIN_FD, termios.TCSAFLUSH, self.new_term)
# Support normal-terminal reset at exit
atexit.register(self.set_normal_term)
def set_normal_term(self) -> None:
termios.tcsetattr(STDIN_FD, termios.TCSAFLUSH, self.old_term)
@staticmethod
def getch() -> str:
return sys.stdin.read(1)
@staticmethod
def getarrow() -> int:
c = sys.stdin.read(3)[2]
vals = [65, 67, 66, 68]
return vals.index(ord(c))
@staticmethod
def kbhit():
''' Returns True if keyboard character was hit, False otherwise.
'''
return select([sys.stdin], [], [], 0)[0] != []
if __name__ == "__main__":
kb = KBHit()
print('Hit any key, or ESC to exit')
while True:
if kb.kbhit():
c = kb.getch()
if c == '\x1b': # ESC
break
print(c)
kb.set_normal_term()
+1 -1
View File
@@ -198,7 +198,7 @@ def get_checksum_state(dbc_name: str) -> ChecksumState | None:
return ChecksumState(8, -1, 7, -1, False, SignalType.FCA_GIORGIO_CHECKSUM, fca_giorgio_checksum)
elif dbc_name.startswith("comma_body"):
return ChecksumState(8, 4, 7, 3, False, SignalType.BODY_CHECKSUM, body_checksum)
elif dbc_name.startswith("tesla_model3_party"):
elif dbc_name.startswith(("tesla_model3_party", "tesla_model3_vehicle")):
return ChecksumState(8, -1, 0, -1, True, SignalType.TESLA_CHECKSUM, tesla_checksum, tesla_setup_signal)
elif dbc_name.startswith("psa_"):
return ChecksumState(4, 4, 7, 3, False, SignalType.PSA_CHECKSUM, psa_checksum)
+4 -4
View File
@@ -1,5 +1,6 @@
import math
import numbers
import time
from collections import defaultdict, deque
from dataclasses import dataclass, field
@@ -154,7 +155,7 @@ class CANParser:
self.last_nonempty_nanos: int = 0
self._last_update_nanos: int = 0
def _add_message(self, name_or_addr: str | int, freq: int | None = None) -> None:
def _add_message(self, name_or_addr: str | int, freq: int | None = None, ignore_counter: bool = False) -> None:
if isinstance(name_or_addr, numbers.Number):
msg = self.dbc.addr_to_msg.get(int(name_or_addr))
else:
@@ -171,16 +172,15 @@ class CANParser:
self.vl_all[msg.name] = self.vl_all[msg.address]
self.ts_nanos[msg.address] = {s: 0 for s in signal_names}
self.ts_nanos[msg.name] = self.ts_nanos[msg.address]
self.dat[msg.address] = b""
self.dat[msg.name] = b""
state = MessageState(
address=msg.address,
name=msg.name,
size=msg.size,
signals=list(msg.sigs.values()),
ignore_alive=freq is not None and math.isnan(freq),
ignore_counter=ignore_counter,
)
state.first_seen_nanos = time.monotonic_ns()
if freq is not None and freq > 0:
state.frequency = freq
else:
@@ -67,6 +67,32 @@ class TestCanParserPacker:
parser.update([t, [msg]])
assert parser.can_valid
def test_lazy_add_not_ignore_alive(self):
"""
Accessing an undeclared message via parser.vl[...] lazily adds it via
_add_message(key) with the default freq=None, which is NOT the same as
declaring it with math.nan (ignore_alive=True). It's treated as "assume
~1Hz, must be seen within ~10s" — so if that message is never fed, the
parser is permanently invalid. Declaring an optional/rarely-sent message
with math.nan (or gating the .vl[...] read entirely) is required to avoid
this; see iqdbc/car/volkswagen/carstate.py's Diagnose_1/EPB_1 bugs.
"""
parser = CANParser(TEST_DBC, [], 0)
assert parser.can_valid
# lazily add STEERING_CONTROL by reading it, without ever declaring it
# or feeding any CAN data for it
_ = parser.vl["STEERING_CONTROL"]
state = parser.message_states[parser.dbc.name_to_msg["STEERING_CONTROL"].address]
assert not state.ignore_alive
# never becomes valid again, no matter how many times it's checked
# (can_valid debounces over MAX_BAD_COUNTER reads before flipping false)
for _ in range(MAX_BAD_COUNTER):
parser.can_valid
for _ in range(20):
assert not parser.can_valid
def test_parser_updated_list(self):
msgs = [("CAN_FD_MESSAGE", 10), ]
parser = CANParser(TEST_DBC, msgs, 0)
-74
View File
@@ -1,74 +0,0 @@
<!--- AUTOGENERATED FROM selfdrive/car/CARS_template.md, DO NOT EDIT. --->
# Support Information for {{all_car_docs | length}} Known Cars
|{{ExtraCarsColumn | map(attribute='value') | join('|') | replace(hardware_col_name, wide_hardware_col_name)}}|
|---|---|---|{% for _ in range((ExtraCarsColumn | length) - 3) %}{{':---:|'}}{% endfor +%}
{% for car_docs in all_car_docs %}
|{% for column in ExtraCarsColumn %}{{car_docs.get_extra_cars_column(column)}}|{% endfor %}
{% endfor %}
# Types of Support
**iqdbc can support many more cars than it currently does.** There are a few reasons your car may not be supported.
If your car doesn't fit into any of the incompatibility criteria here, then there's a good chance it can be supported!
We're adding support for new cars all the time. **We don't have a roadmap for car support**, and in fact, most car
support comes from users like you!
## Upstream
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.
## Under Review
A vehicle under review is one for which software support has been merged into upstream openpilot, but hasn't yet been
tested for drive quality and conformance with [comma safety guidelines](https://github.com/commaai/openpilot/blob/master/docs/SAFETY.md).
This is a normal part of the development and quality assurance process. This vehicle will not work when upstream
openpilot is installed, but custom forks may allow their use.
## Custom
Vehicles in this category are not considered plug-and-play. Software support is included in upstream openpilot, but
these vehicles might not have a harness in the comma store, or the physical install might be at an unusual or cumbersome
location, or they might need unusual configuration after install. These vehicles will not work with release builds of
openpilot, but depending on the situation, development builds or custom forks may allow their use.
### SecOC cars with recoverable keys
For a small subset of SecOC-protected vehicles, tools may be available in the community to recover the SecOC keys. These
tools, and the recovery process, are not part of openpilot. If supplied with a valid SecOC key, development builds or
custom forks may work with these vehicles. Release builds of openpilot don't support SecOC.
## Dashcam
Dashcam vehicles have software support in upstream openpilot, but will go into "dashcam mode" at startup and will not
engage. This may be due to known issues with driving safety or quality, or it may be a work in progress that isn't yet
ready for safety and quality review.
## Community
Although they're not upstream, the community has openpilot running on other makes and models. See the 'Community
Supported Models' section of each make [on our wiki](https://wiki.comma.ai/).
Some notable works-in-progress:
* Honda
* 2022-24 Acura RDX, commaai/iqdbc#1967
* Camera ACC stability improvements, commaai/iqdbc#2192
* Alpha longitudinal stability improvements, commaai/iqdbc#2347 and commaai/iqdbc#2165
## Incompatible
### CAN Bus Security
Vehicles with CAN security measures, such as AUTOSAR Secure Onboard Communication (SecOC) are not usable with openpilot
unless the owner can recover the message signing key and implement CAN message signing. Examples include certain newer
Toyota, and the GM Global B platform.
### FlexRay
All the cars that openpilot supports use a [CAN bus](https://en.wikipedia.org/wiki/CAN_bus) for communication between all the car's computers, however a
CAN bus isn't the only way that the computers in your car can communicate. Most, if not all, vehicles from the following
manufacturers use [FlexRay](https://en.wikipedia.org/wiki/FlexRay) instead of a CAN bus: **BMW, Mercedes, Audi, Land Rover, and some Volvo**. These cars
may one day be supported, but we have no immediate plans to support FlexRay.
+66 -9
View File
@@ -238,6 +238,42 @@ struct CarState {
fuelTankLevelL @63 :Float32; # raw fuel tank level in liters (konn3kt: VW PQ Kombi_1.Tankinhalt)
batteryDetails @65 :BatteryDetails;
# carrotpilot HKG extension state
vCluRatio @68 :Float32;
logCarrot @69 :Text;
softHoldActive @70 :Int16;
activateCruise @71 :Int16;
latEnabled @72 :Bool;
pcmCruiseGap @73 :Int16;
speedLimit @74 :Float32;
speedLimitDistance @75 :Float32;
gearStep @76 :Int16;
tpms @77 :Tpms;
useLaneLineSpeed @78 :Float32;
leftLatDist @79 :Float32;
rightLatDist @80 :Float32;
leftLongDist @81 :Float32;
rightLongDist @82 :Float32;
carrotCruise @83 :Int16;
leftLaneLine @84 :Int16;
rightLaneLine @85 :Int16;
datetime @86 :UInt64;
leftRearLongDist @87 :Float32;
rightRearLongDist @88 :Float32;
leftRearLatDist @89 :Float32;
rightRearLatDist @90 :Float32;
trailerConnected @91 :Bool;
ureaGauge @92 :Float32;
evModeActive @93 :Bool;
evModeValid @94 :Bool;
struct Tpms {
fl @0 :Float32;
fr @1 :Float32;
rl @2 :Float32;
rr @3 :Float32;
}
struct BatteryDetails {
capacity @0 :Float32;
charge @1 :Float32;
@@ -310,8 +346,8 @@ struct CarState {
# deprecated
errorsDEPRECATED @0 :List(OnroadEventDEPRECATED.EventName);
gasDEPRECATED @3 :Float32; # this is user pedal only
brakeLightsDEPRECATED @19 :Bool;
gas @3 :Float32; # this is user pedal only
brakeLights @19 :Bool;
steeringRateLimitedDEPRECATED @29 :Bool;
canMonoTimesDEPRECATED @12: List(UInt64);
canRcvTimeoutDEPRECATED @49 :Bool;
@@ -349,6 +385,18 @@ struct RadarData @0x888ad6581cf0aacb {
# some radars flag measurements VS estimates
measured @6 :Bool;
vLead @7 :Float32; # m/s
aLead @8 :Float32; # m/s^2
jLead @9 :Float32; # m/s^3
radarSource @10 :RadarSource;
enum RadarSource {
frontRadar @0;
scc @1;
corner235 @2;
corner180 @3;
}
}
enum ErrorDEPRECATED {
@@ -404,6 +452,8 @@ struct CarControl {
brake @1: Float32; # [0.0, 1.0]
torqueOutputCan @8: Float32; # value sent over can to the car
speed @6: Float32; # m/s
jerk @9: Float32; # m/s^3
aTarget @10: Float32; # m/s^2
enum LongControlState @0xe40f3a917d908282{
off @0;
@@ -439,6 +489,12 @@ struct CarControl {
leadFollowTime @11: Float32;
leadDistance @12: Float32;
driverUnresponsive @13: Bool;
activeCarrot @14: Int16;
leadRelSpeed @15: Float32;
leadDPath @16: Float32;
leadRadar @17: Int16;
modelDesire @18: Int16;
atcDistance @19: Float32;
# not used with the dash, TODO: separate structs for dash UI and device UI
audibleAlert @5: AudibleAlert;
@@ -502,6 +558,7 @@ struct CarParams {
enableBsm @56 :Bool; # blind spot monitoring
flags @64 :UInt32; # flags for car specific quirks
alphaLongitudinalAvailable @71 :Bool;
extFlags @79 :UInt32; # carrotpilot HKG extension flags
minEnableSpeed @7 :Float32;
minSteerSpeed @8 :Float32;
@@ -538,14 +595,9 @@ struct CarParams {
steerLimitAlert @28 :Bool;
steerLimitTimer @47 :Float32; # time before steerLimitAlert is issued
vEgoStopping @29 :Float32; # Speed at which the car goes into stopping state
vEgoStarting @59 :Float32; # Speed at which the car goes into starting state
steerControlType @34 :SteerControlType;
radarUnavailable @35 :Bool; # True when radar objects aren't visible on CAN or aren't parsed out
stopAccel @60 :Float32; # Required acceleration to keep vehicle stationary
stoppingDecelRate @52 :Float32; # m/s^2/s while trying to stop
startAccel @32 :Float32; # Required acceleration to get car moving
startingState @70 :Bool; # Does this car make use of special starting state
steerActuatorDelay @36 :Float32; # Steering wheel actuator delay in seconds
longitudinalActuatorDelay @58 :Float32; # Gas/Brake actuator delay in seconds
@@ -603,7 +655,7 @@ struct CarParams {
kpV @1 :List(Float32);
kiBP @2 :List(Float32);
kiV @3 :List(Float32);
kfDEPRECATED @6 :Float32;
kf @6 :Float32;
deadzoneBPDEPRECATED @4 :List(Float32);
deadzoneVDEPRECATED @5 :List(Float32);
}
@@ -776,6 +828,11 @@ struct CarParams {
maxSteeringAngleDegDEPRECATED @54 :Float32;
longitudinalActuatorDelayLowerBoundDEPRECATED @61 :Float32;
stoppingControlDEPRECATED @31 :Bool; # Does the car allow full control even at lows speeds when stopping
radarTimeStepDEPRECATED @45: Float32 = 0.05; # time delta between radar updates, 20Hz is very standard
radarTimeStep @45: Float32 = 0.05; # time delta between radar updates, 20Hz is very standard
enableDsuDEPRECATED @5 :Bool; # driving support unit
vEgoStarting @59 :Float32;
startAccel @32 :Float32;
startingState @70 :Bool;
vEgoStoppingDEPRECATED @29 :Float32;
stoppingDecelRateDEPRECATED @52 :Float32;
}
+1 -1
View File
@@ -87,7 +87,7 @@ def fingerprint(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_mu
cached_params: CarParamsT | None,
fixed_fingerprint: str | None) -> tuple[str | None, dict, str, list[CarParams.CarFw], CarParams.FingerprintSource, bool]:
fixed_fingerprint = fixed_fingerprint or os.environ.get('FINGERPRINT', "")
skip_fw_query = os.environ.get('SKIP_FW_QUERY', False)
skip_fw_query = os.environ.get('SKIP_FW_QUERY', False) or bool(fixed_fingerprint)
disable_fw_cache = os.environ.get('DISABLE_FW_CACHE', False)
ecu_rx_addrs = set()
@@ -5,16 +5,16 @@ from iqdbc.car.chrysler import chryslercan
from iqdbc.car.chrysler.values import RAM_CARS, CarControllerParams, ChryslerFlags, RAM_DT
from iqdbc.car.interfaces import CarControllerBase
from iqdbc.lvbs.car.chrysler.carcontroller_ext import CarControllerExt
from iqdbc.lvbs.car.chrysler.iq_carcontroller import IQCarController
from iqdbc.lvbs.car.chrysler.aol import AolCarController
from iqdbc.lvbs.car.chrysler.values_ext import ChryslerFlagsIQ
from iqdbc.lvbs.car.chrysler.iq_values import ChryslerFlagsIQ
class CarController(CarControllerBase, AolCarController, CarControllerExt):
class CarController(CarControllerBase, AolCarController, IQCarController):
def __init__(self, dbc_names, CP, CP_IQ):
CarControllerBase.__init__(self, dbc_names, CP, CP_IQ)
AolCarController.__init__(self)
CarControllerExt.__init__(self, CP, CP_IQ)
IQCarController.__init__(self, CP, CP_IQ)
self.apply_torque_last = 0
self.hud_count = 0
@@ -58,7 +58,7 @@ class CarController(CarControllerBase, AolCarController, CarControllerExt):
# 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_IQ.flags & ChryslerFlagsIQ.NO_MIN_STEERING_SPEED or self.CP.carFingerprint in RAM_DT:
lkas_control_bit = CarControllerExt.get_lkas_control_bit(self, CS, CC, lkas_control_bit)
lkas_control_bit = IQCarController.get_lkas_control_bit(self, CS, CC, lkas_control_bit)
elif CS.out.vEgo > self.CP.minSteerSpeed:
lkas_control_bit = True
elif self.CP.flags & ChryslerFlags.HIGHER_MIN_STEERING_SPEED:
+9 -6
View File
@@ -4,17 +4,17 @@ from iqdbc.car.chrysler.values import DBC, STEER_THRESHOLD, RAM_CARS
from iqdbc.car.common.conversions import Conversions as CV
from iqdbc.car.interfaces import CarStateBase
from iqdbc.lvbs.car.chrysler.carstate_ext import CarStateExt
from iqdbc.lvbs.car.chrysler.aol import AolCarState
from iqdbc.lvbs.car.chrysler.iq_carstate import IQCarState
ButtonType = structs.CarState.ButtonEvent.Type
class CarState(CarStateBase, AolCarState, CarStateExt):
class CarState(CarStateBase, AolCarState, IQCarState):
def __init__(self, CP, CP_IQ):
CarStateBase.__init__(self, CP, CP_IQ)
AolCarState.__init__(self, CP, CP_IQ)
CarStateExt.__init__(self, CP, CP_IQ)
IQCarState.__init__(self, CP, CP_IQ)
self.CP = CP
can_define = CANDefine(DBC[CP.carFingerprint][Bus.pt])
@@ -102,7 +102,7 @@ class CarState(CarStateBase, AolCarState, CarStateExt):
self.button_counter = cp.vl["CRUISE_BUTTONS"]["COUNTER"]
AolCarState.update_aol(self, ret, can_parsers)
CarStateExt.update(self, ret, ret_iq, can_parsers)
IQCarState.update(self, ret, ret_iq, can_parsers)
ret.buttonEvents = [
*create_button_events(self.distance_button, prev_distance_button, {1: ButtonType.gapAdjustCruise}),
@@ -114,7 +114,10 @@ class CarState(CarStateBase, AolCarState, CarStateExt):
@staticmethod
def get_can_parsers(CP, CP_IQ):
pt_messages: list = []
cam_messages: list = []
AolCarState.get_parser(CP, pt_messages, cam_messages)
return {
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 0),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 2),
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], pt_messages, 0),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], cam_messages, 2),
}
@@ -1,9 +1,9 @@
""" AUTO-FORMATTED USING iqdbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
from iqdbc.car.structs import CarParams
from iqdbc.car.chrysler.values import CAR
from iqdbc.lvbs.car.iq_fingerprints import extend_fw_versions
from iqdbc.lvbs.car.chrysler.iq_fingerprints import FW_VERSIONS_EXT
from iqdbc.lvbs.car.fingerprints_ext import extend_fw_versions
from iqdbc.lvbs.car.chrysler.fingerprints_ext import FW_VERSIONS_EXT
Ecu = CarParams.Ecu
@@ -789,4 +789,6 @@ FW_VERSIONS = {
},
}
FW_VERSIONS = extend_fw_versions(FW_VERSIONS, FW_VERSIONS_EXT)
+3 -2
View File
@@ -5,7 +5,7 @@ from iqdbc.car.chrysler.carstate import CarState
from iqdbc.car.chrysler.radar_interface import RadarInterface
from iqdbc.car.chrysler.values import CAR, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags, ChryslerSafetyFlags
from iqdbc.car.interfaces import CarInterfaceBase
from iqdbc.lvbs.car.chrysler.values_ext import ChryslerFlagsIQ
from iqdbc.lvbs.car.chrysler.iq_values import ChryslerFlagsIQ
class CarInterface(CarInterfaceBase):
@@ -100,9 +100,10 @@ class CarInterface(CarInterfaceBase):
stock_cp.wheelbase = 3.79
stock_cp.steerRatio = 19.
# LKAS heartbeat on bus 0 (msg 0x4FF) means the camera is on the ADAS bus and
# IQ.Pilot can steer down to a standstill.
if 0x4FF in fingerprint[0]:
ret.flags |= ChryslerFlagsIQ.NO_MIN_STEERING_SPEED.value
stock_cp.minSteerSpeed = 0.
return ret
-2
View File
@@ -21,7 +21,6 @@ from iqdbc.car.extra_cars import CAR as EXTRA
EXTRA_CARS_MD_OUT = os.path.join(BASEDIR, "../", "../", "docs", "CARS.md")
EXTRA_CARS_MD_TEMPLATE = os.path.join(BASEDIR, "CARS_template.md")
# TODO: merge these platforms into normal car ports with SupportType flag
ExtraPlatform = Platform | EXTRA
@@ -106,7 +105,6 @@ if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Auto generates supportability info docs for all known cars",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("--template", default=EXTRA_CARS_MD_TEMPLATE, help="Override default template filename")
parser.add_argument("--out", default=EXTRA_CARS_MD_OUT, help="Override default generated filename")
args = parser.parse_args()
+1 -6
View File
@@ -5,17 +5,14 @@ from iqdbc.car.ford.fordcan import CanBus
from iqdbc.car.ford.values import DBC, CarControllerParams, FordFlags
from iqdbc.car.interfaces import CarStateBase
from iqdbc.lvbs.car.ford.aol import AolCarState
ButtonType = structs.CarState.ButtonEvent.Type
GearShifter = structs.CarState.GearShifter
TransmissionType = structs.CarParams.TransmissionType
class CarState(CarStateBase, AolCarState):
class CarState(CarStateBase):
def __init__(self, CP, CP_IQ):
CarStateBase.__init__(self, CP, CP_IQ)
AolCarState.__init__(self, CP, CP_IQ)
can_define = CANDefine(DBC[CP.carFingerprint][Bus.pt])
if CP.transmissionType == TransmissionType.automatic:
self.shifter_values = can_define.dv["PowertrainData_10"]["TrnRng_D_Rq"]
@@ -112,8 +109,6 @@ class CarState(CarStateBase, AolCarState):
self.acc_tja_status_stock_values = cp_cam.vl["ACCDATA_3"]
self.lkas_status_stock_values = cp_cam.vl["IPMA_Data"]
AolCarState.update_aol(self, ret, can_parsers)
ret.buttonEvents = [
*create_button_events(self.distance_button, prev_distance_button, {1: ButtonType.gapAdjustCruise}),
*create_button_events(self.lc_button, prev_lc_button, {1: ButtonType.lkas}),
+5 -5
View File
@@ -5,8 +5,8 @@ from iqdbc.car.common.conversions import Conversions as CV
from iqdbc.car.interfaces import CarStateBase
from iqdbc.car.gm.values import DBC, AccState, CruiseButtons, STEER_THRESHOLD, SDGM_CAR, ALT_ACCS
from iqdbc.lvbs.car.gm.carstate_ext import CarStateExt
from iqdbc.lvbs.car.gm.values_ext import GMFlagsIQ
from iqdbc.lvbs.car.gm.iq_carstate import IQCarState
from iqdbc.lvbs.car.gm.iq_values import GMFlagsIQ
ButtonType = structs.CarState.ButtonEvent.Type
TransmissionType = structs.CarParams.TransmissionType
@@ -18,10 +18,10 @@ BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.D
CruiseButtons.MAIN: ButtonType.mainCruise, CruiseButtons.CANCEL: ButtonType.cancel}
class CarState(CarStateBase, CarStateExt):
class CarState(CarStateBase, IQCarState):
def __init__(self, CP, CP_IQ):
CarStateBase.__init__(self, CP, CP_IQ)
CarStateExt.__init__(self, CP, CP_IQ)
IQCarState.__init__(self, CP, CP_IQ)
can_define = CANDefine(DBC[CP.carFingerprint][Bus.pt])
self.shifter_values = can_define.dv["ECMPRDNL2"]["PRNDL2"]
self.cluster_speed_hyst_gap = CV.KPH_TO_MS / 2.
@@ -163,7 +163,7 @@ class CarState(CarStateBase, CarStateExt):
if ret.vEgo < self.CP.minSteerSpeed:
ret.lowSpeedAlert = True
CarStateExt.update(self, ret, can_parsers)
IQCarState.update(self, ret, can_parsers)
return ret, ret_iq
+6 -3
View File
@@ -1,9 +1,8 @@
# ruff: noqa: E501
""" AUTO-FORMATTED USING iqdbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
from iqdbc.car.gm.values import CAR
from iqdbc.lvbs.car.fingerprints_ext import extend_fingerprints
from iqdbc.lvbs.car.gm.fingerprints_ext import FINGERPRINTS_EXT
from iqdbc.lvbs.car.iq_fingerprints import extend_fingerprints
from iqdbc.lvbs.car.gm.iq_fingerprints import FINGERPRINTS_EXT
# Trailblazer also matches as a SILVERADO, TODO: split with fw versions
# FIXME: There are Equinox users with different message lengths, specifically 304 and 320
@@ -54,6 +53,9 @@ FINGERPRINTS = {
}],
CAR.CHEVROLET_SILVERADO: [{
190: 6, 193: 8, 197: 8, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 460: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 534: 2, 560: 8, 562: 8, 563: 5, 565: 5, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 761: 7, 789: 5, 800: 6, 801: 8, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1611: 8, 1930: 7
},
{
190: 6, 193: 8, 197: 8, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 3, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 460: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 534: 2, 560: 8, 562: 8, 563: 5, 565: 5, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 761: 7, 789: 5, 800: 6, 801: 8, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1611: 8, 1930: 7
}],
CAR.CHEVROLET_EQUINOX: [{
190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 510: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1611: 8, 1930: 7
@@ -78,4 +80,5 @@ FINGERPRINTS = {
FW_VERSIONS: dict[str, dict[tuple, list[bytes]]] = {
}
FINGERPRINTS = extend_fingerprints(FINGERPRINTS, FINGERPRINTS_EXT)
+11 -15
View File
@@ -10,13 +10,13 @@ from iqdbc.car.gm.radar_interface import RadarInterface, RADAR_HEADER_MSG, CAMER
from iqdbc.car.gm.values import CAR, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, SDGM_CAR, ALT_ACCS, CanBus, GMSafetyFlags
from iqdbc.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, LateralAccelFromTorqueCallbackType
from iqdbc.lvbs.car.gm.interface_ext import CarInterfaceExt
from iqdbc.lvbs.car.gm.values_ext import GMFlagsIQ, GMSafetyFlagsIQ
from iqdbc.lvbs.car.gm.iq_interface import IQCarInterface
from iqdbc.lvbs.car.gm.iq_values import GMFlagsIQ, GMSafetyFlagsIQ
TransmissionType = structs.CarParams.TransmissionType
NetworkLocation = structs.CarParams.NetworkLocation
# iqpilot-specific torque parameters for Bolt cars that actually use the d parameter
# Bolt Non-ACC uses the 4th (d) tune parameter; stock tunes zero it out.
NON_LINEAR_TORQUE_PARAMS_IQ = {
CAR.CHEVROLET_BOLT_NON_ACC: [2.24, 1.1, 0.28, -0.07],
CAR.CHEVROLET_BOLT_NON_ACC_1ST_GEN: [1.8, 1.1, 0.3, -0.045],
@@ -30,7 +30,7 @@ NON_LINEAR_TORQUE_PARAMS = {
}
class CarInterface(CarInterfaceBase, CarInterfaceExt):
class CarInterface(CarInterfaceBase, IQCarInterface):
CarState = CarState
CarController = CarController
RadarInterface = RadarInterface
@@ -40,7 +40,7 @@ class CarInterface(CarInterfaceBase, CarInterfaceExt):
def __init__(self, CP, CP_IQ):
CarInterfaceBase.__init__(self, CP, CP_IQ)
CarInterfaceExt.__init__(self, CP, CarInterfaceBase)
IQCarInterface.__init__(self, CP, CarInterfaceBase)
@staticmethod
def get_pid_accel_limits(CP, CP_IQ, current_speed, cruise_speed):
@@ -125,9 +125,6 @@ class CarInterface(CarInterfaceBase, CarInterfaceExt):
# Tuning for experimental long
ret.longitudinalTuning.kiV = [2.0, 1.5]
ret.stoppingDecelRate = 2.0 # reach brake quickly after enabling
ret.vEgoStopping = 0.25
ret.vEgoStarting = 0.25
if alpha_long:
ret.pcmCruise = False
@@ -244,11 +241,10 @@ class CarInterface(CarInterfaceBase, CarInterfaceExt):
CAR.CHEVROLET_BOLT_NON_ACC_2ND_GEN, CAR.CHEVROLET_TRAILBLAZER_NON_ACC_2ND_GEN):
stock_cp.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, stock_cp.lateralTuning)
elif candidate in (CAR.CHEVROLET_EQUINOX_NON_ACC_3RD_GEN, ):
CarInterfaceBase.configure_torque_tune(candidate, stock_cp.lateralTuning)
# NON_ACC vehicles should use camera car speed thresholds
# Non-ACC cars steer/long via the forward camera and pcmCruise, not the ASCM.
if ret.flags & GMFlagsIQ.NON_ACC:
stock_cp.dashcamOnly = False
stock_cp.alphaLongitudinalAvailable = False
@@ -257,13 +253,13 @@ class CarInterface(CarInterfaceBase, CarInterfaceExt):
stock_cp.pcmCruise = True
stock_cp.safetyConfigs[0].safetyParam |= GMSafetyFlags.HW_CAM.value
ret.iqSafetyFlags |= GMSafetyFlagsIQ.NON_ACC
stock_cp.minEnableSpeed = 24 * CV.MPH_TO_MS # 24 mph
stock_cp.minSteerSpeed = 3.0 # ~6 mph
stock_cp.minEnableSpeed = 24 * CV.MPH_TO_MS
stock_cp.minSteerSpeed = 3.0
# dashcamOnly platforms: untested platforms, need user validations
# Untested Non-ACC platforms ship dashcam-only pending user validation.
if candidate in (CAR.CHEVROLET_BOLT_NON_ACC_2ND_GEN, CAR.CHEVROLET_EQUINOX_NON_ACC_3RD_GEN,
CAR.CHEVROLET_SUBURBAN_NON_ACC_11TH_GEN, CAR.CADILLAC_CT6_NON_ACC_1ST_GEN, CAR.CHEVROLET_TRAILBLAZER_NON_ACC_2ND_GEN,
CAR.CADILLAC_XT5_NON_ACC_1ST_GEN):
CAR.CHEVROLET_SUBURBAN_NON_ACC_11TH_GEN, CAR.CADILLAC_CT6_NON_ACC_1ST_GEN,
CAR.CHEVROLET_TRAILBLAZER_NON_ACC_2ND_GEN, CAR.CADILLAC_XT5_NON_ACC_1ST_GEN):
stock_cp.dashcamOnly = True
return ret
+10 -17
View File
@@ -4,10 +4,9 @@ from enum import Enum, IntFlag
from iqdbc.car import Bus, PlatformConfig, DbcDict, Platforms, CarSpecs
from iqdbc.car.structs import CarParams
from iqdbc.car.docs_definitions import CarDocs, CarFootnote, CarHarness, CarParts, Column, SupportType
from iqdbc.lvbs.car.gm.iq_values import GMFlagsIQ
from iqdbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
from iqdbc.lvbs.car.gm.values_ext import GMFlagsIQ
Ecu = CarParams.Ecu
@@ -88,13 +87,6 @@ class GMCarDocs(CarDocs):
self.car_parts = CarParts.common([CarHarness.obd_ii])
@dataclass
class GMNonAccCarDocs(GMCarDocs):
package: str = "No Adaptive Cruise Control (Non-ACC)"
support_type: SupportType = SupportType.COMMUNITY
support_link: str = "community"
@dataclass(frozen=True, kw_only=True)
class GMCarSpecs(CarSpecs):
tireStiffnessFactor: float = 0.444 # not optimized yet
@@ -123,6 +115,13 @@ class GMSDGMPlatformConfig(GMPlatformConfig):
self.car_docs = []
@dataclass
class GMNonAccCarDocs(GMCarDocs):
package: str = "No Adaptive Cruise Control (Non-ACC)"
support_type: SupportType = SupportType.COMMUNITY
support_link: str = "community"
@dataclass
class GMNonSccPlatformConfig(GMPlatformConfig):
def init(self):
@@ -209,13 +208,7 @@ class CAR(Platforms):
GMCarSpecs(mass=2490, wheelbase=2.94, steerRatio=17.3, centerToFrontRatio=0.5, tireStiffnessFactor=1.0),
)
# port extensions
# Separate car def is required when there is no ASCM
# (for now) unless there is a way to detect it when it has been unplugged...
# CHEVROLET_VOLT_CC = GMNonSccPlatformConfig(
# [GMNonAccCarDocs("Chevrolet Volt LT 2017-18")],
# CHEVROLET_VOLT.specs,
# )
# IQ.Pilot Non-ACC camera-harness ports (no factory adaptive cruise).
CHEVROLET_BOLT_NON_ACC = GMNonSccPlatformConfig(
[GMNonAccCarDocs("Chevrolet Bolt EV Non-ACC 2017")],
CHEVROLET_BOLT_EUV.specs,
@@ -333,7 +326,7 @@ FW_QUERY_CONFIG = FwQueryConfig(
# TODO: detect most of these sets live
EV_CAR = {CAR.CHEVROLET_VOLT, CAR.CHEVROLET_VOLT_2019, CAR.CHEVROLET_BOLT_EUV,
# port extensions, Non-ACC
# Non-ACC EV ports
CAR.CHEVROLET_BOLT_NON_ACC, CAR.CHEVROLET_BOLT_NON_ACC_1ST_GEN, CAR.CHEVROLET_BOLT_NON_ACC_2ND_GEN}
# We're integrated at the camera with VOACC on these cars (instead of ASCM w/ OBD-II harness)
+4 -4
View File
@@ -10,7 +10,7 @@ from iqdbc.car.honda.values import CAR, DBC, STEER_THRESHOLD, HONDA_BOSCH, HONDA
HondaFlags, CruiseButtons, CruiseSettings, GearShifter, CarControllerParams
from iqdbc.car.interfaces import CarStateBase
from iqdbc.lvbs.car.honda.carstate_ext import CarStateExt
from iqdbc.lvbs.car.honda.iq_carstate import IQCarState
TransmissionType = structs.CarParams.TransmissionType
ButtonType = structs.CarState.ButtonEvent.Type
@@ -20,10 +20,10 @@ BUTTONS_DICT = {CruiseButtons.RES_ACCEL: ButtonType.accelCruise, CruiseButtons.D
SETTINGS_BUTTONS_DICT = {CruiseSettings.DISTANCE: ButtonType.gapAdjustCruise, CruiseSettings.LKAS: ButtonType.lkas}
class CarState(CarStateBase, CarStateExt):
class CarState(CarStateBase, IQCarState):
def __init__(self, CP, CP_IQ):
CarStateBase.__init__(self, CP, CP_IQ)
CarStateExt.__init__(self, CP, CP_IQ)
IQCarState.__init__(self, CP, CP_IQ)
can_define = CANDefine(DBC[CP.carFingerprint][Bus.pt])
if CP.transmissionType != TransmissionType.manual:
@@ -246,7 +246,7 @@ class CarState(CarStateBase, CarStateExt):
*create_button_events(self.cruise_setting, prev_cruise_setting, SETTINGS_BUTTONS_DICT),
]
CarStateExt.update(self, ret, can_parsers)
IQCarState.update(self, ret, can_parsers)
return ret, ret_iq
+3 -2
View File
@@ -2,8 +2,8 @@
from iqdbc.car.structs import CarParams
from iqdbc.car.honda.values import CAR
from iqdbc.lvbs.car.fingerprints_ext import extend_fw_versions
from iqdbc.lvbs.car.honda.fingerprints_ext import FW_VERSIONS_EXT
from iqdbc.lvbs.car.iq_fingerprints import extend_fw_versions
from iqdbc.lvbs.car.honda.iq_fingerprints import FW_VERSIONS_EXT
Ecu = CarParams.Ecu
@@ -994,6 +994,7 @@ FW_VERSIONS = {
b'8S102-T56-A070\x00\x00',
b'8S102-T60-AA10\x00\x00',
b'8S102-T64-A040\x00\x00',
b'8S102-T64-A050\x00\x00',
],
(Ecu.vsa, 0x18da28f1, None): [
b'57114-T20-AB40\x00\x00',
+1 -1
View File
@@ -2,7 +2,7 @@ from iqdbc.car import CanBusBase
from iqdbc.car.common.conversions import Conversions as CV
from iqdbc.car.honda.values import (HondaFlags, HONDA_BOSCH, HONDA_BOSCH_ALT_RADAR, HONDA_BOSCH_RADARLESS,
HONDA_BOSCH_CANFD, CarControllerParams)
from iqdbc.lvbs.car.honda.values_ext import HondaFlagsIQ
from iqdbc.lvbs.car.honda.iq_values import HondaFlagsIQ
# CAN bus layout with relay
# 0 = ACC-CAN - radar side
+10 -2
View File
@@ -11,7 +11,7 @@ from iqdbc.car.honda.carstate import CarState
from iqdbc.car.honda.radar_interface import RadarInterface
from iqdbc.car.interfaces import CarInterfaceBase
from iqdbc.lvbs.car.honda.values_ext import HondaFlagsIQ, HondaSafetyFlagsIQ
from iqdbc.lvbs.car.honda.iq_values import HondaFlagsIQ, HondaSafetyFlagsIQ
TransmissionType = structs.CarParams.TransmissionType
@@ -88,9 +88,11 @@ class CarInterface(CarInterfaceBase):
ret.steerActuatorDelay = 0.1
if candidate in HONDA_BOSCH:
ret.longitudinalActuatorDelay = 0.5 # s
if candidate in HONDA_BOSCH_RADARLESS:
ret.stopAccel = CarControllerParams.BOSCH_ACCEL_MIN # stock uses -4.0 m/s^2 once stopped but limited by safety model
ret.longitudinalActuatorDelay = 0.25 # s
else:
ret.longitudinalActuatorDelay = 0.5 # s
else:
# default longitudinal tuning for all hondas
ret.longitudinalTuning.kiBP = [0., 5., 35.]
@@ -335,6 +337,12 @@ class CarInterface(CarInterfaceBase):
stock_cp.autoResumeSng = stock_cp.autoResumeSng or ret.enableGasInterceptor
if candidate == CAR.HONDA_CITY_7G:
ret.longitudinalStoppingSpeedOverride = 2.0
ret.stoppingDecelRateOverride = 0.3
else:
ret.longitudinalStoppingSpeedOverride = 0.5
ret.stoppingDecelRateOverride = 0.1
return ret
+661 -115
View File
@@ -1,27 +1,47 @@
import numpy as np
from iqdbc.can import CANPacker
from iqdbc.car import Bus, DT_CTRL, make_tester_present_msg, structs
from iqdbc.car.lateral import apply_driver_steer_torque_limits, common_fault_avoidance
from iqdbc.car.lateral import apply_driver_steer_torque_limits, apply_std_steer_angle_limits, common_fault_avoidance
from iqdbc.car.common.conversions import Conversions as CV
from iqdbc.car.hyundai import hyundaicanfd, hyundaican
from iqdbc.car.hyundai.carstate import CarState
from iqdbc.car.hyundai.hyundaicanfd import CanBus
from iqdbc.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CAR
from iqdbc.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CAR, CAN_GEARS, HyundaiExtFlags
from iqdbc.car.interfaces import CarControllerBase
from iqdbc.lvbs.car.hyundai.escc import EsccCarController
from iqdbc.lvbs.car.hyundai.longitudinal.controller import LongitudinalController
from iqdbc.lvbs.car.hyundai.lead_data_ext import LeadDataCarController
from iqdbc.lvbs.car.hyundai.aol import AolCarController
from iqdbc.car.vehicle_model import VehicleModel
VisualAlert = structs.CarControl.HUDControl.VisualAlert
LongCtrlState = structs.CarControl.Actuators.LongControlState
from openpilot.common.params import Params
# EPS faults if you apply torque while the steering angle is above 90 degrees for more than 1 second
# All slightly below EPS thresholds to avoid fault
MAX_ANGLE = 85
MAX_ANGLE_FRAMES = 89
MAX_ANGLE_CONSECUTIVE_FRAMES = 2
DRIVER_TORQUE_FILTER_TAU = 0.12
PRE_OVERRIDE_PREDICTION_TIME = 0.15
PRE_OVERRIDE_START_RATIO = 0.90
PRE_OVERRIDE_FULL_RATIO = 1.05
PRE_OVERRIDE_RAW_MIN_RATIO = 0.70
PRE_OVERRIDE_FILTERED_MIN_RATIO = 0.65
PRE_OVERRIDE_MIN_RATE_RATIO = 0.50
PRE_OVERRIDE_CONFIRM_FRAMES = 2
PRE_OVERRIDE_MAX_TORQUE_DELTA = -10.0
LOW_SPEED_ANGLE_RATE_RAMP_SPEED = 15.0 * CV.KPH_TO_MS
MID_SPEED_ANGLE_RATE_LIMIT_SPEED = 40.0 * CV.KPH_TO_MS
vibrate_intervals = [
(0.0, 0.5),
(1.0, 1.5),
#(2.5, 3.0),
#(3.5, 4.0),
(5.0, 5.5),
(6.0, 6.5),
(7.5, 8.0),
]
def process_hud_alert(enabled, fingerprint, hud_control):
sys_warning = (hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw))
@@ -46,14 +66,75 @@ def process_hud_alert(enabled, fingerprint, hud_control):
return sys_warning, sys_state, left_lane_warning, right_lane_warning
def rate_limit(x, x_last, lo, hi):
return float(np.clip(x, x_last + lo, x_last + hi))
class CarController(CarControllerBase, EsccCarController, LeadDataCarController, LongitudinalController, AolCarController):
def __init__(self, dbc_names, CP, CP_IQ):
CarControllerBase.__init__(self, dbc_names, CP, CP_IQ)
EsccCarController.__init__(self, CP, CP_IQ)
AolCarController.__init__(self)
LeadDataCarController.__init__(self, CP)
LongitudinalController.__init__(self, CP, CP_IQ)
def apply_steer_angle_limits_physics(desired_sw_deg: float,
last_sw_deg: float,
v_ego: float,
steering_sw_deg: float,
lat_active: bool,
wheelbase_m: float,
steer_ratio: float,
steer_sw_max_deg: float,
model_v2=None) -> float:
max_lat_accel = 8.5 # m/s^2
max_lat_jerk = 4.0 # m/s^3
y_std_1s = 0.1
if model_v2 is not None and len(model_v2.position.yStd) > 10:
model_y_std_1s = float(model_v2.position.yStd[10])
if np.isfinite(model_y_std_1s) and model_y_std_1s >= 0.0:
y_std_1s = model_y_std_1s
max_sw_rate_deg_per_tick = float(np.interp(y_std_1s, [0.1, 0.2, 0.4], [2.0, 1.5, 0.8]))
v = max(float(v_ego), 1.0)
target_sw = float(np.clip(desired_sw_deg, -steer_sw_max_deg, steer_sw_max_deg))
if v_ego < MID_SPEED_ANGLE_RATE_LIMIT_SPEED:
# Keep low/mid-speed angle commands quieter without reducing LKAS_ANGLE_MAX_TORQUE.
# Allow the angle, but slow the arrival: 0~15 kph ramps 0.8->1.1 deg/tick,
# then 15~40 kph tapers 1.1->0.8 deg/tick. Above 40 kph, physics limits take over.
low_mid_speed_cap = float(np.interp(v_ego,
[0.0, LOW_SPEED_ANGLE_RATE_RAMP_SPEED, MID_SPEED_ANGLE_RATE_LIMIT_SPEED],
[0.8, 1.1, 0.8]))
max_sw_rate_deg_per_tick = min(max_sw_rate_deg_per_tick, low_mid_speed_cap)
target_rw = target_sw / steer_ratio
last_rw = float(last_sw_deg) / steer_ratio
# --- accel limit ---
rw_max_rad = np.arctan((max_lat_accel * wheelbase_m) / (v * v))
rw_max = float(np.degrees(rw_max_rad))
# --- jerk -> rate limit ---
sec2 = 1.2
max_drw_dt = (max_lat_jerk * wheelbase_m) / (v * v * sec2) # rad/s
max_drw_per_tick = max_drw_dt * DT_CTRL # rad/tick
max_drw_per_tick_deg = float(np.degrees(max_drw_per_tick))
err = abs(target_sw - last_sw_deg)
if err > 20.0:
max_sw_rate_deg_per_tick = min(max_sw_rate_deg_per_tick, 1.0)
max_drw_per_tick_deg = min(
max_drw_per_tick_deg,
max_sw_rate_deg_per_tick / steer_ratio
)
# --- rate limit ---
cmd_rw = rate_limit(target_rw, last_rw, -max_drw_per_tick_deg, max_drw_per_tick_deg)
# --- accel clip ---
cmd_rw = float(np.clip(cmd_rw, -rw_max, rw_max))
if not lat_active:
cmd_rw = float(steering_sw_deg) / steer_ratio
cmd_sw = cmd_rw * steer_ratio
return float(np.clip(cmd_sw, -steer_sw_max_deg, steer_sw_max_deg))
class CarController(CarControllerBase):
def __init__(self, dbc_names, CP, CP_IQ=None):
super().__init__(dbc_names, CP, CP_IQ or structs.IQCarParams())
self.CAN = CanBus(CP)
self.params = CarControllerParams(CP)
self.packer = CANPacker(dbc_names[Bus.pt])
@@ -64,56 +145,302 @@ class CarController(CarControllerBase, EsccCarController, LeadDataCarController,
self.car_fingerprint = CP.carFingerprint
self.last_button_frame = 0
self.hyundai_jerk = HyundaiJerk()
self.speedCameraHapticEndFrame = 0
self.hapticFeedbackWhenSpeedCamera = 0
self.max_angle_frames = MAX_ANGLE_FRAMES
self.blinking_signal = False # 1Hz
self.blinking_frame = int(1.0 / DT_CTRL)
self.soft_hold_mode = 2
self.activateCruise = 0
self.button_wait = 12
self.cruise_buttons_msg_values = None
self.cruise_buttons_msg_cnt = 0
self.button_spamming_count = 0
self.prev_clu_speed = 0
self.button_spam1 = 8
self.button_spam2 = 30
self.button_spam3 = 1
self.apply_angle_last = 0
self.lkas_max_torque = 0
self.angle_max_torque = 250
self.steering_pressed_prev = False
self.recovering_from_override = False
self.full_recovery_frames = 0
self.repeated_override_count = 0
self.override_latched = False
self.override_release_frames = 0
self.driver_torque_filtered = 0.0
self.driver_torque_filtered_prev = 0.0
self.pre_override_frames = 0
self.lkas11_active = False
self.canfd_debug = 0
self.MainMode_ACC_trigger = 0
self.LFA_trigger = 0
self.activeCarrot = 0
self.camera_scc_params = Params().get_int("HyundaiCameraSCC")
self.is_ldws_car = Params().get_bool("IsLdwsCar")
self.enable_corner_radar = 0
self.steerDeltaUpOrg = self.steerDeltaUp = self.steerDeltaUpLC = self.params.STEER_DELTA_UP
self.steerDeltaDownOrg = self.steerDeltaDown = self.steerDeltaDownLC = self.params.STEER_DELTA_DOWN
def update(self, CC, CC_IQ, CS, now_nanos):
EsccCarController.update(self, CS)
LeadDataCarController.update(self, CC_IQ)
AolCarController.update(self, self.CP, CC, CC_IQ, self.frame)
if self.frame % 2 == 0:
LongitudinalController.update(self, CC, CS)
if self.frame % 50 == 0:
params = Params()
self.max_angle_frames = params.get_int("MaxAngleFrames")
steerMax = params.get_int("CustomSteerMax")
steerDeltaUp = params.get_int("CustomSteerDeltaUp")
steerDeltaDown = params.get_int("CustomSteerDeltaDown")
steerDeltaUpLC = params.get_int("CustomSteerDeltaUpLC")
steerDeltaDownLC = params.get_int("CustomSteerDeltaDownLC")
if steerMax > 0:
self.params.STEER_MAX = steerMax
if steerDeltaUp > 0:
self.steerDeltaUp = steerDeltaUp
#self.params.ANGLE_TORQUE_UP_RATE = steerDeltaUp
else:
self.steerDeltaUp = self.steerDeltaUpOrg
if steerDeltaDown > 0:
self.steerDeltaDown = steerDeltaDown
#self.params.ANGLE_TORQUE_DOWN_RATE = steerDeltaDown
else:
self.steerDeltaDown = self.steerDeltaDownOrg
if steerDeltaUpLC > 0:
self.steerDeltaUpLC = steerDeltaUpLC
else:
self.steerDeltaUpLC = self.steerDeltaUp
if steerDeltaDownLC > 0:
self.steerDeltaDownLC = steerDeltaDownLC
else:
self.steerDeltaDownLC = self.steerDeltaDown
self.soft_hold_mode = 1 if params.get_int("AutoCruiseControl") > 1 else 2
self.hapticFeedbackWhenSpeedCamera = int(params.get_int("HapticFeedbackWhenSpeedCamera"))
self.button_spam1 = params.get_int("CruiseButtonTest1")
self.button_spam2 = params.get_int("CruiseButtonTest2")
self.button_spam3 = params.get_int("CruiseButtonTest3")
self.speed_from_pcm = params.get_int("SpeedFromPCM")
self.canfd_debug = params.get_int("CanfdDebug")
self.camera_scc_params = params.get_int("HyundaiCameraSCC")
self.enable_corner_radar = params.get_int("EnableCornerRadar")
actuators = CC.actuators
hud_control = CC.hudControl
if hud_control.modelDesire in [3,4]:
self.params.STEER_DELTA_UP = self.steerDeltaUpLC
self.params.STEER_DELTA_DOWN = self.steerDeltaDownLC
else:
self.params.STEER_DELTA_UP = self.steerDeltaUp
self.params.STEER_DELTA_DOWN = self.steerDeltaDown
angle_control = self.CP.flags & HyundaiFlags.ANGLE_CONTROL
# steering torque
new_torque = int(round(actuators.torque * self.params.STEER_MAX))
apply_torque = apply_driver_steer_torque_limits(new_torque, self.apply_torque_last, CS.out.steeringTorque, self.params)
# >90 degree steering fault prevention
self.angle_limit_counter, apply_steer_req = common_fault_avoidance(abs(CS.out.steeringAngleDeg) >= MAX_ANGLE, CC.latActive,
self.angle_limit_counter, MAX_ANGLE_FRAMES,
self.angle_limit_counter, self.max_angle_frames,
MAX_ANGLE_CONSECUTIVE_FRAMES)
#apply_angle = apply_std_steer_angle_limits(actuators.steeringAngleDeg, self.apply_angle_last, CS.out.vEgoRaw,
# CS.out.steeringAngleDeg, CC.latActive, self.params.ANGLE_LIMITS)
apply_angle = apply_steer_angle_limits_physics(
actuators.steeringAngleDeg,
self.apply_angle_last,
CS.out.vEgoRaw,
CS.out.steeringAngleDeg,
CC.latActive,
self.CP.wheelbase,
self.CP.steerRatio,
self.params.ANGLE_LIMITS.STEER_ANGLE_MAX,
CS.modelV2,
)
if angle_control:
apply_steer_req = CC.latActive
angle_torque_cap = self.angle_max_torque
steering_pressed_rising = CS.out.steeringPressed and not self.steering_pressed_prev
if steering_pressed_rising:
if 0 < self.full_recovery_frames < int(5.0 / DT_CTRL):
self.repeated_override_count = min(self.repeated_override_count + 1, 3)
self.full_recovery_frames = 0
self.recovering_from_override = True
torque_threshold = max(self.params.STEER_THRESHOLD, 1.0)
# Filter signed torque so alternating sensor noise cancels out before its
# magnitude is used for pre-override prediction.
driver_torque = float(CS.out.steeringTorque)
driver_torque_abs = abs(driver_torque)
if not CC.latActive:
self.driver_torque_filtered = driver_torque
self.driver_torque_filtered_prev = driver_torque
self.pre_override_frames = 0
else:
torque_filter_alpha = DT_CTRL / (DRIVER_TORQUE_FILTER_TAU + DT_CTRL)
self.driver_torque_filtered_prev = self.driver_torque_filtered
self.driver_torque_filtered += torque_filter_alpha * (driver_torque - self.driver_torque_filtered)
driver_torque_filtered_abs = abs(self.driver_torque_filtered)
driver_torque_filtered_prev_abs = abs(self.driver_torque_filtered_prev)
driver_torque_rate = max(0.0, (driver_torque_filtered_abs - driver_torque_filtered_prev_abs) / DT_CTRL)
torque_ratio = driver_torque_filtered_abs / torque_threshold
raw_torque_ratio = driver_torque_abs / torque_threshold
predicted_torque_ratio = (
driver_torque_filtered_abs + driver_torque_rate * PRE_OVERRIDE_PREDICTION_TIME
) / torque_threshold
pre_override_candidate = (
CC.latActive and
not CS.out.steeringPressed and
raw_torque_ratio > PRE_OVERRIDE_RAW_MIN_RATIO and
torque_ratio > PRE_OVERRIDE_FILTERED_MIN_RATIO and
predicted_torque_ratio > PRE_OVERRIDE_START_RATIO and
driver_torque_rate > torque_threshold * PRE_OVERRIDE_MIN_RATE_RATIO
)
self.pre_override_frames = self.pre_override_frames + 1 if pre_override_candidate else 0
pre_override_yield = 0.0
if self.pre_override_frames >= PRE_OVERRIDE_CONFIRM_FRAMES:
pre_override_yield = float(np.interp(
predicted_torque_ratio,
[PRE_OVERRIDE_START_RATIO, PRE_OVERRIDE_FULL_RATIO],
[0.0, 1.0],
))
recovery_allowed = False
if CS.out.steeringPressed:
# Start yielding immediately when driver override is confirmed.
self.override_latched = True
self.override_release_frames = 0
torque_delta = -20.0
elif pre_override_yield > 0.0:
# Start handing off gently before steeringPressed flips to avoid a sharp torque drop.
torque_delta = PRE_OVERRIDE_MAX_TORQUE_DELTA * pre_override_yield
elif self.lkas_max_torque >= self.angle_max_torque:
# Once fully recovered, hold full authority until the next driver override.
torque_delta = 0.0
elif self.override_latched:
# Hold reduced authority until driver torque stays below 60% for 0.2 seconds.
self.override_release_frames = self.override_release_frames + 1 if torque_ratio < 0.6 else 0
if self.override_release_frames >= int(0.2 / DT_CTRL):
self.override_latched = False
self.override_release_frames = 0
recovery_allowed = True
else:
torque_delta = 0.0
else:
recovery_allowed = True
if recovery_allowed:
# Use one-second model uncertainty to set the base torque recovery time.
# Missing or invalid model data falls back to a moderate 1.5-second recovery.
y_std_1s = 0.2
if CS.modelV2 is not None and len(CS.modelV2.position.yStd) > 10:
model_y_std_1s = float(CS.modelV2.position.yStd[10])
if np.isfinite(model_y_std_1s) and model_y_std_1s >= 0.0:
y_std_1s = model_y_std_1s
recovery_time = float(np.interp(y_std_1s, [0.1, 0.2, 0.3, 0.4], [0.5, 0.8, 1.5, 3.0]))
recovery_time = max(recovery_time, float(np.interp(
self.repeated_override_count,
[0, 1, 2, 3],
[0.1, 1.0, 2.0, 3.0],
)))
base_rate_up = (self.angle_max_torque - self.params.ANGLE_MIN_TORQUE) * DT_CTRL / recovery_time
# During recovery, taper the rate to zero. Only steeringPressed can reduce authority.
torque_delta = base_rate_up * float(np.interp(torque_ratio, [0.6, 0.8], [1.0, 0.0]))
self.lkas_max_torque = float(np.clip(self.lkas_max_torque + torque_delta,
self.params.ANGLE_MIN_TORQUE, angle_torque_cap))
if not CS.out.steeringPressed and self.recovering_from_override and self.lkas_max_torque >= self.angle_max_torque:
self.recovering_from_override = False
self.full_recovery_frames = 1
elif not CS.out.steeringPressed and self.full_recovery_frames > 0:
self.full_recovery_frames += 1
if self.full_recovery_frames >= int(5.0 / DT_CTRL):
self.full_recovery_frames = 0
self.repeated_override_count = 0
if not CC.latActive:
apply_torque = 0
if self.apply_torque_last > 0:
self.apply_torque_last = max(self.apply_torque_last - self.params.STEER_DELTA_DOWN, 0)
elif self.apply_torque_last < 0:
self.apply_torque_last = min(self.apply_torque_last + self.params.STEER_DELTA_DOWN, 0)
else:
self.apply_torque_last = apply_torque
self.lkas_max_torque = 0
self.recovering_from_override = False
self.full_recovery_frames = 0
self.repeated_override_count = 0
self.override_latched = False
self.override_release_frames = 0
self.driver_torque_filtered = 0.0
self.driver_torque_filtered_prev = 0.0
self.pre_override_frames = 0
if apply_torque == 0 and CC.latActive and CS.out.steeringPressed:
apply_steer_req = False
self.steering_pressed_prev = CS.out.steeringPressed if CC.latActive else False
self.apply_angle_last = apply_angle
# Hold torque with induced temporary fault when cutting the actuation bit
# FIXME: we don't use this with CAN FD?
torque_fault = CC.latActive and not apply_steer_req
self.apply_torque_last = apply_torque
# accel + longitudinal
accel = float(np.clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX))
stopping = actuators.longControlState == LongCtrlState.stopping
set_speed_in_units = hud_control.setSpeed * (CV.MS_TO_KPH if CS.is_metric else CV.MS_TO_MPH)
# HUD messages
sys_warning, sys_state, left_lane_warning, right_lane_warning = process_hud_alert(CC.enabled, self.car_fingerprint,
hud_control)
active_speed_decel = hud_control.activeCarrot == 3 and self.activeCarrot != 3 # 3: Speed Decel
self.activeCarrot = hud_control.activeCarrot
if active_speed_decel and self.speedCameraHapticEndFrame < 0: # 과속카메라 감속시작
self.speedCameraHapticEndFrame = self.frame + (8.0 / DT_CTRL) #8초간 켜줌.
elif not active_speed_decel:
self.speedCameraHapticEndFrame = -1
if 0 <= self.speedCameraHapticEndFrame - self.frame < int(8.0 / DT_CTRL) and self.hapticFeedbackWhenSpeedCamera > 0:
t = (self.frame - (self.speedCameraHapticEndFrame - int(8.0 / DT_CTRL))) * DT_CTRL
for start, end in vibrate_intervals:
if start <= t < end:
left_lane_warning = right_lane_warning = self.hapticFeedbackWhenSpeedCamera
break
if self.frame >= self.speedCameraHapticEndFrame:
self.speedCameraHapticEndFrame = -1
if self.frame % self.blinking_frame == 0:
self.blinking_signal = True
elif self.frame % self.blinking_frame == self.blinking_frame / 2:
self.blinking_signal = False
can_sends = []
# *** common hyundai stuff ***
# tester present - w/ no response (keeps relevant ECU disabled)
if self.frame % 100 == 0 and not ((self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC) or self.ESCC.enabled) and \
self.CP.openpilotLongitudinalControl:
if self.frame % 100 == 0 and not (self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC) and self.CP.openpilotLongitudinalControl:
# for longitudinal control, either radar or ADAS driving ECU
addr, bus = 0x7d0, self.CAN.ECAN if self.CP.flags & HyundaiFlags.CANFD else 0
if self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING.value:
if self.CP.flags & HyundaiFlags.CANFD_HDA2.value:
addr, bus = 0x730, self.CAN.ECAN
can_sends.append(make_tester_present_msg(addr, bus, suppress_response=True))
@@ -121,40 +448,135 @@ class CarController(CarControllerBase, EsccCarController, LeadDataCarController,
if self.CP.flags & HyundaiFlags.ENABLE_BLINKERS:
can_sends.append(make_tester_present_msg(0x7b1, self.CAN.ECAN, suppress_response=True))
# *** CAN/CAN FD specific ***
camera_scc = self.CP.flags & HyundaiFlags.CAMERA_SCC
# CAN-FD platforms
if self.CP.flags & HyundaiFlags.CANFD:
can_sends.extend(self.create_canfd_msgs(apply_steer_req, apply_torque, set_speed_in_units, accel,
stopping, hud_control, CS, CC))
hda2 = self.CP.flags & HyundaiFlags.CANFD_HDA2
hda2_long = hda2 and self.CP.openpilotLongitudinalControl
# steering control
if camera_scc:
can_sends.extend(hyundaicanfd.create_steering_messages_camera_scc(self.frame, self.packer, self.CP, self.CAN, CC, apply_steer_req, apply_torque, CS, apply_angle, self.lkas_max_torque, angle_control))
else:
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled, apply_steer_req, apply_torque, apply_angle, self.lkas_max_torque, angle_control))
# prevent LFA from activating on HDA2 by sending "no lane lines detected" to ADAS ECU
if self.frame % 5 == 0 and hda2 and not camera_scc:
can_sends.extend(hyundaicanfd.create_suppress_lfa(self.packer, self.CAN, CS))
# LFA and HDA icons
if self.frame % 5 == 0 and (not hda2 or hda2_long or camera_scc):
can_sends.extend(hyundaicanfd.create_lfahda_cluster(self.packer, CS, self.CAN, CC.longActive, CC.latActive))
if not camera_scc:
can_sends.extend(hyundaicanfd.create_lfa_icon_non_camera_scc(self.packer, CS, self.CAN, CC))
# blinkers
if hda2 and self.CP.flags & HyundaiFlags.ENABLE_BLINKERS:
can_sends.extend(hyundaicanfd.create_spas_messages(self.packer, self.CAN, self.frame, CC.leftBlinker, CC.rightBlinker))
if self.camera_scc_params in [2, 3]:
self.canfd_toggle_adas(CC, CS)
if self.CP.openpilotLongitudinalControl:
self.hyundai_jerk.make_jerk(self.CP, CS, accel, actuators, hud_control)
self.hyundai_jerk.check_carrot_cruise(CC, CS, hud_control, stopping, accel, actuators.aTarget)
if True: #not camera_scc:
can_sends.extend(hyundaicanfd.create_ccnc_messages(self.CP, self.packer, self.CAN, self.frame, CC, CS, hud_control, apply_angle, left_lane_warning, right_lane_warning, self.enable_corner_radar, stopping, self.canfd_debug))
if hda2:
can_sends.extend(hyundaicanfd.create_adrv_messages(self.CP, self.packer, self.CAN, self.frame))
else:
can_sends.extend(hyundaicanfd.create_fca_warning_light(self.CP, self.packer, self.CAN, self.frame))
if self.frame % 2 == 0:
if self.CP.flags & HyundaiFlags.CAMERA_SCC.value:
msg = hyundaicanfd.create_acc_control_scc2(self.packer, self.CAN, CC.enabled, self.accel_last, accel, stopping, CC.cruiseControl.override,
set_speed_in_units, hud_control, self.hyundai_jerk, CS)
if msg is not None:
can_sends.append(msg)
can_sends.extend(hyundaicanfd.create_tcs_messages(self.packer, self.CAN, CS)) # for sorento SCC radar...
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, self.hyundai_jerk.jerk_u, self.hyundai_jerk.jerk_l, CS))
self.accel_last = accel
else:
# button presses
if self.camera_scc_params == 3: # camera scc but stock long
send_button = self.make_spam_button(CC, CS)
can_sends.extend(hyundaicanfd.forward_button_message(self.packer, self.CAN, self.frame, CS, send_button, self.MainMode_ACC_trigger, self.LFA_trigger))
else:
can_sends.extend(self.create_button_messages(CC, CS, use_clu11=False))
else:
can_sends.extend(self.create_can_msgs(apply_steer_req, apply_torque, torque_fault, set_speed_in_units, accel,
stopping, hud_control, actuators, CS, CC))
if CS.lkas11 is not None:
if self.lkas11_active:
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.CP, apply_torque, apply_steer_req,
torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled,
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
left_lane_warning, right_lane_warning, self.is_ldws_car))
self.lkas11_active = True
if not self.CP.openpilotLongitudinalControl:
can_sends.extend(self.create_button_messages(CC, CS, use_clu11=True))
if self.CP.carFingerprint in CAN_GEARS["send_mdps12"] and CS.mdps12 is not None: # send mdps12 to LKAS to prevent LKAS error
can_sends.append(hyundaican.create_mdps12(self.packer, self.frame, CS.mdps12))
casper_ev = self.CP.carFingerprint == CAR.HYUNDAI_CASPER_EV
if self.frame % 2 == 0 and self.CP.openpilotLongitudinalControl:
self.hyundai_jerk.make_jerk(self.CP, CS, accel, actuators, hud_control)
self.hyundai_jerk.check_carrot_cruise(CC, CS, hud_control, stopping, accel, actuators.aTarget)
#jerk = 3.0 if actuators.longControlState == LongCtrlState.pid else 1.0
use_fca = self.CP.flags & HyundaiFlags.USE_FCA.value
if camera_scc:
can_sends.extend(hyundaican.create_acc_commands_scc(self.packer, CC.enabled, accel, self.hyundai_jerk, int(self.frame / 2),
hud_control, set_speed_in_units, stopping,
CC.cruiseControl.override, casper_ev, CS, self.soft_hold_mode))
else:
can_sends.extend(hyundaican.create_acc_commands(self.packer, CC.enabled, accel, self.hyundai_jerk, int(self.frame / 2),
hud_control, set_speed_in_units, stopping,
CC.cruiseControl.override, use_fca, self.CP, CS, self.soft_hold_mode))
# 20 Hz LFA MFA message
if self.frame % 5 == 0 and self.CP.flags & HyundaiFlags.SEND_LFA.value:
can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC, self.blinking_signal))
# 5 Hz ACC options
if self.frame % 20 == 0 and self.CP.openpilotLongitudinalControl:
if camera_scc:
if CS.scc13 is not None:
if casper_ev:
#can_sends.append(hyundaican.create_acc_opt_copy(CS, self.packer))
pass
pass
else:
can_sends.extend(hyundaican.create_acc_opt(self.packer, self.CP))
# 2 Hz front radar options
if self.frame % 50 == 0 and self.CP.openpilotLongitudinalControl and not camera_scc:
can_sends.append(hyundaican.create_frt_radar_opt(self.packer))
new_actuators = actuators.as_builder()
new_actuators.torque = apply_torque / self.params.STEER_MAX
new_actuators.torqueOutputCan = apply_torque
new_actuators.accel = self.tuning.actual_accel
# torqueOutputCan reflects the steering authority value actually sent over CAN.
# Torque-control platforms send the signed torque command, while angle-control
# platforms send LKAS_ANGLE_MAX_TORQUE alongside the requested angle.
new_actuators.torqueOutputCan = self.lkas_max_torque if angle_control else apply_torque
new_actuators.steeringAngleDeg = float(apply_angle)
new_actuators.accel = accel
self.frame += 1
return new_actuators, can_sends
def create_can_msgs(self, apply_steer_req, apply_torque, torque_fault, set_speed_in_units, accel, stopping, hud_control, actuators, CS, CC):
def create_button_messages(self, CC: structs.CarControl, CS: CarState, use_clu11: bool):
can_sends = []
if CS.out.brakePressed or CS.out.brakeHoldActive:
return can_sends
if use_clu11:
if CS.clu11 is None:
return can_sends
# HUD messages
sys_warning, sys_state, left_lane_warning, right_lane_warning = process_hud_alert(CC.enabled, self.car_fingerprint,
hud_control)
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.CP, apply_torque, apply_steer_req,
torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled,
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
left_lane_warning, right_lane_warning,
self.lkas_icon))
# Button messages
if not self.CP.openpilotLongitudinalControl:
if CC.cruiseControl.cancel:
can_sends.append(hyundaican.create_clu11(self.packer, self.frame, CS.clu11, Buttons.CANCEL, self.CP))
elif CC.cruiseControl.resume:
elif False: #CC.cruiseControl.resume:
# send resume at a max freq of 10Hz
if (self.frame - self.last_button_frame) * DT_CTRL > 0.1:
# send 25 messages at a time to increases the likelihood of resume being accepted
@@ -162,81 +584,205 @@ class CarController(CarControllerBase, EsccCarController, LeadDataCarController,
if (self.frame - self.last_button_frame) * DT_CTRL >= 0.15:
self.last_button_frame = self.frame
if self.frame % 2 == 0 and self.CP.openpilotLongitudinalControl:
# TODO: unclear if this is needed
jerk = 3.0 if actuators.longControlState == LongCtrlState.pid else 1.0
use_fca = self.CP.flags & HyundaiFlags.USE_FCA.value
can_sends.extend(hyundaican.create_acc_commands(self.packer, CC.enabled, accel, jerk, int(self.frame / 2),
self.lead_data, hud_control, set_speed_in_units, stopping,
CC.cruiseControl.override, use_fca, self.CP,
CS.main_cruise_enabled, self.tuning, self.ESCC))
if self.last_button_frame != self.frame:
send_button = self.make_spam_button(CC, CS)
if send_button > 0:
can_sends.append(hyundaican.create_clu11_button(self.packer, self.frame, CS.clu11, send_button, self.CP))
# 20 Hz LFA MFA message
if self.frame % 5 == 0 and self.CP.flags & HyundaiFlags.SEND_LFA.value:
can_sends.append(hyundaican.create_lfahda_mfc(self.packer, CC.enabled, self.lfa_icon))
# 5 Hz ACC options
if self.frame % 20 == 0 and self.CP.openpilotLongitudinalControl:
can_sends.extend(hyundaican.create_acc_opt(self.packer, self.CP, self.ESCC))
# 2 Hz front radar options
if self.frame % 50 == 0 and self.CP.openpilotLongitudinalControl and not self.ESCC.enabled:
can_sends.append(hyundaican.create_frt_radar_opt(self.packer))
return can_sends
def create_canfd_msgs(self, apply_steer_req, apply_torque, set_speed_in_units, accel, stopping, hud_control, CS, CC):
can_sends = []
lka_steering = self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING
lka_steering_long = lka_steering and self.CP.openpilotLongitudinalControl
# steering control
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled, apply_steer_req, apply_torque, self.lkas_icon))
# prevent LFA from activating on LKA steering cars by sending "no lane lines detected" to ADAS ECU
if self.frame % 5 == 0 and lka_steering:
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):
can_sends.append(hyundaicanfd.create_lfahda_cluster(self.packer, self.CAN, CC.enabled, self.lfa_icon))
# blinkers
if lka_steering and self.CP.flags & HyundaiFlags.ENABLE_BLINKERS:
can_sends.extend(hyundaicanfd.create_spas_messages(self.packer, self.CAN, CC.leftBlinker, CC.rightBlinker))
if self.CP.openpilotLongitudinalControl:
if lka_steering:
can_sends.extend(hyundaicanfd.create_adrv_messages(self.packer, self.CAN, self.frame))
else:
can_sends.extend(hyundaicanfd.create_fca_warning_light(self.packer, self.CAN, self.frame))
if self.frame % 2 == 0:
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, self.lead_data, CS.main_cruise_enabled, self.tuning))
self.accel_last = accel
else:
# button presses
# carrot.. 왜 alt_cruise_button는 값이 리스트일까?, 그리고 왜? 빈데이터가 들어오는것일까?
if CS.cruise_buttons_msg is not None and self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
try:
cruise_buttons_msg_values = {key: value[0] for key, value in CS.cruise_buttons_msg.items()}
except: # IndexError:
#print("IndexError....")
cruise_buttons_msg_values = None
self.cruise_buttons_msg_cnt += 1
if cruise_buttons_msg_values is not None:
self.cruise_buttons_msg_values = cruise_buttons_msg_values
self.cruise_buttons_msg_cnt = 0
if (self.frame - self.last_button_frame) * DT_CTRL > 0.25:
# cruise cancel
if CC.cruiseControl.cancel:
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
can_sends.append(hyundaicanfd.create_acc_cancel(self.packer, self.CP, self.CAN, CS.cruise_info))
self.last_button_frame = self.frame
else:
for _ in range(20):
can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter + 1, Buttons.CANCEL))
if (self.frame - self.last_button_frame) * DT_CTRL > 0.1:
print("cruiseControl.cancel222222")
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
#can_sends.append(hyundaicanfd.create_acc_cancel(self.packer, self.CP, self.CAN, CS.scc_control))
if self.cruise_buttons_msg_values is not None:
can_sends.append(hyundaicanfd.alt_cruise_buttons(self.packer, self.CP, self.CAN, Buttons.CANCEL, self.cruise_buttons_msg_values, self.cruise_buttons_msg_cnt))
else:
for _ in range(20):
can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter+1, Buttons.CANCEL))
self.last_button_frame = self.frame
# cruise standstill resume
elif CC.cruiseControl.resume:
elif False: #CC.cruiseControl.resume:
if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
# TODO: resume for alt button cars
pass
else:
for _ in range(20):
can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter + 1, Buttons.RES_ACCEL))
can_sends.append(hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter+1, Buttons.RES_ACCEL))
self.last_button_frame = self.frame
## button 스패밍을 안했을때...
if self.last_button_frame != self.frame:
dat = self.canfd_speed_control_pcm(CC, CS, self.cruise_buttons_msg_values)
if dat is not None:
for _ in range(self.button_spam3):
can_sends.append(dat)
self.cruise_buttons_msg_cnt += 1
return can_sends
def canfd_toggle_adas(self, CC, CS):
trigger_min = -200
trigger_start = 6
self.MainMode_ACC_trigger = max(trigger_min, self.MainMode_ACC_trigger - 1)
self.LFA_trigger = max(trigger_min, self.LFA_trigger - 1)
if self.MainMode_ACC_trigger == trigger_min and self.LFA_trigger == trigger_min:
if CC.enabled and not CS.MainMode_ACC and CS.out.vEgo > 3.:
self.MainMode_ACC_trigger = trigger_start
elif CC.latActive and CS.LFA_ICON == 0:
self.LFA_trigger = trigger_start
def canfd_speed_control_pcm(self, CC, CS, cruise_buttons_msg_values):
alt_buttons = True if self.CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS else False
if alt_buttons and cruise_buttons_msg_values is None:
return None
send_button = self.make_spam_button(CC, CS)
if send_button > 0:
if alt_buttons:
return hyundaicanfd.alt_cruise_buttons(self.packer, self.CP, self.CAN, send_button, cruise_buttons_msg_values, self.cruise_buttons_msg_cnt)
else:
return hyundaicanfd.create_buttons(self.packer, self.CP, self.CAN, CS.buttons_counter+1, send_button)
return None
def make_spam_button(self, CC, CS):
hud_control = CC.hudControl
set_speed_in_units = hud_control.setSpeed * (CV.MS_TO_KPH if CS.is_metric else CV.MS_TO_MPH)
target = int(set_speed_in_units+0.5)
current = int(CS.out.cruiseState.speed * (CV.MS_TO_KPH if CS.is_metric else CV.MS_TO_MPH) + 0.5)
v_ego_kph = CS.out.vEgo * CV.MS_TO_KPH
send_button = 0
activate_cruise = False
if CC.enabled:
if not CS.out.cruiseState.enabled:
if (hud_control.leadVisible or v_ego_kph > 10.0) and self.activateCruise == 0:
send_button = Buttons.RES_ACCEL
self.activateCruise = 1
activate_cruise = True
elif CC.cruiseControl.resume:
send_button = Buttons.RES_ACCEL
elif target < current and current>= 31 and self.speed_from_pcm != 1:
send_button = Buttons.SET_DECEL
elif target > current and current < 160 and self.speed_from_pcm != 1:
send_button = Buttons.RES_ACCEL
elif CS.out.activateCruise: #CC.cruiseControl.activate:
if (hud_control.leadVisible or v_ego_kph > 10.0) and self.activateCruise == 0:
self.activateCruise = 1
send_button = Buttons.RES_ACCEL
activate_cruise = True
if CS.out.brakePressed or CS.out.gasPressed:
self.activateCruise = 0
if send_button == 0:
self.button_spamming_count = 0
self.prev_clu_speed = current
return 0
speed_diff = self.prev_clu_speed - current
spamming_max = self.button_spam1
if CS.cruise_buttons[-1] != Buttons.NONE:
self.last_button_frame = self.frame
self.button_wait = self.button_spam2
self.button_spamming_count = 0
elif abs(self.button_spamming_count) >= spamming_max or abs(speed_diff) > 0:
self.last_button_frame = self.frame
self.button_wait = self.button_spam2 if abs(self.button_spamming_count) >= spamming_max else 7
self.button_spamming_count = 0
self.prev_clu_speed = current
send_button_allowed = (self.frame - self.last_button_frame) > self.button_wait
#CC.debugTextCC = "{} speed_diff={:.1f},{:.0f}/{:.0f}, button={}, button_wait={}, count={}".format(
# send_button_allowed, speed_diff, target, current, send_button, self.button_wait, self.button_spamming_count)
if send_button_allowed or activate_cruise or (CC.cruiseControl.resume and self.frame % 2 == 0):
self.button_spamming_count = self.button_spamming_count + 1 if send_button == Buttons.RES_ACCEL else self.button_spamming_count - 1
return send_button
else:
self.button_spamming_count = 0
return 0
from openpilot.common.filter_simple import MyMovingAverage
class HyundaiJerk:
def __init__(self):
self.params = Params()
self.jerk = 0.0
self.jerk_u = self.jerk_l = 0.0
self.cb_upper = self.cb_lower = 0.0
self.jerk_u_min = 0.5
self.carrot_cruise = 1
self.carrot_cruise_accel = 0.0
def check_carrot_cruise(self, CC, CS, hud_control, stopping, accel, a_target):
carrot_cruise_decel = self.params.get_float("CarrotCruiseDecel")
carrot_cruise_atc_decel = self.params.get_float("CarrotCruiseAtcDecel")
if carrot_cruise_atc_decel >= 0 and 0 < hud_control.atcDistance < 500:
carrot_cruise_decel = max(carrot_cruise_decel, carrot_cruise_atc_decel)
self.carrot_cruise = 0
if CS.out.carrotCruise > 0 and not CC.cruiseControl.override:
if CS.softHoldActive == 0 and not stopping:
if CS.out.vEgo > 10/3.6:
if carrot_cruise_decel < 0:
if (a_target > -0.1 or accel > -0.1):
self.carrot_cruise = 1
self.carrot_cruise_accel = 0.0
else:
self.carrot_cruise = 2
carrot_cruise = min(accel, -carrot_cruise_decel * 0.01)
self.carrot_cruise_accel = max(carrot_cruise, self.carrot_cruise_accel - 1.0 * DT_CTRL) # 점진적으로 줄임.
if self.carrot_cruise == 0:
self.carrot_cruise_accel = CS.out.aEgo
def make_jerk(self, CP, CS, accel, actuators, hud_control):
if actuators.longControlState == LongCtrlState.stopping:
self.jerk = self.jerk_u_min / 2 - CS.out.aEgo
else:
jerk = actuators.jerk if actuators.longControlState == LongCtrlState.pid else 0.0
#a_error = actuators.aTarget - CS.out.aEgo
self.jerk = jerk# + a_error
jerk_max_l = 5.0
jerk_max_u = jerk_max_l
if actuators.longControlState == LongCtrlState.off:
self.jerk_u = jerk_max_u
self.jerk_l = jerk_max_l
self.cb_upper = self.cb_lower = 0.0
else:
if CP.flags & HyundaiFlags.CANFD:
# Keep deceleration authority after the MPC jerk settles to zero. Stock SCC raises the
# lower jerk limit with the raw acceleration request instead of relying on jerk alone.
jerk_l_base = 1.2
jerk_l_raw = np.clip(jerk_l_base + 2.0 * max(0.0, -accel - 2.8), jerk_l_base, jerk_max_l)
jerk_l_mpc = np.clip(-self.jerk * 4.0, jerk_l_base, jerk_max_l)
self.jerk_u = min(max(self.jerk_u_min, self.jerk * 2.0), jerk_max_u)
self.jerk_l = max(jerk_l_raw, jerk_l_mpc)
self.cb_upper = self.cb_lower = 0.0
else:
self.jerk_u = min(max(self.jerk_u_min, self.jerk * 2.0), jerk_max_u)
self.jerk_l = min(max(1.0, -self.jerk * 4.0), jerk_max_l)
self.cb_upper = np.clip(0.9 + accel * 0.2, 0, 1.2)
self.cb_lower = np.clip(0.8 + accel * 0.2, 0, 1.2)
+614 -122
View File
@@ -1,47 +1,83 @@
from collections import deque
import copy
import math
import numpy as np
import ast
from iqdbc.can import CANDefine, CANParser
from iqdbc.car import Bus, create_button_events, structs
from iqdbc.car import Bus, create_button_events, structs, DT_CTRL
from iqdbc.car.common.conversions import Conversions as CV
from iqdbc.car.hyundai.hyundaicanfd import CanBus
from iqdbc.car.hyundai.values import HyundaiFlags, CAR, DBC, Buttons, CarControllerParams
from iqdbc.car.hyundai.values import HyundaiFlags, CAR, DBC, Buttons, CarControllerParams, CAMERA_SCC_CAR, HyundaiExtFlags, \
EV_MODE_ACTIVE_VALUES, EV_MODE_STATUS_ADDR, EV_MODE_STATUS_DLC, EV_MODE_STATUS_MSG, \
EV_MODE_STATUS_SIGNAL
from iqdbc.car.interfaces import CarStateBase
from iqdbc.lvbs.car.hyundai.carstate_ext import CarStateExt
from iqdbc.lvbs.car.hyundai.escc import EsccCarStateBase
from iqdbc.lvbs.car.hyundai.aol import AolCarState
from iqdbc.lvbs.car.hyundai.values import HyundaiFlagsIQ
from openpilot.common.params import Params
from datetime import datetime
from zoneinfo import ZoneInfo
ButtonType = structs.CarState.ButtonEvent.Type
PREV_BUTTON_SAMPLES = 8
CLUSTER_SAMPLE_RATE = 20 # frames
STANDSTILL_THRESHOLD = 12 * 0.03125
STANDSTILL_THRESHOLD = 12 * 0.03125 * CV.KPH_TO_MS
# Cancel button can sometimes be ACC pause/resume button, main button can also enable on some cars
ENABLE_BUTTONS = (Buttons.RES_ACCEL, Buttons.SET_DECEL, Buttons.CANCEL)
BUTTONS_DICT = {Buttons.RES_ACCEL: ButtonType.accelCruise, Buttons.SET_DECEL: ButtonType.decelCruise,
Buttons.GAP_DIST: ButtonType.gapAdjustCruise, Buttons.CANCEL: ButtonType.cancel}
Buttons.GAP_DIST: ButtonType.gapAdjustCruise, Buttons.CANCEL: ButtonType.cancel, Buttons.LFA_BUTTON: ButtonType.lfaButton}
GearShifter = structs.CarState.GearShifter
READY_COUNT_OK = 200
TRAILER_DISCONNECT_GRACE_FRAMES = int(5.0 / DT_CTRL)
EV_MODE_STATUS_TIMEOUT_NS = 500_000_000
class CarState(CarStateBase, EsccCarStateBase, AolCarState, CarStateExt):
def __init__(self, CP, CP_IQ):
CarStateBase.__init__(self, CP, CP_IQ)
EsccCarStateBase.__init__(self)
AolCarState.__init__(self, CP, CP_IQ)
CarStateExt.__init__(self, CP, CP_IQ)
def _get_ev_mode_state(cp: CANParser) -> tuple[bool, bool]:
timestamps = cp.ts_nanos.get(EV_MODE_STATUS_MSG)
if timestamps is None:
return False, False
timestamp = timestamps.get(EV_MODE_STATUS_SIGNAL, 0)
dat = cp.dat.get(EV_MODE_STATUS_ADDR, b"")
# The update timestamp advances even when the whole ECAN bus is silent.
# last_nonempty_nanos would leave the final decoded state valid forever.
age = cp._last_update_nanos - timestamp
valid = timestamp > 0 and len(dat) == EV_MODE_STATUS_DLC and not cp.bus_timeout and 0 <= age <= EV_MODE_STATUS_TIMEOUT_NS
active = valid and int(cp.vl[EV_MODE_STATUS_MSG][EV_MODE_STATUS_SIGNAL]) in EV_MODE_ACTIVE_VALUES
return active, valid
NUMERIC_TO_TZ = {
840: "America/New_York", # 미국 (US) → 동부 시간대
124: "America/Toronto", # 캐나다 (CA) → 동부 시간대
250: "Europe/Paris", # 프랑스 (FR)
276: "Europe/Berlin", # 독일 (DE)
826: "Europe/London", # 영국 (GB)
392: "Asia/Tokyo", # 일본 (JP)
156: "Asia/Shanghai", # 중국 (CN)
410: "Asia/Seoul", # 한국 (KR)
36: "Australia/Sydney", # 호주 (AU)
356: "Asia/Kolkata", # 인도 (IN)
}
class CarState(CarStateBase):
def __init__(self, CP, CP_IQ=None):
super().__init__(CP, CP_IQ or structs.IQCarParams())
can_define = CANDefine(DBC[CP.carFingerprint][Bus.pt])
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.gear_msg_canfd = "ACCELERATOR" if CP.flags & HyundaiFlags.EV else \
self.gear_msg_canfd = "GEAR" if CP.extFlags & HyundaiExtFlags.CANFD_GEARS_69 else \
"ACCELERATOR" if CP.flags & HyundaiFlags.EV else \
"GEAR_ALT" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS else \
"GEAR_ALT_2" if CP.flags & HyundaiFlags.CANFD_ALT_GEARS_2 else \
"GEAR_SHIFTER"
self.use_accelerator = self.gear_msg_canfd == "ACCELERATOR"
if CP.flags & HyundaiFlags.CANFD:
self.shifter_values = can_define.dv[self.gear_msg_canfd]["GEAR"]
elif CP.flags & (HyundaiFlags.HYBRID | HyundaiFlags.EV):
@@ -63,31 +99,210 @@ class CarState(CarStateBase, EsccCarStateBase, AolCarState, CarStateExt):
self.is_metric = False
self.buttons_counter = 0
self.cruise_info = {}
# for generic CAN parsing
self.fca11 = None
self.scc11 = None
self.scc12 = None
self.scc13 = None
self.scc14 = None
self.lkas11 = None
self.clu11 = None
# for CANFD parsing
self.scc_control = None
self.lfa = None
self.lfa_alt = None
self.lfahda_cluster = None
self.adrv_0x161 = None
self.adrv_0x200 = None
self.adrv_0x1ea = None
self.adrv_0x160 = None
self.ccnc_0x162 = None
self.hda_info_4a3 = None
self.tcs = None
self.mdps = None
self.steer_touch_2af = None
self.cruise_buttons_msg = None
self.cam_0x362 = None
self.cam_0x2a4 = None
self.manual_speed_limit_assist = None
self.accelerator = None
self.blinkers = None
self.blinkers_alt = None
self.doors_seatbelts = None
self.cruise_buttons_alt2 = None
# On some cars, CLU15->CF_Clu_VehicleSpeed can oscillate faster than the dash updates. Sample at 5 Hz
self.cluster_speed = 0
self.cluster_speed_counter = CLUSTER_SAMPLE_RATE
self.params = CarControllerParams(CP)
self.op_params = Params()
self.main_enabled = True if self.op_params.get_int("AutoEngage") == 2 else False
self.gear_shifter = GearShifter.drive # Gear_init for Nexo ?? unknown 21.02.23.LSW
self.totalDistance = 0.0
self.speedLimitDistance = 0
self.pcmCruiseGap = 0
self.cruise_buttons_alt = True if self.CP.carFingerprint in (CAR.HYUNDAI_CASPER, CAR.HYUNDAI_CASPER_EV) else False
self.MainMode_ACC = False
self.ACCMode = 0
self.LFA_ICON = 0
self.paddle_button_prev = 0
self.lf_distance = 0
self.rf_distance = 0
self.lr_distance = 0
self.rr_distance = 0
#self.lf_lateral = 0
#self.rf_lateral = 0
fingerprints_str = Params().get("FingerPrints")
try:
fingerprints = ast.literal_eval(fingerprints_str) if fingerprints_str else {i: {} for i in range(8)}
except (SyntaxError, ValueError):
fingerprints = {i: {} for i in range(8)}
#print("fingerprints =", fingerprints)
ecu_disabled = False
if self.CP.openpilotLongitudinalControl and not (self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC):
ecu_disabled = True
self.HAS_LFA_BUTTON = True if 913 in fingerprints[0] else False
self.CRUISE_BUTTON_ALT = True if 1007 in fingerprints[0] else False
cam_bus = CanBus(CP).CAM
pt_bus = CanBus(CP).ECAN
alt_bus = CanBus(CP).ACAN
self.GEAR = True if 69 in fingerprints[pt_bus] else False
self.GEAR_ALT = True if 64 in fingerprints[pt_bus] else False
self.TPMS = True if 0x3a0 in fingerprints[pt_bus] else False
self.LOCAL_TIME = True if 1264 in fingerprints[pt_bus] else False
self.cp_bsm = None
self.time_zone = "UTC"
self.cp = None
self.cp_cam = None
self.cp_alt = None
self.controls_ready_count = 0
# trailer detection
self.trailer_connected = False
self.trailer_timeout_cnt = 0
self.trailer_connected_prev = False
self.trailer_status = None
def monitor_fingerprint(self, can_parsers, canfd):
if self.controls_ready_count <= READY_COUNT_OK:
if Params().get_bool("ControlsReady"):
self.controls_ready_count += 1
self.cp = can_parsers[Bus.pt]
self.cp_cam = can_parsers[Bus.cam]
self.cp_alt = can_parsers[Bus.alt] if Bus.alt in can_parsers else None
def add_if_seen(parser, name, ignore_counter = False):
msg = parser.dbc.name_to_msg.get(name)
if not msg:
print(f"{name} not in DBC")
return
if msg.address not in parser.seen_addresses:
return
if msg.address in parser.addresses:
return
parser._add_message(name, ignore_counter = ignore_counter) # ← 이름으로 등록
def add_and_cache(parser, name: str, attr: str, ignore_counter: bool = False):
add_if_seen(parser, name, ignore_counter)
if name in parser.vl: # 등록 성공했을 때만
setattr(self, attr, parser.vl[name])
return True
return False
if self.controls_ready_count == 50:
self.cp.controls_ready = self.cp_cam.controls_ready = True
if self.cp_alt is not None:
self.cp_alt.controls_ready = True
elif self.controls_ready_count == 100:
self.cp.enable_capture = self.cp_cam.enable_capture = False
if self.cp_alt is not None:
self.cp_alt.enable_capture = False
elif self.controls_ready_count == 101:
print("cp_cam.seen_addresses =", self.cp_cam.seen_addresses)
elif self.controls_ready_count == 102:
print("cp.seen_addresses =", self.cp.seen_addresses)
elif self.controls_ready_count == 103:
if self.cp_alt is not None:
print("cp_alt.seen_addresses =", self.cp_alt.seen_addresses)
else:
print("cp_alt.seen_addresses = None")
if not canfd:
if self.controls_ready_count == 104:
if not add_and_cache(self.cp_cam, "FCA11", "fca11"):
add_and_cache(self.cp, "FCA11", "fca11")
add_and_cache(self.cp_cam, "LKAS11", "lkas11")
add_and_cache(self.cp, "CLU11", "clu11")
elif self.controls_ready_count == 105:
cp_cruise = self.cp_cam if self.CP.flags & HyundaiFlags.CAMERA_SCC else self.cp
scc_messages_expected = not self.CP.openpilotLongitudinalControl or self.CP.flags & HyundaiFlags.CAMERA_SCC
if scc_messages_expected:
add_and_cache(cp_cruise, "SCC11", "scc11")
add_and_cache(cp_cruise, "SCC12", "scc12")
add_and_cache(cp_cruise, "SCC13", "scc13")
add_and_cache(cp_cruise, "SCC14", "scc14")
else: # canfd
if self.controls_ready_count == 120:
cp_cruise = self.cp_cam if self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC else self.cp
add_and_cache(cp_cruise, "SCC_CONTROL", "scc_control")
elif self.controls_ready_count == 121:
add_and_cache(self.cp, "TCS", "tcs")
add_and_cache(self.cp, "MDPS", "mdps")
add_and_cache(self.cp_cam, "LFA", "lfa")
add_and_cache(self.cp_cam, "LFA_ALT", "lfa_alt")
add_and_cache(self.cp_cam, "LFAHDA_CLUSTER", "lfahda_cluster")
elif self.controls_ready_count == 122:
add_and_cache(self.cp_cam, "ADRV_0x161", "adrv_0x161")
add_and_cache(self.cp_cam, "ADRV_0x200", "adrv_0x200")
add_and_cache(self.cp_cam, "ADRV_0x1ea", "adrv_0x1ea")
add_and_cache(self.cp_cam, "ADRV_0x160", "adrv_0x160")
add_and_cache(self.cp_cam, "CCNC_0x162", "ccnc_0x162")
elif self.controls_ready_count == 123:
add_and_cache(self.cp, "HDA_INFO_4A3", "hda_info_4a3")
add_and_cache(self.cp, "STEER_TOUCH_2AF", "steer_touch_2af")
elif self.controls_ready_count == 124:
add_and_cache(self.cp, self.cruise_btns_msg_canfd, "cruise_buttons_msg")
if not add_and_cache(self.cp_cam, "CAM_0x362", "cam_0x362") and self.cp_alt is not None:
add_and_cache(self.cp_alt, "CAM_0x362", "cam_0x362")
if not add_and_cache(self.cp_alt, "CAM_0x2a4", "cam_0x2a4") and self.cp_cam is not None:
add_and_cache(self.cp_cam, "CAM_0x2a4", "cam_0x2a4")
elif self.controls_ready_count == 125:
add_and_cache(self.cp, "MANUAL_SPEED_LIMIT_ASSIST", "manual_speed_limit_assist", ignore_counter = True)
if self.gear_msg_canfd == "ACCELERATOR":
add_and_cache(self.cp, "ACCELERATOR", "accelerator", ignore_counter = True)
add_and_cache(self.cp, "BLINKERS", "blinkers")
add_and_cache(self.cp, "BLINKERS_ALT", "blinkers_alt")
add_and_cache(self.cp, "DOORS_SEATBELTS", "doors_seatbelts")
elif self.controls_ready_count == 126:
add_and_cache(self.cp, "CRUISE_BUTTONS_ALT2", "cruise_buttons_alt2", ignore_counter = True)
add_and_cache(self.cp, "TRAILER_STATUS", "trailer_status", ignore_counter = True)
self.steer_fault_counter = 0
def recent_button_interaction(self) -> bool:
# On some newer model years, the CANCEL button acts as a pause/resume button based on the PCM state
# To avoid re-engaging when openpilot cancels, check user engagement intention via buttons
# 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(self, can_parsers) -> tuple[structs.CarState, structs.IQCarState]:
self.monitor_fingerprint(can_parsers, self.CP.flags & HyundaiFlags.CANFD)
cp = can_parsers[Bus.pt]
cp_cam = can_parsers[Bus.cam]
cp_alt = can_parsers[Bus.alt] if Bus.alt in can_parsers else None
if self.CP.flags & HyundaiFlags.CANFD:
return self.update_canfd(can_parsers)
return self.update_canfd(can_parsers), self.out_iq
ret = structs.CarState()
ret_iq = structs.IQCarState()
cp_cruise = cp_cam if self.CP.flags & HyundaiFlags.CAMERA_SCC else cp
self.is_metric = cp.vl["CLU11"]["CF_Clu_SPEED_UNIT"] == 0
speed_conv = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
@@ -97,13 +312,18 @@ class CarState(CarStateBase, EsccCarStateBase, AolCarState, CarStateExt):
ret.seatbeltUnlatched = cp.vl["CGW1"]["CF_Gway_DrvSeatBeltSw"] == 0
self.parse_wheel_speeds(ret,
if cp.ts_nanos["EMS21"]["SCR_UREA_LEVEL"] > 0:
ret.ureaGauge = float(np.clip(cp.vl["EMS21"]["SCR_UREA_LEVEL"] / 100.0, 0.0, 1.0))
ret.wheelSpeeds = self.get_wheel_speeds(
cp.vl["WHL_SPD11"]["WHL_SPD_FL"],
cp.vl["WHL_SPD11"]["WHL_SPD_FR"],
cp.vl["WHL_SPD11"]["WHL_SPD_RL"],
cp.vl["WHL_SPD11"]["WHL_SPD_RR"],
)
ret.standstill = cp.vl["WHL_SPD11"]["WHL_SPD_FL"] <= STANDSTILL_THRESHOLD and cp.vl["WHL_SPD11"]["WHL_SPD_RR"] <= STANDSTILL_THRESHOLD
ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.standstill = ret.wheelSpeeds.fl <= STANDSTILL_THRESHOLD and ret.wheelSpeeds.rr <= STANDSTILL_THRESHOLD
self.cluster_speed_counter += 1
if self.cluster_speed_counter > CLUSTER_SAMPLE_RATE:
@@ -116,73 +336,109 @@ class CarState(CarStateBase, EsccCarStateBase, AolCarState, CarStateExt):
if not self.is_metric and self.CP.carFingerprint not in (CAR.KIA_SORENTO,):
self.cluster_speed = math.floor(self.cluster_speed * CV.KPH_TO_MPH + CV.KPH_TO_MPH)
ret.vEgoCluster = self.cluster_speed * speed_conv
#ret.vEgoCluster = self.cluster_speed * speed_conv
ret.steeringAngleDeg = cp.vl["SAS11"]["SAS_Angle"]
ret.steeringRateDeg = cp.vl["SAS11"]["SAS_Speed"]
ret.yawRate = cp.vl["ESP12"]["YAW_RATE"]
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(
50, cp.vl["CGW1"]["CF_Gway_TurnSigLh"], cp.vl["CGW1"]["CF_Gway_TurnSigRh"])
ret.steeringTorque = cp.vl["MDPS12"]["CR_Mdps_StrColTq"]
ret.steeringTorqueEps = cp.vl["MDPS12"]["CR_Mdps_OutTq"]
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > self.params.STEER_THRESHOLD, 5)
steer_fault_raw = cp.vl["MDPS12"]["CF_Mdps_ToiUnavail"] != 0 or cp.vl["MDPS12"]["CF_Mdps_ToiFlt"] != 0
self.steer_fault_counter = self.steer_fault_counter + 1 if steer_fault_raw else 0
ret.steerFaultTemporary = self.steer_fault_counter > 5
ret.steerFaultTemporary = cp.vl["MDPS12"]["CF_Mdps_ToiUnavail"] != 0 or cp.vl["MDPS12"]["CF_Mdps_ToiFlt"] != 0
# cruise state
if self.CP.openpilotLongitudinalControl:
# These are not used for engage/disengage since openpilot keeps track of state using the buttons
ret.cruiseState.available = cp.vl["TCS13"]["ACCEnable"] == 0
ret.cruiseState.available = self.main_enabled and self.controls_ready_count >= READY_COUNT_OK #cp.vl["TCS13"]["ACCEnable"] == 0
ret.cruiseState.enabled = cp.vl["TCS13"]["ACC_REQ"] == 1
ret.cruiseState.standstill = False
ret.cruiseState.nonAdaptive = False
elif not self.CP_IQ.flags & HyundaiFlagsIQ.NON_SCC:
ret.cruiseState.available = cp_cruise.vl["SCC11"]["MainMode_ACC"] == 1
elif not self.CP.flags & HyundaiFlags.CC_ONLY_CAR:
self.main_enabled = ret.cruiseState.available = cp_cruise.vl["SCC11"]["MainMode_ACC"] == 1
ret.cruiseState.enabled = cp_cruise.vl["SCC12"]["ACCMode"] != 0
ret.cruiseState.standstill = cp_cruise.vl["SCC11"]["SCCInfoDisplay"] == 4.
ret.cruiseState.nonAdaptive = cp_cruise.vl["SCC11"]["SCCInfoDisplay"] == 2. # Shows 'Cruise Control' on dash
ret.cruiseState.speed = cp_cruise.vl["SCC11"]["VSetDis"] * speed_conv
ret.pcmCruiseGap = cp_cruise.vl["SCC11"]["TauGapSet"]
# TODO: Find brake pressure
ret.brake = 0
ret.brakePressed = cp.vl["TCS13"]["DriverOverride"] == 2 # 2 includes regen braking by user on HEV/EV
ret.brakeHoldActive = cp.vl["TCS15"]["AVH_LAMP"] == 2 # 0 OFF, 1 ERROR, 2 ACTIVE, 3 READY
ret.parkingBrake = cp.vl["TCS13"]["PBRAKE_ACT"] == 1
ret.espDisabled = cp.vl["TCS11"]["TCS_PAS"] == 1
ret.espActive = cp.vl["TCS11"]["ABS_ACT"] == 1
ret.accFaulted = cp.vl["TCS13"]["ACCEnable"] != 0 # 0 ACC CONTROL ENABLED, 1-3 ACC CONTROL DISABLED
if not self.CP.flags & HyundaiFlags.CC_ONLY_CAR:
ret.brakePressed = cp.vl["TCS13"]["DriverOverride"] == 2 # 2 includes regen braking by user on HEV/EV
ret.brakeHoldActive = cp.vl["TCS15"]["AVH_LAMP"] == 2 # 0 OFF, 1 ERROR, 2 ACTIVE, 3 READY
ret.parkingBrake = cp.vl["TCS13"]["PBRAKE_ACT"] == 1
ret.espDisabled = cp.vl["TCS11"]["TCS_PAS"] == 1
ret.espActive = cp.vl["TCS11"]["ABS_ACT"] == 1
ret.accFaulted = cp.vl["TCS13"]["ACCEnable"] != 0 # 0 ACC CONTROL ENABLED, 1-3 ACC CONTROL DISABLED
ret.brakeLights = bool(cp.vl["TCS13"]["BrakeLight"] or ret.brakePressed)
if self.CP.flags & (HyundaiFlags.HYBRID | HyundaiFlags.EV | HyundaiFlags.FCEV):
if self.CP.flags & HyundaiFlags.FCEV:
ret.gasPressed = cp.vl["FCEV_ACCELERATOR"]["ACCELERATOR_PEDAL"] > 0
ret.gas = cp.vl["FCEV_ACCELERATOR"]["ACCELERATOR_PEDAL"] / 254.
elif self.CP.flags & HyundaiFlags.HYBRID:
ret.gasPressed = cp.vl["E_EMS11"]["CR_Vcu_AccPedDep_Pos"] > 0
ret.gas = cp.vl["E_EMS11"]["CR_Vcu_AccPedDep_Pos"] / 254.
else:
ret.gasPressed = cp.vl["E_EMS11"]["Accel_Pedal_Pos"] > 0
ret.gas = cp.vl["E_EMS11"]["Accel_Pedal_Pos"] / 254.
ret.gasPressed = ret.gas > 0
else:
ret.gas = cp.vl["EMS12"]["PV_AV_CAN"] / 100.
ret.gasPressed = bool(cp.vl["EMS16"]["CF_Ems_AclAct"])
# Gear Selection via Cluster - For those Kia/Hyundai which are not fully discovered, we can use the Cluster Indicator for Gear Selection,
# as this seems to be standard over all cars, but is not the preferred method.
if self.CP.flags & (HyundaiFlags.HYBRID | HyundaiFlags.EV):
gear = cp.vl["ELECT_GEAR"]["Elect_Gear_Shifter"]
ret.gearStep = cp.vl["ELECT_GEAR"]["Elect_Gear_Step"]
if self.CP.carFingerprint == CAR.HYUNDAI_CASPER_EV:
ret.gearStep = 0
elif self.CP.flags & HyundaiFlags.FCEV:
gear = cp.vl["EMS20"]["HYDROGEN_GEAR_SHIFTER"]
elif self.CP.flags & HyundaiFlags.CLUSTER_GEARS:
gear = cp.vl["CLU15"]["CF_Clu_Gear"]
if self.CP.carFingerprint == CAR.KIA_K7:
ret.gearStep = cp.vl["LVR11"]["CF_Lvr_GearInf"]
elif self.CP.flags & HyundaiFlags.TCU_GEARS:
gear = cp.vl["TCU12"]["CUR_GR"]
else:
gear = cp.vl["LVR12"]["CF_Lvr_Gear"]
ret.gearStep = cp.vl["LVR11"]["CF_Lvr_GearInf"]
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear))
if not self.CP.carFingerprint in (CAR.HYUNDAI_NEXO):
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear))
else:
gear = cp.vl["ELECT_GEAR"]["Elect_Gear_Shifter"]
gear_disp = cp.vl["ELECT_GEAR"]
if (not self.CP.openpilotLongitudinalControl or self.CP.flags & HyundaiFlags.CAMERA_SCC) and not self.CP_IQ.flags & HyundaiFlagsIQ.NON_SCC:
gear_shifter = GearShifter.unknown
if gear == 1546: # Thank you for Neokii # fix PolorBear 22.06.05
gear_shifter = GearShifter.drive
elif gear == 2314:
gear_shifter = GearShifter.neutral
elif gear == 2569:
gear_shifter = GearShifter.park
elif gear == 2566:
gear_shifter = GearShifter.reverse
if gear_shifter != GearShifter.unknown and self.gear_shifter != gear_shifter:
self.gear_shifter = gear_shifter
ret.gearShifter = self.gear_shifter
if not self.CP.flags & HyundaiFlags.CC_ONLY_CAR and (not self.CP.openpilotLongitudinalControl or self.CP.flags & HyundaiFlags.CAMERA_SCC):
aeb_src = "FCA11" if self.CP.flags & HyundaiFlags.USE_FCA.value else "SCC12"
aeb_sig = "FCA_CmdAct" if self.CP.flags & HyundaiFlags.USE_FCA.value else "AEB_CmdAct"
aeb_warning = cp_cruise.vl[aeb_src]["CF_VSM_Warn"] != 0
scc_warning = cp_cruise.vl["SCC12"]["TakeOverReq"] == 1 # sometimes only SCC system shows an FCW
aeb_braking = cp_cruise.vl[aeb_src]["CF_VSM_DecCmdAct"] != 0 or cp_cruise.vl[aeb_src][aeb_sig] != 0
if self.CP.carFingerprint == CAR.HYUNDAI_CASPER_EV and aeb_src == "FCA11":
fca_fault = cp_cruise.vl["FCA11"]["FCA_Failinfo"] != 0 or cp_cruise.vl["FCA11"]["FCA_Status"] == 3
if fca_fault:
aeb_warning = False
aeb_braking = False
ret.stockFcw = (aeb_warning or scc_warning) and not aeb_braking
ret.stockAeb = aeb_warning and aeb_braking
@@ -190,157 +446,393 @@ class CarState(CarStateBase, EsccCarStateBase, AolCarState, CarStateExt):
ret.leftBlindspot = cp.vl["LCA11"]["CF_Lca_IndLeft"] != 0
ret.rightBlindspot = cp.vl["LCA11"]["CF_Lca_IndRight"] != 0
# save the entire LKAS11 and CLU11
self.lkas11 = copy.copy(cp_cam.vl["LKAS11"])
self.clu11 = copy.copy(cp.vl["CLU11"])
self.steer_state = cp.vl["MDPS12"]["CF_Mdps_ToiActive"] # 0 NOT ACTIVE, 1 ACTIVE
prev_cruise_buttons = self.cruise_buttons[-1]
#self.cruise_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwState"])
#carrot {{
#if self.CRUISE_BUTTON_ALT and cp.vl["CRUISE_BUTTON_ALT"]["SET_ME_1"] == 1:
# self.cruise_buttons_alt = True
cruise_button = [Buttons.NONE]
if self.cruise_buttons_alt:
lfa_button = cp.vl["CRUISE_BUTTON_LFA"]["CruiseSwLfa"]
cruise_button = [Buttons.LFA_BUTTON] if lfa_button > 0 else [cp.vl["CRUISE_BUTTON_ALT"]["CruiseSwState"]]
elif self.HAS_LFA_BUTTON and cp.vl["BCM_PO_11"]["LFA_Pressed"] == 1: # for K5
cruise_button = [Buttons.LFA_BUTTON]
else:
cruise_button = cp.vl_all["CLU11"]["CF_Clu_CruiseSwState"]
self.cruise_buttons.extend(cruise_button)
# }} carrot
prev_main_buttons = self.main_buttons[-1]
prev_lda_button = self.lda_button
self.cruise_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwState"])
self.main_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwMain"])
if self.CP.flags & HyundaiFlags.HAS_LDA_BUTTON:
self.lda_button = cp.vl["BCM_PO_11"]["LDA_BTN"]
#self.cruise_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwState"])
if self.cruise_buttons_alt:
self.main_buttons.extend(cp.vl_all["CRUISE_BUTTON_ALT"]["CruiseSwMain"])
else:
self.main_buttons.extend(cp.vl_all["CLU11"]["CF_Clu_CruiseSwMain"])
self.mdps12 = copy.copy(cp.vl["MDPS12"])
ret.buttonEvents = [*create_button_events(self.cruise_buttons[-1], prev_cruise_buttons, BUTTONS_DICT),
*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.main_buttons[-1], prev_main_buttons, {1: ButtonType.mainCruise})]
if self.CP.openpilotLongitudinalControl:
ret.cruiseState.available = self.get_main_cruise(ret)
CarStateExt.update(self, ret, ret_iq, can_parsers, speed_conv)
if not self.CP.flags & HyundaiFlags.CC_ONLY_CAR:
tpms_unit = cp.vl["TPMS11"]["UNIT"] * 0.725 if int(cp.vl["TPMS11"]["UNIT"]) > 0 else 1.
ret.tpms.fl = tpms_unit * cp.vl["TPMS11"]["PRESSURE_FL"]
ret.tpms.fr = tpms_unit * cp.vl["TPMS11"]["PRESSURE_FR"]
ret.tpms.rl = tpms_unit * cp.vl["TPMS11"]["PRESSURE_RL"]
ret.tpms.rr = tpms_unit * cp.vl["TPMS11"]["PRESSURE_RR"]
ret.blockPcmEnable = not self.recent_button_interaction()
cluSpeed = cp.vl["CLU11"]["CF_Clu_Vanz"]
decimal = cp.vl["CLU11"]["CF_Clu_VanzDecimal"]
if 0. < decimal < 0.5:
cluSpeed += decimal
# low speed steer alert hysteresis logic (only for cars with steer cut off above 10 m/s)
if ret.vEgo < (self.CP.minSteerSpeed + 2.) and self.CP.minSteerSpeed > 10.:
self.low_speed_alert = True
if ret.vEgo > (self.CP.minSteerSpeed + 4.):
self.low_speed_alert = False
ret.lowSpeedAlert = self.low_speed_alert
ret.vEgoCluster = cluSpeed * speed_conv
vEgoClu, aEgoClu = self.update_clu_speed_kf(ret.vEgoCluster)
ret.vCluRatio = (ret.vEgo / vEgoClu) if (vEgoClu > 3. and ret.vEgo > 3.) else 1.0
return ret, ret_iq
if self.CP.extFlags & HyundaiExtFlags.NAVI_CLUSTER.value:
speedLimit = cp.vl["Navi_HU"]["SpeedLim_Nav_Clu"]
speedLimitCam = cp.vl["Navi_HU"]["SpeedLim_Nav_Cam"]
ret.speedLimit = speedLimit if speedLimit < 255 and speedLimitCam == 1 else 0
speed_limit_cam = speedLimitCam == 1
else:
ret.speedLimit = 0
ret.speedLimitDistance = 0
speed_limit_cam = False
def update_canfd(self, can_parsers) -> tuple[structs.CarState, structs.IQCarState]:
self.update_speed_limit(ret, speed_limit_cam)
if prev_main_buttons == 0 and self.main_buttons[-1] != 0:
self.main_enabled = not self.main_enabled
return ret, self.out_iq
def update_speed_limit(self, ret, speed_limit_cam):
self.totalDistance += ret.vEgo * DT_CTRL
if ret.speedLimit > 0 and not ret.gasPressed and speed_limit_cam:
if self.speedLimitDistance <= self.totalDistance:
self.speedLimitDistance = self.totalDistance + ret.speedLimit * 6
self.speedLimitDistance = max(self.totalDistance + 1, self.speedLimitDistance)
else:
self.speedLimitDistance = self.totalDistance
ret.speedLimitDistance = self.speedLimitDistance - self.totalDistance
def update_canfd(self, can_parsers) -> structs.CarState:
cp = can_parsers[Bus.pt]
cp_cam = can_parsers[Bus.cam]
cp_alt = can_parsers[Bus.alt] if Bus.alt in can_parsers else None
ret = structs.CarState()
ret_iq = structs.IQCarState()
if self.CP.extFlags & HyundaiExtFlags.EV_MODE_STATUS_230:
ret.evModeActive, ret.evModeValid = _get_ev_mode_state(cp)
self.is_metric = cp.vl["CRUISE_BUTTONS_ALT"]["DISTANCE_UNIT"] != 1
speed_factor = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
if self.CP.flags & (HyundaiFlags.EV | HyundaiFlags.HYBRID):
ret.gasPressed = cp.vl[self.accelerator_msg_canfd]["ACCELERATOR_PEDAL"] > 1e-5
offset = 255. if self.CP.flags & HyundaiFlags.EV else 1023.
ret.gas = cp.vl[self.accelerator_msg_canfd]["ACCELERATOR_PEDAL"] / offset if not self.use_accelerator else 0 if self.accelerator is None else self.accelerator["ACCELERATOR_PEDAL"] / offset
ret.gasPressed = ret.gas > 1e-5
else:
ret.gasPressed = bool(cp.vl[self.accelerator_msg_canfd]["ACCELERATOR_PEDAL_PRESSED"])
ret.gasPressed = bool(cp.vl[self.accelerator_msg_canfd]["ACCELERATOR_PEDAL_PRESSED"]) if not self.use_accelerator else False if self.accelerator is None else bool(self.accelerator["ACCELERATOR_PEDAL_PRESSED"])
ret.brakePressed = cp.vl["TCS"]["DriverBraking"] == 1
#print(cp.vl["TCS"], cp.vl_all["TCS"]["DriverBraking"][-10:])
ret.doorOpen = cp.vl["DOORS_SEATBELTS"]["DRIVER_DOOR"] == 1
ret.seatbeltUnlatched = cp.vl["DOORS_SEATBELTS"]["DRIVER_SEATBELT"] == 0
if self.doors_seatbelts is not None:
ret.doorOpen = self.doors_seatbelts["DRIVER_DOOR"] == 1
ret.seatbeltUnlatched = self.doors_seatbelts["DRIVER_SEATBELT"] == 0
gear = cp.vl[self.gear_msg_canfd]["GEAR"]
gear = cp.vl[self.gear_msg_canfd]["GEAR"] if not self.use_accelerator else 0 if self.accelerator is None else self.accelerator["GEAR"]
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear))
if self.TPMS:
tpms_unit = cp.vl["TPMS"]["UNIT"] * 0.725 if int(cp.vl["TPMS"]["UNIT"]) > 0 else 1.
ret.tpms.fl = tpms_unit * cp.vl["TPMS"]["PRESSURE_FL"]
ret.tpms.fr = tpms_unit * cp.vl["TPMS"]["PRESSURE_FR"]
ret.tpms.rl = tpms_unit * cp.vl["TPMS"]["PRESSURE_RL"]
ret.tpms.rr = tpms_unit * cp.vl["TPMS"]["PRESSURE_RR"]
# TODO: figure out positions
self.parse_wheel_speeds(ret,
cp.vl["WHEEL_SPEEDS"]["WHL_SpdFLVal"],
cp.vl["WHEEL_SPEEDS"]["WHL_SpdFRVal"],
cp.vl["WHEEL_SPEEDS"]["WHL_SpdRLVal"],
cp.vl["WHEEL_SPEEDS"]["WHL_SpdRRVal"],
ret.wheelSpeeds = self.get_wheel_speeds(
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_1"],
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_2"],
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_3"],
cp.vl["WHEEL_SPEEDS"]["WHEEL_SPEED_4"],
)
ret.standstill = cp.vl["WHEEL_SPEEDS"]["WHL_SpdFLVal"] <= STANDSTILL_THRESHOLD and cp.vl["WHEEL_SPEEDS"]["WHL_SpdFRVal"] <= STANDSTILL_THRESHOLD and \
cp.vl["WHEEL_SPEEDS"]["WHL_SpdRLVal"] <= STANDSTILL_THRESHOLD and cp.vl["WHEEL_SPEEDS"]["WHL_SpdRRVal"] <= STANDSTILL_THRESHOLD
ret.vEgoRaw = (ret.wheelSpeeds.fl + ret.wheelSpeeds.fr + ret.wheelSpeeds.rl + ret.wheelSpeeds.rr) / 4.
ret.vEgo, ret.aEgo = self.update_speed_kf(ret.vEgoRaw)
ret.standstill = ret.wheelSpeeds.fl <= STANDSTILL_THRESHOLD and ret.wheelSpeeds.rr <= STANDSTILL_THRESHOLD
ret.brakeLights = ret.brakePressed or cp.vl["TCS"]["BrakeLight"] == 1 or ret.aEgo < -0.5
ret.steeringRateDeg = cp.vl["STEERING_SENSORS"]["STEERING_RATE"]
ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEERING_ANGLE"]
# steering angle deg값이 이상함. mdps값이 더 신뢰가 가는듯.. torque steering 차량도 확인해야함.
#ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEERING_ANGLE"] * -1
#ret.steeringAngleDeg = cp.vl["MDPS"]["STEERING_ANGLE"] * -1
if self.CP.flags & HyundaiFlags.ANGLE_CONTROL:
ret.steeringAngleDeg = cp.vl["MDPS"]["STEERING_ANGLE_2"] * -1
else:
ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEERING_ANGLE"] * -1
trailer_signal = self.trailer_status is not None and self.trailer_status["TRAILER_CONNECTED"] != 0
if trailer_signal:
# Rising edge is immediate so trailer-specific behavior is preserved.
self.trailer_timeout_cnt = 0
self.trailer_connected = True
elif self.trailer_connected:
# During ignition-off, the trailer bit can clear before the cluster shuts down.
# Keep suppression active through that transient, while still detecting a real
# disconnect after 5 seconds at the 100 Hz CarState update rate.
self.trailer_timeout_cnt += 1
if self.trailer_timeout_cnt > TRAILER_DISCONNECT_GRACE_FRAMES:
self.trailer_connected = False
else:
self.trailer_timeout_cnt = 0
ret.trailerConnected = self.trailer_connected
if self.trailer_connected != self.trailer_connected_prev:
print(f"[TRAILER_DEBUG] connected={self.trailer_connected} timeout={self.trailer_timeout_cnt}")
self.trailer_connected_prev = self.trailer_connected
ret.steeringTorque = cp.vl["MDPS"]["STEERING_COL_TORQUE"]
ret.steeringTorqueEps = cp.vl["MDPS"]["STEERING_OUT_TORQUE"]
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > self.params.STEER_THRESHOLD, 5)
steer_fault_raw = cp.vl["MDPS"]["LKA_FAULT"] != 0
self.steer_fault_counter = self.steer_fault_counter + 1 if steer_fault_raw else 0
ret.steerFaultTemporary = self.steer_fault_counter > 5
ret.steerFaultTemporary = cp.vl["MDPS"]["LKA_FAULT"] != 0 or cp.vl["MDPS"]["LFA2_FAULT"] != 0
#ret.steerFaultTemporary = False
blinkers_info = self.blinkers if self.blinkers is not None else self.blinkers_alt if self.blinkers_alt is not None else None
if blinkers_info is not None:
left_blinker_lamp = blinkers_info["LEFT_LAMP"] or blinkers_info["LEFT_LAMP_ALT"]
right_blinker_lamp = blinkers_info["RIGHT_LAMP"] or blinkers_info["RIGHT_LAMP_ALT"]
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(50, left_blinker_lamp, right_blinker_lamp)
# TODO: alt signal usage may be described by cp.vl['BLINKERS']['USE_ALT_LAMP']
left_blinker_sig, right_blinker_sig = "LEFT_LAMP", "RIGHT_LAMP"
if self.CP.carFingerprint == CAR.HYUNDAI_KONA_EV_2ND_GEN:
left_blinker_sig, right_blinker_sig = "LEFT_LAMP_ALT", "RIGHT_LAMP_ALT"
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_lamp(50, cp.vl["BLINKERS"][left_blinker_sig],
cp.vl["BLINKERS"][right_blinker_sig])
if self.CP.enableBsm:
ret.leftBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["FL_INDICATOR"] != 0
ret.rightBlindspot = cp.vl["BLINDSPOTS_REAR_CORNERS"]["FR_INDICATOR"] != 0
if self.cp_bsm is None:
if 442 in cp.seen_addresses:
self.cp_bsm = cp
print("######## BSM in ECAN")
elif 442 in cp_cam.seen_addresses:
self.cp_bsm = cp_cam
print("######## BSM in CAM")
else:
bsm_info = self.cp_bsm.vl["BLINDSPOTS_REAR_CORNERS"]
ret.leftBlindspot = (bsm_info["FL_INDICATOR"] + bsm_info["INDICATOR_LEFT_TWO"] + bsm_info["INDICATOR_LEFT_FOUR"]) > 0
ret.rightBlindspot = (bsm_info["FR_INDICATOR"] + bsm_info["INDICATOR_RIGHT_TWO"] + bsm_info["INDICATOR_RIGHT_FOUR"]) > 0
# cruise state
if self.cruise_buttons_alt2 is not None:
cruise_button = self.cruise_buttons_alt2["CRUISE_BUTTONS"]
else:
cruise_button = cp.vl[self.cruise_btns_msg_canfd]["CRUISE_BUTTONS"]
if cruise_button in [Buttons.RES_ACCEL, Buttons.SET_DECEL] and self.CP.openpilotLongitudinalControl:
self.main_enabled = True
# CAN FD cars enable on main button press, set available if no TCS faults preventing engagement
ret.cruiseState.available = cp.vl["TCS"]["ACCEnable"] == 0
ret.cruiseState.available = self.main_enabled and self.controls_ready_count >= READY_COUNT_OK #cp.vl["TCS"]["ACCEnable"] == 0
if self.CP.flags & HyundaiFlags.CAMERA_SCC.value:
self.MainMode_ACC = cp_cam.vl["SCC_CONTROL"]["MainMode_ACC"] == 1
self.ACCMode = cp_cam.vl["SCC_CONTROL"]["ACCMode"]
self.LFA_ICON = cp_cam.vl["LFAHDA_CLUSTER"]["HDA_LFA_SymSta"]
if self.CP.openpilotLongitudinalControl:
# These are not used for engage/disengage since openpilot keeps track of state using the buttons
ret.cruiseState.enabled = cp.vl["TCS"]["ACC_REQ"] == 1
ret.cruiseState.standstill = False
if self.MainMode_ACC or self.main_enabled:
self.main_enabled = True
else:
cp_cruise_info = cp_cam if self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC else cp
ret.cruiseState.enabled = cp_cruise_info.vl["SCC_CONTROL"]["ACCMode"] in (1, 2)
ret.cruiseState.standstill = cp_cruise_info.vl["SCC_CONTROL"]["CRUISE_STANDSTILL"] == 1
if cp_cruise_info.vl["SCC_CONTROL"]["MainMode_ACC"] == 1: # carrot
ret.cruiseState.available = self.main_enabled = True
ret.pcmCruiseGap = int(np.clip(cp_cruise_info.vl["SCC_CONTROL"]["DISTANCE_SETTING"], 1, 4))
ret.cruiseState.standstill = cp_cruise_info.vl["SCC_CONTROL"]["InfoDisplay"] >= 4
ret.cruiseState.speed = cp_cruise_info.vl["SCC_CONTROL"]["VSetDis"] * speed_factor
self.cruise_info = copy.copy(cp_cruise_info.vl["SCC_CONTROL"])
ret.brakeHoldActive = cp.vl["ESP_STATUS"]["AUTO_HOLD"] == 1 and cp_cruise_info.vl["SCC_CONTROL"]["ACCMode"] not in (1, 2)
speed_limit_cam = False
corner = False
corner_infos = [info for info in (self.adrv_0x1ea, self.ccnc_0x162) if info is not None]
if corner_infos:
def corner_max(signal):
return max(info[signal] for info in corner_infos)
ret.leftLongDist = self.lf_distance = corner_max("LF_DETECT_DISTANCE")
ret.rightLongDist = self.rf_distance = corner_max("RF_DETECT_DISTANCE")
self.lr_distance = corner_max("LR_DETECT_DISTANCE")
self.rr_distance = corner_max("RR_DETECT_DISTANCE")
ret.leftLatDist = corner_max("LF_DETECT_LATERAL")
ret.rightLatDist = corner_max("RF_DETECT_LATERAL")
ret.leftRearLongDist = self.lr_distance
ret.rightRearLongDist = self.rr_distance
ret.leftRearLatDist = corner_max("LR_DETECT_LATERAL")
ret.rightRearLatDist = corner_max("RR_DETECT_LATERAL")
corner = True
if corner:
raw_corner_radar_enabled = (
self.op_params.get_int("EnableCornerRadar") > 0 and
bool(self.CP.extFlags & (HyundaiExtFlags.CORNER_RADAR_OBJECTS_235.value |
HyundaiExtFlags.CORNER_RADAR_OBJECTS_180.value))
)
left_front_block = (not raw_corner_radar_enabled) and 0 < ret.leftLongDist < 7.0
right_front_block = (not raw_corner_radar_enabled) and 0 < ret.rightLongDist < 7.0
rear_block_dist = 5.0 if raw_corner_radar_enabled else 7.0
left_rear_block = 0 < self.lr_distance < rear_block_dist
right_rear_block = 0 < self.rr_distance < rear_block_dist
left_block = left_front_block or left_rear_block
right_block = right_front_block or right_rear_block
if left_block:
ret.leftBlindspot = True
if right_block:
ret.rightBlindspot = True
if self.hda_info_4a3 is not None:
speedLimit = self.hda_info_4a3["SPEED_LIMIT"]
if not self.is_metric:
speedLimit *= CV.MPH_TO_KPH
ret.speedLimit = speedLimit if speedLimit < 255 else 0
if int(self.hda_info_4a3["MapSource"]) == 2:
speed_limit_cam = True
if self.time_zone == "UTC":
country_code = int(self.hda_info_4a3["CountryCode"])
self.time_zone = ZoneInfo(NUMERIC_TO_TZ.get(country_code, "UTC"))
ret.gearStep = cp.vl["GEAR"]["GEAR_STEP"] if self.GEAR else 0
if 1 <= ret.gearStep <= 8 and ret.gearShifter == GearShifter.unknown:
ret.gearShifter = GearShifter.drive
ret.gearStep = cp.vl["GEAR_ALT"]["GEAR_STEP"] if self.GEAR_ALT else ret.gearStep
lane_info = self.cam_0x2a4 if self.cam_0x2a4 is not None else self.cam_0x362
if lane_info is not None:
left_lane_prob = lane_info["LEFT_LANE_PROB"]
right_lane_prob = lane_info["RIGHT_LANE_PROB"]
left_lane_type = lane_info["LEFT_LANE_TYPE"] # 0: dashed, 1: solid, 2: undecided, 3: road edge, 4: DLM Inner Solid, 5: DLM InnerDashed, 6:DLM Inner Undecided, 7: Botts Dots, 8: Barrier
right_lane_type = lane_info["RIGHT_LANE_TYPE"]
left_lane_color = lane_info["LEFT_LANE_COLOR"] # 0: none, 1: white, 2: yellow, 3: blue
right_lane_color = lane_info["RIGHT_LANE_COLOR"]
left_lane_info = left_lane_color * 10 + left_lane_type
right_lane_info = right_lane_color * 10 + right_lane_type
ret.leftLaneLine = left_lane_info
ret.rightLaneLine = right_lane_info
# Manual Speed Limit Assist is a feature that replaces non-adaptive cruise control on EV CAN FD platforms.
# It limits the vehicle speed, overridable by pressing the accelerator past a certain point.
# The car will brake, but does not respect positive acceleration commands in this mode
# TODO: find this message on ICE & HYBRID cars + cruise control signals (if exists)
if self.CP.flags & HyundaiFlags.EV:
ret.cruiseState.nonAdaptive = cp.vl["MANUAL_SPEED_LIMIT_ASSIST"]["MSLA_ENABLED"] == 1
if self.manual_speed_limit_assist is not None:
#ret.cruiseState.nonAdaptive = cp.vl["MANUAL_SPEED_LIMIT_ASSIST"]["MSLA_ENABLED"] == 1
ret.cruiseState.nonAdaptive = self.manual_speed_limit_assist["MSLA_ENABLED"] == 1
if self.LOCAL_TIME and self.time_zone != "UTC":
lt = cp.vl["LOCAL_TIME"]
y, m, d, H, M, S = int(lt["YEAR"]) + 2000, int(lt["MONTH"]), int(lt["DATE"]), int(lt["HOURS"]), int(lt["MINUTES"]), int(lt["SECONDS"])
try:
dt_local = datetime(y, m, d, H, M, S, tzinfo=self.time_zone)
ret.datetime = int(dt_local.timestamp() * 1000)
except:
#print(f"Error parsing local time: {y}-{m}-{d} {H}:{M}:{S} in {self.time_zone}")
pass
prev_cruise_buttons = self.cruise_buttons[-1]
#self.cruise_buttons.extend(cp.vl_all[self.cruise_btns_msg_canfd]["CRUISE_BUTTONS"])
#carrot {{
if self.cruise_buttons_alt2 is not None:
if int(self.cruise_buttons_alt2.get("LFA_BTN", 0)) == 1:
cruise_button = [Buttons.LFA_BUTTON]
else:
v = int(self.cruise_buttons_alt2.get("CRUISE_BUTTONS", 0))
cruise_button = [v if v < 5 else Buttons.NONE]
elif cp.vl[self.cruise_btns_msg_canfd]["LFA_BTN"]:
cruise_button = [Buttons.LFA_BUTTON]
else:
cruise_button = cp.vl_all[self.cruise_btns_msg_canfd]["CRUISE_BUTTONS"]
self.cruise_buttons.extend(cruise_button)
# }} carrot
#if self.cruise_btns_msg_canfd in cp.vl:
# self.cruise_buttons_msg = copy.copy(cp.vl[self.cruise_btns_msg_canfd])
"""
if self.cruise_btns_msg_canfd in cp.vl: #carrot
if not cp.vl[self.cruise_btns_msg_canfd]["CRUISE_BUTTONS"]:
pass
#print("empty cruise btns...")
else:
self.cruise_buttons_msg = copy.copy(cp.vl[self.cruise_btns_msg_canfd])
"""
prev_main_buttons = self.main_buttons[-1]
prev_lda_button = self.lda_button
self.cruise_buttons.extend(cp.vl_all[self.cruise_btns_msg_canfd]["CRUISE_BUTTONS"])
self.main_buttons.extend(cp.vl_all[self.cruise_btns_msg_canfd]["ADAPTIVE_CRUISE_MAIN_BTN"])
self.lda_button = cp.vl[self.cruise_btns_msg_canfd]["LDA_BTN"]
#self.cruise_buttons.extend(cp.vl_all[self.cruise_btns_msg_canfd]["CRUISE_BUTTONS"])
if self.cruise_buttons_alt2 is not None:
self.main_buttons.extend([1 if int(self.cruise_buttons_alt2.get("CRUISE_BUTTONS", 0)) == 8 else 0])
else:
adaptive_main = cp.vl_all[self.cruise_btns_msg_canfd]["ADAPTIVE_CRUISE_MAIN_BTN"]
normal_main = cp.vl_all[self.cruise_btns_msg_canfd]["NORMAL_CRUISE_MAIN_BTN"]
self.main_buttons.extend(int(adaptive or normal) for adaptive, normal in zip(adaptive_main, normal_main, strict=True))
if self.main_buttons[-1] != prev_main_buttons and not self.main_buttons[-1]: # and self.CP.openpilotLongitudinalControl: #carrot
self.main_enabled = not self.main_enabled
print("main_enabled = {}".format(self.main_enabled))
self.buttons_counter = cp.vl[self.cruise_btns_msg_canfd]["COUNTER"]
ret.accFaulted = cp.vl["TCS"]["ACCEnable"] != 0 # 0 ACC CONTROL ENABLED, 1-3 ACC CONTROL DISABLED
if self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
self.lfa_block_msg = copy.copy(cp_cam.vl["CAM_0x362"] if self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
else cp_cam.vl["CAM_0x2a4"])
speed_conv = CV.KPH_TO_MS # if self.is_metric else CV.MPH_TO_MS
cluSpeed = cp.vl["CRUISE_BUTTONS_ALT"]["CLU_SPEED"]
ret.vEgoCluster = cluSpeed * speed_conv # MPH단위에서도 KPH로 나오는듯..
vEgoClu, aEgoClu = self.update_clu_speed_kf(ret.vEgoCluster)
ret.vCluRatio = (ret.vEgo / vEgoClu) if (vEgoClu > 3. and ret.vEgo > 3.) else 1.0
AolCarState.update_aol_canfd(self, ret, can_parsers)
self.update_speed_limit(ret, speed_limit_cam)
paddle_button = self.paddle_button_prev
if self.cruise_btns_msg_canfd == "CRUISE_BUTTONS":
paddle_button = 1 if cp.vl["CRUISE_BUTTONS"]["LEFT_PADDLE"] == 1 else 2 if cp.vl["CRUISE_BUTTONS"]["RIGHT_PADDLE"] == 1 else 0
elif self.gear_msg_canfd == "GEAR":
paddle_button = 1 if cp.vl["GEAR"]["LEFT_PADDLE"] == 1 else 2 if cp.vl["GEAR"]["RIGHT_PADDLE"] == 1 else 0
ret.buttonEvents = [*create_button_events(self.cruise_buttons[-1], prev_cruise_buttons, BUTTONS_DICT),
*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(paddle_button, self.paddle_button_prev, {1: ButtonType.paddleLeft, 2: ButtonType.paddleRight}),
*create_button_events(self.main_buttons[-1], prev_main_buttons, {1: ButtonType.mainCruise})]
if self.CP.openpilotLongitudinalControl:
ret.cruiseState.available = self.get_main_cruise(ret)
self.paddle_button_prev = paddle_button
CarStateExt.update_canfd_ext(self, ret, ret_iq, can_parsers, speed_factor)
return ret
ret.blockPcmEnable = not self.recent_button_interaction()
return ret, ret_iq
def get_can_parsers_canfd(self, CP):
def get_can_parsers_canfd(self, CP, CP_IQ=None):
msgs = []
if not (CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS):
# TODO: this can be removed once we add dynamic support to vl_all
msgs += [
# this message is 50Hz but the ECU frequently stops transmitting for ~0.5s
("CRUISE_BUTTONS", 1)
("CRUISE_BUTTONS", 50)
]
CAN = CanBus(CP)
pt_parser = CANParser(DBC[CP.carFingerprint][Bus.pt], msgs, CAN.ECAN)
if CP.extFlags & HyundaiExtFlags.EV_MODE_STATUS_230:
# Display-only while the signal is fleet-validated: checksum and freshness
# gate the value without making a counter/alive fault disable controls.
pt_parser._add_message(EV_MODE_STATUS_MSG, math.nan, ignore_counter=True)
return {
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], msgs, CanBus(CP).ECAN),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).CAM),
Bus.pt: pt_parser,
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CAN.CAM),
Bus.alt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CAN.ACAN),
}
def get_can_parsers(self, CP, CP_IQ):
def get_can_parsers(self, CP, CP_IQ=None):
if CP.flags & HyundaiFlags.CANFD:
return self.get_can_parsers_canfd(CP)
return {
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 0),
# EMS21 carries SCR_UREA_LEVEL on diesel platforms. NaN frequency makes
# it optional, so gasoline/EV platforms do not fail CAN validity.
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [("EMS21", math.nan)], 0),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 2),
}
+91 -78
View File
@@ -1,10 +1,6 @@
""" AUTO-FORMATTED USING iqdbc/car/debug/format_fingerprints.py, EDIT STRUCTURE THERE."""
from iqdbc.car.structs import CarParams
from iqdbc.car.hyundai.values import CAR
from iqdbc.lvbs.car.fingerprints_ext import extend_fw_versions
from iqdbc.lvbs.car.hyundai.fingerprints_ext import FW_VERSIONS_EXT
Ecu = CarParams.Ecu
# The existence of SCC or RDR in the fwdRadar FW usually determines the radar's function,
@@ -12,6 +8,14 @@ Ecu = CarParams.Ecu
FW_VERSIONS = {
CAR.HYUNDAI_AZERA_7TH_GEN: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00GN7_ RDR ----- 1.00 1.03 99110-N1000 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00GN7 MFC AT KOR LHD 1.00 1.03 99211-N1000 230322',
],
},
CAR.HYUNDAI_AZERA_6TH_GEN: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00IG__ SCC F-CU- 1.00 1.00 99110-G8100 ',
@@ -21,7 +25,6 @@ FW_VERSIONS = {
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00IG MFC AT MES LHD 1.00 1.04 99211-G8100 200511',
b'\xf1\x00IG MFC AT MES LHD 1.00 1.05 99211-G8100 210409',
],
},
CAR.HYUNDAI_AZERA_HEV_6TH_GEN: {
@@ -55,20 +58,13 @@ FW_VERSIONS = {
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00AE MDPS C 1.00 1.03 56310/G2300 4AEHC103',
b'\xf1\x00AE MDPS C 1.00 1.03 56310G2300\x00 4AEHC103',
b'\xf1\x00AE MDPS C 1.00 1.04 56310G2550\x00 4AEHC104',
b'\xf1\x00AE MDPS C 1.00 1.05 56310/G2500 4AEHC105',
b'\xf1\x00AE MDPS C 1.00 1.05 56310/G2501 4AEHC105',
b'\xf1\x00AE MDPS C 1.00 1.07 56310/G2301 4AEHC107',
b'\xf1\x00AE MDPS C 1.00 1.07 56310/G2501 4AEHC107',
b'\xf1\x00AE MDPS C 1.00 1.07 56310/G2551 4AEHC107',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00AEH MFC AT EUR LHD 1.00 1.00 95740-G2400 180222',
b'\xf1\x00AEH MFC AT EUR LHD 1.00 1.00 95740-G7200 160418',
b'\xf1\x00AEH MFC AT EUR RHD 1.00 1.00 95740-G2200 161014',
b'\xf1\x00AEH MFC AT EUR RHD 1.00 1.00 95740-G2400 180222',
b'\xf1\x00AEH MFC AT USA LHD 1.00 1.00 95740-G2300 170703',
b'\xf1\x00AEH MFC AT USA LHD 1.00 1.00 95740-G2400 180222',
],
},
@@ -78,13 +74,11 @@ FW_VERSIONS = {
b'\xf1\x00AEhe SCC H-CUP 1.01 1.01 96400-G2100 ',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00AE MDPS C 1.00 1.07 56310/G2201 4AEHC107',
b'\xf1\x00AE MDPS C 1.00 1.07 56310/G2501 4AEHC107',
b'\xf1\x00AE MDPS C 1.00 1.07 56310/G2551 4AEHC107',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00AEP MFC AT AUS RHD 1.00 1.00 95740-G2400 180222',
b'\xf1\x00AEP MFC AT EUR LHD 1.00 1.00 95740-G2400 180222',
b'\xf1\x00AEP MFC AT USA LHD 1.00 1.00 95740-G2400 180222',
],
},
@@ -154,12 +148,10 @@ FW_VERSIONS = {
CAR.HYUNDAI_IONIQ_HEV_2022: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00AEhe SCC F-CUP 1.00 1.00 99110-G2600 ',
b'\xf1\x00AEhe SCC F-CUP 1.00 1.02 99110-G2100 ',
b'\xf1\x00AEhe SCC FHCUP 1.00 1.00 99110-G2600 ',
b'\xf1\x00AEhe SCC FHCUP 1.00 1.02 99110-G2100 ',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00AE MDPS C 1.00 1.01 56310-XX000 4APHC101',
b'\xf1\x00AE MDPS C 1.00 1.01 56310/G2510 4APHC101',
b'\xf1\x00AE MDPS C 1.00 1.01 56310G2510\x00 4APHC101',
],
@@ -173,7 +165,6 @@ FW_VERSIONS = {
b'\xf1\x00DN8_ SCC F-CU- 1.00 1.00 99110-L0000 ',
b'\xf1\x00DN8_ SCC F-CUP 1.00 1.00 99110-L0000 ',
b'\xf1\x00DN8_ SCC F-CUP 1.00 1.02 99110-L1000 ',
b'\xf1\x00DN8_ SCC FHCU- 1.00 1.00 99110-L0000 ',
b'\xf1\x00DN8_ SCC FHCUP 1.00 1.00 99110-L0000 ',
b'\xf1\x00DN8_ SCC FHCUP 1.00 1.01 99110-L1000 ',
b'\xf1\x00DN8_ SCC FHCUP 1.00 1.02 99110-L1000 ',
@@ -232,6 +223,15 @@ FW_VERSIONS = {
b'\xf1\x00LFF LKAS AT USA LHD 1.01 1.02 95740-C1000 E52',
],
},
CAR.HYUNDAI_SONATA_2024: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00DN8_ RDR ----- 1.00 1.00 99110-L1800 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00DN8 MFC AT KOR LHD 1.00 1.01 99211-L1800 230512',
b'\xf1\x00DN8 MFC AT USA LHD 1.00 1.01 99211-L1800 230512',
],
},
CAR.HYUNDAI_TUCSON: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00TL__ FCA F-CUP 1.00 1.01 99110-D3500 ',
@@ -293,7 +293,6 @@ FW_VERSIONS = {
b'\xf1\x00TM ESC \x04 101 \x08\x04 58910-S2GA0',
b'\xf1\x00TM ESC \x04 102!\x04\x05 58910-S2GA0',
b'\xf1\x00TM ESC \x04 103"\x07\x08 58910-S2GA0',
b'\xf1\x00TM ESC \x1b 102 \x08\x08 58910-S1DA0',
b'\xf1\x00TM ESC \x1e 102 \x08\x08 58910-S1DA0',
b'\xf1\x00TM ESC 103!\x030 58910-S1MA0',
],
@@ -301,7 +300,6 @@ FW_VERSIONS = {
b'\xf1\x00TM MDPS C 1.00 1.01 56310-S1AB0 4TSDC101',
b'\xf1\x00TM MDPS C 1.00 1.01 56310-S1EB0 4TSDC101',
b'\xf1\x00TM MDPS C 1.00 1.02 56370-S2AA0 0B19',
b'\xf1\x00TM MDPS R 1.00 1.05 57700-S1500 4TSDP105',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00TM MFC AT EUR LHD 1.00 1.03 99211-S1500 210224',
@@ -367,7 +365,6 @@ FW_VERSIONS = {
},
CAR.KIA_STINGER: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00CK__ SCC FHCUP 1.00 1.02 96400-J5000 ',
b'\xf1\x00CK__ SCC F_CUP 1.00 1.01 96400-J5000 ',
b'\xf1\x00CK__ SCC F_CUP 1.00 1.01 96400-J5100 ',
b'\xf1\x00CK__ SCC F_CUP 1.00 1.02 96400-J5100 ',
@@ -383,7 +380,6 @@ FW_VERSIONS = {
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CK MFC AT EUR LHD 1.00 1.03 95740-J5000 170822',
b'\xf1\x00CK MFC AT KOR LHD 1.00 1.04 95740-J5000 180504',
b'\xf1\x00CK MFC AT USA LHD 1.00 1.03 95740-J5000 170822',
b'\xf1\x00CK MFC AT USA LHD 1.00 1.04 95740-J5000 180504',
],
@@ -400,7 +396,6 @@ FW_VERSIONS = {
b'\xf1\x00CK MDPS R 1.00 5.03 57700-J5320 4C2VL503',
b'\xf1\x00CK MDPS R 1.00 5.03 57700-J5380 4C2VR503',
b'\xf1\x00CK MDPS R 1.00 5.03 57700-J5520 4C4VL503',
b'\xf1\x00CK MDPS R 1.00 5.04 57700-J5320 4C2VL504',
b'\xf1\x00CK MDPS R 1.00 5.04 57700-J5520 4C4VL504',
],
(Ecu.fwdCamera, 0x7c4, None): [
@@ -442,7 +437,6 @@ FW_VERSIONS = {
b'\xf1\x00LX ESC \x0b 104 \x10\x13 58910-S8330',
b'\xf1\x00LX ESC \x0b 104 \x10\x16 58910-S8360',
b'\xf1\x00ON ESC \x01 101\x19\t\x08 58910-S9360',
b'\xf1\x00ON ESC \x01 103$\x04\x08 58910-S9360',
b'\xf1\x00ON ESC \x0b 100\x18\x12\x18 58910-S9360',
b'\xf1\x00ON ESC \x0b 101\x19\t\x05 58910-S9320',
b'\xf1\x00ON ESC \x0b 101\x19\t\x08 58910-S9360',
@@ -648,6 +642,14 @@ FW_VERSIONS = {
b'\xf1\x00DL3HMFC AT KOR LHD 1.00 1.04 99210-L2000 210527',
],
},
CAR.KIA_K5_DL3_24_HEV: { # (DL3)
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00DL3HMFC AT KOR LHD 1.00 1.02 99210-L2500 230911'
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00DL3_ RDR ----- 1.00 1.01 99110-L2500 ',
],
},
CAR.HYUNDAI_KONA_EV: {
(Ecu.abs, 0x7d1, None): [
b'\xf1\x00OS IEB \x01 212 \x11\x13 58520-K4000',
@@ -703,7 +705,6 @@ FW_VERSIONS = {
b'\xf1\x00OSP MDPS C 1.00 1.02 56310-K4271 4OEPC102',
b'\xf1\x00OSP MDPS C 1.00 1.02 56310/K4271 4OEPC102',
b'\xf1\x00OSP MDPS C 1.00 1.02 56310/K4970 4OEPC102',
b'\xf1\x00OSP MDPS C 1.00 1.02 56310/K4971 4OEPC102',
b'\xf1\x00OSP MDPS C 1.00 1.02 56310K4260\x00 4OEPC102',
b'\xf1\x00OSP MDPS C 1.00 1.02 56310K4261\x00 4OEPC102',
b'\xf1\x00OSP MDPS C 1.00 1.02 56310K4971\x00 4OEPC102',
@@ -720,6 +721,14 @@ FW_VERSIONS = {
b'\xf1\x00SX2EMFC AT KOR LHD 1.00 1.00 99211-BF000 230410',
],
},
CAR.HYUNDAI_KONA_HEV_2ND_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00SX2HMFC AT EUR RHD 1.00 1.04 99211-BE000 231010',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00SX2_ RDR ----- 1.00 1.02 99110-BE500 ',
],
},
CAR.KIA_NIRO_EV: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00DEev SCC F-CUP 1.00 1.00 99110-Q4000 ',
@@ -737,14 +746,11 @@ FW_VERSIONS = {
b'\xf1\x00DE MDPS C 1.00 1.04 56310Q4100\x00 4DEEC104',
b'\xf1\x00DE MDPS C 1.00 1.05 56310Q4000\x00 4DEEC105',
b'\xf1\x00DE MDPS C 1.00 1.05 56310Q4100\x00 4DEEC105',
b'\xf1\x00DE MDPS C 1.00 1.05 56310Q4150\x00 4DEEC105',
b'\xf1\x00DE MDPS C 1.00 1.05 56310Q4200\x00 4DEEC105',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00DEE MFC AT EUR LHD 1.00 1.00 99211-Q4000 191211',
b'\xf1\x00DEE MFC AT EUR LHD 1.00 1.00 99211-Q4100 200706',
b'\xf1\x00DEE MFC AT EUR LHD 1.00 1.03 95740-Q4000 180821',
b'\xf1\x00DEE MFC AT EUR RHD 1.00 1.00 99211-Q4000 191211',
b'\xf1\x00DEE MFC AT KOR LHD 1.00 1.02 95740-Q4000 180705',
b'\xf1\x00DEE MFC AT KOR LHD 1.00 1.03 95740-Q4000 180821',
b'\xf1\x00DEE MFC AT USA LHD 1.00 1.00 99211-Q4000 191211',
@@ -756,13 +762,9 @@ FW_VERSIONS = {
CAR.KIA_NIRO_EV_2ND_GEN: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00SG2_ RDR ----- 1.00 1.01 99110-AT000 ',
b'\xf1\x00SG__ RDR ----- 1.00 1.00 99110-AT200 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00SG2EMFC AT EUR LHD 1.00 1.00 99211-AT200 240315',
b'\xf1\x00SG2EMFC AT EUR LHD 1.01 1.09 99211-AT000 220801',
b'\xf1\x00SG2EMFC AT USA LHD 1.00 1.00 99211-AT100 230216',
b'\xf1\x00SG2EMFC AT USA LHD 1.00 1.00 99211-AT200 240401',
b'\xf1\x00SG2EMFC AT USA LHD 1.01 1.09 99211-AT000 220801',
],
},
@@ -866,11 +868,9 @@ FW_VERSIONS = {
CAR.KIA_OPTIMA_H_G4_FL: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00JFhe SCC FHCUP 1.00 1.01 99110-A8500 ',
b'\xf1\x00JFhe SCC FHCUP 1.00 1.03 99110-A8500 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00JFH MFC AT KOR LHD 1.00 1.01 95895-A8200 180323',
b'\xf1\x00JFH MFC AT KOR LHD 1.00 1.04 95895-A8200 181217',
],
},
CAR.HYUNDAI_ELANTRA: {
@@ -930,7 +930,6 @@ FW_VERSIONS = {
b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.06 99210-AA000 220111',
b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.07 99210-AA000 220426',
b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.08 99210-AA000 220728',
b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.09 99210-AA000 221108',
],
(Ecu.abs, 0x7d1, None): [
b'\xf1\x00CN ESC \t 101 \x10\x03 58910-AB800',
@@ -998,7 +997,6 @@ FW_VERSIONS = {
},
CAR.KIA_SORENTO: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00UMP LKAS AT AUS RHD 1.00 1.00 96400-C6550 S30',
b'\xf1\x00UMP LKAS AT KOR LHD 1.00 1.00 95740-C5550 S30',
b'\xf1\x00UMP LKAS AT USA LHD 1.00 1.00 95740-C6550 d00',
b'\xf1\x00UMP LKAS AT USA LHD 1.01 1.01 95740-C6550 d01',
@@ -1032,6 +1030,14 @@ FW_VERSIONS = {
b'\xf1\x00CV1 MFC AT USA LHD 1.00 1.06 99210-CV000 220328',
],
},
CAR.KIA_EV6_PE: { # (CV1)
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00CV__ RDR ----- 1.00 1.01 99110-CV500 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00CV MFC AT KOR LHD 1.00 1.01 99210-CV500 240405',
],
},
CAR.HYUNDAI_IONIQ_5: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NE1_ RDR ----- 1.00 1.00 99110-GI000 ',
@@ -1042,7 +1048,6 @@ FW_VERSIONS = {
b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.00 99211-GI100 230915',
b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.01 99211-GI010 211007',
b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.01 99211-GI100 240110',
b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.02 99211-GI010 211206',
b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.03 99211-GI010 220401',
b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.06 99211-GI000 210813',
b'\xf1\x00NE1 MFC AT EUR LHD 1.00 1.06 99211-GI010 230110',
@@ -1056,13 +1061,29 @@ FW_VERSIONS = {
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.00 99211-GI020 230719',
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.00 99211-GI100 230915',
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.01 99211-GI010 211007',
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.01 99211-GI100 240110',
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.02 99211-GI010 211206',
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.03 99211-GI010 220401',
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.05 99211-GI010 220614',
b'\xf1\x00NE1 MFC AT USA LHD 1.00 1.06 99211-GI010 230110',
],
},
CAR.HYUNDAI_IONIQ_5_PE: { # (NE1)
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NE__ RDR ----- 1.00 1.00 99110-GI500 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NE MFC AT KOR LHD 1.00 1.02 99211-GI500 240221',
],
},
CAR.HYUNDAI_IONIQ_5_N: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NE1N RDR ----- 1.00 1.00 99110-NI000 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NE1NMFC AT KOR LHD 1.00 1.04 99211-NI000 231219',
b'\xf1\x00NE1NMFC AT USA LHD 1.00 1.04 99211-NI000 231219',
],
},
CAR.HYUNDAI_IONIQ_6: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00CE__ RDR ----- 1.00 1.01 99110-KL000 ',
@@ -1076,9 +1097,16 @@ FW_VERSIONS = {
b'\xf1\x00CE MFC AT USA LHD 1.00 1.06 99211-KL000 230915',
],
},
CAR.HYUNDAI_IONIQ_9: {
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MEev RDR ----- 1.00 1.00 99110-GO000 ',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00ME MFC AT KOR LHD 1.00 1.00 99211-GO000 241007',
],
},
CAR.HYUNDAI_TUCSON_4TH_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NX4 FR_CMR AT CAN LHD 1.00 1.00 99211-N9220 14K',
b'\xf1\x00NX4 FR_CMR AT CAN LHD 1.00 1.00 99211-N9260 14Y',
b'\xf1\x00NX4 FR_CMR AT CAN LHD 1.00 1.01 99211-N9100 14A',
b'\xf1\x00NX4 FR_CMR AT EUR LHD 1.00 1.00 99211-N9220 14K',
@@ -1090,14 +1118,12 @@ FW_VERSIONS = {
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N9260 14Y',
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.01 99211-N9100 14A',
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.01 99211-N9240 14T',
b'\xf1\x00NX4 FR_CMR AT EUR LHD 1.00 1.00 99211-N9240 14Q',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NX4__ 1.00 1.00 99110-N9100 ',
b'\xf1\x00NX4__ 1.00 1.01 99110-N9000 ',
b'\xf1\x00NX4__ 1.00 1.02 99110-N9000 ',
b'\xf1\x00NX4__ 1.01 1.00 99110-N9100 ',
b'\xf1\x00NX4__ 1.01 1.02 99110-N9000 ',
],
},
CAR.HYUNDAI_SANTA_CRUZ_1ST_GEN: {
@@ -1115,13 +1141,11 @@ FW_VERSIONS = {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00NQ5 FR_CMR AT AUS RHD 1.00 1.00 99211-P1040 663',
b'\xf1\x00NQ5 FR_CMR AT EUR LHD 1.00 1.00 99211-P1040 663',
b'\xf1\x00NQ5 FR_CMR AT GEN LHD 1.00 1.00 99211-P1040 663',
b'\xf1\x00NQ5 FR_CMR AT GEN LHD 1.00 1.00 99211-P1060 665',
b'\xf1\x00NQ5 FR_CMR AT USA LHD 1.00 1.00 99211-P1030 662',
b'\xf1\x00NQ5 FR_CMR AT USA LHD 1.00 1.00 99211-P1040 663',
b'\xf1\x00NQ5 FR_CMR AT USA LHD 1.00 1.00 99211-P1060 665',
b'\xf1\x00NQ5 FR_CMR AT USA LHD 1.00 1.00 99211-P1070 690',
b'\xf1\x00NQ5 FR_CMR AT USA LHD 1.00 1.01 99211-P1060 680',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NQ5__ 1.00 1.02 99110-P1000 ',
@@ -1134,14 +1158,11 @@ FW_VERSIONS = {
CAR.GENESIS_GV70_1ST_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00JK1 MFC AT CAN LHD 1.00 1.02 99211-IY000 230627',
b'\xf1\x00JK1 MFC AT CAN LHD 1.00 1.04 99211-AR100 210204',
b'\xf1\x00JK1 MFC AT USA LHD 1.00 1.01 99211-AR200 220125',
b'\xf1\x00JK1 MFC AT USA LHD 1.00 1.01 99211-AR300 220125',
b'\xf1\x00JK1 MFC AT USA LHD 1.00 1.02 99211-IY000 230627',
b'\xf1\x00JK1 MFC AT USA LHD 1.00 1.04 99211-AR000 210204',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00JK1_ SCC ----- 1.00 1.02 99110-AR100 ',
b'\xf1\x00JK1_ SCC FHCUP 1.00 1.00 99110-AR200 ',
b'\xf1\x00JK1_ SCC FHCUP 1.00 1.00 99110-AR300 ',
b'\xf1\x00JK1_ SCC FHCUP 1.00 1.00 99110-IY000 ',
@@ -1158,6 +1179,24 @@ FW_VERSIONS = {
b'\xf1\x00JKev SCC ----- 1.00 1.01 99110-DS000 ',
],
},
CAR.HYUNDAI_NEXO_1ST_GEN: {
(Ecu.abs, 0x7d1, None): [
b'\xf1\x00FE IEB \x01 312 \x11\x13 58520-M5000',
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00FE MFC AT KOR LHD 1.00 1.00 99211-M5100 201218',
b'\xf1\x00FE MFC AT KOR LHD 1.00 1.02 99211-M5100 220405',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00FE MDPS C 1.00 1.05 56340-M5000 9903',
b'\xf1\x00FE MDPS C 1.00 1.06 56340-M5000 1625',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00FE__ SCC FHCUP 1.00 1.05 99110-M5000 ',
],
},
CAR.GENESIS_GV60_EV_1ST_GEN: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00JW1 MFC AT AUS RHD 1.00 1.03 99211-CU100 221118',
@@ -1189,7 +1228,6 @@ FW_VERSIONS = {
b'\xf1\x00MQ4HMFC AT KOR LHD 1.00 1.12 99210-P2000 230331',
b'\xf1\x00MQ4HMFC AT USA LHD 1.00 1.10 99210-P2000 210406',
b'\xf1\x00MQ4HMFC AT USA LHD 1.00 1.11 99210-P2000 211217',
b'\xf1\x00MQ4HMFC AT USA LHD 1.00 1.12 99210-P2000 230331',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MQhe SCC FHCUP 1.00 1.04 99110-P4000 ',
@@ -1250,40 +1288,15 @@ FW_VERSIONS = {
b'\xf1\x00US4_ RDR ----- 1.00 1.00 99110-CG000 ',
],
},
CAR.HYUNDAI_NEXO_1ST_GEN: {
(Ecu.abs, 0x7d1, None): [
b'\xf1\x00FE IEB \x01 312 \x11\x13 58520-M5000',
CAR.KIA_EV9: { # (MV)
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00MV__ RDR ----- 1.00 1.02 99110-DO700 ',
b'\xf1\x00MV__ RDR ----- 1.00 1.02 99110-DO000 '
],
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00FE MFC AT KOR LHD 1.00 1.00 99211-M5100 201218',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00FE MDPS C 1.00 1.05 56340-M5000 9903',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00FE__ SCC FHCUP 1.00 1.05 99110-M5000 ',
],
},
CAR.HYUNDAI_KONA_2022: {
(Ecu.fwdCamera, 0x7c4, None): [
b'\xf1\x00OSP LKA AT CND LHD 1.00 1.04 99211-J9200 904',
b'\xf1\x00OSP LKA AT USA LHD 1.00 1.04 99211-J9200 904',
],
(Ecu.eps, 0x7d4, None): [
b'\xf1\x00OSP MDPS C 1.00 1.04 56310/J9290 4OPCC104',
b'\xf1\x00OSP MDPS C 1.00 1.04 56310/J9291 4OPCC104',
b'\xf1\x00OSP MDPS C 1.00 1.04 56310J9291\x00 4OPCC104',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00YB__ FCA ----- 1.00 1.01 99110-J9000 \x00\x00\x00',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x00HT6WA280BLHT6VA650A1COS4N20NS1\x00\x00\x00\x00\x00\x00\x15\xf5\x87~',
b'\xf1\x00T01960BL T01E60A1 DOS2T16X4XE60NS4N\x90\xe6\xcb',
b'\xf1\x00T01G00BL T01I00A1 DOS2T16X2XI00NS0\x8c`\xff\xe7',
b'\xf1\x00T01G00BL T01I00A1 DOS2T16X4XI00NS0\x99L\xeeq',
b'\xf1\x00MV MFC AT KOR LHD 1.00 1.01 99211-DO000 230419',
b'\xf1\x00MV MFC AT USA LHD 1.00 1.02 99211-DO000 230616',
b'\xf1\x00MV MFC AT EUR LHD 1.00 1.02 99211-DO000 230616',
],
},
}
FW_VERSIONS = extend_fw_versions(FW_VERSIONS, FW_VERSIONS_EXT)
+255 -120
View File
@@ -1,17 +1,33 @@
import copy
import crcmod
from iqdbc.car.hyundai.values import CAR, HyundaiFlags
from iqdbc.lvbs.car.hyundai.escc import EnhancedSmartCruiseControl
from iqdbc.lvbs.car.hyundai.lead_data_ext import CanLeadData
hyundai_checksum = crcmod.mkCrcFun(0x11D, initCrc=0xFD, rev=False, xorOut=0xdf)
def suppress_casper_ev_fca11_fault(values):
# CASPER EV can report transient FCA faults during camera-SCC handoff.
# Keep the copied FCA11 frame non-faulting without changing other cars.
fca_fault = values["FCA_Failinfo"] != 0 or values["FCA_Status"] == 3
values["FCA_Failinfo"] = 0
if fca_fault:
values["FCA_Status"] = 2
values["CF_VSM_Prefill"] = 0
values["CF_VSM_HBACmd"] = 0
values["CF_VSM_Warn"] = 0
values["CF_VSM_BeltCmd"] = 0
values["CR_VSM_DecCmd"] = 0
values["FCA_CmdAct"] = 0
values["FCA_StopReq"] = 0
values["CF_VSM_DecCmdAct"] = 0
return values
def create_lkas11(packer, frame, CP, apply_torque, steer_req,
torque_fault, lkas11, sys_warning, sys_state, enabled,
left_lane, right_lane,
left_lane_depart, right_lane_depart,
lkas_icon):
left_lane_depart, right_lane_depart, is_ldws_car):
values = {s: lkas11[s] for s in [
"CF_Lkas_LdwsActivemode",
"CF_Lkas_LdwsSysState",
@@ -30,7 +46,7 @@ def create_lkas11(packer, frame, CP, apply_torque, steer_req,
"CF_Lkas_LdwsOpt_USM",
]}
values["CF_Lkas_LdwsSysState"] = sys_state
values["CF_Lkas_SysWarning"] = 3 if sys_warning else 0
values["CF_Lkas_SysWarning"] = 0 # 3 if sys_warning else 0
values["CF_Lkas_LdwsLHWarning"] = left_lane_depart
values["CF_Lkas_LdwsRHWarning"] = right_lane_depart
values["CR_Lkas_StrToqReq"] = apply_torque
@@ -38,16 +54,10 @@ def create_lkas11(packer, frame, CP, apply_torque, steer_req,
values["CF_Lkas_ToiFlt"] = torque_fault # seems to allow actuation on CR_Lkas_StrToqReq
values["CF_Lkas_MsgCount"] = frame % 0x10
if CP.carFingerprint in (CAR.HYUNDAI_SONATA, CAR.HYUNDAI_PALISADE, CAR.KIA_NIRO_EV, CAR.KIA_NIRO_HEV_2021, CAR.KIA_NIRO_PHEV_2022, CAR.HYUNDAI_SANTA_FE,
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.KIA_CEED_PHEV_2022_NON_SCC, CAR.HYUNDAI_KONA_EV_NON_SCC, CAR.HYUNDAI_ELANTRA_2022_NON_SCC,
CAR.GENESIS_G70_2021_NON_SCC, CAR.KIA_SELTOS_2023_NON_SCC, CAR.HYUNDAI_BAYON_1ST_GEN_NON_SCC):
if CP.flags & HyundaiFlags.SEND_LFA.value or CP.carFingerprint in (CAR.HYUNDAI_SANTA_FE):
values["CF_Lkas_LdwsActivemode"] = int(left_lane) + (int(right_lane) << 1)
values["CF_Lkas_LdwsOpt_USM"] = 2
values["CF_Lkas_LdwsOpt_USM"] = 0 if CP.carFingerprint in (CAR.KIA_RAY_EV) else 2
# FcwOpt_USM 5 = Orange blinking car + lanes
# FcwOpt_USM 4 = Orange car + lanes
@@ -55,16 +65,16 @@ def create_lkas11(packer, frame, CP, apply_torque, steer_req,
# FcwOpt_USM 2 = Green car + lanes
# FcwOpt_USM 1 = White car + lanes
# FcwOpt_USM 0 = No car + lanes
values["CF_Lkas_FcwOpt_USM"] = lkas_icon
values["CF_Lkas_FcwOpt_USM"] = 2 if enabled else 1
# SysWarning 4 = keep hands on wheel
# SysWarning 5 = keep hands on wheel (red)
# SysWarning 6 = keep hands on wheel (red) + beep
# Note: the warning is hidden while the blinkers are on
values["CF_Lkas_SysWarning"] = 4 if sys_warning else 0
values["CF_Lkas_SysWarning"] = 0 #4 if sys_warning else 0
# Likely cars lacking the ability to show individual lane lines in the dash
elif CP.carFingerprint in (CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.HYUNDAI_KONA_NON_SCC):
elif CP.carFingerprint in (CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL):
# SysWarning 4 = keep hands on wheel + beep
values["CF_Lkas_SysWarning"] = 4 if sys_warning else 0
@@ -72,7 +82,7 @@ def create_lkas11(packer, frame, CP, apply_torque, steer_req,
# SysState 1-2 = white car + lanes
# SysState 3 = green car + lanes, green steering wheel
# SysState 4 = green car + lanes
values["CF_Lkas_LdwsSysState"] = lkas_icon
values["CF_Lkas_LdwsSysState"] = 3 if enabled else 1
values["CF_Lkas_LdwsOpt_USM"] = 2 # non-2 changes above SysState definition
# these have no effect
@@ -84,6 +94,11 @@ def create_lkas11(packer, frame, CP, apply_torque, steer_req,
# Genesis and Optima fault when forwarding while engaged
values["CF_Lkas_LdwsActivemode"] = 2
if is_ldws_car:
values["CF_Lkas_LdwsOpt_USM"] = 3
values["CF_Lkas_Chksum"] = 0
dat = packer.make_can_msg("LKAS11", 0, values)[1]
if CP.flags & HyundaiFlags.CHECKSUM_CRC8:
@@ -124,145 +139,265 @@ def create_clu11(packer, frame, clu11, button, CP):
return packer.make_can_msg("CLU11", bus, values)
def create_lfahda_mfc(packer, enabled, lfa_icon):
def create_lfahda_mfc(packer, CC, blinking_signal):
activeCarrot = CC.hudControl.activeCarrot
values = {
"LFA_Icon_State": lfa_icon,
"LFA_Icon_State": 2 if CC.latActive else 1 if CC.enabled else 0,
#"HDA_Active": 1 if activeCarrot >= 2 else 0,
#"HDA_Icon_State": 2 if activeCarrot == 3 and blinking_signal else 2 if activeCarrot >= 2 else 0,
"HDA_Icon_State": 0 if activeCarrot == 3 and blinking_signal else 2 if activeCarrot >= 1 else 0,
"HDA_VSetReq": 0, #set_speed_in_units if activeCarrot >= 2 else 0,
"HDA_USM" : 2,
"HDA_Icon_Wheel" : 1 if CC.latActive else 0,
#"HDA_Chime" : 1 if CC.latActive else 0, # comment for K9 chime,
}
return packer.make_can_msg("LFAHDA_MFC", 0, values)
def create_acc_commands_scc(packer, enabled, accel, jerk, idx, hud_control, set_speed, stopping, long_override, suppress_casper_ev_fca, CS, soft_hold_mode):
from iqdbc.car.hyundai.carcontroller import HyundaiJerk
cruise_available = CS.out.cruiseState.available
if CS.paddle_button_prev > 0:
cruise_available = False
soft_hold_active = CS.softHoldActive
soft_hold_info = soft_hold_active > 1 and enabled
#soft_hold_mode = 2 ## some cars can't enable while braking
long_enabled = enabled or (soft_hold_active > 0 and soft_hold_mode == 2)
stop_req = 1 if stopping or (soft_hold_active > 0 and soft_hold_mode == 2) else 0
d = hud_control.leadDistance
objGap = 0 if d == 0 else 2 if d < 25 else 3 if d < 40 else 4 if d < 70 else 5
objGap2 = 0 if objGap == 0 else 2 if hud_control.leadRelSpeed < -0.2 else 1
if long_enabled:
if jerk.carrot_cruise == 1:
long_enabled = False
accel = -0.5
elif jerk.carrot_cruise == 2:
accel = jerk.carrot_cruise_accel
if long_enabled:
scc12_acc_mode = 2 if long_override else 1
scc14_acc_mode = 2 if long_override else 1
if CS.out.brakeHoldActive:
scc12_acc_mode = 0
scc14_acc_mode = 4
elif CS.out.brakePressed:
scc12_acc_mode = 1
scc14_acc_mode = 1
else:
scc12_acc_mode = 0
scc14_acc_mode = 4
warning_front = False
def create_acc_commands(packer, enabled, accel, upper_jerk, idx, lead_data: CanLeadData,
hud_control, set_speed, stopping, long_override, use_fca, CP,
main_cruise_enabled, tuning, ESCC: EnhancedSmartCruiseControl | None = None):
commands = []
if CS.scc11 is not None:
values = copy.copy(CS.scc11)
values["MainMode_ACC"] = 1 if cruise_available else 0
values["TauGapSet"] = hud_control.leadDistanceBars
values["VSetDis"] = set_speed if enabled else 0
values["AliveCounterACC"] = idx % 0x10
values["SCCInfoDisplay"] = 3 if warning_front else 4 if soft_hold_info else 0 if enabled else 0 #2: 크루즈 선택, 3: 전방상황주의, 4: 출발준비
values["ObjValid"] = 1 if hud_control.leadVisible else 0
values["ACC_ObjStatus"] = 1 if hud_control.leadVisible else 0
values["ACC_ObjLatPos"] = 0
values["ACC_ObjRelSpd"] = hud_control.leadRelSpeed
values["ACC_ObjDist"] = int(hud_control.leadDistance)
values["DriverAlertDisplay"] = 0
commands.append(packer.make_can_msg("SCC11", 0, values))
def get_scc11_values():
return {
"MainMode_ACC": 1 if main_cruise_enabled else 0,
"TauGapSet": hud_control.leadDistanceBars,
"VSetDis": set_speed if enabled else 0,
"AliveCounterACC": idx % 0x10,
"ObjValid": int(lead_data.lead_visible), # close lead makes controls tighter
"ACC_ObjStatus": int(lead_data.lead_visible), # close lead makes controls tighter
"ACC_ObjLatPos": 0,
"ACC_ObjRelSpd": lead_data.lead_rel_speed,
"ACC_ObjDist": int(lead_data.lead_distance), # close lead makes controls tighter
}
if CS.scc12 is not None:
values = copy.copy(CS.scc12)
values["ACCMode"] = scc12_acc_mode #2 if enabled and long_override else 1 if long_enabled else 0
values["StopReq"] = stop_req
values["aReqRaw"] = accel
values["aReqValue"] = accel
values["ACCFailInfo"] = 0
def get_scc12_values():
scc12_values = {
"ACCMode": 2 if enabled and long_override else 1 if enabled else 0,
"StopReq": 1 if tuning.stopping else 0,
"aReqRaw": tuning.desired_accel,
"aReqValue": tuning.actual_accel, # stock ramps up and down respecting jerk limit until it reaches aReqRaw
"CR_VSM_Alive": idx % 0xF,
}
#values["DESIRED_DIST"] = CS.out.vEgo * 1.0 + 4.0 # TF: 1.0 + STOPDISTANCE 4.0 m로 가정함.
# show AEB disabled indicator on dash with SCC12 if not sending FCA messages.
# these signals also prevent a TCS fault on non-FCA cars with alpha longitudinal
if not use_fca:
scc12_values["CF_VSM_ConfMode"] = 1
scc12_values["AEB_Status"] = 1 # AEB disabled
# Since we have ESCC available, we can update SCC12 with ESCC values.
if ESCC and ESCC.enabled:
ESCC.update_scc12(scc12_values)
return scc12_values
def calculate_scc12_checksum(values):
values["CR_VSM_ChkSum"] = 0
values["CR_VSM_Alive"] = idx % 0xF
scc12_dat = packer.make_can_msg("SCC12", 0, values)[1]
values["CR_VSM_ChkSum"] = 0x10 - sum(sum(divmod(i, 16)) for i in scc12_dat) % 0x10
return values
def get_scc14_values():
return {
"ComfortBandUpper": tuning.comfort_band_upper, # stock usually is 0 but sometimes uses higher values
"ComfortBandLower": tuning.comfort_band_lower, # stock usually is 0 but sometimes uses higher values
"JerkUpperLimit": tuning.jerk_upper, # stock usually is 1.0 but sometimes uses higher values
"JerkLowerLimit": tuning.jerk_lower, # stock usually is 0.5 but sometimes uses higher values
"ACCMode": 2 if enabled and long_override else 1 if enabled else 4, # stock will always be 4 instead of 0 after first disengage
"ObjGap": lead_data.object_gap, # 5: >30, m, 4: 25-30 m, 3: 20-25 m, 2: < 20 m, 0: no lead
"ObjDistStat": lead_data.object_rel_gap,
}
commands.append(packer.make_can_msg("SCC12", 0, values))
def get_fca11_values():
return {
if CS.scc14 is not None:
values = copy.copy(CS.scc14)
values["ComfortBandUpper"] = jerk.cb_upper
values["ComfortBandLower"] = jerk.cb_lower
values["JerkUpperLimit"] = jerk.jerk_u
values["JerkLowerLimit"] = jerk.jerk_l if long_enabled else 0 # for KONA test
values["ACCMode"] = scc14_acc_mode #2 if enabled and long_override else 1 if long_enabled else 4 # stock will always be 4 instead of 0 after first disengage
values["ObjGap"] = objGap #2 if hud_control.leadVisible else 0 # 5: >30, m, 4: 25-30 m, 3: 20-25 m, 2: < 20 m, 0: no lead
values["ObjDistStat"] = objGap2
commands.append(packer.make_can_msg("SCC14", 0, values))
if CS.fca11 is not None and suppress_casper_ev_fca: # CASPER_EV의 경우 FCA11에서 fail이 간헐적 발생함.. 그냥막자.. 원인불명..
values = suppress_casper_ev_fca11_fault(copy.copy(CS.fca11))
fca11_dat = packer.make_can_msg("FCA11", 0, values)[1]
values["CR_FCA_ChkSum"] = hyundai_checksum(fca11_dat[:7])
commands.append(packer.make_can_msg("FCA11", 0, values))
# Only send FCA11 on cars where it exists on the bus
if False: #use_fca:
# note that some vehicles most likely have an alternate checksum/counter definition
# https://github.com/commaai/iqdbc/commit/9ddcdb22c4929baf310295e832668e6e7fcfa602
fca11_values = {
"CR_FCA_Alive": idx % 0xF,
"PAINT1_Status": 1,
"FCA_DrvSetStatus": 1,
"FCA_Status": 1,
"FCA_Status": 1, # AEB disabled
}
def calculate_fca11_checksum(values):
fca11_dat = packer.make_can_msg("FCA11", 0, values)[1]
values["CR_FCA_ChkSum"] = hyundai_checksum(fca11_dat[:7])
return values
scc11_values = get_scc11_values()
commands.append(packer.make_can_msg("SCC11", 0, scc11_values))
scc12_values = get_scc12_values()
scc12_values = calculate_scc12_checksum(scc12_values)
commands.append(packer.make_can_msg("SCC12", 0, scc12_values))
scc14_values = get_scc14_values()
commands.append(packer.make_can_msg("SCC14", 0, scc14_values))
# Only send FCA11 on cars where it exists on the bus
# On Camera SCC cars, FCA11 is not disabled, so we forward stock FCA11 back to the car forward hooks
# If we don't use ESCC since ESCC does not block FCA11 from stock radar
if use_fca and not ((CP.flags & HyundaiFlags.CAMERA_SCC) or (ESCC and ESCC.enabled)):
# note that some vehicles most likely have an alternate checksum/counter definition
# https://github.com/commaai/iqdbc/commit/9ddcdb22c4929baf310295e832668e6e7fcfa602
fca11_values = get_fca11_values()
fca11_values = calculate_fca11_checksum(fca11_values)
fca11_dat = packer.make_can_msg("FCA11", 0, fca11_values)[1]
fca11_values["CR_FCA_ChkSum"] = hyundai_checksum(fca11_dat[:7])
commands.append(packer.make_can_msg("FCA11", 0, fca11_values))
return commands
def create_acc_opt_copy(CS, packer):
values = copy.copy(CS.scc13)
if values["NEW_SIGNAL_1"] == 255:
values["NEW_SIGNAL_1"] = 218
values["NEW_SIGNAL_2"] = 0
return packer.make_can_msg("SCC13", 0, CS.scc13)
def create_acc_opt(packer, CP, ESCC: EnhancedSmartCruiseControl | None = None):
"""
Creates SCC13 and FCA12. If ESCC is enabled, it will only create SCC13 since ESCC does not block FCA12.
:param packer:
:param ESCC:
:return:
"""
def create_acc_commands(packer, enabled, accel, jerk, idx, hud_control, set_speed, stopping, long_override, use_fca, CP, CS, soft_hold_mode):
from iqdbc.car.hyundai.carcontroller import HyundaiJerk
cruise_available = CS.out.cruiseState.available
soft_hold_active = CS.softHoldActive
soft_hold_info = soft_hold_active > 1 and enabled
#soft_hold_mode = 2 ## some cars can't enable while braking
long_enabled = enabled or (soft_hold_active > 0 and soft_hold_mode == 2)
stop_req = 1 if stopping or (soft_hold_active > 0 and soft_hold_mode == 2) else 0
d = hud_control.leadDistance
objGap = 0 if d == 0 else 2 if d < 25 else 3 if d < 40 else 4 if d < 70 else 5
objGap2 = 0 if objGap == 0 else 2 if hud_control.leadRelSpeed < -0.2 else 1
def get_scc13_values():
return {
"SCCDrvModeRValue": 2,
"SCC_Equip": 1,
"Lead_Veh_Dep_Alert_USM": 2,
}
if long_enabled:
scc12_acc_mode = 2 if long_override else 1
scc14_acc_mode = 2 if long_override else 1
if CS.out.brakeHoldActive:
scc12_acc_mode = 0
scc14_acc_mode = 4
elif CS.out.brakePressed:
scc12_acc_mode = 1
scc14_acc_mode = 1
else:
scc12_acc_mode = 0
scc14_acc_mode = 4
def get_fca12_values():
return {
"FCA_DrvSetState": 2,
"FCA_USM": 1, # AEB disabled
}
warning_front = False
commands = []
scc13_values = get_scc13_values()
commands.append(packer.make_can_msg("SCC13", 0, scc13_values))
scc11_values = {
"MainMode_ACC": 1 if cruise_available else 0,
"TauGapSet": hud_control.leadDistanceBars,
"VSetDis": set_speed if enabled else 0,
"AliveCounterACC": idx % 0x10,
"SCCInfoDisplay": 3 if warning_front else 4 if soft_hold_info else 0 if enabled else 0,
"ObjValid": 1 if hud_control.leadVisible else 0, # close lead makes controls tighter
"ACC_ObjStatus": 1 if hud_control.leadVisible else 0, # close lead makes controls tighter
"ACC_ObjLatPos": 0,
"ACC_ObjRelSpd": hud_control.leadRelSpeed,
"ACC_ObjDist": int(hud_control.leadDistance), # close lead makes controls tighter
"DriverAlertDisplay": 0,
}
commands.append(packer.make_can_msg("SCC11", 0, scc11_values))
# If ESCC is available and enabled, we skip FCA12, since ESCC does not block FCA12
if ESCC and ESCC.enabled:
return commands
scc12_values = {
"ACCMode": scc12_acc_mode,
"StopReq": stop_req,
"aReqRaw": 0 if stop_req > 0 else accel,
"aReqValue": accel, # stock ramps up and down respecting jerk limit until it reaches aReqRaw
#"DESIRED_DIST": CS.out.vEgo * 1.0 + 4.0,
"CR_VSM_Alive": idx % 0xF,
}
# show AEB disabled indicator on dash with SCC12 if not sending FCA messages.
# these signals also prevent a TCS fault on non-FCA cars with alpha longitudinal
if not use_fca:
scc12_values["CF_VSM_ConfMode"] = 1
scc12_values["AEB_Status"] = 1 # AEB disabled
scc12_dat = packer.make_can_msg("SCC12", 0, scc12_values)[1]
scc12_values["CR_VSM_ChkSum"] = 0x10 - sum(sum(divmod(i, 16)) for i in scc12_dat) % 0x10
commands.append(packer.make_can_msg("SCC12", 0, scc12_values))
scc14_values = {
"ComfortBandUpper": jerk.cb_upper, # stock usually is 0 but sometimes uses higher values
"ComfortBandLower": jerk.cb_lower, # stock usually is 0 but sometimes uses higher values
"JerkUpperLimit": jerk.jerk_u, # stock usually is 1.0 but sometimes uses higher values
"JerkLowerLimit": jerk.jerk_l, # stock usually is 0.5 but sometimes uses higher values
"ACCMode": scc14_acc_mode, # if enabled and long_override else 1 if enabled else 4, # stock will always be 4 instead of 0 after first disengage
"ObjGap": objGap, #2 if hud_control.leadVisible else 0, # 5: >30, m, 4: 25-30 m, 3: 20-25 m, 2: < 20 m, 0: no lead
"ObjDistStat": objGap2,
}
commands.append(packer.make_can_msg("SCC14", 0, scc14_values))
# Only send FCA11 on cars where it exists on the bus
# On Camera SCC cars, FCA11 is not disabled, so we forward stock FCA11 back to the car forward hooks
if use_fca and not (CP.flags & HyundaiFlags.CAMERA_SCC):
# note that some vehicles most likely have an alternate checksum/counter definition
# https://github.com/commaai/iqdbc/commit/9ddcdb22c4929baf310295e832668e6e7fcfa602
fca11_values = {
"CR_FCA_Alive": idx % 0xF,
"PAINT1_Status": 1,
"FCA_DrvSetStatus": 1,
"FCA_Status": 1, # AEB disabled
}
fca11_dat = packer.make_can_msg("FCA11", 0, fca11_values)[1]
fca11_values["CR_FCA_ChkSum"] = hyundai_checksum(fca11_dat[:7])
commands.append(packer.make_can_msg("FCA11", 0, fca11_values))
return commands
def create_acc_opt(packer, CP):
commands = []
scc13_values = {
"SCCDrvModeRValue": 2,
"SCC_Equip": 1,
"Lead_Veh_Dep_Alert_USM": 2,
}
commands.append(packer.make_can_msg("SCC13", 0, scc13_values))
# TODO: this needs to be detected and conditionally sent on unsupported long cars
# On Camera SCC cars, FCA12 is not disabled, so we forward stock FCA12 back to the car forward hooks
if not (CP.flags & HyundaiFlags.CAMERA_SCC):
fca12_values = get_fca12_values()
fca12_values = {
"FCA_DrvSetState": 2,
"FCA_USM": 1, # AEB disabled
}
commands.append(packer.make_can_msg("FCA12", 0, fca12_values))
return commands
def create_frt_radar_opt(packer):
frt_radar11_values = {
"CF_FCA_Equip_Front_Radar": 1,
}
return packer.make_can_msg("FRT_RADAR11", 0, frt_radar11_values)
def create_clu11_button(packer, frame, clu11, button, CP):
values = clu11.copy()
values["CF_Clu_CruiseSwState"] = button
#values["CF_Clu_AliveCnt1"] = frame % 0x10
values["CF_Clu_AliveCnt1"] = (values["CF_Clu_AliveCnt1"] + 1) % 0x10
# send buttons to camera on camera-scc based cars
bus = 2 if CP.flags & HyundaiFlags.CAMERA_SCC else 0
return packer.make_can_msg("CLU11", bus, values)
def create_mdps12(packer, frame, mdps12):
values = mdps12
values["CF_Mdps_ToiActive"] = 0
values["CF_Mdps_ToiUnavail"] = 1
values["CF_Mdps_MsgCount2"] = frame % 0x100
values["CF_Mdps_Chksum2"] = 0
dat = packer.make_can_msg("MDPS12", 2, values)[1]
checksum = sum(dat) % 256
values["CF_Mdps_Chksum2"] = checksum
return packer.make_can_msg("MDPS12", 2, values)
+767 -95
View File
@@ -2,22 +2,42 @@ import copy
import numpy as np
from iqdbc.car import CanBusBase
from iqdbc.car.crc import CRC16_XMODEM
from iqdbc.car.hyundai.values import HyundaiFlags
from iqdbc.lvbs.car.hyundai.lead_data_ext import CanFdLeadData
from iqdbc.car.hyundai.values import HyundaiFlags, HyundaiExtFlags
from openpilot.common.params import Params
from iqdbc.car.common.conversions import Conversions as CV
from cereal import log
LaneChangeState = log.LaneChangeState
LaneChangeDirection = log.LaneChangeDirection
TurnDirection = log.Desire
def hyundai_crc8(data: bytes) -> int:
poly = 0x2F
crc = 0xFF
for byte in data:
crc ^= byte
for _ in range(8):
if crc & 0x80:
crc = ((crc << 1) ^ poly) & 0xFF
else:
crc = (crc << 1) & 0xFF
return crc ^ 0xFF
class CanBus(CanBusBase):
def __init__(self, CP, fingerprint=None, lka_steering=None) -> None:
super().__init__(CP, fingerprint)
if lka_steering is None:
lka_steering = CP.flags & HyundaiFlags.CANFD_LKA_STEERING.value if CP is not None else False
lka_steering = CP.flags & HyundaiFlags.CANFD_HDA2.value if CP is not None else False
# On the CAN-FD platforms, the LKAS camera is on both A-CAN and E-CAN. LKA steering cars
# have a different harness than the LFA steering variants in order to split
# a different bus, since the steering is done by different ECUs.
self._a, self._e = 1, 0
if lka_steering:
if lka_steering and Params().get_int("HyundaiCameraSCC") == 0: #배선개조는 무조건 Bus0가 ECAN임.
self._a, self._e = 0, 1
self._a += self.offset
@@ -36,50 +56,190 @@ class CanBus(CanBusBase):
def CAM(self):
return self._cam
# CAN LIST (CAM) - 롱컨개조시... ADAS + CAM
# 160: ADRV_0x160
# 1da: ADRV_0x1da
# 1ea: ADRV_0x1ea
# 200: ADRV_0x200
# 345: ADRV_0x345
# 1fa: CLUSTER_SPEED_LIMIT
# 12a: LFA
# 1e0: LFAHDA_CLUSTER
# 11a:
# 1b5:
# 1a0: SCC_CONTROL
def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque, lkas_icon):
common_values = {
"LKA_MODE": 2,
"LKA_ICON": lkas_icon,
"TORQUE_REQUEST": apply_torque,
"LKA_ASSIST": 0,
"STEER_REQ": 1 if lat_active else 0,
"STEER_MODE": 0,
"HAS_LANE_SAFETY": 0, # hide LKAS settings
"NEW_SIGNAL_2": 0,
"DAMP_FACTOR": 100, # can potentially tuned for better perf [3, 200]
}
# CAN LIST (ACAN)
# 160: ADRV_0x160
# 51: ADRV_0x51
# 180: CAM_0x180
# ...
# 185: CAM_0x185
# 1b6: CAM_0x1b6
# ...
# 1b9: CAM_0x1b9
# 1fb: CAM_0x1fb
# 2a2 - 2a4
# 2bb - 2be
# LKAS
# 201 - 2a0
lkas_values = copy.copy(common_values)
lkas_values["LKA_AVAILABLE"] = 0
lfa_values = copy.copy(common_values)
lfa_values["NEW_SIGNAL_1"] = 0
def create_steering_messages_camera_scc(frame, packer, CP, CAN, CC, lat_active, apply_steer, CS, apply_angle, max_torque, angle_control):
emergency_steering = False
if CS.adrv_0x161 is not None:
values = CS.adrv_0x161
emergency_steering = values["ALERTS_1"] in [11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 26]
ret = []
if CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
lkas_msg = "LKAS_ALT" if CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT else "LKAS"
if CP.openpilotLongitudinalControl:
ret.append(packer.make_can_msg("LFA", CAN.ECAN, lfa_values))
ret.append(packer.make_can_msg(lkas_msg, CAN.ACAN, lkas_values))
else:
ret.append(packer.make_can_msg("LFA", CAN.ECAN, lfa_values))
if CS.mdps is not None:
values = copy.copy(CS.mdps)
#rx_counter = values.pop("COUNTER", None)
if angle_control:
if CS.lfa_alt is not None:
values["LFA2_ACTIVE"] = CS.lfa_alt["LKAS_ANGLE_ACTIVE"]
else:
if CS.lfa is not None:
values["LKA_ACTIVE"] = 1 if CS.lfa["STEER_REQ"] == 1 else 0
if frame % 1000 < 40:
values["STEERING_COL_TORQUE"] += 220
#ret.append(packer.make_can_msg("MDPS", CAN.CAM, values, rx_counter = rx_counter))
ret.append(packer.make_can_msg("MDPS", CAN.CAM, values))
if frame % 10 == 0:
if CS.steer_touch_2af is not None:
values = copy.copy(CS.steer_touch_2af)
if frame % 1000 < 40:
values["TOUCH_DETECT"] = 3
values["TOUCH1"] = 50
values["TOUCH2"] = 50
values["CHECKSUM_"] = 0
dat = packer.make_can_msg("STEER_TOUCH_2AF", 0, values)[1]
values["CHECKSUM_"] = hyundai_crc8(dat[1:8])
ret.append(packer.make_can_msg("STEER_TOUCH_2AF", CAN.CAM, values))
if angle_control:
if CS.lfa_alt is not None:
values = copy.copy(CS.lfa_alt)
rx_counter = values.pop("COUNTER", None)
if emergency_steering:
pass
else:
#values = {} #CS.lfa_alt
values["LKAS_ANGLE_ACTIVE"] = 2 if CC.latActive else 1
values["LKAS_ANGLE_CMD"] = -apply_angle
values["LKAS_ANGLE_MAX_TORQUE"] = max_torque if CC.latActive else 0
ret.append(packer.make_can_msg("LFA_ALT", CAN.ECAN, values, rx_counter = rx_counter))
if CS.lfa is not None:
values = copy.copy(CS.lfa)
rx_counter = values.pop("COUNTER", None)
if not emergency_steering:
values["LKA_MODE"] = 0
values["LKA_ICON"] = 2 if CC.latActive else 1
values["TORQUE_REQUEST"] = -1024 # apply_steer,
values["VALUE63"] = 0 # LKA_ASSIST
values["STEER_REQ"] = 0 # 1 if lat_active else 0,
values["HAS_LANE_SAFETY"] = 0 # hide LKAS settings
values["LKA_ACTIVE"] = 3 if CC.latActive else 0 # this changes sometimes, 3 seems to indicate engaged
values["VALUE64"] = 0 #STEER_MODE, NEW_SIGNAL_2
values["LKAS_ANGLE_CMD"] = -25.6 #-apply_angle,
values["LKAS_ANGLE_ACTIVE"] = 0 #2 if lat_active else 1,
values["LKAS_ANGLE_MAX_TORQUE"] = 0 #max_torque if lat_active else 0,
values["NEW_SIGNAL_1"] = 10
ret.append(packer.make_can_msg("LFA", CAN.ECAN, values, rx_counter = rx_counter))
elif CS.lfa is not None:
values = {}
values["LKA_MODE"] = 2
values["LKA_ICON"] = 2 if lat_active else 1
values["TORQUE_REQUEST"] = apply_steer
values["STEER_REQ"] = 1 if lat_active else 0
values["VALUE64"] = 0 # STEER_MODE, NEW_SIGNAL_2
values["HAS_LANE_SAFETY"] = 0
values["LKA_ACTIVE"] = 0 # NEW_SIGNAL_1
values["DampingGain"] = 0 if lat_active else 100
#values["VALUE63"] = 0
#values["VALUE82_SET256"] = 0
ret.append(packer.make_can_msg("LFA", CAN.ECAN, values))
return ret
def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_steer, apply_angle, max_torque, angle_control):
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
ret = []
if angle_control:
values = {
"LKA_MODE": 0,
"LKA_ICON": 2 if enabled else 1,
"TORQUE_REQUEST": 0, # apply_steer,
"VALUE63": 0, # LKA_ASSIST
"STEER_REQ": 0, # 1 if lat_active else 0,
"HAS_LANE_SAFETY": 0, # hide LKAS settings
"LKA_ACTIVE": 3 if lat_active else 0, # this changes sometimes, 3 seems to indicate engaged
"VALUE64": 0, #STEER_MODE, NEW_SIGNAL_2
"LKAS_ANGLE_CMD": -apply_angle,
"LKAS_ANGLE_ACTIVE": 2 if lat_active else 1,
"LKAS_ANGLE_MAX_TORQUE": max_torque if lat_active else 0,
values = {f"BYTE{i}": lfa_block_msg[f"BYTE{i}"] for i in range(3, msg_bytes) if i != 7}
# test for EV6PE
"NEW_SIGNAL_1": 10, #2,
"DampingGain": 9,
"VALUE231": 146,
"VALUE239": 1,
"VALUE247": 255,
"VALUE255": 255,
}
else:
values = {
"LKA_MODE": 2,
"LKA_ICON": 2 if enabled else 1,
"TORQUE_REQUEST": apply_steer,
"DampingGain": 100, #3 if enabled else 100,
"STEER_REQ": 1 if lat_active else 0,
#"STEER_MODE": 0,
"HAS_LANE_SAFETY": 0, # hide LKAS settings
"VALUE63": 0,
"VALUE64": 100,
}
if CP.flags & HyundaiFlags.CANFD_HDA2:
lkas_msg = "LKAS_ALT" if CP.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING else "LKAS"
if CP.openpilotLongitudinalControl:
ret.append(packer.make_can_msg("LFA", CAN.ECAN, values))
if not (CP.flags & HyundaiFlags.CAMERA_SCC.value):
ret.append(packer.make_can_msg(lkas_msg, CAN.ACAN, values))
else:
ret.append(packer.make_can_msg("LFA", CAN.ECAN, values))
return ret
def create_suppress_lfa(packer, CAN, CS):
if CS.cam_0x362 is not None:
suppress_msg = "CAM_0x362"
lfa_block_msg = CS.cam_0x362
elif CS.cam_0x2a4 is not None:
suppress_msg = "CAM_0x2a4"
lfa_block_msg = CS.cam_0x2a4
else:
return []
#values = {f"BYTE{i}": lfa_block_msg[f"BYTE{i}"] for i in range(3, msg_bytes) if i != 7}
values = copy.copy(lfa_block_msg)
values["COUNTER"] = lfa_block_msg["COUNTER"]
values["SET_ME_0"] = 0
values["SET_ME_0_2"] = 0
values["LEFT_LANE_LINE"] = 0
values["RIGHT_LANE_LINE"] = 0
return packer.make_can_msg(suppress_msg, CAN.ACAN, values)
return [packer.make_can_msg(suppress_msg, CAN.ACAN, values)]
def create_buttons(packer, CP, CAN, cnt, btn):
values = {
@@ -88,14 +248,12 @@ def create_buttons(packer, CP, CAN, cnt, btn):
"CRUISE_BUTTONS": btn,
}
bus = CAN.ECAN if CP.flags & HyundaiFlags.CANFD_LKA_STEERING else CAN.CAM
#bus = CAN.ECAN if CP.flags & HyundaiFlags.CANFD_HDA2 else CAN.CAM
bus = CAN.ECAN
return packer.make_can_msg("CRUISE_BUTTONS", bus, values)
def create_acc_cancel(packer, CP, CAN, cruise_info_copy):
# CAN FD camera-based SCC requires additional signals to be preserved
# verbatim from the previous SCC_CONTROL frame to avoid checksum or
# state validation faults. Classic CAN SCC only validates a subset.
# TODO: why do we copy different values here?
if CP.flags & HyundaiFlags.CANFD_CAMERA_SCC.value:
values = {s: cruise_info_copy[s] for s in [
"COUNTER",
@@ -124,49 +282,169 @@ def create_acc_cancel(packer, CP, CAN, cruise_info_copy):
})
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
def create_lfahda_cluster(packer, CAN, enabled, lfa_icon):
values = {
"HDA_ICON": 1 if enabled else 0,
"LFA_ICON": lfa_icon,
}
return packer.make_can_msg("LFAHDA_CLUSTER", CAN.ECAN, values)
def create_lfahda_cluster(packer, CS, CAN, long_active, lat_active):
def create_acc_control(packer, CAN, enabled, accel_last, accel, stopping, gas_override, set_speed, hud_control,
lead_data: CanFdLeadData, main_cruise_enabled, tuning):
if CS.lfahda_cluster is not None:
values = copy.copy(CS.lfahda_cluster)
rx_counter = values.pop("COUNTER", None)
else:
return []
values = {}
rx_counter = None
values["LFA_OptUsmSta"] = 2
values["HDA_OptUsmSta"] = 2
values["HDA_CntrlModSta"] = 2 if long_active else 0
values["HDA_LFA_SymSta"] = 2 if lat_active else 0
return [packer.make_can_msg("LFAHDA_CLUSTER", CAN.ECAN, values, rx_counter=rx_counter)]
def create_lfa_icon_non_camera_scc(packer, CS, CAN, CC):
ret = []
if CS.adrv_0x161 is not None:
values = copy.copy(CS.adrv_0x161)
rx_counter = values.pop("COUNTER", None)
lat_active = CC.latActive
lat_enabled = CS.out.latEnabled
values["LFA_ICON"] = 2 if lat_active else 1 if lat_enabled else 0
values["LKA_ICON"] = 4 if lat_active else 3 if lat_enabled else 0
if values["ALERTS_2"] in [1, 2, 5, 6, 10, 21, 22]:
values["ALERTS_2"] = 0
values["DAW_ICON"] = 0
if values["ALERTS_1"] == 0:
values["SOUNDS_1"] = 0
values["SOUNDS_2"] = 0
values["SOUNDS_4"] = 0
if values["ALERTS_3"] in [3, 4, 11, 12, 13, 14, 17, 19, 26, 7, 8, 9, 10]:
values["ALERTS_3"] = 0
values["SOUNDS_3"] = 0
if values["ALERTS_5"] in [1, 2, 3, 4, 5]:
values["ALERTS_5"] = 0
ret.append(packer.make_can_msg("ADRV_0x161", CAN.ECAN, values, rx_counter=rx_counter))
return ret
def create_acc_control_scc2(packer, CAN, enabled, accel_last, accel, stopping, gas_override, set_speed, hud_control, hyundai_jerk, CS):
if CS.scc_control is None:
return None
enabled = (enabled or CS.softHoldActive > 0) and CS.paddle_button_prev == 0
acc_mode = 0 if not enabled else (2 if gas_override else 1)
if hyundai_jerk.carrot_cruise == 1:
acc_mode = 4 if enabled else 0
enabled = False
accel = accel_last = 0.5
elif hyundai_jerk.carrot_cruise == 2:
accel = accel_last = hyundai_jerk.carrot_cruise_accel
jerk_u = hyundai_jerk.jerk_u
jerk_l = hyundai_jerk.jerk_l
jerk = 5
jn = jerk / 50
if not enabled or gas_override:
a_val, a_raw = 0, 0
else:
a_raw = accel # noqa: F841
a_val = np.clip(accel, accel_last - jn, accel_last + jn) # noqa: F841
a_raw = accel
a_val = accel #np.clip(accel, accel_last - jn, accel_last + jn)
values = copy.copy(CS.scc_control)
rx_counter = values.pop("COUNTER", None)
values["ACCMode"] = acc_mode
values["MainMode_ACC"] = 1
values["StopReq"] = 1 if stopping or CS.softHoldActive > 0 else 0 # 1: Stop control is required, 2: Not used, 3: Error Indicator
values["aReqValue"] = a_val
values["aReqRaw"] = a_raw
values["VSetDis"] = set_speed
#values["JerkLowerLimit"] = jerk if enabled else 1
#values["JerkUpperLimit"] = 3.0
values["JerkLowerLimit"] = jerk_l if enabled else 1
values["JerkUpperLimit"] = 2.0 if stopping or CS.softHoldActive else jerk_u
values["DISTANCE_SETTING"] = hud_control.leadDistanceBars # + 5
#values["DISTANCE_SETTING"] = hud_control.leadDistanceBars + 5
#values["ACC_ObjDist"] = 1
#values["ObjValid"] = 0
#values["OBJ_STATUS"] = 2
#values["NSCCOper"] = 1 if enabled else 0 # 0: off, 1: Ready, 2: Act, 3: Error Indicator
#values["NSCCOnOff"] = 2 # 0: Default, 1: Off, 2: On, 3: Invalid
#values["SET_ME_3"] = 0x3 # objRelsped와 충돌
#values["ACC_ObjLatPos"] = - hud_control.leadDPath
values["DriveMode"] = 0 # 0: Default, 1: Comfort Mode, 2:Normal mode, 3:Dynamic mode, reserved
hud_lead_info = 0
if hud_control.leadVisible:
hud_lead_info = 1 if values["ACC_ObjRelSpd"] > 0 else 2
values["HUD_LEAD_INFO"] = hud_lead_info #1: in-path object detected(uncontrollable), 2: controllable long, 3: controllable long & lat, ... reserved
values["DriverAlert"] = 0 # 1: SCC Disengaged, 2: No SCC Engage condition, 3: SCC Disenganed when the vehicle stops
values["TARGET_DISTANCE"] = CS.out.vEgo * 1.0 + 4.0
soft_hold_info = 1 if CS.softHoldActive > 1 and enabled else 0
# 이거안하면 정지중 뒤로 밀리는 현상 발생하는듯.. (신호정지중에 뒤로 밀리는 경험함.. 시험해봐야)
if values["InfoDisplay"] != 5: #5: Front Car Departure Notice
values["InfoDisplay"] = 4 if stopping and CS.out.aEgo > -0.3 else 0 # 1: SCC Mode, 2: Convention Cruise Mode, 3: Object disappered at low speed, 4: Available to resume acceleration control, 5: Front vehicle departure notice, 6: Reserved, 7: Invalid
values["TakeOverReq"] = 0 # 1: Takeover request, 2: Not used, 3: Error indicator , 이것이 켜지면 가속을 안하는듯함.
#values["NEW_SIGNAL_4"] = 9 if hud_control.leadVisible else 0
# AccelLimitBandUpper, Lower
values["SysFailState"] = 0 # 1: Performance degredation, 2: system temporairy unavailble, 3: SCC Service required , 눈이 묻어 레이더오류시... 2가 됨. 이때 가속을 안함...
values["AccelLimitBandUpper"] = 0.0 # 이값이 1.26일때 가속을 안하는 증상이 보임..
values["AccelLimitBandLower"] = 0.0
values["ZEROS_7"] = 1
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
def create_acc_control(packer, CAN, enabled, accel_last, accel, stopping, gas_override, set_speed, hud_control, jerk_u, jerk_l, CS):
enabled = enabled or CS.softHoldActive > 0
jerk = 5
jn = jerk / 50
if not enabled or gas_override:
a_val, a_raw = 0, 0
else:
a_raw = accel
a_val = np.clip(accel, accel_last - jn, accel_last + jn)
values = {
"ACCMode": 0 if not enabled else (2 if gas_override else 1),
"MainMode_ACC": 1 if main_cruise_enabled else 0,
"StopReq": 1 if tuning.stopping else 0,
"aReqValue": tuning.actual_accel,
"aReqRaw": tuning.actual_accel,
"MainMode_ACC": 1,
"StopReq": 1 if stopping or CS.softHoldActive > 0 else 0,
"aReqValue": a_val,
"aReqRaw": a_raw,
"VSetDis": set_speed,
"JerkLowerLimit": tuning.jerk_lower,
"JerkUpperLimit": tuning.jerk_upper,
#"JerkLowerLimit": jerk if enabled else 1,
#"JerkUpperLimit": 3.0,
"JerkLowerLimit": jerk_l if enabled else 1,
"JerkUpperLimit": jerk_u,
"ACC_ObjDist": int(lead_data.lead_distance),
"ACC_ObjRelSpd": lead_data.lead_rel_speed,
"ObjValid": int(not lead_data.lead_visible),
"SCC_ObjSta": 0 if not (enabled and lead_data.lead_visible) else (1 if gas_override else 2),
"SET_ME_2": 0x4,
"SET_ME_3": 0x3,
"SET_ME_TMP_64": 0x64,
"DISTANCE_SETTING": hud_control.leadDistanceBars,
"ACC_ObjDist": 1,
#"ObjValid": 0,
#"OBJ_STATUS": 2,
"NSCCOper": 0,
"NSCCOnOff": 2,
"DriveMode": 0,
#"SET_ME_3": 0x3,
"ACC_ObjLatPos": 0x64,
"DISTANCE_SETTING": hud_control.leadDistanceBars, # + 5,
"InfoDisplay": 4 if stopping and CS.out.cruiseState.standstill else 0,
}
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
def create_spas_messages(packer, CAN, left_blink, right_blink):
def create_spas_messages(packer, CAN, frame, left_blink, right_blink):
ret = []
values = {
@@ -186,8 +464,10 @@ def create_spas_messages(packer, CAN, left_blink, right_blink):
return ret
def create_fca_warning_light(packer, CAN, frame):
def create_fca_warning_light(CP, packer, CAN, frame):
ret = []
if CP.flags & HyundaiFlags.CAMERA_SCC.value:
return ret
if frame % 2 == 0:
values = {
@@ -196,53 +476,100 @@ def create_fca_warning_light(packer, CAN, frame):
'SET_ME_FF': 0xff,
'SET_ME_FC': 0xfc,
'SET_ME_9': 0x9,
#'DATA102': 1,
}
ret.append(packer.make_can_msg("ADRV_0x160", CAN.ECAN, values))
return ret
def create_tcs_messages(packer, CAN, CS):
ret = []
if CS.tcs is not None:
values = copy.copy(CS.tcs)
#rx_counter = values.pop("COUNTER", None)
values["DriverBraking"] = 0
values["NEW_SIGNAL_20"] = 0
values["NEW_SIGNAL_11"] = 0
values["DriverBrakingLowSens"] = 0
#values["NEW_SIGNAL_1"] = 0 # accel과 관련.. 옆두부 꺼지는것과 관련? 확인필요
#values["ACC_REQ"] = 1 # 옆두부 꺼지는것과 관련? 확인필요.. 항상 켜지게함..
values["NEW_SIGNAL_1"] = 0 if values["ACC_REQ"] == 1 else 1 # 옆두부..
#ret.append(packer.make_can_msg("TCS", CAN.CAM, values, rx_counter = rx_counter))
ret.append(packer.make_can_msg("TCS", CAN.CAM, values))
return ret
def create_adrv_messages(packer, CAN, frame):
def forward_button_message(packer, CAN, frame, CS, cruise_button, MainMode_ACC_trigger, LFA_trigger):
ret = []
if frame % 2 == 0:
if CS.cruise_buttons_msg is not None:
values = copy.copy(CS.cruise_buttons_msg)
# A held MAIN is reported on this bit and switches some clusters to LIMIT mode.
values["NORMAL_CRUISE_MAIN_BTN"] = 0
#rx_counter = values.pop("COUNTER", None)
cruise_button_driver = values["CRUISE_BUTTONS"]
if cruise_button_driver == 0:
values["CRUISE_BUTTONS"] = cruise_button
if MainMode_ACC_trigger > 0:
#values["ADAPTIVE_CRUISE_MAIN_BTN"] = 1
pass
elif LFA_trigger > 0:
values["LFA_BTN"] = 1
#ret.append(packer.make_can_msg(CS.cruise_btns_msg_canfd, CAN.CAM, values, rx_counter = rx_counter))
ret.append(packer.make_can_msg(CS.cruise_btns_msg_canfd, CAN.CAM, values))
return ret
def create_adrv_messages(CP, packer, CAN, frame):
# messages needed to car happy after disabling
# the ADAS Driving ECU to do longitudinal control
ret = []
values = {
}
ret.append(packer.make_can_msg("ADRV_0x51", CAN.ACAN, values))
if not CP.flags & HyundaiFlags.CAMERA_SCC.value:
values = {}
ret.extend(create_fca_warning_light(packer, CAN, frame))
ret.extend(create_fca_warning_light(CP, packer, CAN, frame))
if frame % 5 == 0:
values = {
#'HDA_MODE1': 0x8,
'HDA_MODE2': 0x1,
#'SET_ME_1C': 0x1c,
'SET_ME_FF': 0xff,
#'SET_ME_TMP_F': 0xf,
#'SET_ME_TMP_F_2': 0xf,
#'DATA26': 1, #1
#'DATA32': 5, #5
}
ret.append(packer.make_can_msg("ADRV_0x1ea", CAN.ECAN, values))
if frame % 5 == 0:
values = {
'SET_ME_1C': 0x1c,
'SET_ME_FF': 0xff,
'SET_ME_TMP_F': 0xf,
'SET_ME_TMP_F_2': 0xf,
}
ret.append(packer.make_can_msg("ADRV_0x1ea", CAN.ECAN, values))
values = {
'SET_ME_E1': 0xe1,
#'SET_ME_3A': 0x3a,
'TauGapSet' : 1,
'NEW_SIGNAL_2': 3,
}
ret.append(packer.make_can_msg("ADRV_0x200", CAN.ECAN, values))
values = {
'SET_ME_E1': 0xe1,
'SET_ME_3A': 0x3a,
}
ret.append(packer.make_can_msg("ADRV_0x200", CAN.ECAN, values))
if frame % 20 == 0:
values = {
'SET_ME_15': 0x15,
}
ret.append(packer.make_can_msg("ADRV_0x345", CAN.ECAN, values))
if frame % 20 == 0:
values = {
'SET_ME_15': 0x15,
}
ret.append(packer.make_can_msg("ADRV_0x345", CAN.ECAN, values))
if frame % 100 == 0:
values = {
'SET_ME_22': 0x22,
'SET_ME_41': 0x41,
}
ret.append(packer.make_can_msg("ADRV_0x1da", CAN.ECAN, values))
if frame % 100 == 0:
values = {
'SET_ME_22': 0x22,
'SET_ME_41': 0x41,
}
ret.append(packer.make_can_msg("ADRV_0x1da", CAN.ECAN, values))
return ret
## carrot
def alt_cruise_buttons(packer, CP, CAN, buttons, cruise_btns_msg, cnt):
cruise_btns_msg["CRUISE_BUTTONS"] = buttons
cruise_btns_msg["COUNTER"] = (cruise_btns_msg["COUNTER"] + 1 + cnt) % 256
bus = CAN.ECAN if CP.flags & HyundaiFlags.CANFD_HDA2 else CAN.CAM
return packer.make_can_msg("CRUISE_BUTTONS_ALT", bus, cruise_btns_msg)
def hkg_can_fd_checksum(address: int, sig, d: bytearray) -> int:
crc = 0
@@ -259,3 +586,348 @@ def hkg_can_fd_checksum(address: int, sig, d: bytearray) -> int:
elif len(d) == 32:
crc ^= 0x9F5B
return crc
def _clip_int(x, lo, hi):
return lo if x < lo else hi if x > hi else int(x)
def _get_desire_and_lane_changing(md):
desire = 0
lane_changing = 0
if md is not None:
desire = md.meta.desire.raw
ds = md.meta.desireState
if len(ds) > 4:
if ds[1] > 0.9: lane_changing = 1
if ds[2] > 0.9: lane_changing = 2
if ds[3] > 0.9: lane_changing = 3
if ds[4] > 0.9: lane_changing = 4
return desire, lane_changing
def _apply_lane_desire(values, desire):
#values['LANE_CHANGING'] = 0
if desire == 1: # 좌회전
values['LANE_CHANGING'] = 1
values["LANELINE_CURVATURE"] = 15
values["LANELINE_CURVATURE_DIRECTION"] = 0
elif desire == 2: # 우회전
values['LANE_CHANGING'] = 2
values["LANELINE_CURVATURE"] = 15
values["LANELINE_CURVATURE_DIRECTION"] = 1
elif desire == 3: # 좌차선변경
values['LANE_CHANGING'] = 3
elif desire == 4: # 우차선변경
values['LANE_CHANGING'] = 4
def _apply_radar_blink(values, radar_pairs, frame, *,
disp_dist=30.0, min_dist=14.0,
max_interval=100, t=1.0):
"""
거리 > min_dist 때만 깜빡임.
거리 멀수록 interval 커짐(느리게).
"""
for det_key, dist_key in radar_pairs:
dist = values[dist_key]
if dist <= min_dist:
continue
d = min(dist, disp_dist)
interval = int((1 + (max_interval - 1) * (d / disp_dist)) * t)
interval = _clip_int(interval, 1, max_interval)
blink = (frame // interval) & 1
values[det_key] = 2 - blink
values[dist_key] = min_dist
def _suppress_trailer_mode_warning(values, CS):
# Logs from IONIQ 9 show ALERTS_5=6 is the periodic
# "driver assistance limited in trailer mode" popup.
if CS.trailer_connected and values.get("ALERTS_5") == 6:
values["ALERTS_5"] = 0
def _make_ccnc_values(values, CS, lat_active, frame, hud_control,
lane_line=True, corner_radar=True,
desire=0,
blink_pairs=None,
blink_t=1.0):
if lane_line:
curvature = round(CS.out.steeringAngleDeg / 3)
mag = min(abs(curvature), 15)
curv = mag + (-1 if curvature < 0 else 0)
direction = 1 if curvature < 0 else 0
values["LANELINE_CURVATURE"] = curv if lat_active else 0
values["LANELINE_CURVATURE_DIRECTION"] = direction if lat_active else 0
if desire:
_apply_lane_desire(values, desire)
if corner_radar:
radar_all = [
('LF_DETECT', 'LF_DETECT_DISTANCE'),
('RF_DETECT', 'RF_DETECT_DISTANCE'),
('LR_DETECT', 'LR_DETECT_DISTANCE'),
('RR_DETECT', 'RR_DETECT_DISTANCE'),
]
for det_key, dist_key in radar_all:
if values[det_key] >= 4 and values[dist_key] != 0:
values[det_key] = 1
if blink_pairs:
_apply_radar_blink(values, blink_pairs, frame, t=blink_t)
def create_ccnc_messages(CP, packer, CAN, frame, CC, CS, hud_control,
disp_angle, left_lane_warning, right_lane_warning,
enable_corner_radar, stopping, canfd_debug):
ret = []
md = CS.modelV2
if not hasattr(create_ccnc_messages, '_lane_line_check') or frame % 100 == 0:
create_ccnc_messages._lane_line_check = Params().get_int("LaneLineCheck")
lane_line_check = create_ccnc_messages._lane_line_check
desire, lane_changing = _get_desire_and_lane_changing(md)
if CP.flags & HyundaiFlags.CAMERA_SCC.value:
HDA_CntrlModSta = 0
HDA_LFA_SymSta = 0
if CS.lfahda_cluster is not None:
HDA_CntrlModSta = CS.lfahda_cluster["HDA_CntrlModSta"]
HDA_LFA_SymSta = CS.lfahda_cluster["HDA_LFA_SymSta"]
if frame % 2 == 0:
#if CS.adrv_0x160 is not None:
# values = copy.copy(CS.adrv_0x160)
# ret.append(packer.make_can_msg("ADRV_0x160", CAN.ECAN, values))
if CS.cruise_buttons_msg is not None:
values = copy.copy(CS.cruise_buttons_msg)
# Keep the physical long press on ECAN for CarState, but don't forward it to CAM.
values["NORMAL_CRUISE_MAIN_BTN"] = 0
if HDA_LFA_SymSta == 0 and 0 < frame % 200 < 12:
values["LFA_BTN"] = 1
if CC.enabled:
if not CS.MainMode_ACC:
if 10 < frame % 200 <= 16 and CS.out.vEgo > 3.:
values["ADAPTIVE_CRUISE_MAIN_BTN"] = 1
elif CS.ACCMode in [0, 4]:
if 10 < frame % 200 <= 16 and CS.out.vEgo > 3.:
values["CRUISE_BUTTONS"] = 2
elif CS.scc_control is not None and CS.scc_control["InfoDisplay"] == 4:
if 10 < frame % 30 <= 16 and not stopping:
values["CRUISE_BUTTONS"] = 2
else:
if CS.adrv_0x1ea is not None and CS.adrv_0x1ea["HDA_MODE2"] == 0: # if corner radar is disabled, send main btn
if 10 < frame % 1000 <= 16 and CS.out.vEgo > 3:
values["ADAPTIVE_CRUISE_MAIN_BTN"] = 1
ret.append(packer.make_can_msg(CS.cruise_btns_msg_canfd, CAN.CAM, values))
# --- 0x161/0x200/0x1ea/0x162 (frame%5) ---
if frame % 5 == 0:
lat_active = CC.latActive
if CS.adrv_0x161 is not None:
main_enabled = CS.out.cruiseState.available
cruise_enabled = CC.enabled
lat_enabled = CS.out.latEnabled
nav_active = hud_control.activeCarrot > 1
# hdpuse carrot
hdp_use = int(Params().get("HDPuse"))
hdp_active = False
if hdp_use == 1:
hdp_active = cruise_enabled and nav_active
elif hdp_use == 2:
hdp_active = cruise_enabled
# hdpuse carrot
values = copy.copy(CS.adrv_0x161)
rx_counter = values.pop("COUNTER", None)
values["SETSPEED"] = (6 if hdp_active else 3 if cruise_enabled else 1) if main_enabled else 0
values["SETSPEED_HUD"] = (5 if hdp_active else 3 if cruise_enabled else 1) if main_enabled else 0
set_speed_in_units = hud_control.setSpeed * (CV.MS_TO_KPH if CS.is_metric else CV.MS_TO_MPH)
values["vSetDis"] = int(set_speed_in_units + 0.5)
values["DISTANCE"] = 4 if hdp_active else hud_control.leadDistanceBars
values["DISTANCE_LEAD"] = 2 if cruise_enabled and hud_control.leadVisible else 1 if main_enabled and hud_control.leadVisible else 0
values["DISTANCE_CAR"] = 3 if hdp_active else 2 if cruise_enabled else 1 if main_enabled else 0
values["DISTANCE_SPACING"] = 5 if hdp_active else 1 if cruise_enabled else 0
values["TARGET"] = 1 if hud_control.leadVisible and cruise_enabled else 0
values["TARGET_DISTANCE"] = int(hud_control.leadDistance)
values["BACKGROUND"] = 6 if CS.paddle_button_prev > 0 else 1 if cruise_enabled else 3 if lat_active else 7
values["CENTERLINE"] = 1 if HDA_CntrlModSta > 0 else 0
values["CAR_CIRCLE"] = 2 if hdp_active else 1 if cruise_enabled else 0
values["NAV_ICON"] = 2 if nav_active and cruise_enabled else 1 if main_enabled and nav_active else 0
values["HDA_ICON"] = 5 if hdp_active else 2 if cruise_enabled else 1 if main_enabled else 0
values["LFA_ICON"] = 5 if hdp_active else 2 if lat_active else 1 if lat_enabled else 0
values["LKA_ICON"] = 4 if lat_active else 3 if lat_enabled else 0
values["FCA_ALT_ICON"] = 0
if values["ALERTS_2"] in [1, 2, 5, 6, 10, 21, 22]:
values["ALERTS_2"] = 0
values["DAW_ICON"] = 0
if values["ALERTS_1"] == 0: # alerts가 있으면 사운드도 같이 나옴
values["SOUNDS_1"] = 0
values["SOUNDS_2"] = 0
values["SOUNDS_4"] = 0
if values["ALERTS_3"] in [3, 4, 11, 12, 13, 14, 17, 19, 20, 26, 27, 28, 7, 8, 9, 10]: # hide gap distance msg.(11,12,13,14), lanechange(19,20,27, 28)
values["ALERTS_3"] = 0
values["SOUNDS_3"] = 0
if values["ALERTS_5"] in [1, 2, 3, 4, 5]:
values["ALERTS_5"] = 0
if values["ALERTS_5"] in [11] and CS.softHoldActive == 0:
values["ALERTS_5"] = 0
# curvature 표시(0x161쪽 기존 로직 유지)
_suppress_trailer_mode_warning(values, CS)
curvature = round(CS.out.steeringAngleDeg / 3)
values["LANELINE_CURVATURE"] = (min(abs(curvature), 15) + (-1 if curvature < 0 else 0)) if lat_active else 0
values["LANELINE_CURVATURE_DIRECTION"] = 1 if curvature < 0 and lat_active else 0
trailer_lane_change_blocked = CS.trailer_connected
if trailer_lane_change_blocked:
values["LANELINE_LEFT"] = 2 if hud_control.leftLaneVisible else 0
values["LANELINE_RIGHT"] = 2 if hud_control.rightLaneVisible else 0
else:
lane_color = 6 if md is not None and md.meta.laneChangeAvailableLeft else 2
if lane_line_check >= 1:
lane_line_warn_left = CS.out.leftLaneLine % 10 not in (0, 5)
else:
lane_line_warn_left = CS.out.leftLaneLine // 10 == 2
lane_color = 4 if lane_line_warn_left or CS.out.leftBlindspot else lane_color
if hud_control.leftLaneDepart:
values["LANELINE_LEFT"] = 4 if (frame // 50) % 2 == 0 else 1
else:
values["LANELINE_LEFT"] = lane_color if hud_control.leftLaneVisible else 0
lane_color = 6 if md is not None and md.meta.laneChangeAvailableRight else 2
if lane_line_check >= 1:
lane_line_warn_right = CS.out.rightLaneLine % 10 not in (0, 5)
else:
lane_line_warn_right = CS.out.rightLaneLine // 10 == 2
lane_color = 4 if lane_line_warn_right or CS.out.rightBlindspot else lane_color
if hud_control.rightLaneDepart:
values["LANELINE_RIGHT"] = 4 if (frame // 50) % 2 == 0 else 1
else:
values["LANELINE_RIGHT"] = lane_color if hud_control.rightLaneVisible else 0
values["LCA_LEFT_ARROW"] = 2 if CS.out.leftBlinker else 0
values["LCA_RIGHT_ARROW"] = 2 if CS.out.rightBlinker else 0
if trailer_lane_change_blocked:
values["LCA_LEFT_ICON"] = 1 if lat_active else 0
values["LCA_RIGHT_ICON"] = 1 if lat_active else 0
else:
values["LCA_LEFT_ICON"] = (1 if CS.out.leftBlindspot else 2) if lat_active else 0
values["LCA_RIGHT_ICON"] = (1 if CS.out.rightBlindspot else 2) if lat_active else 0
values["LANE_LEFT"] = 0 if trailer_lane_change_blocked else 1 if desire in (1, 3) else 0
values["LANE_RIGHT"] = 0 if trailer_lane_change_blocked else 1 if desire in (2, 4) else 0
ret.append(packer.make_can_msg("ADRV_0x161", CAN.ECAN, values, rx_counter = rx_counter))
if CS.adrv_0x200 is not None:
values = copy.copy(CS.adrv_0x200)
rx_counter = values.pop("COUNTER", None)
values["TauGapSet"] = hud_control.leadDistanceBars
ret.append(packer.make_can_msg("ADRV_0x200", CAN.ECAN, values, rx_counter = rx_counter))
if CS.adrv_0x1ea is not None:
values = copy.copy(CS.adrv_0x1ea)
rx_counter = values.pop("COUNTER", None)
# blinker hold
values['LEFT_BLINK_HOLD'] = 1 if lane_changing == 3 else 0
values['RIGHT_BLINK_HOLD'] = 1 if lane_changing == 4 else 0
_make_ccnc_values(
values, CS, lat_active, frame, hud_control,
lane_line=True,
corner_radar=True,
desire=desire,
# 기존대로 LR/RR만 깜빡임
blink_pairs=[('LR_DETECT', 'LR_DETECT_DISTANCE'),
('RR_DETECT', 'RR_DETECT_DISTANCE')],
blink_t=1.0
)
ret.append(packer.make_can_msg("ADRV_0x1ea", CAN.ECAN, values, rx_counter = rx_counter))
if CS.ccnc_0x162 is not None:
values = copy.copy(CS.ccnc_0x162)
if hud_control.leadDistance > 0:
values["FF_DISTANCE"] = hud_control.leadDistance
ff_type = 3 if hud_control.leadRadar == 1 else 13
values["FF_DETECT"] = ff_type if hud_control.leadRelSpeed > -0.1 else ff_type + 1
_make_ccnc_values(
values, CS, lat_active, frame, hud_control,
lane_line=False,
corner_radar=True,
desire=0,
# 필요하면 162도 깜빡임 적용(원래 코드처럼 LR/RR만)
blink_pairs=[('LR_DETECT', 'LR_DETECT_DISTANCE'),
('RR_DETECT', 'RR_DETECT_DISTANCE')],
blink_t=1.0
)
if (left_lane_warning and not CS.out.leftBlinker) or (right_lane_warning and not CS.out.rightBlinker):
values["VIBRATE"] = 1
if canfd_debug > 0:
values["FAULT_LSS"] = 0
values["FAULT_DAS"] = 0
ret.append(packer.make_can_msg("CCNC_0x162", CAN.ECAN, values))
# --- NEW_MSG_4B9 (corner radar keep-alive?) ---
if enable_corner_radar > 0:
if HDA_CntrlModSta == 0:
if frame % 500 in [10, 20, 30]:
values = {
'BYTE_1': 0,
'BYTE_2': 0,
'BYTE_3': 0x80,
'BYTE_4': 0x8A,
'BYTE_5': 0x32,
'BYTE_6': 0x30,
'BYTE_7': 0x01,
'BYTE_8': 0x00,
}
ret.append(packer.make_can_msg("NEW_MSG_4B9", CAN.CAM, values))
elif frame % 500 in [40, 50, 60]:
values = {
'BYTE_1': 0xff,
'BYTE_2': 0xff,
'BYTE_3': 0xff,
'BYTE_4': 0xff,
'BYTE_5': 0xff,
'BYTE_6': 0xff,
'BYTE_7': 0xff,
'BYTE_8': 0xff,
}
ret.append(packer.make_can_msg("NEW_MSG_4B9", CAN.CAM, values))
if False: # canfd_debug > 1 and frame % 20 == 0:
if CS.hda_info_4a3 is not None:
values = copy.copy(CS.hda_info_4a3)
values["LinkClass"] = 1
values["SPEED_LIMIT"] = 100
ret.append(packer.make_can_msg("HDA_INFO_4A3", CAN.CAM, values))
return ret
+194 -130
View File
@@ -1,8 +1,10 @@
from iqdbc.car import Bus, get_safety_config, structs, uds
from iqdbc.car import Bus, get_safety_config, structs
from iqdbc.car.hyundai.hyundaicanfd import CanBus
from iqdbc.car.hyundai.values import HyundaiFlags, CAR, DBC, \
from iqdbc.car.hyundai.values import HyundaiFlags, HyundaiFlagsIQ, CAR, DBC, CANFD_RADAR_SCC_CAR, \
CANFD_UNSUPPORTED_LONGITUDINAL_CAR, \
UNSUPPORTED_LONGITUDINAL_CAR, HyundaiSafetyFlags
UNSUPPORTED_LONGITUDINAL_CAR, HyundaiSafetyFlags, HyundaiSafetyFlagsIQ, HyundaiExtFlags, \
CANFD_HYBRID_STATUS_ADDR, CANFD_HYBRID_STATUS_DLC, \
EV_MODE_STATUS_ADDR, EV_MODE_STATUS_DLC
from iqdbc.car.hyundai.radar_interface import RADAR_START_ADDR
from iqdbc.car.interfaces import CarInterfaceBase
from iqdbc.car.disable_ecu import disable_ecu
@@ -10,9 +12,7 @@ from iqdbc.car.hyundai.carcontroller import CarController
from iqdbc.car.hyundai.carstate import CarState
from iqdbc.car.hyundai.radar_interface import RadarInterface
from iqdbc.lvbs.car.hyundai.escc import ESCC_MSG
from iqdbc.lvbs.car.hyundai.longitudinal.helpers import get_longitudinal_tune
from iqdbc.lvbs.car.hyundai.values import HyundaiFlagsIQ, HyundaiSafetyFlagsIQ
from openpilot.common.params import Params
ButtonType = structs.CarState.ButtonEvent.Type
Ecu = structs.CarParams.Ecu
@@ -20,67 +20,125 @@ Ecu = structs.CarParams.Ecu
# Cancel button can sometimes be ACC pause/resume button, main button can also enable on some cars
ENABLE_BUTTONS = (ButtonType.accelCruise, ButtonType.decelCruise, ButtonType.cancel, ButtonType.mainCruise)
SteerControlType = structs.CarParams.SteerControlType
class CarInterface(CarInterfaceBase):
CarState = CarState
CarController = CarController
RadarInterface = RadarInterface
DRIVABLE_GEARS = (structs.CarState.GearShifter.sport, structs.CarState.GearShifter.manumatic)
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
params = Params()
camera_scc = params.get_int("HyundaiCameraSCC")
if camera_scc > 0:
ret.flags |= HyundaiFlags.CAMERA_SCC.value
print("$$$CAMERA_SCC toggled...")
ret.brand = "hyundai"
# "LKA steering" if LKAS or LKAS_ALT messages are seen coming from the camera.
# Generally means our LKAS message is forwarded to another ECU (commonly ADAS ECU)
# that finally retransmits our steering command in LFA or LFA_ALT to the MDPS.
# "LFA steering" if camera directly sends LFA to the MDPS
cam_can = CanBus(None, fingerprint).CAM
lka_steering = 0x50 in fingerprint[cam_can] or 0x110 in fingerprint[cam_can]
CAN = CanBus(None, fingerprint, lka_steering)
if candidate == CAR.KIA_SORENTO:
ret.extFlags |= HyundaiExtFlags.RADAR_GROUP4.value
cam_can = CanBus(None, fingerprint).CAM if camera_scc == 0 else 1
hda2 = False #0x50 in fingerprint[cam_can] or 0x110 in fingerprint[cam_can]
hda2 = hda2 or params.get_int("CanfdHDA2") > 0
CAN = CanBus(None, fingerprint, hda2)
if ret.flags & HyundaiFlags.CANFD:
# Shared configuration for CAN-FD cars
ret.alphaLongitudinalAvailable = candidate not in CANFD_UNSUPPORTED_LONGITUDINAL_CAR
if lka_steering and Ecu.adas not in [fw.ecu for fw in car_fw]:
# this needs to be figured out for cars without an ADAS ECU
ret.alphaLongitudinalAvailable = False
ret.alphaLongitudinalAvailable = True #candidate not in (CANFD_UNSUPPORTED_LONGITUDINAL_CAR | CANFD_RADAR_SCC_CAR)
#ret.enableBsm = 0x1e5 in fingerprint[CAN.ECAN]
ret.enableBsm = 0x1ba in fingerprint[CAN.ECAN] # BLINDSPOTS_REAR_CORNERS 0x1ba(442)
ret.enableBsm = 0x1e5 in fingerprint[CAN.ECAN]
# Check if the car is hybrid. Only HEV/PHEV cars have 0xFA on E-CAN.
if 0xFA in fingerprint[CAN.ECAN]:
if 0x105 in fingerprint[CAN.ECAN]:
ret.flags |= HyundaiFlags.HYBRID.value
if lka_steering:
# detect LKA steering
ret.flags |= HyundaiFlags.CANFD_LKA_STEERING.value
if 0x110 in fingerprint[CAN.CAM]:
ret.flags |= HyundaiFlags.CANFD_LKA_STEERING_ALT.value
# Keep drivetrain/safety classification unchanged: this capability is display-only and requires both exact ECAN frames.
has_ev_mode_status = fingerprint[CAN.ECAN].get(CANFD_HYBRID_STATUS_ADDR) == CANFD_HYBRID_STATUS_DLC and \
fingerprint[CAN.ECAN].get(EV_MODE_STATUS_ADDR) == EV_MODE_STATUS_DLC
if has_ev_mode_status:
ret.extFlags |= HyundaiExtFlags.EV_MODE_STATUS_230.value
if 203 in fingerprint[CAN.CAM]: # LFA_ALT
print("##### Anglecontrol detected (LFA_ALT)")
ret.flags |= HyundaiFlags.ANGLE_CONTROL.value
print("ACAN=", fingerprint[CAN.ACAN])
if 0x210 in fingerprint[CAN.ACAN]:
print("##### Radar Group 1 detected (0x210)")
ret.extFlags |= HyundaiExtFlags.RADAR_GROUP1.value
elif 0x400 in fingerprint[CAN.ACAN] and 0x41D in fingerprint[CAN.ACAN]:
print("##### Radar Group 3 detected (0x400-0x41D)")
ret.extFlags |= HyundaiExtFlags.RADAR_GROUP3.value
if all(fingerprint[CAN.ACAN].get(addr) == 32 for addr in range(0x235, 0x249)):
ret.extFlags |= HyundaiExtFlags.CORNER_RADAR_OBJECTS_235.value
print("##### Corner radar objects 0x235 group detected")
if all(fingerprint[CAN.ACAN].get(addr) == 32 for addr in range(0x180, 0x185)):
ret.extFlags |= HyundaiExtFlags.CORNER_RADAR_OBJECTS_180.value
print("##### Corner radar objects 0x180 group detected")
if all(fingerprint[CAN.ACAN].get(addr) == 32 for addr in tuple(range(0x430, 0x438)) + tuple(range(0x440, 0x448))):
ret.extFlags |= HyundaiExtFlags.CORNER_RADAR_OBJECTS_430.value
print("##### Corner radar objects 0x430/0x440 group detected")
# detect HDA2 with ADAS Driving ECU
if hda2:
print("$$$CANFD HDA2")
ret.flags |= HyundaiFlags.CANFD_HDA2.value
if camera_scc > 0:
if 0x110 in fingerprint[CAN.ACAN]:
ret.flags |= HyundaiFlags.CANFD_HDA2_ALT_STEERING.value
print("$$$CANFD ALT_STEERING1")
else:
if 0x110 in fingerprint[CAN.CAM]: # 0x110(272): LKAS_ALT
ret.flags |= HyundaiFlags.CANFD_HDA2_ALT_STEERING.value
print("$$$CANFD ALT_STEERING1")
## carrot_todo: sorento:
if 0x2a4 not in fingerprint[CAN.CAM]: # 0x2a4(676): CAM_0x2a4
ret.flags |= HyundaiFlags.CANFD_HDA2_ALT_STEERING.value
print("$$$CANFD ALT_STEERING2")
## carrot: canival 4th, no 0x1cf
if 0x1cf not in fingerprint[CAN.ECAN]: # 0x1cf(463): CRUISE_BUTTONS
ret.flags |= HyundaiFlags.CANFD_ALT_BUTTONS.value
print("$$$CANFD ALT_BUTTONS")
else:
# no LKA steering
# non-HDA2
print("$$$CANFD non HDA2")
if 0x1cf not in fingerprint[CAN.ECAN]:
ret.flags |= HyundaiFlags.CANFD_ALT_BUTTONS.value
if not ret.flags & HyundaiFlags.RADAR_SCC:
ret.flags |= HyundaiFlags.CANFD_CAMERA_SCC.value
# Some LKA steering cars have alternative messages for gear checks
print("$$$CANFD ALT_BUTTONS")
#if not ret.flags & HyundaiFlags.RADAR_SCC:
# ret.flags |= HyundaiFlags.CANFD_CAMERA_SCC.value
# print("$$$CANFD CAMERA_SCC")
# Some HDA2 cars have alternative messages for gear checks
# ICE cars do not have 0x130; GEARS message on 0x40 or 0x70 instead
if 0x130 not in fingerprint[CAN.ECAN]:
if 0x40 not in fingerprint[CAN.ECAN]:
ret.flags |= HyundaiFlags.CANFD_ALT_GEARS_2.value
else:
ret.flags |= HyundaiFlags.CANFD_ALT_GEARS.value
if 0x40 in fingerprint[CAN.ECAN]: # 0x40(64): GEAR_ALT
ret.flags |= HyundaiFlags.CANFD_ALT_GEARS.value
print("$$$CANFD ALT_GEARS")
elif 69 in fingerprint[CAN.ECAN]: # Special case
ret.extFlags |= HyundaiExtFlags.CANFD_GEARS_69.value
print("$$$CANFD GEARS_69")
elif 112 in fingerprint[CAN.ECAN]: # carrot: eGV70
ret.flags |= HyundaiFlags.CANFD_ALT_GEARS_2.value
print("$$$CANFD ALT_GEARS_2")
elif 0x130 in fingerprint[CAN.ECAN]: # 0x130(304): GEAR_SHIFTER
print("$$$CANFD GEAR_SHIFTER present")
else:
ret.extFlags |= HyundaiExtFlags.CANFD_GEARS_NONE.value
print("$$$CANFD GEARS_NONE")
cfgs = [get_safety_config(structs.CarParams.SafetyModel.hyundaiCanfd), ]
if CAN.ECAN >= 4:
cfgs.insert(0, get_safety_config(structs.CarParams.SafetyModel.noOutput))
ret.safetyConfigs = cfgs
if ret.flags & HyundaiFlags.CANFD_LKA_STEERING:
if ret.flags & HyundaiFlags.CANFD_HDA2:
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CANFD_LKA_STEERING.value
if ret.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT:
if ret.flags & HyundaiFlags.CANFD_HDA2_ALT_STEERING:
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CANFD_LKA_STEERING_ALT.value
if ret.flags & HyundaiFlags.CANFD_ALT_BUTTONS:
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CANFD_ALT_BUTTONS.value
@@ -89,56 +147,93 @@ class CarInterface(CarInterfaceBase):
else:
# Shared configuration for non CAN-FD cars
ret.alphaLongitudinalAvailable = candidate not in UNSUPPORTED_LONGITUDINAL_CAR
ret.alphaLongitudinalAvailable = True #candidate not in (UNSUPPORTED_LONGITUDINAL_CAR | CAMERA_SCC_CAR)
ret.enableBsm = 0x58b in fingerprint[0]
print(f"$$$ enableBsm = {ret.enableBsm}")
# Send LFA message on cars with HDA
if 0x485 in fingerprint[2]:
ret.flags |= HyundaiFlags.SEND_LFA.value
print("$$$SEND_LFA")
# These cars use the FCA11 message for the AEB and FCW signals, all others use SCC12
if 0x38d in fingerprint[0] or 0x38d in fingerprint[2]:
ret.flags |= HyundaiFlags.USE_FCA.value
print("$$$USE_FCA")
if ret.flags & HyundaiFlags.LEGACY:
# these cars require a special panda safety mode due to missing counters and checksums in the messages
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.hyundaiLegacy)]
print("$$$Legacy Safety Model")
else:
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.hyundai, 0)]
if ret.flags & HyundaiFlags.CAMERA_SCC:
ret.safetyConfigs[0].safetyParam |= HyundaiSafetyFlags.CAMERA_SCC.value
# These cars have the LFA button on the steering wheel
if 0x391 in fingerprint[0]:
ret.flags |= HyundaiFlags.HAS_LDA_BUTTON.value
print("$$$CAMERA_SCC")
# Common lateral control setup
ret.centerToFront = ret.wheelbase * 0.4
ret.steerActuatorDelay = 0.1
ret.steerLimitTimer = 0.4
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
if ret.flags & HyundaiFlags.ANGLE_CONTROL:
ret.steerControlType = SteerControlType.angle
else:
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
if ret.flags & HyundaiFlags.ALT_LIMITS:
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.ALT_LIMITS.value
if ret.flags & HyundaiFlags.ALT_LIMITS_2:
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.ALT_LIMITS_2.value
# see https://github.com/commaai/iqdbc/pull/1137/
ret.dashcamOnly = True
# Common longitudinal control setup
ret.radarUnavailable = RADAR_START_ADDR not in fingerprint[1] or Bus.radar not in DBC[ret.carFingerprint]
ret.openpilotLongitudinalControl = alpha_long and ret.alphaLongitudinalAvailable
# carrot, if camera_scc enabled, enable openpilotLongitudinalControl
enable_radar_tracks = params.get_int("EnableRadarTracks")
if ret.flags & HyundaiFlags.CAMERA_SCC.value or enable_radar_tracks > 0 or enable_radar_tracks == -2:
ret.radarUnavailable = False
ret.openpilotLongitudinalControl = True if camera_scc < 3 else False
print(f"$$$OenpilotLongitudinalControl = True, CAMERA_SCC({ret.flags & HyundaiFlags.CAMERA_SCC.value}) or RadarTracks{enable_radar_tracks}")
else:
print(f"$$$OenpilotLongitudinalControl = {alpha_long}")
#ret.radarUnavailable = False # TODO: canfd... carrot, hyundai cars have radar
ret.radarTimeStep = 0.05 #if params.get_int("EnableRadarTracks") > 0 else 0.02
ret.pcmCruise = not ret.openpilotLongitudinalControl
ret.startingState = True
ret.startingState = False # True # carrot
ret.vEgoStarting = 0.1
ret.startAccel = 1.0
ret.longitudinalActuatorDelay = 0.5
ret.longitudinalTuning.kpBP = [0.]
ret.longitudinalTuning.kpV = [1.]
ret.longitudinalTuning.kf = 1.0
# *** feature detection ***
if ret.flags & HyundaiFlags.CANFD:
print(f"$$$$$ CanFD ECAN = {CAN.ECAN}")
if 0x1fa in fingerprint[CAN.ECAN]:
ret.extFlags |= HyundaiExtFlags.NAVI_CLUSTER.value
print("$$$$ NaviCluster = True")
else:
print("$$$$ NaviCluster = False")
else:
if 1348 in fingerprint[0]:
ret.extFlags |= HyundaiExtFlags.NAVI_CLUSTER.value
print("$$$$ NaviCluster = True")
if 1157 in fingerprint[0] or 1157 in fingerprint[2]:
ret.extFlags |= HyundaiExtFlags.HAS_LFAHDA.value
print("$$$$ HasLFAHDA")
if 1007 in fingerprint[0]:
print("#### cruiseButtonAlt")
print(f"$$$$ enableBsm = {ret.enableBsm}")
if ret.openpilotLongitudinalControl:
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.LONG.value
if ret.flags & HyundaiFlags.HYBRID:
@@ -155,95 +250,64 @@ class CarInterface(CarInterfaceBase):
# Dashcam cars are missing a test route, or otherwise need validation
# TODO: Optima Hybrid 2017 uses a different SCC12 checksum
if candidate in (CAR.KIA_OPTIMA_H,):
ret.dashcamOnly = True
#ret.dashcamOnly = candidate in {CAR.KIA_OPTIMA_H, }
return ret
@staticmethod
def _get_params_iq(stock_cp: structs.CarParams, ret: structs.IQCarParams, candidate, fingerprint: dict[int, dict[int, int]],
car_fw: list[structs.CarParams.CarFw], alpha_long: bool, is_release_iq: bool, docs: bool) -> structs.IQCarParams:
# identical logic used in _get_params
# "LKA steering" if LKAS or LKAS_ALT messages are seen coming from the camera.
# Generally means our LKAS message is forwarded to another ECU (commonly ADAS ECU)
# that finally retransmits our steering command in LFA or LFA_ALT to the MDPS.
# "LFA steering" if camera directly sends LFA to the MDPS
cam_can = CanBus(None, fingerprint).CAM
lka_steering = 0x50 in fingerprint[cam_can] or 0x110 in fingerprint[cam_can]
CAN = CanBus(None, fingerprint, lka_steering)
if not stock_cp.flags & HyundaiFlags.CANFD:
# TODO-IQ: add route with ESCC message for process replay
if ESCC_MSG in fingerprint[0]:
ret.flags |= HyundaiFlagsIQ.ENHANCED_SCC.value
if ret.flags & HyundaiFlagsIQ.ENHANCED_SCC:
ret.iqSafetyFlags |= HyundaiSafetyFlagsIQ.ESCC
stock_cp.radarUnavailable = False
if stock_cp.flags & HyundaiFlags.HAS_LDA_BUTTON:
def _get_params_iq(stock_cp, ret, candidate, fingerprint, car_fw, alpha_long, is_release_iq, docs):
del candidate, car_fw, alpha_long, is_release_iq, docs
if not stock_cp.flags & HyundaiFlags.CANFD and 0x391 in fingerprint[0]:
ret.flags |= HyundaiFlagsIQ.HAS_LFA_BUTTON
ret.iqSafetyFlags |= HyundaiSafetyFlagsIQ.HAS_LDA_BUTTON
if stock_cp.flags & (HyundaiFlags.CANFD_CAMERA_SCC | HyundaiFlags.CAMERA_SCC):
stock_cp.radarUnavailable = False
if stock_cp.flags & HyundaiFlags.ALT_LIMITS_2:
stock_cp.dashcamOnly = False
if ret.flags & HyundaiFlagsIQ.NON_SCC:
stock_cp.alphaLongitudinalAvailable = False
stock_cp.openpilotLongitudinalControl = False
stock_cp.pcmCruise = True
ret.iqSafetyFlags |= HyundaiSafetyFlagsIQ.NON_SCC
# untested non-SCC platforms, need user validations
if stock_cp.carFingerprint in (CAR.HYUNDAI_BAYON_1ST_GEN_NON_SCC, CAR.KIA_FORTE_2021_NON_SCC,
CAR.KIA_SELTOS_2023_NON_SCC, CAR.GENESIS_G70_2021_NON_SCC):
stock_cp.dashcamOnly = True
if stock_cp.flags & HyundaiFlags.CANFD:
if 0x1fa in fingerprint[CAN.ECAN]:
ret.flags |= HyundaiFlagsIQ.SPEED_LIMIT_AVAILABLE.value
else:
# Detect smartMDPS, which bypasses EPS low-speed lockout, allowing iqpilot to send steering commands down to 0
if 0x2AA in fingerprint[0]:
stock_cp.minSteerSpeed = 0.0
stock_cp.flags &= ~HyundaiFlags.MIN_STEER_32_MPH.value
if 0x544 in fingerprint[0]:
ret.flags |= HyundaiFlagsIQ.SPEED_LIMIT_AVAILABLE.value
if 0x53E in fingerprint[2]:
ret.flags |= HyundaiFlagsIQ.HAS_LKAS12.value
return ret
@staticmethod
def _get_longitudinal_tuning_iq(stock_cp: structs.CarParams, ret: structs.IQCarParams) -> structs.IQCarParams:
if ret.flags & (HyundaiFlagsIQ.LONG_TUNING_DYNAMIC | HyundaiFlagsIQ.LONG_TUNING_PREDICTIVE):
get_longitudinal_tune(stock_cp)
def init(CP, can_recv, can_send):
return ret
Params().put_int('LongitudinalPersonalityMax', 4)
@staticmethod
def init(CP, CP_IQ, can_recv, can_send, communication_control=None):
# 0x80 silences response
if communication_control is None:
communication_control = bytes([uds.SERVICE_TYPE.COMMUNICATION_CONTROL, 0x80 | uds.CONTROL_TYPE.DISABLE_RX_DISABLE_TX, uds.MESSAGE_TYPE.NORMAL])
if CP.openpilotLongitudinalControl and not ((CP.flags & (HyundaiFlags.CANFD_CAMERA_SCC | HyundaiFlags.CAMERA_SCC)) or
(CP_IQ.flags & HyundaiFlagsIQ.ENHANCED_SCC)):
addr, bus = 0x7d0, CanBus(CP).ECAN if CP.flags & HyundaiFlags.CANFD else 0
if CP.flags & HyundaiFlags.CANFD_LKA_STEERING.value:
if CP.openpilotLongitudinalControl and not (CP.flags & HyundaiFlags.CANFD_CAMERA_SCC):
addr, bus = 0x7d0, 0
if CP.flags & HyundaiFlags.CANFD_HDA2.value:
addr, bus = 0x730, CanBus(CP).ECAN
disable_ecu(can_recv, can_send, bus=bus, addr=addr, com_cont_req=communication_control)
disable_ecu(can_recv, can_send, bus=bus, addr=addr, com_cont_req=b'\x28\x83\x01')
params = Params()
if params.get_int("EnableRadarTracks") > 0 and not CP.flags & HyundaiFlags.CANFD:
result = enable_radar_tracks(CP, can_recv, can_send)
params.put_bool("EnableRadarTracksResult", result)
# for blinkers
if CP.flags & HyundaiFlags.ENABLE_BLINKERS:
disable_ecu(can_recv, can_send, bus=CanBus(CP).ECAN, addr=0x7B1, com_cont_req=communication_control)
disable_ecu(can_recv, can_send, bus=CanBus(CP).ECAN, addr=0x7B1, com_cont_req=b'\x28\x83\x01')
@staticmethod
def deinit(CP, can_recv, can_send):
communication_control = bytes([uds.SERVICE_TYPE.COMMUNICATION_CONTROL, 0x80 | uds.CONTROL_TYPE.ENABLE_RX_ENABLE_TX, uds.MESSAGE_TYPE.NORMAL])
CarInterface.init(CP, can_recv, can_send, communication_control)
def enable_radar_tracks(CP, logcan, sendcan):
from iqdbc.car.isotp_parallel_query import IsoTpParallelQuery
print("################ Try To Enable Radar Tracks ####################")
ret = False
sccBus = 2 if CP.flags & HyundaiFlags.CAMERA_SCC.value else 0
rdr_fw = None
rdr_fw_address = 0x7d0 #
try:
try:
query = IsoTpParallelQuery(sendcan, logcan, sccBus, [rdr_fw_address], [b'\x10\x07'], [b'\x50\x07'])
for addr, dat in query.get_data(0.1).items(): # pylint: disable=unused-variable
print("ecu write data by id ...")
new_config = b"\x00\x00\x00\x01\x00\x01"
#new_config = b"\x00\x00\x00\x00\x00\x01"
dataId = b'\x01\x42'
WRITE_DAT_REQUEST = b'\x2e'
WRITE_DAT_RESPONSE = b'\x68'
query = IsoTpParallelQuery(sendcan, logcan, sccBus, [rdr_fw_address], [WRITE_DAT_REQUEST+dataId+new_config], [WRITE_DAT_RESPONSE])
result = query.get_data(0)
print("result=", result)
ret = True
break
except Exception as e:
print(f"Failed : {e}")
except Exception as e:
print("############## Failed to enable tracks" + str(e))
print("################ END Try to enable radar tracks")
return ret
+851 -52
View File
@@ -1,86 +1,885 @@
import math
import os
from collections import deque
from iqdbc import DBC_PATH
from iqdbc.can import CANParser
from iqdbc.car import Bus, structs
from iqdbc.car.interfaces import RadarInterfaceBase
from iqdbc.car.hyundai.values import DBC
from iqdbc.lvbs.car.hyundai.radar_interface_ext import RadarInterfaceExt
from iqdbc.car.hyundai.values import DBC, HyundaiFlags, HyundaiExtFlags
from openpilot.common.params import Params
from iqdbc.car.hyundai.hyundaicanfd import CanBus
from openpilot.common.filter_simple import MyMovingAverage
SCC_TID = 0
RADAR_START_ADDR = 0x500
RADAR_MSG_COUNT = 32
RADAR_MSG_COUNT4 = 8
RADAR_GROUP4_MAX_LONG_DIST = 325.0
RADAR_GROUP4_MAX_YREL = 6.0
RADAR_START_ADDR_CANFD1 = 0x210
RADAR_MSG_COUNT1 = 16
RADAR_START_ADDR_CANFD2 = 0x3A5 # Group 2, Group 1: 0x210 2개씩?어???단 보류.
RADAR_MSG_COUNT2 = 32
RADAR_START_ADDR_CANFD3 = 0x400
RADAR_MSG_COUNT3 = 30
CORNER_OBJECT_235_START_ADDR = 0x235
CORNER_OBJECT_235_MSG_COUNT = 20
CORNER_OBJECT_235_TRACK_ID_OFFSET = 200
CORNER_OBJECT_235_DBC = 'hyundai_canfd_corner_radar_235_generated'
CORNER_OBJECT_180_START_ADDR = 0x180
CORNER_OBJECT_180_MSG_COUNT = 5
CORNER_OBJECT_180_SLOTS_PER_MSG = 2
CORNER_OBJECT_180_TRACK_ID_OFFSET = 240
CORNER_OBJECT_180_DBC = 'hyundai_canfd_corner_radar_180_generated'
CORNER_OBJECT_430_LEFT_START_ADDR = 0x430
CORNER_OBJECT_430_RIGHT_START_ADDR = 0x440
CORNER_OBJECT_430_MSG_COUNT_PER_SIDE = 8
CORNER_OBJECT_430_SLOTS_PER_MSG = 7
CORNER_OBJECT_430_TRACK_ID_OFFSET = 300
CORNER_OBJECT_430_DBC = 'hyundai_canfd_corner_radar_430_generated'
CORNER_OBJECT_430_EMPTY_RAW_VALUES = (0x010d1f40, 0x00010d1f)
CORNER_OBJECT_430_DEFAULT_DISTANCE_RAW_MIN = 2520 # 126.0 m
CORNER_OBJECT_430_DEFAULT_DISTANCE_RAW_MAX = 2600 # 130.0 m
CORNER_OBJECT_430_MAX_DREL = 120.0
CORNER_OBJECT_430_MAX_TRACKS_PER_SIDE = 4
CORNER_OBJECT_430_DT = 0.05
CORNER_OBJECT_430_MAX_DREL_DELTA = 1.5
CORNER_OBJECT_430_CANDIDATE_META_BYTE_3 = (2,)
CORNER_OBJECT_430_CANDIDATE_EXCLUDED_SLOTS = (1,)
CORNER_OBJECT_430_CANDIDATE_RAW_DELTA = 200
CORNER_OBJECT_430_STRONG_META_BYTE_2 = (10,)
CORNER_OBJECT_430_WEAK_META_BYTE_2 = (5, 6, 7, 8, 9)
CORNER_OBJECT_430_STRONG_MIN_SUPPORT = 2
CORNER_OBJECT_430_WEAK_MIN_SUPPORT = 3
CORNER_OBJECT_430_CLUSTER_RAW_GAP = 200
CORNER_OBJECT_430_TRACK_MATCH_MAX_DREL_DELTA = 3.0
CORNER_OBJECT_430_MAX_ABS_VREL = 20.0
CORNER_OBJECT_430_MAX_ABS_YVREL = 3.0
CORNER_OBJECT_430_VREL_ALPHA = 0.35
CORNER_OBJECT_430_YVREL_ALPHA = 0.35
CORNER_OBJECT_430_LATERAL_CELL_MSG_WEIGHT = 0.35
CORNER_OBJECT_430_LATERAL_CELL_SLOT_WEIGHT = 0.65
CORNER_OBJECT_430_YREL_OFFSET = 5.8
CORNER_OBJECT_430_YREL_SCALE = 1.1
CORNER_OBJECT_430_RIGHT_CELL_MIRROR = 7.0
CORNER_OBJECT_430_MIN_ABS_YREL = 0.8
CORNER_OBJECT_430_MAX_ABS_YREL = 4.2
CORNER_OBJECT_430_HISTORY_SIZE = 8
CORNER_OBJECT_430_MIN_HISTORY = 5
CORNER_OBJECT_430_MIN_INWARD_YREL_DELTA = 0.35
CORNER_OBJECT_430_MIN_RECENT_INWARD_YREL_DELTA = 0.05
CORNER_OBJECT_430_MIN_INWARD_RATIO = 0.65
CORNER_OBJECT_430_INWARD_CENTER_ABS_YREL = 1.55
CORNER_OBJECT_430_INWARD_KEEP_YVREL_ABS_YREL = 2.2
CORNER_OBJECT_430_EARLY_INWARD_NONCENTER_FRAMES = 2
CORNER_OBJECT_430_SIDE_KEEP_ABS_YREL = 2.0
CORNER_OBJECT_STABLE_TRACK_ID_START = 1000
CORNER_SIDE_OBJECT_MAX_DREL = 0.2
CORNER_SIDE_OBJECT_MIN_ABS_YREL = 1.4
CORNER_SIDE_OBJECT_MAX_ABS_YREL = 4.5
# POC for parsing corner radars: https://github.com/commaai/openpilot/pull/24221/
def get_radar_can_parser(CP):
if Bus.radar not in DBC[CP.carFingerprint]:
class CornerObjectTrackIdManager:
def __init__(self):
self.next_track_id = CORNER_OBJECT_STABLE_TRACK_ID_START
self.objects: dict[tuple[str, int], tuple[int, int]] = {}
def get_track_id(self, source: str, object_id: int, age: int) -> int:
key = (source, object_id)
previous = self.objects.get(key)
if previous is None or age < previous[1]:
track_id = self.next_track_id
self.next_track_id += 1
else:
track_id = previous[0]
self.objects[key] = (track_id, age)
return track_id
def clear_source(self, source: str):
self.objects = {key: value for key, value in self.objects.items() if key[0] != source}
def corner_object_position_valid(d_rel: float, y_rel: float) -> bool:
normal_object = 0.2 < d_rel < 180.0
clipped_side_object = (
0.0 <= d_rel <= CORNER_SIDE_OBJECT_MAX_DREL and
CORNER_SIDE_OBJECT_MIN_ABS_YREL <= abs(y_rel) <= CORNER_SIDE_OBJECT_MAX_ABS_YREL
)
return (normal_object or clipped_side_object) and abs(y_rel) < 40.0
def get_radar_can_parser(CP, radar_tracks, msg_start_addr, msg_count, radar_group4=False):
if not radar_tracks:
return None
#if Bus.radar not in DBC[CP.carFingerprint]:
# return None
print("RadarInterface: RadarTracks...")
if CP.flags & HyundaiFlags.CANFD:
CAN = CanBus(CP)
messages = [(f"RADAR_TRACK_{addr:x}", 20) for addr in range(msg_start_addr, msg_start_addr + msg_count)]
return CANParser('hyundai_canfd_radar_generated', messages, CAN.ACAN)
else:
messages = [(f"RADAR_TRACK_{addr:x}", 20) for addr in range(msg_start_addr, msg_start_addr + msg_count)]
#return CANParser(DBC[CP.carFingerprint][Bus.radar], messages, 1)
dbc_name = 'hyundai_kia_denso_front_radar_generated' if radar_group4 else 'hyundai_kia_mando_front_radar_generated'
return CANParser(dbc_name, messages, 1)
def get_corner_object_can_parser(CP, enabled):
if not enabled or not (CP.flags & HyundaiFlags.CANFD):
return None
messages = [(f"RADAR_TRACK_{addr:x}", 50) for addr in range(RADAR_START_ADDR, RADAR_START_ADDR + RADAR_MSG_COUNT)]
return CANParser(DBC[CP.carFingerprint][Bus.radar], messages, 1)
dbc_path = os.path.join(DBC_PATH, f"{CORNER_OBJECT_235_DBC}.dbc")
if not os.path.exists(dbc_path):
print(f"RadarInterface: missing {CORNER_OBJECT_235_DBC}.dbc, 0x235 corner radar disabled")
return None
CAN = CanBus(CP)
messages = [(f"CORNER_RADAR_235_OBJECTS_{addr:x}", 33) for addr in range(CORNER_OBJECT_235_START_ADDR, CORNER_OBJECT_235_START_ADDR + CORNER_OBJECT_235_MSG_COUNT)]
return CANParser(CORNER_OBJECT_235_DBC, messages, CAN.ACAN)
class RadarInterface(RadarInterfaceBase, RadarInterfaceExt):
def __init__(self, CP, CP_IQ):
RadarInterfaceBase.__init__(self, CP, CP_IQ)
RadarInterfaceExt.__init__(self, CP, CP_IQ)
self.updated_messages = set()
self.trigger_msg = RADAR_START_ADDR + RADAR_MSG_COUNT - 1
def get_corner_object_180_can_parser(CP, enabled):
if not enabled or not (CP.flags & HyundaiFlags.CANFD):
return None
dbc_path = os.path.join(DBC_PATH, f"{CORNER_OBJECT_180_DBC}.dbc")
if not os.path.exists(dbc_path):
print(f"RadarInterface: missing {CORNER_OBJECT_180_DBC}.dbc, 0x180 corner radar disabled")
return None
CAN = CanBus(CP)
messages = [(f"CORNER_RADAR_180_OBJECTS_{addr:x}", 33) for addr in range(CORNER_OBJECT_180_START_ADDR, CORNER_OBJECT_180_START_ADDR + CORNER_OBJECT_180_MSG_COUNT)]
return CANParser(CORNER_OBJECT_180_DBC, messages, CAN.ACAN)
def get_corner_object_430_can_parser(CP, enabled):
if not enabled or not (CP.flags & HyundaiFlags.CANFD):
return None
dbc_path = os.path.join(DBC_PATH, f"{CORNER_OBJECT_430_DBC}.dbc")
if not os.path.exists(dbc_path):
print(f"RadarInterface: missing {CORNER_OBJECT_430_DBC}.dbc, 0x430/0x440 corner radar disabled")
return None
CAN = CanBus(CP)
messages = [(f"CORNER_RADAR_430_OBJECTS_{addr:x}", 33) for addr in range(CORNER_OBJECT_430_LEFT_START_ADDR, CORNER_OBJECT_430_LEFT_START_ADDR + CORNER_OBJECT_430_MSG_COUNT_PER_SIDE)]
messages += [(f"CORNER_RADAR_430_OBJECTS_{addr:x}", 33) for addr in range(CORNER_OBJECT_430_RIGHT_START_ADDR, CORNER_OBJECT_430_RIGHT_START_ADDR + CORNER_OBJECT_430_MSG_COUNT_PER_SIDE)]
return CANParser(CORNER_OBJECT_430_DBC, messages, CAN.ACAN)
def get_radar_can_parser_scc(CP):
CAN = CanBus(CP)
if CP.flags & HyundaiFlags.CANFD:
messages = [("SCC_CONTROL", 50)]
bus = CAN.ECAN
else:
messages = [("SCC11", 50)]
bus = CAN.ECAN
print("$$$$$$$$ ECAN = ", CAN.ECAN)
bus = CAN.CAM if CP.flags & HyundaiFlags.CAMERA_SCC else bus
return CANParser(DBC[CP.carFingerprint][Bus.pt], messages, bus)
class RadarInterface(RadarInterfaceBase):
def __init__(self, CP, CP_IQ=None):
super().__init__(CP, CP_IQ or structs.IQCarParams())
self.v_ego = 0.0
self.canfd = True if CP.flags & HyundaiFlags.CANFD else False
self.radar_group1 = False
self.radar_group3 = False
self.radar_group4 = not self.canfd and bool(CP.extFlags & HyundaiExtFlags.RADAR_GROUP4.value)
if self.canfd:
if CP.extFlags & HyundaiExtFlags.RADAR_GROUP1.value:
self.radar_start_addr = RADAR_START_ADDR_CANFD1
self.radar_msg_count = RADAR_MSG_COUNT1
self.radar_group1 = True
elif CP.extFlags & HyundaiExtFlags.RADAR_GROUP3.value:
self.radar_start_addr = RADAR_START_ADDR_CANFD3
self.radar_msg_count = RADAR_MSG_COUNT3
self.radar_group3 = True
else:
self.radar_start_addr = RADAR_START_ADDR_CANFD2
self.radar_msg_count = RADAR_MSG_COUNT2
else:
self.radar_start_addr = RADAR_START_ADDR
self.radar_msg_count = RADAR_MSG_COUNT4 if self.radar_group4 else RADAR_MSG_COUNT
self.params = Params()
self.radar_tracks = self.params.get_int("EnableRadarTracks") >= 1
self.corner_object_tracks = bool(CP.extFlags & HyundaiExtFlags.CORNER_RADAR_OBJECTS_235.value) and self.params.get_int("EnableCornerRadar") > 0
self.corner_object_180_tracks = bool(CP.extFlags & HyundaiExtFlags.CORNER_RADAR_OBJECTS_180.value) and self.params.get_int("EnableCornerRadar") > 0
self.corner_object_430_tracks = bool(CP.extFlags & HyundaiExtFlags.CORNER_RADAR_OBJECTS_430.value) and self.params.get_int("EnableCornerRadar") > 0
self.updated_tracks = set()
self.updated_scc = set()
self.updated_corner_objects = set()
self.updated_corner_objects_180 = set()
self.updated_corner_objects_430 = set()
self.corner_object_missed_updates = 0
self.corner_object_180_missed_updates = 0
self.corner_object_430_missed_updates = 0
self.corner_object_track_ids = CornerObjectTrackIdManager()
self.rcp_tracks = get_radar_can_parser(CP, self.radar_tracks, self.radar_start_addr, self.radar_msg_count, self.radar_group4)
self.rcp_corner_objects = get_corner_object_can_parser(CP, self.corner_object_tracks)
self.rcp_corner_objects_180 = get_corner_object_180_can_parser(CP, self.corner_object_180_tracks)
self.rcp_corner_objects_430 = get_corner_object_430_can_parser(CP, self.corner_object_430_tracks)
# Enabling raw radar tracks on legacy CAN disables the stock SCC11 stream on
# some Hyundai/Kia platforms. Camera-SCC cars may still use SCC11.
use_scc_parser = not (self.radar_tracks and not self.canfd and not (CP.flags & HyundaiFlags.CAMERA_SCC))
self.rcp_scc = get_radar_can_parser_scc(CP) if use_scc_parser else None
self.trigger_msg_scc = 416 if self.canfd else 0x420
self.trigger_msg_tracks = self.radar_start_addr + self.radar_msg_count - 1
self.trigger_msg_corner_objects = CORNER_OBJECT_235_START_ADDR + CORNER_OBJECT_235_MSG_COUNT - 1
self.trigger_msg_corner_objects_180 = CORNER_OBJECT_180_START_ADDR + CORNER_OBJECT_180_MSG_COUNT - 1
self.trigger_msg_corner_objects_430 = CORNER_OBJECT_430_RIGHT_START_ADDR + CORNER_OBJECT_430_MSG_COUNT_PER_SIDE - 1
self.track_id = 0
self.radar_off_can = CP.radarUnavailable
self.rcp = get_radar_can_parser(CP)
self.corner_objects_available = self.rcp_corner_objects is not None or self.rcp_corner_objects_180 is not None or self.rcp_corner_objects_430 is not None
self.radar_off_can = CP.radarUnavailable and not self.corner_objects_available
print(
"RadarInterface: "
f"radarUnavailable={CP.radarUnavailable} radarTracks={self.radar_tracks} "
f"group4={self.radar_group4} "
f"corner235={self.rcp_corner_objects is not None} corner180={self.rcp_corner_objects_180 is not None} "
f"corner430={self.rcp_corner_objects_430 is not None} "
f"radarOffCan={self.radar_off_can}"
)
self.vRel_last = 0
self.dRel_last = 0
self.corner_object_430_prev_d_rel = {}
self.corner_object_430_prev_v_rel = {}
self.corner_object_430_prev_y_rel = {}
self.corner_object_430_prev_yv_rel = {}
self.corner_object_430_prev_code = {}
self.corner_object_430_history = {}
self.corner_object_430_noncenter_inward_frames = {}
# Initialize pts
if self.rcp_tracks is not None:
total_tracks = self.radar_msg_count * (2 if self.radar_group1 else 1)
for track_id in range(total_tracks):
t_id = track_id + 32
self.pts[t_id] = structs.RadarData.RadarPoint()
self.pts[t_id].measured = False
self.pts[t_id].trackId = t_id
if self.rcp_scc is not None:
self.pts[SCC_TID] = structs.RadarData.RadarPoint()
self.pts[SCC_TID].trackId = SCC_TID
self.pts[SCC_TID].radarSource = "scc"
if self.rcp_corner_objects is not None:
for slot in range(CORNER_OBJECT_235_MSG_COUNT):
t_id = CORNER_OBJECT_235_TRACK_ID_OFFSET + slot
self.pts[t_id] = structs.RadarData.RadarPoint()
self.pts[t_id].measured = False
self.pts[t_id].trackId = t_id
self.pts[t_id].radarSource = "corner235"
if self.rcp_corner_objects_180 is not None:
for slot in range(CORNER_OBJECT_180_MSG_COUNT * CORNER_OBJECT_180_SLOTS_PER_MSG):
t_id = CORNER_OBJECT_180_TRACK_ID_OFFSET + slot
self.pts[t_id] = structs.RadarData.RadarPoint()
self.pts[t_id].measured = False
self.pts[t_id].trackId = t_id
self.pts[t_id].radarSource = "corner180"
if self.rcp_corner_objects_430 is not None:
for slot in range(CORNER_OBJECT_430_MSG_COUNT_PER_SIDE * 2 * CORNER_OBJECT_430_SLOTS_PER_MSG):
t_id = CORNER_OBJECT_430_TRACK_ID_OFFSET + slot
self.pts[t_id] = structs.RadarData.RadarPoint()
self.pts[t_id].measured = False
self.pts[t_id].trackId = t_id
self.frame = 0
if self.rcp is None:
self.initialize_radar_ext(self.trigger_msg)
def update(self, can_strings):
if self.radar_off_can or (self.rcp is None):
self.frame += 1
if self.radar_off_can or (self.rcp_tracks is None and self.rcp_scc is None and self.rcp_corner_objects is None and self.rcp_corner_objects_180 is None and self.rcp_corner_objects_430 is None):
return super().update(None)
vls = self.rcp.update(can_strings)
self.updated_messages.update(vls)
if self.rcp_scc is not None:
vls_s = self.rcp_scc.update(can_strings)
self.updated_scc.update(vls_s)
if self.trigger_msg not in self.updated_messages:
track_ready = False
if self.radar_tracks and self.rcp_tracks is not None:
vls_t = self.rcp_tracks.update(can_strings)
self.updated_tracks.update(vls_t)
track_ready = self.trigger_msg_tracks in self.updated_tracks
corner_ready = False
if self.rcp_corner_objects is not None:
vls_c = self.rcp_corner_objects.update(can_strings)
self.updated_corner_objects.update(vls_c)
corner_ready = self.trigger_msg_corner_objects in self.updated_corner_objects
corner_180_ready = False
if self.rcp_corner_objects_180 is not None:
vls_180 = self.rcp_corner_objects_180.update(can_strings)
self.updated_corner_objects_180.update(vls_180)
corner_180_ready = self.trigger_msg_corner_objects_180 in self.updated_corner_objects_180
corner_430_ready = False
if self.rcp_corner_objects_430 is not None:
vls_430 = self.rcp_corner_objects_430.update(can_strings)
self.updated_corner_objects_430.update(vls_430)
corner_430_ready = self.trigger_msg_corner_objects_430 in self.updated_corner_objects_430
scc_ready = not self.radar_tracks and self.frame % 5 == 0 and self.rcp_scc is not None
if track_ready:
self._update(self.updated_tracks)
self.updated_tracks.clear()
if corner_ready:
self._update_corner_objects(self.updated_corner_objects)
self.corner_object_missed_updates = 0
self.updated_corner_objects.clear()
if corner_180_ready:
self._update_corner_objects_180(self.updated_corner_objects_180)
self.corner_object_180_missed_updates = 0
self.updated_corner_objects_180.clear()
if corner_430_ready:
self._update_corner_objects_430(self.updated_corner_objects_430)
self.corner_object_430_missed_updates = 0
self.updated_corner_objects_430.clear()
# Corner radar runs at its own cadence. Do not let corner-only frames publish
# RadarData, since liveTracks uses a fixed radarTimeStep for aLead/jLead.
publish_ready = track_ready or scc_ready
if not publish_ready:
return None
rr = self._update(self.updated_messages)
self.updated_messages.clear()
if self.rcp_scc is not None:
self._update_scc(self.updated_scc)
if self.rcp_corner_objects is not None:
if self.updated_corner_objects:
self._update_corner_objects(self.updated_corner_objects)
self.corner_object_missed_updates = 0
else:
self.corner_object_missed_updates += 1
if self.corner_object_missed_updates > 10:
self._clear_corner_objects()
if self.rcp_corner_objects_180 is not None:
if self.updated_corner_objects_180:
self._update_corner_objects_180(self.updated_corner_objects_180)
self.corner_object_180_missed_updates = 0
else:
self.corner_object_180_missed_updates += 1
if self.corner_object_180_missed_updates > 10:
self._clear_corner_objects_180()
if self.rcp_corner_objects_430 is not None:
if self.updated_corner_objects_430:
self._update_corner_objects_430(self.updated_corner_objects_430)
self.corner_object_430_missed_updates = 0
else:
self.corner_object_430_missed_updates += 1
if self.corner_object_430_missed_updates > 10:
self._clear_corner_objects_430()
self.updated_scc.clear()
self.updated_corner_objects.clear()
self.updated_corner_objects_180.clear()
self.updated_corner_objects_430.clear()
return rr
ret = structs.RadarData()
if ((self.rcp_tracks is not None and self.radar_tracks and not self.rcp_tracks.can_valid) or
(self.rcp_scc is not None and not self.corner_objects_available and not self.rcp_scc.can_valid) or
(self.rcp_corner_objects is not None and not self.rcp_corner_objects.can_valid) or
(self.rcp_corner_objects_180 is not None and not self.rcp_corner_objects_180.can_valid) or
(self.rcp_corner_objects_430 is not None and not self.rcp_corner_objects_430.can_valid)):
ret.errors.canError = True
ret.points = [point for point in self.pts.values() if point.measured]
return ret
def _update(self, updated_messages):
ret = structs.RadarData()
if self.rcp is None:
return ret
if not self.rcp.can_valid:
ret.errors.canError = True
t_id = 32
for addr in range(self.radar_start_addr, self.radar_start_addr + self.radar_msg_count):
if self.use_radar_interface_ext:
return self.update_ext(ret)
for addr in range(RADAR_START_ADDR, RADAR_START_ADDR + RADAR_MSG_COUNT):
msg = self.rcp.vl[f"RADAR_TRACK_{addr:x}"]
if addr not in self.pts:
self.pts[addr] = structs.RadarData.RadarPoint()
self.pts[addr].trackId = self.track_id
self.track_id += 1
valid = msg['STATE'] in (3, 4)
if valid:
azimuth = math.radians(msg['AZIMUTH'])
self.pts[addr].measured = True
self.pts[addr].dRel = math.cos(azimuth) * msg['LONG_DIST']
self.pts[addr].yRel = 0.5 * -math.sin(azimuth) * msg['LONG_DIST']
self.pts[addr].vRel = msg['REL_SPEED']
self.pts[addr].aRel = msg['REL_ACCEL']
self.pts[addr].yvRel = float('nan')
msg = self.rcp_tracks.vl[f"RADAR_TRACK_{addr:x}"]
if self.radar_group1:
valid = msg['VALID_CNT1'] > 10
elif self.radar_group3:
# Group 3 marks an empty object slot with LONG_DIST raw 0x7ff (204.7 m).
valid = msg['LONG_DIST'] < 204.7
elif self.canfd:
valid = msg['VALID_CNT'] > 10
elif self.radar_group4:
# EN: DNMWR006 exposes eight stable tracked-object slots at 0x500-0x507.
# Messages from 0x508 onward are distance-sorted raw detections without
# stable IDs, so they are excluded. OBJECT_STATE 3 is a confirmed track;
# empty slots use LONG_DIST raw 0xfff8 (409.55 m). Driving logs reached
# 317.80 m, so 325 m preserves every observed confirmed track while
# retaining margin from the empty-slot sentinel. Keep the +/-6 m
# ego/adjacent-lane envelope to suppress farther roadside reflections.
# KO: DNMWR006의 안정적인 추적 객체 슬롯은 0x500~0x507의 8개임.
# 0x508 이후 메시지는 고정 ID가 없는 거리순 raw detection이므로 제외함.
# OBJECT_STATE 3은 확정 추적 객체이며, 빈 슬롯은 LONG_DIST raw
# 0xfff8(409.55m)을 사용함. 주행 로그의 최대값은 317.80m였으므로
# 325m 상한으로 관측된 확정 트랙을 모두 보존하면서 빈 슬롯 값과 충분한
# 여유를 확보함. 원거리 도로변 반사를 줄이기 위해 좌우 6m 범위를 유지함.
valid = (msg['OBJECT_STATE'] == 3 and 0.2 < msg['LONG_DIST'] < RADAR_GROUP4_MAX_LONG_DIST and
abs(msg['LAT_DIST']) <= RADAR_GROUP4_MAX_YREL)
else:
del self.pts[addr]
valid = msg['STATE'] in (3, 4)
ret.points = list(self.pts.values())
return ret
self.pts[t_id].measured = bool(valid)
if not valid:
self.pts[t_id].dRel = 0
self.pts[t_id].yRel = 0
self.pts[t_id].vRel = 0
self.pts[t_id].vLead = self.pts[t_id].vRel + self.v_ego
self.pts[t_id].aRel = float('nan')
self.pts[t_id].yvRel = 0
elif self.radar_group1:
self.pts[t_id].dRel = msg['LONG_DIST1']
self.pts[t_id].yRel = msg['LAT_DIST1']
self.pts[t_id].vRel = msg['REL_SPEED1']
self.pts[t_id].vLead = self.pts[t_id].vRel + self.v_ego
self.pts[t_id].aRel = msg['REL_ACCEL1']
self.pts[t_id].yvRel = msg['LAT_SPEED1']
elif self.canfd:
if self.radar_group3:
# Group 3 reports the object's center. Convert it to the rear surface to match SCC/vision dRel.
self.pts[t_id].dRel = max(0.0, msg['LONG_DIST'] - msg['OBJECT_LENGTH'] * 0.5 - 0.1)
else:
self.pts[t_id].dRel = msg['LONG_DIST']
self.pts[t_id].yRel = msg['LAT_DIST']
self.pts[t_id].vRel = msg['REL_SPEED']
self.pts[t_id].vLead = self.pts[t_id].vRel + self.v_ego
self.pts[t_id].aRel = float('nan') if self.radar_group3 else msg['REL_ACCEL']
self.pts[t_id].yvRel = 0.0 if self.radar_group3 else msg['LAT_SPEED']
elif self.radar_group4:
self.pts[t_id].dRel = msg['LONG_DIST']
self.pts[t_id].yRel = -msg['LAT_DIST']
self.pts[t_id].vRel = msg['REL_SPEED']
self.pts[t_id].vLead = self.pts[t_id].vRel + self.v_ego
self.pts[t_id].aRel = float('nan')
self.pts[t_id].yvRel = 0.0
else:
azimuth = math.radians(msg['AZIMUTH'])
self.pts[t_id].dRel = math.cos(azimuth) * msg['LONG_DIST']
self.pts[t_id].yRel = 0.5 * -math.sin(azimuth) * msg['LONG_DIST']
self.pts[t_id].vRel = msg['REL_SPEED']
self.pts[t_id].vLead = self.pts[t_id].vRel + self.v_ego
self.pts[t_id].aRel = msg['REL_ACCEL']
self.pts[t_id].yvRel = 0.0
t_id += 1
# radar group1? ?나??msg??2개의 ?이?? ?어?음.
if self.radar_group1:
for addr in range(self.radar_start_addr, self.radar_start_addr + self.radar_msg_count):
msg = self.rcp_tracks.vl[f"RADAR_TRACK_{addr:x}"]
valid = msg['VALID_CNT2'] > 10
self.pts[t_id].measured = bool(valid)
if not valid:
self.pts[t_id].dRel = 0
self.pts[t_id].yRel = 0
self.pts[t_id].vRel = 0
self.pts[t_id].vLead = self.pts[t_id].vRel + self.v_ego
self.pts[t_id].aRel = float('nan')
self.pts[t_id].yvRel = 0
else:
self.pts[t_id].dRel = msg['LONG_DIST2']
self.pts[t_id].yRel = msg['LAT_DIST2']
self.pts[t_id].vRel = msg['REL_SPEED2']
self.pts[t_id].vLead = self.pts[t_id].vRel + self.v_ego
self.pts[t_id].aRel = msg['REL_ACCEL2']
self.pts[t_id].yvRel = msg['LAT_SPEED2']
t_id += 1
def _update_corner_objects(self, updated_messages):
if self.rcp_corner_objects is None:
return
if not updated_messages:
self._clear_corner_objects()
return
candidates = []
for slot, addr in enumerate(range(CORNER_OBJECT_235_START_ADDR, CORNER_OBJECT_235_START_ADDR + CORNER_OBJECT_235_MSG_COUNT)):
t_id = CORNER_OBJECT_235_TRACK_ID_OFFSET + slot
msg = self.rcp_corner_objects.vl[f"CORNER_RADAR_235_OBJECTS_{addr:x}"]
d_rel = msg["OBJ_REL_POS_X"]
y_rel = msg["OBJ_REL_POS_Y"]
v_rel = msg["OBJ_REL_VEL_X"]
yv_rel = msg["OBJ_REL_VEL_Y"]
a_rel = msg["OBJ_REL_ACCEL_X"]
# Side objects are clipped to x=0 by the corner radar. Quality, identity,
# and lateral motion still describe a real object, so keep them for
# corner-confirmed front-radar association in radard.
valid = msg["OBJ_QUAL_LEVEL"] > 0 and corner_object_position_valid(d_rel, y_rel) and v_rel > -99.0
if not valid:
continue
candidates.append((t_id, int(msg["OBJ_OBJECT_ID"]), int(msg["OBJ_AGE"]), int(msg["OBJ_QUAL_LEVEL"]),
d_rel, y_rel, v_rel, yv_rel, a_rel))
self._apply_corner_objects("corner235", candidates,
range(CORNER_OBJECT_235_TRACK_ID_OFFSET,
CORNER_OBJECT_235_TRACK_ID_OFFSET + CORNER_OBJECT_235_MSG_COUNT))
def _update_corner_objects_180(self, updated_messages):
if self.rcp_corner_objects_180 is None:
return
if not updated_messages:
self._clear_corner_objects_180()
return
candidates = []
for msg_index, addr in enumerate(range(CORNER_OBJECT_180_START_ADDR, CORNER_OBJECT_180_START_ADDR + CORNER_OBJECT_180_MSG_COUNT)):
msg = self.rcp_corner_objects_180.vl[f"CORNER_RADAR_180_OBJECTS_{addr:x}"]
for slot_index in range(CORNER_OBJECT_180_SLOTS_PER_MSG):
t_id = CORNER_OBJECT_180_TRACK_ID_OFFSET + msg_index * CORNER_OBJECT_180_SLOTS_PER_MSG + slot_index
prefix = f"SLOT{slot_index + 1}_"
d_rel = msg[f"{prefix}REL_POS_X"]
y_rel = msg[f"{prefix}REL_POS_Y"]
v_rel = msg[f"{prefix}REL_VEL_X"]
yv_rel = msg[f"{prefix}REL_VEL_Y"]
a_rel = msg[f"{prefix}REL_ACCEL_X"]
valid = msg[f"{prefix}QUAL_LEVEL"] > 0 and corner_object_position_valid(d_rel, y_rel) and v_rel > -99.0
if not valid:
continue
candidates.append((t_id, int(msg[f"{prefix}OBJECT_ID"]), int(msg[f"{prefix}AGE"]), int(msg[f"{prefix}QUAL_LEVEL"]),
d_rel, y_rel, v_rel, yv_rel, a_rel))
self._apply_corner_objects("corner180", candidates,
range(CORNER_OBJECT_180_TRACK_ID_OFFSET,
CORNER_OBJECT_180_TRACK_ID_OFFSET + CORNER_OBJECT_180_MSG_COUNT * CORNER_OBJECT_180_SLOTS_PER_MSG))
def _apply_corner_objects(self, source, candidates, slot_ids):
for t_id in slot_ids:
self._clear_point(t_id)
# The same object can occupy two CAN slots for one cycle during a slot handoff.
# Publish only the newest/highest-quality copy so trackId stays unique.
objects = {}
for candidate in candidates:
object_id = candidate[1]
previous = objects.get(object_id)
if previous is None or (candidate[2], candidate[3]) > (previous[2], previous[3]):
objects[object_id] = candidate
for t_id, object_id, age, _, d_rel, y_rel, v_rel, yv_rel, a_rel in objects.values():
point = self.pts[t_id]
point.measured = True
point.trackId = self.corner_object_track_ids.get_track_id(source, object_id, age)
point.radarSource = source
point.dRel = d_rel
point.yRel = y_rel
point.vRel = v_rel
point.vLead = v_rel + self.v_ego
point.aRel = a_rel
point.yvRel = yv_rel
def _update_corner_objects_430(self, updated_messages):
if self.rcp_corner_objects_430 is None:
return
if not updated_messages:
self._clear_corner_objects_430()
return
bank_defs = (
(CORNER_OBJECT_430_LEFT_START_ADDR, 1.0, 0),
(CORNER_OBJECT_430_RIGHT_START_ADDR, -1.0, CORNER_OBJECT_430_MSG_COUNT_PER_SIDE * CORNER_OBJECT_430_SLOTS_PER_MSG),
)
for start_addr, side_sign, track_base in bank_defs:
bins = []
for msg_index, addr in enumerate(range(start_addr, start_addr + CORNER_OBJECT_430_MSG_COUNT_PER_SIDE)):
msg = self.rcp_corner_objects_430.vl[f"CORNER_RADAR_430_OBJECTS_{addr:x}"]
for slot_index in range(CORNER_OBJECT_430_SLOTS_PER_MSG):
prefix = f"SLOT{slot_index + 1}_"
distance_raw = int(msg[f"{prefix}DISTANCE_RAW"])
raw = (
distance_raw |
(int(msg[f"{prefix}META_13_15"]) << 13) |
(int(msg[f"{prefix}META_BYTE_2"]) << 16) |
(int(msg[f"{prefix}META_BYTE_3"]) << 24)
)
code = (
int(msg[f"{prefix}META_13_15"]),
int(msg[f"{prefix}META_BYTE_2"]),
int(msg[f"{prefix}META_BYTE_3"]),
)
d_rel = distance_raw * 0.05
default_distance = CORNER_OBJECT_430_DEFAULT_DISTANCE_RAW_MIN <= distance_raw <= CORNER_OBJECT_430_DEFAULT_DISTANCE_RAW_MAX
base_valid = (
raw not in CORNER_OBJECT_430_EMPTY_RAW_VALUES and
distance_raw not in (0, 8000, 8191) and
not default_distance and
0.2 < d_rel < CORNER_OBJECT_430_MAX_DREL
)
candidate_valid = (
base_valid and
slot_index + 1 not in CORNER_OBJECT_430_CANDIDATE_EXCLUDED_SLOTS and
code[2] in CORNER_OBJECT_430_CANDIDATE_META_BYTE_3 and
code[1] in CORNER_OBJECT_430_STRONG_META_BYTE_2 + CORNER_OBJECT_430_WEAK_META_BYTE_2
)
bins.append({
"msg_index": msg_index,
"slot_index": slot_index,
"distance_raw": distance_raw,
"d_rel": d_rel,
"code": code,
"candidate_valid": candidate_valid,
})
supported_bins = []
candidates = [b for b in bins if b["candidate_valid"]]
for b in candidates:
support = 1
for other in candidates:
if other is b:
continue
if abs(other["msg_index"] - b["msg_index"]) > 1:
continue
if abs(other["slot_index"] - b["slot_index"]) > 2:
continue
if abs(other["distance_raw"] - b["distance_raw"]) > CORNER_OBJECT_430_CANDIDATE_RAW_DELTA:
continue
support += 1
min_support = (CORNER_OBJECT_430_STRONG_MIN_SUPPORT if b["code"][1] in CORNER_OBJECT_430_STRONG_META_BYTE_2
else CORNER_OBJECT_430_WEAK_MIN_SUPPORT)
if support >= min_support:
supported_bins.append({**b, "support": support})
clusters = []
for b in sorted(supported_bins, key=lambda item: item["distance_raw"]):
if not clusters or b["distance_raw"] - clusters[-1][-1]["distance_raw"] > CORNER_OBJECT_430_CLUSTER_RAW_GAP:
clusters.append([b])
else:
clusters[-1].append(b)
clusters = sorted(clusters, key=lambda cluster: sum(b["distance_raw"] for b in cluster) / len(cluster))[:CORNER_OBJECT_430_MAX_TRACKS_PER_SIDE]
cluster_objects = []
for cluster in clusters:
msg_index = sum(b["msg_index"] for b in cluster) / len(cluster)
slot = sum(b["slot_index"] + 1 for b in cluster) / len(cluster)
lateral_cell = (CORNER_OBJECT_430_LATERAL_CELL_MSG_WEIGHT * msg_index +
CORNER_OBJECT_430_LATERAL_CELL_SLOT_WEIGHT * slot)
mapped_cell = lateral_cell if side_sign > 0.0 else CORNER_OBJECT_430_RIGHT_CELL_MIRROR - lateral_cell
y_abs = max(CORNER_OBJECT_430_MIN_ABS_YREL,
min(CORNER_OBJECT_430_MAX_ABS_YREL,
CORNER_OBJECT_430_YREL_OFFSET - CORNER_OBJECT_430_YREL_SCALE * mapped_cell))
cluster_objects.append({
"d_rel": sum(b["d_rel"] for b in cluster) / len(cluster),
"y_rel": side_sign * y_abs,
"code": max((b["code"] for b in cluster), key=lambda code: sum(1 for item in cluster if item["code"] == code)),
})
active_t_ids = set()
side_track_ids = [
CORNER_OBJECT_430_TRACK_ID_OFFSET + track_base + slot
for slot in range(CORNER_OBJECT_430_MAX_TRACKS_PER_SIDE)
]
unmatched_track_ids = {t_id for t_id in side_track_ids if t_id in self.corner_object_430_prev_d_rel}
unused_track_ids = [t_id for t_id in side_track_ids if t_id not in unmatched_track_ids]
for cluster in cluster_objects:
d_rel = cluster["d_rel"]
code = cluster["code"]
matched_t_id = None
if unmatched_track_ids:
nearest_t_id = min(unmatched_track_ids, key=lambda t_id: abs(d_rel - self.corner_object_430_prev_d_rel[t_id]))
if abs(d_rel - self.corner_object_430_prev_d_rel[nearest_t_id]) <= CORNER_OBJECT_430_TRACK_MATCH_MAX_DREL_DELTA:
matched_t_id = nearest_t_id
unmatched_track_ids.remove(matched_t_id)
if matched_t_id is None and unused_track_ids:
matched_t_id = unused_track_ids.pop(0)
if matched_t_id is None:
continue
t_id = matched_t_id
active_t_ids.add(t_id)
prev_d_rel = self.corner_object_430_prev_d_rel.get(t_id)
prev_code = self.corner_object_430_prev_code.get(t_id)
self.corner_object_430_prev_d_rel[t_id] = d_rel
self.corner_object_430_prev_y_rel[t_id] = cluster["y_rel"]
self.corner_object_430_prev_code[t_id] = code
reset_track = prev_d_rel is None or code != prev_code or abs(d_rel - prev_d_rel) > CORNER_OBJECT_430_MAX_DREL_DELTA
if reset_track:
self.corner_object_430_prev_v_rel.pop(t_id, None)
self.corner_object_430_prev_yv_rel.pop(t_id, None)
self.corner_object_430_history.pop(t_id, None)
self.corner_object_430_noncenter_inward_frames.pop(t_id, None)
history = self.corner_object_430_history.setdefault(t_id, deque(maxlen=CORNER_OBJECT_430_HISTORY_SIZE))
history.append((d_rel, cluster["y_rel"]))
if len(history) < CORNER_OBJECT_430_MIN_HISTORY:
self._clear_point(t_id)
continue
window_dt = CORNER_OBJECT_430_DT * (len(history) - 1)
first_d_rel, first_y_rel = history[0]
hist_v_rel = (d_rel - first_d_rel) / window_dt
if abs(hist_v_rel) > CORNER_OBJECT_430_MAX_ABS_VREL:
self.corner_object_430_prev_v_rel.pop(t_id, None)
self.corner_object_430_prev_yv_rel.pop(t_id, None)
self.corner_object_430_history.pop(t_id, None)
self.corner_object_430_noncenter_inward_frames.pop(t_id, None)
self._clear_point(t_id)
continue
prev_v_rel = self.corner_object_430_prev_v_rel.get(t_id, hist_v_rel)
v_rel = (1.0 - CORNER_OBJECT_430_VREL_ALPHA) * prev_v_rel + CORNER_OBJECT_430_VREL_ALPHA * hist_v_rel
self.corner_object_430_prev_v_rel[t_id] = v_rel
inward_steps = 0
usable_steps = 0
prev_abs_y = abs(history[0][1])
for _, y_rel in list(history)[1:]:
abs_y = abs(y_rel)
delta = prev_abs_y - abs_y
if abs(delta) > 1e-3:
usable_steps += 1
if delta > 0.0:
inward_steps += 1
prev_abs_y = abs_y
net_inward_y = abs(first_y_rel) - abs(cluster["y_rel"])
inward_ratio = inward_steps / usable_steps if usable_steps > 0 else 0.0
hist_yv_rel = (cluster["y_rel"] - first_y_rel) / window_dt
recent_inward_y = abs(history[-3][1]) - abs(cluster["y_rel"]) if len(history) >= 3 else net_inward_y
if (net_inward_y < CORNER_OBJECT_430_MIN_INWARD_YREL_DELTA or
recent_inward_y < CORNER_OBJECT_430_MIN_RECENT_INWARD_YREL_DELTA or
inward_ratio < CORNER_OBJECT_430_MIN_INWARD_RATIO or
abs(hist_yv_rel) > CORNER_OBJECT_430_MAX_ABS_YVREL):
hist_yv_rel = 0.0
inward_motion_candidate = hist_yv_rel != 0.0 and abs(cluster["y_rel"]) <= CORNER_OBJECT_430_INWARD_KEEP_YVREL_ABS_YREL
inward_center_candidate = inward_motion_candidate and abs(cluster["y_rel"]) <= CORNER_OBJECT_430_INWARD_CENTER_ABS_YREL
y_rel = cluster["y_rel"]
if inward_motion_candidate:
if inward_center_candidate:
self.corner_object_430_noncenter_inward_frames[t_id] = 0
prev_yv_rel = self.corner_object_430_prev_yv_rel.get(t_id, hist_yv_rel)
yv_rel = (1.0 - CORNER_OBJECT_430_YVREL_ALPHA) * prev_yv_rel + CORNER_OBJECT_430_YVREL_ALPHA * hist_yv_rel
else:
noncenter_frames = self.corner_object_430_noncenter_inward_frames.get(t_id, 0) + 1
self.corner_object_430_noncenter_inward_frames[t_id] = noncenter_frames
if noncenter_frames <= CORNER_OBJECT_430_EARLY_INWARD_NONCENTER_FRAMES:
prev_yv_rel = self.corner_object_430_prev_yv_rel.get(t_id, hist_yv_rel)
yv_rel = (1.0 - CORNER_OBJECT_430_YVREL_ALPHA) * prev_yv_rel + CORNER_OBJECT_430_YVREL_ALPHA * hist_yv_rel
else:
yv_rel = 0.0
if not inward_center_candidate and abs(y_rel) < CORNER_OBJECT_430_SIDE_KEEP_ABS_YREL:
y_rel = math.copysign(CORNER_OBJECT_430_SIDE_KEEP_ABS_YREL, y_rel)
else:
hist_yv_rel = 0.0
yv_rel = 0.0
self.corner_object_430_noncenter_inward_frames[t_id] = 0
if abs(y_rel) < CORNER_OBJECT_430_SIDE_KEEP_ABS_YREL:
y_rel = math.copysign(CORNER_OBJECT_430_SIDE_KEEP_ABS_YREL, y_rel)
self.corner_object_430_prev_yv_rel[t_id] = yv_rel
self.pts[t_id].measured = True
self.pts[t_id].trackId = t_id
self.pts[t_id].dRel = d_rel
self.pts[t_id].yRel = y_rel
self.pts[t_id].vRel = v_rel
self.pts[t_id].vLead = v_rel + self.v_ego
self.pts[t_id].aRel = float('nan')
self.pts[t_id].yvRel = yv_rel
side_track_count = CORNER_OBJECT_430_MSG_COUNT_PER_SIDE * CORNER_OBJECT_430_SLOTS_PER_MSG
for slot in range(side_track_count):
t_id = CORNER_OBJECT_430_TRACK_ID_OFFSET + track_base + slot
if t_id in active_t_ids:
continue
self.corner_object_430_prev_d_rel.pop(t_id, None)
self.corner_object_430_prev_v_rel.pop(t_id, None)
self.corner_object_430_prev_y_rel.pop(t_id, None)
self.corner_object_430_prev_yv_rel.pop(t_id, None)
self.corner_object_430_prev_code.pop(t_id, None)
self.corner_object_430_history.pop(t_id, None)
self.corner_object_430_noncenter_inward_frames.pop(t_id, None)
self._clear_point(t_id)
def _clear_point(self, t_id):
self.pts[t_id].measured = False
self.pts[t_id].dRel = 0
self.pts[t_id].yRel = 0
self.pts[t_id].vRel = 0
self.pts[t_id].vLead = self.v_ego
self.pts[t_id].aRel = float('nan')
self.pts[t_id].yvRel = 0
def _clear_corner_objects(self):
for slot in range(CORNER_OBJECT_235_MSG_COUNT):
self._clear_point(CORNER_OBJECT_235_TRACK_ID_OFFSET + slot)
self.corner_object_track_ids.clear_source("corner235")
def _clear_corner_objects_180(self):
for slot in range(CORNER_OBJECT_180_MSG_COUNT * CORNER_OBJECT_180_SLOTS_PER_MSG):
self._clear_point(CORNER_OBJECT_180_TRACK_ID_OFFSET + slot)
self.corner_object_track_ids.clear_source("corner180")
def _clear_corner_objects_430(self):
self.corner_object_430_prev_d_rel.clear()
self.corner_object_430_prev_v_rel.clear()
self.corner_object_430_prev_y_rel.clear()
self.corner_object_430_prev_yv_rel.clear()
self.corner_object_430_prev_code.clear()
self.corner_object_430_history.clear()
self.corner_object_430_noncenter_inward_frames.clear()
for slot in range(CORNER_OBJECT_430_MSG_COUNT_PER_SIDE * 2 * CORNER_OBJECT_430_SLOTS_PER_MSG):
self._clear_point(CORNER_OBJECT_430_TRACK_ID_OFFSET + slot)
def _update_scc(self, updated_messages):
cpt = self.rcp_scc.vl
t_id = SCC_TID
if self.canfd:
dRel = cpt["SCC_CONTROL"]['ACC_ObjDist']
vRel = cpt["SCC_CONTROL"]['ACC_ObjRelSpd']
new_pts = abs(dRel - self.dRel_last) > 3 or abs(vRel - self.vRel_last) > 1
vLead = vRel + self.v_ego
valid = 0 < dRel < 150 and not new_pts #cpt["SCC_CONTROL"]['OBJ_STATUS'] and dRel < 150
self.pts[t_id].measured = bool(valid)
if not valid:
self.pts[t_id].dRel = 0
self.pts[t_id].yRel = 0
self.pts[t_id].vRel = 0
self.pts[t_id].vLead = self.pts[t_id].vRel + self.v_ego
self.pts[t_id].aRel = float('nan')
self.pts[t_id].yvRel = 0
else:
self.pts[t_id].dRel = dRel
self.pts[t_id].yRel = 0
self.pts[t_id].vRel = vRel
self.pts[t_id].vLead = vLead
self.pts[t_id].aRel = float('nan')
self.pts[t_id].yvRel = 0 #float('nan')
else:
dRel = cpt["SCC11"]['ACC_ObjDist']
vRel = cpt["SCC11"]['ACC_ObjRelSpd']
new_pts = abs(dRel - self.dRel_last) > 3 or abs(vRel - self.vRel_last) > 1
vLead = vRel + self.v_ego
valid = cpt["SCC11"]['ACC_ObjStatus'] and dRel < 150 and not new_pts
self.pts[t_id].measured = bool(valid)
if not valid:
self.pts[t_id].dRel = 0
self.pts[t_id].yRel = 0
self.pts[t_id].vRel = 0
self.pts[t_id].vLead = self.pts[t_id].vRel + self.v_ego
self.pts[t_id].aRel = float('nan')
self.pts[t_id].yvRel = 0
else:
self.pts[t_id].dRel = dRel
self.pts[t_id].yRel = -cpt["SCC11"]['ACC_ObjLatPos'] # in car frame's y axis, left is negative
self.pts[t_id].vRel = vRel
self.pts[t_id].vLead = vLead
self.pts[t_id].aRel = float('nan')
self.pts[t_id].yvRel = 0 #float('nan')
self.dRel_last = dRel
self.vRel_last = vRel
View File
@@ -0,0 +1,194 @@
import math
import pytest
from iqdbc.can import CANPacker, CANParser
from iqdbc.car import Bus, gen_empty_fingerprint, structs
from iqdbc.car.hyundai.carstate import CarState, EV_MODE_STATUS_TIMEOUT_NS, _get_ev_mode_state
from iqdbc.car.hyundai.interface import CarInterface
from iqdbc.car.hyundai.values import CANFD_HYBRID_STATUS_ADDR, CANFD_HYBRID_STATUS_DLC, CAR, DBC, EV_MODE_ACTIVE_VALUES, \
EV_MODE_STATUS_ADDR, EV_MODE_STATUS_DLC, EV_MODE_STATUS_MSG, EV_MODE_STATUS_SIGNAL, \
HyundaiExtFlags, HyundaiFlags
from openpilot.common.params import Params
def get_params(candidate, *, hybrid=True, hybrid_bus=0, hybrid_status=None, hybrid_status_bus=0,
hybrid_status_dlc=CANFD_HYBRID_STATUS_DLC, status_bus=0, status_dlc=EV_MODE_STATUS_DLC):
Params().put_int("HyundaiCameraSCC", 1)
Params().put_int("CanfdHDA2", 1)
fingerprint = gen_empty_fingerprint()
if hybrid:
fingerprint[hybrid_bus][0x105] = 32
if hybrid_status is None:
hybrid_status = hybrid
if hybrid_status:
fingerprint[hybrid_status_bus][CANFD_HYBRID_STATUS_ADDR] = hybrid_status_dlc
if status_bus is not None:
fingerprint[status_bus][EV_MODE_STATUS_ADDR] = status_dlc
return CarInterface.get_params(candidate, fingerprint, [], False, False, False)
@pytest.mark.parametrize(("candidate", "hybrid", "status_bus", "status_dlc", "expected"), (
(CAR.HYUNDAI_SANTAFE_MX5_HEV, True, 0, 32, True),
(CAR.HYUNDAI_SANTAFE_MX5_HEV, False, 0, 32, False),
(CAR.HYUNDAI_SANTAFE_MX5_HEV, True, None, 32, False),
(CAR.HYUNDAI_SANTAFE_MX5_HEV, True, 1, 32, False),
(CAR.HYUNDAI_SANTAFE_MX5_HEV, True, 0, 16, False),
(CAR.KIA_SORENTO_4TH_GEN, False, None, 32, False),
# Shared ICE/HEV/PHEV candidates rely on the observed ECAN capability frames, not their model name.
(CAR.HYUNDAI_TUCSON_4TH_GEN, True, 0, 32, True),
(CAR.HYUNDAI_TUCSON_4TH_GEN, False, 0, 32, False),
(CAR.KIA_SORENTO_HEV_4TH_GEN, True, 0, 32, True),
(CAR.HYUNDAI_KONA_HEV_2ND_GEN, True, 0, 32, True),
(CAR.HYUNDAI_KONA_HEV_2ND_GEN, False, 0, 32, False),
(CAR.HYUNDAI_ELANTRA_HEV_2021, True, 0, 32, False),
))
def test_ev_mode_capability(candidate, hybrid, status_bus, status_dlc, expected):
CP = get_params(candidate, hybrid=hybrid, status_bus=status_bus, status_dlc=status_dlc)
assert bool(CP.extFlags & HyundaiExtFlags.EV_MODE_STATUS_230) == expected
@pytest.mark.parametrize(("hybrid_status_bus", "hybrid_status_dlc"), (
(1, CANFD_HYBRID_STATUS_DLC),
(0, 16),
))
def test_ev_mode_capability_requires_ecan_hybrid_status_dlc32(hybrid_status_bus, hybrid_status_dlc):
CP = get_params(CAR.HYUNDAI_SANTAFE_MX5_HEV, hybrid_status_bus=hybrid_status_bus,
hybrid_status_dlc=hybrid_status_dlc)
assert not bool(CP.extFlags & HyundaiExtFlags.EV_MODE_STATUS_230)
def test_0x105_and_ev_status_without_0xfa_do_not_enable_ev_mode():
CP = get_params(CAR.HYUNDAI_TUCSON_4TH_GEN, hybrid=True, hybrid_status=False)
assert bool(CP.flags & HyundaiFlags.HYBRID)
assert not bool(CP.extFlags & HyundaiExtFlags.EV_MODE_STATUS_230)
def test_ev_mode_display_capability_does_not_change_hybrid_safety_classification():
CP = get_params(CAR.HYUNDAI_TUCSON_4TH_GEN, hybrid=False, hybrid_status=True)
assert not bool(CP.flags & HyundaiFlags.HYBRID)
assert bool(CP.extFlags & HyundaiExtFlags.EV_MODE_STATUS_230)
def test_ev_mode_capability_uses_the_detected_ecan_offset():
CP = get_params(CAR.KIA_SORENTO_HEV_4TH_GEN, hybrid_bus=4, hybrid_status_bus=4, status_bus=4)
assert bool(CP.extFlags & HyundaiExtFlags.EV_MODE_STATUS_230)
def test_ev_mode_parser_registration_is_capability_gated():
supported = get_params(CAR.HYUNDAI_SANTAFE_MX5_HEV)
unsupported = get_params(CAR.KIA_SORENTO_4TH_GEN, hybrid=False, status_bus=1, status_dlc=16)
supported_parser = CarState.get_can_parsers_canfd(None, supported)[Bus.pt]
unsupported_parser = CarState.get_can_parsers_canfd(None, unsupported)[Bus.pt]
assert EV_MODE_STATUS_ADDR in supported_parser.addresses
assert supported_parser.message_states[EV_MODE_STATUS_ADDR].ignore_alive
assert supported_parser.message_states[EV_MODE_STATUS_ADDR].ignore_counter
assert EV_MODE_STATUS_ADDR not in unsupported_parser.addresses
def test_sorento_ice_corner_radar_status_does_not_enable_ev_mode():
Params().put_int("HyundaiCameraSCC", 1)
Params().put_int("CanfdHDA2", 1)
fingerprint = gen_empty_fingerprint()
fingerprint[1][0x230] = 16 # Actual Sorento ICE ACAN corner-radar status frame.
CP = CarInterface.get_params(CAR.KIA_SORENTO_4TH_GEN, fingerprint, [], False, False, False)
parser = CarState.get_can_parsers_canfd(None, CP)[Bus.pt]
assert not bool(CP.extFlags & HyundaiExtFlags.EV_MODE_STATUS_230)
assert EV_MODE_STATUS_ADDR not in parser.addresses
def test_ev_mode_state_requires_a_fresh_dlc32_frame():
CP = get_params(CAR.HYUNDAI_SANTAFE_MX5_HEV)
parser = CarState.get_can_parsers_canfd(None, CP)[Bus.pt]
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
assert _get_ev_mode_state(parser) == (False, False)
timestamp = 1_000_000_000
wrong_bus_short_status = (EV_MODE_STATUS_ADDR, b"\x00" * 16, 1)
parser.update([timestamp, [wrong_bus_short_status]])
assert _get_ev_mode_state(parser) == (False, False)
ev_active = packer.make_can_msg(EV_MODE_STATUS_MSG, parser.bus, {"COUNTER": 1, EV_MODE_STATUS_SIGNAL: 6})
parser.update([timestamp + 100_000_000, [ev_active]])
assert _get_ev_mode_state(parser) == (True, True)
ev_inactive = packer.make_can_msg(EV_MODE_STATUS_MSG, parser.bus, {"COUNTER": 2, EV_MODE_STATUS_SIGNAL: 3})
parser.update([timestamp + 200_000_000, [ev_inactive]])
assert _get_ev_mode_state(parser) == (False, True)
parser.dat[EV_MODE_STATUS_ADDR] = b"\x00" * 16
assert _get_ev_mode_state(parser) == (False, False)
parser.dat[EV_MODE_STATUS_ADDR] = ev_inactive[1]
parser.update([timestamp + 200_000_000 + EV_MODE_STATUS_TIMEOUT_NS + 1, []])
assert not parser.bus_timeout
assert _get_ev_mode_state(parser) == (False, False)
@pytest.mark.parametrize("mode", range(16))
def test_ev_mode_enum_mapping(mode):
CP = get_params(CAR.HYUNDAI_SANTAFE_MX5_HEV)
parser = CarState.get_can_parsers_canfd(None, CP)[Bus.pt]
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
msg = packer.make_can_msg(EV_MODE_STATUS_MSG, parser.bus, {"COUNTER": mode, EV_MODE_STATUS_SIGNAL: mode})
parser.update([1_000_000_000, [msg]])
assert int(parser.vl[EV_MODE_STATUS_MSG][EV_MODE_STATUS_SIGNAL]) == mode
assert _get_ev_mode_state(parser) == (mode in EV_MODE_ACTIVE_VALUES, True)
@pytest.mark.parametrize(("payload", "expected_mode", "expected_active"), (
("758139048402000000000000000000000000d3009805001c24000000c05dc80f", 1, True),
("4d466f048402000000000000000000000000bf007408001c24000000c05dc80f", 2, True),
("32206e048402000000000000000000000000c9007418001c24000000c05dc80f", 6, True),
# Route 455 mode 3 was a false positive with the old single-bit 0x230 interpretation.
("4f935e0484020000000000000000000000004400640d001c10000000c05dc40f", 3, False),
("066558048402000000000000000000000000c4003020001c24000000c05dc80f", 8, False),
("059683048402000000000000000000000000a1008425001c24000000c05dc80f", 9, False),
("011305048402000000000000000000000000d600a829001c24000000c05dc80f", 10, False),
))
def test_ev_mode_dbc_decodes_real_mx5_frames(payload, expected_mode, expected_active):
parser = CANParser("hyundai_canfd_generated", [(EV_MODE_STATUS_MSG, math.nan)], 0)
updated = parser.update([1_000_000_000, [(EV_MODE_STATUS_ADDR, bytes.fromhex(payload), 0)]])
assert updated == {EV_MODE_STATUS_ADDR}
assert int(parser.vl[EV_MODE_STATUS_MSG][EV_MODE_STATUS_SIGNAL]) == expected_mode
assert _get_ev_mode_state(parser) == (expected_active, True)
def test_ev_mode_rejects_checksum_corruption():
CP = get_params(CAR.HYUNDAI_SANTAFE_MX5_HEV)
parser = CarState.get_can_parsers_canfd(None, CP)[Bus.pt]
payload = bytearray.fromhex("32206e048402000000000000000000000000c9007418001c24000000c05dc80f")
payload[-1] ^= 1
parser.update([1_000_000_000, [(EV_MODE_STATUS_ADDR, bytes(payload), parser.bus)]])
assert EV_MODE_STATUS_ADDR not in parser.dat
assert _get_ev_mode_state(parser) == (False, False)
def test_ev_mode_fields_default_invalid():
state = structs.CarState()
assert not state.evModeActive
assert not state.evModeValid
def test_ev_mode_parser_is_optional_for_can_validity():
CP = get_params(CAR.HYUNDAI_SANTAFE_MX5_HEV)
parser = CarState.get_can_parsers_canfd(None, CP)[Bus.pt]
state = parser.message_states[EV_MODE_STATUS_ADDR]
assert state.ignore_alive
assert state.ignore_counter
@@ -6,13 +6,11 @@ from iqdbc.car import gen_empty_fingerprint
from iqdbc.car.structs import CarParams
from iqdbc.car.fw_versions import build_fw_dict
from iqdbc.car.hyundai.interface import CarInterface
from iqdbc.car.hyundai.hyundaicanfd import CanBus
from iqdbc.car.hyundai.radar_interface import RADAR_START_ADDR
from iqdbc.car.hyundai.values import CAMERA_SCC_CAR, CANFD_CAR, CAN_GEARS, CAR, CHECKSUM, DATE_FW_ECUS, \
HYBRID_CAR, EV_CAR, FW_QUERY_CONFIG, LEGACY_SAFETY_MODE_CAR, CANFD_FUZZY_WHITELIST, \
UNSUPPORTED_LONGITUDINAL_CAR, PLATFORM_CODE_ECUS, HYUNDAI_VERSION_REQUEST_LONG, \
HyundaiFlags, get_platform_codes, HyundaiSafetyFlags, \
NON_SCC_CAR
from iqdbc.car.hyundai.values import CANFD_CAR, CAN_GEARS, CAR, CHECKSUM, DATE_FW_ECUS, \
HYBRID_CAR, EV_CAR, FW_QUERY_CONFIG, LEGACY_SAFETY_MODE_CAR, \
PLATFORM_CODE_ECUS, HYUNDAI_VERSION_REQUEST_LONG, \
HyundaiFlags, get_platform_codes, HyundaiSafetyFlags
from iqdbc.car.hyundai.fingerprints import FW_VERSIONS
Ecu = CarParams.Ecu
@@ -45,16 +43,8 @@ CANFD_EXPECTED_ECUS = {Ecu.fwdCamera, Ecu.fwdRadar}
class TestHyundaiFingerprint:
def test_feature_detection(self):
# LKA steering
for lka_steering in (True, False):
fingerprint = gen_empty_fingerprint()
if lka_steering:
cam_can = CanBus(None, fingerprint).CAM
fingerprint[cam_can] = [0x50, 0x110] # LKA steering messages
CP = CarInterface.get_params(CAR.KIA_EV6, fingerprint, [], False, False, False)
assert bool(CP.flags & HyundaiFlags.CANFD_LKA_STEERING) == lka_steering
def test_feature_detection(self, monkeypatch, tmp_path):
monkeypatch.setenv("PARAMS_ROOT", str(tmp_path))
# radar available
for radar in (True, False):
fingerprint = gen_empty_fingerprint()
@@ -74,19 +64,20 @@ class TestHyundaiFingerprint:
# Test no EV/HEV in any gear lists (should all use ELECT_GEAR)
assert set.union(*CAN_GEARS.values()) & (HYBRID_CAR | EV_CAR) == set()
# Test CAN FD car not in CAN feature lists
can_specific_feature_list = set.union(*CAN_GEARS.values(), *CHECKSUM.values(), LEGACY_SAFETY_MODE_CAR, UNSUPPORTED_LONGITUDINAL_CAR, CAMERA_SCC_CAR)
# Test CAN FD cars are not classified with classic-CAN-only parsing or safety modes.
can_specific_feature_list = set.union(*CAN_GEARS.values(), *CHECKSUM.values(), LEGACY_SAFETY_MODE_CAR)
for car_model in CANFD_CAR:
assert car_model not in can_specific_feature_list, "CAN FD car unexpectedly found in a CAN feature list"
def test_hybrid_ev_sets(self):
assert HYBRID_CAR & EV_CAR == set(), "Shared cars between hybrid and EV"
assert CANFD_CAR & HYBRID_CAR == set(), "Hard coding CAN FD cars as hybrid is no longer supported"
assert HYBRID_CAR <= set(CAR)
assert EV_CAR <= set(CAR)
def test_canfd_ecu_whitelist(self):
# Asserts only expected Ecus can exist in database for CAN-FD cars
for car_model in CANFD_CAR:
ecus = {fw[0] for fw in FW_VERSIONS[car_model].keys()}
ecus = {fw[0] for fw in FW_VERSIONS.get(car_model, {}).keys()}
ecus_not_in_whitelist = ecus - CANFD_EXPECTED_ECUS
ecu_strings = ", ".join([f"Ecu.{ecu}" for ecu in ecus_not_in_whitelist])
assert len(ecus_not_in_whitelist) == 0, \
@@ -158,8 +149,6 @@ class TestHyundaiFingerprint:
continue
if platform_code_ecu == Ecu.eps and car_model in no_eps_platforms:
continue
if car_model in NON_SCC_CAR:
continue
assert platform_code_ecu in [e[0] for e in ecus]
def test_fw_format(self, subtests):
@@ -174,9 +163,6 @@ class TestHyundaiFingerprint:
if ecu[0] not in PLATFORM_CODE_ECUS:
continue
if car_model in NON_SCC_CAR:
continue
codes = set()
for fw in fws:
result = get_platform_codes([fw])
@@ -225,15 +211,6 @@ class TestHyundaiFingerprint:
(b"ON-S9100", b"190405"), (b"ON-S9100", b"190720")}
def test_fuzzy_excluded_platforms(self):
# Asserts a list of platforms that will not fuzzy fingerprint with platform codes due to them being shared.
# This list can be shrunk as we combine platforms and detect features
excluded_platforms = {
CAR.GENESIS_G70, # shared platform code, part number, and date
CAR.GENESIS_G70_2020,
}
excluded_platforms |= CANFD_CAR - EV_CAR - CANFD_FUZZY_WHITELIST # shared platform codes
excluded_platforms |= NO_DATES_PLATFORMS # date codes are required to match
platforms_with_shared_codes = set()
for platform, fw_by_addr in FW_VERSIONS.items():
car_fw = []
@@ -243,9 +220,6 @@ class TestHyundaiFingerprint:
car_fw.append(CarParams.CarFw(ecu=ecu_name, fwVersion=fw, address=addr,
subAddress=0 if sub_addr is None else sub_addr))
if platform in NON_SCC_CAR:
continue
CP = CarParams(carFw=car_fw)
matches = FW_QUERY_CONFIG.match_fw_to_car_fuzzy(build_fw_dict(CP.carFw), CP.carVin, FW_VERSIONS)
if len(matches) == 1:
@@ -253,4 +227,6 @@ class TestHyundaiFingerprint:
else:
platforms_with_shared_codes.add(platform)
assert platforms_with_shared_codes == excluded_platforms
# A unique fuzzy match must always resolve back to the platform that supplied the firmware.
# Ambiguity is expected for shared platform codes and for platforms without parseable dates.
assert {CAR.GENESIS_G70, CAR.GENESIS_G70_2020} <= platforms_with_shared_codes
@@ -0,0 +1,53 @@
import pytest
from iqdbc.car import gen_empty_fingerprint, structs
from iqdbc.car.hyundai.interface import CarInterface
from iqdbc.car.hyundai.values import CAR, HyundaiFlagsIQ, HyundaiSafetyFlagsIQ
@pytest.mark.parametrize("candidate", list(CAR), ids=lambda candidate: candidate.value)
@pytest.mark.parametrize("alpha_long", (False, True), ids=("stock_long", "openpilot_long"))
def test_all_platform_state_controller_and_radar(candidate, alpha_long, monkeypatch, tmp_path):
"""Every declared HKG platform must initialize and execute one complete interface cycle."""
monkeypatch.setenv("PARAMS_ROOT", str(tmp_path / candidate.value))
fingerprint = gen_empty_fingerprint()
cp = CarInterface.get_params(candidate, fingerprint, [], alpha_long, False, False)
cp_iq = CarInterface.get_params_iq(cp, candidate, fingerprint, [], alpha_long, False, False)
interface = CarInterface(cp, cp_iq)
state, state_iq = interface.update([])
actuators, can_sends = interface.apply(structs.CarControl().as_reader(), structs.IQCarControl())
radar = interface.RadarInterface(cp, cp_iq)
radar_result = radar.update([])
assert state.vEgo == 0.0
assert state_iq is not None
assert actuators is not None
assert isinstance(can_sends, list)
assert radar_result is None
def test_parameter_defaults_do_not_require_persisted_fingerprint(monkeypatch, tmp_path):
"""A clean installation must not crash when carrotpilot-specific Params have not been written yet."""
monkeypatch.setenv("PARAMS_ROOT", str(tmp_path))
fingerprint = gen_empty_fingerprint()
cp = CarInterface.get_params(CAR.HYUNDAI_SONATA, fingerprint, [], False, False, False)
cp_iq = CarInterface.get_params_iq(cp, CAR.HYUNDAI_SONATA, fingerprint, [], False, False, False)
interface = CarInterface(cp, cp_iq)
state, _ = interface.update([])
assert state.vEgo == 0.0
def test_classic_lfa_button_capability_survives_iq_module_removal(monkeypatch, tmp_path):
monkeypatch.setenv("PARAMS_ROOT", str(tmp_path))
fingerprint = gen_empty_fingerprint()
fingerprint[0][0x391] = 8
cp = CarInterface.get_params(CAR.HYUNDAI_SONATA, fingerprint, [], False, False, False)
cp_iq = CarInterface.get_params_iq(cp, CAR.HYUNDAI_SONATA, fingerprint, [], False, False, False)
assert cp_iq.flags & HyundaiFlagsIQ.HAS_LFA_BUTTON
assert cp_iq.iqSafetyFlags & HyundaiSafetyFlagsIQ.HAS_LDA_BUTTON
@@ -0,0 +1,326 @@
import math
import pytest
from iqdbc.can import CANParser
from iqdbc.car import Bus, structs
import iqdbc.car.hyundai.hyundaicanfd as hyundaicanfd
import iqdbc.car.hyundai.radar_interface as radar_interface_module
from iqdbc.car.hyundai.radar_interface import (
CORNER_OBJECT_STABLE_TRACK_ID_START,
RADAR_MSG_COUNT3,
RADAR_MSG_COUNT4,
RADAR_START_ADDR_CANFD3,
CornerObjectTrackIdManager,
RadarInterface,
corner_object_position_valid,
)
from iqdbc.car.hyundai.values import CAR, HyundaiExtFlags, HyundaiFlags
class TestDensoRadar:
@staticmethod
def parse(addr, dat):
name = f"RADAR_TRACK_{addr:x}"
parser = CANParser("hyundai_kia_denso_front_radar_generated", [(name, 20)], 1)
parser.update([0, [(addr, bytes.fromhex(dat), 1)]])
return parser.vl[name]
def test_active_track_signals(self):
# Person walking toward the parked car, left of the camera center.
track = self.parse(0x503, "bc047efcc1fe8b00")
assert track["LONG_DIST"] == pytest.approx(7.1875)
assert track["LAT_DIST"] == pytest.approx(-1.625)
assert track["REL_SPEED"] == pytest.approx(-0.734375)
assert track["OBJECT_STATE"] == 3
def test_empty_track(self):
track = self.parse(0x507, "53fff80000000081")
assert track["LONG_DIST"] == pytest.approx(409.55)
assert track["LAT_DIST"] == 0
assert track["REL_SPEED"] == 0
assert track["OBJECT_STATE"] == 0
def test_long_range_lateral_distance(self):
# Real driving sample: treating the signed field as -12 degrees would put
# this target about 34 m sideways at 161 m. It is instead -3.0 m lateral.
track = self.parse(0x506, "b664eafa00cd230b")
assert track["LONG_DIST"] == pytest.approx(161.4625)
assert track["LAT_DIST"] == pytest.approx(-3.0)
assert track["OBJECT_STATE"] == 3
def test_parser_selection_and_point_conversion(self, monkeypatch):
class FakeParams:
def get_int(self, key):
return 1 if key == "EnableRadarTracks" else 0
monkeypatch.setattr(radar_interface_module, "Params", FakeParams)
cp = structs.CarParams()
cp.carFingerprint = CAR.KIA_SORENTO
cp.flags = 0
cp.extFlags = HyundaiExtFlags.RADAR_GROUP4.value
cp.radarUnavailable = False
cp.safetyConfigs = [structs.CarParams.SafetyConfig()]
radar_interface = RadarInterface(cp)
assert radar_interface.radar_group4
assert RADAR_MSG_COUNT4 == 8
assert radar_interface.radar_msg_count == RADAR_MSG_COUNT4
assert radar_interface.trigger_msg_tracks == 0x507
active_dat = bytes.fromhex("bc047efcc1fe8b00")
empty_dat = bytes.fromhex("bcfff80000000081")
packets = [(addr, active_dat if addr == 0x503 else empty_dat, 1) for addr in range(0x500, 0x508)]
radar_data = radar_interface.update([0, packets])
point = next(point for point in radar_data.points if point.trackId == 35)
assert point.measured
assert point.dRel == pytest.approx(7.1875)
assert point.yRel == pytest.approx(1.625)
assert point.vRel == pytest.approx(-0.734375)
assert math.isnan(point.aRel)
# EN: Confirm that the long-range sample survives the filter and converts
# radar-left-negative to openpilot-left-positive coordinates.
# KO: 장거리 샘플의 필터 통과와 레이더 좌측 음수 좌표가 openpilot 좌측
# 양수 좌표로 변환되는지 확인함.
long_range_dat = bytes.fromhex("b664eafa00cd230b")
packets = [(addr, long_range_dat if addr == 0x506 else empty_dat, 1) for addr in range(0x500, 0x508)]
radar_data = radar_interface.update([0, packets])
point = next(point for point in radar_data.points if point.trackId == 38)
assert point.dRel == pytest.approx(161.4625)
assert point.yRel == pytest.approx(3.0)
# EN: A state-0 raw detection must not enter a stable tracked-object slot.
# KO: 상태 0인 raw detection이 안정적인 추적 객체 슬롯에 들어오지 않음을 확인함.
raw_detection = bytes.fromhex("d702f4fc200000e4")
packets = [(addr, raw_detection if addr == 0x503 else empty_dat, 1) for addr in range(0x500, 0x508)]
radar_data = radar_interface.update([0, packets])
assert not radar_data.points
# EN: A real confirmed track beyond the former 205 m limit remains valid.
# KO: 기존 205m 상한을 넘는 실제 확정 트랙도 유효하게 유지됨.
confirmed_213m_track = bytes.fromhex("35854c0780f163e0")
packets = [(addr, confirmed_213m_track if addr == 0x503 else empty_dat, 1) for addr in range(0x500, 0x508)]
radar_data = radar_interface.update([0, packets])
point = next(point for point in radar_data.points if point.trackId == 35)
assert point.dRel == pytest.approx(213.275)
assert point.yRel == pytest.approx(-3.75)
# EN: The 325 m boundary is rejected, leaving ample separation from the
# 409.55 m empty-slot sentinel.
# KO: 325m 경계값을 제외해 409.55m 빈 슬롯 값과 충분한 간격을 확보함.
boundary_track = bytes.fromhex("bccb200000000300")
packets = [(addr, boundary_track if addr == 0x503 else empty_dat, 1) for addr in range(0x500, 0x508)]
radar_data = radar_interface.update([0, packets])
assert not radar_data.points
# EN: The wider profile keeps a real stable track at 4.875 m, covering more
# of the outer adjacent lane than the conservative 4.5 m profile.
# KO: 넓어진 필터에서 4.875m의 실제 안정 트랙을 유지해 보수적인 4.5m
# 설정보다 바깥쪽 인접 차선을 더 넓게 포함함.
outer_lane_track = bytes.fromhex("d80b66f640000300")
packets = [(addr, outer_lane_track if addr == 0x503 else empty_dat, 1) for addr in range(0x500, 0x508)]
radar_data = radar_interface.update([0, packets])
point = next(point for point in radar_data.points if point.trackId == 35)
assert point.yRel == pytest.approx(4.875)
# EN: Tracks beyond the widened envelope are rejected as roadside clutter;
# this payload differs only in lateral distance (-7.0 m).
# KO: 넓어진 범위를 벗어난 트랙은 도로변 잡음으로 제외함. 이 payload는
# 횡방향 거리(-7.0m)만 다름.
far_side_reflection = bytes.fromhex("d80b66f200000300")
packets = [(addr, far_side_reflection if addr == 0x503 else empty_dat, 1) for addr in range(0x500, 0x508)]
radar_data = radar_interface.update([0, packets])
assert not radar_data.points
class TestRadarGroup3:
@staticmethod
def parse(addr, dat):
name = f"RADAR_TRACK_{addr:x}"
parser = CANParser("hyundai_canfd_radar_generated", [(name, 20)], 1)
parser.update([0, [(addr, bytes.fromhex(dat), 1)]])
return parser.vl[name]
def test_group3_active_track(self):
track = self.parse(0x406, "e1043b0f02590e692a227e16f80fe00f28fcc753a20a0000")
assert track["OBJECT_LENGTH"] == pytest.approx(4.4)
assert track["LONG_DIST"] == pytest.approx(55.4)
assert track["LAT_DIST"] == pytest.approx(-3.0)
assert track["REL_SPEED"] == pytest.approx(4.4)
def test_group3_empty_track(self):
track = self.parse(0x407, "c03d3b0000000000ff0700000000000000d0020000000000")
assert track["OBJECT_LENGTH"] == 0
assert track["LONG_DIST"] == pytest.approx(204.7)
assert track["LAT_DIST"] == 0
assert track["REL_SPEED"] == 0
def test_group3_parser_selection(self, monkeypatch):
class FakeParams:
def get_int(self, key):
return 1 if key == "EnableRadarTracks" else 0
monkeypatch.setattr(radar_interface_module, "Params", FakeParams)
monkeypatch.setattr(hyundaicanfd, "Params", FakeParams)
cp = structs.CarParams()
cp.carFingerprint = next(car for car, dbc in radar_interface_module.DBC.items() if "hyundai_canfd" in dbc[Bus.pt])
cp.flags = HyundaiFlags.CANFD.value
cp.extFlags = HyundaiExtFlags.RADAR_GROUP3.value
cp.radarUnavailable = False
cp.safetyConfigs = [structs.CarParams.SafetyConfig()]
radar_interface = RadarInterface(cp)
assert radar_interface.radar_group3
assert radar_interface.radar_start_addr == RADAR_START_ADDR_CANFD3
assert radar_interface.radar_msg_count == RADAR_MSG_COUNT3
assert radar_interface.trigger_msg_tracks == 0x41D
active_dat = bytes.fromhex("e1043b0f02590e692a227e16f80fe00f28fcc753a20a0000")
empty_dat = bytes.fromhex("c03d3b0000000000ff0700000000000000d0020000000000")
packets = [(addr, active_dat if addr == 0x406 else empty_dat, 1) for addr in range(0x400, 0x41E)]
radar_data = radar_interface.update([0, packets])
point = next(point for point in radar_data.points if point.trackId == 38)
assert point.measured
assert point.dRel == pytest.approx(53.1)
assert point.yRel == pytest.approx(-3.0)
assert point.vRel == pytest.approx(4.4)
class TestCornerRadarObjectIdentity:
@staticmethod
def set_bits(data, start, size, value):
for offset in range(size):
bit = start + offset
data[bit // 8] |= ((value >> offset) & 1) << (bit % 8)
@pytest.mark.parametrize(
"dbc,msg_name,addr,age_signal,id_signal,age_start,id_start",
(
("hyundai_canfd_corner_radar_180_generated", "CORNER_RADAR_180_OBJECTS_180", 0x180,
"SLOT1_AGE", "SLOT1_OBJECT_ID", 32, 44),
("hyundai_canfd_corner_radar_235_generated", "CORNER_RADAR_235_OBJECTS_235", 0x235,
"OBJ_AGE", "OBJ_OBJECT_ID", 32, 44),
),
)
def test_object_identity_signals(self, dbc, msg_name, addr, age_signal, id_signal, age_start, id_start):
data = bytearray(32)
self.set_bits(data, age_start, 8, 23)
self.set_bits(data, id_start, 7, 46)
parser = CANParser(dbc, [(msg_name, 33)], 1)
parser.update([0, [(addr, bytes(data), 1)]])
assert parser.vl[msg_name][age_signal] == 23
assert parser.vl[msg_name][id_signal] == 46
def test_track_id_survives_slot_move_and_resets_with_age(self):
manager = CornerObjectTrackIdManager()
first_id = manager.get_track_id("corner180", object_id=108, age=240)
assert first_id == CORNER_OBJECT_STABLE_TRACK_ID_START
assert manager.get_track_id("corner180", object_id=108, age=241) == first_id
assert manager.get_track_id("corner235", object_id=108, age=241) != first_id
assert manager.get_track_id("corner180", object_id=108, age=2) != first_id
def test_clipped_side_object_position_is_valid(self):
assert corner_object_position_valid(0.0, 2.8)
assert corner_object_position_valid(25.0, 0.2)
assert not corner_object_position_valid(0.0, 0.0)
assert not corner_object_position_valid(0.0, 5.0)
class TestCornerRadar430CandidateFilter:
@staticmethod
def slot_word(distance_raw, meta13=0, b2=10, b3=2):
return distance_raw | (meta13 << 13) | (b2 << 16) | (b3 << 24)
@classmethod
def message(cls, slots):
words = [0x010d1f40] * 7
for slot, word in slots.items():
words[slot - 1] = word
dat = bytearray(32)
for idx, word in enumerate(words):
dat[4 + idx * 4:8 + idx * 4] = int(word).to_bytes(4, "little")
return bytes(dat)
@staticmethod
def build_interface(monkeypatch):
class FakeParams:
def get_int(self, key):
return 1 if key == "EnableCornerRadar" else 0
monkeypatch.setattr(radar_interface_module, "Params", FakeParams)
monkeypatch.setattr(hyundaicanfd, "Params", FakeParams)
cp = structs.CarParams()
cp.carFingerprint = next(car for car, dbc in radar_interface_module.DBC.items() if "hyundai_canfd" in dbc[Bus.pt])
cp.flags = HyundaiFlags.CANFD.value
cp.extFlags = HyundaiExtFlags.CORNER_RADAR_OBJECTS_430.value
cp.radarUnavailable = True
cp.safetyConfigs = [structs.CarParams.SafetyConfig()]
return RadarInterface(cp)
@staticmethod
def update_frames(radar_interface, packets, frames=5):
radar_data = None
for _ in range(frames):
radar_data = radar_interface.update([0, packets])
return radar_data
def test_430_promotes_supported_neighbor_bins(self, monkeypatch):
radar_interface = self.build_interface(monkeypatch)
empty = self.message({})
supported_bins = self.message({
6: self.slot_word(1000),
7: self.slot_word(1004),
})
packets = [(addr, supported_bins if addr == 0x436 else empty, 1) for addr in range(0x430, 0x438)]
packets += [(addr, empty, 1) for addr in range(0x440, 0x448)]
radar_data = self.update_frames(radar_interface, packets)
points = {point.trackId: point for point in radar_data.points}
assert points[300].measured
assert points[300].dRel == pytest.approx(50.1)
assert points[300].yRel == pytest.approx(2.0)
assert points[300].yvRel == 0.0
def test_430_expires_noncenter_inward_yvrel(self, monkeypatch):
radar_interface = self.build_interface(monkeypatch)
empty = self.message({})
frame_defs = (
(0x431, 4, 5),
(0x433, 3, 4),
(0x435, 2, 3),
(0x430, 5, 6),
(0x432, 4, 5),
(0x434, 3, 4),
(0x436, 2, 3),
)
radar_data = None
for addr, first_slot, second_slot in frame_defs:
msg = self.message({
first_slot: self.slot_word(1000),
second_slot: self.slot_word(1004),
})
packets = [(a, msg if a == addr else empty, 1) for a in range(0x430, 0x438)]
packets += [(a, empty, 1) for a in range(0x440, 0x448)]
radar_data = radar_interface.update([0, packets])
radar_data = self.update_frames(radar_interface, packets, frames=3)
points = {point.trackId: point for point in radar_data.points}
assert points[300].measured
assert points[300].yRel == pytest.approx(2.0)
assert points[300].yvRel == 0.0
+275 -92
View File
@@ -1,21 +1,37 @@
import re
from dataclasses import dataclass, field
from enum import IntFlag
from enum import Enum, IntFlag
from iqdbc.car import Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds
from iqdbc.car.lateral import AngleSteeringLimits
from iqdbc.car.common.conversions import Conversions as CV
from iqdbc.car.structs import CarParams
from iqdbc.car.docs_definitions import CarHarness, CarDocs, CarParts, SupportType
from iqdbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column
from iqdbc.car.fw_query_definitions import FwQueryConfig, Request, p16
from iqdbc.lvbs.car.hyundai.values import HyundaiFlagsIQ
Ecu = CarParams.Ecu
class CarControllerParams:
ACCEL_MIN = -3.5 # m/s
ACCEL_MAX = 2.0 # m/s
ACCEL_MIN = -4.0 # m/s
ACCEL_MAX = 2.5 # m/s
ANGLE_LIMITS: AngleSteeringLimits = AngleSteeringLimits(
# LKAS angle command is unlimited, but LFA is limited to 176.7 deg (but does not fault if requesting above)
175, # deg
# stock comma's
#([0, 9, 16, 25], [1.4, 0.6, 0.4, 0.1]),
#([0, 9, 16, 25], [1.4, 0.7, 0.5, 0.1]),
([0, 9, 16, 25], [1.8, 1.6, 1.3, 0.8]),
([0, 9, 16, 25], [2.4, 2.0, 1.6, 1.0]),
#([0, 9, 16, 25], [1.6, 1.0, 0.6, 0.15]),
#([0, 9, 16, 25], [2.0, 1.2, 0.8, 0.28]),
)
# Stock LFA system is seen sending 250 max, but for LKAS events it's 175 max.
# 250 can at least achieve 4 m/s^2, 80 corresponds to ~2.5 m/s^2
ANGLE_MAX_TORQUE = 200 # The maximum amount of torque that will be allowed
ANGLE_MIN_TORQUE = 25 # equivalent to ~0.8 m/s^2 of torque (based on ANGLE_MAX_TORQUE) when overriding
ANGLE_TORQUE_UP_RATE = 8 #2 # Indicates how fast the torque ramps up after user intervention.
ANGLE_TORQUE_DOWN_RATE = 12 #4 Indicates how fast the torque ramps down during user intervention (handing off).
def __init__(self, CP):
self.STEER_DELTA_UP = 3
@@ -41,20 +57,17 @@ class CarControllerParams:
CAR.KIA_OPTIMA_H, CAR.KIA_OPTIMA_H_G4_FL, CAR.KIA_SORENTO):
self.STEER_MAX = 255
elif CP.carFingerprint in (CAR.HYUNDAI_SANTA_FE_PHEV_2022):
self.STEER_MAX = 409
# these cars have significantly more torque than most HKG; limit to 70% of max
elif CP.flags & HyundaiFlags.ALT_LIMITS:
self.STEER_MAX = 270
self.STEER_DELTA_UP = 2
self.STEER_DELTA_DOWN = 3
elif CP.flags & HyundaiFlags.ALT_LIMITS_2:
self.STEER_MAX = 170
self.STEER_MAX = 384
self.STEER_DELTA_UP = 2
self.STEER_DELTA_DOWN = 3
# Default for most HKG
else:
self.STEER_MAX = 384
self.STEER_MAX = 409
class HyundaiSafetyFlags(IntFlag):
@@ -94,12 +107,7 @@ class HyundaiFlagsIQ(IntFlag):
class HyundaiFlags(IntFlag):
# Dynamic Flags
# Default assumption: all cars use LFA (ADAS) steering from the camera.
# CANFD_LKA_STEERING/CANFD_LKA_STEERING_ALT cars typically have both LKA (camera) and LFA (ADAS) steering messages,
# with LKA commands forwarded to the ADAS DRV ECU.
# Most HDA2 trims are assumed to be equipped with the ADAS DRV ECU, though some variants may not be equipped with one.
CANFD_LKA_STEERING = 1
CANFD_HDA2 = 1
CANFD_ALT_BUTTONS = 2
CANFD_ALT_GEARS = 2 ** 2
CANFD_CAMERA_SCC = 2 ** 3
@@ -109,7 +117,7 @@ class HyundaiFlags(IntFlag):
CANFD_ALT_GEARS_2 = 2 ** 6
SEND_LFA = 2 ** 7
USE_FCA = 2 ** 8
CANFD_LKA_STEERING_ALT = 2 ** 9
CANFD_HDA2_ALT_STEERING = 2 ** 9
# these cars use a different gas signal
HYBRID = 2 ** 10
@@ -125,7 +133,7 @@ class HyundaiFlags(IntFlag):
# The radar does SCC on these cars when HDA I, rather than the camera
RADAR_SCC = 2 ** 14
# The camera does SCC on these cars, rather than the radar
CAMERA_SCC = 2 ** 15
CAMERA_SCC = CANFD_CAMERA_SCC #2 ** 15
CHECKSUM_CRC8 = 2 ** 16
CHECKSUM_6B = 2 ** 17
@@ -144,23 +152,41 @@ class HyundaiFlags(IntFlag):
MIN_STEER_32_MPH = 2 ** 23
HAS_LDA_BUTTON = 2 ** 24
ANGLE_CONTROL = 2 ** 24
FCEV = 2 ** 25
ALT_LIMITS_2 = 2 ** 26
CC_ONLY_CAR = 2 ** 31
class HyundaiExtFlags(IntFlag):
NAVI_CLUSTER = 2 ** 2
HAS_LFAHDA = 2 ** 4
CANFD_GEARS_NONE = 2 ** 6
RADAR_GROUP1 = 2 ** 7 # 0x210 radar group 1, 0x3A5 radar group 2
CANFD_GEARS_69 = 2 ** 10
RADAR_GROUP3 = 2 ** 11 # 0x400-0x41D radar object group
CORNER_RADAR_OBJECTS_235 = 2 ** 12 # 0x230 status + 0x235-0x248 raw corner radar objects
CORNER_RADAR_OBJECTS_180 = 2 ** 13 # 0x180-0x184 bus 1 two-slot raw corner/front radar objects
CORNER_RADAR_OBJECTS_430 = 2 ** 14 # 0x430-0x437 left + 0x440-0x447 right IONIQ 9 corner radar bins
RADAR_GROUP4 = 2 ** 15 # 0x500-0x507 Denso DNMWR006 stable radar tracks
EV_MODE_STATUS_230 = 2 ** 16 # ECAN 0x230/DLC32 exposes the hybrid power-flow mode used for the EV indicator
class Footnote(Enum):
CANFD = CarFootnote(
"Requires a <a href=\"https://comma.ai/shop/can-fd-panda-kit\" target=\"_blank\">CAN FD panda kit</a> if not using " +
"comma 3X for this <a href=\"https://en.wikipedia.org/wiki/CAN_FD\" target=\"_blank\">CAN FD car</a>.",
Column.MODEL)
@dataclass
class HyundaiCarDocs(CarDocs):
package: str = "Smart Cruise Control (SCC)"
@dataclass
class HyundaiNonSccCarDocs(CarDocs):
package: str = "No Smart Cruise Control (Non-SCC)"
support_type: SupportType = SupportType.COMMUNITY
support_link: str = "community"
def init_make(self, CP: CarParams):
if CP.flags & HyundaiFlags.CANFD:
self.footnotes.insert(0, Footnote.CANFD)
@dataclass
@@ -175,20 +201,9 @@ class HyundaiPlatformConfig(PlatformConfig):
self.specs = self.specs.override(minSteerSpeed=32 * CV.MPH_TO_MS)
@dataclass
class HyundaiNonSccPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "hyundai_kia_generic"})
def init(self):
self.iq_flags |= HyundaiFlagsIQ.NON_SCC
if self.flags & HyundaiFlags.MIN_STEER_32_MPH:
self.specs = self.specs.override(minSteerSpeed=32 * CV.MPH_TO_MS)
@dataclass
class HyundaiCanFDPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "hyundai_canfd_generated"})
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "hyundai_canfd_generated", Bus.radar: 'hyundai_canfd_radar_generated'})
def init(self):
self.flags |= HyundaiFlags.CANFD
@@ -196,6 +211,10 @@ class HyundaiCanFDPlatformConfig(PlatformConfig):
class CAR(Platforms):
# Hyundai
HYUNDAI_AZERA_7TH_GEN = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Azera 2023-2024", "All", car_parts=CarParts.common([CarHarness.hyundai_a]))],
CarSpecs(mass=1700, wheelbase=2.895, steerRatio=16.5),
)
HYUNDAI_AZERA_6TH_GEN = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Azera 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_k]))],
CarSpecs(mass=1600, wheelbase=2.885, steerRatio=14.5),
@@ -282,9 +301,9 @@ class CAR(Platforms):
flags=HyundaiFlags.CLUSTER_GEARS | HyundaiFlags.ALT_LIMITS,
)
HYUNDAI_KONA_2022 = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Kona 2022-23", car_parts=CarParts.common([CarHarness.hyundai_o]))],
[HyundaiCarDocs("Hyundai Kona 2022", car_parts=CarParts.common([CarHarness.hyundai_o]))],
CarSpecs(mass=1491, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385),
flags=HyundaiFlags.CAMERA_SCC | HyundaiFlags.ALT_LIMITS_2,
flags=HyundaiFlags.CAMERA_SCC,
)
HYUNDAI_KONA_EV = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Kona Electric 2018-21", car_parts=CarParts.common([CarHarness.hyundai_g]))],
@@ -307,6 +326,11 @@ class CAR(Platforms):
CarSpecs(mass=1425, wheelbase=2.6, steerRatio=13.42, tireStiffnessFactor=0.385),
flags=HyundaiFlags.HYBRID | HyundaiFlags.ALT_LIMITS,
)
HYUNDAI_KONA_HEV_2ND_GEN = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Kona Hybrid 2024", car_parts=CarParts.common([CarHarness.hyundai_l]))],
CarSpecs(mass=1590, wheelbase=2.66, steerRatio=13.6, tireStiffnessFactor=0.385),
flags=HyundaiFlags.HYBRID,
)
HYUNDAI_NEXO_1ST_GEN = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Nexo 2021", "All", car_parts=CarParts.common([CarHarness.hyundai_h]))],
CarSpecs(mass=3990 * CV.LB_TO_KG, wheelbase=2.79, steerRatio=14.19), # https://www.hyundainews.com/assets/documents/original/42768-2021NEXOProductGuideSpecs.pdf
@@ -322,17 +346,17 @@ class CAR(Platforms):
[HyundaiCarDocs("Hyundai Santa Fe 2021-23", "All", video="https://youtu.be/VnHzSTygTS4",
car_parts=CarParts.common([CarHarness.hyundai_l]))],
HYUNDAI_SANTA_FE.specs,
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8,
flags=HyundaiFlags.CHECKSUM_CRC8,
)
HYUNDAI_SANTA_FE_HEV_2022 = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Santa Fe Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l]))],
HYUNDAI_SANTA_FE.specs,
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
)
HYUNDAI_SANTA_FE_PHEV_2022 = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Santa Fe Plug-in Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l]))],
HYUNDAI_SANTA_FE.specs,
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
)
HYUNDAI_SONATA = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Sonata 2020-23", "All", video="https://www.youtube.com/watch?v=ix63r9kE3Fw",
@@ -343,8 +367,14 @@ class CAR(Platforms):
HYUNDAI_SONATA_LF = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Sonata 2018-19", car_parts=CarParts.common([CarHarness.hyundai_e]))],
CarSpecs(mass=1536, wheelbase=2.804, steerRatio=13.27 * 1.15), # 15% higher at the center seems reasonable
flags=HyundaiFlags.UNSUPPORTED_LONGITUDINAL | HyundaiFlags.TCU_GEARS,
)
HYUNDAI_SONATA_2024 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Sonata 2024-25", "All", car_parts=CarParts.common([CarHarness.hyundai_a]))],
CarSpecs(mass=1556, wheelbase=2.84, steerRatio=12.81),
flags=HyundaiFlags.CAMERA_SCC,
)
HYUNDAI_STARIA_4TH_GEN = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Staria 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k]))],
CarSpecs(mass=2205, wheelbase=3.273, steerRatio=11.94), # https://www.hyundai.com/content/dam/hyundai/au/en/models/staria-load/premium-pip-update-2023/spec-sheet/STARIA_Load_Spec-Table_March_2023_v3.1.pdf
@@ -384,11 +414,32 @@ class CAR(Platforms):
CarSpecs(mass=1948, wheelbase=2.97, steerRatio=14.26, tireStiffnessFactor=0.65),
flags=HyundaiFlags.EV,
)
HYUNDAI_IONIQ_5_PE = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Hyundai IONIQ 5 PE (NE1)", car_parts=CarParts.common([CarHarness.hyundai_q])),
HyundaiCarDocs("Hyundai Ioniq 5 PE (with HDA II) 2024", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])),
],
#CarSpecs(mass=2012, wheelbase=3.0, steerRatio=14.26, tireStiffnessFactor=0.65),
CarSpecs(mass=2012, wheelbase=3.0, steerRatio=14.26, tireStiffnessFactor=1.0),
flags=HyundaiFlags.EV | HyundaiFlags.ANGLE_CONTROL,
)
HYUNDAI_IONIQ_5_N = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Ioniq 5 N (with HDA II) 2024", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_m]))],
CarSpecs(mass=2200, wheelbase=3.00, steerRatio=12.54),
flags=HyundaiFlags.EV,
)
HYUNDAI_IONIQ_6 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Hyundai Ioniq 6 (with HDA II) 2023-24", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p]))],
HYUNDAI_IONIQ_5.specs,
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_NO_RADAR_DISABLE,
)
HYUNDAI_IONIQ_9 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Hyundai Ioniq 9", "All", car_parts=CarParts.common([CarHarness.hyundai_m])),
],
CarSpecs(mass=2505, wheelbase=3.13, steerRatio=16.02),
flags=HyundaiFlags.EV | HyundaiFlags.ANGLE_CONTROL,
)
HYUNDAI_TUCSON_4TH_GEN = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Hyundai Tucson 2022", car_parts=CarParts.common([CarHarness.hyundai_n])),
@@ -408,6 +459,36 @@ class CAR(Platforms):
CarSpecs(mass=1690, wheelbase=3.055, steerRatio=17), # mass: from https://www.hyundai-motor.com.tw/clicktobuy/custin#spec_0, steerRatio: from learner
flags=HyundaiFlags.CHECKSUM_CRC8,
)
HYUNDAI_CASPER = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Casper 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k]))],
CarSpecs(mass=1060, wheelbase=2.4, steerRatio=14.3), # mass: from https://www.hyundai-motor.com.tw/clicktobuy/custin#spec_0, steerRatio: from learner
flags=HyundaiFlags.CAMERA_SCC | HyundaiFlags.CHECKSUM_CRC8,
)
HYUNDAI_CASPER_EV = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Casper EV 2024", "All", car_parts=CarParts.common([CarHarness.hyundai_k]))],
CarSpecs(mass=1355, wheelbase=2.58, steerRatio=14.3), # mass: from https://www.hyundai-motor.com.tw/clicktobuy/custin#spec_0, steerRatio: from learner
flags=HyundaiFlags.CAMERA_SCC | HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.EV
)
HYUNDAI_PORTER_II_EV = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Porter II EV 2024", car_parts=CarParts.common([CarHarness.hyundai_h]))],
CarSpecs(mass=1970, wheelbase=2.64, steerRatio=14.5),
flags=HyundaiFlags.EV | HyundaiFlags.CC_ONLY_CAR,
)
HYUNDAI_SANTAFE_MX5 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Hyundai SANTAFE (MX5)", car_parts=CarParts.common([CarHarness.hyundai_k])),
],
CarSpecs(mass=1910, wheelbase=2.76, steerRatio=15.8, tireStiffnessFactor=0.82),
flags=HyundaiFlags.ANGLE_CONTROL,
)
HYUNDAI_SANTAFE_MX5_HEV = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Hyundai SANTAFE HYBRID (MX5)", car_parts=CarParts.common([CarHarness.hyundai_k])),
],
HYUNDAI_SANTAFE_MX5.specs,
flags=HyundaiFlags.ANGLE_CONTROL,
)
# Kia
KIA_FORTE = HyundaiPlatformConfig(
@@ -427,6 +508,18 @@ class CAR(Platforms):
KIA_K5_2021.specs,
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
)
KIA_K5_DL3_24 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia K5 2024", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_k])),
],
CarSpecs(mass=1553, wheelbase=2.85, steerRatio=13.27, tireStiffnessFactor=0.5),
)
KIA_K5_DL3_24_HEV = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia K5 Hybrid 2024", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_k])),
],
CarSpecs(mass=1553, wheelbase=2.85, steerRatio=13.27, tireStiffnessFactor=0.5),
)
KIA_K8_HEV_1ST_GEN = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Kia K8 Hybrid (with HDA II) 2023", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q]))],
# mass: https://carprices.ae/brands/kia/2023/k8/1.6-turbo-hybrid, steerRatio: guesstimate from K5 platform
@@ -443,16 +536,13 @@ class CAR(Platforms):
flags=HyundaiFlags.MANDO_RADAR | HyundaiFlags.EV,
)
KIA_NIRO_EV_2ND_GEN = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia Niro EV (without HDA II) 2023-25", "All", car_parts=CarParts.common([CarHarness.hyundai_a])),
HyundaiCarDocs("Kia Niro EV (with HDA II) 2025", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_r])),
],
[HyundaiCarDocs("Kia Niro EV 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_a]))],
KIA_NIRO_EV.specs,
flags=HyundaiFlags.EV,
)
KIA_NIRO_PHEV = HyundaiPlatformConfig(
[
HyundaiCarDocs("Kia Niro Hybrid 2018", min_enable_speed=10. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_c])),
HyundaiCarDocs("Kia Niro Hybrid 2018", "All", min_enable_speed=10. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_c])),
HyundaiCarDocs("Kia Niro Plug-in Hybrid 2018-19", "All", min_enable_speed=10. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_c])),
HyundaiCarDocs("Kia Niro Plug-in Hybrid 2020", car_parts=CarParts.common([CarHarness.hyundai_d])),
],
@@ -493,7 +583,7 @@ class CAR(Platforms):
# TODO: may support adjacent years. may have a non-zero minimum steering speed
KIA_OPTIMA_H = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia Optima Hybrid 2017", "Advanced Smart Cruise Control", car_parts=CarParts.common([CarHarness.hyundai_c]))],
CarSpecs(mass=3558 * CV.LB_TO_KG, wheelbase=2.8, steerRatio=13.75, tireStiffnessFactor=0.5),
CarSpecs(mass=3758 * CV.LB_TO_KG, wheelbase=2.8, steerRatio=13.75, tireStiffnessFactor=0.5),
flags=HyundaiFlags.HYBRID | HyundaiFlags.LEGACY,
)
KIA_OPTIMA_H_G4_FL = HyundaiPlatformConfig(
@@ -521,6 +611,7 @@ class CAR(Platforms):
HyundaiCarDocs("Kia Sorento 2019", video="https://www.youtube.com/watch?v=Fkh3s6WHJz8", car_parts=CarParts.common([CarHarness.hyundai_e])),
],
CarSpecs(mass=1985, wheelbase=2.78, steerRatio=14.4 * 1.1), # 10% higher at the center seems reasonable
dbc_dict={Bus.pt: "hyundai_kia_generic", Bus.radar: "hyundai_kia_denso_front_radar_generated"},
flags=HyundaiFlags.CHECKSUM_6B | HyundaiFlags.UNSUPPORTED_LONGITUDINAL,
)
KIA_SORENTO_4TH_GEN = HyundaiCanFDPlatformConfig(
@@ -550,6 +641,11 @@ class CAR(Platforms):
CarSpecs(mass=1450, wheelbase=2.65, steerRatio=13.75, tireStiffnessFactor=0.5),
flags=HyundaiFlags.LEGACY,
)
KIA_EV4 = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Kia EV4 2025", "All", car_parts=CarParts.common([CarHarness.hyundai_q]))],
CarSpecs(mass=1710, wheelbase=2.83, steerRatio=14.5, tireStiffnessFactor=0.65),
flags=HyundaiFlags.EV,
)
KIA_EV6 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia EV6 (Southeast Asia only) 2022-24", "All", car_parts=CarParts.common([CarHarness.hyundai_p])),
@@ -559,6 +655,14 @@ class CAR(Platforms):
CarSpecs(mass=2055, wheelbase=2.9, steerRatio=16, tireStiffnessFactor=0.65),
flags=HyundaiFlags.EV,
)
KIA_EV6_PE = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia EV6 PE (CV1)", car_parts=CarParts.common([CarHarness.hyundai_p])),
HyundaiCarDocs("Kia EV6 PE (with HDA II) 2025", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p]))
],
CarSpecs(mass=2055, wheelbase=2.9, steerRatio=16, tireStiffnessFactor=0.65),
flags=HyundaiFlags.EV | HyundaiFlags.ANGLE_CONTROL,
)
KIA_CARNIVAL_4TH_GEN = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia Carnival 2022-24", car_parts=CarParts.common([CarHarness.hyundai_a])),
@@ -627,54 +731,116 @@ class CAR(Platforms):
CarSpecs(mass=2258, wheelbase=2.95, steerRatio=14.14),
flags=HyundaiFlags.RADAR_SCC,
)
# port extensions
HYUNDAI_BAYON_1ST_GEN_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Hyundai Bayon Non-SCC 2021", car_parts=CarParts.common([CarHarness.hyundai_n]))],
CarSpecs(mass=1150, wheelbase=2.58, steerRatio=13.27 * 1.15),
flags=HyundaiFlags.CHECKSUM_CRC8,
GENESIS_GV70_EV_1ST_GEN = HyundaiCanFDPlatformConfig(
[HyundaiCarDocs("Genesis GV70 EV 2020-2023", "All", car_parts=CarParts.common([CarHarness.hyundai_m]))],
CarSpecs(mass=2230, wheelbase=2.87, steerRatio=14.6),
flags=HyundaiFlags.EV | HyundaiFlags.RADAR_SCC,
)
HYUNDAI_ELANTRA_2022_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Hyundai Elantra Non-SCC 2022", car_parts=CarParts.common([CarHarness.hyundai_k]))],
HYUNDAI_ELANTRA_2021.specs,
flags=HyundaiFlags.CHECKSUM_CRC8,
HYUNDAI_GRANDEUR_IG = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Grandeur 2018-19", "All", car_parts=CarParts.common([CarHarness.hyundai_c]))],
CarSpecs(mass=1570, wheelbase=2.845, steerRatio=16., tireStiffnessFactor=0.7),
flags=HyundaiFlags.LEGACY | HyundaiFlags.CLUSTER_GEARS,
)
HYUNDAI_KONA_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Hyundai Kona Non-SCC 2019", car_parts=CarParts.common([CarHarness.hyundai_b]))],
HYUNDAI_KONA.specs,
flags=HyundaiFlags.ALT_LIMITS,
HYUNDAI_GRANDEUR_IG_HEV = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Grandeur HEV 2018-19", "All", car_parts=CarParts.common([CarHarness.hyundai_c]))],
CarSpecs(mass=1570, wheelbase=2.845, steerRatio=16., tireStiffnessFactor=0.7),
flags=HyundaiFlags.HYBRID | HyundaiFlags.LEGACY,
)
HYUNDAI_KONA_EV_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Hyundai Kona Electric Non-SCC 2019", car_parts=CarParts.common([CarHarness.hyundai_g]))],
HYUNDAI_KONA_EV.specs,
flags=HyundaiFlags.EV | HyundaiFlags.ALT_LIMITS,
GENESIS_EQ900 = HyundaiPlatformConfig(
[HyundaiCarDocs("Genesis EQ900 2017", "All", car_parts=CarParts.common([CarHarness.hyundai_c]))],
CarSpecs(mass=2200, wheelbase=3.15, steerRatio=16., tireStiffnessFactor=0.7),
flags=HyundaiFlags.LEGACY,
)
KIA_CEED_PHEV_2022_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Kia Ceed Plug-in Hybrid Non-SCC 2022", car_parts=CarParts.common([CarHarness.hyundai_i]))],
CarSpecs(mass=1650, wheelbase=2.65, steerRatio=13.75, tireStiffnessFactor=0.5),
GENESIS_EQ900_L = HyundaiPlatformConfig(
[HyundaiCarDocs("Genesis EQ900 LIMOUSINE", "All", car_parts=CarParts.common([CarHarness.hyundai_c]))],
CarSpecs(mass=2290, wheelbase=3.45, steerRatio=16., tireStiffnessFactor=0.7),
flags=HyundaiFlags.LEGACY,
)
GENESIS_G90_2019 = HyundaiPlatformConfig(
[HyundaiCarDocs("Genesis G90 2019", "All", car_parts=CarParts.common([CarHarness.hyundai_c]))],
CarSpecs(mass=2150, wheelbase=3.16, steerRatio=16., tireStiffnessFactor=0.7),
flags=HyundaiFlags.LEGACY,
)
HYUNDAI_NEXO = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Nexo", "All", car_parts=CarParts.common([CarHarness.hyundai_a]))],
CarSpecs(mass=1885, wheelbase=2.79, steerRatio=15.3, tireStiffnessFactor=0.385),
flags=HyundaiFlags.EV,
)
KIA_MOHAVE = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia Mohave 2019", "All", car_parts=CarParts.common([CarHarness.hyundai_k]))],
CarSpecs(mass=2285, wheelbase=2.895, steerRatio=16., tireStiffnessFactor=0.7),
flags=HyundaiFlags.LEGACY,
)
KIA_K5 = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia K5 2019 & 2016", "All", car_parts=CarParts.common([CarHarness.hyundai_b]))],
CarSpecs(mass=1515, wheelbase=2.80, steerRatio=15.5, tireStiffnessFactor=0.7),
flags=HyundaiFlags.LEGACY | HyundaiFlags.TCU_GEARS,
)
KIA_K5_HEV = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia K5 Hybrid 2017", "All", car_parts=CarParts.common([CarHarness.hyundai_c]))],
CarSpecs(mass=1705, wheelbase=2.80, steerRatio=15.5, tireStiffnessFactor=0.7),
flags=HyundaiFlags.HYBRID | HyundaiFlags.LEGACY,
)
KIA_K5_HEV_2022 = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia K5 Hybrid 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_c]))],
CarSpecs(mass=1515, wheelbase=2.85, steerRatio=15.5, tireStiffnessFactor=0.7),
flags=HyundaiFlags.HYBRID | HyundaiFlags.LEGACY,
)
KIA_K7 = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia K7 2016-2019", "All", car_parts=CarParts.common([CarHarness.hyundai_c]))],
CarSpecs(mass=1850, wheelbase=2.855, steerRatio=15.5, tireStiffnessFactor=0.7),
flags=HyundaiFlags.LEGACY | HyundaiFlags.CLUSTER_GEARS,
)
KIA_K7_HEV = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia K7 Hybrid 2016-2019", "All", car_parts=CarParts.common([CarHarness.hyundai_c]))],
CarSpecs(mass=1515, wheelbase=2.855, steerRatio=15.5, tireStiffnessFactor=0.7),
flags=HyundaiFlags.HYBRID | HyundaiFlags.LEGACY,
)
KIA_K7_PE = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia K7 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_c]))],
CarSpecs(mass=1850, wheelbase=2.855, steerRatio=15.5, tireStiffnessFactor=0.7),
)
KIA_K7_HEV_PE = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia K7 Hybrid 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_c]))],
CarSpecs(mass=1515, wheelbase=2.855, steerRatio=15.5, tireStiffnessFactor=0.7),
flags=HyundaiFlags.HYBRID,
)
KIA_FORTE_2019_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Kia Forte Non-SCC 2019", car_parts=CarParts.common([CarHarness.hyundai_g]))],
KIA_FORTE.specs,
iq_flags=HyundaiFlagsIQ.NON_SCC_NO_FCA,
KIA_K9 = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia K9 2016-2019", "All", car_parts=CarParts.common([CarHarness.hyundai_h]))],
CarSpecs(mass=2075, wheelbase=3.15, steerRatio=14.5, tireStiffnessFactor=0.7),
flags=HyundaiFlags.LEGACY,
)
KIA_FORTE_2021_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Kia Forte Non-SCC 2021", car_parts=CarParts.common([CarHarness.hyundai_g]))],
KIA_FORTE.specs,
KIA_EV_SK3 = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia Soul EV 2019", car_parts=CarParts.common([CarHarness.hyundai_c]))],
CarSpecs(mass=1695, wheelbase=2.6, steerRatio=13.75),
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.EV,
)
KIA_SELTOS_2023_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Kia Seltos Non-SCC 2023-24", car_parts=CarParts.common([CarHarness.hyundai_l]))],
KIA_SELTOS.specs,
flags=HyundaiFlags.CHECKSUM_CRC8,
KIA_EV9 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia EV9 (MV)", car_parts=CarParts.common([CarHarness.hyundai_k])),
],
CarSpecs(mass=2625, wheelbase=3.1, steerRatio=16.02),
flags=HyundaiFlags.EV | HyundaiFlags.ANGLE_CONTROL,
)
GENESIS_G70_2021_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Genesis G70 Non-SCC 2021", car_parts=CarParts.common([CarHarness.hyundai_f]))],
GENESIS_G70_2020.specs,
flags=HyundaiFlags.CHECKSUM_CRC8,
iq_flags=HyundaiFlagsIQ.NON_SCC_RADAR_FCA,
KIA_EV3 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia EV3 (SV1)", car_parts=CarParts.common([CarHarness.hyundai_n])),
],
CarSpecs(mass=2055, wheelbase=2.90, steerRatio=16.0, tireStiffnessFactor=0.65),
flags=HyundaiFlags.EV,
)
KIA_PV5 = HyundaiCanFDPlatformConfig(
[
HyundaiCarDocs("Kia PV5 (SW1)", car_parts=CarParts.common([CarHarness.hyundai_n])),
],
CarSpecs(mass=2600, wheelbase=2.995, steerRatio=16.0, tireStiffnessFactor=0.65),
flags=HyundaiFlags.EV,
)
KIA_RAY_EV = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia Ray EV", car_parts=CarParts.common([CarHarness.hyundai_h]))],
CarSpecs(mass=1295, wheelbase=2.520, steerRatio=14.5),
flags=HyundaiFlags.EV | HyundaiFlags.CC_ONLY_CAR | HyundaiFlags.CHECKSUM_CRC8,
)
class Buttons:
NONE = 0
@@ -682,6 +848,7 @@ class Buttons:
SET_DECEL = 2
GAP_DIST = 3
CANCEL = 4 # on newer models, this is a pause/resume button
LFA_BUTTON = 5
def get_platform_codes(fw_versions: list[bytes]) -> set[tuple[bytes, bytes | None]]:
@@ -862,11 +1029,21 @@ CAN_GEARS = {
# which message has the gear. hybrid and EV use ELECT_GEAR
"use_cluster_gears": CAR.with_flags(HyundaiFlags.CLUSTER_GEARS),
"use_tcu_gears": CAR.with_flags(HyundaiFlags.TCU_GEARS),
"send_mdps12": {CAR.GENESIS_G90, CAR.GENESIS_G90_2019, CAR.KIA_K9, CAR.KIA_K7},
}
CANFD_CAR = CAR.with_flags(HyundaiFlags.CANFD)
CANFD_RADAR_SCC_CAR = CAR.with_flags(HyundaiFlags.RADAR_SCC) # TODO: merge with UNSUPPORTED_LONGITUDINAL_CAR
CANFD_HYBRID_STATUS_ADDR = 0xFA
CANFD_HYBRID_STATUS_DLC = 32
EV_MODE_STATUS_ADDR = 0x230
EV_MODE_STATUS_DLC = 32
EV_MODE_STATUS_MSG = "HCU_STATUS_230"
EV_MODE_STATUS_SIGNAL = "HYBRID_POWER_FLOW_MODE"
EV_MODE_ACTIVE_VALUES = frozenset((1, 2, 6))
CANFD_UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.CANFD_NO_RADAR_DISABLE) # TODO: merge with UNSUPPORTED_LONGITUDINAL_CAR
CAMERA_SCC_CAR = CAR.with_flags(HyundaiFlags.CAMERA_SCC)
@@ -881,7 +1058,13 @@ LEGACY_SAFETY_MODE_CAR = CAR.with_flags(HyundaiFlags.LEGACY)
# HyundaiFlags.CANFD_RADAR_SCC | HyundaiFlags.CANFD_NO_RADAR_DISABLE | )
UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.LEGACY) | CAR.with_flags(HyundaiFlags.UNSUPPORTED_LONGITUDINAL)
# port extensions
NON_SCC_CAR = CAR.with_iq_flags(HyundaiFlagsIQ.NON_SCC)
DBC = CAR.create_dbc_map()
if __name__ == "__main__":
cars = []
for platform in CAR:
for doc in platform.config.car_docs:
cars.append(doc.name)
cars.sort()
for c in cars:
print(c)
+22 -3
View File
@@ -253,9 +253,6 @@ class CarInterfaceBase(ABC, CarInterfaceBaseIQ):
ret.steerRatioRear = 0. # no rear steering, at least on the listed cars aboveA
ret.openpilotLongitudinalControl = False
ret.stopAccel = -2.0
ret.stoppingDecelRate = 0.8 # brake_travel/s while trying to stop
ret.vEgoStopping = 0.5
ret.vEgoStarting = 0.5
ret.longitudinalTuning.kpBP = [0.]
ret.longitudinalTuning.kpV = [0.]
ret.longitudinalTuning.kiBP = [0.]
@@ -335,6 +332,12 @@ class CarStateBase(ABC):
x0=[[0.0], [0.0]]
K = get_kalman_gain(DT_CTRL, np.array(A), np.array(C), np.array(Q), R)
self.v_ego_kf = KF1D(x0=x0, A=A, C=C[0], K=K)
self.v_ego_clu_kf = KF1D(x0=x0, A=A, C=C[0], K=K)
self.softHoldActive = 0
self.is_metric = True
self.lkas_enabled = False
self.modelV2 = None
@abstractmethod
def update(self, can_parsers) -> tuple[structs.CarState, structs.IQCarState]:
@@ -351,6 +354,22 @@ class CarStateBase(ABC):
v_ego_x = self.v_ego_kf.update(v_ego_raw)
return float(v_ego_x[0]), float(v_ego_x[1])
def update_clu_speed_kf(self, v_ego_raw):
if abs(v_ego_raw - self.v_ego_clu_kf.x[0][0]) > 2.0:
self.v_ego_clu_kf.set_x([[v_ego_raw], [0.0]])
v_ego_x = self.v_ego_clu_kf.update(v_ego_raw)
return float(v_ego_x[0]), float(v_ego_x[1])
def get_wheel_speeds(self, fl, fr, rl, rr, unit=CV.KPH_TO_MS):
factor = unit * self.CP.wheelSpeedFactor
wheel_speeds = structs.CarState.WheelSpeeds()
wheel_speeds.fl = fl * factor
wheel_speeds.fr = fr * factor
wheel_speeds.rl = rl * factor
wheel_speeds.rr = rr * factor
return wheel_speeds
def update_blinker_from_lamp(self, blinker_time: int, left_blinker_lamp: bool, right_blinker_lamp: bool):
"""Update blinkers from lights. Enable output when light was seen within the last `blinker_time`
iterations"""
-1
View File
@@ -6,7 +6,6 @@ from iqdbc.car.vehicle_model import VehicleModel
FRICTION_THRESHOLD = 0.2
# ISO 11270
ISO_LATERAL_ACCEL = 3.0 # m/s^2
ISO_LATERAL_JERK = 5.0 # m/s^3
AVERAGE_ROAD_ROLL = 0.06 # ~3.4 degrees
+2 -2
View File
@@ -3,7 +3,7 @@ import os
import capnp
import urllib.parse
import warnings
from urllib.request import urlopen
from urllib.request import urlopen, Request
import zstandard as zstd
from iqdbc.car.common.basedir import BASEDIR
@@ -27,7 +27,7 @@ class LogReader:
_, ext = os.path.splitext(urllib.parse.urlparse(fn).path)
if fn.startswith("http"):
with urlopen(fn) as f:
with urlopen(Request(fn, headers={"User-Agent": "iqdbc"})) as f:
dat = f.read()
else:
with open(fn, "rb") as f:
+5 -4
View File
@@ -5,17 +5,18 @@ from iqdbc.car import Bus, create_button_events, structs
from iqdbc.car.common.conversions import Conversions as CV
from iqdbc.car.interfaces import CarStateBase
from iqdbc.car.nissan.values import CAR, DBC, CarControllerParams
from iqdbc.lvbs.car.nissan.carstate_ext import CarStateExt
from iqdbc.lvbs.car.nissan.iq_carstate import IQCarState
ButtonType = structs.CarState.ButtonEvent.Type
TORQUE_SAMPLES = 12
class CarState(CarStateBase, CarStateExt):
class CarState(CarStateBase, IQCarState):
def __init__(self, CP, CP_IQ):
CarStateBase.__init__(self, CP, CP_IQ)
CarStateExt.__init__(self, CP, CP_IQ)
IQCarState.__init__(self, CP, CP_IQ)
can_define = CANDefine(DBC[CP.carFingerprint][Bus.pt])
self.lkas_hud_msg = {}
@@ -129,7 +130,7 @@ class CarState(CarStateBase, CarStateExt):
self.lkas_hud_msg = copy.copy(cp_adas.vl["PROPILOT_HUD"])
self.lkas_hud_info_msg = copy.copy(cp_adas.vl["PROPILOT_HUD_INFO_MSG"])
CarStateExt.update(self, ret, ret_iq, can_parsers)
IQCarState.update(self, ret, ret_iq, can_parsers)
ret.buttonEvents = [
*create_button_events(self.distance_button, prev_distance_button, {1: ButtonType.gapAdjustCruise}),
+5 -5
View File
@@ -4,15 +4,15 @@ from iqdbc.car import Bus, structs
from iqdbc.car.interfaces import CarStateBase
from iqdbc.car.rivian.values import DBC, GEAR_MAP
from iqdbc.car.common.conversions import Conversions as CV
from iqdbc.lvbs.car.rivian.carstate_ext import CarStateExt
from iqdbc.lvbs.car.rivian.iq_carstate import IQCarState
GearShifter = structs.CarState.GearShifter
class CarState(CarStateBase, CarStateExt):
class CarState(CarStateBase, IQCarState):
def __init__(self, CP, CP_IQ):
CarStateBase.__init__(self, CP, CP_IQ)
CarStateExt.__init__(self, CP, CP_IQ)
IQCarState.__init__(self, CP, CP_IQ)
self.last_speed = 30
self.acm_lka_hba_cmd = None
@@ -95,7 +95,7 @@ class CarState(CarStateBase, CarStateExt):
self.sccm_wheel_touch = copy.copy(cp.vl["SCCM_WheelTouch"])
self.vdm_adas_status = copy.copy(cp.vl["VDM_AdasSts"])
CarStateExt.update(self, ret, can_parsers)
IQCarState.update(self, ret, can_parsers)
return ret, ret_iq
@@ -105,5 +105,5 @@ class CarState(CarStateBase, CarStateExt):
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 0),
Bus.adas: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 1),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 2),
**CarStateExt.get_parser(CP, CP_IQ),
**IQCarState.get_parser(CP, CP_IQ),
}
+2 -1
View File
@@ -32,7 +32,6 @@ class CarInterface(CarInterfaceBase):
ret.safetyConfigs[0].safetyParam |= RivianSafetyFlags.LONG_CONTROL.value
ret.longitudinalActuatorDelay = 0.35
ret.vEgoStopping = 0.25
ret.stopAccel = 0
return ret
@@ -40,6 +39,8 @@ class CarInterface(CarInterfaceBase):
@staticmethod
def _get_params_iq(stock_cp: structs.CarParams, ret: structs.IQCarParams, candidate, fingerprint: dict[int, dict[int, int]],
car_fw: list[structs.CarParams.CarFw], alpha_long: bool, is_release_iq: bool, docs: bool) -> structs.IQCarParams:
# Longitudinal harness upgrade advertises msg 0x31a on bus 5; when present it
# unlocks radar, blind-spot and openpilot longitudinal on Rivian.
if 0x31a in fingerprint[5]:
ret.flags |= RivianFlagsIQ.LONGITUDINAL_HARNESS_UPGRADE.value
stock_cp.radarUnavailable = False
+1
View File
@@ -52,6 +52,7 @@ class IQCarParams:
pcmCruiseSpeed: bool = auto_field()
enableGasInterceptor: bool = auto_field()
longitudinalStoppingSpeedOverride: float = auto_field()
stoppingDecelRateOverride: float = auto_field()
iqLateralNet: 'IQCarParams.LateralNet' = field(default_factory=lambda: IQCarParams.LateralNet())
+2
View File
@@ -0,0 +1,2 @@
# FIXME: gate by FingerPrint
TESLA_BLINKERS = False
@@ -3,11 +3,13 @@ from iqdbc.can import CANPacker
from iqdbc.car import Bus
from iqdbc.car.lateral import apply_steer_angle_limits_vm
from iqdbc.car.interfaces import CarControllerBase
from iqdbc.car.tesla import TESLA_BLINKERS
from iqdbc.car.tesla.teslacan import TeslaCAN
from iqdbc.car.tesla.values import CarControllerParams
from iqdbc.car.vehicle_model import VehicleModel
from openpilot.iqpilot.selfdrive.car.enhanced_stock_longitudinal_control import get_set_speed_kph_from_params
from iqdbc.lvbs.car.tesla.torque_blend import TorqueBlendController
from iqdbc.lvbs.car.tesla.values import TeslaFlagsIQ
def get_safety_CP():
@@ -28,6 +30,11 @@ class CarController(CarControllerBase):
# Vehicle model used for lateral limiting
self.VM = VehicleModel(get_safety_CP())
self.has_vehicle_bus = bool(CP_IQ.flags & TeslaFlagsIQ.HAS_VEHICLE_BUS)
self.body_controls_counter_last = -1
self.blinker_request_prev = False
self.blinker_cancel_frame = 0
def update(self, CC, CC_IQ, CS, now_nanos):
actuators = CC.actuators
can_sends = []
@@ -52,6 +59,8 @@ class CarController(CarControllerBase):
if self.frame % 4 == 0:
state = 13 if CC.cruiseControl.cancel or CS.das_accCancel else 4 # 4=ACC_ON, 13=ACC_CANCEL_GENERIC_SILENT
accel = float(np.clip(actuators.accel, CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX))
if not CC.longActive:
accel = 0.
cntr = (self.frame // 4) % 8
set_speed_kph = get_set_speed_kph_from_params(CC_IQ.params)
can_sends.append(self.tesla_can.create_longitudinal_command(state, accel, cntr, CS.out.vEgo, CC.longActive,
@@ -63,6 +72,30 @@ class CarController(CarControllerBase):
cntr = (CS.das_control["DAS_controlCounter"] + 1) % 8
can_sends.append(self.tesla_can.create_longitudinal_command(13, 0, cntr, CS.out.vEgo, False, True))
# Nav blinker control via DAS_bodyControls on the vehicle bus, phase-locked to the car's
# counter. Cancel on the trailing edge since the body controller latches the signal.
stock_dat = getattr(CS, 'das_body_controls_dat', b"")
# FIXME: gate by FingerPrint
if TESLA_BLINKERS and self.has_vehicle_bus and len(stock_dat) >= 8:
left_blinker = CC.leftBlinker
right_blinker = CC.rightBlinker
driver_opposes = (left_blinker and CS.out.rightBlinker) or (right_blinker and CS.out.leftBlinker)
if driver_opposes:
left_blinker = right_blinker = False
nav_requesting = left_blinker or right_blinker
if self.blinker_request_prev and not nav_requesting and not driver_opposes:
self.blinker_cancel_frame = self.frame + 150 # ~1.5 s
self.blinker_request_prev = nav_requesting
cancel = not nav_requesting and not driver_opposes and self.frame < self.blinker_cancel_frame
body_counter = stock_dat[6] >> 4
if body_counter != self.body_controls_counter_last:
can_sends.append(self.tesla_can.create_body_controls(stock_dat, left_blinker, right_blinker, cancel))
self.body_controls_counter_last = body_counter
# TODO: HUD control
new_actuators = actuators.as_builder()
new_actuators.steeringAngleDeg = self.apply_angle_last
+31 -42
View File
@@ -1,34 +1,37 @@
import copy
from iqdbc.can import CANDefine, CANParser
from iqdbc.car import Bus, create_button_events, structs
from iqdbc.car.carlog import carlog
from iqdbc.car.common.conversions import Conversions as CV
from iqdbc.car.interfaces import CarStateBase
from iqdbc.car.tesla.teslacan import get_steer_ctrl_type
from iqdbc.car.tesla import TESLA_BLINKERS
from iqdbc.car.tesla.values import DBC, CANBUS, GEAR_MAP, STEER_THRESHOLD, TeslaFlags
from iqdbc.lvbs.car.tesla.carstate_ext import CarStateExt
from iqdbc.lvbs.car.tesla.iq_carstate import IQCarState
from iqdbc.lvbs.car.tesla.values import TeslaFlagsIQ
from openpilot.common.params import Params
from openpilot.system.proprietary_runtime._verified_import import import_verified_module
vehicle_state = import_verified_module("iqpilot_alc_private", "iqpilot_private.konn3kt.iqlvbs.vehicle_state")
ButtonType = structs.CarState.ButtonEvent.Type
class CarState(CarStateBase, CarStateExt):
class CarState(CarStateBase, IQCarState):
def __init__(self, CP, CP_IQ):
CarStateBase.__init__(self, CP, CP_IQ)
CarStateExt.__init__(self, CP, CP_IQ)
IQCarState.__init__(self, CP, CP_IQ)
self.can_define = CANDefine(DBC[CP.carFingerprint][Bus.party])
self.shifter_values = self.can_define.dv["DI_systemStatus"]["DI_gear"]
self.summon = False
self.summon_prev = False
self.cruise_enabled_prev = False
self.fsd14_error_logged = False
self.suspected_fsd14 = False
self.suspected_fsd14_clear_frames = 0
self.hands_on_level = 0
self.acc_state_last = 0
self.das_control = None
self.das_body_controls_dat = b""
self._odometer_store = vehicle_state.VehicleOdometerStore(CP, Params())
self.cruise_override = False
def update_summon_state(self, summon_state: str, cruise_enabled: bool):
@@ -136,54 +139,40 @@ class CarState(CarStateBase, CarStateExt):
ret.stockAeb = cp_ap_party.vl["DAS_control"]["DAS_aebEvent"] == 1
# LKAS
# On FSD 14+, ANGLE_CONTROL behavior changed to allow user winddown while actuating.
# FSD switched from using ANGLE_CONTROL to LANE_KEEP_ASSIST to likely keep the old steering override disengage logic.
# LKAS switched from LANE_KEEP_ASSIST to ANGLE_CONTROL to likely allow overriding LKAS events smoothly
lkas_ctrl_type = get_steer_ctrl_type(self.CP.flags, 2)
ret.stockLkas = cp_ap_party.vl["DAS_steeringControl"]["DAS_steeringControlType"] == lkas_ctrl_type # LANE_KEEP_ASSIST
steer_control_type = int(cp_ap_party.vl["DAS_steeringControl"]["DAS_steeringControlType"])
if self.CP.flags & TeslaFlags.LEGACY_DAS_STEERING:
steer_control_type >>= 1 # legacy firmware uses a 2-bit field, one bit up from the 3-bit signal
ret.stockLkas = steer_control_type == 2 # LANE_KEEP_ASSIST
# Stock Autosteer should be disengaged (includes FSD)
# TODO: find for TESLA_MODEL_X and HW2.5 vehicles
if not (self.CP.flags & TeslaFlags.MISSING_DAS_SETTINGS):
ret.invalidLkasSetting = cp_ap_party.vl["DAS_status"]["DAS_autopilotState"] not in (0, 1, 2) # DISABLED, UNAVAILABLE, AVAILABLE
# Because we don't have FSD 14 detection outside of a set of FW, we should check if this FW is accidentally missing from FSD_14_FW
# 1. If in Autosteer or FSD, already caught by invalidLkasSetting
# 2. If in TACC and DAS ever sends ANGLE_CONTROL (1), we can infer it's trying to do LKAS on FSD 14+
# NOTE: Tesla's latest firmware changed ELDA (Emergency Lane Departure Assist) to use ANGLE_CONTROL (1)
# instead of EMERGENCY_LANE_KEEP (3). Exclude ELDA by checking eac_status so it doesn't latch suspected_fsd14.
eac_is_emergency = eac_status == "EMERGENCY_LANE_KEEP"
angle_control = cp_ap_party.vl["DAS_steeringControl"]["DAS_steeringControlType"] == 1 and not eac_is_emergency # ANGLE_CONTROL, excluding ELDA
if not ret.invalidLkasSetting and angle_control and not self.CP.flags & TeslaFlags.FSD_14:
self.suspected_fsd14 = True
self.suspected_fsd14_clear_frames = 0
if self.suspected_fsd14:
ret.invalidLkasSetting = True
if not self.fsd14_error_logged:
carlog.error("FSD 14 detected, but FW not in FSD_14_FW set")
self.fsd14_error_logged = True
# Un-latch if ANGLE_CONTROL has been absent for ~3 s (100 frames @ ~33 Hz).
# This allows re-engagement after transient triggers (e.g. if ELDA slips through on new FW variants).
if not angle_control:
self.suspected_fsd14_clear_frames += 1
if self.suspected_fsd14_clear_frames >= 100:
self.suspected_fsd14 = False
self.suspected_fsd14_clear_frames = 0
else:
self.suspected_fsd14_clear_frames = 0
# Buttons # ToDo: add Gap adjust button
# Messages needed by carcontroller
self.das_control = copy.copy(cp_ap_party.vl["DAS_control"])
CarStateExt.update(self, ret, ret_iq, can_parsers)
# Raw stock DAS_bodyControls bytes (bus 2), used to ride the blinker on the vehicle bus.
# FIXME: gate by FingerPrint
if TESLA_BLINKERS and Bus.cam in can_parsers:
self.das_body_controls_dat = bytes(can_parsers[Bus.cam].dat.get(0x3E9, b""))
IQCarState.update(self, ret, ret_iq, can_parsers)
if ret.odometer > 0.0:
ret.odometer = self._odometer_store.record(ret.odometer) or 0.0
return ret, ret_iq
@staticmethod
def get_can_parsers(CP, CP_IQ):
return {
parsers = {
Bus.party: CANParser(DBC[CP.carFingerprint][Bus.party], [], CANBUS.party),
Bus.ap_party: CANParser(DBC[CP.carFingerprint][Bus.party], [], CANBUS.autopilot_party),
**CarStateExt.get_parser(CP, CP_IQ),
**IQCarState.get_parser(CP, CP_IQ),
}
# Stock DAS_bodyControls from the AP bus (bus 2) for the nav blinker.
if TESLA_BLINKERS and CP_IQ.flags & TeslaFlagsIQ.HAS_VEHICLE_BUS and Bus.adas in DBC[CP.carFingerprint]:
parsers[Bus.cam] = CANParser(DBC[CP.carFingerprint][Bus.adas], [("DAS_bodyControls", 2)], CANBUS.autopilot_party)
return parsers
+9 -10
View File
@@ -2,7 +2,7 @@ from iqdbc.car import Bus, get_safety_config, structs
from iqdbc.car.interfaces import CarInterfaceBase
from iqdbc.car.tesla.carcontroller import CarController
from iqdbc.car.tesla.carstate import CarState
from iqdbc.car.tesla.values import TeslaSafetyFlags, TeslaFlags, CANBUS, CAR, DBC, FSD_14_FW, Ecu
from iqdbc.car.tesla.values import TeslaSafetyFlags, TeslaFlags, CANBUS, CAR, DBC, LEGACY_DAS_STEERING_FW, Ecu
from iqdbc.car.tesla.radar_interface import RadarInterface, RADAR_START_ADDR
from iqdbc.lvbs.car.tesla.values import TeslaFlagsIQ, TeslaSafetyFlagsIQ
@@ -41,14 +41,10 @@ class CarInterface(CarInterfaceBase):
ret.openpilotLongitudinalControl = True
ret.safetyConfigs[0].safetyParam |= TeslaSafetyFlags.LONG_CONTROL.value
ret.vEgoStopping = 0.1
ret.vEgoStarting = 0.1
ret.stoppingDecelRate = 0.3
fsd_14 = any(fw.ecu == Ecu.eps and fw.fwVersion in FSD_14_FW.get(candidate, []) for fw in car_fw)
if fsd_14:
ret.flags |= TeslaFlags.FSD_14.value
ret.safetyConfigs[0].safetyParam |= TeslaSafetyFlags.FSD_14.value
legacy_das = any(fw.ecu == Ecu.eps and fw.fwVersion in LEGACY_DAS_STEERING_FW.get(candidate, []) for fw in car_fw)
if legacy_das:
ret.flags |= TeslaFlags.LEGACY_DAS_STEERING.value
ret.safetyConfigs[0].safetyParam |= TeslaSafetyFlags.LEGACY_DAS_STEERING.value
ret.dashcamOnly = candidate in (CAR.TESLA_MODEL_X,) # dashcam only, pending find invalidLkasSetting signal
@@ -63,7 +59,10 @@ class CarInterface(CarInterfaceBase):
if candidate == CAR.TESLA_MODEL_X:
stock_cp.dashcamOnly = False
if 0x3DF in fingerprint[1]:
# Vehicle-bus messages can be slow enough to miss the initial capture window.
# Accept either the established 0x3DF marker or the absolute odometer frame.
vehicle_bus_seen = any(0x3DF in bus or 0x3B6 in bus for bus in fingerprint.values())
if vehicle_bus_seen:
ret.flags |= TeslaFlagsIQ.HAS_VEHICLE_BUS.value
ret.iqSafetyFlags |= TeslaSafetyFlagsIQ.HAS_VEHICLE_BUS
+32 -13
View File
@@ -3,14 +3,6 @@ from iqdbc.car.tesla.values import CANBUS, CarControllerParams, TeslaFlags
from iqdbc.car import DT_CTRL
def get_steer_ctrl_type(flags: int, ctrl_type: int) -> int:
# Returns the flipped signal value for DAS_steeringControlType on FSD 14
if flags & TeslaFlags.FSD_14:
return {1: 2, 2: 1}.get(ctrl_type, ctrl_type)
else:
return ctrl_type
class TeslaCAN:
def __init__(self, CP, packer):
self.CP = CP
@@ -18,14 +10,15 @@ class TeslaCAN:
self.l_jerk = 0.0
def create_steering_control(self, angle, enabled, control_type):
# On FSD 14+, ANGLE_CONTROL behavior changed to allow user winddown while actuating.
# with openpilot, after overriding w/ ANGLE_CONTROL the wheel snaps back to the original angle abruptly
# so we now use LANE_KEEP_ASSIST to match stock FSD.
# see carstate.py for more details
# control_type comes from torque_blend: ANGLE_CONTROL (1) normally, LANE_KEEP_ASSIST (2) when cooperative steering is enabled
control_type = control_type if enabled else 0
if self.CP.flags & TeslaFlags.LEGACY_DAS_STEERING:
control_type <<= 1 # legacy firmware uses a 2-bit field, one bit up from the 3-bit signal
values = {
"DAS_steeringAngleRequest": -angle,
"DAS_steeringHapticRequest": 0,
"DAS_steeringControlType": get_steer_ctrl_type(self.CP.flags, control_type if enabled else 0),
"DAS_steeringControlType": control_type,
}
return self.packer.make_can_msg("DAS_steeringControl", CANBUS.party, values)
@@ -60,6 +53,32 @@ class TeslaCAN:
return self.packer.make_can_msg("APS_eacMonitor", CANBUS.party, values)
def create_body_controls(self, stock_dat, left_blinker, right_blinker, cancel=False):
# Ride alongside the car's native DAS_bodyControls: copy the raw frame, override only the
# turn-indicator bits, and stamp counter + 1 so our frame supersedes the stock one.
dat = bytearray(stock_dat)
if len(dat) < 8:
dat.extend(b"\x00" * (8 - len(dat)))
if left_blinker or right_blinker:
turn_req = 1 if left_blinker else 2 # DAS_TURN_INDICATOR_LEFT / _RIGHT
dat[1] = (dat[1] & ~0x07) | (turn_req & 0x07)
dat[2] = (dat[2] & ~0x3C) | (1 << 2) # DAS_ACTIVE_NAV_LANE_CHANGE
elif cancel:
dat[1] = (dat[1] & ~0x07) | 0x03 # DAS_TURN_INDICATOR_CANCEL
dat[2] = (dat[2] & ~0x3C) | (4 << 2) # DAS_CANCEL_LANE_CHANGE
counter = (((dat[6] >> 4) + 1) & 0x0F)
dat[6] = (dat[6] & ~0xF0) | (counter << 4)
addr = 0x3E9
checksum = (addr & 0xFF) + ((addr >> 8) & 0xFF)
for i in range(7):
checksum += dat[i]
dat[7] = checksum & 0xFF
return addr, bytes(dat), CANBUS.vehicle
def tesla_checksum(address: int, sig, d: bytearray) -> int:
checksum = (address & 0xFF) + ((address >> 8) & 0xFF)
@@ -4,6 +4,7 @@ from iqdbc.car.tesla.teslacan import TeslaCAN
from iqdbc.car.tesla.radar_interface import RADAR_START_ADDR
from iqdbc.car.tesla.carcontroller import CarController
from iqdbc.car.tesla.values import CAR
from iqdbc.can import CANPacker, CANParser
class TestTeslaFingerprint:
@@ -31,6 +32,19 @@ class TestTeslaCan:
def make_can_msg(self, name, bus, values):
return name, bus, values
def test_vehicle_bus_odometer_decodes_kilometers(self):
packer = CANPacker("tesla_model3_vehicle")
parser = CANParser("tesla_model3_vehicle", [("ID3B6UI_odometer", 1)], 1)
message = packer.make_can_msg("ID3B6UI_odometer", 1, {
"UI_odometer": 29150.377,
"UI_odometerCounter": 1,
"UI_odometerChecksum": 0,
})
parser.update([1_000_000_000, [message]])
assert parser.vl["ID3B6UI_odometer"]["UI_odometer"] == 29150.377
def test_longitudinal_command_does_not_reference_missing_jerk_attr(self):
CP = CarInterface.get_non_essential_params(CAR.TESLA_MODEL_3)
tesla_can = TeslaCAN(CP, self.DummyPacker())
+34 -9
View File
@@ -79,16 +79,41 @@ FW_QUERY_CONFIG = FwQueryConfig(
]
)
# Cars with this EPS FW have FSD 14 and use TeslaFlags.FSD_14
FSD_14_FW = {
# Cars with this EPS FW have a 2-bit DAS_steeringControlType and use TeslaFlags.LEGACY_DAS_STEERING
LEGACY_DAS_STEERING_FW = {
CAR.TESLA_MODEL_3: [
b'TeMYG4_Main_0.0.0 (77),E4HP015.04.5',
b'TeMYG4_Main_0.0.0 (78),E4HP015.05.0',
b'TeM3_E014p10_0.0.0 (16),E014.17.00',
b'TeM3_E014p10_0.0.0 (16),EL014.17.00',
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',
b'TeMYG4_Legacy3Y_0.0.0 (2),E4015.02.0',
b'TeMYG4_Legacy3Y_0.0.0 (5),E4015.03.2',
b'TeMYG4_Legacy3Y_0.0.0 (5),E4L015.03.2',
b'TeMYG4_Main_0.0.0 (59),E4H014.29.0',
b'TeMYG4_Main_0.0.0 (65),E4H015.01.0',
b'TeMYG4_Main_0.0.0 (67),E4H015.02.1',
b'TeMYG4_SingleECU_0.0.0 (33),E4S014.27',
],
CAR.TESLA_MODEL_Y: [
b'TeMYG4_Legacy3Y_0.0.0 (6),Y4003.04.0',
b'TeMYG4_Main_0.0.0 (77),Y4003.05.4',
]
b'TeM3_E014p10_0.0.0 (16),Y002.18.00',
b'TeM3_E014p10_0.0.0 (16),YP002.18.00',
b'TeM3_ES014p11_0.0.0 (16),YS002.17',
b'TeM3_ES014p11_0.0.0 (25),YS002.19.0',
b'TeMYG4_DCS_Update_0.0.0 (13),Y4002.27.1',
b'TeMYG4_DCS_Update_0.0.0 (13),Y4P002.27.1',
b'TeMYG4_DCS_Update_0.0.0 (9),Y4P002.25.0',
b'TeMYG4_Legacy3Y_0.0.0 (2),Y4003.02.0',
b'TeMYG4_Legacy3Y_0.0.0 (2),Y4P003.02.0',
b'TeMYG4_Legacy3Y_0.0.0 (5),Y4003.03.2',
b'TeMYG4_Legacy3Y_0.0.0 (5),Y4P003.03.2',
b'TeMYG4_SingleECU_0.0.0 (28),Y4S002.23.0',
b'TeMYG4_SingleECU_0.0.0 (33),Y4S002.26',
],
CAR.TESLA_MODEL_X: [
b'TeM3_SP_XP002p2_0.0.0 (23),XPR003.6.0',
b'TeM3_SP_XP002p2_0.0.0 (36),XPR003.10.0',
],
}
@@ -139,12 +164,12 @@ class CarControllerParams:
class TeslaSafetyFlags(IntFlag):
LONG_CONTROL = 1
FSD_14 = 2
LEGACY_DAS_STEERING = 2
class TeslaFlags(IntFlag):
LONG_CONTROL = 1
FSD_14 = 2
LEGACY_DAS_STEERING = 2
MISSING_DAS_SETTINGS = 4
+2 -1
View File
@@ -301,7 +301,8 @@ routes = [
CarTestRoute("578742b26807f756|00000010--41ee3e5bec", VOLKSWAGEN.VOLKSWAGEN_JETTA_MK6),
CarTestRoute("58a7d3b707987d65/2021-03-25--17-26-37", VOLKSWAGEN.VOLKSWAGEN_JETTA_MK7),
CarTestRoute("4d134e099430fba2/2021-03-26--00-26-06", VOLKSWAGEN.VOLKSWAGEN_PASSAT_MK8),
CarTestRoute("3cfdec54aa035f3f/2022-07-19--23-45-10", VOLKSWAGEN.VOLKSWAGEN_PASSAT_NMS),
CarTestRoute("b29ee8c5a0a735d1|000000dc--a384e9083e", VOLKSWAGEN.VOLKSWAGEN_PASSAT_NMS, segment=0),
CarTestRoute("0f53129ed44f6920|00000287--3efbddeb96", VOLKSWAGEN.VOLKSWAGEN_PASSAT_NMS, segment=0),
CarTestRoute("0cd0b7f7e31a3853/2021-11-03--19-30-22", VOLKSWAGEN.VOLKSWAGEN_POLO_MK6),
CarTestRoute("064d1816e448f8eb/2022-09-29--15-32-34", VOLKSWAGEN.VOLKSWAGEN_SHARAN_MK2),
CarTestRoute("7d82b2f3a9115f1f/2021-10-21--15-39-42", VOLKSWAGEN.VOLKSWAGEN_TAOS_MK1),
@@ -0,0 +1,133 @@
#!/usr/bin/env python3
"""Real-CAN replay invariant tests for VW torque platforms (PQ / MQB / MLB).
Replays real konn3kt routes through the car interface with openpilot lateral
INACTIVE (latActive=False) and asserts openpilot never transmits an active-steering
HCA command (active status or non-zero torque). Re-transmitting the stock camera's
active HCA while not in control (stock-LKAS forwarding) leaves the EPS faulted for
the whole drive (LH2_Sta_HCA=FAULT) - the regression that bricked steering on a PQ
Passat NMS with a factory LKAS camera. Panda accepts these frames, so only a replay
invariant like this catches it.
Self-contained within iqdbc: routes are resolved through konn3kt's public
/v1/route/<id>/files endpoint (URLs are signed server-side, no auth/token needed).
"""
import json
import os
import urllib.parse
import urllib.request
from collections import Counter
import pytest
from iqdbc.can.parser import CANParser
from iqdbc.car import Bus, structs
from iqdbc.car.can_definitions import CanData
from iqdbc.car.car_helpers import can_fingerprint, interfaces
from iqdbc.car.logreader import LogReader
from iqdbc.car.volkswagen.values import CAR, DBC, VolkswagenFlags
API_HOST = os.environ.get("API_HOST", "https://api-iqlabs.konn3kt.com")
# konn3kt-hosted VW routes. (route_id, segment, platform, label)
# Add MQB routes here as konn3kt-hosted MQB logs become available.
VW_ROUTES = [
("b29ee8c5a0a735d1|000000dc--a384e9083e", 0, CAR.VOLKSWAGEN_PASSAT_NMS, "PQ with stock LKAS camera"),
("0f53129ed44f6920|00000287--3efbddeb96", 0, CAR.VOLKSWAGEN_PASSAT_NMS, "PQ without stock LKAS camera"),
]
# Per-platform HCA message: (address, msg, status signal, torque signal, active-status values)
HCA_INFO = {
"pq": (0xD2, "HCA_1", "HCA_Status", "LM_Offset", (5, 7)),
"mqb": (0x126, "HCA_01", "HCA_01_Status_HCA", "HCA_01_LM_Offset", (5, 6, 7)),
}
def _request_headers() -> dict[str, str]:
# konn3kt's edge rejects the default urllib User-Agent with 403. The routes are public
# (access returns early for public routes), but IQ.Pilot/Cabana tooling conventionally
# sends a Konn3kt user JWT, so include one when available (env or ~/.comma/auth.json).
headers = {"User-Agent": "iqdbc"}
token = os.environ.get("KONN3KT_ACCESS_TOKEN")
if not token:
try:
with open(os.path.expanduser("~/.comma/auth.json")) as f:
token = json.load(f).get("access_token")
except (OSError, ValueError):
token = None
if token:
headers["Authorization"] = f"JWT {token}"
return headers
def _rlog_url(route_id: str, segment: int) -> str:
req = urllib.request.Request(f"{API_HOST}/v1/route/{urllib.parse.quote(route_id, safe='|')}/files",
headers=_request_headers())
with urllib.request.urlopen(req, timeout=30) as f:
files = json.load(f)
for url in files.get("logs", []):
# path looks like /connectdata/<dongle>/<log>/<seg>/rlog.zst
parts = urllib.parse.urlparse(url).path.rstrip("/").split("/")
if len(parts) >= 2 and parts[-2] == str(segment):
return url
raise RuntimeError(f"no rlog for {route_id} segment {segment} (uploaded & public?)")
def _load_can(route_id: str, segment: int):
lr = LogReader(_rlog_url(route_id, segment), only_union_types=True, sort_by_time=True)
return [(m.logMonoTime, [CanData(c.address, c.dat, c.src) for c in m.can]) for m in lr if m.which() == "can"]
@pytest.mark.parametrize("route_id,segment,platform,label", VW_ROUTES)
def test_vw_inactive_steering_invariant(route_id, segment, platform, label):
can_msgs = _load_can(route_id, segment)
assert len(can_msgs) > 1000, f"insufficient CAN data for {label}: {len(can_msgs)} frames"
# fingerprint from a fresh iterator over the (unmutated) frame list
frame_iter = (frames for _, frames in can_msgs)
def can_recv(wait_for_one: bool = False):
return [next(frame_iter, [])]
_, fingerprint = can_fingerprint(can_recv)
CarInterface = interfaces[platform]
CP = CarInterface.get_params(platform, fingerprint, [], False, False, False)
CP_IQ = CarInterface.get_params_iq(CP, platform, fingerprint, [], False, False, False)
if CP.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO):
pytest.skip("invariant covers torque-based VW platforms (PQ/MQB/MLB)")
key = "pq" if CP.flags & VolkswagenFlags.PQ else "mqb"
hca_addr, hca_msg, status_sig, torque_sig, active_status = HCA_INFO[key]
cp = CANParser(DBC[platform][Bus.pt], [(hca_msg, 0)], 0)
CI = CarInterface(CP, CP_IQ)
CC = structs.CarControl().as_reader() # latActive defaults to False
CC_IQ = structs.IQCarControl()
hca_seen = 0
violations = Counter()
for i, (mono, frames) in enumerate(can_msgs):
CI.update([(mono, frames)])
_, sendcan = CI.apply(CC, CC_IQ, mono)
if i < 300: # CarController / CANParser warmup
continue
for addr, dat, bus in sendcan:
if addr != hca_addr or bus != 0:
continue
hca_seen += 1
cp.update([(mono, [(addr, bytes(dat), 0)])])
if int(cp.vl[hca_msg][status_sig]) in active_status:
violations["active_status"] += 1
if abs(cp.vl[hca_msg][torque_sig]) > 0:
violations["nonzero_torque"] += 1
assert hca_seen > 50, f"{label}: no HCA steering messages transmitted to inspect"
assert not len(violations), \
f"{label}: openpilot TX'd active HCA while latActive=False: {dict(violations)}"
if __name__ == "__main__":
import sys
sys.exit(pytest.main([__file__, "-v"]))
+54 -37
View File
@@ -7,9 +7,6 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"NISSAN_LEAF" = [nan, 1.5, nan]
"NISSAN_ROGUE" = [nan, 1.5, nan]
# PSA angle based controllers
"PSA_PEUGEOT_208" = [nan, 2.0, nan]
# New subarus angle based controllers
"SUBARU_FORESTER_2022" = [nan, 3.0, nan]
"SUBARU_OUTBACK_2023" = [nan, 3.0, nan]
@@ -21,14 +18,11 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
# Tesla angle based controllers
"TESLA_MODEL_3" = [nan, 2.5, nan]
"TESLA_MODEL_Y" = [nan, 2.5, nan]
"TESLA_MODEL_X" = [nan, 2.5, nan]
# Guess
"FORD_BRONCO_SPORT_MK1" = [nan, 1.5, nan]
"FORD_ESCAPE_MK4" = [nan, 1.5, nan]
"FORD_ESCAPE_MK4_5" = [nan, 1.5, nan]
"FORD_EXPLORER_MK6" = [nan, 1.5, nan]
"FORD_EXPEDITION_MK4" = [nan, 1.5, nan]
"FORD_F_150_MK14" = [nan, 1.5, nan]
"FORD_FOCUS_MK4" = [nan, 1.5, nan]
"FORD_MAVERICK_MK1" = [nan, 1.5, nan]
@@ -44,26 +38,20 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"RAM_1500_5TH_GEN" = [2.0, 2.0, 0.05]
"RAM_HD_5TH_GEN" = [1.4, 1.4, 0.05]
"SUBARU_OUTBACK" = [2.0, 1.5, 0.2]
"BUICK_BABYENCLAVE" = [1.45, 1.6, 0.2]
"CADILLAC_ESCALADE" = [1.899999976158142, 1.842270016670227, 0.1120000034570694]
"CADILLAC_ESCALADE_ESV_2019" = [1.15, 1.3, 0.2]
"CADILLAC_XT4" = [1.45, 1.6, 0.2]
"CHEVROLET_BOLT_EUV" = [1.0, 2.0, 0.175]
"CHEVROLET_BOLT_EUV" = [2.0, 2.0, 0.05]
"CHEVROLET_MALIBU_CC" = [1.85, 1.85, 0.075]
"CHEVROLET_SILVERADO" = [1.9, 1.9, 0.112]
"CHEVROLET_TRAILBLAZER" = [1.33, 1.9, 0.16]
"CHEVROLET_TRAVERSE" = [1.33, 1.33, 0.18]
"CHEVROLET_EQUINOX" = [2.5, 2.5, 0.05]
"CHEVROLET_VOLT_2019" = [1.4, 1.4, 0.16]
"VOLKSWAGEN_GOLF_MK8" = [nan, 2.5, nan]
"CUPRA_BORN_MK1" = [nan, 2.5, nan]
"VOLKSWAGEN_ID3_MK1" = [nan, 2.5, nan]
"VOLKSWAGEN_ID3_MK2" = [nan, 2.5, nan]
"VOLKSWAGEN_ID4_MK1" = [nan, 2.5, nan]
"VOLKSWAGEN_ID4_MK2" = [nan, 2.5, nan]
"AUDI_Q4_MK1" = [nan, 2.5, nan]
"AUDI_Q4_MK2" = [nan, 2.5, nan]
"SEAT_LEON_MK4" = [nan, 2.5, nan]
"SKODA_ENYAQ_MK1" = [nan, 2.5, nan]
"SKODA_ENYAQ_MK2" = [nan, 2.5, nan]
"CHEVROLET_TRAX" = [1.33, 1.9, 0.16]
"VOLKSWAGEN_ID4_MK1" = [2.0, 2.0, 0.1]
"VOLKSWAGEN_ID4_MK2" = [2.0, 2.0, 0.1]
"VOLKSWAGEN_CADDY_MK3" = [1.2, 1.2, 0.1]
"VOLKSWAGEN_PASSAT_NMS" = [2.5, 2.5, 0.1]
"VOLKSWAGEN_SHARAN_MK2" = [2.5, 2.5, 0.1]
@@ -81,33 +69,22 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"GMC_ACADIA" = [1.6, 1.6, 0.2]
"LEXUS_IS_TSS2" = [2.0, 2.0, 0.1]
"HYUNDAI_KONA_EV_2ND_GEN" = [2.5, 2.5, 0.1]
"HYUNDAI_KONA_HEV_2ND_GEN" = [2.5, 2.5, 0.1]
"HYUNDAI_IONIQ_6" = [2.5, 2.5, 0.005]
"HYUNDAI_IONIQ_9" = [1.75, 1.75, 0.15]
"HYUNDAI_AZERA_7TH_GEN" = [1.8, 1.8, 0.1]
"HYUNDAI_AZERA_6TH_GEN" = [1.8, 1.8, 0.1]
"HYUNDAI_AZERA_HEV_6TH_GEN" = [1.8, 1.8, 0.1]
"KIA_K8_HEV_1ST_GEN" = [2.5, 2.5, 0.1]
"HYUNDAI_CUSTIN_1ST_GEN" = [2.5, 2.5, 0.1]
"LEXUS_GS_F" = [2.5, 2.5, 0.08]
"HYUNDAI_STARIA_4TH_GEN" = [1.8, 2.0, 0.15]
"HYUNDAI_PORTER_II_EV" = [1.8, 2.0, 0.15]
"KIA_RAY_EV" = [1.8, 2.0, 0.15]
"GENESIS_GV70_ELECTRIFIED_1ST_GEN" = [1.9, 1.9, 0.09]
"GENESIS_G80_2ND_GEN_FL" = [2.5819356441497803, 2.5, 0.11244568973779678]
# Note that some Rivians achieve significantly less lateral acceleration than this
"RIVIAN_R1_GEN1" = [2.8, 2.5, 0.07]
"HYUNDAI_NEXO_1ST_GEN" = [2.5, 2.5, 0.1]
"HONDA_ACCORD_11G" = [1.35, 1.35, 0.17]
"HONDA_PILOT_4G" = [1.25, 1.25, 0.21]
"HONDA_PASSPORT_4G" = [1.2, 1.2, 0.16]
"ACURA_MDX_4G_MMR" = [1.25, 1.25, 0.15]
"HONDA_CRV_6G" = [1.3, 1.3, 0.2]
"HONDA_CITY_7G" = [1.2, 1.2, 0.23]
"HONDA_ODYSSEY_5G_MMR" = [0.9, 0.9, 0.2]
"HONDA_NBOX_2G" = [1.2, 1.2, 0.2]
"ACURA_TLX_2G" = [1.2, 1.2, 0.15]
"ACURA_TLX_2G_MMR" = [1.7, 1.7, 0.16]
"PORSCHE_MACAN_MK1" = [2.0, 2.0, 0.2]
"AUDI_Q5_MK1" = [1.8, 1.8, 0.18]
"LEXUS_LS" = [1.35, 1.7, 0.17]
"TOYOTA_RAV4_PRIME" = [1.7, 2.0, 0.14]
"TOYOTA_RAV4_TSS2_2022" = [1.9, 1.9304407208090029, 0.112174]
# Dashcam or fallback configured as ideal car
"MOCK" = [10.0, 10, 0.0]
@@ -116,6 +93,46 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"HONDA_CIVIC_2022" = [2.5, 1.2, 0.15]
"HONDA_HRV_3G" = [2.5, 1.2, 0.2]
# port extensions
"HONDA_CLARITY" = [0.96, 0.4018518740819229, 0.19]
"CHEVROLET_MALIBU_NON_ACC_9TH_GEN" = [1.85, 1.85, 0.075]
# Community
"HYUNDAI_GRANDEUR_IG" = [2.3, 2.3, 0.1]
"HYUNDAI_GRANDEUR_IG_HEV" = [2.3, 2.3, 0.1]
"GENESIS_EQ900" = [2.3, 2.3, 0.1]
"GENESIS_EQ900_L" = [2.3, 2.3, 0.1]
"GENESIS_G90_2019" = [2.3, 2.3, 0.1]
"KIA_MOHAVE" = [2.3, 2.3, 0.1]
"KIA_K5" = [2.3, 2.3, 0.1]
"KIA_K5_HEV" = [2.3, 2.3, 0.1]
"KIA_K5_HEV_2022" = [2.3, 2.3, 0.1]
"KIA_K7" = [2.3, 2.3, 0.1]
"KIA_K7_HEV" = [2.3, 2.3, 0.1]
"KIA_K7_PE" = [2.3, 2.3, 0.1]
"KIA_K7_HEV_PE" = [2.3, 2.3, 0.1]
"KIA_K9" = [2.3, 2.3, 0.1]
"GENESIS_GV70_EV_1ST_GEN" = [2.42, 2.42, 0.1]
"HYUNDAI_NEXO" = [2.7, 2.7, 0.1]
"KIA_EV_SK3" = [2.5, 2.5, 0.1]
"HYUNDAI_CASPER"= [2.5, 2.5, 0.1]
"HYUNDAI_CASPER_EV"= [2.5, 2.5, 0.1]
"HYUNDAI_IONIQ_5_N" = [3.17, 2.71, 0.097]
"HYUNDAI_IONIQ_5_PE" = [1.75, 1.75, 0.15]
# Synced verbatim from comma opendbc torque_data/override.toml — these platforms
# exist upstream and were simply never carried over when iqdbc forked.
ACURA_MDX_4G_MMR = [1.25, 1.25, 0.15]
ACURA_TLX_2G = [1.2, 1.2, 0.15]
ACURA_TLX_2G_MMR = [1.7, 1.7, 0.16]
AUDI_Q5_MK1 = [1.8, 1.8, 0.18]
CUPRA_BORN_MK1 = [nan, 2.5, nan]
FORD_ESCAPE_MK4_5 = [nan, 1.5, nan]
FORD_EXPEDITION_MK4 = [nan, 1.5, nan]
HONDA_ACCORD_11G = [1.35, 1.35, 0.17]
HONDA_CITY_7G = [1.2, 1.2, 0.23]
HONDA_CRV_6G = [1.3, 1.3, 0.2]
HONDA_NBOX_2G = [1.2, 1.2, 0.2]
HONDA_ODYSSEY_5G_MMR = [0.9, 0.9, 0.2]
HONDA_PASSPORT_4G = [1.2, 1.2, 0.16]
HONDA_PILOT_4G = [1.25, 1.25, 0.21]
LEXUS_LS = [1.35, 1.7, 0.17]
PORSCHE_MACAN_MK1 = [2.0, 2.0, 0.2]
PSA_PEUGEOT_208 = [nan, 2.0, nan]
TESLA_MODEL_X = [nan, 2.5, nan]
+10 -1
View File
@@ -37,13 +37,21 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"HYUNDAI_SONATA" = [2.9638737459977467, 2.1259108157250735, 0.07813665616927593]
"HYUNDAI_SONATA_HYBRID" = [2.8990264092395734, 2.061410192222139, 0.0899805488717382]
"HYUNDAI_TUCSON_4TH_GEN" = [2.960174, 2.860284, 0.108745]
"HYUNDAI_SANTAFE_MX5" = [3.2, 3.0, 0.05]
"HYUNDAI_SANTAFE_MX5_HEV" = [3.2, 3.0, 0.05]
"JEEP_GRAND_CHEROKEE_2019" = [2.30972, 1.289689569171081, 0.117048]
"JEEP_GRAND_CHEROKEE" = [2.27116, 1.4057367824262523, 0.11725947414922003]
"KIA_EV6" = [3.2, 2.093457, 0.005]
"KIA_EV6_PE" = [3.2, 2.093457, 0.005]
"KIA_K5_2021" = [2.405339728085138, 1.460032270828705, 0.11650989850813716]
"KIA_K5_DL3_24" = [2.5, 2.5, 0.1]
"KIA_K5_DL3_24_HEV" = [2.5, 2.5, 0.1]
"KIA_NIRO_EV" = [2.9215954981365337, 2.1500583840260044, 0.09236802474810267]
"KIA_SORENTO" = [2.464854685101844, 1.5335274218367956, 0.12056170567599558]
"KIA_STINGER" = [2.7499043387418967, 1.849652021986449, 0.12048334239559202]
"KIA_EV9" = [1.75, 1.75, 0.15]
"KIA_EV3" = [3.2, 2.093457, 0.005]
"KIA_PV5" = [3.2, 2.093457, 0.005]
"LEXUS_ES_TSS2" = [2.0357564999999997, 1.999082295195227, 0.101533]
"LEXUS_NX" = [2.3525924753753613, 1.9731412277641067, 0.15168101064205927]
"LEXUS_NX_TSS2" = [2.4331999786982936, 2.1045680431705414, 0.14099899317761067]
@@ -69,8 +77,9 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"TOYOTA_PRIUS" = [1.60, 1.5023147650693636, 0.151515]
"TOYOTA_PRIUS_TSS2" = [1.972600, 1.9104337425537743, 0.170968]
"TOYOTA_RAV4" = [2.085695074355425, 2.2142832316984733, 0.13339165270103975]
"TOYOTA_RAV4_TSS2" = [1.9557514786720276, 2.087101966779332, 0.12075843289494514]
"TOYOTA_RAV4_TSS2" = [2.279239424615458, 2.087101966779332, 0.13682208413446817]
"TOYOTA_RAV4H" = [1.9796257271652042, 1.7503987331707576, 0.14628860048885406]
"TOYOTA_RAV4_TSS2_2022" = [2.241883248393209, 1.9304407208090029, 0.112174]
"TOYOTA_SIENNA" = [1.689726, 1.3208264576110418, 0.140456]
"TOYOTA_YARIS" = [2.22984, 1.86145, 0.168189]
"VOLKSWAGEN_ARTEON_MK1" = [1.45136518053819, 1.3639364049316804, 0.23806361745695032]
@@ -9,12 +9,12 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"TOYOTA_ALPHARD_TSS2" = "TOYOTA_SIENNA"
"TOYOTA_PRIUS_V" = "TOYOTA_PRIUS"
"TOYOTA_SIENNA_4TH_GEN" = "TOYOTA_RAV4_PRIME"
"TOYOTA_RAV4_PRIME" = "TOYOTA_RAV4_TSS2"
"TOYOTA_SIENNA_4TH_GEN" = "TOYOTA_RAV4_TSS2"
"LEXUS_IS" = "LEXUS_NX"
"LEXUS_CTH" = "LEXUS_NX"
"LEXUS_ES" = "TOYOTA_CAMRY"
"LEXUS_RC" = "LEXUS_NX_TSS2"
"LEXUS_RC_TSS2" = "LEXUS_NX_TSS2"
"LEXUS_LC_TSS2" = "LEXUS_NX_TSS2"
"KIA_OPTIMA_G4" = "HYUNDAI_SONATA"
@@ -25,6 +25,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"KIA_CEED" = "HYUNDAI_SONATA"
"KIA_SELTOS" = "HYUNDAI_SONATA"
"KIA_NIRO_PHEV" = "KIA_NIRO_EV"
"KIA_EV4" = "KIA_EV6"
"KIA_NIRO_PHEV_2022" = "KIA_NIRO_EV"
"KIA_NIRO_HEV_2021" = "KIA_NIRO_EV"
"HYUNDAI_VELOSTER" = "HYUNDAI_SONATA_LF"
@@ -45,12 +46,13 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"GENESIS_G90" = "GENESIS_G70"
"GENESIS_G80" = "GENESIS_G70"
"GENESIS_G70_2020" = "HYUNDAI_SONATA"
"HYUNDAI_SONATA_2024" = "HYUNDAI_SONATA"
"HONDA_FREED" = "HONDA_ODYSSEY"
"HONDA_CRV_EU" = "HONDA_CRV"
"HONDA_CIVIC_BOSCH_DIESEL" = "HONDA_CIVIC_BOSCH"
"HONDA_E" = "HONDA_CIVIC_BOSCH"
"HONDA_ODYSSEY_TWN" = "HONDA_ODYSSEY"
"HONDA_ODYSSEY_CHN" = "HONDA_ODYSSEY"
"BUICK_LACROSSE" = "CHEVROLET_VOLT"
"BUICK_REGAL" = "CHEVROLET_VOLT"
@@ -58,6 +60,18 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"CADILLAC_ATS" = "CHEVROLET_VOLT"
"CHEVROLET_MALIBU" = "CHEVROLET_VOLT"
"HOLDEN_ASTRA" = "CHEVROLET_VOLT"
"CHEVROLET_VOLT_CC" = "CHEVROLET_VOLT"
"CHEVROLET_BOLT_CC" = "CHEVROLET_BOLT_EUV"
"CHEVROLET_BOLT_2017" = "CHEVROLET_BOLT_EUV"
"CHEVROLET_BOLT_2018" = "CHEVROLET_BOLT_EUV"
"CHEVROLET_EQUINOX_CC" = "CHEVROLET_EQUINOX"
"CHEVROLET_SUBURBAN" = "CHEVROLET_SILVERADO"
"CHEVROLET_SUBURBAN_CC" = "CHEVROLET_SILVERADO"
"CHEVROLET_TRAX_2024" = "CHEVROLET_VOLT"
"CADILLAC_CT6_CC" = "CHEVROLET_VOLT"
"CADILLAC_CT6_ACC" = "CHEVROLET_VOLT"
"CHEVROLET_TRAILBLAZER_CC" = "CHEVROLET_TRAILBLAZER"
"CADILLAC_XT5_CC" = "GMC_ACADIA"
"SKODA_FABIA_MK4" = "VOLKSWAGEN_GOLF_MK7"
"SKODA_OCTAVIA_MK3" = "SKODA_SUPERB_MK3"
@@ -74,8 +88,6 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"VOLKSWAGEN_POLO_MK6" = "VOLKSWAGEN_GOLF_MK7"
"SEAT_ATECA_MK1" = "VOLKSWAGEN_GOLF_MK7"
"VOLKSWAGEN_JETTA_MK6" = "VOLKSWAGEN_PASSAT_NMS"
"VOLKSWAGEN_PASSAT_MK7" = "VOLKSWAGEN_PASSAT_NMS"
"VOLKSWAGEN_PASSAT_NMS_PLUS" = "VOLKSWAGEN_PASSAT_NMS"
"SUBARU_CROSSTREK_HYBRID" = "SUBARU_IMPREZA_2020"
"SUBARU_FORESTER_HYBRID" = "SUBARU_IMPREZA_2020"
@@ -88,21 +100,34 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"SUBARU_LEGACY_PREGLOBAL" = "SUBARU_IMPREZA"
"SUBARU_ASCENT" = "SUBARU_FORESTER"
# port extensions
"KIA_CEED_PHEV_2022_NON_SCC" = "HYUNDAI_SONATA"
"HYUNDAI_KONA_EV_NON_SCC" = "HYUNDAI_KONA_EV"
"KIA_FORTE_2019_NON_SCC" = "HYUNDAI_SONATA"
"KIA_FORTE_2021_NON_SCC" = "HYUNDAI_SONATA"
"KIA_SELTOS_2023_NON_SCC" = "HYUNDAI_SONATA"
"HYUNDAI_KONA_NON_SCC" = "HYUNDAI_KONA_EV"
"HYUNDAI_ELANTRA_2022_NON_SCC" = "HYUNDAI_ELANTRA_2021"
"GENESIS_G70_2021_NON_SCC" = "HYUNDAI_SONATA"
"HYUNDAI_BAYON_1ST_GEN_NON_SCC" = "HYUNDAI_SONATA"
"CHEVROLET_BOLT_NON_ACC_2ND_GEN" = "CHEVROLET_BOLT_EUV"
# IQ.Pilot GM Non-ACC ports borrow their base platform's lateral tune
"CHEVROLET_BOLT_NON_ACC" = "CHEVROLET_BOLT_EUV"
"CHEVROLET_BOLT_NON_ACC_1ST_GEN" = "CHEVROLET_BOLT_EUV"
"CHEVROLET_BOLT_NON_ACC_2ND_GEN" = "CHEVROLET_BOLT_EUV"
"CHEVROLET_EQUINOX_NON_ACC_3RD_GEN" = "CHEVROLET_EQUINOX"
"CHEVROLET_SUBURBAN_NON_ACC_11TH_GEN" = "CHEVROLET_SILVERADO"
"CADILLAC_CT6_NON_ACC_1ST_GEN" = "CHEVROLET_VOLT"
"CHEVROLET_TRAILBLAZER_NON_ACC_2ND_GEN" = "CHEVROLET_TRAILBLAZER"
"CADILLAC_XT5_NON_ACC_1ST_GEN" = "GMC_ACADIA"
"CHEVROLET_MALIBU_NON_ACC_9TH_GEN" = "CHEVROLET_VOLT"
"CADILLAC_XT5_NON_ACC_1ST_GEN" = "CADILLAC_XT4"
# Synced verbatim from comma opendbc torque_data/substitute.toml
"HONDA_ODYSSEY_TWN" = "HONDA_ODYSSEY"
"LEXUS_RC_TSS2" = "LEXUS_NX_TSS2"
# Platform-sibling substitutes: comma has never shipped torque data for these,
# so rather than invent lateral-accel figures they borrow the tune of the closest
# platform twin. REVIEW: replace with measured values when available.
"SEAT_ALHAMBRA_MK1" = "VOLKSWAGEN_SHARAN_MK2"
"VOLKSWAGEN_PASSAT_NMS_PLUS" = "VOLKSWAGEN_PASSAT_NMS"
"VOLKSWAGEN_ID3_MK1" = "VOLKSWAGEN_ID4_MK1"
"VOLKSWAGEN_ID3_MK2" = "VOLKSWAGEN_ID4_MK2"
"SKODA_ENYAQ_MK1" = "VOLKSWAGEN_ID4_MK1"
"SKODA_ENYAQ_MK2" = "VOLKSWAGEN_ID4_MK2"
"AUDI_Q4_MK1" = "VOLKSWAGEN_ID4_MK1"
"AUDI_Q4_MK2" = "VOLKSWAGEN_ID4_MK2"
"VOLKSWAGEN_GOLF_MK8" = "VOLKSWAGEN_GOLF_MK7"
"SEAT_LEON_MK4" = "VOLKSWAGEN_GOLF_MK7"
"VOLKSWAGEN_PASSAT_MK7" = "VOLKSWAGEN_PASSAT_NMS"
"VOLKSWAGEN_PASSAT_B7" = "VOLKSWAGEN_PASSAT_NMS"
"HONDA_CLARITY" = "HONDA_ACCORD"
+4 -1
View File
@@ -260,6 +260,7 @@ class CarController(CarControllerBase, GasInterceptorCarController):
freeze_integrator=actuators.longControlState != LongCtrlState.pid)
else:
self.long_pid.reset()
pcm_accel_cmd = 0.
# Along with rate limiting positive jerk above, this greatly improves gas response time
# Consider the net acceleration request that the PCM should be applying (pitch included)
@@ -269,7 +270,9 @@ class CarController(CarControllerBase, GasInterceptorCarController):
elif net_acceleration_request_min > 0.3:
self.permit_braking = False
pcm_accel_cmd = pcm_accel_cmd if self.CP.carFingerprint in TSS2_CAR else actuators.accel
sdsu_tssp_long_active = bool(self.CP_IQ.flags & ToyotaFlagsIQ.SMART_DSU) and \
(self.CP.carFingerprint not in TSS2_CAR) and CC.longActive
pcm_accel_cmd = actuators.accel if sdsu_tssp_long_active else pcm_accel_cmd
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
+4 -4
View File
@@ -8,7 +8,7 @@ from iqdbc.car.interfaces import CarStateBase
from iqdbc.car.toyota.values import ToyotaFlags, CAR, DBC, STEER_THRESHOLD, NO_STOP_TIMER_CAR, \
TSS2_CAR, RADAR_ACC_CAR, EPS_SCALE, UNSUPPORTED_DSU_CAR, \
SECOC_CAR
from iqdbc.lvbs.car.toyota.carstate_ext import CarStateExt
from iqdbc.lvbs.car.toyota.iq_carstate import IQCarState
from iqdbc.lvbs.car.toyota.values import ToyotaFlagsIQ
ButtonType = structs.CarState.ButtonEvent.Type
@@ -26,10 +26,10 @@ TEMP_STEER_FAULTS = (0, 9, 11, 21, 25)
PERM_STEER_FAULTS = (3, 17)
class CarState(CarStateBase, CarStateExt):
class CarState(CarStateBase, IQCarState):
def __init__(self, CP, CP_IQ):
CarStateBase.__init__(self, CP, CP_IQ)
CarStateExt.__init__(self, CP, CP_IQ)
IQCarState.__init__(self, CP, CP_IQ)
can_define = CANDefine(DBC[CP.carFingerprint][Bus.pt])
self.eps_torque_scale = EPS_SCALE[CP.carFingerprint] / 100.
self.cluster_speed_hyst_gap = CV.KPH_TO_MS / 2.
@@ -210,7 +210,7 @@ class CarState(CarStateBase, CarStateExt):
ret.buttonEvents = buttonEvents
CarStateExt.update(self, ret, ret_iq, can_parsers)
IQCarState.update(self, ret, ret_iq, can_parsers)
return ret, ret_iq
+2 -2
View File
@@ -2,8 +2,8 @@
from iqdbc.car.structs import CarParams
from iqdbc.car.toyota.values import CAR
from iqdbc.lvbs.car.fingerprints_ext import extend_fw_versions
from iqdbc.lvbs.car.toyota.fingerprints_ext import FW_VERSIONS_EXT
from iqdbc.lvbs.car.iq_fingerprints import extend_fw_versions
from iqdbc.lvbs.car.toyota.iq_fingerprints import FW_VERSIONS_EXT
Ecu = CarParams.Ecu
-8
View File
@@ -36,7 +36,6 @@ class CarInterface(CarInterfaceBase):
if ret.flags & ToyotaFlags.SECOC.value:
ret.secOcRequired = True
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.SECOC.value
ret.dashcamOnly = is_release
if candidate in ANGLE_CONTROL_CAR:
ret.steerControlType = SteerControlType.angle
@@ -119,10 +118,6 @@ class CarInterface(CarInterfaceBase):
if candidate in TSS2_CAR:
ret.flags |= ToyotaFlags.RAISED_ACCEL_LIMIT.value
ret.vEgoStopping = 0.25
ret.vEgoStarting = 0.25
ret.stoppingDecelRate = 0.3 # reach stopping target smoothly
# Hybrids have much quicker longitudinal actuator response
if ret.flags & ToyotaFlags.HYBRID.value:
ret.longitudinalActuatorDelay = 0.05
@@ -132,9 +127,6 @@ class CarInterface(CarInterfaceBase):
@staticmethod
def _get_params_iq(stock_cp: structs.CarParams, ret: structs.IQCarParams, candidate, fingerprint: dict[int, dict[int, int]],
car_fw: list[structs.CarParams.CarFw], alpha_long: bool, is_release_iq: bool, docs: bool) -> structs.IQCarParams:
if stock_cp.flags & ToyotaFlags.SECOC.value and stock_cp.fingerprintSource == structs.CarParams.FingerprintSource.fixed:
stock_cp.dashcamOnly = False
if candidate in UNSUPPORTED_DSU_CAR:
ret.iqSafetyFlags |= ToyotaSafetyFlagsIQ.UNSUPPORTED_DSU
@@ -4,26 +4,20 @@ from iqdbc.car.toyota.values import CAR, ToyotaFlags
from iqdbc.lvbs.car.toyota.values import ToyotaFlagsIQ
def test_forced_secoc_toyota_clears_dashcam_mode():
def test_secoc_toyota_not_dashcam_on_release():
# SecOC Toyotas are controllable regardless of branch or fingerprint source.
candidate = CAR.TOYOTA_SIENNA_4TH_GEN
# is_release=True (release branch) must not force dashcam mode
cp = CarInterface.get_params(candidate, gen_empty_fingerprint(), [], False, True, False)
assert cp.dashcamOnly
cp.fingerprintSource = structs.CarParams.FingerprintSource.fixed
CarInterface.get_params_iq(cp, candidate, gen_empty_fingerprint(), [], False, True, False)
assert cp.flags & ToyotaFlags.SECOC.value
assert cp.secOcRequired
assert not cp.dashcamOnly
def test_automatic_secoc_toyota_release_stays_dashcam():
candidate = CAR.TOYOTA_SIENNA_4TH_GEN
cp = CarInterface.get_params(candidate, gen_empty_fingerprint(), [], False, True, False)
assert cp.dashcamOnly
# fw/can-sourced fingerprint (not forced) must also stay controllable
cp.fingerprintSource = structs.CarParams.FingerprintSource.can
CarInterface.get_params_iq(cp, candidate, gen_empty_fingerprint(), [], False, True, False)
assert cp.dashcamOnly
assert not cp.dashcamOnly
def test_smart_dsu_clears_disable_radar_on_radar_acc_toyota():
+125 -33
View File
@@ -14,10 +14,16 @@ from iqdbc.car.common.conversions import Conversions as CV
from iqdbc.car.common.numpy_fast import clip, interp
from iqdbc.car.interfaces import CarControllerBase
from iqdbc.car.volkswagen import mlbcan, mqbcan, pqcan, mebcan
from iqdbc.car.volkswagen.values import CanBus, CarControllerParams, VolkswagenFlags, VolkswagenFlagsIQ
from iqdbc.car.volkswagen.pq_radar_handler import PQRadarHandler
from iqdbc.car.volkswagen.values import (
CanBus, CarControllerParams, MQB_A0_CARS, VolkswagenFlags, VolkswagenFlagsIQ, apply_pq_stopping_accel,
)
from iqdbc.car.volkswagen.mebutils import LongControlJerk, LongControlLimit, LatControlCurvature
from iqdbc.car.vehicle_model import VehicleModel
from openpilot.iqpilot.konn3kt.iqlvbs import alc as iq_lvbs_alc
from openpilot.system.proprietary_runtime._verified_import import import_verified_module
iq_lvbs_alc = import_verified_module("iqpilot_alc_private", "iqpilot_private.konn3kt.iqlvbs.alc")
iq_lvbs_commander = import_verified_module("iqpilot_commander_private", "iqpilot_private.konn3kt.iqlvbs.iqlvbs_commander")
iqpilot_path = os.path.join(os.path.dirname(__file__), '..', '..', '..')
sys.path.insert(0, iqpilot_path)
@@ -27,9 +33,19 @@ except ImportError:
pass
VisualAlert = structs.CarControl.HUDControl.VisualAlert
AudibleAlert = structs.CarControl.HUDControl.AudibleAlert
LongCtrlState = structs.CarControl.Actuators.LongControlState
def dVisual(CCS, CS):
if CCS == mqbcan:
decelV = CS.tsk_verzoeg_anf
elif CCS == pqcan:
decelV = CS.br8_acc_anf
else:
decelV = False
return decelV
class MQBStandstillManager:
BRAKE_TORQUE_RAMP_RATE = 2000.0 # Nm/s
ASSUMED_WHEEL_RADIUS = 0.328 # m, typical tire rolling radius
@@ -176,7 +192,6 @@ class MQBStandstillManager:
self.prev_accel = accel
return long_active, accel, stopping, starting, esp_starting_override, esp_stopping_override
class CarController(CarControllerBase):
def __init__(self, dbc_names, CP, CP_IQ):
super().__init__(dbc_names, CP, CP_IQ)
@@ -185,8 +200,11 @@ class CarController(CarControllerBase):
self.CAN = CanBus(CP)
self.packer_pt = CANPacker(dbc_names[Bus.pt])
self._pt_tx_bus = self.CAN.pt
if CP.flags & VolkswagenFlags.PQ:
self.CCS = pqcan
if CP.flags & VolkswagenFlagsIQ.IQ_PQ_LOWLINE:
self._pt_tx_bus = self.CAN.aux
elif CP.flags & VolkswagenFlags.MLB:
self.CCS = mlbcan
elif CP.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO):
@@ -211,13 +229,16 @@ class CarController(CarControllerBase):
self.long_jerk_control = LongControlJerk(dt=(DT_CTRL * self.CCP.ACC_CONTROL_STEP)) if self.CP.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO) else None
self.long_limit_control = LongControlLimit(dt=(DT_CTRL * self.CCP.ACC_CONTROL_STEP)) if self.CP.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO) else None
self.gra_acc_counter_last = None
self.motor3_frame_last = None
self.motor3_was_stopping = False
self.motor3_resuming = False
self.sng_handoff_active = False
self.acc_counter_seeded = False
self.klr_counter_last = None
self.eps_timer_soft_disable_alert = False
self.hca_frame_timer_running = 0
self.hca_frame_same_torque = 0
self.accel_last = 0
self.accel_diff = 0
self.long_deviation = 0
self.long_jerklimit = 0
self.HCA_Status = 3
@@ -231,19 +252,43 @@ class CarController(CarControllerBase):
self.radar_disabled_warning_timer = 0
# Check once at init whether the DBC includes MEB_AWV_01 (AEB HUD for radar-disabled camera harness cars)
self._has_aeb_hud_msg = "MEB_AWV_01" in self.packer_pt.dbc.name_to_msg
self.eps_timer_workaround = bool(CP.flags & VolkswagenFlags.MLB)
self.eps_timer_workaround = bool(CP.flags & (VolkswagenFlags.MLB | VolkswagenFlagsIQ.IQ_PQ_TIMEBOMB))
self._pq_patch_checked = not bool(CP.flags & VolkswagenFlags.PQ) or self.eps_timer_workaround
self.hca_frame_timer_resetting = 0
self.hca_frame_low_torque = 0
self.long_override_counter = 0
self.long_disabled_counter = 0
self.standstill_manager = MQBStandstillManager(CP.mass, self.CCP.ACCEL_MIN) if self.CCS == mqbcan else None
self.radar_handler = PQRadarHandler(self.CAN) if self.CCS is pqcan else None
self.blend_stock_radar = False
self.unavailable = False
self.unavailable_hold = 0
self.VM = VehicleModel(CP)
self.is_mqb_a0 = self._is_mqb_a0_car(CP.carFingerprint)
self.LateralController = (
LatControlCurvature(self.CCP.CURVATURE_PID, self.CCP.CURVATURE_LIMITS.CURVATURE_MAX, 1 / (DT_CTRL * self.CCP.STEER_STEP))
if (CP.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO))
else None
)
@staticmethod
def _is_mqb_a0_car(candidate) -> bool:
return candidate in MQB_A0_CARS
def _get_mqb_steering_torque_scale(self, v_ego: float, enabled: bool) -> float:
if enabled and self.CCS == mqbcan:
return float(np.interp(v_ego, [0.4, 3.5, 4.0], [0.8, 0.95, 1.0]))
return 1.0
def _should_spam_mqb_a0_resume(self, CS, enabled: bool) -> bool:
return bool(
enabled and
self.is_mqb_a0 and
self.CCS == mqbcan and
CS.out.standstill and
self.frame % 50 < 15
)
def update(self, CC, CC_IQ, CS, now_nanos):
actuators = CC.actuators
hud_control = CC.hudControl
@@ -252,10 +297,23 @@ class CarController(CarControllerBase):
apply_torque = 0
pqhca5or7Toggle = self._params.get_bool("pqhca5or7Toggle")
eBrakeActive = self._params.get_bool("eBrakeActive")
iq_mqb_steering_lockout = self._params.get_bool("iqMqbSteeringLockout")
iq_mqb_acc_resume = self._params.get_bool("iqMqbAccResume")
self.blend_stock_radar = self._params.get_bool("IQDynamicBlendStockRadar")
if not self._pq_patch_checked:
self._pq_patch_checked = True
self.eps_timer_workaround = not self._params.get_bool("VwPqEpsPatched")
blend_active = bool(self.blend_stock_radar and self.radar_handler is not None and self.CP.openpilotLongitudinalControl)
AngleLateralControl = iq_lvbs_alc.angle_lateral_control_enabled(self, CS)
self.entering = CS.vw_iq_lvbs_alc_entering
self.active = CS.vw_iq_lvbs_alc_active
if hud_control.audibleAlert == AudibleAlert.refuse:
self.unavailable_hold = self.CCP.IQ_PQ_UNAVAILABLE_HUD_FRAMES
else:
self.unavailable_hold = max(0, self.unavailable_hold - 1)
self.unavailable = self.unavailable_hold > 0
CS.force_rhd_for_bsm = getattr(CC, "forceRHDForBSM", False)
CS.enable_predicative_speed_limit = getattr(CC.cruiseControl, "speedLimitPredicative", False)
CS.enable_pred_react_to_speed_limits = getattr(CC.cruiseControl, "speedLimitPredReactToSL", False)
@@ -296,7 +354,8 @@ class CarController(CarControllerBase):
self.steering_power_last = steering_power
else:
if CC.latActive and not AngleLateralControl:
new_torque = int(round(actuators.torque * self.CCP.STEER_MAX))
torque_scale = self._get_mqb_steering_torque_scale(CS.out.vEgo, iq_mqb_steering_lockout)
new_torque = int(round(actuators.torque * self.CCP.STEER_MAX * torque_scale))
apply_torque = apply_driver_steer_torque_limits(new_torque, self.apply_torque_last, CS.out.steeringTorque, self.CCP)
self.hca_frame_timer_running += self.CCP.STEER_STEP
if self.apply_torque_last == apply_torque:
@@ -341,10 +400,10 @@ class CarController(CarControllerBase):
self.eps_timer_soft_disable_alert = self.hca_frame_timer_running > self.CCP.STEER_TIME_ALERT / DT_CTRL
self.apply_torque_last = apply_torque
if not (AngleLateralControl and self.CCS in (mqbcan, pqcan)):
can_sends.append(self.CCS.create_hca_steering_control(self.packer_pt, self.CAN.pt, output_torque, self.HCA_Status))
if not (AngleLateralControl and self.CCS in (mqbcan, pqcan, mlbcan)):
can_sends.append(self.CCS.create_hca_steering_control(self.packer_pt, self._pt_tx_bus, output_torque, self.HCA_Status))
if self.CP.flags & VolkswagenFlags.STOCK_HCA_PRESENT:
if self.CP.flags & VolkswagenFlags.STOCK_HCA_PRESENT and self.CCS == mqbcan:
ea_simulated_torque = float(np.clip(apply_torque * 2, -self.CCP.STEER_MAX, self.CCP.STEER_MAX))
if abs(CS.out.steeringTorque) > abs(ea_simulated_torque):
ea_simulated_torque = CS.out.steeringTorque
@@ -379,7 +438,7 @@ class CarController(CarControllerBase):
if self.frame % self.CCP.ACC_CONTROL_STEP == 0 and self.CP.openpilotLongitudinalControl and not CS.out.radarDisableFailed:
stopping = actuators.longControlState == LongCtrlState.stopping
if self.CP.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO):
starting = actuators.longControlState == LongCtrlState.starting and CS.out.vEgo <= self.CP.vEgoStarting
starting = actuators.longControlState == LongCtrlState.pid and (CS.esp_hold_confirmation or CS.out.vEgo < 0.25)
accel = float(np.clip(actuators.accel, self.CCP.ACCEL_MIN, self.CCP.ACCEL_MAX) if CC.enabled else 0)
long_override = CC.cruiseControl.override or CS.out.gasPressed
@@ -409,7 +468,7 @@ class CarController(CarControllerBase):
self.accel_last = accel
else:
stopping = actuators.longControlState == LongCtrlState.stopping
starting = actuators.longControlState == LongCtrlState.pid and (CS.esp_hold_confirmation or CS.out.vEgo < self.CP.vEgoStopping)
starting = actuators.longControlState == LongCtrlState.pid and (CS.esp_hold_confirmation or CS.out.vEgo < 0.25)
long_active = CC.longActive
accel = actuators.accel
esp_starting_override = None
@@ -424,10 +483,16 @@ class CarController(CarControllerBase):
acc_control = self.CCS.acc_control_value(CS.out.cruiseState.available, long_active, CC.cruiseControl.override, CS.out.accFaulted)
accel = float(np.clip(accel, self.CCP.ACCEL_MIN, self.CCP.ACCEL_MAX) if (long_active or CC.cruiseControl.override) else 0)
self.accel_diff = (0.0019 * (accel - self.accel_last)) + (1 - 0.0019) * self.accel_diff
self.long_jerklimit = 3.0 # AendGrad (0.01 * (np.clip(abs(accel), 0.7, 2))) + (1 - 0.01) * self.long_jerklimit
self.long_deviation = 0.2 # RegelAbw np.interp(abs(accel - self.accel_diff), [0, 0.3, 1.0], [0.02, 0.04, 0.08])
self.long_jerklimit = float(interp(accel, [-1.5, -0.3, 0.05], [2.0, 0.52, 0.30]))
self.long_deviation = float(interp(accel, [-0.3, 0.1], [0.0, 0.20]))
self.accel_last = accel
if blend_active and getattr(CC_IQ, "useRadarAccel", False) and CS.acc_radar_sta_adr == 1 and not self.radar_handler.failed:
accel = float(np.clip(CS.acc_radar_sollbeschl, self.CCP.ACCEL_MIN, self.CCP.ACCEL_MAX))
self.long_jerklimit = CS.acc_radar_aendgrad
self.long_deviation = CS.acc_radar_regelabw
self.accel_last = accel
if self.CCS == mqbcan:
can_sends.extend(self.CCS.create_acc_accel_control(
self.packer_pt, self.CAN.pt, CS.acc_type, accel, acc_control, stopping, starting, CS.esp_hold_confirmation,
@@ -435,10 +500,19 @@ class CarController(CarControllerBase):
esp_starting_override=esp_starting_override, esp_stopping_override=esp_stopping_override,
))
else:
can_sends.extend(self.CCS.create_acc_accel_control(
self.packer_pt, self.CAN.pt, CS.acc_type, accel, acc_control, stopping, starting, CS.esp_hold_confirmation,
self.long_deviation, self.long_jerklimit, eBrakeActive,
))
accel = apply_pq_stopping_accel(self.CP.carFingerprint, accel, stopping)
sng_ecd_enabled = bool(self.CP.flags & VolkswagenFlagsIQ.IQ_PQ_SNG_ECD)
low_speed = CS.out.vEgo <= self.CCP.SNG_HANDOFF_SPEED
if sng_ecd_enabled and long_active and low_speed and accel <= 0.05 and not starting:
self.sng_handoff_active = True
else:
self.sng_handoff_active = False
sng_decel_req = float(np.clip(accel, self.CCP.ACCEL_MIN, self.CCP.SNG_HOLD_DECEL_MAX)) if self.sng_handoff_active else 0.0
can_sends.extend(self.CCS.create_acc_accel_control(self.packer_pt, self.CAN.pt, CS.acc_type, accel, acc_control, stopping and not self.motor3_resuming, starting, CS.esp_hold_confirmation, self.long_deviation, self.long_jerklimit, eBrakeActive, sng_active=self.sng_handoff_active))
if sng_ecd_enabled:
can_sends.append(self.CCS.create_sng_handoff_control(self.packer_pt, self.CAN.aux, self.sng_handoff_active, sng_decel_req))
if (self.CP.flags & VolkswagenFlags.DISABLE_RADAR) and self.CP.openpilotLongitudinalControl and not CS.out.radarDisableFailed:
if self.CP.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO):
@@ -468,15 +542,16 @@ class CarController(CarControllerBase):
can_sends.append(self.CCS.create_lka_hud_control(self.packer_pt, self.CAN.pt, CS.ldw_stock_values, CC.latActive, CS.out.steeringPressed,
hud_alert, hud_control, sound_alert))
else:
can_sends.append(self.CCS.create_lka_hud_control(self.packer_pt, self.CAN.pt, CS.ldw_stock_values, CC.latActive, steering_pressed_hud,
can_sends.append(self.CCS.create_lka_hud_control(self.packer_pt, self._pt_tx_bus, CS.ldw_stock_values, CC.latActive, steering_pressed_hud,
hud_alert, hud_control, self.entering, self.CCS is pqcan and AngleLateralControl, self.active))
if hud_control.leadDistanceBars != self.lead_distance_bars_last:
self.distance_bar_frame = self.frame
if self.frame % self.CCP.ACC_HUD_STEP == 0 and self.CP.openpilotLongitudinalControl and not CS.out.radarDisableFailed:
if self.frame % self.CCP.ACC_HUD_STEP == 0 and self.CP.openpilotLongitudinalControl:
fcw_alert = hud_control.visualAlert == VisualAlert.fcw
d_unresponsive = hud_control.driverUnresponsive
if self.CP.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO):
fcw_alert = hud_control.visualAlert == VisualAlert.fcw
show_distance_bars = self.frame - self.distance_bar_frame < 400
gap = max(8, CS.out.vEgo * hud_control.leadFollowTime)
distance = max(8, hud_control.leadDistance) if hud_control.leadDistance != 0 else 0
@@ -498,23 +573,29 @@ class CarController(CarControllerBase):
CS.esp_hold_confirmation, distance, gap, fcw_alert, acc_hud_event, speed_limit))
else:
leadDistance = min(8, hud_control.leadDistance) if hud_control.leadDistance != 0 else 0
fcw_alert = hud_control.visualAlert == VisualAlert.fcw
self.leadDistanceBars = min(3, hud_control.leadDistanceBars)
acc_hud_status = self.CCS.acc_hud_status_value(CS.out.cruiseState.available, CS.out.accFaulted, CC.longActive, CC.cruiseControl.override)
set_speed = hud_control.setSpeed * CV.MS_TO_KPH
can_sends.append(self.CCS.create_acc_hud_control(self.packer_pt, self.CAN.pt, acc_hud_status, set_speed,
leadDistance, self.leadDistanceBars, fcw_alert, hud_control.leadVisible))
decel = dVisual(self.CCS, CS)
can_sends.append(self.CCS.create_acc_hud_control(self.packer_pt, self.CAN.pt, acc_hud_status, set_speed, leadDistance, self.leadDistanceBars, fcw_alert, hud_control.leadVisible, self.unavailable, decel, d_unresponsive))
if self.CP.flags & VolkswagenFlags.PQ:
if self.frame % 2 == 0:
self.blinkerActive = CS.leftBlinkerUpdate or CS.rightBlinkerUpdate
leftBlinker = CC.leftBlinker if not self.blinkerActive else False
rightBlinker = CC.rightBlinker if not self.blinkerActive else False
can_sends.append(self.CCS.create_blinker_control(self.packer_pt, self.CAN.pt, leftBlinker, rightBlinker))
iq_lvbs_commander.update_turn_signals(self, CC, CS, can_sends)
if self.CP.openpilotLongitudinalControl and (self.CP.flags & VolkswagenFlags.PQ):
if self.frame % 2 == 0:
if blend_active:
can_sends.extend(self.radar_handler.update(
self.packer_pt, self.frame, CS,
blend_active=True,
engage_req=getattr(CC_IQ, "radarEngageReq", False),
cancel_req=getattr(CC_IQ, "radarCancelReq", False),
set_speed_kph=getattr(CC_IQ, "radarSetSpeedKph", 0.0),
gap_bars=getattr(CC_IQ, "radarGapBars", 0),
v_ego=CS.out.vEgo,
))
elif self.frame % 2 == 0:
can_sends.append(self.CCS.filter_motor2(self.packer_pt, self.CAN.ext, CS.motor2_stock))
can_sends.append(self.CCS.filter_motor5(self.packer_pt, self.CAN.ext, CS.motor5_stock))
gra_send_ready = self.CP.pcmCruise and CS.gra_stock_values["COUNTER"] != self.gra_acc_counter_last
if self.CP.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO):
@@ -526,15 +607,25 @@ class CarController(CarControllerBase):
stock_cancel_pressed = bool(CS.gra_stock_values["GRA_Abbrechen"])
cancel_cmd = stock_cancel_pressed or CC.cruiseControl.cancel
if gra_send_ready and (cancel_cmd or CC.cruiseControl.resume):
resume_cmd = CC.cruiseControl.resume or self._should_spam_mqb_a0_resume(CS, iq_mqb_acc_resume)
if gra_send_ready and (cancel_cmd or resume_cmd):
bus_send = self.CAN.aux if self.CP.flags & VolkswagenFlags.PQ else self.CAN.ext
can_sends.append(self.CCS.create_acc_buttons_control(self.packer_pt, bus_send, CS.gra_stock_values,
cancel=cancel_cmd, resume=CC.cruiseControl.resume))
cancel=cancel_cmd, resume=resume_cmd))
if self.CP.openpilotLongitudinalControl and self.CCS == pqcan:
if self.CP.openpilotLongitudinalControl and self.CCS == pqcan and not blend_active:
if self.frame % 3:
can_sends.append(self.CCS.create_gra_neu(self.packer_pt, self.CAN.ext, CS.gra_stock_values, CC.longActive))
if self.CP.flags & VolkswagenFlagsIQ.IQ_PQ_ACC_FTS_EPB:
is_stopping = actuators.longControlState == LongCtrlState.stopping
if CS.out.vEgo > 0.5 or not CC.longActive:
self.motor3_resuming = False
elif (self.motor3_was_stopping and not is_stopping and self.CP.openpilotLongitudinalControl and CC.longActive):
self.motor3_resuming = True
if self.motor3_resuming and CS.motor3_stock:
can_sends.append(self.CCS.create_motor3_resume(self.packer_pt, self.CAN.aux, CS.motor1_stock, CS.motor3_stock, resume=True))
self.motor3_was_stopping = is_stopping
new_actuators = actuators.as_builder()
new_actuators.torque = self.apply_torque_last / self.CCP.STEER_MAX
@@ -547,5 +638,6 @@ class CarController(CarControllerBase):
self.lead_distance_bars_last = hud_control.leadDistanceBars
self.gra_acc_counter_last = CS.gra_stock_values["COUNTER"]
self.motor3_frame_last = CS.motor3_frame
self.frame += 1
return new_actuators, can_sends
+110 -40
View File
@@ -12,7 +12,10 @@ from iqdbc.car.common.conversions import Conversions as CV
from iqdbc.car.volkswagen.values import CAR, DBC, CanBus, NetworkLocation, RADAR_DISABLE_STATE, TransmissionType, GearShifter, \
CarControllerParams, VolkswagenFlags, VolkswagenFlagsIQ
from iqdbc.car.volkswagen.speed_limit_manager import SpeedLimitManager
from openpilot.iqpilot.konn3kt.iqlvbs import alc as iq_lvbs_alc
from openpilot.system.proprietary_runtime._verified_import import import_verified_module
iq_lvbs_alc = import_verified_module("iqpilot_alc_private", "iqpilot_private.konn3kt.iqlvbs.alc")
vehicle_state = import_verified_module("iqpilot_alc_private", "iqpilot_private.konn3kt.iqlvbs.vehicle_state")
iqpilot_path = os.path.join(os.path.dirname(__file__), '..', '..', '..')
sys.path.insert(0, iqpilot_path)
@@ -45,6 +48,14 @@ class CarState(CarStateBase):
self.ea_hud_stock_values = {}
self.ea_control_stock_values = {}
self.acc_type = 0
self.acc_radar_sollbeschl = 0.0
self.acc_radar_regelabw = 0.0
self.acc_radar_aendgrad = 0.0
self.acc_radar_sta_adr = 0
self.acc_radar_fehler = False
self.acc_radar_v_wunsch = 0.0
self.acc_radar_sta_acc = 0
self.epb_freigabe_ver = False
self.acc_stock_counters: dict[str, int] = {}
self.esp_stopping = False
self.tsk_brake_torque = 0.0
@@ -85,7 +96,20 @@ class CarState(CarStateBase):
self.PQ_ALC_Status_raw = 0
self.alcOverrideAlert = False
self.bremse8_stock = None
self.br8_acc_anf = False
self.tsk_verzoeg_anf = False
self.cruise_main_switch = False
self.motor3_stock = {}
self.motor1_stock = {}
self.motor3_frame = 0
self.motor1_frame = 0
self._odometer_store = vehicle_state.VehicleOdometerStore(CP, self._params)
def _update_odometer(self, ret: structs.CarState, raw_km: float) -> None:
"""Publish the cluster value while proprietary Konn3kt code owns persistence."""
odometer_km = self._odometer_store.record(raw_km)
if odometer_km is not None:
ret.odometer = odometer_km
def _apply_iq_private_flags(self, ret_iq: structs.IQCarState) -> None:
ret_iq.alcOverrideAlert = bool(self.alcOverrideAlert)
@@ -117,6 +141,9 @@ class CarState(CarStateBase):
def _update_mqb_iq_alc_state(self, pt_cp):
iq_lvbs_alc.update_mqb_carstate_alc_state(self, pt_cp)
def _update_mlb_iq_alc_state(self, pt_cp):
iq_lvbs_alc.update_mlb_carstate_alc_state(self, pt_cp)
def _update_pq_iq_alc_state(self, pt_cp):
iq_lvbs_alc.update_pq_carstate_alc_state(self, pt_cp)
@@ -126,7 +153,8 @@ class CarState(CarStateBase):
ext_cp = pt_cp if self.CP.networkLocation == NetworkLocation.fwdCamera else cam_cp
if self.CP.flags & VolkswagenFlags.PQ:
return self.update_pq(pt_cp, cam_cp, ext_cp)
aux_cp = can_parsers.get(Bus.aux)
return self.update_pq(pt_cp, cam_cp, ext_cp, aux_cp)
elif self.CP.flags & VolkswagenFlags.MLB:
br_cp = can_parsers[Bus.aux]
return self.update_mlb(pt_cp, br_cp, cam_cp, ext_cp)
@@ -218,6 +246,7 @@ class CarState(CarStateBase):
self.grade = pt_cp.vl["Motor_16"]["TSK_Steigung"]
acc_limiter_mode = False if cc_only else ext_cp.vl["ACC_02"]["ACC_Gesetzte_Zeitluecke"] == 0
speed_limiter_mode = bool(pt_cp.vl["TSK_06"]["TSK_Limiter_ausgewaehlt"])
self.tsk_verzoeg_anf = bool(pt_cp.vl["TSK_06"]["TSK_Freig_Verzoeg_Anf"])
self._update_mqb_iq_alc_state(pt_cp)
@@ -256,12 +285,15 @@ class CarState(CarStateBase):
ret.buttonEvents = self.create_button_events(pt_cp, self.CCP.BUTTONS)
ret.lowSpeedAlert = self.update_low_speed_alert(ret.vEgo)
ret.cruiseFaultLateralMode = False
ret.lateralAvailable = ret.cruiseState.available
ret.blockPcmEnable = False
allow_lat_only = self._params.get_bool("AllowLateralWhenLongUnavailable") and self._params.get_bool("AolEnabled")
cruise_main_switch = bool(self.gra_stock_values["GRA_Hauptschalter"])
ret.cruiseFaultLateralMode = allow_lat_only and ret.accFaulted and cruise_main_switch
ret.lateralAvailable = ret.cruiseState.available or ret.cruiseFaultLateralMode
ret.blockPcmEnable = ret.cruiseFaultLateralMode
ret.fuelGauge = pt_cp.vl["Kombi_02"]["KBI_Inhalt_Tank"] / 55.0
ret.fuelTankLevelL = pt_cp.vl["Kombi_02"]["KBI_Inhalt_Tank"] # raw liters for konn3kt
self._update_odometer(ret, pt_cp.vl["Kombi_02"]["KBI_Kilometerstand"])
self.cruise_faulted = ret.accFaulted
self._apply_iq_private_flags(ret_iq)
@@ -340,19 +372,11 @@ class CarState(CarStateBase):
self.ldw_stock_values = cam_cp.vl["LDW_02"]
if not (self.CP.flags & VolkswagenFlags.DISABLE_RADAR):
awv_values = ext_cp.vl.get("AWV_03", ext_cp.vl.get("ACC_10", {}))
ret.stockFcw = bool(awv_values.get("FCW_Active", 0)) or bool(awv_values.get("AWV2_Freigabe", 0))
else:
ret.stockFcw = False
awv_values = ext_cp.vl.get("AWV_03", ext_cp.vl.get("ACC_10", {}))
ret.stockFcw = bool(awv_values.get("FCW_Active", 0)) or bool(awv_values.get("AWV2_Freigabe", 0))
ret.stockAeb = False
# Camera harness (DISABLE_RADAR): ext_cp == pt_cp, so ACC_18 reads our own sent value.
# Hardcode acc_type=2 (stop-and-go capable) to avoid self-referential read on first frame.
if self.CP.flags & VolkswagenFlags.DISABLE_RADAR:
self.acc_type = 2
else:
self.acc_type = ext_cp.vl["ACC_18"]["ACC_Typ"]
self.acc_type = ext_cp.vl["ACC_18"]["ACC_Typ"]
self.travel_assist_available = bool(pt_cp.vl.get("TA_01", {}).get("Travel_Assist_Available", 0))
ret.cruiseState.available = pt_cp.vl["Motor_51"]["TSK_Status"] in (2, 3, 4, 5)
@@ -385,6 +409,7 @@ class CarState(CarStateBase):
psd_06_values = main_cp.vl["PSD_06"] if self.CP.flags & VolkswagenFlags.STOCK_PSD_PRESENT else {}
psd_06_values = pt_cp.vl["PSD_06"] if not psd_06_values and self.CP.flags & VolkswagenFlags.STOCK_PSD_06_PRESENT else psd_06_values
diagnose_01_values = pt_cp.vl["Diagnose_01"] if self.CP.flags & VolkswagenFlags.STOCK_DIAGNOSE_01_PRESENT else {}
self._update_odometer(ret, pt_cp.vl["Diagnose_01"]["KBI_Kilometerstand"])
if self.enable_speed_limit_predicative and not self.enable_predicative_speed_limit:
self.enable_predicative_speed_limit = True
@@ -409,10 +434,8 @@ class CarState(CarStateBase):
ret.espDisabled = bool(pt_cp.vl["ESP_21"]["ESP_Tastung_passiv"])
ret.espActive = bool(pt_cp.vl["ESP_21"]["ESP_Eingriff"])
allow_lat_only = self._params.get_bool("AllowLateralWhenLongUnavailable")
cruise_main_switch = bool(pt_cp.vl["GRA_ACC_01"]["GRA_Hauptschalter"])
cruise_fault_candidate = allow_lat_only and ret.accFaulted and cruise_main_switch
allow_lat_only = self._params.get_bool("AllowLateralWhenLongUnavailable") and self._params.get_bool("AolEnabled")
cruise_fault_candidate = allow_lat_only and ret.accFaulted and bool(pt_cp.vl["GRA_ACC_01"]["GRA_Hauptschalter"])
if cruise_fault_candidate:
self.cruise_faulted_frames += 1
self.cruise_fault_clear_frames = 0
@@ -426,12 +449,10 @@ class CarState(CarStateBase):
self.cruise_fault_lateral_active = False
else:
self.cruise_fault_clear_frames = 0
if not allow_lat_only:
self.cruise_fault_lateral_active = False
self.cruise_faulted_frames = 0
self.cruise_fault_clear_frames = 0
ret.cruiseFaultLateralMode = self.cruise_fault_lateral_active
ret.lateralAvailable = ret.cruiseState.available or ret.cruiseFaultLateralMode
ret.blockPcmEnable = ret.cruiseFaultLateralMode
@@ -461,9 +482,10 @@ class CarState(CarStateBase):
self._apply_iq_private_flags(ret_iq)
return ret, ret_iq
def update_pq(self, pt_cp, cam_cp, ext_cp) -> tuple[structs.CarState, structs.IQCarState]:
def update_pq(self, pt_cp, cam_cp, ext_cp, aux_cp=None) -> tuple[structs.CarState, structs.IQCarState]:
ret = structs.CarState()
ret_iq = structs.IQCarState()
sng_ecd_enabled = bool(self.CP.flags & VolkswagenFlagsIQ.IQ_PQ_SNG_ECD)
# vEgo obtained from Bremse_1 vehicle speed rather than Bremse_3 wheel speeds because Bremse_3 isn't present on NSF
ret.vEgoRaw = pt_cp.vl["Bremse_1"]["BR1_Rad_kmh"] * CV.KPH_TO_MS
@@ -476,7 +498,7 @@ class CarState(CarStateBase):
ret.steeringTorque = pt_cp.vl["Lenkhilfe_3"]["LH3_LM"] * (1, -1)[int(pt_cp.vl["Lenkhilfe_3"]["LH3_LMSign"])]
ret.steeringPressed = abs(ret.steeringTorque) > self.CCP.STEER_DRIVER_ALLOWANCE
hca_status = self.CCP.hca_status_values.get(pt_cp.vl["Lenkhilfe_2"]["LH2_Sta_HCA"])
ret.steerFaultTemporary, ret.steerFaultPermanent = self.update_hca_state(hca_status)
ret.steerFaultTemporary, ret.steerFaultPermanent = self.update_hca_state(hca_status, ready_confirms_init=False)
# Update gas, brakes, and gearshift.
ret.gasPressed = pt_cp.vl["Motor_3"]["MO3_Pedalwert"] > 0
@@ -511,8 +533,11 @@ class CarState(CarStateBase):
# Consume blind-spot monitoring info/warning LED states, if available.
# Infostufe: BSM LED on, Warnung: BSM LED flashing
if self.CP.enableBsm:
ret.leftBlindspot = bool(ext_cp.vl["SWA_1"]["SWA_Infostufe_SWA_li"]) or bool(ext_cp.vl["SWA_1"]["SWA_Warnung_SWA_li"])
ret.rightBlindspot = bool(ext_cp.vl["SWA_1"]["SWA_Infostufe_SWA_re"]) or bool(ext_cp.vl["SWA_1"]["SWA_Warnung_SWA_re"])
blindspot_li = bool(ext_cp.vl["SWA_1"]["SWA_Infostufe_SWA_li"]) or bool(ext_cp.vl["SWA_1"]["SWA_Warnung_SWA_li"])
blindspot_re = bool(ext_cp.vl["SWA_1"]["SWA_Infostufe_SWA_re"]) or bool(ext_cp.vl["SWA_1"]["SWA_Warnung_SWA_re"])
force_rhd = self.force_rhd_for_bsm or self._params.get_bool("ForceRHDForBSM")
ret.leftBlindspot = blindspot_re if force_rhd else blindspot_li
ret.rightBlindspot = blindspot_li if force_rhd else blindspot_re
# Consume factory LDW data relevant for factory SWA (Lane Change Assist)
# and capture it for forwarding to the blind spot radar controller
@@ -532,16 +557,18 @@ class CarState(CarStateBase):
# Update ACC radar status.
self.acc_type = 0 if cc_only else ext_cp.vl["ACC_System"]["ACS_Typ_ACC"]
cruise_main_switch = bool(pt_cp.vl["Motor_5"]["MO5_GRA_Hauptsch"])
cruise_tsk_status = bool(pt_cp.vl["Motor_2"]["MO2_Status_TSK"])
self.cruise_main_switch = cruise_main_switch or cruise_tsk_status
self.cruise_main_switch = cruise_main_switch
MO2_StaGRA = pt_cp.vl["Motor_2"]["MO2_Sta_GRA"] in (1, 2)
ACS_StaADR = False if cc_only else ext_cp.vl["ACC_System"]["ACS_Sta_ADR"] == 1
cruiseActive = MO2_StaGRA or ACS_StaADR
if cruiseActive:
self.epb_freigabe_ver = bool(aux_cp.vl["EPB_1"]["EP1_Freigabe_Ver"]) if sng_ecd_enabled and not cc_only else False
sng_holding = sng_ecd_enabled and self.epb_freigabe_ver
if cruiseActive or sng_holding:
self.last_cruiseActive = True
elif not MO2_StaGRA and not ACS_StaADR:
elif not MO2_StaGRA and not ACS_StaADR and not sng_holding:
self.last_cruiseActive = False
ret.cruiseState.enabled = self.last_cruiseActive
self.br8_acc_anf = bool(pt_cp.vl["Bremse_8"]["BR8_Sta_ACC_Anf"]) if not cc_only else False
if self.CP.pcmCruise:
if cc_only:
@@ -552,9 +579,9 @@ class CarState(CarStateBase):
cruise_faulted = pt_cp.vl["Motor_2"]["MO2_Sta_GRA"] == 3
ret.accFaulted = cruise_faulted
ret.cruiseState.available = (cruise_main_switch or cruise_tsk_status) and not cruise_faulted
ret.cruiseState.available = cruise_main_switch and not cruise_faulted
allow_lat_only = self._params.get_bool("AllowLateralWhenLongUnavailable") and self._params.get_bool("AolEnabled")
cruise_main_available = cruise_main_switch or cruise_tsk_status
cruise_main_available = cruise_main_switch
ret.cruiseFaultLateralMode = allow_lat_only and cruise_faulted and cruise_main_available
ret.lateralAvailable = ret.cruiseState.available or ret.cruiseFaultLateralMode
ret.blockPcmEnable = ret.cruiseFaultLateralMode
@@ -571,6 +598,33 @@ class CarState(CarStateBase):
ret.cruiseState.speed = 0
self.motor2_stock = pt_cp.vl["Motor_2"]
self.motor5_stock = pt_cp.vl["Motor_5"]
if self.CP.flags & VolkswagenFlagsIQ.IQ_PQ_ACC_FTS_EPB:
self.motor3_stock = aux_cp.vl["Motor_3"]
self.motor1_stock = aux_cp.vl["Motor_1"]
self.motor3_frame += 1
self.motor1_frame += 1
if cc_only:
self.acc_radar_sollbeschl = 0.0
self.acc_radar_regelabw = 0.0
self.acc_radar_aendgrad = 0.0
self.acc_radar_sta_adr = 0
self.acc_radar_fehler = False
self.acc_radar_v_wunsch = 0.0
self.acc_radar_sta_acc = 0
else:
self.acc_radar_sollbeschl = ext_cp.vl["ACC_System"]["ACS_Sollbeschl"]
self.acc_radar_regelabw = ext_cp.vl["ACC_System"]["ACS_zul_Regelabw"]
self.acc_radar_aendgrad = ext_cp.vl["ACC_System"]["ACS_max_AendGrad"]
self.acc_radar_sta_adr = int(ext_cp.vl["ACC_System"]["ACS_Sta_ADR"])
self.acc_radar_fehler = bool(ext_cp.vl["ACC_System"]["ACS_Fehler"])
self.acc_radar_v_wunsch = ext_cp.vl["ACC_GRA_Anzeige"]["ACA_V_Wunsch"]
self.acc_radar_sta_acc = int(ext_cp.vl["ACC_GRA_Anzeige"]["ACA_StaACC"])
ret_iq.accRadarStaAdr = self.acc_radar_sta_adr
ret_iq.accRadarFehler = self.acc_radar_fehler
# Update button states for turn signals and ACC controls, capture all ACC button state/config for passthrough
ret.leftBlinker, ret.rightBlinker = self.update_blinker_from_stalk(300, pt_cp.vl["Gate_Komf_1"]["GK1_Blinker_li"],
@@ -587,6 +641,13 @@ class CarState(CarStateBase):
ret.fuelGauge = pt_cp.vl["Kombi_1"]["Tankinhalt"] / 55.0
ret.fuelTankLevelL = pt_cp.vl["Kombi_1"]["Tankinhalt"] # raw liters for konn3kt
if aux_cp is not None:
self._update_odometer(ret, aux_cp.vl["Kombi_3"]["Kilometerstand"])
if self.CP.flags & VolkswagenFlagsIQ.IQ_PQ_ACC_FTS_EPB:
ret.cruiseState.standstill = self.CP.pcmCruise and bool(pt_cp.vl["Bremse_5"]["BR5_Stillstand"]) and ret.cruiseState.enabled
elif sng_ecd_enabled:
ret.cruiseState.standstill = sng_holding and ret.standstill
self.cruise_faulted = ret.accFaulted
self.frame += 1
@@ -623,6 +684,7 @@ class CarState(CarStateBase):
ret.accFaulted = pt_cp.vl["TSK_02"]["TSK_Status"] in (3,)
self.parse_mlb_mqb_steering_state(ret, pt_cp)
self._update_mlb_iq_alc_state(pt_cp)
ret.brake = pt_cp.vl["ESP_05"]["ESP_Bremsdruck"] / 250.0
brake_pedal_pressed = bool(pt_cp.vl["Motor_03"]["MO_Fahrer_bremst"])
@@ -656,6 +718,7 @@ class CarState(CarStateBase):
ret.fuelGauge = br_cp.vl["Kombi_02"]["KBI_Inhalt_Tank"] / 55.0
ret.fuelTankLevelL = br_cp.vl["Kombi_02"]["KBI_Inhalt_Tank"] # raw liters for konn3kt
self._update_odometer(ret, br_cp.vl["Kombi_02"]["KBI_Kilometerstand"])
ret.buttonEvents = self.create_button_events(pt_cp, self.CCP.BUTTONS)
@@ -688,10 +751,11 @@ class CarState(CarStateBase):
ret.steerFaultTemporary, ret.steerFaultPermanent = self.update_hca_state(hca_status, drive_mode)
return
def update_hca_state(self, hca_status, drive_mode=True):
def update_hca_state(self, hca_status, drive_mode=True, ready_confirms_init=True):
# Treat FAULT as temporary for worst likely EPS recovery time, for cars without factory Lane Assist
# DISABLED means the EPS hasn't been configured to support Lane Assist
self.eps_init_complete = self.eps_init_complete or (hca_status in ("DISABLED", "READY", "ACTIVE") or self.frame > 600)
init_statuses = ("DISABLED", "READY", "ACTIVE") if ready_confirms_init else ("DISABLED", "ACTIVE")
self.eps_init_complete = self.eps_init_complete or hca_status in init_statuses or self.frame > 1000
perm_fault = drive_mode and hca_status == "DISABLED" or (self.eps_init_complete and hca_status == "FAULT")
temp_fault = drive_mode and hca_status in ("REJECTED", "PREEMPTED") or not self.eps_init_complete
return temp_fault, perm_fault
@@ -743,8 +807,11 @@ class CarState(CarStateBase):
]
if CP.flags & VolkswagenFlags.MLB:
pt_messages += [
("Blinkmodi_01", math.nan) # From J519 BCM (is inactive when no lights active, 50Hz when active)
("Blinkmodi_01", math.nan), # From J519 BCM (is inactive when no lights active, 50Hz when active)
("Kombi_02", math.nan), # Auxiliary-bus cluster odometer
]
else:
pt_messages += [("Kombi_02", math.nan)] # Auxiliary-bus cluster odometer
if CP.flags & VolkswagenFlags.STOCK_HCA_PRESENT:
cam_messages += [
("HCA_01", 1), # From R242 Driver assistance camera, 50Hz if steering/1Hz if not
@@ -758,8 +825,12 @@ class CarState(CarStateBase):
@staticmethod
def get_can_parsers_pq(CP):
aux_messages = [("Kombi_3", math.nan)] # Bus 1 cluster odometer
if CP.flags & VolkswagenFlagsIQ.IQ_PQ_ACC_FTS_EPB:
aux_messages.append(("Motor_3", 0))
return {
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).pt),
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).powertrain),
Bus.aux: CANParser(DBC[CP.carFingerprint][Bus.pt], aux_messages, CanBus(CP).aux),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).cam),
}
@@ -771,14 +842,13 @@ class CarState(CarStateBase):
# TA_01 lives on bus 0 (car ECU / OP-generated when long is active).
# math.nan → ignore_alive=True so it never contributes to can_valid.
("TA_01", math.nan),
("Diagnose_01", math.nan), # Bus 0 cluster odometer
]
# AWV_03 (stock radar FCW/AEB) — don't subscribe when DISABLE_RADAR, the radar is silenced
# and our carcontroller sends the replacement. Subscribing causes CAN parser timeout errors.
if CP.networkLocation == NetworkLocation.fwdCamera and not (CP.flags & VolkswagenFlags.DISABLE_RADAR):
if CP.networkLocation == NetworkLocation.fwdCamera:
pt_messages.append(("AWV_03", 1))
cam_messages = []
if CP.networkLocation == NetworkLocation.gateway and not (CP.flags & VolkswagenFlags.DISABLE_RADAR):
if CP.networkLocation == NetworkLocation.gateway:
cam_messages.append(("AWV_03", 1))
return {
@@ -681,6 +681,14 @@ FW_VERSIONS = {
b'\xf1\x873QF907572A \xf1\x890132',
],
},
CAR.VOLKSWAGEN_PASSAT_B7: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8703L906018RE\xf1\x899979',
],
(Ecu.fwdCamera, 0x74f, None): [
b'\xf1\x873AA980654D \xf1\x890300\xf1\x82\x0143',
],
},
CAR.VOLKSWAGEN_POLO_MK6: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8704C906025H \xf1\x895177',
@@ -716,6 +724,17 @@ FW_VERSIONS = {
b'\xf1\x877N0907572C \xf1\x890211\xf1\x82\x0153',
],
},
CAR.SEAT_ALHAMBRA_MK1: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8704L906016HE\xf1\x894635',
],
(Ecu.srs, 0x715, None): [
b'\xf1\x877N0959655D \xf1\x890016\xf1\x82\x0801100705----10--',
],
(Ecu.fwdRadar, 0x757, None): [
b'\xf1\x877N0907572C \xf1\x890211\xf1\x82\x0153',
],
},
CAR.VOLKSWAGEN_TAOS_MK1: {
(Ecu.engine, 0x7e0, None): [
b'\xf1\x8704E906025CK\xf1\x892228',
+49 -24
View File
@@ -1,15 +1,16 @@
import time
from iqdbc.car.common.conversions import Conversions as CV
from iqdbc.car import get_safety_config, structs, uds
from iqdbc.car.carlog import carlog
from iqdbc.car.interfaces import CarInterfaceBase
from iqdbc.car.isotp_parallel_query import IsoTpParallelQuery
from iqdbc.car.volkswagen.carcontroller import CarController
from iqdbc.car.volkswagen.carstate import CarState
from iqdbc.car.volkswagen.values import CanBus, CAR, DashcamOnlyReason, NetworkLocation, RADAR_DISABLE_STATE, TransmissionType, VolkswagenFlags, VolkswagenSafetyFlags, VolkswagenFlagsIQ
from iqdbc.car.volkswagen.values import (
CAR, CanBus, DashcamOnlyReason, NetworkLocation, RADAR_DISABLE_STATE, TransmissionType,
VolkswagenFlags, VolkswagenSafetyFlags, VolkswagenFlagsIQ, get_longitudinal_stopping_speed_override,
)
from iqdbc.car.volkswagen.radar_interface import RadarInterface
from iqdbc.car.common.conversions import Conversions as CV
import sys
import os
iqpilot_path = os.path.join(os.path.dirname(__file__), '..', '..', '..')
@@ -19,6 +20,11 @@ try:
except ImportError:
pass
try:
from openpilot.system.proprietary_runtime._verified_import import import_verified_module
except Exception:
import_verified_module = None
class CarInterface(CarInterfaceBase):
CarState = CarState
@@ -39,9 +45,16 @@ class CarInterface(CarInterfaceBase):
if ret.flags & VolkswagenFlags.PQ:
# Set global PQ35/PQ46/NMS parameters
safety_configs = [get_safety_config(structs.CarParams.SafetyModel.volkswagenPq)]
if candidate == CAR.SEAT_ALHAMBRA_MK1:
ret.flags |= VolkswagenFlagsIQ.IQ_PQ_TIMEBOMB.value
if not (ret.flags & VolkswagenFlagsIQ.IQ_PQ_TIMEBOMB) and _params.get_bool("VwPqEpsPatched"):
ret.minSteerSpeed = 0
if angle_lat_enabled:
ret.flags |= VolkswagenFlagsIQ.IQ_LVBS_ALC_MODULE.value
safety_configs[0].safetyParam |= VolkswagenSafetyFlags.PQ_ALC_MODULE.value
if alpha_long:
ret.flags |= VolkswagenFlagsIQ.IQ_PQ_SNG_ECD.value
safety_configs[0].safetyParam |= VolkswagenSafetyFlags.PQ_SNG_ECD.value
ret.enableBsm = 0x3BA in fingerprint[0] # SWA_1
if 0x440 in fingerprint[0] or docs: # Getriebe_1
@@ -59,6 +72,13 @@ class CarInterface(CarInterfaceBase):
else:
ret.flags |= VolkswagenFlagsIQ.IQ_CC_ONLY_NO_RADAR.value
cc_only_flags = VolkswagenFlagsIQ.IQ_CC_ONLY | VolkswagenFlagsIQ.IQ_CC_ONLY_NO_RADAR
if ret.flags & cc_only_flags:
safety_configs[0].safetyParam |= VolkswagenSafetyFlags.PQ_NO_CAM_BUS.value
if (ret.flags & cc_only_flags) and not fingerprint[0]:
ret.flags |= VolkswagenFlagsIQ.IQ_PQ_LOWLINE.value
safety_configs[0].safetyParam |= VolkswagenSafetyFlags.PQ_LOWLINE.value
if any(msg in fingerprint[1] for msg in (0x1A0, 0xC2)): # Bremse_1, Lenkwinkel_1
ret.networkLocation = NetworkLocation.gateway
else:
@@ -151,7 +171,7 @@ class CarInterface(CarInterfaceBase):
ret.steerLimitTimer = 0.4
if ret.flags & VolkswagenFlags.PQ:
ret.steerActuatorDelay = 0.2
ret.longitudinalTuning.kfDEPRECATED = 1.2
ret.longitudinalTuning.kf = 1.2
ret.longitudinalTuning.kpBP = [0.]
ret.longitudinalTuning.kpV = [.45]
ret.longitudinalTuning.kiBP = [0.]
@@ -164,19 +184,20 @@ class CarInterface(CarInterfaceBase):
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif ret.flags & VolkswagenFlags.MLB:
ret.steerActuatorDelay = 0.2
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
if angle_lat_enabled:
ret.steerControlType = structs.CarParams.SteerControlType.angle
ret.steerAtStandstill = bool(joystick_mode)
else:
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif ret.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO):
ret.steerActuatorDelay = 0.3
else:
ret.steerActuatorDelay = 0.1
ret.lateralTuning.pid.kpBP = [0.]
ret.lateralTuning.pid.kiBP = [0.]
ret.lateralTuning.pid.kf = 0.00006
ret.lateralTuning.pid.kpV = [0.6]
ret.lateralTuning.pid.kiV = [0.2]
if angle_lat_enabled:
ret.steerControlType = structs.CarParams.SteerControlType.angle
ret.steerAtStandstill = bool(joystick_mode)
else:
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
# Global longitudinal tuning defaults, can be overridden per-vehicle
@@ -200,17 +221,12 @@ class CarInterface(CarInterfaceBase):
if candidate == CAR.PORSCHE_MACAN_MK1:
ret.steerActuatorDelay = 0.07
if candidate == CAR.VOLKSWAGEN_PASSAT_B7 or CAR.SEAT_ALHAMBRA_MK1:
ret.flags |= VolkswagenFlagsIQ.IQ_PQ_ACC_FTS_EPB.value
safety_configs[0].safetyParam |= VolkswagenSafetyFlags.PQ_ACC_FTS_EPB.value
ret.pcmCruise = not ret.openpilotLongitudinalControl
if ret.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO):
ret.startingState = True
ret.startAccel = 0.8
ret.vEgoStarting = 0.5
ret.vEgoStopping = 0.1
ret.stopAccel = -0.55
else:
ret.stopAccel = -0.55
ret.vEgoStarting = 0.1
ret.vEgoStopping = 1.5 * CV.KPH_TO_MS if ret.flags & VolkswagenFlags.PQ else 0.1
ret.stopAccel = -0.55
ret.autoResumeSng = ret.minEnableSpeed == -1
CAN = CanBus(fingerprint=fingerprint)
if CAN.pt >= 4:
@@ -221,10 +237,18 @@ class CarInterface(CarInterfaceBase):
@staticmethod
def pre_init(CP: structs.CarParams, CP_IQ: structs.IQCarParams, can_recv, can_send):
# Engine-on check moved to init(): if radar can't be disabled, radarDisableFailed=True
# gates only long control (carcontroller line ~308) while lateral still works.
# Full dashcam mode here was too aggressive — lateral doesn't need radar disabled.
pass
if not (CP.flags & VolkswagenFlags.PQ) or (CP.flags & VolkswagenFlagsIQ.IQ_PQ_TIMEBOMB) or import_verified_module is None:
return
try:
params = Params()
if params.get_bool("VwPqEpsPatched"):
return
flasher = import_verified_module("iqpilot_hephaestusd_private", "iqpilot_private.konn3kt.hephaestus.vw_pq_flasher")
status = flasher.check_eps_patch_status(1, can_recv, can_send)
except Exception:
return
if status == "patched":
params.put_bool("VwPqEpsPatched", True)
@staticmethod
def init(CP: structs.CarParams, CP_IQ: structs.IQCarParams, can_recv, can_send):
@@ -321,4 +345,5 @@ class CarInterface(CarInterfaceBase):
@staticmethod
def _get_params_iq(stock_cp: structs.CarParams, ret: structs.IQCarParams, candidate, fingerprint: dict[int, dict[int, int]], car_fw: list[structs.CarParams.CarFw], alpha_long: bool, is_release_iq: bool, docs: bool) -> structs.IQCarParams:
ret.longitudinalStoppingSpeedOverride = get_longitudinal_stopping_speed_override(candidate, stock_cp.flags)
return ret
+1 -1
View File
@@ -270,5 +270,5 @@ class LatControlCurvature():
error = desired_curvature - actual_curvature
freeze_integrator = CC.steerLimited or CS.vEgo < 5
output_curvature = self.pid.update(error, feedforward=desired_curvature_corr, speed=CS.vEgo,
freeze_integrator=freeze_integrator, override=False)
freeze_integrator=freeze_integrator, override=CS.steeringPressed)
return output_curvature
+1 -1
View File
@@ -45,7 +45,7 @@ def create_acc_accel_control(packer, bus, acc_type, accel, acc_control, stopping
return [packer.make_can_msg("ACC_05", bus, values)]
def create_acc_hud_control(packer, bus, acc_hud_status, set_speed, leadDistance, distanceBars, fcw_alert, leadVisible):
def create_acc_hud_control(packer, bus, acc_hud_status, set_speed, leadDistance, distanceBars, fcw_alert, leadVisible, unavailable, decel, d_unresponsive):
values = {}
return packer.make_can_msg("ACC_02", bus, values)
+11 -7
View File
@@ -114,10 +114,10 @@ def create_acc_accel_control(packer, bus, acc_type, accel, acc_control, stopping
"ACC_Status_ACC": acc_control,
"ACC_StartStopp_Info": acc_enabled,
"ACC_Sollbeschleunigung_02": accel if acc_enabled else 3.01,
"ACC_zul_Regelabw_unten": comfortBand if acc_enabled else 0.2,
"ACC_zul_Regelabw_oben": comfortBand if acc_enabled else 0.2,
"ACC_neg_Sollbeschl_Grad_02": jerkLimit if acc_enabled else 0,
"ACC_pos_Sollbeschl_Grad_02": jerkLimit if acc_enabled else 0,
"ACC_zul_Regelabw_unten": 0.2,
"ACC_zul_Regelabw_oben": 0.2,
"ACC_neg_Sollbeschl_Grad_02": 3.0 if acc_enabled else 0,
"ACC_pos_Sollbeschl_Grad_02": 3.0 if acc_enabled else 0,
"ACC_Anfahren": starting if acc_enabled else False,
"ACC_Anhalten": stopping if acc_enabled else False,
}
@@ -149,12 +149,16 @@ def create_acc_accel_control(packer, bus, acc_type, accel, acc_control, stopping
return commands
def create_acc_hud_control(packer, bus, acc_hud_status, set_speed, leadDistance, distanceBars, fcw_alert, leadVisible):
def create_acc_hud_control(packer, bus, acc_hud_status, set_speed, leadDistance, distanceBars, fcw_alert, leadVisible, unavailable, decel, d_unresponsive):
priodisp = 0 if fcw_alert else 1 if (acc_hud_status == 4 or decel or leadVisible) else 2 if (acc_hud_status in (3, 2)) else 0
leadDistanceBars = distanceBars + 1 if distanceBars in (1, 2, 3) else 2
values = {
"ACC_Status_Anzeige": acc_hud_status,
"ACC_Wunschgeschw_02": set_speed if set_speed < 250 else 327.36,
"ACC_Gesetzte_Zeitluecke": distanceBars,
"ACC_Display_Prio": 3,
"ACC_Gesetzte_Zeitluecke": leadDistanceBars,
"ACC_Optischer_Fahrerhinweis": 1 if fcw_alert else 0,
"ACC_Display_Prio": priodisp,
"ACC_Relevantes_Objekt": leadDistanceBars,
"ACC_Abstandsindex": leadDistance if leadVisible else 0,
"ACC_Akustik_02": fcw_alert,
}
@@ -0,0 +1,106 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
"""
from iqdbc.car.common.conversions import Conversions as CV
from iqdbc.car.volkswagen import pqcan
class PQRadarHandler:
GRA_STEP = 3 # GRA_Neu cadence (~33 Hz), matches stock stalk module
SPOOF_STEP = 2 # Motor_2 / Motor_5 spoof cadence (50 Hz)
ENGAGE_FLOOR = 1.0 * CV.KPH_TO_MS # never SET at/below 1 kph
REENGAGE_FLOOR = 2.0 * CV.KPH_TO_MS # hysteresis: only (re)engage above 2 kph
SETSPEED_TOL_KPH = 1.0 # don't chase set-speed within this band
SHORT_STEP_KPH = 1.0 * CV.MPH_TO_KPH # GRA_*_kurz ~= 1 mph
LONG_STEP_KPH = 5.0 * CV.MPH_TO_KPH # GRA_*_lang ~= 5 mph
TAP_RELEASE_CYCLES = 2 # GRA cycles to release between set-speed taps
def __init__(self, CAN):
self.bus = CAN.ext # radar lives on bus 2 (ext/cam)
self.counter = 0
self.failed = False # ACS_Fehler / irrev_Fehler -> radar dead for the drive
self.want_engaged = False # our belief the radar cruise should be on
self._press_phase = 0 # alternate press/release for SET/cancel discrete edges
self._tap_cooldown = 0 # set-speed tap rate limiter
def reset(self):
self.want_engaged = False
self._press_phase = 0
self._tap_cooldown = 0
@staticmethod
def _map_gap_bars(gap_bars):
if not gap_bars:
return None
return int(min(3, max(1, gap_bars)))
def update(self, packer, frame, CS, *, blend_active, engage_req, cancel_req,
set_speed_kph, gap_bars, v_ego):
can_sends = []
if not blend_active:
self.reset()
return can_sends
if CS.acc_radar_fehler or CS.acc_radar_sta_adr == 3:
self.failed = True
radar_active = (CS.acc_radar_sta_adr == 1) and not self.failed
if self.failed:
self.want_engaged = False
elif cancel_req:
self.want_engaged = False
elif engage_req and v_ego > self.REENGAGE_FLOOR:
self.want_engaged = True
if (frame % self.SPOOF_STEP) == 0:
# Mirror the stock engage handshake ORDER: the radar leads (SET -> ADR active) and only THEN
# does the engine report GRA regulating. Asserting MO2_Sta_GRA=1 before the radar's ADR is
# active presents an impossible state ("engine regulating but ADR didn't initiate it") and the
# radar latches an irreversible fault. So only assert cruise-active once ADR is actually active;
# relay Motor_5 (main switch) as verbatim OEM passthrough throughout.
hold_engaged = self.want_engaged and radar_active
can_sends.append(pqcan.filter_motor2(packer, self.bus, CS.motor2_stock, gra_active=hold_engaged))
can_sends.append(pqcan.filter_motor5(packer, self.bus, CS.motor5_stock, gra_active=False))
if (frame % self.GRA_STEP) == 0:
set_btn = resume_btn = cancel = up_s = down_s = up_l = down_l = False
self._press_phase ^= 1
pressing = self._press_phase == 0
if self.failed:
pass
elif cancel_req:
# Only actually press cancel when the radar is engaged (or overdriven) -- otherwise it's a
# no-op against a passive/off radar and just spams GRA_Abbrechen at ~16 Hz. Fires while
# active and stops once the radar leaves the active state.
cancel = pressing and radar_active
elif self.want_engaged and not radar_active and v_ego > self.ENGAGE_FLOOR:
# Engage with RESUME (GRA_Recall), not SET. Ground-truth from an OEM ACC engage (radar leads:
# RESUME -> ACS_Sta_ADR 2->1 in ~90ms -> engine MO2_Sta_GRA 0->1 ~80ms later) shows this radar
# engages from passive on GRA_Recall; it ignores GRA_Neu_Setzen from that state.
resume_btn = pressing
elif self.want_engaged and radar_active:
if self._tap_cooldown > 0:
self._tap_cooldown -= 1
elif set_speed_kph > 0:
delta = set_speed_kph - CS.acc_radar_v_wunsch
if abs(delta) >= self.SETSPEED_TOL_KPH:
big = abs(delta) >= self.LONG_STEP_KPH
if delta > 0:
up_l, up_s = big, not big
else:
down_l, down_s = big, not big
self._tap_cooldown = self.TAP_RELEASE_CYCLES
self.counter = (self.counter + 1) % 16
can_sends.append(pqcan.create_radar_gra(
packer, self.bus, CS.gra_stock_values, self.counter,
set_btn=set_btn, resume=resume_btn, cancel=cancel, up_short=up_s, down_short=down_s,
up_long=up_l, down_long=down_l, zeitluecke=self._map_gap_bars(gap_bars),
))
return can_sends
+74 -20
View File
@@ -1,3 +1,7 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
"""
def create_hca_steering_control(packer, bus, apply_torque, HCA_Status):
values = {
"LM_Offset": abs(apply_torque),
@@ -95,20 +99,20 @@ def acc_hud_status_value(main_switch_on, acc_faulted, longActive, longOverride):
return hud_status
def create_acc_accel_control(packer, bus, acc_type, accel, acc_control, stopping, starting, esp_hold, comfortBand, jerkLimit, eBrakeActive):
def create_acc_accel_control(packer, bus, acc_type, accel, acc_control, stopping, starting, esp_hold, comfortBand, jerkLimit, eBrakeActive, sng_active=False):
commands = []
acc_enabled = acc_control == 1
acc_enabled = acc_control == 1 and not sng_active
values = {
"ACS_Sta_ADR": acc_control,
"ACS_Sta_ADR": 0 if sng_active else acc_control,
"ACS_StSt_Info": acc_enabled,
"ACS_Typ_ACC": acc_type,
"ACS_Anhaltewunsch": acc_type == 1 and stopping or eBrakeActive,
"ACS_Anhaltewunsch": (acc_type == 1 and stopping or eBrakeActive) or sng_active,
"ACS_FreigSollB": acc_enabled,
"ACS_Sollbeschl": accel if acc_enabled else 3.01,
"ACS_zul_Regelabw": comfortBand if acc_enabled else 1.27,
"ACS_max_AendGrad": jerkLimit if acc_enabled else 5.08,
"ACS_Schubabsch": 1 if acc_enabled and (accel > 0.05) else 0,
"ACS_Schubabsch": 0,
"ACS_MomEingriff": 0,
"ACS_ADR_Schub": 0,
}
@@ -118,6 +122,14 @@ def create_acc_accel_control(packer, bus, acc_type, accel, acc_control, stopping
return commands
def create_sng_handoff_control(packer, bus, handoff_active, decel_req):
values = {
"SNG_HandoffActive": handoff_active,
"SNG_DecelReq": decel_req if handoff_active else 0.0,
}
return packer.make_can_msg("SNG_1", bus, values)
def create_blinker_control(packer, bus, leftBlinker, rightBlinker):
values = {
"BM_rechts": rightBlinker,
@@ -126,29 +138,71 @@ def create_blinker_control(packer, bus, leftBlinker, rightBlinker):
return packer.make_can_msg("Blinkmodi_02", bus, values)
def create_acc_hud_control(packer, bus, acc_hud_status, set_speed, leadDistance, distanceBars, fcw_alert, leadVisible):
if distanceBars == 1:
leadDistanceBars = 2
elif distanceBars == 2:
leadDistanceBars = 3
elif distanceBars == 3:
leadDistanceBars = 4
else:
leadDistanceBars = 2
def create_acc_hud_control(packer, bus, acc_hud_status, set_speed, leadDistance, distanceBars, fcw_alert, leadVisible, unavailable, decel, d_unresponsive):
priodisp = 0 if fcw_alert else 1 if (acc_hud_status == 4 or decel or leadVisible) else 2 if (acc_hud_status in (3, 2)) else 0
leadDistanceBars = distanceBars + 1 if distanceBars in (1, 2, 3) else 2
values = {
"ACA_StaACC": acc_hud_status,
"ACA_AnzDisplay": 1 if acc_hud_status in (3, 4) else 0,
"ACA_Zeitluecke": leadDistanceBars,
"ACA_V_Wunsch": set_speed,
"ACA_gemZeitl": min(15, max(1, int(round(leadDistance)))) if leadVisible else 0,
"ACA_PrioDisp": 3,
"ACA_PrioDisp": priodisp,
"ACA_Akustik1": d_unresponsive,
# "ACA_Fahrerhinw": unavailable,
"ACA_Akustik2": fcw_alert,
"ACA_ACC_Verz": decel,
}
return packer.make_can_msg("ACC_GRA_Anzeige", bus, values)
def filter_motor2(packer, bus, motor2_stock):
values = motor2_stock
values.update({
"MO2_Sta_GRA": 0,
})
def filter_motor2(packer, bus, motor2_stock, gra_active=False):
values = dict(motor2_stock)
if gra_active:
values.update({
"MO2_Sta_GRA": 1,
"MO2_Status_TSK": 1,
})
else:
values.update({
"MO2_Sta_GRA": 0,
})
return packer.make_can_msg("Motor_2", bus, values)
def filter_motor5(packer, bus, motor5_stock, gra_active=False):
values = dict(motor5_stock)
if gra_active:
values["MO5_GRA_Hauptsch"] = 1
return packer.make_can_msg("Motor_5", bus, values)
def create_motor3_resume(packer, bus, motor1_stock, motor3_stock, resume=False):
values = dict(motor3_stock)
values_motor1 = dict(motor1_stock)
if resume:
values["MO3_Pedalwert"] = values_motor1["MO1_Pedalwert"]
return packer.make_can_msg("Motor_3", bus, values)
def create_radar_gra(packer, bus, gra_stock, counter, set_btn=False, cancel=False, resume=False,
up_short=False, down_short=False, up_long=False, down_long=False, zeitluecke=None):
values = {s: gra_stock[s] for s in [
"GRA_Hauptschalt", # ACC main switch passthrough
"GRA_Typ_Hauptschalt", # momentary vs latching
"GRA_Kodierinfo", # configuration
"GRA_Sender", # CAN originator
]}
values.update({
"COUNTER": counter % 16,
"GRA_Neu_Setzen": 1 if set_btn else 0,
"GRA_Abbrechen": 1 if cancel else 0,
"GRA_Recall": 1 if resume else 0,
"GRA_Up_kurz": 1 if up_short else 0,
"GRA_Down_kurz": 1 if down_short else 0,
"GRA_Up_lang": 1 if up_long else 0,
"GRA_Down_lang": 1 if down_long else 0,
})
if zeitluecke is not None:
values["GRA_Zeitluecke"] = zeitluecke
return packer.make_can_msg("GRA_Neu", bus, values)
@@ -0,0 +1,49 @@
from types import SimpleNamespace
from iqdbc.car.volkswagen import mqbcan, pqcan
from iqdbc.car.volkswagen.carcontroller import CarController
from iqdbc.car.volkswagen.values import CAR, MQB_A0_CARS
def test_is_mqb_a0_car_matches_expected_platforms():
assert CAR.VOLKSWAGEN_POLO_MK6 in MQB_A0_CARS
assert CAR.VOLKSWAGEN_TCROSS_MK1 in MQB_A0_CARS
assert CAR.SKODA_FABIA_MK4 in MQB_A0_CARS
assert CAR.SKODA_KAMIQ_MK1 in MQB_A0_CARS
assert CarController._is_mqb_a0_car(CAR.VOLKSWAGEN_POLO_MK6)
assert CarController._is_mqb_a0_car(CAR.VOLKSWAGEN_TCROSS_MK1)
assert CarController._is_mqb_a0_car(CAR.SKODA_FABIA_MK4)
assert CarController._is_mqb_a0_car(CAR.SKODA_KAMIQ_MK1)
assert not CarController._is_mqb_a0_car(CAR.VOLKSWAGEN_GOLF_MK7)
def test_mqb_steering_torque_scale_only_changes_when_toggle_enabled():
controller = object.__new__(CarController)
controller.CCS = mqbcan
assert controller._get_mqb_steering_torque_scale(0.4, False) == 1.0
assert controller._get_mqb_steering_torque_scale(0.4, True) == 0.8
assert controller._get_mqb_steering_torque_scale(4.0, True) == 1.0
controller.CCS = pqcan
assert controller._get_mqb_steering_torque_scale(0.4, True) == 1.0
def test_mqb_a0_resume_spam_requires_toggle_platform_and_window():
controller = object.__new__(CarController)
controller.CCS = mqbcan
controller.is_mqb_a0 = True
controller.frame = 10
cs = SimpleNamespace(out=SimpleNamespace(standstill=True))
assert controller._should_spam_mqb_a0_resume(cs, True)
controller.frame = 20
assert not controller._should_spam_mqb_a0_resume(cs, True)
controller.frame = 10
controller.is_mqb_a0 = False
assert not controller._should_spam_mqb_a0_resume(cs, True)
controller.is_mqb_a0 = True
assert not controller._should_spam_mqb_a0_resume(cs, False)

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