IQ.Pilot Release Commit @ 27f668a

This commit is contained in:
IQ.Lvbs CI [bot]
2026-09-03 18:17:35 -05:00
parent 7745f48100
commit 1b2f28290b
69 changed files with 728 additions and 193 deletions

View File

@@ -201,8 +201,8 @@
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/egpu_helpers.py": {
"mode": 420,
"sha256": "0b8f118dc45ad0c3809855800960b47c893ee121ea75d94e8569dcce46d58ce5",
"size": 7312
"sha256": "07b3ace231d9912f1926ffe7cef0f2a5d9649f75b7ebac2b9f296957d98be166",
"size": 7681
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/egpu_model.py": {
"mode": 420,
@@ -216,8 +216,8 @@
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/messaging.py": {
"mode": 420,
"sha256": "17d48331eeeaf746ee6982a9c65f28d740a15a4c34c37617115c104894d49c06",
"size": 11920
"sha256": "1d345eb35d4eea2a0b8c8e8491b4c1a74e073d8340703cdd334ec78992026f72",
"size": 12033
},
"python/_iqclosure/iqpilot/selfdrive/iqmodeld/metadata.py": {
"mode": 420,
@@ -536,27 +536,27 @@
},
"python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "f42a1bab373092b3010ca45b02d7ea7b27cd1e1e574c97f701129b2fad01f833",
"sha256": "0fec7e56196064f024cf317a6f23d9f2111f90753c48bb7d616e06e27335157d",
"size": 333888
},
"python/iqpilot_private/emac/ffs_holder.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "306f0362e86558d33072f800b736fa71bfaf4ee5e4383c4e743b72b74724d2e3",
"sha256": "4d6e7c4bc97ced0bf2def103d2fc8eb98364747aa33e495bc448c68644739750",
"size": 68064
},
"python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "31339f53f093c3877e13eac90a6647cbca2de92f339c47e5006a85bcd2e3656c",
"sha256": "a23b14f9f2c5c7a058c20ef0421bb741fc67c2adff13824c1c808e6831430f91",
"size": 267960
},
"python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "d5e855d1784a8ba66844639b871d2747ccce264c55359cf0e641ce7e3d66460d",
"size": 203512
"sha256": "189c7d6d5599f92fc224a086ef55520118130785f482c0667cc808071c84e718",
"size": 203544
},
"python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "34d4ef1746143ea714f84f3c325e2ddaecec6a19c544fed68d8fc4080578c73f",
"sha256": "2cf0bc9d6362f2d4c9691f4f0ef7989a5651cc3153e99e5c2e15d9da1b93e598",
"size": 399040
},
"runtime": {
@@ -568,10 +568,10 @@
}
},
"signatures": {
"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": "Sv2riVgvm+4PDVTDLTuy1wpf15JObbP8hSAD4iiwF4RUs49GVGhulXkjzO28mLmF6tEuNDNWHRucxmuZrCKNAg==",
"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": "hvBkDKSMKjpxmRzhhGoRZT5WuOWLY+g3p2NIMlA7b6qYYpVaApoZNuquOerywEF1zJnwPiwpsg5QtQVe/5DfAA==",
"python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": "d3JAlH7P/X87II6iobeQ51melHy1vxdlXn0Nzo2DG65rcdVLM2B66YETPpwlX6Qv9NxhUlDcl/364CR+V+eyDQ=="
"python/iqpilot_private/emac/bulk_transport.cpython-312-aarch64-linux-gnu.so": "laKo35uiFJAsuGj7+N5l5IVbXmTQ/jyB01c31OfA7XCT0AG09/cg9ArgDhQv5qr3babesKgUKoPg2vtFnnXyDg==",
"python/iqpilot_private/emac/ffs_holder.cpython-312-aarch64-linux-gnu.so": "Sd6VF2hrGHXoXTe3IpCYoTDczM741xcQ1sKiHGcCgNqkeORvLumWxic20ylmWhWOTHziPB9Y723vFOGcKZzDAQ==",
"python/iqpilot_private/emac/mac_client.cpython-312-aarch64-linux-gnu.so": "O9O/B4IDlCutovmEPDnLxdKAFsURjXS+pyaK0OvmoOlOQW1fQS+ZahxaQ0dkwASDzEMWUxUFr0j9PrlihUVpCA==",
"python/iqpilot_private/emac/mac_protocol.cpython-312-aarch64-linux-gnu.so": "77vKNawQIdv95LPwTFn79N2RXaz6dQUcRtgtnH+R7T2TN2LBSKhKvtzu2sKkKyDG7038qrJfhE9BiJYtho56BA==",
"python/iqpilot_private/emac/maciqmodeld.cpython-312-aarch64-linux-gnu.so": "Fhxo++ZiJmWGBghe/dH/ytHe+T67v/HAA6k4js7Bd+C07dA/zMlgTI45QlBx2fZN2qC602Z4Stijy+/EQziuCg=="
}
}

View File

@@ -1 +1 @@
KylnzVsK5XC3Jq1G7bBbKa5lv+oJSlfBpBsNpWMEEUgHDyOEj7+5EeeHDOgy8Lt4LEFgEeIV2WEJW0GJVpxUCQ==
nBzDAB5lnwH7Ynehfwf48y7ACh39HIRNiff55kmMtNG466wcxBrkMcnEJz/rDf7ehRcZmyD08CVW5QCfMsHBAA==

View File

@@ -71,6 +71,11 @@ def egpu_oob_pkl_path(meta: dict) -> str:
return os.path.join(Paths.model_root(), f"egpu_{meta['key']}_{meta['sha256'][:8]}_amd_policy_oob.pkl")
def egpu_model_oob_pkl_path(meta: dict) -> str:
from iqpilot.system.hardware.hw import Paths
return os.path.join(Paths.model_root(), f"egpu_{meta['key']}_{meta['sha256'][:8]}_amd_model_oob.pkl")
def onnx_cache_path(meta: dict) -> str:
from iqpilot.system.hardware.hw import Paths
return os.path.join(Paths.model_root(), f"{meta['model_name']}_{meta['sha256'][:8]}.onnx")
@@ -156,13 +161,21 @@ def download_onnx(meta: dict, progress_cb=None) -> str:
return path
def download_precompiled(meta: dict, progress_cb=None, policy: bool = False, oob: bool = False) -> str | None:
field = "egpu_oob_artifact" if oob else "egpu_policy_artifact" if policy else "egpu_artifact"
ARTIFACT_PATHS = {
"egpu_model_oob_artifact": egpu_model_oob_pkl_path,
"egpu_oob_artifact": egpu_oob_pkl_path,
"egpu_policy_artifact": egpu_policy_pkl_path,
"egpu_artifact": egpu_pkl_path,
}
def download_precompiled(meta: dict, progress_cb=None, policy: bool = False, oob: bool = False, field: str | None = None) -> str | None:
field = field or ("egpu_oob_artifact" if oob else "egpu_policy_artifact" if policy else "egpu_artifact")
art = meta.get(field)
if not art or not (art.get("objects") or art.get("hf_path")):
return None
from iqpilot.selfdrive.iqmodeld.model_bundle_downloader import download_hf_file, download_lfs_bundle
dest = egpu_oob_pkl_path(meta) if oob else egpu_policy_pkl_path(meta) if policy else egpu_pkl_path(meta)
dest = ARTIFACT_PATHS[field](meta)
if art.get("hf_path"):
try:
return download_hf_file(art["hf_path"], dest, art["sha256"], int(art.get("size", 0)), progress_cb=progress_cb)

View File

