IQ.Pilot Release Commit @ 0babf78

This commit is contained in:
IQ.Lvbs CI [bot]
2026-07-27 01:40:11 -05:00
parent 6fb5c0141c
commit b39791a93f
425 changed files with 12180 additions and 6137 deletions

View File

@@ -8,11 +8,11 @@
* A modern comma, or clone device to run this software (Comma 3, 3x, 4, Konik A1/M, Mr.One C3, C3 Lite)
* One of [the supported cars](https://gitlvb.teallvbs.xyz/IQ.Lvbs/IQ.Pilot/src/branch/release/iqdbc_repo/docs/CARS.md).
* A [car harness](https://comma.ai/shop/products/car-harness) to connect to your car
#### Side Note: Volkswagen Group, and Tesla vehicles are currently the most compatible for use with IQ.Pilot, other manufacturers are supported at a minimum to the same level as stock openpilot, but are not a top priority while we are in beta.
#### Wondering if IQ.Pilot supports your car? IQ.Pilot supports every car [stock openpilot](https://gitlvb.teallvbs.xyz/IQ.Lvbs/IQ.Pilot/src/branch/release/iqdbc_repo/docs/CARS.md) supports!
## Installation
#### Installing Via Installer URL:
#### Enter the following into your device custom URL box to install IQ.Pilot:
`IQLvbs/release`
`installer.iqlvbs.com/release`
#### Having Trouble? If your device is currently running AGNOS 13.1 or older, you should install latest stock openpilot, then install IQ.Pilot, or try one of the alternative methods listed below!
@@ -27,11 +27,11 @@
#### Installing Via SSH:
#### Once you are connected to your device via SSH, you can paste the following command below to install IQ.Pilot:
`cd .. && rm -rf openpilot && git clone https://github.com/IQLvbs/openpilot.git -b release && cd openpilot && sudo reboot`
`cd .. && rm -rf openpilot && git clone https://git.konn3kt.com/IQ.Lvbs/IQ.Pilot openpilot -b release && cd openpilot && sudo reboot`
#### If you'd like to backup your previous installation as well, paste the following command below to install IQ.Pilot:
`cd .. && mv openpilot openpilot_backup_X && git clone https://github.com/IQLvbs/openpilot.git -b release && cd openpilot && sudo reboot`
#### Alternatively, you can use your existing fork's built in tools to switch your branch as well:
`git remote add iqpilot https://github.com/IQLvbs/openpilot.git && op switch iqpilot release`
`cd .. && mv openpilot openpilot_backup_X && git clone https://git.konn3kt.com/IQ.Lvbs/IQ.Pilot -b release && cd openpilot && sudo reboot`
#### Alternatively, you can use your existing fork's built in tools to switch your branch as well:
`git remote add iqpilot https://git.konn3kt.com/IQ.Lvbs/IQ.Pilot && op switch iqpilot release`
---

View File

@@ -16,18 +16,28 @@
},
"python/iqpilot_private/konn3kt/iqlvbs/alc.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "a7ac44d7ce568d335b6cd5e31c940c993c3febfbf150dbc5b625fec930789d30",
"sha256": "77a601a12a498f09f01c4b1618e164638d5d9702913b6d5bb30a3c729a1c7d54",
"size": 401688
},
"python/iqpilot_private/konn3kt/iqlvbs/vehicle_state.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "ca91f99d94c8bb7842332a69896d4b3a31c109c027426d1d7386876f11710d80",
"size": 69696
},
"runtime": {
"entries": {
"iqpilot_private.konn3kt.iqlvbs.alc": {
"path": "python/iqpilot_private/konn3kt/iqlvbs/alc.cpython-312-aarch64-linux-gnu.so",
"type": "python-module"
},
"iqpilot_private.konn3kt.iqlvbs.vehicle_state": {
"path": "python/iqpilot_private/konn3kt/iqlvbs/vehicle_state.cpython-312-aarch64-linux-gnu.so",
"type": "python-module"
}
}
},
"signatures": {
"python/iqpilot_private/konn3kt/iqlvbs/alc.cpython-312-aarch64-linux-gnu.so": "OgV+x72Bjg9Gxxqv8pGyaTEee7bJK2xuCcfgmeKN34YKg6jjlyBgvvsx69UVrHlJx8It6udCEfDqfGjtRbzRCA=="
"python/iqpilot_private/konn3kt/iqlvbs/alc.cpython-312-aarch64-linux-gnu.so": "4KY001fMJhdkiQNoUXbvtOKQ2nWocmg9ZL0umWnDjAztueIstXBoc1iqW83plm2Xq0jR9NLk1Jd4yl3AydWFBA==",
"python/iqpilot_private/konn3kt/iqlvbs/vehicle_state.cpython-312-aarch64-linux-gnu.so": "JqSaxsDJT9q0UYgVJUo9Pqm0J0Aqwy5gIa2C8n9/ioOg/fV6PLn2KiVGn6470U4F5oNaaOgAmm+AM9jwYrZXAA=="
}
}

View File

