IQ.Pilot Release Commit @ 67fd9c2

This commit is contained in:
IQ.Lvbs CI [bot]
2026-09-01 20:09:51 -05:00
parent f8f9335ba3
commit 4efaff4cb2
58 changed files with 494 additions and 92 deletions

View File

@@ -506,8 +506,8 @@
}, },
"python/_iqclosure/iqpilot/system/hardware/usb.py": { "python/_iqclosure/iqpilot/system/hardware/usb.py": {
"mode": 420, "mode": 420,
"sha256": "7c2cd7c6d5a859fb8454059da1b36e3b5ce4639aca6fea296b4f91096b46c1ba", "sha256": "8c5b26f3967100ce0e5af7043a47a852fb5e8311f00ac95e7a263934c38e4ec0",
"size": 7257 "size": 8069
}, },
"python/_iqclosure/iqpilot/system/micd.py": { "python/_iqclosure/iqpilot/system/micd.py": {
"mode": 420, "mode": 420,
@@ -536,28 +536,28 @@
}, },
"python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "2130f52658e9e6bbb2c3354b497c84a80fd29d0cd81c2c7d524ad31de56dc3f1", "sha256": "f42a1bab373092b3010ca45b02d7ea7b27cd1e1e574c97f701129b2fad01f833",
"size": 333888 "size": 333888
}, },
"python/iqpilot_private/emac/ffs_holder.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/emac/ffs_holder.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "4dea04cbb68903af8af7745b4691941d4dfd703e9b01bc238791d03d103192aa", "sha256": "306f0362e86558d33072f800b736fa71bfaf4ee5e4383c4e743b72b74724d2e3",
"size": 68072 "size": 68064
}, },
"python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "87db24c0c6c5cc200cadaaf0348a3f969da3a9fdc99c3fc9e7624b6664d4f895", "sha256": "31339f53f093c3877e13eac90a6647cbca2de92f339c47e5006a85bcd2e3656c",
"size": 267952 "size": 267960
}, },
"python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "1db2acc63c5538d47b336bb9703bcfbfa0de89f431d102fd727bb0c3e1a2248f", "sha256": "d5e855d1784a8ba66844639b871d2747ccce264c55359cf0e641ce7e3d66460d",
"size": 203544 "size": 203512
}, },
"python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "a7a3ca084886882e8d61e3511a59c621bb44ff5e80a27ed6fcee36e9687d31d3", "sha256": "34d4ef1746143ea714f84f3c325e2ddaecec6a19c544fed68d8fc4080578c73f",
"size": 333336 "size": 399040
}, },
"runtime": { "runtime": {
"entries": { "entries": {
@@ -568,10 +568,10 @@
} }
}, },
"signatures": { "signatures": {
"python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": "ufSruB5sta4eGn6fuqxIp5v+uBkUTrot/V+Yf2kiZbrA3xcgOU2qG2AllwWDrn7Hg7a+eczvhJDHLzupuRZaAQ==", "python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": "7BtEAP11moL2lbQW5dYt1hDqJmDV1EuiEoSfJ0Tfislun2kS+/rwPiy7Ws8kjgrKLv+ZlXlWz6G5IBWk6z+aAw==",
"python/iqpilot_private/emac/ffs_holder.cpython-312-aarch64-linux-gnu.so": "G+hzeZYCDOph6YPDRVGbcWxojtJcBGjXcm6EPfUtfvYxvAu3QDjHXd+B3XdtOSekE/6AiH2CWURpDIwDqgZSBw==", "python/iqpilot_private/emac/ffs_holder.cpython-312-aarch64-linux-gnu.so": "Sv2riVgvm+4PDVTDLTuy1wpf15JObbP8hSAD4iiwF4RUs49GVGhulXkjzO28mLmF6tEuNDNWHRucxmuZrCKNAg==",
"python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": "8byO3iMIYoPD9r9SoS1LcxekC+9vdQaFW9s+CZ4MIZmq6h9scsIHNCq3peh6tUcScj/WRFuES/bdZxTItHycAg==", "python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": "d4kaBPAEwe55BrQEVHWmxcVlEoQPsMVLOAYgpZVd6uiBnwWSUxl1X5nmqdKtxWSk8TFqU2xZkhfw4OOgG9JmDw==",
"python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": "YYmTLXA024Dgu8zxXxfXMWIv/lmMvhuH1/C7aaNumZD/b5pkV6gihKomGlM7v/EnE8lYVtWo5wsnUCvl9a9BCQ==", "python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": "hvBkDKSMKjpxmRzhhGoRZT5WuOWLY+g3p2NIMlA7b6qYYpVaApoZNuquOerywEF1zJnwPiwpsg5QtQVe/5DfAA==",
"python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": "f59Fh4UaquFHnDXTAmKdtOwc+++QN5MWi3Zx74L2qZgMLgk3T0/ouTb0WMSTbHgoWn61r+/g/2bH1//Y+NUeAw==" "python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": "d3JAlH7P/X87II6iobeQ51melHy1vxdlXn0Nzo2DG65rcdVLM2B66YETPpwlX6Qv9NxhUlDcl/364CR+V+eyDQ=="
} }
} }

View File

@@ -1 +1 @@
OSV+ZbTBvP8stU4KDyT4wavo+fGUZEqcuvN7ltBw4LyMopdaMcbz9jYyIimicnQQniA1d4vaGuWRRQmvm0emAg== KylnzVsK5XC3Jq1G7bBbKa5lv+oJSlfBpBsNpWMEEUgHDyOEj7+5EeeHDOgy8Lt4LEFgEeIV2WEJW0GJVpxUCQ==

View File

@@ -13,6 +13,7 @@ The USB eGPU dock is identified by VID/PID only. comma's internal codename for
it is deliberately not used here: IQ.Pilot runs these models on several it is deliberately not used here: IQ.Pilot runs these models on several
backends (eGPU dock, eMac), so the naming stays about the role, not the vendor. backends (eGPU dock, eMac), so the naming stays about the role, not the vendor.
""" """
import subprocess
from pathlib import Path from pathlib import Path
# comma's USB eGPU dock, both shipped USB IDs. The ROM ids are the same board # comma's USB eGPU dock, both shipped USB IDs. The ROM ids are the same board
@@ -124,6 +125,26 @@ def get_link_error_count(soc: Path = SOC_PLATFORM_PATH) -> int:
return sum(link_errors(c) for c in usb_controllers(soc)) return sum(link_errors(c) for c in usb_controllers(soc))
def host_role_controller(soc: Path = SOC_PLATFORM_PATH, udc_root: Path = UDC_PATH) -> Path | None:
ctrl = link_controller(udc_root)
return (soc / ctrl / "mode") if ctrl else None
def ensure_host_role(mode_path: Path | None = None) -> bool:
"""A usbpd blip mid-drive can leave the Type-C controller in 'none'/'peripheral', and the
dock can never re-enumerate until something puts the port back into host mode."""
path = mode_path if mode_path is not None else host_role_controller()
if path is None:
return False
current = read(path)
if current == "host":
return True
if current is None:
return False
rc = subprocess.run(["sudo", "-n", "sh", "-c", f"echo host > {path}"], check=False, capture_output=True)
return rc.returncode == 0 and read(path) == "host"
def egpu_dock_present(root: Path = USB_DEVICES_PATH) -> bool: def egpu_dock_present(root: Path = USB_DEVICES_PATH) -> bool:
"""A dock in ROM/bootloader state is deliberately NOT counted as present: it """A dock in ROM/bootloader state is deliberately NOT counted as present: it
enumerates but cannot serve a GPU until vendor firmware is flashed.""" enumerates but cannot serve a GPU until vendor firmware is flashed."""

View File

@@ -1 +0,0 @@
3184657cdc4d4fc1c69a924491f8a52466f10ec2

View File