@@ -248,16 +248,19 @@ def populate_drive_messages(primary_msg: capnp._DynamicStructBuilder, extended_m
def populate_odometry_message(msg: capnp._DynamicStructBuilder, outputs: dict[str, np.ndarray],
vipc_frame_id: int, vipc_dropped_frames: int,
timestamp_eof: int, live_calib_seen: bool) -> None:
msg.valid = live_calib_seen & (vipc_dropped_frames < 1)
pose = outputs["pose"][0, :6]
pose_stds = outputs["pose_stds"][0, :6]
pose_finite = bool(np.isfinite(pose).all() and np.isfinite(pose_stds).all())
msg.valid = live_calib_seen & (vipc_dropped_frames < 1) & pose_finite
odo = msg.cameraOdometry
odo.frameId = vipc_frame_id
odo.timestampEof = timestamp_eof
odo.trans = outputs["pose"][0, :3].tolist()
odo.rot = outputs["pose"][0, 3:].tolist()
odo.trans = pose[:3].tolist()
odo.rot = pose[3:6].tolist()
odo.wideFromDeviceEuler = outputs["wide_from_device_euler"][0, :].tolist()
odo.roadTransformTrans = outputs["road_transform"][0, :3].tolist()
odo.transStd = outputs["pose_stds"][0, :3].tolist()
odo.rotStd = outputs["pose_stds"][0, 3:].tolist()
odo.transStd = pose_stds[:3].tolist()
odo.rotStd = pose_stds[3:6].tolist()
odo.wideFromDeviceEulerStd = outputs["wide_from_device_euler_stds"][0, :].tolist()
odo.roadTransformTransStd = outputs["road_transform_stds"][0, :3].tolist()

View File

@@ -176,8 +176,8 @@
},
"python/iqpilot_private/models/__init__.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "aec72c78d58c37afb1b57bea4a29d322032fffdfa59deabdc85b4322b5ca3787",
"size": 67616
"sha256": "665a6a5163899f37bb7e41fd42fe79ecd77e50496285b60c6555971b8337f463",
"size": 67568
},
"python/iqpilot_private/models/__init__.py": {
"mode": 420,
@@ -186,38 +186,38 @@
},
"python/iqpilot_private/models/big_catalog.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "30a25ebd3b37e2307c3a80ae0fea70b314ab71427d313ba5e2d540c4ba7658ed",
"size": 133840
"sha256": "8a63da11107132869d7ea4e391197ca8bde2d13d6d92b09a83484503910a75d5",
"size": 133856
},
"python/iqpilot_private/models/egpu_model.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "e8cb29388e31f636d19f916d5a1d32f38c38113f5fa882344d4effceb6e37d85",
"size": 68112
"sha256": "7090be17ce2063905fc364ee925384708895150f4d6eef642a94b941d3c6c88d",
"size": 68096
},
"python/iqpilot_private/models/emac_model_meta.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "b7938d9521352d5c1fa6b0d7ff8f3f11062e33bcc0d9a72ffb0edfb525423b6e",
"size": 68264
"sha256": "c2691e7a3e77e78d20d21cbf0d0bc4d59fd294935c7ebd0cc7225b0d77619914",
"size": 134032
},
"python/iqpilot_private/models/fetcher.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "dad0c3c3a73338647da25dead17da0db6cebb24527bc9db381cd16753741f676",
"sha256": "d085741bb6a7d2a94d616d53e78e0ae76d381ca926017646ec33931375fb5963",
"size": 136200
},
"python/iqpilot_private/models/git_auth.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "ce815034dd8bdadb79131e0b51013a9f9772c4421dfb96b62431d84125089ad2",
"size": 68056
"sha256": "951ecc3665d6a2232f50ca3002f7f84753789ecd1adfc06cc85884b42c47e05c",
"size": 68032
},
"python/iqpilot_private/models/helpers.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "798843f52120210c19da5b6c98723ca15d782ea9cd445cc8f96009471990d618",
"size": 137944
"sha256": "8670d22574d59c9b2f43c0fa047967a3236fc4eb0ca315a61fe0c9f2636d886a",
"size": 203512
},
"python/iqpilot_private/models/manager.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "96be19ef4f59b87e44092c61f70676468617365923c4c8b91cf7e0b88cc62c05",
"size": 340000
"sha256": "c0fcfd5193cd11e52f22d8c804ace3b436f49aaa6655689c2f1903931544a977",
"size": 339976
},
"runtime": {
"entries": {
@@ -228,13 +228,13 @@
}
},
"signatures": {
"python/iqpilot_private/models/__init__.cpython-312-aarch64-linux-gnu.so": "CHPHmtjVAGTdtbWnwE5awrEKFJHs06YK4Gao9IKVaHZYXyj5WAc/d4t7hOKM3lm2/UfBaraTdVTdeSOij3dpCQ==",
"python/iqpilot_private/models/big_catalog.cpython-312-aarch64-linux-gnu.so": "Ekiv6tp3mDx+7Z4lNI+byVomzjShe93UkYQEOhYwl6kHlpxN06A3IqV8y5pwlJII4ZpE5yYEX9qp+VMXsdm9Aw==",
"python/iqpilot_private/models/egpu_model.cpython-312-aarch64-linux-gnu.so": "njYujmFBsfewILpjLI0nUN34rqzMNITattpewwxE4LuTcdkAgihQ3w/S/aYetSJzUtV/SH9mk26DhMciwn8KAw==",
"python/iqpilot_private/models/emac_model_meta.cpython-312-aarch64-linux-gnu.so": "zE2s6oQ3YqanaFG4RMNPydekiblRQVBk7QEJ5G30W2thGqzWt+1BW+75XdwaCigw//rINxKHbt3Neu5rub7PAw==",
"python/iqpilot_private/models/fetcher.cpython-312-aarch64-linux-gnu.so": "hBKd/smbmwEszsGjU7FP2RNVmrINBpU4NPZMGDjEHAF7Rx1WBF49+pH1s0k571RBqdtqN7nya2HCnKKO0tHUBw==",
"python/iqpilot_private/models/git_auth.cpython-312-aarch64-linux-gnu.so": "eKa9qfsfNNaSQpqggWSGw5YV/6cJk5iJcmhY+N/v3xdFvEMfCsIf+N3BhP0S3ZDO5T2CeE/I/Xc+1QbZh7oNDQ==",
"python/iqpilot_private/models/helpers.cpython-312-aarch64-linux-gnu.so": "k4BcKQAua+1YSC6P8jVEu2wBrI+WL8Bb5kKQbOnBYn/lupnrA96vMRYu4XVVDLGBoIqK8L7K/PYCYxu9Cx4RDA==",
"python/iqpilot_private/models/manager.cpython-312-aarch64-linux-gnu.so": "+53NsV73jkF/1x9Bzsw99oZRhVBtqlQTeXaPfHydTAssd2V3UMO+BWqagtGbVG5qIOcgjaNweNExGu+Abo7iDA=="
"python/iqpilot_private/models/__init__.cpython-312-aarch64-linux-gnu.so": "BaGn+mLXPAGLqnVJhDUt8C2ZEm1E9+MUCPoOnJhQGQybFOr36MuoubfpEgPoy1993Tm4IGD3pZASyFefJNXtBQ==",
"python/iqpilot_private/models/big_catalog.cpython-312-aarch64-linux-gnu.so": "DfeUTDggp3zRuVHoECcYdCliGhB1A/BdQoicWHddyTS4YptXxaow5Rads9qnmRIiBpSgR8d0vH9fwNxpQl0aBw==",
"python/iqpilot_private/models/egpu_model.cpython-312-aarch64-linux-gnu.so": "9sIFDj6iIVEUE+lxa3Ryk/djxgiPMu78MM/WQdjYfpVQNv2OQ7IkBu121iNa5XIv/CTFMl1G6KGTWO5/Qm9iBw==",
"python/iqpilot_private/models/emac_model_meta.cpython-312-aarch64-linux-gnu.so": "CjvP1I4QBJu6zRW75XHEUWX6F6Wks9xtnuu1hdjv/V2Vw11Vf+Wa8zCztpKBgMx3dzbQ/p++xpTf5p7TCoMyAA==",
"python/iqpilot_private/models/fetcher.cpython-312-aarch64-linux-gnu.so": "WNq4sNwhPeu7NuW+hj1UOv1lAiQN2k8lPwImfeS7vFLY0G+pUBpnCVhD49SmjMmPyfCoxJkxD+wdhogPT2u6DQ==",
"python/iqpilot_private/models/git_auth.cpython-312-aarch64-linux-gnu.so": "FJ7yptMwvQxz5kB6XLjNJKBDy3+jrwWsPe/KBa8ki/e6uNavH63Umu18z5e6aY4AYp9D8z0pBOt5lk1X+bFvBg==",
"python/iqpilot_private/models/helpers.cpython-312-aarch64-linux-gnu.so": "9sX2Peumx+BT3G+LRIbPtcs42mULnRiXJMbQq3esj7yfaGsbf3l5XQ/9BDr8U/A6aFWfatOYSwbFD37ZGdLxCg==",
"python/iqpilot_private/models/manager.cpython-312-aarch64-linux-gnu.so": "uyUgRSqgfhMcboJeDLTF1vAuw6thTLubkKp4wsn3i3VFAGkLzE0fEkhzoRo1TSn8hkb787kNQs+Z/woCPxi5AQ=="
}
}

View File

@@ -1 +1 @@
8t0c8OQ6AY5JlVXqGQkiF2rlj3kN3MIwGHVz4TOgaudkW7XegnRhUqngcpNo5C74wFDKNGWsvXOJ4WtIiIrfCg==
aZ2PpZxlTDJ1Nffu+hFtfKK0qyUkkllomgYgTX69J4YU4/aODiwBvJDwHZ6+1nlvv4fDA10s7SPNxwwP9j0OBA==

View File