@@ -16,29 +16,44 @@
},
"python/iqpilot_private/konn3kt/backups/archive_codec.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "fddb1a432475585812d5d3cea8fbb2db3770df19d205b372b0df0463b2ec0840",
"sha256": "dfc76d7b791879501396d4de4891718d0052d23b1e6cb217b0be01f3e3e6d857",
"size": 135536
},
"python/iqpilot_private/konn3kt/backups/backup_keys.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "a996e5ef6a6451e18f7dc49bc70a1223455278215c8d96092701e4ecbb68a538",
"sha256": "d1a7fd4e222b08c1e574ed1188943acddecc8d3f5ac02a1983bf90bde5e00a2c",
"size": 67664
},
"python/iqpilot_private/konn3kt/backups/backup_orchestrator.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "59c62ac7fe547df307e5f73054e00f1f650b18b8f95adf3ca0e79de5540d7427",
"sha256": "5393274eb99f50917fef30f994b1ad5fab66860bd535b7d270a86d144d4e25b7",
"size": 204624
},
"python/iqpilot_private/konn3kt/backups/cbc_vault.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "ad3c2e37af910f38af41a6866e8485cbdf81b3b9bb68def3a378d3960f925d75",
"sha256": "9c08c3f6e3dc3b73f1a28d50d5aa76fa0894e9cd83bf02b7e9cc83bda5e84b4e",
"size": 69752
},
"python/iqpilot_private/konn3kt/backups/imahelper.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "1f6909f98ae72ee50e56f797846d4c5623c0d60487c63f07a2f94945f735f45b",
"sha256": "fb417b7fed56353caad6d5a96429952ff0b87ee1bab7d6a8f0856d1ca40e4b4a",
"size": 203072
},
"python/iqpilot_private/konn3kt/flockd/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"size": 0
},
"python/iqpilot_private/konn3kt/flockd/flockd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "73164bb5c3eafe0a5409508140c093503ec40c1c58d0a8e79281bac3d22d811c",
"size": 202168
},
"python/iqpilot_private/konn3kt/flockd/signatures.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "fb6d56fbec20965d988f936c46e7a70f31def71a2fba27a6506567b3c13574cf",
"size": 68080
},
"python/iqpilot_private/konn3kt/hephaestus/__init__.py": {
"mode": 420,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
@@ -46,67 +61,67 @@
},
"python/iqpilot_private/konn3kt/hephaestus/_vendor/localapi_runtime.zip": {
"mode": 420,
"sha256": "732160f1baf7f0161925bad753d1f577d4a1d8c78623f229484849c2052945c9",
"sha256": "52026a3b96c4e7eee5cb33d70a323896ab0de02557196980653afb99498af84d",
"size": 2100238
},
"python/iqpilot_private/konn3kt/hephaestus/ble_auth.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "7b92999c3119cf76e73c0847a26961210c52fcaafc1d6d2877e2efd0a4456795",
"sha256": "aa3530a7f7a44ba1de0d8c1b5de3d26405bf0ee0b9599190bfbbea55d2ed2071",
"size": 268064
},
"python/iqpilot_private/konn3kt/hephaestus/ble_gatt.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "ae928487c4e757cefe1f9881207c1137dc8b106563bcd78b9d9e6e10fb40b851",
"sha256": "5a6bf50d4f8e3ec43603c2b3d9743dd650b37c6dd2075ec19266d0f1368b8cff",
"size": 340584
},
"python/iqpilot_private/konn3kt/hephaestus/ble_rpc_dispatch.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "f4adfd7598757a54205fa1ad91641f02d98a7df290378c74108f557bae2627d5",
"sha256": "ff36bfb143294790a6ab0e2fe3d14852f3b04374185ef19a614231ca56a40999",
"size": 68048
},
"python/iqpilot_private/konn3kt/hephaestus/ble_transportd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "174978b5f138a3c49734dcbb0e1353098ca2bdf3f2d2c81fbccd08db87ace417",
"sha256": "ea54bc8d8a2ba2d803ce782898a6987457d248945da81cb9ab08bc32f6763fe1",
"size": 335728
},
"python/iqpilot_private/konn3kt/hephaestus/bt_gamepad.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "ca3feeb930cdd5b7f6a32fdb11acd6ecc5aabe29bd67d58ace6c9ffc15e545eb",
"sha256": "f7706a6cabf49660c48609ca4a85c840c1e22e5e4b17d250114e25b56a7b9c34",
"size": 271680
},
"python/iqpilot_private/konn3kt/hephaestus/cloud_routes.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "c523dacac84c4ad80181ccfa3e886252cb964fe71796d4d3c82935f68d6705d0",
"sha256": "5fc96f8ac2c67f3ff660b8c060d14b0e526cba7eaa5ce64fae94f3d0e784d417",
"size": 134408
},
"python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "f6154067c4885232d0cc72c346a1e6462941d8956c70eaf7341457b302b13c4a",
"size": 3448272
"sha256": "85ecde61b3fcc0d18dc260855a94eb8d30ab2e02adbfea67e209b3fe320608b1",
"size": 3514416
},
"python/iqpilot_private/konn3kt/hephaestus/kwp2000.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "94b222b5bea69ad307349be5328a5043f90b1e69c30e60670c034117c52f0c6a",
"sha256": "c7b1b7a441f9c4ad395efb3de26d972379d9927f9c7ea9c121d1d06b8170139b",
"size": 136448
},
"python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "00d9d730c09439d8b23145e38afc657486ebf5daf11501711ad4713d1ecb9f59",
"sha256": "a7fddf4e77d804f255ae4821fe64eada65ee6f2cc41cfb5c995acd497a0caca4",
"size": 68136
},
"python/iqpilot_private/konn3kt/hephaestus/motd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "d9e1b7bd29123482d9883e4bc47f3fe45c84fad326c06f2e861da07cbfdee13a",
"sha256": "2ff9ebdb8887bb71a971a4d06fd362c51f5a4446595a073a45edc8bb630a642a",
"size": 67840
},
"python/iqpilot_private/konn3kt/hephaestus/tp20.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "b838d67347e4983886d3c366a6461cb349cbe27064928fbf25f643253ca68c86",
"sha256": "789481c2f41a6a38492f4f6e62d90c18c59276ff7a9b60fd8a5063b1754efe82",
"size": 135648
},
"python/iqpilot_private/konn3kt/hephaestus/vw_pq_flasher.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "f44609d1974b2ec11cda57db2c9c09bd4e54a224bd96a743af6a9f507dc56478",
"sha256": "280bee6ecfcde50d1856835a319aaa3354ecfdc4c9d552243493ae7070f9f169",
"size": 269568
},
"python/iqpilot_private/konn3kt/uploaderd/__init__.py": {
@@ -116,11 +131,19 @@
},
"python/iqpilot_private/konn3kt/uploaderd/iquploaderd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "bb9f65c0d0baf02144971d2c721f37b118007df9c5f45a8983400941ec7d68a6",
"sha256": "b1df9786798beaf80ae110b20b79c2e88b7bbdf38c576fa249fc896e360f9ad1",
"size": 204184
},
"runtime": {
"entries": {
"iqpilot_private.konn3kt.backups.backup_orchestrator": {
"path": "python/iqpilot_private/konn3kt/backups/backup_orchestrator.cpython-312-aarch64-linux-gnu.so",
"type": "python-module"
},
"iqpilot_private.konn3kt.flockd.flockd": {
"path": "python/iqpilot_private/konn3kt/flockd/flockd.cpython-312-aarch64-linux-gnu.so",
"type": "python-module"
},
"iqpilot_private.konn3kt.hephaestus.ble_transportd": {
"path": "python/iqpilot_private/konn3kt/hephaestus/ble_transportd.cpython-312-aarch64-linux-gnu.so",
"type": "python-module"
@@ -148,23 +171,25 @@
}
},
"signatures": {
"python/iqpilot_private/konn3kt/backups/archive_codec.cpython-312-aarch64-linux-gnu.so": "njPmYch3VDP4JuJv/57vY03J1darpkL5QqjL7w07XYgkF6vzflZYFJq2ANSE0gqS3hWio2IZxOaVG16MJvxRCw==",
"python/iqpilot_private/konn3kt/backups/backup_keys.cpython-312-aarch64-linux-gnu.so": "eTwlMQbC5Z+Hplu3X00ewYQT7ftqnLCgComW9n54KIPSUiZbxwjiw4G2CeFhe28JfJUBr6sDUDlVoVNnZ9OqBA==",
"python/iqpilot_private/konn3kt/backups/backup_orchestrator.cpython-312-aarch64-linux-gnu.so": "k09CSvePXfMH7+q5uTsdIt69+STam/crgEXWvp5WGqAh3Hl0ZDVnRNHh8f+SKmppSTzU8gtjE9Yx/wzgjjYeBw==",
"python/iqpilot_private/konn3kt/backups/cbc_vault.cpython-312-aarch64-linux-gnu.so": "j0EBLQA9VD3hBRi8eiXZlB4/ZcruECmsppEr+BP9NrzLskdlKAysN83o4YDUAMMinLNq0VYOx9Ix4PHksQmJDg==",
"python/iqpilot_private/konn3kt/backups/imahelper.cpython-312-aarch64-linux-gnu.so": "iqWiLWOAPNTB9FV/NqnFLImvPn3xEvuAqcjrQsY9v6GYBev51+uBUgVEwZhEW5VOaK3tGLjErLvhczu1s205Bg==",
"python/iqpilot_private/konn3kt/hephaestus/ble_auth.cpython-312-aarch64-linux-gnu.so": "/RVYVsF0U1n5ZjoMMSqmrHvv0THuRauCmVMZPEDOMU5EIWW+ZYiQrgO24xPUILSV/og3S1wKpEBpUgXFm4sCBg==",
"python/iqpilot_private/konn3kt/hephaestus/ble_gatt.cpython-312-aarch64-linux-gnu.so": "PHdyy9tyV0wrwHvawZqHZv3W7MkJ6uaqluURusZF3BbIV4hdbok6i4bPyUUHIaiztFAclc7ktqm1D7nY4k7oBg==",
"python/iqpilot_private/konn3kt/hephaestus/ble_rpc_dispatch.cpython-312-aarch64-linux-gnu.so": "scXkv1O9w1hFvZmfoLWBSghQW3d7stQW/Tp8u09MaCaUyzIxEMySFMGWyuLsK/+Q72neAN9PfUD75l8CAZrzAA==",
"python/iqpilot_private/konn3kt/hephaestus/ble_transportd.cpython-312-aarch64-linux-gnu.so": "REH0sYrMI7XDjqxdVt+1odlWJa/xA+47aMZ/ow/tF6N6c8RTdEAQ3bse4ylh4zONQk+ozP1FP8KESPjCx9ImDQ==",
"python/iqpilot_private/konn3kt/hephaestus/bt_gamepad.cpython-312-aarch64-linux-gnu.so": "IFpGbURNm++Q+0+w/c26C7rGEA0aExvptQADdwGFpzOey8qn7Lm88w0dS4XrLmwYi46BjtbWm/nkN61steemBA==",
"python/iqpilot_private/konn3kt/hephaestus/cloud_routes.cpython-312-aarch64-linux-gnu.so": "lqzf8FZk6+1wSIHBIhRgzzv0Vd1DjvDOfVz0NdIV23y3l+2Eif/gkzUjuZpsXpIgIw9bBqh8L3PKXWW4jJFkBQ==",
"python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so": "JbVCZ+G97IMYDzWnWP87Dk9mWnaWmxQXV41RWuDEwJWnM4HBi3+7HCziPo/GdEW9YpORyu1WBqg6Y5y0ijXlDA==",
"python/iqpilot_private/konn3kt/hephaestus/kwp2000.cpython-312-aarch64-linux-gnu.so": "1AOr8xUf6+iUX853cpCYEsdxKpeJom/u+neR4fh3pW/xMscieI7Y5I9DJgjdU4/7Zor2M1Lstxi5FDI45W8gDQ==",
"python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so": "lkHdhndsENTYxHNskHwva2FFmkQXs5vKKdSOcSbBmNWKDiYUSxNYzYBdUlYkYlBQqMZU6I57Dk54erq8Muj8AQ==",
"python/iqpilot_private/konn3kt/hephaestus/motd.cpython-312-aarch64-linux-gnu.so": "8ch+yvT6IdR63R6oSiqJnF6EVVZRiaNFk2QwHqYDNoT9o8h0i4/vssb6n86tVCj01ZLyT9rHvyznL36BittuAQ==",
"python/iqpilot_private/konn3kt/hephaestus/tp20.cpython-312-aarch64-linux-gnu.so": "aRWcvotSk/hilzlmj867MNro2kNalEd5ySGdqJkE7DKmGHxxCaG+x9E0AYTyvbzQs0ar59o9kQe31vL4Mr9ZDw==",
"python/iqpilot_private/konn3kt/hephaestus/vw_pq_flasher.cpython-312-aarch64-linux-gnu.so": "4SjG/+6RRSYaEVhsMnHTXRfnjnVstF2J8ttWQyfDYHhvFqmrshUf2d6r3aAZtNTDuqU3MgopfxZw7Rs/4OlqAw==",
"python/iqpilot_private/konn3kt/uploaderd/iquploaderd.cpython-312-aarch64-linux-gnu.so": "3U+IiMq2SN7NDiqwA8yFtM1eMq/64HnxDYVeeQWa7hdhwXoetfteI05VqUOG90GHgaGob026MP8f+v7rKd2WCA=="
"python/iqpilot_private/konn3kt/backups/archive_codec.cpython-312-aarch64-linux-gnu.so": "/YL6W76Q7ejmJvQraTclgzsGxYGpYbSBF3qql0E8Z40MZBQcm8wy+9mrMm1en5W9xGebuaT5xN2+Rji5MUCrDQ==",
"python/iqpilot_private/konn3kt/backups/backup_keys.cpython-312-aarch64-linux-gnu.so": "0/MsPs9cfZy51uqtyCEK8/U8paw+eQXgqVnglRdUM3Xx6WaPTku3DXfx7MquCTXl+ia9XlM1xdaH/ax6zxhdAw==",
"python/iqpilot_private/konn3kt/backups/backup_orchestrator.cpython-312-aarch64-linux-gnu.so": "1i1wEWih2NTsQ9jgK/cpb8V/B0y5prTk/OHG1wu4qoaMx4Qm1gVm6KAWU7rkvaQHMqC5eZG5lgL7O6tGflgOBA==",
"python/iqpilot_private/konn3kt/backups/cbc_vault.cpython-312-aarch64-linux-gnu.so": "Q0TBxMRrSRv99t5NIpcYgwRcj26IycOSUSX+Yh4f4S82hY3dXwME3XSqWFLaL2wNF+uG7dMZr0pZkZnHBHmaDg==",
"python/iqpilot_private/konn3kt/backups/imahelper.cpython-312-aarch64-linux-gnu.so": "wFMRv5A2/dCAjlBC/x5uN876dq/kMrgMg5F7nmOKjDlrC5lZ014MbZXU0BKzI49CSch9uU3TLbvUR1p9gWPcBA==",
"python/iqpilot_private/konn3kt/flockd/flockd.cpython-312-aarch64-linux-gnu.so": "de2W5b0SVEEAhjf9r1Ht9tg6W4RWldp7Tc/IzUQdphK4XBOzqouXIYCItN5xgEbc1tFTmGgKu2pfMXAoXkgYDw==",
"python/iqpilot_private/konn3kt/flockd/signatures.cpython-312-aarch64-linux-gnu.so": "yEkA8O096lTrh5YTvZgmuGpyuZ0vTnZDvLnLGR7dhBfypiNPsF4dxp2bcgDpz14GRKPHKa3v8O2v/G/06lk4CQ==",
"python/iqpilot_private/konn3kt/hephaestus/ble_auth.cpython-312-aarch64-linux-gnu.so": "fpzjKBczfi9su9/BzPINkMJ8+Qx2KljBmYg/lhpaCRZDQX9kIT2qgcgbu5CqahfEQAjWDH2YKDwRhAGzkx1LBw==",
"python/iqpilot_private/konn3kt/hephaestus/ble_gatt.cpython-312-aarch64-linux-gnu.so": "mUUGiu3/nIkcp9McjoX+qOTzBwuJlFzxxZdbW3uU2C4ppAlQ+bYMz9p33qnmkUiJUGfjcBwFnJebL8AXo/kXBQ==",
"python/iqpilot_private/konn3kt/hephaestus/ble_rpc_dispatch.cpython-312-aarch64-linux-gnu.so": "Nge3RnzUjW2gPUjEyBRxh+D6frwx9dJ/N9W0e0vzFWLLvbo+cT5zjrNAT7buA7XAKGfHSy14ssOxL8Jz+uZBBw==",
"python/iqpilot_private/konn3kt/hephaestus/ble_transportd.cpython-312-aarch64-linux-gnu.so": "KXjqWoG0lsVWqK8uRhOOChREx/ThGtcdKoFxaDD2WpGtEoFBTz2fDsNP8lgyLcrH+9ETT6ggnXqHMf2SnEabBw==",
"python/iqpilot_private/konn3kt/hephaestus/bt_gamepad.cpython-312-aarch64-linux-gnu.so": "+0ZLw/tXrJtcempSJZFTKz9vlzs7q13YrnzSkuSpHzaGUm4Z2l4H3Syy0rPj3iJ8OLbkMAE9LegxScVq9mb1Bg==",
"python/iqpilot_private/konn3kt/hephaestus/cloud_routes.cpython-312-aarch64-linux-gnu.so": "Q44rxaPzw1QoSLoHyoaCMtW9N45YeYUNhtgPETz6Z1zLJ5o9NPec6yP06673sX9xdOnRXr0maeLfI2ji7iFiBg==",
"python/iqpilot_private/konn3kt/hephaestus/hephaestusd.cpython-312-aarch64-linux-gnu.so": "MUg6v8qvb7Rhhk5LPoU7krD0CKb39e8wsoh5ER+VvzWYJZ2fnra7Od14xSGfhFZoHIItPwtOlASUbqlbPKReCg==",
"python/iqpilot_private/konn3kt/hephaestus/kwp2000.cpython-312-aarch64-linux-gnu.so": "ERRMJ3hrOhetsuiNyWrsXH6hwtKzw5CAjg8kB/025JkDyA4OR3yLGMnqXqryygc4r4onUUF0Q55SzTbxxHWzDA==",
"python/iqpilot_private/konn3kt/hephaestus/manage_hephaestusd.cpython-312-aarch64-linux-gnu.so": "+gyiBvn9Uz56mpHovmDtUsNxvlnPYLH8/v5J+f0o8nr/E26f1xvSk2yQbYwYrCeZZ8AOxgY6GmdGRnkly4WCCg==",
"python/iqpilot_private/konn3kt/hephaestus/motd.cpython-312-aarch64-linux-gnu.so": "Y/cinSdkb/kWX44T/w4woXX/Ex9+Q1KPtUC318dtygZeTiHDjCM+0IUjfS/BXwbdKETQPEpyIoKLsJaP2Gk1Cw==",
"python/iqpilot_private/konn3kt/hephaestus/tp20.cpython-312-aarch64-linux-gnu.so": "N+JrGIiTUzESxzH9fdgub5y01psxzlmEw75Y1AuxQqFx7mG4Z+Tx20UeeyQtAw27UuOctiKpGPHq9trtijJwBA==",
"python/iqpilot_private/konn3kt/hephaestus/vw_pq_flasher.cpython-312-aarch64-linux-gnu.so": "1MxRuPAWgjtsNORklvPZBml0OOVqIu8vMluY0QlK/pqE3S8EoC28/Gn39Bhb6hMjk8yTAUaTnTn4HlwkENpvBA==",
"python/iqpilot_private/konn3kt/uploaderd/iquploaderd.cpython-312-aarch64-linux-gnu.so": "Uxa3OMIwXvFEYz1LP+UDWB1sVhi4z8yC9bP+Xypzw26LNRAMkJZta2r+drXGPEe1TFg4zgVWrb9Q1+Y3pA9HAQ=="
}
}