@@ -186,7 +186,7 @@
}, },
"python/iqpilot_private/navd/__init__.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/__init__.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "9459a9e4551e5fd87cd98457a4cebcdd6f4763fc2ce03e13202011659289f8d3", "sha256": "6e6de2f3270ad097c9b847fa10e833bac6b8eaab8ddad955603d163474a3c846",
"size": 67616 "size": 67616
}, },
"python/iqpilot_private/navd/__init__.py": { "python/iqpilot_private/navd/__init__.py": {
@@ -196,107 +196,122 @@
}, },
"python/iqpilot_private/navd/alpr_matcher.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/alpr_matcher.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "ea3451196fa13a2a8e2f9be7abff1b084308c856c52f001260f8340cc22e220f", "sha256": "516370d0603f5134955826a3d8e9fea8ec0a9d27c116ad05af149a5c7ac15bcc",
"size": 269936 "size": 269936
}, },
"python/iqpilot_private/navd/amap_client.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/amap_client.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "96398bb3c989d670b641582e15bcd3ebc73f43f20b081dfaecd3d26fa81dc873", "sha256": "dc8f27970c61a3b60c383ea69804896ee356d76c8bf7af5532a9338525fbc956",
"size": 202040 "size": 202040
}, },
"python/iqpilot_private/navd/event_builder.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/event_builder.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "bdd6cc139cdac78d3b6bf93e7930764bbd0538f24799b939fedd1e32f25c5266", "sha256": "96eb02a86cab08d557f8df0adebd2cc72232b9348e9896acd9b71784a86b42d5",
"size": 136960 "size": 136960
}, },
"python/iqpilot_private/navd/helpers.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/helpers.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "2df3400081a717f6ed6a819d3f5789ceda0707f89a6f6851dd77bd827b4721c6", "sha256": "8fb8cc47f47971c530a68ccd81b9b5d9fc8ce82cdd1270967fa845399009e519",
"size": 135960 "size": 135960
}, },
"python/iqpilot_private/navd/incident_feed.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/incident_feed.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "fe29e5f6eb290d17c7fab03e55db700772d9b6abc77e34954d952175a9a8dd45", "sha256": "b49f7ce7e9a33777ffa896a95fc4a5580988b4966d5a9330efd8c79a5116be91",
"size": 135528 "size": 135584
}, },
"python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "f6104209fc944accc4bca9d76598c14ea7bb32b5c06c6675c69ed71abbc18692", "sha256": "747ae769bb35650dcd16b94a0a6260213373e352ede88bc1e57f114f27761d5a",
"size": 135632 "size": 135632
}, },
"python/iqpilot_private/navd/long_decel.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/long_decel.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "b9a69dc97ff120e5efc5a41b0e24d14d9b57a4e83faf4a2fb9c414cc5feae407", "sha256": "eae425cd9bf60286fff4b980c6057351cb940f28981556269f05022258d80f0c",
"size": 69752 "size": 69752
}, },
"python/iqpilot_private/navd/mapbox_client.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/mapbox_client.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "c6e2f1e77e0ef71ddb7ae6178e3a22990b11b224b4b62e5db767e0f78d854402", "sha256": "0fc4356fe57a11538e4a248dc803ceee61781d7c700640f78241a2d57cbb4574",
"size": 68144 "size": 68144
}, },
"python/iqpilot_private/navd/nav_cameras.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/nav_cameras.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "fcabb4944766db54e4ef13849886339b352f9604b620b93e7b09bbddc5172a58", "sha256": "92521da14a5bf334324f32315b13773d37d5f7d209d4bad6d62afc12ab5d1705",
"size": 203056 "size": 203056
}, },
"python/iqpilot_private/navd/nav_turn_signals.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/nav_turn_signals.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "f95c6ae94b726d5c6634a0867e60cb433fcf41a07270512ee02ae422f580fb2a", "sha256": "67454e461abb76c7a3053a7f728baa0beb30fba235639a0a2a927a9c80443a4a",
"size": 135560 "size": 135560
}, },
"python/iqpilot_private/navd/navd.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/navd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "4b04550554f61540c72d7657d172e699f17535faaf29b8223f6640eb71a9c0dc", "sha256": "9e0d5e929bfcd960fdba11804d1f70848ba40a68a65093c025a6d48171d0b2cc",
"size": 797328 "size": 797392
}, },
"python/iqpilot_private/navd/navincidentd.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/navincidentd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "9a51d7c696a819e5ba47ba552bd1e7d21c28cdcb91fc9e0c22eb4b8d7c6d37e8", "sha256": "33d40decd32e8cfe3f9a267ebfccf48e8bfc67bf9609206ef230a9566bf5581d",
"size": 135344 "size": 135344
}, },
"python/iqpilot_private/navd/navrenderd.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/navrenderd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "fd120500f581eb408700c1c43dc6789b0391041b7a1b4341ee9352e5edeb1cf1", "sha256": "edf0d16511686ed3d4d7d6bf2b6b44fd8d484af348dab0eb415999656eb1d0af",
"size": 135584 "size": 135584
}, },
"python/iqpilot_private/navd/osm_cameras.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/osm_cameras.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "89ca86442a4cad9c79046c6a7d5a96dd65c352829b6234e41d131997e27ef28b", "sha256": "b11bec6f1b49f169f218a87467781b6d62d64c367e0c3df5356341adce8c3f6b",
"size": 202384 "size": 202384
}, },
"python/iqpilot_private/navd/reroute.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/reroute.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "8850331bcad9b7d708cb5cabff7c80364ed75b4dd000f1f3cca0098fb02bc5a7", "sha256": "d38218a6787234730ed39bdb04c00ab0778b1b56eefcedcb9ecfb31346d36378",
"size": 136216 "size": 136216
}, },
"python/iqpilot_private/navd/road_incidents.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/road_incidents.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "9faab3cc244eeba8dfae46b6ec57f4e8d9d03ca0c465b5ce2da836f1988aa0f6", "sha256": "f89f69e128b02eebc31b4c3702d62e12747f837863ad390026ea5004e506ea7c",
"size": 203136 "size": 203136
}, },
"python/iqpilot_private/navd/route_manager.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/route_manager.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "92e599dc73871b128be19c80057f983423023d7e2e3f9589ee8fe3c308ef2297", "sha256": "311926ffa2b4474320411240e6656ee676ce8b61d1cea444c34cd9520113a6a6",
"size": 877864 "size": 943640
}, },
"python/iqpilot_private/navd/runtime_common.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/runtime_common.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "e24efc1da92e76a996549c22d8c56708e8713193be36c6365868de29be9a0276", "sha256": "dc08b2e6bf7be3fe0b4a7ccdfceab6c434317e7626efd9b40ea5bf15739647ba",
"size": 135760 "size": 135760
}, },
"python/iqpilot_private/navd/traffic.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/traffic.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "b97fae21d32dee6f240b79e3090c94f06e0328b8bf511b869667e56a23836901", "sha256": "f47146eddd7c8b499359d411150533c0e28e80851096d076ee8552a4d45175d9",
"size": 69720 "size": 69720
}, },
"python/iqpilot_private/navd/traffic_sources.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "98845f8be1fd44009acf88fe685f6985ffb996d5d508858bdbb3edd2984a5146",
"size": 135968
},
"python/iqpilot_private/navd/traffic_writer.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "15aba494968b9673987d2d0168d797bfbd307046555cb89efa2378acc8bb042a",
"size": 136936
},
"python/iqpilot_private/navd/turn_desire.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/turn_desire.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "822c67671b249a96bb5de1a83bd7c047ffb780bd252c62791a25a0ea7c2101fc", "sha256": "715f94a985599a983750affa5bf36e1fc2e6502871f1af7de553f9aaf29028cb",
"size": 69768 "size": 69768
}, },
"python/iqpilot_private/navd/valhalla_process.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "2658d1a9f08f49e1294a3c26be733170ac7943970a79ee2969d9404cd4b55758",
"size": 204072
},
"python/iqpilot_private/navd/web_server.cpython-312-aarch64-linux-gnu.so": { "python/iqpilot_private/navd/web_server.cpython-312-aarch64-linux-gnu.so": {
"mode": 493, "mode": 493,
"sha256": "2c1c671d8d933d6896387894fe62b213b99b9d75085b54c61806e2bb7d381d7b", "sha256": "d03bf4d8d7d1547c5fcd085dea2ff78f8071e04e51e7f4d002f7ba23b431038e",
"size": 201400 "size": 201400
}, },
"runtime": { "runtime": {
@@ -336,27 +351,30 @@
} }
}, },
"signatures": { "signatures": {
"python/iqpilot_private/navd/__init__.cpython-312-aarch64-linux-gnu.so": "lg9M4QXLrh1ynX/Hev+0FAPnbN9mDoqv8diic0U+aYqjLMgkW+wHcB4r78J8oTrpDC+vF1wWztWj3cQOx98NCw==", "python/iqpilot_private/navd/__init__.cpython-312-aarch64-linux-gnu.so": "9i3/SfFfCLGyTzyQQYpsSxbA3ASc1OSK7fCYXpIiIMnX+jKB288Qy7TzJnr3uINt+LhRW1wwnMI6hWBXjgv5AA==",
"python/iqpilot_private/navd/alpr_matcher.cpython-312-aarch64-linux-gnu.so": "DGacFfOsQ9paqF8ytUgX572A/wPOZi7rJefAl9vydnAFbQv5NL+c9ot5YDGI1s4mXs/SHPbn0WtTOOU2aS/hDQ==", "python/iqpilot_private/navd/alpr_matcher.cpython-312-aarch64-linux-gnu.so": "5K4w4dDRtts8QvxBAXL0qrYofb/bg9PV/rKn/GrVmtAVDMd0DhAiKAugoxkhl8n4JviFY8ZmsHwWBrQPIMdkAA==",
"python/iqpilot_private/navd/amap_client.cpython-312-aarch64-linux-gnu.so": "r0vM+nE7A00o362Oj745OQzat76z889uBd/niFp18Qds4P1Wpvynd1ctrbOlo9H1yESeC09NuP/Qw4k1pvt1Cg==", "python/iqpilot_private/navd/amap_client.cpython-312-aarch64-linux-gnu.so": "X+hzm32FyMwE9fSIaGZH/GQqHn78izqrX8o1FdATb0Fg4+62tcgI9LHe9PIM8VY6+IaryaWyKqFtATaq6BZrDw==",
"python/iqpilot_private/navd/event_builder.cpython-312-aarch64-linux-gnu.so": "JkfNSqU9uM4oZqxteomIbPo6abywxUzzdBx5B2myJNtUjGW5bHEii2D2DAUKuO+gd7x+6+IwgPwSOJJyN+QYBQ==", "python/iqpilot_private/navd/event_builder.cpython-312-aarch64-linux-gnu.so": "dQnA8CCeRMXqiARr/Kzf2x736DuaT1/ALC8ZLDZep2/nY2IJ7StZeZ8DwOBuRPRK8KJ5HNU424QMZkVzQcDnBg==",
"python/iqpilot_private/navd/helpers.cpython-312-aarch64-linux-gnu.so": "/CNm/bxjvo/IgjjDTde0p8c1Gl6I54dSwiSS0sEVUcrTRP3Y31S0dh8dnBqn1VTH3PIGlUDBF7EmTavXUOAfDg==", "python/iqpilot_private/navd/helpers.cpython-312-aarch64-linux-gnu.so": "tdLykhS2t9M7R115Rse80M2Urg/8sNQTLaOz3YFyUTTkEzpaBlk3JnbgOQVpzDjrPB7y22y6mNORCkYI3cKfCw==",
"python/iqpilot_private/navd/incident_feed.cpython-312-aarch64-linux-gnu.so": "3IB5k4NEsFoKHckxlqoOZVXBAyVhYkVWRCvRdBnH4H+8udEuW5EpRXHv0rxtqBAs82ozbpOcZ7QjtfFgQPbXCQ==", "python/iqpilot_private/navd/incident_feed.cpython-312-aarch64-linux-gnu.so": "MJ/i1ZvGb3bYBVxKvT/zBk43CM/XT37GZa6la52Tfmqp9mrx6oz98PpvLZMgJ+18J4lSdMf2uGuJ2IHx5wWTAQ==",
"python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so": "Dp4PpzIqDJ5ySg1lIi0b5V49g7U2aOx1T+SeKGGKhOaEcCLnUEAd2EilrXt2luq8Xj+nnHWbMrZUho0KV7MfBQ==", "python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so": "+ULQz9iDSDBtYRYSVVWz63TImB3DizGV2oFER6pmNttfJ6vA4SBVToxKV1YjHAebdMiSrTBrEkgPd5K4unE0Bg==",
"python/iqpilot_private/navd/long_decel.cpython-312-aarch64-linux-gnu.so": "z87MDJLWKyqWqsXzTnoxjNfjkPxi6W0wsUt+Cafw5QWHMB6jGkJ1ZedYeS83qqdaxZGR3OaBdKohMSi9JKQHDA==", "python/iqpilot_private/navd/long_decel.cpython-312-aarch64-linux-gnu.so": "PB+q882Oo/cOi+fN0IahlaBP7xEQrjF7BykAnCsxrnK8zJGkFZdrVUcLCS4T5DYjXhwlXbdveb3ZHdG/LoMcBQ==",
"python/iqpilot_private/navd/mapbox_client.cpython-312-aarch64-linux-gnu.so": "FtOYM9RJA3LudYiGW9h4+EO94TqJVV4/hhEb9DOIL3hrbJYMWCctCTfo70UmgL+v8+8MSCyHgL3KBqPsW58gCg==", "python/iqpilot_private/navd/mapbox_client.cpython-312-aarch64-linux-gnu.so": "vqQRU0t5AS/oUs1JWxZlwHC5mrpaaHD10jgQCLzDGUL4Yv1aFHWAoM6KfFdUldmdSG1d4e6VGu+Y2bMdBIwAAw==",
"python/iqpilot_private/navd/nav_cameras.cpython-312-aarch64-linux-gnu.so": "XfkYcx/mj7TT57RjfAdlleSW5kgdGkLYt0h+SpJV+UdQeb+NTVHYa3+PgglT1KP8WjO5Tbyr6i5V4pq4ZwbhCQ==", "python/iqpilot_private/navd/nav_cameras.cpython-312-aarch64-linux-gnu.so": "OfuUD6/swlx5ggtvLfUVjGqpJbZWf+Ezwia5fE81FXA6RcuR4SMx1t/D38+TaMKDGvaBV31s08TJgh+/hXiiDA==",
"python/iqpilot_private/navd/nav_turn_signals.cpython-312-aarch64-linux-gnu.so": "GGhf7FRjRzg9uLp4ClSosUUXJk+ocH4NETh8wetaqTkXev1smeJCd2OWbTqgWmlGRfjLqsnXzpv+bxBIKishAw==", "python/iqpilot_private/navd/nav_turn_signals.cpython-312-aarch64-linux-gnu.so": "WQOzntcyeraddagnvnxAwH/ciLFMztjfEhg07HFHy+++XJPCSGctdaxlgPWX6xq9kiE7aEkcTsW2CwOwYo3tDw==",
"python/iqpilot_private/navd/navd.cpython-312-aarch64-linux-gnu.so": "YwIAOpt1komu3aFiqx1uhCJFpFZ/tCub2RNrJMyh8RS7SI0ySxmiQ9MXMyuXt5Gxnt9ReGmxVSKo7x3UDsuCCg==", "python/iqpilot_private/navd/navd.cpython-312-aarch64-linux-gnu.so": "5c68ZgFSkAXf9m32zKbBDSNHF0rVTwZPVu0puTiIcM7laHyHVtkQJduq6g4SUm+wz634nLK2u5pggaNf+mkhAw==",
"python/iqpilot_private/navd/navincidentd.cpython-312-aarch64-linux-gnu.so": "2agJVQwkglB5XE1acDWp+u/ZzwP0qJcEklPATDv4jez1KkftYqQb47J1/iGSxOtwh9NuXe//MNg+EEmwSlzvAQ==", "python/iqpilot_private/navd/navincidentd.cpython-312-aarch64-linux-gnu.so": "LBV8K1SPj+bN8idsPR/3B24grXQYRNarYsCUbVaRdkfORuib70gRlGz4KtyOyYT/vpCU4iuDhnSRsd4ltH4RAg==",
"python/iqpilot_private/navd/navrenderd.cpython-312-aarch64-linux-gnu.so": "G2ccii+ezg3lFQcNXI15EMVXWpszZlJJ4dy+nylF0OLEWWKRGFofb+vq4fYfwrJXmbn4nK9F+286nt4Q81WQAQ==", "python/iqpilot_private/navd/navrenderd.cpython-312-aarch64-linux-gnu.so": "ZT4bVD2W2w9w2AdbKkZ7AgfQv+zqTmJH5126ngiDDRFFuW2Bnp+E/4M+6U912uGZsV5xayCw8QmJbM9EeSqgDQ==",
"python/iqpilot_private/navd/osm_cameras.cpython-312-aarch64-linux-gnu.so": "+zTVSWeyZBkJFOC95QdK0LGd0EzLBZVB/GbKd3RiMsiQw83oAwNomLWs3SbsUhRBCZyGF56n2TQXEZQWjknaCg==", "python/iqpilot_private/navd/osm_cameras.cpython-312-aarch64-linux-gnu.so": "jjmBJtLWRVkPsO4n7/N/pDVD9jrC4SCMm0lIT/P8HCzsMhIIAkXhRAMGKdl1kYDxK8VMGRMMF7eoiCKEFPq2AA==",
"python/iqpilot_private/navd/reroute.cpython-312-aarch64-linux-gnu.so": "V+CQv3lqojsqJ/1gJJCuaR94ncCkRX4fT0iD+mxvJNmTLBGvw90hKl9v6IgtUvCOhqnHo2nJf30iH07C8R7iBA==", "python/iqpilot_private/navd/reroute.cpython-312-aarch64-linux-gnu.so": "A0FeGUh384rTKZJLOL6wNdX3bLTTsmUX7kF3ysax7WjnyUSlctbms15E+3Ww87qmXwjMGbyPdlTdsnhvpcstAA==",
"python/iqpilot_private/navd/road_incidents.cpython-312-aarch64-linux-gnu.so": "y5v5qBwMoKEvcYxjEy5tOwEB2BXaCAKIaKEBQKUuUfZpEZw7IpvPDGZbD5P5f1j05EJBde6jV9XbPB1mHIiHCg==", "python/iqpilot_private/navd/road_incidents.cpython-312-aarch64-linux-gnu.so": "J/I+XcODFE7LKvtyImt/Dw8fh2uvQeEhZ1IWx2IHZPH8sXhuVGVQIECVFGQJGfsckjzoUpgZq8AYNReRF62sBw==",
"python/iqpilot_private/navd/route_manager.cpython-312-aarch64-linux-gnu.so": "BIpSZfiTIaOZucyCX3pGiAtNAtRIg8WNQyfaMqnMEy52Ttowb1NheURl/qn11wqKaKeQN5+wgDmTwIkx3Eu6CQ==", "python/iqpilot_private/navd/route_manager.cpython-312-aarch64-linux-gnu.so": "n/RFD32mlinaYGrWvKWGv8ziTTSFP8+qCS/I1ibFXRYU6gtwx/+03eUR+p2jh5hzPteo5RxLUfHDwxFvJMJzDA==",
"python/iqpilot_private/navd/runtime_common.cpython-312-aarch64-linux-gnu.so": "pyRMyI5u6/IG5LKIgCuZf4rcquMN19P8M2rcmr+yjnfO50o8SRT18+iklNXj0VY96LKMyQx/ejV7y7Jgt+qnDw==", "python/iqpilot_private/navd/runtime_common.cpython-312-aarch64-linux-gnu.so": "/5mYKn9um2aBLipkFsEptfM3sOId3SKkV8AHXGslWdH9Evp2ll8XENvirAX5tV1X35WslGYIybK3Xv3cZ4o8DQ==",
"python/iqpilot_private/navd/traffic.cpython-312-aarch64-linux-gnu.so": "MVSL4IXTQ8SFa6zDY5Y/QWN5yhcFBx6LaujCzLJQfALs0bYb7jyy7XVbxlyQshpOy4n151YggEBhuNSCGHSaCA==", "python/iqpilot_private/navd/traffic.cpython-312-aarch64-linux-gnu.so": "kfl1Mm7G3CguQGtUvDutvbd4StDnKr0pW7ojLn6gydVTyxEiD/VAEFfTKBK/CAyUcw3eeX5+U8Qq2+6o0zEUBA==",
"python/iqpilot_private/navd/turn_desire.cpython-312-aarch64-linux-gnu.so": "A6QTQvEit2o797TrW2PuahnLODdACAAIahBw3UWtOTKHhD+vQaXke0yvxBcgVxgaBJLfpIz8SjKaZuEeIPphDg==", "python/iqpilot_private/navd/traffic_sources.cpython-312-aarch64-linux-gnu.so": "SzicVJ2/F8aMjcOHVCmEeJ952a+JPL8NLI6wsVkA2f5xiOSKMdynvcf7EgvrvBA4bgTRUGOmtpoqzMiCcNh2AA==",
"python/iqpilot_private/navd/web_server.cpython-312-aarch64-linux-gnu.so": "Qe32cW7+K4rQR6eRvi9vM8BZta5mPsK/xo9/dF4IZnfPYTP0bHzBFXZjsO4LVntwY/bUwMagp4fnRgXxwhNFCw==" "python/iqpilot_private/navd/traffic_writer.cpython-312-aarch64-linux-gnu.so": "tMmnyV9knGA0eoRq1+bYLaQRAmLLrpSC9KraH9iNWMgleg+xdY3dIpx+uyP77de/oOQ2Bovsp3Vq2MobcDACCQ==",
"python/iqpilot_private/navd/turn_desire.cpython-312-aarch64-linux-gnu.so": "kFb5U48pTMYRWee+JbH6CbOpE2je60Xk5ntJZIUCw66KWLTOUGFtQqMcUUcQ1b2/3xnCz7sb3Yt0GdbyyspMDg==",
"python/iqpilot_private/navd/valhalla_process.cpython-312-aarch64-linux-gnu.so": "u6o5lRGFT3PA7XPM0IXyr7iEsngyLKqh+kJhHNW7U7+iTI3hH25ag73YFQjnCfIuzPk2NtB4v2JKqcqBaNaQAQ==",
"python/iqpilot_private/navd/web_server.cpython-312-aarch64-linux-gnu.so": "4AB7oakyIWLG7OFuGpO+SidZnJzq3rC7/uWemSbhFVn9oOZBMQZ5IWN5Y3WfImyqoZXjZVGqxE9Yf0d8gmWdDA=="
} }
} }