@@ -186,8 +186,8 @@
},
"python/iqpilot_private/navd/__init__.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "6e6de2f3270ad097c9b847fa10e833bac6b8eaab8ddad955603d163474a3c846",
"size": 67616
"sha256": "10896807faa5044f6d42807fb7e5efefc6ef64243edea9da5639332964e1cb19",
"size": 67568
},
"python/iqpilot_private/navd/__init__.py": {
"mode": 420,
@@ -196,123 +196,128 @@
},
"python/iqpilot_private/navd/alpr_matcher.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "516370d0603f5134955826a3d8e9fea8ec0a9d27c116ad05af149a5c7ac15bcc",
"size": 269936
"sha256": "5faf834f012075671a390de2fe4ebb09f9c7d4c05406fa89407aa9e011e9d38c",
"size": 269896
},
"python/iqpilot_private/navd/amap_client.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "dc8f27970c61a3b60c383ea69804896ee356d76c8bf7af5532a9338525fbc956",
"size": 202040
"sha256": "a8361fe53878b61d30d430d0b0488eaabe1641e11b7e584e1ad3566370e55085",
"size": 202088
},
"python/iqpilot_private/navd/event_builder.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "96eb02a86cab08d557f8df0adebd2cc72232b9348e9896acd9b71784a86b42d5",
"size": 136960
"sha256": "47c673a898e6ceb321f01eff980250bbc85ddd058a8161098e8e91cb02f8f4a6",
"size": 136928
},
"python/iqpilot_private/navd/helpers.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "8fb8cc47f47971c530a68ccd81b9b5d9fc8ce82cdd1270967fa845399009e519",
"size": 135960
"sha256": "55a8b9eb2e17f9baa1a851919d3ce908b69004bfbd09591985195af2da4cc40c",
"size": 135928
},
"python/iqpilot_private/navd/incident_feed.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "b49f7ce7e9a33777ffa896a95fc4a5580988b4966d5a9330efd8c79a5116be91",
"size": 135584
"sha256": "115b57594d411fde87f66d0d030cd5de043bf8d986274dfb8fa7007b8f752c87",
"size": 135496
},
"python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "747ae769bb35650dcd16b94a0a6260213373e352ede88bc1e57f114f27761d5a",
"size": 135632
"sha256": "90c8b38b5c0a328c9ccce27292f8399546a237cc72caf837ffa4c1a55a529c47",
"size": 135600
},
"python/iqpilot_private/navd/long_decel.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "eae425cd9bf60286fff4b980c6057351cb940f28981556269f05022258d80f0c",
"size": 69752
"sha256": "6d80f03b5a4da8083d4b7f2d4b165748556230859bcdb96c2ff5164aa5500b6c",
"size": 135256
},
"python/iqpilot_private/navd/mapbox_client.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "0fc4356fe57a11538e4a248dc803ceee61781d7c700640f78241a2d57cbb4574",
"size": 68144
"sha256": "a6809b5c118c603706e6b709597a29daa2345fd53d736526f6a055a009ad70ac",
"size": 68128
},
"python/iqpilot_private/navd/nav_cameras.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "92521da14a5bf334324f32315b13773d37d5f7d209d4bad6d62afc12ab5d1705",
"size": 203056
"sha256": "05f7b0433be88afdae23a68f7acd92ff0c8ce6b04d83fc46ad2233c3df51dea0",
"size": 203024
},
"python/iqpilot_private/navd/nav_turn_signals.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "67454e461abb76c7a3053a7f728baa0beb30fba235639a0a2a927a9c80443a4a",
"size": 135560
"sha256": "96414985cacbd85d02f69473b84ff8215fbf959996ca1f1f0dfdf7c77503c9cc",
"size": 135536
},
"python/iqpilot_private/navd/navbridge_source.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "8bd53fba3f00e3f8320f873e72b3665f22b6065ee10425d270167b21d2348ad9",
"size": 136576
},
"python/iqpilot_private/navd/navd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "9e0d5e929bfcd960fdba11804d1f70848ba40a68a65093c025a6d48171d0b2cc",
"size": 797392
"sha256": "d395b00b81da1acdab40d6033b5ccf084effcc6531978af63d568d4ba17ee894",
"size": 862920
},
"python/iqpilot_private/navd/navincidentd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "33d40decd32e8cfe3f9a267ebfccf48e8bfc67bf9609206ef230a9566bf5581d",
"size": 135344
"sha256": "43bdd7f93c317f26bfa9203953b3a35bf91bd0475dc3777aa5fc3e991216c3f4",
"size": 135464
},
"python/iqpilot_private/navd/navrenderd.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "edf0d16511686ed3d4d7d6bf2b6b44fd8d484af348dab0eb415999656eb1d0af",
"size": 135584
"sha256": "4678f2e256e0fd2b01d4441af848056b173d7d2d6c8ba590ad32b8e23ee8591e",
"size": 135552
},
"python/iqpilot_private/navd/osm_cameras.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "b11bec6f1b49f169f218a87467781b6d62d64c367e0c3df5356341adce8c3f6b",
"size": 202384
"sha256": "52e4d03639c9d9be88209ab2e4a1f0f23c0adf81631983248f7d069d46cf61a5",
"size": 267936
},
"python/iqpilot_private/navd/reroute.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "d38218a6787234730ed39bdb04c00ab0778b1b56eefcedcb9ecfb31346d36378",
"size": 136216
"sha256": "cb066c1e5637b8a6b60b44753b6a0855a04ce7f5771464d71fc9da245de4a56e",
"size": 136224
},
"python/iqpilot_private/navd/road_incidents.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "f89f69e128b02eebc31b4c3702d62e12747f837863ad390026ea5004e506ea7c",
"size": 203136
"sha256": "2e226e29757151bc3cf350a933f6e8f402d7149b0b9e66b82ac9cb721cadef8e",
"size": 203096
},
"python/iqpilot_private/navd/route_manager.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "311926ffa2b4474320411240e6656ee676ce8b61d1cea444c34cd9520113a6a6",
"size": 943640
"sha256": "2b12153db93198ec46c268384cfbd65a9ae526faa584274987ed8a289531547c",
"size": 1009128
},
"python/iqpilot_private/navd/runtime_common.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "dc08b2e6bf7be3fe0b4a7ccdfceab6c434317e7626efd9b40ea5bf15739647ba",
"size": 135760
"sha256": "6ba9e9ae810a4e283a8ad544e1f42e899e2e98939754f9b88ffc21f320a8514f",
"size": 135752
},
"python/iqpilot_private/navd/traffic.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "f47146eddd7c8b499359d411150533c0e28e80851096d076ee8552a4d45175d9",
"size": 69720
"sha256": "0e233af11a3c07d6469e221b5f02cebee4addfe1706d635f8ed7624eb16ff908",
"size": 69688
},
"python/iqpilot_private/navd/traffic_sources.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "98845f8be1fd44009acf88fe685f6985ffb996d5d508858bdbb3edd2984a5146",
"size": 135968
"sha256": "8485347d40907cd0e654b93e1e872dffd156278c3868c640f7d2e51884d52a00",
"size": 135944
},
"python/iqpilot_private/navd/traffic_writer.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "15aba494968b9673987d2d0168d797bfbd307046555cb89efa2378acc8bb042a",
"size": 136936
"sha256": "c8b907c51c69c7950bc99ff577ddf4473dbb42f90e53252e3dd325df2a4f9ed3",
"size": 136944
},
"python/iqpilot_private/navd/turn_desire.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "715f94a985599a983750affa5bf36e1fc2e6502871f1af7de553f9aaf29028cb",
"size": 69768
"sha256": "c13b5fdec4a8d4a33dbd7c25d9ffba30f6faffa1d286438d80c4d9a615b31145",
"size": 135280
},
"python/iqpilot_private/navd/valhalla_process.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "2658d1a9f08f49e1294a3c26be733170ac7943970a79ee2969d9404cd4b55758",
"size": 204072
"sha256": "695322ac198a75121f05989387bd32a733f18921f01f373d45e22f7ec839e8a5",
"size": 204064
},
"python/iqpilot_private/navd/web_server.cpython-312-aarch64-linux-gnu.so": {
"mode": 493,
"sha256": "d03bf4d8d7d1547c5fcd085dea2ff78f8071e04e51e7f4d002f7ba23b431038e",
"size": 201400
"sha256": "24d967c264e1b879b0517da9cd6fddefdc4787efe6a52adcd2429efaac2db3db",
"size": 201368
},
"runtime": {
"entries": {
@@ -351,30 +356,31 @@
}
},
"signatures": {
"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": "5K4w4dDRtts8QvxBAXL0qrYofb/bg9PV/rKn/GrVmtAVDMd0DhAiKAugoxkhl8n4JviFY8ZmsHwWBrQPIMdkAA==",
"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": "dQnA8CCeRMXqiARr/Kzf2x736DuaT1/ALC8ZLDZep2/nY2IJ7StZeZ8DwOBuRPRK8KJ5HNU424QMZkVzQcDnBg==",
"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": "MJ/i1ZvGb3bYBVxKvT/zBk43CM/XT37GZa6la52Tfmqp9mrx6oz98PpvLZMgJ+18J4lSdMf2uGuJ2IHx5wWTAQ==",
"python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so": "+ULQz9iDSDBtYRYSVVWz63TImB3DizGV2oFER6pmNttfJ6vA4SBVToxKV1YjHAebdMiSrTBrEkgPd5K4unE0Bg==",
"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": "vqQRU0t5AS/oUs1JWxZlwHC5mrpaaHD10jgQCLzDGUL4Yv1aFHWAoM6KfFdUldmdSG1d4e6VGu+Y2bMdBIwAAw==",
"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": "WQOzntcyeraddagnvnxAwH/ciLFMztjfEhg07HFHy+++XJPCSGctdaxlgPWX6xq9kiE7aEkcTsW2CwOwYo3tDw==",
"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": "LBV8K1SPj+bN8idsPR/3B24grXQYRNarYsCUbVaRdkfORuib70gRlGz4KtyOyYT/vpCU4iuDhnSRsd4ltH4RAg==",
"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": "jjmBJtLWRVkPsO4n7/N/pDVD9jrC4SCMm0lIT/P8HCzsMhIIAkXhRAMGKdl1kYDxK8VMGRMMF7eoiCKEFPq2AA==",
"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": "J/I+XcODFE7LKvtyImt/Dw8fh2uvQeEhZ1IWx2IHZPH8sXhuVGVQIECVFGQJGfsckjzoUpgZq8AYNReRF62sBw==",
"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": "/5mYKn9um2aBLipkFsEptfM3sOId3SKkV8AHXGslWdH9Evp2ll8XENvirAX5tV1X35WslGYIybK3Xv3cZ4o8DQ==",
"python/iqpilot_private/navd/traffic.cpython-312-aarch64-linux-gnu.so": "kfl1Mm7G3CguQGtUvDutvbd4StDnKr0pW7ojLn6gydVTyxEiD/VAEFfTKBK/CAyUcw3eeX5+U8Qq2+6o0zEUBA==",
"python/iqpilot_private/navd/traffic_sources.cpython-312-aarch64-linux-gnu.so": "SzicVJ2/F8aMjcOHVCmEeJ952a+JPL8NLI6wsVkA2f5xiOSKMdynvcf7EgvrvBA4bgTRUGOmtpoqzMiCcNh2AA==",
"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=="
"python/iqpilot_private/navd/__init__.cpython-312-aarch64-linux-gnu.so": "3zeWvLW0gfxiEeHYfOO7atzH0V9/YxbbPpLV6cdokoSP+UlxRJUrMTkJiNCiLBzXuhnNbEhj0Y6q9dLK8QNDDw==",
"python/iqpilot_private/navd/alpr_matcher.cpython-312-aarch64-linux-gnu.so": "1vJWjDPECvOrFe5UfWFN3rFeZSfO3Te9o3Xj0ue1Q+2Zt+gMkvaXgOZG3DNfbs7yaZ2yheabOhKRdwvwHGumCA==",
"python/iqpilot_private/navd/amap_client.cpython-312-aarch64-linux-gnu.so": "6IWRbqN7iDaCKDPQ3Ho6cEhWrP0Tp6f5nKoo5sXEEAtYfUJVZADBHXY7Qk5MszNoa13Sn+8H2eE0imUwjGUtDA==",
"python/iqpilot_private/navd/event_builder.cpython-312-aarch64-linux-gnu.so": "gakLE/auDWpkOm9vOzHryjZm5dAIln/GbASMoFl4iMiMRVmFmBjdV0uyY6bkVDKPVHX6FRr65f1yuJbF1d09DA==",
"python/iqpilot_private/navd/helpers.cpython-312-aarch64-linux-gnu.so": "yOqbMSdUVZSlHAiw08Yb+vSyEr2YoHBsSIWtFQa1RqoSmLbeLUeFpTiuczVaW/HtCNeZZU8mJYXWyTUM1N0xCQ==",
"python/iqpilot_private/navd/incident_feed.cpython-312-aarch64-linux-gnu.so": "8uEYXTWYXVzhDctzoEBbSc5wJ0mqX8qAe9DuXeKgfmpPsM6rR8jHpjHiyjkLAAPorWDRVN3a5W3iyH2u2tD6Cw==",
"python/iqpilot_private/navd/iqmapd.cpython-312-aarch64-linux-gnu.so": "+kTCQnh0TKGtTVl/XqtKJfrFDVJloVVhsUmt793WbvaULUK9aAJmtaEJRvTNV/DiOjYGdgL1texfnBw3x3CkDw==",
"python/iqpilot_private/navd/long_decel.cpython-312-aarch64-linux-gnu.so": "oFJS3s2xxAmMxBpLAiaFklhstR8719Ah/GtUy+YMPdaDZBP6TYLIe89aqmPjW7LUHagkmxsJogppJXl3YtFUAg==",
"python/iqpilot_private/navd/mapbox_client.cpython-312-aarch64-linux-gnu.so": "rsDD81jJXWuvS+IYSDwKVZy/C2lvnmmEh+wYucqLuUg64BIW5Q8YpdtRM9Z++fYpSut90FeYHtrIJsAavTmOAA==",
"python/iqpilot_private/navd/nav_cameras.cpython-312-aarch64-linux-gnu.so": "5CiNWqZqmmiNfnj1plhm67WEF5PXkPGr6dMsVui4VplsrVwaWCSQYJ4ywPHlsoUeX+c4FoCX7HJSNqwxjn5IBg==",
"python/iqpilot_private/navd/nav_turn_signals.cpython-312-aarch64-linux-gnu.so": "dkNKNQEHnq0Wzw/Usmwf/qNxN72cpBZBnPHRKN1ZEq5nExMqrrraTe6bn+EWM2yo1hMsXsep5JPpNGxpqqusBQ==",
"python/iqpilot_private/navd/navbridge_source.cpython-312-aarch64-linux-gnu.so": "0PDFbzaD2KoWZRAP6I38SKdUG0T11j86Ue27RgjziWLAPuxDSd6ogJPbTuPxKLV6pBIASN6y1KU3Cf4BLOkKBg==",
"python/iqpilot_private/navd/navd.cpython-312-aarch64-linux-gnu.so": "WikwGS5js59suyyzVj90X0dYz6I+wBb7N/KHRGkD4kYEX59jP8hAK+MlLsUAr9Vbmss131S8z5inlifsCjN4BA==",
"python/iqpilot_private/navd/navincidentd.cpython-312-aarch64-linux-gnu.so": "LOnh9gEJeT8leh4xo0gabomCwTOo1Xhp2oALjrsL0Ij1MCur9bLl5QE33iFVcomQr8jGH0iw7Vq1vXDMlIRuBg==",
"python/iqpilot_private/navd/navrenderd.cpython-312-aarch64-linux-gnu.so": "KthwWou6yvP+17m4g/3PGY7aPTKKjUqLT8KTtsiTr8/y3Le6YTntbn680C1hJU/xKm1j+IcnAomDw7wdma1PDA==",
"python/iqpilot_private/navd/osm_cameras.cpython-312-aarch64-linux-gnu.so": "cdd78rET10BlVNmGXsNJgYpbS9VCSNEB5HJQ9krzeMu05ikqVnI7vHA/zlxmuHHFiHnZyAN00c16mEmgf62dDw==",
"python/iqpilot_private/navd/reroute.cpython-312-aarch64-linux-gnu.so": "QyrJUPOGnI39p3n24ZCKdHG+MI/bqIvBwUdFyGO527eqHVVWJyinfneK+X7vARV+HLwxqO5bBcgwqHLdi9enAQ==",
"python/iqpilot_private/navd/road_incidents.cpython-312-aarch64-linux-gnu.so": "YkM9q3yJaDmYhExd3Te/7tpFQXXrt+/82M0QRZrVxjKRKOxrR7xu3jdjXQIT67I6WxpHzsNIFhnzX2Bl2/9eBQ==",
"python/iqpilot_private/navd/route_manager.cpython-312-aarch64-linux-gnu.so": "+Uu9KthRwOh5+YJaj1/SD/QuSOfEuT0d4CwO//wB7sCueUKsVx7+CcyAC5T2YB8bRN+42NJDn8pQeA9JcrPoDg==",
"python/iqpilot_private/navd/runtime_common.cpython-312-aarch64-linux-gnu.so": "BoQYpXsWgO+RdUhI5HL4WSk8La2HJ8V8DSFWvAylCaDNWJX3yaClso603Nf0CcKd1MDycb8J33hiHvJSd7R7AQ==",
"python/iqpilot_private/navd/traffic.cpython-312-aarch64-linux-gnu.so": "5+ip8rEk+Upsvyp2tlfdVhmIdQnlamloY1SCuwFzkSM39y+W9tvo4r7JTeb5pcMiUijmwK54m4Rvx0XWvxz5AA==",
"python/iqpilot_private/navd/traffic_sources.cpython-312-aarch64-linux-gnu.so": "WtPKZHGDG8iQj7q1UsXw6jTXxJsWLfNaw9kfJ3huWYjxWcHz0K7ibJ5JzEGSc5l5u3LdRkt6/aZHlBMrDPBaBA==",
"python/iqpilot_private/navd/traffic_writer.cpython-312-aarch64-linux-gnu.so": "VVFiWnkiwn7nwhq+6q3aYsnxMW1GLdfaGB/a/h8JV46OoSn0NOTVA72jPYlzs0d4bacEN+IgMAelCjnMlqiBBA==",
"python/iqpilot_private/navd/turn_desire.cpython-312-aarch64-linux-gnu.so": "upuJWa/0B6du9cu3JwdzK1cSqSAORKfvd6ir7sh/oWSRPEhkxyjPmo9/uoNw4Qeq2zoh3iKkV2udK5gue/PzCQ==",
"python/iqpilot_private/navd/valhalla_process.cpython-312-aarch64-linux-gnu.so": "DXSI5ljQTXPHV+QvQCQu1+oOfLKcPXMkx7qx6id0NTFPHI/M5KIooBYKgJjbN4abl+mzUXKrtOmGF/y3f0lwDw==",
"python/iqpilot_private/navd/web_server.cpython-312-aarch64-linux-gnu.so": "h4bQodknw1y0emCI3aJYviy3kzaQH8hElJMFmpS/sMvvC+sCHfOGUo+8BK+T7JGZ9/VzNXBzP8RKkHG5cAf0Cg=="
}
}