View File

@@ -6,7 +6,7 @@
},
"python/iqpilot_private/iqvd/__init__.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "3731e0eaf0f13f83fa5997108de084a98812869eb16b35aeda5671225a9ae468",
"sha256": "d36af86bf1fe721522e729b6a48e3a962ce033dcfe89f9e00e3d6208b1801591",
"size": 67616
},
"python/iqpilot_private/iqvd/__init__.py": {
@@ -16,12 +16,12 @@
},
"python/iqpilot_private/iqvd/iqvd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "67f55a32787720c3adb423330fe4dba529e99be227af14ad56df2088206523ad",
"size": 201520
"sha256": "f81036080eb7bc0c91bae10b11f52064041311a5fc67e7dc0fba32f585318e41",
"size": 201584
},
"python/iqpilot_private/iqvd/yolov8_net.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "6ca0d723546fa9a670fee6bed6b96b009856628ba9727e0ae5f9b6cf90cb862f",
"sha256": "976be8806f006a175a14b0c63e9a1c2c2b845ee2dab77a41eeb6759c7feab641",
"size": 204568
},
"runtime": {
@@ -33,8 +33,8 @@
}
},
"signatures": {
"python/iqpilot_private/iqvd/__init__.cpython-312-aarch64-linux-gnu.so": "VwC9JwdPjTasc0t562kGkyPRBuV5Dfe+wlSmctOyBMmMZEjB6J6aBz9M/o/zmkFbylVNKWbBrnS2WMcbWiseDg==",
"python/iqpilot_private/iqvd/iqvd.cpython-312-aarch64-linux-gnu.so": "Gq4Qs2SgFk66OEq1pFVZKkhsyG+ougZTRcHd+gTqca8P5QV932gDv1zaIYz538VUzksHhZleJFi76I4y4BGVCQ==",
"python/iqpilot_private/iqvd/yolov8_net.cpython-312-aarch64-linux-gnu.so": "9Xh6hjmUFKQTU4LTn+WSKe/BM3lmh/gleSwdhEYXavmN3KwF0ypYdjg5DDpduGjKG/VqQauz42kqdcYKXd1hAg=="
"python/iqpilot_private/iqvd/__init__.cpython-312-aarch64-linux-gnu.so": "bMboPEsPzZjh0Y1Zk3Brvc6fyWFF5CxuzN9KQr2RK0Hn+l8XiD6l/ym2Y10nFxvj8+uLikIZ0xSAovJlJcRUAA==",
"python/iqpilot_private/iqvd/iqvd.cpython-312-aarch64-linux-gnu.so": "seyE6m33mqlKZ4H7b1JL6pxxz+r/+dKP1PzcfXxhdhHvsl8Ka8Noi6+ln5z+QSvBgt5ulxQeBJMBP5qdu6lOCw==",
"python/iqpilot_private/iqvd/yolov8_net.cpython-312-aarch64-linux-gnu.so": "RvzkJLNxPA+u7yEnH5jd2jecT02PJCQkIDj/LiWvBCE4lFmFFzOf1Q+ZCGGjrFQzhLQZlm75SvW80mlqJ9BJCw=="
}
}

View File

