mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-24 01:33:46 +08:00
Compare commits
315 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1a527cb927 | |||
| 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;
|
||||
|
||||
+61
-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,51 @@ 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._buffer = memoryview(b"")
|
||||
|
||||
def readable(self):
|
||||
return True
|
||||
|
||||
def readinto(self, buffer):
|
||||
count = 0
|
||||
while count < len(buffer):
|
||||
if not self._buffer:
|
||||
path = next(self._paths, None)
|
||||
if path is None:
|
||||
break
|
||||
self._buffer = memoryview(Path(path).read_bytes())
|
||||
continue
|
||||
take = min(len(buffer) - count, len(self._buffer))
|
||||
buffer[count:count + take] = self._buffer[:take]
|
||||
self._buffer = self._buffer[take:]
|
||||
count += take
|
||||
return count
|
||||
|
||||
|
||||
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 +94,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)
|
||||
|
||||
+224
-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,16 @@ 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}},
|
||||
{"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 +58,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 +73,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 +128,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 +150,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 +171,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 +186,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 +217,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 +239,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 +250,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 +263,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 +283,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 +339,67 @@ 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}},
|
||||
{"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 +411,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 +429,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 +461,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 +511,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 +555,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 +574,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 +583,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 +602,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 +625,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 +642,30 @@ 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}},
|
||||
{"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,16 @@
|
||||
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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+25
-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,28 @@ 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 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 +161,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
-1
@@ -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
|
||||
|
||||
@@ -36,7 +54,16 @@ function agnos_init {
|
||||
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 +71,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 +114,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 +152,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 +192,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 +212,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 +221,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]) + \
|
||||
|
||||
@@ -258,6 +258,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,27 @@ def shape_truck_positive_accel(accel: float, v_ego: float, enabled: bool) -> flo
|
||||
return accel
|
||||
|
||||
|
||||
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 +272,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 +284,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 +357,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 +479,11 @@ 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.lka_steering_cmd_counter = 0
|
||||
self.lka_icon_status_last = (False, False)
|
||||
@@ -479,6 +544,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 +935,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 +1010,21 @@ 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_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 +1042,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 +1104,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 +1146,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 +1192,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,10 @@ MALIBU_BUTTON_MAP = {
|
||||
CruiseButtons.CANCEL: 5,
|
||||
}
|
||||
|
||||
ACC_CRUISE_STATE_ADAPTIVE = 2
|
||||
XT4_CC_BUTTON_BURST_FRAMES = 6
|
||||
XT4_CC_BUTTON_COUNTER_DELAY_FRAMES = 1
|
||||
|
||||
|
||||
def malibu_phase_map_for_button(button):
|
||||
key = MALIBU_BUTTON_MAP.get(button)
|
||||
@@ -215,16 +219,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,
|
||||
}
|
||||
@@ -308,6 +320,42 @@ def create_gm_cc_spam_command(packer, controller, CS, actuators, starpilot_toggl
|
||||
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):
|
||||
|
||||
@@ -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.
|
||||
@@ -595,6 +609,9 @@ class CarInterface(CarInterfaceBase):
|
||||
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 +655,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 +706,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 +717,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 +737,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,12 @@ 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_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 +358,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 +426,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 +435,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 +479,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 +488,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 +497,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 +506,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 +515,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 +828,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 +846,37 @@ 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_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,
|
||||
@@ -168,6 +169,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 +197,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 +251,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 +286,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 +362,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 +433,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 +557,53 @@ class TestGMCarController:
|
||||
|
||||
assert [msg[2] for msg in msgs] == [0]
|
||||
|
||||
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 +618,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 +668,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)
|
||||
|
||||
|
||||
@@ -283,6 +283,10 @@ class CAR(Platforms):
|
||||
[GMCarDocs("Buick LaCrosse 2017-19 ASCM Harness")],
|
||||
BUICK_LACROSSE.specs,
|
||||
)
|
||||
BUICK_LACROSSE_ASCM_19US = GMPlatformConfig(
|
||||
[GMCarDocs("Buick LaCrosse 2019 US ASCM Harness")],
|
||||
BUICK_LACROSSE.specs,
|
||||
)
|
||||
BUICK_REGAL = GMASCMPlatformConfig(
|
||||
[GMCarDocs("Buick Regal Essence 2018")],
|
||||
GMCarSpecs(mass=1714, wheelbase=2.83, steerRatio=14.4, centerToFrontRatio=0.4),
|
||||
@@ -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")],
|
||||
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),
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -615,26 +759,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 +842,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 +911,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
|
||||
|
||||
@@ -136,6 +137,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 +155,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 = (
|
||||
@@ -448,6 +460,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 +479,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 +554,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 +564,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 +614,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
|
||||
|
||||
@@ -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),
|
||||
]
|
||||
|
||||
@@ -48,6 +48,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
|
||||
@@ -104,10 +110,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 +121,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,6 +158,13 @@ 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
|
||||
|
||||
@@ -174,6 +191,8 @@ 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:
|
||||
@@ -218,6 +237,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 +259,20 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.vEgoStarting = 0.5
|
||||
ret.vEgoStopping = 0.35
|
||||
|
||||
if candidate == CAR.HYUNDAI_ELANTRA_2021:
|
||||
ret.longitudinalActuatorDelay = 0.22
|
||||
ret.stopAccel = -1.5
|
||||
ret.stoppingDecelRate = 0.5
|
||||
|
||||
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,10 +8,14 @@ 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 import hyundaican, hyundaicanfd
|
||||
@@ -89,6 +93,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 +241,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 +265,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 +300,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 +369,13 @@ class TestHyundaiFingerprint:
|
||||
assert palisade_2023.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CAN_CANFD_BLENDED
|
||||
assert palisade_2023.safetyConfigs[-1].safetyParam & HyundaiSafetyFlags.CANCEL_BTN_ENABLE
|
||||
|
||||
@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
|
||||
@@ -478,6 +528,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 +689,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(-1.5)
|
||||
assert CP.stoppingDecelRate == pytest.approx(0.5)
|
||||
|
||||
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 +731,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 = [
|
||||
@@ -1003,6 +1110,14 @@ 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)
|
||||
|
||||
def test_ioniq_6_longitudinal_tuning_helper_matches_dynamic_profile(self):
|
||||
state = Ioniq6LongitudinalTuningState()
|
||||
|
||||
@@ -1094,6 +1209,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 +1574,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 +1588,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 +1596,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 +1698,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 +1752,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 +2094,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 +2102,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 +2130,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 +2143,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 +2182,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 +2321,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)
|
||||
|
||||
@@ -103,6 +103,7 @@ class HyundaiSafetyFlags(IntFlag):
|
||||
NON_SCC = 4096
|
||||
CAN_CANFD_BLENDED = 8192
|
||||
CANCEL_BTN_ENABLE = 16384
|
||||
CAN_REFRESH_MSGS = 32768
|
||||
CCNC = 32768
|
||||
|
||||
|
||||
@@ -218,6 +219,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 +295,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
|
||||
@@ -764,7 +783,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 +794,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(
|
||||
@@ -1038,8 +1075,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
|
||||
@@ -1135,10 +1172,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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -36,6 +36,7 @@ non_tested_cars = [
|
||||
HYUNDAI.HYUNDAI_TUCSON_PHEV_2025,
|
||||
HYUNDAI.KIA_EV6_2025,
|
||||
HYUNDAI.KIA_EV9,
|
||||
HYUNDAI.KIA_CARNIVAL_2025,
|
||||
HYUNDAI.KIA_SPORTAGE_2026,
|
||||
HYUNDAI.KIA_SORENTO_2024,
|
||||
HYUNDAI.KIA_SORENTO_HEV_4TH_GEN_LFA2,
|
||||
@@ -164,6 +165,7 @@ routes = [
|
||||
CarTestRoute("656ac0d830792fcc/2021-12-28--14-45-56", HYUNDAI.HYUNDAI_SANTA_FE_PHEV_2022, segment=1),
|
||||
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("6c0069dcd5bbb6c1/00000020--6b95507969", HYUNDAI.KIA_CARNIVAL_HEV_4TH_GEN), # HDA II
|
||||
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),
|
||||
@@ -232,7 +234,11 @@ routes = [
|
||||
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("65ef8b49f9b0dd24/00000141--5c8720a01c", HYUNDAI.HYUNDAI_ELANTRA_HEV_2024),
|
||||
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),
|
||||
|
||||
@@ -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
|
||||
@@ -79,6 +79,7 @@ def get_test_starpilot_toggles() -> SimpleNamespace:
|
||||
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 +213,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
|
||||
@@ -18,6 +18,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
# 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 +37,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 +91,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,7 +9,6 @@ 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"
|
||||
"LEXUS_IS" = "LEXUS_NX"
|
||||
"LEXUS_CTH" = "LEXUS_NX"
|
||||
@@ -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"
|
||||
@@ -66,6 +67,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
|
||||
"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 +120,6 @@ 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"
|
||||
|
||||
"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)
|
||||
@@ -426,7 +460,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 +475,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 +501,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 +535,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
|
||||
|
||||
@@ -28,6 +28,9 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.toyota)]
|
||||
ret.safetyConfigs[0].safetyParam = EPS_SCALE[candidate]
|
||||
|
||||
if candidate == CAR.LEXUS_IS:
|
||||
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.ALT_CRUISE.value
|
||||
|
||||
# BRAKE_MODULE is on a different address for these cars
|
||||
if DBC[candidate][Bus.pt] == "toyota_new_mc_pt_generated":
|
||||
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.ALT_BRAKE.value
|
||||
@@ -60,6 +63,17 @@ class CarInterface(CarInterfaceBase):
|
||||
# sDSU / radar filter hardware needs the Toyota safety long-filter TX set.
|
||||
ret.safetyConfigs[0].safetyParam |= ToyotaSafetyFlags.LONG_FILTER.value
|
||||
|
||||
# A DSU bypass adapter reroutes the stock DSU messages to the camera bus.
|
||||
# These messages are normally absent there on pre-TSS2 platforms.
|
||||
camera_fingerprint = fingerprint.get(2, {})
|
||||
has_dsu_bypass = 0x343 in camera_fingerprint or 0x4CB in camera_fingerprint
|
||||
if candidate == CAR.LEXUS_IS:
|
||||
# The IS mirrors its native buses onto camera bus during startup without a bypass adapter.
|
||||
has_dsu_bypass = ((0x343 in camera_fingerprint and 0x343 not in fingerprint.get(1, {})) or
|
||||
(0x4CB in camera_fingerprint and 0x4CB not in fingerprint.get(0, {})))
|
||||
if not use_sdsu and candidate not in TSS2_CAR and has_dsu_bypass:
|
||||
ret.flags |= ToyotaFlags.DSU_BYPASS.value
|
||||
|
||||
# In TSS2 cars, the camera does long control
|
||||
found_ecus = [fw.ecu for fw in car_fw]
|
||||
|
||||
@@ -113,7 +127,9 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
# No radar dbc for cars without DSU which are not TSS 2.0
|
||||
# TODO: make an adas dbc file for dsu-less models
|
||||
ret.radarUnavailable = Bus.radar not in DBC[candidate] or candidate in (NO_DSU_CAR - TSS2_CAR)
|
||||
ret.radarUnavailable = Bus.radar not in DBC[candidate] or candidate in (NO_DSU_CAR - TSS2_CAR - {CAR.TOYOTA_CAMRY})
|
||||
if candidate == CAR.TOYOTA_CAMRY:
|
||||
ret.radarTimeStepDEPRECATED = 0.1
|
||||
|
||||
# Since we don't yet parse radar on TSS2/TSS-P radar-based ACC cars, gate
|
||||
# longitudinal behind the alpha-long toggle.
|
||||
@@ -135,6 +151,7 @@ class CarInterface(CarInterfaceBase):
|
||||
# - TSS2 radar ACC cars (disables radar)
|
||||
|
||||
ret.openpilotLongitudinalControl = (use_sdsu or
|
||||
bool(ret.flags & ToyotaFlags.DSU_BYPASS.value) or
|
||||
candidate in (TSS2_CAR - RADAR_ACC_CAR) or
|
||||
bool(ret.flags & ToyotaFlags.DISABLE_RADAR.value))
|
||||
|
||||
@@ -156,6 +173,8 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.minEnableSpeed = -1. if (stop_and_go or ret.enableGasInterceptorDEPRECATED) else MIN_ACC_SPEED
|
||||
|
||||
prius_long_defaults = candidate == CAR.TOYOTA_PRIUS and ret.openpilotLongitudinalControl
|
||||
camry_hybrid_long_defaults = (candidate == CAR.TOYOTA_CAMRY and ret.openpilotLongitudinalControl and
|
||||
bool(ret.flags & ToyotaFlags.HYBRID.value))
|
||||
|
||||
if candidate in TSS2_CAR or ret.enableGasInterceptorDEPRECATED or prius_long_defaults:
|
||||
ret.flags |= ToyotaFlags.RAISED_ACCEL_LIMIT.value
|
||||
@@ -168,6 +187,13 @@ class CarInterface(CarInterfaceBase):
|
||||
if ret.flags & ToyotaFlags.HYBRID.value:
|
||||
ret.longitudinalActuatorDelay = 0.05
|
||||
|
||||
if camry_hybrid_long_defaults:
|
||||
# The THS eCVT responds much faster than the legacy non-TSS2 ICE tune.
|
||||
ret.longitudinalActuatorDelay = 0.05
|
||||
ret.vEgoStopping = 0.25
|
||||
ret.vEgoStarting = 0.25
|
||||
ret.stoppingDecelRate = 0.3
|
||||
|
||||
if ret.enableGasInterceptorDEPRECATED:
|
||||
# Pedal/SDSU Toyotas feel best with a softer final stop clamp.
|
||||
ret.longitudinalActuatorDelay = max(ret.longitudinalActuatorDelay, 0.2)
|
||||
|
||||
@@ -2,9 +2,14 @@
|
||||
from opendbc.can import CANParser
|
||||
from opendbc.car import Bus
|
||||
from opendbc.car.structs import RadarData
|
||||
from opendbc.car.toyota.values import DBC, TSS2_CAR
|
||||
from opendbc.car.toyota.values import CAR, DBC, TSS2_CAR
|
||||
from opendbc.car.interfaces import RadarInterfaceBase
|
||||
|
||||
RADAR_ACC_TSSP_CAR = {CAR.TOYOTA_CAMRY}
|
||||
TSSP_CLUSTER_MSGS = list(range(0x680, 0x686))
|
||||
KPH_TO_MS = 1. / 3.6
|
||||
TSSP_RADAR_EGO_SPEED_SCALE = 0.922
|
||||
|
||||
|
||||
def _create_radar_can_parser(car_fingerprint):
|
||||
if car_fingerprint in TSS2_CAR:
|
||||
@@ -21,22 +26,37 @@ def _create_radar_can_parser(car_fingerprint):
|
||||
return CANParser(DBC[car_fingerprint][Bus.radar], messages, 1)
|
||||
|
||||
|
||||
def _create_tssp_radar_can_parser(car_fingerprint):
|
||||
return CANParser(DBC[car_fingerprint][Bus.radar], [(addr, 10) for addr in TSSP_CLUSTER_MSGS], 1)
|
||||
|
||||
|
||||
def _create_wheel_speed_can_parser(car_fingerprint):
|
||||
return CANParser(DBC[car_fingerprint][Bus.pt], [("WHEEL_SPEEDS", 80)], 0)
|
||||
|
||||
|
||||
class RadarInterface(RadarInterfaceBase):
|
||||
def __init__(self, CP):
|
||||
super().__init__(CP)
|
||||
self.track_id = 0
|
||||
self.radar_acc_tssp = CP.carFingerprint in RADAR_ACC_TSSP_CAR
|
||||
|
||||
if CP.carFingerprint in TSS2_CAR:
|
||||
self.RADAR_A_MSGS = list(range(0x180, 0x190))
|
||||
self.RADAR_B_MSGS = list(range(0x190, 0x1a0))
|
||||
if self.radar_acc_tssp:
|
||||
self.RADAR_MSGS = TSSP_CLUSTER_MSGS
|
||||
self.rcp = None if CP.radarUnavailable else _create_tssp_radar_can_parser(CP.carFingerprint)
|
||||
self.pt_cp = None if CP.radarUnavailable else _create_wheel_speed_can_parser(CP.carFingerprint)
|
||||
self.trigger_msg = self.RADAR_MSGS[-1]
|
||||
else:
|
||||
self.RADAR_A_MSGS = list(range(0x210, 0x220))
|
||||
self.RADAR_B_MSGS = list(range(0x220, 0x230))
|
||||
if CP.carFingerprint in TSS2_CAR:
|
||||
self.RADAR_A_MSGS = list(range(0x180, 0x190))
|
||||
self.RADAR_B_MSGS = list(range(0x190, 0x1a0))
|
||||
else:
|
||||
self.RADAR_A_MSGS = list(range(0x210, 0x220))
|
||||
self.RADAR_B_MSGS = list(range(0x220, 0x230))
|
||||
self.valid_cnt = {key: 0 for key in self.RADAR_A_MSGS}
|
||||
self.rcp = None if CP.radarUnavailable else _create_radar_can_parser(CP.carFingerprint)
|
||||
self.pt_cp = None
|
||||
self.trigger_msg = self.RADAR_B_MSGS[-1]
|
||||
|
||||
self.valid_cnt = {key: 0 for key in self.RADAR_A_MSGS}
|
||||
|
||||
self.rcp = None if CP.radarUnavailable else _create_radar_can_parser(CP.carFingerprint)
|
||||
self.trigger_msg = self.RADAR_B_MSGS[-1]
|
||||
self.updated_messages = set()
|
||||
|
||||
def update(self, can_strings):
|
||||
@@ -45,16 +65,72 @@ class RadarInterface(RadarInterfaceBase):
|
||||
|
||||
vls = self.rcp.update(can_strings)
|
||||
self.updated_messages.update(vls)
|
||||
if self.pt_cp is not None:
|
||||
self.pt_cp.update(can_strings)
|
||||
|
||||
if self.trigger_msg not in self.updated_messages:
|
||||
return None
|
||||
|
||||
if self.pt_cp is not None and not self.pt_cp.can_valid:
|
||||
self.updated_messages.clear()
|
||||
ret = RadarData()
|
||||
ret.errors.canError = True
|
||||
return ret
|
||||
|
||||
rr = self._update(self.updated_messages)
|
||||
self.updated_messages.clear()
|
||||
|
||||
return rr
|
||||
|
||||
def _get_v_ego(self):
|
||||
ws = self.pt_cp.vl["WHEEL_SPEEDS"]
|
||||
wheel_speed = (ws["WHEEL_SPEED_FL"] + ws["WHEEL_SPEED_FR"] +
|
||||
ws["WHEEL_SPEED_RL"] + ws["WHEEL_SPEED_RR"]) / 4.
|
||||
return wheel_speed * KPH_TO_MS * self.CP.wheelSpeedFactor
|
||||
|
||||
def _update_tssp(self, updated_messages):
|
||||
ret = RadarData()
|
||||
if not self.rcp.can_valid:
|
||||
ret.errors.canError = True
|
||||
|
||||
v_ego = self._get_v_ego()
|
||||
updated_ids = set()
|
||||
for ii in sorted(updated_messages):
|
||||
if ii not in self.RADAR_MSGS:
|
||||
continue
|
||||
|
||||
cpt = self.rcp.vl[ii]
|
||||
track_id = int(cpt["ID"])
|
||||
if track_id == 0x3f or cpt["LONG_DIST"] <= 0:
|
||||
continue
|
||||
|
||||
updated_ids.add(track_id)
|
||||
if track_id not in self.pts:
|
||||
self.pts[track_id] = RadarData.RadarPoint()
|
||||
self.pts[track_id].trackId = self.track_id
|
||||
self.track_id += 1
|
||||
|
||||
self.pts[track_id].dRel = float(cpt["LONG_DIST"])
|
||||
self.pts[track_id].yRel = -float(cpt["LAT_DIST"])
|
||||
self.pts[track_id].vRel = float(cpt["SPEED"]) - v_ego * TSSP_RADAR_EGO_SPEED_SCALE
|
||||
self.pts[track_id].aRel = float("nan")
|
||||
self.pts[track_id].yvRel = float(cpt["LAT_SPEED"])
|
||||
self.pts[track_id].measured = True
|
||||
|
||||
for track_id in list(self.pts):
|
||||
if track_id not in updated_ids:
|
||||
del self.pts[track_id]
|
||||
|
||||
ret.points = list(self.pts.values())
|
||||
return ret
|
||||
|
||||
def _update(self, updated_messages):
|
||||
if self.radar_acc_tssp:
|
||||
return self._update_tssp(updated_messages)
|
||||
|
||||
return self._update_denso(updated_messages)
|
||||
|
||||
def _update_denso(self, updated_messages):
|
||||
ret = RadarData()
|
||||
if not self.rcp.can_valid:
|
||||
ret.errors.canError = True
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
from hypothesis import given, settings, strategies as st
|
||||
|
||||
from opendbc.car import Bus, structs
|
||||
@@ -7,11 +8,15 @@ from opendbc.can import CANPacker, CANParser
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.fw_versions import build_fw_dict
|
||||
from opendbc.car.toyota import toyotacan
|
||||
from opendbc.car.toyota.carcontroller import CarController, get_prius_positive_feedforward_scale, limit_interceptor_pcm_accel, \
|
||||
limit_interceptor_stopping_accel, limit_prius_stopping_accel, update_permit_braking
|
||||
from opendbc.car.toyota.carstate import calculate_interceptor_gas_pressed
|
||||
from opendbc.car.toyota.carcontroller import CarController, get_camry_hybrid_feedforward, get_long_tune, get_prius_feedforward, \
|
||||
get_prius_positive_feedforward_scale, \
|
||||
limit_interceptor_pcm_accel, \
|
||||
limit_interceptor_stopping_accel, limit_no_lead_cruise_sign_flip, \
|
||||
limit_prius_stopping_accel, update_permit_braking
|
||||
from opendbc.car.toyota.carstate import CarState, LKAS_BUTTON_CAR, calculate_interceptor_gas_pressed, create_lkas_button_events
|
||||
from opendbc.car.toyota.fingerprints import FW_VERSIONS
|
||||
from opendbc.car.toyota.interface import CarInterface
|
||||
from opendbc.car.toyota.radar_interface import RadarInterface, TSSP_RADAR_EGO_SPEED_SCALE
|
||||
from opendbc.car.toyota.values import CAR, DBC, TSS2_CAR, ANGLE_CONTROL_CAR, RADAR_ACC_CAR, SECOC_CAR, \
|
||||
FW_QUERY_CONFIG, PLATFORM_CODE_ECUS, FUZZY_EXCLUDED_PLATFORMS, \
|
||||
ToyotaFlags, ToyotaSafetyFlags, get_platform_codes
|
||||
@@ -35,6 +40,23 @@ class TestToyotaInterfaces:
|
||||
# At this time, only RAV4 2023 is expected to use LTA/angle control
|
||||
assert ANGLE_CONTROL_CAR == {CAR.TOYOTA_RAV4_TSS2_2023}
|
||||
|
||||
def test_rav4_prime_force_torque_controller(self):
|
||||
fingerprint = {bus: {} for bus in range(8)}
|
||||
|
||||
default_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_RAV4_PRIME, fingerprint, [], False, False, False,
|
||||
SimpleNamespace(force_torque_controller=False, nnff=False, nnff_lite=False),
|
||||
)
|
||||
forced_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_RAV4_PRIME, fingerprint, [], False, False, False,
|
||||
SimpleNamespace(force_torque_controller=True, nnff=False, nnff_lite=False),
|
||||
)
|
||||
|
||||
assert default_params.lateralTuning.which() == "pid"
|
||||
assert forced_params.lateralTuning.which() == "torque"
|
||||
assert forced_params.lateralTuning.torque.latAccelFactor == pytest.approx(1.7)
|
||||
assert forced_params.lateralTuning.torque.friction == pytest.approx(0.14)
|
||||
|
||||
def test_tss2_dbc(self):
|
||||
# We make some assumptions about TSS2 platforms,
|
||||
# like looking up certain signals only in this DBC
|
||||
@@ -79,6 +101,189 @@ class TestToyotaInterfaces:
|
||||
assert abs(car_params.vEgoStopping - 0.25) < 1e-6
|
||||
assert abs(car_params.vEgoStarting - 0.25) < 1e-6
|
||||
|
||||
@pytest.mark.parametrize("camera_message", [0x343, 0x4CB])
|
||||
def test_dsu_bypass_enables_longitudinal(self, camera_message):
|
||||
fingerprint = {bus: {} for bus in range(8)}
|
||||
fingerprint[2][camera_message] = 8
|
||||
|
||||
car_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_COROLLA,
|
||||
fingerprint,
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
|
||||
assert car_params.flags & ToyotaFlags.DSU_BYPASS.value
|
||||
assert car_params.openpilotLongitudinalControl
|
||||
assert not car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
|
||||
|
||||
starpilot_params = CarInterface.get_starpilot_params(
|
||||
CAR.TOYOTA_COROLLA, fingerprint, [], car_params, SimpleNamespace(),
|
||||
)
|
||||
car_state = CarState(car_params, starpilot_params)
|
||||
can_parsers = car_state.get_can_parsers(car_params)
|
||||
car_state.update(can_parsers, SimpleNamespace(cluster_offset=1.0))
|
||||
|
||||
for message in ("ACC_CONTROL", "PRE_COLLISION", "PCS_HUD"):
|
||||
assert message not in can_parsers[Bus.pt].vl
|
||||
assert message in can_parsers[Bus.cam].vl
|
||||
|
||||
@pytest.mark.parametrize(("native_bus", "message"), [(1, 0x343), (0, 0x4CB)])
|
||||
def test_dsu_bypass_ignores_startup_bus_mirror(self, native_bus, message):
|
||||
fingerprint = {bus: {} for bus in range(8)}
|
||||
fingerprint[native_bus][message] = 8
|
||||
fingerprint[2][message] = 8
|
||||
|
||||
car_params = CarInterface.get_params(
|
||||
CAR.LEXUS_IS,
|
||||
fingerprint,
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
|
||||
assert not car_params.flags & ToyotaFlags.DSU_BYPASS.value
|
||||
assert not car_params.openpilotLongitudinalControl
|
||||
assert car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
|
||||
assert car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.ALT_CRUISE.value
|
||||
|
||||
@pytest.mark.parametrize(("native_bus", "message"), [(1, 0x343), (0, 0x4CB)])
|
||||
def test_prius_dsu_bypass_allows_native_bus_message(self, native_bus, message):
|
||||
fingerprint = {bus: {} for bus in range(8)}
|
||||
fingerprint[native_bus][message] = 8
|
||||
fingerprint[2][message] = 8
|
||||
|
||||
car_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_PRIUS,
|
||||
fingerprint,
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
|
||||
assert car_params.flags & ToyotaFlags.DSU_BYPASS.value
|
||||
assert car_params.openpilotLongitudinalControl
|
||||
assert not car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.STOCK_LONGITUDINAL.value
|
||||
assert not car_params.safetyConfigs[0].safetyParam & ToyotaSafetyFlags.ALT_CRUISE.value
|
||||
|
||||
def test_dsu_bypass_does_not_change_tss2_or_smart_dsu(self):
|
||||
fingerprint = {bus: {} for bus in range(8)}
|
||||
fingerprint[0][0x2FF] = 8
|
||||
fingerprint[2][0x343] = 8
|
||||
|
||||
smart_dsu_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_COROLLA,
|
||||
fingerprint,
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
tss2_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_CAMRY_TSS2,
|
||||
fingerprint,
|
||||
[],
|
||||
alpha_long=False,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
|
||||
assert not smart_dsu_params.flags & ToyotaFlags.DSU_BYPASS.value
|
||||
assert not tss2_params.flags & ToyotaFlags.DSU_BYPASS.value
|
||||
|
||||
def test_camry_hybrid_continental_radar_uses_ths_longitudinal_tune(self):
|
||||
fingerprint = {bus: ({0x2FF: 8} if bus == 0 else {}) for bus in range(8)}
|
||||
hybrid_fw = [CarParams.CarFw(ecu=Ecu.hybrid, address=0x7D2, fwVersion=b"test")]
|
||||
car_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_CAMRY,
|
||||
fingerprint,
|
||||
hybrid_fw,
|
||||
alpha_long=True,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
|
||||
assert car_params.openpilotLongitudinalControl
|
||||
assert not car_params.radarUnavailable
|
||||
assert abs(car_params.radarTimeStepDEPRECATED - 0.1) < 1e-6
|
||||
assert abs(car_params.longitudinalActuatorDelay - 0.05) < 1e-6
|
||||
assert abs(car_params.vEgoStopping - 0.25) < 1e-6
|
||||
assert abs(car_params.vEgoStarting - 0.25) < 1e-6
|
||||
assert abs(car_params.stoppingDecelRate - 0.3) < 1e-6
|
||||
assert not car_params.flags & ToyotaFlags.NO_STOP_TIMER.value
|
||||
|
||||
controller = get_long_tune(car_params, SimpleNamespace(ACCEL_MIN=-3.5, ACCEL_MAX=2.0))
|
||||
controller.speed = 0.0
|
||||
assert controller.k_i == pytest.approx(0.5)
|
||||
assert controller.k_f == pytest.approx(1.0)
|
||||
|
||||
radar_interface = RadarInterface(car_params)
|
||||
assert radar_interface.radar_acc_tssp
|
||||
assert radar_interface.rcp is not None
|
||||
assert radar_interface.pt_cp is not None
|
||||
|
||||
def test_camry_ice_keeps_legacy_longitudinal_tune(self):
|
||||
fingerprint = {bus: ({0x2FF: 8} if bus == 0 else {}) for bus in range(8)}
|
||||
car_params = CarInterface.get_params(
|
||||
CAR.TOYOTA_CAMRY,
|
||||
fingerprint,
|
||||
[],
|
||||
alpha_long=True,
|
||||
is_release=False,
|
||||
docs=False,
|
||||
starpilot_toggles=SimpleNamespace(),
|
||||
)
|
||||
|
||||
assert not car_params.flags & ToyotaFlags.HYBRID.value
|
||||
assert car_params.longitudinalActuatorDelay == pytest.approx(0.15)
|
||||
assert car_params.vEgoStopping == pytest.approx(0.5)
|
||||
assert car_params.stoppingDecelRate == pytest.approx(0.8)
|
||||
|
||||
controller = get_long_tune(car_params, SimpleNamespace(ACCEL_MIN=-3.5, ACCEL_MAX=2.0))
|
||||
controller.speed = 0.0
|
||||
assert controller.k_i == pytest.approx(3.6)
|
||||
assert controller.k_f == pytest.approx(1.0)
|
||||
|
||||
def test_camry_continental_radar_converts_absolute_target_speed(self):
|
||||
radar_interface = RadarInterface.__new__(RadarInterface)
|
||||
radar_interface.CP = SimpleNamespace(wheelSpeedFactor=1.0)
|
||||
radar_interface.pts = {}
|
||||
radar_interface.track_id = 0
|
||||
radar_interface.RADAR_MSGS = [0x680]
|
||||
radar_interface.pt_cp = SimpleNamespace(vl={
|
||||
"WHEEL_SPEEDS": {
|
||||
"WHEEL_SPEED_FL": 36.0,
|
||||
"WHEEL_SPEED_FR": 36.0,
|
||||
"WHEEL_SPEED_RL": 36.0,
|
||||
"WHEEL_SPEED_RR": 36.0,
|
||||
},
|
||||
})
|
||||
radar_interface.rcp = SimpleNamespace(can_valid=True, vl={
|
||||
0x680: {
|
||||
"ID": 7,
|
||||
"LONG_DIST": 40.0,
|
||||
"LAT_DIST": -0.2,
|
||||
"SPEED": 11.0,
|
||||
"LAT_SPEED": 0.1,
|
||||
},
|
||||
})
|
||||
|
||||
radar_data = radar_interface._update_tssp({0x680})
|
||||
|
||||
assert len(radar_data.points) == 1
|
||||
assert radar_data.points[0].dRel == 40.0
|
||||
assert radar_data.points[0].vRel == pytest.approx(11.0 - 10.0 * TSSP_RADAR_EGO_SPEED_SCALE)
|
||||
|
||||
def test_essential_ecus(self, subtests):
|
||||
# Asserts standard ECUs exist for each platform
|
||||
common_ecus = {Ecu.fwdRadar, Ecu.fwdCamera}
|
||||
@@ -292,6 +497,18 @@ class TestToyotaCarController:
|
||||
assert update_permit_braking(False, 0.10, True, True, 25.0, False) is True
|
||||
assert update_permit_braking(False, 0.10, False, False, 25.0, False) is True
|
||||
|
||||
def test_no_lead_cruise_sign_flip_clamps_negative_pulse_when_set_speed_is_ahead(self):
|
||||
limited = limit_no_lead_cruise_sign_flip(-0.44, 0.0, False, 23.3, 25.0, False)
|
||||
assert limited == 0.0
|
||||
|
||||
def test_no_lead_cruise_sign_flip_keeps_real_decel_requests(self):
|
||||
limited = limit_no_lead_cruise_sign_flip(-0.44, -0.15, False, 23.3, 25.0, False)
|
||||
assert limited == -0.44
|
||||
|
||||
def test_no_lead_cruise_sign_flip_keeps_lead_follow_brake(self):
|
||||
limited = limit_no_lead_cruise_sign_flip(-0.44, 0.0, False, 23.3, 25.0, True)
|
||||
assert limited == -0.44
|
||||
|
||||
def test_prius_stopping_accel_unwinds_stale_stop_hold(self):
|
||||
limited = limit_prius_stopping_accel(-3.28, -0.05, True, 0.0, True)
|
||||
assert -1.5 < limited < 0.0
|
||||
@@ -306,7 +523,15 @@ class TestToyotaCarController:
|
||||
|
||||
def test_prius_positive_feedforward_scale_restores_cruise_authority(self):
|
||||
assert get_prius_positive_feedforward_scale(20.0) > get_prius_positive_feedforward_scale(8.0)
|
||||
assert abs(get_prius_positive_feedforward_scale(20.0) - 0.85) < 1e-6
|
||||
assert abs(get_prius_positive_feedforward_scale(20.0) - 1.0) < 1e-6
|
||||
|
||||
def test_prius_feedforward_adds_braking_authority_without_changing_acceleration(self):
|
||||
assert get_prius_feedforward(-2.0, 8.0) == pytest.approx(-2.25)
|
||||
assert get_prius_feedforward(1.0, 8.0) == pytest.approx(0.7)
|
||||
|
||||
def test_camry_hybrid_feedforward_only_softens_acceleration(self):
|
||||
assert get_camry_hybrid_feedforward(1.0) == pytest.approx(0.8)
|
||||
assert get_camry_hybrid_feedforward(-2.0) == pytest.approx(-2.0)
|
||||
|
||||
def test_sng_hack_clears_existing_standstill_latch(self):
|
||||
controller = self._make_controller(standstill_req=True, last_standstill=True)
|
||||
@@ -344,6 +569,22 @@ class TestToyotaCarController:
|
||||
assert parser.vl["LKAS_HUD"]["LEFT_LINE"] == 0
|
||||
assert parser.vl["LKAS_HUD"]["RIGHT_LINE"] == 0
|
||||
|
||||
def test_acc_control_can_suppress_long_press_behavior_while_gap_button_is_held(self):
|
||||
packer = CANPacker(DBC[CAR.TOYOTA_HIGHLANDER_TSS2][Bus.pt])
|
||||
parser = CANParser(DBC[CAR.TOYOTA_HIGHLANDER_TSS2][Bus.pt], [("ACC_CONTROL", 0)], 0)
|
||||
|
||||
default_msg = toyotacan.create_accel_command(
|
||||
packer, 0.0, False, True, False, False, 1, False, 0, False,
|
||||
)
|
||||
parser.update([(1, [default_msg])])
|
||||
assert parser.vl["ACC_CONTROL"]["ALLOW_LONG_PRESS"] == 1
|
||||
|
||||
suppressed_msg = toyotacan.create_accel_command(
|
||||
packer, 0.0, False, True, False, False, 1, False, 0, False, allow_long_press=0,
|
||||
)
|
||||
parser.update([(1, [suppressed_msg])])
|
||||
assert parser.vl["ACC_CONTROL"]["ALLOW_LONG_PRESS"] == 0
|
||||
|
||||
def test_auto_brake_hold_sends_modified_pre_collision_after_timer(self):
|
||||
controller = self._make_controller()
|
||||
controller.packer = CANPacker(DBC[CAR.TOYOTA_CAMRY_TSS2][Bus.pt])
|
||||
@@ -495,6 +736,29 @@ class TestToyotaCarController:
|
||||
|
||||
|
||||
class TestToyotaCarState:
|
||||
def test_lkas_button_platforms(self):
|
||||
assert CAR.TOYOTA_PRIUS in LKAS_BUTTON_CAR
|
||||
assert TSS2_CAR <= LKAS_BUTTON_CAR
|
||||
assert CAR.TOYOTA_CAMRY not in LKAS_BUTTON_CAR
|
||||
assert CAR.LEXUS_RX not in LKAS_BUTTON_CAR
|
||||
|
||||
@pytest.mark.parametrize("lkas_button,prev_lkas_button,event_count", [
|
||||
(0, 0, 0),
|
||||
(1, 0, 2),
|
||||
(1, 1, 0),
|
||||
(0, 1, 0),
|
||||
(2, 1, 2),
|
||||
])
|
||||
def test_lkas_button_events(self, lkas_button, prev_lkas_button, event_count):
|
||||
events = create_lkas_button_events(lkas_button, prev_lkas_button)
|
||||
|
||||
assert len(events) == event_count
|
||||
if events:
|
||||
assert [(event.type, event.pressed) for event in events] == [
|
||||
(structs.CarState.ButtonEvent.Type.lkas, True),
|
||||
(structs.CarState.ButtonEvent.Type.lkas, False),
|
||||
]
|
||||
|
||||
def test_interceptor_gas_pressed_threshold(self):
|
||||
cp = SimpleNamespace(vl={
|
||||
"GAS_SENSOR": {
|
||||
|
||||
@@ -40,8 +40,12 @@ def create_lta_steer_command_2(packer, frame):
|
||||
return packer.make_can_msg("STEERING_LTA_2", 0, values)
|
||||
|
||||
|
||||
def create_accel_command(packer, accel, pcm_cancel, permit_braking, standstill_req, lead, acc_type, fcw_alert, distance, reverse_cruise_active):
|
||||
def create_accel_command(packer, accel, pcm_cancel, permit_braking, standstill_req, lead, acc_type, fcw_alert,
|
||||
distance, reverse_cruise_active, allow_long_press=None):
|
||||
# TODO: find the exact canceling bit that does not create a chime
|
||||
if allow_long_press is None:
|
||||
allow_long_press = 2 if reverse_cruise_active else 1
|
||||
|
||||
values = {
|
||||
"ACCEL_CMD": accel,
|
||||
"ACC_TYPE": acc_type,
|
||||
@@ -50,7 +54,7 @@ def create_accel_command(packer, accel, pcm_cancel, permit_braking, standstill_r
|
||||
"PERMIT_BRAKING": permit_braking,
|
||||
"RELEASE_STANDSTILL": not standstill_req,
|
||||
"CANCEL_REQ": pcm_cancel,
|
||||
"ALLOW_LONG_PRESS": 2 if reverse_cruise_active else 1,
|
||||
"ALLOW_LONG_PRESS": allow_long_press,
|
||||
"ACC_CUT_IN": fcw_alert, # only shown when ACC enabled
|
||||
}
|
||||
return packer.make_can_msg("ACC_CONTROL", 0, values)
|
||||
|
||||
@@ -58,12 +58,15 @@ class ToyotaSafetyFlags(IntFlag):
|
||||
SECOC = (8 << 8)
|
||||
LONG_FILTER = (16 << 8)
|
||||
GAS_INTERCEPTOR = (32 << 8)
|
||||
ALT_CRUISE = (64 << 8)
|
||||
|
||||
|
||||
class ToyotaFlags(IntFlag):
|
||||
# Detected flags
|
||||
HYBRID = 1
|
||||
DISABLE_RADAR = 4
|
||||
# The DSU's ACC messages are rerouted through the camera bus by an adapter.
|
||||
DSU_BYPASS = 8192
|
||||
|
||||
# Static flags
|
||||
TSS2 = 8
|
||||
@@ -171,7 +174,7 @@ class CAR(Platforms):
|
||||
ToyotaCarDocs("Toyota Camry Hybrid 2018-20", video="https://www.youtube.com/watch?v=Q2DYY0AWKgk"),
|
||||
],
|
||||
CarSpecs(mass=3400. * CV.LB_TO_KG, wheelbase=2.82448, steerRatio=13.7, tireStiffnessFactor=0.7933),
|
||||
dbc_dict('toyota_nodsu_pt_generated', 'toyota_adas'),
|
||||
dbc_dict('toyota_nodsu_pt_generated', 'toyota_radar_dsu_tssp'),
|
||||
flags=ToyotaFlags.NO_DSU,
|
||||
)
|
||||
TOYOTA_CAMRY_TSS2 = ToyotaTSS2PlatformConfig( # TSS 2.5
|
||||
|
||||
@@ -94,6 +94,9 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
if candidate == CAR.PORSCHE_MACAN_MK1:
|
||||
ret.steerActuatorDelay = 0.07
|
||||
elif candidate == CAR.VOLKSWAGEN_TAOS_MK1:
|
||||
# Logged Taos braking response aligns about 0.1 s later than the MQB default.
|
||||
ret.longitudinalActuatorDelay = 0.25
|
||||
|
||||
ret.pcmCruise = not ret.openpilotLongitudinalControl
|
||||
ret.stopAccel = -0.55
|
||||
|
||||
@@ -2,6 +2,7 @@ import random
|
||||
import re
|
||||
|
||||
from opendbc.car.structs import CarParams
|
||||
from opendbc.car.volkswagen.interface import CarInterface
|
||||
from opendbc.car.volkswagen.values import CAR, FW_QUERY_CONFIG, WMI
|
||||
from opendbc.car.volkswagen.fingerprints import FW_VERSIONS
|
||||
|
||||
@@ -13,6 +14,13 @@ SPARE_PART_FW_PATTERN = re.compile(b'\xf1\x87(?P<gateway>[0-9][0-9A-Z]{2})(?P<un
|
||||
|
||||
|
||||
class TestVolkswagenPlatformConfigs:
|
||||
def test_taos_longitudinal_actuator_delay(self):
|
||||
taos_cp = CarInterface.get_non_essential_params(CAR.VOLKSWAGEN_TAOS_MK1)
|
||||
golf_cp = CarInterface.get_non_essential_params(CAR.VOLKSWAGEN_GOLF_MK7)
|
||||
|
||||
assert abs(taos_cp.longitudinalActuatorDelay - 0.25) < 1e-6
|
||||
assert abs(golf_cp.longitudinalActuatorDelay - 0.15) < 1e-6
|
||||
|
||||
def test_spare_part_fw_pattern(self, subtests):
|
||||
# Relied on for determining if a FW is likely VW
|
||||
for platform, ecus in FW_VERSIONS.items():
|
||||
|
||||
@@ -10,6 +10,7 @@ source_files = [
|
||||
for f in Path("generator").rglob("*")
|
||||
if f.is_file() and f.suffix in {".py", ".dbc"}
|
||||
]
|
||||
source_files.append(File("hyundai_kia_generic.dbc"))
|
||||
|
||||
output_files = [
|
||||
f.name.replace(".dbc", "_generated.dbc")
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python3
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
GENERIC_LFAHDA = "BO_ 1157 LFAHDA_MFC: 4 XXX"
|
||||
REFRESH_LFAHDA = "BO_ 1157 LFAHDA_MFC: 8 XXX"
|
||||
|
||||
|
||||
def main() -> None:
|
||||
dbc_dir = Path(__file__).resolve().parents[2]
|
||||
source = dbc_dir / "hyundai_kia_generic.dbc"
|
||||
target = dbc_dir / "hyundai_can_refresh_generated.dbc"
|
||||
|
||||
dbc = source.read_text(encoding="utf-8")
|
||||
if dbc.count(GENERIC_LFAHDA) != 1:
|
||||
raise RuntimeError(f"expected exactly one {GENERIC_LFAHDA!r} definition")
|
||||
|
||||
target.write_text(dbc.replace(GENERIC_LFAHDA, REFRESH_LFAHDA, 1), encoding="utf-8")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -82,18 +82,31 @@ BO_ 261 ACCELERATOR_ALT: 32 XXX
|
||||
BO_ 272 LKAS_ALT: 32 XXX
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ LKA_OptUsmSta : 24|3@1+ (1,0) [0|7] "" ADAS_DRV
|
||||
SG_ LKA_MODE : 24|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ LKA_RcgSta : 27|3@1+ (1,0) [0|7] "" ADAS_DRV
|
||||
SG_ LKA_AVAILABLE : 27|2@1+ (1,0) [0|255] "" XXX
|
||||
SG_ LKA_LHLnWrnSta : 30|2@1+ (1,0) [0|3] "" ADAS_DRV
|
||||
SG_ LKA_RHLnWrnSta : 32|2@1+ (1,0) [0|3] "" ADAS_DRV
|
||||
SG_ LKA_WARNING : 32|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ LKA_HndsoffSnd : 34|2@1+ (1,0) [0|3] "" ADAS_DRV
|
||||
SG_ LKA_StrSnd : 36|2@1+ (1,0) [0|3] "" ADAS_DRV
|
||||
SG_ LKA_SysIndReq : 38|3@1+ (1,0) [0|7] "" ADAS_DRV
|
||||
SG_ LKA_ICON : 38|2@1+ (1,0) [0|255] "" XXX
|
||||
SG_ FCA_SYSWARN : 40|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ StrTqReqVal : 41|11@1+ (1,-1024) [0|4095] "" XXX
|
||||
SG_ TORQUE_REQUEST : 41|11@1+ (1,-1024) [0|4095] "" XXX
|
||||
SG_ ActToiSta : 52|2@1+ (1,0) [0|3] "" ADAS_DRV
|
||||
SG_ STEER_REQ : 52|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ ToiFltSta : 54|2@1+ (1,0) [0|3] "" ADAS_DRV
|
||||
SG_ LFA_BUTTON : 56|1@1+ (1,0) [0|255] "" XXX
|
||||
SG_ LKA_SysWrn : 60|4@1+ (1,0) [0|15] "" ADAS_DRV
|
||||
SG_ LKA_ASSIST : 62|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Damping_Gain : 64|8@1+ (1,0) [0|255] "" CGW
|
||||
SG_ STEER_MODE : 65|3@1+ (1,0) [0|1] "" XXX
|
||||
SG_ NEW_SIGNAL_2 : 70|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ LKAS_ANGLE_ACTIVE : 77|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ LKA_UsmMod : 80|2@1+ (1,0) [0|3] "" ADAS_DRV
|
||||
SG_ HAS_LANE_SAFETY : 80|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ ADAS_StrAnglReqVal : 82|14@1- (0.1,0) [0|176.7] "Deg" GW_RGW,MDPS,SFA
|
||||
SG_ ADAS_ACIAnglTqRedcGainVal : 96|8@1+ (0.004,0) [0|1] "" GW_RGW,MDPS,SFA
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -319,18 +319,31 @@ BO_ 261 ACCELERATOR_ALT: 32 XXX
|
||||
BO_ 272 LKAS_ALT: 32 XXX
|
||||
SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX
|
||||
SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX
|
||||
SG_ LKA_OptUsmSta : 24|3@1+ (1,0) [0|7] "" ADAS_DRV
|
||||
SG_ LKA_MODE : 24|3@1+ (1,0) [0|7] "" XXX
|
||||
SG_ LKA_RcgSta : 27|3@1+ (1,0) [0|7] "" ADAS_DRV
|
||||
SG_ LKA_AVAILABLE : 27|2@1+ (1,0) [0|255] "" XXX
|
||||
SG_ LKA_LHLnWrnSta : 30|2@1+ (1,0) [0|3] "" ADAS_DRV
|
||||
SG_ LKA_RHLnWrnSta : 32|2@1+ (1,0) [0|3] "" ADAS_DRV
|
||||
SG_ LKA_WARNING : 32|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ LKA_HndsoffSnd : 34|2@1+ (1,0) [0|3] "" ADAS_DRV
|
||||
SG_ LKA_StrSnd : 36|2@1+ (1,0) [0|3] "" ADAS_DRV
|
||||
SG_ LKA_SysIndReq : 38|3@1+ (1,0) [0|7] "" ADAS_DRV
|
||||
SG_ LKA_ICON : 38|2@1+ (1,0) [0|255] "" XXX
|
||||
SG_ FCA_SYSWARN : 40|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ StrTqReqVal : 41|11@1+ (1,-1024) [0|4095] "" XXX
|
||||
SG_ TORQUE_REQUEST : 41|11@1+ (1,-1024) [0|4095] "" XXX
|
||||
SG_ ActToiSta : 52|2@1+ (1,0) [0|3] "" ADAS_DRV
|
||||
SG_ STEER_REQ : 52|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ ToiFltSta : 54|2@1+ (1,0) [0|3] "" ADAS_DRV
|
||||
SG_ LFA_BUTTON : 56|1@1+ (1,0) [0|255] "" XXX
|
||||
SG_ LKA_SysWrn : 60|4@1+ (1,0) [0|15] "" ADAS_DRV
|
||||
SG_ LKA_ASSIST : 62|1@1+ (1,0) [0|1] "" XXX
|
||||
SG_ Damping_Gain : 64|8@1+ (1,0) [0|255] "" CGW
|
||||
SG_ STEER_MODE : 65|3@1+ (1,0) [0|1] "" XXX
|
||||
SG_ NEW_SIGNAL_2 : 70|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ LKAS_ANGLE_ACTIVE : 77|2@0+ (1,0) [0|3] "" XXX
|
||||
SG_ LKA_UsmMod : 80|2@1+ (1,0) [0|3] "" ADAS_DRV
|
||||
SG_ HAS_LANE_SAFETY : 80|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ ADAS_StrAnglReqVal : 82|14@1- (0.1,0) [0|176.7] "Deg" GW_RGW,MDPS,SFA
|
||||
SG_ ADAS_ACIAnglTqRedcGainVal : 96|8@1+ (0.004,0) [0|1] "" GW_RGW,MDPS,SFA
|
||||
|
||||
@@ -23,6 +23,7 @@ typedef enum {
|
||||
} ChryslerPlatform;
|
||||
static ChryslerPlatform chrysler_platform;
|
||||
static const ChryslerAddrs *chrysler_addrs;
|
||||
static bool chrysler_jeep_brake_hold;
|
||||
|
||||
static uint32_t chrysler_get_checksum(const CANPacket_t *msg) {
|
||||
int checksum_byte = GET_LEN(msg) - 1U;
|
||||
@@ -153,18 +154,27 @@ static bool chrysler_tx_hook(const CANPacket_t *msg) {
|
||||
if (msg->addr == chrysler_addrs->CRUISE_BUTTONS || msg->addr == chrysler_addrs->CRUISE_BUTTONS_ALT) {
|
||||
const bool is_cancel = msg->data[0] == 1U;
|
||||
const bool is_resume = msg->data[0] == 0x10U;
|
||||
const bool allowed = is_cancel || (is_resume && controls_allowed);
|
||||
const bool allow_resume_standstill = chrysler_jeep_brake_hold && is_resume && acc_main_on &&
|
||||
!vehicle_moving && !gas_pressed && !brake_pressed;
|
||||
const bool allowed = is_cancel || (is_resume && (controls_allowed || allow_resume_standstill));
|
||||
if (!allowed) {
|
||||
tx = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (msg->addr == chrysler_addrs->DAS_3) {
|
||||
if (!chrysler_jeep_brake_hold || !acc_main_on || vehicle_moving || gas_pressed || brake_pressed) {
|
||||
tx = false;
|
||||
}
|
||||
}
|
||||
|
||||
return tx;
|
||||
}
|
||||
|
||||
static safety_config chrysler_init(uint16_t param) {
|
||||
|
||||
const uint32_t CHRYSLER_PARAM_RAM_DT = 1U; // set for Ram DT platform
|
||||
const uint32_t CHRYSLER_PARAM_JEEP_BRAKE_HOLD = 4U;
|
||||
|
||||
// CAN messages for Chrysler/Jeep platforms
|
||||
static const ChryslerAddrs CHRYSLER_ADDRS = {
|
||||
@@ -217,6 +227,7 @@ static safety_config chrysler_init(uint16_t param) {
|
||||
{CHRYSLER_ADDRS.CRUISE_BUTTONS, 0, 3, .check_relay = false},
|
||||
{CHRYSLER_ADDRS.LKAS_COMMAND, 0, 6, .check_relay = true},
|
||||
{CHRYSLER_ADDRS.DAS_6, 0, 8, .check_relay = true},
|
||||
{CHRYSLER_ADDRS.DAS_3, 0, 8, .check_relay = false},
|
||||
|
||||
// RealFast variables
|
||||
{CHRYSLER_ADDRS.CRUISE_BUTTONS_ALT, 2, 3, .check_relay = false},
|
||||
@@ -287,6 +298,7 @@ static safety_config chrysler_init(uint16_t param) {
|
||||
chrysler_addrs = &CHRYSLER_ADDRS;
|
||||
ret = BUILD_SAFETY_CFG(chrysler_rx_checks, CHRYSLER_TX_MSGS);
|
||||
}
|
||||
chrysler_jeep_brake_hold = GET_FLAG(param, CHRYSLER_PARAM_JEEP_BRAKE_HOLD) && (chrysler_platform == CHRYSLER_PACIFICA);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -86,6 +86,8 @@ static bool ford_get_quality_flag_valid(const CANPacket_t *msg) {
|
||||
|
||||
#define FORD_CANFD_INACTIVE_CURVATURE_RATE 1024U
|
||||
|
||||
static bool ford_lka_steering = false;
|
||||
|
||||
// Curvature rate limits
|
||||
#define FORD_LIMITS(limit_lateral_acceleration) { \
|
||||
.max_angle = 1000, /* 0.02 curvature */ \
|
||||
@@ -227,12 +229,10 @@ static bool ford_tx_hook(const CANPacket_t *msg) {
|
||||
|
||||
// Safety check for Lane_Assist_Data1 action
|
||||
if (msg->addr == FORD_Lane_Assist_Data1) {
|
||||
// Do not allow steering using Lane_Assist_Data1 (Lane-Departure Aid).
|
||||
// This message must be sent for Lane Centering to work, and can include
|
||||
// values such as the steering angle or lane curvature for debugging,
|
||||
// but the action (LkaActvStats_D2_Req) must be set to zero.
|
||||
unsigned int action = msg->data[0] >> 5;
|
||||
if (action != 0U) {
|
||||
bool valid_lka_action = action == 0U;
|
||||
valid_lka_action |= ford_lka_steering && controls_allowed && ((action == 2U) || (action == 4U));
|
||||
if (!valid_lka_action) {
|
||||
tx = false;
|
||||
}
|
||||
}
|
||||
@@ -330,7 +330,9 @@ static safety_config ford_init(uint16_t param) {
|
||||
};
|
||||
|
||||
const uint16_t FORD_PARAM_CANFD = 2;
|
||||
const uint16_t FORD_PARAM_LKA_STEERING = 4;
|
||||
const bool ford_canfd = GET_FLAG(param, FORD_PARAM_CANFD);
|
||||
ford_lka_steering = GET_FLAG(param, FORD_PARAM_LKA_STEERING);
|
||||
|
||||
bool ford_longitudinal = false;
|
||||
|
||||
|
||||
@@ -517,13 +517,14 @@ static bool gm_fwd_hook(int bus_num, int addr) {
|
||||
bool is_lkas_msg = addr == 0x180U;
|
||||
bool is_acc_status_msg = addr == 0x370U;
|
||||
bool is_acc_actuation_msg = (addr == 0x315U) || (addr == 0x2CBU);
|
||||
bool is_acc_counter_msg = addr == 0x2CDU;
|
||||
|
||||
block_msg = is_lkas_msg;
|
||||
if (gm_cam_long || gm_pedal_long) {
|
||||
block_msg |= is_acc_status_msg;
|
||||
}
|
||||
if (gm_cam_long) {
|
||||
block_msg |= is_acc_actuation_msg;
|
||||
block_msg |= is_acc_actuation_msg || is_acc_counter_msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -579,8 +580,8 @@ static safety_config gm_init(uint16_t param) {
|
||||
};
|
||||
|
||||
// block PSCMStatus (0x184); forwarded through openpilot to hide an alert from the camera
|
||||
static const CanMsg GM_CAM_LONG_TX_MSGS[] = {{0x180, 0, 4, .check_relay = true}, {0x315, 0, 5, .check_relay = true}, {0x2CB, 0, 8, .check_relay = true}, {0x370, 0, 6, .check_relay = true}, {0x3D1, 0, 8, .check_relay = false}, // pt bus
|
||||
{0x184, 2, 8, .check_relay = true}, // camera bus
|
||||
static const CanMsg GM_CAM_LONG_TX_MSGS[] = {{0x180, 0, 4, .check_relay = true}, {0x315, 0, 5, .check_relay = true}, {0x2CB, 0, 8, .check_relay = true}, {0x2CD, 0, 5, .check_relay = true}, {0x370, 0, 6, .check_relay = true}, {0x3D1, 0, 8, .check_relay = false}, // pt bus
|
||||
{0x184, 2, 8, .check_relay = true}, {0x315, 2, 5, .check_relay = false}, // camera bus (SASCM brake relay)
|
||||
{0x200, 0, 6, .check_relay = false}, {0x1E1, 0, 7, .check_relay = false},
|
||||
{0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; // pt bus
|
||||
|
||||
|
||||
@@ -25,13 +25,13 @@ const LongitudinalLimits HYUNDAI_LONG_LIMITS = {
|
||||
.min_accel = -350, // 1/100 m/s2
|
||||
};
|
||||
|
||||
#define HYUNDAI_COMMON_TX_MSGS(scc_bus) \
|
||||
{0x340, 0, 8, .check_relay = true}, /* LKAS11 Bus 0 */ \
|
||||
{0x4F1, scc_bus, 4, .check_relay = false}, /* CLU11 Bus 0 (radar-SCC) or 2 (camera-SCC) */ \
|
||||
{0x485, 0, 4, .check_relay = true}, /* LFAHDA_MFC Bus 0 */ \
|
||||
#define HYUNDAI_COMMON_TX_MSGS(scc_bus, can_refresh) \
|
||||
{0x340, 0, 8, .check_relay = true}, /* LKAS11 Bus 0 */ \
|
||||
{0x4F1, scc_bus, 4, .check_relay = false}, /* CLU11 Bus 0 (radar-SCC) or 2 (camera-SCC) */ \
|
||||
{0x485, 0, (can_refresh) ? 8 : 4, .check_relay = true}, /* LFAHDA_MFC Bus 0 */ \
|
||||
|
||||
#define HYUNDAI_LONG_COMMON_TX_MSGS(scc_bus) \
|
||||
HYUNDAI_COMMON_TX_MSGS(scc_bus) \
|
||||
#define HYUNDAI_LONG_COMMON_TX_MSGS(scc_bus, can_refresh) \
|
||||
HYUNDAI_COMMON_TX_MSGS(scc_bus, can_refresh) \
|
||||
{0x420, 0, 8, .check_relay = true}, /* SCC11 Bus 0 */ \
|
||||
{0x421, 0, 8, .check_relay = true}, /* SCC12 Bus 0 */ \
|
||||
{0x50A, 0, 8, .check_relay = true}, /* SCC13 Bus 0 */ \
|
||||
@@ -67,11 +67,22 @@ const LongitudinalLimits HYUNDAI_LONG_LIMITS = {
|
||||
{.msg = {{0x592U, 0, 8, 10U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
|
||||
|
||||
static const CanMsg HYUNDAI_TX_MSGS[] = {
|
||||
HYUNDAI_COMMON_TX_MSGS(0)
|
||||
HYUNDAI_COMMON_TX_MSGS(0, false)
|
||||
};
|
||||
|
||||
static const CanMsg HYUNDAI_REFRESH_TX_MSGS[] = {
|
||||
HYUNDAI_COMMON_TX_MSGS(0, true)
|
||||
};
|
||||
|
||||
static const CanMsg HYUNDAI_LONG_TX_MSGS[] = {
|
||||
HYUNDAI_LONG_COMMON_TX_MSGS(0)
|
||||
HYUNDAI_LONG_COMMON_TX_MSGS(0, false)
|
||||
{0x38D, 0, 8, .check_relay = false}, // FCA11 Bus 0
|
||||
{0x483, 0, 8, .check_relay = false}, // FCA12 Bus 0
|
||||
{0x7D0, 0, 8, .check_relay = false}, // radar UDS TX addr Bus 0 (for radar disable)
|
||||
};
|
||||
|
||||
static const CanMsg HYUNDAI_LONG_REFRESH_TX_MSGS[] = {
|
||||
HYUNDAI_LONG_COMMON_TX_MSGS(0, true)
|
||||
{0x38D, 0, 8, .check_relay = false}, // FCA11 Bus 0
|
||||
{0x483, 0, 8, .check_relay = false}, // FCA12 Bus 0
|
||||
{0x7D0, 0, 8, .check_relay = false}, // radar UDS TX addr Bus 0 (for radar disable)
|
||||
@@ -330,11 +341,19 @@ static bool hyundai_tx_hook(const CANPacket_t *msg) {
|
||||
|
||||
static safety_config hyundai_init(uint16_t param) {
|
||||
static const CanMsg HYUNDAI_CAMERA_SCC_TX_MSGS[] = {
|
||||
HYUNDAI_COMMON_TX_MSGS(2)
|
||||
HYUNDAI_COMMON_TX_MSGS(2, false)
|
||||
};
|
||||
|
||||
static const CanMsg HYUNDAI_CAMERA_SCC_REFRESH_TX_MSGS[] = {
|
||||
HYUNDAI_COMMON_TX_MSGS(2, true)
|
||||
};
|
||||
|
||||
static const CanMsg HYUNDAI_CAMERA_SCC_LONG_TX_MSGS[] = {
|
||||
HYUNDAI_LONG_COMMON_TX_MSGS(2)
|
||||
HYUNDAI_LONG_COMMON_TX_MSGS(2, false)
|
||||
};
|
||||
|
||||
static const CanMsg HYUNDAI_CAMERA_SCC_LONG_REFRESH_TX_MSGS[] = {
|
||||
HYUNDAI_LONG_COMMON_TX_MSGS(2, true)
|
||||
};
|
||||
|
||||
static const CanMsg HYUNDAI_CAN_CANFD_BLENDED_TX_MSGS[] = {
|
||||
@@ -403,11 +422,19 @@ static safety_config hyundai_init(uint16_t param) {
|
||||
}
|
||||
}
|
||||
if (hyundai_camera_scc) {
|
||||
SET_TX_MSGS(HYUNDAI_CAMERA_SCC_LONG_TX_MSGS, ret);
|
||||
if (hyundai_can_refresh_msgs) {
|
||||
SET_TX_MSGS(HYUNDAI_CAMERA_SCC_LONG_REFRESH_TX_MSGS, ret);
|
||||
} else {
|
||||
SET_TX_MSGS(HYUNDAI_CAMERA_SCC_LONG_TX_MSGS, ret);
|
||||
}
|
||||
} else if (hyundai_can_canfd_blended) {
|
||||
SET_TX_MSGS(HYUNDAI_CAN_CANFD_BLENDED_LONG_TX_MSGS, ret);
|
||||
} else {
|
||||
SET_TX_MSGS(HYUNDAI_LONG_TX_MSGS, ret);
|
||||
if (hyundai_can_refresh_msgs) {
|
||||
SET_TX_MSGS(HYUNDAI_LONG_REFRESH_TX_MSGS, ret);
|
||||
} else {
|
||||
SET_TX_MSGS(HYUNDAI_LONG_TX_MSGS, ret);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (hyundai_camera_scc) {
|
||||
@@ -425,9 +452,14 @@ static safety_config hyundai_init(uint16_t param) {
|
||||
};
|
||||
|
||||
if (hyundai_has_lda_button) {
|
||||
ret = BUILD_SAFETY_CFG(hyundai_cam_scc_rx_checks_lda, HYUNDAI_CAMERA_SCC_TX_MSGS);
|
||||
SET_RX_CHECKS(hyundai_cam_scc_rx_checks_lda, ret);
|
||||
} else {
|
||||
ret = BUILD_SAFETY_CFG(hyundai_cam_scc_rx_checks, HYUNDAI_CAMERA_SCC_TX_MSGS);
|
||||
SET_RX_CHECKS(hyundai_cam_scc_rx_checks, ret);
|
||||
}
|
||||
if (hyundai_can_refresh_msgs) {
|
||||
SET_TX_MSGS(HYUNDAI_CAMERA_SCC_REFRESH_TX_MSGS, ret);
|
||||
} else {
|
||||
SET_TX_MSGS(HYUNDAI_CAMERA_SCC_TX_MSGS, ret);
|
||||
}
|
||||
} else if (hyundai_can_canfd_blended) {
|
||||
static RxCheck hyundai_can_canfd_blended_rx_checks[] = {
|
||||
@@ -509,7 +541,11 @@ static safety_config hyundai_init(uint16_t param) {
|
||||
HYUNDAI_LDA_BUTTON_ADDR_CHECK
|
||||
};
|
||||
|
||||
SET_TX_MSGS(HYUNDAI_TX_MSGS, ret);
|
||||
if (hyundai_can_refresh_msgs) {
|
||||
SET_TX_MSGS(HYUNDAI_REFRESH_TX_MSGS, ret);
|
||||
} else {
|
||||
SET_TX_MSGS(HYUNDAI_TX_MSGS, ret);
|
||||
}
|
||||
if (hyundai_fcev_gas_signal) {
|
||||
if (hyundai_has_lda_button) {
|
||||
SET_RX_CHECKS(hyundai_fcev_rx_checks_lda, ret);
|
||||
@@ -557,6 +593,7 @@ static safety_config hyundai_legacy_init(uint16_t param) {
|
||||
hyundai_common_init(param);
|
||||
hyundai_legacy = true;
|
||||
hyundai_camera_scc = false;
|
||||
hyundai_can_refresh_msgs = false;
|
||||
return hyundai_longitudinal ? BUILD_SAFETY_CFG(hyundai_legacy_rx_checks, HYUNDAI_LONG_TX_MSGS) :
|
||||
BUILD_SAFETY_CFG(hyundai_legacy_rx_checks, HYUNDAI_TX_MSGS);
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
|
||||
#define HYUNDAI_CANFD_LKA_STEERING_ALT_COMMON_TX_MSGS(a_can, e_can) \
|
||||
HYUNDAI_CANFD_CRUISE_BUTTON_TX_MSGS(e_can) \
|
||||
{0x110, a_can, 32, .check_relay = (a_can) == 0}, /* LKAS_ALT */ \
|
||||
{0x362, a_can, 32, .check_relay = (a_can) == 0}, /* CAM_0x362 */ \
|
||||
{0x110, a_can, 32, .check_relay = (a_can) == 0, .disable_static_blocking = true}, /* LKAS_ALT */ \
|
||||
{0x362, a_can, 32, .check_relay = (a_can) == 0, .disable_static_blocking = true}, /* CAM_0x362 */ \
|
||||
|
||||
#define HYUNDAI_CANFD_LFA_STEERING_COMMON_TX_MSGS(e_can) \
|
||||
{0x12A, e_can, 16, .check_relay = (e_can) == 0}, /* LFA */ \
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#define HYUNDAI_CANFD_MRR35_RADAR_TRACK_START 0x3A5
|
||||
#define HYUNDAI_CANFD_MRR35_RADAR_TRACK_END 0x3C4
|
||||
#define HYUNDAI_CANFD_INACTIVE_ACCEL_TX_THRESHOLD 10U
|
||||
|
||||
#define HYUNDAI_CANFD_BLINDSPOT_DASH_TX_MSGS(e_can) \
|
||||
{0x1BA, e_can, 24, .check_relay = false}, /* BLINDSPOTS_REAR_CORNERS */ \
|
||||
@@ -60,6 +61,9 @@ static bool hyundai_canfd_alt_buttons = false;
|
||||
static bool hyundai_canfd_lka_steering_alt = false;
|
||||
static bool hyundai_canfd_angle_steering = false;
|
||||
static bool hyundai_ccnc = false;
|
||||
static bool hyundai_canfd_ccnc_angle_long = false;
|
||||
static bool hyundai_canfd_lka_alt_drive_gear = false;
|
||||
static uint8_t hyundai_canfd_inactive_accel_tx_count = 0U;
|
||||
|
||||
static unsigned int hyundai_canfd_get_lka_addr(void) {
|
||||
return hyundai_canfd_lka_steering_alt ? 0x110U : 0x50U;
|
||||
@@ -80,6 +84,20 @@ static uint32_t hyundai_canfd_get_checksum(const CANPacket_t *msg) {
|
||||
return chksum;
|
||||
}
|
||||
|
||||
static bool hyundai_canfd_lka_alt_forward_addr(int addr) {
|
||||
return (addr == 0x110) || (addr == 0x362);
|
||||
}
|
||||
|
||||
static bool hyundai_canfd_lka_alt_openpilot_allowed(void) {
|
||||
const bool angle_steering_allowed = !hyundai_canfd_angle_steering || vehicle_moving;
|
||||
return (aol_allowed || controls_allowed) && angle_steering_allowed &&
|
||||
(!hyundai_ev_gas_signal || hyundai_canfd_lka_alt_drive_gear);
|
||||
}
|
||||
|
||||
static bool hyundai_canfd_lka_alt_stock_forwarding(void) {
|
||||
return hyundai_canfd_lka_steering_alt && hyundai_canfd_angle_steering && !hyundai_canfd_lka_alt_openpilot_allowed();
|
||||
}
|
||||
|
||||
static void hyundai_canfd_rx_all_hook(const CANPacket_t *msg) {
|
||||
SAFETY_UNUSED(msg);
|
||||
}
|
||||
@@ -87,6 +105,10 @@ static void hyundai_canfd_rx_all_hook(const CANPacket_t *msg) {
|
||||
static bool hyundai_canfd_fwd_hook(int bus_num, int addr) {
|
||||
const bool mrr35_radar_track = (addr >= HYUNDAI_CANFD_MRR35_RADAR_TRACK_START) && (addr <= HYUNDAI_CANFD_MRR35_RADAR_TRACK_END);
|
||||
|
||||
if ((bus_num == 2) && hyundai_canfd_lka_steering_alt && hyundai_canfd_lka_alt_forward_addr(addr)) {
|
||||
return !hyundai_canfd_lka_alt_stock_forwarding();
|
||||
}
|
||||
|
||||
// On LKA-steering long-control cars using live MRR35 radar tracks, openpilot parses
|
||||
// the tracks directly from bus 0. Forwarding them to bus 2 creates a returned TX copy
|
||||
// of every object frame on the logged CAN stream without adding planner data.
|
||||
@@ -105,7 +127,9 @@ static void hyundai_canfd_rx_hook(const CANPacket_t *msg) {
|
||||
torque_driver_new -= 4095;
|
||||
update_sample(&torque_driver, torque_driver_new);
|
||||
|
||||
int angle_meas_new = (msg->data[13] << 8U) | msg->data[12];
|
||||
// CCNC angle-long platforms publish the usable angle in STEERING_ANGLE_2.
|
||||
const unsigned int angle_offset = hyundai_canfd_ccnc_angle_long ? 16U : 12U;
|
||||
int angle_meas_new = (msg->data[angle_offset + 1U] << 8U) | msg->data[angle_offset];
|
||||
angle_meas_new = to_signed(angle_meas_new, 16);
|
||||
update_sample(&angle_meas, angle_meas_new);
|
||||
}
|
||||
@@ -113,6 +137,7 @@ static void hyundai_canfd_rx_hook(const CANPacket_t *msg) {
|
||||
// cruise buttons
|
||||
const unsigned int button_addr = hyundai_canfd_alt_buttons ? 0x1aaU : 0x1cfU;
|
||||
if (msg->addr == button_addr) {
|
||||
const bool controls_allowed_prev = controls_allowed;
|
||||
bool main_button = false;
|
||||
int cruise_button = 0;
|
||||
if (msg->addr == 0x1cfU) {
|
||||
@@ -127,11 +152,15 @@ static void hyundai_canfd_rx_hook(const CANPacket_t *msg) {
|
||||
hyundai_lkas_button_check(GET_BIT(msg, 39U));
|
||||
}
|
||||
hyundai_common_cruise_buttons_check(cruise_button, main_button);
|
||||
if (!controls_allowed_prev && controls_allowed) {
|
||||
hyundai_canfd_inactive_accel_tx_count = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
// gas press, different for EV, hybrid, and ICE models
|
||||
if ((msg->addr == 0x35U) && hyundai_ev_gas_signal) {
|
||||
gas_pressed = msg->data[5] != 0U;
|
||||
hyundai_canfd_lka_alt_drive_gear = (msg->data[24] & 0x7U) == 5U;
|
||||
} else if ((msg->addr == 0x105U) && hyundai_hybrid_gas_signal) {
|
||||
gas_pressed = GET_BIT(msg, 103U) || (msg->data[13] != 0U) || GET_BIT(msg, 112U);
|
||||
} else if ((msg->addr == 0x100U) && !hyundai_ev_gas_signal && !hyundai_hybrid_gas_signal) {
|
||||
@@ -202,6 +231,10 @@ static bool hyundai_canfd_tx_hook(const CANPacket_t *msg) {
|
||||
|
||||
bool tx = true;
|
||||
|
||||
if ((msg->bus == 0U) && hyundai_canfd_lka_alt_forward_addr(msg->addr) && hyundai_canfd_lka_alt_stock_forwarding()) {
|
||||
tx = false;
|
||||
}
|
||||
|
||||
if (msg->addr == 0xCBU) {
|
||||
if (!hyundai_canfd_angle_steering) {
|
||||
tx = false;
|
||||
@@ -209,6 +242,10 @@ static bool hyundai_canfd_tx_hook(const CANPacket_t *msg) {
|
||||
const int lfa_angle_active = (msg->data[3] >> 4U) & 0xFU;
|
||||
const bool steer_angle_req = lfa_angle_active == 2;
|
||||
|
||||
if (steer_angle_req && hyundai_canfd_ccnc_angle_long && !hyundai_canfd_lka_alt_openpilot_allowed()) {
|
||||
tx = false;
|
||||
}
|
||||
|
||||
int desired_angle = (((uint32_t)(msg->data[5] & 0x3FU)) << 8) | (uint32_t)msg->data[4];
|
||||
desired_angle = to_signed(desired_angle, 14);
|
||||
|
||||
@@ -281,6 +318,18 @@ static bool hyundai_canfd_tx_hook(const CANPacket_t *msg) {
|
||||
bool violation = false;
|
||||
|
||||
if (hyundai_longitudinal) {
|
||||
const int acc_mode = (msg->data[8] >> 4) & 0x7U;
|
||||
const bool inactive_accel = (acc_mode == 0) && (desired_accel_raw == 0) && (desired_accel_val == 0);
|
||||
if (inactive_accel) {
|
||||
hyundai_canfd_inactive_accel_tx_count = SAFETY_MIN(hyundai_canfd_inactive_accel_tx_count + 1U,
|
||||
HYUNDAI_CANFD_INACTIVE_ACCEL_TX_THRESHOLD);
|
||||
if (hyundai_canfd_inactive_accel_tx_count >= HYUNDAI_CANFD_INACTIVE_ACCEL_TX_THRESHOLD) {
|
||||
controls_allowed = false;
|
||||
}
|
||||
} else {
|
||||
hyundai_canfd_inactive_accel_tx_count = 0U;
|
||||
}
|
||||
|
||||
violation |= longitudinal_accel_checks(desired_accel_raw, HYUNDAI_LONG_LIMITS);
|
||||
violation |= longitudinal_accel_checks(desired_accel_val, HYUNDAI_LONG_LIMITS);
|
||||
} else {
|
||||
@@ -320,6 +369,16 @@ static safety_config hyundai_canfd_init(uint16_t param) {
|
||||
HYUNDAI_CANFD_LKA_STEERING_ALT_COMMON_TX_MSGS(0, 1)
|
||||
};
|
||||
|
||||
static const CanMsg HYUNDAI_CANFD_LKA_STEERING_ALT_BUTTONS_TX_MSGS[] = {
|
||||
HYUNDAI_CANFD_LKA_STEERING_COMMON_TX_MSGS(0, 1)
|
||||
HYUNDAI_CANFD_SCC_CONTROL_COMMON_TX_MSGS(1, false)
|
||||
};
|
||||
|
||||
static const CanMsg HYUNDAI_CANFD_LKA_STEERING_ALT_ALT_BUTTONS_TX_MSGS[] = {
|
||||
HYUNDAI_CANFD_LKA_STEERING_ALT_COMMON_TX_MSGS(0, 1)
|
||||
HYUNDAI_CANFD_SCC_CONTROL_COMMON_TX_MSGS(1, false)
|
||||
};
|
||||
|
||||
static const CanMsg HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS[] = {
|
||||
HYUNDAI_CANFD_LKA_STEERING_COMMON_TX_MSGS(0, 1)
|
||||
HYUNDAI_CANFD_LFA_STEERING_COMMON_TX_MSGS(1)
|
||||
@@ -350,6 +409,24 @@ static safety_config hyundai_canfd_init(uint16_t param) {
|
||||
{0x1DA, 1, 32, .check_relay = false}, // ADRV_0x1da
|
||||
};
|
||||
|
||||
static const CanMsg HYUNDAI_CANFD_CCNC_ANGLE_LONG_TX_MSGS[] = {
|
||||
HYUNDAI_CANFD_LKA_STEERING_ALT_COMMON_TX_MSGS(0, 1)
|
||||
HYUNDAI_CANFD_LFA_STEERING_COMMON_TX_MSGS(1)
|
||||
HYUNDAI_CANFD_SCC_CONTROL_COMMON_TX_MSGS(1, true)
|
||||
{0x1BA, 1, 24, .check_relay = false}, // BLINDSPOTS_REAR_CORNERS
|
||||
{0x1E5, 1, 16, .check_relay = false}, // BLINDSPOTS_FRONT_CORNER_1
|
||||
{0x100, 0, 24, .check_relay = false}, // ACCELERATOR_BRAKE_ALT radar heartbeat
|
||||
{0x730, 1, 8, .check_relay = false}, // tester present for ADAS ECU disable
|
||||
{0x160, 1, 16, .check_relay = false}, // ADRV_0x160
|
||||
{0x161, 1, 32, .check_relay = false}, // CCNC_0x161
|
||||
{0x162, 1, 32, .check_relay = false}, // CCNC_0x162
|
||||
{0x1EA, 1, 32, .check_relay = false}, // ADRV_0x1ea
|
||||
{0x200, 1, 8, .check_relay = false}, // ADRV_0x200
|
||||
{0x345, 1, 8, .check_relay = false}, // ADRV_0x345
|
||||
{0x38C, 1, 32, .check_relay = false}, // CCNC support frame
|
||||
{0x1DA, 1, 32, .check_relay = false}, // ADRV_0x1da
|
||||
};
|
||||
|
||||
static const CanMsg HYUNDAI_CANFD_LFA_STEERING_TX_MSGS[] = {
|
||||
HYUNDAI_CANFD_CRUISE_BUTTON_TX_MSGS(2)
|
||||
HYUNDAI_CANFD_LFA_STEERING_COMMON_TX_MSGS(0)
|
||||
@@ -389,6 +466,10 @@ static safety_config hyundai_canfd_init(uint16_t param) {
|
||||
hyundai_canfd_lka_steering_alt = GET_FLAG(param, HYUNDAI_PARAM_CANFD_LKA_STEERING_ALT);
|
||||
hyundai_canfd_angle_steering = GET_FLAG(param, HYUNDAI_PARAM_CANFD_ANGLE_STEERING);
|
||||
hyundai_ccnc = GET_FLAG(param, HYUNDAI_PARAM_CCNC);
|
||||
hyundai_canfd_ccnc_angle_long = hyundai_longitudinal && hyundai_canfd_lka_steering &&
|
||||
hyundai_canfd_lka_steering_alt && hyundai_canfd_angle_steering && hyundai_ccnc;
|
||||
hyundai_canfd_lka_alt_drive_gear = false;
|
||||
hyundai_canfd_inactive_accel_tx_count = 0U;
|
||||
|
||||
safety_config ret;
|
||||
if (hyundai_longitudinal) {
|
||||
@@ -397,8 +478,18 @@ static safety_config hyundai_canfd_init(uint16_t param) {
|
||||
HYUNDAI_CANFD_STD_BUTTONS_RX_CHECKS(1)
|
||||
};
|
||||
|
||||
SET_RX_CHECKS(hyundai_canfd_lka_steering_long_rx_checks, ret);
|
||||
if (hyundai_canfd_lka_steering_alt) {
|
||||
static RxCheck hyundai_canfd_lka_steering_alt_buttons_long_rx_checks[] = {
|
||||
HYUNDAI_CANFD_ALT_BUTTONS_RX_CHECKS(1)
|
||||
};
|
||||
|
||||
if (hyundai_canfd_alt_buttons) {
|
||||
SET_RX_CHECKS(hyundai_canfd_lka_steering_alt_buttons_long_rx_checks, ret);
|
||||
} else {
|
||||
SET_RX_CHECKS(hyundai_canfd_lka_steering_long_rx_checks, ret);
|
||||
}
|
||||
if (hyundai_canfd_ccnc_angle_long) {
|
||||
SET_TX_MSGS(HYUNDAI_CANFD_CCNC_ANGLE_LONG_TX_MSGS, ret);
|
||||
} else if (hyundai_canfd_lka_steering_alt) {
|
||||
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_LONG_TX_MSGS, ret);
|
||||
} else {
|
||||
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_LONG_TX_MSGS, ret);
|
||||
@@ -444,17 +535,33 @@ static safety_config hyundai_canfd_init(uint16_t param) {
|
||||
if (hyundai_canfd_lka_steering) {
|
||||
// *** LKA steering checks ***
|
||||
// E-CAN is on bus 1, SCC messages are sent on cars with ADRV ECU.
|
||||
// Does not use the alt buttons message
|
||||
static RxCheck hyundai_canfd_lka_steering_rx_checks[] = {
|
||||
HYUNDAI_CANFD_STD_BUTTONS_RX_CHECKS(1)
|
||||
HYUNDAI_CANFD_SCC_ADDR_CHECK(1)
|
||||
};
|
||||
|
||||
SET_RX_CHECKS(hyundai_canfd_lka_steering_rx_checks, ret);
|
||||
if (hyundai_canfd_lka_steering_alt) {
|
||||
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_TX_MSGS, ret);
|
||||
static RxCheck hyundai_canfd_lka_steering_alt_buttons_rx_checks[] = {
|
||||
HYUNDAI_CANFD_ALT_BUTTONS_RX_CHECKS(1)
|
||||
HYUNDAI_CANFD_SCC_ADDR_CHECK(1)
|
||||
};
|
||||
|
||||
if (hyundai_canfd_alt_buttons) {
|
||||
SET_RX_CHECKS(hyundai_canfd_lka_steering_alt_buttons_rx_checks, ret);
|
||||
} else {
|
||||
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_TX_MSGS, ret);
|
||||
SET_RX_CHECKS(hyundai_canfd_lka_steering_rx_checks, ret);
|
||||
}
|
||||
if (hyundai_canfd_lka_steering_alt) {
|
||||
if (hyundai_canfd_alt_buttons) {
|
||||
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_ALT_BUTTONS_TX_MSGS, ret);
|
||||
} else {
|
||||
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_TX_MSGS, ret);
|
||||
}
|
||||
} else {
|
||||
if (hyundai_canfd_alt_buttons) {
|
||||
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_ALT_BUTTONS_TX_MSGS, ret);
|
||||
} else {
|
||||
SET_TX_MSGS(HYUNDAI_CANFD_LKA_STEERING_TX_MSGS, ret);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (!hyundai_camera_scc) {
|
||||
|
||||
@@ -57,6 +57,9 @@ bool hyundai_non_scc = false;
|
||||
extern bool hyundai_cancel_button_enable;
|
||||
bool hyundai_cancel_button_enable = false;
|
||||
|
||||
extern bool hyundai_can_refresh_msgs;
|
||||
bool hyundai_can_refresh_msgs = false;
|
||||
|
||||
static uint8_t hyundai_last_button_interaction; // button messages since the user pressed an enable button
|
||||
static bool acc_main_on_prev;
|
||||
static bool acc_main_on_tx;
|
||||
@@ -76,6 +79,7 @@ void hyundai_common_init(uint16_t param) {
|
||||
const uint16_t HYUNDAI_PARAM_NON_SCC = 4096;
|
||||
const uint16_t HYUNDAI_PARAM_CAN_CANFD_BLENDED = 8192;
|
||||
const uint16_t HYUNDAI_PARAM_CANCEL_BTN_ENABLE = 16384;
|
||||
const uint16_t HYUNDAI_PARAM_CAN_REFRESH_MSGS = 32768;
|
||||
|
||||
hyundai_ev_gas_signal = GET_FLAG(param, HYUNDAI_PARAM_EV_GAS);
|
||||
hyundai_hybrid_gas_signal = !hyundai_ev_gas_signal && GET_FLAG(param, HYUNDAI_PARAM_HYBRID_GAS);
|
||||
@@ -90,6 +94,7 @@ void hyundai_common_init(uint16_t param) {
|
||||
hyundai_aol_lkas_on_engage = GET_FLAG(param, HYUNDAI_PARAM_AOL_LKAS_ON_ENGAGE);
|
||||
hyundai_non_scc = GET_FLAG(param, HYUNDAI_PARAM_NON_SCC);
|
||||
hyundai_cancel_button_enable = GET_FLAG(param, HYUNDAI_PARAM_CANCEL_BTN_ENABLE);
|
||||
hyundai_can_refresh_msgs = GET_FLAG(param, HYUNDAI_PARAM_CAN_REFRESH_MSGS);
|
||||
|
||||
hyundai_last_button_interaction = HYUNDAI_PREV_BUTTON_SAMPLES;
|
||||
acc_main_on_prev = false;
|
||||
@@ -199,7 +204,6 @@ void hyundai_common_acc_main_on_sync(void) {
|
||||
|
||||
if (acc_main_on_mismatches >= 3U) {
|
||||
acc_main_on = false;
|
||||
lkas_on = false;
|
||||
}
|
||||
} else {
|
||||
acc_main_on_mismatches = 0U;
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
|
||||
static bool nissan_alt_eps = false;
|
||||
|
||||
static void nissan_rx_all_hook(const CANPacket_t *msg) {
|
||||
if ((msg->addr == 0x1B6U) && (msg->bus == (nissan_alt_eps ? 2U : 1U))) {
|
||||
acc_main_on = GET_BIT(msg, 36U);
|
||||
}
|
||||
}
|
||||
|
||||
static void nissan_rx_hook(const CANPacket_t *msg) {
|
||||
|
||||
if (msg->bus == (nissan_alt_eps ? 1U : 0U)) {
|
||||
@@ -51,10 +57,6 @@ static void nissan_rx_hook(const CANPacket_t *msg) {
|
||||
pcm_cruise_check(cruise_engaged);
|
||||
}
|
||||
|
||||
if ((msg->addr == 0x1B6U) && (msg->bus == (nissan_alt_eps ? 2U : 1U))) {
|
||||
acc_main_on = GET_BIT(msg, 36U);
|
||||
}
|
||||
|
||||
if ((msg->addr == 0x239U) && (msg->bus == 0U)) {
|
||||
acc_main_on = GET_BIT(msg, 17U);
|
||||
}
|
||||
@@ -140,6 +142,7 @@ static safety_config nissan_init(uint16_t param) {
|
||||
|
||||
const safety_hooks nissan_hooks = {
|
||||
.init = nissan_init,
|
||||
.rx_all = nissan_rx_all_hook,
|
||||
.rx = nissan_rx_hook,
|
||||
.tx = nissan_tx_hook,
|
||||
};
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
#include "opendbc/safety/declarations.h"
|
||||
|
||||
static bool tesla_longitudinal = false;
|
||||
static bool tesla_coop_steering = false;
|
||||
static bool tesla_stock_aeb = false;
|
||||
|
||||
#define TESLA_STEERING_DISENGAGE_TORQUE 500 // cNm
|
||||
|
||||
// Only rising edges while controls are not allowed are considered for these systems:
|
||||
// TODO: Only LKAS (non-emergency) is currently supported since we've only seen it
|
||||
static bool tesla_stock_lkas = false;
|
||||
@@ -101,11 +104,14 @@ static void tesla_rx_hook(const CANPacket_t *msg) {
|
||||
update_sample(&angle_meas, angle_meas_new);
|
||||
|
||||
const int hands_on_level = msg->data[4] >> 6; // EPAS3S_handsOnLevel
|
||||
const int torsion_bar_torque = (((msg->data[2] & 0x0FU) << 8) | msg->data[3]) - 2050; // 0.01 Nm
|
||||
const int eac_status = msg->data[6] >> 5; // EPAS3S_eacStatus
|
||||
const int eac_error_code = msg->data[2] >> 4; // EPAS3S_eacErrorCode
|
||||
|
||||
// Disengage on normal user override, or if high angle rate fault from user overriding extremely quickly
|
||||
steering_disengage = (hands_on_level >= 3) || ((eac_status == 0) && (eac_error_code == 9));
|
||||
steering_disengage = (hands_on_level >= 3) ||
|
||||
(tesla_coop_steering && (SAFETY_ABS(torsion_bar_torque) > TESLA_STEERING_DISENGAGE_TORQUE)) ||
|
||||
((eac_status == 0) && (eac_error_code == 9));
|
||||
}
|
||||
|
||||
// Vehicle speed (DI_speed)
|
||||
@@ -333,7 +339,11 @@ static safety_config tesla_init(uint16_t param) {
|
||||
SAFETY_UNUSED(param);
|
||||
#ifdef ALLOW_DEBUG
|
||||
const uint16_t TESLA_FLAG_LONGITUDINAL_CONTROL = 1;
|
||||
const uint16_t TESLA_FLAG_COOP_STEERING = 256;
|
||||
tesla_longitudinal = GET_FLAG(param, TESLA_FLAG_LONGITUDINAL_CONTROL);
|
||||
tesla_coop_steering = GET_FLAG(param, TESLA_FLAG_COOP_STEERING);
|
||||
#else
|
||||
tesla_coop_steering = false;
|
||||
#endif
|
||||
|
||||
tesla_stock_aeb = false;
|
||||
|
||||
@@ -50,21 +50,36 @@
|
||||
#define TOYOTA_COMMON_RX_CHECKS(lta) \
|
||||
{.msg = {{ 0xaa, 0, 8, 83U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
|
||||
{.msg = {{0x260, 0, 8, 50U, .ignore_counter = true, .ignore_quality_flag=!(lta)}, { 0 }, { 0 }}}, \
|
||||
/* StarPilot Variables */ \
|
||||
|
||||
#define TOYOTA_CRUISE_RX_CHECK \
|
||||
{.msg = {{0x1D3, 0, 8, 33U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
|
||||
|
||||
#define TOYOTA_ALT_CRUISE_RX_CHECK \
|
||||
{.msg = {{0x1D3, 0, 8, 33U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, \
|
||||
{0x1D3, 0, 5, 33U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, \
|
||||
{0x365, 0, 7, 10U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}}}, \
|
||||
|
||||
#define TOYOTA_RX_CHECKS(lta) \
|
||||
TOYOTA_COMMON_RX_CHECKS(lta) \
|
||||
TOYOTA_CRUISE_RX_CHECK \
|
||||
{.msg = {{0x1D2, 0, 8, 33U, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
|
||||
{.msg = {{0x226, 0, 8, 40U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
|
||||
|
||||
#define TOYOTA_ALT_CRUISE_RX_CHECKS(lta) \
|
||||
TOYOTA_COMMON_RX_CHECKS(lta) \
|
||||
TOYOTA_ALT_CRUISE_RX_CHECK \
|
||||
{.msg = {{0x1D2, 0, 8, 33U, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
|
||||
{.msg = {{0x226, 0, 8, 40U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
|
||||
|
||||
#define TOYOTA_ALT_BRAKE_RX_CHECKS(lta) \
|
||||
TOYOTA_COMMON_RX_CHECKS(lta) \
|
||||
TOYOTA_CRUISE_RX_CHECK \
|
||||
{.msg = {{0x1D2, 0, 8, 33U, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
|
||||
{.msg = {{0x224, 0, 8, 40U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
|
||||
|
||||
#define TOYOTA_SECOC_RX_CHECKS \
|
||||
TOYOTA_COMMON_RX_CHECKS(false) \
|
||||
TOYOTA_CRUISE_RX_CHECK \
|
||||
{.msg = {{0x176, 0, 8, 32U, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
|
||||
{.msg = {{0x116, 0, 8, 42U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
|
||||
{.msg = {{0x101, 0, 8, 50U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
|
||||
@@ -78,6 +93,7 @@ static bool toyota_stock_longitudinal = false;
|
||||
static bool toyota_lta = false;
|
||||
static int toyota_dbc_eps_torque_factor = 100; // conversion factor for STEER_TORQUE_EPS in %: see dbc file
|
||||
static bool toyota_long_filter = false;
|
||||
static bool toyota_alt_cruise = false;
|
||||
|
||||
static uint32_t toyota_compute_checksum(const CANPacket_t *msg) {
|
||||
int len = GET_LEN(msg);
|
||||
@@ -108,6 +124,12 @@ static bool toyota_get_quality_flag_valid(const CANPacket_t *msg) {
|
||||
return valid;
|
||||
}
|
||||
|
||||
static void toyota_rx_all_hook(const CANPacket_t *msg) {
|
||||
if (toyota_alt_cruise && (msg->bus == 0U) && (msg->addr == 0x365U) && (GET_LEN(msg) == 7U)) {
|
||||
acc_main_on = GET_BIT(msg, 0U); // DSU_CRUISE.MAIN_ON
|
||||
}
|
||||
}
|
||||
|
||||
static void toyota_rx_hook(const CANPacket_t *msg) {
|
||||
if (msg->bus == 0U) {
|
||||
|
||||
@@ -185,14 +207,10 @@ static void toyota_rx_hook(const CANPacket_t *msg) {
|
||||
UPDATE_VEHICLE_SPEED(speed / 4.0 * 0.01 * KPH_TO_MS);
|
||||
}
|
||||
|
||||
if (msg->addr == 0x1D3U) {
|
||||
if ((msg->addr == 0x1D3U) && (GET_LEN(msg) == 8U)) {
|
||||
acc_main_on = GET_BIT(msg, 15U);
|
||||
}
|
||||
|
||||
if (msg->addr == 0x365U) {
|
||||
acc_main_on = GET_BIT(msg, 0U);
|
||||
}
|
||||
|
||||
if (enable_gas_interceptor && (msg->addr == 0x201U)) {
|
||||
// Match the DBC's physical pedal threshold to avoid controls state mismatches.
|
||||
const int toyota_gas_interceptor_threshold = 805;
|
||||
@@ -437,6 +455,7 @@ static safety_config toyota_init(uint16_t param) {
|
||||
const uint32_t TOYOTA_PARAM_LTA = 4UL << TOYOTA_PARAM_OFFSET;
|
||||
const uint32_t TOYOTA_PARAM_LONG_FILTER = 16UL << TOYOTA_PARAM_OFFSET;
|
||||
const uint32_t TOYOTA_PARAM_GAS_INTERCEPTOR = 32UL << TOYOTA_PARAM_OFFSET;
|
||||
const uint32_t TOYOTA_PARAM_ALT_CRUISE = 64UL << TOYOTA_PARAM_OFFSET;
|
||||
|
||||
#ifdef ALLOW_DEBUG
|
||||
const uint32_t TOYOTA_PARAM_SECOC = 8UL << TOYOTA_PARAM_OFFSET;
|
||||
@@ -448,6 +467,7 @@ static safety_config toyota_init(uint16_t param) {
|
||||
toyota_lta = GET_FLAG(param, TOYOTA_PARAM_LTA);
|
||||
toyota_long_filter = GET_FLAG(param, TOYOTA_PARAM_LONG_FILTER);
|
||||
enable_gas_interceptor = GET_FLAG(param, TOYOTA_PARAM_GAS_INTERCEPTOR);
|
||||
toyota_alt_cruise = GET_FLAG(param, TOYOTA_PARAM_ALT_CRUISE);
|
||||
toyota_dbc_eps_torque_factor = param & TOYOTA_EPS_FACTOR;
|
||||
|
||||
if (toyota_stock_longitudinal || toyota_secoc) {
|
||||
@@ -515,8 +535,19 @@ static safety_config toyota_init(uint16_t param) {
|
||||
TOYOTA_ALT_BRAKE_RX_CHECKS(false)
|
||||
TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK
|
||||
};
|
||||
static RxCheck toyota_lka_alt_cruise_rx_checks[] = {
|
||||
TOYOTA_ALT_CRUISE_RX_CHECKS(false)
|
||||
};
|
||||
static RxCheck toyota_lka_alt_cruise_interceptor_rx_checks[] = {
|
||||
TOYOTA_ALT_CRUISE_RX_CHECKS(false)
|
||||
TOYOTA_GAS_INTERCEPTOR_ADDR_CHECK
|
||||
};
|
||||
|
||||
if (enable_gas_interceptor && !toyota_alt_brake) {
|
||||
if (toyota_alt_cruise && enable_gas_interceptor) {
|
||||
SET_RX_CHECKS(toyota_lka_alt_cruise_interceptor_rx_checks, ret);
|
||||
} else if (toyota_alt_cruise) {
|
||||
SET_RX_CHECKS(toyota_lka_alt_cruise_rx_checks, ret);
|
||||
} else if (enable_gas_interceptor && !toyota_alt_brake) {
|
||||
SET_RX_CHECKS(toyota_lka_interceptor_rx_checks, ret);
|
||||
} else if (enable_gas_interceptor) {
|
||||
SET_RX_CHECKS(toyota_lka_alt_brake_interceptor_rx_checks, ret);
|
||||
@@ -542,6 +573,7 @@ static bool toyota_fwd_hook(int bus_num, int addr) {
|
||||
const safety_hooks toyota_hooks = {
|
||||
.init = toyota_init,
|
||||
.rx = toyota_rx_hook,
|
||||
.rx_all = toyota_rx_all_hook,
|
||||
.tx = toyota_tx_hook,
|
||||
.fwd = toyota_fwd_hook,
|
||||
.get_checksum = toyota_get_checksum,
|
||||
|
||||
@@ -984,6 +984,7 @@ class SafetyTest(SafetyTestBase):
|
||||
continue
|
||||
if {attr, current_test}.issubset({'TestHyundaiLongitudinalSafety', 'TestHyundaiLongitudinalSafetyCameraSCC',
|
||||
'TestHyundaiSafetyFCEVLong', 'TestHyundaiLongitudinalAolLkasOnEngageSafety',
|
||||
'TestHyundaiSafetyCanRefreshLong', 'TestHyundaiSafetyCanRefreshLongCameraSCC',
|
||||
'TestHyundaiCanCanfdBlendedLongitudinalSafety',
|
||||
'TestHyundaiLegacyLongitudinalSafetyHEV'}):
|
||||
continue
|
||||
|
||||
@@ -208,6 +208,28 @@ class HyundaiLongitudinalBase(common.LongitudinalAccelSafetyTest):
|
||||
|
||||
|
||||
class HyundaiAolLkasOnEngageBase:
|
||||
def test_acc_main_sync_does_not_clear_aol_lkas_latch(self):
|
||||
try:
|
||||
tx_acc_state_msg = self._tx_acc_state_msg(False)
|
||||
except NotImplementedError as err:
|
||||
raise unittest.SkipTest("ACC main TX state message not implemented") from err
|
||||
|
||||
self.safety.set_alternative_experience(ALTERNATIVE_EXPERIENCE.ALWAYS_ON_LATERAL)
|
||||
|
||||
self._rx(self._button_msg(Buttons.NONE, main_button=0))
|
||||
self._rx(self._button_msg(Buttons.NONE, main_button=1))
|
||||
self._rx(self._button_msg(Buttons.NONE, main_button=0))
|
||||
self._rx(self._button_msg(Buttons.SET))
|
||||
self._rx(self._button_msg(Buttons.NONE))
|
||||
|
||||
self.safety.set_controls_allowed(False)
|
||||
for _ in range(3):
|
||||
self._tx(tx_acc_state_msg)
|
||||
self.assertFalse(self.safety.get_acc_main_on())
|
||||
|
||||
self._set_prev_torque(0)
|
||||
self.assertTrue(self._tx(self._torque_cmd_msg(self.MAX_RATE_UP)))
|
||||
|
||||
def test_aol_lkas_auto_enables_on_set_engagement(self):
|
||||
torque_cmd = self.MAX_RATE_UP
|
||||
|
||||
|
||||
@@ -23,15 +23,20 @@ def is_steering_msg(mode, param, addr):
|
||||
elif mode in (CarParams.SafetyModel.hyundai, CarParams.SafetyModel.hyundaiLegacy):
|
||||
ret = addr == 832
|
||||
elif mode == CarParams.SafetyModel.hyundaiCanfd:
|
||||
ret = addr == (0x110 if param & HyundaiSafetyFlags.CANFD_LKA_STEERING_ALT else
|
||||
0x50 if param & HyundaiSafetyFlags.CANFD_LKA_STEERING else
|
||||
0x12A)
|
||||
if param & HyundaiSafetyFlags.CCNC and param & HyundaiSafetyFlags.LONG and param & HyundaiSafetyFlags.CANFD_ANGLE_STEERING:
|
||||
ret = addr == 0xCB
|
||||
else:
|
||||
ret = addr == (0x110 if param & HyundaiSafetyFlags.CANFD_LKA_STEERING_ALT else
|
||||
0x50 if param & HyundaiSafetyFlags.CANFD_LKA_STEERING else
|
||||
0x12A)
|
||||
elif mode == CarParams.SafetyModel.chrysler:
|
||||
ret = addr == 0x292
|
||||
elif mode == CarParams.SafetyModel.subaru:
|
||||
ret = addr == 0x122
|
||||
elif mode == CarParams.SafetyModel.ford:
|
||||
ret = addr == 0x3d6 if param & FordSafetyFlags.CANFD else addr == 0x3d3
|
||||
ret = addr == (0x3ca if param & FordSafetyFlags.LKA_STEERING else
|
||||
0x3d6 if param & FordSafetyFlags.CANFD else
|
||||
0x3d3)
|
||||
elif mode == CarParams.SafetyModel.nissan:
|
||||
ret = addr == 0x169
|
||||
elif mode == CarParams.SafetyModel.rivian:
|
||||
@@ -60,14 +65,24 @@ def get_steer_value(mode, param, msg):
|
||||
elif mode in (CarParams.SafetyModel.hyundai, CarParams.SafetyModel.hyundaiLegacy):
|
||||
torque = (((msg.data[3] & 0x7) << 8) | msg.data[2]) - 1024
|
||||
elif mode == CarParams.SafetyModel.hyundaiCanfd:
|
||||
torque = ((msg.data[5] >> 1) | (msg.data[6] & 0xF) << 7) - 1024
|
||||
if param & HyundaiSafetyFlags.CANFD_ANGLE_STEERING:
|
||||
if param & HyundaiSafetyFlags.CCNC and param & HyundaiSafetyFlags.LONG:
|
||||
angle = ((msg.data[5] & 0x3F) << 8) | msg.data[4]
|
||||
else:
|
||||
angle = (msg.data[11] << 6) | (msg.data[10] >> 2)
|
||||
angle = to_signed(angle, 14)
|
||||
else:
|
||||
torque = ((msg.data[5] >> 1) | (msg.data[6] & 0xF) << 7) - 1024
|
||||
elif mode == CarParams.SafetyModel.chrysler:
|
||||
torque = (((msg.data[0] & 0x7) << 8) | msg.data[1]) - 1024
|
||||
elif mode == CarParams.SafetyModel.subaru:
|
||||
torque = ((msg.data[3] & 0x1F) << 8) | msg.data[2]
|
||||
torque = -to_signed(torque, 13)
|
||||
elif mode == CarParams.SafetyModel.ford:
|
||||
if param & FordSafetyFlags.CANFD:
|
||||
if param & FordSafetyFlags.LKA_STEERING:
|
||||
action = msg.data[0] >> 5
|
||||
angle = 1 if action in (2, 4) else 0
|
||||
elif param & FordSafetyFlags.CANFD:
|
||||
angle = ((msg.data[2] << 3) | (msg.data[3] >> 5)) - 1000
|
||||
else:
|
||||
angle = ((msg.data[0] << 3) | (msg.data[1] >> 5)) - 1000
|
||||
|
||||
@@ -9,7 +9,7 @@ from opendbc.safety.tests.common import CANPackerSafety
|
||||
|
||||
|
||||
class TestChryslerSafety(common.CarSafetyTest, common.MotorTorqueSteeringSafetyTest):
|
||||
TX_MSGS = [[0x23B, 0], [0x292, 0], [0x2A6, 0]]
|
||||
TX_MSGS = [[0x23B, 0], [0x292, 0], [0x2A6, 0], [0x1F4, 0]]
|
||||
RELAY_MALFUNCTION_ADDRS = {0: (0x292, 0x2A6)}
|
||||
FWD_BLACKLISTED_ADDRS = {2: [0x292, 0x2A6]}
|
||||
|
||||
@@ -37,6 +37,14 @@ class TestChryslerSafety(common.CarSafetyTest, common.MotorTorqueSteeringSafetyT
|
||||
values = {"ACC_ACTIVE": enable}
|
||||
return self.packer.make_can_msg_safety("DAS_3", self.DAS_BUS, values)
|
||||
|
||||
def _main_on_msg(self, available=True, active=False):
|
||||
values = {"ACC_AVAILABLE": available, "ACC_ACTIVE": active}
|
||||
return self.packer.make_can_msg_panda("DAS_3", self.DAS_BUS, values)
|
||||
|
||||
def _das_3_msg(self):
|
||||
values = {"ACC_AVAILABLE": 1, "ACC_ACTIVE": 1, "ACC_DECEL_REQ": 1, "ACC_DECEL": -2.0}
|
||||
return self.packer.make_can_msg_safety("DAS_3", self.DAS_BUS, values)
|
||||
|
||||
def _speed_msg(self, speed):
|
||||
values = {"SPEED_LEFT": speed, "SPEED_RIGHT": speed}
|
||||
return self.packer.make_can_msg_safety("SPEED_1", 0, values)
|
||||
@@ -71,6 +79,58 @@ class TestChryslerSafety(common.CarSafetyTest, common.MotorTorqueSteeringSafetyT
|
||||
self.assertFalse(self._tx(self._button_msg(cancel=True, resume=True)))
|
||||
self.assertFalse(self._tx(self._button_msg(cancel=False, resume=False)))
|
||||
|
||||
def test_jeep_brake_hold_resume_at_standstill_requires_safety_flag_and_main_on(self):
|
||||
if self.__class__ is not TestChryslerSafety:
|
||||
self.skipTest("Jeep brake hold applies to base Chrysler/Jeep safety only")
|
||||
|
||||
self._rx(self._speed_msg(0))
|
||||
self._rx(self._main_on_msg(True))
|
||||
self.safety.set_controls_allowed(False)
|
||||
|
||||
self.assertFalse(self._tx(self._button_msg(resume=True)))
|
||||
|
||||
self.safety.set_safety_hooks(CarParams.SafetyModel.chrysler, ChryslerSafetyFlags.JEEP_BRAKE_HOLD)
|
||||
self.safety.init_tests()
|
||||
self._rx(self._speed_msg(0))
|
||||
self._rx(self._main_on_msg(True))
|
||||
self.assertTrue(self._tx(self._button_msg(resume=True)))
|
||||
|
||||
self._rx(self._user_gas_msg(1))
|
||||
self.assertFalse(self._tx(self._button_msg(resume=True)))
|
||||
self._rx(self._user_gas_msg(0))
|
||||
|
||||
self._rx(self._user_brake_msg(True))
|
||||
self.assertFalse(self._tx(self._button_msg(resume=True)))
|
||||
self._rx(self._user_brake_msg(False))
|
||||
|
||||
self._rx(self._speed_msg(1))
|
||||
self.assertFalse(self._tx(self._button_msg(resume=True)))
|
||||
|
||||
def test_jeep_brake_hold_das_3_requires_safety_flag_main_on_and_standstill(self):
|
||||
if self.__class__ is not TestChryslerSafety:
|
||||
self.skipTest("Jeep brake hold applies to base Chrysler/Jeep safety only")
|
||||
|
||||
self.assertFalse(self._tx(self._das_3_msg()))
|
||||
|
||||
self.safety.set_safety_hooks(CarParams.SafetyModel.chrysler, ChryslerSafetyFlags.JEEP_BRAKE_HOLD)
|
||||
self.safety.init_tests()
|
||||
self._rx(self._speed_msg(0))
|
||||
self.assertFalse(self._tx(self._das_3_msg()))
|
||||
|
||||
self._rx(self._main_on_msg(True))
|
||||
self.assertTrue(self._tx(self._das_3_msg()))
|
||||
|
||||
self._rx(self._user_gas_msg(1))
|
||||
self.assertFalse(self._tx(self._das_3_msg()))
|
||||
self._rx(self._user_gas_msg(0))
|
||||
|
||||
self._rx(self._user_brake_msg(True))
|
||||
self.assertFalse(self._tx(self._das_3_msg()))
|
||||
self._rx(self._user_brake_msg(False))
|
||||
|
||||
self._rx(self._speed_msg(1))
|
||||
self.assertFalse(self._tx(self._das_3_msg()))
|
||||
|
||||
def _toggle_aol(self, toggle_on):
|
||||
# DAS_3, bit 20 is ACC_AVAILABLE
|
||||
values = {"ACC_AVAILABLE": 1 if toggle_on else 0}
|
||||
|
||||
@@ -76,6 +76,7 @@ class TestFordSafetyBase(common.CarSafetyTest):
|
||||
STEER_MESSAGE = 0
|
||||
|
||||
# Curvature control limits
|
||||
LKA_STEERING = False
|
||||
DEG_TO_CAN = 50000 # 1 / (2e-5) rad to can
|
||||
MAX_CURVATURE = 0.02
|
||||
MAX_CURVATURE_ERROR = 0.002
|
||||
@@ -354,12 +355,13 @@ class TestFordSafetyBase(common.CarSafetyTest):
|
||||
self._set_prev_desired_angle(sign * (curvature_offset + initial_curvature))
|
||||
self.assertEqual(should_tx, self._tx(self._lat_ctl_msg(True, 0, 0, sign * (curvature_offset + desired_curvature), 0)))
|
||||
|
||||
def test_prevent_lkas_action(self):
|
||||
self.safety.set_controls_allowed(1)
|
||||
self.assertFalse(self._tx(self._lkas_command_msg(1)))
|
||||
|
||||
self.safety.set_controls_allowed(0)
|
||||
self.assertFalse(self._tx(self._lkas_command_msg(1)))
|
||||
def test_lkas_action(self):
|
||||
for controls_allowed in (0, 1):
|
||||
self.safety.set_controls_allowed(controls_allowed)
|
||||
for action in range(8):
|
||||
should_tx = action == 0
|
||||
should_tx |= self.LKA_STEERING and controls_allowed and action in (2, 4)
|
||||
self.assertEqual(should_tx, self._tx(self._lkas_command_msg(action)))
|
||||
|
||||
def test_acc_buttons(self):
|
||||
for allowed in (0, 1):
|
||||
@@ -484,6 +486,16 @@ class TestFordLongitudinalSafety(TestFordLongitudinalSafetyBase):
|
||||
pass
|
||||
|
||||
|
||||
class TestFordLKASteeringSafety(TestFordLongitudinalSafety):
|
||||
LKA_STEERING = True
|
||||
|
||||
def setUp(self):
|
||||
self.packer = CANPackerSafety("ford_lincoln_base_pt")
|
||||
self.safety = libsafety_py.libsafety
|
||||
self.safety.set_safety_hooks(CarParams.SafetyModel.ford, FordSafetyFlags.LKA_STEERING)
|
||||
self.safety.init_tests()
|
||||
|
||||
|
||||
class TestFordCANFDLongitudinalSafety(TestFordLongitudinalSafetyBase):
|
||||
STEER_MESSAGE = MSG_LateralMotionControl2
|
||||
|
||||
|
||||
@@ -346,10 +346,10 @@ class TestGmCameraNoCameraSafety(TestGmCameraSafety):
|
||||
|
||||
|
||||
class TestGmCameraLongitudinalSafety(GmLongitudinalBase, TestGmCameraSafetyBase):
|
||||
TX_MSGS = [[0x180, 0], [0x315, 0], [0x2CB, 0], [0x370, 0], [0x200, 0], [0x1E1, 0], [0x3D1, 0], [0xBD, 0], [0x1F5, 0], # pt bus
|
||||
TX_MSGS = [[0x180, 0], [0x315, 0], [0x2CB, 0], [0x2CD, 0], [0x370, 0], [0x200, 0], [0x1E1, 0], [0x3D1, 0], [0xBD, 0], [0x1F5, 0], # pt bus
|
||||
[0x184, 2]] # camera bus
|
||||
FWD_BLACKLISTED_ADDRS = {2: [0x180, 0x2CB, 0x370, 0x315], 0: [0x184]} # block LKAS, ACC messages and PSCMStatus
|
||||
RELAY_MALFUNCTION_ADDRS = {0: (0x180, 0x2CB, 0x370, 0x315), 2: (0x184,)}
|
||||
FWD_BLACKLISTED_ADDRS = {2: [0x180, 0x2CB, 0x2CD, 0x370, 0x315], 0: [0x184]} # block LKAS, ACC messages and PSCMStatus
|
||||
RELAY_MALFUNCTION_ADDRS = {0: (0x180, 0x2CB, 0x2CD, 0x370, 0x315), 2: (0x184,)}
|
||||
BUTTONS_BUS = 0 # rx only
|
||||
|
||||
MAX_GAS = 2698
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user