View File

@@ -1 +1 @@
NH/aexXGu1jWh6ks/x7lbVEUX9VZPhETrwkIozXSjmPR2isUyVlaZppveq23NB7BeIQTJuS/+KAuZ/pbL/kaDQ==
kmjHw5sbr1uhbckWG6FoCjTR0idPL+RGlQ19Wri9phayqsni9P3V2rqLDUPeZ0ds+CRqMb378Jgghj6eJ6gxCQ==

View File

@@ -238,6 +238,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"IQEmacEnabled", {PERSISTENT, BOOL, "0"}},
{"IQEmacHost", {PERSISTENT, STRING}},
{"IQEmacModel", {PERSISTENT, STRING}},
{"IQEmacSmallModel", {PERSISTENT, BOOL, "0"}},
{"IQEmacCatalogCache", {PERSISTENT, STRING}},
{"MacModelDownloadProgress", {CLEAR_ON_MANAGER_START, STRING, "1.0"}},
{"MacModelStatus", {CLEAR_ON_MANAGER_START, STRING}},
@@ -315,6 +316,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"IQEmacEnabled", {PERSISTENT, BOOL, "0"}},
{"IQEmacHost", {PERSISTENT, STRING}},
{"IQEmacModel", {PERSISTENT, STRING}},
{"IQEmacSmallModel", {PERSISTENT, BOOL, "0"}},
{"IQEmacCatalogCache", {PERSISTENT, JSON}},
{"IQEgpuDisabled", {PERSISTENT, BOOL, "0"}},
{"IQEgpuEnabled", {PERSISTENT, BOOL, "0"}},
@@ -359,6 +361,14 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
// iqpilot model params
{"CameraOffset", {PERSISTENT, FLOAT, "0.0"}},
{"IQAndroidNav", {PERSISTENT, BOOL, "0"}},
{"IQAndroidNavStatus", {PERSISTENT, STRING}},
// "waze" or "maps": exactly one runs, the other is disabled outright.
{"IQAndroidNavApp", {PERSISTENT, STRING, "waze"}},
// Consumed by androidd to sign the container in once, then cleared: the credential is
// never meant to survive a manager restart or reach a log.
{"IQAndroidNavEmail", {CLEAR_ON_MANAGER_START | DONT_LOG, STRING}},
{"IQAndroidNavPassword", {CLEAR_ON_MANAGER_START | DONT_LOG, STRING}},
{"IQLiveSteerDelay", {PERSISTENT, BOOL, "1"}},
{"IQLateralAccelSlew", {PERSISTENT, BOOL, "0"}},
{"IQLateralCurvatureLookahead", {PERSISTENT, BOOL, "0"}},

View File

@@ -235,6 +235,7 @@ class Controls(IQControlsLayer):
actuators = CC.actuators
actuators.longControlState = self.LoC.long_control_state
actuators.speed = float(max(long_plan.speeds, default=0.0))
if not CC.latActive:
self.LaC.reset()

View File