@@ -6,7 +6,7 @@
},
"python/iqpilot_private/navd/__init__.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "8b6114f67eb7a83d47e90113a0e75801ffd55d6c732d1db9a6af620f3cfba1d8",
"sha256": "449495db52a85f8d0b0e2bb3128ee480598f8bdb5a44ec13153877fe8b372a91",
"size": 67616
},
"python/iqpilot_private/navd/__init__.py": {
@@ -16,72 +16,72 @@
},
"python/iqpilot_private/navd/event_builder.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "4ebabfe3b85a380441cb22730ea3779c3c477e7ec40a88d0ba4d8130a2827877",
"sha256": "ae2b25f52f7c7b02f3c977bbf46f5e9e938bfeda78bdb60f2f897eb04d698e43",
"size": 136944
},
"python/iqpilot_private/navd/helpers.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "7506530560907e8a39e54c05c988c5be3b86bbdaa5279059ada9d847d1e2889c",
"sha256": "f6ad0590f2a0bdbed56e7126203df2f6cdad86074f76ab9dded38b9e1600c8af",
"size": 135936
},
"python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "905391fcf31991b315abe6126797bc870066352596cd2e9574b289a9c0f386be",
"sha256": "b4ed48cada1dfb3383ff8ee106a7d93967bf65115d4c528b60dc3d73112a2f0e",
"size": 135608
},
"python/iqpilot_private/navd/long_decel.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "33f91bfad8894347729d4fd072e8a81af766240e5ec8921de0f92230e3d5faf1",
"sha256": "e68aca6829cb00c5eda5330e1dfedd6269381ea5831abaca1055adf6df1f53ff",
"size": 69728
},
"python/iqpilot_private/navd/mapbox_client.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "4572bb1d704de4b9919d4e81e7c28d64197dadbdd8d84f4f1a203ad924d000f6",
"sha256": "ebd30c5c34fbbe82d7d24343a2e791919e526997d192fd1441df749b15526704",
"size": 68088
},
"python/iqpilot_private/navd/nav_cameras.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "5087cd98a1bf042e0f3d19021af43a0d12ae0ce3f502158a0f4d3a0ace2bbabe",
"sha256": "6e69c5fcac3c302e401b3ed3735b42f3de4af4e0d188d15a7add7aa02920f4c5",
"size": 135440
},
"python/iqpilot_private/navd/navd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "f26cb01eaad63b14c686932496a62a3ec5aef1ebb4f36fb46680c124c77e0e16",
"size": 664448
"sha256": "55c88f8c5996dd7094c0feaf454fe0f1aec97276aed8926d7e8f6d1597de5a83",
"size": 664480
},
"python/iqpilot_private/navd/navrenderd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "3e4a79640333390203e50fb226b00b72aa97a78637544e0f616ed54547c40489",
"sha256": "b971e1093f5713a633636d6ee74891206a2d5abfb492406c7ae5df7a66bdc0d9",
"size": 135560
},
"python/iqpilot_private/navd/osm_cameras.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "cc7040441e8ce97be6ba7bdb371761e7f14c3caf99ab15bf343635a69f4e0fb1",
"sha256": "882ccc23c3de2e4e34c685a7abb00075f6c5be634eaf7e81cab86461c624449d",
"size": 136368
},
"python/iqpilot_private/navd/reroute.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "2b9357dd886231d18e4a9b180b91ea2747f5fa23d91d2d349e032286ea330db2",
"sha256": "df0bf89d492dba3b6c3792dde1bcb30a60e692f62519be7c62a517ef514ec276",
"size": 136072
},
"python/iqpilot_private/navd/route_manager.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "3a85759d30234271a83a9473d6d18af74d672c892f686b5d082e9b6df1b70159",
"sha256": "0311a1fbde1510b31055606db21a7c5f74e6872f9f1aabd6fb4a0a501395eaef",
"size": 600528
},
"python/iqpilot_private/navd/runtime_common.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "2423bca9e7637997d1b6a86365cbc9e9a7d47c89f96aa21be4de32a898ad795c",
"sha256": "1f08c7c462b4cb08a8de9140d968ef5dc74b31afffe16c9f8fad4e0271bec6cc",
"size": 135752
},
"python/iqpilot_private/navd/turn_desire.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "12b333b4c8904483cae4bae1346016b24b9bee0866d332b2b61dc9fbc30a14b7",
"sha256": "aea71be79c99884a0aeaaf8e5c0f70e37bb4f0c0123025cd07df830189774ba9",
"size": 69832
},
"python/iqpilot_private/navd/web_server.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "3954330e8004abad9dd31f3a02b57306dc7e83e73ee0ba4072b1b4ecc7f88dfc",
"sha256": "5c7dd6ef7dafb9a35662ee3d22c08b17b02807df736bf146af88cb260f5bbf09",
"size": 201384
},
"runtime": {
@@ -105,20 +105,20 @@
}
},
"signatures": {
"python/iqpilot_private/navd/__init__.cpython-312-aarch64-linux-gnu.so": "j3fNuxY5XzC219Okj5DV4kId8oUAKcwTcZhwLpp8S/kWXpFv4gHo43Van3A+OkWPFa50ZvKqWH7WhoQc8IgPBw==",
"python/iqpilot_private/navd/event_builder.cpython-312-aarch64-linux-gnu.so": "+Pj9wj6MC4cZ8YxZvvT4XlrzS/N6gVDf5LGZ6kkWiJhVuolBW4ey5KSjbggp4NSIiZWi9aARMZfGsWabQi+nDA==",
"python/iqpilot_private/navd/helpers.cpython-312-aarch64-linux-gnu.so": "KYCV1qnZ8+mrne7n0H7x+kTu8x/U9H71SXYZt3URy/yIgdtTTXkSS6IQs1UvusfMHzRdBGcT58ado3UcLpsNAg==",
"python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so": "qC8oELP8nBJSvaDsCMo7/JqoiqCZBJa/LeUE0b36DdkT7kzHSLQ+MuGP5Vc8ms8VowBh447LaLq2WWURGB/fAQ==",
"python/iqpilot_private/navd/long_decel.cpython-312-aarch64-linux-gnu.so": "6kFnnmSPQPXe1HuiBL14HrPx5cr2XmOYzTcESKHvt7DAL7M5tMKy0BWufo34F/LH4MYXQu1/k5TyzHt4wQ7jBw==",
"python/iqpilot_private/navd/mapbox_client.cpython-312-aarch64-linux-gnu.so": "F0JFe9uN7YtdFvtSwHXPch4l6Cz7yWTOtKPHOdBxqk9CkHh0vfO2d4EAeGTpvHINFS5Kf2KcF99qyEhWfr31Ag==",
"python/iqpilot_private/navd/nav_cameras.cpython-312-aarch64-linux-gnu.so": "7sQ0zXNASjEVBknO94P5wnMifC+8cVAKaBBbQjfbbaV1lsg18mm/rtTOGlFX7NJD23kJFxyLJmJ+wKjRAZXkBg==",
"python/iqpilot_private/navd/navd.cpython-312-aarch64-linux-gnu.so": "VTg/Ojx4I8+JJz+vf9gDwUSBY9MCUvIg9wGnNJiQwIKDJr/VkSRoaiM4nMoazauhLEgYpmlfX7C8OEAWswKhBQ==",
"python/iqpilot_private/navd/navrenderd.cpython-312-aarch64-linux-gnu.so": "4i882AjYz/zCgoGlr1LGNcVGfzyybHPMOL0aKva8VNsHBEtMbnnetecRBurtAzlU6O8+p89gY1GNDaJfyi7OAQ==",
"python/iqpilot_private/navd/osm_cameras.cpython-312-aarch64-linux-gnu.so": "hO9yDTiAaidM3yQCfh4J8vP54R2z42ejhkqU8E6l+wWUXhRrfS4Z6qLyxeQqQGJSPSAMfsWl/yODWogqoH+MDQ==",
"python/iqpilot_private/navd/reroute.cpython-312-aarch64-linux-gnu.so": "xPCGbuG+pQUqCjBx3NijbInbyo/rRteGLCbhVun2odrw1lGMx4JwzSyNUUsEU7xFPEZ+oZwPUaZcVOHRPnknAw==",
"python/iqpilot_private/navd/route_manager.cpython-312-aarch64-linux-gnu.so": "ZDojmbuEkZUbZ7a56pPvO8hFcYHM5zB9OvGs+r90swvPzU0GRm4SkEiDRLhG6zSyP+vHhJnrsTs+dvyaBkzvCQ==",
"python/iqpilot_private/navd/runtime_common.cpython-312-aarch64-linux-gnu.so": "888SP/NpbJtdv2BKbsW7WzuD0pKh2rM50YvS4GlD8brL2xq7a7GPPeKM/b5F+pTzVvS7CbUmELqh3HmeAtOKCw==",
"python/iqpilot_private/navd/turn_desire.cpython-312-aarch64-linux-gnu.so": "cmYohrfIfttybS+9x+b9DFzgxyaDGNtgr+olaMWUZ7ytgenB+EvwKAXJHmicFtvBTsKOxYzywKR2zLb71DDFDg==",
"python/iqpilot_private/navd/web_server.cpython-312-aarch64-linux-gnu.so": "laxlmUb7INupC/eu6k/eok3bXW4c5H7Lui5MDJXLCoSFCiutla374A6rN8Dh+g3AcSY2NfLBzrnyqQDqQH0KBw=="
"python/iqpilot_private/navd/__init__.cpython-312-aarch64-linux-gnu.so": "E3+Gd+btNi+4qFqM+nFMfWgCLJhP21ZgIwZrE/CJjqafB1htd/qOhh9QtrulS46HW5geZ/nwW3jlxPbFeNubBw==",
"python/iqpilot_private/navd/event_builder.cpython-312-aarch64-linux-gnu.so": "hHQQCCPcxRXdO9vko9476AYbgbfNwhi9bVYpoOzQQStPC8DbIO0QN/RRzaJVGr2kgJGZlXDmnGl85m7EHgC1BA==",
"python/iqpilot_private/navd/helpers.cpython-312-aarch64-linux-gnu.so": "Wz5yfWlMeoASj274KqrCWwEJD/wtm5WyxLenceYu7MkXaMlN1QyztvJhq8Y8K8Jfis7R5fQ/aAnlsQgWTIVEBg==",
"python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so": "6WFWUXmkyy21rh/9xa7s7S7CTiCRnywOahU859T6iP0447NkVxHRYmXAVCH1/vRDPXASHRNPyUk8oSDLIGK1Ag==",
"python/iqpilot_private/navd/long_decel.cpython-312-aarch64-linux-gnu.so": "ZUsjQ/AGPa5pVKAv3IkycDPIVBWh+4jBNor/xrUi7BBPHki8G6T1grv3exeHhr2WF2JiqN727ge3JaqvlU1KDQ==",
"python/iqpilot_private/navd/mapbox_client.cpython-312-aarch64-linux-gnu.so": "t+VHmWhxf7oKcR5e+tJe6hPKy6xz0bMY84WEhujwcwBlWgIXscfc9aNSP6e4N7GITW7cOn+RlDRTTfwQsA6PDA==",
"python/iqpilot_private/navd/nav_cameras.cpython-312-aarch64-linux-gnu.so": "qOrpSi2I+aFLKN4WjBljfzbk2U1rvOgDuyV+p7IvshugeUOoqTKiYYSG9xwyi2/eB/syyfiVFoqAg+ETFXXPDA==",
"python/iqpilot_private/navd/navd.cpython-312-aarch64-linux-gnu.so": "PcFJWRYrb52pJCbTdsAPm3IC0OR9zt/7P0uc1GlQr4CVGvzfXukbnt/7sSeL8T29w7drf9MfGkpRe4BjbWwtDw==",
"python/iqpilot_private/navd/navrenderd.cpython-312-aarch64-linux-gnu.so": "gp1noPJ35MRVcQL3t4wKbsEH0L0+n9Yf8Zty+VCY7uy9EsK8211kW2YIw7Lcyw879VLLbVntHIWz/BMankSeAw==",
"python/iqpilot_private/navd/osm_cameras.cpython-312-aarch64-linux-gnu.so": "N6yH0R8V/Cw1J3hYPfFeZXByolRwtACwNe2wHMzPEQ69UTWnSNFRXWjOLCEvtwB8Z+iglAqFx8uztZ6wXZKUCQ==",
"python/iqpilot_private/navd/reroute.cpython-312-aarch64-linux-gnu.so": "oQQU70Yp8wzkqB9Ktm2KDEHNVZfB3otqIoG2mkqQNh6QdtVKXraIv6cAbt2Zjob5914C06El2z+NPZQE5NjODQ==",
"python/iqpilot_private/navd/route_manager.cpython-312-aarch64-linux-gnu.so": "ayPdxCUGA/o+AavLMd9mGDY6zYnZk54gAEVaMQXFYlztjXiREC+IEmV1iETOYcsEFAxr8qtUFfcwZ3p7rXljCw==",
"python/iqpilot_private/navd/runtime_common.cpython-312-aarch64-linux-gnu.so": "Rh0wUjYjRabedjFSnsE9BAjILjA1+RHMdfuCqTye1AlhFlXVUQMRo6Ier6Z71kyiOYw+CXCOrHUF9fP69cP4DQ==",
"python/iqpilot_private/navd/turn_desire.cpython-312-aarch64-linux-gnu.so": "13bqig90oceup9ovo8weu9UpdQN/l/pq7REFm8dwwgqquuh8jJFkzNz7dvIhDlaxIGAQwzWJe4+QzIQfhg0jAA==",
"python/iqpilot_private/navd/web_server.cpython-312-aarch64-linux-gnu.so": "Vm1Inx4XY0BOmLuNizOCRNAl4tPDcvJ/4A+TwVSr1cZuMg7pg1jeFg613e6cFM6t08hYvPsysXc2+gD69zkcBA=="
}
}