View File

@@ -1 +1 @@
Fhs5w4K9yXGTe1Y9xpYpVEnHRHUsq4pJ0opKgllTGam0njHeskbHsYQmlj3n94vPeCJhQw52x9Bd1S2/R37WAg== NH/aexXGu1jWh6ks/x7lbVEUX9VZPhETrwkIozXSjmPR2isUyVlaZppveq23NB7BeIQTJuS/+KAuZ/pbL/kaDQ==

View File

@@ -129,6 +129,8 @@ class CarInterface(CarInterfaceBase):
car_fw: list[structs.CarParams.CarFw], alpha_long: bool, is_release_iq: bool, docs: bool) -> structs.IQCarParams: car_fw: list[structs.CarParams.CarFw], alpha_long: bool, is_release_iq: bool, docs: bool) -> structs.IQCarParams:
if candidate in UNSUPPORTED_DSU_CAR: if candidate in UNSUPPORTED_DSU_CAR:
ret.iqSafetyFlags |= ToyotaSafetyFlagsIQ.UNSUPPORTED_DSU ret.iqSafetyFlags |= ToyotaSafetyFlagsIQ.UNSUPPORTED_DSU
if candidate in TSS2_CAR:
ret.iqSafetyFlags |= ToyotaSafetyFlagsIQ.LKAS_HUD
# Detect smartDSU, which intercepts ACC_CMD from the DSU (or radar) allowing openpilot to send it # Detect smartDSU, which intercepts ACC_CMD from the DSU (or radar) allowing openpilot to send it
# 0x2AA is sent by a similar device which intercepts the radar instead of DSU on NO_DSU_CARs # 0x2AA is sent by a similar device which intercepts the radar instead of DSU on NO_DSU_CARs

View File

@@ -1,7 +1,7 @@
from iqdbc.car import gen_empty_fingerprint, structs from iqdbc.car import gen_empty_fingerprint, structs
from iqdbc.car.toyota.interface import CarInterface from iqdbc.car.toyota.interface import CarInterface
from iqdbc.car.toyota.values import CAR, ToyotaFlags from iqdbc.car.toyota.values import CAR, ToyotaFlags
from iqdbc.lvbs.car.toyota.values import ToyotaFlagsIQ from iqdbc.lvbs.car.toyota.values import ToyotaFlagsIQ, ToyotaSafetyFlagsIQ
def test_secoc_toyota_not_dashcam_on_release(): def test_secoc_toyota_not_dashcam_on_release():
@@ -34,3 +34,11 @@ def test_smart_dsu_clears_disable_radar_on_radar_acc_toyota():
assert not (cp.flags & ToyotaFlags.DISABLE_RADAR.value) assert not (cp.flags & ToyotaFlags.DISABLE_RADAR.value)
assert cp.alphaLongitudinalAvailable assert cp.alphaLongitudinalAvailable
assert cp.openpilotLongitudinalControl assert cp.openpilotLongitudinalControl
def test_lkas_hud_safety_flag_tracks_tss2():
for candidate, expected in ((CAR.TOYOTA_CHR_TSS2, True), (CAR.TOYOTA_PRIUS_V, False)):
fingerprint = gen_empty_fingerprint()
cp = CarInterface.get_params(candidate, fingerprint, [], False, False, False)
cp_iq = CarInterface.get_params_iq(cp, candidate, fingerprint, [], False, False, False)
assert bool(cp_iq.iqSafetyFlags & ToyotaSafetyFlagsIQ.LKAS_HUD) is expected

