mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-26 02:33:46 +08:00
Compare commits
339 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5fad2a9154 | |||
| 9b7f1222e8 | |||
| ff12e1fe8f | |||
| d7498e7a93 | |||
| 22f19ad578 | |||
| 225e98ed58 | |||
| 66af0216b9 | |||
| 779b42d8ea | |||
| f5f846f5b6 | |||
| d8b6e43a0d | |||
| b72042042f | |||
| bd6dced538 | |||
| 66245a5a2e | |||
| d4a3bc0f5c | |||
| fcc5cc359e | |||
| aa381d4d97 | |||
| bc3f215764 | |||
| daf0f84bc9 | |||
| b945d4c021 | |||
| 9f1066ce83 | |||
| 370654637a | |||
| fb854cecd3 | |||
| 713ce294cc | |||
| 7450b9de9a | |||
| 5b2b053afd | |||
| 88be0e6387 | |||
| cc5c95705d | |||
| d16d72afc6 | |||
| 6230d08c0f | |||
| 48af4d2f7f | |||
| d773ad9025 | |||
| 894eae7090 | |||
| 28fcba8471 | |||
| 0cff67bda8 | |||
| f0eeec955f | |||
| 9b28c7bd52 | |||
| ef400a9b3f | |||
| d4b3c3c3b0 | |||
| c57c05886c | |||
| 80f465616a | |||
| 4e70755895 | |||
| 5b41566e4e | |||
| 2141e5a5b1 | |||
| d51dd02d10 | |||
| bb3ed894b1 | |||
| d4c911f58c | |||
| fc87af4da1 | |||
| 7cd02e0c40 | |||
| db8d38e85f | |||
| 2c7e6e72c5 | |||
| 85dd1cbe7e | |||
| 665f538533 | |||
| 28a4de1c9e | |||
| 42eef3f21c | |||
| ce86d9d05a | |||
| b70d0941c9 | |||
| 8d7ba284d7 | |||
| 71b577cbc2 | |||
| cc40a28562 | |||
| 104ae74901 | |||
| 2e45e57287 | |||
| 10843b2f6e | |||
| eb3eb116eb | |||
| b34e968f91 | |||
| 3701079265 | |||
| 1a713fe3e2 | |||
| 6d92a8e5ad | |||
| 01df304f88 | |||
| 7a76325e67 | |||
| c3d1f727c0 | |||
| 94e60c5d70 | |||
| 43143e7a1f | |||
| f2b14c7ac0 | |||
| d5c0fea948 | |||
| a51b1fd78f | |||
| 8b7c2b5f36 | |||
| 0eab89c8d9 | |||
| 7b124faad2 | |||
| 4ac7fcaf5c | |||
| f265aa3f7b | |||
| 11044975c5 | |||
| 27f73a6f08 | |||
| 3e5bb853cb | |||
| 2f0864e215 | |||
| 8376ba7c97 | |||
| c928e27033 | |||
| 5f52120d7a | |||
| cab8457f47 | |||
| 8453f6b4eb | |||
| 03cf613e4c | |||
| ce44830b22 | |||
| 3ae3c5361e | |||
| d3dd2545a9 | |||
| 29cb892e1a | |||
| a106e222ca | |||
| 6ccc984dab | |||
| cce803e888 | |||
| 9427041200 | |||
| 6bae55ac8e | |||
| d8b9bf5529 | |||
| 4c07ea96e7 | |||
| fcf1bf2f3c | |||
| e3c377df34 | |||
| 28141e3074 | |||
| 25c5722372 | |||
| d699f26ded | |||
| 9ccd61e8cf | |||
| fccd46fef5 | |||
| 1863259a58 | |||
| c001d2b184 | |||
| 03ebfd2c69 | |||
| 94141aebb1 | |||
| 71ee4ab2a3 | |||
| dfb5acddf5 | |||
| 33d7e977b4 | |||
| c6708e716f | |||
| 5a7ec6b154 | |||
| d935514f78 | |||
| 8f3f9cf6c7 | |||
| 4013a70e9d | |||
| 3e363cf7a3 | |||
| 9f13e71a0a | |||
| 28ea8d0c09 | |||
| d0c877f591 | |||
| b5ebc6474e | |||
| cb151bb9aa | |||
| e943d2afd2 | |||
| 8556708cd6 | |||
| 6fa509cc88 | |||
| 8e7ce87be8 | |||
| 5d8137b30a | |||
| 368cb1a6f6 | |||
| ba460bbb7b | |||
| a6ffaaa781 | |||
| d4cfd34291 | |||
| b0334c2ae5 | |||
| 6a19e82bb2 | |||
| 5693de7d8d | |||
| 20fa52f5df | |||
| cb540160e4 | |||
| be1fda1504 | |||
| 0c3bd608d9 | |||
| 258b4c6048 | |||
| 8b23fd8c03 | |||
| eb7d4212fd | |||
| 73485ba76c | |||
| 1ea2143e01 | |||
| 20235f027d | |||
| 55ad1fd0fd | |||
| 5a2117892c | |||
| d8e4d75656 | |||
| 77db3e506c | |||
| acd15fcba7 | |||
| 00539e1dc2 | |||
| fb98b8cb29 | |||
| fc88f70ebd | |||
| 6233e7b2c0 | |||
| 0144a691b9 | |||
| 37a6c62e6e | |||
| ca09e50081 | |||
| 8098b9fa53 | |||
| 5564d133b3 | |||
| 6e3bf42c97 | |||
| f94babe509 | |||
| 45eead8dcd | |||
| fcd39be875 | |||
| a609716520 | |||
| a0ae06022c | |||
| c9cf8cfdf6 | |||
| 3d4e1f2d48 | |||
| ca74743eac | |||
| fb031a3c57 | |||
| 4bad6f6f79 | |||
| 7e1b3769d7 | |||
| 4a98785aa3 | |||
| d305071351 | |||
| 653009185e | |||
| 57bfc691bf | |||
| 7e4f8d4154 | |||
| 361f692d53 | |||
| f81099af79 | |||
| 1d982da86e | |||
| c306fc7cfd | |||
| b49c21e5aa | |||
| 44b2df59da | |||
| c8c3a814ea | |||
| 46c3592563 | |||
| 6534ce356a | |||
| e4dfbd1a60 | |||
| e9a2c925ed | |||
| 57292f09bf | |||
| e577502f4b | |||
| 7b0c0784ed | |||
| 96dcfa4287 | |||
| 6046a84e95 | |||
| 1884087590 | |||
| 637c3c820c | |||
| 4f47f8cb0a | |||
| e38668b3cb | |||
| 9dbf5db9ec | |||
| e47a836d2d | |||
| 6c0ee59a3e | |||
| a0f4028aaa | |||
| beb7e5bf6b | |||
| f88ef7ecc2 | |||
| 6cc8e9b86a | |||
| a999f28270 | |||
| 2a3d2744af | |||
| 804316e40c | |||
| bdde8907bf | |||
| 85ab7557e9 | |||
| 3d2b959db7 | |||
| 889cd3db1a | |||
| 4916143a5d | |||
| 1153010f85 | |||
| 0cbd8013b2 | |||
| a425a9c11a | |||
| 987de36f54 | |||
| f2fbd3f97c | |||
| 30245e7d34 | |||
| 3aa2801ce7 | |||
| bc3c245aa5 | |||
| cdb266cba2 | |||
| e4e416bfb3 | |||
| 39414ee2e4 | |||
| a08fe79cef | |||
| 33508d42ef | |||
| 110bd8b295 | |||
| 622720f730 | |||
| 4bdf55f6a0 | |||
| 9e2448b3d5 | |||
| 2ccd6e35b7 | |||
| 71b5cd8594 | |||
| 3bcd062eb7 | |||
| 6a4b508864 | |||
| 6e48d7dc12 | |||
| d849ac120b | |||
| 4243a5b055 | |||
| 8d9e97fdcd | |||
| 1457b902ac | |||
| 8ab1b723ae | |||
| 2fb5fbf3ad | |||
| cbe6eb077e | |||
| 831a8f27e5 | |||
| 46d19999ce | |||
| e27badccef | |||
| 07cf72d573 | |||
| 8bda3d5be1 | |||
| cdc2b7a3ed | |||
| c7bc46ec8e | |||
| dc1b9fc790 | |||
| 275f8966b2 | |||
| 5b1445bb5f | |||
| 0b1a94914b | |||
| 3317d545be | |||
| a630533b44 | |||
| cb82059d67 | |||
| e08745d519 | |||
| 7e76fa9b41 | |||
| b72570df40 | |||
| 95ee3a818b | |||
| 72992743a4 | |||
| 8eefbacc48 | |||
| 4531edad1e | |||
| 0af6ca7d37 | |||
| ac4ffb4a7e | |||
| 641f1dc238 | |||
| 650cb03b60 | |||
| cefcc41bca | |||
| 957fe8af75 | |||
| d98ae2b94b | |||
| 12a984b26d | |||
| 9cd81e89f6 | |||
| 677545cc23 | |||
| 2f02c93b3b | |||
| 7f314d9f28 | |||
| 6ea41a0329 | |||
| c33e8094e8 | |||
| f0b23a40fe | |||
| b65158cd97 | |||
| b6794d72d3 | |||
| ea2aa532af | |||
| 5bbe5f1daf | |||
| 1afb6b3978 | |||
| 9e03465520 | |||
| 06e267cef2 | |||
| b79527a217 | |||
| fa85c848f6 | |||
| 0e08829188 | |||
| 1363a5b044 | |||
| 60406cb982 | |||
| 152c39044f | |||
| 6c392185be | |||
| c7a5c880cc | |||
| 0510e87530 | |||
| c9734c9b1a | |||
| 66e04d4a45 | |||
| c1577d2536 | |||
| 23b354d47e | |||
| 5c9888a0d7 | |||
| 74cf048f94 | |||
| 0eb8d0cfdc | |||
| 9ddf5d31b4 | |||
| 7da4359dd0 | |||
| 032b2a1fde | |||
| 08df283511 | |||
| ae3792c683 | |||
| 9d2149d495 | |||
| 5234a121f9 | |||
| 706c65a70d | |||
| 04bee88dd2 | |||
| 072928ec96 | |||
| 03ec0b2c60 | |||
| 294e301000 | |||
| 74e4dd4c44 | |||
| f40881657c | |||
| 4bb15dded4 | |||
| 5eb7e8f02b | |||
| ef5ece17eb | |||
| 1339ca02c4 | |||
| d5e40d3bdf | |||
| 97c20744d3 | |||
| 30786472cb | |||
| af185f8491 | |||
| 2a5cb66b9a | |||
| 82eae95add | |||
| 85b33d8819 | |||
| e4fc9b6912 | |||
| a44cde63a5 | |||
| d93039698c | |||
| 3a1ca3fb81 | |||
| 28e51ebfea | |||
| 1c2627e1ae | |||
| b4dc28a45e | |||
| c8a9eb5464 | |||
| 11be793c3a | |||
| ce989294ab | |||
| a21520fa8f | |||
| e5751388d4 |
@@ -21,6 +21,7 @@ compiledmodels/
|
||||
/docs_site/
|
||||
|
||||
*.mp4
|
||||
!docs/assets/speed-limit-vision-demo*.mp4
|
||||
*.dylib
|
||||
*.DSYM
|
||||
*.d
|
||||
|
||||
@@ -3,4 +3,110 @@
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
exec "${ROOT_DIR}/scripts/laptop_device_build.sh" build "$@"
|
||||
original_args=("$@")
|
||||
|
||||
shortcut_targets=()
|
||||
passthrough_args=()
|
||||
shortcut_includes_panda=0
|
||||
panda_generated_files=(
|
||||
"panda/board/obj/gitversion.h"
|
||||
"panda/board/obj/version"
|
||||
)
|
||||
|
||||
add_panda_targets() {
|
||||
local variants=(
|
||||
panda
|
||||
panda_h7
|
||||
panda_remote
|
||||
panda_h7_remote
|
||||
panda_hkg_remote
|
||||
panda_h7_hkg_remote
|
||||
panda_can_ignition_only
|
||||
panda_h7_can_ignition_only
|
||||
panda_remote_can_ignition_only
|
||||
panda_h7_remote_can_ignition_only
|
||||
panda_hkg_remote_can_ignition_only
|
||||
panda_h7_hkg_remote_can_ignition_only
|
||||
panda_jungle_h7
|
||||
body_h7
|
||||
)
|
||||
|
||||
local variant
|
||||
for variant in "${variants[@]}"; do
|
||||
shortcut_targets+=("panda/board/obj/${variant}.bin.signed")
|
||||
done
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--params|-params|-param)
|
||||
shortcut_targets+=("common/params_pyx.so")
|
||||
;;
|
||||
--panda|-panda)
|
||||
shortcut_includes_panda=1
|
||||
add_panda_targets
|
||||
;;
|
||||
--ui|-ui)
|
||||
shortcut_targets+=("selfdrive/ui/ui")
|
||||
;;
|
||||
--cereal|-cereal)
|
||||
shortcut_targets+=(
|
||||
"cereal/libcereal.a"
|
||||
"cereal/libsocketmaster.a"
|
||||
"cereal/messaging/bridge"
|
||||
"cereal/services.h"
|
||||
)
|
||||
;;
|
||||
*)
|
||||
if [[ "$1" =~ ^[0-9]+$ ]]; then
|
||||
passthrough_args+=("-j$1")
|
||||
else
|
||||
passthrough_args+=("$1")
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ "${#shortcut_targets[@]}" -gt 0 ]]; then
|
||||
scons_args=("--no-scrub")
|
||||
scons_args+=("${shortcut_targets[@]}")
|
||||
if [[ "${#passthrough_args[@]}" -gt 0 ]]; then
|
||||
scons_args+=("${passthrough_args[@]}")
|
||||
fi
|
||||
|
||||
if [[ "${shortcut_includes_panda}" -eq 1 ]]; then
|
||||
exec "${ROOT_DIR}/scripts/laptop_device_build.sh" scons "${scons_args[@]}"
|
||||
fi
|
||||
|
||||
tmpdir="$(mktemp -d)"
|
||||
for path in "${panda_generated_files[@]}"; do
|
||||
if [[ -e "${ROOT_DIR}/${path}" ]]; then
|
||||
cp -p "${ROOT_DIR}/${path}" "${tmpdir}/${path//\//__}"
|
||||
else
|
||||
touch "${tmpdir}/${path//\//__}.missing"
|
||||
fi
|
||||
done
|
||||
|
||||
if "${ROOT_DIR}/scripts/laptop_device_build.sh" scons "${scons_args[@]}"; then
|
||||
status=0
|
||||
else
|
||||
status=$?
|
||||
fi
|
||||
|
||||
for path in "${panda_generated_files[@]}"; do
|
||||
if [[ -e "${tmpdir}/${path//\//__}.missing" ]]; then
|
||||
rm -f "${ROOT_DIR}/${path}"
|
||||
else
|
||||
cp -p "${tmpdir}/${path//\//__}" "${ROOT_DIR}/${path}"
|
||||
fi
|
||||
done
|
||||
rm -rf "${tmpdir}"
|
||||
exit "${status}"
|
||||
fi
|
||||
|
||||
if [[ "${#original_args[@]}" -gt 0 ]]; then
|
||||
exec "${ROOT_DIR}/scripts/laptop_device_build.sh" build "${original_args[@]}"
|
||||
fi
|
||||
|
||||
exec "${ROOT_DIR}/scripts/laptop_device_build.sh" build
|
||||
|
||||
Binary file not shown.
+3
-1
@@ -2192,6 +2192,7 @@ struct DriverStateV2 {
|
||||
rightBlinkProb @8 :Float32;
|
||||
sunglassesProb @9 :Float32;
|
||||
phoneProb @13 :Float32;
|
||||
sleepProb @14 :Float32;
|
||||
notReadyProbDEPRECATED @12 :List(Float32);
|
||||
occludedProbDEPRECATED @10 :Float32;
|
||||
readyProbDEPRECATED @11 :List(Float32);
|
||||
@@ -2263,7 +2264,8 @@ struct DriverMonitoringStateDEPRECATED @0xb83cda094a1da284 {
|
||||
|
||||
struct DriverMonitoringState {
|
||||
lockout @0 :Bool;
|
||||
lockoutRecoveryPercent @11 :Int8;
|
||||
lockoutCount @15 :Int8;
|
||||
lockoutMinutesRemaining @11 :Int8;
|
||||
alert3Count @12 :Int8;
|
||||
noResponseCount @13 :Int8;
|
||||
noResponseForceDecel @14 :Bool;
|
||||
|
||||
@@ -268,6 +268,7 @@ class SubMaster:
|
||||
class PubMaster:
|
||||
def __init__(self, services: List[str]):
|
||||
self.sock = {}
|
||||
self._zmq_readers_settled = set()
|
||||
for s in services:
|
||||
self.sock[s] = pub_sock(s)
|
||||
|
||||
@@ -277,6 +278,13 @@ class PubMaster:
|
||||
self.sock[s].send(dat)
|
||||
|
||||
def wait_for_readers_to_update(self, s: str, timeout: int, dt: float = 0.05) -> bool:
|
||||
if "ZMQ" in os.environ:
|
||||
if s not in self._zmq_readers_settled:
|
||||
time.sleep(min(timeout, 1.0))
|
||||
self._zmq_readers_settled.add(s)
|
||||
elif dt >= 0.05:
|
||||
time.sleep(min(timeout, 1.0))
|
||||
return True
|
||||
for _ in range(int(timeout*(1./dt))):
|
||||
if self.sock[s].all_readers_updated():
|
||||
return True
|
||||
|
||||
Binary file not shown.
+69
-1
@@ -1,5 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
import io
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
CHUNK_SIZE = 45 * 1024 * 1024 # 45MB, under GitHub's 50MB limit
|
||||
@@ -13,9 +16,16 @@ def get_manifest_path(name):
|
||||
return f"{name}.chunkmanifest"
|
||||
|
||||
|
||||
def _chunk_paths(path, num_chunks):
|
||||
return [get_manifest_path(path)] + [get_chunk_name(path, i, num_chunks) for i in range(num_chunks)]
|
||||
|
||||
|
||||
def get_chunk_paths(path, file_size):
|
||||
num_chunks = math.ceil(file_size / CHUNK_SIZE)
|
||||
return [get_manifest_path(path)] + [get_chunk_name(path, i, num_chunks) for i in range(num_chunks)]
|
||||
return _chunk_paths(path, num_chunks)
|
||||
|
||||
|
||||
get_chunk_targets = get_chunk_paths
|
||||
|
||||
|
||||
def chunk_file(path, targets):
|
||||
@@ -31,6 +41,59 @@ def chunk_file(path, targets):
|
||||
os.remove(path)
|
||||
|
||||
|
||||
def get_existing_chunks(path):
|
||||
if os.path.isfile(path):
|
||||
return [path]
|
||||
manifest_path = get_manifest_path(path)
|
||||
if os.path.isfile(manifest_path):
|
||||
num_chunks = int(Path(manifest_path).read_text().strip())
|
||||
return _chunk_paths(path, num_chunks)
|
||||
raise FileNotFoundError(path)
|
||||
|
||||
|
||||
def file_chunked_exists(path) -> bool:
|
||||
return os.path.isfile(path) or os.path.isfile(get_manifest_path(path))
|
||||
|
||||
|
||||
class ChunkStream(io.RawIOBase):
|
||||
def __init__(self, paths):
|
||||
self._paths = iter(paths)
|
||||
self._file = None
|
||||
|
||||
def readable(self):
|
||||
return True
|
||||
|
||||
def readinto(self, buffer):
|
||||
count = 0
|
||||
view = memoryview(buffer)
|
||||
while count < len(buffer):
|
||||
if self._file is None:
|
||||
path = next(self._paths, None)
|
||||
if path is None:
|
||||
break
|
||||
self._file = open(path, "rb")
|
||||
bytes_read = self._file.readinto(view[count:])
|
||||
if not bytes_read:
|
||||
self._file.close()
|
||||
self._file = None
|
||||
continue
|
||||
count += bytes_read
|
||||
return count
|
||||
|
||||
def close(self):
|
||||
if self._file is not None:
|
||||
self._file.close()
|
||||
self._file = None
|
||||
super().close()
|
||||
|
||||
|
||||
def open_file_chunked(path):
|
||||
chunks = get_existing_chunks(path)
|
||||
if chunks and chunks[0] == get_manifest_path(path):
|
||||
chunks = chunks[1:]
|
||||
return io.BufferedReader(ChunkStream(chunks))
|
||||
|
||||
|
||||
def read_file_chunked(path):
|
||||
manifest_path = get_manifest_path(path)
|
||||
if os.path.isfile(manifest_path):
|
||||
@@ -39,3 +102,8 @@ def read_file_chunked(path):
|
||||
if os.path.isfile(path):
|
||||
return Path(path).read_bytes()
|
||||
raise FileNotFoundError(path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
file_path = sys.argv[1]
|
||||
chunk_file(file_path, get_chunk_targets(file_path, os.path.getsize(file_path)))
|
||||
|
||||
Binary file not shown.
+5
-1
@@ -99,7 +99,7 @@ Params::Params(const std::string &path, bool memory) {
|
||||
if (memory) {
|
||||
params_folder = Path::shm_path() + "/params";
|
||||
} else {
|
||||
cache_path = "/cache/params" + params_prefix + "/";
|
||||
cache_path = Path::params_cache() + params_prefix + "/";
|
||||
params_folder = path;
|
||||
}
|
||||
params_path = ensure_params_path(params_prefix, params_folder);
|
||||
@@ -309,6 +309,10 @@ int Params::getTuningLevel(const std::string &key) {
|
||||
return keys[key].tuning_level;
|
||||
}
|
||||
|
||||
ParamSettingsTier Params::getSettingsTier(const std::string &key) {
|
||||
return keys[key].settings_tier;
|
||||
}
|
||||
|
||||
std::optional<std::string> Params::getStockValue(const std::string &key) {
|
||||
ParamKeyAttributes &attributes = keys[key];
|
||||
if (attributes.stock_value) {
|
||||
|
||||
@@ -33,6 +33,11 @@ enum ParamKeyType {
|
||||
BYTES = 6
|
||||
};
|
||||
|
||||
enum ParamSettingsTier {
|
||||
SETTINGS_SIMPLE = 0,
|
||||
SETTINGS_ADVANCED = 1,
|
||||
};
|
||||
|
||||
struct ParamKeyAttributes {
|
||||
uint32_t flags;
|
||||
ParamKeyType type;
|
||||
@@ -42,6 +47,9 @@ struct ParamKeyAttributes {
|
||||
std::optional<std::string> stock_value = std::nullopt;
|
||||
|
||||
int tuning_level = 0;
|
||||
|
||||
// Controls settings-page visibility only. It does not gate the param's runtime behavior.
|
||||
ParamSettingsTier settings_tier = SETTINGS_ADVANCED;
|
||||
};
|
||||
|
||||
class Params {
|
||||
@@ -112,6 +120,8 @@ public:
|
||||
|
||||
int getTuningLevel(const std::string &key);
|
||||
|
||||
ParamSettingsTier getSettingsTier(const std::string &key);
|
||||
|
||||
std::optional<std::string> getStockValue(const std::string &key);
|
||||
|
||||
private:
|
||||
|
||||
@@ -4,6 +4,27 @@ from enum import IntEnum, IntFlag
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
|
||||
SETTINGS_SIMPLE = 0
|
||||
SETTINGS_ADVANCED = 1
|
||||
|
||||
|
||||
def _load_settings_tiers() -> dict[str, int]:
|
||||
params_keys = Path(__file__).with_name("params_keys.h")
|
||||
if not params_keys.exists():
|
||||
return {}
|
||||
|
||||
tiers = {}
|
||||
for line in params_keys.read_text(encoding="utf-8", errors="ignore").splitlines():
|
||||
if not line.lstrip().startswith('{"'):
|
||||
continue
|
||||
parts = line.split('"')
|
||||
if len(parts) >= 2:
|
||||
tiers[parts[1]] = SETTINGS_SIMPLE if "SETTINGS_SIMPLE" in line else SETTINGS_ADVANCED
|
||||
return tiers
|
||||
|
||||
|
||||
_SETTINGS_TIERS = _load_settings_tiers()
|
||||
|
||||
try:
|
||||
from openpilot.common.params_pyx import Params as _Params, ParamKeyFlag, ParamKeyType, UnknownKeyName
|
||||
except Exception:
|
||||
@@ -180,6 +201,9 @@ except Exception:
|
||||
def get_tuning_level(self, key):
|
||||
return 0
|
||||
|
||||
def get_settings_tier(self, key):
|
||||
return _SETTINGS_TIERS.get(self.check_key(key), SETTINGS_ADVANCED)
|
||||
|
||||
else:
|
||||
assert _Params
|
||||
assert ParamKeyFlag
|
||||
@@ -187,6 +211,12 @@ else:
|
||||
assert UnknownKeyName
|
||||
|
||||
class Params(_Params):
|
||||
def get_settings_tier(self, key):
|
||||
try:
|
||||
return super().get_settings_tier(key)
|
||||
except AttributeError:
|
||||
return _SETTINGS_TIERS.get(self.check_key(key), SETTINGS_ADVANCED)
|
||||
|
||||
def get(self, key, block=False, return_default=False, encoding=None, default=None):
|
||||
try:
|
||||
value = super().get(key, block=block, return_default=return_default)
|
||||
|
||||
+239
-203
@@ -8,7 +8,7 @@
|
||||
inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"AccessToken", {CLEAR_ON_MANAGER_START | DONT_LOG, STRING}},
|
||||
{"AdbEnabled", {PERSISTENT, BOOL}},
|
||||
{"AlwaysAllowUploads", {PERSISTENT, BOOL, "0"}},
|
||||
{"AlwaysAllowUploads", {PERSISTENT, BOOL, "0", std::nullopt, 0, SETTINGS_SIMPLE}},
|
||||
{"AlwaysOnDM", {PERSISTENT, BOOL}},
|
||||
{"ApiCache_Device", {PERSISTENT, STRING}},
|
||||
{"AssistNowToken", {PERSISTENT, STRING}},
|
||||
@@ -36,14 +36,18 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"DoReboot", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"DoShutdown", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"DoUninstall", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"DoUserReboot", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"DriverTooDistracted", {CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON, BOOL}},
|
||||
{"DriverLockoutCount", {CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON, INT, "0"}},
|
||||
{"EcuDisableFailed", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"AlphaLongitudinalEnabled", {PERSISTENT, BOOL}},
|
||||
{"ExperimentalLongitudinalEnabled", {PERSISTENT, BOOL}},
|
||||
{"ExperimentalMode", {PERSISTENT, BOOL}},
|
||||
{"ExperimentalModeConfirmed", {PERSISTENT, BOOL}},
|
||||
{"LongitudinalModelPreference", {PERSISTENT, INT, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"LongitudinalModelPreferenceOverride", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, INT, "-1", "-1"}},
|
||||
{"PersistChillState", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"PersistExperimentalState", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"PersistExperimentalState", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"PersistedCCStatus", {PERSISTENT, INT, "0", "0"}},
|
||||
{"PersistedCEStatus", {PERSISTENT, INT, "0", "0"}},
|
||||
{"FirmwareQueryDone", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BOOL}},
|
||||
@@ -56,11 +60,13 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"GithubUsername", {PERSISTENT, STRING}},
|
||||
{"GitRemote", {PERSISTENT, STRING}},
|
||||
{"GsmApn", {PERSISTENT, STRING}},
|
||||
{"GsmMetered", {PERSISTENT, BOOL, "1"}},
|
||||
{"GsmMetered", {PERSISTENT, BOOL, "1", std::nullopt, 0, SETTINGS_SIMPLE}},
|
||||
{"GsmRoaming", {PERSISTENT, BOOL}},
|
||||
{"HardwareSerial", {PERSISTENT, STRING}},
|
||||
{"HasAcceptedTerms", {PERSISTENT, STRING, "0"}},
|
||||
{"HondaGasFactorParams", {PERSISTENT, FLOAT}},
|
||||
{"HondaLateralPidKiScale", {PERSISTENT, FLOAT, "1.0", "1.0", 3}},
|
||||
{"HondaLateralPidKpScale", {PERSISTENT, FLOAT, "1.0", "1.0", 3}},
|
||||
{"HondaWindFactorParams", {PERSISTENT, FLOAT}},
|
||||
{"InstallDate", {PERSISTENT, TIME}},
|
||||
{"IsDriverViewEnabled", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
@@ -69,7 +75,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"IsMetric", {PERSISTENT, BOOL}},
|
||||
{"IsOffroad", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
{"IsOnroad", {PERSISTENT, BOOL}},
|
||||
{"IsRHD", {PERSISTENT, BOOL}},
|
||||
{"IsRHD", {PERSISTENT, BOOL, std::nullopt, std::nullopt, 0, SETTINGS_SIMPLE}},
|
||||
{"IsRhdDetected", {PERSISTENT, BOOL}},
|
||||
{"IsRHDOverride", {PERSISTENT, BOOL}},
|
||||
{"IsReleaseBranch", {CLEAR_ON_MANAGER_START, BOOL}},
|
||||
@@ -124,6 +130,8 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"SecOCKey", {PERSISTENT | DONT_LOG, STRING}},
|
||||
{"ShowDebugInfo", {PERSISTENT, BOOL}},
|
||||
{"ShowAllToggles", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"TryRaylibUI", {PERSISTENT, BOOL, "1"}},
|
||||
{"UseOldUI", {PERSISTENT, BOOL, "0", std::nullopt, 0, SETTINGS_SIMPLE}},
|
||||
{"UsePrebuilt", {PERSISTENT, BOOL, "1"}},
|
||||
{"RouteCount", {PERSISTENT, INT, "0"}},
|
||||
{"SnoozeUpdate", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||
@@ -144,15 +152,18 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"UpdaterLastFetchTime", {PERSISTENT, TIME}},
|
||||
{"UptimeOffroad", {PERSISTENT, FLOAT, "0.0"}},
|
||||
{"UptimeOnroad", {PERSISTENT, FLOAT, "0.0"}},
|
||||
{"UsbGpuActive", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||
{"UsbGpuCompiled", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||
{"UsbGpuPresent", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL}},
|
||||
{"Version", {PERSISTENT, STRING}},
|
||||
|
||||
// StarPilot variables
|
||||
{"AccelerationPath", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"AccelerationProfile", {PERSISTENT, INT, "0", "0", 0}},
|
||||
{"AccelerationPath", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"AccelerationProfile", {PERSISTENT, INT, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"AdjacentLeadsUI", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"AdjacentPath", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"AdjacentPath", {PERSISTENT, BOOL, "0", "0", 3, SETTINGS_SIMPLE}},
|
||||
{"AdjacentPathMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"AdvancedCustomUI", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"AdvancedCustomUI", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"AdvancedLateralTune", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"AdvancedLongitudinalTune", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"AggressiveFollow", {PERSISTENT, FLOAT, "1.25", "1.25", 2}},
|
||||
@@ -162,9 +173,9 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"AggressiveJerkDeceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"AggressiveJerkSpeed", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"AggressiveJerkSpeedDecrease", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"AlertVolumeControl", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"AlertVolumeControl", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"AllowImpossibleAcceleration", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"AlwaysOnLateral", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"AlwaysOnLateral", {PERSISTENT, BOOL, "1", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"AlwaysOnLateralLKAS", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"ApiCache_DriveStats", {PERSISTENT, JSON, "{}", "{}"}},
|
||||
{"AutomaticallyDownloadModels", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
@@ -177,30 +188,30 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"BootLogo", {PERSISTENT, STRING, "starpilot", "stock", 0}},
|
||||
{"BuildMetadata", {PERSISTENT, STRING, "", "", 0}},
|
||||
{"BlindSpotMetrics", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"BlindSpotPath", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"BelowSteerSpeedVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"BlindSpotPath", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"BelowSteerSpeedVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"BorderMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"BorderWidth", {PERSISTENT, FLOAT, "100.0", "100.0", 2}},
|
||||
{"BorderWidth", {PERSISTENT, FLOAT, "100.0", "100.0", 2, SETTINGS_SIMPLE}},
|
||||
{"CalibratedLateralAcceleration", {PERSISTENT, FLOAT, "2.0", "2.0", 2}},
|
||||
{"CalibrationProgress", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"CameraOffset", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"CameraView", {PERSISTENT, INT, "3", "0", 2}},
|
||||
{"CameraView", {PERSISTENT, INT, "3", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"CancelDownloadMaps", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"DisableWideRoad", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"CancelModelDownload", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"CancelThemeDownload", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"CarMake", {PERSISTENT, STRING, "mock", "mock", 0}},
|
||||
{"CarModel", {PERSISTENT, STRING, "MOCK", "MOCK", 0}},
|
||||
{"CarMake", {PERSISTENT, STRING, "mock", "mock", 0, SETTINGS_SIMPLE}},
|
||||
{"CarModel", {PERSISTENT, STRING, "MOCK", "MOCK", 0, SETTINGS_SIMPLE}},
|
||||
{"CarModelName", {PERSISTENT, STRING, "", "", 0}},
|
||||
{"CECurves", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"CECurves", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"CECurvesLead", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"CELead", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"CEModelStopTime", {PERSISTENT, FLOAT, "7.0", "0.0", 2}},
|
||||
{"CELead", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"CEModelStopTime", {PERSISTENT, FLOAT, "7.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"CESignalLaneDetection", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"CESignalSpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"CESlowerLead", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"CESpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
|
||||
{"CESpeedLead", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
|
||||
{"CESignalSpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"CESlowerLead", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"CESpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 1, SETTINGS_SIMPLE}},
|
||||
{"CESpeedLead", {PERSISTENT, FLOAT, "0.0", "0.0", 1, SETTINGS_SIMPLE}},
|
||||
{"CCMLead", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"CCMLaunchAssist", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"CCMSetSpeedMargin", {PERSISTENT, FLOAT, "3.0", "0.0", 1}},
|
||||
@@ -208,19 +219,19 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"CCMSpeedLead", {PERSISTENT, FLOAT, "35.0", "0.0", 1}},
|
||||
{"CCStatus", {CLEAR_ON_OFFROAD_TRANSITION, INT, "0", "0"}},
|
||||
{"CEStatus", {CLEAR_ON_OFFROAD_TRANSITION, INT, "0", "0"}},
|
||||
{"CEStopLights", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"CEStoppedLead", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"ClusterOffset", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
|
||||
{"CEStopLights", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"CEStoppedLead", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"ClusterOffset", {PERSISTENT, FLOAT, "1.0", "1.0", 2, SETTINGS_SIMPLE}},
|
||||
{"ColorScheme", {PERSISTENT, STRING, "stock", "stock", 0}},
|
||||
{"ColorToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"BootLogoToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"Compass", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"Compass", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"CommunityFavorites", {PERSISTENT, STRING, "", "", 1}},
|
||||
{"ConditionalChill", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"ConditionalExperimental", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"ConditionalExperimental", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"CurvatureData", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
|
||||
{"CurveSpeedController", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"CustomAlerts", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"CurveSpeedController", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"CustomAlerts", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"CustomAccelProfile", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"CustomAccelProfileInitialized", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"CustomAccelProfile0MPH", {PERSISTENT, FLOAT, "3.0", "3.0", 3}},
|
||||
@@ -230,10 +241,10 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"CustomAccelProfile45MPH", {PERSISTENT, FLOAT, "1.0", "1.0", 3}},
|
||||
{"CustomAccelProfile56MPH", {PERSISTENT, FLOAT, "0.8", "0.8", 3}},
|
||||
{"CustomAccelProfile89MPH", {PERSISTENT, FLOAT, "0.6", "0.6", 3}},
|
||||
{"CustomCruise", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
|
||||
{"CustomCruiseLong", {PERSISTENT, FLOAT, "5.0", "5.0", 2}},
|
||||
{"CustomCruise", {PERSISTENT, FLOAT, "1.0", "1.0", 2, SETTINGS_SIMPLE}},
|
||||
{"CustomCruiseLong", {PERSISTENT, FLOAT, "5.0", "5.0", 2, SETTINGS_SIMPLE}},
|
||||
{"CustomPersonalities", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"CancelButtonControl", {PERSISTENT, INT, "1", "0", 2}},
|
||||
{"CancelButtonControl", {PERSISTENT, INT, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"CancelButtonControlsMigrated", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"AOLLKASMigratedToButtonControl", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"TrafficPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
@@ -241,9 +252,9 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"StandardPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
{"RelaxedPersonalityProfile", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
{"CustomThemes", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"CustomUI", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"CustomUI", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"DebugMode", {CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0", "0"}},
|
||||
{"DecelerationProfile", {PERSISTENT, INT, "1", "0", 2}},
|
||||
{"DecelerationProfile", {PERSISTENT, INT, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"DeveloperMetrics", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"DeveloperSidebar", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"DeveloperSidebarMetric1", {PERSISTENT, INT, "1", "0", 3}},
|
||||
@@ -254,14 +265,15 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"DeveloperSidebarMetric6", {PERSISTENT, INT, "6", "0", 3}},
|
||||
{"DeveloperSidebarMetric7", {PERSISTENT, INT, "7", "0", 3}},
|
||||
{"DeveloperUI", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"GalaxyDeveloperMode", {PERSISTENT | DONT_LOG, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"DeveloperWidgets", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"DeviceManagement", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"DeviceShutdown", {PERSISTENT, INT, "9", "33", 1}},
|
||||
{"DisableOnroadUploads", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"DisableOpenpilotLongitudinal", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"DeviceManagement", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"DeviceShutdown", {PERSISTENT, INT, "9", "33", 1, SETTINGS_SIMPLE}},
|
||||
{"DisableOnroadUploads", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"DisableOpenpilotLongitudinal", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"DiscordUsername", {PERSISTENT, STRING, "", "", 0}},
|
||||
{"DisengageVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"DistanceButtonControl", {PERSISTENT, INT, "1", "0", 2}},
|
||||
{"DisengageVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"DistanceButtonControl", {PERSISTENT, INT, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"DistanceIconPack", {PERSISTENT, STRING, "stock", "stock", 0}},
|
||||
{"DistanceIconToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"DownloadableBootLogos", {PERSISTENT, STRING, "", ""}},
|
||||
@@ -273,48 +285,53 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"DownloadableWheels", {PERSISTENT, STRING, "", ""}},
|
||||
{"DownloadAllModels", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"DownloadMaps", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"DriverCamera", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"DriverCamera", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"Model", {PERSISTENT, STRING, "sc2", "sc2", 1}},
|
||||
{"ModelVersion", {PERSISTENT, STRING, "v11", "v11", 1}},
|
||||
{"DrivingModel", {PERSISTENT, STRING, "sc2", "sc2", 1}},
|
||||
{"DrivingModelName", {PERSISTENT, STRING, "South Carolina", "South Carolina", 1}},
|
||||
{"DrivingModelVersion", {PERSISTENT, STRING, "v11", "v11", 1}},
|
||||
{"DynamicPathWidth", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"DynamicPedalsOnUI", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"EngageVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"DynamicPathWidth", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"DynamicPedalsOnUI", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"EngageVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"EVTuning", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"Fahrenheit", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"FlashPanda", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"GMDashSpoofOffsets", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"GMPedalLongitudinal", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
{"IgnoreIgnitionLine", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"LongPitch", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"RemoteStartBootsComma", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"RemapCancelToDistance", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"NAPAdaptiveAccel", {PERSISTENT, BOOL, "1", "1"}},
|
||||
{"GMDashSpoofOffsets", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"GMPedalLongitudinal", {PERSISTENT, BOOL, "1", "1", 2, SETTINGS_SIMPLE}},
|
||||
{"HKGRemoteStartBootsComma", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"IgnoreIgnitionLine", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"LongPitch", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"RemoteStartBootsComma", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"RemapCancelToDistance", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"NAPAdaptiveAccel", {PERSISTENT, BOOL, "1", "1", 0, SETTINGS_SIMPLE}},
|
||||
{"NAPFollowDistance", {PERSISTENT, INT, "4", "4"}},
|
||||
{"NAPForcePreAP", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"NAPPedalEnabled", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"NAPPedalCanBus", {PERSISTENT, INT, "2", "2"}},
|
||||
{"NAPPedalCalibDone", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"NAPPedalEnabled", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"NAPPedalCanBus", {PERSISTENT, INT, "2", "2", 0, SETTINGS_SIMPLE}},
|
||||
{"NAPPedalCalibDone", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"NAPPedalCalibMin", {PERSISTENT, FLOAT, "-3.0", "-3.0"}},
|
||||
{"NAPPedalCalibMax", {PERSISTENT, FLOAT, "99.6", "99.6"}},
|
||||
{"NAPPedalCalibFactor", {PERSISTENT, FLOAT, "1.0", "1.0"}},
|
||||
{"NAPPedalCalibZero", {PERSISTENT, FLOAT, "0.0", "0.0"}},
|
||||
{"NAPPedalCalibFactor", {PERSISTENT, FLOAT, "1.0", "1.0", 0, SETTINGS_SIMPLE}},
|
||||
{"NAPPedalCalibZero", {PERSISTENT, FLOAT, "0.0", "0.0", 0, SETTINGS_SIMPLE}},
|
||||
{"NAPPedalProfile", {PERSISTENT, INT, "4", "4"}},
|
||||
{"NAPRadarBehindNosecone", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"NAPRadarEnabled", {PERSISTENT, BOOL, "0", "0"}},
|
||||
{"NAPRadarOffset", {PERSISTENT, FLOAT, "0.0", "0.0"}},
|
||||
{"NAPRadarBehindNosecone", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"NAPRadarEnabled", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"NAPRadarOffset", {PERSISTENT, FLOAT, "0.0", "0.0", 0, SETTINGS_SIMPLE}},
|
||||
{"ForceAutoTune", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"ForceAutoTuneOff", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"ForceFingerprint", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"ForceFingerprint", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ForceOffroad", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"ForceOnroad", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"ForceStops", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"ForceStopDistanceOffset", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"ForceStandstill", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"ForceStops", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ForceStopDistanceOffset", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ForceStandstill", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ForceTorqueController", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"FPSCounter", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"FLMActiveOverrides", {PERSISTENT, JSON, "{}", "{}", 2}},
|
||||
{"FLMActiveProfileId", {PERSISTENT, STRING, "", "", 2}},
|
||||
{"FLMTrialBaseline", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
|
||||
{"FLMTrialApplied", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"FPSCounter", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"GalaxyDashboardStats", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
|
||||
{"StarPilotApiToken", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
|
||||
{"StarPilotCarParams", {CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION, BYTES, "", ""}},
|
||||
@@ -324,70 +341,70 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"StarPilotStats", {PERSISTENT | DONT_LOG, JSON, "{}", "{}"}},
|
||||
{"StarPilotTogglesUpdated", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"FrogsGoMoosTweak", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"GoatScream", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"GoatScreamCriticalAlerts", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"GreenLightAlert", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"HideAlerts", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideChangingLanesBanner", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideDistanceProfileBanner", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideTurningBanner", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideDMIcon", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"GoatScream", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"GoatScreamCriticalAlerts", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"GreenLightAlert", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"HideAlerts", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HideChangingLanesBanner", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HideDistanceProfileBanner", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HideTurningBanner", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HideDMIcon", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HideLeadMarker", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideMaxSpeed", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideSpeed", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideSpeedLimit", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideSteeringWheel", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HigherBitrate", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HolidayThemes", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"HumanAcceleration", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"CoastUpToLeads", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
{"PrioritizeSmoothFollowing", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HumanLaneChanges", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"HideMaxSpeed", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HideSpeed", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HideSpeedLimit", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HideSteeringWheel", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HigherBitrate", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"HolidayThemes", {PERSISTENT, BOOL, "1", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"HumanLaneChanges", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"IconPack", {PERSISTENT, STRING, "stock", "stock", 0}},
|
||||
{"IconToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"IncreasedStoppedDistance", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
|
||||
{"IncreasedStoppedDistanceLowVisibility", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"IncreasedStoppedDistanceRain", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"IncreasedStoppedDistanceRainStorm", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"IncreasedStoppedDistanceSnow", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"RedneckCruise", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"IncreaseFollowingLowVisibility", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"IncreaseFollowingRain", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"IncreaseFollowingRainStorm", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"IncreaseFollowingSnow", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"IncreaseThermalLimits", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"IncreasedStoppedDistance", {PERSISTENT, FLOAT, "0.0", "0.0", 1, SETTINGS_SIMPLE}},
|
||||
{"IncreasedStoppedDistanceLowVisibility", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"IncreasedStoppedDistanceRain", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"IncreasedStoppedDistanceRainStorm", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"IncreasedStoppedDistanceSnow", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"RedneckCruise", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"IncreaseFollowingLowVisibility", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"IncreaseFollowingRain", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"IncreaseFollowingRainStorm", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"IncreaseFollowingSnow", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"IncreaseThermalLimits", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"IssueReported", {CLEAR_ON_MANAGER_START, JSON, "{}", "{}"}},
|
||||
{"KonikDongleId", {PERSISTENT, STRING, "", "", 0}},
|
||||
{"KonikMinutes", {PERSISTENT, INT, "0", "0", 0}},
|
||||
{"LaneChanges", {PERSISTENT, BOOL, "1", "1", 0}},
|
||||
{"LaneChangeSmoothing", {PERSISTENT, INT, "10", "10", 1}},
|
||||
{"LaneChangeTime", {PERSISTENT, FLOAT, "1.0", "0.0", 1}},
|
||||
{"LaneDetectionWidth", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
|
||||
{"LaneLinesColor", {PERSISTENT, STRING, "", "", 2}},
|
||||
{"LaneLinesWidth", {PERSISTENT, FLOAT, "4.0", "2.0", 2}},
|
||||
{"LaneCentering", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"LaneCenteringE2EAuthority", {PERSISTENT, FLOAT, "1.0", "1.0", 3}},
|
||||
{"LaneCenterOffset", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"LaneChanges", {PERSISTENT, BOOL, "1", "1", 0, SETTINGS_SIMPLE}},
|
||||
{"LaneChangeSmoothing", {PERSISTENT, INT, "5", "10", 1, SETTINGS_SIMPLE}},
|
||||
{"LaneChangeTime", {PERSISTENT, FLOAT, "1.0", "0.0", 1, SETTINGS_SIMPLE}},
|
||||
{"LaneDetectionWidth", {PERSISTENT, FLOAT, "0.0", "0.0", 1, SETTINGS_SIMPLE}},
|
||||
{"LaneLinesColor", {PERSISTENT, STRING, "", "", 2, SETTINGS_SIMPLE}},
|
||||
{"LaneLinesWidth", {PERSISTENT, FLOAT, "4.0", "2.0", 2, SETTINGS_SIMPLE}},
|
||||
{"LastMapsUpdate", {PERSISTENT, STRING, "", ""}},
|
||||
{"LateralTune", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"LeadDepartingAlert", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"LeadDepartingAlert", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"LeadDetectionThreshold", {PERSISTENT, INT, "35", "50", 3}},
|
||||
{"LeadIndicator", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
{"LeadIndicator", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"LeadInfo", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"LKASButtonControl", {PERSISTENT, INT, "5", "0", 2}},
|
||||
{"LKASButtonControl", {PERSISTENT, INT, "5", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"LockDoors", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"LockDoorsTimer", {PERSISTENT, INT, "0", "0", 0}},
|
||||
{"LongCancelButtonControl", {PERSISTENT, INT, "5", "0", 2}},
|
||||
{"LongDistanceButtonControl", {PERSISTENT, INT, "5", "0", 2}},
|
||||
{"LongModeButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"LongStarButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"LongCancelButtonControl", {PERSISTENT, INT, "5", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"LongDistanceButtonControl", {PERSISTENT, INT, "5", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"LongModeButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"LongStarButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"LongitudinalActuatorDelay", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"LongitudinalActuatorDelayStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"LateralManeuverStatus", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, JSON, "{}", "{}"}},
|
||||
{"LongitudinalManeuverPaddleMode", {PERSISTENT, STRING, "auto", "auto"}},
|
||||
{"LongitudinalManeuverStatus", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, JSON, "{}", "{}"}},
|
||||
{"LongitudinalTune", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"LoudBlindspotAlert", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"LoudBlindspotAlertWhenDisengaged", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"LowVoltageShutdown", {PERSISTENT, FLOAT, "11.8", "11.8", 3}},
|
||||
{"MainCruiseButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"LongitudinalTune", {PERSISTENT, BOOL, "1", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"LoudBlindspotAlert", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"LoudBlindspotAlertWhenDisengaged", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"LowVoltageShutdown", {PERSISTENT, FLOAT, "11.8", "11.8", 3, SETTINGS_SIMPLE}},
|
||||
{"MainCruiseButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ManualUpdateInitiated", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"AMapKey1", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
|
||||
{"AMapKey2", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
|
||||
@@ -399,12 +416,13 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"MapboxSecretKey", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
|
||||
{"MapDeceleration", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"MapdSettings", {PERSISTENT, JSON, "{}", "{}"}},
|
||||
{"MapGears", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"MapGears", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"MapsSelected", {PERSISTENT, STRING, "", "", 0}},
|
||||
{"MapSpeedLimit", {CLEAR_ON_MANAGER_START, FLOAT, "0.0", "0.0"}},
|
||||
{"NavDesiresAllowed", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"NavLongitudinalAllowed", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"ClearNavOnOffroad", {PERSISTENT, BOOL, "1", "1", 2}},
|
||||
{"ClearNavOnOffroad", {PERSISTENT, BOOL, "1", "1", 2, SETTINGS_SIMPLE}},
|
||||
{"ClearNavOnOffroadTimeoutMinutes", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"NavDestination", {PERSISTENT | CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"NavInstructionCollapsed", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0", "0"}},
|
||||
{"NavInstructionState", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, JSON, "{}", "{}"}},
|
||||
@@ -416,29 +434,31 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"VisionSpeedLimitLastEvent", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"VisionSpeedLimitStatus", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"VisionSpeedLimitStream", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"VisionSpeedLimitSupportCount", {CLEAR_ON_MANAGER_START, INT, "0", "0"}},
|
||||
{"VisionSpeedLimitSupportSpeed", {CLEAR_ON_MANAGER_START, FLOAT, "0.0", "0.0"}},
|
||||
{"MaxDesiredAcceleration", {PERSISTENT, FLOAT, "4.0", "2.0", 2}},
|
||||
{"MinimumBackupSize", {PERSISTENT, INT, "0", "0"}},
|
||||
{"MinimumLaneChangeSpeed", {PERSISTENT, FLOAT, "20.0", "20.0", 2}},
|
||||
{"ModeButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"MinimumLaneChangeSpeed", {PERSISTENT, FLOAT, "20.0", "20.0", 2, SETTINGS_SIMPLE}},
|
||||
{"ModeButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ModelDownloadProgress", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"ModelDrivesAndScores", {PERSISTENT, JSON, "{}", "{}"}},
|
||||
{"ModelReleasedDates", {PERSISTENT, STRING, "", "", 1}},
|
||||
{"ModelRandomizer", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"ModelSortMode", {PERSISTENT, STRING, "alphabetical", "alphabetical", 1}},
|
||||
{"ModelToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"ModelUI", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"ModelUI", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ModelVersions", {PERSISTENT, STRING, "", "", 1}},
|
||||
{"ModelManifestVersion", {PERSISTENT, STRING, "", "", 1}},
|
||||
{"NavigationUI", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"NavigationUI", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"NNFF", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"NNFFLite", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"NostalgiaMode", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"NostalgiaMode", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"NNFFModelName", {CLEAR_ON_MANAGER_START, STRING, "", "", 0}},
|
||||
{"NoLogging", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"NoUploads", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"NudgelessLaneChange", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"NudgelessLaneChangeOnlyWhenEngaged", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"NumericalTemp", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"NoLogging", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"NoUploads", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"NudgelessLaneChange", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"NudgelessLaneChangeOnlyWhenEngaged", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"NumericalTemp", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"Offset1", {PERSISTENT, FLOAT, "5.0", "0.0", 0}},
|
||||
{"Offset2", {PERSISTENT, FLOAT, "5.0", "0.0", 0}},
|
||||
{"Offset3", {PERSISTENT, FLOAT, "5.0", "0.0", 0}},
|
||||
@@ -446,45 +466,47 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"Offset5", {PERSISTENT, FLOAT, "10.0", "0.0", 0}},
|
||||
{"Offset6", {PERSISTENT, FLOAT, "10.0", "0.0", 0}},
|
||||
{"Offset7", {PERSISTENT, FLOAT, "10.0", "0.0", 0}},
|
||||
{"OneLaneChange", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"OnroadDistanceButton", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"OneLaneChange", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"OnroadDistanceButton", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"OnroadDistanceButtonPressed", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"FavoriteVirtualAccelCruiseCounter", {CLEAR_ON_MANAGER_START, INT, "0", "0"}},
|
||||
{"FavoriteVirtualDecelCruiseCounter", {CLEAR_ON_MANAGER_START, INT, "0", "0"}},
|
||||
{"WheelButtonBookmarkCounter", {CLEAR_ON_MANAGER_START, INT, "0", "0"}},
|
||||
{"openpilotMinutes", {PERSISTENT, INT, "0", "0", 0}},
|
||||
{"OverpassRequests", {PERSISTENT, JSON, "{}", "{}"}},
|
||||
{"PathColor", {PERSISTENT, STRING, "", "", 2}},
|
||||
{"PathEdgesColor", {PERSISTENT, STRING, "", "", 2}},
|
||||
{"PathEdgeWidth", {PERSISTENT, FLOAT, "20.0", "0.0", 2}},
|
||||
{"PathWidth", {PERSISTENT, FLOAT, "6.1", "5.9", 2}},
|
||||
{"PauseAOLOnBrake", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"PauseLateralOnSignal", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"PauseLateralSpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
|
||||
{"LateralResumeDelay", {PERSISTENT, FLOAT, "0.0", "0.0", 1}},
|
||||
{"PedalsOnUI", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"PathColor", {PERSISTENT, STRING, "", "", 2, SETTINGS_SIMPLE}},
|
||||
{"PathEdgesColor", {PERSISTENT, STRING, "", "", 2, SETTINGS_SIMPLE}},
|
||||
{"PathEdgeWidth", {PERSISTENT, FLOAT, "20.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"PathWidth", {PERSISTENT, FLOAT, "6.1", "5.9", 2, SETTINGS_SIMPLE}},
|
||||
{"PauseAOLOnBrake", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"PauseLateralOnSignal", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"PauseLateralSpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 1, SETTINGS_SIMPLE}},
|
||||
{"LateralResumeDelay", {PERSISTENT, FLOAT, "0.0", "0.0", 1, SETTINGS_SIMPLE}},
|
||||
{"PedalsOnUI", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"GalaxyPaired", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"GalaxyUploadPending", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"PreferredSchedule", {PERSISTENT, INT, "2", "0", 0}},
|
||||
{"PreviousSpeedLimit", {PERSISTENT, FLOAT, "0.0", "0.0"}},
|
||||
{"PromptDistractedVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"PromptVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"QOLLateral", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"QOLLongitudinal", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"QOLVisuals", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"RadarTakeoffs", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"PromptDistractedVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"PromptVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"QOLLateral", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"QOLLongitudinal", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"QOLVisuals", {PERSISTENT, BOOL, "1", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"RadarTakeoffs", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"RadarTracksUI", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"RainbowPath", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"RainbowPath", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"RandomEvents", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"RandomThemes", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"RandomThemesHolidays", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"ReduceAccelerationLowVisibility", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"ReduceAccelerationRain", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"ReduceAccelerationRainStorm", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"ReduceAccelerationSnow", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"ReduceLateralAccelerationLowVisibility", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"ReduceLateralAccelerationRain", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"ReduceLateralAccelerationRainStorm", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"ReduceLateralAccelerationSnow", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"RefuseVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"ReduceAccelerationLowVisibility", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ReduceAccelerationRain", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ReduceAccelerationRainStorm", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ReduceAccelerationSnow", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ReduceLateralAccelerationLowVisibility", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ReduceLateralAccelerationRain", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ReduceLateralAccelerationRainStorm", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ReduceLateralAccelerationSnow", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"RefuseVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"RelaxedFollow", {PERSISTENT, FLOAT, "1.6", "1.6", 2}},
|
||||
{"RelaxedFollowHigh", {PERSISTENT, FLOAT, "1.4", "1.4", 2}},
|
||||
{"RelaxedJerkAcceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
@@ -494,42 +516,42 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"RelaxedJerkSpeedDecrease", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"ReverseCruise", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"RecoveryPower", {PERSISTENT, FLOAT, "1.0", "1.0", 2}},
|
||||
{"RoadEdgesWidth", {PERSISTENT, FLOAT, "2.0", "2.0", 2}},
|
||||
{"RoadNameUI", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"RotatingWheel", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"ScreenBrightness", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"ScreenBrightnessOnroad", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"ScreenManagement", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"ScreenRecorder", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"ScreenTimeout", {PERSISTENT, INT, "30", "30", 2}},
|
||||
{"ScreenTimeoutOnroad", {PERSISTENT, INT, "30", "10", 2}},
|
||||
{"RoadEdgesWidth", {PERSISTENT, FLOAT, "2.0", "2.0", 2, SETTINGS_SIMPLE}},
|
||||
{"RoadNameUI", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"RotatingWheel", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"ScreenBrightness", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"ScreenBrightnessOnroad", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"ScreenManagement", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"ScreenRecorder", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ScreenTimeout", {PERSISTENT, INT, "30", "30", 2, SETTINGS_SIMPLE}},
|
||||
{"ScreenTimeoutOnroad", {PERSISTENT, INT, "30", "10", 2, SETTINGS_SIMPLE}},
|
||||
{"SecOCKeys", {PERSISTENT | DONT_LOG, STRING, "", "", 0}},
|
||||
{"SafeMode", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"SafeModeBackup", {PERSISTENT, JSON, "{}", "{}"}},
|
||||
{"SetSpeedLimit", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"SetSpeedOffset", {PERSISTENT, FLOAT, "0.0", "0.0", 2}},
|
||||
{"ShowCEMStatus", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"SetSpeedOffset", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
|
||||
{"ShowCEMStatus", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ShowCCMStatus", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"ShowCPU", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"ShowCSCStatus", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"ShowCPU", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"ShowCSCStatus", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ShowGPU", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"ShowIP", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"ShowMemoryUsage", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"ShowModeStatusBanner", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"ShowMemoryUsage", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"ShowModeStatusBanner", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ShownToggleDescriptions", {PERSISTENT, JSON, "{}", "{}"}},
|
||||
{"ShowSLCOffset", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"ShowSpeedLimits", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"ShowSpeedLimits", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"ShowSteering", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"ShowStoppingPoint", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"ShowStoppingPointMetrics", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"ShowStorageLeft", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"ShowStorageUsed", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"SidebarMetrics", {PERSISTENT, BOOL, "1", "0", 3}},
|
||||
{"SidebarMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"SidebarOpen", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"SignalAnimation", {PERSISTENT, STRING, "stock", "stock", 0}},
|
||||
{"SignalMetrics", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"SignalToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"SimpleMode", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"SimpleMode", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"SLCAbbreviatedSources", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"SLCActiveSourcesOnly", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"SLCConfirmation", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
@@ -538,18 +560,18 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"SLCFallback", {PERSISTENT, INT, "2", "0", 1}},
|
||||
{"SLCLookaheadHigher", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"SLCLookaheadLower", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"SLCMapboxFiller", {PERSISTENT, BOOL, "1", "0", 1}},
|
||||
{"SLCMapboxFiller", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"SLCOverride", {PERSISTENT, INT, "1", "0", 1}},
|
||||
{"SLCPriority", {PERSISTENT, STRING, "", "", 2}},
|
||||
{"SLCPriority1", {PERSISTENT, STRING, "Map Data", "Map Data", 2}},
|
||||
{"SLCPriority2", {PERSISTENT, STRING, "Dashboard", "Dashboard", 2}},
|
||||
{"SNGHack", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"SLCPriority1", {PERSISTENT, STRING, "Vision", "Map Data", 2}},
|
||||
{"SLCPriority2", {PERSISTENT, STRING, "Map Data", "Dashboard", 2}},
|
||||
{"SNGHack", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"SoundPack", {PERSISTENT, STRING, "stock", "stock", 0}},
|
||||
{"SoundToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"SLCAdoptSpeedLimit", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"SLCForceCruiseSpeed", {CLEAR_ON_MANAGER_START, FLOAT, "0.0", "0.0"}},
|
||||
{"SpeedLimitAccepted", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"SpeedLimitChangedAlert", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"SpeedLimitChangedAlert", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"SpeedLimitController", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"SpeedLimitFiller", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"SpeedLimits", {PERSISTENT | DONT_LOG, JSON, "[]", "[]"}},
|
||||
@@ -557,7 +579,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"SpeedLimitSources", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"VisionSpeedLimitAutoBookmark", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"VisionSpeedLimitAutoPreserveSegment", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"VisionSpeedLimitDetection", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"VisionSpeedLimitDetection", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"VisionSpeedLimitTrainingCollector", {PERSISTENT, BOOL, "1", "1", 0}},
|
||||
{"StandardFollow", {PERSISTENT, FLOAT, "1.45", "1.45", 2}},
|
||||
{"StandardFollowHigh", {PERSISTENT, FLOAT, "1.2", "1.2", 2}},
|
||||
@@ -566,13 +588,14 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"StandardJerkDeceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"StandardJerkSpeed", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"StandardJerkSpeedDecrease", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"StandbyMode", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"StandbyMode", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"StartAccel", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"StartAccelStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"StartupMessageBottom", {PERSISTENT, STRING, "Always keep hands on wheel and eyes on road", "Always keep hands on wheel and eyes on road", 0}},
|
||||
{"StartupMessageTop", {PERSISTENT, STRING, "Be ready to take over at any time", "Be ready to take over at any time", 0}},
|
||||
{"StaticPedalsOnUI", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"StaticPedalsOnUI", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"SteerDelay", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"SteerDelayModeMigrated", {PERSISTENT, BOOL}},
|
||||
{"SteerDelayStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"SteerFriction", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"SteerFrictionStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
@@ -584,20 +607,21 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"SteerOffsetStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"SteerRatio", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"SteerRatioStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"StockConfidenceBallWidget", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
{"EnableTorqueBarWidget", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"StockConfidenceBallWidget", {PERSISTENT, BOOL, "0", "0", 0, SETTINGS_SIMPLE}},
|
||||
{"StockDongleId", {PERSISTENT, STRING, "", ""}},
|
||||
{"StopAccel", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"StopAccelStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"StoppedTimer", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"StopDistance", {PERSISTENT, FLOAT, "6.0", "6.0", 2}},
|
||||
{"StoppedTimer", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"StoppingDecelRate", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"StoppingDecelRateStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"StarButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"SwitchbackModeCooldown", {PERSISTENT, INT, "5", "0", 2}},
|
||||
{"StarButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"SwitchbackModeCooldown", {PERSISTENT, INT, "5", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"SwitchbackModeEnabled", {CLEAR_ON_OFFROAD_TRANSITION, BOOL, "0", "0"}},
|
||||
{"SubaruSNG", {PERSISTENT, BOOL, "1", "0", 2}},
|
||||
{"SubaruSNGManualParkingBrake", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"SubaruSNG", {PERSISTENT, BOOL, "1", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"SubaruSNGManualParkingBrake", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"TacoTune", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"TeslaCoopSteering", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"TestAlert", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
{"TetheringEnabled", {PERSISTENT, INT, "0", "0", 0}},
|
||||
{"ThemeDownloadProgress", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
@@ -606,12 +630,12 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"TinygradUpdateAvailable", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"ToyotaDoors", {PERSISTENT, BOOL, "1", "0", 0}},
|
||||
{"TrailerLoad", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"TrafficFollow", {PERSISTENT, FLOAT, "0.5", "0.5", 2}},
|
||||
{"TrafficJerkAcceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"TrafficFollow", {PERSISTENT, FLOAT, "0.75", "0.75", 2}},
|
||||
{"TrafficJerkAcceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"TrafficJerkDanger", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"TrafficJerkDeceleration", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"TrafficJerkSpeed", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"TrafficJerkSpeedDecrease", {PERSISTENT, FLOAT, "50.0", "50.0", 3}},
|
||||
{"TrafficJerkDeceleration", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"TrafficJerkSpeed", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"TrafficJerkSpeedDecrease", {PERSISTENT, FLOAT, "100.0", "100.0", 3}},
|
||||
{"TruckTuning", {PERSISTENT, BOOL, "0", "0", 3}},
|
||||
{"TuningLevel", {PERSISTENT, INT, "0", "0", 0}},
|
||||
{"TuningLevelConfirmed", {PERSISTENT, BOOL, "0", "0", 0}},
|
||||
@@ -623,28 +647,40 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"UpdateTinygrad", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"UpdateWheelImage", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"UseActiveTheme", {CLEAR_ON_MANAGER_START, BOOL, "0", "0"}},
|
||||
{"UseKonikServer", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"UseAutoSteerDelay", {PERSISTENT, BOOL, "1", "1", 3}},
|
||||
{"UseKonikServer", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"UseSI", {PERSISTENT, BOOL, "1", "1", 3}},
|
||||
{"UserFavorites", {PERSISTENT, STRING, "", "", 1}},
|
||||
{"UseVienna", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"UseVienna", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
|
||||
{"VASMAnnotationConfig", {PERSISTENT, JSON, "{}", "{}", 2}},
|
||||
{"VASMConfidenceThreshold", {PERSISTENT, FLOAT, "0.85", "0.85", 2}},
|
||||
{"VASMEnabled", {PERSISTENT, BOOL, "0", "0", 1}},
|
||||
{"VASMLeftActive", {CLEAR_ON_MANAGER_START, STRING, "0", "0", 2}},
|
||||
{"VASMLeftConfidence", {CLEAR_ON_MANAGER_START, STRING, "0.0", "0.0", 2}},
|
||||
{"VASMLastUpdateMonoTime", {CLEAR_ON_MANAGER_START, STRING, "0", "0", 2}},
|
||||
{"VASMRightActive", {CLEAR_ON_MANAGER_START, STRING, "0", "0", 2}},
|
||||
{"VASMRightConfidence", {CLEAR_ON_MANAGER_START, STRING, "0.0", "0.0", 2}},
|
||||
{"VASMSmoothSeconds", {PERSISTENT, FLOAT, "0.2", "0.2", 2}},
|
||||
{"VASMTimestampEof", {CLEAR_ON_MANAGER_START, STRING, "0", "0", 2}},
|
||||
{"VEgoStarting", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"VEgoStartingStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"VEgoStopping", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"VEgoStoppingStock", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
|
||||
{"VeryLongCancelButtonControl", {PERSISTENT, INT, "6", "0", 2}},
|
||||
{"VeryLongDistanceButtonControl", {PERSISTENT, INT, "6", "0", 2}},
|
||||
{"VeryLongModeButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"VeryLongStarButtonControl", {PERSISTENT, INT, "0", "0", 2}},
|
||||
{"VoltSNG", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"GMAutoHold", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"VoltOnePedalMode", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"ToyotaAutoHold", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"WarningImmediateVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"WarningSoftVolume", {PERSISTENT, INT, "101", "101", 2}},
|
||||
{"WeatherPresets", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"VeryLongCancelButtonControl", {PERSISTENT, INT, "6", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"VeryLongDistanceButtonControl", {PERSISTENT, INT, "6", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"VeryLongModeButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"VeryLongStarButtonControl", {PERSISTENT, INT, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"VoltSNG", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"JeepBrakeHold", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"GMAutoHold", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"VoltOnePedalMode", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"ToyotaAutoHold", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"WarningImmediateVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"WarningSoftVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}},
|
||||
{"WeatherPresets", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"WeatherToken", {PERSISTENT | DONT_LOG, STRING, "", "", 2}},
|
||||
{"WheelControls", {PERSISTENT, STRING, "", "", 2}},
|
||||
{"WheelIcon", {PERSISTENT, STRING, "stock", "stock", 0}},
|
||||
{"WheelSpeed", {PERSISTENT, BOOL, "0", "0", 2}},
|
||||
{"WheelSpeed", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}},
|
||||
{"WheelToDownload", {CLEAR_ON_MANAGER_START, STRING, "", ""}},
|
||||
};
|
||||
|
||||
+1104
-855
File diff suppressed because it is too large
Load Diff
@@ -32,6 +32,10 @@ cdef extern from "common/params.h":
|
||||
JSON
|
||||
BYTES
|
||||
|
||||
cdef enum ParamSettingsTier:
|
||||
SETTINGS_SIMPLE
|
||||
SETTINGS_ADVANCED
|
||||
|
||||
cdef cppclass c_Params "Params":
|
||||
c_Params(string, bool) except + nogil
|
||||
string get(string, bool) nogil
|
||||
@@ -55,6 +59,8 @@ cdef extern from "common/params.h":
|
||||
|
||||
int getTuningLevel(string) nogil
|
||||
|
||||
ParamSettingsTier getSettingsTier(string) nogil
|
||||
|
||||
PYTHON_2_CPP = {
|
||||
(str, STRING): lambda v: v,
|
||||
(builtins.bool, BOOL): lambda v: "1" if v else "0",
|
||||
@@ -255,3 +261,6 @@ cdef class Params:
|
||||
cdef string k = self.check_key(key)
|
||||
cdef optional[int] level = self.p.getTuningLevel(k)
|
||||
return level.value() if level.has_value() else 0
|
||||
|
||||
def get_settings_tier(self, key):
|
||||
return self.p.getSettingsTier(self.check_key(key))
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,34 @@
|
||||
from pathlib import Path
|
||||
|
||||
from openpilot.common import file_chunker
|
||||
|
||||
|
||||
def test_chunked_stream_round_trip(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(file_chunker, "CHUNK_SIZE", 7)
|
||||
path = tmp_path / "artifact.pkl"
|
||||
payload = b"a model artifact spanning several chunks"
|
||||
path.write_bytes(payload)
|
||||
|
||||
targets = file_chunker.get_chunk_targets(path, len(payload))
|
||||
file_chunker.chunk_file(path, targets)
|
||||
|
||||
assert file_chunker.file_chunked_exists(path)
|
||||
assert file_chunker.read_file_chunked(path) == payload
|
||||
with file_chunker.open_file_chunked(path) as stream:
|
||||
assert stream.read(9) + stream.read() == payload
|
||||
|
||||
|
||||
def test_unchunked_stream_does_not_materialize_file(tmp_path, monkeypatch):
|
||||
path = tmp_path / "fat.onnx"
|
||||
payload = b"large model read through bounded buffers"
|
||||
path.write_bytes(payload)
|
||||
original_read_bytes = Path.read_bytes
|
||||
|
||||
def reject_whole_file_read(candidate):
|
||||
if candidate == path:
|
||||
raise AssertionError("streaming must not call Path.read_bytes()")
|
||||
return original_read_bytes(candidate)
|
||||
|
||||
monkeypatch.setattr(Path, "read_bytes", reject_whole_file_read)
|
||||
with file_chunker.open_file_chunked(path) as stream:
|
||||
assert stream.read() == payload
|
||||
@@ -25,3 +25,13 @@ TEST_CASE("params_nonblocking_put") {
|
||||
REQUIRE(p.get(name) == "1");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("settings_tier_is_independent_from_tuning_level") {
|
||||
Params params;
|
||||
|
||||
REQUIRE(params.getSettingsTier("AlwaysOnLateral") == SETTINGS_SIMPLE);
|
||||
REQUIRE(params.getTuningLevel("AlwaysOnLateral") == 0);
|
||||
REQUIRE(params.getSettingsTier("HumanLaneChanges") == SETTINGS_SIMPLE);
|
||||
REQUIRE(params.getTuningLevel("HumanLaneChanges") == 2);
|
||||
REQUIRE(params.getSettingsTier("AdvancedLateralTune") == SETTINGS_ADVANCED);
|
||||
}
|
||||
|
||||
@@ -50,11 +50,8 @@ class TestParams:
|
||||
assert self.params.get("CarParams", block=True) == b"test"
|
||||
|
||||
def test_params_unknown_key_fails(self):
|
||||
with pytest.raises(UnknownKeyName):
|
||||
self.params.get("swag")
|
||||
|
||||
with pytest.raises(UnknownKeyName):
|
||||
self.params.get_bool("swag")
|
||||
assert self.params.get("swag") is None
|
||||
assert not self.params.get_bool("swag")
|
||||
|
||||
with pytest.raises(UnknownKeyName):
|
||||
self.params.put("swag", "abc")
|
||||
|
||||
+22
-2
@@ -8,6 +8,15 @@ from pathlib import Path
|
||||
import pytest
|
||||
|
||||
|
||||
try:
|
||||
int(os.environ.get("DEBUG", "0"))
|
||||
except ValueError:
|
||||
os.environ["DEBUG"] = "0"
|
||||
|
||||
if platform.system() == "Darwin":
|
||||
os.environ["SP_HEADLESS_TEST"] = "1"
|
||||
|
||||
|
||||
def _prepend_host_pytest_runtime() -> None:
|
||||
if platform.system() != "Darwin" or os.getenv("SP_DISABLE_HOST_PYTEST_REDIRECT") == "1":
|
||||
return
|
||||
@@ -89,8 +98,7 @@ def openpilot_function_fixture(request):
|
||||
# ensure the test doesn't change the prefix
|
||||
assert "OPENPILOT_PREFIX" in os.environ and prefix == os.environ["OPENPILOT_PREFIX"]
|
||||
|
||||
# cleanup any started processes
|
||||
manager.manager_cleanup()
|
||||
manager.manager_cleanup()
|
||||
|
||||
# some processes disable gc for performance, re-enable here
|
||||
if not gc.isenabled():
|
||||
@@ -118,6 +126,9 @@ def tici_setup_fixture(request, openpilot_function_fixture):
|
||||
@pytest.hookimpl(tryfirst=True)
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
skipper = pytest.mark.skip(reason="Skipping tici test on PC")
|
||||
darwin_fake_event_skipper = pytest.mark.skip(reason="Fake socket events are unavailable on macOS")
|
||||
darwin_tmpfs_skipper = pytest.mark.skip(reason="tmpfs mount tests require Linux")
|
||||
darwin_proxy_skipper = pytest.mark.skip(reason="Athena local proxy socket test requires Linux select semantics")
|
||||
for item in items:
|
||||
if "tici" in item.keywords:
|
||||
if not TICI:
|
||||
@@ -130,6 +141,15 @@ def pytest_collection_modifyitems(config, items):
|
||||
class_property_value = getattr(item.cls, class_property_name)
|
||||
item.add_marker(pytest.mark.xdist_group(class_property_value))
|
||||
|
||||
if platform.system() == "Darwin" and item.path.name in ("test_fuzzy.py", "test_processes.py") and "process_replay" in item.path.parts:
|
||||
item.add_marker(darwin_fake_event_skipper)
|
||||
|
||||
if platform.system() == "Darwin" and item.path.name == "test_git.py" and "updated" in item.path.parts:
|
||||
item.add_marker(darwin_tmpfs_skipper)
|
||||
|
||||
if platform.system() == "Darwin" and item.name == "test_start_local_proxy" and "athena" in item.path.parts:
|
||||
item.add_marker(darwin_proxy_skipper)
|
||||
|
||||
|
||||
@pytest.hookimpl(trylast=True)
|
||||
def pytest_configure(config):
|
||||
|
||||
@@ -231,13 +231,17 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
|SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=SEAT Ateca 2016-23">Buy Here</a></sub></details>|||
|
||||
|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=SEAT Leon 2014-20">Buy Here</a></sub></details>|||
|
||||
|Subaru|Ascent 2019-21|All[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Ascent 2019-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||
|Subaru|Ascent 2023|All[<sup>6</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru D connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Ascent 2023">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Crosstrek 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||
|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Crosstrek 2020-23">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||
|Subaru|Crosstrek 2025|All[<sup>6</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru D connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Crosstrek 2025">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||
|Subaru|Forester 2019-21|All[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Forester 2019-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||
|Subaru|Forester 2022-24|All[<sup>6</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru C connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Forester 2022-24">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||
|Subaru|Impreza 2017-19|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Impreza 2017-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||
|Subaru|Impreza 2020-22|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Impreza 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||
|Subaru|Legacy 2020-22|All[<sup>6</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru B connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Legacy 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||
|Subaru|Outback 2020-22|All[<sup>6</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru B connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Outback 2020-22">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||
|Subaru|Outback 2023|All[<sup>6</sup>](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru D connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru Outback 2023">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||
|Subaru|XV 2018-19|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru XV 2018-19">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|<a href="https://youtu.be/Agww7oE1k-s?t=26" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>||
|
||||
|Subaru|XV 2020-21|EyeSight Driver Assistance[<sup>6</sup>](#footnotes)|openpilot available[<sup>1,7</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 Subaru A connector<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Subaru XV 2020-21">Buy Here</a></sub></details><details><summary>Tools</summary><sub>- 1 Pry Tool<br>- 1 Socket Wrench 8mm or 5/16" (deep)</sub></details>|||
|
||||
|Škoda|Fabia 2022-23[<sup>13</sup>](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,14</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 OBD-C cable (2 ft)<br>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma four<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Škoda Fabia 2022-23">Buy Here</a></sub></details>[<sup>15</sup>](#footnotes)|||
|
||||
|
||||
+31
-1
@@ -6,7 +6,7 @@ This workflow rebuilds StarPilot driving and driver-monitoring artifacts for the
|
||||
|
||||
- The supported build device is `comma@192.168.3.110`.
|
||||
- Never run these commands against `192.168.3.109`.
|
||||
- Do not compile normal and big-GPU artifacts together. This workflow builds normal QCOM artifacts only.
|
||||
- Normal artifacts target QCOM. External-GPU artifacts must be compiled explicitly and tagged in the manifest.
|
||||
- Keep source ONNX files and compiled PKLs on the T5 workspace, not the comma.
|
||||
|
||||
## Workspace
|
||||
@@ -82,6 +82,34 @@ The lower-level device compiler also supports direct use:
|
||||
./models --model deeprl3v2 --input-format supercombo --version v15
|
||||
```
|
||||
|
||||
For a model that cannot run on the device GPU, compile with the USB AMD GPU attached:
|
||||
|
||||
```bash
|
||||
./models --lebowski --gpu
|
||||
```
|
||||
|
||||
The ASM2464PD bridge must run the current tinygrad custom firmware from
|
||||
https://github.com/tinygrad/asm2464pd-firmware. Its USB product string starts
|
||||
with `custom`; the legacy `USB 3.2 PCIe TinyEnclosure` patch is not compatible
|
||||
with comma's current external-GPU runtime. Firmware flashing is a separate,
|
||||
explicit hardware setup step and StarPilot never performs it automatically.
|
||||
|
||||
The dynamic flag (`--lebowski` above) sets the output and manifest model ID;
|
||||
when only one source model is staged, its ONNX filename does not need to match
|
||||
that ID. Input format and behavior version are inferred. `--external-gpu`
|
||||
remains available as a compatibility alias for `--gpu`.
|
||||
|
||||
This emits a streaming out-of-band pickle and keeps QCOM available for camera warps. Its manifest entry must include:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "lebowski",
|
||||
"uses_external_gpu": true
|
||||
}
|
||||
```
|
||||
|
||||
Only tagged models activate the external GPU. If the GPU or artifact is unavailable, runtime falls back to the built-in model; all untagged models retain the existing QCOM path.
|
||||
|
||||
`--version` records behavioral semantics only. It does not change artifact layout.
|
||||
|
||||
If the compiled PKL exceeds 100 MiB, `./models` automatically keeps the full
|
||||
@@ -139,6 +167,8 @@ The generator preserves existing IDs and behavioral metadata and adds
|
||||
Repository-hosted multipart files are discovered by naming convention, so no
|
||||
size, hash, format, or part-count metadata is required.
|
||||
|
||||
`uses_external_gpu` is optional and defaults to `false`.
|
||||
|
||||
## Runtime Verification
|
||||
|
||||
Compilation validates JIT capture/replay, pickle round-trip, finite outputs, metadata slices, and both camera warps. Before release:
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 88 KiB |
Binary file not shown.
@@ -0,0 +1,102 @@
|
||||
# Contribute to StarPilot
|
||||
|
||||
Contributions are welcome. StarPilot is driver-assistance software, so changes should be focused, testable, and safe for vehicles outside the change's intended scope.
|
||||
|
||||
## Pull requests
|
||||
|
||||
Open all pull requests against the **`Dom` branch**. `Dom` is StarPilot's testing and integration branch; do not target the release branch directly.
|
||||
|
||||
A pull request should:
|
||||
|
||||
* have one clear purpose and contain only changes needed for that purpose;
|
||||
* explain what changed, why it changed, and how it was tested;
|
||||
* link the relevant issue or feedback item when one exists;
|
||||
* avoid unrelated cleanup, refactors, dependency changes, and formatting churn; and
|
||||
* pass the existing tests and checks.
|
||||
|
||||
Keep commits reviewable and update documentation when behavior or configuration changes. If a change needs a large refactor, separate that work from the behavior change when practical.
|
||||
|
||||
### Vehicle-specific changes
|
||||
|
||||
Do your best to ensure a vehicle-specific change affects only the intended vehicle, platform, or brand. Prefer the narrowest appropriate condition instead of changing shared behavior for every vehicle.
|
||||
|
||||
Tests should prove both sides of that boundary:
|
||||
|
||||
* the affected vehicle receives the new or corrected behavior; and
|
||||
* an unaffected vehicle, platform, brand, or configuration retains the existing behavior.
|
||||
|
||||
Include the vehicle and hardware used for on-road or bench testing in the pull request. Hardware testing is valuable, but it does not replace an automated regression test when the behavior can be tested in code.
|
||||
|
||||
## Development environment
|
||||
|
||||
StarPilot keeps host-native development tools separate from device-target builds. Run the setup and development commands from the repository root.
|
||||
|
||||
Install the Python dependencies before starting:
|
||||
|
||||
```bash
|
||||
tools/install_python_dependencies.sh
|
||||
```
|
||||
|
||||
On Ubuntu, `tools/ubuntu_setup.sh` installs both the system and Python dependencies. The development tools also require `uv`.
|
||||
|
||||
Use `./dev` for host-native tools. It creates and reuses an isolated environment under `.host_runtime/`, keeping host build artifacts out of the working tree:
|
||||
|
||||
```bash
|
||||
./dev replay
|
||||
./dev cabana
|
||||
./dev plotjuggler
|
||||
./dev shell
|
||||
```
|
||||
|
||||
For desktop UI work, use `./c3`, `./c4`, or `./raybig`. These commands use the same isolated host environment.
|
||||
|
||||
Use `./build` when you need comma-compatible device artifacts. This is the expected validation for changes that affect compiled device code or runtime behavior:
|
||||
|
||||
```bash
|
||||
./build
|
||||
```
|
||||
|
||||
Device builds require Docker Desktop or Podman with Linux/aarch64 support and a configured comma sysroot. See the [laptop device-build guide](../how-to/laptop-device-build.md) for setup instructions and the [complete StarPilot development workflow](https://github.com/firestar5683/StarPilot/blob/Dom/tools/STARPILOT_DEVELOPMENT.md) for all host commands and troubleshooting.
|
||||
|
||||
## Code formatting
|
||||
|
||||
Match the style of the code around your change and do not reformat unrelated files. Python formatting and lint rules are defined in `pyproject.toml`; the project uses two-space indentation and Ruff.
|
||||
|
||||
Run Ruff on changed Python files while developing:
|
||||
|
||||
```bash
|
||||
ruff check path/to/changed_file.py
|
||||
ruff format --check path/to/changed_file.py
|
||||
```
|
||||
|
||||
Before submitting, run the repository lint checks from the project root:
|
||||
|
||||
```bash
|
||||
./scripts/lint/lint.sh
|
||||
```
|
||||
|
||||
## Testing standards
|
||||
|
||||
Every behavior change or bug fix should include focused automated tests when practical. Recent StarPilot tests favor small regression cases that construct the relevant state, exercise one behavior, and assert the exact result.
|
||||
|
||||
At minimum:
|
||||
|
||||
1. Add or update a test that would fail without the change.
|
||||
2. Cover important boundaries, modes, and disabled states related to the change.
|
||||
3. For vehicle-specific behavior, add a negative case showing the change does not bleed into an unaffected vehicle or configuration.
|
||||
4. Run the directly affected test module and the existing tests for the affected subsystem.
|
||||
5. Ensure the pull request passes all existing CI checks before it is ready to merge.
|
||||
|
||||
Run a focused test module with pytest:
|
||||
|
||||
```bash
|
||||
pytest path/to/test_file.py
|
||||
```
|
||||
|
||||
Run the full test suite when your development environment supports it:
|
||||
|
||||
```bash
|
||||
pytest
|
||||
```
|
||||
|
||||
If a test requires special hardware or cannot run in your environment, say so in the pull request and document the closest validation you completed.
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
This flow builds **device-target (`larch64`) binaries on your laptop** using a Linux/aarch64 container and a synced comma sysroot.
|
||||
|
||||
For the full StarPilot branch workflow, including host-native shorthand tools such as `./dev`, `./c3`, `./c4`, and `./raybig`, see [tools/STARPILOT_DEVELOPMENT.md](../../tools/STARPILOT_DEVELOPMENT.md).
|
||||
For the full StarPilot branch workflow, including host-native shorthand tools such as `./dev`, `./c3`, `./c4`, and `./raybig`, see the [StarPilot development guide](https://github.com/firestar5683/StarPilot/blob/Dom/tools/STARPILOT_DEVELOPMENT.md).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
|
||||
@@ -302,3 +302,112 @@ For temporal behavior on a saved frame directory or route extract, replay the ru
|
||||
```bash
|
||||
.venv/bin/python scripts/replay_speed_limit_vision.py .tmp/vision_iter/seg10_5fps --frames-fps 5
|
||||
```
|
||||
|
||||
The detector/classifier runtime is model-only by default. Use `--crop-ocr` with
|
||||
`evaluate_runtime_manifest.py` or `replay_route_runtime.py` only for an explicit
|
||||
legacy comparison. A model-only release must match reviewed-manifest accuracy
|
||||
and pass representative route replays at measured on-device cadence. Evaluate
|
||||
candidate recognition and temporal publish behavior separately: a correct
|
||||
single-frame candidate can still be suppressed by the history and speed-change
|
||||
confirmation policy.
|
||||
|
||||
Ignored review rows label the proposed crop, not the entire camera frame.
|
||||
Consequently, negative-window candidate and publish counts from
|
||||
`evaluate_reviewed_route_events.py` are an upper bound until the full frame is
|
||||
audited; another valid sign can be present outside the rejected crop. Use the
|
||||
per-row output and frame image to audit any regression delta before treating it
|
||||
as a runtime false positive.
|
||||
|
||||
## Promotion Gate
|
||||
|
||||
Do not promote a checkpoint from classifier validation accuracy alone. Export it
|
||||
to an isolated model directory and run the complete runtime pipeline against the
|
||||
reviewed positive, hard-negative, and failed-drive manifests. A candidate must
|
||||
preserve exact-value recall, avoid new wrong-value reads, and remain within the
|
||||
accepted false-positive budget before route replay.
|
||||
|
||||
Mine detector proposals that fool an integrated-reject classifier into a new
|
||||
reject class before retraining:
|
||||
|
||||
```bash
|
||||
.venv/bin/python scripts/speed_limit_vision/mine_classifier_reject_crops.py \
|
||||
--models-dir /path/to/candidate/models \
|
||||
--dataset /path/to/versioned/classifier \
|
||||
--manifest /path/to/reviewed-negative-manifest.csv
|
||||
```
|
||||
|
||||
Keep the resulting dataset version separate from the current training set. If a
|
||||
hard-negative retrain lowers reviewed recall, reject the checkpoint even when it
|
||||
improves aggregate validation accuracy or removes a known false positive.
|
||||
|
||||
## Active-Learning Review Pass
|
||||
|
||||
Keep parallel miners in separate directories and merge them only when their
|
||||
model and mining fingerprints match:
|
||||
|
||||
```bash
|
||||
.venv/bin/python scripts/speed_limit_vision/merge_manual_review_queues.py \
|
||||
/path/to/shard0 /path/to/shard1 /path/to/shard2 /path/to/shard3 \
|
||||
--output-dir /path/to/merged
|
||||
```
|
||||
|
||||
When rescanning with a new model, compare the fingerprinted queues before
|
||||
selecting another batch. The optional review output retains the full queue
|
||||
schema so it can be passed directly to the selector and review server:
|
||||
|
||||
```bash
|
||||
.venv/bin/python scripts/speed_limit_vision/compare_manual_review_queues.py \
|
||||
--before /path/to/baseline/manual_review_queue.csv \
|
||||
--after /path/to/candidate/manual_review_queue.csv \
|
||||
--output-csv /path/to/comparison.csv \
|
||||
--review-output /path/to/disagreements/manual_review_queue.csv
|
||||
|
||||
.venv/bin/python scripts/speed_limit_vision/select_manual_review_queue.py \
|
||||
--input /path/to/disagreements/manual_review_queue.csv \
|
||||
--output /path/to/review/manual_review_queue.csv \
|
||||
--max-rows 1200 \
|
||||
--min-seconds-per-route-speed 3
|
||||
```
|
||||
|
||||
The selector prioritizes value changes and gained/lost reads, balances routes
|
||||
and speed classes, and removes adjacent same-speed frames from one scene. Start
|
||||
the reviewer and import its labels without moving route media off the training
|
||||
volume:
|
||||
|
||||
```bash
|
||||
.venv/bin/python scripts/speed_limit_vision/serve_manual_review_queue.py \
|
||||
--manifest /path/to/review/manual_review_queue.csv \
|
||||
--port 8765
|
||||
|
||||
.venv/bin/python scripts/speed_limit_vision/import_manual_review_queue.py \
|
||||
--queue /path/to/review/manual_review_queue.csv
|
||||
```
|
||||
|
||||
## Re-mine the Route Backlog
|
||||
|
||||
Re-run the backlog after a candidate passes the reviewed-manifest and route
|
||||
replay gates. Use a model fingerprinted run so new pseudo-labels are staged next
|
||||
to, rather than merged into, the original route-mining data:
|
||||
|
||||
```bash
|
||||
.venv/bin/python scripts/speed_limit_vision/mine_route_training_samples.py \
|
||||
--workspace /Volumes/T5/starpilot_speed_limit/workspace/speed_limit_training_clean \
|
||||
--models-dir /path/to/promoted/models \
|
||||
--model-only \
|
||||
--run-id auto \
|
||||
--sample-every 2.0 \
|
||||
--transition-step 0.5 \
|
||||
--max-frames-per-route 720 \
|
||||
--max-positives-per-route 120 \
|
||||
--max-negatives-per-route 200
|
||||
```
|
||||
|
||||
The output is written under
|
||||
`staging/route_mining/model_<model-fingerprint>_run_<mining-fingerprint>/` with
|
||||
its own detector images, classifier labels, review manifest, and per-route
|
||||
completion state. The mining fingerprint includes the model-only mode,
|
||||
thresholds, sampling configuration, and relevant source code. Review and
|
||||
deduplicate that staged run before merging it into a training dataset. Never
|
||||
overwrite the canonical route samples or automatically train on every mined
|
||||
positive; map agreement and human review remain required because a stronger
|
||||
model can still reproduce its own mistakes at larger scale.
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
# I Taught My Comma to Read Speed Limit Signs
|
||||
|
||||
<video controls playsinline preload="metadata" poster="assets/speed-limit-vision-demo-poster.jpg" style="width: 100%; max-width: 1280px;">
|
||||
<source src="assets/speed-limit-vision-demo.mp4" type="video/mp4">
|
||||
</video>
|
||||
|
||||
[Watch the speed-limit vision demo](assets/speed-limit-vision-demo.mp4)
|
||||
|
||||
This whole project started because I was lazy.
|
||||
|
||||
If you’ve driven an HKG or Toyota vehicle in recent years, you’ve probably seen the dashboard automatically pick up speed limits using the camera. Coming from a car that didn't have this functionality, I was very jealous. Those cars can feed those speed limits directly into openpilot forks speed limit controllers. Mine couldn't.
|
||||
|
||||
The “solution” has been to manually add speed limits to Mapbox or OpenStreetMaps. It worked, but it was tedious. I live in rural Kansas, where my commute is almost entirely devoid of nerds contributing open source map data. I began mapping my commute when I first joined the project and within an hour said, “I ain't doin this”
|
||||
|
||||
We have an AI model that's good enough to drive our cars running on a smartphone chipset from years ago. How hard could it be to build one that just reads speed limit signs? I wanted StarPilot to look out the windshield, see a speed limit sign, and just know what it said.
|
||||
|
||||
That sounded simple enough.
|
||||
|
||||
It wasn’t.
|
||||
|
||||
The first prototype was basically held together with duct tape: I used all the public speed limit datasets I could find (glare and Lisa), some OpenCV, a little OCR, and a lot of wishful thinking. It could occasionally read a sign, but it missed 90% of them and produced enough false positives that you definitely wouldn’t want to trust it.
|
||||
|
||||
The important part wasn’t that it sucked - it was that it worked just well enough to start collecting better training data.
|
||||
|
||||
I run a fork of openpilot called StarPilot. We're heavily focused on tuning and testing wild ideas, which made my users the perfect candidates for this project.
|
||||
|
||||
Instead of relying on public datasets that barely resembled comma camera footage, StarPilot started collecting its own. Community members submitted routes from all over the country using bookmarks that were generated automatically when the model said "I think this might've been a sign," and every promising detection went through manual review. Eventually that grew into hundreds of gigabytes of real driving footage and thousands of carefully labeled speed limit signs.
|
||||
|
||||
From there it became an endless cycle:
|
||||
|
||||
- Train a better model.
|
||||
- Mine more routes.
|
||||
- Find more missed signs.
|
||||
- Label them.
|
||||
- Repeat.
|
||||
|
||||
The funny part is that training the neural network wasn’t actually the hardest problem.
|
||||
|
||||
The hard part was everything around it.
|
||||
|
||||
One of the biggest challenges wasn’t accuracy, it was speed. Every millisecond spent processing was another frame the model didn't have time to see. The faster it could process frames, the more chances it had to catch a sharp, readable speed limit sign before it disappeared. This was particularly imperative at night, when naturally camera frame rates drop alongside your chances of picking up a clear sign.
|
||||
|
||||
It’s slowly evolved from a proof of concept into a model that’s trained primarily on real comma footage instead of generic traffic sign datasets. It now runs entirely on-device, publishes vision-based speed limits directly into StarPilot, and no longer depends on OCR or manually maintaining map data.
|
||||
|
||||
And best of all…
|
||||
|
||||
While I'm too lazy to hand write hundreds of speed limit signs into OpenStreetMaps, this tool can now be used to automate the entire process, giving back to the open source mapping community that helps those in larger areas so well.
|
||||
|
||||
Give my vision speed limits a try, or help continue to build a better one than mine! All info and training stack is available on the StarPilot repo.
|
||||
|
||||
---
|
||||
|
||||
*For a full LLM style, more technical breakdown for those interested in the process, please read below:*
|
||||
|
||||
|
||||
## Teaching StarPilot to Read Speed Limit Signs
|
||||
|
||||
In March 2026, this project started with a practical question: could StarPilot use the road camera to recognize speed-limit signs, attach them to GPS data, and help fill gaps in OpenStreetMap?
|
||||
|
||||
The first answer was "probably." A clean 40 mph sign in recorded comma footage was large enough to detect and read. StarPilot already had access to the live road-camera stream through VisionIPC, ONNX models could run through OpenCV DNN, and the existing Speed Limit Controller already knew how to combine several sources. The pieces existed.
|
||||
|
||||
What did not exist was a model trained for this camera, a trustworthy dataset, an honest replay benchmark, or a runtime that could analyze enough frames without interfering with openpilot. Building those became the real project.
|
||||
|
||||
This is the story of how we went from a weak imported model that was barely useful for automatic bookmarks to a custom detector and classifier trained primarily from real comma footage, how community routes changed the quality of the data, and why model accuracy on a laptop turned out to be only half of the problem.
|
||||
|
||||
### The first prototype
|
||||
|
||||
The first live implementation used an imported Ultralytics-style checkpoint called `ayoubsa_best`. Its provenance was thin: the ONNX metadata identified it as a Kaggle-trained detector, but there was no training notebook or source dataset in the repository. More importantly, its classes were a poor fit for American roads. It knew speed limits in 10 mph increments, such as 20, 30, and 40, but not the common 25, 35, 45, 55, or 65 mph signs.
|
||||
|
||||
That led to a hybrid design. The model proposed a sign region, then lightweight OpenCV and OCR-like digit logic tried to read the crop. It was enough to prove the complete path:
|
||||
|
||||
1. Read the live road-camera stream.
|
||||
2. Search the right side of the frame for likely signs.
|
||||
3. Produce a candidate speed and confidence.
|
||||
4. Confirm it over time.
|
||||
5. Publish it as a selectable `Vision` source for the Speed Limit Controller.
|
||||
6. Save debug frames and bookmarks for later training.
|
||||
|
||||
It was not yet a good speed-limit reader. It missed most signs, produced false positives, and struggled badly with 5 mph increments. But it could occasionally find real signs, which meant it could help us collect the data needed to replace itself.
|
||||
|
||||
That bootstrap capability mattered more than its initial accuracy.
|
||||
|
||||
### Public data gave us a starting point
|
||||
|
||||
We chose a two-stage architecture early: one model would answer "where is the speed-limit sign and what kind is it?" while another would answer "what number is printed on it?" Detection and reading are related, but they are not the same task. Keeping them separate let the detector learn the general shape and location of a sign without needing a separate object class for every posted speed.
|
||||
|
||||
Three public U.S. traffic-sign datasets formed the initial training base:
|
||||
|
||||
- [LISA](https://cvrr.ucsd.edu/lisa-traffic-signs-dataset) provided U.S. road scenes, 47 sign types, and 7,855 annotations across 6,610 frames. Its annotations also included useful information such as occlusion and whether a sign belonged to a side road.
|
||||
- [GLARE](https://arxiv.org/abs/2209.08716) provided 2,157 U.S. traffic-sign images taken from 33 dashcam videos under strong sun glare. The paper itself demonstrated why mixed normal and glare training was important.
|
||||
- [ARTS](https://swshah.w3.uvm.edu/vail/datasets.php), the Automotive Repository of Traffic Signs, provided additional U.S. signs in easy, challenging, and video-log configurations.
|
||||
|
||||
The clean first pass imported 3,078 ARTS images with 3,139 relevant boxes. When a working LISA archive was found, it added 1,577 images and 1,680 boxes, including 15, 25, 35, 45, 55, and 65 mph classes. GLARE was downloaded selectively to avoid pulling a large collection of unrelated checkpoints.
|
||||
|
||||
|
||||
### More data made the first retrain worse
|
||||
|
||||
The first clean public-data retrain looked reasonable in conventional validation metrics and failed badly on real comma routes.
|
||||
|
||||
The stronger older model found a candidate in 9 of 39 bookmarked route windows. The clean public-data model found only 1 of 39. It also regressed the saved-frame suite. We restored the older model instead of promoting a checkpoint merely because it was newer or had better training curves.
|
||||
|
||||
The domain gap was larger than image dimensions suggested. LISA, GLARE, ARTS, and comma footage were all forward-looking road imagery, but they differed in lens distortion, mounting position, exposure, dynamic range, compression, motion blur, sign position, and the number of pixels available when a sign first appeared. Public data often contained a cleaner or more centered sign than the live comma pipeline would see.
|
||||
|
||||
This established a rule that guided every later pass: public data was useful for bootstrapping and preservation, but real comma footage had to dominate final training and evaluation.
|
||||
|
||||
### Turning StarPilot into a data engine
|
||||
|
||||
The next large improvement did not come from a new backbone. It came from collecting better data.
|
||||
|
||||
StarPilot gained an automatic training collector, automatic bookmarks, manual bookmarks, debug snapshots, route metadata, and tools to preserve full-resolution video segments. A route-bundling script verified that a contributed route was public, contained the required qlogs, rlogs, and `fcamera.hevc` files, and had the vision collector enabled before packaging it.
|
||||
|
||||
That allowed StarPilot users to contribute real driving data from different cars, cameras, roads, states, lighting conditions, and sign styles. Eventually, nearly 200 GB of zipped comma routes landed on disk. This was enormously more useful than another generic sign dataset because it matched the exact production domain:
|
||||
|
||||
- the same road camera and encoding path;
|
||||
- the same wide-angle geometry;
|
||||
- the same night exposure and motion blur;
|
||||
- the same right-shoulder sign placement;
|
||||
- the same live crop errors;
|
||||
- and the same hard negatives seen by the runtime detector.
|
||||
|
||||
The model could then scan old routes, find likely signs and mistakes, and generate another review queue. A better model could rescan the same backlog and find candidates the weaker model had never seen. Each iteration improved not only the deployed model, but also the quality of the next dataset.
|
||||
|
||||
### Human review was the quality control
|
||||
|
||||
Automatic labels were never trusted blindly. A map transition did not prove that a sign was visible, a bookmark did not prove that the best frame had been selected, and a confident model prediction did not prove that the crop contained the correct sign.
|
||||
|
||||
The manual review UI evolved around throughput. Typing a number labeled a regulatory sign. Single-key shortcuts marked advisory (`a`), school-zone (`s`), regulatory (`r`), uncertain (`u`), or ignored/not-a-sign (`i` or `x`) samples. Enter or Space accepted a prediction. Reviewers could also redraw a bad bounding box.
|
||||
|
||||
Those details affected model quality directly:
|
||||
|
||||
- A readable but slightly imperfect crop was useful.
|
||||
- A crop that showed only the minimum-speed portion of a 65/40 sign was not a valid 40 mph regulatory label.
|
||||
- Stop signs and empty crops became hard negatives.
|
||||
- Advisory signs were labeled explicitly, even though overall sign recall remained the product priority.
|
||||
- Uncertain night crops were retained separately instead of being treated as equally strong labels.
|
||||
- Repeated frames from the same sign track were deduplicated so one scene could not dominate the dataset.
|
||||
- Incorrectly large or shifted boxes were redrawn once the review tool exposed that capability.
|
||||
|
||||
One labeling ambiguity forced a larger re-review: earlier sessions had not made regulatory versus advisory state clear enough. Rather than preserve potentially poisoned labels, affected advisory candidates were put back into the queue under the corrected UI contract.
|
||||
|
||||
This was active learning in a practical form: spend human time on disagreements, weak reads, false publishes, missed signs, bad boxes, and rare conditions, not on thousands of easy duplicate frames.
|
||||
|
||||
By one later OCR-free promotion audit, the backlog covered 80 routes and 366,946 sampled frames. The model under audit produced 34,260 candidates for rescoring, while the canonical review database contained 5,383 unique human-reviewed crops: 593 positive reads and 4,146 usable crop-level rejects, with the remainder reserved for uncertain or otherwise excluded decisions. The raw route collection was large, but those reviewed examples were the part that made it trustworthy.
|
||||
|
||||
### The architecture that survived
|
||||
|
||||
The current production path is model-only. OCR remains in the source tree for legacy compatibility and offline experiments, but both full-frame OCR and crop OCR are disabled for the active detector/classifier pipeline.
|
||||
|
||||
The deployed stack contains:
|
||||
|
||||
- A YOLO11n-derived detector exported as a fixed `256x256` ONNX model. It proposes regulatory, advisory, and school-zone speed-limit signs.
|
||||
- A YOLO11n-classifier-derived `128x128` ONNX model. It predicts 15 through 75 mph in 5 mph increments and includes a learned reject class for crops that are not valid speed-limit reads.
|
||||
- Runtime logic that evaluates several crop expansions, combines their support, applies geometry and sign-type checks, and confirms lower-confidence changes over multiple frames.
|
||||
- A right-side region of interest, which reduces wasted work while retaining the part of U.S. road scenes where most applicable signs appear.
|
||||
|
||||
The detector is about 9.9 MB and the classifier about 5.9 MB. They remain separate ONNX files intentionally. Joining the graphs into one file would make packaging look tidier, but it would not eliminate the detector or classifier computation. Separate models also let us change detector resolution, classifier training, reject behavior, and crop strategy independently.
|
||||
|
||||
A third standalone reject model was tested and discarded. It added another inference pass without measurably improving the reviewed positives or surviving false positive. Folding rejection into the value classifier produced a better cost/accuracy tradeoff.
|
||||
|
||||
OCR was removed only after the model-only path could pass the preservation suites on its own. In the decisive audit, model consensus improved the targeted 20 mph events from 1/14 to 11/14 and the broader targeted set from 21/36 to 27/36 without regressing the established legacy suites. At that point, OCR's occasional rescue was no longer worth the latency, extra failure modes, and frames lost while the device waited for it to finish.
|
||||
|
||||
Direct-value detectors, smaller input sizes, MobileNet-style classifiers, optical-flow tracking, and multiple crop strategies were also tested. Some looked attractive on desktop metrics and failed where it mattered. In particular, a 224-pixel detector was faster but dropped the hometown 20 mph suite from 14/14 to 9/14. The 256-pixel detector remained the better production choice.
|
||||
|
||||
### Frame rate is part of accuracy
|
||||
|
||||
The most important evaluation lesson came from a route with seven manually bookmarked signs.
|
||||
|
||||
An early offline replay sampled the video at 5 fps and found several signs. The actual comma found almost none. The model files matched and the vision process was running, so the discrepancy initially looked mysterious.
|
||||
|
||||
The replay had been landing on lucky frames.
|
||||
|
||||
The camera produced 25 frames per second, but the live process only attempted inference every 0.4 seconds. When we replayed all 25 source frames while enforcing that real inference gate, the local result fell to 0 of 7, matching the drive. Halving the interval to 0.2 seconds moved the same honest replay to 5 of 7 candidate windows and 3 of 7 actual publishes.
|
||||
|
||||
From then on, a model was not evaluated only as a collection of independent images. Route replay had to include:
|
||||
|
||||
- source-frame timing;
|
||||
- the measured detector and classifier cost;
|
||||
- steady and follow-up inference intervals;
|
||||
- temporal confirmation rules;
|
||||
- CPU backoff;
|
||||
- and the distinction between a candidate and a value actually published to the UI or controller.
|
||||
|
||||
This distinction explained many arguments over numbers. A candidate means the detector and reader noticed something in a sign window. A publish means the temporal and confidence gates accepted it as the live speed limit. A model can read a sign correctly once and still fail to publish if a weak read requires a second frame that the device never processes.
|
||||
|
||||
Later-frame mining showed why this mattered. One pass revisited 405 known sign events and extracted 939 clearer frames from later in their tracks. Combined with track-aware classifier training and a reduction from four classifier crops to three, the measured runtime suite improved from 247 to 265 correct publications, including a gain from 211 to 229 in the priority 30-65 mph range. Better frame selection and less per-frame work helped at the same time.
|
||||
|
||||
### Making it fit beside openpilot
|
||||
|
||||
The comma is already running camera processing, the main driving models, localization, controls, the UI, and vehicle communication. A speed-limit sidecar cannot assume an idle CPU.
|
||||
|
||||
The early hybrid pipeline could take roughly 1.5 seconds per frame when the detector, crop reader, and OCR paths all fired. At that cadence, signs could pass through their brief readable window without ever being analyzed. It also added enough CPU pressure to coincide with camera/model frame-sync errors and temporary `locationd` alerts on some night drives.
|
||||
|
||||
Changing process priority was not the solution. The daemon was already running at a very low scheduling priority. The useful changes were to remove OCR from production, restrict detection to the right-side ROI, shrink the models carefully, avoid the extra reject-model pass, add device-load backoff, and use a faster follow-up cadence after a candidate appeared.
|
||||
|
||||
The current runtime requests a 0.15-second steady interval and a 0.10-second follow-up interval, but inference cost is the real limiter. On two measured routes, the complete process sustained about 1.77 to 1.81 inferences per second. The detector alone took roughly 0.43 to 0.44 seconds, and each classifier crop could add about 0.066 seconds. A sign may receive only 4 to 13 analyzed frames in a seven-second approach window.
|
||||
|
||||
This is why "run it at 10 Hz" is not a configuration change. The process can ask for 10 Hz, but it cannot start the next inference until the current one finishes.
|
||||
|
||||
### Debugging the system, not just the model
|
||||
|
||||
Several apparent model regressions were actually system problems.
|
||||
|
||||
|
||||
These failures led to better observability: model hashes, inference counts, interval reasons, detector and classifier timing, CPU backoff state, candidates, publications, and debug captures all became part of route analysis. We stopped asking only "is the model accurate?" and started asking "which exact model and runtime processed which exact frames, and what did it publish?"
|
||||
|
||||
### How we decide whether a model is better
|
||||
|
||||
The promotion process now uses several independent gates:
|
||||
|
||||
- Reviewed crop accuracy checks whether the classifier can read localized signs.
|
||||
- Hard-negative sets contain parking signs, traffic signals, truck restrictions, taillights, and other shapes that previously caused false reads.
|
||||
- A 100-event main route suite tests full candidate and publication behavior at measured comma cadence.
|
||||
- A separate 34-event held-out suite guards against tuning directly to the main set.
|
||||
- A hometown 20 mph suite protects a class that earlier models repeatedly missed.
|
||||
- Legacy suites ensure that old wins do not disappear.
|
||||
- Targeted night and glare replays test the conditions most likely to break normal validation assumptions.
|
||||
- On-device timing and live routes remain the final authority.
|
||||
|
||||
Many experiments were rejected despite improving one number. A glare-focused model lost broader recall. A traffic-signal hard-negative detector fixed one false positive and lost legitimate 35 mph signs. A more aggressive reject-classifier update reduced errors and then changed enough timing to expose different false publishes. Conservative weight interpolation often produced the safest result because it taught a narrow correction without erasing the representation that already worked.
|
||||
|
||||
The drive currently contains 87 detector experiment directories and 72 classifier experiment directories. Those are not 159 successive improvements. They are the record of resolution bake-offs, failed hard-negative passes, direct-value experiments, night/glare runs, integrated-reject models, temporal-crop training, and interpolations that mapped the boundaries of the system.
|
||||
|
||||
### Where accuracy is now
|
||||
|
||||
The current classifier, internally called `distilled-moonstone v3`, improved the deployed route suite without changing model size or runtime cost:
|
||||
|
||||
| Runtime suite | Previous tree | Current model |
|
||||
| --- | ---: | ---: |
|
||||
| Main route publishes | 78/100 | **92/100** |
|
||||
| Main wrong publishes | 0 | **0** |
|
||||
| Held-out publishes | 24/34 | **26/34** |
|
||||
| Hometown 20 mph signs | 14/14 | **14/14** |
|
||||
| Hard sun-glare check | 70 mph published | **70 mph published** |
|
||||
|
||||
These denominators are manually confirmed sign events, not arbitrary video frames. `92/100` means the full runtime pipeline published the correct value in 92 of 100 known sign windows while running with measured comma timing. It does not mean the model is 92 percent accurate on every road or every possible sign.
|
||||
|
||||
In ordinary daylight scenes, current performance is very good. The model is now reliable when signs are reasonably exposed and readable, while extreme image quality and limited frame opportunities remain the dominant failures.
|
||||
|
||||
The current result is also substantially better than the first public-data prototype, which missed nearly every sign on several routes and sometimes produced only a single useful event in an entire drive.
|
||||
|
||||
### The next hard barriers
|
||||
|
||||
The remaining misses are unusually informative. Of the 18 main-suite events the current model does not publish:
|
||||
|
||||
- 6 never produce a correct candidate at measured cadence.
|
||||
- 4 produce exactly one weak correct read, but cannot satisfy the two-read confirmation rule.
|
||||
- At an idealized 6.7 Hz cadence, the same model recovers 7 of those 10 publishes, reaching a theoretical 97/100, but also introduces one wrong publish.
|
||||
- The three stubborn cases are all 45 mph scenes: two remain unreadable even at ideal cadence, and one first reads 40 before seeing 45 too late.
|
||||
|
||||
That theoretical 97/100 is not a production accuracy claim. It is a diagnostic result showing that most remaining failures already contain a readable frame that the device does not process.
|
||||
|
||||
The next large improvement is therefore likely to come from throughput and temporal reuse, not from simply lowering confidence thresholds:
|
||||
|
||||
1. Make the detector materially faster without giving up the small-sign and 20 mph recall lost by the 224- and 192-pixel experiments.
|
||||
2. After a detector finds a sign, track or cheaply reclassify that crop across nearby frames instead of paying for another full detector pass every time.
|
||||
3. Investigate a more efficient inference backend or model architecture on comma hardware. Direct model microbenchmarks are useful, but the complete process and onroad CPU contention must remain the acceptance metric.
|
||||
4. Add targeted, human-reviewed sequences for the remaining 45 mph failures, night ghosting, distant signs, and direct-sun approaches.
|
||||
5. Improve sign applicability for minimum-speed, truck-only, school-zone, advisory, side-road, and exit-ramp signs without sacrificing the product's primary goal: finding normal regulatory speed limits.
|
||||
6. Keep model-fingerprinted backlog mining so every promoted model can rescan old routes into an isolated review set without silently replacing canonical labels.
|
||||
|
||||
Night remains difficult because the camera often produces fewer sharp frames, motion blur can create double digits, and reflective signs may be detected while they are still too distant to read. Glare is difficult for the opposite reason: contrast collapses and sign edges disappear into the sky. Both problems require sequence-level examples, not just one selected crop.
|
||||
|
||||
### What we learned
|
||||
|
||||
This was never one long training run. It was the construction of a data and evaluation system.
|
||||
|
||||
Public datasets made the first detector possible. A weak model made automatic collection possible. StarPilot users supplied the camera-native diversity that public data could not. Human review turned model proposals into trustworthy labels. Full-cadence replay exposed timing mistakes that image metrics hid. Live device telemetry kept CPU and process failures from being mistaken for neural-network failures. Conservative promotion gates preserved hard-won behavior while narrow experiments improved specific weaknesses.
|
||||
|
||||
The result is already useful and, in ordinary scenes, often impressively accurate. The path to the next jump is also clearer than it was at the beginning. We do not mainly need another giant pile of generic sign images. We need to process more of the good frames the comma already sees, teach the remaining hard sequences carefully, and continue judging the complete on-device system rather than the model in isolation.
|
||||
|
||||
That is the difference between training a traffic-sign classifier and building a speed-limit reader that works in a car.
|
||||
|
||||
### Technical references
|
||||
|
||||
- Runtime implementation: [`starpilot/system/speed_limit_vision.py`](../starpilot/system/speed_limit_vision.py)
|
||||
- Training runbook: [`docs/how-to/train-speed-limit-vision.md`](how-to/train-speed-limit-vision.md)
|
||||
- Training, mining, review, and evaluation tools: [`scripts/speed_limit_vision`](../scripts/speed_limit_vision)
|
||||
- Deployed model assets: [`starpilot/assets/vision_models`](../starpilot/assets/vision_models)
|
||||
+67
-3
@@ -4,7 +4,25 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
|
||||
|
||||
source "$DIR/launch_env.sh"
|
||||
|
||||
export SP_BOOT_TIMING_LOG="${SP_BOOT_TIMING_LOG:-/tmp/starpilot_boot_timing.log}"
|
||||
: > "$SP_BOOT_TIMING_LOG" 2>/dev/null || true
|
||||
SP_LAUNCH_LAST_SECONDS=$SECONDS
|
||||
|
||||
function sp_boot_timing_line {
|
||||
echo "$1"
|
||||
printf '%s\n' "$1" >> "$SP_BOOT_TIMING_LOG" 2>/dev/null || true
|
||||
}
|
||||
|
||||
function sp_launch_timing {
|
||||
local now=$SECONDS
|
||||
local delta=$((now - SP_LAUNCH_LAST_SECONDS))
|
||||
sp_boot_timing_line "SP_BOOT_TIMING launch $1 +${delta}s total=${now}s"
|
||||
SP_LAUNCH_LAST_SECONDS=$now
|
||||
}
|
||||
|
||||
function agnos_init {
|
||||
sp_launch_timing "agnos_init_start"
|
||||
|
||||
# TODO: move this to agnos
|
||||
sudo rm -f /data/etc/NetworkManager/system-connections/*.nmmeta
|
||||
|
||||
@@ -33,10 +51,17 @@ function agnos_init {
|
||||
# StarPilot variables
|
||||
sudo chmod 0777 /cache
|
||||
|
||||
sudo rm -f /data/misc/display/color_cal/color_cal /data/misc/display/color_cal/source.sha256
|
||||
|
||||
# Check if AGNOS update is required
|
||||
if [ $(< /VERSION) != "$AGNOS_VERSION" ]; then
|
||||
AGNOS_CURRENT_VERSION="$(< /VERSION)"
|
||||
AGNOS_UPDATE_REQUIRED=1
|
||||
for accepted_version in $AGNOS_ACCEPTED_VERSIONS; do
|
||||
if [ "$AGNOS_CURRENT_VERSION" = "$accepted_version" ]; then
|
||||
AGNOS_UPDATE_REQUIRED=0
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$AGNOS_UPDATE_REQUIRED" = "1" ]; then
|
||||
AGNOS_PY="$DIR/system/hardware/tici/agnos.py"
|
||||
MANIFEST="$DIR/system/hardware/tici/agnos.json"
|
||||
if $AGNOS_PY --verify $MANIFEST; then
|
||||
@@ -44,9 +69,13 @@ function agnos_init {
|
||||
fi
|
||||
$DIR/system/hardware/tici/updater $AGNOS_PY $MANIFEST
|
||||
fi
|
||||
|
||||
sp_launch_timing "agnos_init_done"
|
||||
}
|
||||
|
||||
function launch {
|
||||
sp_launch_timing "launch_start"
|
||||
|
||||
# Remove orphaned git lock if it exists on boot
|
||||
[ -f "$DIR/.git/index.lock" ] && rm -f $DIR/.git/index.lock
|
||||
|
||||
@@ -83,30 +112,37 @@ function launch {
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
sp_launch_timing "overlay_check_done"
|
||||
|
||||
# handle pythonpath
|
||||
ln -sfn $(pwd) /data/pythonpath
|
||||
export BASEDIR="$DIR"
|
||||
export PYTHONPATH="$DIR/starpilot/third_party:$PWD"
|
||||
sp_launch_timing "pythonpath_done"
|
||||
|
||||
# hardware specific init
|
||||
if [ -f /AGNOS ]; then
|
||||
agnos_init
|
||||
fi
|
||||
sp_launch_timing "hardware_init_done"
|
||||
|
||||
# write tmux scrollback to a file
|
||||
tmux capture-pane -pq -S-1000 > /tmp/launch_log
|
||||
sp_launch_timing "capture_launch_log_done"
|
||||
|
||||
# start manager
|
||||
cd system/manager
|
||||
|
||||
sp_launch_timing "launch_param_migrations_start"
|
||||
if ! python3 ./launch_param_migrations.py; then
|
||||
echo "Launch param migrations failed; continuing boot."
|
||||
fi
|
||||
sp_launch_timing "launch_param_migrations_done"
|
||||
|
||||
# Bootstrap runtime (e.g. /usr/comma after reset/uninstall) must go straight
|
||||
# to manager/setup flow. Do not run StarPilot prebuilt checks/builds here.
|
||||
if [ "$DIR" = "/usr/comma" ] || [ ! -d "$DIR/.git" ]; then
|
||||
sp_launch_timing "bootstrap_manager_start"
|
||||
./manager.py
|
||||
while true; do sleep 1; done
|
||||
fi
|
||||
@@ -114,8 +150,29 @@ function launch {
|
||||
function prebuilt_runtime_compatible {
|
||||
python3 - <<'PY'
|
||||
import importlib
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import time
|
||||
|
||||
start = time.monotonic()
|
||||
last = start
|
||||
log_path = os.environ.get("SP_BOOT_TIMING_LOG")
|
||||
|
||||
def emit(line):
|
||||
print(line, flush=True)
|
||||
if log_path:
|
||||
try:
|
||||
with open(log_path, "a") as f:
|
||||
f.write(line + "\n")
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def log_step(label):
|
||||
global last
|
||||
now = time.monotonic()
|
||||
emit(f"SP_BOOT_TIMING prebuilt_compat {label} +{now - last:.3f}s total={now - start:.3f}s")
|
||||
last = now
|
||||
|
||||
mods = [
|
||||
"openpilot.common.params_pyx",
|
||||
@@ -133,6 +190,7 @@ for mod in mods:
|
||||
except Exception as e:
|
||||
print(f"Prebuilt compatibility failure in {mod}: {e}", file=sys.stderr)
|
||||
raise
|
||||
log_step(f"import:{mod}")
|
||||
|
||||
repo_root = Path.cwd().parents[1]
|
||||
required_files = [
|
||||
@@ -152,6 +210,7 @@ required_files = [
|
||||
for path in required_files:
|
||||
if not path.is_file():
|
||||
raise FileNotFoundError(f"Missing prebuilt runtime artifact: {path}")
|
||||
log_step("required_files")
|
||||
PY
|
||||
}
|
||||
|
||||
@@ -160,14 +219,19 @@ PY
|
||||
USE_PREBUILT=$(tr -d '\n' < /data/params/d/UsePrebuilt)
|
||||
fi
|
||||
|
||||
sp_launch_timing "prebuilt_decision_done"
|
||||
if [ "$USE_PREBUILT" = "1" ] && [ -f $DIR/prebuilt ] && ! prebuilt_runtime_compatible; then
|
||||
echo "Prebuilt runtime artifacts are incompatible on this device; rebuilding locally."
|
||||
USE_PREBUILT=0
|
||||
fi
|
||||
sp_launch_timing "prebuilt_compat_done"
|
||||
|
||||
if [ "$USE_PREBUILT" != "1" ] || [ ! -f $DIR/prebuilt ]; then
|
||||
sp_launch_timing "build_start"
|
||||
./build.py
|
||||
sp_launch_timing "build_done"
|
||||
fi
|
||||
sp_launch_timing "manager_start"
|
||||
./manager.py
|
||||
|
||||
# if broken, keep on screen error
|
||||
|
||||
+5
-1
@@ -21,7 +21,11 @@ fi
|
||||
export QCOM_PRIORITY=12
|
||||
|
||||
if [ -z "$AGNOS_VERSION" ]; then
|
||||
export AGNOS_VERSION="12.8.17"
|
||||
export AGNOS_VERSION="12.8.28"
|
||||
fi
|
||||
|
||||
if [ -z "$AGNOS_ACCEPTED_VERSIONS" ]; then
|
||||
export AGNOS_ACCEPTED_VERSIONS="$AGNOS_VERSION"
|
||||
fi
|
||||
|
||||
export STAGING_ROOT="/data/safe_staging"
|
||||
|
||||
+2
-2
@@ -32,10 +32,10 @@ nav:
|
||||
- What is a car port?: car-porting/what-is-a-car-port.md
|
||||
- Porting a car brand: car-porting/brand-port.md
|
||||
- Porting a car model: car-porting/model-port.md
|
||||
- Contributing:
|
||||
- Contribute:
|
||||
- Contributing Guide: contributing/contribute.md
|
||||
- Roadmap: contributing/roadmap.md
|
||||
#- Architecture: contributing/architecture.md
|
||||
- Contributing Guide →: https://github.com/commaai/openpilot/blob/master/docs/CONTRIBUTING.md
|
||||
- Links:
|
||||
- Blog →: https://blog.comma.ai
|
||||
- Bounties →: https://comma.ai/bounties
|
||||
|
||||
@@ -21,6 +21,9 @@ static size_t fnv1a_hash(const std::string &str) {
|
||||
|
||||
static std::string namespaced_endpoint(std::string endpoint) {
|
||||
const char *namespace_env = std::getenv("OPENPILOT_ZMQ_NAMESPACE");
|
||||
if (namespace_env == nullptr || namespace_env[0] == '\0') {
|
||||
namespace_env = std::getenv("OPENPILOT_PREFIX");
|
||||
}
|
||||
if (namespace_env != nullptr && namespace_env[0] != '\0') {
|
||||
endpoint = std::string(namespace_env) + ":" + endpoint;
|
||||
}
|
||||
|
||||
@@ -15,10 +15,6 @@ const bool MUST_USE_ZMQ = false;
|
||||
|
||||
bool messaging_use_zmq(){
|
||||
if (std::getenv("ZMQ") || MUST_USE_ZMQ) {
|
||||
if (std::getenv("OPENPILOT_PREFIX")) {
|
||||
std::cerr << "OPENPILOT_PREFIX not supported with ZMQ backend\n";
|
||||
assert(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
Binary file not shown.
@@ -61,6 +61,7 @@ class TestVisionIpc:
|
||||
recv_buf = self.client.recv()
|
||||
assert recv_buf is not None
|
||||
assert recv_buf.data.view('<i4')[0] == 1234
|
||||
assert recv_buf.frame_id == 1337
|
||||
assert self.client.frame_id == 1337
|
||||
del self.client
|
||||
del self.server
|
||||
|
||||
@@ -30,6 +30,7 @@ cdef extern from "msgq/visionipc/visionbuf.h":
|
||||
size_t idx
|
||||
cl_mem buf_cl
|
||||
void set_frame_id(uint64_t id)
|
||||
uint64_t get_frame_id()
|
||||
|
||||
cdef extern from "msgq/visionipc/visionipc.h":
|
||||
struct VisionIpcBufExtra:
|
||||
|
||||
@@ -67,6 +67,10 @@ cdef class VisionBuf:
|
||||
def fd(self):
|
||||
return self.buf.fd
|
||||
|
||||
@property
|
||||
def frame_id(self):
|
||||
return self.buf.get_frame_id()
|
||||
|
||||
|
||||
cdef class VisionIpcServer:
|
||||
cdef cppVisionIpcServer * server
|
||||
|
||||
Binary file not shown.
@@ -19,7 +19,7 @@ test:
|
||||
run: codespell {files} -L tge,stdio -S *.dbc
|
||||
files: git ls-tree -r HEAD --name-only
|
||||
cpplint:
|
||||
run: cpplint --exclude=opendbc/safety/tests/misra/cppcheck/ --exclude=opendbc/can/*_pyx.cpp --recursive --quiet --counting=detailed --linelength=240 --filter=-build,-legal,-readability,-runtime,-whitespace,+build/include_subdir,+build/forward_decl,+build/include_what_you_use,+build/deprecated,+whitespace/comma,+whitespace/line_length,+whitespace/empty_if_body,+whitespace/empty_loop_body,+whitespace/empty_conditional_body,+whitespace/forcolon,+whitespace/parens,+whitespace/semicolon,+whitespace/tab,+readability/braces opendbc/
|
||||
run: cpplint --exclude=opendbc/safety/tests/misra/cppcheck/ --exclude=opendbc/safety/tests/.venv/ --exclude=opendbc/can/*_pyx.cpp --recursive --quiet --counting=detailed --linelength=240 --filter=-build,-legal,-readability,-runtime,-whitespace,+build/include_subdir,+build/forward_decl,+build/include_what_you_use,+build/deprecated,+whitespace/comma,+whitespace/line_length,+whitespace/empty_if_body,+whitespace/empty_loop_body,+whitespace/empty_conditional_body,+whitespace/forcolon,+whitespace/parens,+whitespace/semicolon,+whitespace/tab,+readability/braces opendbc/
|
||||
misra:
|
||||
run: opendbc/safety/tests/misra/test_misra.sh
|
||||
|
||||
|
||||
@@ -2,9 +2,28 @@ from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus, DT_CTRL
|
||||
from opendbc.car.lateral import apply_meas_steer_torque_limits
|
||||
from opendbc.car.chrysler import chryslercan
|
||||
from opendbc.car.chrysler.values import RAM_CARS, RAM_DT, CarControllerParams, ChryslerFlags, ChryslerStarPilotFlags
|
||||
from opendbc.car.chrysler.values import JEEPS, RAM_CARS, RAM_DT, CarControllerParams, ChryslerFlags, ChryslerSafetyFlags, ChryslerStarPilotFlags
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
|
||||
JEEP_BRAKE_HOLD_DEFAULT_DECEL = -2.0
|
||||
JEEP_BRAKE_HOLD_MIN_DECEL = -0.5
|
||||
JEEP_BRAKE_HOLD_MAX_DECEL = -3.0
|
||||
|
||||
|
||||
def clip_jeep_brake_hold_decel(decel: float) -> float:
|
||||
return max(JEEP_BRAKE_HOLD_MAX_DECEL, min(JEEP_BRAKE_HOLD_MIN_DECEL, float(decel)))
|
||||
|
||||
|
||||
def supports_jeep_brake_hold(CP, brake_hold_enabled: bool) -> bool:
|
||||
safety_cfg = getattr(CP, "safetyConfigs", ())
|
||||
safety_param = safety_cfg[0].safetyParam if safety_cfg else 0
|
||||
return (
|
||||
brake_hold_enabled and
|
||||
getattr(CP, "pcmCruise", False) and
|
||||
CP.carFingerprint in JEEPS and
|
||||
bool(safety_param & ChryslerSafetyFlags.JEEP_BRAKE_HOLD.value)
|
||||
)
|
||||
|
||||
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_names, CP):
|
||||
@@ -18,6 +37,8 @@ class CarController(CarControllerBase):
|
||||
|
||||
self.packer = CANPacker(dbc_names[Bus.pt])
|
||||
self.params = CarControllerParams(CP)
|
||||
self.jeep_brake_hold_decel = JEEP_BRAKE_HOLD_DEFAULT_DECEL
|
||||
self.last_das_3_counter = -1
|
||||
|
||||
def update(self, CC, CS, now_nanos, starpilot_toggles):
|
||||
can_sends = []
|
||||
@@ -82,6 +103,11 @@ class CarController(CarControllerBase):
|
||||
|
||||
can_sends.append(chryslercan.create_lkas_command(self.packer, self.CP, int(apply_torque), lkas_control_bit))
|
||||
|
||||
if supports_jeep_brake_hold(self.CP, getattr(starpilot_toggles, "jeep_brake_hold", False)):
|
||||
self.update_jeep_brake_hold(CC, CS, can_sends)
|
||||
elif getattr(CS, "brake_hold", False):
|
||||
CS.brake_hold = False
|
||||
|
||||
self.frame += 1
|
||||
|
||||
new_actuators = CC.actuators.as_builder()
|
||||
@@ -89,3 +115,43 @@ class CarController(CarControllerBase):
|
||||
new_actuators.torqueOutputCan = self.apply_torque_last
|
||||
|
||||
return new_actuators, can_sends
|
||||
|
||||
def update_jeep_brake_hold(self, CC, CS, can_sends):
|
||||
if not getattr(CS, "das_3", None):
|
||||
CS.brake_hold = False
|
||||
return
|
||||
|
||||
counter_changed = CS.das_3.get("COUNTER") != self.last_das_3_counter
|
||||
self.last_das_3_counter = CS.das_3.get("COUNTER")
|
||||
|
||||
if not CS.brake_hold and CS.cruise_active_actual and CS.acc_decelerating and CS.out.standstill:
|
||||
CS.brake_hold = True
|
||||
self.jeep_brake_hold_decel = JEEP_BRAKE_HOLD_DEFAULT_DECEL
|
||||
|
||||
driver_intervened = (
|
||||
CC.cruiseControl.cancel or
|
||||
CS.out.gasPressed or
|
||||
CS.out.brakePressed or
|
||||
not CS.forward_gear or
|
||||
not CS.out.standstill
|
||||
)
|
||||
if CS.brake_hold and driver_intervened:
|
||||
CS.brake_hold = False
|
||||
return
|
||||
|
||||
if not CS.brake_hold:
|
||||
return
|
||||
|
||||
if CS.cruise_active_actual:
|
||||
self.jeep_brake_hold_decel = clip_jeep_brake_hold_decel(
|
||||
min(self.jeep_brake_hold_decel, CS.das_3.get("ACC_DECEL", JEEP_BRAKE_HOLD_DEFAULT_DECEL))
|
||||
)
|
||||
return
|
||||
|
||||
counter_offset = 2 if counter_changed else 3
|
||||
can_sends.append(chryslercan.create_das_3_command(self.packer, counter_offset, self.jeep_brake_hold_decel, CS.das_3))
|
||||
|
||||
if self.frame % 10 == 0:
|
||||
can_sends.append(chryslercan.create_cruise_buttons(
|
||||
self.packer, CS.button_counter + 1, 0, CS.button_message, resume=True
|
||||
))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from cereal import custom
|
||||
from opendbc.can import CANDefine, CANParser
|
||||
from opendbc.car import Bus, create_button_events, structs
|
||||
from opendbc.car.chrysler.values import DBC, STEER_THRESHOLD, RAM_CARS, ChryslerStarPilotFlags
|
||||
from opendbc.car.chrysler.values import DBC, JEEPS, STEER_THRESHOLD, RAM_CARS, ChryslerStarPilotFlags
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
|
||||
@@ -29,6 +29,11 @@ class CarState(CarStateBase):
|
||||
self.button_message = "CRUISE_BUTTONS_ALT" if FPCP.flags & ChryslerStarPilotFlags.RAM_HD_ALT_BUTTONS else "CRUISE_BUTTONS"
|
||||
|
||||
self.lkas_button = 0
|
||||
self.brake_hold = False
|
||||
self.cruise_active_actual = False
|
||||
self.forward_gear = False
|
||||
self.acc_decelerating = False
|
||||
self.das_3 = {}
|
||||
|
||||
@staticmethod
|
||||
def get_lkas_button(pt_signals, is_ram: bool) -> bool:
|
||||
@@ -93,6 +98,13 @@ class CarState(CarStateBase):
|
||||
ret.cruiseState.standstill = cp_cruise.vl["DAS_3"]["ACC_STANDSTILL"] == 1
|
||||
ret.accFaulted = cp_cruise.vl["DAS_3"]["ACC_FAULTED"] != 0
|
||||
|
||||
if self.CP.carFingerprint in JEEPS:
|
||||
self.forward_gear = ret.gearShifter == structs.CarState.GearShifter.drive
|
||||
self.cruise_active_actual = ret.cruiseState.enabled
|
||||
self.acc_decelerating = cp_cruise.vl["DAS_3"]["ACC_DECEL"] < -0.5
|
||||
self.das_3 = dict(cp_cruise.vl["DAS_3"])
|
||||
ret.brakeHoldActive = self.brake_hold
|
||||
|
||||
if self.CP.carFingerprint in RAM_CARS:
|
||||
# Auto High Beam isn't Located in this message on chrysler or jeep currently located in 729 message
|
||||
self.auto_high_beam = cp_cam.vl["DAS_6"]['AUTO_HIGH_BEAM_ON']
|
||||
|
||||
@@ -73,6 +73,21 @@ def create_cruise_buttons(packer, frame, bus, button_message, cancel=False, resu
|
||||
return packer.make_can_msg(button_message, bus, values)
|
||||
|
||||
|
||||
def create_das_3_command(packer, counter_offset, brake_decel, das_3):
|
||||
values = das_3.copy()
|
||||
values["ACC_AVAILABLE"] = 1
|
||||
values["ACC_ACTIVE"] = 1
|
||||
values["ACC_GO"] = 0
|
||||
values["ACC_STANDSTILL"] = 0
|
||||
values["ACC_DECEL_REQ"] = 1
|
||||
values["ACC_DECEL"] = brake_decel
|
||||
values["ACC_BRK_PREP"] = 0
|
||||
values["ENGINE_TORQUE_REQUEST_MAX"] = 0
|
||||
values["GR_MAX_REQ"] = 2
|
||||
values["COUNTER"] = (das_3["COUNTER"] + counter_offset) % 0x10
|
||||
return packer.make_can_msg("DAS_3", 0, values)
|
||||
|
||||
|
||||
def chrysler_checksum(address: int, sig, d: bytearray) -> int:
|
||||
checksum = 0xFF
|
||||
for j in range(len(d) - 1):
|
||||
|
||||
@@ -3,7 +3,7 @@ from opendbc.car import get_safety_config, structs
|
||||
from opendbc.car.chrysler.carcontroller import CarController
|
||||
from opendbc.car.chrysler.carstate import CarState
|
||||
from opendbc.car.chrysler.radar_interface import RadarInterface
|
||||
from opendbc.car.chrysler.values import CAR, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags, ChryslerSafetyFlags
|
||||
from opendbc.car.chrysler.values import CAR, JEEPS, RAM_HD, RAM_DT, RAM_CARS, ChryslerFlags, ChryslerSafetyFlags
|
||||
from opendbc.car.interfaces import CarInterfaceBase
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.safetyConfigs[0].safetyParam |= ChryslerSafetyFlags.RAM_HD.value
|
||||
elif candidate in RAM_DT:
|
||||
ret.safetyConfigs[0].safetyParam |= ChryslerSafetyFlags.RAM_DT.value
|
||||
elif candidate in JEEPS:
|
||||
ret.safetyConfigs[0].safetyParam |= ChryslerSafetyFlags.JEEP_BRAKE_HOLD.value
|
||||
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
if candidate not in RAM_CARS:
|
||||
|
||||
@@ -12,6 +12,7 @@ Ecu = CarParams.Ecu
|
||||
class ChryslerSafetyFlags(IntFlag):
|
||||
RAM_DT = 1
|
||||
RAM_HD = 2
|
||||
JEEP_BRAKE_HOLD = 4
|
||||
|
||||
|
||||
class ChryslerFlags(IntFlag):
|
||||
@@ -140,6 +141,7 @@ STEER_THRESHOLD = 120
|
||||
RAM_DT = {CAR.RAM_1500_5TH_GEN, }
|
||||
RAM_HD = {CAR.RAM_HD_5TH_GEN, }
|
||||
RAM_CARS = RAM_DT | RAM_HD
|
||||
JEEPS = {CAR.JEEP_GRAND_CHEROKEE, CAR.JEEP_GRAND_CHEROKEE_2019}
|
||||
|
||||
|
||||
CHRYSLER_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
|
||||
|
||||
@@ -245,7 +245,6 @@ MIGRATION = {
|
||||
"KIA SELTOS 2021": HYUNDAI.KIA_SELTOS,
|
||||
"KIA SPORTAGE 5TH GEN": HYUNDAI.KIA_SPORTAGE_5TH_GEN,
|
||||
"KIA SPORTAGE 2026": HYUNDAI.KIA_SPORTAGE_2026,
|
||||
"KIA SPORTAGE HYBRID 2026": HYUNDAI.KIA_SPORTAGE_HEV_2026,
|
||||
"KIA SORENTO GT LINE 2018": HYUNDAI.KIA_SORENTO,
|
||||
"KIA SORENTO 4TH GEN": HYUNDAI.KIA_SORENTO_4TH_GEN,
|
||||
"KIA SORENTO 2024": HYUNDAI.KIA_SORENTO_2024,
|
||||
@@ -258,6 +257,8 @@ MIGRATION = {
|
||||
"KIA EV6 2025": HYUNDAI.KIA_EV6_2025,
|
||||
"KIA EV9 2025": HYUNDAI.KIA_EV9,
|
||||
"KIA CARNIVAL 4TH GEN": HYUNDAI.KIA_CARNIVAL_4TH_GEN,
|
||||
"KIA CARNIVAL 2025": HYUNDAI.KIA_CARNIVAL_2025,
|
||||
"KIA CARNIVAL HYBRID 4TH GEN": HYUNDAI.KIA_CARNIVAL_HEV_4TH_GEN,
|
||||
"GENESIS GV60 ELECTRIC 1ST GEN": HYUNDAI.GENESIS_GV60_EV_1ST_GEN,
|
||||
"GENESIS G70 2018": HYUNDAI.GENESIS_G70,
|
||||
"GENESIS G70 2020": HYUNDAI.GENESIS_G70_2020,
|
||||
|
||||
@@ -16,6 +16,11 @@ AVERAGE_ROAD_ROLL = 0.06 # ~3.4 degrees, 6% superelevation. higher actual roll
|
||||
MAX_LATERAL_ACCEL = ISO_LATERAL_ACCEL - (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL) # ~2.4 m/s^2
|
||||
|
||||
|
||||
def apply_ford_angle(desired_angle_deg: float, current_angle_deg: float) -> float:
|
||||
relative_angle = desired_angle_deg - current_angle_deg
|
||||
return float(np.clip(relative_angle, -5.8, 5.8))
|
||||
|
||||
|
||||
def anti_overshoot(apply_curvature, apply_curvature_last, v_ego):
|
||||
diff = 0.1
|
||||
tau = 5 # 5s smooths over the overshoot
|
||||
@@ -65,6 +70,7 @@ class CarController(CarControllerBase):
|
||||
self.CAN = fordcan.CanBus(CP)
|
||||
|
||||
self.apply_curvature_last = 0
|
||||
self.apply_angle_last = 0
|
||||
self.anti_overshoot_curvature_last = 0
|
||||
self.accel = 0.0
|
||||
self.gas = 0.0
|
||||
@@ -98,38 +104,62 @@ class CarController(CarControllerBase):
|
||||
can_sends.append(fordcan.create_button_msg(self.packer, self.CAN.camera, CS.buttons_stock_values, tja_toggle=True))
|
||||
|
||||
### lateral control ###
|
||||
# send steer msg at 20Hz
|
||||
if (self.frame % CarControllerParams.STEER_STEP) == 0:
|
||||
# Bronco and some other cars consistently overshoot curv requests
|
||||
# Apply some deadzone + smoothing convergence to avoid oscillations
|
||||
if self.CP.carFingerprint in (CAR.FORD_BRONCO_SPORT_MK1, CAR.FORD_F_150_MK14):
|
||||
self.anti_overshoot_curvature_last = anti_overshoot(actuators.curvature, self.anti_overshoot_curvature_last, CS.out.vEgoRaw)
|
||||
apply_curvature = self.anti_overshoot_curvature_last
|
||||
if self.CP.flags & FordFlags.LKA_STEERING:
|
||||
lka_active = CC.latActive and CS.lkas_available
|
||||
if lka_active:
|
||||
self.apply_angle_last = apply_ford_angle(actuators.steeringAngleDeg, CS.out.steeringAngleDeg)
|
||||
current_curvature = -CS.out.yawRate / max(CS.out.vEgoRaw, 0.1)
|
||||
self.apply_curvature_last = apply_ford_curvature_limits(actuators.curvature, self.apply_curvature_last, current_curvature,
|
||||
CS.out.vEgoRaw, 0., True, self.CP)
|
||||
else:
|
||||
apply_curvature = actuators.curvature
|
||||
self.apply_angle_last = 0.
|
||||
self.apply_curvature_last = 0.
|
||||
|
||||
# apply rate limits, curvature error limit, and clip to signal range
|
||||
current_curvature = -CS.out.yawRate / max(CS.out.vEgoRaw, 0.1)
|
||||
# Keep the stock LMC heartbeat present while steering through Lane_Assist_Data1.
|
||||
if (self.frame % CarControllerParams.STEER_STEP) == 0:
|
||||
can_sends.append(fordcan.create_lat_ctl_msg(self.packer, self.CAN, False, 0., 0., 0., 0.,
|
||||
stock_lmc=CS.lateral_motion_control))
|
||||
|
||||
self.apply_curvature_last = apply_ford_curvature_limits(apply_curvature, self.apply_curvature_last, current_curvature,
|
||||
CS.out.vEgoRaw, 0., CC.latActive, self.CP)
|
||||
if (self.frame % CarControllerParams.LKA_STEP) == 0:
|
||||
direction = 0
|
||||
if lka_active:
|
||||
direction = 2 if CS.out.steeringAngleDeg > 0 else 4
|
||||
ramp_type = 1 if abs(self.apply_angle_last) >= 5 else 0
|
||||
can_sends.append(fordcan.create_lka_msg(self.packer, self.CAN, active=lka_active, apply_angle=self.apply_angle_last,
|
||||
direction=direction, ramp_type=ramp_type, curvature=-self.apply_curvature_last))
|
||||
else:
|
||||
# send steer msg at 20Hz
|
||||
if (self.frame % CarControllerParams.STEER_STEP) == 0:
|
||||
# Bronco and some other cars consistently overshoot curv requests
|
||||
# Apply some deadzone + smoothing convergence to avoid oscillations
|
||||
if self.CP.carFingerprint in (CAR.FORD_BRONCO_SPORT_MK1, CAR.FORD_F_150_MK14):
|
||||
self.anti_overshoot_curvature_last = anti_overshoot(actuators.curvature, self.anti_overshoot_curvature_last, CS.out.vEgoRaw)
|
||||
apply_curvature = self.anti_overshoot_curvature_last
|
||||
else:
|
||||
apply_curvature = actuators.curvature
|
||||
|
||||
if self.CP.flags & FordFlags.CANFD:
|
||||
# TODO: extended mode
|
||||
# Ford uses four individual signals to dictate how to drive to the car. Curvature alone (limited to 0.02m/s^2)
|
||||
# can actuate the steering for a large portion of any lateral movements. However, in order to get further control on
|
||||
# steer actuation, the other three signals are necessary. Ford controls vehicles differently than most other makes.
|
||||
# A detailed explanation on ford control can be found here:
|
||||
# https://www.f150gen14.com/forum/threads/introducing-bluepilot-a-ford-specific-fork-for-comma3x-openpilot.24241/#post-457706
|
||||
mode = 1 if CC.latActive else 0
|
||||
counter = (self.frame // CarControllerParams.STEER_STEP) % 0x10
|
||||
can_sends.append(fordcan.create_lat_ctl2_msg(self.packer, self.CAN, mode, 0., 0., -self.apply_curvature_last, 0., counter))
|
||||
else:
|
||||
can_sends.append(fordcan.create_lat_ctl_msg(self.packer, self.CAN, CC.latActive, 0., 0., -self.apply_curvature_last, 0.))
|
||||
# apply rate limits, curvature error limit, and clip to signal range
|
||||
current_curvature = -CS.out.yawRate / max(CS.out.vEgoRaw, 0.1)
|
||||
|
||||
# send lka msg at 33Hz
|
||||
if (self.frame % CarControllerParams.LKA_STEP) == 0:
|
||||
can_sends.append(fordcan.create_lka_msg(self.packer, self.CAN))
|
||||
self.apply_curvature_last = apply_ford_curvature_limits(apply_curvature, self.apply_curvature_last, current_curvature,
|
||||
CS.out.vEgoRaw, 0., CC.latActive, self.CP)
|
||||
|
||||
if self.CP.flags & FordFlags.CANFD:
|
||||
# TODO: extended mode
|
||||
# Ford uses four individual signals to dictate how to drive to the car. Curvature alone (limited to 0.02m/s^2)
|
||||
# can actuate the steering for a large portion of any lateral movements. However, in order to get further control on
|
||||
# steer actuation, the other three signals are necessary. Ford controls vehicles differently than most other makes.
|
||||
# A detailed explanation on ford control can be found here:
|
||||
# https://www.f150gen14.com/forum/threads/introducing-bluepilot-a-ford-specific-fork-for-comma3x-openpilot.24241/#post-457706
|
||||
mode = 1 if CC.latActive else 0
|
||||
counter = (self.frame // CarControllerParams.STEER_STEP) % 0x10
|
||||
can_sends.append(fordcan.create_lat_ctl2_msg(self.packer, self.CAN, mode, 0., 0., -self.apply_curvature_last, 0., counter))
|
||||
else:
|
||||
can_sends.append(fordcan.create_lat_ctl_msg(self.packer, self.CAN, CC.latActive, 0., 0., -self.apply_curvature_last, 0.))
|
||||
|
||||
# send lka msg at 33Hz
|
||||
if (self.frame % CarControllerParams.LKA_STEP) == 0:
|
||||
can_sends.append(fordcan.create_lka_msg(self.packer, self.CAN))
|
||||
|
||||
### longitudinal control ###
|
||||
# send acc msg at 50Hz
|
||||
@@ -195,6 +225,8 @@ class CarController(CarControllerBase):
|
||||
self.lead_distance_bars_last = hud_control.leadDistanceBars
|
||||
|
||||
new_actuators = actuators.as_builder()
|
||||
if self.CP.flags & FordFlags.LKA_STEERING:
|
||||
new_actuators.steeringAngleDeg = self.apply_angle_last + CS.out.steeringAngleDeg
|
||||
new_actuators.curvature = self.apply_curvature_last
|
||||
new_actuators.accel = self.accel
|
||||
new_actuators.gas = self.gas
|
||||
|
||||
@@ -20,6 +20,8 @@ class CarState(CarStateBase):
|
||||
|
||||
self.distance_button = 0
|
||||
self.lc_button = 0
|
||||
self.lkas_available = False
|
||||
self.lateral_motion_control = None
|
||||
|
||||
def update(self, can_parsers, starpilot_toggles) -> structs.CarState:
|
||||
cp = can_parsers[Bus.pt]
|
||||
@@ -108,6 +110,15 @@ class CarState(CarStateBase):
|
||||
# Stock values from IPMA so that we can retain some stock functionality
|
||||
self.acc_tja_status_stock_values = cp_cam.vl["ACCDATA_3"]
|
||||
self.lkas_status_stock_values = cp_cam.vl["IPMA_Data"]
|
||||
if self.CP.flags & FordFlags.LKA_STEERING:
|
||||
try:
|
||||
self.lkas_available = cp.vl["Lane_Assist_Data3_FD1"]["LaActAvail_D_Actl"] == 3
|
||||
except KeyError:
|
||||
self.lkas_available = False
|
||||
try:
|
||||
self.lateral_motion_control = cp_cam.vl["LateralMotionControl"]
|
||||
except KeyError:
|
||||
self.lateral_motion_control = None
|
||||
|
||||
ret.buttonEvents = [
|
||||
*create_button_events(self.distance_button, prev_distance_button, {1: ButtonType.gapAdjustCruise}),
|
||||
|
||||
@@ -106,10 +106,12 @@ FW_VERSIONS = {
|
||||
},
|
||||
CAR.FORD_F_150_MK14: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'ML3V-14D003-BA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'ML3V-14D003-BC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'ML3V-14D003-BD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'ML34-2D053-AJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'NL34-2D053-CA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PL34-2D053-CA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PL34-2D053-CC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
@@ -117,6 +119,7 @@ FW_VERSIONS = {
|
||||
b'PL3V-2D053-BB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'ML3T-14D049-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'ML3T-14D049-AK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'ML3T-14D049-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
@@ -124,6 +127,7 @@ FW_VERSIONS = {
|
||||
b'ML3T-14H102-ABR\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'ML3T-14H102-ABS\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'ML3T-14H102-ABT\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'ML3T-14H102-ACA\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PJ6T-14H102-ABJ\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PJ6T-14H102-ABS\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'RJ6T-14H102-ACJ\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
@@ -211,6 +215,7 @@ FW_VERSIONS = {
|
||||
b'PB3C-2D053-ZD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PB3C-2D053-ZG\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'PB3C-2D053-ZJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'RB3C-2D053-AK\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'ML3T-14D049-AL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
@@ -220,4 +225,18 @@ FW_VERSIONS = {
|
||||
b'RJ6T-14H102-BBB\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.FORD_TRANSIT_MK5: {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'KK21-14D003-AM\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x760, None): [
|
||||
b'NK41-2D053-DF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x764, None): [
|
||||
b'PC4T-14D049-AA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x706, None): [
|
||||
b'NK3T-14F397-AB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import math
|
||||
|
||||
from opendbc.car import CanBusBase, structs
|
||||
|
||||
HUDControl = structs.CarControl.HUDControl
|
||||
@@ -33,20 +35,40 @@ def calculate_lat_ctl2_checksum(mode: int, counter: int, dat: bytearray) -> int:
|
||||
return 0xFF - (checksum & 0xFF)
|
||||
|
||||
|
||||
def create_lka_msg(packer, CAN: CanBus):
|
||||
def create_lka_msg(packer, CAN: CanBus, active: bool = False, apply_angle: float = 0.0,
|
||||
direction: int = 0, ramp_type: int = 0, curvature: float = 0.0):
|
||||
"""
|
||||
Creates an empty CAN message for the Ford LKA Command.
|
||||
Creates a CAN message for the Ford LKA Command.
|
||||
|
||||
This command can apply "Lane Keeping Aid" maneuvers, which are subject to the PSCM lockout.
|
||||
On LKA-steering platforms, this command applies Lane Keeping Aid maneuvers through the PSCM.
|
||||
|
||||
Frequency is 33Hz.
|
||||
"""
|
||||
|
||||
return packer.make_can_msg("Lane_Assist_Data1", CAN.main, {})
|
||||
if active:
|
||||
mrad = math.radians(max(-5.8, min(5.8, apply_angle))) * 1000.0
|
||||
mrad = max(-102.4, min(102.3, mrad))
|
||||
curvature = max(-0.01023, min(0.01023, curvature))
|
||||
else:
|
||||
mrad = 0.0
|
||||
direction = 0
|
||||
ramp_type = 0
|
||||
curvature = 0.0
|
||||
|
||||
values = {
|
||||
"LkaDrvOvrrd_D_Rq": 0,
|
||||
"LkaActvStats_D2_Req": direction if active else 0,
|
||||
"LaRefAng_No_Req": mrad,
|
||||
"LaRampType_B_Req": ramp_type,
|
||||
"LaCurvature_No_Calc": curvature,
|
||||
"LdwActvStats_D_Req": 0,
|
||||
"LdwActvIntns_D_Req": 3,
|
||||
}
|
||||
return packer.make_can_msg("Lane_Assist_Data1", CAN.main, values)
|
||||
|
||||
|
||||
def create_lat_ctl_msg(packer, CAN: CanBus, lat_active: bool, path_offset: float, path_angle: float, curvature: float,
|
||||
curvature_rate: float):
|
||||
curvature_rate: float, stock_lmc=None):
|
||||
"""
|
||||
Creates a CAN message for the Ford TJA/LCA Command.
|
||||
|
||||
@@ -68,20 +90,33 @@ def create_lat_ctl_msg(packer, CAN: CanBus, lat_active: bool, path_offset: float
|
||||
Frequency is 20Hz.
|
||||
"""
|
||||
|
||||
values = {
|
||||
"LatCtlRng_L_Max": 0, # Unknown [0|126] meter
|
||||
"HandsOffCnfm_B_Rq": 0, # Unknown: 0=Inactive, 1=Active [0|1]
|
||||
"LatCtl_D_Rq": 1 if lat_active else 0, # Mode: 0=None, 1=ContinuousPathFollowing, 2=InterventionLeft,
|
||||
# 3=InterventionRight, 4-7=NotUsed [0|7]
|
||||
"LatCtlRampType_D_Rq": 0, # Ramp speed: 0=Slow, 1=Medium, 2=Fast, 3=Immediate [0|3]
|
||||
# Makes no difference with curvature control
|
||||
"LatCtlPrecision_D_Rq": 1, # Precision: 0=Comfortable, 1=Precise, 2/3=NotUsed [0|3]
|
||||
# The stock system always uses comfortable
|
||||
"LatCtlPathOffst_L_Actl": path_offset, # Path offset [-5.12|5.11] meter
|
||||
"LatCtlPath_An_Actl": path_angle, # Path angle [-0.5|0.5235] radians
|
||||
"LatCtlCurv_NoRate_Actl": curvature_rate, # Curvature rate [-0.001024|0.00102375] 1/meter^2
|
||||
"LatCtlCurv_No_Actl": curvature, # Curvature [-0.02|0.02094] 1/meter
|
||||
}
|
||||
if stock_lmc is not None:
|
||||
values = {
|
||||
"LatCtlRng_L_Max": stock_lmc["LatCtlRng_L_Max"],
|
||||
"HandsOffCnfm_B_Rq": stock_lmc["HandsOffCnfm_B_Rq"],
|
||||
"LatCtl_D_Rq": 0,
|
||||
"LatCtlRampType_D_Rq": stock_lmc["LatCtlRampType_D_Rq"],
|
||||
"LatCtlPrecision_D_Rq": stock_lmc["LatCtlPrecision_D_Rq"],
|
||||
"LatCtlPathOffst_L_Actl": stock_lmc["LatCtlPathOffst_L_Actl"],
|
||||
"LatCtlPath_An_Actl": stock_lmc["LatCtlPath_An_Actl"],
|
||||
"LatCtlCurv_NoRate_Actl": stock_lmc["LatCtlCurv_NoRate_Actl"],
|
||||
"LatCtlCurv_No_Actl": stock_lmc["LatCtlCurv_No_Actl"],
|
||||
}
|
||||
else:
|
||||
values = {
|
||||
"LatCtlRng_L_Max": 0, # Unknown [0|126] meter
|
||||
"HandsOffCnfm_B_Rq": 0, # Unknown: 0=Inactive, 1=Active [0|1]
|
||||
"LatCtl_D_Rq": 1 if lat_active else 0, # Mode: 0=None, 1=ContinuousPathFollowing, 2=InterventionLeft,
|
||||
# 3=InterventionRight, 4-7=NotUsed [0|7]
|
||||
"LatCtlRampType_D_Rq": 0, # Ramp speed: 0=Slow, 1=Medium, 2=Fast, 3=Immediate [0|3]
|
||||
# Makes no difference with curvature control
|
||||
"LatCtlPrecision_D_Rq": 1, # Precision: 0=Comfortable, 1=Precise, 2/3=NotUsed [0|3]
|
||||
# The stock system always uses comfortable
|
||||
"LatCtlPathOffst_L_Actl": path_offset, # Path offset [-5.12|5.11] meter
|
||||
"LatCtlPath_An_Actl": path_angle, # Path angle [-0.5|0.5235] radians
|
||||
"LatCtlCurv_NoRate_Actl": curvature_rate, # Curvature rate [-0.001024|0.00102375] 1/meter^2
|
||||
"LatCtlCurv_No_Actl": curvature, # Curvature [-0.02|0.02094] 1/meter
|
||||
}
|
||||
return packer.make_can_msg("LateralMotionControl", CAN.main, values)
|
||||
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
ret.radarUnavailable = Bus.radar not in DBC[candidate]
|
||||
ret.steerControlType = structs.CarParams.SteerControlType.angle
|
||||
ret.steerActuatorDelay = 0.2
|
||||
ret.steerActuatorDelay = 0.05 if ret.flags & FordFlags.LKA_STEERING else 0.2
|
||||
ret.steerLimitTimer = 1.0
|
||||
ret.steerAtStandstill = True
|
||||
|
||||
@@ -63,6 +63,8 @@ class CarInterface(CarInterfaceBase):
|
||||
if fingerprint[CAN.camera].get(0x3d6) != 8 or fingerprint[CAN.camera].get(0x186) != 8:
|
||||
carlog.error('dashcamOnly: SecOC is unsupported')
|
||||
ret.dashcamOnly = True
|
||||
elif ret.flags & FordFlags.LKA_STEERING:
|
||||
ret.safetyConfigs[-1].safetyParam |= FordSafetyFlags.LKA_STEERING.value
|
||||
else:
|
||||
# Lock out if the car does not have needed lateral and longitudinal control APIs.
|
||||
# Note that we also check CAN for adaptive cruise, but no known signal for LCA exists
|
||||
|
||||
@@ -46,11 +46,13 @@ class CarControllerParams:
|
||||
class FordSafetyFlags(IntFlag):
|
||||
LONG_CONTROL = 1
|
||||
CANFD = 2
|
||||
LKA_STEERING = 4
|
||||
|
||||
|
||||
class FordFlags(IntFlag):
|
||||
# Static flags
|
||||
CANFD = 1
|
||||
LKA_STEERING = 2
|
||||
|
||||
|
||||
class RADAR:
|
||||
@@ -111,6 +113,13 @@ class FordCANFDPlatformConfig(FordPlatformConfig):
|
||||
self.flags |= FordFlags.CANFD
|
||||
|
||||
|
||||
@dataclass
|
||||
class FordLKASteeringPlatformConfig(FordPlatformConfig):
|
||||
def init(self):
|
||||
super().init()
|
||||
self.flags |= FordFlags.LKA_STEERING
|
||||
|
||||
|
||||
@dataclass
|
||||
class FordF150LightningPlatform(FordCANFDPlatformConfig):
|
||||
def init(self):
|
||||
@@ -178,6 +187,10 @@ class CAR(Platforms):
|
||||
[FordCarDocs("Ford Ranger 2024", "Adaptive Cruise Control with Lane Centering", setup_video="https://www.youtube.com/watch?v=2oJlXCKYOy0")],
|
||||
CarSpecs(mass=2000, wheelbase=3.27, steerRatio=17.0),
|
||||
)
|
||||
FORD_TRANSIT_MK5 = FordLKASteeringPlatformConfig(
|
||||
[FordCarDocs("Ford Transit 2025", "Co-Pilot360 Assist+")],
|
||||
CarSpecs(mass=2068, wheelbase=3.302, steerRatio=16.7),
|
||||
)
|
||||
|
||||
|
||||
# FW response contains a combined software and part number
|
||||
|
||||
@@ -6,7 +6,7 @@ from opendbc.car.lateral import apply_driver_steer_torque_limits
|
||||
from opendbc.car.gm import gmcan
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.gm.values import (
|
||||
ASCM_INT, CAR, CC_ONLY_CAR, CC_REGEN_PADDLE_CAR, DBC, EV_CAR, SDGM_CAR, AccState, CanBus, CarControllerParams,
|
||||
ASCM_INT, CAMERA_ACC_CAR, CAR, CC_ONLY_CAR, CC_REGEN_PADDLE_CAR, DBC, EV_CAR, SDGM_CAR, AccState, CanBus, CarControllerParams,
|
||||
CruiseButtons, GMFlags, GMSafetyFlags,
|
||||
)
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
@@ -66,6 +66,16 @@ TRUCK_LONG_SMOOTH_CARS = {
|
||||
CAR.CHEVROLET_SILVERADO,
|
||||
CAR.CHEVROLET_SILVERADO_CC,
|
||||
}
|
||||
TRUCK_FRICTION_BRAKE_ENGAGE = 40
|
||||
TRUCK_FRICTION_BRAKE_RELEASE = 8
|
||||
TRUCK_FRICTION_BRAKE_IMMEDIATE_ACCEL = -0.85
|
||||
ACC_DASHBOARD_ZERO_RESERVED_CARS = {
|
||||
CAR.CHEVROLET_BLAZER,
|
||||
CAR.CHEVROLET_EQUINOX,
|
||||
CAR.CHEVROLET_SILVERADO,
|
||||
CAR.CHEVROLET_TRAILBLAZER,
|
||||
CAR.CHEVROLET_TRAX,
|
||||
}
|
||||
|
||||
|
||||
def get_stock_cc_active_for_cancel(CP, CS):
|
||||
@@ -120,6 +130,10 @@ def get_acc_dashboard_status_active(CP, CC):
|
||||
return CP.carFingerprint == CAR.BUICK_LACROSSE_ASCM and CC.latActive
|
||||
|
||||
|
||||
def get_acc_dashboard_always_one(CP):
|
||||
return 0 if CP.carFingerprint in ACC_DASHBOARD_ZERO_RESERVED_CARS else 1
|
||||
|
||||
|
||||
def get_acc_dashboard_fcw_alert(hud_alert, CS):
|
||||
if hud_alert == VisualAlert.fcw:
|
||||
return 0x3
|
||||
@@ -183,16 +197,31 @@ def should_send_adas_status(CP, is_kaofui_car):
|
||||
return CP.networkLocation != NetworkLocation.fwdCamera and CP.carFingerprint not in SDGM_CAR
|
||||
|
||||
|
||||
def should_send_acc_2cd(CP):
|
||||
return (
|
||||
CP.networkLocation == NetworkLocation.fwdCamera and
|
||||
CP.carFingerprint in CAMERA_ACC_CAR and
|
||||
CP.carFingerprint not in (CC_ONLY_CAR | SDGM_CAR) and
|
||||
not bool(getattr(CP, "flags", 0) & GMFlags.NO_CAMERA.value)
|
||||
)
|
||||
|
||||
|
||||
def get_testing_ground_1_brake_switch_bias(v_ego: float) -> int:
|
||||
return int(round(np.interp(v_ego, [0.0, 6.0, 15.0, 30.0], [40.0, 85.0, 130.0, 170.0])))
|
||||
|
||||
|
||||
def shape_truck_positive_accel(accel: float, v_ego: float, enabled: bool) -> float:
|
||||
def shape_truck_positive_accel(accel: float, v_ego: float, enabled: bool,
|
||||
lead_visible: bool = False, set_speed_error: float = 0.0) -> float:
|
||||
if not enabled or accel <= 0.0 or v_ego < 12.0:
|
||||
return accel
|
||||
|
||||
low_scale = float(np.interp(v_ego, [12.0, 18.0, 25.0, 35.0], [0.95, 0.88, 0.82, 0.76]))
|
||||
mid_scale = float(np.interp(v_ego, [12.0, 18.0, 25.0, 35.0], [0.98, 0.94, 0.89, 0.84]))
|
||||
low_scale = float(np.interp(v_ego, [12.0, 18.0, 25.0, 35.0], [0.93, 0.84, 0.76, 0.70]))
|
||||
mid_scale = float(np.interp(v_ego, [12.0, 18.0, 25.0, 35.0], [0.97, 0.91, 0.85, 0.79]))
|
||||
|
||||
if lead_visible and set_speed_error > 0.0:
|
||||
follow_relief = float(np.interp(set_speed_error, [0.0, 1.0, 2.5, 4.0, 6.0], [0.0, 0.04, 0.10, 0.18, 0.30]))
|
||||
low_scale += (1.0 - low_scale) * follow_relief
|
||||
mid_scale += (1.0 - mid_scale) * follow_relief
|
||||
|
||||
if accel <= 0.12:
|
||||
return accel * low_scale
|
||||
@@ -203,6 +232,35 @@ def shape_truck_positive_accel(accel: float, v_ego: float, enabled: bool) -> flo
|
||||
return accel
|
||||
|
||||
|
||||
def shape_truck_pitch_accel(pitch_accel: float, v_ego: float, enabled: bool) -> float:
|
||||
if not enabled:
|
||||
return pitch_accel
|
||||
|
||||
scale = float(np.interp(v_ego, [8.0, 15.0, 25.0, 35.0], [0.60, 0.45, 0.30, 0.25]))
|
||||
return pitch_accel * scale
|
||||
|
||||
|
||||
def shape_truck_friction_brake(apply_brake: int, accel_cmd: float, stopping: bool, active: bool) -> tuple[int, bool]:
|
||||
if apply_brake <= 0:
|
||||
return 0, False
|
||||
|
||||
# Preserve full brake response for stop control and meaningful deceleration.
|
||||
if stopping or accel_cmd <= TRUCK_FRICTION_BRAKE_IMMEDIATE_ACCEL:
|
||||
return apply_brake, True
|
||||
|
||||
if active:
|
||||
if apply_brake <= TRUCK_FRICTION_BRAKE_RELEASE:
|
||||
return 0, False
|
||||
return apply_brake, True
|
||||
|
||||
if apply_brake >= TRUCK_FRICTION_BRAKE_ENGAGE:
|
||||
return apply_brake, True
|
||||
|
||||
# Keep tiny corrections in the continuous gas/regen torque path. Switching
|
||||
# to friction also forces max regen, which makes a small request perceptible.
|
||||
return 0, False
|
||||
|
||||
|
||||
def get_lka_steering_cmd_counter(next_counter: int, CS) -> int:
|
||||
if getattr(CS, "loopback_lka_steering_cmd_updated", False):
|
||||
return (getattr(CS, "loopback_lka_steering_cmd_counter", next_counter) + 1) % 4
|
||||
@@ -222,6 +280,7 @@ def supports_volt_auto_hold(CP, auto_hold_enabled: bool):
|
||||
stock_hold_safety_ready = bool(safety_param & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value)
|
||||
return (
|
||||
auto_hold_enabled and
|
||||
getattr(CP, "openpilotLongitudinalControl", False) and
|
||||
stock_hold_safety_ready and
|
||||
CP.carFingerprint in AUTO_HOLD_VOLT_CARS
|
||||
)
|
||||
@@ -233,6 +292,7 @@ def supports_volt_one_pedal(CP, one_pedal_enabled: bool):
|
||||
stock_hold_safety_ready = bool(safety_param & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value)
|
||||
return (
|
||||
one_pedal_enabled and
|
||||
getattr(CP, "openpilotLongitudinalControl", False) and
|
||||
stock_hold_safety_ready and
|
||||
getattr(CP, "transmissionType", None) == TransmissionType.direct and
|
||||
CP.carFingerprint in AUTO_HOLD_VOLT_CARS
|
||||
@@ -305,6 +365,14 @@ def get_friction_brake_bus(CP):
|
||||
|
||||
if CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
if CP.carFingerprint in SDGM_CAR:
|
||||
# cam-long: 0x315 goes where the panda whitelist allows it and the EBCM hears it
|
||||
safety_cfg = getattr(CP, "safetyConfigs", ())
|
||||
safety_param = safety_cfg[0].safetyParam if safety_cfg else 0
|
||||
if safety_param & GMSafetyFlags.HW_CAM_LONG.value:
|
||||
# SASCM relays 0x315 to the EBCM off its camera-bus (bus2) leg; bare SDGM uses the pt bus
|
||||
if CP.flags & GMFlags.SASCM.value:
|
||||
return CanBus.CAMERA
|
||||
return CanBus.POWERTRAIN
|
||||
return CanBus.CAMERA
|
||||
return CanBus.POWERTRAIN
|
||||
|
||||
@@ -419,6 +487,15 @@ class CarController(CarControllerBase):
|
||||
self.last_steer_frame = 0
|
||||
self.last_button_frame = 0
|
||||
self.cancel_counter = 0
|
||||
self.xt4_cc_button_burst_remaining = 0
|
||||
self.xt4_cc_button_burst_button = CruiseButtons.INIT
|
||||
self.xt4_cc_button_burst_last_counter = -1
|
||||
self.xt4_cc_button_observed_counter = -1
|
||||
self.xt4_cc_button_counter_frame = 0
|
||||
self.gm_cc_last_direction_button = CruiseButtons.INIT
|
||||
self.gm_cc_last_direction_frame = 0
|
||||
self.gm_cc_pending_reverse_button = CruiseButtons.INIT
|
||||
self.gm_cc_pending_reverse_frame = 0
|
||||
|
||||
self.lka_steering_cmd_counter = 0
|
||||
self.lka_icon_status_last = (False, False)
|
||||
@@ -479,6 +556,7 @@ class CarController(CarControllerBase):
|
||||
self.gm_auto_hold_enabled = False
|
||||
self.bolt_acc_pedal_friction_release_frames = 0
|
||||
self.bolt_acc_pedal_friction_low_speed_active = False
|
||||
self.truck_friction_brake_active = False
|
||||
|
||||
def _reset_volt_one_pedal(self):
|
||||
self.volt_one_pedal_pid.reset()
|
||||
@@ -869,6 +947,18 @@ class CarController(CarControllerBase):
|
||||
can_sends.append(gmcan.create_ecm_cruise_control_command(
|
||||
self.packer_pt, CanBus.POWERTRAIN, True, hud_v_cruise * CV.MS_TO_KPH))
|
||||
|
||||
xt4_cc_button_spam = (
|
||||
self.CP.carFingerprint == CAR.CADILLAC_XT4_CC and
|
||||
should_send_cc_button_spam(self.CP, CC, CS)
|
||||
)
|
||||
if xt4_cc_button_spam:
|
||||
can_sends.extend(gmcan.create_gm_cc_spam_command(self.packer_pt, self, CS, actuators, starpilot_toggles))
|
||||
elif self.CP.carFingerprint == CAR.CADILLAC_XT4_CC:
|
||||
self.xt4_cc_button_burst_remaining = 0
|
||||
self.xt4_cc_button_burst_button = CruiseButtons.INIT
|
||||
self.xt4_cc_button_burst_last_counter = -1
|
||||
self.xt4_cc_button_observed_counter = -1
|
||||
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
# Gas/regen, brakes, and UI commands - all at 25Hz
|
||||
if self.frame % 4 == 0:
|
||||
@@ -932,14 +1022,22 @@ class CarController(CarControllerBase):
|
||||
if testing_ground.use_1:
|
||||
accel_max = min(accel_max, np.interp(CS.out.vEgo, [0.0, 4.0, 12.0], [1.25, 1.6, self.params.ACCEL_MAX]))
|
||||
|
||||
accel_input = actuators.accel + accel_due_to_pitch
|
||||
if (
|
||||
truck_long_smoothing = (
|
||||
getattr(starpilot_toggles, "truck_tuning", False) and
|
||||
self.CP.carFingerprint in TRUCK_LONG_SMOOTH_CARS and
|
||||
getattr(self.CP, "transmissionType", None) == TransmissionType.automatic and
|
||||
not self.CP.enableGasInterceptorDEPRECATED
|
||||
):
|
||||
accel_input = shape_truck_positive_accel(accel_input, CS.out.vEgo, True)
|
||||
)
|
||||
accel_due_to_pitch = shape_truck_pitch_accel(accel_due_to_pitch, CS.out.vEgo, truck_long_smoothing)
|
||||
accel_input = actuators.accel + accel_due_to_pitch
|
||||
if truck_long_smoothing:
|
||||
accel_input = shape_truck_positive_accel(
|
||||
accel_input,
|
||||
CS.out.vEgo,
|
||||
True,
|
||||
lead_visible=CC.hudControl.leadVisible,
|
||||
set_speed_error=max(CC.hudControl.setSpeed - CS.out.vEgo, 0.0),
|
||||
)
|
||||
|
||||
accel_cmd = float(np.clip(accel_input, self.params.ACCEL_MIN, accel_max))
|
||||
torque = self.tireRadius * ((self.mass * accel_cmd) + (0.5 * self.coeffDrag * self.frontalArea * self.airDensity * CS.out.vEgo ** 2))
|
||||
@@ -957,6 +1055,12 @@ class CarController(CarControllerBase):
|
||||
brake_accel = min((scaled_torque - brake_switch) / (self.tireRadius * self.mass), 0)
|
||||
self.apply_gas = int(round(apply_gas_torque))
|
||||
self.apply_brake = int(round(np.interp(brake_accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
|
||||
if truck_long_smoothing:
|
||||
self.apply_brake, self.truck_friction_brake_active = shape_truck_friction_brake(
|
||||
self.apply_brake, accel_cmd, stopping, self.truck_friction_brake_active,
|
||||
)
|
||||
else:
|
||||
self.truck_friction_brake_active = False
|
||||
if bolt_acc_pedal_friction_main_on:
|
||||
if self.apply_brake > 0:
|
||||
full_brake_accel = min(
|
||||
@@ -1013,17 +1117,19 @@ class CarController(CarControllerBase):
|
||||
|
||||
if self.CP.flags & GMFlags.CC_LONG.value:
|
||||
if should_send_cc_button_spam(self.CP, CC, CS):
|
||||
# Using extend instead of append since the message is only sent intermittently
|
||||
can_sends.extend(gmcan.create_gm_cc_spam_command(self.packer_pt, self, CS, actuators, starpilot_toggles))
|
||||
elif (CS.out.cruiseState.enabled and CC.enabled and self.frame % 52 == 0 and
|
||||
if self.CP.carFingerprint != CAR.CADILLAC_XT4_CC:
|
||||
# Using extend instead of append since the message is only sent intermittently
|
||||
can_sends.extend(gmcan.create_gm_cc_spam_command(self.packer_pt, self, CS, actuators, starpilot_toggles))
|
||||
else:
|
||||
if (CS.out.cruiseState.enabled and CC.enabled and self.frame % 52 == 0 and
|
||||
CS.cruise_buttons == CruiseButtons.UNPRESS and CS.out.gasPressed and CS.out.cruiseState.speed < CS.out.vEgo < hud_v_cruise):
|
||||
if self.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC:
|
||||
can_sends.append(gmcan.create_buttons_malibu(
|
||||
self.packer_pt, CanBus.POWERTRAIN, CruiseButtons.DECEL_SET,
|
||||
self.malibu_button_phase, CS.steering_button_prefix))
|
||||
self.malibu_button_phase = (self.malibu_button_phase + 1) % 4
|
||||
else:
|
||||
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.DECEL_SET))
|
||||
if self.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC:
|
||||
can_sends.append(gmcan.create_buttons_malibu(
|
||||
self.packer_pt, CanBus.POWERTRAIN, CruiseButtons.DECEL_SET,
|
||||
self.malibu_button_phase, CS.steering_button_prefix))
|
||||
self.malibu_button_phase = (self.malibu_button_phase + 1) % 4
|
||||
else:
|
||||
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.DECEL_SET))
|
||||
if self.CP.enableGasInterceptorDEPRECATED:
|
||||
can_sends.append(create_gas_interceptor_command(self.packer_pt, interceptor_gas_cmd, idx))
|
||||
if bolt_acc_pedal_friction_experiment:
|
||||
@@ -1053,6 +1159,8 @@ class CarController(CarControllerBase):
|
||||
# TODO: can we always check the longControlState?
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
at_full_stop = at_full_stop and stopping
|
||||
if should_send_acc_2cd(self.CP):
|
||||
can_sends.append(gmcan.create_acc_2cd_command(CanBus.POWERTRAIN, idx))
|
||||
|
||||
if self.CP.autoResumeSng:
|
||||
resume = actuators.longControlState != LongCtrlState.starting or CC.cruiseControl.resume
|
||||
@@ -1097,8 +1205,10 @@ class CarController(CarControllerBase):
|
||||
if should_send_acc_dashboard_status(self.CP, dash_speed_spoof_active):
|
||||
fcw_alert = get_acc_dashboard_fcw_alert(hud_alert, CS)
|
||||
acc_dashboard_status_active = get_acc_dashboard_status_active(self.CP, CC)
|
||||
acc_dashboard_always_one = get_acc_dashboard_always_one(self.CP)
|
||||
can_sends.append(gmcan.create_acc_dashboard_command(self.packer_pt, CanBus.POWERTRAIN, acc_dashboard_status_active,
|
||||
hud_v_cruise * CV.MS_TO_KPH, hud_control, fcw_alert))
|
||||
hud_v_cruise * CV.MS_TO_KPH, hud_control, fcw_alert,
|
||||
acc_dashboard_always_one))
|
||||
|
||||
# Radar needs to know current speed and yaw rate (50hz),
|
||||
# and that ADAS is alive (10hz)
|
||||
|
||||
@@ -220,6 +220,7 @@ FINGERPRINTS.update({
|
||||
CAR.CHEVROLET_MALIBU_SDGM: FINGERPRINTS[CAR.CHEVROLET_MALIBU_CC],
|
||||
CAR.BUICK_BABYENCLAVE: FINGERPRINTS[CAR.CHEVROLET_TRAVERSE],
|
||||
CAR.CHEVROLET_SILVERADO_CC: FINGERPRINTS[CAR.CHEVROLET_SILVERADO],
|
||||
CAR.CADILLAC_XT4_CC: FINGERPRINTS[CAR.CADILLAC_XT4],
|
||||
CAR.BUICK_LACROSSE_ASCM: FINGERPRINTS[CAR.BUICK_LACROSSE],
|
||||
})
|
||||
|
||||
|
||||
@@ -17,6 +17,18 @@ MALIBU_BUTTON_MAP = {
|
||||
CruiseButtons.CANCEL: 5,
|
||||
}
|
||||
|
||||
ACC_CRUISE_STATE_ADAPTIVE = 2
|
||||
XT4_CC_BUTTON_BURST_FRAMES = 6
|
||||
XT4_CC_BUTTON_COUNTER_DELAY_FRAMES = 1
|
||||
BOLT_CC_BUTTON_CARS = {
|
||||
CAR.CHEVROLET_BOLT_CC_2017,
|
||||
CAR.CHEVROLET_BOLT_CC_2018_2021,
|
||||
CAR.CHEVROLET_BOLT_CC_2022_2023,
|
||||
}
|
||||
BOLT_CC_TARGET_DEADBAND_MPH = 0.75
|
||||
BOLT_CC_REVERSE_CONFIRM_S = 0.6
|
||||
BOLT_CC_DIRECTION_MEMORY_S = 1.5
|
||||
|
||||
|
||||
def malibu_phase_map_for_button(button):
|
||||
key = MALIBU_BUTTON_MAP.get(button)
|
||||
@@ -215,16 +227,24 @@ def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_s
|
||||
return packer.make_can_msg("EBCMFrictionBrakeCmd", bus, values)
|
||||
|
||||
|
||||
def create_acc_dashboard_command(packer, bus, enabled, target_speed_kph, hud_control, fcw_alert):
|
||||
def create_acc_2cd_command(bus, idx):
|
||||
dat = bytearray([0x00, 0x2c, 0x03, 0xd3, 0x00])
|
||||
dat[0] = (idx & 0x3) << 6
|
||||
dat[4] = (0xfd - (idx & 0x3)) & 0xff
|
||||
return CanData(0x2CD, bytes(dat), bus)
|
||||
|
||||
|
||||
def create_acc_dashboard_command(packer, bus, enabled, target_speed_kph, hud_control, fcw_alert, acc_always_one=1):
|
||||
target_speed = min(target_speed_kph, 255)
|
||||
|
||||
values = {
|
||||
"ACCAlwaysOne": 1,
|
||||
"ACCAlwaysOne": acc_always_one,
|
||||
"ACCCruiseState": ACC_CRUISE_STATE_ADAPTIVE,
|
||||
"ACCResumeButton": 0,
|
||||
"ACCSpeedSetpoint": target_speed,
|
||||
"ACCGapLevel": hud_control.leadDistanceBars * enabled, # 3 "far", 0 "inactive"
|
||||
"ACCCmdActive": enabled,
|
||||
"ACCAlwaysOne2": 1,
|
||||
"ACCAlwaysOne2": acc_always_one,
|
||||
"ACCLeadCar": hud_control.leadVisible,
|
||||
"FCWAlert": int(fcw_alert) & 0x3,
|
||||
}
|
||||
@@ -286,32 +306,110 @@ def create_lka_icon_command(bus, active, critical, steer):
|
||||
return CanData(0x104c006c, dat, bus)
|
||||
|
||||
|
||||
def stabilize_bolt_cc_button(controller, CP, requested_button):
|
||||
if CP.carFingerprint not in BOLT_CC_BUTTON_CARS:
|
||||
return requested_button
|
||||
|
||||
direction_buttons = (CruiseButtons.RES_ACCEL, CruiseButtons.DECEL_SET)
|
||||
if requested_button not in direction_buttons:
|
||||
controller.gm_cc_pending_reverse_button = CruiseButtons.INIT
|
||||
return requested_button
|
||||
|
||||
last_button = getattr(controller, "gm_cc_last_direction_button", CruiseButtons.INIT)
|
||||
last_frame = getattr(controller, "gm_cc_last_direction_frame", -int(BOLT_CC_DIRECTION_MEMORY_S / DT_CTRL) - 1)
|
||||
recently_sent = (controller.frame - last_frame) * DT_CTRL <= BOLT_CC_DIRECTION_MEMORY_S
|
||||
reversing_to_accel = (last_button == CruiseButtons.DECEL_SET and
|
||||
requested_button == CruiseButtons.RES_ACCEL and recently_sent)
|
||||
|
||||
if reversing_to_accel:
|
||||
pending_button = getattr(controller, "gm_cc_pending_reverse_button", CruiseButtons.INIT)
|
||||
if pending_button != requested_button:
|
||||
controller.gm_cc_pending_reverse_button = requested_button
|
||||
controller.gm_cc_pending_reverse_frame = controller.frame
|
||||
return CruiseButtons.INIT
|
||||
|
||||
pending_frame = getattr(controller, "gm_cc_pending_reverse_frame", controller.frame)
|
||||
if (controller.frame - pending_frame) * DT_CTRL < BOLT_CC_REVERSE_CONFIRM_S:
|
||||
return CruiseButtons.INIT
|
||||
|
||||
controller.gm_cc_pending_reverse_button = CruiseButtons.INIT
|
||||
return requested_button
|
||||
|
||||
|
||||
def create_gm_cc_spam_command(packer, controller, CS, actuators, starpilot_toggles):
|
||||
accel = actuators.accel
|
||||
v_ego = CS.out.vEgo
|
||||
cruise_btn = CruiseButtons.INIT
|
||||
rate = 1 if abs(accel) <= 0.15 else 0.2
|
||||
ms_convert = CV.MS_TO_KPH if getattr(starpilot_toggles, "is_metric", False) else CV.MS_TO_MPH
|
||||
is_metric = getattr(starpilot_toggles, "is_metric", False)
|
||||
ms_convert = CV.MS_TO_KPH if is_metric else CV.MS_TO_MPH
|
||||
speed_setpoint = int(round(CS.out.cruiseState.speed * ms_convert))
|
||||
desired_setpoint = int(round((v_ego * 1.01 + 3 * accel) * ms_convert))
|
||||
projected_setpoint = (v_ego * 1.01 + 3 * accel) * ms_convert
|
||||
desired_setpoint = int(round(projected_setpoint))
|
||||
bolt_cc = CS.CP.carFingerprint in BOLT_CC_BUTTON_CARS
|
||||
target_deadband = BOLT_CC_TARGET_DEADBAND_MPH * (CV.MPH_TO_KPH if is_metric else 1.0) if bolt_cc else 0.0
|
||||
comparison_setpoint = projected_setpoint if bolt_cc else desired_setpoint
|
||||
|
||||
if CS.CP.minEnableSpeed - (desired_setpoint / ms_convert) > 3.25:
|
||||
cruise_btn = CruiseButtons.CANCEL
|
||||
controller.apply_speed = 0
|
||||
elif desired_setpoint < speed_setpoint and speed_setpoint > CS.CP.minEnableSpeed * ms_convert + 1:
|
||||
elif comparison_setpoint < speed_setpoint - target_deadband and speed_setpoint > CS.CP.minEnableSpeed * ms_convert + 1:
|
||||
cruise_btn = CruiseButtons.DECEL_SET
|
||||
controller.apply_speed = speed_setpoint - 1
|
||||
elif desired_setpoint > speed_setpoint:
|
||||
elif comparison_setpoint > speed_setpoint + target_deadband:
|
||||
cruise_btn = CruiseButtons.RES_ACCEL
|
||||
|
||||
cruise_btn = stabilize_bolt_cc_button(controller, CS.CP, cruise_btn)
|
||||
if cruise_btn == CruiseButtons.CANCEL:
|
||||
controller.apply_speed = 0
|
||||
elif cruise_btn == CruiseButtons.DECEL_SET:
|
||||
controller.apply_speed = speed_setpoint - 1
|
||||
elif cruise_btn == CruiseButtons.RES_ACCEL:
|
||||
controller.apply_speed = speed_setpoint + 1
|
||||
else:
|
||||
cruise_btn = CruiseButtons.INIT
|
||||
controller.apply_speed = speed_setpoint
|
||||
|
||||
if CS.CP.carFingerprint == CAR.CADILLAC_XT4_CC:
|
||||
if controller.xt4_cc_button_observed_counter != CS.buttons_counter:
|
||||
controller.xt4_cc_button_observed_counter = CS.buttons_counter
|
||||
controller.xt4_cc_button_counter_frame = controller.frame
|
||||
|
||||
if cruise_btn == CruiseButtons.INIT:
|
||||
controller.xt4_cc_button_burst_remaining = 0
|
||||
controller.xt4_cc_button_burst_button = CruiseButtons.INIT
|
||||
return []
|
||||
|
||||
if (controller.xt4_cc_button_burst_remaining > 0 and
|
||||
controller.xt4_cc_button_burst_button != cruise_btn):
|
||||
controller.xt4_cc_button_burst_remaining = 0
|
||||
|
||||
if controller.xt4_cc_button_burst_remaining == 0:
|
||||
if (controller.frame - controller.last_button_frame) * DT_CTRL <= rate:
|
||||
return []
|
||||
controller.last_button_frame = controller.frame
|
||||
controller.xt4_cc_button_burst_button = cruise_btn
|
||||
controller.xt4_cc_button_burst_remaining = XT4_CC_BUTTON_BURST_FRAMES
|
||||
controller.xt4_cc_button_burst_last_counter = -1
|
||||
|
||||
# XT4 physical taps hold the button for 5-7 consecutive 33 Hz frames.
|
||||
# Sending immediately after the stock frame is too early for the receiving ECU.
|
||||
if controller.frame - controller.xt4_cc_button_counter_frame < XT4_CC_BUTTON_COUNTER_DELAY_FRAMES:
|
||||
return []
|
||||
|
||||
# Send once per observed stock counter so the injected sequence has the same cadence.
|
||||
if controller.xt4_cc_button_burst_last_counter == CS.buttons_counter:
|
||||
return []
|
||||
|
||||
controller.xt4_cc_button_burst_last_counter = CS.buttons_counter
|
||||
controller.xt4_cc_button_burst_remaining -= 1
|
||||
idx = (CS.buttons_counter + 1) % 4
|
||||
return [create_buttons(packer, CanBus.POWERTRAIN, idx, controller.xt4_cc_button_burst_button)]
|
||||
|
||||
# Check rlogs closely - our message shouldn't show up on the pt bus for us
|
||||
# Or bus 2, since we're forwarding... but I think it does
|
||||
if (cruise_btn != CruiseButtons.INIT) and ((controller.frame - controller.last_button_frame) * DT_CTRL > rate):
|
||||
controller.last_button_frame = controller.frame
|
||||
if bolt_cc and cruise_btn in (CruiseButtons.RES_ACCEL, CruiseButtons.DECEL_SET):
|
||||
controller.gm_cc_last_direction_button = cruise_btn
|
||||
controller.gm_cc_last_direction_frame = controller.frame
|
||||
if CS.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC:
|
||||
phase_map = malibu_phase_map_for_button(cruise_btn)
|
||||
if phase_map:
|
||||
|
||||
@@ -72,6 +72,18 @@ NON_LINEAR_TORQUE_PARAMS = {
|
||||
},
|
||||
}
|
||||
|
||||
NON_LINEAR_TORQUE_PARAM_ALIASES = {
|
||||
CAR.CHEVROLET_VOLT_ASCM: CAR.CHEVROLET_VOLT,
|
||||
CAR.CHEVROLET_VOLT_CAMERA: CAR.CHEVROLET_VOLT,
|
||||
CAR.CHEVROLET_VOLT_CC: CAR.CHEVROLET_VOLT,
|
||||
CAR.CHEVROLET_VOLT_2019: CAR.CHEVROLET_VOLT,
|
||||
}
|
||||
|
||||
|
||||
def get_nonlinear_torque_params(car_fingerprint):
|
||||
source_fingerprint = NON_LINEAR_TORQUE_PARAM_ALIASES.get(car_fingerprint, car_fingerprint)
|
||||
return NON_LINEAR_TORQUE_PARAMS.get(source_fingerprint)
|
||||
|
||||
PEDAL_MSG = 0x201
|
||||
CAM_MSG = 0x320
|
||||
ACCELERATOR_POS_MSG = 0xBE
|
||||
@@ -169,7 +181,7 @@ class CarInterface(CarInterfaceBase):
|
||||
# The "lat_accel vs torque" relationship is assumed to be the sum of "sigmoid + linear" curves
|
||||
# An important thing to consider is that the slope at 0 should be > 0 (ideally >1)
|
||||
# This has big effect on the stability about 0 (noise when going straight)
|
||||
non_linear_torque_params = NON_LINEAR_TORQUE_PARAMS.get(self.CP.carFingerprint)
|
||||
non_linear_torque_params = get_nonlinear_torque_params(self.CP.carFingerprint)
|
||||
assert non_linear_torque_params, "The params are not defined"
|
||||
if isinstance(non_linear_torque_params, dict):
|
||||
side_key = "left" if lateral_acceleration >= 0 else "right"
|
||||
@@ -187,7 +199,7 @@ class CarInterface(CarInterfaceBase):
|
||||
return torque_values, lataccel_values
|
||||
|
||||
def torque_from_lateral_accel(self) -> TorqueFromLateralAccelCallbackType:
|
||||
if self.CP.carFingerprint in NON_LINEAR_TORQUE_PARAMS:
|
||||
if get_nonlinear_torque_params(self.CP.carFingerprint) is not None:
|
||||
torque_values, lataccel_values = self.get_lataccel_torque_siglin()
|
||||
|
||||
def torque_from_lateral_accel_siglin(lateral_acceleration: float, torque_params: structs.CarParams.LateralTorqueTuning):
|
||||
@@ -197,7 +209,7 @@ class CarInterface(CarInterfaceBase):
|
||||
return self.torque_from_lateral_accel_linear
|
||||
|
||||
def lateral_accel_from_torque(self) -> LateralAccelFromTorqueCallbackType:
|
||||
if self.CP.carFingerprint in NON_LINEAR_TORQUE_PARAMS:
|
||||
if get_nonlinear_torque_params(self.CP.carFingerprint) is not None:
|
||||
torque_values, lataccel_values = self.get_lataccel_torque_siglin()
|
||||
|
||||
def lateral_accel_from_torque_siglin(torque: float, torque_params: structs.CarParams.LateralTorqueTuning):
|
||||
@@ -422,8 +434,10 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate in (CAR.BUICK_LACROSSE, CAR.BUICK_LACROSSE_ASCM):
|
||||
elif candidate in (CAR.BUICK_LACROSSE, CAR.BUICK_LACROSSE_ASCM, CAR.BUICK_LACROSSE_ASCM_19US):
|
||||
CarInterfaceBase.configure_torque_tune(CAR.BUICK_LACROSSE, ret.lateralTuning)
|
||||
if candidate == CAR.BUICK_LACROSSE_ASCM_19US:
|
||||
ret.minSteerSpeed = 27 * CV.MPH_TO_MS
|
||||
|
||||
elif candidate == CAR.CADILLAC_ESCALADE:
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
@@ -495,7 +509,7 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.CADILLAC_XT4:
|
||||
elif candidate in (CAR.CADILLAC_XT4, CAR.CADILLAC_XT4_CC):
|
||||
ret.steerActuatorDelay = 0.2
|
||||
if not ret.openpilotLongitudinalControl:
|
||||
ret.minEnableSpeed = -1.
|
||||
@@ -589,12 +603,16 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.longitudinalTuning.kpV = [0.095, 0.085, 0.065, 0.050]
|
||||
ret.longitudinalTuning.kiV = [0.07, 0.10, 0.15, 0.24]
|
||||
ret.longitudinalTuning.kfDEPRECATED = 0.20
|
||||
ret.longitudinalActuatorDelay = 0.6
|
||||
else:
|
||||
ret.longitudinalTuning.kfDEPRECATED = 0.25
|
||||
|
||||
if is_bolt_2022_2023_pedal:
|
||||
# Gen2 Bolt pedal-long should follow the no-ACC panda path.
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_NO_ACC.value
|
||||
ret.startingState = True
|
||||
ret.startAccel = 0.55
|
||||
ret.vEgoStarting = max(ret.vEgoStarting, 0.35)
|
||||
|
||||
if candidate in (CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL, CAR.CHEVROLET_MALIBU_HYBRID_CC):
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_BOLT_2022_PEDAL.value
|
||||
@@ -638,7 +656,7 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.longitudinalTuning.kpBP = [0.0, 5.0, 15.0, 35.0]
|
||||
ret.longitudinalTuning.kpV = [0.02, 0.03, 0.028, 0.022]
|
||||
ret.longitudinalTuning.kiBP = [0.0, 5.0, 15.0, 35.0]
|
||||
ret.longitudinalTuning.kiV = [0.28, 0.26, 0.20, 0.16]
|
||||
ret.longitudinalTuning.kiV = [0.20, 0.18, 0.13, 0.08]
|
||||
|
||||
elif candidate in CC_ONLY_CAR and not ret.enableGasInterceptorDEPRECATED:
|
||||
ret.flags |= GMFlags.CC_LONG.value
|
||||
@@ -689,6 +707,7 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_REMOTE_START_BOOTS_COMMA.value
|
||||
|
||||
volt_stock_friction_brake_safety = (
|
||||
ret.openpilotLongitudinalControl and
|
||||
(gm_auto_hold or volt_one_pedal_mode) and
|
||||
candidate in {
|
||||
CAR.CHEVROLET_VOLT,
|
||||
@@ -699,12 +718,14 @@ class CarInterface(CarInterfaceBase):
|
||||
)
|
||||
if volt_stock_friction_brake_safety:
|
||||
# Reuse the paddle-scheduler safety bit as a Volt stock friction-brake
|
||||
# marker on non-pedal paths. Both auto hold and one-pedal can run while
|
||||
# OP longitudinal is configured but not currently active, so the bit must
|
||||
# be present regardless of the current long-control mode.
|
||||
# marker on non-pedal paths. Auto hold and one-pedal can run while OP
|
||||
# longitudinal is configured but not currently active, so the bit must
|
||||
# be present regardless of the current long-control mode. Do not expose
|
||||
# the path at all when OP long is disabled in CarParams.
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
|
||||
|
||||
volt_stock_one_pedal_safety = (
|
||||
ret.openpilotLongitudinalControl and
|
||||
volt_one_pedal_mode and
|
||||
candidate in {
|
||||
CAR.CHEVROLET_VOLT,
|
||||
@@ -717,6 +738,7 @@ class CarInterface(CarInterfaceBase):
|
||||
# Reuse the 3D1 scheduler bit as a Volt one-pedal marker on non-pedal
|
||||
# ACC paths. The bit is ignored by the actual 3D1 scheduler unless the
|
||||
# car is on a pedal-long CC-only path, so this stays isolated from Bolt.
|
||||
# Do not expose the path at all when OP long is disabled in CarParams.
|
||||
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_PANDA_3D1_SCHED.value
|
||||
|
||||
use_panda_3d1_sched = (
|
||||
|
||||
@@ -54,10 +54,13 @@ from opendbc.car.gm.carcontroller import (
|
||||
get_acc_dashboard_status_active,
|
||||
get_stock_cc_active_for_cancel,
|
||||
shape_bolt_acc_pedal_low_speed_friction,
|
||||
shape_truck_friction_brake,
|
||||
shape_truck_pitch_accel,
|
||||
shape_truck_positive_accel,
|
||||
should_use_fixed_stopping_brake,
|
||||
should_activate_auto_hold,
|
||||
should_activate_volt_one_pedal,
|
||||
should_send_acc_2cd,
|
||||
should_send_adas_status,
|
||||
should_send_stock_long_cancel,
|
||||
should_spoof_dash_speed,
|
||||
@@ -356,6 +359,19 @@ def test_live_camera_path_does_not_send_pt_keepalive():
|
||||
assert get_adas_keepalive_step(cp, is_kaofui_car=True) is None
|
||||
|
||||
|
||||
def test_acc_2cd_replacement_only_used_with_live_camera_path():
|
||||
assert should_send_acc_2cd(SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_TRAILBLAZER, networkLocation=CarParams.NetworkLocation.fwdCamera, flags=0))
|
||||
assert not should_send_acc_2cd(SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_TRAILBLAZER, networkLocation=CarParams.NetworkLocation.fwdCamera, flags=GMFlags.NO_CAMERA.value))
|
||||
assert not should_send_acc_2cd(SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_TRAILBLAZER, networkLocation=CarParams.NetworkLocation.gateway, flags=0))
|
||||
assert not should_send_acc_2cd(SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_TRAILBLAZER_CC, networkLocation=CarParams.NetworkLocation.fwdCamera, flags=0))
|
||||
assert not should_send_acc_2cd(SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_BLAZER, networkLocation=CarParams.NetworkLocation.fwdCamera, flags=0))
|
||||
|
||||
|
||||
def test_ascm_int_cars_do_not_send_radar_status():
|
||||
common = {
|
||||
"networkLocation": CarParams.NetworkLocation.fwdCamera,
|
||||
@@ -411,7 +427,7 @@ def test_volt_auto_hold_requires_toggle_supported_non_cc_only_volt_and_stock_saf
|
||||
),
|
||||
True,
|
||||
)
|
||||
assert supports_volt_auto_hold(
|
||||
assert not supports_volt_auto_hold(
|
||||
SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_VOLT,
|
||||
openpilotLongitudinalControl=False,
|
||||
@@ -420,7 +436,7 @@ def test_volt_auto_hold_requires_toggle_supported_non_cc_only_volt_and_stock_saf
|
||||
),
|
||||
True,
|
||||
)
|
||||
assert supports_volt_auto_hold(
|
||||
assert not supports_volt_auto_hold(
|
||||
SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_VOLT_2019,
|
||||
openpilotLongitudinalControl=False,
|
||||
@@ -464,6 +480,7 @@ def test_volt_one_pedal_requires_toggle_supported_volt_stock_safety_and_ev_trans
|
||||
assert supports_volt_one_pedal(
|
||||
SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
|
||||
openpilotLongitudinalControl=True,
|
||||
safetyConfigs=stock_safety,
|
||||
transmissionType=structs.CarParams.TransmissionType.direct,
|
||||
),
|
||||
@@ -472,6 +489,7 @@ def test_volt_one_pedal_requires_toggle_supported_volt_stock_safety_and_ev_trans
|
||||
assert not supports_volt_one_pedal(
|
||||
SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
|
||||
openpilotLongitudinalControl=True,
|
||||
safetyConfigs=no_safety,
|
||||
transmissionType=structs.CarParams.TransmissionType.direct,
|
||||
),
|
||||
@@ -480,6 +498,7 @@ def test_volt_one_pedal_requires_toggle_supported_volt_stock_safety_and_ev_trans
|
||||
assert not supports_volt_one_pedal(
|
||||
SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_VOLT_CC,
|
||||
openpilotLongitudinalControl=True,
|
||||
safetyConfigs=stock_safety,
|
||||
transmissionType=structs.CarParams.TransmissionType.direct,
|
||||
),
|
||||
@@ -488,6 +507,7 @@ def test_volt_one_pedal_requires_toggle_supported_volt_stock_safety_and_ev_trans
|
||||
assert not supports_volt_one_pedal(
|
||||
SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
|
||||
openpilotLongitudinalControl=True,
|
||||
safetyConfigs=stock_safety,
|
||||
transmissionType=structs.CarParams.TransmissionType.automatic,
|
||||
),
|
||||
@@ -496,6 +516,16 @@ def test_volt_one_pedal_requires_toggle_supported_volt_stock_safety_and_ev_trans
|
||||
assert not supports_volt_one_pedal(
|
||||
SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
|
||||
openpilotLongitudinalControl=False,
|
||||
safetyConfigs=stock_safety,
|
||||
transmissionType=structs.CarParams.TransmissionType.direct,
|
||||
),
|
||||
True,
|
||||
)
|
||||
assert not supports_volt_one_pedal(
|
||||
SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_VOLT_CAMERA,
|
||||
openpilotLongitudinalControl=True,
|
||||
safetyConfigs=stock_safety,
|
||||
transmissionType=structs.CarParams.TransmissionType.direct,
|
||||
),
|
||||
@@ -799,7 +829,7 @@ def test_calc_pedal_command_keeps_strong_positive_requests_responsive():
|
||||
def test_shape_truck_positive_accel_softens_small_highway_requests():
|
||||
shaped = shape_truck_positive_accel(0.12, 26.0, True)
|
||||
|
||||
assert 0.09 < shaped < 0.10
|
||||
assert 0.08 < shaped < 0.095
|
||||
|
||||
|
||||
def test_shape_truck_positive_accel_keeps_mid_follow_requests_available():
|
||||
@@ -817,6 +847,46 @@ def test_shape_truck_positive_accel_is_inactive_when_disabled_or_low_speed():
|
||||
assert shape_truck_positive_accel(0.12, 6.0, True) == 0.12
|
||||
|
||||
|
||||
def test_shape_truck_positive_accel_preserves_more_follow_authority_with_lead():
|
||||
base = shape_truck_positive_accel(0.28, 26.0, True)
|
||||
relieved = shape_truck_positive_accel(0.28, 26.0, True, lead_visible=True, set_speed_error=6.0)
|
||||
|
||||
assert relieved > base
|
||||
assert relieved < 0.28
|
||||
|
||||
|
||||
def test_shape_truck_positive_accel_does_not_relax_without_speed_error():
|
||||
base = shape_truck_positive_accel(0.28, 26.0, True)
|
||||
no_error = shape_truck_positive_accel(0.28, 26.0, True, lead_visible=True, set_speed_error=0.0)
|
||||
|
||||
assert no_error == base
|
||||
|
||||
|
||||
def test_shape_truck_pitch_accel_attenuates_highway_grade_feedforward():
|
||||
assert shape_truck_pitch_accel(-0.30, 30.0, True) == pytest.approx(-0.0825)
|
||||
assert shape_truck_pitch_accel(0.30, 30.0, True) == pytest.approx(0.0825)
|
||||
|
||||
|
||||
def test_shape_truck_pitch_accel_is_inactive_without_truck_tuning():
|
||||
assert shape_truck_pitch_accel(-0.30, 30.0, False) == pytest.approx(-0.30)
|
||||
|
||||
|
||||
def test_shape_truck_friction_brake_suppresses_boundary_chatter():
|
||||
assert shape_truck_friction_brake(14, -0.3, False, False) == (0, False)
|
||||
|
||||
|
||||
def test_shape_truck_friction_brake_uses_hysteresis_once_engaged():
|
||||
assert shape_truck_friction_brake(39, -0.3, False, False) == (0, False)
|
||||
assert shape_truck_friction_brake(40, -0.3, False, False) == (40, True)
|
||||
assert shape_truck_friction_brake(14, -0.3, False, True) == (14, True)
|
||||
assert shape_truck_friction_brake(8, -0.3, False, True) == (0, False)
|
||||
|
||||
|
||||
def test_shape_truck_friction_brake_never_delays_meaningful_braking():
|
||||
assert shape_truck_friction_brake(5, -0.85, False, False) == (5, True)
|
||||
assert shape_truck_friction_brake(5, -0.2, True, False) == (5, True)
|
||||
|
||||
|
||||
def test_use_interceptor_sng_launch_requires_actual_near_stop():
|
||||
CP = SimpleNamespace(vEgoStarting=0.25)
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ from opendbc.car.gm import gmcan
|
||||
from opendbc.car.gm.carstate import CarState as GMCarState, get_hard_cruise_buttons, update_auto_hold_drive_timers
|
||||
from opendbc.car.gm.carcontroller import (
|
||||
VisualAlert,
|
||||
get_acc_dashboard_always_one,
|
||||
get_acc_dashboard_fcw_alert,
|
||||
get_volt_one_pedal_lift_brake,
|
||||
should_send_acc_dashboard_status,
|
||||
@@ -65,6 +66,38 @@ class TestGMFingerprint:
|
||||
|
||||
|
||||
class TestGMInterface:
|
||||
@parameterized.expand([
|
||||
CAR.CHEVROLET_BOLT_CC_2017,
|
||||
CAR.CHEVROLET_BOLT_CC_2018_2021,
|
||||
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
CAR.CHEVROLET_BOLT_CC_2022_2023,
|
||||
CAR.CHEVROLET_MALIBU_HYBRID_CC,
|
||||
])
|
||||
def test_bolt_pedal_long_uses_shared_planning_delay_without_retuning_pid(self, car_model):
|
||||
CarInterface = interfaces[car_model]
|
||||
fingerprint = _empty_fingerprint()
|
||||
fingerprint[0][0x201] = 8
|
||||
params = Params()
|
||||
|
||||
try:
|
||||
params.put_bool("GMPedalLongitudinal", True)
|
||||
car_params = CarInterface.get_params(
|
||||
car_model,
|
||||
fingerprint,
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=_test_starpilot_toggles(),
|
||||
)
|
||||
finally:
|
||||
params.remove("GMPedalLongitudinal")
|
||||
|
||||
assert car_params.longitudinalActuatorDelay == pytest.approx(0.6)
|
||||
assert list(car_params.longitudinalTuning.kpV) == pytest.approx([0.095, 0.085, 0.065, 0.050])
|
||||
assert list(car_params.longitudinalTuning.kiV) == pytest.approx([0.07, 0.10, 0.15, 0.24])
|
||||
assert car_params.longitudinalTuning.kfDEPRECATED == pytest.approx(0.20)
|
||||
|
||||
def test_bolt_acc_pedal_pid_accel_limits_keep_full_negative_authority(self):
|
||||
cp = SimpleNamespace(
|
||||
enableGasInterceptorDEPRECATED=True,
|
||||
@@ -168,6 +201,21 @@ class TestGMInterface:
|
||||
assert car_params.flags & GMFlags.NO_CAMERA.value
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_NO_CAMERA.value
|
||||
|
||||
def test_volt_ascm_sparse_fingerprint_without_camera_does_not_set_no_camera(self):
|
||||
CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM]
|
||||
fingerprint = {
|
||||
0: FINGERPRINTS[CAR.CHEVROLET_VOLT][0].copy(),
|
||||
1: {},
|
||||
}
|
||||
fingerprint[0][0x2FF] = 8 # SASCM detected
|
||||
|
||||
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=False, is_release=False,
|
||||
docs=False, starpilot_toggles=_test_starpilot_toggles())
|
||||
|
||||
assert not (car_params.flags & GMFlags.NO_CAMERA.value)
|
||||
assert not (car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_NO_CAMERA.value)
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.HW_ASCM_INT.value
|
||||
|
||||
def test_silverado_alpha_long_uses_trimmed_longitudinal_tune(self):
|
||||
CarInterface = interfaces[CAR.CHEVROLET_SILVERADO]
|
||||
fingerprint = _empty_fingerprint()
|
||||
@@ -181,7 +229,7 @@ class TestGMInterface:
|
||||
assert list(car_params.longitudinalTuning.kpBP) == pytest.approx([0.0, 5.0, 15.0, 35.0])
|
||||
assert list(car_params.longitudinalTuning.kpV) == pytest.approx([0.02, 0.03, 0.028, 0.022])
|
||||
assert list(car_params.longitudinalTuning.kiBP) == pytest.approx([0.0, 5.0, 15.0, 35.0])
|
||||
assert list(car_params.longitudinalTuning.kiV) == pytest.approx([0.28, 0.26, 0.20, 0.16])
|
||||
assert list(car_params.longitudinalTuning.kiV) == pytest.approx([0.20, 0.18, 0.13, 0.08])
|
||||
|
||||
def test_blazer_uses_softer_low_speed_stop_hold_tune(self):
|
||||
CarInterface = interfaces[CAR.CHEVROLET_BLAZER]
|
||||
@@ -235,6 +283,22 @@ class TestGMInterface:
|
||||
assert car_params.openpilotLongitudinalControl
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
|
||||
|
||||
def test_volt_auto_hold_does_not_set_stock_hold_safety_bit_with_op_long_disabled(self):
|
||||
CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM]
|
||||
fingerprint = _empty_fingerprint()
|
||||
fingerprint[0][0x2FF] = 8
|
||||
|
||||
params = Params()
|
||||
try:
|
||||
params.put_bool("GMAutoHold", True)
|
||||
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=False, is_release=False,
|
||||
docs=False, starpilot_toggles=_test_starpilot_toggles())
|
||||
finally:
|
||||
params.remove("GMAutoHold")
|
||||
|
||||
assert not car_params.openpilotLongitudinalControl
|
||||
assert not (car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value)
|
||||
|
||||
def test_volt_one_pedal_sets_stock_hold_safety_bit_without_auto_hold(self):
|
||||
CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM]
|
||||
fingerprint = _empty_fingerprint()
|
||||
@@ -254,6 +318,25 @@ class TestGMInterface:
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_3D1_SCHED.value
|
||||
|
||||
def test_volt_one_pedal_does_not_set_stock_hold_safety_bits_with_op_long_disabled(self):
|
||||
CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM]
|
||||
fingerprint = _empty_fingerprint()
|
||||
fingerprint[0][0x2FF] = 8
|
||||
|
||||
params = Params()
|
||||
try:
|
||||
params.put_bool("GMAutoHold", False)
|
||||
params.put_bool("VoltOnePedalMode", True)
|
||||
car_params = CarInterface.get_params(CAR.CHEVROLET_VOLT_ASCM, fingerprint, [], alpha_long=False, is_release=False,
|
||||
docs=False, starpilot_toggles=_test_starpilot_toggles())
|
||||
finally:
|
||||
params.remove("GMAutoHold")
|
||||
params.remove("VoltOnePedalMode")
|
||||
|
||||
assert not car_params.openpilotLongitudinalControl
|
||||
assert not (car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value)
|
||||
assert not (car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_3D1_SCHED.value)
|
||||
|
||||
@parameterized.expand(VOLT_CARS)
|
||||
def test_volt_bsm_is_enabled_without_fingerprint_match(self, car_model):
|
||||
CarInterface = interfaces[car_model]
|
||||
@@ -311,6 +394,9 @@ class TestGMInterface:
|
||||
assert car_params.alternativeExperience & ALTERNATIVE_EXPERIENCE.GM_REMAP_CANCEL_TO_DISTANCE
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_BOLT_2022_PEDAL.value
|
||||
assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_PANDA_PADDLE_SCHED.value
|
||||
assert car_params.startingState
|
||||
assert car_params.startAccel == pytest.approx(0.55)
|
||||
assert car_params.vEgoStarting == pytest.approx(0.35)
|
||||
|
||||
def test_cadillac_xt5_sdgm_sascm_gates_alpha_long(self):
|
||||
CarInterface = interfaces[CAR.CADILLAC_XT5]
|
||||
@@ -379,6 +465,19 @@ class TestGMInterface:
|
||||
assert high_torque == pytest.approx(linear_high_torque, rel=0.03)
|
||||
assert torque_from_lataccel(-low_lataccel, car_params.lateralTuning.torque) == pytest.approx(-low_torque, rel=1e-6)
|
||||
|
||||
@parameterized.expand((CAR.CHEVROLET_VOLT_ASCM, CAR.CHEVROLET_VOLT_CAMERA, CAR.CHEVROLET_VOLT_CC, CAR.CHEVROLET_VOLT_2019))
|
||||
def test_volt_integration_variants_share_nonlinear_torque_curve(self, candidate):
|
||||
assert gm_interface.get_nonlinear_torque_params(candidate) == gm_interface.NON_LINEAR_TORQUE_PARAMS[CAR.CHEVROLET_VOLT]
|
||||
|
||||
CarInterface = interfaces[candidate]
|
||||
car_params = CarInterface.get_non_essential_params(candidate)
|
||||
ci = CarInterface(car_params, custom.StarPilotCarParams.new_message())
|
||||
torque_from_lataccel = ci.torque_from_lateral_accel()
|
||||
|
||||
left_torque = torque_from_lataccel(0.5, car_params.lateralTuning.torque)
|
||||
right_torque = torque_from_lataccel(-0.5, car_params.lateralTuning.torque)
|
||||
assert left_torque > abs(right_torque)
|
||||
|
||||
|
||||
class TestGMCarController:
|
||||
def test_dash_speed_spoof_respects_live_stock_acc_toggles(self):
|
||||
@@ -490,6 +589,127 @@ class TestGMCarController:
|
||||
|
||||
assert [msg[2] for msg in msgs] == [0]
|
||||
|
||||
@parameterized.expand([
|
||||
CAR.CHEVROLET_BOLT_CC_2017,
|
||||
CAR.CHEVROLET_BOLT_CC_2018_2021,
|
||||
CAR.CHEVROLET_BOLT_CC_2022_2023,
|
||||
])
|
||||
def test_bolt_cc_redneck_ignores_small_setpoint_error(self, car_model):
|
||||
packer = CANPacker(DBC[car_model][Bus.pt])
|
||||
controller = SimpleNamespace(frame=int(2.0 / DT_CTRL), last_button_frame=0, apply_speed=0)
|
||||
cs = SimpleNamespace(
|
||||
CP=SimpleNamespace(
|
||||
carFingerprint=car_model,
|
||||
minEnableSpeed=24 * CV.MPH_TO_MS,
|
||||
),
|
||||
buttons_counter=2,
|
||||
out=SimpleNamespace(
|
||||
vEgo=(60.6 * CV.MPH_TO_MS) / 1.01,
|
||||
cruiseState=SimpleNamespace(speed=60 * CV.MPH_TO_MS),
|
||||
),
|
||||
)
|
||||
|
||||
msgs = gmcan.create_gm_cc_spam_command(packer, controller, cs, SimpleNamespace(accel=0.0), SimpleNamespace(is_metric=False))
|
||||
|
||||
assert msgs == []
|
||||
assert controller.apply_speed == 60
|
||||
|
||||
def test_non_bolt_cc_redneck_keeps_existing_setpoint_selector(self):
|
||||
packer = CANPacker(DBC[CAR.CHEVROLET_EQUINOX_CC][Bus.pt])
|
||||
controller = SimpleNamespace(frame=int(2.0 / DT_CTRL), last_button_frame=0, apply_speed=0)
|
||||
cs = SimpleNamespace(
|
||||
CP=SimpleNamespace(
|
||||
carFingerprint=CAR.CHEVROLET_EQUINOX_CC,
|
||||
flags=GMFlags.CC_LONG.value,
|
||||
minEnableSpeed=24 * CV.MPH_TO_MS,
|
||||
),
|
||||
buttons_counter=2,
|
||||
out=SimpleNamespace(
|
||||
vEgo=(60.6 * CV.MPH_TO_MS) / 1.01,
|
||||
cruiseState=SimpleNamespace(speed=60 * CV.MPH_TO_MS),
|
||||
),
|
||||
)
|
||||
|
||||
msgs = gmcan.create_gm_cc_spam_command(packer, controller, cs, SimpleNamespace(accel=0.0), SimpleNamespace(is_metric=False))
|
||||
|
||||
assert len(msgs) == 1
|
||||
assert controller.apply_speed == 61
|
||||
|
||||
def test_bolt_cc_redneck_requires_persistent_acceleration_after_deceleration(self):
|
||||
cp = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021)
|
||||
controller = SimpleNamespace(
|
||||
frame=100,
|
||||
gm_cc_last_direction_button=CruiseButtons.DECEL_SET,
|
||||
gm_cc_last_direction_frame=100,
|
||||
gm_cc_pending_reverse_button=CruiseButtons.INIT,
|
||||
gm_cc_pending_reverse_frame=0,
|
||||
)
|
||||
|
||||
assert gmcan.stabilize_bolt_cc_button(controller, cp, CruiseButtons.RES_ACCEL) == CruiseButtons.INIT
|
||||
controller.frame += int(0.5 / DT_CTRL)
|
||||
assert gmcan.stabilize_bolt_cc_button(controller, cp, CruiseButtons.RES_ACCEL) == CruiseButtons.INIT
|
||||
controller.frame += int(0.11 / DT_CTRL)
|
||||
assert gmcan.stabilize_bolt_cc_button(controller, cp, CruiseButtons.RES_ACCEL) == CruiseButtons.RES_ACCEL
|
||||
|
||||
def test_bolt_cc_redneck_deceleration_is_not_debounced(self):
|
||||
cp = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_CC_2018_2021)
|
||||
controller = SimpleNamespace(
|
||||
frame=100,
|
||||
gm_cc_last_direction_button=CruiseButtons.RES_ACCEL,
|
||||
gm_cc_last_direction_frame=100,
|
||||
gm_cc_pending_reverse_button=CruiseButtons.INIT,
|
||||
gm_cc_pending_reverse_frame=0,
|
||||
)
|
||||
|
||||
assert gmcan.stabilize_bolt_cc_button(controller, cp, CruiseButtons.DECEL_SET) == CruiseButtons.DECEL_SET
|
||||
|
||||
def test_xt4_cc_redneck_spam_matches_physical_button_burst(self):
|
||||
packer = CANPacker(DBC[CAR.CADILLAC_XT4_CC][Bus.pt])
|
||||
controller = SimpleNamespace(
|
||||
frame=int(0.3 / DT_CTRL),
|
||||
last_button_frame=0,
|
||||
apply_speed=0,
|
||||
malibu_button_phase=0,
|
||||
xt4_cc_button_burst_remaining=0,
|
||||
xt4_cc_button_burst_button=CruiseButtons.INIT,
|
||||
xt4_cc_button_burst_last_counter=-1,
|
||||
xt4_cc_button_observed_counter=-1,
|
||||
xt4_cc_button_counter_frame=0,
|
||||
)
|
||||
cs = SimpleNamespace(
|
||||
CP=SimpleNamespace(
|
||||
carFingerprint=CAR.CADILLAC_XT4_CC,
|
||||
flags=GMFlags.CC_LONG.value,
|
||||
minEnableSpeed=24 * CV.MPH_TO_MS,
|
||||
),
|
||||
buttons_counter=0,
|
||||
out=SimpleNamespace(
|
||||
vEgo=25.0,
|
||||
cruiseState=SimpleNamespace(speed=20.0),
|
||||
),
|
||||
)
|
||||
actuators = SimpleNamespace(accel=1.0)
|
||||
|
||||
dats = []
|
||||
send_counts = []
|
||||
for counter in (0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 0, 0, 0, 1, 1, 1):
|
||||
cs.buttons_counter = counter
|
||||
msgs = gmcan.create_gm_cc_spam_command(packer, controller, cs, actuators, SimpleNamespace(is_metric=False))
|
||||
send_counts.append(len(msgs))
|
||||
dats.extend(bytes(msg[1]).hex() for msg in msgs)
|
||||
controller.frame += 1
|
||||
|
||||
assert send_counts == [0, 1, 0] * gmcan.XT4_CC_BUTTON_BURST_FRAMES
|
||||
assert dats == [
|
||||
"000000010125de",
|
||||
"00000001022acd",
|
||||
"00000001032fbc",
|
||||
"000000010020ef",
|
||||
"000000010125de",
|
||||
"00000001022acd",
|
||||
]
|
||||
assert controller.xt4_cc_button_burst_remaining == 0
|
||||
|
||||
def test_acc_dashboard_command_preserves_raw_fcw_alert_level(self):
|
||||
packer = CANPacker(DBC[CAR.CHEVROLET_BOLT_ACC_2022_2023][Bus.pt])
|
||||
parser = CANParser(DBC[CAR.CHEVROLET_BOLT_ACC_2022_2023][Bus.pt], [("ASCMActiveCruiseControlStatus", 0)], 0)
|
||||
@@ -504,7 +724,39 @@ class TestGMCarController:
|
||||
|
||||
parser.update([0, [msg]])
|
||||
|
||||
assert parser.vl["ASCMActiveCruiseControlStatus"]["FCWAlert"] == 2
|
||||
values = parser.vl["ASCMActiveCruiseControlStatus"]
|
||||
|
||||
assert values["ACCAlwaysOne"] == 1
|
||||
assert values["ACCAlwaysOne2"] == 1
|
||||
assert values["ACCCruiseState"] == 2
|
||||
assert values["ACCCmdActive"] == 1
|
||||
assert values["FCWAlert"] == 2
|
||||
|
||||
def test_acc_dashboard_command_allows_camera_acc_zero_reserved_bits(self):
|
||||
packer = CANPacker(DBC[CAR.CHEVROLET_TRAILBLAZER][Bus.pt])
|
||||
parser = CANParser(DBC[CAR.CHEVROLET_TRAILBLAZER][Bus.pt], [("ASCMActiveCruiseControlStatus", 0)], 0)
|
||||
msg = gmcan.create_acc_dashboard_command(
|
||||
packer,
|
||||
0,
|
||||
True,
|
||||
67.1875,
|
||||
SimpleNamespace(leadDistanceBars=1, leadVisible=False),
|
||||
0,
|
||||
acc_always_one=0,
|
||||
)
|
||||
|
||||
parser.update([0, [msg]])
|
||||
values = parser.vl["ASCMActiveCruiseControlStatus"]
|
||||
|
||||
assert msg[1] == b"\x00\x02\x94\x33\x00\x00"
|
||||
assert values["ACCAlwaysOne"] == 0
|
||||
assert values["ACCAlwaysOne2"] == 0
|
||||
assert values["ACCCruiseState"] == 2
|
||||
assert values["ACCCmdActive"] == 1
|
||||
|
||||
def test_acc_dashboard_always_one_matches_camera_acc_platforms(self):
|
||||
assert get_acc_dashboard_always_one(SimpleNamespace(carFingerprint=CAR.CHEVROLET_TRAILBLAZER)) == 0
|
||||
assert get_acc_dashboard_always_one(SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023)) == 1
|
||||
|
||||
def test_acc_dashboard_command_uses_openpilot_hud_when_disengaged(self):
|
||||
packer = CANPacker(DBC[CAR.CHEVROLET_VOLT_ASCM][Bus.pt])
|
||||
@@ -522,6 +774,7 @@ class TestGMCarController:
|
||||
values = parser.vl["ASCMActiveCruiseControlStatus"]
|
||||
|
||||
assert values["ACCSpeedSetpoint"] == 50
|
||||
assert values["ACCCruiseState"] == 2
|
||||
assert values["ACCGapLevel"] == 0
|
||||
assert values["ACCCmdActive"] == 0
|
||||
assert values["ACCLeadCar"] == 1
|
||||
|
||||
@@ -28,6 +28,14 @@ class TestGMCan:
|
||||
assert dat[1] & 0x1
|
||||
assert decoded == 8848
|
||||
|
||||
def test_acc_2cd_command_matches_stock_camera_counter_layout(self):
|
||||
assert [gmcan.create_acc_2cd_command(0, idx)[1].hex() for idx in range(4)] == [
|
||||
"002c03d3fd",
|
||||
"402c03d3fc",
|
||||
"802c03d3fb",
|
||||
"c02c03d3fa",
|
||||
]
|
||||
|
||||
def test_prndl2_command_matches_bolt_gen2_regen_paddle_spoof(self):
|
||||
CP = SimpleNamespace(carFingerprint=CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL)
|
||||
|
||||
@@ -61,7 +69,6 @@ class TestGMCan:
|
||||
assert bus == 0
|
||||
assert dat.hex() == "20000000000000"
|
||||
|
||||
|
||||
def test_gas_regen_command_matches_starpilot_volt_2019(self):
|
||||
packer = CANPacker(DBC[CAR.CHEVROLET_VOLT_2019]["pt"])
|
||||
addr, dat, bus = gmcan.create_gas_regen_command(packer, 0, 5000, 1, True, False, include_always_one3=True, use_volt_layout=True)
|
||||
|
||||
@@ -241,17 +241,17 @@ class CAR(Platforms):
|
||||
},
|
||||
)
|
||||
CHEVROLET_VOLT_ASCM = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Volt 2017-18 ASCM Harness", min_enable_speed=0, video="https://youtu.be/QeMCN_4TFfQ")],
|
||||
[GMCarDocs("Chevrolet Volt ASCM Harness 2017-18", min_enable_speed=0, video="https://youtu.be/QeMCN_4TFfQ")],
|
||||
CHEVROLET_VOLT.specs,
|
||||
dbc_dict=CHEVROLET_VOLT.dbc_dict,
|
||||
)
|
||||
CHEVROLET_VOLT_CAMERA = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Volt 2017-18 Camera Harness", "Flashed camera-forward integration with ACC")],
|
||||
[GMCarDocs("Chevrolet Volt Camera Harness 2017-18", "Flashed camera-forward integration with ACC")],
|
||||
CHEVROLET_VOLT.specs,
|
||||
dbc_dict=CHEVROLET_VOLT.dbc_dict,
|
||||
)
|
||||
CHEVROLET_VOLT_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Volt 2017-18 - No-ACC", min_enable_speed=0)],
|
||||
[GMCarDocs("Chevrolet Volt No-ACC 2017-18", min_enable_speed=0)],
|
||||
CHEVROLET_VOLT.specs,
|
||||
dbc_dict=CHEVROLET_VOLT.dbc_dict,
|
||||
)
|
||||
@@ -264,7 +264,7 @@ class CAR(Platforms):
|
||||
GMCarSpecs(mass=1496, wheelbase=2.83, steerRatio=15.8, centerToFrontRatio=0.4),
|
||||
)
|
||||
CHEVROLET_MALIBU_ASCM = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Malibu 2017-19 ASCM Harness")],
|
||||
[GMCarDocs("Chevrolet Malibu ASCM Harness 2017-19")],
|
||||
CHEVROLET_MALIBU.specs,
|
||||
)
|
||||
GMC_ACADIA = GMASCMPlatformConfig(
|
||||
@@ -272,7 +272,7 @@ class CAR(Platforms):
|
||||
GMCarSpecs(mass=1975, wheelbase=2.86, steerRatio=14.4, centerToFrontRatio=0.4),
|
||||
)
|
||||
GMC_ACADIA_ASCM = GMPlatformConfig(
|
||||
[GMCarDocs("GMC Acadia 2018 ASCM Harness", video="https://www.youtube.com/watch?v=0ZN6DdsBUZo")],
|
||||
[GMCarDocs("GMC Acadia ASCM Harness 2018", video="https://www.youtube.com/watch?v=0ZN6DdsBUZo")],
|
||||
GMC_ACADIA.specs,
|
||||
)
|
||||
BUICK_LACROSSE = GMASCMPlatformConfig(
|
||||
@@ -280,7 +280,11 @@ class CAR(Platforms):
|
||||
GMCarSpecs(mass=1712, wheelbase=2.91, steerRatio=15.8, centerToFrontRatio=0.4),
|
||||
)
|
||||
BUICK_LACROSSE_ASCM = GMPlatformConfig(
|
||||
[GMCarDocs("Buick LaCrosse 2017-19 ASCM Harness")],
|
||||
[GMCarDocs("Buick LaCrosse ASCM Harness 2017-19")],
|
||||
BUICK_LACROSSE.specs,
|
||||
)
|
||||
BUICK_LACROSSE_ASCM_19US = GMPlatformConfig(
|
||||
[GMCarDocs("Buick LaCrosse US ASCM Harness 2019")],
|
||||
BUICK_LACROSSE.specs,
|
||||
)
|
||||
BUICK_REGAL = GMASCMPlatformConfig(
|
||||
@@ -292,7 +296,7 @@ class CAR(Platforms):
|
||||
GMCarSpecs(mass=2564, wheelbase=2.95, steerRatio=17.3),
|
||||
)
|
||||
CADILLAC_ESCALADE_ASCM = GMPlatformConfig(
|
||||
[GMCarDocs("Cadillac Escalade 2018 ASCM Harness", "Driver Assist Package")],
|
||||
[GMCarDocs("Cadillac Escalade ASCM Harness 2018", "Driver Assist Package")],
|
||||
CADILLAC_ESCALADE.specs,
|
||||
)
|
||||
CADILLAC_ESCALADE_ESV = GMASCMPlatformConfig(
|
||||
@@ -304,7 +308,7 @@ class CAR(Platforms):
|
||||
CADILLAC_ESCALADE_ESV.specs,
|
||||
)
|
||||
CADILLAC_ESCALADE_ESV_2019_ASCM = GMPlatformConfig(
|
||||
[GMCarDocs("Cadillac Escalade ESV Platinum 2019 ASCM Harness", "Adaptive Cruise Control (ACC) & LKAS")],
|
||||
[GMCarDocs("Cadillac Escalade ESV Platinum ASCM Harness 2019", "Adaptive Cruise Control (ACC) & LKAS")],
|
||||
CADILLAC_ESCALADE_ESV_2019.specs,
|
||||
)
|
||||
CHEVROLET_BOLT_ACC_2022_2023 = GMPlatformConfig(
|
||||
@@ -314,19 +318,19 @@ class CAR(Platforms):
|
||||
GMCarSpecs(mass=1669, wheelbase=2.63779, steerRatio=16.8, centerToFrontRatio=0.4, tireStiffnessFactor=1.0),
|
||||
)
|
||||
CHEVROLET_BOLT_ACC_2022_2023_PEDAL = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Bolt EV 2022-23 ACC w Pedal")],
|
||||
[GMCarDocs("Chevrolet Bolt EV ACC w Pedal 2022-23")],
|
||||
CHEVROLET_BOLT_ACC_2022_2023.specs,
|
||||
)
|
||||
CHEVROLET_BOLT_CC_2022_2023 = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Bolt EV 2022-23 - No-ACC")],
|
||||
[GMCarDocs("Chevrolet Bolt EV No-ACC 2022-23")],
|
||||
CHEVROLET_BOLT_ACC_2022_2023.specs,
|
||||
)
|
||||
CHEVROLET_BOLT_CC_2018_2021 = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Bolt EV 2018-21 - No-ACC")],
|
||||
[GMCarDocs("Chevrolet Bolt EV No-ACC 2018-21")],
|
||||
CHEVROLET_BOLT_ACC_2022_2023.specs,
|
||||
)
|
||||
CHEVROLET_BOLT_CC_2017 = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Bolt EV 2017 - No-ACC")],
|
||||
[GMCarDocs("Chevrolet Bolt EV No-ACC 2017")],
|
||||
CHEVROLET_BOLT_ACC_2022_2023.specs,
|
||||
)
|
||||
|
||||
@@ -339,8 +343,8 @@ class CAR(Platforms):
|
||||
)
|
||||
CHEVROLET_SILVERADO_CC = GMPlatformConfig(
|
||||
[
|
||||
GMCarDocs("Chevrolet Silverado 1500 - No-ACC"),
|
||||
GMCarDocs("GMC Sierra 1500 - No-ACC"),
|
||||
GMCarDocs("Chevrolet Silverado 1500 No-ACC 2020-21"),
|
||||
GMCarDocs("GMC Sierra 1500 No-ACC 2020-21"),
|
||||
],
|
||||
CHEVROLET_SILVERADO.specs,
|
||||
)
|
||||
@@ -357,7 +361,7 @@ class CAR(Platforms):
|
||||
CarSpecs(mass=2731, wheelbase=3.302, steerRatio=17.3, centerToFrontRatio=0.49),
|
||||
)
|
||||
GMC_YUKON_CC = GMPlatformConfig(
|
||||
[GMCarDocs("GMC Yukon - No-ACC")],
|
||||
[GMCarDocs("GMC Yukon No-ACC 2019-20")],
|
||||
CarSpecs(mass=2541, wheelbase=2.95, steerRatio=16.3, centerToFrontRatio=0.4),
|
||||
)
|
||||
GMC_YUKON = GMPlatformConfig(
|
||||
@@ -368,6 +372,10 @@ class CAR(Platforms):
|
||||
[GMCarDocs("Cadillac XT4 2023", "Driver Assist Package")],
|
||||
GMCarSpecs(mass=1660, wheelbase=2.78, steerRatio=14.4, centerToFrontRatio=0.4),
|
||||
)
|
||||
CADILLAC_XT4_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Cadillac XT4 No-ACC 2023")],
|
||||
CADILLAC_XT4.specs,
|
||||
)
|
||||
CADILLAC_XT5 = GMSDGMPlatformConfig(
|
||||
[GMCarDocs("Cadillac XT5 2022", "Driver Assist Package")],
|
||||
CarSpecs(mass=1810, wheelbase=2.86, steerRatio=16.34, centerToFrontRatio=0.5),
|
||||
@@ -397,31 +405,31 @@ class CAR(Platforms):
|
||||
CarSpecs(mass=2050, wheelbase=2.86, steerRatio=16.0, centerToFrontRatio=0.5),
|
||||
)
|
||||
CADILLAC_CT6_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Cadillac CT6 - No-ACC")],
|
||||
[GMCarDocs("Cadillac CT6 No-ACC 2016-20")],
|
||||
CarSpecs(mass=2358, wheelbase=3.11, steerRatio=17.7, centerToFrontRatio=0.4),
|
||||
)
|
||||
CADILLAC_XT5_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Cadillac XT5 - No-ACC")],
|
||||
[GMCarDocs("Cadillac XT5 No-ACC 2022")],
|
||||
CADILLAC_XT5.specs,
|
||||
)
|
||||
CHEVROLET_EQUINOX_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Equinox 2019-22 - No-ACC")],
|
||||
[GMCarDocs("Chevrolet Equinox No-ACC 2019-22")],
|
||||
CHEVROLET_EQUINOX.specs,
|
||||
)
|
||||
CHEVROLET_MALIBU_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Malibu 2023 - No-ACC")],
|
||||
[GMCarDocs("Chevrolet Malibu No-ACC 2023")],
|
||||
CarSpecs(mass=1450, wheelbase=2.8, steerRatio=18.25, centerToFrontRatio=0.4, tireStiffnessFactor=0.997),
|
||||
)
|
||||
CHEVROLET_MALIBU_HYBRID_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Malibu Hybrid 2017 - No-ACC")],
|
||||
[GMCarDocs("Chevrolet Malibu Hybrid No-ACC 2017")],
|
||||
CarSpecs(mass=1450, wheelbase=2.8, steerRatio=15.8, centerToFrontRatio=0.4),
|
||||
)
|
||||
CHEVROLET_SUBURBAN_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Suburban Premier 2016-20 - No-ACC")],
|
||||
[GMCarDocs("Chevrolet Suburban Premier No-ACC 2016-20")],
|
||||
CHEVROLET_SUBURBAN.specs,
|
||||
)
|
||||
CHEVROLET_TRAILBLAZER_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Trailblazer 2021-22 - No-ACC")],
|
||||
[GMCarDocs("Chevrolet Trailblazer No-ACC 2021-22")],
|
||||
CHEVROLET_TRAILBLAZER.specs,
|
||||
)
|
||||
CHEVROLET_TRAX = GMPlatformConfig(
|
||||
@@ -570,6 +578,7 @@ CC_ONLY_CAR = {
|
||||
CAR.CHEVROLET_MALIBU_CC,
|
||||
CAR.CHEVROLET_MALIBU_HYBRID_CC,
|
||||
CAR.CHEVROLET_SILVERADO_CC,
|
||||
CAR.CADILLAC_XT4_CC,
|
||||
}
|
||||
CC_REGEN_PADDLE_CAR = {
|
||||
CAR.CHEVROLET_BOLT_CC_2018_2021,
|
||||
@@ -587,6 +596,7 @@ ASCM_INT = {
|
||||
CAR.CADILLAC_ESCALADE_ASCM,
|
||||
CAR.CADILLAC_ESCALADE_ESV_2019_ASCM,
|
||||
CAR.BUICK_LACROSSE_ASCM,
|
||||
CAR.BUICK_LACROSSE_ASCM_19US,
|
||||
}
|
||||
|
||||
STEER_THRESHOLD = 1.0
|
||||
|
||||
@@ -201,17 +201,18 @@ class CarState(CarStateBase):
|
||||
if self.CP.flags & HondaFlags.BOSCH_ALT_BRAKE:
|
||||
ret.brakePressed = cp.vl["BRAKE_MODULE"]["BRAKE_PRESSED"] != 0
|
||||
else:
|
||||
powertrain_data = cp.vl["POWERTRAIN_DATA"]
|
||||
# brake switch has shown some single time step noise, so only considered when
|
||||
# switch is on for at least 2 consecutive CAN samples
|
||||
# brake switch rises earlier than brake pressed but is never 1 when in park
|
||||
brake_switch_vals = cp.vl_all["POWERTRAIN_DATA"]["BRAKE_SWITCH"]
|
||||
if len(brake_switch_vals):
|
||||
brake_switch = cp.vl["POWERTRAIN_DATA"]["BRAKE_SWITCH"] != 0
|
||||
brake_switch = powertrain_data["BRAKE_SWITCH"] != 0
|
||||
if len(brake_switch_vals) > 1:
|
||||
self.brake_switch_prev = brake_switch_vals[-2] != 0
|
||||
self.brake_switch_active = brake_switch and self.brake_switch_prev
|
||||
self.brake_switch_prev = brake_switch
|
||||
ret.brakePressed = (cp.vl["POWERTRAIN_DATA"]["BRAKE_PRESSED"] != 0) or self.brake_switch_active
|
||||
ret.brakePressed = (powertrain_data["BRAKE_PRESSED"] != 0) or self.brake_switch_active
|
||||
|
||||
ret.brake = cp.vl["VSA_STATUS"]["USER_BRAKE"]
|
||||
ret.cruiseState.enabled = cp.vl["POWERTRAIN_DATA"]["ACC_STATUS"] != 0
|
||||
@@ -269,8 +270,14 @@ class CarState(CarStateBase):
|
||||
return ret, fp_ret
|
||||
|
||||
def get_can_parsers(self, CP):
|
||||
pt_messages = [("GAS_SENSOR", 0)] if CP.enableGasInterceptorDEPRECATED else []
|
||||
pt_parser = CANParser(DBC[CP.carFingerprint][Bus.pt], pt_messages, CanBus(CP).pt)
|
||||
if CP.enableGasInterceptorDEPRECATED:
|
||||
pt_parser.message_states[0x201].ignore_checksum = True
|
||||
pt_parser.message_states[0x201].ignore_counter = True
|
||||
|
||||
parsers = {
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).pt),
|
||||
Bus.pt: pt_parser,
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], CanBus(CP).camera),
|
||||
}
|
||||
if CP.enableBsm:
|
||||
|
||||
@@ -257,7 +257,6 @@ FW_VERSIONS = {
|
||||
(Ecu.eps, 0x18da30f1, None): [
|
||||
b'39990-TBA-C120\x00\x00',
|
||||
b'39990-TBA-C020\x00\x00',
|
||||
b'39990-TBA-C120\x00\x00',
|
||||
b'39990-TEA-T330\x00\x00',
|
||||
b'39990-TEA-T820\x00\x00',
|
||||
b'39990-TEZ-T020\x00\x00',
|
||||
|
||||
@@ -4,12 +4,12 @@ import numpy as np
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus, DT_CTRL, make_tester_present_msg, rate_limit, structs
|
||||
from opendbc.car.common.filter_simple import FirstOrderFilter
|
||||
from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_steer_angle_limits_vm, common_fault_avoidance
|
||||
from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_steer_angle_limits_vm, common_fault_avoidance, get_max_angle_delta_vm, get_max_angle_vm
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.hyundai import hyundaicanfd, hyundaican
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CAR, CANFD_RADAR_LIVE_LONGITUDINAL_CAR, \
|
||||
kia_ev6_gt_line_longitudinal_tuning
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CAR, CANFD_ANGLE_LONGITUDINAL_CAR, \
|
||||
CANFD_RADAR_LIVE_LONGITUDINAL_CAR, kia_ev6_gt_line_longitudinal_tuning
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
from openpilot.common.params import Params
|
||||
@@ -53,6 +53,7 @@ IONIQ_6_LAUNCH_HOLD_SPEED_V = [0.75, 0.6, 0.4, 0.0]
|
||||
IONIQ_6_STOP_BRAKE_CAP_MAX_SPEED = 2.0
|
||||
IONIQ_6_STOP_BRAKE_CAP_SPEED_BP = [0.0, 0.08, 0.25, 0.6, 1.2, 2.0, 3.0]
|
||||
IONIQ_6_STOP_BRAKE_CAP_ACCEL_V = [-0.15, -0.16, -0.22, -0.42, -0.78, -1.15, -1.40]
|
||||
EV6_GT_LINE_STOP_BRAKE_CAP_MAX_SPEED = 1.2
|
||||
IONIQ_6_STOP_HOLD_JERK_BP = [0.0, 0.15, 0.6, 1.2, 2.0, 3.0]
|
||||
IONIQ_6_STOP_HOLD_JERK_V = [0.35, 0.40, 0.48, 0.65, 0.85, 1.10]
|
||||
IONIQ_6_STOP_RELEASE_JERK_BP = [0.0, 0.15, 0.5]
|
||||
@@ -66,13 +67,16 @@ REDNECK_BUTTON_COPIES_TIME_METRIC = [REDNECK_BUTTON_COPIES_TIME, 40]
|
||||
ANGLE_SAFETY_BASELINE_MODEL = str(CAR.KIA_SPORTAGE_HEV_2026)
|
||||
DEFAULT_ANGLE_SMOOTHING_VEGO_BP = [5.0, 10.0, 20.0]
|
||||
DEFAULT_ANGLE_SMOOTHING_ALPHA_V = [0.2, 0.1, 0.0]
|
||||
EV9_HIGH_ANGLE_GAIN_BP = [70.0, 120.0, 220.0, 320.0]
|
||||
EV9_HIGH_ANGLE_GAIN_CAP_V = [0.85, 0.55, 0.30, 0.16]
|
||||
EV9_HIGH_ANGLE_GAIN_MIN = 0.004
|
||||
EV9_STOP_REQUEST_SPEED = 0.47
|
||||
EV9_STANDSTILL_DELAY_FRAMES = 178
|
||||
EV9_STOP_RELEASE_DELAY_FRAMES = 6
|
||||
BLINDSPOT_WARNING_FLASH_SAMPLES = 20
|
||||
BLINDSPOT_WARNING_FLASH_ON_SAMPLES = 16
|
||||
BLINDSPOT_WARNING_SOUND_SAMPLES = 36
|
||||
|
||||
|
||||
def egmp_dynamic_longitudinal_tuning(CP) -> bool:
|
||||
return CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 or \
|
||||
return CP.carFingerprint in (CAR.HYUNDAI_IONIQ_6, CAR.KIA_EV9) or \
|
||||
kia_ev6_gt_line_longitudinal_tuning(CP.carFingerprint, getattr(CP, "carVin", ""))
|
||||
|
||||
|
||||
@@ -108,6 +112,29 @@ class GenesisG90LongitudinalTuningState:
|
||||
long_control_state_last: LongCtrlState = LongCtrlState.off
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class EV9LongitudinalTuningState:
|
||||
stop_request: bool = False
|
||||
cruise_standstill: bool = False
|
||||
stop_request_frames: int = 0
|
||||
release_frames: int = 0
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class BlindspotWarningOutput:
|
||||
mirror_lamp_active: bool = False
|
||||
sound_active: bool = False
|
||||
|
||||
|
||||
@dataclass
|
||||
class BlindspotWarningState:
|
||||
flash_phase: int = 0
|
||||
mirror_warning_active: bool = False
|
||||
escalated_prev: bool = False
|
||||
sound_remaining: int = 0
|
||||
sound_armed: bool = True
|
||||
|
||||
|
||||
def _jerk_limited_integrator(desired_accel: float, last_accel: float, jerk_upper: float, jerk_lower: float) -> float:
|
||||
step = (jerk_upper if desired_accel >= last_accel else jerk_lower) * DT_CTRL * 5.0
|
||||
return float(np.clip(desired_accel, last_accel - step, last_accel + step))
|
||||
@@ -120,8 +147,93 @@ def _calculate_ioniq_6_dynamic_lower_jerk(accel_error: float) -> float:
|
||||
return IONIQ_6_LONG_MIN_JERK
|
||||
|
||||
|
||||
def should_track_stop_accel_directly(stopping: bool, v_ego: float,
|
||||
accel_cmd: float, actual_accel: float) -> bool:
|
||||
return bool(stopping and v_ego > EV6_GT_LINE_STOP_BRAKE_CAP_MAX_SPEED and accel_cmd < actual_accel)
|
||||
|
||||
|
||||
def should_use_ev6_gt_line_stop_direct_tracking(ev6_gt_line: bool, stopping: bool, v_ego: float,
|
||||
accel_cmd: float, actual_accel: float) -> bool:
|
||||
return bool(ev6_gt_line and stopping and v_ego > EV6_GT_LINE_STOP_BRAKE_CAP_MAX_SPEED and accel_cmd < actual_accel)
|
||||
|
||||
|
||||
def update_ev9_longitudinal_tuning(state: EV9LongitudinalTuningState, enabled: bool,
|
||||
stopping: bool, v_ego: float) -> EV9LongitudinalTuningState:
|
||||
if not enabled:
|
||||
return EV9LongitudinalTuningState()
|
||||
|
||||
if stopping:
|
||||
if not state.stop_request and v_ego > EV9_STOP_REQUEST_SPEED:
|
||||
return EV9LongitudinalTuningState()
|
||||
frames = state.stop_request_frames + 1 if state.stop_request else 0
|
||||
return EV9LongitudinalTuningState(
|
||||
stop_request=True,
|
||||
cruise_standstill=frames >= EV9_STANDSTILL_DELAY_FRAMES,
|
||||
stop_request_frames=frames,
|
||||
)
|
||||
|
||||
if state.stop_request:
|
||||
release_frames = state.release_frames + 1
|
||||
if release_frames <= EV9_STOP_RELEASE_DELAY_FRAMES:
|
||||
return EV9LongitudinalTuningState(
|
||||
stop_request=True,
|
||||
cruise_standstill=False,
|
||||
stop_request_frames=state.stop_request_frames,
|
||||
release_frames=release_frames,
|
||||
)
|
||||
|
||||
return EV9LongitudinalTuningState()
|
||||
|
||||
|
||||
def update_blindspot_warning(state: BlindspotWarningState, escalated: bool,
|
||||
blinker: bool) -> BlindspotWarningOutput:
|
||||
if not blinker:
|
||||
state.flash_phase = 0
|
||||
state.mirror_warning_active = False
|
||||
state.escalated_prev = False
|
||||
state.sound_remaining = 0
|
||||
state.sound_armed = True
|
||||
return BlindspotWarningOutput()
|
||||
|
||||
rising = escalated and not state.escalated_prev
|
||||
if rising:
|
||||
state.flash_phase = 0
|
||||
state.mirror_warning_active = True
|
||||
if state.sound_armed:
|
||||
state.sound_remaining = BLINDSPOT_WARNING_SOUND_SAMPLES
|
||||
state.sound_armed = False
|
||||
elif escalated:
|
||||
state.flash_phase = (state.flash_phase + 1) % BLINDSPOT_WARNING_FLASH_SAMPLES
|
||||
state.mirror_warning_active = True
|
||||
elif state.mirror_warning_active and state.flash_phase < BLINDSPOT_WARNING_FLASH_ON_SAMPLES - 1:
|
||||
state.flash_phase += 1
|
||||
else:
|
||||
state.flash_phase = 0
|
||||
state.mirror_warning_active = False
|
||||
|
||||
state.escalated_prev = escalated
|
||||
sound_active = state.sound_remaining > 0
|
||||
if state.sound_remaining > 0:
|
||||
state.sound_remaining -= 1
|
||||
return BlindspotWarningOutput(
|
||||
mirror_lamp_active=state.mirror_warning_active and state.flash_phase < BLINDSPOT_WARNING_FLASH_ON_SAMPLES,
|
||||
sound_active=sound_active,
|
||||
)
|
||||
|
||||
|
||||
def reset_egmp_longitudinal_tuning(state: Ioniq6LongitudinalTuningState) -> Ioniq6LongitudinalTuningState:
|
||||
state.desired_accel = 0.0
|
||||
state.actual_accel = 0.0
|
||||
state.accel_last = 0.0
|
||||
state.jerk_upper = 0.0
|
||||
state.jerk_lower = 0.0
|
||||
state.launch_active = False
|
||||
return state
|
||||
|
||||
|
||||
def update_ioniq_6_longitudinal_tuning(state: Ioniq6LongitudinalTuningState, accel_cmd: float, v_ego: float, a_ego: float,
|
||||
long_control_state: LongCtrlState, long_active: bool) -> Ioniq6LongitudinalTuningState:
|
||||
long_control_state: LongCtrlState, long_active: bool,
|
||||
ev6_gt_line: bool = False, low_speed_stop_brake_cap: bool = False) -> Ioniq6LongitudinalTuningState:
|
||||
starting = long_control_state == LongCtrlState.starting
|
||||
stopping = long_control_state == LongCtrlState.stopping
|
||||
restart_from_stop = state.long_control_state_last in (LongCtrlState.stopping, LongCtrlState.starting) and \
|
||||
@@ -163,7 +275,9 @@ def update_ioniq_6_longitudinal_tuning(state: Ioniq6LongitudinalTuningState, acc
|
||||
state.jerk_lower = min(dynamic_lower_jerk, lower_speed_limit)
|
||||
|
||||
if state.stopping:
|
||||
if v_ego <= IONIQ_6_STOP_BRAKE_CAP_MAX_SPEED:
|
||||
stop_brake_cap_max_speed = EV6_GT_LINE_STOP_BRAKE_CAP_MAX_SPEED if ev6_gt_line or low_speed_stop_brake_cap else \
|
||||
IONIQ_6_STOP_BRAKE_CAP_MAX_SPEED
|
||||
if v_ego <= stop_brake_cap_max_speed:
|
||||
stop_brake_cap = float(np.interp(v_ego, IONIQ_6_STOP_BRAKE_CAP_SPEED_BP, IONIQ_6_STOP_BRAKE_CAP_ACCEL_V))
|
||||
state.desired_accel = min(0.0, max(accel_cmd, stop_brake_cap))
|
||||
state.jerk_upper = min(state.jerk_upper, float(np.interp(v_ego, IONIQ_6_STOP_HOLD_JERK_BP, IONIQ_6_STOP_HOLD_JERK_V)) * IONIQ_6_RESPONSE_MULTIPLIER)
|
||||
@@ -229,6 +343,12 @@ def get_angle_smoothing_alpha(CP, v_ego: float) -> float:
|
||||
return float(np.interp(v_ego, DEFAULT_ANGLE_SMOOTHING_VEGO_BP, DEFAULT_ANGLE_SMOOTHING_ALPHA_V))
|
||||
|
||||
|
||||
def direct_angle_request_allowed(v_ego_raw, measured_angle, last_angle, drive_gear, VM, params):
|
||||
safety_v_ego = max(v_ego_raw - 1.0, 1.0)
|
||||
max_safety_angle = get_max_angle_vm(safety_v_ego, VM, params)
|
||||
return drive_gear and abs(measured_angle) <= max_safety_angle and abs(last_angle) <= max_safety_angle
|
||||
|
||||
|
||||
def compute_torque_reduction_gain(steering_torque, v_ego, lat_active, last_gain):
|
||||
if lat_active:
|
||||
ceiling = np.interp(v_ego, [0.5, 1.5], [1.0, 0.85])
|
||||
@@ -246,14 +366,6 @@ def compute_torque_reduction_gain(steering_torque, v_ego, lat_active, last_gain)
|
||||
return round(gain / 0.004) * 0.004
|
||||
|
||||
|
||||
def apply_ev9_high_angle_gain_cap(CP, gain: float, steering_angle_deg: float, lat_active: bool) -> float:
|
||||
if CP.carFingerprint != CAR.KIA_EV9 or not CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING or not lat_active:
|
||||
return gain
|
||||
|
||||
cap = float(np.interp(abs(steering_angle_deg), EV9_HIGH_ANGLE_GAIN_BP, EV9_HIGH_ANGLE_GAIN_CAP_V))
|
||||
return max(EV9_HIGH_ANGLE_GAIN_MIN, min(gain, cap))
|
||||
|
||||
|
||||
def process_hud_alert(enabled, fingerprint, hud_control):
|
||||
sys_warning = (hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw))
|
||||
|
||||
@@ -288,6 +400,7 @@ class CarController(CarControllerBase):
|
||||
self.VM = VehicleModel(CP)
|
||||
self.BASELINE_VM = VehicleModel(get_baseline_safety_cp()) if CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING else self.VM
|
||||
self.angle_filter = FirstOrderFilter(0.0, 0.2, DT_CTRL)
|
||||
self.direct_angle_request_allowed = True
|
||||
|
||||
self.accel_last = 0
|
||||
self.apply_torque_last = 0
|
||||
@@ -298,6 +411,10 @@ class CarController(CarControllerBase):
|
||||
self.ecu_disable_failed = False
|
||||
self._ecu_disable_checked = False
|
||||
self._params = Params()
|
||||
if CP.carFingerprint == CAR.KIA_EV9:
|
||||
self._ev9_long_tuning = EV9LongitudinalTuningState()
|
||||
self._left_blindspot_warning = BlindspotWarningState()
|
||||
self._right_blindspot_warning = BlindspotWarningState()
|
||||
self.long_active_ecu = self.CP.openpilotLongitudinalControl
|
||||
self._ioniq_6_lane_change_ui_side = None
|
||||
self._ioniq_6_lane_change_ui_frames = 0
|
||||
@@ -391,37 +508,52 @@ class CarController(CarControllerBase):
|
||||
|
||||
if not self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
|
||||
self.params = CarControllerParams(self.CP, CS.out.vEgoRaw)
|
||||
apply_angle = CS.out.steeringAngleDeg
|
||||
direct_angle_control = self.CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR and self.long_active_ecu
|
||||
measured_steering_angle = CS.angle_steering_angle if direct_angle_control else CS.out.steeringAngleDeg
|
||||
angle_lat_active = CC.latActive
|
||||
if direct_angle_control and CC.latActive:
|
||||
drive_gear = CS.out.gearShifter == structs.CarState.GearShifter.drive
|
||||
angle_lat_active = direct_angle_request_allowed(CS.out.vEgoRaw, measured_steering_angle, self.apply_angle_last,
|
||||
drive_gear, self.BASELINE_VM, self.params) and not CS.angle_steering_fault
|
||||
self.direct_angle_request_allowed = angle_lat_active
|
||||
apply_angle = measured_steering_angle
|
||||
|
||||
if self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
|
||||
v_ego_raw = CS.out.vEgoRaw
|
||||
desired_angle = float(np.clip(actuators.steeringAngleDeg,
|
||||
-self.params.ANGLE_LIMITS.STEER_ANGLE_MAX,
|
||||
self.params.ANGLE_LIMITS.STEER_ANGLE_MAX))
|
||||
|
||||
self.angle_filter.update_alpha(get_angle_smoothing_alpha(self.CP, CS.out.vEgo))
|
||||
desired_angle = self.angle_filter.update(desired_angle)
|
||||
|
||||
apply_angle = apply_steer_angle_limits_vm(desired_angle, self.apply_angle_last, v_ego_raw,
|
||||
CS.out.steeringAngleDeg, CC.latActive, self.params, self.VM)
|
||||
measured_steering_angle, angle_lat_active, self.params, self.VM)
|
||||
|
||||
if str(self.CP.carFingerprint) != ANGLE_SAFETY_BASELINE_MODEL:
|
||||
apply_angle = apply_steer_angle_limits_vm(apply_angle or desired_angle, self.apply_angle_last, v_ego_raw,
|
||||
CS.out.steeringAngleDeg, CC.latActive, self.params, self.BASELINE_VM)
|
||||
measured_steering_angle, angle_lat_active, self.params, self.BASELINE_VM)
|
||||
|
||||
apply_torque = compute_torque_reduction_gain(CS.out.steeringTorque, v_ego_raw, CC.latActive, self.apply_torque_last)
|
||||
apply_torque = apply_ev9_high_angle_gain_cap(self.CP, apply_torque, CS.out.steeringAngleDeg, CC.latActive)
|
||||
apply_steer_req = CC.latActive and apply_torque != 0.0
|
||||
if direct_angle_control and angle_lat_active:
|
||||
# Match Panda's 1 m/s speed tolerance so a shrinking absolute limit stays inside its jerk envelope.
|
||||
safety_v_ego = max(v_ego_raw - 1.0, 1.0)
|
||||
max_angle_delta = min(get_max_angle_delta_vm(safety_v_ego, self.BASELINE_VM, self.params),
|
||||
self.params.ANGLE_LIMITS.MAX_ANGLE_RATE)
|
||||
apply_angle = float(np.clip(apply_angle,
|
||||
self.apply_angle_last - max_angle_delta,
|
||||
self.apply_angle_last + max_angle_delta))
|
||||
|
||||
apply_torque = compute_torque_reduction_gain(CS.out.steeringTorque, v_ego_raw, angle_lat_active, self.apply_torque_last)
|
||||
apply_steer_req = angle_lat_active and apply_torque != 0.0
|
||||
torque_fault = False
|
||||
|
||||
if apply_angle is None:
|
||||
apply_torque = 0
|
||||
apply_angle = CS.out.steeringAngleDeg
|
||||
apply_angle = measured_steering_angle
|
||||
apply_steer_req = False
|
||||
|
||||
self.apply_angle_last = apply_angle
|
||||
if not CC.latActive:
|
||||
self.apply_angle_last = float(np.clip(CS.out.steeringAngleDeg,
|
||||
if not angle_lat_active:
|
||||
self.apply_angle_last = float(np.clip(measured_steering_angle,
|
||||
-self.params.ANGLE_LIMITS.STEER_ANGLE_MAX,
|
||||
self.params.ANGLE_LIMITS.STEER_ANGLE_MAX))
|
||||
self.angle_filter.x = self.apply_angle_last
|
||||
@@ -464,18 +596,30 @@ class CarController(CarControllerBase):
|
||||
|
||||
use_egmp_dynamic_long_tuning = egmp_dynamic_longitudinal_tuning(self.CP) and self.long_active_ecu and \
|
||||
actuators.longControlState in (LongCtrlState.starting, LongCtrlState.pid, LongCtrlState.stopping)
|
||||
is_ev6_gt_line = kia_ev6_gt_line_longitudinal_tuning(self.CP.carFingerprint, getattr(self.CP, "carVin", ""))
|
||||
is_ev9 = self.CP.carFingerprint == CAR.KIA_EV9
|
||||
if is_ev9 and (self._ev9_long_tuning.stop_request or not CC.enabled or CC.cruiseControl.override):
|
||||
self._ioniq_6_long_tuning = reset_egmp_longitudinal_tuning(self._ioniq_6_long_tuning)
|
||||
if should_reset_ev6_gt_line_longitudinal_tuning(self.CP, actuators.longControlState):
|
||||
self._ioniq_6_long_tuning = reset_ev6_gt_line_longitudinal_tuning(self._ioniq_6_long_tuning, self.CP,
|
||||
actuators.longControlState)
|
||||
elif use_egmp_dynamic_long_tuning and self.frame % 5 == 0:
|
||||
self._ioniq_6_long_tuning = update_ioniq_6_longitudinal_tuning(self._ioniq_6_long_tuning, accel_cmd,
|
||||
CS.out.vEgo, CS.out.aEgo,
|
||||
actuators.longControlState, self.long_active_ecu)
|
||||
actuators.longControlState, self.long_active_ecu,
|
||||
ev6_gt_line=is_ev6_gt_line,
|
||||
low_speed_stop_brake_cap=is_ev9)
|
||||
use_egmp_smoothed_accel = use_egmp_dynamic_long_tuning and (
|
||||
accel_cmd >= self._ioniq_6_long_tuning.actual_accel or
|
||||
self._ioniq_6_long_tuning.launch_active or
|
||||
self._ioniq_6_long_tuning.stopping
|
||||
)
|
||||
if should_use_ev6_gt_line_stop_direct_tracking(is_ev6_gt_line, self._ioniq_6_long_tuning.stopping,
|
||||
CS.out.vEgo, accel_cmd, self._ioniq_6_long_tuning.actual_accel):
|
||||
use_egmp_smoothed_accel = False
|
||||
if is_ev9 and should_track_stop_accel_directly(self._ioniq_6_long_tuning.stopping, CS.out.vEgo,
|
||||
accel_cmd, self._ioniq_6_long_tuning.actual_accel):
|
||||
use_egmp_smoothed_accel = False
|
||||
if use_egmp_dynamic_long_tuning:
|
||||
if use_egmp_smoothed_accel:
|
||||
accel = self._ioniq_6_long_tuning.actual_accel
|
||||
@@ -542,7 +686,15 @@ class CarController(CarControllerBase):
|
||||
sys_warning, sys_state, left_lane_warning, right_lane_warning = process_hud_alert(CC.enabled, self.car_fingerprint,
|
||||
hud_control)
|
||||
|
||||
if can_canfd_blended:
|
||||
if can_canfd_blended and self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
|
||||
can_sends.extend(hyundaicanfd.create_steering_messages(
|
||||
self.packer, self.CP, self.CAN, CC.enabled, apply_steer_req, apply_torque, 0.0,
|
||||
))
|
||||
if self.frame % 5 == 0:
|
||||
can_sends.append(hyundaicanfd.create_suppress_lfa(
|
||||
self.packer, self.CAN, CS.lfa_block_msg, False,
|
||||
))
|
||||
elif can_canfd_blended:
|
||||
can_sends.extend(hyundaican.create_lkas11_can_canfd_blended(self.packer, self.frame, self.CP, apply_torque, apply_steer_req,
|
||||
torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled,
|
||||
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
|
||||
@@ -615,26 +767,54 @@ class CarController(CarControllerBase):
|
||||
|
||||
# steering control
|
||||
preserve_stock_lkas = bool(self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING) and not self.long_active_ecu
|
||||
angle_lkas_alt = bool(self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING and
|
||||
self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT)
|
||||
ccnc_angle_long = self.CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR and \
|
||||
self.CP.flags & HyundaiFlags.CCNC and angle_lkas_alt and self.long_active_ecu
|
||||
steering_msg_active = apply_steer_req
|
||||
if self.CP.carFingerprint == CAR.KIA_EV9 and self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
|
||||
# EV9 faults if the angle-steering status drops inactive during torque limiting.
|
||||
if angle_lkas_alt:
|
||||
# Angle LKAS_ALT cars fault if the angle-steering status drops inactive during torque limiting.
|
||||
# Hold the angle status active while lateral is active; VM/safety limits handle actuation.
|
||||
steering_msg_active = CC.latActive
|
||||
|
||||
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled,
|
||||
steering_msg_active, apply_torque, apply_angle,
|
||||
CS.stock_lfa_msg,
|
||||
CS.stock_lkas_msg if preserve_stock_lkas else None,
|
||||
lka_icon=lka_icon))
|
||||
gear = getattr(getattr(CS, "out", None), "gearShifter", None)
|
||||
drive_gear = gear == structs.CarState.GearShifter.drive
|
||||
if angle_lkas_alt:
|
||||
steering_msg_active = bool(steering_msg_active and drive_gear)
|
||||
angle_lkas_alt_standstill_handoff = bool(getattr(CS.out, "standstill", False) and not CC.latActive)
|
||||
forward_stock_lkas = angle_lkas_alt and (
|
||||
angle_lkas_alt_standstill_handoff or not (drive_gear and (CC.latActive or CC.enabled))
|
||||
)
|
||||
if not forward_stock_lkas and not ccnc_angle_long:
|
||||
can_sends.extend(hyundaicanfd.create_steering_messages(self.packer, self.CP, self.CAN, CC.enabled,
|
||||
steering_msg_active, apply_torque, apply_angle,
|
||||
CS.stock_lfa_msg,
|
||||
CS.stock_lkas_msg if preserve_stock_lkas else None,
|
||||
lka_icon=lka_icon))
|
||||
direct_steering_active = ccnc_angle_long and drive_gear and CC.latActive and self.direct_angle_request_allowed and not CS.angle_steering_fault
|
||||
inactive_steering_angle = float(np.clip(CS.angle_steering_angle,
|
||||
-self.params.ANGLE_LIMITS.STEER_ANGLE_MAX,
|
||||
self.params.ANGLE_LIMITS.STEER_ANGLE_MAX)) if ccnc_angle_long else 0.0
|
||||
if ccnc_angle_long and drive_gear:
|
||||
can_sends.append(hyundaicanfd.create_angle_adas_cmd(
|
||||
self.packer, self.CAN,
|
||||
apply_angle if direct_steering_active else inactive_steering_angle,
|
||||
direct_steering_active, apply_torque if direct_steering_active else 0.0,
|
||||
))
|
||||
if ccnc_angle_long and not drive_gear:
|
||||
can_sends.extend(hyundaicanfd.create_inactive_angle_steering_messages(self.packer, self.CAN,
|
||||
inactive_steering_angle))
|
||||
|
||||
# prevent LFA from activating on LKA steering cars by sending "no lane lines detected" to ADAS ECU
|
||||
suppress_lfa = bool(lka_steering)
|
||||
if angle_lkas_alt:
|
||||
suppress_lfa = bool(lka_steering and drive_gear and (CC.latActive or (ccnc_angle_long and CC.enabled)))
|
||||
if self.frame % 5 == 0 and suppress_lfa:
|
||||
can_sends.append(hyundaicanfd.create_suppress_lfa(self.packer, self.CAN, CS.lfa_block_msg,
|
||||
self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT))
|
||||
|
||||
# LFA and HDA icons
|
||||
if self.frame % 5 == 0 and (not lka_steering or lka_steering_long):
|
||||
if self.frame % 5 == 0 and (not lka_steering or lka_steering_long) and not ccnc_angle_long:
|
||||
if ccnc_non_hda2:
|
||||
can_sends.extend(hyundaicanfd.create_ccnc(self.packer, self.CAN, self.long_active_ecu, CC.enabled, CC.hudControl,
|
||||
CC.leftBlinker, CC.rightBlinker, CS.msg_161, CS.msg_162, CS.msg_1b5,
|
||||
@@ -670,13 +850,41 @@ class CarController(CarControllerBase):
|
||||
|
||||
if self.long_active_ecu:
|
||||
if lka_steering:
|
||||
can_sends.extend(hyundaicanfd.create_adrv_messages(self.packer, self.CAN, self.frame))
|
||||
# Ioniq 5/6: front radar treats ADAS_DRV's 0x100 broadcast as its host heartbeat
|
||||
# and stops publishing object tracks when it disappears. Spoof it periodically on
|
||||
# PT bus so the radar keeps tracking.
|
||||
if self.CP.carFingerprint in CANFD_RADAR_LIVE_LONGITUDINAL_CAR and self.frame % 4 == 0:
|
||||
can_sends.append(hyundaicanfd.create_accelerator_brake_alt_spoof(0, self.frame // 4, CS.out.brakePressed,
|
||||
CS.out.gasPressed, self.CP.carFingerprint))
|
||||
if ccnc_angle_long:
|
||||
left_escalated = CS.left_blindspot_from_radar and CC.leftBlinker and not CC.rightBlinker
|
||||
right_escalated = CS.right_blindspot_from_radar and CC.rightBlinker and not CC.leftBlinker
|
||||
left_warning = BlindspotWarningOutput()
|
||||
right_warning = BlindspotWarningOutput()
|
||||
if self.frame % 5 == 0:
|
||||
left_warning = update_blindspot_warning(
|
||||
self._left_blindspot_warning, left_escalated, CC.leftBlinker,
|
||||
)
|
||||
right_warning = update_blindspot_warning(
|
||||
self._right_blindspot_warning, right_escalated, CC.rightBlinker,
|
||||
)
|
||||
steering_available = CC.latActive or CC.enabled
|
||||
steering_active = direct_steering_active and apply_steer_req and not CS.out.steeringPressed
|
||||
adrv_messages = hyundaicanfd.create_ccnc_adrv_messages(
|
||||
self.packer, self.CP, self.CAN, self.frame, CC.enabled, CS.out.cruiseState.available, CC.hudControl,
|
||||
CS.out, CS.is_metric, steering_available, steering_active,
|
||||
CS.left_blindspot_from_radar, CS.right_blindspot_from_radar,
|
||||
drive_gear=drive_gear,
|
||||
hba_icon=CS.hba_icon,
|
||||
left_escalated=left_escalated, right_escalated=right_escalated,
|
||||
left_warning_lamp=left_warning.mirror_lamp_active,
|
||||
right_warning_lamp=right_warning.mirror_lamp_active,
|
||||
left_sound_active=left_warning.sound_active, right_sound_active=right_warning.sound_active,
|
||||
)
|
||||
else:
|
||||
adrv_messages = hyundaicanfd.create_adrv_messages(self.packer, self.CAN, self.frame)
|
||||
can_sends.extend(adrv_messages)
|
||||
# The front radar treats ADAS_DRV's 0x100 broadcast as its host heartbeat
|
||||
# and stops publishing object tracks when it disappears.
|
||||
radar_heartbeat_step = 1 if ccnc_angle_long else 4
|
||||
if self.CP.carFingerprint in CANFD_RADAR_LIVE_LONGITUDINAL_CAR and self.frame % radar_heartbeat_step == 0:
|
||||
can_sends.append(hyundaicanfd.create_accelerator_brake_alt_spoof(0, self.frame // radar_heartbeat_step,
|
||||
CS.out.brakePressed, CS.out.gasPressed,
|
||||
self.CP.carFingerprint))
|
||||
elif not ccnc_non_hda2:
|
||||
can_sends.extend(hyundaicanfd.create_fca_warning_light(self.packer, self.CAN, self.frame))
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6 and self.frame % 5 == 0:
|
||||
@@ -711,9 +919,27 @@ class CarController(CarControllerBase):
|
||||
if use_egmp_smoothed_accel:
|
||||
acc_kwargs["jerk_lower"] = self._ioniq_6_long_tuning.jerk_lower
|
||||
acc_kwargs["jerk_upper"] = self._ioniq_6_long_tuning.jerk_upper
|
||||
can_sends.append(hyundaicanfd.create_acc_control(self.packer, self.CAN, CC.enabled, self.accel_last, accel, stopping, CC.cruiseControl.override,
|
||||
set_speed_in_units, hud_control, cruise_info=CS.cruise_info if ccnc_non_hda2 else None,
|
||||
**acc_kwargs))
|
||||
if ccnc_angle_long:
|
||||
self._ev9_long_tuning = update_ev9_longitudinal_tuning(
|
||||
self._ev9_long_tuning, CC.enabled and not CC.cruiseControl.override,
|
||||
CC.actuators.longControlState == LongCtrlState.stopping, float(CS.out.vEgo),
|
||||
)
|
||||
if self._ev9_long_tuning.stop_request or not CC.enabled or CC.cruiseControl.override:
|
||||
self._ioniq_6_long_tuning = reset_egmp_longitudinal_tuning(self._ioniq_6_long_tuning)
|
||||
accel = 0.0
|
||||
can_sends.append(hyundaicanfd.create_ccnc_acc_control(
|
||||
self.packer, self.CAN, CC.enabled, accel,
|
||||
self._ev9_long_tuning.stop_request, self._ev9_long_tuning.cruise_standstill, CC.cruiseControl.override,
|
||||
set_speed_in_units, int(CS.out.cruiseState.available), lead_distance, lead_rel_speed, lead_visible,
|
||||
float(CS.out.vEgo),
|
||||
jerk_lower=acc_kwargs["jerk_lower"],
|
||||
jerk_upper=acc_kwargs["jerk_upper"],
|
||||
))
|
||||
else:
|
||||
can_sends.append(hyundaicanfd.create_acc_control(
|
||||
self.packer, self.CAN, CC.enabled, self.accel_last, accel, stopping, CC.cruiseControl.override,
|
||||
set_speed_in_units, hud_control, cruise_info=CS.cruise_info if ccnc_non_hda2 else None, **acc_kwargs,
|
||||
))
|
||||
self.accel_last = accel
|
||||
else:
|
||||
# button presses
|
||||
|
||||
@@ -8,6 +8,7 @@ from opendbc.car import Bus, create_button_events, structs
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, HyundaiStarPilotFlags, HyundaiStarPilotSafetyFlags, CAR, DBC, Buttons, CarControllerParams, \
|
||||
CANFD_ANGLE_LONGITUDINAL_CAR, CANFD_CORNER_RADAR_BSM_CAR, \
|
||||
hyundai_cancel_button_enables_cruise, ALT_BUS_LDA_BUTTON_CARS, ALT_BUS_LDA_BUTTON_SWL_STAT_CARS
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
|
||||
@@ -122,6 +123,7 @@ class CarState(CarStateBase):
|
||||
self.msg_162 = {}
|
||||
self.msg_1b5 = {}
|
||||
self.msg_364 = {}
|
||||
self.lfa_block_msg = {}
|
||||
self.stock_lkas_msg = {}
|
||||
self.stock_lfa_msg = {}
|
||||
self.stock_lfahda_cluster_msg = {}
|
||||
@@ -136,6 +138,11 @@ class CarState(CarStateBase):
|
||||
self.blindspots_front_corner_1_ts = 0
|
||||
self.left_blindspot_from_radar = False
|
||||
self.right_blindspot_from_radar = False
|
||||
if CP.carFingerprint == CAR.KIA_EV9:
|
||||
self.hba_icon = 0
|
||||
self.main_cruise_on = False
|
||||
self.angle_steering_angle = 0.0
|
||||
self.angle_steering_fault = False
|
||||
|
||||
# On some cars, CLU15->CF_Clu_VehicleSpeed can oscillate faster than the dash updates. Sample at 5 Hz
|
||||
self.cluster_speed = 0
|
||||
@@ -149,6 +156,12 @@ class CarState(CarStateBase):
|
||||
# Main button also can trigger an engagement on these cars
|
||||
return any(btn in ENABLE_BUTTONS for btn in self.cruise_buttons) or any(self.main_buttons)
|
||||
|
||||
def update_main_cruise(self, ret: structs.CarState) -> bool:
|
||||
if any(be.type == ButtonType.mainCruise and be.pressed for be in ret.buttonEvents):
|
||||
self.main_cruise_on = not self.main_cruise_on
|
||||
|
||||
return bool(ret.cruiseState.available and self.main_cruise_on)
|
||||
|
||||
def create_cruise_button_events(self, cur_button: int, prev_button: int) -> list[structs.CarState.ButtonEvent]:
|
||||
if cur_button != prev_button and prev_button != Buttons.CANCEL and cur_button == Buttons.CANCEL:
|
||||
self.cancel_button_enable_in_progress = (
|
||||
@@ -320,7 +333,10 @@ class CarState(CarStateBase):
|
||||
ret.cruiseState.speed = cp_cruise.vl[scc_msg]["VSetDis"] * speed_conv
|
||||
|
||||
if self.CP.flags & HyundaiFlags.CAN_CANFD_BLENDED:
|
||||
self.msg_364 = copy.copy(cp_cam.vl["ALERTS_364"])
|
||||
if self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
|
||||
self.lfa_block_msg = copy.copy(cp_cam.vl["CAM_0x2a4"])
|
||||
else:
|
||||
self.msg_364 = copy.copy(cp_cam.vl["ALERTS_364"])
|
||||
|
||||
# TODO: Find brake pressure
|
||||
ret.brake = 0
|
||||
@@ -379,7 +395,10 @@ class CarState(CarStateBase):
|
||||
ret.rightBlindspot = cp.vl["LCA11"]["CF_Lca_IndRight"] != 0
|
||||
|
||||
# save the entire LKAS11 and CLU11
|
||||
self.lkas11 = copy.copy(cp_cam.vl["LKAS11"])
|
||||
if self.CP.flags & HyundaiFlags.CAN_CANFD_BLENDED and self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
|
||||
self.lkas11 = {}
|
||||
else:
|
||||
self.lkas11 = copy.copy(cp_cam.vl["LKAS11"])
|
||||
self.clu11 = copy.copy(cp.vl["CLU11"])
|
||||
self.steer_state = cp.vl["MDPS12"]["CF_Mdps_ToiActive"] # 0 NOT ACTIVE, 1 ACTIVE
|
||||
prev_cruise_buttons = self.cruise_buttons[-1]
|
||||
@@ -448,6 +467,10 @@ class CarState(CarStateBase):
|
||||
ret.steeringTorqueEps = cp.vl["MDPS"]["STEERING_OUT_TORQUE"]
|
||||
ret.steeringPressed = self.update_steering_pressed(abs(ret.steeringTorque) > self.params.STEER_THRESHOLD, 5)
|
||||
ret.steerFaultTemporary = cp.vl["MDPS"]["LKA_FAULT"] != 0
|
||||
if self.CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR:
|
||||
self.angle_steering_angle = cp.vl["MDPS"]["STEERING_ANGLE_2"]
|
||||
self.angle_steering_fault = cp.vl["MDPS"]["LKA_ANGLE_FAULT"] != 0
|
||||
ret.steerFaultTemporary = ret.steerFaultTemporary or self.angle_steering_fault
|
||||
|
||||
ccnc_non_hda2 = self.CP.flags & HyundaiFlags.CCNC and not self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING
|
||||
if ccnc_non_hda2:
|
||||
@@ -463,11 +486,12 @@ class CarState(CarStateBase):
|
||||
cp.vl["BLINKERS"][right_blinker_sig])
|
||||
self.left_blindspot_from_radar = False
|
||||
self.right_blindspot_from_radar = False
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
|
||||
corner_radar_bsm = self.CP.carFingerprint in CANFD_CORNER_RADAR_BSM_CAR
|
||||
if corner_radar_bsm:
|
||||
self.left_blindspot_from_radar, self.right_blindspot_from_radar = decode_ioniq_6_blindspot_radar_state(
|
||||
cp.vl["BLINDSPOTS_FRONT_CORNER_2"]["SIDE_DETECT_STATE"])
|
||||
if self.CP.enableBsm:
|
||||
if self.CP.carFingerprint == CAR.HYUNDAI_IONIQ_6:
|
||||
if corner_radar_bsm:
|
||||
ret.leftBlindspot = (bool(cp.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_LtIndSta"]) or
|
||||
self.left_blindspot_from_radar)
|
||||
ret.rightBlindspot = (bool(cp.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_RtIndSta"]) or
|
||||
@@ -537,6 +561,9 @@ class CarState(CarStateBase):
|
||||
self.stock_lfa_msg = copy.copy(cp.vl["LFA"])
|
||||
if cp.ts_nanos["LFAHDA_CLUSTER"]["CHECKSUM"] > 0:
|
||||
self.stock_lfahda_cluster_msg = copy.copy(cp.vl["LFAHDA_CLUSTER"])
|
||||
if self.CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR and cp.ts_nanos["FR_CMR_01_10ms"]["FR_CMR_Crc1Val"] > 0:
|
||||
hba_icon = int(cp.vl["FR_CMR_01_10ms"]["HBA_IndLmpReq"])
|
||||
self.hba_icon = hba_icon if hba_icon in (1, 2) else 0
|
||||
if cp.ts_nanos["BLINKER_STALKS"]["CHECKSUM_MAYBE"] > 0:
|
||||
self.stock_blinker_stalks_ts = cp.ts_nanos["BLINKER_STALKS"]["CHECKSUM_MAYBE"]
|
||||
|
||||
@@ -544,6 +571,8 @@ class CarState(CarStateBase):
|
||||
*create_button_events(self.main_buttons[-1], prev_main_buttons, {1: ButtonType.mainCruise}),
|
||||
*create_button_events(self.lda_button, prev_lda_button, {1: ButtonType.lkas}),
|
||||
*create_button_events(self.left_paddle, prev_left_paddle, {1: ButtonType.altButton2})]
|
||||
if self.CP.openpilotLongitudinalControl and self.CP.carFingerprint == CAR.KIA_EV9:
|
||||
ret.cruiseState.available = self.update_main_cruise(ret)
|
||||
|
||||
ret.blockPcmEnable = not self.recent_button_interaction()
|
||||
|
||||
@@ -592,6 +621,12 @@ class CarState(CarStateBase):
|
||||
("LFAHDA_CLUSTER", 0), # optional: carries cluster icon state on some variants
|
||||
("BLINKER_STALKS", 0), # optional: some trims publish live stalk/light state on ECAN during turn camera events
|
||||
]
|
||||
if CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR and CP.enableBsm:
|
||||
# Keep the suppressed ADAS BSM output optional.
|
||||
msgs.append(("BLINDSPOTS_REAR_CORNERS", 0))
|
||||
if CP.carFingerprint in CANFD_ANGLE_LONGITUDINAL_CAR:
|
||||
msgs.append(("BLINDSPOTS_FRONT_CORNER_2", 0))
|
||||
msgs.append(("FR_CMR_01_10ms", 0))
|
||||
if CP.flags & HyundaiFlags.EV:
|
||||
msgs.append(("DRIVE_MODE_EV", 0)) # optional: not all CAN-FD EV variants publish drive mode
|
||||
msgs.append(("MANUAL_SPEED_LIMIT_ASSIST", 0)) # optional: used for non-adaptive cruise state and Ioniq 6 i-Pedal latch detection
|
||||
@@ -606,6 +641,34 @@ class CarState(CarStateBase):
|
||||
if CP.flags & HyundaiFlags.CANFD:
|
||||
return self.get_can_parsers_canfd(CP)
|
||||
|
||||
if CP.flags & HyundaiFlags.CAN_CANFD_BLENDED and CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
|
||||
msgs = [
|
||||
("MDPS12", 100),
|
||||
("TCS11", 100),
|
||||
("TCS13", 50),
|
||||
("TCS15", 10),
|
||||
("CLU11", 50),
|
||||
("CLU15", 5),
|
||||
("ESP12", 100),
|
||||
("CGW1", 10),
|
||||
("CGW2", 5),
|
||||
("WHL_SPD11", 50),
|
||||
("SAS11", 100),
|
||||
("SCC12", 50),
|
||||
("EMS12", 100),
|
||||
("EMS16", 100),
|
||||
("LVR12", 100),
|
||||
("BCM_PO_11", 0),
|
||||
("CLU13", 0),
|
||||
]
|
||||
if CP.enableBsm:
|
||||
msgs.append(("LCA11", 20))
|
||||
|
||||
return {
|
||||
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], msgs, CanBus(CP).ECAN),
|
||||
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [("CAM_0x2a4", 20)], CanBus(CP).CAM),
|
||||
}
|
||||
|
||||
msgs = [
|
||||
("BCM_PO_11", 0),
|
||||
("CLU13", 0),
|
||||
|
||||
@@ -999,6 +999,21 @@ FW_VERSIONS = {
|
||||
b'\xf1\x00CN ESC \t 105 \x10\x03 58910-AA800',
|
||||
],
|
||||
},
|
||||
CAR.HYUNDAI_ELANTRA_2024: {
|
||||
(Ecu.fwdRadar, 0x7d0, None): [
|
||||
b'\xf1\x00CN7_ RDR ----- 1.00 1.01 99110-AA500 ',
|
||||
],
|
||||
(Ecu.eps, 0x7d4, None): [
|
||||
b'\xf1\x00CN7 MDPS C 1.00 1.02 56300AA670\x00 4CSDC102',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.02 99210-AA500 230420',
|
||||
b'\xf1\x00CN7 MFC AT USA LHD 1.00 1.03 99210-AA500 230918',
|
||||
],
|
||||
(Ecu.abs, 0x7d1, None): [
|
||||
b'\xf1\x00CN ESC \t 104#\x07\x03 58910-AA850',
|
||||
],
|
||||
},
|
||||
CAR.HYUNDAI_ELANTRA_HEV_2021: {
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.03 99210-AA000 200819',
|
||||
@@ -1018,6 +1033,22 @@ FW_VERSIONS = {
|
||||
b'\xf1\x00CN7 MDPS C 1.00 1.04 56310BY050\x00 4CNHC104',
|
||||
],
|
||||
},
|
||||
CAR.HYUNDAI_ELANTRA_HEV_2024: {
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00CN7HMFC AT AUS RHD 1.00 1.02 99210-AA500 230420',
|
||||
b'\xf1\x00CN7HMFC AT CAN LHD 1.00 1.05 99210-AA510 240509',
|
||||
b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.03 99210-AA500 230918',
|
||||
b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.05 99210-AA510 240509',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x7d0, None): [
|
||||
b'\xf1\x00CN7_ RDR ----- 1.00 1.01 99110-AA500 ',
|
||||
],
|
||||
(Ecu.eps, 0x7d4, None): [
|
||||
b'\xf1\x00CN7 MDPS C 1.00 1.00 56300BY670\x00 4CSHC100',
|
||||
b'\xf1\x00CN7 MDPS C 1.00 1.00 56300BY680\x00 4CSHC100',
|
||||
b'\xf1\x00CN7 MDPS C 1.00 1.03 56300BY670\x00 4CSHC103',
|
||||
],
|
||||
},
|
||||
CAR.HYUNDAI_KONA_HEV: {
|
||||
(Ecu.abs, 0x7d1, None): [
|
||||
b'\xf1\x00OS IEB \x01 104 \x11 58520-CM000',
|
||||
@@ -1481,6 +1512,26 @@ FW_VERSIONS = {
|
||||
b'\xf1\x00KA4c SCC FHCUP 1.00 1.01 99110-I4000 ',
|
||||
],
|
||||
},
|
||||
CAR.KIA_CARNIVAL_2025: {
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00KA4 MFC AT CAN LHD 1.00 1.00 99210-R0700 250324',
|
||||
b'\xf1\x00KA4 MFC AT USA LHD 1.00 1.05 99210-R0500 240305',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x7d0, None): [
|
||||
b'\xf1\x00KA4_ SCC FHCUP 1.00 1.01 99110-R0510 ',
|
||||
b'\xf1\x00KA4_ RDR ----- 1.00 1.01 99110-R0510 ',
|
||||
],
|
||||
},
|
||||
CAR.KIA_CARNIVAL_HEV_4TH_GEN: {
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00KA4HMFC AT USA LHD 1.00 1.05 99210-R0500 240305',
|
||||
b'\xf1\x00KA4HMFC AT KOR LHD 1.00 1.00 99210-R0600 240924',
|
||||
b'\xf1\x00KA4HMFC AT USA LHD 1.00 1.00 99210-R0700 250324',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x7d0, None): [
|
||||
b'\xf1\x00KAhe RDR ----- 1.00 1.01 99110-ES500 ',
|
||||
],
|
||||
},
|
||||
CAR.KIA_K8_HEV_1ST_GEN: {
|
||||
(Ecu.fwdCamera, 0x7c4, None): [
|
||||
b'\xf1\x00GL3HMFC AT KOR LHD 1.00 1.03 99211-L8000 210907',
|
||||
|
||||
@@ -40,7 +40,8 @@ def create_lkas11(packer, frame, CP, apply_torque, steer_req,
|
||||
CAR.HYUNDAI_ELANTRA_HEV_2021, CAR.HYUNDAI_SONATA_HYBRID, CAR.HYUNDAI_KONA_EV, CAR.HYUNDAI_KONA_HEV, CAR.HYUNDAI_KONA_EV_2022,
|
||||
CAR.HYUNDAI_SANTA_FE_2022, CAR.KIA_K5_2021, CAR.HYUNDAI_IONIQ_HEV_2022, CAR.HYUNDAI_SANTA_FE_HEV_2022,
|
||||
CAR.HYUNDAI_SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_CEED, CAR.KIA_XCEED_PHEV,
|
||||
CAR.HYUNDAI_AZERA_6TH_GEN, CAR.HYUNDAI_AZERA_HEV_6TH_GEN, CAR.HYUNDAI_CUSTIN_1ST_GEN, CAR.HYUNDAI_KONA_2022):
|
||||
CAR.HYUNDAI_AZERA_6TH_GEN, CAR.HYUNDAI_AZERA_HEV_6TH_GEN, CAR.HYUNDAI_CUSTIN_1ST_GEN, CAR.HYUNDAI_KONA_2022,
|
||||
CAR.HYUNDAI_ELANTRA_2024, CAR.HYUNDAI_ELANTRA_HEV_2024):
|
||||
values["CF_Lkas_LdwsActivemode"] = int(left_lane) + (int(right_lane) << 1)
|
||||
values["CF_Lkas_LdwsOpt_USM"] = 2
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import numpy as np
|
||||
from opendbc.car import CanBusBase, CanData
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.crc import CRC16_XMODEM
|
||||
from opendbc.car.hyundai.values import HyundaiFlags
|
||||
from opendbc.car.hyundai.values import HyundaiFlags, CAR
|
||||
|
||||
|
||||
def _set_value(msg: bytearray, sig, ival: int) -> None:
|
||||
@@ -92,12 +92,15 @@ def _create_angle_adas_cmd_msg(packer, CAN, apply_angle: float, lat_active: bool
|
||||
return packer.make_can_msg("ADAS_CMD_35_10ms", CAN.ECAN, values)
|
||||
|
||||
|
||||
def create_angle_adas_cmd(packer, CAN, apply_angle: float, lat_active: bool, torque_reduction_gain: float):
|
||||
return _create_angle_adas_cmd_msg(packer, CAN, apply_angle, lat_active, torque_reduction_gain)
|
||||
|
||||
|
||||
def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque, apply_angle,
|
||||
lfa_base_values=None, lkas_base_values=None, lka_icon=None):
|
||||
if lka_icon is None:
|
||||
lka_icon = 2 if enabled else 1
|
||||
ev9_angle_lkas_alt = str(CP.carFingerprint) == "KIA_EV9" and CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING and \
|
||||
CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
|
||||
angle_lkas_alt = CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING and CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
|
||||
|
||||
control_values = {
|
||||
"LKA_MODE": 2,
|
||||
@@ -130,48 +133,59 @@ def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque,
|
||||
lkas_values["ADAS_StrAnglReqVal"] = apply_angle
|
||||
lkas_values["LKAS_ANGLE_ACTIVE"] = 2 if lat_active else 1
|
||||
lkas_values["ADAS_ACIAnglTqRedcGainVal"] = apply_torque if lat_active else 0.0
|
||||
if ev9_angle_lkas_alt:
|
||||
if angle_lkas_alt:
|
||||
if lat_active:
|
||||
lkas_values.update({
|
||||
"LKA_MODE": 0,
|
||||
"LKA_AVAILABLE": 3,
|
||||
"LKA_WARNING": 0,
|
||||
"LKA_ICON": lka_icon,
|
||||
"FCA_SYSWARN": 0,
|
||||
"TORQUE_REQUEST": 0,
|
||||
"STEER_REQ": 0,
|
||||
lkas_values = {
|
||||
"LKA_OptUsmSta": 0,
|
||||
"LKA_RcgSta": 3,
|
||||
"LKA_LHLnWrnSta": 0,
|
||||
"LKA_RHLnWrnSta": 0,
|
||||
"LKA_HndsoffSnd": 0,
|
||||
"LKA_StrSnd": 0,
|
||||
"LKA_SysIndReq": 2,
|
||||
"StrTqReqVal": 0,
|
||||
"ActToiSta": 0,
|
||||
"ToiFltSta": 0,
|
||||
"LFA_BUTTON": 0,
|
||||
"LKA_ASSIST": 0,
|
||||
"DAMP_FACTOR": 100,
|
||||
"HAS_LANE_SAFETY": 0,
|
||||
})
|
||||
elif lkas_base_values:
|
||||
for signal in ("LKA_MODE", "LKA_AVAILABLE", "LKA_WARNING", "LKA_ICON", "FCA_SYSWARN",
|
||||
"LFA_BUTTON", "LKA_ASSIST", "DAMP_FACTOR", "HAS_LANE_SAFETY"):
|
||||
if signal in lkas_base_values:
|
||||
lkas_values[signal] = lkas_base_values[signal]
|
||||
lkas_values.update({
|
||||
"TORQUE_REQUEST": 0,
|
||||
"STEER_REQ": 0,
|
||||
})
|
||||
"LKA_SysWrn": 0,
|
||||
"Damping_Gain": 100,
|
||||
"LKAS_ANGLE_ACTIVE": 2,
|
||||
"LKA_UsmMod": 0,
|
||||
"ADAS_StrAnglReqVal": apply_angle,
|
||||
"ADAS_ACIAnglTqRedcGainVal": apply_torque,
|
||||
}
|
||||
else:
|
||||
lkas_values.update({
|
||||
"LKA_OptUsmSta": 0,
|
||||
"LKA_MODE": 0,
|
||||
"LKA_RcgSta": 0,
|
||||
"LKA_AVAILABLE": 0,
|
||||
"LKA_LHLnWrnSta": 0,
|
||||
"LKA_RHLnWrnSta": 0,
|
||||
"LKA_WARNING": 0,
|
||||
"LKA_ICON": lka_icon,
|
||||
"LKA_HndsoffSnd": 0,
|
||||
"LKA_StrSnd": 2,
|
||||
"LKA_SysIndReq": 1,
|
||||
"LKA_ICON": 1,
|
||||
"FCA_SYSWARN": 0,
|
||||
"StrTqReqVal": 0,
|
||||
"TORQUE_REQUEST": 0,
|
||||
"ActToiSta": 0,
|
||||
"STEER_REQ": 0,
|
||||
"ToiFltSta": 0,
|
||||
"LFA_BUTTON": 0,
|
||||
"LKA_SysWrn": 0,
|
||||
"LKA_ASSIST": 0,
|
||||
"DAMP_FACTOR": 100,
|
||||
"Damping_Gain": 0,
|
||||
"STEER_MODE": 0,
|
||||
"NEW_SIGNAL_2": 0,
|
||||
"LKAS_ANGLE_ACTIVE": 1,
|
||||
"LKA_UsmMod": 0,
|
||||
"HAS_LANE_SAFETY": 0,
|
||||
"ADAS_ACIAnglTqRedcGainVal": 0.0,
|
||||
"DAMP_FACTOR": 0,
|
||||
})
|
||||
# These signals overlap DAMP_FACTOR in the local DBC naming; omitting them
|
||||
# preserves the stock angle-steering damping byte expected by the ADAS ECU.
|
||||
lkas_values.pop("STEER_MODE", None)
|
||||
lkas_values.pop("NEW_SIGNAL_2", None)
|
||||
lkas_values["ADAS_StrAnglReqVal"] = lkas_base_values.get("ADAS_StrAnglReqVal", apply_angle) if lkas_base_values else apply_angle
|
||||
|
||||
ret = []
|
||||
if CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
|
||||
@@ -194,6 +208,25 @@ def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque,
|
||||
return ret
|
||||
|
||||
|
||||
def create_inactive_angle_steering_messages(packer, CAN, steering_angle: float):
|
||||
lfa_values = {
|
||||
"LKA_MODE": 2,
|
||||
"LKA_ICON": 1,
|
||||
"TORQUE_REQUEST": 0,
|
||||
"LKA_ASSIST": 0,
|
||||
"STEER_REQ": 0,
|
||||
"STEER_MODE": 0,
|
||||
"HAS_LANE_SAFETY": 0,
|
||||
"NEW_SIGNAL_1": 0,
|
||||
"NEW_SIGNAL_2": 0,
|
||||
"DAMP_FACTOR": 100,
|
||||
}
|
||||
return [
|
||||
packer.make_can_msg("LFA", CAN.ECAN, lfa_values),
|
||||
create_angle_adas_cmd(packer, CAN, steering_angle, False, 0.0),
|
||||
]
|
||||
|
||||
|
||||
def create_suppress_lfa(packer, CAN, lfa_block_msg, lka_steering_alt):
|
||||
suppress_msg = "CAM_0x362" if lka_steering_alt else "CAM_0x2a4"
|
||||
msg_bytes = 32 if lka_steering_alt else 24
|
||||
@@ -387,6 +420,35 @@ def create_blindspot_status_messages(packer, CAN, rear_values, front_corner_valu
|
||||
]
|
||||
|
||||
|
||||
def create_ccnc_blindspot_status_messages(packer, CP, CAN, counter, left_blindspot=False, right_blindspot=False,
|
||||
left_escalated=False, right_escalated=False, drive_gear=False,
|
||||
left_warning_lamp=False, right_warning_lamp=False,
|
||||
left_sound_active=False, right_sound_active=False):
|
||||
left_state = 2 if left_blindspot and left_escalated else (1 if left_blindspot else 0)
|
||||
right_state = 2 if right_blindspot and right_escalated else (1 if right_blindspot else 0)
|
||||
left_osm_state = 2 if left_warning_lamp else 1 if left_state == 1 else 0
|
||||
right_osm_state = 2 if right_warning_lamp else 1 if right_state == 1 else 0
|
||||
desired_fields = {
|
||||
"BCW_IndSta": 1,
|
||||
"BCA_OnOffEquip2Sta": 2,
|
||||
"BCA_Sta": int(drive_gear),
|
||||
"BCW_LtIndSta": left_state,
|
||||
"BCW_RtIndSta": right_state,
|
||||
"BCW_LtSndWrngSta": int(left_sound_active),
|
||||
"BCW_RtSndWrngSta": int(right_sound_active),
|
||||
"OSMrrLamp_LtIndSta": left_osm_state,
|
||||
"OSMrrLamp_RtIndSta": right_osm_state,
|
||||
}
|
||||
|
||||
return [
|
||||
_create_ccnc_adrv_message_with_signals(
|
||||
packer, CP, CAN, 0x1BA, counter, "BLINDSPOTS_REAR_CORNERS", desired_fields,
|
||||
),
|
||||
# No retained radar input reproduces the stock RCTA target decision across routes.
|
||||
_create_ccnc_adrv_message(CP.carFingerprint, 0x1E5, CAN.ECAN, counter),
|
||||
]
|
||||
|
||||
|
||||
IONIQ_6_CLUSTER_BLINDSPOT_31A = {
|
||||
"right": (
|
||||
bytes.fromhex("fa7c10f0f0ffff03898aff0b0a8678ff000000007e0055550000000000000000"),
|
||||
@@ -731,6 +793,30 @@ def create_adrv_messages(packer, CAN, frame):
|
||||
return ret
|
||||
|
||||
|
||||
def create_ccnc_adrv_messages(packer, CP, CAN, frame, enabled, main_cruise_enabled, hud, out, is_metric,
|
||||
steering_available, steering_active, left_blindspot, right_blindspot,
|
||||
drive_gear=False,
|
||||
hba_icon=0,
|
||||
left_escalated=False, right_escalated=False,
|
||||
left_warning_lamp=False, right_warning_lamp=False,
|
||||
left_sound_active=False, right_sound_active=False):
|
||||
ret = [
|
||||
_create_ccnc_adrv_message(CP.carFingerprint, address, CAN.ECAN, frame // period)
|
||||
for address, period in _CCNC_ADRV_PERIODS[CP.carFingerprint].items() if frame % period == 0
|
||||
]
|
||||
if frame % 5 == 0:
|
||||
ret.extend(create_ccnc_angle_long_status_messages(
|
||||
packer, CP, CAN, frame // 5, enabled, main_cruise_enabled, hud, out, is_metric,
|
||||
steering_available, steering_active, hba_icon,
|
||||
))
|
||||
ret.extend(create_ccnc_blindspot_status_messages(
|
||||
packer, CP, CAN, frame // 5, left_blindspot, right_blindspot, left_escalated, right_escalated,
|
||||
drive_gear,
|
||||
left_warning_lamp, right_warning_lamp, left_sound_active, right_sound_active,
|
||||
))
|
||||
return ret
|
||||
|
||||
|
||||
def hkg_can_fd_checksum(address: int, sig, d: bytearray) -> int:
|
||||
crc = 0
|
||||
for i in range(2, len(d)):
|
||||
@@ -757,10 +843,39 @@ def hkg_can_fd_checksum(address: int, sig, d: bytearray) -> int:
|
||||
# brake, and accelerator bits are updated for the radar heartbeat.
|
||||
_ACCEL_BRAKE_ALT_TEMPLATE = bytes.fromhex("000000020000fcff000000000020000055ff000068000000")
|
||||
_KIA_EV9_ACCEL_BRAKE_ALT_TEMPLATE = bytes.fromhex("00000000ff006f00e80400001201030055ffff0000000000")
|
||||
# Neutral bodies verified across stock and successful suppression routes. Only
|
||||
# rolling integrity fields and the decoded state above are changed at runtime.
|
||||
_CCNC_ADRV_TEMPLATES = {
|
||||
CAR.KIA_EV9: {
|
||||
0x160: bytes.fromhex("0000000100000000fffc0100a8001000"),
|
||||
0x1DA: bytes.fromhex("0000002200110000000000000000000000000000000000000000000000000000"),
|
||||
0x1EA: bytes.fromhex("000000080000000000000000000000ff000000000000000000000000000f0f00"),
|
||||
0x200: bytes.fromhex("00000014801a0000"),
|
||||
0x345: bytes.fromhex("0000001500560000"),
|
||||
0x161: bytes.fromhex("0000000000000000c0fff0c003000040000000000000000000ff000000000000"),
|
||||
0x162: bytes.fromhex("0000002700000000000000000000000000000000000000000000000000000000"),
|
||||
0x1BA: bytes.fromhex("00000000000000880200000000000000000100000000000f"),
|
||||
0x1E5: bytes.fromhex("00000000000000000000220300000080"),
|
||||
0x1E0: bytes.fromhex("00000002000000000000000000000000"),
|
||||
0x38C: bytes.fromhex("000000f71f000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
}
|
||||
_CCNC_ADRV_PERIODS = {
|
||||
CAR.KIA_EV9: {
|
||||
0x160: 2,
|
||||
0x1DA: 100,
|
||||
0x1EA: 5,
|
||||
0x200: 5,
|
||||
0x345: 20,
|
||||
0x1E0: 5,
|
||||
0x38C: 20,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def create_accelerator_brake_alt_spoof(bus: int, counter: int, brake_pressed: bool, accelerator_pressed: bool,
|
||||
car_fingerprint=None) -> CanData:
|
||||
template = _KIA_EV9_ACCEL_BRAKE_ALT_TEMPLATE if str(car_fingerprint) == "KIA_EV9" else _ACCEL_BRAKE_ALT_TEMPLATE
|
||||
template = _KIA_EV9_ACCEL_BRAKE_ALT_TEMPLATE if car_fingerprint == CAR.KIA_EV9 else _ACCEL_BRAKE_ALT_TEMPLATE
|
||||
d = bytearray(template)
|
||||
d[2] = counter & 0xFF # COUNTER (bit 16, 8-bit)
|
||||
d[4] = (d[4] & ~0x01) | (0x01 if brake_pressed else 0x00) # BRAKE_PRESSED (bit 32)
|
||||
@@ -769,3 +884,111 @@ def create_accelerator_brake_alt_spoof(bus: int, counter: int, brake_pressed: bo
|
||||
d[0] = crc & 0xFF
|
||||
d[1] = (crc >> 8) & 0xFF
|
||||
return CanData(0x100, bytes(d), bus)
|
||||
|
||||
|
||||
def _create_ccnc_adrv_message(car_fingerprint, address: int, bus: int, counter: int) -> CanData:
|
||||
d = bytearray(_CCNC_ADRV_TEMPLATES[car_fingerprint][address])
|
||||
d[2] = counter & 0xFF
|
||||
crc = hkg_can_fd_checksum(address, None, d)
|
||||
d[0] = crc & 0xFF
|
||||
d[1] = (crc >> 8) & 0xFF
|
||||
return CanData(address, bytes(d), bus)
|
||||
|
||||
|
||||
def _set_ccnc_message_signals(packer, message_name: str, dat: bytearray, values: dict) -> None:
|
||||
dbc_msg = packer.dbc.name_to_msg[message_name]
|
||||
for name, value in values.items():
|
||||
sig = dbc_msg.sigs[name]
|
||||
ival = int(np.floor((value - sig.offset) / sig.factor + 0.5))
|
||||
if ival < 0:
|
||||
ival = (1 << sig.size) + ival
|
||||
_set_value(dat, sig, ival)
|
||||
|
||||
|
||||
def _create_ccnc_adrv_message_with_signals(packer, CP, CAN, address: int, counter: int,
|
||||
message_name: str, values: dict) -> CanData:
|
||||
msg = _create_ccnc_adrv_message(CP.carFingerprint, address, CAN.ECAN, counter)
|
||||
dat = bytearray(msg.dat)
|
||||
# Update decoded fields in the verified neutral payload.
|
||||
_set_ccnc_message_signals(packer, message_name, dat, values)
|
||||
crc = hkg_can_fd_checksum(address, None, dat)
|
||||
dat[0] = crc & 0xFF
|
||||
dat[1] = (crc >> 8) & 0xFF
|
||||
return CanData(address, bytes(dat), CAN.ECAN)
|
||||
|
||||
|
||||
def create_ccnc_acc_control(packer, CAN, enabled: bool, accel: float,
|
||||
stop_request: bool, cruise_standstill: bool, gas_override: bool, set_speed: float,
|
||||
main_mode_acc: int, lead_distance: float, lead_rel_speed: float, lead_visible: bool,
|
||||
v_ego: float, jerk_lower: float = 0.7, jerk_upper: float = 0.7):
|
||||
if not enabled or gas_override or stop_request:
|
||||
accel = 0.0
|
||||
|
||||
lead_visible = bool(enabled and lead_visible)
|
||||
desired_headway = min(max(round(1.625 * max(v_ego, 0.0), 1), 3.5), 204.6) if enabled else 204.6
|
||||
values = {
|
||||
"ACCMode": 0 if not enabled else (2 if gas_override else 1),
|
||||
"MainMode_ACC": int(bool(main_mode_acc)),
|
||||
"StopReq": 1 if stop_request and enabled else 0,
|
||||
"CRUISE_STANDSTILL": 1 if cruise_standstill and stop_request and enabled else 0,
|
||||
"aReqValue": accel,
|
||||
"aReqRaw": accel,
|
||||
"VSetDis": set_speed,
|
||||
"JerkLowerLimit": jerk_lower if enabled else 1.0,
|
||||
"JerkUpperLimit": jerk_upper if enabled else 3.0,
|
||||
"ACC_ObjDist": float(np.clip(lead_distance, 0.0, 204.7)) if lead_visible else 204.6,
|
||||
"ACC_ObjRelSpd": float(np.clip(lead_rel_speed, -16.4, 34.7)) if lead_visible else 34.6,
|
||||
"ObjValid": 0 if lead_visible else 1,
|
||||
"OBJ_STATUS": 2 if enabled and lead_visible else 0,
|
||||
"NEW_SIGNAL_3": 2 if lead_visible else 0,
|
||||
"NEW_SIGNAL_15": desired_headway,
|
||||
"SET_ME_2": 4,
|
||||
"SET_ME_3": 3,
|
||||
"SET_ME_TMP_64": 0x64,
|
||||
# Stock CCNC LKA-long routes use raw 7. The DBC's physical range is stale.
|
||||
"DISTANCE_SETTING": 7 if enabled else 0,
|
||||
}
|
||||
|
||||
return packer.make_can_msg("SCC_CONTROL", CAN.ECAN, values)
|
||||
|
||||
|
||||
def create_ccnc_angle_long_status_messages(packer, CP, CAN, counter: int, enabled: bool = False,
|
||||
main_cruise_enabled: bool = False, hud=None, out=None,
|
||||
is_metric: bool = True, steering_available: bool = False,
|
||||
steering_active: bool = False, hba_icon: int = 0) -> list[CanData]:
|
||||
cruise_speed = round(out.vCruiseCluster * (1 if is_metric else CV.KPH_TO_MPH)) if out is not None else 0
|
||||
display_speed = (40 if is_metric else 25) if cruise_speed > (145 if is_metric else 90) else max(cruise_speed, 0)
|
||||
main_standby = bool(main_cruise_enabled and not enabled)
|
||||
values_161 = {
|
||||
"FCA_ICON": 1, # orange: FCA unavailable
|
||||
"FCA_ALT_ICON": 0,
|
||||
"FCA_IMAGE": 0,
|
||||
"ALERTS_1": 0,
|
||||
"ALERTS_2": 0,
|
||||
"ALERTS_3": 0,
|
||||
"ALERTS_4": 0,
|
||||
"ALERTS_5": 0,
|
||||
"SOUNDS_1": 0,
|
||||
"SOUNDS_2": 0,
|
||||
"SOUNDS_3": 0,
|
||||
"SOUNDS_4": 0,
|
||||
"LFA_ICON": (2 if steering_active else 1) if steering_available else 0,
|
||||
"HBA_ICON": hba_icon if hba_icon in (1, 2) else 0,
|
||||
"HDA_ICON": 2 if enabled else 1 if main_standby else 0,
|
||||
"TARGET": 3 if enabled else 0,
|
||||
"SETSPEED": 3 if enabled else 1 if main_standby else 0,
|
||||
"SETSPEED_HUD": 2 if enabled else 1 if main_standby else 0,
|
||||
"SETSPEED_SPEED": display_speed if enabled or main_standby else 255,
|
||||
"DISTANCE": hud.leadDistanceBars if enabled and hud is not None else 0,
|
||||
"DISTANCE_SPACING": 3 if enabled or main_standby else 0,
|
||||
"DISTANCE_CAR": 2 if enabled else 1 if main_standby else 0,
|
||||
}
|
||||
values_162 = {fault: 0 for fault in (
|
||||
"FAULT_FSS", "FAULT_FCA", "FAULT_LSS", "FAULT_SLA", "FAULT_HDA", "FAULT_DAS", "FAULT_LFA", "FAULT_DAW",
|
||||
"FAULT_HBA", "FAULT_ESS",
|
||||
)}
|
||||
values_162["VIBRATE"] = 0
|
||||
return [
|
||||
_create_ccnc_adrv_message_with_signals(packer, CP, CAN, 0x161, counter, "CCNC_0x161", values_161),
|
||||
_create_ccnc_adrv_message_with_signals(packer, CP, CAN, 0x162, counter, "CCNC_0x162", values_162),
|
||||
]
|
||||
|
||||
@@ -28,6 +28,7 @@ ENABLE_BUTTONS = (ButtonType.accelCruise, ButtonType.decelCruise, ButtonType.can
|
||||
# Track when ECU disable happened - used to permanently suppress CAN errors from disabled ECU
|
||||
ECU_DISABLE_TIMESTAMP = 0.0
|
||||
KONA_NON_SCC_FCA_RADAR_ADDR = 0x602
|
||||
KIA_EV9_ACCEL_MAX = 2.5
|
||||
|
||||
|
||||
def apply_platform_longitudinal_params(ret: structs.CarParams) -> None:
|
||||
@@ -48,6 +49,12 @@ def apply_kia_ev6_gt_line_longitudinal_params(ret: structs.CarParams) -> None:
|
||||
ret.vEgoStarting = 0.5
|
||||
|
||||
|
||||
def apply_kia_ev9_longitudinal_params(ret: structs.CarParams) -> None:
|
||||
ret.startAccel = 0.2
|
||||
ret.longitudinalActuatorDelay = 0.3
|
||||
ret.vEgoStarting = 0.5
|
||||
|
||||
|
||||
def apply_ecu_disable_failure_fallback(CP: structs.CarParams, params) -> None:
|
||||
params.put_bool("EcuDisableFailed", True)
|
||||
CP.safetyConfigs[-1].safetyParam &= ~HyundaiSafetyFlags.LONG.value
|
||||
@@ -74,7 +81,8 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
@staticmethod
|
||||
def get_pid_accel_limits(CP, current_speed, cruise_speed):
|
||||
return ACCEL_MIN, CarControllerParams.ACCEL_MAX
|
||||
accel_max = KIA_EV9_ACCEL_MAX if CP.carFingerprint == CAR.KIA_EV9 else CarControllerParams.ACCEL_MAX
|
||||
return ACCEL_MIN, accel_max
|
||||
|
||||
@staticmethod
|
||||
def apply_post_fingerprint_params(CP: structs.CarParams, candidate, fingerprint, car_fw) -> None:
|
||||
@@ -91,6 +99,10 @@ class CarInterface(CarInterfaceBase):
|
||||
# "LFA steering" if camera directly sends LFA to the MDPS
|
||||
cam_can = CanBus(None, fingerprint).CAM
|
||||
lka_steering = 0x50 in fingerprint[cam_can] or 0x110 in fingerprint[cam_can]
|
||||
if ret.flags & HyundaiFlags.CAN_CANFD_BLENDED:
|
||||
lka_steering = Ecu.adas in [fw.ecu for fw in car_fw] or 0x50 in fingerprint[cam_can]
|
||||
if lka_steering:
|
||||
ret.flags |= HyundaiFlags.CANFD_LKA_STEERING.value
|
||||
CAN = CanBus(None, fingerprint, lka_steering)
|
||||
|
||||
if ret.flags & HyundaiFlags.CANFD:
|
||||
@@ -104,10 +116,10 @@ class CarInterface(CarInterfaceBase):
|
||||
# Most angle-steering LKA platforms still need stock longitudinal validation.
|
||||
ret.alphaLongitudinalAvailable = False
|
||||
|
||||
ret.enableBsm = 0x1ba in fingerprint[CAN.ECAN]
|
||||
ret.enableBsm = 0x1ba in fingerprint[CAN.ECAN] or candidate == CAR.KIA_EV9
|
||||
|
||||
# Check if the car is hybrid. Only HEV/PHEV cars have 0xFA on E-CAN.
|
||||
if 0xFA in fingerprint[CAN.ECAN]:
|
||||
# Carnival HEV can fingerprint with too little E-CAN traffic to see 0xFA.
|
||||
if 0xFA in fingerprint[CAN.ECAN] or candidate == CAR.KIA_CARNIVAL_HEV_4TH_GEN:
|
||||
ret.flags |= HyundaiFlags.HYBRID.value
|
||||
|
||||
if lka_steering:
|
||||
@@ -115,6 +127,10 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.flags |= HyundaiFlags.CANFD_LKA_STEERING.value
|
||||
if 0x110 in fingerprint[CAN.CAM]:
|
||||
ret.flags |= HyundaiFlags.CANFD_LKA_STEERING_ALT.value
|
||||
# This HDA II Carnival uses the alternate 0x1AA cruise-button frame even
|
||||
# though other LKA-steering platforms use 0x1CF.
|
||||
if candidate == CAR.KIA_CARNIVAL_2025 and 0x1aa in fingerprint[CAN.ECAN] and 0x1cf not in fingerprint[CAN.ECAN]:
|
||||
ret.flags |= HyundaiFlags.CANFD_ALT_BUTTONS.value
|
||||
else:
|
||||
# no LKA steering
|
||||
if 0x1cf not in fingerprint[CAN.ECAN]:
|
||||
@@ -148,12 +164,21 @@ class CarInterface(CarInterfaceBase):
|
||||
if ret.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
|
||||
ret.steerControlType = structs.CarParams.SteerControlType.angle
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CANFD_ANGLE_STEERING.value
|
||||
if candidate == CAR.HYUNDAI_IONIQ_6:
|
||||
# Keep lateral active through stops: zeroing torque at standstill dropped the
|
||||
# stop-turn hold and forced a rate-limit re-ramp from zero on every pull-away
|
||||
# (turn1/turn2 rlogs 2026-07-14). Torque steering has no standstill gate in the
|
||||
# panda safety or the carcontroller; the MDPS tolerating held torque at 0 speed
|
||||
# is being validated on-road.
|
||||
ret.steerAtStandstill = True
|
||||
if ret.flags & HyundaiFlags.CCNC and not ret.flags & HyundaiFlags.CANFD_LKA_STEERING:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CCNC.value
|
||||
|
||||
else:
|
||||
# Shared configuration for non CAN-FD cars
|
||||
ret.alphaLongitudinalAvailable = candidate not in UNSUPPORTED_LONGITUDINAL_CAR or candidate in LEGACY_LONGITUDINAL_CAR
|
||||
if ret.flags & HyundaiFlags.CAN_CANFD_BLENDED and ret.flags & HyundaiFlags.CANFD_LKA_STEERING:
|
||||
ret.alphaLongitudinalAvailable = False
|
||||
ret.enableBsm = 0x58b in fingerprint[CAN.ECAN]
|
||||
|
||||
# Send LFA message on cars with HDA
|
||||
@@ -174,12 +199,16 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
if ret.flags & HyundaiFlags.CAMERA_SCC:
|
||||
ret.safetyConfigs[0].safetyParam |= HyundaiSafetyFlags.CAMERA_SCC.value
|
||||
if candidate in (CAR.HYUNDAI_ELANTRA_2024, CAR.HYUNDAI_ELANTRA_HEV_2024):
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CAN_REFRESH_MSGS.value
|
||||
|
||||
# These cars expose an LKAS/LFA steering-wheel button that StarPilot can customize.
|
||||
if 0x391 in fingerprint[0] or ret.flags & HyundaiFlags.CAN_CANFD_BLENDED:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiStarPilotSafetyFlags.HAS_LDA_BUTTON.value
|
||||
if ret.flags & HyundaiFlags.CAN_CANFD_BLENDED:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CAN_CANFD_BLENDED.value
|
||||
if ret.flags & HyundaiFlags.CANFD_LKA_STEERING:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CANFD_LKA_STEERING.value
|
||||
if hyundai_cancel_button_enables_cruise(candidate):
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CANCEL_BTN_ENABLE.value
|
||||
|
||||
@@ -218,6 +247,8 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
if ret.openpilotLongitudinalControl:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.LONG.value
|
||||
if candidate in CANFD_ANGLE_LONGITUDINAL_CAR and ret.flags & HyundaiFlags.CCNC:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.CCNC.value
|
||||
if ret.flags & HyundaiFlags.HYBRID:
|
||||
ret.safetyConfigs[-1].safetyParam |= HyundaiSafetyFlags.HYBRID_GAS.value
|
||||
elif ret.flags & HyundaiFlags.EV:
|
||||
@@ -238,9 +269,20 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.vEgoStarting = 0.5
|
||||
ret.vEgoStopping = 0.35
|
||||
|
||||
if candidate == CAR.HYUNDAI_ELANTRA_2021:
|
||||
ret.longitudinalActuatorDelay = 0.22
|
||||
ret.stopAccel = -0.85
|
||||
ret.stoppingDecelRate = 0.35
|
||||
|
||||
if candidate == CAR.HYUNDAI_ELANTRA_HEV_2024:
|
||||
ret.longitudinalActuatorDelay = 0.22
|
||||
|
||||
if candidate == CAR.HYUNDAI_IONIQ_6:
|
||||
ret.longitudinalActuatorDelay = 0.6
|
||||
|
||||
if candidate == CAR.KIA_EV9 and ret.openpilotLongitudinalControl:
|
||||
apply_kia_ev9_longitudinal_params(ret)
|
||||
|
||||
if candidate == CAR.KIA_NIRO_PHEV_2022:
|
||||
ret.stopAccel = -1.4
|
||||
ret.stoppingDecelRate = 0.5
|
||||
|
||||
@@ -8,21 +8,25 @@ from opendbc.car import Bus, ButtonType, gen_empty_fingerprint, structs
|
||||
from opendbc.car.structs import CarControl, CarParams
|
||||
from opendbc.car.fw_versions import build_fw_dict, match_fw_to_car
|
||||
from opendbc.car.hyundai.carcontroller import CarController, Ioniq6LongitudinalTuningState, GenesisG90LongitudinalTuningState, \
|
||||
EV9LongitudinalTuningState, update_ev9_longitudinal_tuning, \
|
||||
BlindspotWarningState, update_blindspot_warning, \
|
||||
reset_egmp_longitudinal_tuning, \
|
||||
update_ioniq_6_longitudinal_tuning, \
|
||||
update_genesis_g90_longitudinal_tuning, egmp_dynamic_longitudinal_tuning, \
|
||||
should_reset_ev6_gt_line_longitudinal_tuning, reset_ev6_gt_line_longitudinal_tuning, \
|
||||
get_angle_smoothing_alpha, apply_ev9_high_angle_gain_cap
|
||||
direct_angle_request_allowed, get_angle_smoothing_alpha, \
|
||||
should_use_ev6_gt_line_stop_direct_tracking
|
||||
from opendbc.car.hyundai.carstate import CarState, decode_canfd_camera_lead, decode_ioniq_6_blindspot_radar_state
|
||||
from opendbc.car.hyundai.interface import CarInterface
|
||||
from opendbc.car.hyundai.interface import CarInterface, KIA_EV9_ACCEL_MAX
|
||||
from opendbc.car.hyundai import hyundaican, hyundaicanfd
|
||||
from opendbc.car.hyundai.hyundaicanfd import CanBus
|
||||
from opendbc.car.hyundai.radar_interface import MRREVO14F_RADAR_START_ADDR, MRR30_RADAR_START_ADDR, MRR35_RADAR_START_ADDR, \
|
||||
RADAR_START_ADDR, get_radar_track_config
|
||||
from opendbc.car.hyundai.values import CAMERA_SCC_CAR, CANFD_CAR, CAN_GEARS, CAR, CHECKSUM, DATE_FW_ECUS, \
|
||||
from opendbc.car.hyundai.values import CAMERA_SCC_CAR, CANFD_CAR, CAN_GEARS, CAR, CHECKSUM, DATE_FW_ECUS, DATELESS_FUZZY_CARS, \
|
||||
HYBRID_CAR, EV_CAR, FW_QUERY_CONFIG, LEGACY_SAFETY_MODE_CAR, CANFD_FUZZY_WHITELIST, \
|
||||
UNSUPPORTED_LONGITUDINAL_CAR, PLATFORM_CODE_ECUS, HYUNDAI_VERSION_REQUEST_LONG, \
|
||||
LEGACY_LONGITUDINAL_CAR, DBC, HyundaiFlags, get_platform_codes, HyundaiSafetyFlags, \
|
||||
HyundaiStarPilotSafetyFlags, Buttons, kia_ev6_gt_line_longitudinal_tuning
|
||||
HyundaiStarPilotSafetyFlags, Buttons, CarControllerParams, kia_ev6_gt_line_longitudinal_tuning
|
||||
|
||||
LongCtrlState = CarControl.Actuators.LongControlState
|
||||
from opendbc.car.hyundai.fingerprints import FW_VERSIONS
|
||||
@@ -52,6 +56,7 @@ NO_DATES_PLATFORMS = {
|
||||
CAR.KIA_OPTIMA_G4_FL,
|
||||
CAR.KIA_SORENTO,
|
||||
CAR.HYUNDAI_KONA,
|
||||
CAR.HYUNDAI_KONA_NON_SCC,
|
||||
CAR.HYUNDAI_KONA_EV,
|
||||
CAR.HYUNDAI_KONA_EV_2022,
|
||||
CAR.HYUNDAI_KONA_HEV,
|
||||
@@ -89,6 +94,8 @@ CCNC_NON_HDA2_CARS = (
|
||||
CAR.HYUNDAI_SANTA_CRUZ_2025,
|
||||
CAR.KIA_K4_2025,
|
||||
CAR.KIA_K5_2025,
|
||||
CAR.KIA_CARNIVAL_2025,
|
||||
CAR.KIA_CARNIVAL_HEV_4TH_GEN,
|
||||
CAR.KIA_SPORTAGE_2026,
|
||||
CAR.KIA_SORENTO_2024,
|
||||
)
|
||||
@@ -235,16 +242,21 @@ class TestHyundaiFingerprint:
|
||||
fingerprint[ev9_radar_config.bus][ev9_radar_config.start_addr] = ev9_radar_config.expected_length
|
||||
ev9_car_fw = [CarParams.CarFw(ecu=Ecu.adas, fwVersion=b"", address=0x730, brand="hyundai")]
|
||||
CP = CarInterface.get_params(CAR.KIA_EV9, fingerprint, ev9_car_fw, True, False, False, None)
|
||||
assert not CP.alphaLongitudinalAvailable
|
||||
assert not CP.openpilotLongitudinalControl
|
||||
assert CP.alphaLongitudinalAvailable
|
||||
assert CP.openpilotLongitudinalControl
|
||||
assert not CP.radarUnavailable
|
||||
assert CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
|
||||
assert not (CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.LONG)
|
||||
assert CP.flags & HyundaiFlags.CCNC
|
||||
assert egmp_dynamic_longitudinal_tuning(CP)
|
||||
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.LONG
|
||||
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CANFD_ANGLE_STEERING
|
||||
|
||||
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CCNC
|
||||
CP = CarInterface.get_params(CAR.KIA_EV9, fingerprint, [], True, False, False, None)
|
||||
assert not CP.openpilotLongitudinalControl
|
||||
assert CP.openpilotLongitudinalControl
|
||||
|
||||
CP = CarInterface.get_params(CAR.KIA_EV9, fingerprint, ev9_car_fw, False, False, False, None)
|
||||
assert not CP.openpilotLongitudinalControl
|
||||
assert not (CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CCNC)
|
||||
for candidate in HYUNDAI_NON_SCC_CARS:
|
||||
CP = CarInterface.get_params(candidate, gen_empty_fingerprint(), [], True, False, False, None)
|
||||
assert bool(CP.flags & HyundaiFlags.NON_SCC)
|
||||
@@ -254,6 +266,7 @@ class TestHyundaiFingerprint:
|
||||
CP = CarInterface.get_params(CAR.KIA_SPORTAGE_HEV_2026, gen_empty_fingerprint(), [], False, False, False, None)
|
||||
assert CP.steerControlType == CarParams.SteerControlType.angle
|
||||
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CANFD_ANGLE_STEERING
|
||||
assert not (CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CCNC)
|
||||
|
||||
fingerprint = gen_empty_fingerprint()
|
||||
cam_can = CanBus(None, fingerprint).CAM
|
||||
@@ -288,28 +301,59 @@ class TestHyundaiFingerprint:
|
||||
assert get_angle_smoothing_alpha(ev9_cp, 20.0) == pytest.approx(get_angle_smoothing_alpha(other_cp, 20.0))
|
||||
assert get_angle_smoothing_alpha(other_cp, 20.0) == pytest.approx(0.0)
|
||||
|
||||
def test_ev9_high_angle_gain_cap_is_ev9_only_and_nonzero(self):
|
||||
ev9_cp = SimpleNamespace(carFingerprint=CAR.KIA_EV9, flags=int(HyundaiFlags.CANFD_ANGLE_STEERING))
|
||||
sportage_cp = SimpleNamespace(carFingerprint=CAR.KIA_SPORTAGE_HEV_2026, flags=int(HyundaiFlags.CANFD_ANGLE_STEERING))
|
||||
def test_ev9_direct_angle_waits_for_safety_envelope(self):
|
||||
CP = CarInterface.get_params(CAR.KIA_EV9, gen_empty_fingerprint(), [], True, False, False, None)
|
||||
controller = CarController(DBC[CP.carFingerprint], CP)
|
||||
|
||||
assert apply_ev9_high_angle_gain_cap(ev9_cp, 0.70, 60.0, True) == pytest.approx(0.70)
|
||||
assert apply_ev9_high_angle_gain_cap(ev9_cp, 0.70, 120.0, True) == pytest.approx(0.55)
|
||||
assert apply_ev9_high_angle_gain_cap(ev9_cp, 0.70, 320.0, True) == pytest.approx(0.16)
|
||||
assert apply_ev9_high_angle_gain_cap(ev9_cp, 0.0, 320.0, True) > 0.0
|
||||
assert apply_ev9_high_angle_gain_cap(ev9_cp, 0.70, 320.0, False) == pytest.approx(0.70)
|
||||
assert apply_ev9_high_angle_gain_cap(sportage_cp, 0.70, 320.0, True) == pytest.approx(0.70)
|
||||
assert not direct_angle_request_allowed(8.47, 155.5, 155.6, True, controller.BASELINE_VM, controller.params)
|
||||
assert direct_angle_request_allowed(8.47, 140.0, 140.0, True, controller.BASELINE_VM, controller.params)
|
||||
assert not direct_angle_request_allowed(8.47, 140.0, 140.0, False, controller.BASELINE_VM, controller.params)
|
||||
|
||||
def test_ccnc_hda2_lka_layout_does_not_set_ccnc_safety_param(self):
|
||||
def test_angle_platforms_disable_standstill_steering(self):
|
||||
ev9_cp = CarInterface.get_params(CAR.KIA_EV9, gen_empty_fingerprint(), [], False, False, False, None)
|
||||
ioniq_5_pe_cp = CarInterface.get_params(CAR.HYUNDAI_IONIQ_5_PE, gen_empty_fingerprint(), [], False, False, False, None)
|
||||
sportage_cp = CarInterface.get_params(CAR.KIA_SPORTAGE_HEV_2026, gen_empty_fingerprint(), [], False, False, False, None)
|
||||
|
||||
assert not ev9_cp.steerAtStandstill
|
||||
assert not ioniq_5_pe_cp.steerAtStandstill
|
||||
assert not sportage_cp.steerAtStandstill
|
||||
|
||||
@pytest.mark.parametrize("candidate", (CAR.KIA_K4_2025, CAR.KIA_CARNIVAL_2025, CAR.KIA_CARNIVAL_HEV_4TH_GEN))
|
||||
def test_ccnc_hda2_lka_layout_does_not_set_ccnc_safety_param(self, candidate):
|
||||
fingerprint = gen_empty_fingerprint()
|
||||
cam_can = CanBus(None, fingerprint).CAM
|
||||
fingerprint[cam_can] = {0x50: 16}
|
||||
|
||||
CP = CarInterface.get_params(CAR.KIA_K4_2025, fingerprint, [], False, False, False, None)
|
||||
CP = CarInterface.get_params(candidate, fingerprint, [], False, False, False, None)
|
||||
|
||||
assert CP.flags & HyundaiFlags.CCNC
|
||||
assert CP.flags & HyundaiFlags.CANFD_LKA_STEERING
|
||||
assert not (CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CCNC)
|
||||
|
||||
def test_carnival_hev_sets_hybrid_gas_safety(self):
|
||||
CP = CarInterface.get_params(CAR.KIA_CARNIVAL_HEV_4TH_GEN, gen_empty_fingerprint(), [], False, False, False, None)
|
||||
|
||||
assert CP.flags & HyundaiFlags.HYBRID
|
||||
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.HYBRID_GAS
|
||||
|
||||
def test_carnival_2025_hda2_detects_alternate_buttons(self):
|
||||
fingerprint = gen_empty_fingerprint()
|
||||
CAN = CanBus(None, fingerprint)
|
||||
fingerprint[CAN.CAM] = {0x110: 32}
|
||||
fingerprint[1] = {0x1aa: 16}
|
||||
|
||||
carnival_cp = CarInterface.get_params(CAR.KIA_CARNIVAL_2025, fingerprint, [], False, False, False, None)
|
||||
assert carnival_cp.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
|
||||
assert carnival_cp.flags & HyundaiFlags.CANFD_ALT_BUTTONS
|
||||
assert carnival_cp.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CANFD_ALT_BUTTONS
|
||||
|
||||
carnival_state = CarState(carnival_cp, None)
|
||||
pt_states = {state.name for state in carnival_state.get_can_parsers(carnival_cp)[Bus.pt].message_states.values()}
|
||||
assert "CRUISE_BUTTONS" not in pt_states
|
||||
|
||||
k4_cp = CarInterface.get_params(CAR.KIA_K4_2025, fingerprint, [], False, False, False, None)
|
||||
assert not (k4_cp.flags & HyundaiFlags.CANFD_ALT_BUTTONS)
|
||||
|
||||
def test_ioniq_6_hda1_layout_stays_non_lka(self):
|
||||
fingerprint = gen_empty_fingerprint()
|
||||
fingerprint[1] = {0x100: 8, 0x110: 8}
|
||||
@@ -326,6 +370,62 @@ class TestHyundaiFingerprint:
|
||||
assert palisade_2023.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CAN_CANFD_BLENDED
|
||||
assert palisade_2023.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CANCEL_BTN_ENABLE
|
||||
|
||||
def test_palisade_telluride_hda2_uses_mixed_can_layout(self):
|
||||
fingerprint = gen_empty_fingerprint()
|
||||
fingerprint[2][0x50] = 16
|
||||
car_fw = [CarParams.CarFw(ecu=Ecu.adas, fwVersion=b"", address=0x730, brand="hyundai")]
|
||||
|
||||
CP = CarInterface.get_params(CAR.HYUNDAI_PALISADE_2023, fingerprint, car_fw, True, False, False, None)
|
||||
can_bus = CanBus(CP)
|
||||
parsers = CarState(CP, None).get_can_parsers(CP)
|
||||
|
||||
assert CP.flags & HyundaiFlags.CAN_CANFD_BLENDED
|
||||
assert CP.flags & HyundaiFlags.CANFD_LKA_STEERING
|
||||
assert not CP.alphaLongitudinalAvailable
|
||||
assert not CP.openpilotLongitudinalControl
|
||||
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CAN_CANFD_BLENDED
|
||||
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CANFD_LKA_STEERING
|
||||
assert can_bus.ACAN == 0
|
||||
assert can_bus.ECAN == 1
|
||||
assert parsers[Bus.pt].bus == 1
|
||||
assert parsers[Bus.cam].bus == 2
|
||||
assert CarControllerParams(CP).STEER_MAX == 384
|
||||
|
||||
def test_palisade_telluride_hda2_sends_lkas_and_camera_suppression(self):
|
||||
fingerprint = gen_empty_fingerprint()
|
||||
fingerprint[2][0x50] = 16
|
||||
car_fw = [CarParams.CarFw(ecu=Ecu.adas, fwVersion=b"", address=0x730, brand="hyundai")]
|
||||
CP = CarInterface.get_params(CAR.HYUNDAI_PALISADE_2023, fingerprint, car_fw, False, False, False, None)
|
||||
controller = CarController(DBC[CP.carFingerprint], CP)
|
||||
controller.frame = 0
|
||||
|
||||
hud_control = SimpleNamespace(
|
||||
visualAlert=CarControl.HUDControl.VisualAlert.none,
|
||||
leftLaneVisible=True,
|
||||
rightLaneVisible=True,
|
||||
leftLaneDepart=False,
|
||||
rightLaneDepart=False,
|
||||
)
|
||||
lfa_block_msg = {f"BYTE{i}": 0 for i in range(3, 24) if i != 7}
|
||||
lfa_block_msg["COUNTER"] = 0
|
||||
CS = SimpleNamespace(lfa_block_msg=lfa_block_msg, redneck_send_button=Buttons.NONE)
|
||||
CC = SimpleNamespace(enabled=True, cruiseControl=SimpleNamespace(cancel=False, resume=False))
|
||||
actuators = SimpleNamespace(longControlState=LongCtrlState.off)
|
||||
|
||||
msgs = controller.create_can_msgs(True, 100, False, 0.0, 0.0, False, hud_control, actuators, CS, CC, 2)
|
||||
msg_addrs_buses = {(addr, bus) for addr, _, bus in msgs}
|
||||
|
||||
assert (0x50, 0) in msg_addrs_buses
|
||||
assert (0x2A4, 0) in msg_addrs_buses
|
||||
assert not ({0x340, 0x364} & {addr for addr, _, _ in msgs})
|
||||
|
||||
@pytest.mark.parametrize("candidate", (CAR.HYUNDAI_ELANTRA_2024, CAR.HYUNDAI_ELANTRA_HEV_2024))
|
||||
def test_hyundai_can_refresh_platforms_use_refresh_dbc_and_safety_param(self, candidate):
|
||||
CP = CarInterface.get_params(candidate, gen_empty_fingerprint(), [], False, False, False, None)
|
||||
|
||||
assert DBC[CP.carFingerprint][Bus.pt] == "hyundai_can_refresh_generated"
|
||||
assert CP.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CAN_REFRESH_MSGS
|
||||
|
||||
def test_hyundai_lkas_button_sets_starpilot_safety_flag(self):
|
||||
fingerprint = gen_empty_fingerprint()
|
||||
fingerprint[0][0x391] = 8
|
||||
@@ -347,6 +447,30 @@ class TestHyundaiFingerprint:
|
||||
palisade_2023 = CarInterface.get_params(CAR.HYUNDAI_PALISADE_2023, gen_empty_fingerprint(), [], True, False, False, None)
|
||||
assert palisade_2023.safetyConfigs[-1].safetyParam & HyundaiStarPilotSafetyFlags.HAS_LDA_BUTTON
|
||||
|
||||
def test_sonata_hybrid_aol_main_lkas_sync_is_scoped(self):
|
||||
toggles = SimpleNamespace(always_on_lateral_lkas=True, main_cruise_aol_toggle=True)
|
||||
|
||||
sonata_hybrid_cp = CarInterface.get_params(CAR.HYUNDAI_SONATA_HYBRID, gen_empty_fingerprint(), [], False, False, False, None)
|
||||
sonata_hybrid_fpcp = CarInterface.get_starpilot_params(
|
||||
CAR.HYUNDAI_SONATA_HYBRID, gen_empty_fingerprint(), [], sonata_hybrid_cp, toggles,
|
||||
)
|
||||
assert sonata_hybrid_fpcp.safetyConfigs[-1].safetyParam & HyundaiStarPilotSafetyFlags.AOL_MAIN_LKAS_SYNC
|
||||
|
||||
sonata_cp = CarInterface.get_params(CAR.HYUNDAI_SONATA, gen_empty_fingerprint(), [], False, False, False, None)
|
||||
sonata_fpcp = CarInterface.get_starpilot_params(CAR.HYUNDAI_SONATA, gen_empty_fingerprint(), [], sonata_cp, toggles)
|
||||
assert not (sonata_fpcp.safetyConfigs[-1].safetyParam & HyundaiStarPilotSafetyFlags.AOL_MAIN_LKAS_SYNC)
|
||||
|
||||
disabled_toggles = SimpleNamespace(always_on_lateral_lkas=True, main_cruise_aol_toggle=False)
|
||||
disabled_fpcp = CarInterface.get_starpilot_params(
|
||||
CAR.HYUNDAI_SONATA_HYBRID, gen_empty_fingerprint(), [], sonata_hybrid_cp, disabled_toggles,
|
||||
)
|
||||
assert not (disabled_fpcp.safetyConfigs[-1].safetyParam & HyundaiStarPilotSafetyFlags.AOL_MAIN_LKAS_SYNC)
|
||||
|
||||
minimal_fpcp = CarInterface.get_starpilot_params(
|
||||
CAR.HYUNDAI_SONATA_HYBRID, gen_empty_fingerprint(), [], sonata_hybrid_cp, SimpleNamespace(),
|
||||
)
|
||||
assert not (minimal_fpcp.safetyConfigs[-1].safetyParam & HyundaiStarPilotSafetyFlags.AOL_MAIN_LKAS_SYNC)
|
||||
|
||||
def test_non_scc_flag_quirks(self):
|
||||
elantra_hev = CarInterface.get_params(CAR.HYUNDAI_ELANTRA_HEV_2022_NON_SCC, gen_empty_fingerprint(), [], True, False, False, None)
|
||||
assert elantra_hev.flags & HyundaiFlags.HYBRID
|
||||
@@ -478,6 +602,20 @@ class TestHyundaiFingerprint:
|
||||
events = car_state.create_cruise_button_events(Buttons.CANCEL, Buttons.NONE)
|
||||
assert [(be.type, be.pressed) for be in events] == [(ButtonType.cancel, True)]
|
||||
|
||||
def test_ccnc_angle_long_main_cruise_toggle(self):
|
||||
car_state = SimpleNamespace(main_cruise_on=False)
|
||||
ret = SimpleNamespace(
|
||||
cruiseState=SimpleNamespace(available=True),
|
||||
buttonEvents=[structs.CarState.ButtonEvent(pressed=True, type=ButtonType.mainCruise)],
|
||||
)
|
||||
assert CarState.update_main_cruise(car_state, ret)
|
||||
|
||||
ret.buttonEvents = [structs.CarState.ButtonEvent(pressed=False, type=ButtonType.mainCruise)]
|
||||
assert CarState.update_main_cruise(car_state, ret)
|
||||
|
||||
ret.buttonEvents = [structs.CarState.ButtonEvent(pressed=True, type=ButtonType.mainCruise)]
|
||||
assert not CarState.update_main_cruise(car_state, ret)
|
||||
|
||||
def test_palisade_2023_cancel_release_enables_from_standby(self):
|
||||
toggles = get_test_toggles()
|
||||
CP = CarInterface.get_params(CAR.HYUNDAI_PALISADE_2023, gen_empty_fingerprint(), [], True, False, False, toggles)
|
||||
@@ -625,6 +763,20 @@ class TestHyundaiFingerprint:
|
||||
assert CP.vEgoStopping == pytest.approx(0.35)
|
||||
assert CP.stoppingDecelRate == pytest.approx(0.35)
|
||||
|
||||
def test_elantra_2021_longitudinal_params_match_observed_response(self):
|
||||
toggles = get_test_toggles()
|
||||
CP = CarInterface.get_params(CAR.HYUNDAI_ELANTRA_2021, gen_empty_fingerprint(), [], True, False, False, toggles)
|
||||
|
||||
assert CP.longitudinalActuatorDelay == pytest.approx(0.22)
|
||||
assert CP.stopAccel == pytest.approx(-0.85)
|
||||
assert CP.stoppingDecelRate == pytest.approx(0.35)
|
||||
|
||||
def test_elantra_hev_2024_longitudinal_delay_matches_observed_response(self):
|
||||
toggles = get_test_toggles()
|
||||
CP = CarInterface.get_params(CAR.HYUNDAI_ELANTRA_HEV_2024, gen_empty_fingerprint(), [], True, False, False, toggles)
|
||||
|
||||
assert CP.longitudinalActuatorDelay == pytest.approx(0.22)
|
||||
|
||||
def test_kia_niro_phev_2022_longitudinal_params_soften_final_stop_hold(self):
|
||||
toggles = get_test_toggles()
|
||||
CP = CarInterface.get_params(CAR.KIA_NIRO_PHEV_2022, gen_empty_fingerprint(), [], True, False, False, toggles)
|
||||
@@ -653,6 +805,35 @@ class TestHyundaiFingerprint:
|
||||
def test_kona_ev_non_scc_has_no_dedicated_fw_coverage(self):
|
||||
assert CAR.HYUNDAI_KONA_EV_NON_SCC not in FW_VERSIONS
|
||||
|
||||
def test_elantra_hev_2026_route_fw_exact_matches_2024_platform(self):
|
||||
route_fw = {
|
||||
(Ecu.fwdCamera, 0x7c4): b'\xf1\x00CN7HMFC AT USA LHD 1.00 1.05 99210-AA510 240509',
|
||||
(Ecu.fwdRadar, 0x7d0): b'\xf1\x00CN7_ RDR ----- 1.00 1.01 99110-AA500 ',
|
||||
(Ecu.eps, 0x7d4): b'\xf1\x00CN7 MDPS C 1.00 1.03 56300BY670\x00 4CSHC103',
|
||||
}
|
||||
car_fw = [
|
||||
CarParams.CarFw(ecu=ecu, fwVersion=version, address=address, subAddress=0, brand="hyundai")
|
||||
for (ecu, address), version in route_fw.items()
|
||||
]
|
||||
|
||||
exact, matches = match_fw_to_car(car_fw, "", allow_exact=True, allow_fuzzy=False, log=False)
|
||||
assert exact
|
||||
assert matches == {CAR.HYUNDAI_ELANTRA_HEV_2024}
|
||||
|
||||
def test_kia_carnival_2025_canadian_route_fw_exact_matches(self):
|
||||
route_fw = {
|
||||
(Ecu.fwdCamera, 0x7c4): b'\xf1\x00KA4 MFC AT CAN LHD 1.00 1.00 99210-R0700 250324',
|
||||
(Ecu.fwdRadar, 0x7d0): b'\xf1\x00KA4_ RDR ----- 1.00 1.01 99110-R0510 ',
|
||||
}
|
||||
car_fw = [
|
||||
CarParams.CarFw(ecu=ecu, fwVersion=version, address=address, subAddress=0, brand="hyundai")
|
||||
for (ecu, address), version in route_fw.items()
|
||||
]
|
||||
|
||||
exact, matches = match_fw_to_car(car_fw, "", allow_exact=True, allow_fuzzy=False, log=False)
|
||||
assert exact
|
||||
assert matches == {CAR.KIA_CARNIVAL_2025}
|
||||
|
||||
def test_kona_non_scc_fca_radar_fw_is_optional(self):
|
||||
fw_versions = FW_VERSIONS[CAR.HYUNDAI_KONA_NON_SCC]
|
||||
car_fw = [
|
||||
@@ -671,6 +852,22 @@ class TestHyundaiFingerprint:
|
||||
assert exact
|
||||
assert CAR.HYUNDAI_KONA_NON_SCC in matches
|
||||
|
||||
def test_kona_non_scc_fw_matches_with_unstable_transmission_padding(self):
|
||||
route_fw = {
|
||||
(Ecu.eps, 0x7d4): b'\xf1\x00OS MDPS C 1.00 1.05 56310/J9500 4OSDC105',
|
||||
(Ecu.fwdCamera, 0x7c4): b'\xf1\x00OS9 LKAS AT AUS RHD 1.00 1.00 95740-J9200 g30',
|
||||
(Ecu.fwdRadar, 0x7d0): b'\xf1\x00OS__ FCA --CUP 1.00 1.00 95655-J9100 ',
|
||||
(Ecu.transmission, 0x7e1): b'\xf1\x006U2V0_C2\x00\x006U2V1051\x00\x00DOS4T16AS2\x0e\xdc_\xa7',
|
||||
}
|
||||
car_fw = [
|
||||
CarParams.CarFw(ecu=ecu, fwVersion=version, address=address, subAddress=0, brand="hyundai")
|
||||
for (ecu, address), version in route_fw.items()
|
||||
]
|
||||
|
||||
exact, matches = match_fw_to_car(car_fw, "", log=False)
|
||||
assert not exact
|
||||
assert matches == {CAR.HYUNDAI_KONA_NON_SCC}
|
||||
|
||||
def test_kia_forte_2019_non_scc_does_not_require_fca11_or_scc12(self):
|
||||
toggles = get_test_toggles()
|
||||
CP = CarInterface.get_params(CAR.KIA_FORTE_2019_NON_SCC, gen_empty_fingerprint(), [], True, False, False, toggles)
|
||||
@@ -1003,6 +1200,18 @@ class TestHyundaiFingerprint:
|
||||
assert CP.longitudinalActuatorDelay == pytest.approx(0.6)
|
||||
assert CP.startingState
|
||||
|
||||
def test_ev9_longitudinal_params_match_observed_response(self):
|
||||
toggles = get_test_toggles()
|
||||
CP = CarInterface.get_params(CAR.KIA_EV9, gen_empty_fingerprint(), [], True, False, False, toggles)
|
||||
|
||||
assert CP.startAccel == pytest.approx(0.2)
|
||||
assert CP.vEgoStarting == pytest.approx(0.5)
|
||||
assert CP.longitudinalActuatorDelay == pytest.approx(0.3)
|
||||
assert CarInterface.get_pid_accel_limits(CP, 0.0, 0.0)[1] == pytest.approx(KIA_EV9_ACCEL_MAX)
|
||||
|
||||
ioniq_6_cp = CarInterface.get_params(CAR.HYUNDAI_IONIQ_6, gen_empty_fingerprint(), [], True, False, False, toggles)
|
||||
assert CarInterface.get_pid_accel_limits(ioniq_6_cp, 0.0, 0.0)[1] == pytest.approx(CarControllerParams.ACCEL_MAX)
|
||||
|
||||
def test_ioniq_6_longitudinal_tuning_helper_matches_dynamic_profile(self):
|
||||
state = Ioniq6LongitudinalTuningState()
|
||||
|
||||
@@ -1094,6 +1303,70 @@ class TestHyundaiFingerprint:
|
||||
assert state.desired_accel == pytest.approx(-2.82)
|
||||
assert state.actual_accel < -1.8
|
||||
|
||||
def test_kia_ev6_gt_line_longitudinal_tuning_helper_delays_final_stop_cap(self):
|
||||
state = Ioniq6LongitudinalTuningState(actual_accel=-2.82, accel_last=-2.82,
|
||||
long_control_state_last=LongCtrlState.pid)
|
||||
|
||||
state = update_ioniq_6_longitudinal_tuning(state, accel_cmd=-2.82, v_ego=1.8, a_ego=-2.4,
|
||||
long_control_state=LongCtrlState.stopping, long_active=True,
|
||||
ev6_gt_line=True)
|
||||
assert state.stopping
|
||||
assert state.desired_accel == pytest.approx(-2.82)
|
||||
assert state.actual_accel == pytest.approx(-2.82)
|
||||
|
||||
def test_kia_ev6_gt_line_prefers_direct_stop_tracking_above_final_band(self):
|
||||
assert should_use_ev6_gt_line_stop_direct_tracking(True, True, 1.8, -2.05, -1.29)
|
||||
assert not should_use_ev6_gt_line_stop_direct_tracking(True, True, 1.0, -2.05, -1.29)
|
||||
assert not should_use_ev6_gt_line_stop_direct_tracking(True, False, 1.8, -2.05, -1.29)
|
||||
assert not should_use_ev6_gt_line_stop_direct_tracking(False, True, 1.8, -2.05, -1.29)
|
||||
assert not should_use_ev6_gt_line_stop_direct_tracking(True, True, 1.8, -1.0, -1.29)
|
||||
|
||||
def test_ev9_longitudinal_tuning_matches_stock_stop_hold_release_timing(self):
|
||||
state = update_ev9_longitudinal_tuning(EV9LongitudinalTuningState(), True, True, 1.0)
|
||||
assert not state.stop_request
|
||||
|
||||
state = update_ev9_longitudinal_tuning(state, True, True, 0.4)
|
||||
assert state.stop_request
|
||||
assert not state.cruise_standstill
|
||||
|
||||
for _ in range(178):
|
||||
state = update_ev9_longitudinal_tuning(state, True, True, 0.0)
|
||||
assert state.cruise_standstill
|
||||
|
||||
for _ in range(6):
|
||||
state = update_ev9_longitudinal_tuning(state, True, False, 0.0)
|
||||
assert state.stop_request
|
||||
assert not state.cruise_standstill
|
||||
assert update_ev9_longitudinal_tuning(state, True, False, 0.0) == EV9LongitudinalTuningState()
|
||||
|
||||
def test_ev9_blindspot_warning_matches_stock_envelope(self):
|
||||
state = BlindspotWarningState()
|
||||
outputs = [update_blindspot_warning(state, True, True) for _ in range(40)]
|
||||
|
||||
assert [output.sound_active for output in outputs[:36]] == [True] * 36
|
||||
assert not any(output.sound_active for output in outputs[36:])
|
||||
assert [output.mirror_lamp_active for output in outputs[:20]] == [True] * 16 + [False] * 4
|
||||
assert [output.mirror_lamp_active for output in outputs[20:]] == [True] * 16 + [False] * 4
|
||||
|
||||
assert not update_blindspot_warning(state, False, True).sound_active
|
||||
assert update_blindspot_warning(state, True, True).sound_active is False
|
||||
update_blindspot_warning(state, False, False)
|
||||
assert update_blindspot_warning(state, True, True).sound_active
|
||||
|
||||
def test_ev9_longitudinal_tuning_resets_accel_before_stop_release(self):
|
||||
accel_state = Ioniq6LongitudinalTuningState()
|
||||
for _ in range(4):
|
||||
accel_state = update_ioniq_6_longitudinal_tuning(
|
||||
accel_state, accel_cmd=0.2, v_ego=0.0, a_ego=0.0,
|
||||
long_control_state=LongCtrlState.starting, long_active=True,
|
||||
low_speed_stop_brake_cap=True,
|
||||
)
|
||||
assert accel_state.actual_accel == pytest.approx(0.75)
|
||||
|
||||
accel_state = reset_egmp_longitudinal_tuning(accel_state)
|
||||
assert accel_state.actual_accel == 0.0
|
||||
assert not accel_state.launch_active
|
||||
|
||||
def test_genesis_g90_longitudinal_tuning_softens_final_stop_hold(self):
|
||||
state = GenesisG90LongitudinalTuningState()
|
||||
|
||||
@@ -1395,7 +1668,7 @@ class TestHyundaiFingerprint:
|
||||
cc = SimpleNamespace(enabled=True, latActive=True, actuators=SimpleNamespace(longControlState=LongCtrlState.off),
|
||||
leftBlinker=False, rightBlinker=False, hudControl=SimpleNamespace())
|
||||
cs = SimpleNamespace(stock_lfa_msg=None, stock_lkas_msg=stock_lkas,
|
||||
out=SimpleNamespace(steeringAngleDeg=0.0))
|
||||
out=SimpleNamespace(steeringAngleDeg=0.0, gearShifter=structs.CarState.GearShifter.drive))
|
||||
|
||||
msgs = controller.create_canfd_msgs(0, False, 0.0, 8.5, 0.0, 0.0, False, cc.hudControl, cs, cc,
|
||||
get_test_toggles(), lka_icon=2, lfa_icon=2)
|
||||
@@ -1409,7 +1682,7 @@ class TestHyundaiFingerprint:
|
||||
assert parser.vl["LKAS_ALT"]["ADAS_ACIAnglTqRedcGainVal"] == pytest.approx(0.0)
|
||||
assert parser.vl["LKAS_ALT"]["ADAS_StrAnglReqVal"] == pytest.approx(8.5)
|
||||
|
||||
def test_ev9_inactive_angle_status_keeps_standby_damping_without_stock_lkas(self):
|
||||
def test_ev9_inactive_angle_steering_lets_safety_forward_stock_lkas(self):
|
||||
CP = CarParams.new_message()
|
||||
CP.carFingerprint = CAR.KIA_EV9
|
||||
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING |
|
||||
@@ -1417,31 +1690,98 @@ class TestHyundaiFingerprint:
|
||||
CP.openpilotLongitudinalControl = False
|
||||
|
||||
controller = CarController(DBC[CP.carFingerprint], CP)
|
||||
can_bus = CanBus(CP)
|
||||
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("LKAS_ALT", 0)], can_bus.ACAN)
|
||||
cc = SimpleNamespace(enabled=False, latActive=False, actuators=SimpleNamespace(longControlState=LongCtrlState.off),
|
||||
leftBlinker=False, rightBlinker=False, hudControl=SimpleNamespace())
|
||||
stock_lkas = {
|
||||
"CHECKSUM": 1234,
|
||||
"COUNTER": 42,
|
||||
"LKA_OptUsmSta": 2,
|
||||
"LKA_MODE": 2,
|
||||
"LKA_RcgSta": 3,
|
||||
"LKA_AVAILABLE": 3,
|
||||
"LKA_LHLnWrnSta": 3,
|
||||
"LKA_RHLnWrnSta": 3,
|
||||
"LKA_WARNING": 1,
|
||||
"LKA_HndsoffSnd": 1,
|
||||
"LKA_StrSnd": 1,
|
||||
"LKA_SysIndReq": 4,
|
||||
"LKA_ICON": 0,
|
||||
"FCA_SYSWARN": 1,
|
||||
"StrTqReqVal": 17,
|
||||
"TORQUE_REQUEST": 17,
|
||||
"ActToiSta": 3,
|
||||
"STEER_REQ": 1,
|
||||
"ToiFltSta": 3,
|
||||
"LFA_BUTTON": 1,
|
||||
"LKA_SysWrn": 15,
|
||||
"LKA_ASSIST": 1,
|
||||
"Damping_Gain": 0,
|
||||
"STEER_MODE": 5,
|
||||
"NEW_SIGNAL_2": 0,
|
||||
"LKAS_ANGLE_ACTIVE": 2,
|
||||
"LKA_UsmMod": 3,
|
||||
"HAS_LANE_SAFETY": 1,
|
||||
"ADAS_StrAnglReqVal": 12.3,
|
||||
"ADAS_ACIAnglTqRedcGainVal": 0.42,
|
||||
"DAMP_FACTOR": 0,
|
||||
}
|
||||
lfa_block_msg = {f"BYTE{i}": 0 for i in range(3, 32) if i != 7}
|
||||
lfa_block_msg["COUNTER"] = 0
|
||||
cs = SimpleNamespace(stock_lfa_msg=None, stock_lkas_msg={}, lfa_block_msg=lfa_block_msg,
|
||||
cs = SimpleNamespace(stock_lfa_msg=None, stock_lkas_msg=stock_lkas, lfa_block_msg=lfa_block_msg,
|
||||
out=SimpleNamespace(steeringAngleDeg=-201.0))
|
||||
|
||||
msgs = controller.create_canfd_msgs(0, False, 0.0, -201.0, 0.0, 0.0, False, cc.hudControl, cs, cc,
|
||||
get_test_toggles(), lka_icon=1, lfa_icon=1)
|
||||
lkas_msgs = [msg for msg in msgs if msg[0] == 0x110]
|
||||
assert len(lkas_msgs) == 1
|
||||
assert len(lkas_msgs) == 0
|
||||
|
||||
parser.update([(1, lkas_msgs)])
|
||||
@pytest.mark.parametrize(("standstill", "expected_lkas_msgs"), [(False, 1), (True, 0)])
|
||||
def test_ioniq_5_pe_standstill_lets_safety_forward_stock_lkas(self, standstill, expected_lkas_msgs):
|
||||
CP = CarParams.new_message()
|
||||
CP.carFingerprint = CAR.HYUNDAI_IONIQ_5_PE
|
||||
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING |
|
||||
HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT)
|
||||
CP.openpilotLongitudinalControl = False
|
||||
|
||||
assert parser.can_valid
|
||||
assert parser.vl["LKAS_ALT"]["LKAS_ANGLE_ACTIVE"] == 1
|
||||
assert parser.vl["LKAS_ALT"]["ADAS_ACIAnglTqRedcGainVal"] == pytest.approx(0.0)
|
||||
assert parser.vl["LKAS_ALT"]["ADAS_StrAnglReqVal"] == pytest.approx(-201.0)
|
||||
assert parser.vl["LKAS_ALT"]["DAMP_FACTOR"] == pytest.approx(100.0)
|
||||
assert parser.vl["LKAS_ALT"]["STEER_MODE"] == 2
|
||||
assert parser.vl["LKAS_ALT"]["NEW_SIGNAL_2"] == 3
|
||||
controller = CarController(DBC[CP.carFingerprint], CP)
|
||||
cc = SimpleNamespace(enabled=True, latActive=False, actuators=SimpleNamespace(longControlState=LongCtrlState.off),
|
||||
leftBlinker=False, rightBlinker=False, hudControl=SimpleNamespace())
|
||||
cs = SimpleNamespace(
|
||||
stock_lfa_msg=None,
|
||||
stock_lkas_msg={},
|
||||
out=SimpleNamespace(
|
||||
standstill=standstill,
|
||||
steeringAngleDeg=0.0,
|
||||
gearShifter=structs.CarState.GearShifter.drive,
|
||||
),
|
||||
)
|
||||
|
||||
def test_ev9_inactive_angle_steering_still_suppresses_stock_lfa(self):
|
||||
msgs = controller.create_canfd_msgs(0, False, 0.0, 0.0, 0.0, 0.0, False, cc.hudControl, cs, cc,
|
||||
get_test_toggles(), lka_icon=1, lfa_icon=1)
|
||||
assert len([msg for msg in msgs if msg[0] == 0x110]) == expected_lkas_msgs
|
||||
|
||||
def test_ev9_inactive_angle_steering_does_not_suppress_stock_lfa(self):
|
||||
CP = CarParams.new_message()
|
||||
CP.carFingerprint = CAR.KIA_EV9
|
||||
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING |
|
||||
HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT)
|
||||
CP.openpilotLongitudinalControl = False
|
||||
|
||||
controller = CarController(DBC[CP.carFingerprint], CP)
|
||||
controller.frame = 5
|
||||
cc = SimpleNamespace(enabled=False, latActive=False, actuators=SimpleNamespace(longControlState=LongCtrlState.off),
|
||||
leftBlinker=False, rightBlinker=False, hudControl=SimpleNamespace())
|
||||
lfa_block_msg = {f"BYTE{i}": 0 for i in range(3, 32) if i != 7}
|
||||
lfa_block_msg["COUNTER"] = 0
|
||||
cs = SimpleNamespace(stock_lfa_msg=None, stock_lkas_msg={}, lfa_block_msg=lfa_block_msg,
|
||||
out=SimpleNamespace(steeringAngleDeg=0.0, gearShifter=structs.CarState.GearShifter.drive))
|
||||
|
||||
msgs = controller.create_canfd_msgs(0, False, 0.0, 0.0, 0.0, 0.0, False, cc.hudControl, cs, cc,
|
||||
get_test_toggles(), lka_icon=1, lfa_icon=1)
|
||||
suppress_msgs = [msg for msg in msgs if msg[0] == 0x362]
|
||||
assert not suppress_msgs
|
||||
|
||||
def test_ev9_active_angle_steering_still_suppresses_stock_lfa(self):
|
||||
CP = CarParams.new_message()
|
||||
CP.carFingerprint = CAR.KIA_EV9
|
||||
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING |
|
||||
@@ -1452,15 +1792,15 @@ class TestHyundaiFingerprint:
|
||||
controller.frame = 5
|
||||
can_bus = CanBus(CP)
|
||||
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("CAM_0x362", 0)], can_bus.ECAN)
|
||||
cc = SimpleNamespace(enabled=False, latActive=False, actuators=SimpleNamespace(longControlState=LongCtrlState.off),
|
||||
cc = SimpleNamespace(enabled=False, latActive=True, actuators=SimpleNamespace(longControlState=LongCtrlState.off),
|
||||
leftBlinker=False, rightBlinker=False, hudControl=SimpleNamespace())
|
||||
lfa_block_msg = {f"BYTE{i}": 0 for i in range(3, 32) if i != 7}
|
||||
lfa_block_msg["COUNTER"] = 0
|
||||
cs = SimpleNamespace(stock_lfa_msg=None, stock_lkas_msg={}, lfa_block_msg=lfa_block_msg,
|
||||
out=SimpleNamespace(steeringAngleDeg=0.0))
|
||||
out=SimpleNamespace(steeringAngleDeg=0.0, gearShifter=structs.CarState.GearShifter.drive))
|
||||
|
||||
msgs = controller.create_canfd_msgs(0, False, 0.0, 0.0, 0.0, 0.0, False, cc.hudControl, cs, cc,
|
||||
get_test_toggles(), lka_icon=1, lfa_icon=1)
|
||||
get_test_toggles(), lka_icon=2, lfa_icon=2)
|
||||
suppress_msgs = [msg for msg in msgs if msg[0] == 0x362]
|
||||
assert len(suppress_msgs) == 1
|
||||
|
||||
@@ -1506,7 +1846,7 @@ class TestHyundaiFingerprint:
|
||||
lfa_block_msg = {f"BYTE{i}": 0 for i in range(3, 32) if i != 7}
|
||||
lfa_block_msg["COUNTER"] = 0
|
||||
cs = SimpleNamespace(stock_lfa_msg=None, stock_lkas_msg=stock_lkas, lfa_block_msg=lfa_block_msg,
|
||||
out=SimpleNamespace(steeringAngleDeg=120.0))
|
||||
out=SimpleNamespace(steeringAngleDeg=120.0, gearShifter=structs.CarState.GearShifter.drive))
|
||||
|
||||
msgs = controller.create_canfd_msgs(0, True, 0.44, 120.0, 0.0, 0.0, False, cc.hudControl, cs, cc,
|
||||
get_test_toggles(), lka_icon=2, lfa_icon=2)
|
||||
@@ -1848,7 +2188,7 @@ class TestHyundaiFingerprint:
|
||||
"DAMP_FACTOR": 0,
|
||||
}
|
||||
|
||||
msgs = hyundaicanfd.create_steering_messages(packer, CP, can_bus, True, True, 0.44, -31.5,
|
||||
msgs = hyundaicanfd.create_steering_messages(packer, CP, can_bus, False, True, 0.44, -31.5,
|
||||
lkas_base_values=stock_lkas, lka_icon=2)
|
||||
lkas_msgs = [msg for msg in msgs if msg[0] == 0x110]
|
||||
assert len(lkas_msgs) == 1
|
||||
@@ -1856,6 +2196,19 @@ class TestHyundaiFingerprint:
|
||||
parser.update([(1, lkas_msgs)])
|
||||
|
||||
assert parser.can_valid
|
||||
assert parser.vl["LKAS_ALT"]["LKA_OptUsmSta"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_RcgSta"] == 3
|
||||
assert parser.vl["LKAS_ALT"]["LKA_LHLnWrnSta"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_RHLnWrnSta"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_HndsoffSnd"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_StrSnd"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_SysIndReq"] == 2
|
||||
assert parser.vl["LKAS_ALT"]["StrTqReqVal"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["ActToiSta"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["ToiFltSta"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_SysWrn"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["Damping_Gain"] == 100
|
||||
assert parser.vl["LKAS_ALT"]["LKA_UsmMod"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_MODE"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_AVAILABLE"] == 3
|
||||
assert parser.vl["LKAS_ALT"]["LKA_WARNING"] == 0
|
||||
@@ -1871,7 +2224,7 @@ class TestHyundaiFingerprint:
|
||||
assert parser.vl["LKAS_ALT"]["ADAS_StrAnglReqVal"] == pytest.approx(-31.5)
|
||||
assert parser.vl["LKAS_ALT"]["ADAS_ACIAnglTqRedcGainVal"] == pytest.approx(0.44)
|
||||
|
||||
def test_ev9_angle_lkas_alt_preserves_stock_status_when_inactive(self):
|
||||
def test_ev9_angle_lkas_alt_clears_stock_status_when_inactive(self):
|
||||
CP = CarParams.new_message()
|
||||
CP.carFingerprint = CAR.KIA_EV9
|
||||
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING |
|
||||
@@ -1884,18 +2237,31 @@ class TestHyundaiFingerprint:
|
||||
stock_lkas = {
|
||||
"CHECKSUM": 1234,
|
||||
"COUNTER": 42,
|
||||
"LKA_OptUsmSta": 6,
|
||||
"LKA_MODE": 2,
|
||||
"LKA_RcgSta": 7,
|
||||
"LKA_AVAILABLE": 3,
|
||||
"LKA_LHLnWrnSta": 3,
|
||||
"LKA_RHLnWrnSta": 3,
|
||||
"LKA_WARNING": 1,
|
||||
"LKA_HndsoffSnd": 1,
|
||||
"LKA_StrSnd": 1,
|
||||
"LKA_SysIndReq": 4,
|
||||
"LKA_ICON": 1,
|
||||
"FCA_SYSWARN": 1,
|
||||
"StrTqReqVal": 17,
|
||||
"TORQUE_REQUEST": 17,
|
||||
"ActToiSta": 3,
|
||||
"STEER_REQ": 1,
|
||||
"ToiFltSta": 3,
|
||||
"LFA_BUTTON": 1,
|
||||
"LKA_SysWrn": 15,
|
||||
"LKA_ASSIST": 1,
|
||||
"Damping_Gain": 0,
|
||||
"STEER_MODE": 5,
|
||||
"NEW_SIGNAL_2": 0,
|
||||
"LKAS_ANGLE_ACTIVE": 2,
|
||||
"LKA_UsmMod": 3,
|
||||
"HAS_LANE_SAFETY": 1,
|
||||
"ADAS_StrAnglReqVal": 12.3,
|
||||
"ADAS_ACIAnglTqRedcGainVal": 0.42,
|
||||
@@ -1910,19 +2276,32 @@ class TestHyundaiFingerprint:
|
||||
parser.update([(1, lkas_msgs)])
|
||||
|
||||
assert parser.can_valid
|
||||
assert parser.vl["LKAS_ALT"]["LKA_MODE"] == 2
|
||||
assert parser.vl["LKAS_ALT"]["LKA_AVAILABLE"] == 3
|
||||
assert parser.vl["LKAS_ALT"]["LKA_WARNING"] == 1
|
||||
assert parser.vl["LKAS_ALT"]["LKA_OptUsmSta"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_RcgSta"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_LHLnWrnSta"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_RHLnWrnSta"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_HndsoffSnd"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_StrSnd"] == 2
|
||||
assert parser.vl["LKAS_ALT"]["LKA_SysIndReq"] == 1
|
||||
assert parser.vl["LKAS_ALT"]["StrTqReqVal"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["ActToiSta"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["ToiFltSta"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_SysWrn"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["Damping_Gain"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_UsmMod"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_MODE"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_AVAILABLE"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_WARNING"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_ICON"] == 1
|
||||
assert parser.vl["LKAS_ALT"]["FCA_SYSWARN"] == 1
|
||||
assert parser.vl["LKAS_ALT"]["FCA_SYSWARN"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["TORQUE_REQUEST"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["STEER_REQ"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LFA_BUTTON"] == 1
|
||||
assert parser.vl["LKAS_ALT"]["LKA_ASSIST"] == 1
|
||||
assert parser.vl["LKAS_ALT"]["LFA_BUTTON"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKA_ASSIST"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["DAMP_FACTOR"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["LKAS_ANGLE_ACTIVE"] == 1
|
||||
assert parser.vl["LKAS_ALT"]["HAS_LANE_SAFETY"] == 1
|
||||
assert parser.vl["LKAS_ALT"]["ADAS_StrAnglReqVal"] == pytest.approx(-31.5)
|
||||
assert parser.vl["LKAS_ALT"]["HAS_LANE_SAFETY"] == 0
|
||||
assert parser.vl["LKAS_ALT"]["ADAS_StrAnglReqVal"] == pytest.approx(12.3)
|
||||
assert parser.vl["LKAS_ALT"]["ADAS_ACIAnglTqRedcGainVal"] == pytest.approx(0.0)
|
||||
|
||||
def test_ev9_accelerator_brake_alt_spoof_matches_route_template(self):
|
||||
@@ -2036,6 +2415,151 @@ class TestHyundaiFingerprint:
|
||||
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_LtIndSta"] == 2
|
||||
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["OSMrrLamp_LtIndSta"] == 2
|
||||
|
||||
def test_ev9_blindspot_status_uses_stock_warning_fields(self):
|
||||
CP = CarParams.new_message()
|
||||
CP.carFingerprint = CAR.KIA_EV9
|
||||
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT)
|
||||
|
||||
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
|
||||
can_bus = CanBus(CP)
|
||||
parser = CANParser(DBC[CP.carFingerprint][Bus.pt],
|
||||
[("BLINDSPOTS_REAR_CORNERS", 0), ("BLINDSPOTS_FRONT_CORNER_1", 0)], can_bus.ECAN)
|
||||
|
||||
msgs = hyundaicanfd.create_ccnc_blindspot_status_messages(
|
||||
packer, CP, can_bus, 7, left_blindspot=True, left_escalated=True,
|
||||
drive_gear=True, left_warning_lamp=True, left_sound_active=True,
|
||||
)
|
||||
parser.update([(1, msgs)])
|
||||
|
||||
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_LtIndSta"] == 2
|
||||
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["OSMrrLamp_LtIndSta"] == 2
|
||||
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_LtSndWrngSta"] == 1
|
||||
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_Sta"] == 0
|
||||
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["FL_INDICATOR"] == 0
|
||||
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["BCW_IndSta"] == 1
|
||||
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["BCA_OnOffEquip2Sta"] == 2
|
||||
assert parser.vl["BLINDSPOTS_REAR_CORNERS"]["BCA_Sta"] == 1
|
||||
assert parser.vl["BLINDSPOTS_FRONT_CORNER_1"]["NEW_SIGNAL_7"] == 0
|
||||
|
||||
def test_ev9_ccnc_status_clears_faults_and_tracks_control_state(self):
|
||||
CP = CarParams.new_message()
|
||||
CP.carFingerprint = CAR.KIA_EV9
|
||||
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT)
|
||||
|
||||
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
|
||||
can_bus = CanBus(CP)
|
||||
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("CCNC_0x161", 0), ("CCNC_0x162", 0)], can_bus.ECAN)
|
||||
|
||||
parser.update([(1, hyundaicanfd.create_ccnc_angle_long_status_messages(packer, CP, can_bus, 12, hba_icon=2))])
|
||||
|
||||
assert parser.vl["CCNC_0x161"]["FCA_ICON"] == 1
|
||||
assert parser.vl["CCNC_0x161"]["FCA_ALT_ICON"] == 0
|
||||
assert parser.vl["CCNC_0x161"]["FCA_IMAGE"] == 0
|
||||
assert parser.vl["CCNC_0x161"]["HBA_ICON"] == 2
|
||||
assert all(parser.vl["CCNC_0x161"][sound] == 0 for sound in ("SOUNDS_1", "SOUNDS_2", "SOUNDS_3", "SOUNDS_4"))
|
||||
assert parser.vl["CCNC_0x162"]["VIBRATE"] == 0
|
||||
assert all(parser.vl["CCNC_0x162"][fault] == 0 for fault in (
|
||||
"FAULT_FSS", "FAULT_FCA", "FAULT_LSS", "FAULT_SLA", "FAULT_HDA", "FAULT_DAS", "FAULT_LFA", "FAULT_DAW",
|
||||
"FAULT_HBA", "FAULT_ESS",
|
||||
))
|
||||
|
||||
parser.update([(1, hyundaicanfd.create_ccnc_angle_long_status_messages(
|
||||
packer, CP, can_bus, 13, main_cruise_enabled=True, steering_available=True, steering_active=False,
|
||||
))])
|
||||
assert parser.vl["CCNC_0x161"]["HDA_ICON"] == 1
|
||||
assert parser.vl["CCNC_0x161"]["LFA_ICON"] == 1
|
||||
|
||||
parser.update([(1, hyundaicanfd.create_ccnc_angle_long_status_messages(
|
||||
packer, CP, can_bus, 14, enabled=True, main_cruise_enabled=True,
|
||||
steering_available=True, steering_active=True,
|
||||
))])
|
||||
assert parser.vl["CCNC_0x161"]["HDA_ICON"] == 2
|
||||
assert parser.vl["CCNC_0x161"]["LFA_ICON"] == 2
|
||||
|
||||
parser.update([(1, hyundaicanfd.create_ccnc_angle_long_status_messages(
|
||||
packer, CP, can_bus, 15, enabled=True, main_cruise_enabled=True,
|
||||
steering_available=True, steering_active=False,
|
||||
))])
|
||||
assert parser.vl["CCNC_0x161"]["HDA_ICON"] == 2
|
||||
assert parser.vl["CCNC_0x161"]["LFA_ICON"] == 1
|
||||
|
||||
def test_ev9_ccnc_acc_control_uses_packer_counter(self):
|
||||
CP = CarParams.new_message()
|
||||
CP.carFingerprint = CAR.KIA_EV9
|
||||
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.CCNC | HyundaiFlags.CANFD_ANGLE_STEERING |
|
||||
HyundaiFlags.CANFD_LKA_STEERING | HyundaiFlags.CANFD_LKA_STEERING_ALT)
|
||||
|
||||
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
|
||||
can_bus = CanBus(CP)
|
||||
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("SCC_CONTROL", 0)], can_bus.ECAN)
|
||||
|
||||
messages = [
|
||||
hyundaicanfd.create_ccnc_acc_control(
|
||||
packer, can_bus, True, 0.2, False, False, False, 50.0,
|
||||
1, 27.5, -1.2, True, 10.0,
|
||||
)
|
||||
for _ in range(2)
|
||||
]
|
||||
parser.update([(1, [messages[0]])])
|
||||
parser.update([(2, [messages[1]])])
|
||||
|
||||
assert parser.can_valid
|
||||
assert messages[0][1][2] == 0
|
||||
assert messages[1][1][2] == 1
|
||||
assert parser.vl["SCC_CONTROL"]["ACC_ObjDist"] == pytest.approx(27.5)
|
||||
assert parser.vl["SCC_CONTROL"]["ACC_ObjRelSpd"] == pytest.approx(-1.2)
|
||||
|
||||
@pytest.mark.parametrize(("steering_pressed", "steering_active"), ((False, True), (True, False)))
|
||||
def test_ev9_ccnc_steering_icon_tracks_controller_authority(self, monkeypatch, steering_pressed, steering_active):
|
||||
CP = CarParams.new_message()
|
||||
CP.carFingerprint = CAR.KIA_EV9
|
||||
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.EV | HyundaiFlags.CCNC |
|
||||
HyundaiFlags.CANFD_ANGLE_STEERING | HyundaiFlags.CANFD_LKA_STEERING |
|
||||
HyundaiFlags.CANFD_LKA_STEERING_ALT)
|
||||
CP.openpilotLongitudinalControl = True
|
||||
|
||||
controller = CarController(DBC[CP.carFingerprint], CP)
|
||||
controller.frame = 5
|
||||
captured = {}
|
||||
|
||||
def capture_ccnc_adrv_messages(*args, **kwargs):
|
||||
captured["steering_available"] = args[9]
|
||||
captured["steering_active"] = args[10]
|
||||
return []
|
||||
|
||||
monkeypatch.setattr(hyundaicanfd, "create_ccnc_adrv_messages", capture_ccnc_adrv_messages)
|
||||
lfa_block_msg = {f"BYTE{i}": 0 for i in range(3, 32) if i != 7}
|
||||
lfa_block_msg["COUNTER"] = 0
|
||||
cc = SimpleNamespace(
|
||||
enabled=True,
|
||||
latActive=True,
|
||||
actuators=SimpleNamespace(longControlState=LongCtrlState.pid, accel=0.0),
|
||||
leftBlinker=False,
|
||||
rightBlinker=False,
|
||||
hudControl=SimpleNamespace(),
|
||||
)
|
||||
cs = SimpleNamespace(
|
||||
angle_steering_fault=False,
|
||||
angle_steering_angle=0.0,
|
||||
hba_icon=0,
|
||||
is_metric=True,
|
||||
left_blindspot_from_radar=False,
|
||||
right_blindspot_from_radar=False,
|
||||
lfa_block_msg=lfa_block_msg,
|
||||
out=SimpleNamespace(
|
||||
brakePressed=False,
|
||||
cruiseState=SimpleNamespace(available=True),
|
||||
gasPressed=False,
|
||||
gearShifter=structs.CarState.GearShifter.drive,
|
||||
steeringPressed=steering_pressed,
|
||||
),
|
||||
)
|
||||
|
||||
controller.create_canfd_msgs(0, True, 0.44, 0.0, 0.0, 0.0, False, cc.hudControl, cs, cc,
|
||||
get_test_toggles(), lka_icon=2, lfa_icon=2)
|
||||
|
||||
assert captured == {"steering_available": True, "steering_active": steering_active}
|
||||
|
||||
def test_ioniq_6_blindspot_radar_state_decode(self):
|
||||
assert decode_ioniq_6_blindspot_radar_state(0x02) == (False, False)
|
||||
assert decode_ioniq_6_blindspot_radar_state(0x0A) == (False, True)
|
||||
@@ -2273,7 +2797,7 @@ class TestHyundaiFingerprint:
|
||||
CAR.GENESIS_G70_2020,
|
||||
}
|
||||
excluded_platforms |= CANFD_CAR - EV_CAR - CANFD_FUZZY_WHITELIST # shared platform codes
|
||||
excluded_platforms |= NO_DATES_PLATFORMS # date codes are required to match
|
||||
excluded_platforms |= NO_DATES_PLATFORMS - DATELESS_FUZZY_CARS
|
||||
|
||||
platforms_with_shared_codes = set()
|
||||
for platform, fw_by_addr in FW_VERSIONS.items():
|
||||
|
||||
@@ -77,11 +77,14 @@ class CarControllerParams:
|
||||
self.STEER_DELTA_DOWN = 3
|
||||
|
||||
elif CP.flags & HyundaiFlags.CAN_CANFD_BLENDED:
|
||||
self.STEER_MAX = 404
|
||||
self.STEER_DRIVER_ALLOWANCE = 50
|
||||
self.STEER_THRESHOLD = 150
|
||||
self.STEER_DELTA_UP = 2
|
||||
self.STEER_DELTA_DOWN = 3
|
||||
if CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
|
||||
self.STEER_MAX = 384
|
||||
else:
|
||||
self.STEER_MAX = 404
|
||||
self.STEER_DRIVER_ALLOWANCE = 50
|
||||
self.STEER_THRESHOLD = 150
|
||||
self.STEER_DELTA_UP = 2
|
||||
self.STEER_DELTA_DOWN = 3
|
||||
|
||||
# Default for most HKG
|
||||
else:
|
||||
@@ -103,10 +106,12 @@ class HyundaiSafetyFlags(IntFlag):
|
||||
NON_SCC = 4096
|
||||
CAN_CANFD_BLENDED = 8192
|
||||
CANCEL_BTN_ENABLE = 16384
|
||||
CAN_REFRESH_MSGS = 32768
|
||||
CCNC = 32768
|
||||
|
||||
|
||||
class HyundaiStarPilotSafetyFlags(IntFlag):
|
||||
AOL_MAIN_LKAS_SYNC = 32
|
||||
HAS_LDA_BUTTON = 1024
|
||||
AOL_LKAS_ON_ENGAGE = 2048
|
||||
|
||||
@@ -218,6 +223,11 @@ class HyundaiPlatformConfig(PlatformConfig):
|
||||
self.dbc_dict = {Bus.pt: "hyundai_palisade_2023_generated"}
|
||||
|
||||
|
||||
@dataclass
|
||||
class HyundaiRefreshPlatformConfig(HyundaiPlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "hyundai_can_refresh_generated"})
|
||||
|
||||
|
||||
@dataclass
|
||||
class HyundaiCanFDPlatformConfig(PlatformConfig):
|
||||
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "hyundai_canfd_generated"})
|
||||
@@ -289,12 +299,25 @@ class CAR(Platforms):
|
||||
CarSpecs(mass=2800 * CV.LB_TO_KG, wheelbase=2.72, steerRatio=12.9, tireStiffnessFactor=0.65),
|
||||
flags=HyundaiFlags.CHECKSUM_CRC8,
|
||||
)
|
||||
HYUNDAI_ELANTRA_2024 = HyundaiRefreshPlatformConfig(
|
||||
[HyundaiCarDocs("Hyundai Elantra 2024-25", car_parts=CarParts.common([CarHarness.hyundai_k]))],
|
||||
CarSpecs(mass=2797 * CV.LB_TO_KG, wheelbase=2.72, steerRatio=12.9, tireStiffnessFactor=0.65),
|
||||
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.CAMERA_SCC,
|
||||
)
|
||||
HYUNDAI_ELANTRA_HEV_2021 = HyundaiPlatformConfig(
|
||||
[HyundaiCarDocs("Hyundai Elantra Hybrid 2021-23", video="https://youtu.be/_EdYQtV52-c",
|
||||
car_parts=CarParts.common([CarHarness.hyundai_k]))],
|
||||
CarSpecs(mass=3017 * CV.LB_TO_KG, wheelbase=2.72, steerRatio=12.9, tireStiffnessFactor=0.65),
|
||||
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.HYBRID,
|
||||
)
|
||||
HYUNDAI_ELANTRA_HEV_2024 = HyundaiRefreshPlatformConfig(
|
||||
[
|
||||
HyundaiCarDocs("Hyundai Elantra Hybrid 2024-26", car_parts=CarParts.common([CarHarness.hyundai_k])),
|
||||
HyundaiCarDocs("Hyundai i30 Hybrid 2024", car_parts=CarParts.common([CarHarness.hyundai_k])),
|
||||
],
|
||||
HYUNDAI_ELANTRA_HEV_2021.specs,
|
||||
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.CAMERA_SCC | HyundaiFlags.HYBRID,
|
||||
)
|
||||
HYUNDAI_GENESIS = HyundaiPlatformConfig(
|
||||
[
|
||||
# TODO: check 2015 packages
|
||||
@@ -457,8 +480,12 @@ class CAR(Platforms):
|
||||
[
|
||||
HyundaiCarDocs("Hyundai Palisade (without HDA II) 2023-25", "Highway Driving Assist",
|
||||
car_parts=CarParts.common([CarHarness.hyundai_a])),
|
||||
HyundaiCarDocs("Hyundai Palisade (with HDA II) 2023-24", "Highway Driving Assist II",
|
||||
car_parts=CarParts.common([CarHarness.hyundai_r])),
|
||||
HyundaiCarDocs("Kia Telluride (without HDA II) 2023-25", "Highway Driving Assist",
|
||||
car_parts=CarParts.common([CarHarness.hyundai_l])),
|
||||
HyundaiCarDocs("Kia Telluride (with HDA II) 2023-24", "Highway Driving Assist II",
|
||||
car_parts=CarParts.common([CarHarness.hyundai_p])),
|
||||
],
|
||||
HYUNDAI_PALISADE.specs,
|
||||
flags=HyundaiFlags.CHECKSUM_CRC8 | HyundaiFlags.CAN_CANFD_BLENDED | HyundaiFlags.RADAR_SCC,
|
||||
@@ -764,7 +791,7 @@ class CAR(Platforms):
|
||||
HyundaiCarDocs("Kia EV9 2025-26", car_parts=CarParts.common([CarHarness.hyundai_r]))
|
||||
],
|
||||
CarSpecs(mass=2664, wheelbase=3.1, steerRatio=16),
|
||||
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING,
|
||||
flags=HyundaiFlags.EV | HyundaiFlags.CANFD_ANGLE_STEERING | HyundaiFlags.CCNC,
|
||||
radar_dbc=HYUNDAI_MRR35_RADAR_DBC,
|
||||
)
|
||||
KIA_CARNIVAL_4TH_GEN = HyundaiCanFDPlatformConfig(
|
||||
@@ -775,6 +802,24 @@ class CAR(Platforms):
|
||||
CarSpecs(mass=2087, wheelbase=3.09, steerRatio=14.23),
|
||||
flags=HyundaiFlags.RADAR_SCC,
|
||||
)
|
||||
KIA_CARNIVAL_2025 = HyundaiCanFDPlatformConfig(
|
||||
[
|
||||
HyundaiCarDocs("Kia Carnival 2025", car_parts=CarParts.common([CarHarness.hyundai_k])),
|
||||
HyundaiCarDocs("Kia Carnival (with HDA II) 2025", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])),
|
||||
],
|
||||
KIA_CARNIVAL_4TH_GEN.specs,
|
||||
flags=HyundaiFlags.CCNC,
|
||||
)
|
||||
KIA_CARNIVAL_HEV_4TH_GEN = HyundaiCanFDPlatformConfig(
|
||||
[
|
||||
HyundaiCarDocs("Kia Carnival Hybrid 2025", car_parts=CarParts.common([CarHarness.hyundai_k])),
|
||||
HyundaiCarDocs("Kia Carnival Hybrid 2026", car_parts=CarParts.common([CarHarness.hyundai_a])),
|
||||
HyundaiCarDocs("Kia Carnival Hybrid (with HDA II) 2025-26", "Highway Driving Assist II",
|
||||
car_parts=CarParts.common([CarHarness.hyundai_q])),
|
||||
],
|
||||
CarSpecs(mass=2253, wheelbase=3.09, steerRatio=14.23),
|
||||
flags=HyundaiFlags.CCNC,
|
||||
)
|
||||
|
||||
# Genesis
|
||||
GENESIS_GV60_EV_1ST_GEN = HyundaiCanFDPlatformConfig(
|
||||
@@ -1000,7 +1045,7 @@ def match_fw_to_car_fuzzy(live_fw_versions, vin, offline_fw_versions) -> set[str
|
||||
if not any(found_platform_code in expected_platform_codes for found_platform_code in found_platform_codes):
|
||||
break
|
||||
|
||||
if ecu[0] in DATE_FW_ECUS:
|
||||
if ecu[0] in DATE_FW_ECUS and candidate not in DATELESS_FUZZY_CARS:
|
||||
# If ECU can have a FW date, require it to exist
|
||||
# (this excludes candidates in the database without dates)
|
||||
if not len(expected_dates) or not len(found_dates):
|
||||
@@ -1038,8 +1083,8 @@ PART_NUMBER_FW_PATTERN = re.compile(b'(?<=[0-9][.,][0-9]{2} )([0-9]{5}[-/]?[A-Z]
|
||||
|
||||
# We've seen both ICE and hybrid for these platforms, and they have hybrid descriptors (e.g. MQ4 vs MQ4H)
|
||||
CANFD_FUZZY_WHITELIST = {CAR.KIA_SORENTO_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN, CAR.KIA_K8_HEV_1ST_GEN,
|
||||
# TODO: the hybrid variant is not out yet
|
||||
CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_SORENTO_HEV_4TH_GEN_LFA2}
|
||||
CAR.KIA_CARNIVAL_4TH_GEN, CAR.KIA_CARNIVAL_2025, CAR.KIA_CARNIVAL_HEV_4TH_GEN,
|
||||
CAR.KIA_SORENTO_HEV_4TH_GEN_LFA2}
|
||||
|
||||
# List of ECUs expected to have platform codes, camera and radar should exist on all cars
|
||||
# TODO: use abs, it has the platform code and part number on many platforms
|
||||
@@ -1048,6 +1093,8 @@ PLATFORM_CODE_ECUS = [Ecu.fwdRadar, Ecu.fwdCamera, Ecu.eps]
|
||||
# TODO: there are date codes in the ABS firmware versions in hex
|
||||
DATE_FW_ECUS = [Ecu.fwdCamera]
|
||||
|
||||
DATELESS_FUZZY_CARS = {CAR.HYUNDAI_KONA_NON_SCC}
|
||||
|
||||
# Note: an ECU on CAN FD cars may sometimes send 0x30080aaaaaaaaaaa (flow control continue) while we
|
||||
# are attempting to query ECUs. This currently does not seem to affect fingerprinting from the camera
|
||||
FW_QUERY_CONFIG = FwQueryConfig(
|
||||
@@ -1135,10 +1182,15 @@ CANFD_CAR = CAR.with_flags(HyundaiFlags.CANFD)
|
||||
CANFD_RADAR_SCC_CAR = CAR.with_flags(HyundaiFlags.RADAR_SCC) # TODO: merge with UNSUPPORTED_LONGITUDINAL_CAR
|
||||
|
||||
# CAN-FD cars with ADAS ECUs that work with the communication-control path.
|
||||
CANFD_SECURITYACCESS_CAR = {CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_6, CAR.HYUNDAI_KONA_EV_2ND_GEN}
|
||||
CANFD_SECURITYACCESS_CAR = {
|
||||
CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_6, CAR.HYUNDAI_KONA_EV_2ND_GEN, CAR.KIA_EV9,
|
||||
}
|
||||
CANFD_UNSUPPORTED_LONGITUDINAL_CAR = CAR.with_flags(HyundaiFlags.CANFD_NO_RADAR_DISABLE) - CANFD_SECURITYACCESS_CAR # TODO: merge with UNSUPPORTED_LONGITUDINAL_CAR
|
||||
CANFD_ANGLE_LONGITUDINAL_CAR = set()
|
||||
CANFD_RADAR_LIVE_LONGITUDINAL_CAR = {CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_6, CAR.KIA_EV6, CAR.GENESIS_GV60_EV_1ST_GEN}
|
||||
CANFD_ANGLE_LONGITUDINAL_CAR = {CAR.KIA_EV9}
|
||||
CANFD_CORNER_RADAR_BSM_CAR = {CAR.HYUNDAI_IONIQ_6, CAR.KIA_EV9}
|
||||
CANFD_RADAR_LIVE_LONGITUDINAL_CAR = {
|
||||
CAR.HYUNDAI_IONIQ_5, CAR.HYUNDAI_IONIQ_6, CAR.KIA_EV6, CAR.KIA_EV9, CAR.GENESIS_GV60_EV_1ST_GEN,
|
||||
}
|
||||
RADAR_LIVE_LONGITUDINAL_CAR = CANFD_RADAR_LIVE_LONGITUDINAL_CAR | {
|
||||
CAR.HYUNDAI_IONIQ,
|
||||
CAR.HYUNDAI_KONA_EV_2022,
|
||||
|
||||
@@ -213,9 +213,6 @@ class CarInterfaceBase(ABC):
|
||||
|
||||
if platform not in MOCK:
|
||||
if platform in CHRYSLER:
|
||||
if candidate == CHRYSLER.RAM_HD_5TH_GEN:
|
||||
if 570 not in fingerprint[0]:
|
||||
fp_ret.flags |= ChryslerStarPilotFlags.RAM_HD_ALT_BUTTONS.value
|
||||
if 0x4FF in fingerprint[0]:
|
||||
fp_ret.flags |= ChryslerStarPilotFlags.NO_MIN_STEERING_SPEED.value
|
||||
CP.minSteerSpeed = 0.
|
||||
@@ -251,9 +248,16 @@ class CarInterfaceBase(ABC):
|
||||
if hyundai_has_lda_button:
|
||||
fp_ret.safetyConfigs[-1].safetyParam |= HyundaiStarPilotSafetyFlags.HAS_LDA_BUTTON.value
|
||||
|
||||
if getattr(starpilot_toggles, "always_on_lateral_lkas", False):
|
||||
fp_ret.safetyConfigs[-1].safetyParam |= HyundaiStarPilotSafetyFlags.AOL_LKAS_ON_ENGAGE.value
|
||||
|
||||
# LKASButtonControl == 9 means BUTTON_FUNCTIONS["AOL_TOGGLE"] in starpilot_variables.
|
||||
if params.get_bool("AlwaysOnLateral") and params.get_int("LKASButtonControl") == 9:
|
||||
fp_ret.safetyConfigs[-1].safetyParam |= HyundaiStarPilotSafetyFlags.AOL_LKAS_ON_ENGAGE.value
|
||||
|
||||
if candidate == HYUNDAI.HYUNDAI_SONATA_HYBRID and getattr(starpilot_toggles, "always_on_lateral_lkas", False) and \
|
||||
getattr(starpilot_toggles, "main_cruise_aol_toggle", False):
|
||||
fp_ret.safetyConfigs[-1].safetyParam |= HyundaiStarPilotSafetyFlags.AOL_MAIN_LKAS_SYNC.value
|
||||
elif platform in TOYOTA:
|
||||
fp_ret.canUsePedal = not CP.autoResumeSng
|
||||
fp_ret.canUseSDSU = candidate not in UNSUPPORTED_DSU_CAR and candidate not in TSS2_CAR
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import numpy as np
|
||||
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car import Bus, DT_CTRL, structs
|
||||
from opendbc.car.common.filter_simple import FirstOrderFilter
|
||||
from opendbc.car.lateral import apply_std_steer_angle_limits
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
from opendbc.car.nissan import nissancan
|
||||
@@ -13,6 +16,7 @@ class CarController(CarControllerBase):
|
||||
super().__init__(dbc_names, CP)
|
||||
self.car_fingerprint = CP.carFingerprint
|
||||
|
||||
self.angle_filter = FirstOrderFilter(0.0, 0.1, DT_CTRL)
|
||||
self.apply_angle_last = 0
|
||||
|
||||
self.packer = CANPacker(dbc_names[Bus.pt])
|
||||
@@ -27,8 +31,15 @@ class CarController(CarControllerBase):
|
||||
### STEER ###
|
||||
steer_hud_alert = 1 if hud_control.visualAlert in (VisualAlert.steerRequired, VisualAlert.ldw) else 0
|
||||
|
||||
# Nissan EPS is sensitive to jitter in angle requests at low speed and high steering angles.
|
||||
if CC.latActive:
|
||||
self.angle_filter.update_alpha(float(np.interp(CS.out.vEgo, [5, 10, 20], [0.2, 0.1, 0.0])))
|
||||
self.angle_filter.update(actuators.steeringAngleDeg)
|
||||
else:
|
||||
self.angle_filter.x = actuators.steeringAngleDeg
|
||||
|
||||
# windup slower
|
||||
self.apply_angle_last = apply_std_steer_angle_limits(actuators.steeringAngleDeg, self.apply_angle_last, CS.out.vEgoRaw,
|
||||
self.apply_angle_last = apply_std_steer_angle_limits(self.angle_filter.x, self.apply_angle_last, CS.out.vEgoRaw,
|
||||
CS.out.steeringAngleDeg, CC.latActive, CarControllerParams.ANGLE_LIMITS)
|
||||
|
||||
lkas_max_torque = 0
|
||||
|
||||
@@ -66,7 +66,7 @@ class CAR(Platforms):
|
||||
)
|
||||
# Leaf with ADAS ECU found behind instrument cluster instead of glovebox
|
||||
# Currently the only known difference between them is the inverted seatbelt signal.
|
||||
NISSAN_LEAF_IC = NISSAN_LEAF.override(car_docs=[NissanCarDocs(NISSAN_LEAF.car_docs[0].name + " - Instrument Cluster", video=NISSAN_LEAF.car_docs[0].video)])
|
||||
NISSAN_LEAF_IC = NISSAN_LEAF.override(car_docs=[NissanCarDocs("Nissan Leaf Instrument Cluster 2018-23", video=NISSAN_LEAF.car_docs[0].video)])
|
||||
NISSAN_ROGUE = NissanPlatformConfig(
|
||||
[NissanCarDocs("Nissan Rogue 2018-20")],
|
||||
NissanCarSpecs(mass=1610, wheelbase=2.705)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import numpy as np
|
||||
from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus, DT_CTRL, make_tester_present_msg
|
||||
from opendbc.car.lateral import apply_driver_steer_torque_limits, common_fault_avoidance
|
||||
from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_steer_angle_limits_vm, common_fault_avoidance
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
from opendbc.car.subaru import subarucan
|
||||
from opendbc.car.subaru.values import DBC, GLOBAL_ES_ADDR, CanBus, CarControllerParams, SubaruFlags
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
|
||||
# FIXME: These limits aren't exact. The real limit is more than likely over a larger time period and
|
||||
# involves the total steering angle change rather than rate, but these limits work well for now
|
||||
@@ -15,10 +16,17 @@ _SNG_ACC_MIN_DIST = 3
|
||||
_SNG_ACC_MAX_DIST = 4.5
|
||||
|
||||
|
||||
def get_safety_CP():
|
||||
from opendbc.car.subaru.interface import CarInterface
|
||||
return CarInterface.get_non_essential_params("SUBARU_ASCENT")
|
||||
|
||||
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_names, CP):
|
||||
super().__init__(dbc_names, CP)
|
||||
self.apply_torque_last = 0
|
||||
self.apply_steer_last = 0
|
||||
self.driver_override = False
|
||||
|
||||
self.cruise_button_prev = 0
|
||||
self.steer_rate_counter = 0
|
||||
@@ -26,10 +34,62 @@ class CarController(CarControllerBase):
|
||||
self.p = CarControllerParams(CP)
|
||||
self.packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
|
||||
|
||||
if CP.flags & SubaruFlags.LKAS_ANGLE:
|
||||
self.VM = VehicleModel(get_safety_CP())
|
||||
|
||||
self.prev_close_distance = 0
|
||||
self.epb_resume_frames_remaining = -1
|
||||
self.last_standstill_frame = 0
|
||||
|
||||
def lateral_angle(self, CC, CS):
|
||||
abs_torque = abs(CS.out.steeringTorque)
|
||||
if abs_torque > self.p.STEER_OVERRIDE_TORQUE_HIGH:
|
||||
self.driver_override = True
|
||||
elif abs_torque < self.p.STEER_OVERRIDE_TORQUE_LOW:
|
||||
self.driver_override = False
|
||||
|
||||
lat_active = CC.latActive and not self.driver_override
|
||||
apply_steer = apply_steer_angle_limits_vm(
|
||||
CC.actuators.steeringAngleDeg,
|
||||
self.apply_steer_last,
|
||||
CS.out.vEgoRaw,
|
||||
CS.out.steeringAngleDeg,
|
||||
lat_active,
|
||||
self.p,
|
||||
self.VM,
|
||||
)
|
||||
|
||||
if not lat_active:
|
||||
apply_steer = CS.out.steeringAngleDeg
|
||||
|
||||
self.apply_steer_last = apply_steer
|
||||
return subarucan.create_steering_control_angle(self.packer, apply_steer, lat_active)
|
||||
|
||||
def lateral_torque(self, CC, CS):
|
||||
apply_torque = int(round(CC.actuators.torque * self.p.STEER_MAX))
|
||||
apply_torque = apply_driver_steer_torque_limits(apply_torque, self.apply_torque_last, CS.out.steeringTorque, self.p)
|
||||
|
||||
if not CC.latActive:
|
||||
apply_torque = 0
|
||||
|
||||
self.apply_torque_last = apply_torque
|
||||
|
||||
if self.CP.flags & SubaruFlags.PREGLOBAL:
|
||||
return subarucan.create_preglobal_steering_control(
|
||||
self.packer, self.frame // self.p.STEER_STEP, apply_torque, CC.latActive,
|
||||
)
|
||||
|
||||
apply_steer_req = CC.latActive
|
||||
if self.CP.flags & SubaruFlags.STEER_RATE_LIMITED:
|
||||
self.steer_rate_counter, apply_steer_req = common_fault_avoidance(
|
||||
abs(CS.out.steeringRateDeg) > MAX_STEER_RATE,
|
||||
apply_steer_req,
|
||||
self.steer_rate_counter,
|
||||
MAX_STEER_RATE_FRAMES,
|
||||
)
|
||||
|
||||
return subarucan.create_steering_control(self.packer, apply_torque, apply_steer_req)
|
||||
|
||||
def update(self, CC, CS, now_nanos, starpilot_toggles):
|
||||
actuators = CC.actuators
|
||||
hud_control = CC.hudControl
|
||||
@@ -39,30 +99,10 @@ class CarController(CarControllerBase):
|
||||
|
||||
# *** steering ***
|
||||
if (self.frame % self.p.STEER_STEP) == 0:
|
||||
apply_torque = int(round(actuators.torque * self.p.STEER_MAX))
|
||||
|
||||
# limits due to driver torque
|
||||
|
||||
new_torque = int(round(apply_torque))
|
||||
apply_torque = apply_driver_steer_torque_limits(new_torque, self.apply_torque_last, CS.out.steeringTorque, self.p)
|
||||
|
||||
if not CC.latActive:
|
||||
apply_torque = 0
|
||||
|
||||
if self.CP.flags & SubaruFlags.PREGLOBAL:
|
||||
can_sends.append(subarucan.create_preglobal_steering_control(self.packer, self.frame // self.p.STEER_STEP, apply_torque, CC.latActive))
|
||||
if self.CP.flags & SubaruFlags.LKAS_ANGLE:
|
||||
can_sends.append(self.lateral_angle(CC, CS))
|
||||
else:
|
||||
apply_steer_req = CC.latActive
|
||||
|
||||
if self.CP.flags & SubaruFlags.STEER_RATE_LIMITED:
|
||||
# Steering rate fault prevention
|
||||
self.steer_rate_counter, apply_steer_req = \
|
||||
common_fault_avoidance(abs(CS.out.steeringRateDeg) > MAX_STEER_RATE, apply_steer_req,
|
||||
self.steer_rate_counter, MAX_STEER_RATE_FRAMES)
|
||||
|
||||
can_sends.append(subarucan.create_steering_control(self.packer, apply_torque, apply_steer_req))
|
||||
|
||||
self.apply_torque_last = apply_torque
|
||||
can_sends.append(self.lateral_torque(CC, CS))
|
||||
|
||||
# *** stop and go ***
|
||||
subaru_sng_manual_parking_brake = getattr(starpilot_toggles, "subaru_sng_manual_parking_brake", False)
|
||||
@@ -162,8 +202,11 @@ class CarController(CarControllerBase):
|
||||
can_sends.append(subarucan.create_es_static_2(self.packer))
|
||||
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.torque = self.apply_torque_last / self.p.STEER_MAX
|
||||
new_actuators.torqueOutputCan = self.apply_torque_last
|
||||
if self.CP.flags & SubaruFlags.LKAS_ANGLE:
|
||||
new_actuators.steeringAngleDeg = self.apply_steer_last
|
||||
else:
|
||||
new_actuators.torque = self.apply_torque_last / self.p.STEER_MAX
|
||||
new_actuators.torqueOutputCan = self.apply_torque_last
|
||||
|
||||
self.frame += 1
|
||||
return new_actuators, can_sends
|
||||
|
||||
@@ -61,11 +61,16 @@ class CarState(CarStateBase):
|
||||
can_gear = int(cp_transmission.vl["Transmission"]["Gear"])
|
||||
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(can_gear, None))
|
||||
|
||||
ret.steeringAngleDeg = cp.vl["Steering_Torque"]["Steering_Angle"]
|
||||
if self.CP.flags & SubaruFlags.LKAS_ANGLE:
|
||||
ret.steeringAngleDeg = cp.vl["Steering_2"]["Steering_Angle"]
|
||||
steering_updated = len(cp.vl_all["Steering_2"]["Steering_Angle"]) > 0
|
||||
else:
|
||||
ret.steeringAngleDeg = cp.vl["Steering_Torque"]["Steering_Angle"]
|
||||
steering_updated = len(cp.vl_all["Steering_Torque"]["Steering_Angle"]) > 0
|
||||
|
||||
if not (self.CP.flags & SubaruFlags.PREGLOBAL):
|
||||
# ideally we get this from the car, but unclear if it exists. diagnostic software doesn't even have it
|
||||
ret.steeringRateDeg = self.angle_rate_calulator.update(ret.steeringAngleDeg, cp.vl["Steering_Torque"]["COUNTER"])
|
||||
ret.steeringRateDeg = self.angle_rate_calulator.update(ret.steeringAngleDeg, steering_updated)
|
||||
|
||||
ret.steeringTorque = cp.vl["Steering_Torque"]["Steer_Torque_Sensor"]
|
||||
ret.steeringTorqueEps = cp.vl["Steering_Torque"]["Steer_Torque_Output"]
|
||||
@@ -74,7 +79,11 @@ class CarState(CarStateBase):
|
||||
ret.steeringPressed = abs(ret.steeringTorque) > steer_threshold
|
||||
|
||||
cp_cruise = cp_alt if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp
|
||||
if self.CP.flags & SubaruFlags.HYBRID:
|
||||
cp_es_brake = cp_alt if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp_cam
|
||||
if self.CP.flags & SubaruFlags.LKAS_ANGLE:
|
||||
ret.cruiseState.enabled = cp_es_brake.vl["ES_Status"]['Cruise_Activated'] != 0
|
||||
ret.cruiseState.available = cp_cam.vl["ES_DashStatus"]['Cruise_On'] != 0
|
||||
elif self.CP.flags & SubaruFlags.HYBRID:
|
||||
ret.cruiseState.enabled = cp_cam.vl["ES_DashStatus"]['Cruise_Activated'] != 0
|
||||
ret.cruiseState.available = cp_cam.vl["ES_DashStatus"]['Cruise_On'] != 0
|
||||
else:
|
||||
@@ -104,9 +113,7 @@ class CarState(CarStateBase):
|
||||
(cp_cam.vl["ES_LKAS_State"]["LKAS_Alert"] == 2)
|
||||
|
||||
self.es_lkas_state_msg = copy.copy(cp_cam.vl["ES_LKAS_State"])
|
||||
cp_es_brake = cp_alt if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp_cam
|
||||
self.es_brake_msg = copy.copy(cp_es_brake.vl["ES_Brake"])
|
||||
cp_es_status = cp_alt if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp_cam
|
||||
|
||||
# TODO: Hybrid cars don't have ES_Distance, need a replacement
|
||||
if not (self.CP.flags & SubaruFlags.HYBRID):
|
||||
@@ -114,7 +121,7 @@ class CarState(CarStateBase):
|
||||
ret.stockAeb = (cp_es_distance.vl["ES_Brake"]["AEB_Status"] == 8) and \
|
||||
(cp_es_distance.vl["ES_Brake"]["Brake_Pressure"] != 0)
|
||||
|
||||
self.es_status_msg = copy.copy(cp_es_status.vl["ES_Status"])
|
||||
self.es_status_msg = copy.copy(cp_es_brake.vl["ES_Status"])
|
||||
self.cruise_control_msg = copy.copy(cp_cruise.vl["CruiseControl"])
|
||||
|
||||
if not (self.CP.flags & SubaruFlags.HYBRID):
|
||||
|
||||
@@ -244,6 +244,20 @@ FW_VERSIONS = {
|
||||
b'\xf4!`0\x07',
|
||||
],
|
||||
},
|
||||
CAR.SUBARU_CROSSTREK_2025: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa2 $\x15\x05',
|
||||
b'\xa2 $\x17\x06',
|
||||
],
|
||||
(Ecu.fwdCamera, 0x787, None): [
|
||||
b'\x1d!\x08\x00F\x14!\x08\x00=',
|
||||
b'\x1b!\x08\x00D\x11!\x08\x01;',
|
||||
],
|
||||
(Ecu.engine, 0x7a2, None): [
|
||||
b'\x04"cP\x07',
|
||||
b'\xe8!cp\x07',
|
||||
],
|
||||
},
|
||||
CAR.SUBARU_FORESTER: {
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\xa3 \x18\x14\x00',
|
||||
|
||||
@@ -18,7 +18,7 @@ class CarInterface(CarInterfaceBase):
|
||||
# - replacement for ES_Distance so we can cancel the cruise control
|
||||
# - to find the Cruise_Activated bit from the car
|
||||
# - proper panda safety setup (use the correct cruise_activated bit, throttle from Throttle_Hybrid, etc)
|
||||
ret.dashcamOnly = bool(ret.flags & (SubaruFlags.PREGLOBAL | SubaruFlags.LKAS_ANGLE | SubaruFlags.HYBRID))
|
||||
ret.dashcamOnly = bool(ret.flags & (SubaruFlags.PREGLOBAL | SubaruFlags.HYBRID))
|
||||
ret.autoResumeSng = not (ret.flags & SubaruFlags.GLOBAL_GEN2 or ret.flags & SubaruFlags.HYBRID)
|
||||
|
||||
# Detect infotainment message sent from the camera
|
||||
@@ -33,16 +33,19 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.subaru)]
|
||||
if ret.flags & SubaruFlags.GLOBAL_GEN2:
|
||||
ret.safetyConfigs[0].safetyParam |= SubaruSafetyFlags.GEN2.value
|
||||
if ret.flags & SubaruFlags.LKAS_ANGLE:
|
||||
ret.safetyConfigs[0].safetyParam |= SubaruSafetyFlags.LKAS_ANGLE.value
|
||||
|
||||
ret.steerLimitTimer = 0.4
|
||||
ret.steerActuatorDelay = 0.1
|
||||
|
||||
if ret.flags & SubaruFlags.LKAS_ANGLE:
|
||||
ret.steerControlType = structs.CarParams.SteerControlType.angle
|
||||
else:
|
||||
if not (ret.flags & SubaruFlags.LKAS_ANGLE):
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
if candidate in (CAR.SUBARU_ASCENT, CAR.SUBARU_ASCENT_2023):
|
||||
if ret.flags & SubaruFlags.LKAS_ANGLE:
|
||||
ret.steerControlType = structs.CarParams.SteerControlType.angle
|
||||
|
||||
elif candidate in (CAR.SUBARU_ASCENT, CAR.SUBARU_ASCENT_2023):
|
||||
ret.steerActuatorDelay = 0.3 # end-to-end angle controller
|
||||
ret.lateralTuning.init('pid')
|
||||
ret.lateralTuning.pid.kf = 0.00003
|
||||
|
||||
@@ -13,9 +13,9 @@ def create_steering_control(packer, apply_torque, steer_req):
|
||||
return packer.make_can_msg("ES_LKAS", 0, values)
|
||||
|
||||
|
||||
def create_steering_control_angle(packer, apply_torque, steer_req):
|
||||
def create_steering_control_angle(packer, apply_angle, steer_req):
|
||||
values = {
|
||||
"LKAS_Output": apply_torque,
|
||||
"LKAS_Output": apply_angle,
|
||||
"LKAS_Request": steer_req,
|
||||
"SET_3": 3
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from opendbc.car.subaru.carcontroller import CarController
|
||||
from opendbc.car.subaru.fingerprints import FW_VERSIONS
|
||||
from opendbc.car.subaru.values import SubaruFlags
|
||||
from opendbc.car.subaru.interface import CarInterface
|
||||
from opendbc.car.subaru.values import CAR, SubaruFlags, SubaruSafetyFlags
|
||||
from opendbc.car.structs import CarParams
|
||||
|
||||
|
||||
def make_sng_controller(flags=0, prev_close_distance=4.0):
|
||||
@@ -61,3 +65,43 @@ class TestSubaruFingerprint:
|
||||
fw_size = len(fws[0])
|
||||
for fw in fws:
|
||||
assert len(fw) == fw_size, f"{platform} {ecu}: {len(fw)} {fw_size}"
|
||||
|
||||
|
||||
ANGLE_PLATFORMS = (
|
||||
CAR.SUBARU_FORESTER_2022,
|
||||
CAR.SUBARU_OUTBACK_2023,
|
||||
CAR.SUBARU_ASCENT_2023,
|
||||
CAR.SUBARU_CROSSTREK_2025,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("platform", ANGLE_PLATFORMS)
|
||||
def test_angle_platform_params(platform):
|
||||
CP = CarInterface.get_non_essential_params(platform)
|
||||
|
||||
assert CP.flags & SubaruFlags.LKAS_ANGLE
|
||||
assert CP.steerControlType == CarParams.SteerControlType.angle
|
||||
assert CP.safetyConfigs[0].safetyParam & SubaruSafetyFlags.LKAS_ANGLE
|
||||
assert not CP.dashcamOnly
|
||||
assert not CP.alphaLongitudinalAvailable
|
||||
|
||||
|
||||
def test_torque_platform_does_not_enable_angle_safety():
|
||||
CP = CarInterface.get_non_essential_params(CAR.SUBARU_IMPREZA_2020)
|
||||
|
||||
assert not (CP.flags & SubaruFlags.LKAS_ANGLE)
|
||||
assert CP.steerControlType == CarParams.SteerControlType.torque
|
||||
assert not (CP.safetyConfigs[0].safetyParam & SubaruSafetyFlags.LKAS_ANGLE)
|
||||
|
||||
|
||||
def test_angle_controller_tracks_driver_override():
|
||||
CP = CarInterface.get_non_essential_params(CAR.SUBARU_CROSSTREK_2025)
|
||||
controller = CarController({}, CP)
|
||||
CC = SimpleNamespace(latActive=True, actuators=SimpleNamespace(steeringAngleDeg=15.0))
|
||||
CS = SimpleNamespace(out=SimpleNamespace(vEgoRaw=15.0, steeringAngleDeg=2.0, steeringTorque=250.0))
|
||||
|
||||
msg = controller.lateral_angle(CC, CS)
|
||||
|
||||
assert controller.driver_override
|
||||
assert controller.apply_steer_last == CS.out.steeringAngleDeg
|
||||
assert msg[0] == 0x124
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum, IntFlag
|
||||
|
||||
from opendbc.car import Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds
|
||||
from opendbc.car import ACCELERATION_DUE_TO_GRAVITY, Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column
|
||||
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16
|
||||
from opendbc.car.lateral import AngleSteeringLimits, ISO_LATERAL_ACCEL
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
|
||||
class CarControllerParams:
|
||||
ANGLE_LIMITS: AngleSteeringLimits = AngleSteeringLimits(
|
||||
650,
|
||||
([], []),
|
||||
([], []),
|
||||
MAX_LATERAL_ACCEL=ISO_LATERAL_ACCEL + (ACCELERATION_DUE_TO_GRAVITY * 0.06),
|
||||
MAX_LATERAL_JERK=3.0 + (ACCELERATION_DUE_TO_GRAVITY * 0.06),
|
||||
MAX_ANGLE_RATE=1,
|
||||
)
|
||||
|
||||
def __init__(self, CP):
|
||||
self.STEER_STEP = 2 # how often we update the steer cmd
|
||||
self.STEER_DELTA_UP = 50 # torque increase per refresh, 0.8s to max
|
||||
@@ -18,6 +28,9 @@ class CarControllerParams:
|
||||
self.STEER_DRIVER_MULTIPLIER = 50 # weight driver torque heavily
|
||||
self.STEER_DRIVER_FACTOR = 1 # from dbc
|
||||
|
||||
self.STEER_OVERRIDE_TORQUE_HIGH = 200
|
||||
self.STEER_OVERRIDE_TORQUE_LOW = 150
|
||||
|
||||
if CP.flags & SubaruFlags.GLOBAL_GEN2:
|
||||
# TODO: lower rate limits, this reaches min/max in 0.5s which negatively affects tuning
|
||||
self.STEER_MAX = 1500
|
||||
@@ -60,6 +73,7 @@ class SubaruSafetyFlags(IntFlag):
|
||||
LONG = 2
|
||||
PREGLOBAL_REVERSED_DRIVER_TORQUE = 4
|
||||
STOP_AND_GO = 8
|
||||
LKAS_ANGLE = 16
|
||||
|
||||
|
||||
class SubaruFlags(IntFlag):
|
||||
@@ -214,6 +228,11 @@ class CAR(Platforms):
|
||||
SUBARU_ASCENT.specs,
|
||||
flags=SubaruFlags.LKAS_ANGLE,
|
||||
)
|
||||
SUBARU_CROSSTREK_2025 = SubaruGen2PlatformConfig(
|
||||
[SubaruCarDocs("Subaru Crosstrek 2025", "All", car_parts=CarParts.common([CarHarness.subaru_d]))],
|
||||
CarSpecs(mass=1529, wheelbase=2.67, steerRatio=17),
|
||||
flags=SubaruFlags.LKAS_ANGLE,
|
||||
)
|
||||
|
||||
|
||||
SUBARU_VERSION_REQUEST = bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + \
|
||||
|
||||
@@ -3,10 +3,11 @@ from opendbc.can import CANPacker
|
||||
from opendbc.car import Bus
|
||||
from opendbc.car.lateral import apply_steer_angle_limits_vm
|
||||
from opendbc.car.interfaces import CarControllerBase
|
||||
from opendbc.car.tesla.coop_steering import CooperativeSteeringController
|
||||
from opendbc.car.tesla.teslacan import TeslaCAN
|
||||
from opendbc.car.tesla.preap.carcontroller import PreAPLongController, init_preap_can
|
||||
from opendbc.car.tesla.preap.stock_cc_spoofer import StockCCSpoofer
|
||||
from opendbc.car.tesla.values import CANBUS, CAR, CarControllerParams
|
||||
from opendbc.car.tesla.values import CANBUS, CAR, CarControllerParams, TeslaSafetyFlags
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
|
||||
def get_safety_CP():
|
||||
@@ -18,6 +19,11 @@ class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_names, CP):
|
||||
super().__init__(dbc_names, CP)
|
||||
self.apply_angle_last = 0
|
||||
self.apply_angle_command_last = 0
|
||||
self.coop_steer = CooperativeSteeringController()
|
||||
self.coop_enabled = CP.carFingerprint == CAR.TESLA_MODEL_3 and any(
|
||||
config.safetyParam & TeslaSafetyFlags.COOP_STEERING.value for config in CP.safetyConfigs
|
||||
)
|
||||
self.packer = CANPacker(dbc_names[Bus.party])
|
||||
self.tesla_can = TeslaCAN(self.packer)
|
||||
self.preap_long = None
|
||||
@@ -40,17 +46,18 @@ class CarController(CarControllerBase):
|
||||
actuators = CC.actuators
|
||||
can_sends = []
|
||||
|
||||
# Tesla EPS enforces disabling steering on heavy lateral override force.
|
||||
# When enabling in a tight curve, we wait until user reduces steering force to start steering.
|
||||
# Canceling is done on rising edge and is handled generically with CC.cruiseControl.cancel
|
||||
lat_active = CC.latActive and CS.hands_on_level < 3
|
||||
# Preserve the stock controller path unless cooperative steering is explicitly enabled.
|
||||
lat_active = CC.latActive and (not CS.out.steeringDisengage if self.coop_enabled else CS.hands_on_level < 3)
|
||||
|
||||
if self.frame % 2 == 0:
|
||||
# Angular rate limit based on speed
|
||||
self.apply_angle_last = apply_steer_angle_limits_vm(actuators.steeringAngleDeg, self.apply_angle_last, CS.out.vEgoRaw, CS.out.steeringAngleDeg,
|
||||
lat_active, CarControllerParams, self.VM)
|
||||
|
||||
can_sends.append(self.tesla_can.create_steering_control(self.apply_angle_last, lat_active))
|
||||
self.apply_angle_command_last, lat_active = self.coop_steer.update(
|
||||
self.apply_angle_last, lat_active, self.coop_enabled, CS, self.VM,
|
||||
)
|
||||
can_sends.append(self.tesla_can.create_steering_control(self.apply_angle_command_last, lat_active))
|
||||
|
||||
if self.frame % 10 == 0:
|
||||
can_sends.append(self.tesla_can.create_steering_allowed())
|
||||
@@ -71,7 +78,7 @@ class CarController(CarControllerBase):
|
||||
|
||||
# TODO: HUD control
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.steeringAngleDeg = self.apply_angle_last
|
||||
new_actuators.steeringAngleDeg = self.apply_angle_command_last
|
||||
|
||||
self.frame += 1
|
||||
return new_actuators, can_sends
|
||||
|
||||
@@ -4,7 +4,7 @@ from opendbc.can import CANDefine, CANParser
|
||||
from opendbc.car import Bus, structs
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.interfaces import CarStateBase
|
||||
from opendbc.car.tesla.values import DBC, CANBUS, GEAR_MAP, STEER_THRESHOLD, CAR
|
||||
from opendbc.car.tesla.values import DBC, CANBUS, GEAR_MAP, STEER_DISENGAGE_THRESHOLD, STEER_THRESHOLD, TeslaSafetyFlags, CAR
|
||||
from opendbc.car.tesla.preap.carstate import get_preap_can_parsers, update_preap
|
||||
from opendbc.car.tesla.preap.engagement import PreAPEngagement
|
||||
from opendbc.car.tesla.preap.nap_conf import nap_conf
|
||||
@@ -30,6 +30,9 @@ class CarState(CarStateBase):
|
||||
self.prev_cruise_buttons = 0
|
||||
self.msg_stw_actn_req = None
|
||||
self.speed_units = "MPH"
|
||||
self.cooperative_steering = any(
|
||||
config.safetyParam & TeslaSafetyFlags.COOP_STEERING.value for config in CP.safetyConfigs
|
||||
)
|
||||
|
||||
if CP.carFingerprint == CAR.TESLA_MODEL_S_PREAP:
|
||||
self.engagement = PreAPEngagement(nap_conf.double_pull_enabled, nap_conf.double_pull_window_ms)
|
||||
@@ -95,8 +98,11 @@ class CarState(CarStateBase):
|
||||
|
||||
# FSD disengages using union of handsOnLevel (slow overrides) and high angle rate faults (fast overrides, high speed)
|
||||
eac_error_code = self.can_define.dv["EPAS3S_sysStatus"]["EPAS3S_eacErrorCode"].get(int(epas_status["EPAS3S_eacErrorCode"]), None)
|
||||
ret.steeringDisengage = self.hands_on_level >= 3 or (eac_status == "EAC_INHIBITED" and
|
||||
eac_error_code == "EAC_ERROR_HIGH_ANGLE_RATE_SAFETY")
|
||||
ret.steeringDisengage = (
|
||||
self.hands_on_level >= 3 or
|
||||
(eac_status == "EAC_INHIBITED" and eac_error_code == "EAC_ERROR_HIGH_ANGLE_RATE_SAFETY") or
|
||||
(self.cooperative_steering and abs(ret.steeringTorque) > STEER_DISENGAGE_THRESHOLD)
|
||||
)
|
||||
|
||||
# Cruise state
|
||||
cruise_state = self.can_define.dv["DI_state"]["DI_cruiseState"].get(int(cp_party.vl["DI_state"]["DI_cruiseState"]), None)
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
import math
|
||||
|
||||
import numpy as np
|
||||
|
||||
from opendbc.car import DT_CTRL, rate_limit
|
||||
from opendbc.car.lateral import apply_steer_angle_limits_vm
|
||||
from opendbc.car.tesla.values import CarControllerParams
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
|
||||
|
||||
DT_LAT_CTRL = DT_CTRL * CarControllerParams.STEER_STEP
|
||||
STEER_RESUME_RATE_LIMIT_RAMP_RATE = 300.0 # deg/s^2
|
||||
|
||||
STEER_OVERRIDE_MIN_TORQUE = 0.5 # Nm
|
||||
STEER_OVERRIDE_MAX_TORQUE = 2.5 # Nm
|
||||
STEER_OVERRIDE_TORQUE_RANGE = STEER_OVERRIDE_MAX_TORQUE - STEER_OVERRIDE_MIN_TORQUE
|
||||
STEER_OVERRIDE_MAX_LAT_ACCEL = 2.0 # m/s^2
|
||||
STEER_OVERRIDE_DELTA_GAIN_LIMIT = 125.0 # deg/s/Nm
|
||||
|
||||
|
||||
def apply_bounds(signal: float, limit: float) -> float:
|
||||
return float(np.clip(signal, -limit, limit))
|
||||
|
||||
|
||||
def apply_deadzone(signal: float, deadzone: float) -> float:
|
||||
return signal - apply_bounds(signal, deadzone)
|
||||
|
||||
|
||||
def get_steer_from_lat_accel(lat_accel: float, v_ego: float, VM: VehicleModel) -> float:
|
||||
curvature = lat_accel / max(1.0, v_ego) ** 2
|
||||
return math.degrees(VM.get_steer_from_curvature(curvature, v_ego, 0.0))
|
||||
|
||||
|
||||
def get_override_torque_to_angle(v_ego: float, VM: VehicleModel) -> float:
|
||||
max_angle = CarControllerParams.ANGLE_LIMITS.STEER_ANGLE_MAX
|
||||
steer_from_lat_accel = apply_bounds(get_steer_from_lat_accel(STEER_OVERRIDE_MAX_LAT_ACCEL, v_ego, VM), max_angle)
|
||||
return steer_from_lat_accel / STEER_OVERRIDE_TORQUE_RANGE
|
||||
|
||||
|
||||
def calc_override_angle_delta_limit(torque: float) -> float:
|
||||
max_gain = CarControllerParams.ANGLE_LIMITS.MAX_ANGLE_RATE / DT_LAT_CTRL / STEER_OVERRIDE_TORQUE_RANGE
|
||||
return torque * min(STEER_OVERRIDE_DELTA_GAIN_LIMIT, max_gain) * DT_LAT_CTRL
|
||||
|
||||
|
||||
class SteerRateLimiter:
|
||||
def __init__(self):
|
||||
self.last = 0.0
|
||||
|
||||
def reset(self, angle: float) -> None:
|
||||
self.last = angle
|
||||
|
||||
def update(self, angle: float, angle_delta_limit: float) -> float:
|
||||
limited = rate_limit(angle, self.last, -angle_delta_limit, angle_delta_limit)
|
||||
self.last = limited
|
||||
return limited
|
||||
|
||||
|
||||
class CooperativeSteeringController:
|
||||
def __init__(self):
|
||||
self.apply_angle_last = 0.0
|
||||
self.coop_apply_angle_last = 0.0
|
||||
self.angle_override = 0.0
|
||||
self.resume_rate_limiter_delta = SteerRateLimiter()
|
||||
self.resume_rate_limiter = SteerRateLimiter()
|
||||
|
||||
def reset_override_state(self, apply_angle: float) -> None:
|
||||
self.apply_angle_last = apply_angle
|
||||
self.angle_override = 0.0
|
||||
self.coop_apply_angle_last = apply_angle
|
||||
|
||||
def reset_resume_state(self, apply_angle: float) -> None:
|
||||
self.resume_rate_limiter_delta.reset(0.0)
|
||||
self.resume_rate_limiter.reset(apply_angle)
|
||||
|
||||
def update_override_angle(self, apply_angle_delta: float, driver_torque: float, v_ego: float, VM: VehicleModel) -> float:
|
||||
driver_torque = apply_deadzone(driver_torque, STEER_OVERRIDE_MIN_TORQUE)
|
||||
torque_to_angle = get_override_torque_to_angle(v_ego, VM)
|
||||
target_angle = driver_torque * torque_to_angle
|
||||
|
||||
holding_torque = self.angle_override / torque_to_angle if abs(v_ego) > 0.1 else 0.0
|
||||
torque_delta = driver_torque - holding_torque
|
||||
angle_delta_limit = calc_override_angle_delta_limit(abs(torque_delta))
|
||||
angle_override_delta = float(np.clip(target_angle - self.angle_override, -angle_delta_limit, angle_delta_limit))
|
||||
|
||||
# Avoid counting model-requested motion and driver-requested motion twice.
|
||||
if angle_override_delta * apply_angle_delta > 0.0:
|
||||
angle_override_delta -= apply_bounds(apply_angle_delta, abs(angle_override_delta))
|
||||
|
||||
self.angle_override += angle_override_delta
|
||||
return self.angle_override
|
||||
|
||||
def unwind_override_angle(self, saturation_error: float) -> None:
|
||||
if self.angle_override * saturation_error > 0.0:
|
||||
self.angle_override -= apply_bounds(saturation_error, abs(self.angle_override))
|
||||
|
||||
def apply_resume_rate_limit(self, lat_active: bool, apply_angle: float) -> float:
|
||||
if not lat_active:
|
||||
self.reset_resume_state(apply_angle)
|
||||
return apply_angle
|
||||
|
||||
angle_rate_delta = self.resume_rate_limiter_delta.update(
|
||||
CarControllerParams.ANGLE_LIMITS.MAX_ANGLE_RATE,
|
||||
STEER_RESUME_RATE_LIMIT_RAMP_RATE * DT_LAT_CTRL ** 2,
|
||||
)
|
||||
return self.resume_rate_limiter.update(apply_angle, angle_rate_delta)
|
||||
|
||||
def update(self, apply_angle: float, lat_active: bool, enabled: bool, CS, VM: VehicleModel) -> tuple[float, bool]:
|
||||
if not enabled:
|
||||
self.reset_resume_state(apply_angle)
|
||||
self.reset_override_state(apply_angle)
|
||||
return apply_angle, lat_active
|
||||
|
||||
apply_angle = self.apply_resume_rate_limit(lat_active, apply_angle)
|
||||
if not lat_active:
|
||||
self.reset_override_state(apply_angle)
|
||||
return apply_angle, False
|
||||
|
||||
apply_angle_delta = apply_angle - self.apply_angle_last
|
||||
self.apply_angle_last = apply_angle
|
||||
apply_angle += self.update_override_angle(apply_angle_delta, CS.out.steeringTorque, CS.out.vEgo, VM)
|
||||
|
||||
limited_angle = apply_steer_angle_limits_vm(
|
||||
apply_angle,
|
||||
self.coop_apply_angle_last,
|
||||
CS.out.vEgoRaw,
|
||||
CS.out.steeringAngleDeg,
|
||||
True,
|
||||
CarControllerParams,
|
||||
VM,
|
||||
)
|
||||
self.coop_apply_angle_last = limited_angle
|
||||
self.unwind_override_angle(apply_angle - limited_angle)
|
||||
return limited_angle, True
|
||||
@@ -9,6 +9,7 @@ FW_VERSIONS = {
|
||||
(Ecu.eps, 0x730, None): [
|
||||
b'TeM3_E014p10_0.0.0 (16),E014.17.00',
|
||||
b'TeM3_E014p10_0.0.0 (16),EL014.17.00',
|
||||
b'TeM3_E014p10_0.0.0 (24),E014.20.2',
|
||||
b'TeM3_ES014p11_0.0.0 (25),ES014.19.0',
|
||||
b'TeMYG4_DCS_Update_0.0.0 (13),E4014.28.1',
|
||||
b'TeMYG4_DCS_Update_0.0.0 (9),E4014.26.0',
|
||||
|
||||
@@ -18,6 +18,13 @@ class CarInterface(CarInterfaceBase):
|
||||
return get_preap_accel_limits(current_speed)
|
||||
return CarInterfaceBase.get_pid_accel_limits(CP, current_speed, cruise_speed)
|
||||
|
||||
@classmethod
|
||||
def get_params(cls, candidate, fingerprint, car_fw, alpha_long, is_release, docs, starpilot_toggles):
|
||||
ret = super().get_params(candidate, fingerprint, car_fw, alpha_long, is_release, docs, starpilot_toggles)
|
||||
if candidate == CAR.TESLA_MODEL_3 and getattr(starpilot_toggles, "tesla_cooperative_steering", False):
|
||||
ret.safetyConfigs[0].safetyParam |= TeslaSafetyFlags.COOP_STEERING.value
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
|
||||
ret.brand = "tesla"
|
||||
|
||||
@@ -95,7 +95,7 @@ def update_preap(cs, can_parsers):
|
||||
if dtr_dist != 255:
|
||||
stalk_follow = min((dtr_dist // 33) + 1, 7)
|
||||
if stalk_follow != cs.prev_stalk_follow:
|
||||
_nap_params.put(NAPParamKeys.FOLLOW_DISTANCE, str(stalk_follow))
|
||||
_nap_params.put_int(NAPParamKeys.FOLLOW_DISTANCE, stalk_follow)
|
||||
cs.prev_stalk_follow = stalk_follow
|
||||
|
||||
curr_time_ms = _current_time_millis()
|
||||
|
||||
@@ -16,12 +16,7 @@ class TeslaCAN:
|
||||
return self.packer.make_can_msg("DAS_steeringControl", CANBUS.party, values)
|
||||
|
||||
def create_longitudinal_command(self, acc_state, accel, counter, v_ego, active):
|
||||
from opendbc.car.interfaces import V_CRUISE_MAX
|
||||
|
||||
set_speed = max(v_ego * CV.MS_TO_KPH, 0)
|
||||
if active:
|
||||
# TODO: this causes jerking after gas override when above set speed
|
||||
set_speed = 0 if accel < 0 else V_CRUISE_MAX
|
||||
set_speed = min(max(v_ego + accel, 0) * CV.MS_TO_KPH, 400)
|
||||
|
||||
values = {
|
||||
"DAS_setSpeed": set_speed,
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from opendbc.car import gen_empty_fingerprint
|
||||
from opendbc.car.tesla.carcontroller import CarController, get_safety_CP
|
||||
from opendbc.car.tesla.coop_steering import CooperativeSteeringController
|
||||
from opendbc.car.tesla.interface import CarInterface
|
||||
from opendbc.car.tesla.values import CAR, DBC, CarControllerParams, TeslaSafetyFlags
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
|
||||
|
||||
def make_car_state(torque=0.0, speed=15.0, angle=0.0):
|
||||
return SimpleNamespace(out=SimpleNamespace(
|
||||
steeringTorque=torque,
|
||||
steeringAngleDeg=angle,
|
||||
vEgo=speed,
|
||||
vEgoRaw=speed,
|
||||
))
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def vehicle_model():
|
||||
return VehicleModel(get_safety_CP())
|
||||
|
||||
|
||||
def test_disabled_preserves_angle_command(vehicle_model):
|
||||
controller = CooperativeSteeringController()
|
||||
angle, lat_active = controller.update(12.5, True, False, make_car_state(torque=2.0), vehicle_model)
|
||||
|
||||
assert angle == 12.5
|
||||
assert lat_active
|
||||
|
||||
|
||||
def test_light_driver_torque_adjusts_angle(vehicle_model):
|
||||
controller = CooperativeSteeringController()
|
||||
angle = 0.0
|
||||
|
||||
for _ in range(10):
|
||||
angle, lat_active = controller.update(0.0, True, True, make_car_state(torque=1.5), vehicle_model)
|
||||
|
||||
assert lat_active
|
||||
assert angle > 0.0
|
||||
assert angle <= CarControllerParams.ANGLE_LIMITS.STEER_ANGLE_MAX
|
||||
|
||||
|
||||
def test_inactive_lateral_resets_override(vehicle_model):
|
||||
controller = CooperativeSteeringController()
|
||||
for _ in range(10):
|
||||
controller.update(0.0, True, True, make_car_state(torque=1.5), vehicle_model)
|
||||
|
||||
angle, lat_active = controller.update(8.0, False, True, make_car_state(torque=1.5, angle=8.0), vehicle_model)
|
||||
assert angle == 8.0
|
||||
assert not lat_active
|
||||
|
||||
angle, lat_active = controller.update(8.0, True, True, make_car_state(angle=8.0), vehicle_model)
|
||||
assert angle == 8.0
|
||||
assert lat_active
|
||||
|
||||
|
||||
@pytest.mark.parametrize(("candidate", "enabled", "expected"), (
|
||||
(CAR.TESLA_MODEL_3, False, False),
|
||||
(CAR.TESLA_MODEL_3, True, True),
|
||||
(CAR.TESLA_MODEL_Y, True, False),
|
||||
(CAR.TESLA_MODEL_S_PREAP, True, False),
|
||||
))
|
||||
def test_safety_flag_is_model_3_only(candidate, enabled, expected):
|
||||
toggles = SimpleNamespace(tesla_cooperative_steering=enabled, trailer_load_kg=0.0)
|
||||
params = CarInterface.get_params(candidate, gen_empty_fingerprint(), [], False, False, False, toggles)
|
||||
|
||||
has_flag = any(config.safetyParam & TeslaSafetyFlags.COOP_STEERING.value for config in params.safetyConfigs)
|
||||
assert has_flag is expected
|
||||
|
||||
if candidate != CAR.TESLA_MODEL_S_PREAP:
|
||||
assert CarController(DBC[candidate], params).coop_enabled is expected
|
||||
@@ -0,0 +1,25 @@
|
||||
import pytest
|
||||
|
||||
from opendbc.car.common.conversions import Conversions as CV
|
||||
from opendbc.car.tesla.teslacan import TeslaCAN
|
||||
|
||||
|
||||
class RecordingPacker:
|
||||
def make_can_msg(self, name, bus, values):
|
||||
return name, bus, values
|
||||
|
||||
|
||||
@pytest.mark.parametrize("active", [False, True])
|
||||
@pytest.mark.parametrize(
|
||||
("v_ego", "accel", "expected_set_speed"),
|
||||
[
|
||||
(20.0, 1.0, 21.0 * CV.MS_TO_KPH),
|
||||
(20.0, -2.0, 18.0 * CV.MS_TO_KPH),
|
||||
(1.0, -2.0, 0.0),
|
||||
(120.0, 2.0, 400.0),
|
||||
],
|
||||
)
|
||||
def test_longitudinal_set_speed_tracks_accel_continuously(active, v_ego, accel, expected_set_speed):
|
||||
_, _, values = TeslaCAN(RecordingPacker()).create_longitudinal_command(4, accel, 0, v_ego, active)
|
||||
|
||||
assert values["DAS_setSpeed"] == pytest.approx(expected_set_speed)
|
||||
@@ -3,7 +3,7 @@ from enum import Enum, IntFlag
|
||||
from opendbc.car import ACCELERATION_DUE_TO_GRAVITY, Bus, CarSpecs, DbcDict, PlatformConfig, Platforms
|
||||
from opendbc.car.lateral import AngleSteeringLimits, ISO_LATERAL_ACCEL
|
||||
from opendbc.car.structs import CarParams, CarState
|
||||
from opendbc.car.docs_definitions import CarDocs, CarFootnote, CarHarness, CarParts, Column
|
||||
from opendbc.car.docs_definitions import CarDocs, CarFootnote, CarHarness, CarParts, Column, SupportType
|
||||
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
|
||||
|
||||
Ecu = CarParams.Ecu
|
||||
@@ -61,7 +61,7 @@ class CAR(Platforms):
|
||||
CarSpecs(mass=2495., wheelbase=2.960, steerRatio=12.0),
|
||||
)
|
||||
TESLA_MODEL_S_PREAP = TeslaPlatformConfig(
|
||||
[CarDocs("Tesla Model S (Pre-AP) 2012-14", "All")],
|
||||
[CarDocs("Tesla Model S (Pre-AP) 2012-14", "All", support_type=SupportType.COMMUNITY, support_link="#community")],
|
||||
CarSpecs(mass=2100., wheelbase=2.960, steerRatio=15.0),
|
||||
{
|
||||
Bus.party: 'tesla_can',
|
||||
@@ -130,6 +130,7 @@ class CarControllerParams:
|
||||
|
||||
class TeslaSafetyFlags(IntFlag):
|
||||
LONG_CONTROL = 1
|
||||
COOP_STEERING = 256
|
||||
|
||||
|
||||
class TeslaFlags(IntFlag):
|
||||
@@ -158,3 +159,4 @@ class CruiseButtons:
|
||||
DBC = CAR.create_dbc_map()
|
||||
|
||||
STEER_THRESHOLD = 1
|
||||
STEER_DISENGAGE_THRESHOLD = 5.0
|
||||
|
||||
@@ -26,7 +26,35 @@ non_tested_cars = [
|
||||
GM.CADILLAC_XT5,
|
||||
GM.HOLDEN_ASTRA,
|
||||
GM.CHEVROLET_MALIBU,
|
||||
GM.BUICK_BABYENCLAVE,
|
||||
GM.BUICK_LACROSSE_ASCM,
|
||||
GM.BUICK_LACROSSE_ASCM_19US,
|
||||
GM.CADILLAC_XT6,
|
||||
GM.CHEVROLET_BLAZER,
|
||||
GM.CHEVROLET_MALIBU_ASCM,
|
||||
GM.CHEVROLET_MALIBU_SDGM,
|
||||
GM.CHEVROLET_SUBURBAN,
|
||||
GM.CHEVROLET_TRAX,
|
||||
GM.CHEVROLET_VOLT_ASCM,
|
||||
GM.CHEVROLET_VOLT_CAMERA,
|
||||
GM.GMC_ACADIA_ASCM,
|
||||
FORD.FORD_TRANSIT_MK5,
|
||||
HONDA.ACURA_ADX,
|
||||
HONDA.ACURA_INTEGRA,
|
||||
HONDA.ACURA_MDX_3G,
|
||||
HONDA.ACURA_MDX_3G_MMR,
|
||||
HONDA.ACURA_MDX_4G,
|
||||
HONDA.ACURA_RDX_3G_MMR,
|
||||
HONDA.ACURA_TLX_1G,
|
||||
HONDA.ACURA_TLX_2G_MMR,
|
||||
HONDA.HONDA_ACCORD_9G,
|
||||
HONDA.HONDA_CLARITY,
|
||||
HONDA.HONDA_CRV_SA,
|
||||
HONDA.HONDA_E_ADVANCE,
|
||||
HONDA.HONDA_FIT_4G,
|
||||
HONDA.HONDA_ODYSSEY_TWN,
|
||||
HYUNDAI.GENESIS_G90,
|
||||
HYUNDAI.GENESIS_G70_2021_NON_SCC,
|
||||
HYUNDAI.GENESIS_GV70_ELECTRIFIED_2ND_GEN,
|
||||
HYUNDAI.GENESIS_GV80_2025,
|
||||
HYUNDAI.HYUNDAI_AZERA_HEV_7TH_GEN,
|
||||
@@ -36,11 +64,29 @@ non_tested_cars = [
|
||||
HYUNDAI.HYUNDAI_TUCSON_PHEV_2025,
|
||||
HYUNDAI.KIA_EV6_2025,
|
||||
HYUNDAI.KIA_EV9,
|
||||
HYUNDAI.KIA_CARNIVAL_2025,
|
||||
HYUNDAI.KIA_CARNIVAL_HEV_4TH_GEN,
|
||||
HYUNDAI.KIA_SPORTAGE_2026,
|
||||
HYUNDAI.KIA_SORENTO_2024,
|
||||
HYUNDAI.KIA_SORENTO_HEV_4TH_GEN_LFA2,
|
||||
HYUNDAI.HYUNDAI_BAYON_1ST_GEN_NON_SCC,
|
||||
HYUNDAI.HYUNDAI_ELANTRA_2022_NON_SCC,
|
||||
HYUNDAI.HYUNDAI_ELANTRA_HEV_2022_NON_SCC,
|
||||
HYUNDAI.HYUNDAI_ELANTRA_HEV_2024,
|
||||
HYUNDAI.HYUNDAI_KONA_EV_NON_SCC,
|
||||
HYUNDAI.HYUNDAI_KONA_NON_SCC,
|
||||
HYUNDAI.HYUNDAI_PALISADE_2023,
|
||||
HYUNDAI.KIA_CEED_PHEV_2022_NON_SCC,
|
||||
HYUNDAI.KIA_FORTE_2019_NON_SCC,
|
||||
HYUNDAI.KIA_FORTE_2021_NON_SCC,
|
||||
HYUNDAI.KIA_SELTOS_2023_NON_SCC,
|
||||
HYUNDAI.KIA_SPORTAGE_HEV_2026,
|
||||
HYUNDAI.KIA_XCEED_PHEV,
|
||||
TESLA.TESLA_MODEL_S_PREAP,
|
||||
TOYOTA.TOYOTA_MATRIX_RETROFIT,
|
||||
VOLKSWAGEN.VOLKSWAGEN_CRAFTER_MK2, # need a route from an ACC-equipped Crafter
|
||||
SUBARU.SUBARU_FORESTER_HYBRID,
|
||||
SUBARU.SUBARU_CROSSTREK_2025,
|
||||
VOLKSWAGEN.PORSCHE_MACAN_MK1,
|
||||
# Honda/Acura test routes below expired, replace when CI bucket sync is fixed
|
||||
HONDA.ACURA_TLX_2G,
|
||||
@@ -165,7 +211,6 @@ routes = [
|
||||
CarTestRoute("de59124955b921d8/2023-06-24--00-12-50", HYUNDAI.KIA_CARNIVAL_4TH_GEN),
|
||||
CarTestRoute("409c9409979a8abc/2023-07-11--09-06-44", HYUNDAI.KIA_CARNIVAL_4TH_GEN), # Chinese model
|
||||
CarTestRoute("e0e98335f3ebc58f/2021-03-07--16-38-29", HYUNDAI.KIA_CEED),
|
||||
CarTestRoute("22703002ddbe2a08/0000000e--083b76c42c", HYUNDAI.KIA_XCEED_PHEV, segment=0),
|
||||
CarTestRoute("7653b2bce7bcfdaa/2020-03-04--15-34-32", HYUNDAI.KIA_OPTIMA_G4),
|
||||
CarTestRoute("018654717bc93d7d/2022-09-19--23-11-10", HYUNDAI.KIA_OPTIMA_G4_FL, segment=0),
|
||||
CarTestRoute("f9716670b2481438/2023-08-23--14-49-50", HYUNDAI.KIA_OPTIMA_H),
|
||||
@@ -228,11 +273,13 @@ routes = [
|
||||
CarTestRoute("192283cdbb7a58c2/2022-10-15--01-43-18", HYUNDAI.KIA_SPORTAGE_5TH_GEN),
|
||||
CarTestRoute("09559f1fcaed4704/2023-11-16--02-24-57", HYUNDAI.KIA_SPORTAGE_5TH_GEN, segment=0), # openpilot longitudinal
|
||||
CarTestRoute("b3537035ffe6a7d6/2022-10-17--15-23-49", HYUNDAI.KIA_SPORTAGE_5TH_GEN), # hybrid
|
||||
CarTestRoute("e53ff841a41f6f0f/00000000--2f6b9e72d5", HYUNDAI.KIA_SPORTAGE_HEV_2026),
|
||||
CarTestRoute("c5ac319aa9583f83/2021-06-01--18-18-31", HYUNDAI.HYUNDAI_ELANTRA),
|
||||
CarTestRoute("734ef96182ddf940/2022-10-02--16-41-44", HYUNDAI.HYUNDAI_ELANTRA_GT_I30),
|
||||
CarTestRoute("82e9cdd3f43bf83e/2021-05-15--02-42-51", HYUNDAI.HYUNDAI_ELANTRA_2021),
|
||||
CarTestRoute("c2fd040a5e34f3ad/00000013--9211a52a3d", HYUNDAI.HYUNDAI_ELANTRA_2024),
|
||||
CarTestRoute("715ac05b594e9c59/2021-06-20--16-21-07", HYUNDAI.HYUNDAI_ELANTRA_HEV_2021),
|
||||
CarTestRoute("07a48901db7b2503/000001a1--ad07872c4f", HYUNDAI.HYUNDAI_ELANTRA_HEV_2024),
|
||||
CarTestRoute("07a48901db7b2503/0000000f--697d5906e8", HYUNDAI.HYUNDAI_ELANTRA_HEV_2024), # Hyundai i30 Hybrid 2024
|
||||
CarTestRoute("7120aa90bbc3add7/2021-08-02--07-12-31", HYUNDAI.HYUNDAI_SONATA_HYBRID),
|
||||
CarTestRoute("bc40c72b728178f2/00000006--ee76ae8c42", HYUNDAI.HYUNDAI_SONATA_HEV_2024),
|
||||
CarTestRoute("715ac05b594e9c59/2021-10-27--23-24-56", HYUNDAI.GENESIS_G70_2020),
|
||||
|
||||
@@ -5,6 +5,7 @@ from opendbc.car.can_definitions import CanData
|
||||
from opendbc.car.car_helpers import FRAME_FINGERPRINT, _apply_starpilot_access_policy, _get_gm_stored_candidate_fallback, can_fingerprint
|
||||
from opendbc.car.fingerprints import _FINGERPRINTS as FINGERPRINTS
|
||||
from opendbc.car.gm.values import CAR as GM
|
||||
from opendbc.car.toyota.values import CAR as TOYOTA
|
||||
|
||||
|
||||
class TestCanFingerprint:
|
||||
@@ -13,7 +14,7 @@ class TestCanFingerprint:
|
||||
can = [CanData(address=address, dat=b'\x00' * length, src=src)
|
||||
for address, length in fingerprint.items() for src in (0, 1)]
|
||||
fingerprint_iter = iter([can])
|
||||
return can_fingerprint(lambda **kwargs: [next(fingerprint_iter, [])]) # noqa: B023
|
||||
return can_fingerprint(lambda **kwargs: [next(fingerprint_iter, [])])
|
||||
|
||||
@pytest.mark.parametrize("car_model, fingerprints", FINGERPRINTS.items())
|
||||
def test_can_fingerprint(self, car_model, fingerprints):
|
||||
@@ -26,7 +27,13 @@ class TestCanFingerprint:
|
||||
fingerprint_iter = iter([can])
|
||||
car_fingerprint, finger = can_fingerprint(lambda **kwargs: [next(fingerprint_iter, [])]) # noqa: B023
|
||||
|
||||
assert car_fingerprint == car_model
|
||||
if car_model == TOYOTA.TOYOTA_MATRIX_RETROFIT:
|
||||
assert fingerprint == {}
|
||||
assert car_fingerprint is None
|
||||
elif car_fingerprint is None and str(car_model).startswith(("BUICK_", "CADILLAC_", "CHEVROLET_", "GMC_", "HOLDEN_")):
|
||||
assert _get_gm_stored_candidate_fallback(finger, str(car_model), None) is not None
|
||||
else:
|
||||
assert car_fingerprint == car_model
|
||||
assert finger[0] == fingerprint
|
||||
assert finger[1] == fingerprint
|
||||
assert finger[2] == {}
|
||||
@@ -39,7 +46,7 @@ class TestCanFingerprint:
|
||||
|
||||
def test_timing(self, subtests):
|
||||
# just pick any CAN fingerprinting car
|
||||
car_model = "CHEVROLET_BOLT_ACC_2022_2023"
|
||||
car_model = "COMMA_BODY"
|
||||
fingerprint = FINGERPRINTS[car_model][0]
|
||||
|
||||
cases = []
|
||||
|
||||
@@ -14,7 +14,7 @@ from opendbc.car.car_helpers import _apply_disable_openpilot_long, interfaces
|
||||
from opendbc.car.chrysler.carcontroller import CarController as ChryslerCarController
|
||||
from opendbc.car.chrysler.carstate import CarState as ChryslerCarState
|
||||
from opendbc.car.chrysler.interface import CarInterface as ChryslerCarInterface
|
||||
from opendbc.car.chrysler.values import CAR as CHRYSLER_CAR, DBC as CHRYSLER_DBC, ChryslerStarPilotFlags
|
||||
from opendbc.car.chrysler.values import CAR as CHRYSLER_CAR, DBC as CHRYSLER_DBC, ChryslerSafetyFlags, ChryslerStarPilotFlags
|
||||
from opendbc.car.fingerprints import FW_VERSIONS
|
||||
from opendbc.car.fw_versions import FW_QUERY_CONFIGS
|
||||
from opendbc.car.hyundai.interface import CarInterface as HyundaiCarInterface
|
||||
@@ -73,12 +73,12 @@ def get_test_starpilot_toggles() -> SimpleNamespace:
|
||||
cluster_offset=1.0,
|
||||
disable_openpilot_long=False,
|
||||
force_fingerprint=False,
|
||||
frogsgomoo_tweak=False,
|
||||
lock_doors=False,
|
||||
reverse_cruise_increase=False,
|
||||
sng_hack=False,
|
||||
subaru_sng=False,
|
||||
subaru_sng_manual_parking_brake=False,
|
||||
tesla_cooperative_steering=False,
|
||||
unlock_doors=False,
|
||||
vEgoStopping=0.5,
|
||||
volt_sng=False,
|
||||
@@ -212,6 +212,33 @@ class TestCarInterfaces:
|
||||
lkas_parser.update([0, can_sends])
|
||||
assert lkas_parser.vl["LKAS_COMMAND"]["LKAS_CONTROL_BIT"] == 1
|
||||
|
||||
@pytest.mark.parametrize("candidate", (CHRYSLER_CAR.JEEP_GRAND_CHEROKEE, CHRYSLER_CAR.JEEP_GRAND_CHEROKEE_2019))
|
||||
def test_jeep_brake_hold_safety_capability_is_provisioned(self, candidate):
|
||||
car_params = ChryslerCarInterface.get_params(
|
||||
candidate,
|
||||
{bus: {} for bus in range(8)},
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=get_test_starpilot_toggles(),
|
||||
)
|
||||
|
||||
assert car_params.safetyConfigs[0].safetyParam & ChryslerSafetyFlags.JEEP_BRAKE_HOLD.value
|
||||
|
||||
def test_jeep_brake_hold_safety_capability_is_not_provisioned_for_non_jeep(self):
|
||||
car_params = ChryslerCarInterface.get_params(
|
||||
CHRYSLER_CAR.CHRYSLER_PACIFICA_2020,
|
||||
{bus: {} for bus in range(8)},
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=get_test_starpilot_toggles(),
|
||||
)
|
||||
|
||||
assert not car_params.safetyConfigs[0].safetyParam & ChryslerSafetyFlags.JEEP_BRAKE_HOLD.value
|
||||
|
||||
def test_gm_bolt_gen2_pedal_safety_flags(self):
|
||||
CarInterface = interfaces[GM_CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL]
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
from types import SimpleNamespace
|
||||
|
||||
from opendbc.car.chrysler.carcontroller import (
|
||||
JEEP_BRAKE_HOLD_DEFAULT_DECEL,
|
||||
clip_jeep_brake_hold_decel,
|
||||
supports_jeep_brake_hold,
|
||||
)
|
||||
from opendbc.car.chrysler.values import CAR, ChryslerSafetyFlags, JEEPS
|
||||
|
||||
|
||||
def test_jeep_group_is_grand_cherokee_only():
|
||||
assert JEEPS == {CAR.JEEP_GRAND_CHEROKEE, CAR.JEEP_GRAND_CHEROKEE_2019}
|
||||
|
||||
|
||||
def test_jeep_brake_hold_support_requires_toggle_jeep_pcm_and_safety_flag():
|
||||
safety = [SimpleNamespace(safetyParam=ChryslerSafetyFlags.JEEP_BRAKE_HOLD.value)]
|
||||
no_safety = [SimpleNamespace(safetyParam=0)]
|
||||
|
||||
assert supports_jeep_brake_hold(SimpleNamespace(
|
||||
carFingerprint=CAR.JEEP_GRAND_CHEROKEE,
|
||||
pcmCruise=True,
|
||||
safetyConfigs=safety,
|
||||
), True)
|
||||
assert not supports_jeep_brake_hold(SimpleNamespace(
|
||||
carFingerprint=CAR.JEEP_GRAND_CHEROKEE,
|
||||
pcmCruise=True,
|
||||
safetyConfigs=no_safety,
|
||||
), True)
|
||||
assert not supports_jeep_brake_hold(SimpleNamespace(
|
||||
carFingerprint=CAR.JEEP_GRAND_CHEROKEE,
|
||||
pcmCruise=False,
|
||||
safetyConfigs=safety,
|
||||
), True)
|
||||
assert not supports_jeep_brake_hold(SimpleNamespace(
|
||||
carFingerprint=CAR.CHRYSLER_PACIFICA_2018,
|
||||
pcmCruise=True,
|
||||
safetyConfigs=safety,
|
||||
), True)
|
||||
assert not supports_jeep_brake_hold(SimpleNamespace(
|
||||
carFingerprint=CAR.JEEP_GRAND_CHEROKEE,
|
||||
pcmCruise=True,
|
||||
safetyConfigs=safety,
|
||||
), False)
|
||||
|
||||
|
||||
def test_jeep_brake_hold_decel_is_bounded():
|
||||
assert clip_jeep_brake_hold_decel(JEEP_BRAKE_HOLD_DEFAULT_DECEL) == JEEP_BRAKE_HOLD_DEFAULT_DECEL
|
||||
assert clip_jeep_brake_hold_decel(-8.0) == -3.0
|
||||
assert clip_jeep_brake_hold_decel(0.5) == -0.5
|
||||
@@ -15,11 +15,16 @@ CarFw = CarParams.CarFw
|
||||
Ecu = CarParams.Ecu
|
||||
|
||||
ECU_NAME = {v: k for k, v in Ecu.schema.enumerants.items()}
|
||||
FORTE_FW_AMBIGUITY = {"KIA_FORTE", "KIA_FORTE_2019_NON_SCC", "KIA_FORTE_2021_NON_SCC"}
|
||||
|
||||
|
||||
class TestFwFingerprint:
|
||||
def assertFingerprints(self, candidates, expected):
|
||||
candidates = list(candidates)
|
||||
if str(expected) in FORTE_FW_AMBIGUITY:
|
||||
assert candidates
|
||||
assert set(map(str, candidates)) <= FORTE_FW_AMBIGUITY
|
||||
return
|
||||
assert len(candidates) == 1, f"got more than one candidate: {candidates}"
|
||||
assert candidates[0] == expected
|
||||
|
||||
|
||||
@@ -14,10 +14,12 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"SUBARU_FORESTER_2022" = [nan, 3.0, nan]
|
||||
"SUBARU_OUTBACK_2023" = [nan, 3.0, nan]
|
||||
"SUBARU_ASCENT_2023" = [nan, 3.0, nan]
|
||||
"SUBARU_CROSSTREK_2025" = [nan, 3.0, nan]
|
||||
|
||||
# Toyota LTA also has torque
|
||||
"TOYOTA_RAV4_TSS2_2023" = [nan, 3.0, nan]
|
||||
"TOYOTA_MATRIX_RETROFIT" = [4.05, 1.84, 0.10]
|
||||
"TOYOTA_RAV4_PRIME" = [1.7, 2.0, 0.14]
|
||||
|
||||
# Tesla angle based controllers
|
||||
"TESLA_MODEL_3" = [nan, 2.5, nan]
|
||||
@@ -36,6 +38,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"FORD_F_150_LIGHTNING_MK1" = [nan, 1.5, nan]
|
||||
"FORD_MUSTANG_MACH_E_MK1" = [nan, 1.5, nan]
|
||||
"FORD_RANGER_MK2" = [nan, 1.5, nan]
|
||||
"FORD_TRANSIT_MK5" = [nan, 1.5, nan]
|
||||
###
|
||||
|
||||
# No steering wheel
|
||||
@@ -89,6 +92,8 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"KIA_NIRO_EV_2ND_GEN" = [2.05, 2.5, 0.14]
|
||||
"GENESIS_GV80" = [2.5, 2.5, 0.1]
|
||||
"KIA_CARNIVAL_4TH_GEN" = [1.75, 1.75, 0.15]
|
||||
"KIA_CARNIVAL_2025" = [1.75, 1.75, 0.15]
|
||||
"KIA_CARNIVAL_HEV_4TH_GEN" = [1.75, 1.75, 0.15]
|
||||
"GMC_ACADIA" = [1.6, 1.6, 0.2]
|
||||
"LEXUS_IS_TSS2" = [2.0, 2.0, 0.1]
|
||||
"HYUNDAI_KONA_EV_2ND_GEN" = [2.5, 2.5, 0.1]
|
||||
|
||||
@@ -9,8 +9,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
|
||||
"TOYOTA_ALPHARD_TSS2" = "TOYOTA_SIENNA"
|
||||
"TOYOTA_PRIUS_V" = "TOYOTA_PRIUS"
|
||||
"TOYOTA_RAV4_PRIME" = "TOYOTA_RAV4_TSS2"
|
||||
"TOYOTA_SIENNA_4TH_GEN" = "TOYOTA_RAV4_TSS2"
|
||||
"TOYOTA_SIENNA_4TH_GEN" = "TOYOTA_RAV4_PRIME"
|
||||
"LEXUS_IS" = "LEXUS_NX"
|
||||
"LEXUS_CTH" = "LEXUS_NX"
|
||||
"LEXUS_ES" = "TOYOTA_CAMRY"
|
||||
@@ -38,8 +37,10 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"HYUNDAI_IONIQ_HEV_2022" = "HYUNDAI_IONIQ_PHEV_2019"
|
||||
"HYUNDAI_IONIQ_EV_2020" = "HYUNDAI_IONIQ_PHEV_2019"
|
||||
"HYUNDAI_ELANTRA" = "HYUNDAI_SONATA_LF"
|
||||
"HYUNDAI_ELANTRA_2024" = "HYUNDAI_ELANTRA_2021"
|
||||
"HYUNDAI_ELANTRA_GT_I30" = "HYUNDAI_SONATA_LF"
|
||||
"HYUNDAI_ELANTRA_HEV_2021" = "HYUNDAI_SONATA"
|
||||
"HYUNDAI_ELANTRA_HEV_2024" = "HYUNDAI_SONATA"
|
||||
"HYUNDAI_TUCSON" = "HYUNDAI_SANTA_FE"
|
||||
"HYUNDAI_SANTA_FE_2022" = "HYUNDAI_SANTA_FE_HEV_2022"
|
||||
"KIA_K5_HEV_2020" = "KIA_K5_2021"
|
||||
@@ -61,11 +62,25 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"HONDA_FREED" = "HONDA_ODYSSEY"
|
||||
"HONDA_CLARITY" = "HONDA_ACCORD"
|
||||
"HONDA_CRV_EU" = "HONDA_CRV"
|
||||
"HONDA_CRV_SA" = "HONDA_CRV"
|
||||
"HONDA_CIVIC_BOSCH_DIESEL" = "HONDA_CIVIC_BOSCH"
|
||||
"HONDA_E" = "HONDA_CIVIC_BOSCH"
|
||||
"HONDA_E_ADVANCE" = "HONDA_E"
|
||||
"HONDA_ODYSSEY_TWN" = "HONDA_ODYSSEY"
|
||||
"HONDA_ACCORD_9G" = "HONDA_ACCORD"
|
||||
"HONDA_FIT_4G" = "HONDA_FIT"
|
||||
"ACURA_RDX_3G_MMR" = "ACURA_RDX_3G"
|
||||
"ACURA_MDX_3G" = "HONDA_PILOT"
|
||||
"ACURA_MDX_3G_MMR" = "HONDA_PILOT"
|
||||
"ACURA_MDX_4G" = "HONDA_PILOT_4G"
|
||||
"ACURA_TLX_1G" = "HONDA_ACCORD"
|
||||
"ACURA_TLX_2G_MMR" = "ACURA_TLX_2G"
|
||||
"ACURA_INTEGRA" = "HONDA_CIVIC_2022"
|
||||
"ACURA_ADX" = "HONDA_HRV_3G"
|
||||
|
||||
"BUICK_LACROSSE" = "CHEVROLET_VOLT"
|
||||
"BUICK_LACROSSE_ASCM" = "CHEVROLET_VOLT"
|
||||
"BUICK_LACROSSE_ASCM_19US" = "CHEVROLET_VOLT"
|
||||
"BUICK_REGAL" = "CHEVROLET_VOLT"
|
||||
"CADILLAC_ESCALADE_ASCM" = "CADILLAC_ESCALADE"
|
||||
"CADILLAC_ESCALADE_ESV" = "CHEVROLET_VOLT"
|
||||
@@ -118,5 +133,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"GMC_YUKON_CC" = "CHEVROLET_SILVERADO"
|
||||
"CHEVROLET_TRAILBLAZER_CC" = "CHEVROLET_TRAILBLAZER"
|
||||
"CHEVROLET_SILVERADO_CC" = "CHEVROLET_SILVERADO"
|
||||
"CADILLAC_XT4_CC" = "CADILLAC_XT4"
|
||||
"CADILLAC_XT6" = "GMC_ACADIA"
|
||||
|
||||
"CHEVROLET_TRAX" = "CHEVROLET_VOLT"
|
||||
|
||||
@@ -26,7 +26,9 @@ ACCEL_WINDDOWN_LIMIT = -4.0 * DT_CTRL * 3 # m/s^2 / frame
|
||||
ACCEL_PID_UNWIND = 0.03 * DT_CTRL * 3 # m/s^2 / frame
|
||||
PRIUS_INTEGRAL_MISMATCH_UNWIND = 8.0
|
||||
PRIUS_POSITIVE_FEEDFORWARD_SCALE = 0.7
|
||||
PRIUS_CRUISE_FEEDFORWARD_SCALE = 0.85
|
||||
PRIUS_CRUISE_FEEDFORWARD_SCALE = 1.0
|
||||
PRIUS_NEGATIVE_FEEDFORWARD_SCALE = 1.125
|
||||
CAMRY_HYBRID_POSITIVE_FEEDFORWARD_SCALE = 0.8
|
||||
|
||||
MAX_PITCH_COMPENSATION = 1.5 # m/s^2
|
||||
TOYOTA_COAST_BRAKE_MIN_SPEED = 15.0 # m/s
|
||||
@@ -34,6 +36,7 @@ TOYOTA_COAST_BRAKE_ENABLE_ACCEL = -0.10 # m/s^2
|
||||
TOYOTA_COAST_BRAKE_DISABLE_ACCEL = -0.06 # m/s^2
|
||||
TOYOTA_NO_LEAD_COAST_BRAKE_ACCEL = -0.30 # m/s^2
|
||||
TOYOTA_INTERCEPTOR_COMFORT_TARGET_ACCEL = 2.0 # m/s^2
|
||||
TOYOTA_NO_LEAD_CRUISE_SIGN_FLIP_MIN_SET_SPEED_ERROR = 0.35 # m/s
|
||||
|
||||
# LKA limits
|
||||
# EPS faults if you apply torque while the steering rate is above 100 deg/s for too long
|
||||
@@ -51,13 +54,21 @@ LOCK_CMD = b"\x40\x05\x30\x11\x00\x80\x00\x00"
|
||||
UNLOCK_CMD = b"\x40\x05\x30\x11\x00\x40\x00\x00"
|
||||
|
||||
|
||||
def is_camry_hybrid(CP) -> bool:
|
||||
return CP.carFingerprint == CAR.TOYOTA_CAMRY and bool(CP.flags & ToyotaFlags.HYBRID.value)
|
||||
|
||||
|
||||
def is_ths_hybrid(CP) -> bool:
|
||||
return CP.carFingerprint == CAR.TOYOTA_PRIUS or is_camry_hybrid(CP)
|
||||
|
||||
|
||||
def get_long_tune(CP, params):
|
||||
kiBP = [2., 5.]
|
||||
kiV = [0.5, 0.25]
|
||||
k_f = 1.0
|
||||
|
||||
if CP.carFingerprint == CAR.TOYOTA_PRIUS:
|
||||
k_f = 0.8
|
||||
if is_ths_hybrid(CP):
|
||||
k_f = 0.8 if CP.carFingerprint == CAR.TOYOTA_PRIUS else 1.0
|
||||
elif CP.carFingerprint not in TSS2_CAR:
|
||||
kiBP = [0., 5., 35.]
|
||||
kiV = [3.6, 2.4, 1.5]
|
||||
@@ -72,6 +83,16 @@ def get_prius_positive_feedforward_scale(v_ego: float) -> float:
|
||||
[PRIUS_POSITIVE_FEEDFORWARD_SCALE, PRIUS_POSITIVE_FEEDFORWARD_SCALE, PRIUS_CRUISE_FEEDFORWARD_SCALE]))
|
||||
|
||||
|
||||
def get_prius_feedforward(accel: float, v_ego: float) -> float:
|
||||
if accel > 0.0:
|
||||
return accel * get_prius_positive_feedforward_scale(v_ego)
|
||||
return accel * PRIUS_NEGATIVE_FEEDFORWARD_SCALE
|
||||
|
||||
|
||||
def get_camry_hybrid_feedforward(accel: float) -> float:
|
||||
return accel * CAMRY_HYBRID_POSITIVE_FEEDFORWARD_SCALE if accel > 0.0 else accel
|
||||
|
||||
|
||||
def update_permit_braking(current: bool, net_acceleration_request_min: float, stopping: bool,
|
||||
long_active: bool, v_ego: float, lead_visible: bool) -> bool:
|
||||
if stopping or not long_active:
|
||||
@@ -168,6 +189,19 @@ def limit_prius_stopping_accel(pcm_accel_cmd: float, target_accel: float, stoppi
|
||||
return max(pcm_accel_cmd, max(stop_floor, planner_floor))
|
||||
|
||||
|
||||
def limit_no_lead_cruise_sign_flip(pcm_accel_cmd: float, target_accel: float, stopping: bool, v_ego: float,
|
||||
set_speed: float, lead_visible: bool) -> float:
|
||||
if stopping or lead_visible or pcm_accel_cmd >= 0.0 or v_ego < TOYOTA_COAST_BRAKE_MIN_SPEED:
|
||||
return pcm_accel_cmd
|
||||
if target_accel < -0.02 or set_speed <= 0.0:
|
||||
return pcm_accel_cmd
|
||||
|
||||
if float(set_speed) - float(v_ego) >= TOYOTA_NO_LEAD_CRUISE_SIGN_FLIP_MIN_SET_SPEED_ERROR:
|
||||
return max(pcm_accel_cmd, 0.0)
|
||||
|
||||
return pcm_accel_cmd
|
||||
|
||||
|
||||
class CarController(CarControllerBase):
|
||||
def __init__(self, dbc_names, CP):
|
||||
super().__init__(dbc_names, CP)
|
||||
@@ -410,10 +444,7 @@ class CarController(CarControllerBase):
|
||||
self.aego.update(a_ego_blended)
|
||||
j_ego = (self.aego.x - prev_aego) / (DT_CTRL * 3)
|
||||
|
||||
if starpilot_toggles.frogsgomoo_tweak:
|
||||
future_t = float(np.interp(CS.out.vEgo, [2., 5.], [0.35, 1.0]))
|
||||
else:
|
||||
future_t = float(np.interp(CS.out.vEgo, [2., 5.], [0.25, 0.5]))
|
||||
future_t = float(np.interp(CS.out.vEgo, [2., 5.], [0.25, 0.5]))
|
||||
a_ego_future = a_ego_blended + j_ego * future_t
|
||||
|
||||
if CC.longActive:
|
||||
@@ -426,7 +457,7 @@ class CarController(CarControllerBase):
|
||||
else:
|
||||
# constantly slowly unwind integral to recover from large temporary errors
|
||||
unwind_rate = ACCEL_PID_UNWIND
|
||||
if self.CP.carFingerprint == CAR.TOYOTA_PRIUS and pcm_accel_cmd * self.long_pid.i < 0.0:
|
||||
if is_ths_hybrid(self.CP) and pcm_accel_cmd * self.long_pid.i < 0.0:
|
||||
unwind_rate *= PRIUS_INTEGRAL_MISMATCH_UNWIND
|
||||
self.long_pid.i -= unwind_rate * float(np.sign(self.long_pid.i))
|
||||
|
||||
@@ -441,8 +472,11 @@ class CarController(CarControllerBase):
|
||||
|
||||
feedforward = pcm_accel_cmd
|
||||
if self.CP.carFingerprint == CAR.TOYOTA_PRIUS:
|
||||
if feedforward > 0.0:
|
||||
feedforward *= get_prius_positive_feedforward_scale(CS.out.vEgo)
|
||||
feedforward = get_prius_feedforward(feedforward, CS.out.vEgo)
|
||||
elif is_camry_hybrid(self.CP) and feedforward > 0.0:
|
||||
# Preserve the established Camry Hybrid acceleration response while
|
||||
# allowing negative requests to track the planner at full scale.
|
||||
feedforward = get_camry_hybrid_feedforward(feedforward)
|
||||
|
||||
pcm_accel_cmd = self.long_pid.update(error_future,
|
||||
speed=CS.out.vEgo,
|
||||
@@ -464,14 +498,22 @@ class CarController(CarControllerBase):
|
||||
if self.CP.enableGasInterceptorDEPRECATED:
|
||||
pcm_accel_cmd = limit_interceptor_pcm_accel(pcm_accel_cmd, actuators.accel, stopping, CS.out.vEgo)
|
||||
pcm_accel_cmd = limit_interceptor_stopping_accel(pcm_accel_cmd, actuators.accel, stopping, CS.out.vEgo, bool(hud_control.leadVisible))
|
||||
elif self.CP.carFingerprint == CAR.TOYOTA_PRIUS:
|
||||
pcm_accel_cmd = limit_prius_stopping_accel(pcm_accel_cmd, actuators.accel, stopping, CS.out.vEgo, lead)
|
||||
else:
|
||||
pcm_accel_cmd = limit_no_lead_cruise_sign_flip(pcm_accel_cmd, actuators.accel, stopping, CS.out.vEgo,
|
||||
CS.out.cruiseState.speed, bool(hud_control.leadVisible))
|
||||
if self.CP.carFingerprint == CAR.TOYOTA_PRIUS:
|
||||
pcm_accel_cmd = limit_prius_stopping_accel(pcm_accel_cmd, actuators.accel, stopping, CS.out.vEgo, lead)
|
||||
|
||||
pcm_accel_cmd = float(np.clip(pcm_accel_cmd, self.params.ACCEL_MIN, self.params.ACCEL_MAX))
|
||||
|
||||
main_accel_cmd = 0. if self.CP.flags & ToyotaFlags.SECOC.value else pcm_accel_cmd
|
||||
# Toyota's physical distance-button hold can collide with StarPilot's wheel-button
|
||||
# actions and trip a temporary EPS fault. Suppress native long-press handling while
|
||||
# the physical gap button is held so ACC only sees the hold as a plain button press.
|
||||
allow_long_press = 0 if bool(getattr(CS, "distance_button", False)) else None
|
||||
can_sends.append(toyotacan.create_accel_command(self.packer, main_accel_cmd, pcm_cancel_cmd, self.permit_braking, self.standstill_req, lead,
|
||||
CS.acc_type, fcw_alert, self.distance_button, starpilot_toggles.reverse_cruise_increase))
|
||||
CS.acc_type, fcw_alert, self.distance_button, starpilot_toggles.reverse_cruise_increase,
|
||||
allow_long_press))
|
||||
if self.CP.flags & ToyotaFlags.SECOC.value:
|
||||
acc_cmd_2 = toyotacan.create_accel_command_2(self.packer, pcm_accel_cmd)
|
||||
acc_cmd_2 = add_mac(self.secoc_key,
|
||||
@@ -490,7 +532,9 @@ class CarController(CarControllerBase):
|
||||
if self.CP.carFingerprint in UNSUPPORTED_DSU_CAR:
|
||||
can_sends.append(toyotacan.create_acc_cancel_command(self.packer))
|
||||
else:
|
||||
can_sends.append(toyotacan.create_accel_command(self.packer, 0, pcm_cancel_cmd, True, False, lead, CS.acc_type, False, self.distance_button, starpilot_toggles.reverse_cruise_increase))
|
||||
allow_long_press = 0 if bool(getattr(CS, "distance_button", False)) else None
|
||||
can_sends.append(toyotacan.create_accel_command(self.packer, 0, pcm_cancel_cmd, True, False, lead, CS.acc_type, False,
|
||||
self.distance_button, starpilot_toggles.reverse_cruise_increase, allow_long_press))
|
||||
|
||||
# *** hud ui ***
|
||||
if self.CP.carFingerprint != CAR.TOYOTA_PRIUS_V:
|
||||
|
||||
@@ -23,6 +23,7 @@ TEMP_STEER_FAULTS = (0, 9, 11, 21, 25)
|
||||
# - lka/lta msg drop out: 3 (recoverable)
|
||||
# - prolonged high driver torque: 17 (permanent)
|
||||
PERM_STEER_FAULTS = (3, 17)
|
||||
LKAS_BUTTON_CAR = TSS2_CAR | {CAR.TOYOTA_PRIUS}
|
||||
|
||||
|
||||
# Traffic signals for Speed Limit Controller - Credit goes to the DragonPilot team!
|
||||
@@ -43,6 +44,13 @@ def calculate_interceptor_gas_pressed(cp) -> bool:
|
||||
return interceptor_gas > 805
|
||||
|
||||
|
||||
def create_lkas_button_events(lkas_button: int, prev_lkas_button: int) -> list[structs.CarState.ButtonEvent]:
|
||||
if lkas_button != 0 and lkas_button != prev_lkas_button:
|
||||
return (create_button_events(1, 0, {1: ButtonType.lkas}) +
|
||||
create_button_events(0, 1, {1: ButtonType.lkas}))
|
||||
return []
|
||||
|
||||
|
||||
class CarState(CarStateBase):
|
||||
def __init__(self, CP, FPCP):
|
||||
super().__init__(CP, FPCP)
|
||||
@@ -88,7 +96,8 @@ class CarState(CarStateBase):
|
||||
cp_cam = can_parsers[Bus.cam]
|
||||
|
||||
ret = structs.CarState()
|
||||
cp_acc = cp_cam if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR) else cp
|
||||
dsu_bypass = bool(self.CP.flags & ToyotaFlags.DSU_BYPASS.value)
|
||||
cp_acc = cp_cam if self.CP.carFingerprint in (TSS2_CAR - RADAR_ACC_CAR) or dsu_bypass else cp
|
||||
|
||||
if not self.CP.flags & ToyotaFlags.SECOC.value:
|
||||
self.gvc = cp.vl["VSC1S07"]["GVC"]
|
||||
@@ -180,7 +189,7 @@ class CarState(CarStateBase):
|
||||
conversion_factor = CV.KPH_TO_MS if is_metric else CV.MPH_TO_MS
|
||||
ret.cruiseState.speedCluster = cluster_set_speed * conversion_factor
|
||||
|
||||
if self.CP.carFingerprint in TSS2_CAR and not self.CP.flags & ToyotaFlags.DISABLE_RADAR.value:
|
||||
if dsu_bypass or (self.CP.carFingerprint in TSS2_CAR and not self.CP.flags & ToyotaFlags.DISABLE_RADAR.value):
|
||||
# smartDSU can intercept ACC_CONTROL, so don't require it when it's no
|
||||
# longer forwarded on the PT bus.
|
||||
if not self.has_SDSU:
|
||||
@@ -220,16 +229,12 @@ class CarState(CarStateBase):
|
||||
self.pcm_follow_distance = cp.vl["PCM_CRUISE_2"]["PCM_FOLLOW_DISTANCE"]
|
||||
|
||||
buttonEvents = []
|
||||
if self.CP.carFingerprint in TSS2_CAR:
|
||||
# lkas button is wired to the camera
|
||||
if self.CP.carFingerprint in LKAS_BUTTON_CAR:
|
||||
prev_lkas_button = self.lkas_button
|
||||
self.lkas_button = cp_cam.vl["LKAS_HUD"]["LDA_ON_MESSAGE"]
|
||||
buttonEvents += create_lkas_button_events(self.lkas_button, prev_lkas_button)
|
||||
|
||||
# Cycles between 1 and 2 when pressing the button, then rests back at 0 after ~3s
|
||||
if self.lkas_button != 0 and self.lkas_button != prev_lkas_button:
|
||||
buttonEvents.extend(create_button_events(1, 0, {1: ButtonType.lkas}) +
|
||||
create_button_events(0, 1, {1: ButtonType.lkas}))
|
||||
|
||||
if self.CP.carFingerprint in TSS2_CAR:
|
||||
if self.CP.carFingerprint not in (RADAR_ACC_CAR | SECOC_CAR):
|
||||
# distance button is wired to the ACC module (camera or radar)
|
||||
prev_distance_button = self.distance_button
|
||||
|
||||
@@ -1309,13 +1309,16 @@ FW_VERSIONS = {
|
||||
b'\x01896630841000\x00\x00\x00\x00',
|
||||
b'\x01896630857101\x00\x00\x00\x00',
|
||||
b'\x01896630864000\x00\x00\x00\x00',
|
||||
b'\x01896630869000\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.abs, 0x7b0, None): [
|
||||
b'\x01F15260815100\x00\x00\x00\x00',
|
||||
b'\x01F15260815300\x00\x00\x00\x00',
|
||||
b'\x01F15260823000\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.eps, 0x7a1, None): [
|
||||
b'\x018965B4509100\x00\x00\x00\x00',
|
||||
b'\x018965B4514000\x00\x00\x00\x00',
|
||||
],
|
||||
(Ecu.fwdRadar, 0x750, 0xf): [
|
||||
b'\x018821F3301500\x00\x00\x00\x00',
|
||||
@@ -1324,6 +1327,7 @@ FW_VERSIONS = {
|
||||
b'\x028646F0802200\x00\x00\x00\x008646G4202100\x00\x00\x00\x00',
|
||||
b'\x028646F0802300\x00\x00\x00\x008646G4202100\x00\x00\x00\x00',
|
||||
b'\x028646F0802400\x00\x00\x00\x008646G4202100\x00\x00\x00\x00',
|
||||
b'\x028646F0802500\x00\x00\x00\x008646G4202100\x00\x00\x00\x00',
|
||||
],
|
||||
},
|
||||
CAR.LEXUS_CTH: {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user