View File

@@ -242,8 +242,8 @@ struct IQPlan @0xda401323ae805f2b {
}
struct E2eAlerts {
greenLightAlert @0 :Bool;
leadDepartAlert @1 :Bool;
pathOpen @0 :Bool;
leadPullaway @1 :Bool;
}
}
@@ -734,6 +734,7 @@ struct IQVehicleTracks @0xb877ef4b20a4ae22 {
frameHeight @2 :UInt16;
processingMs @3 :Float32;
tracks @4 :List(Track);
wide @5 :Bool;
struct Track {
# box corners normalized [0,1] in the road-camera frame

View File

@@ -1,3 +1,8 @@
from collections import deque
import numpy as np
class FirstOrderFilter:
def __init__(self, x0, rc, dt, initialized=True):
self.x = x0
@@ -32,3 +37,35 @@ class BounceFilter(FirstOrderFilter):
self.velocity.x = 0.0
self.x += self.velocity.x
return self.x
class MyMovingAverage:
def __init__(self, window_size, value=None):
self.window_size = window_size
if value is not None:
self.values = deque([value] * window_size, maxlen=window_size)
self.sum = value * window_size
self.result = value
else:
self.values = deque(maxlen=window_size)
self.sum = 0
self.result = 0
def set(self, value):
self.values.clear()
self.values.append(value)
self.sum = value
self.result = value
return value
def set_all(self, value):
self.values = deque([value] * self.window_size, maxlen=self.window_size)
self.sum = value * self.window_size
self.result = value
return value
def process(self, value, median=False):
self.values.append(value)
self.sum = sum(self.values)
self.result = float(np.median(self.values)) if median else float(self.sum) / len(self.values)
return self.result

View File

@@ -63,6 +63,14 @@ public:
inline bool getBool(const std::string &key, bool block = false) {
return get(key, block) == "1";
}
inline int getInt(const std::string &key, bool block = false) {
std::string value = get(key, block);
return value.empty() ? 0 : std::stoi(value);
}
inline float getFloat(const std::string &key, bool block = false) {
std::string value = get(key, block);
return value.empty() ? 0.0F : std::stof(value);
}
std::map<std::string, std::string> readAll();
// helpers for writing values
@@ -73,10 +81,24 @@ public:
inline int putBool(const std::string &key, bool val) {
return put(key.c_str(), val ? "1" : "0", 1);
}
inline int putInt(const std::string &key, int val) {
const std::string value = std::to_string(val);
return put(key.c_str(), value.c_str(), value.size());
}
inline int putFloat(const std::string &key, float val) {
const std::string value = std::to_string(val);
return put(key.c_str(), value.c_str(), value.size());
}
void putNonBlocking(const std::string &key, const std::string &val);
inline void putBoolNonBlocking(const std::string &key, bool val) {
putNonBlocking(key, val ? "1" : "0");
}
inline void putIntNonBlocking(const std::string &key, int val) {
putNonBlocking(key, std::to_string(val));
}
inline void putFloatNonBlocking(const std::string &key, float val) {
putNonBlocking(key, std::to_string(val));
}
private:
void asyncWriteThread();

View File

@@ -9,12 +9,14 @@ except ImportError:
pass
class ParamKeyFlag(IntFlag):
PERSISTENT = 1
CLEAR_ON_MANAGER_START = 2
CLEAR_ON_ONROAD_TRANSITION = 4
CLEAR_ON_OFFROAD_TRANSITION = 8
DONT_LOG = 16
DEVELOPMENT_ONLY = 32
# must stay in lockstep with enum ParamKeyFlag in common/params.h
PERSISTENT = 0x02
CLEAR_ON_MANAGER_START = 0x04
CLEAR_ON_ONROAD_TRANSITION = 0x08
CLEAR_ON_OFFROAD_TRANSITION = 0x10
DONT_LOG = 0x20
DEVELOPMENT_ONLY = 0x40
CLEAR_ON_IGNITION_ON = 0x80
ALL = 0xFFFFFFFF
class ParamKeyType(IntEnum):
@@ -62,6 +64,14 @@ except ImportError:
except (FileNotFoundError, NotADirectoryError, IsADirectoryError):
return False
def get_int(self, key, block: bool = False) -> int:
value = self.get(key, block=block)
return int(value) if value else 0
def get_float(self, key, block: bool = False) -> float:
value = self.get(key, block=block)
return float(value) if value else 0.0
def put(self, key, dat):
if isinstance(dat, str):
dat = dat.encode("utf-8")
@@ -78,12 +88,24 @@ except ImportError:
def put_bool(self, key, val: bool):
self.put(key, b"1" if val else b"0")
def put_int(self, key, val: int):
self.put(key, str(val))
def put_float(self, key, val: float):
self.put(key, str(val))
def put_nonblocking(self, key, dat):
self.put(key, dat)
def put_bool_nonblocking(self, key, val: bool):
self.put_bool(key, val)
def put_int_nonblocking(self, key, val: int):
self.put_int(key, val)
def put_float_nonblocking(self, key, val: float):
self.put_float(key, val)
def remove(self, key):
try:
os.remove(self._p(key))

View File

@@ -153,8 +153,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
// --- iqpilot params --- //
{"ApiCache_DriveStats", {PERSISTENT, JSON}},
{"AutoLaneChangeBsmDelay", {PERSISTENT, BOOL, "0"}},
{"AutoLaneChangeTimer", {PERSISTENT, INT, "0"}},
{"IQLaneChangeBsmDelay", {PERSISTENT, BOOL, "0"}},
{"IQLaneChangeTimer", {PERSISTENT, INT, "0"}},
{"NavExitLaneChange", {PERSISTENT, BOOL, "0"}},
{"IQBlinkerMinLateralSpeed", {PERSISTENT, INT, "20"}}, // MPH or km/h
{"IQBlinkerPauseLateral", {PERSISTENT, INT, "0"}},
@@ -165,28 +165,31 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"IQCarParamsPersistent", {PERSISTENT, BYTES}},
{"IQCarParamsPersistentV2", {PERSISTENT, BYTES}},
{"CarPlatformBundle", {PERSISTENT, JSON}},
{"Konn3ktVwOdometers", {PERSISTENT, JSON}},
{"Konn3ktVehicleOdometers", {PERSISTENT, JSON}},
{"ChevronInfo", {PERSISTENT, INT, "4"}},
{"DeviceBootMode", {PERSISTENT, INT, "0"}},
{"IQDevUIInfo", {PERSISTENT, INT, "0"}},
{"EnableEsimProvisioning", {PERSISTENT, BOOL, "1"}},
{"GreenLightAlert", {PERSISTENT, BOOL, "0"}},
{"EndToEndAlert", {PERSISTENT, BOOL, "0"}},
{"InteractivityTimeout", {PERSISTENT, INT, "0"}},
{"IsDevelopmentBranch", {CLEAR_ON_MANAGER_START, BOOL}},
{"IsReleaseIqBranch", {CLEAR_ON_MANAGER_START, BOOL}},
{"LastGPSPositionIQLoc", {PERSISTENT, STRING}},
{"LeadDepartAlert", {PERSISTENT, BOOL, "0"}},
{"EndToEndLeadAlert", {PERSISTENT, BOOL, "0"}},
{"LongIncrementsEnabled", {PERSISTENT, BOOL, "0"}},
{"LongIncrementTapStep", {PERSISTENT, INT, "1"}},
{"LongIncrementHoldStep", {PERSISTENT, INT, "5"}},
{"IQE2ESetSpeedMode", {PERSISTENT, INT, "0"}},
{"IQE2ESetSpeedUseCurrent", {PERSISTENT, BOOL, "0"}},
{"IQE2ESetSpeedMph", {PERSISTENT, INT, "65"}},
{"expSpeedConv", {PERSISTENT, BOOL, "0"}},
{"MaxTimeOffroad", {PERSISTENT, INT, "1800"}},
{"NightMode", {PERSISTENT, BOOL, "0"}},
{"newLeadMpc", {PERSISTENT, BOOL, "1"}},
{"ModelRunnerTypeCache", {CLEAR_ON_ONROAD_TRANSITION, INT}},
{"ForceOnroadUntil", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, INT, "0"}},
{"OffroadMode", {CLEAR_ON_MANAGER_START, BOOL}},
{"IQAlwaysOffroad", {CLEAR_ON_MANAGER_START, BOOL}},
{"Offroad_TiciSupport", {CLEAR_ON_MANAGER_START, JSON}},
{"OnroadScreenOffBrightness", {PERSISTENT, INT, "0"}},
{"OnroadScreenOffTimer", {PERSISTENT, INT, "15"}},
@@ -225,6 +228,32 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
// iqpilot car specific params
{"HyundaiLongitudinalTuning", {PERSISTENT, INT, "0"}},
{"AutoCruiseControl", {PERSISTENT, INT, "0"}},
{"AutoEngage", {PERSISTENT, INT, "0"}},
{"CanfdDebug", {PERSISTENT, INT, "0"}},
{"CanfdHDA2", {PERSISTENT, INT, "0"}},
{"CarrotCruiseAtcDecel", {PERSISTENT, INT, "-1"}},
{"CarrotCruiseDecel", {PERSISTENT, INT, "-1"}},
{"CruiseButtonTest1", {PERSISTENT, INT, "8"}},
{"CruiseButtonTest2", {PERSISTENT, INT, "30"}},
{"CruiseButtonTest3", {PERSISTENT, INT, "1"}},
{"CustomSteerDeltaDown", {PERSISTENT, INT, "0"}},
{"CustomSteerDeltaDownLC", {PERSISTENT, INT, "0"}},
{"CustomSteerDeltaUp", {PERSISTENT, INT, "0"}},
{"CustomSteerDeltaUpLC", {PERSISTENT, INT, "0"}},
{"CustomSteerMax", {PERSISTENT, INT, "0"}},
{"EnableCornerRadar", {PERSISTENT, INT, "0"}},
{"EnableRadarTracks", {PERSISTENT, INT, "0"}},
{"EnableRadarTracksResult", {PERSISTENT | CLEAR_ON_MANAGER_START, INT}},
{"FingerPrints", {PERSISTENT | CLEAR_ON_MANAGER_START, STRING}},
{"HDPuse", {PERSISTENT, INT, "0"}},
{"HapticFeedbackWhenSpeedCamera", {PERSISTENT, INT, "0"}},
{"HyundaiCameraSCC", {PERSISTENT, INT, "0"}},
{"IsLdwsCar", {PERSISTENT, INT, "0"}},
{"LaneLineCheck", {PERSISTENT, INT, "0"}},
{"LongitudinalPersonalityMax", {PERSISTENT, INT, "3"}},
{"MaxAngleFrames", {PERSISTENT, INT, "89"}},
{"SpeedFromPCM", {PERSISTENT, INT, "2"}},
{"SubaruStopAndGo", {PERSISTENT, BOOL, "0"}},
{"SubaruStopAndGoManualParkingBrake", {PERSISTENT, BOOL, "0"}},
{"TeslaCoopSteering", {PERSISTENT, BOOL, "0"}},
@@ -255,9 +284,9 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
// iqpilot model params
{"CameraOffset", {PERSISTENT, FLOAT, "0.0"}},
{"LagdToggle", {PERSISTENT, BOOL, "1"}},
{"LagdToggleDelay", {PERSISTENT, FLOAT, "0.2"}},
{"LagdValueCache", {PERSISTENT, FLOAT, "0.2"}},
{"IQLiveSteerDelay", {PERSISTENT, BOOL, "1"}},
{"IQSoftwareSteerDelay", {PERSISTENT, FLOAT, "0.2"}},
{"IQSteerDelayCache", {PERSISTENT, FLOAT, "0.2"}},
{"LaneChangeBsd", {PERSISTENT, INT, "0"}}, // -1 ignore BSD, 0 default, 1 block lane change on BSD
{"LaneChangeContinuous", {PERSISTENT, BOOL, "0"}}, // 0 one-shot per blinker, 1 chain on held blinker (torque-gated)
{"LaneChangeDelay", {PERSISTENT, FLOAT, "0.0"}}, // tenths of a second; scaled by 0.1 in desire_helper