View File

@@ -679,7 +679,7 @@ class CarState(CarStateBase):
if self.CP.carFingerprint == CAR.PORSCHE_MACAN_MK1: if self.CP.carFingerprint == CAR.PORSCHE_MACAN_MK1:
ret.gearShifter = self.parse_gear_shifter(self.CCP.shifter_values.get(pt_cp.vl["Getriebe_03"]["GE_Waehlhebel"], None)) ret.gearShifter = self.parse_gear_shifter(self.CCP.shifter_values.get(pt_cp.vl["Getriebe_03"]["GE_Waehlhebel"], None))
elif self.CP.transmissionType == TransmissionType.manual: elif self.CP.transmissionType == TransmissionType.manual:
reverse = bool(pt_cp.vl["Gateway_05"]["BCM1_Rueckfahrlicht_Schalter"]) reverse = bool(br_cp.vl["Gateway_05"]["BCM1_Rueckfahrlicht_Schalter"])
ret.gearShifter = GearShifter.reverse if reverse else GearShifter.drive ret.gearShifter = GearShifter.reverse if reverse else GearShifter.drive
else: else:
ret.gearShifter = GearShifter.drive ret.gearShifter = GearShifter.drive

View File

@@ -8,7 +8,7 @@ from iqdbc.car.volkswagen.carcontroller import CarController
from iqdbc.car.volkswagen.carstate import CarState from iqdbc.car.volkswagen.carstate import CarState
from iqdbc.car.volkswagen.values import ( from iqdbc.car.volkswagen.values import (
CAR, CanBus, DashcamOnlyReason, MLB_ACC_COORDINATOR_MSGS, MLB_GEARBOX_MSGS, MLB_MSG_ACC_10, CAR, CanBus, DashcamOnlyReason, MLB_ACC_COORDINATOR_MSGS, MLB_GEARBOX_MSGS, MLB_MSG_ACC_10,
MLB_MSG_LH_EPS_03, NetworkLocation, RADAR_DISABLE_STATE, TransmissionType, MLB_MSG_GATEWAY_05, MLB_MSG_LH_EPS_03, NetworkLocation, RADAR_DISABLE_STATE, TransmissionType,
VolkswagenFlags, VolkswagenSafetyFlags, VolkswagenFlagsIQ, get_longitudinal_stopping_speed_override, VolkswagenFlags, VolkswagenSafetyFlags, VolkswagenFlagsIQ, get_longitudinal_stopping_speed_override,
) )
from iqdbc.car.volkswagen.radar_interface import RadarInterface from iqdbc.car.volkswagen.radar_interface import RadarInterface
@@ -115,7 +115,11 @@ class CarInterface(CarInterfaceBase):
if fingerprinted and MLB_MSG_LH_EPS_03 not in pt_msgs: if fingerprinted and MLB_MSG_LH_EPS_03 not in pt_msgs:
ret.flags |= VolkswagenFlagsIQ.IQ_MLB_NO_HCA_EPS.value ret.flags |= VolkswagenFlagsIQ.IQ_MLB_NO_HCA_EPS.value
if fingerprinted and not any(msg in pt_msgs for msg in MLB_GEARBOX_MSGS): all_msgs = ecan_msgs | fingerprint[1]
gearbox_seen = any(msg in all_msgs for msg in MLB_GEARBOX_MSGS)
transmission_ecu = any(fw.ecu == structs.CarParams.Ecu.transmission for fw in car_fw)
reverse_switch_seen = MLB_MSG_GATEWAY_05 in fingerprint[1]
if fingerprinted and not gearbox_seen and not transmission_ecu and reverse_switch_seen:
ret.transmissionType = TransmissionType.manual ret.transmissionType = TransmissionType.manual
elif ret.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO): elif ret.flags & (VolkswagenFlags.MEB | VolkswagenFlags.MQB_EVO):

View File