@@ -63,6 +63,11 @@ def resolve_model_name(params, keys) -> str:
from iqpilot.selfdrive.iqmodeld.egpu_model import DEFAULT_EGPU_MODEL, resolve_egpu_model
resolved = resolve_egpu_model(params, allow_refresh=False)
return resolved["key"] if resolved else DEFAULT_EGPU_MODEL
if params.get_bool("IQEmacSmallModel"):
from iqpilot.selfdrive.iqmodeld.models.helpers import get_active_bundle
bundle = get_active_bundle(params)
if bundle is not None and (bundle.internalName or bundle.displayName):
return bundle.internalName or bundle.displayName
name = params.get("IQEmacModel") or b"lebrowski"
return name.decode() if isinstance(name, bytes) else name
@@ -168,7 +173,7 @@ def _patch_and_send(pm: PubMaster, payload: dict, frame_drop_perc: float, select
if mismatch is None:
mismatch = source_lag > 0
big = payload.get("source") in BIG_SOURCES
big = bool(payload.get("big", payload.get("source") in BIG_SOURCES))
model_msg = log_from_bytes(msgs["modelV2"]).as_builder()
if mismatch:
model_msg.modelV2.frameId = target

View File

@@ -272,6 +272,23 @@ class TestChannelContract:
assert sent["modelV2"].modelV2.frameDropPerc == 0.0
assert sent["cameraOdometry"].valid
def test_selector_big_flag_follows_payload_then_source(self):
from iqpilot.selfdrive.iqmodeld.modeld_selector import _patch_and_send
sent = {}
class PM:
def send(self, service, msg):
sent[service] = msg
payload = make_big_channel_payload(42, True, 0.03, 25.0, self._real_msgs())
_patch_and_send(PM(), payload, frame_drop_perc=0.0, selector_dropped=0, target=42, source_lag=0)
assert sent["modelV2"].modelV2.big and sent["drivingModelData"].drivingModelData.big
small_on_mac = {**make_big_channel_payload(43, True, 0.03, 25.0, self._real_msgs()), "source": "mac_big", "big": False}
_patch_and_send(PM(), small_on_mac, frame_drop_perc=0.0, selector_dropped=0, target=43, source_lag=0)
assert not sent["modelV2"].modelV2.big and not sent["drivingModelData"].drivingModelData.big
def test_selector_lag_patches_frame_id(self):
from iqpilot.selfdrive.iqmodeld.modeld_selector import _patch_and_send

View File

@@ -9,6 +9,7 @@ LONGITUDINAL_MODE_STOCK = 0
LONGITUDINAL_MODE_CHILL = 1
LONGITUDINAL_MODE_DYNAMIC = 2
LONGITUDINAL_MODE_PILOT = 3
IQ_LONGITUDINAL_MODES = (LONGITUDINAL_MODE_CHILL, LONGITUDINAL_MODE_DYNAMIC, LONGITUDINAL_MODE_PILOT)
PERSONALITY_AGGRESSIVE = log.LongitudinalPersonality.schema.enumerants["aggressive"]
PERSONALITY_STANDARD = log.LongitudinalPersonality.schema.enumerants["standard"]
@@ -60,6 +61,20 @@ def apply_longitudinal_mode(params, mode: int) -> None:
raise ValueError(f"invalid longitudinal mode: {mode}")
def longitudinal_mode_needs_cycle(previous: int, mode: int) -> bool:
return (previous == LONGITUDINAL_MODE_STOCK) != (mode == LONGITUDINAL_MODE_STOCK)
def next_longitudinal_mode(current: int, onroad: bool, iq_modes_available: bool) -> int:
if onroad:
order = list(IQ_LONGITUDINAL_MODES)
else:
order = [LONGITUDINAL_MODE_STOCK] + (list(IQ_LONGITUDINAL_MODES) if iq_modes_available else [])
if current not in order:
return current if onroad else order[0]
return order[(order.index(current) + 1) % len(order)]
def get_follow_distance_state(params) -> tuple[int | None, bool]:
mode = get_longitudinal_mode(params)
if mode == LONGITUDINAL_MODE_STOCK:

View File

@@ -7,6 +7,7 @@ import pytest
from iqpilot.selfdrive.longitudinal_settings import (
LONGITUDINAL_MODE_CHILL,
LONGITUDINAL_MODE_DYNAMIC,
LONGITUDINAL_MODE_PILOT,
LONGITUDINAL_MODE_STOCK,
PERSONALITY_AGGRESSIVE,
PERSONALITY_RELAXED,
@@ -16,6 +17,8 @@ from iqpilot.selfdrive.longitudinal_settings import (
get_follow_distance_state,
get_longitudinal_mode,
get_runtime_personality,
longitudinal_mode_needs_cycle,
next_longitudinal_mode,
set_valid_personality,
)
@@ -108,3 +111,31 @@ def test_dynamic_and_pilot_enable_valid_personality_selection():
params.values["IQDynamicMode"] = False
assert get_follow_distance_state(params) == (PERSONALITY_STANDARD, True)
def test_onroad_cycles_only_between_iq_modes():
assert next_longitudinal_mode(LONGITUDINAL_MODE_CHILL, True, True) == LONGITUDINAL_MODE_DYNAMIC
assert next_longitudinal_mode(LONGITUDINAL_MODE_DYNAMIC, True, True) == LONGITUDINAL_MODE_PILOT
assert next_longitudinal_mode(LONGITUDINAL_MODE_PILOT, True, True) == LONGITUDINAL_MODE_CHILL
def test_onroad_stock_acc_is_locked():
assert next_longitudinal_mode(LONGITUDINAL_MODE_STOCK, True, True) == LONGITUDINAL_MODE_STOCK
assert next_longitudinal_mode(LONGITUDINAL_MODE_STOCK, True, False) == LONGITUDINAL_MODE_STOCK
def test_offroad_cycles_through_stock_acc():
assert next_longitudinal_mode(LONGITUDINAL_MODE_PILOT, False, True) == LONGITUDINAL_MODE_STOCK
assert next_longitudinal_mode(LONGITUDINAL_MODE_STOCK, False, True) == LONGITUDINAL_MODE_CHILL
def test_offroad_without_iq_modes_only_offers_stock_acc():
assert next_longitudinal_mode(LONGITUDINAL_MODE_STOCK, False, False) == LONGITUDINAL_MODE_STOCK
assert next_longitudinal_mode(LONGITUDINAL_MODE_PILOT, False, False) == LONGITUDINAL_MODE_STOCK
def test_cycle_only_when_crossing_stock_boundary():
assert longitudinal_mode_needs_cycle(LONGITUDINAL_MODE_STOCK, LONGITUDINAL_MODE_CHILL)
assert longitudinal_mode_needs_cycle(LONGITUDINAL_MODE_PILOT, LONGITUDINAL_MODE_STOCK)
assert not longitudinal_mode_needs_cycle(LONGITUDINAL_MODE_CHILL, LONGITUDINAL_MODE_PILOT)
assert not longitudinal_mode_needs_cycle(LONGITUDINAL_MODE_DYNAMIC, LONGITUDINAL_MODE_CHILL)

View File

@@ -6,13 +6,17 @@ from iqpilot.common.params import Params, UnknownKeyName
from iqpilot.selfdrive.longitudinal_settings import (
LONGITUDINAL_MODE_DYNAMIC,
LONGITUDINAL_MODE_PILOT,
LONGITUDINAL_MODE_STOCK,
PERSONALITY_VALUES,
apply_longitudinal_mode,
get_follow_distance_state,
get_longitudinal_mode,
longitudinal_mode_needs_cycle,
next_longitudinal_mode,
set_valid_personality,
)
from iqpilot.selfdrive.ui.mici.widgets.stock_button import BigButton, BigMultiToggle, BigToggle, BigParamControl
from iqpilot.selfdrive.ui.ui_state import ui_state
from iqpilot.system.ui.lib.multilang import tr
@@ -138,24 +142,48 @@ class IQModeSelector(BigMultiToggle):
super().__init__(tr("IQ Mode"), self._display_options)
self._params = Params()
self._mode_callback = mode_callback
self._mode = LONGITUDINAL_MODE_STOCK
self._iq_modes_available = False
self.refresh()
self.set_enabled(lambda: self._next() != self._mode)
def _index(self) -> int:
return get_longitudinal_mode(self._params)
def is_dynamic(self) -> bool:
return self._index() == 2
return self._mode == LONGITUDINAL_MODE_DYNAMIC
def _toyota_factory_long_forced(self) -> bool:
cp = ui_state.CP
return bool(cp is not None and cp.brand == "toyota" and self._params.get_bool("IQToyotaFactoryLong"))
def _read_iq_modes_available(self) -> bool:
cp = ui_state.CP
alpha_available = bool(cp is not None and cp.alphaLongitudinalAvailable)
return alpha_available or self._params.get_bool("AlphaLongitudinalEnabled") or self._toyota_factory_long_forced()
def _next(self) -> int:
return next_longitudinal_mode(self._mode, ui_state.is_onroad(), self._iq_modes_available)
def refresh(self):
self.set_value(self._display_options[self._index()])
self._mode = self._index()
self._iq_modes_available = self._read_iq_modes_available()
self.set_value(self._display_options[self._mode])
def _apply(self, idx: int):
previous = self._mode
toyota_forced = self._toyota_factory_long_forced()
apply_longitudinal_mode(self._params, idx)
if idx != LONGITUDINAL_MODE_STOCK and toyota_forced:
self._params.put_bool("IQToyotaFactoryLong", False)
if longitudinal_mode_needs_cycle(previous, idx) or (idx != LONGITUDINAL_MODE_STOCK and toyota_forced):
self._params.put_bool("OnroadCycleRequested", True)
def _handle_mouse_release(self, mouse_pos):
nxt = (self._index() + 1) % len(self.OPTIONS)
nxt = self._next()
if nxt == self._mode:
return
self._apply(nxt)
self.set_value(self._display_options[nxt])
self.refresh()
if self._mode_callback:
self._mode_callback()

View File

@@ -128,6 +128,8 @@ class ModelsLayoutMici(NavScroller):
self._big = BigButton(tr("big model"))
self._big.set_click_callback(self._show_big_models)
self._small_on_mac = BigParamControl(tr("active model on eMac"), "IQEmacSmallModel", toggle_callback=self._small_on_mac_toggled)
self._cancel = BigButton(tr("stop download"))
self._cancel.set_click_callback(self._cancel_model_request)
self._cancel.set_visible(self._is_downloading)
@@ -158,7 +160,8 @@ class ModelsLayoutMici(NavScroller):
self._lane_speed = MappedParamToggle(tr("lane turn speed"), "IQLaneTurnValue", [tr("slow"), tr("normal"), tr("fast")], _LANE_TURN_VALUES)
self._lane_speed.set_visible(lambda: self._lane_turn._checked)
self._main_items = [self._current, self._big, self._cancel, self._supercombo, self._vision, self._policy, self._redownload, self._refresh, self._clear,
self._main_items = [self._current, self._big, self._small_on_mac, self._cancel, self._supercombo, self._vision, self._policy,
self._redownload, self._refresh, self._clear,
self._steer_delay, self._sw_delay, self._lane_turn, self._lane_speed]
self._scroller.add_widgets(self._main_items)
@@ -394,9 +397,28 @@ class ModelsLayoutMici(NavScroller):
except (TypeError, ValueError):
return None
def _small_on_mac_toggled(self, checked: bool) -> None:
p = ui_state.params
if checked:
p.put_bool("IQEmacEnabled", True)
else:
p.put_bool("IQEmacEnabled", bool(p.get("IQEmacModel")))
def _small_on_mac_value(self) -> str:
try:
active = self.model_manager.activeBundle
name = _display_model_name(active) if active and active.ref else ""
except Exception:
name = ""
return f"{name} ({tr('eMac')})" if name else tr("active model")
def _big_model_value(self) -> str:
p = ui_state.params
dock = bool(getattr(ui_state.sm["deviceState"], "egpuDockPresent", False))
if p.get_bool("IQEmacEnabled") and p.get_bool("IQEmacSmallModel"):
progress = self._big_setup_progress()
value = self._small_on_mac_value()
return f"{value} {int(progress * 100)}%" if progress is not None and progress < 1.0 else value
if not p.get_bool("IQEmacEnabled") and not dock:
return tr("Off")
key = p.get("IQEmacModel")
@@ -577,5 +599,5 @@ class ModelsLayoutMici(NavScroller):
def show_event(self):
super().show_event()
for w in (self._steer_delay, self._sw_delay, self._lane_turn, self._lane_speed):
for w in (self._steer_delay, self._sw_delay, self._lane_turn, self._lane_speed, self._small_on_mac):
w.refresh()

View File

View File

@@ -0,0 +1,200 @@
# Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
from __future__ import annotations
import json
import os
import socket
import subprocess
import time
from iqpilot.cereal import messaging
from iqpilot.common.params import Params
from iqpilot.common.realtime import Ratekeeper
from iqpilot.common.swaglog import cloudlog
ANDROID_ROOT = "/data/android"
HEADLESS = f"{ANDROID_ROOT}/waydroid_headless.sh"
TRIM = f"{ANDROID_ROOT}/android_trim.sh"
GUARD = f"{ANDROID_ROOT}/android_guard.sh"
LXC_ATTACH = f"{ANDROID_ROOT}/root/usr/bin/lxc-attach"
LD = f"{ANDROID_ROOT}/root/usr/lib/aarch64-linux-gnu"
# Waze's launcher is FreeMapAppActivity; there is no com.waze.MainActivity, and asking for
# one fails with "Activity class does not exist" rather than anything that names the problem.
NAV_APPS = {
"waze": ("com.waze", "com.waze/.FreeMapAppActivity"),
"maps": ("com.google.android.apps.maps", "com.google.android.apps.maps/com.google.android.maps.MapsActivity"),
}
BRIDGE_HOST = "192.168.240.112"
BRIDGE_PORT = 8099
GPS_SOURCES = ["iqLiveLocation", "liveLocationKalman", "gpsLocationExternal", "gpsLocation"]
PROVIDERS = ("gps", "fused", "network")
GPS_INTERVAL_S = 0.5
def attach(*args: str, timeout: float = 10.0) -> subprocess.CompletedProcess:
cmd = ["sudo", "-n", "env", f"LD_LIBRARY_PATH={LD}", LXC_ATTACH,
"-P", "/var/lib/waydroid/lxc", "-n", "waydroid", "--", *args]
return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout, check=False)
def container_booted() -> bool:
try:
return attach("/system/bin/getprop", "sys.boot_completed", timeout=8).stdout.strip() == "1"
except (subprocess.SubprocessError, OSError):
return False
def bring_up() -> None:
# AGNOS mounts / read-only, so no unit file can be installed; a transient unit is the only
# way the compositor and lxc-start survive this process exiting.
subprocess.run(["sudo", "-n", "systemd-run", "--unit=iq-android", "--service-type=oneshot",
"--remain-after-exit", HEADLESS, "up"],
capture_output=True, text=True, timeout=120, check=False)
def start_guard() -> None:
subprocess.run(["sudo", "-n", "systemd-run", "--unit=iq-android-guard", GUARD],
capture_output=True, text=True, timeout=30, check=False)
def trim(nav_app: str) -> None:
subprocess.run(["sudo", "-n", "env", f"ANDROID_NAV_APP={nav_app}", TRIM],
capture_output=True, timeout=240, check=False)
def enable_mock_location() -> None:
# Wiped by every container restart, and the package form does not cover uid 0: without the
# --uid form every injection fails with SecurityException and the apps just say "no GPS".
attach("/system/bin/cmd", "appops", "set", "--uid", "0", "android:mock_location", "allow")
for p in PROVIDERS:
attach("/system/bin/cmd", "location", "providers", "add-test-provider", p)
attach("/system/bin/cmd", "location", "providers", "set-test-provider-enabled", p, "true")
# The a11y tree is only updated while the display is awake; asleep it emits nothing at all.
attach("/system/bin/svc", "power", "stayon", "true")
def inject(lat: float, lon: float) -> None:
for p in PROVIDERS:
attach("/system/bin/cmd", "location", "providers", "set-test-provider-location", p,
"--location", f"{lat:.6f},{lon:.6f}", "--accuracy", "4", timeout=6)
def read_position(sm: messaging.SubMaster) -> tuple[float, float] | None:
for src in GPS_SOURCES:
if src not in sm.data or not sm.valid.get(src, False):
continue
msg = sm[src]
for lat_a, lon_a in (("latitude", "longitude"), ("lat", "lon")):
lat = getattr(msg, lat_a, None)
lon = getattr(msg, lon_a, None)
if lat is not None and lon is not None and (lat or lon):
return float(lat), float(lon)
return None
class AndroidNavDaemon:
def __init__(self) -> None:
self.params = Params()
self.sources = [s for s in GPS_SOURCES if s in messaging.SERVICE_LIST]
self.sm = messaging.SubMaster(self.sources) if self.sources else None
self.last_inject = 0.0
self.up = False
def status(self, text: str) -> None:
self.params.put("IQAndroidNavStatus", text)
def sign_in(self) -> None:
email = self.params.get("IQAndroidNavEmail", encoding="utf8")
password = self.params.get("IQAndroidNavPassword", encoding="utf8")
if not email or not password:
return
try:
with socket.create_connection((BRIDGE_HOST, BRIDGE_PORT), timeout=10) as s:
s.sendall(json.dumps({"cmd": "signin", "email": email, "password": password}).encode() + b"\n")
self.status("sign-in requested")
except OSError:
cloudlog.exception("androidd: sign-in request failed")
self.status("sign-in failed")
finally:
# Never let the credential outlive the one use it was handed over for.
self.params.remove("IQAndroidNavEmail")
self.params.remove("IQAndroidNavPassword")
def nav_app(self) -> str:
value = self.params.get("IQAndroidNavApp", encoding="utf8") or "waze"
return value if value in NAV_APPS else "waze"
def enforce_single_app(self, nav_app: str) -> None:
for name, (pkg, _) in NAV_APPS.items():
if name != nav_app:
attach("/system/bin/am", "force-stop", pkg)
def ensure_app_running(self, nav_app: str) -> None:
pkg, component = NAV_APPS[nav_app]
if attach("/system/bin/pidof", pkg, timeout=8).stdout.strip():
return
attach("/system/bin/am", "start", "-n", component, timeout=20)
def ensure_up(self) -> None:
if container_booted():
if not self.up:
nav_app = self.nav_app()
enable_mock_location()
trim(nav_app)
self.enforce_single_app(nav_app)
start_guard()
self.status(f"running:{nav_app}")
self.up = True
return
self.up = False
self.status("starting")
bring_up()
def step(self) -> None:
if not self.params.get_bool("IQAndroidNav"):
if self.up:
subprocess.run(["sudo", "-n", "systemctl", "stop", "iq-android", "iq-android-guard"],
capture_output=True, timeout=60, check=False)
self.up = False
self.status("disabled")
return
self.ensure_up()
if not self.up:
return
self.sign_in()
self.ensure_app_running(self.nav_app())
if self.sm is None:
return
self.sm.update(0)
now = time.monotonic()
if now - self.last_inject < GPS_INTERVAL_S:
return
position = read_position(self.sm)
if position is not None:
self.last_inject = now
try:
inject(*position)
except subprocess.SubprocessError:
cloudlog.exception("androidd: location inject failed")
def main() -> None:
if not os.path.exists(HEADLESS):
cloudlog.warning("androidd: android stack not staged, exiting")
return
daemon = AndroidNavDaemon()
rk = Ratekeeper(2.0, print_delay_threshold=None)
while True:
try:
daemon.step()
except Exception:
cloudlog.exception("androidd: step failed")
rk.keep_time()
if __name__ == "__main__":
main()

View File

@@ -67,6 +67,9 @@ def qcomgps(started: bool, params: Params, CP: car.CarParams) -> bool:
def always_run(started: bool, params: Params, CP: car.CarParams) -> bool:
return True
def android_nav(started: bool, params: Params, CP: car.CarParams) -> bool:
return params.get_bool("IQAndroidNav")
def only_onroad(started: bool, params: Params, CP: car.CarParams) -> bool:
return started
@@ -176,6 +179,7 @@ procs = [
PythonProcess("journald", "iqpilot.system.journald", only_onroad, platform.system() != "Darwin"),
PythonProcess("micd", "iqpilot.system.micd", or_(iscar, livestream)),
PythonProcess("timed", "iqpilot.system.timed", always_run, enabled=not PC),
PythonProcess("androidd", "iqpilot.system.android.androidd", android_nav, enabled=TICI, restart_if_crash=True),
PythonProcess("dmonitoringmodeld", "iqpilot.selfdrive.dmonitoringmodeld.dmonitoringmodeld", driver_monitoring, enabled=not PC),

View File

@@ -1,10 +1,6 @@
import os
import sys
venv_site_packages = os.path.join(Dir("#").abspath, ".venv", "lib", "python3.12", "site-packages")
if os.path.isdir(venv_site_packages) and venv_site_packages not in sys.path:
sys.path.insert(0, venv_site_packages)
import imgui
import iqdbc
import libusb

View File

@@ -49,7 +49,7 @@ void ReplayStream::mergeSegments() {
}
bool ReplayStream::loadRoute(const std::string &route, const std::string &data_dir, uint32_t replay_flags, bool auto_source) {
replay.reset(new Replay(route, {"can", "narrowRoadEncodeIdx", "cabinEncodeIdx", "wideRoadEncodeIdx", "carParams"},
replay.reset(new Replay(route, {"can", "roadEncodeIdx", "driverEncodeIdx", "wideRoadEncodeIdx", "carParams"},
{}, nullptr, replay_flags, data_dir, auto_source));
replay->setSegmentCacheLimit(settings.max_cached_minutes);
replay->installEventFilter([this](const Event *event) { return eventFilter(event); });

View File

@@ -90,3 +90,16 @@ class TestFrontendRemoval:
def test_imgui_frontend_is_present(self):
assert (CABANA_DIR / "ui" / "app.cc").is_file()
assert (CABANA_DIR / "ui" / "main.cc").is_file()
def test_stream_selector_stays_in_main_window(self):
app = read("ui/app.cc")
assert "io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable" not in app
class TestReplayVideo:
def test_iqpilot_camera_index_services_are_replayed(self):
source = read("streams/replaystream.cc")
for service in ("roadEncodeIdx", "driverEncodeIdx", "wideRoadEncodeIdx"):
assert f'"{service}"' in source
assert '"narrowRoadEncodeIdx"' not in source
assert '"cabinEncodeIdx"' not in source

View File

@@ -144,8 +144,6 @@ public:
ImPlot::CreateContext();
ImGuiIO &io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
io.ConfigViewportsNoDecoration = false;
io.IniFilename = nullptr;
io.LogFilename = nullptr;
if (!ImGui_ImplGlfw_InitForOpenGL(window, true)) {

View File

@@ -15,19 +15,19 @@
#include "tools/cabana/utils/util.h"
void OpenReplayWidget::draw() {
ImGui::AlignTextToFramePadding();
ImGui::TextDisabled("Replay a local or Konn3kt route with optional camera streams.");
ImGui::Spacing();
ImGui::TextUnformatted("Route");
ImGui::SameLine();
ImGui::SetNextItemWidth(-250.0f);
ImGui::SetNextItemWidth(-1.0f);
inputText("##route", &route_, "Enter route name or browse for local/remote route");
ImGui::SameLine();
if (ImGui::Button("Remote route...")) {
const float route_button_width = (ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ItemSpacing.x) * 0.5f;
if (ImGui::Button("Browse Konn3kt routes", ImVec2(route_button_width, 34.0f))) {
routes_dialog_.open(utils::guarded(alive_, [this](bool accepted, const std::string &route) {
if (accepted) route_ = route;
}));
}
ImGui::SameLine();
if (ImGui::Button("Local route...")) {
if (ImGui::Button("Choose local route", ImVec2(-1.0f, 34.0f))) {
FileDialog::getExistingDirectory("Open Local Route", settings.last_route_dir, utils::guarded(alive_, [this](const std::string &dir) {
if (!dir.empty()) {
route_ = dir;
@@ -35,6 +35,8 @@ void OpenReplayWidget::draw() {
}
}));
}
ImGui::Spacing();
ImGui::SeparatorText("Camera streams");
checkBox("Road camera", &cameras_[0]);
ImGui::SameLine();
checkBox("Driver camera", &cameras_[1]);
@@ -126,6 +128,8 @@ void OpenPandaWidget::buildConfigForm() {
}
void OpenPandaWidget::draw() {
ImGui::TextDisabled("Connect directly to a Panda and configure each CAN bus.");
ImGui::Spacing();
if (already_connected_) {
ImGui::Text("Already connected to %s.", can->routeName().c_str());
ImGui::TextUnformatted("Close the current connection via [File menu -> Close Stream] before connecting to another Panda.");
@@ -184,6 +188,8 @@ std::unique_ptr<AbstractStream> OpenPandaWidget::open() {
}
void OpenDeviceWidget::draw() {
ImGui::TextDisabled("Connect to a running IQ.Pilot instance or a local message queue.");
ImGui::Spacing();
ImGui::RadioButton("MSGQ", &mode_, 0);
ImGui::RadioButton("ZMQ", &mode_, 1);
ImGui::RadioButton("Bridge", &mode_, 2);
@@ -226,6 +232,8 @@ void OpenSocketCanWidget::refreshDevices() {
}
void OpenSocketCanWidget::draw() {
ImGui::TextDisabled("Read CAN traffic from a Linux SocketCAN interface.");
ImGui::Spacing();
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted("Device");
ImGui::SameLine();
@@ -248,7 +256,6 @@ std::unique_ptr<AbstractStream> OpenSocketCanWidget::open() {
void StreamSelector::open(Callback on_done) {
on_done_ = std::move(on_done);
open_ = true;
popup_.reset();
first_frame_ = true;
dbc_file_.clear();
widgets_.clear();
@@ -264,32 +271,65 @@ void StreamSelector::open(Callback on_done) {
void StreamSelector::draw() {
if (!open_) return;
if (!beginDialog("Open stream", &popup_, ImVec2(640.0f, 0.0f))) return;
const ImGuiViewport *viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->WorkPos);
ImGui::SetNextWindowSize(viewport->WorkSize);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
const ImGuiWindowFlags page_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking;
if (!ImGui::Begin("##stream_selector_page", nullptr, page_flags)) {
ImGui::End();
ImGui::PopStyleVar(2);
return;
}
ImGui::PopStyleVar(2);
const ImVec2 available = ImGui::GetContentRegionAvail();
const ImVec2 card_size(std::clamp(available.x - 80.0f, 680.0f, 920.0f),
std::clamp(available.y - 80.0f, 470.0f, 570.0f));
ImGui::SetCursorPos(ImVec2(std::max(24.0f, (available.x - card_size.x) * 0.5f),
std::max(24.0f, (available.y - card_size.y) * 0.5f)));
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 12.0f);
ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(28.0f, 24.0f));
ImGui::BeginChild("##stream_selector_card", card_size, ImGuiChildFlags_Borders, ImGuiWindowFlags_NoScrollbar);
ImGui::PopStyleVar(3);
ImGui::PushFont(boldFont(), 26.0f);
ImGui::TextUnformatted("Open Cabana");
ImGui::PopFont();
ImGui::TextDisabled("Choose a data source to inspect CAN traffic, signals, and video.");
ImGui::Spacing();
ImGui::Spacing();
AbstractOpenStreamWidget *current = nullptr;
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(16.0f, 7.0f));
if (ImGui::BeginTabBar("streams")) {
for (auto &w : widgets_) {
ImGuiTabItemFlags tab_flags = (first_frame_ && w == widgets_.front()) ? ImGuiTabItemFlags_SetSelected : 0;
if (ImGui::BeginTabItem(w->title(), nullptr, tab_flags)) {
current = w.get();
ImGui::BeginChild("tab", ImVec2(0, 130.0f));
const float content_height = std::max(170.0f, card_size.y - 300.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(18.0f, 16.0f));
ImGui::BeginChild("tab", ImVec2(0, content_height), ImGuiChildFlags_Borders);
w->draw();
ImGui::EndChild();
ImGui::PopStyleVar();
ImGui::EndTabItem();
}
}
ImGui::EndTabBar();
}
ImGui::PopStyleVar();
first_frame_ = false;
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted("dbc File");
ImGui::SameLine();
ImGui::SetNextItemWidth(-90.0f);
ImGui::SeparatorText("DBC file");
ImGui::SetNextItemWidth(-120.0f);
inputText("##dbc", &dbc_file_, "Choose a dbc file to open", ImGuiInputTextFlags_ReadOnly);
ImGui::SameLine();
if (ImGui::Button("Browse...")) {
if (ImGui::Button("Browse...", ImVec2(-1.0f, 0.0f))) {
FileDialog::getOpenFileName("Open File", settings.last_dir, ".dbc", [this](const std::string &fn) {
if (!fn.empty()) {
dbc_file_ = fn;
@@ -302,7 +342,13 @@ void StreamSelector::draw() {
bool accepted = false, rejected = false;
std::unique_ptr<AbstractStream> stream;
bool open_clicked = false;
dialogButtons("Open", &open_clicked, &rejected, current != nullptr && current->openEnabled());
const float open_width = 180.0f;
if (ImGui::Button("Cancel", ImVec2(100.0f, 38.0f))) rejected = true;
ImGui::SameLine();
ImGui::SetCursorPosX(ImGui::GetContentRegionMax().x - open_width);
ImGui::BeginDisabled(current == nullptr || !current->openEnabled());
if (ImGui::Button("Open source", ImVec2(open_width, 38.0f))) open_clicked = true;
ImGui::EndDisabled();
if (open_clicked) {
if (stream = current->open(); stream) accepted = true;
}
@@ -312,8 +358,8 @@ void StreamSelector::draw() {
FileDialog::draw();
MessageBox::draw();
if (accepted || rejected) ImGui::CloseCurrentPopup();
ImGui::EndPopup();
ImGui::EndChild();
ImGui::End();
if (accepted || rejected) {
open_ = false;
widgets_.clear();

View File

@@ -98,7 +98,6 @@ public:
private:
bool open_ = false;
PopupOwner popup_;
bool first_frame_ = false;
std::string dbc_file_;
std::vector<std::unique_ptr<AbstractOpenStreamWidget>> widgets_;

View File

@@ -44,6 +44,20 @@ def host():
class TestFileDownload:
def test_head_connection_released(self, monkeypatch):
class Response:
status = 200
headers = {"content-length": "4"}
released = False
def release_conn(self):
self.released = True
response = Response()
monkeypatch.setattr(URLFile, "_request", lambda self, method, url, headers=None: response)
assert URLFile("https://example.com/test").get_length_online() == 4
assert response.released
def test_pipeline_defaults(self, host):
# TODO: parameterize the defaults so we don't rely on hard-coded values in xx

View File

@@ -128,10 +128,13 @@ class URLFile:
def get_length_online(self) -> int:
response = self._request('HEAD', self._url)
try:
if not (200 <= response.status <= 299):
return -1
length = response.headers.get('content-length', 0)
return int(length)
finally:
response.release_conn()
def get_length(self) -> int:
if self._length is not None:

View File

@@ -40,6 +40,36 @@ def write_rlog(path: Path, n_frames: int = 200):
f.write(msg.to_bytes())
def write_video_rlog(path: Path, n_frames: int):
with open(path, "wb") as f:
cp = messaging.new_message('carParams')
cp.logMonoTime = 1_000_000_000
cp.carParams.carFingerprint = "TOYOTA_RAV4_TSS2"
cp.carParams.brand = "toyota"
f.write(cp.to_bytes())
for i in range(n_frames):
timestamp = 1_000_000_000 + i * 50_000_000
msg = messaging.new_message('can', 1)
msg.logMonoTime = timestamp
msg.can[0].address = 0x1D2
msg.can[0].src = 0
msg.can[0].dat = bytes([i % 256] * 8)
f.write(msg.to_bytes())
idx = messaging.new_message('roadEncodeIdx')
idx.logMonoTime = timestamp
idx.roadEncodeIdx.frameId = i
idx.roadEncodeIdx.type = 'fullHEVC'
idx.roadEncodeIdx.encodeId = i
idx.roadEncodeIdx.segmentNum = 0
idx.roadEncodeIdx.segmentId = i
idx.roadEncodeIdx.segmentIdEncode = i
idx.roadEncodeIdx.timestampSof = timestamp
idx.roadEncodeIdx.timestampEof = timestamp + 10_000_000
f.write(idx.to_bytes())
@pytest.fixture(scope="module")
def local_route(tmp_path_factory):
data_dir = tmp_path_factory.mktemp("routes")
@@ -50,6 +80,25 @@ def local_route(tmp_path_factory):
return data_dir
@pytest.fixture(scope="module")
def local_video_route(tmp_path_factory):
data_dir = tmp_path_factory.mktemp("video_routes")
seg_dir = data_dir / f"{DONGLE_ID}|{TIMESTAMP}--0"
seg_dir.mkdir()
frame_count = 20
result = subprocess.run([
"ffmpeg", "-hide_banner", "-loglevel", "error", "-f", "lavfi",
"-i", "testsrc=size=320x180:rate=20", "-frames:v", str(frame_count),
"-pix_fmt", "yuv420p", "-c:v", "libx265", "-preset", "ultrafast",
"-x265-params", "pools=1:frame-threads=1:log-level=error", "-f", "hevc",
str(seg_dir / "fcamera.hevc"),
], capture_output=True, text=True)
if result.returncode != 0:
pytest.skip(result.stderr)
write_video_rlog(seg_dir / "rlog", frame_count)
return data_dir
def run(cmd, timeout=180):
return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout, env=os.environ.copy(),
cwd=TOOLS_DIR.parent)
@@ -62,6 +111,33 @@ def cabana_command(*args):
return command
def cabana_output_until(args, expected, timeout=60):
master, slave = pty.openpty()
proc = subprocess.Popen(cabana_command(*args), stdout=slave, stderr=slave,
env=os.environ.copy(), cwd=TOOLS_DIR.parent)
os.close(slave)
output = bytearray()
deadline = time.monotonic() + timeout
try:
while time.monotonic() < deadline:
ready, _, _ = select.select([master], [], [], min(1, deadline - time.monotonic()))
if not ready:
if proc.poll() is not None:
break
continue
try:
output.extend(os.read(master, 4096))
except OSError:
break
if expected.encode() in output:
break
finally:
proc.kill()
proc.wait()
os.close(master)
return output.decode(errors="replace")
def test_jotpluggler_renders_a_local_route(local_route, tmp_path):
assert JOTPLUGGLER_BIN.exists(), "jotpluggler not built"
out = tmp_path / "plot.png"
@@ -74,38 +150,20 @@ def test_jotpluggler_renders_a_local_route(local_route, tmp_path):
def test_cabana_loads_a_local_route(local_route):
assert CABANA_BIN.exists(), "cabana not built"
master, slave = pty.openpty()
proc = subprocess.Popen(cabana_command("--data_dir", str(local_route), "--no-vipc", ROUTE),
stdout=slave, stderr=slave,
env=os.environ.copy(), cwd=TOOLS_DIR.parent)
os.close(slave)
loaded = f"loaded route {ROUTE} with 2 valid segments"
output = bytearray()
deadline = time.monotonic() + 60
try:
while time.monotonic() < deadline:
ready, _, _ = select.select([master], [], [], min(1, deadline - time.monotonic()))
if not ready:
if proc.poll() is not None:
break
continue
try:
output.extend(os.read(master, 4096))
except OSError:
break
if loaded.encode() in output:
break
finally:
proc.kill()
proc.wait()
os.close(master)
out = output.decode(errors="replace")
out = cabana_output_until(("--data_dir", str(local_route), "--no-vipc", ROUTE), loaded)
assert "failed to load route" not in out, out
assert "invalid route format" not in out, out
assert loaded in out, out
def test_cabana_replays_local_video(local_video_route):
expected = "camera[0] vipc send #1"
out = cabana_output_until(("--data_dir", str(local_video_route), ROUTE), expected)
assert "failed to get frame" not in out, out
assert expected in out, out
def test_replay_logreader_reports_load_stats(local_route):
assert shutil.which("python3") is not None
header = (TOOLS_DIR / "replay" / "logreader.h").read_text()

View File

@@ -70,7 +70,7 @@ from iqpilot.system.ui.iqwidgets.widgets.list_view import (
Spacer,
)
from iqpilot.system.ui.iqwidgets.widgets.list_view import IQListItem
from iqpilot.system.ui.iqwidgets.widgets.list_view import IQListItem, IQMultipleButtonAction, IQToggleAction, IQLineSeparator
from iqpilot.system.ui.iqwidgets.widgets.list_view import IQListItem, IQMultipleButtonAction, IQToggleAction, IQLineSeparator, toggle_item_iq
from iqpilot.system.ui.iqwidgets.widgets.list_view import button_item, toggle_item
from iqpilot.system.ui.iqwidgets.widgets.list_view import NoticeModal
from iqpilot.system.ui.iqwidgets.widgets.list_view import PickerDialog, PickerItem, PickerGroup
@@ -1655,6 +1655,14 @@ class ModelsLayout(Widget):
)
self.big_model_item.action_item.set_value(self._big_model_value())
self.small_on_mac_item = toggle_item_iq(
lambda: tr("Active Model on eMac"),
tr("Run the selected small model on the Mac instead of a big model."),
initial_state=ui_state.params.get_bool("IQEmacSmallModel"),
callback=self._on_small_on_mac_toggled,
param="IQEmacSmallModel",
)
self.supercombo_label = progress_item(tr("Combined Model"))
self.vision_label = progress_item(tr("Vision Weights"))
self.policy_label = progress_item(tr("Policy Weights"))
@@ -1671,7 +1679,7 @@ class ModelsLayout(Widget):
self.redownload_item = button_item(lambda: tr("Redownload Current Model"), lambda: tr("REDOWNLOAD"), "", self._redownload_model)
self.cancel_download_item = button_item(tr("Stop Download"), tr("Cancel"), "", self._cancel_model_request)
self.items = [self.current_model_item, self.big_model_item, self.cancel_download_item, self.supercombo_label, self.vision_label,
self.items = [self.current_model_item, self.big_model_item, self.small_on_mac_item, self.cancel_download_item, self.supercombo_label, self.vision_label,
self.policy_label, self.redownload_item, self.refresh_item, self.clear_cache_item]
def _is_downloading(self):
@@ -1921,10 +1929,18 @@ class ModelsLayout(Widget):
get_folders_fn=self._get_folders, on_exit=self._on_model_selected)
gui_app.set_modal_overlay(self.model_dialog, callback=self._on_model_selected)
def _on_small_on_mac_toggled(self, state: bool) -> None:
p = ui_state.params
p.put_bool("IQEmacSmallModel", bool(state))
p.put_bool("IQEmacEnabled", True if state else bool(p.get("IQEmacModel")))
self.big_model_item.action_item.set_value(self._big_model_value())
@staticmethod
def _big_model_value() -> str:
if not ui_state.params.get_bool("IQEmacEnabled"):
return tr("Off")
if ui_state.params.get_bool("IQEmacSmallModel"):
return tr("Active model (eMac)")
key = ui_state.params.get("IQEmacModel")
key = key.decode() if isinstance(key, bytes) else (key or "")
return _big_model_label(key) if key in [n for n, _ in _big_model_options()] else tr("Off")

View File

@@ -2,6 +2,7 @@
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos/
"""
import math
import time
import pyray as rl
@@ -17,6 +18,9 @@ from iqpilot.system.ui.widgets import Widget
_POLL_S = 1.0
_FONT_SIZE = 70
_ICON_H = 76
_ICONS = {"MAC": ("mac", 62 / 46), "GPU": ("egpu", 1.0)}
_GREY = rl.Color(165, 165, 170, 235)
class EmacStatusRenderer(Widget):
@@ -27,6 +31,21 @@ class EmacStatusRenderer(Widget):
self._last_poll = 0.0
self._label = ""
self._state = SourceState.HIDDEN
self._icons: dict[str, dict[str, rl.Texture]] = {}
def _icon_set(self, label: str) -> dict[str, rl.Texture] | None:
spec = _ICONS.get(label)
if spec is None:
return None
if label not in self._icons:
base, aspect = spec
w = int(_ICON_H * aspect)
self._icons[label] = {
"base": gui_app.texture(f"icons_mici/{base}.png", w, _ICON_H),
"green": gui_app.texture(f"icons_mici/{base}_green.png", w, _ICON_H),
"orange": gui_app.texture(f"icons_mici/{base}_orange.png", int(w * 1.26), _ICON_H),
}
return self._icons[label]
def update(self):
now = time.monotonic()
@@ -38,7 +57,25 @@ class EmacStatusRenderer(Widget):
def _render(self, rect: rl.Rectangle):
if self._state == SourceState.HIDDEN:
return
icons = self._icon_set(self._label)
if icons is None:
size = measure_text_cached(self._font, self._label, _FONT_SIZE)
x = rect.x + UI_BORDER_SIZE + BTN_SIZE // 2 - size.x / 2
y = rect.y + rect.height / 2 - size.y / 2
draw_source_label(self._font, self._label, self._state, rl.Vector2(x, y), _FONT_SIZE)
return
if self._state == SourceState.ACTIVE:
tex, tint = icons["green"], rl.Color(255, 255, 255, 255)
elif self._state == SourceState.FAILED:
tex, tint = icons["orange"], rl.Color(255, 255, 255, 255)
elif self._state == SourceState.CROSSED:
tex, tint = icons["base"], rl.Color(255, 255, 255, 165)
else:
pulse = 0.35 + 0.65 * (0.5 - 0.5 * math.cos(rl.get_time() * 6.0))
tex, tint = icons["base"], rl.Color(_GREY.r, _GREY.g, _GREY.b, int(_GREY.a * pulse))
x = int(rect.x + UI_BORDER_SIZE + BTN_SIZE // 2 - tex.width / 2)
y = int(rect.y + rect.height / 2 - tex.height / 2)
rl.draw_texture(tex, x, y, tint)
if self._state == SourceState.CROSSED:
cy = y + tex.height // 2
rl.draw_line_ex(rl.Vector2(x - 4, cy), rl.Vector2(x + tex.width + 4, cy), 4, rl.Color(255, 255, 255, 165))