View File

@@ -33,11 +33,17 @@ cdef extern from "common/params.h":
c_Params(string) except + nogil
string get(string, bool) nogil
bool getBool(string, bool) nogil
int getInt(string, bool) nogil
float getFloat(string, bool) nogil
int remove(string) nogil
int put(string, string) nogil
void putNonBlocking(string, string) nogil
void putBoolNonBlocking(string, bool) nogil
int putBool(string, bool) nogil
int putInt(string, int) nogil
int putFloat(string, float) nogil
void putIntNonBlocking(string, int) nogil
void putFloatNonBlocking(string, float) nogil
bool checkKey(string) nogil
ParamKeyType getKeyType(string) nogil
optional[string] getKeyDefaultValue(string) nogil
@@ -136,6 +142,20 @@ cdef class Params:
r = self.p.getBool(k, block)
return r
def get_int(self, key, bool block=False):
cdef string k = self.check_key(key)
cdef int r
with nogil:
r = self.p.getInt(k, block)
return r
def get_float(self, key, bool block=False):
cdef string k = self.check_key(key)
cdef float r
with nogil:
r = self.p.getFloat(k, block)
return r
def _put_cast(self, key, dat):
cdef string k = self.check_key(key)
cdef ParamKeyType t = self.p.getKeyType(k)
@@ -158,6 +178,16 @@ cdef class Params:
with nogil:
self.p.putBool(k, val)
def put_int(self, key, int val):
cdef string k = self.check_key(key)
with nogil:
self.p.putInt(k, val)
def put_float(self, key, float val):
cdef string k = self.check_key(key)
with nogil:
self.p.putFloat(k, val)
def put_nonblocking(self, key, dat):
cdef string k = self.check_key(key)
cdef string dat_bytes = self._put_cast(key, dat)
@@ -169,6 +199,16 @@ cdef class Params:
with nogil:
self.p.putBoolNonBlocking(k, val)
def put_int_nonblocking(self, key, int val):
cdef string k = self.check_key(key)
with nogil:
self.p.putIntNonBlocking(k, val)
def put_float_nonblocking(self, key, float val):
cdef string k = self.check_key(key)
with nogil:
self.p.putFloatNonBlocking(k, val)
def remove(self, key):
cdef string k = self.check_key(key)
with nogil:

View File

@@ -4,6 +4,61 @@ collect_ignore_glob = [
"iqdbc/safety/tests/misra/cppcheck/",
]
# --- host test harness -------------------------------------------------------
# tesla/volkswagen carstate+carcontroller import the closed-source ALC / odometer
# runtime via import_verified_module(), which gates on a rootfs integrity manifest
# that only exists on-device. Off-device (host CI / dev), install minimal stubs so
# the whole car interface set is importable and test_car_interfaces can collect and
# run. On-device the manifest is present and these stubs are never installed, so the
# real verified modules are used.
import os as _os
if not _os.path.exists("/usr/libexec/iqpilot/runtime_integrity.json"):
import sys as _sys
import types as _types
def _stub_module(name, attrs):
mod = _types.ModuleType(name)
for key, value in attrs.items():
setattr(mod, key, value)
_sys.modules[name] = mod
_noop = lambda *args, **kwargs: None # noqa: E731
class _VehicleOdometerStoreStub:
def __init__(self, *args, **kwargs):
pass
def record(self, km):
return km
_stub_module("iqpilot_private.konn3kt.iqlvbs.vehicle_state",
{"VehicleOdometerStore": _VehicleOdometerStoreStub})
_stub_module("iqpilot_private.konn3kt.iqlvbs.alc", {
"angle_lateral_control_enabled": lambda *a, **k: False,
"update_vw_alc": _noop,
"append_private_apd": _noop,
"update_mqb_carstate_alc_state": _noop,
"update_mlb_carstate_alc_state": _noop,
"update_pq_carstate_alc_state": _noop,
})
_stub_module("iqpilot_private.konn3kt.iqlvbs.iqlvbs_commander", {
"update_turn_signals": _noop,
})
_stub_module("iqpilot_private.konn3kt.hephaestus.vw_pq_flasher", {})
try:
from openpilot.system.proprietary_runtime import _verified_import as _vi
_real_import_verified_module = _vi.import_verified_module
def _import_verified_module(bundle, module):
return _sys.modules.get(module) or _real_import_verified_module(bundle, module)
_vi.import_verified_module = _import_verified_module
except Exception:
pass
# --- end host test harness ---------------------------------------------------
_ABSTRACT_BASES = frozenset(("CarSafetyTest", "AolSafetyTestBase", "SafetyTest", "SafetyTestBase"))
def _method_from_base(cls, method_name):

View File