@@ -9,9 +9,9 @@ from iqdbc.car import Bus, DT_CTRL, structs
from iqdbc.car.volkswagen.carstate import CarState from iqdbc.car.volkswagen.carstate import CarState
from iqdbc.car.structs import CarParams from iqdbc.car.structs import CarParams
from iqdbc.car.volkswagen.interface import CarInterface from iqdbc.car.volkswagen.interface import CarInterface
from iqdbc.car.volkswagen.values import (CAR, FW_QUERY_CONFIG, MLB_ACC_COORDINATOR_MSGS, MLB_MSG_ACC_10, from iqdbc.car.volkswagen.values import (CAR, FW_QUERY_CONFIG, MLB_ACC_COORDINATOR_MSGS, MLB_GEARBOX_MSGS, MLB_MSG_ACC_10,
MLB_MSG_LH_EPS_03, WMI, VolkswagenFlags, VolkswagenFlagsIQ, MLB_MSG_GATEWAY_05, MLB_MSG_GETRIEBE_01, MLB_MSG_LH_EPS_03, WMI, VolkswagenFlags,
VolkswagenSafetyFlags) VolkswagenFlagsIQ, VolkswagenSafetyFlags)
from iqdbc.car.volkswagen.fingerprints import FW_VERSIONS from iqdbc.car.volkswagen.fingerprints import FW_VERSIONS
Ecu = CarParams.Ecu Ecu = CarParams.Ecu
@@ -333,6 +333,58 @@ def test_mlb_manual_gear_follows_the_reverse_light_switch():
assert _run(a4, lambda: _a4_mk4_frames(packer, reverse=False)).gearShifter == structs.CarState.GearShifter.drive assert _run(a4, lambda: _a4_mk4_frames(packer, reverse=False)).gearShifter == structs.CarState.GearShifter.drive
def _q5_params(bus0, bus1=(), car_fw=()):
fingerprints = _mlb_fingerprint(0, bus0)
fingerprints[1] = {msg: 8 for msg in bus1}
fingerprints[2] = {msg: 8 for msg in MLB_ECAN_CAMERA}
return CarInterface.get_params(CAR.AUDI_Q5_MK1, fingerprints, list(car_fw), alpha_long=False, is_release=False, docs=False)
MLB_ECAN_GATEWAY_NO_GEARBOX = tuple(msg for msg in MLB_ECAN_GATEWAY if msg not in MLB_GEARBOX_MSGS)
def test_mlb_gearbox_on_the_powertrain_bus_keeps_automatic():
params = _q5_params(MLB_ECAN_GATEWAY_NO_GEARBOX, bus1=(MLB_MSG_GETRIEBE_01, MLB_MSG_GATEWAY_05))
assert params.transmissionType == CarParams.TransmissionType.automatic
def test_mlb_transmission_ecu_firmware_keeps_automatic():
transmission = CarParams.CarFw.new_message(ecu=Ecu.transmission, address=0x7e1)
params = _q5_params(MLB_ECAN_GATEWAY_NO_GEARBOX, bus1=(MLB_MSG_GATEWAY_05,), car_fw=(transmission,))
assert params.transmissionType == CarParams.TransmissionType.automatic
def test_mlb_reverse_switch_on_the_extended_can_alone_keeps_automatic():
params = _q5_params(MLB_ECAN_GATEWAY_NO_GEARBOX + (MLB_MSG_GATEWAY_05,))
assert params.transmissionType == CarParams.TransmissionType.automatic
def test_mlb_manual_needs_no_gearbox_anywhere_and_a_reverse_switch_on_the_powertrain_bus():
params = _q5_params(MLB_ECAN_GATEWAY_NO_GEARBOX, bus1=(MLB_MSG_GATEWAY_05,))
assert params.transmissionType == CarParams.TransmissionType.manual
def test_mlb_manual_gateway_car_reads_reverse_from_the_powertrain_bus():
fingerprints = _mlb_fingerprint(0, MLB_ECAN_GATEWAY_NO_GEARBOX)
fingerprints[1] = {MLB_MSG_GATEWAY_05: 8}
fingerprints[2] = {msg: 8 for msg in MLB_ECAN_CAMERA}
q5 = _build_mlb_car(CAR.AUDI_Q5_MK1, fingerprints)
assert q5.CP.transmissionType == CarParams.TransmissionType.manual
packer = CANPacker("vw_mlb")
frames = lambda reverse: [packer.make_can_msg("Gateway_05", 1, {"BCM1_Rueckfahrlicht_Schalter": int(reverse)})]
assert _run(q5, lambda: frames(True)).gearShifter == structs.CarState.GearShifter.reverse
assert _run(q5, lambda: frames(False)).gearShifter == structs.CarState.GearShifter.drive
assert MLB_MSG_GATEWAY_05 not in q5.can_parsers[Bus.pt].addresses
def test_mlb_automatic_never_subscribes_gateway_05():
q5 = _q5_mk1_car()
q5.update([(int(DT_CTRL * 1e9), [])])
assert MLB_MSG_GATEWAY_05 not in q5.can_parsers[Bus.pt].addresses
assert MLB_MSG_GATEWAY_05 not in q5.can_parsers[Bus.aux].addresses
@pytest.mark.parametrize("button_type", ( @pytest.mark.parametrize("button_type", (
structs.CarState.ButtonEvent.Type.setCruise, structs.CarState.ButtonEvent.Type.setCruise,
structs.CarState.ButtonEvent.Type.resumeCruise, structs.CarState.ButtonEvent.Type.resumeCruise,

View File

@@ -348,6 +348,7 @@ MLB_MSG_ACC_01 = 0x109
MLB_MSG_ACC_05 = 0x10D MLB_MSG_ACC_05 = 0x10D
MLB_MSG_ACC_02 = 0x30C MLB_MSG_ACC_02 = 0x30C
MLB_MSG_ACC_10 = 0x117 MLB_MSG_ACC_10 = 0x117
MLB_MSG_GATEWAY_05 = 0x39C
MLB_ACC_COORDINATOR_MSGS = (MLB_MSG_ACC_01, MLB_MSG_ACC_05, MLB_MSG_ACC_02) MLB_ACC_COORDINATOR_MSGS = (MLB_MSG_ACC_01, MLB_MSG_ACC_05, MLB_MSG_ACC_02)
MLB_GEARBOX_MSGS = (MLB_MSG_GETRIEBE_01, MLB_MSG_GETRIEBE_02, MLB_MSG_GETRIEBE_03, MLB_MSG_GETRIEBE_04) MLB_GEARBOX_MSGS = (MLB_MSG_GETRIEBE_01, MLB_MSG_GETRIEBE_02, MLB_MSG_GETRIEBE_03, MLB_MSG_GETRIEBE_04)

View File

@@ -17,5 +17,5 @@ class ToyotaSafetyFlagsIQ:
DEFAULT = 0 DEFAULT = 0
UNSUPPORTED_DSU = 1 UNSUPPORTED_DSU = 1
GAS_INTERCEPTOR = 2 GAS_INTERCEPTOR = 2
LKAS_HUD = 4

View File

@@ -39,7 +39,6 @@
#define TOYOTA_COMMON_RX_CHECKS(lta) \ #define TOYOTA_COMMON_RX_CHECKS(lta) \
{.msg = {{ 0xaa, 0, 8, 83U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ {.msg = {{ 0xaa, 0, 8, 83U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{0x260, 0, 8, 50U, .ignore_counter = true, .ignore_quality_flag=!(lta)}, { 0 }, { 0 }}}, \ {.msg = {{0x260, 0, 8, 50U, .ignore_counter = true, .ignore_quality_flag=!(lta)}, { 0 }, { 0 }}}, \
{.msg = {{0x412, 2, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true, .frequency = 1U}, { 0 }, { 0 }}}, \
#define TOYOTA_RX_CHECKS(lta) \ #define TOYOTA_RX_CHECKS(lta) \
TOYOTA_COMMON_RX_CHECKS(lta) \ TOYOTA_COMMON_RX_CHECKS(lta) \
@@ -66,6 +65,9 @@
#define TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK \ #define TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK \
{.msg = {{0x201, 0, 6, 50U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \ {.msg = {{0x201, 0, 6, 50U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
#define TOYOTA_LKAS_HUD_ADDR_CHECK \
{.msg = {{0x412, 2, 8, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true, .frequency = 1U}, { 0 }, { 0 }}}, \
static bool toyota_secoc = false; static bool toyota_secoc = false;
static bool toyota_alt_brake = false; static bool toyota_alt_brake = false;
static bool toyota_stock_longitudinal = false; static bool toyota_stock_longitudinal = false;
@@ -433,6 +435,7 @@ static safety_config toyota_init(uint16_t param) {
const uint16_t TOYOTA_PARAM_IQ_UNSUPPORTED_DSU = 1; const uint16_t TOYOTA_PARAM_IQ_UNSUPPORTED_DSU = 1;
const uint16_t TOYTOA_PARAM_IQ_GAS_INTERCEPTOR = 2; const uint16_t TOYTOA_PARAM_IQ_GAS_INTERCEPTOR = 2;
const uint16_t TOYOTA_PARAM_IQ_LKAS_HUD = 4;
#ifdef ALLOW_DEBUG #ifdef ALLOW_DEBUG
const uint32_t TOYOTA_PARAM_SECOC = 8UL << TOYOTA_PARAM_OFFSET; const uint32_t TOYOTA_PARAM_SECOC = 8UL << TOYOTA_PARAM_OFFSET;
@@ -446,6 +449,7 @@ static safety_config toyota_init(uint16_t param) {
const bool toyota_unsupported_dsu = GET_FLAG(current_safety_param_iq, TOYOTA_PARAM_IQ_UNSUPPORTED_DSU); const bool toyota_unsupported_dsu = GET_FLAG(current_safety_param_iq, TOYOTA_PARAM_IQ_UNSUPPORTED_DSU);
enable_gas_interceptor = GET_FLAG(current_safety_param_iq, TOYTOA_PARAM_IQ_GAS_INTERCEPTOR); enable_gas_interceptor = GET_FLAG(current_safety_param_iq, TOYTOA_PARAM_IQ_GAS_INTERCEPTOR);
const bool toyota_lkas_hud = GET_FLAG(current_safety_param_iq, TOYOTA_PARAM_IQ_LKAS_HUD);
// gas interceptor should not be used if openpilot is not controlling longitudinal or is a TSK car // gas interceptor should not be used if openpilot is not controlling longitudinal or is a TSK car
if (toyota_stock_longitudinal || toyota_secoc) { if (toyota_stock_longitudinal || toyota_secoc) {
@@ -471,6 +475,7 @@ static safety_config toyota_init(uint16_t param) {
static RxCheck toyota_secoc_rx_checks[] = { static RxCheck toyota_secoc_rx_checks[] = {
TOYOTA_SECOC_RX_CHECKS TOYOTA_SECOC_RX_CHECKS
TOYOTA_PCM_CRUISE_2_ADDR_CHECK TOYOTA_PCM_CRUISE_2_ADDR_CHECK
TOYOTA_LKAS_HUD_ADDR_CHECK
}; };
SET_RX_CHECKS(toyota_secoc_rx_checks, ret); SET_RX_CHECKS(toyota_secoc_rx_checks, ret);
@@ -479,6 +484,7 @@ static safety_config toyota_init(uint16_t param) {
static RxCheck toyota_lta_rx_checks[] = { static RxCheck toyota_lta_rx_checks[] = {
TOYOTA_RX_CHECKS(true) TOYOTA_RX_CHECKS(true)
TOYOTA_PCM_CRUISE_2_ADDR_CHECK TOYOTA_PCM_CRUISE_2_ADDR_CHECK
TOYOTA_LKAS_HUD_ADDR_CHECK
}; };
SET_RX_CHECKS(toyota_lta_rx_checks, ret); SET_RX_CHECKS(toyota_lta_rx_checks, ret);
@@ -486,18 +492,22 @@ static safety_config toyota_init(uint16_t param) {
static RxCheck toyota_lka_rx_checks[] = { static RxCheck toyota_lka_rx_checks[] = {
TOYOTA_RX_CHECKS(false) TOYOTA_RX_CHECKS(false)
TOYOTA_PCM_CRUISE_2_ADDR_CHECK TOYOTA_PCM_CRUISE_2_ADDR_CHECK
TOYOTA_LKAS_HUD_ADDR_CHECK
}; };
static RxCheck toyota_lka_alt_brake_rx_checks[] = { static RxCheck toyota_lka_alt_brake_rx_checks[] = {
TOYOTA_ALT_BRAKE_RX_CHECKS(false) TOYOTA_ALT_BRAKE_RX_CHECKS(false)
TOYOTA_PCM_CRUISE_2_ADDR_CHECK TOYOTA_PCM_CRUISE_2_ADDR_CHECK
TOYOTA_LKAS_HUD_ADDR_CHECK
}; };
static RxCheck toyota_lka_unsupported_dsu_rx_checks[] = { static RxCheck toyota_lka_unsupported_dsu_rx_checks[] = {
TOYOTA_RX_CHECKS(false) TOYOTA_RX_CHECKS(false)
TOYOTA_DSU_CRUISE_ADDR_CHECK TOYOTA_DSU_CRUISE_ADDR_CHECK
TOYOTA_LKAS_HUD_ADDR_CHECK
}; };
static RxCheck toyota_lka_alt_brake_unsupported_dsu_rx_checks[] = { static RxCheck toyota_lka_alt_brake_unsupported_dsu_rx_checks[] = {
TOYOTA_ALT_BRAKE_RX_CHECKS(false) TOYOTA_ALT_BRAKE_RX_CHECKS(false)
TOYOTA_DSU_CRUISE_ADDR_CHECK TOYOTA_DSU_CRUISE_ADDR_CHECK
TOYOTA_LKAS_HUD_ADDR_CHECK
}; };
if (!toyota_alt_brake) { if (!toyota_alt_brake) {
@@ -523,6 +533,7 @@ static safety_config toyota_init(uint16_t param) {
TOYOTA_RX_CHECKS(true) TOYOTA_RX_CHECKS(true)
TOYOTA_PCM_CRUISE_2_ADDR_CHECK TOYOTA_PCM_CRUISE_2_ADDR_CHECK
TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK
TOYOTA_LKAS_HUD_ADDR_CHECK
}; };
SET_RX_CHECKS(toyota_lta_interceptor_rx_checks, ret); SET_RX_CHECKS(toyota_lta_interceptor_rx_checks, ret);
@@ -531,21 +542,25 @@ static safety_config toyota_init(uint16_t param) {
TOYOTA_RX_CHECKS(false) TOYOTA_RX_CHECKS(false)
TOYOTA_PCM_CRUISE_2_ADDR_CHECK TOYOTA_PCM_CRUISE_2_ADDR_CHECK
TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK
TOYOTA_LKAS_HUD_ADDR_CHECK
}; };
static RxCheck toyota_lka_alt_brake_interceptor_rx_checks[] = { static RxCheck toyota_lka_alt_brake_interceptor_rx_checks[] = {
TOYOTA_ALT_BRAKE_RX_CHECKS(false) TOYOTA_ALT_BRAKE_RX_CHECKS(false)
TOYOTA_PCM_CRUISE_2_ADDR_CHECK TOYOTA_PCM_CRUISE_2_ADDR_CHECK
TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK
TOYOTA_LKAS_HUD_ADDR_CHECK
}; };
static RxCheck toyota_lka_unsupported_dsu_interceptor_rx_checks[] = { static RxCheck toyota_lka_unsupported_dsu_interceptor_rx_checks[] = {
TOYOTA_RX_CHECKS(false) TOYOTA_RX_CHECKS(false)
TOYOTA_DSU_CRUISE_ADDR_CHECK TOYOTA_DSU_CRUISE_ADDR_CHECK
TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK
TOYOTA_LKAS_HUD_ADDR_CHECK
}; };
static RxCheck toyota_lka_alt_brake_unsupported_dsu_interceptor_rx_checks[] = { static RxCheck toyota_lka_alt_brake_unsupported_dsu_interceptor_rx_checks[] = {
TOYOTA_ALT_BRAKE_RX_CHECKS(false) TOYOTA_ALT_BRAKE_RX_CHECKS(false)
TOYOTA_DSU_CRUISE_ADDR_CHECK TOYOTA_DSU_CRUISE_ADDR_CHECK
TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK
TOYOTA_LKAS_HUD_ADDR_CHECK
}; };
if (!toyota_alt_brake) { if (!toyota_alt_brake) {
@@ -564,6 +579,10 @@ static safety_config toyota_init(uint16_t param) {
} }
} }
if (!toyota_lkas_hud) {
ret.rx_checks_len -= 1;
}
return ret; return ret;
} }

View File

@@ -21,8 +21,8 @@ TOYOTA_COMMON_LONG_TX_MSGS = [[0x283, 0], [0x2E6, 0], [0x2E7, 0], [0x33E, 0], [0
GAS_INTERCEPTOR_TX_MSGS = [[0x200, 0]] GAS_INTERCEPTOR_TX_MSGS = [[0x200, 0]]
UNSUPPORTED_DSU = [ UNSUPPORTED_DSU = [
{"SAFETY_PARAM_IQ": ToyotaSafetyFlagsIQ.DEFAULT}, {"SAFETY_PARAM_IQ": ToyotaSafetyFlagsIQ.LKAS_HUD},
{"SAFETY_PARAM_IQ": ToyotaSafetyFlagsIQ.UNSUPPORTED_DSU}, {"SAFETY_PARAM_IQ": ToyotaSafetyFlagsIQ.UNSUPPORTED_DSU | ToyotaSafetyFlagsIQ.LKAS_HUD},
] ]
@@ -33,7 +33,7 @@ class TestToyotaSafetyBase(common.CarSafetyTest, common.LongitudinalAccelSafetyT
FWD_BLACKLISTED_ADDRS = {2: [0x2E4, 0x412, 0x191, 0x343]} FWD_BLACKLISTED_ADDRS = {2: [0x2E4, 0x412, 0x191, 0x343]}
EPS_SCALE = 73 EPS_SCALE = 73
SAFETY_PARAM_IQ: int = 0 SAFETY_PARAM_IQ: int = ToyotaSafetyFlagsIQ.LKAS_HUD
packer: CANPackerSafety packer: CANPackerSafety
safety: libsafety_py.LibSafety safety: libsafety_py.LibSafety
@@ -96,6 +96,9 @@ class TestToyotaSafetyBase(common.CarSafetyTest, common.LongitudinalAccelSafetyT
return self.packer.make_can_msg_safety("LKAS_HUD", 2, values) return self.packer.make_can_msg_safety("LKAS_HUD", 2, values)
def test_enable_control_allowed_with_aol_button(self): def test_enable_control_allowed_with_aol_button(self):
self.safety.set_current_safety_param_iq(self.SAFETY_PARAM_IQ | ToyotaSafetyFlagsIQ.LKAS_HUD)
self.safety.set_safety_hooks(CarParams.SafetyModel.toyota, self.safety.get_current_safety_param())
self.safety.init_tests()
for enable_aol in (True, False): for enable_aol in (True, False):
with self.subTest("enable_aol", aol_enabled=enable_aol): with self.subTest("enable_aol", aol_enabled=enable_aol):
self.safety.set_aol_params(enable_aol, False, False) self.safety.set_aol_params(enable_aol, False, False)
@@ -120,6 +123,16 @@ class TestToyotaSafetyBase(common.CarSafetyTest, common.LongitudinalAccelSafetyT
self.safety.set_controls_allowed_lat(False) self.safety.set_controls_allowed_lat(False)
self.safety.set_aol_params(False, False, False) self.safety.set_aol_params(False, False, False)
def test_lkas_button_requires_lkas_hud(self):
self.safety.set_current_safety_param_iq(self.SAFETY_PARAM_IQ & ~ToyotaSafetyFlagsIQ.LKAS_HUD)
self.safety.set_safety_hooks(CarParams.SafetyModel.toyota, self.safety.get_current_safety_param())
self.safety.init_tests()
self.safety.set_aol_params(True, False, False)
button_state = self.safety.get_aol_button_press()
self._rx(self._lkas_button_msg(True))
self.assertEqual(button_state, self.safety.get_aol_button_press())
self.assertFalse(self.safety.get_controls_allowed_lat())
def test_diagnostics(self, stock_longitudinal: bool = False, ecu_disabled: bool = True): def test_diagnostics(self, stock_longitudinal: bool = False, ecu_disabled: bool = True):
for should_tx, msg in ((False, b"\x6D\x02\x3E\x00\x00\x00\x00\x00"), # fwdCamera tester present for should_tx, msg in ((False, b"\x6D\x02\x3E\x00\x00\x00\x00\x00"), # fwdCamera tester present
(False, b"\x0F\x03\xAA\xAA\x00\x00\x00\x00"), # non-tester present (False, b"\x0F\x03\xAA\xAA\x00\x00\x00\x00"), # non-tester present
@@ -250,6 +263,7 @@ class TestToyotaSafetyAngle(TestToyotaSafetyBase, common.AngleSteeringSafetyTest
def setUp(self): def setUp(self):
self.packer = CANPackerSafety("toyota_nodsu_pt_generated") self.packer = CANPackerSafety("toyota_nodsu_pt_generated")
self.safety = libsafety_py.libsafety self.safety = libsafety_py.libsafety
self.safety.set_current_safety_param_iq(self.SAFETY_PARAM_IQ)
self.safety.set_safety_hooks(CarParams.SafetyModel.toyota, self.EPS_SCALE | ToyotaSafetyFlags.LTA) self.safety.set_safety_hooks(CarParams.SafetyModel.toyota, self.EPS_SCALE | ToyotaSafetyFlags.LTA)
self.safety.init_tests() self.safety.init_tests()
@@ -431,6 +445,7 @@ class TestToyotaStockLongitudinalAngle(TestToyotaStockLongitudinalBase, TestToyo
def setUp(self): def setUp(self):
self.packer = CANPackerSafety("toyota_nodsu_pt_generated") self.packer = CANPackerSafety("toyota_nodsu_pt_generated")
self.safety = libsafety_py.libsafety self.safety = libsafety_py.libsafety
self.safety.set_current_safety_param_iq(self.SAFETY_PARAM_IQ)
self.safety.set_safety_hooks(CarParams.SafetyModel.toyota, self.safety.set_safety_hooks(CarParams.SafetyModel.toyota,
self.EPS_SCALE | ToyotaSafetyFlags.STOCK_LONGITUDINAL | ToyotaSafetyFlags.LTA) self.EPS_SCALE | ToyotaSafetyFlags.STOCK_LONGITUDINAL | ToyotaSafetyFlags.LTA)
self.safety.init_tests() self.safety.init_tests()
@@ -445,6 +460,7 @@ class TestToyotaSecOcSafetyBase(TestToyotaSafetyBase):
def setUp(self): def setUp(self):
self.packer = CANPackerSafety("toyota_secoc_pt_generated") self.packer = CANPackerSafety("toyota_secoc_pt_generated")
self.safety = libsafety_py.libsafety self.safety = libsafety_py.libsafety
self.safety.set_current_safety_param_iq(self.SAFETY_PARAM_IQ)
self.safety.set_safety_hooks(CarParams.SafetyModel.toyota, self.safety.set_safety_hooks(CarParams.SafetyModel.toyota,
self.EPS_SCALE | ToyotaSafetyFlags.SECOC) self.EPS_SCALE | ToyotaSafetyFlags.SECOC)
self.safety.init_tests() self.safety.init_tests()
@@ -485,6 +501,7 @@ class TestToyotaSecOcSafetyStockLongitudinal(TestToyotaSecOcSafetyBase, TestToyo
def setUp(self): def setUp(self):
self.packer = CANPackerSafety("toyota_secoc_pt_generated") self.packer = CANPackerSafety("toyota_secoc_pt_generated")
self.safety = libsafety_py.libsafety self.safety = libsafety_py.libsafety
self.safety.set_current_safety_param_iq(self.SAFETY_PARAM_IQ)
self.safety.set_safety_hooks(CarParams.SafetyModel.toyota, self.safety.set_safety_hooks(CarParams.SafetyModel.toyota,
self.EPS_SCALE | ToyotaSafetyFlags.STOCK_LONGITUDINAL | ToyotaSafetyFlags.SECOC) self.EPS_SCALE | ToyotaSafetyFlags.STOCK_LONGITUDINAL | ToyotaSafetyFlags.SECOC)
self.safety.init_tests() self.safety.init_tests()
@@ -498,6 +515,7 @@ class TestToyotaSecOcSafety(TestToyotaSecOcSafetyBase):
def setUp(self): def setUp(self):
self.packer = CANPackerSafety("toyota_secoc_pt_generated") self.packer = CANPackerSafety("toyota_secoc_pt_generated")
self.safety = libsafety_py.libsafety self.safety = libsafety_py.libsafety
self.safety.set_current_safety_param_iq(self.SAFETY_PARAM_IQ)
self.safety.set_safety_hooks(CarParams.SafetyModel.toyota, self.EPS_SCALE | ToyotaSafetyFlags.SECOC) self.safety.set_safety_hooks(CarParams.SafetyModel.toyota, self.EPS_SCALE | ToyotaSafetyFlags.SECOC)
self.safety.init_tests() self.safety.init_tests()

View File

@@ -249,6 +249,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"MacModelFailed", {CLEAR_ON_MANAGER_START, BOOL}}, {"MacModelFailed", {CLEAR_ON_MANAGER_START, BOOL}},
{"MacModelLastError", {CLEAR_ON_MANAGER_START, STRING}}, {"MacModelLastError", {CLEAR_ON_MANAGER_START, STRING}},
{"MacModelLatencyMs", {CLEAR_ON_MANAGER_START, FLOAT, "0.0"}}, {"MacModelLatencyMs", {CLEAR_ON_MANAGER_START, FLOAT, "0.0"}},
{"MacModelRetryBackoff", {CLEAR_ON_MANAGER_START, STRING, "10.0"}},
// comma USB eGPU big-model backend. Mutually exclusive with eMac at // comma USB eGPU big-model backend. Mutually exclusive with eMac at
// runtime (eMac wins). UsbGpu* naming/flags mirror comma's handover branch // runtime (eMac wins). UsbGpu* naming/flags mirror comma's handover branch
@@ -363,6 +364,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"IQLateralCurvatureLookahead", {PERSISTENT, BOOL, "0"}}, {"IQLateralCurvatureLookahead", {PERSISTENT, BOOL, "0"}},
{"IQSoftwareSteerDelay", {PERSISTENT, FLOAT, "0.2"}}, {"IQSoftwareSteerDelay", {PERSISTENT, FLOAT, "0.2"}},
{"IQSteerDelayCache", {PERSISTENT, FLOAT, "0.2"}}, {"IQSteerDelayCache", {PERSISTENT, FLOAT, "0.2"}},
{"IQLatJerkGain", {PERSISTENT, STRING, "1.0"}},
{"LaneChangeBsd", {PERSISTENT, INT, "0"}}, // -1 ignore BSD, 0 default, 1 block lane change on BSD {"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) {"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 {"LaneChangeDelay", {PERSISTENT, FLOAT, "0.0"}}, // tenths of a second; scaled by 0.1 in desire_helper

View File

@@ -209,6 +209,25 @@ def region_bundle_path(selector: str) -> Path:
return region_bundle_dir(selector) / "tiles" / "offline.mbtiles" return region_bundle_dir(selector) / "tiles" / "offline.mbtiles"
def region_valhalla_path(selector: str) -> Path:
# valhalla mmaps this tar in place, so it stays uncompressed on disk
return region_bundle_dir(selector) / "valhalla" / "tiles.tar"
def region_valhalla_installed(selector: str) -> bool:
return region_valhalla_path(selector).exists()
def installed_valhalla_selectors() -> list[str]:
regions_root = offline_map_root() / "regions"
if not regions_root.exists():
return []
return sorted(
child.name for child in regions_root.iterdir()
if child.is_dir() and (child / "valhalla" / "tiles.tar").exists()
)
def region_bundle_installed(selector: str) -> bool: def region_bundle_installed(selector: str) -> bool:
return region_bundle_path(selector).exists() return region_bundle_path(selector).exists()
@@ -313,6 +332,22 @@ class TileBundleDownloader:
) )
if not day_ok: if not day_ok:
cloudlog.warning(f"iq_maps: day-style bundle failed for {selector}; night set installed") cloudlog.warning(f"iq_maps: day-style bundle failed for {selector}; night set installed")
if entry.get("valhalla_path"):
# routing is additive: a region whose extract is missing or corrupt must still end up
# with a usable map rather than failing the whole download
try:
nav_ok = self._download_file(
selector, base_url, entry["valhalla_path"], int(entry.get("valhalla_bytes", 0)),
str(entry.get("valhalla_sha256", "")).strip().lower(),
region_valhalla_path(selector),
progress_offset + int(entry.get("bytes", 0)) + int(entry.get("day_bytes", 0)),
progress_total, 1, entry.get("valhalla_objects"),
)
except Exception as exc:
nav_ok = False
cloudlog.warning(f"iq_maps: routing extract errored for {selector}: {exc}")
if not nav_ok:
cloudlog.warning(f"iq_maps: routing extract failed for {selector}; map tiles installed")
_write_manifest(selector, entry) _write_manifest(selector, entry)
cloudlog.info(f"iq_maps: installed tile bundle {selector}") cloudlog.info(f"iq_maps: installed tile bundle {selector}")
return True return True

View File

@@ -186,6 +186,8 @@ class NNTorqueModel:
PLAN_SAMPLE_START = 5 PLAN_SAMPLE_START = 5
LAG_EXTRA_S = 0.0 LAG_EXTRA_S = 0.0
JERK_AHEAD_TAU_S = 0.15 # low-pass on the model-derived jerk feed-forward (kills big-model accel.y noise)
JERK_PARAM_REFRESH = 100 # cycles (~1s at 100Hz)
BASE_P = 0.8 BASE_P = 0.8
BASE_I = 0.15 BASE_I = 0.15
@@ -264,6 +266,15 @@ class PilotLateralBrain:
self.friction_look_ahead_bp = [9.0, 30.0] self.friction_look_ahead_bp = [9.0, 30.0]
self.lat_jerk_friction_factor = 0.4 self.lat_jerk_friction_factor = 0.4
self.lat_accel_friction_factor = 0.7 self.lat_accel_friction_factor = 0.7
# The model-derived jerk term is a frame-to-frame derivative of model_v2.acceleration.y; on
# spatial/big models that array is slightly noisy and the raw derivative drives in-lane steering
# oscillation (sunny/stock has no such term). Low-pass it, and expose a live gain so it can be
# tuned to 0 (== stock friction ff) without a software push.
self._jerk_lp = FirstOrderFilter(0.0, JERK_AHEAD_TAU_S, 0.01)
self._jerk_gain = 1.0
self._jerk_param_frame = 0
self._jerk_param_ok = True
self._params = Params()
self.t_diffs = np.diff(ModelConstants.T_IDXS) self.t_diffs = np.diff(ModelConstants.T_IDXS)
self.desired_lat_jerk_time = cp.steerActuatorDelay + LAG_EXTRA_S self.desired_lat_jerk_time = cp.steerActuatorDelay + LAG_EXTRA_S
@@ -294,6 +305,17 @@ class PilotLateralBrain:
self.jerk_ahead = 0.0 self.jerk_ahead = 0.0
def update_calculations(self, car_state, vehicle_model, desired_lat_accel): def update_calculations(self, car_state, vehicle_model, desired_lat_accel):
self._jerk_param_frame += 1
if self._jerk_param_ok and self._jerk_param_frame % JERK_PARAM_REFRESH == 0:
try:
raw = self._params.get("IQLatJerkGain")
self._jerk_gain = float(raw) if raw not in (None, b"", "") else 1.0
except (ValueError, TypeError):
self._jerk_gain = 1.0
except Exception:
# param key absent (params not rebuilt) — never let a param read touch lateral control
self._jerk_param_ok = False
self._jerk_gain = 1.0
self._reset_jerk_estimates(car_state, vehicle_model) self._reset_jerk_estimates(car_state, vehicle_model)
if not self.model_valid: if not self.model_valid:
return return
@@ -305,6 +327,7 @@ class PilotLateralBrain:
forecast = _pointwise_jerk(accel_y, self.t_diffs) forecast = _pointwise_jerk(accel_y, self.t_diffs)
window = forecast[PLAN_SAMPLE_START:self._horizon_index(car_state.vEgo)] window = forecast[PLAN_SAMPLE_START:self._horizon_index(car_state.vEgo)]
self.jerk_ahead = sign_locked_min(window, desired_jerk) self.jerk_ahead = sign_locked_min(window, desired_jerk)
self.jerk_ahead = self._jerk_lp.update(self.jerk_ahead) * self._jerk_gain
if self.jerk_ahead == 0.0: if self.jerk_ahead == 0.0:
self.jerk_now = 0.0 self.jerk_now = 0.0

View File

@@ -0,0 +1,81 @@
"""
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
"""
import numpy as np
from iqpilot.cereal import car, log
from iqdbc.car.car_helpers import interfaces
from iqdbc.car.toyota.values import CAR as TOYOTA
from iqdbc.car.vehicle_model import VehicleModel
from iqpilot.common.realtime import DT_CTRL
from iqpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
from iqpilot.selfdrive.car.helpers import convert_to_capnp
from iqpilot.selfdrive.car import interfaces as iqpilot_interfaces
from iqpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N
CAR_NAME = TOYOTA.TOYOTA_COROLLA_TSS2
def _brain():
CI_cls = interfaces[CAR_NAME]
CP = CI_cls.get_non_essential_params(CAR_NAME)
CP_IQ = CI_cls.get_non_essential_params_iq(CP, CAR_NAME)
CI = CI_cls(CP, CP_IQ)
iqpilot_interfaces.apply_iq_car_config(CI)
ctrl = LatControlTorque(CP.as_reader(), convert_to_capnp(CP_IQ).as_reader(), CI, DT_CTRL)
return ctrl.nnff_assist, VehicleModel(CP)
def _model(rng):
# same-sign accel ramp (so sign_locked_min yields a real jerk) whose slope jitters frame to
# frame the way a spatial big model's path does — this is what drives jerk_ahead to swing.
n = max(CONTROL_N, 33)
slope = abs(0.5 + rng.normal(0, 0.25))
m = log.ModelDataV2.new_message()
m.acceleration.y = (slope * np.arange(n) * 0.1).tolist()
m.orientation.x = [0.0] * n
return m
def _cs():
cs = car.CarState.new_message()
cs.vEgo = 25.0
cs.steeringRateDeg = 0.0
return cs
def _run(lp_on):
brain, VM = _brain()
rng = np.random.default_rng(7)
cs = _cs()
out = []
for _ in range(400):
brain.update_model_v2(_model(rng))
if not lp_on:
brain._jerk_lp.update = lambda x: x # bypass low-pass == pre-fix behavior
brain.update_calculations(cs, VM, 0.0)
out.append(brain.jerk_ahead)
return np.array(out)
def test_lowpass_cuts_jerk_command_swing():
old = _run(lp_on=False)
new = _run(lp_on=True)
# the path must actually exercise the jerk feed-forward (guard against a vacuous test)
assert np.abs(np.diff(old)).mean() > 0.02, "input did not exercise jerk_ahead"
old_swing = np.abs(np.diff(old)).mean()
new_swing = np.abs(np.diff(new)).mean()
# low-pass must cut the frame-to-frame jerk swing (the wheel oscillation) by a large margin
assert new_swing < 0.3 * old_swing, (old_swing, new_swing)
def test_gain_zero_matches_stock():
brain, VM = _brain()
brain._jerk_param_ok = False
brain._jerk_gain = 0.0
rng = np.random.default_rng(1)
cs = _cs()
for _ in range(60):
brain.update_model_v2(_model(rng))
brain.update_calculations(cs, VM, 0.0)
assert brain.jerk_ahead == 0.0 # no model-jerk term == sunny/stock feedforward

View File

@@ -357,6 +357,7 @@ def main() -> None:
})) }))
if run_count % 100 == 0: if run_count % 100 == 0:
cloudlog.warning(f"modeld_selector misses: {miss_reasons}") cloudlog.warning(f"modeld_selector misses: {miss_reasons}")
pwriter.put_bool(keys["active"], latch.active)
cloudlog.warning(f"modeld_selector: big_used={big_used_count}/{run_count} " cloudlog.warning(f"modeld_selector: big_used={big_used_count}/{run_count} "
f"last_big_peek={big_peek} target={target} active={latch.active} " f"last_big_peek={big_peek} target={target} active={latch.active} "
f"max_big_lag={BIG_MAX_LAG_FRAMES}") f"max_big_lag={BIG_MAX_LAG_FRAMES}")

View File

@@ -13,7 +13,9 @@ import time
os.environ.setdefault("FLOAT16", "1") os.environ.setdefault("FLOAT16", "1")
os.environ.setdefault("JIT_BATCH_SIZE", "0") os.environ.setdefault("JIT_BATCH_SIZE", "0")
os.environ.setdefault("GMMU", "0") os.environ.setdefault("GMMU", "0")
os.environ.setdefault("TC_OPT", "2") # TC_OPT=2 lets tinygrad pick tensor-core kernels; on some models a TC kernel miscompiles and biases
# the output (documented on Metal). A parity gate below catches it and re-compiles with TC off.
os.environ.setdefault("TC_OPT", "0" if ("--tc-off" in sys.argv or os.environ.get("IQ_EGPU_TC_OFF")) else "2")
HOST = "--host" in sys.argv HOST = "--host" in sys.argv
if HOST: if HOST:
@@ -32,6 +34,10 @@ INPUT_SPEC = dict(MODEL_INPUT_SPEC)
patch_tinygrad_fetch_fw() patch_tinygrad_fetch_fw()
SEED = 42 SEED = 42
class _ParityFail(RuntimeError):
pass
KERNEL_PROGRESS_SCALE = 260.0 KERNEL_PROGRESS_SCALE = 260.0
@@ -151,6 +157,25 @@ def _policy_frame(seed: int, input_spec: dict):
return warped return warped
def _tc_off_reference(onnx_path: str, meta: dict):
"""Compile+run the model with tensor cores OFF in a child process and return the last of 3
policy frames. This is the trusted reference: TC-off kernels are the conservative path the
eMac gate also trusts. Used to catch a TC kernel miscompile that would bias steering."""
import subprocess
import tempfile
with tempfile.TemporaryDirectory() as td:
ref = os.path.join(td, "ref.npy")
env = {k: v for k, v in os.environ.items() if k not in ("TC_OPT", "BEAM")}
env["TC_OPT"] = "0"
env["IQ_EGPU_REFERENCE"] = ref
r = subprocess.run([sys.executable, "-m", "iqpilot.selfdrive.iqmodeld.tools.compile_egpu_model",
"--model", meta["key"], "--onnx", onnx_path, "--tc-off"],
env=env, capture_output=True, text=True, timeout=14400)
if r.returncode != 0 or not os.path.isfile(ref):
raise RuntimeError(f"parity reference compile failed:\n{r.stderr[-2000:]}")
return np.load(ref)
def compile_policy_model(meta: dict, onnx_path: str, out_path: str) -> str: def compile_policy_model(meta: dict, onnx_path: str, out_path: str) -> str:
from tinygrad.device import Device from tinygrad.device import Device
from tinygrad.engine.jit import TinyJit from tinygrad.engine.jit import TinyJit
@@ -216,6 +241,10 @@ def compile_policy_model(meta: dict, onnx_path: str, out_path: str) -> str:
flat = out.numpy().reshape(-1) flat = out.numpy().reshape(-1)
packed.views["prev_feat"][:] = flat[meta["output_slices"]["hidden_state"]].reshape(packed.views["prev_feat"].shape) packed.views["prev_feat"][:] = flat[meta["output_slices"]["hidden_state"]].reshape(packed.views["prev_feat"].shape)
outs.append(flat) outs.append(flat)
ref_target = os.environ.get("IQ_EGPU_REFERENCE")
if ref_target:
np.save(ref_target, outs[-1])
return out_path
if HOST: if HOST:
os.replace(tmp, out_path) os.replace(tmp, out_path)
return out_path return out_path
@@ -229,6 +258,13 @@ def compile_policy_model(meta: dict, onnx_path: str, out_path: str) -> str:
from iqpilot.selfdrive.iqmodeld.tools.compile_supercombo import _slice_outputs, _validate_pose_outputs from iqpilot.selfdrive.iqmodeld.tools.compile_supercombo import _slice_outputs, _validate_pose_outputs
_validate_pose_outputs(PhaseParser().parse_vision_outputs(_slice_outputs(outs[-1], meta["output_slices"]))) _validate_pose_outputs(PhaseParser().parse_vision_outputs(_slice_outputs(outs[-1], meta["output_slices"])))
if os.environ.get("TC_OPT") != "0" and not os.environ.get("IQ_EGPU_SKIP_PARITY"):
ref = _tc_off_reference(onnx_path, meta)
rel = float(np.abs(outs[-1] - ref).mean() / max(1e-3, float(np.abs(ref).mean())))
if rel > 0.01:
raise _ParityFail(f"PARITY FAIL: TC kernels miscompiled {meta['key']} (rel={rel:.4f} vs TC-off); recompiling with tensor cores disabled")
print(f" parity vs TC-off reference: rel={rel:.6f} OK")
os.replace(tmp, out_path) os.replace(tmp, out_path)
return out_path return out_path
@@ -244,6 +280,7 @@ def main() -> None:
p.add_argument("--format", type=int, default=2, choices=(1, 2)) p.add_argument("--format", type=int, default=2, choices=(1, 2))
p.add_argument("--host", action="store_true", help="compile on a mock dock (no AMD hardware); outputs need a dock parity gate") p.add_argument("--host", action="store_true", help="compile on a mock dock (no AMD hardware); outputs need a dock parity gate")
p.add_argument("--arch", default=None, help="target gfx arch for --host") p.add_argument("--arch", default=None, help="target gfx arch for --host")
p.add_argument("--tc-off", action="store_true", help="disable tensor-core kernels (conservative; auto-set on parity failure)")
args = p.parse_args() args = p.parse_args()
if args.host and args.format != 2: if args.host and args.format != 2:
raise SystemExit("--host supports format 2 only") raise SystemExit("--host supports format 2 only")
@@ -275,7 +312,15 @@ def main() -> None:
sampler.start() sampler.start()
try: try:
build = compile_policy_model if args.format == 2 else compile_model build = compile_policy_model if args.format == 2 else compile_model
out = build(meta, onnx_path, args.output or egpu_pkl_path(meta)) try:
out = build(meta, onnx_path, args.output or egpu_pkl_path(meta))
except _ParityFail as e:
if os.environ.get("TC_OPT") == "0" or args.format != 2:
raise
print(f"{e}\nretrying compile with tensor cores disabled", flush=True)
os.environ["TC_OPT"] = "0"
os.environ["IQ_EGPU_TC_OFF"] = "1"
out = compile_policy_model(meta, onnx_path, args.output or egpu_pkl_path(meta))
finally: finally:
if stop is not None: if stop is not None:
stop.set() stop.set()

View File

@@ -274,7 +274,10 @@ class SelfdriveD(GapButtonActions):
dock_present = self.sm['deviceState'].egpuDockPresent dock_present = self.sm['deviceState'].egpuDockPresent
mac_active = self.params.get_bool("MacModelActive") mac_active = self.params.get_bool("MacModelActive")
model_unavailable = big_active is True and self.sm.seen['modelV2'] and not self.sm.alive['modelV2'] model_unavailable = big_active is True and self.sm.seen['modelV2'] and not self.sm.alive['modelV2']
big_failed = (big_active is False or model_unavailable # an explicit False before this session's first activation is just the selector arming
# (it pre-clears the param on startup); alerting on it pops "big model failed" on every
# return to the road until the latch warms up
big_failed = ((self.big_model_active and big_active is False) or model_unavailable
or (self.big_model_active and not dock_present)) and not mac_active or (self.big_model_active and not dock_present)) and not mac_active
if big_failed: if big_failed:
self.events.add(EventName.bigModelFailed) self.events.add(EventName.bigModelFailed)

View File

@@ -152,7 +152,7 @@ class MainLayout(Widget):
self._layouts[MainState.ROUTES].set_on_play(self.open_video) self._layouts[MainState.ROUTES].set_on_play(self.open_video)
self._layouts[MainState.VIDEO].set_on_back(self.open_routes) self._layouts[MainState.VIDEO].set_on_back(self.open_routes)
self._layouts[MainState.ONROAD].set_click_callback(self._on_onroad_clicked) self._layouts[MainState.ONROAD].set_click_callback(self._on_onroad_clicked)
device.add_interactive_timeout_callback(self._set_mode_for_state) device.add_interactive_timeout_callback(self._on_interactive_timeout)
def _update_layout_rects(self): def _update_layout_rects(self):
self._sidebar_rect = rl.Rectangle(self._rect.x, self._rect.y, SIDEBAR_WIDTH, self._rect.height) self._sidebar_rect = rl.Rectangle(self._rect.x, self._rect.y, SIDEBAR_WIDTH, self._rect.height)
@@ -166,6 +166,20 @@ class MainLayout(Widget):
self._set_mode_for_state() self._set_mode_for_state()
def _car_stationary(self) -> bool:
if not ui_state.sm.valid["carState"]:
return False
return ui_state.sm["carState"].vEgo < 0.1
def _on_interactive_timeout(self):
# The idle timeout normally returns the UI to the road view. Don't yank the user out of Settings
# while the car is stationary - e.g. a hybrid parked with the engine running to charge reads as
# onroad (ignition tracks the ICE), so this would otherwise make Settings unusable while parked.
# A moving car still returns to the road view.
if self._current_mode == MainState.SETTINGS and ui_state.started and self._car_stationary():
return
self._set_mode_for_state()
def _set_mode_for_state(self): def _set_mode_for_state(self):
if ui_state.started: if ui_state.started:
# Don't hide sidebar from interactive timeout # Don't hide sidebar from interactive timeout

View File

@@ -0,0 +1,56 @@
from types import SimpleNamespace
import pytest
from iqpilot.selfdrive.ui.layouts.main import MainLayout, MainState
def make_layout(current_mode):
layout = object.__new__(MainLayout)
layout._current_mode = current_mode
layout._set_mode_calls = []
layout._set_mode_for_state = lambda: layout._set_mode_calls.append(current_mode)
return layout
class FakeSm:
def __init__(self, v_ego, carstate_valid):
self.valid = {"carState": carstate_valid}
self._v_ego = v_ego
def __getitem__(self, key):
return SimpleNamespace(vEgo=self._v_ego)
class TestSettingsInteractiveTimeout:
def _run(self, current_mode, started, v_ego, carstate_valid=True):
layout = make_layout(current_mode)
fake = SimpleNamespace(started=started, sm=FakeSm(v_ego, carstate_valid))
monkeypatch = pytest.MonkeyPatch()
monkeypatch.setattr("iqpilot.selfdrive.ui.layouts.main.ui_state", fake)
try:
layout._on_interactive_timeout()
finally:
monkeypatch.undo()
return layout._set_mode_calls
def test_stationary_in_settings_stays(self):
# parked/charging hybrid reads onroad; the timeout must not eject from Settings
assert self._run(MainState.SETTINGS, started=True, v_ego=0.0) == []
def test_moving_in_settings_returns_to_road(self):
assert self._run(MainState.SETTINGS, started=True, v_ego=5.0) == [MainState.SETTINGS]
def test_onroad_layout_always_handled(self):
assert self._run(MainState.ONROAD, started=True, v_ego=0.0) == [MainState.ONROAD]
def test_home_layout_always_handled(self):
assert self._run(MainState.HOME, started=True, v_ego=0.0) == [MainState.HOME]
def test_offroad_in_settings_handled(self):
# car off (offroad): existing behavior is unchanged
assert self._run(MainState.SETTINGS, started=False, v_ego=0.0) == [MainState.SETTINGS]
def test_invalid_carstate_treated_as_moving(self):
# if speed is unknown, fail safe to the road view rather than trapping in settings
assert self._run(MainState.SETTINGS, started=True, v_ego=0.0, carstate_valid=False) == [MainState.SETTINGS]

View File

@@ -67,14 +67,14 @@
}, },
{ {
"name": "boot", "name": "boot",
"url": "https://git.konn3kt.com/IQ.Lvbs/iqos/raw/branch/master/boot-aea4aecefd188d9c95726b699902378676c6266a7ae37008b5c2aa0e1e1190fb.img.xz", "url": "https://git.konn3kt.com/IQ.Lvbs/iqos/raw/branch/master/boot-4258981292746f5622f4a03a1e0c1d77379660e46bbaa455c504d638b5307906.img.xz",
"hash": "aea4aecefd188d9c95726b699902378676c6266a7ae37008b5c2aa0e1e1190fb", "hash": "4258981292746f5622f4a03a1e0c1d77379660e46bbaa455c504d638b5307906",
"hash_raw": "aea4aecefd188d9c95726b699902378676c6266a7ae37008b5c2aa0e1e1190fb", "hash_raw": "4258981292746f5622f4a03a1e0c1d77379660e46bbaa455c504d638b5307906",
"size": 18216960, "size": 18216960,
"sparse": false, "sparse": false,
"full_check": true, "full_check": true,
"has_ab": true, "has_ab": true,
"ondevice_hash": "f568e4394e36a367de58cce2b982bf597963e088b6a00b1ac88ca03cce8f62fd" "ondevice_hash": "7be61ba2be5617ac22d6beb8ba8b896109a554347021f5e44b72458748336975"
}, },
{ {
"name": "system", "name": "system",

View File

@@ -1 +1 @@
qT72MCHtDUWnARJbSsLUcPaISRZxjFPNf282R9ZC1SXvFJz6X6NmjgknK3OLDLijOQWlGvvJZGsDgz2vsdPVAg== Myu8k7msX7pt28JKl1Lx5/NnXu9i15nT8bFWnwGsip8wdCYPJqj69AWrfCtlg9YrR98O6ThCAEA1uzUfsrTeDQ==

View File

@@ -56,14 +56,14 @@
}, },
{ {
"name": "boot", "name": "boot",
"url": "https://git.konn3kt.com/IQ.Lvbs/iqos/raw/branch/master/boot-aea4aecefd188d9c95726b699902378676c6266a7ae37008b5c2aa0e1e1190fb.img.xz", "url": "https://git.konn3kt.com/IQ.Lvbs/iqos/raw/branch/master/boot-4258981292746f5622f4a03a1e0c1d77379660e46bbaa455c504d638b5307906.img.xz",
"hash": "aea4aecefd188d9c95726b699902378676c6266a7ae37008b5c2aa0e1e1190fb", "hash": "4258981292746f5622f4a03a1e0c1d77379660e46bbaa455c504d638b5307906",
"hash_raw": "aea4aecefd188d9c95726b699902378676c6266a7ae37008b5c2aa0e1e1190fb", "hash_raw": "4258981292746f5622f4a03a1e0c1d77379660e46bbaa455c504d638b5307906",
"size": 18216960, "size": 18216960,
"sparse": false, "sparse": false,
"full_check": true, "full_check": true,
"has_ab": true, "has_ab": true,
"ondevice_hash": "f568e4394e36a367de58cce2b982bf597963e088b6a00b1ac88ca03cce8f62fd" "ondevice_hash": "7be61ba2be5617ac22d6beb8ba8b896109a554347021f5e44b72458748336975"
}, },
{ {
"name": "system", "name": "system",

View File

@@ -1 +1 @@
fL46Z+k/wqjavP3J1S/VzE90BXcvvRF+S41MgHEXYjW+Jlnu5REOp6rvp3SJFEkKIEaiSf9PsHyiDgQkC6J2Aw== aGtkoy//jP3MWJmd5uvb/mxKu1KsE8ZlhrTUr1CPCotwjIVzsxOncUGN9HnUcMXoUWZbUWz9H8Q+ARYxz41/Ag==