@@ -6,63 +6,18 @@ It does not replace the repository-wide license files for IQ.Pilot itself or any
## Credits:
### Hyundai / Kia / Genesis (HKG) - Sunnypilot
#### Contributors
Full credit for the Hyundai / Kia / Genesis port, along with the HKG-specific tuning work used in this tree, belongs to:
- Jason Wen
- James Vecellio-Grant
- sunnypilot
#### Attribution
This repository carries HKG-side tuning and port lineage that originated from, and/or is verbatim their work. They are the authors of the Sunnypilot-side HKG port and its tuning, and are accredited as such, IQ.Lvbs claims no copyright, credit, or authorship of the components listed below.
#### Upstream License Notice
The HKG work attributed above is acknowledged here under LICENSE notice:
```text
MIT License
Copyright (c) 2024 Jason Wen, James Vecellio-Grant, Sunnypilot, and a number of other contributors.
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 `iqdbc_repo/iqdbc/car/hyundai` and `iqdbc_repo/iqdbc/iqpilot/car/hyundai`.
### Hyundai / Kia / Genesis (HKG) - carrotpilot
#### Contributors
Full credit for the carrotpilot-side HKG tuning lineage carried in this tree belongs to:
Full credit for the Hyundai / Kia / Genesis port and its HKG-specific tuning belongs to:
- carrotpilot devs (ajoutam)
- carrotpilot
- ajouatom
#### Attribution
This repository carries HKG-side tuning and port lineage that originated from, and/or is verbatim their work. They are the authors of the carrotpilot-side HKG tuning credited here, and are accredited as such, IQ.Lvbs claims no copyright, credit, or authorship of the components listed below.
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
@@ -80,7 +35,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
#### Scope
This licensing is limited to `iqdbc_repo/iqdbc/car/hyundai` and `iqdbc_repo/iqdbc/iqpilot/car/hyundai`.
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

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:

View File

@@ -209,6 +209,8 @@ struct CarState {
lateralAvailable @61 :Bool; # lateral control is available even if cruise is faulted
cruiseFaultLateralMode @62 :Bool; # cruise is faulted but lateral control is still active
radarDisableFailed @66 :Bool;
# Physical vehicle odometer in kilometers. Zero means unavailable on this platform.
odometer @67 :Float64;
# cruise state
cruiseState @10 :CruiseState;
@@ -236,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;
@@ -300,13 +338,16 @@ struct CarState {
setCruise @9;
resumeCruise @10;
gapAdjustCruise @11;
lfaButton @12;
paddleLeft @13;
paddleRight @14;
}
}
# 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;
@@ -344,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 {
@@ -399,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;
@@ -434,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;
@@ -497,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;
@@ -593,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);
}
@@ -766,11 +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
vEgoStartingDEPRECATED @59 :Float32;
startAccelDEPRECATED @32 :Float32;
startingStateDEPRECATED @70 :Bool;
vEgoStarting @59 :Float32;
startAccel @32 :Float32;
startingState @70 :Bool;
vEgoStoppingDEPRECATED @29 :Float32;
stoppingDecelRateDEPRECATED @52 :Float32;
}

View File

@@ -11,7 +11,7 @@ from iqdbc.car.mock.values import CAR as MOCK
from iqdbc.car.values import BRANDS
from iqdbc.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN
from iqdbc.iqpilot.car.interfaces import setup_interfaces as iqpilot_interfaces
from iqdbc.lvbs.car.interfaces import apply_iq_car_config as iqpilot_interfaces
FRAME_FINGERPRINT = 100 # 1s

View File

@@ -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.iqpilot.car.chrysler.carcontroller_ext import CarControllerExt
from iqdbc.iqpilot.car.chrysler.aol import AolCarController
from iqdbc.iqpilot.car.chrysler.values_ext import ChryslerFlagsIQ
from iqdbc.lvbs.car.chrysler.iq_carcontroller import IQCarController
from iqdbc.lvbs.car.chrysler.aol import AolCarController
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:

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.iqpilot.car.chrysler.carstate_ext import CarStateExt
from iqdbc.iqpilot.car.chrysler.aol import AolCarState
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),
}

View File

@@ -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.iqpilot.car.fingerprints_ext import merge_fw_versions
from iqdbc.iqpilot.car.chrysler.fingerprints_ext import FW_VERSIONS_EXT
Ecu = CarParams.Ecu
@@ -789,4 +789,6 @@ FW_VERSIONS = {
},
}
FW_VERSIONS = merge_fw_versions(FW_VERSIONS, FW_VERSIONS_EXT)
FW_VERSIONS = extend_fw_versions(FW_VERSIONS, FW_VERSIONS_EXT)

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.iqpilot.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

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.iqpilot.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}),

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.iqpilot.car.gm.carstate_ext import CarStateExt
from iqdbc.iqpilot.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

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.iqpilot.car.fingerprints_ext import merge_fingerprints
from iqdbc.iqpilot.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 = merge_fingerprints(FINGERPRINTS, FINGERPRINTS_EXT)
FINGERPRINTS = extend_fingerprints(FINGERPRINTS, FINGERPRINTS_EXT)

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.iqpilot.car.gm.interface_ext import CarInterfaceExt
from iqdbc.iqpilot.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):
@@ -241,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
@@ -254,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

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.iqpilot.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)

View File

@@ -8,8 +8,8 @@ from iqdbc.car.honda.values import CAR, CruiseButtons, HONDA_BOSCH, HONDA_BOSCH_
HONDA_BOSCH_TJA_CONTROL, HONDA_NIDEC_ALT_PCM_ACCEL, CarControllerParams
from iqdbc.car.interfaces import CarControllerBase
from iqdbc.iqpilot.car.honda.aol import AolCarController
from iqdbc.iqpilot.car.honda.gas_interceptor import GasInterceptorCarController
from iqdbc.lvbs.car.honda.aol import AolCarController
from iqdbc.lvbs.car.honda.gas_interceptor import GasInterceptorCarController
VisualAlert = structs.CarControl.HUDControl.VisualAlert
LongCtrlState = structs.CarControl.Actuators.LongControlState

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.iqpilot.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

View File

@@ -2,8 +2,8 @@
from iqdbc.car.structs import CarParams
from iqdbc.car.honda.values import CAR
from iqdbc.iqpilot.car.fingerprints_ext import merge_fw_versions
from iqdbc.iqpilot.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
@@ -1098,4 +1098,4 @@ FW_VERSIONS = {
},
}
FW_VERSIONS = merge_fw_versions(FW_VERSIONS, FW_VERSIONS_EXT)
FW_VERSIONS = extend_fw_versions(FW_VERSIONS, FW_VERSIONS_EXT)

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.iqpilot.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

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.iqpilot.car.honda.values_ext import HondaFlagsIQ, HondaSafetyFlagsIQ
from iqdbc.lvbs.car.honda.iq_values import HondaFlagsIQ, HondaSafetyFlagsIQ
TransmissionType = structs.CarParams.TransmissionType

View File

@@ -1,31 +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.iqpilot.car.hyundai.escc import EsccCarController
from iqdbc.iqpilot.car.hyundai.longitudinal.controller import LongitudinalController
from iqdbc.iqpilot.car.hyundai.lead_data_ext import LeadDataCarController
from iqdbc.iqpilot.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
def _use_stock_lkas_request_path(CP) -> bool:
return CP.carFingerprint == CAR.HYUNDAI_PALISADE
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))
@@ -50,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])
@@ -68,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 not _use_stock_lkas_request_path(self.CP) and 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 = False if _use_stock_lkas_request_path(self.CP) else (CC.latActive and not apply_steer_req)
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))
@@ -125,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
@@ -166,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)

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.iqpilot.car.hyundai.carstate_ext import CarStateExt
from iqdbc.iqpilot.car.hyundai.escc import EsccCarStateBase
from iqdbc.iqpilot.car.hyundai.aol import AolCarState
from iqdbc.iqpilot.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),
}

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.iqpilot.car.fingerprints_ext import merge_fw_versions
from iqdbc.iqpilot.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 = merge_fw_versions(FW_VERSIONS, FW_VERSIONS_EXT)

View File

@@ -1,21 +1,33 @@
import copy
import crcmod
from iqdbc.car.hyundai.values import CAR, HyundaiFlags
from iqdbc.iqpilot.car.hyundai.escc import EnhancedSmartCruiseControl
from iqdbc.iqpilot.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
def _use_stock_scc_surrogates(CP) -> bool:
return CP.carFingerprint == CAR.HYUNDAI_PALISADE
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",
@@ -34,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
@@ -42,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
@@ -59,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
@@ -76,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
@@ -88,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:
@@ -128,152 +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():
values = {
"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 _use_stock_scc_surrogates(CP):
values["ObjValid"] = 1
values["ACC_ObjStatus"] = 1
values["ACC_ObjDist"] = 1
return values
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():
values = {
"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
}
if not _use_stock_scc_surrogates(CP):
values["ObjDistStat"] = lead_data.object_rel_gap
return values
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)

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.iqpilot.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

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.iqpilot.car.hyundai.escc import ESCC_MSG
from iqdbc.iqpilot.car.hyundai.longitudinal.helpers import get_longitudinal_tune
from iqdbc.iqpilot.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,53 +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 = 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:
@@ -152,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

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.iqpilot.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

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

View File

@@ -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

View File

@@ -1,61 +0,0 @@
from iqdbc.can import CANParser, CANPacker
from iqdbc.car import Bus
from iqdbc.car.hyundai import hyundaican
from iqdbc.car.hyundai.values import CAR, DBC
class DummyHudControl:
leadDistanceBars = 3
leadVisible = True
class DummyLeadData:
lead_visible = False
lead_rel_speed = -7
lead_distance = 42
object_gap = 5
object_rel_gap = 2
class DummyTuning:
comfort_band_upper = 0.2
comfort_band_lower = 0.3
jerk_upper = 1.7
jerk_lower = 1.2
desired_accel = 0.4
actual_accel = 0.3
stopping = False
class DummyCP:
carFingerprint = CAR.HYUNDAI_PALISADE
flags = 0
def _decode(msg_name: str, addr: int, dat: bytes):
cp = CANParser(DBC[CAR.HYUNDAI_PALISADE][Bus.pt], [(msg_name, 0)], 0)
cp.update([(0, [(addr, dat, 0)])])
return cp.vl[msg_name]
def test_palisade_uses_stock_scc_surrogates():
packer = CANPacker(DBC[CAR.HYUNDAI_PALISADE][Bus.pt])
cp = DummyCP()
hud = DummyHudControl()
tuning = DummyTuning()
lead = DummyLeadData()
scc11 = hyundaican.create_acc_commands(
packer, True, 0.5, 3.0, 1, lead, hud, 72, False, False, True, cp, False, tuning
)[0]
scc14 = hyundaican.create_acc_commands(
packer, True, 0.5, 3.0, 1, lead, hud, 72, False, False, True, cp, False, tuning
)[2]
scc11_vals = _decode("SCC11", scc11[0], scc11[1])
assert scc11_vals["ObjValid"] == 1
assert scc11_vals["ACC_ObjStatus"] == 1
assert scc11_vals["ACC_ObjDist"] == 1
scc14_vals = _decode("SCC14", scc14[0], scc14[1])
assert "ObjDistStat" not in scc14_vals or scc14_vals["ObjDistStat"] == 0

View File

@@ -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

View File

@@ -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

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.iqpilot.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):
@@ -70,14 +83,31 @@ class HyundaiSafetyFlags(IntFlag):
ALT_LIMITS_2 = 512
class HyundaiSafetyFlagsIQ:
DEFAULT = 0
ESCC = 16
MAIN_BTN_LONG_TOGGLE = 32
HAS_LDA_BUTTON = 64
NON_SCC = 128
class HyundaiFlagsIQ(IntFlag):
ENHANCED_SCC = 1
HAS_LFA_BUTTON = 2
MAIN_BTN_LONG_TOGGLE = 2 ** 2
ENABLE_RADAR_TRACKS_DEPRECATED = 2 ** 3
LONG_TUNING_DYNAMIC = 2 ** 4
LONG_TUNING_PREDICTIVE = 2 ** 5
NON_SCC = 2 ** 6
NON_SCC_RADAR_FCA = 2 ** 7
NON_SCC_NO_FCA = 2 ** 8
SPEED_LIMIT_AVAILABLE = 2 ** 9
HAS_LKAS12 = 2 ** 10
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
@@ -87,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
@@ -103,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
@@ -122,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
@@ -153,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
@@ -174,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),
@@ -260,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]))],
@@ -285,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
@@ -300,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",
@@ -321,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
@@ -362,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])),
@@ -386,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(
@@ -405,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
@@ -421,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])),
],
@@ -471,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(
@@ -499,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(
@@ -528,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])),
@@ -537,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])),
@@ -605,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
@@ -660,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]]:
@@ -840,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)
@@ -859,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)

View File

@@ -18,7 +18,7 @@ from iqdbc.car.values import PLATFORMS
from iqdbc.can import CANParser
from iqdbc.car.carlog import carlog
from iqdbc.iqpilot.car.interfaces import CarInterfaceBaseIQ
from iqdbc.lvbs.car.interfaces import CarInterfaceBaseIQ
GearShifter = structs.CarState.GearShifter
ButtonType = structs.CarState.ButtonEvent.Type
@@ -332,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]:
@@ -348,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"""

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.iqpilot.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}),

View File

@@ -3,7 +3,7 @@ from iqdbc.car.interfaces import CarInterfaceBase
from iqdbc.car.nissan.carcontroller import CarController
from iqdbc.car.nissan.carstate import CarState
from iqdbc.car.nissan.values import CAR, NissanSafetyFlags
from iqdbc.iqpilot.car.nissan.values import NissanSafetyFlagsIQ
from iqdbc.lvbs.car.nissan.values import NissanSafetyFlagsIQ
class CarInterface(CarInterfaceBase):
CarState = CarState

View File

@@ -6,7 +6,7 @@ from iqdbc.car.interfaces import CarControllerBase
from iqdbc.car.rivian.riviancan import create_lka_steering, create_longitudinal, create_wheel_touch, create_adas_status
from iqdbc.car.rivian.values import CarControllerParams
from iqdbc.iqpilot.car.rivian.aol import AolCarController
from iqdbc.lvbs.car.rivian.aol import AolCarController
class CarController(CarControllerBase, AolCarController):

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.iqpilot.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),
}

View File

@@ -4,7 +4,7 @@ from iqdbc.car.rivian.carcontroller import CarController
from iqdbc.car.rivian.carstate import CarState
from iqdbc.car.rivian.radar_interface import RadarInterface
from iqdbc.car.rivian.values import RivianSafetyFlags
from iqdbc.iqpilot.car.rivian.values import RivianFlagsIQ
from iqdbc.lvbs.car.rivian.values import RivianFlagsIQ
class CarInterface(CarInterfaceBase):
@@ -39,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

View File

@@ -6,7 +6,7 @@ from iqdbc.car.interfaces import CarControllerBase
from iqdbc.car.subaru import subarucan
from iqdbc.car.subaru.values import DBC, GLOBAL_ES_ADDR, CanBus, CarControllerParams, SubaruFlags
from iqdbc.iqpilot.car.subaru.stop_and_go import SnGCarController
from iqdbc.lvbs.car.subaru.stop_and_go import IQStopAndGoController
# FIXME: These limits aren't exact. The real limit is more than likely over a larger time period and
# involves the total steering angle change rather than rate, but these limits work well for now
@@ -14,10 +14,10 @@ MAX_STEER_RATE = 25 # deg/s
MAX_STEER_RATE_FRAMES = 7 # tx control frames needed before torque can be cut
class CarController(CarControllerBase, SnGCarController):
class CarController(CarControllerBase, IQStopAndGoController):
def __init__(self, dbc_names, CP, CP_IQ):
CarControllerBase.__init__(self, dbc_names, CP, CP_IQ)
SnGCarController.__init__(self, CP, CP_IQ)
IQStopAndGoController.__init__(self, CP, CP_IQ)
self.apply_torque_last = 0
self.cruise_button_prev = 0
@@ -139,7 +139,7 @@ class CarController(CarControllerBase, SnGCarController):
if self.frame % 2 == 0:
can_sends.append(subarucan.create_es_static_2(self.packer))
can_sends.extend(SnGCarController.create_stop_and_go(self, self.packer, CC, CS, self.frame))
can_sends.extend(IQStopAndGoController.create_stop_and_go(self, self.packer, CC, CS, self.frame))
new_actuators = actuators.as_builder()
new_actuators.torque = self.apply_torque_last / self.p.STEER_MAX

View File

@@ -6,15 +6,15 @@ from iqdbc.car.interfaces import CarStateBase
from iqdbc.car.subaru.values import DBC, CanBus, SubaruFlags
from iqdbc.car import CanSignalRateCalculator
from iqdbc.iqpilot.car.subaru.aol import AolCarState
from iqdbc.iqpilot.car.subaru.stop_and_go import SnGCarState
from iqdbc.lvbs.car.subaru.aol import AolCarState
from iqdbc.lvbs.car.subaru.stop_and_go import IQStopAndGoState
class CarState(CarStateBase, AolCarState, SnGCarState):
class CarState(CarStateBase, AolCarState, IQStopAndGoState):
def __init__(self, CP, CP_IQ):
CarStateBase.__init__(self, CP, CP_IQ)
AolCarState.__init__(self, CP, CP_IQ)
SnGCarState.__init__(self, CP, CP_IQ)
IQStopAndGoState.__init__(self, CP, CP_IQ)
can_define = CANDefine(DBC[CP.carFingerprint][Bus.pt])
self.shifter_values = can_define.dv["Transmission"]["Gear"]
@@ -144,7 +144,7 @@ class CarState(CarStateBase, AolCarState, SnGCarState):
self.es_infotainment_msg = copy.copy(cp_cam.vl["ES_Infotainment"])
AolCarState.update_aol(self, ret, can_parsers)
SnGCarState.update(self, ret, can_parsers)
IQStopAndGoState.update(self, ret, can_parsers)
return ret, ret_iq

View File

@@ -8,8 +8,8 @@ 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.iqpilot.car.tesla.coop_steering import CoopSteeringCarController
from iqdbc.iqpilot.car.tesla.values import TeslaFlagsIQ
from iqdbc.lvbs.car.tesla.coop_steering import CoopSteeringCarController
from iqdbc.lvbs.car.tesla.values import TeslaFlagsIQ
def get_safety_CP():

View File

@@ -6,16 +6,20 @@ from iqdbc.car.interfaces import CarStateBase
from iqdbc.car.tesla import TESLA_BLINKERS
from iqdbc.car.tesla.values import DBC, CANBUS, GEAR_MAP, STEER_THRESHOLD, TeslaFlags
from iqdbc.iqpilot.car.tesla.carstate_ext import CarStateExt
from iqdbc.iqpilot.car.tesla.values import TeslaFlagsIQ
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"]
@@ -27,6 +31,7 @@ class CarState(CarStateBase, CarStateExt):
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):
@@ -154,7 +159,9 @@ class CarState(CarStateBase, CarStateExt):
if TESLA_BLINKERS and Bus.cam in can_parsers:
self.das_body_controls_dat = bytes(can_parsers[Bus.cam].dat.get(0x3E9, b""))
CarStateExt.update(self, ret, ret_iq, can_parsers)
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
@@ -163,7 +170,7 @@ class CarState(CarStateBase, CarStateExt):
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]:

View File

@@ -5,7 +5,7 @@ from iqdbc.car.tesla.carstate import CarState
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.iqpilot.car.tesla.values import TeslaFlagsIQ, TeslaSafetyFlagsIQ
from iqdbc.lvbs.car.tesla.values import TeslaFlagsIQ, TeslaSafetyFlagsIQ
class CarInterface(CarInterfaceBase):
@@ -59,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

View File

@@ -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())

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,30 +38,23 @@ 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]
"SEAT_ALHAMBRA_MK1" = [2.5, 2.5, 0.1]
"HYUNDAI_SANTA_CRUZ_1ST_GEN" = [2.7, 2.7, 0.1]
"KIA_SPORTAGE_5TH_GEN" = [2.6, 2.6, 0.1]
"GENESIS_GV70_1ST_GEN" = [2.42, 2.42, 0.1]
@@ -82,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]
@@ -117,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]

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]

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