mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-08-25 02:03:43 +08:00
Compare commits
196 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 890876c233 | |||
| 981cc7d30b | |||
| ccadc60b13 | |||
| a9defe7a3d | |||
| 8edaf3e2e3 | |||
| 43332bf3b3 | |||
| 5230a5264b | |||
| 50eec7fe22 | |||
| bcdd694154 | |||
| ee8028ebcd | |||
| 90e38339e7 | |||
| 1ad0de4e6d | |||
| dd58cc48ee | |||
| 13fbf4b90d | |||
| dfd6f30f2c | |||
| ce85d3cf2c | |||
| 2633b39c55 | |||
| f8d1f75c4d | |||
| 6c16b9a14b | |||
| c93c19128e | |||
| ec5289a0a9 | |||
| 184585beaa | |||
| 7cc750e77d | |||
| ed9027fb9c | |||
| 6fba035118 | |||
| 986a66f8f1 | |||
| 3976ed8d9a | |||
| 4bac569626 | |||
| d80b973e6f | |||
| 26305c70f0 | |||
| 4cfbaf1149 | |||
| ac847558fa | |||
| 309e9dcaf4 | |||
| a0b6461d92 | |||
| 03b4debc39 | |||
| 6495fe73e1 | |||
| 4e1d7e1917 | |||
| 0a8f96f266 | |||
| b3b27c2b60 | |||
| 2d8d2a4360 | |||
| 8833ac6ff5 | |||
| 28a82e85c2 | |||
| 39532ba0ee | |||
| 823ceb2228 | |||
| 0267dd4f98 | |||
| bba72c85c7 | |||
| 743863a4a5 | |||
| 78da8d7641 | |||
| 9be0df54c2 | |||
| 245c39ed0d | |||
| 21effc702e | |||
| 24687b1f79 | |||
| c7751a283a | |||
| 17f38ea13e | |||
| 25d904d146 | |||
| c39aec3bcf | |||
| c4d22c7917 | |||
| 9120aecdae | |||
| 0635bd4dcd | |||
| 1fb31425b6 | |||
| 2a67bdb589 | |||
| bbdb6b99bf | |||
| 5f8513f4d3 | |||
| a80201c553 | |||
| 4c3c5ddbbb | |||
| a4acab2d5c | |||
| 4503026936 | |||
| ce941e9f25 | |||
| 8cb1514465 | |||
| 8a09dbc5b0 | |||
| e5fec364bb | |||
| e82e034a31 | |||
| 104a60c9e0 | |||
| 5ecfae7ede | |||
| 7294fe03be | |||
| cf606c06aa | |||
| 2b5a613c65 | |||
| b9ae966de7 | |||
| 7eb280bfb1 | |||
| 1acb197111 | |||
| b2ed87c3d7 | |||
| b52177f8b6 | |||
| dc191e833e | |||
| 51c54cc353 | |||
| 456e3435d5 | |||
| 69fa82d3b4 | |||
| bcd9a35be8 | |||
| 67ad32c0f5 | |||
| d9ca40bc22 | |||
| 0f09e149ec | |||
| 8a44631529 | |||
| 7c55dbe49a | |||
| 1a1e9f0736 | |||
| 4492123c26 | |||
| ab77c06512 | |||
| fd320de1c1 | |||
| eefa06eab1 | |||
| dd7e289d95 | |||
| e7e8facca9 | |||
| a757ef5b65 | |||
| 47bc5af5ee | |||
| 61a04f8367 | |||
| 7299f52b24 | |||
| 3648900095 | |||
| 65bada6b31 | |||
| 4e63b10a5d | |||
| 264e9d8a77 | |||
| fb85f286c9 | |||
| 1f5c54e570 | |||
| aca330de59 | |||
| 6852e8ff22 | |||
| 37b8cf3a09 | |||
| e426e9c935 | |||
| 51a1f124eb | |||
| dc9f3a15d6 | |||
| 0d2ccaf1c8 | |||
| df4f2ef5f5 | |||
| 0e0c2b0a33 | |||
| 0e5acdb660 | |||
| 6218550e6d | |||
| ac3290c0e7 | |||
| f25410c6e9 | |||
| e502094d04 | |||
| e78ac3497a | |||
| 4d6a44c836 | |||
| bc1e51836b | |||
| ae4b450c0b | |||
| 95e9627674 | |||
| 078de0467d | |||
| 42f712949b | |||
| 2eeea3072e | |||
| 60927c16a6 | |||
| 5498f28556 | |||
| fbf0c26d0f | |||
| 7da7df2297 | |||
| 4607e1bce2 | |||
| 05d668e2fd | |||
| e876ebdc31 | |||
| 5d0edc8cbb | |||
| 60ef10bd32 | |||
| 4b24894f0c | |||
| 1c17ee1869 | |||
| a8d95255d7 | |||
| 629e02e961 | |||
| 9daa218754 | |||
| 0f60736928 | |||
| 59f9aa6d92 | |||
| 8a9d0e5e6a | |||
| 0bc9d08c12 | |||
| f703cf3429 | |||
| 9a72ba61ce | |||
| d03dfd8e65 | |||
| 7e403c0ad4 | |||
| b0189d0828 | |||
| 4677300fde | |||
| 3548375b17 | |||
| 88816e4f66 | |||
| 9027259e0c | |||
| 4084424c89 | |||
| dff7a4dbb3 | |||
| 699c986eec | |||
| 43f592b10b | |||
| 74e1c1b4dd | |||
| b5a46ee191 | |||
| 2e59f0f06a | |||
| 0b940638ed | |||
| f79f390dca | |||
| 000ed604fe | |||
| af4b46c96e | |||
| 77160afa1a | |||
| 445262d4f2 | |||
| c7fde11c1b | |||
| 1a9751d321 | |||
| aab6b6f6ab | |||
| 47d73bfe3d | |||
| da60a98f32 | |||
| 0d1a6de0f3 | |||
| b59929ae09 | |||
| e3f9a9fb6c | |||
| 1c91819689 | |||
| 9eab9cd574 | |||
| 8a0f3a86f6 | |||
| 0155bd7e1b | |||
| 038a5e20e2 | |||
| caeeb30cc3 | |||
| b87ba63846 | |||
| 7c1cad687c | |||
| a4a7e3bbaa | |||
| 21f58becce | |||
| f819e72075 | |||
| cbf66c5e9b | |||
| fdf04aac54 | |||
| 3530b623d8 | |||
| e139430025 | |||
| b4b15b555a | |||
| 1e36f01748 |
+1
-1
@@ -105,7 +105,7 @@ struct FrogPilotCarParams @0xf35cc4560bbf6ec2 {
|
||||
isHDA2 @3 :Bool;
|
||||
openpilotLongitudinalControlDisabled @4 :Bool;
|
||||
safetyConfigs @5 :List(SafetyConfig);
|
||||
|
||||
canUseSASCM @6 :Bool;
|
||||
|
||||
struct SafetyConfig {
|
||||
safetyParam @0 :UInt16;
|
||||
|
||||
@@ -224,6 +224,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"AdvancedLateralTune", PERSISTENT},
|
||||
{"AdvancedLongitudinalTune", PERSISTENT},
|
||||
{"AggressiveFollow", PERSISTENT},
|
||||
{"AggressiveFollowHigh", PERSISTENT},
|
||||
{"AggressiveJerkAcceleration", PERSISTENT},
|
||||
{"AggressiveJerkDanger", PERSISTENT},
|
||||
{"AggressiveJerkDeceleration", PERSISTENT},
|
||||
@@ -246,6 +247,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"BlacklistedModels", PERSISTENT},
|
||||
{"BlindSpotMetrics", PERSISTENT},
|
||||
{"BlindSpotPath", PERSISTENT},
|
||||
{"BootLogo", PERSISTENT},
|
||||
{"BorderMetrics", PERSISTENT},
|
||||
{"CalibratedLateralAcceleration", PERSISTENT},
|
||||
{"CalibrationProgress", PERSISTENT},
|
||||
@@ -271,6 +273,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"CEStatus", CLEAR_ON_OFFROAD_TRANSITION},
|
||||
{"CEStoppedLead", PERSISTENT},
|
||||
{"ClusterOffset", PERSISTENT},
|
||||
{"BootLogoToDownload", CLEAR_ON_MANAGER_START},
|
||||
{"ColorToDownload", CLEAR_ON_MANAGER_START},
|
||||
{"Compass", PERSISTENT},
|
||||
{"ConditionalExperimental", PERSISTENT},
|
||||
@@ -309,6 +312,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"DisengageVolume", PERSISTENT},
|
||||
{"DoToggleReset", PERSISTENT},
|
||||
{"DoToggleResetStock", PERSISTENT},
|
||||
{"DownloadableBootLogos", PERSISTENT},
|
||||
{"DownloadableColors", PERSISTENT},
|
||||
{"DownloadableDistanceIcons", PERSISTENT},
|
||||
{"DownloadableIcons", PERSISTENT},
|
||||
@@ -321,6 +325,8 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"DynamicPedalsOnUI", PERSISTENT},
|
||||
{"EngageVolume", PERSISTENT},
|
||||
{"ExperimentalGMTune", PERSISTENT},
|
||||
{"EVTuning", PERSISTENT},
|
||||
{"TruckTuning", PERSISTENT},
|
||||
{"Fahrenheit", PERSISTENT},
|
||||
{"FavoriteDestinations", PERSISTENT | DONT_LOG},
|
||||
{"FlashPanda", CLEAR_ON_MANAGER_START},
|
||||
@@ -456,6 +462,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"RandomThemes", PERSISTENT},
|
||||
{"RefuseVolume", PERSISTENT},
|
||||
{"RelaxedFollow", PERSISTENT},
|
||||
{"RelaxedFollowHigh", PERSISTENT},
|
||||
{"RelaxedJerkAcceleration", PERSISTENT},
|
||||
{"RelaxedJerkDanger", PERSISTENT},
|
||||
{"RelaxedJerkDeceleration", PERSISTENT},
|
||||
@@ -466,6 +473,8 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"RoadEdgesWidth", PERSISTENT},
|
||||
{"RoadName", CLEAR_ON_MANAGER_START},
|
||||
{"RoadNameUI", PERSISTENT},
|
||||
{"RedPanda", PERSISTENT},
|
||||
{"RemoteStartBootsComma", PERSISTENT},
|
||||
{"RotatingWheel", PERSISTENT},
|
||||
{"ScreenBrightness", PERSISTENT},
|
||||
{"ScreenBrightnessOnroad", PERSISTENT},
|
||||
@@ -515,6 +524,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"SpeedLimitsFiltered", PERSISTENT | DONT_LOG},
|
||||
{"SpeedLimitSources", PERSISTENT},
|
||||
{"StandardFollow", PERSISTENT},
|
||||
{"StandardFollowHigh", PERSISTENT},
|
||||
{"StandardJerkAcceleration", PERSISTENT},
|
||||
{"StandardJerkDanger", PERSISTENT},
|
||||
{"StandardJerkDeceleration", PERSISTENT},
|
||||
@@ -531,6 +541,8 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"SteerDelayStock", PERSISTENT},
|
||||
{"SteerFriction", PERSISTENT},
|
||||
{"SteerFrictionStock", PERSISTENT},
|
||||
{"SteerOffset", PERSISTENT},
|
||||
{"SteerOffsetStock", PERSISTENT},
|
||||
{"SteerLatAccel", PERSISTENT},
|
||||
{"SteerLatAccelStock", PERSISTENT},
|
||||
{"SteerKP", PERSISTENT},
|
||||
@@ -545,6 +557,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"StoppedTimer", PERSISTENT},
|
||||
{"TacoTune", PERSISTENT},
|
||||
{"TacoTuneHacks", PERSISTENT},
|
||||
{"TrailerLoad", PERSISTENT},
|
||||
{"TestAlert", CLEAR_ON_MANAGER_START},
|
||||
{"TetheringEnabled", PERSISTENT},
|
||||
{"ThemeDownloadProgress", CLEAR_ON_MANAGER_START},
|
||||
@@ -584,6 +597,7 @@ std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"WheelIcon", PERSISTENT},
|
||||
{"WheelSpeed", PERSISTENT},
|
||||
{"StopDistance", PERSISTENT},
|
||||
{"RecoveryPower", PERSISTENT},
|
||||
{"WheelToDownload", CLEAR_ON_MANAGER_START},
|
||||
};
|
||||
|
||||
|
||||
@@ -29,13 +29,15 @@ def check_github_rate_limit():
|
||||
print(f"Error checking GitHub rate limit: {error}")
|
||||
return False
|
||||
|
||||
def download_file(cancel_param, destination, progress_param, url, download_param, params_memory):
|
||||
def download_file(cancel_param, destination, progress_param, url, download_param, params_memory, allow_unknown_size=False, suppress_errors=False):
|
||||
try:
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
total_size = get_remote_file_size(url)
|
||||
if total_size == 0:
|
||||
total_size = get_remote_file_size(url, suppress_errors=suppress_errors or allow_unknown_size)
|
||||
if total_size == 0 and not allow_unknown_size:
|
||||
if not url.endswith(".gif"):
|
||||
if suppress_errors:
|
||||
return
|
||||
handle_error(None, "Download invalid...", "Download invalid...", download_param, progress_param, params_memory)
|
||||
return
|
||||
|
||||
@@ -56,24 +58,30 @@ def download_file(cancel_param, destination, progress_param, url, download_param
|
||||
temp_file.write(chunk)
|
||||
downloaded_size += len(chunk)
|
||||
|
||||
progress = (downloaded_size / total_size) * 100
|
||||
if progress != 100:
|
||||
params_memory.put(progress_param, f"{progress:.0f}%")
|
||||
else:
|
||||
if total_size > 0:
|
||||
progress = (downloaded_size / total_size) * 100
|
||||
if progress != 100:
|
||||
params_memory.put(progress_param, f"{progress:.0f}%")
|
||||
else:
|
||||
params_memory.put(progress_param, "Verifying authenticity...")
|
||||
elif downloaded_size > 0:
|
||||
params_memory.put(progress_param, "Verifying authenticity...")
|
||||
|
||||
temp_file_path.rename(destination)
|
||||
|
||||
except Exception as error:
|
||||
if suppress_errors:
|
||||
return
|
||||
handle_request_error(error, destination, download_param, progress_param, params_memory)
|
||||
|
||||
def get_remote_file_size(url):
|
||||
def get_remote_file_size(url, suppress_errors=False):
|
||||
try:
|
||||
response = requests.head(url, headers={"Accept-Encoding": "identity"}, timeout=10)
|
||||
response.raise_for_status()
|
||||
return int(response.headers.get("Content-Length", 0))
|
||||
except Exception as error:
|
||||
handle_request_error(error, None, None, None, None)
|
||||
if not suppress_errors:
|
||||
handle_request_error(error, None, None, None, None)
|
||||
return 0
|
||||
|
||||
def get_repository_url():
|
||||
@@ -104,8 +112,17 @@ def handle_request_error(error, destination, download_param, progress_param, par
|
||||
error_message = error_map.get(type(error), "Unexpected error")
|
||||
handle_error(destination, f"Failed: {error_message}", error, download_param, progress_param, params_memory)
|
||||
|
||||
def verify_download(file_path, url):
|
||||
remote_file_size = get_remote_file_size(url)
|
||||
def verify_download(file_path, url, allow_unknown_size=False):
|
||||
remote_file_size = get_remote_file_size(url, suppress_errors=allow_unknown_size)
|
||||
|
||||
if remote_file_size == 0 and allow_unknown_size:
|
||||
if not file_path.is_file():
|
||||
print(f"File not found: {file_path}")
|
||||
return False
|
||||
if file_path.stat().st_size == 0:
|
||||
print(f"File is empty: {file_path}")
|
||||
return False
|
||||
return True
|
||||
|
||||
if remote_file_size == 0:
|
||||
print(f"Error fetching remote size for {file_path}")
|
||||
|
||||
@@ -107,7 +107,7 @@ class ModelManager:
|
||||
self.downloading_model = False
|
||||
return
|
||||
|
||||
if model_version in ("v8", "v9", "v10", "v11"):
|
||||
if model_version in ("v8", "v9", "v10", "v11", "v12"):
|
||||
# Download all PKL and metadata files for multi-file tinygrad models (v8 and v9)
|
||||
filenames = [
|
||||
f"{model_to_download}_driving_policy_tinygrad.pkl",
|
||||
@@ -115,6 +115,11 @@ class ModelManager:
|
||||
f"{model_to_download}_driving_policy_metadata.pkl",
|
||||
f"{model_to_download}_driving_vision_metadata.pkl",
|
||||
]
|
||||
if model_version == "v12":
|
||||
filenames += [
|
||||
f"{model_to_download}_driving_off_policy_tinygrad.pkl",
|
||||
f"{model_to_download}_driving_off_policy_metadata.pkl",
|
||||
]
|
||||
for filename in filenames:
|
||||
model_path = MODELS_PATH / filename
|
||||
model_url = f"{repo_url}/Models/{filename}"
|
||||
@@ -208,13 +213,18 @@ class ModelManager:
|
||||
except Exception:
|
||||
model_version = None
|
||||
|
||||
if model_version in ("v8", "v9", "v10", "v11"):
|
||||
if model_version in ("v8", "v9", "v10", "v11", "v12"):
|
||||
v8_v9_files = [
|
||||
f"{model}_driving_policy_tinygrad.pkl",
|
||||
f"{model}_driving_vision_tinygrad.pkl",
|
||||
f"{model}_driving_policy_metadata.pkl",
|
||||
f"{model}_driving_vision_metadata.pkl",
|
||||
]
|
||||
if model_version == "v12":
|
||||
v8_v9_files += [
|
||||
f"{model}_driving_off_policy_tinygrad.pkl",
|
||||
f"{model}_driving_off_policy_metadata.pkl",
|
||||
]
|
||||
if all((MODELS_PATH / f).is_file() for f in v8_v9_files):
|
||||
downloaded_models.add(model)
|
||||
elif model_version == "v7":
|
||||
@@ -259,13 +269,18 @@ class ModelManager:
|
||||
except Exception:
|
||||
model_version = None
|
||||
|
||||
if model_version in ("v8", "v9", "v10", "v11"):
|
||||
if model_version in ("v8", "v9", "v10", "v11", "v12"):
|
||||
v8_v9_files = [
|
||||
f"{model}_driving_policy_tinygrad.pkl",
|
||||
f"{model}_driving_vision_tinygrad.pkl",
|
||||
f"{model}_driving_policy_metadata.pkl",
|
||||
f"{model}_driving_vision_metadata.pkl",
|
||||
]
|
||||
if model_version == "v12":
|
||||
v8_v9_files += [
|
||||
f"{model}_driving_off_policy_tinygrad.pkl",
|
||||
f"{model}_driving_off_policy_metadata.pkl",
|
||||
]
|
||||
for filename in v8_v9_files:
|
||||
path = MODELS_PATH / filename
|
||||
expected_size = model_sizes.get(filename.rsplit(".", 1)[0])
|
||||
@@ -378,13 +393,18 @@ class ModelManager:
|
||||
handle_error(None, "Download cancelled...", "Download cancelled...", MODEL_DOWNLOAD_ALL_PARAM, DOWNLOAD_PROGRESS_PARAM, params_memory)
|
||||
return
|
||||
|
||||
if model_version in ("v8", "v9", "v10", "v11"):
|
||||
if model_version in ("v8", "v9", "v10", "v11", "v12"):
|
||||
required_files = [
|
||||
f"{model}_driving_policy_tinygrad.pkl",
|
||||
f"{model}_driving_vision_tinygrad.pkl",
|
||||
f"{model}_driving_policy_metadata.pkl",
|
||||
f"{model}_driving_vision_metadata.pkl",
|
||||
]
|
||||
if model_version == "v12":
|
||||
required_files += [
|
||||
f"{model}_driving_off_policy_tinygrad.pkl",
|
||||
f"{model}_driving_off_policy_metadata.pkl",
|
||||
]
|
||||
missing = [f for f in required_files if not (MODELS_PATH / f).is_file()]
|
||||
if missing:
|
||||
print(f"Tinygrad model {model} is missing files. Preparing to download...")
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"input_std":[[9.342214],[1.5915664],[0.60113484],[0.048193663],[1.5680411],[1.57577],[1.5836853],[1.5711677],[1.5445132],[1.5007596],[1.4529978],[0.047915205],[0.04800539],[0.04808892],[0.04822398],[0.04817677],[0.047881734],[0.047405947]],"model_test_loss":0.019342588260769844,"input_size":18,"current_date_and_time":"2023-08-05_06-09-11","input_mean":[[22.757933],[-0.016342578],[-0.001405228],[-0.014619173],[-0.018091483],[-0.018382493],[-0.019270267],[-0.018759886],[-0.019559544],[-0.017848592],[-0.020014366],[-0.014564899],[-0.01457757],[-0.014600966],[-0.014757987],[-0.014915743],[-0.015121007],[-0.015359475]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.045577038],[-2.8160777],[-0.21418032],[2.9552083],[-0.06852827],[0.029141279],[-0.053249933]],"dense_1_W":[[0.0010664682,1.3813498,-8.153277,-0.009101015,-0.1484779,1.0008405,-1.5930991,-1.195274,0.6136762,0.3560462,-0.30077773,0.93531257,0.14550218,-0.40074933,-0.21716364,0.13834935,-0.0658936,-0.49427196],[-0.7338253,-0.037501235,-0.49922377,-1.0526338,-0.46141604,-1.3649396,0.7309814,-0.91667104,0.044683233,-0.18628967,-0.20878822,-0.2678598,0.48446324,0.51164204,0.09547851,0.41491362,-0.4524314,0.16594785],[-0.0032175342,3.5451593,-0.11421584,-0.2511033,0.27893174,0.6384996,0.80790603,1.3835542,1.8000495,2.249461,1.4026878,0.7851167,-0.35052946,0.041820426,-0.39790183,0.45703772,-0.30459648,0.18287674],[0.72289664,-0.77138704,-0.5025135,-0.49323097,-0.39893976,-0.8779149,0.6848096,-0.58729875,-0.16643623,-0.14427555,-0.15013328,-0.11467142,-0.27280045,0.48508415,0.52080667,-0.0029569597,-0.28170735,0.06367212],[0.00014026981,-2.0353239,0.0060915067,0.4794941,1.1882906,-1.512865,0.80642134,-0.26044324,-0.56213886,-0.365122,0.55078083,-0.34897435,-0.32031298,0.46106994,0.37792024,-0.14116105,0.15597256,-0.32811671],[0.0011494387,1.9060265,8.3941965,-0.3078165,-0.9530427,0.6589645,-1.2487054,-0.541133,0.13451256,0.2805466,-0.33187166,0.9900705,0.2821258,-0.51270896,-0.5130031,-0.28222615,0.042734932,0.27233443],[-0.0014444552,-1.0933362,0.003359957,-0.15052961,0.37165833,1.7087307,-1.4971043,0.673802,-0.108465396,-0.088505454,0.21561684,0.6252258,-0.15019403,-0.34727478,0.08958758,-0.1723621,-0.25557828,0.29778987]],"activation":"σ"},{"dense_2_W":[[0.20810607,-0.13481373,-0.15003344,-0.6067625,-0.31937903,-0.37380016,0.4666911],[0.71834767,0.15669753,-0.11389502,0.27131546,-0.060118295,0.039282244,0.52961355],[-0.27200606,0.43380752,-0.106262706,-0.49363258,0.35659012,-0.01706296,-1.0807354],[-0.79583454,-0.66428274,-0.42157334,-0.52979547,-0.2803004,-0.57637554,0.084149174],[0.1230394,-0.57023287,0.28639448,-0.506744,-0.9920808,0.7510901,0.8584937],[-0.41256845,0.35423175,0.13009231,0.5198333,0.7576573,-0.7053181,-0.3390053],[-0.15090485,0.33375353,-0.64164793,0.57386595,0.25700107,-0.15245672,-0.49020413],[-0.50120676,0.5210849,-0.3282951,0.4276323,1.0257447,-1.0398436,-0.8911315],[0.11402861,-0.41491523,0.247507,-0.39776865,-0.6089287,0.20646147,0.6692102],[0.09411492,0.31819808,0.41330767,0.024243973,0.2314893,0.08529045,0.14911193],[0.5179265,0.20473345,-0.36762783,-0.009045922,-0.19436747,-0.548099,-0.08656279],[-0.69121784,-0.14201449,0.3887621,0.10787631,1.0371574,-0.64747447,-0.73437464],[-0.07860244,-0.597764,0.019551078,0.010199122,-0.7493642,0.66619915,0.121666186]],"activation":"σ","dense_2_b":[[-0.04259814],[-0.053309314],[-0.28045595],[-0.23801634],[0.00944943],[-0.109921046],[-0.037457183],[0.24627604],[-0.0069056284],[-0.04725389],[-0.03361769],[0.044101644],[-0.0009850285]]},{"dense_3_W":[[-0.19631335,-0.47722518,-0.019853225,-0.29363275,-0.625923,0.35145283,0.62401354,0.14337404,-0.25707108,-0.51310915,0.2565006,0.61742216,-0.07952821],[0.5868974,-0.36786363,-0.0177682,-0.45121866,0.68608487,-0.55178356,-0.35236016,-0.7226455,-0.015394288,-0.10650332,0.5391039,0.17791761,0.34196886],[0.16821232,-0.35580373,0.26906335,0.41736925,-0.6979869,0.41801625,0.34707317,0.7784011,-0.10002936,0.32485273,-0.3644285,0.49092358,-0.43872768]],"activation":"identity","dense_3_b":[[-0.019481273],[0.031026587],[-0.03595322]]},{"dense_4_W":[[-0.72340566,0.21244061,-0.67248434]],"dense_4_b":[[0.026229527]],"activation":"identity"}]}
|
||||
@@ -0,0 +1 @@
|
||||
{"input_std":[[9.342214],[1.5915664],[0.60113484],[0.048193663],[1.5680411],[1.57577],[1.5836853],[1.5711677],[1.5445132],[1.5007596],[1.4529978],[0.047915205],[0.04800539],[0.04808892],[0.04822398],[0.04817677],[0.047881734],[0.047405947]],"model_test_loss":0.019342588260769844,"input_size":18,"current_date_and_time":"2023-08-05_06-09-11","input_mean":[[22.757933],[-0.016342578],[-0.001405228],[-0.014619173],[-0.018091483],[-0.018382493],[-0.019270267],[-0.018759886],[-0.019559544],[-0.017848592],[-0.020014366],[-0.014564899],[-0.01457757],[-0.014600966],[-0.014757987],[-0.014915743],[-0.015121007],[-0.015359475]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.045577038],[-2.8160777],[-0.21418032],[2.9552083],[-0.06852827],[0.029141279],[-0.053249933]],"dense_1_W":[[0.0010664682,1.3813498,-8.153277,-0.009101015,-0.1484779,1.0008405,-1.5930991,-1.195274,0.6136762,0.3560462,-0.30077773,0.93531257,0.14550218,-0.40074933,-0.21716364,0.13834935,-0.0658936,-0.49427196],[-0.7338253,-0.037501235,-0.49922377,-1.0526338,-0.46141604,-1.3649396,0.7309814,-0.91667104,0.044683233,-0.18628967,-0.20878822,-0.2678598,0.48446324,0.51164204,0.09547851,0.41491362,-0.4524314,0.16594785],[-0.0032175342,3.5451593,-0.11421584,-0.2511033,0.27893174,0.6384996,0.80790603,1.3835542,1.8000495,2.249461,1.4026878,0.7851167,-0.35052946,0.041820426,-0.39790183,0.45703772,-0.30459648,0.18287674],[0.72289664,-0.77138704,-0.5025135,-0.49323097,-0.39893976,-0.8779149,0.6848096,-0.58729875,-0.16643623,-0.14427555,-0.15013328,-0.11467142,-0.27280045,0.48508415,0.52080667,-0.0029569597,-0.28170735,0.06367212],[0.00014026981,-2.0353239,0.0060915067,0.4794941,1.1882906,-1.512865,0.80642134,-0.26044324,-0.56213886,-0.365122,0.55078083,-0.34897435,-0.32031298,0.46106994,0.37792024,-0.14116105,0.15597256,-0.32811671],[0.0011494387,1.9060265,8.3941965,-0.3078165,-0.9530427,0.6589645,-1.2487054,-0.541133,0.13451256,0.2805466,-0.33187166,0.9900705,0.2821258,-0.51270896,-0.5130031,-0.28222615,0.042734932,0.27233443],[-0.0014444552,-1.0933362,0.003359957,-0.15052961,0.37165833,1.7087307,-1.4971043,0.673802,-0.108465396,-0.088505454,0.21561684,0.6252258,-0.15019403,-0.34727478,0.08958758,-0.1723621,-0.25557828,0.29778987]],"activation":"σ"},{"dense_2_W":[[0.20810607,-0.13481373,-0.15003344,-0.6067625,-0.31937903,-0.37380016,0.4666911],[0.71834767,0.15669753,-0.11389502,0.27131546,-0.060118295,0.039282244,0.52961355],[-0.27200606,0.43380752,-0.106262706,-0.49363258,0.35659012,-0.01706296,-1.0807354],[-0.79583454,-0.66428274,-0.42157334,-0.52979547,-0.2803004,-0.57637554,0.084149174],[0.1230394,-0.57023287,0.28639448,-0.506744,-0.9920808,0.7510901,0.8584937],[-0.41256845,0.35423175,0.13009231,0.5198333,0.7576573,-0.7053181,-0.3390053],[-0.15090485,0.33375353,-0.64164793,0.57386595,0.25700107,-0.15245672,-0.49020413],[-0.50120676,0.5210849,-0.3282951,0.4276323,1.0257447,-1.0398436,-0.8911315],[0.11402861,-0.41491523,0.247507,-0.39776865,-0.6089287,0.20646147,0.6692102],[0.09411492,0.31819808,0.41330767,0.024243973,0.2314893,0.08529045,0.14911193],[0.5179265,0.20473345,-0.36762783,-0.009045922,-0.19436747,-0.548099,-0.08656279],[-0.69121784,-0.14201449,0.3887621,0.10787631,1.0371574,-0.64747447,-0.73437464],[-0.07860244,-0.597764,0.019551078,0.010199122,-0.7493642,0.66619915,0.121666186]],"activation":"σ","dense_2_b":[[-0.04259814],[-0.053309314],[-0.28045595],[-0.23801634],[0.00944943],[-0.109921046],[-0.037457183],[0.24627604],[-0.0069056284],[-0.04725389],[-0.03361769],[0.044101644],[-0.0009850285]]},{"dense_3_W":[[-0.19631335,-0.47722518,-0.019853225,-0.29363275,-0.625923,0.35145283,0.62401354,0.14337404,-0.25707108,-0.51310915,0.2565006,0.61742216,-0.07952821],[0.5868974,-0.36786363,-0.0177682,-0.45121866,0.68608487,-0.55178356,-0.35236016,-0.7226455,-0.015394288,-0.10650332,0.5391039,0.17791761,0.34196886],[0.16821232,-0.35580373,0.26906335,0.41736925,-0.6979869,0.41801625,0.34707317,0.7784011,-0.10002936,0.32485273,-0.3644285,0.49092358,-0.43872768]],"activation":"identity","dense_3_b":[[-0.019481273],[0.031026587],[-0.03595322]]},{"dense_4_W":[[-0.72340566,0.21244061,-0.67248434]],"dense_4_b":[[0.026229527]],"activation":"identity"}]}
|
||||
@@ -0,0 +1 @@
|
||||
{"input_std":[[9.342214],[1.5915664],[0.60113484],[0.048193663],[1.5680411],[1.57577],[1.5836853],[1.5711677],[1.5445132],[1.5007596],[1.4529978],[0.047915205],[0.04800539],[0.04808892],[0.04822398],[0.04817677],[0.047881734],[0.047405947]],"model_test_loss":0.019342588260769844,"input_size":18,"current_date_and_time":"2023-08-05_06-09-11","input_mean":[[22.757933],[-0.016342578],[-0.001405228],[-0.014619173],[-0.018091483],[-0.018382493],[-0.019270267],[-0.018759886],[-0.019559544],[-0.017848592],[-0.020014366],[-0.014564899],[-0.01457757],[-0.014600966],[-0.014757987],[-0.014915743],[-0.015121007],[-0.015359475]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.045577038],[-2.8160777],[-0.21418032],[2.9552083],[-0.06852827],[0.029141279],[-0.053249933]],"dense_1_W":[[0.0010664682,1.3813498,-8.153277,-0.009101015,-0.1484779,1.0008405,-1.5930991,-1.195274,0.6136762,0.3560462,-0.30077773,0.93531257,0.14550218,-0.40074933,-0.21716364,0.13834935,-0.0658936,-0.49427196],[-0.7338253,-0.037501235,-0.49922377,-1.0526338,-0.46141604,-1.3649396,0.7309814,-0.91667104,0.044683233,-0.18628967,-0.20878822,-0.2678598,0.48446324,0.51164204,0.09547851,0.41491362,-0.4524314,0.16594785],[-0.0032175342,3.5451593,-0.11421584,-0.2511033,0.27893174,0.6384996,0.80790603,1.3835542,1.8000495,2.249461,1.4026878,0.7851167,-0.35052946,0.041820426,-0.39790183,0.45703772,-0.30459648,0.18287674],[0.72289664,-0.77138704,-0.5025135,-0.49323097,-0.39893976,-0.8779149,0.6848096,-0.58729875,-0.16643623,-0.14427555,-0.15013328,-0.11467142,-0.27280045,0.48508415,0.52080667,-0.0029569597,-0.28170735,0.06367212],[0.00014026981,-2.0353239,0.0060915067,0.4794941,1.1882906,-1.512865,0.80642134,-0.26044324,-0.56213886,-0.365122,0.55078083,-0.34897435,-0.32031298,0.46106994,0.37792024,-0.14116105,0.15597256,-0.32811671],[0.0011494387,1.9060265,8.3941965,-0.3078165,-0.9530427,0.6589645,-1.2487054,-0.541133,0.13451256,0.2805466,-0.33187166,0.9900705,0.2821258,-0.51270896,-0.5130031,-0.28222615,0.042734932,0.27233443],[-0.0014444552,-1.0933362,0.003359957,-0.15052961,0.37165833,1.7087307,-1.4971043,0.673802,-0.108465396,-0.088505454,0.21561684,0.6252258,-0.15019403,-0.34727478,0.08958758,-0.1723621,-0.25557828,0.29778987]],"activation":"σ"},{"dense_2_W":[[0.20810607,-0.13481373,-0.15003344,-0.6067625,-0.31937903,-0.37380016,0.4666911],[0.71834767,0.15669753,-0.11389502,0.27131546,-0.060118295,0.039282244,0.52961355],[-0.27200606,0.43380752,-0.106262706,-0.49363258,0.35659012,-0.01706296,-1.0807354],[-0.79583454,-0.66428274,-0.42157334,-0.52979547,-0.2803004,-0.57637554,0.084149174],[0.1230394,-0.57023287,0.28639448,-0.506744,-0.9920808,0.7510901,0.8584937],[-0.41256845,0.35423175,0.13009231,0.5198333,0.7576573,-0.7053181,-0.3390053],[-0.15090485,0.33375353,-0.64164793,0.57386595,0.25700107,-0.15245672,-0.49020413],[-0.50120676,0.5210849,-0.3282951,0.4276323,1.0257447,-1.0398436,-0.8911315],[0.11402861,-0.41491523,0.247507,-0.39776865,-0.6089287,0.20646147,0.6692102],[0.09411492,0.31819808,0.41330767,0.024243973,0.2314893,0.08529045,0.14911193],[0.5179265,0.20473345,-0.36762783,-0.009045922,-0.19436747,-0.548099,-0.08656279],[-0.69121784,-0.14201449,0.3887621,0.10787631,1.0371574,-0.64747447,-0.73437464],[-0.07860244,-0.597764,0.019551078,0.010199122,-0.7493642,0.66619915,0.121666186]],"activation":"σ","dense_2_b":[[-0.04259814],[-0.053309314],[-0.28045595],[-0.23801634],[0.00944943],[-0.109921046],[-0.037457183],[0.24627604],[-0.0069056284],[-0.04725389],[-0.03361769],[0.044101644],[-0.0009850285]]},{"dense_3_W":[[-0.19631335,-0.47722518,-0.019853225,-0.29363275,-0.625923,0.35145283,0.62401354,0.14337404,-0.25707108,-0.51310915,0.2565006,0.61742216,-0.07952821],[0.5868974,-0.36786363,-0.0177682,-0.45121866,0.68608487,-0.55178356,-0.35236016,-0.7226455,-0.015394288,-0.10650332,0.5391039,0.17791761,0.34196886],[0.16821232,-0.35580373,0.26906335,0.41736925,-0.6979869,0.41801625,0.34707317,0.7784011,-0.10002936,0.32485273,-0.3644285,0.49092358,-0.43872768]],"activation":"identity","dense_3_b":[[-0.019481273],[0.031026587],[-0.03595322]]},{"dense_4_W":[[-0.72340566,0.21244061,-0.67248434]],"dense_4_b":[[0.026229527]],"activation":"identity"}]}
|
||||
@@ -0,0 +1 @@
|
||||
{"input_std":[[9.342214],[1.5915664],[0.60113484],[0.048193663],[1.5680411],[1.57577],[1.5836853],[1.5711677],[1.5445132],[1.5007596],[1.4529978],[0.047915205],[0.04800539],[0.04808892],[0.04822398],[0.04817677],[0.047881734],[0.047405947]],"model_test_loss":0.019342588260769844,"input_size":18,"current_date_and_time":"2023-08-05_06-09-11","input_mean":[[22.757933],[-0.016342578],[-0.001405228],[-0.014619173],[-0.018091483],[-0.018382493],[-0.019270267],[-0.018759886],[-0.019559544],[-0.017848592],[-0.020014366],[-0.014564899],[-0.01457757],[-0.014600966],[-0.014757987],[-0.014915743],[-0.015121007],[-0.015359475]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.045577038],[-2.8160777],[-0.21418032],[2.9552083],[-0.06852827],[0.029141279],[-0.053249933]],"dense_1_W":[[0.0010664682,1.3813498,-8.153277,-0.009101015,-0.1484779,1.0008405,-1.5930991,-1.195274,0.6136762,0.3560462,-0.30077773,0.93531257,0.14550218,-0.40074933,-0.21716364,0.13834935,-0.0658936,-0.49427196],[-0.7338253,-0.037501235,-0.49922377,-1.0526338,-0.46141604,-1.3649396,0.7309814,-0.91667104,0.044683233,-0.18628967,-0.20878822,-0.2678598,0.48446324,0.51164204,0.09547851,0.41491362,-0.4524314,0.16594785],[-0.0032175342,3.5451593,-0.11421584,-0.2511033,0.27893174,0.6384996,0.80790603,1.3835542,1.8000495,2.249461,1.4026878,0.7851167,-0.35052946,0.041820426,-0.39790183,0.45703772,-0.30459648,0.18287674],[0.72289664,-0.77138704,-0.5025135,-0.49323097,-0.39893976,-0.8779149,0.6848096,-0.58729875,-0.16643623,-0.14427555,-0.15013328,-0.11467142,-0.27280045,0.48508415,0.52080667,-0.0029569597,-0.28170735,0.06367212],[0.00014026981,-2.0353239,0.0060915067,0.4794941,1.1882906,-1.512865,0.80642134,-0.26044324,-0.56213886,-0.365122,0.55078083,-0.34897435,-0.32031298,0.46106994,0.37792024,-0.14116105,0.15597256,-0.32811671],[0.0011494387,1.9060265,8.3941965,-0.3078165,-0.9530427,0.6589645,-1.2487054,-0.541133,0.13451256,0.2805466,-0.33187166,0.9900705,0.2821258,-0.51270896,-0.5130031,-0.28222615,0.042734932,0.27233443],[-0.0014444552,-1.0933362,0.003359957,-0.15052961,0.37165833,1.7087307,-1.4971043,0.673802,-0.108465396,-0.088505454,0.21561684,0.6252258,-0.15019403,-0.34727478,0.08958758,-0.1723621,-0.25557828,0.29778987]],"activation":"σ"},{"dense_2_W":[[0.20810607,-0.13481373,-0.15003344,-0.6067625,-0.31937903,-0.37380016,0.4666911],[0.71834767,0.15669753,-0.11389502,0.27131546,-0.060118295,0.039282244,0.52961355],[-0.27200606,0.43380752,-0.106262706,-0.49363258,0.35659012,-0.01706296,-1.0807354],[-0.79583454,-0.66428274,-0.42157334,-0.52979547,-0.2803004,-0.57637554,0.084149174],[0.1230394,-0.57023287,0.28639448,-0.506744,-0.9920808,0.7510901,0.8584937],[-0.41256845,0.35423175,0.13009231,0.5198333,0.7576573,-0.7053181,-0.3390053],[-0.15090485,0.33375353,-0.64164793,0.57386595,0.25700107,-0.15245672,-0.49020413],[-0.50120676,0.5210849,-0.3282951,0.4276323,1.0257447,-1.0398436,-0.8911315],[0.11402861,-0.41491523,0.247507,-0.39776865,-0.6089287,0.20646147,0.6692102],[0.09411492,0.31819808,0.41330767,0.024243973,0.2314893,0.08529045,0.14911193],[0.5179265,0.20473345,-0.36762783,-0.009045922,-0.19436747,-0.548099,-0.08656279],[-0.69121784,-0.14201449,0.3887621,0.10787631,1.0371574,-0.64747447,-0.73437464],[-0.07860244,-0.597764,0.019551078,0.010199122,-0.7493642,0.66619915,0.121666186]],"activation":"σ","dense_2_b":[[-0.04259814],[-0.053309314],[-0.28045595],[-0.23801634],[0.00944943],[-0.109921046],[-0.037457183],[0.24627604],[-0.0069056284],[-0.04725389],[-0.03361769],[0.044101644],[-0.0009850285]]},{"dense_3_W":[[-0.19631335,-0.47722518,-0.019853225,-0.29363275,-0.625923,0.35145283,0.62401354,0.14337404,-0.25707108,-0.51310915,0.2565006,0.61742216,-0.07952821],[0.5868974,-0.36786363,-0.0177682,-0.45121866,0.68608487,-0.55178356,-0.35236016,-0.7226455,-0.015394288,-0.10650332,0.5391039,0.17791761,0.34196886],[0.16821232,-0.35580373,0.26906335,0.41736925,-0.6979869,0.41801625,0.34707317,0.7784011,-0.10002936,0.32485273,-0.3644285,0.49092358,-0.43872768]],"activation":"identity","dense_3_b":[[-0.019481273],[0.031026587],[-0.03595322]]},{"dense_4_W":[[-0.72340566,0.21244061,-0.67248434]],"dense_4_b":[[0.026229527]],"activation":"identity"}]}
|
||||
@@ -0,0 +1 @@
|
||||
{"input_std":[[9.342214],[1.5915664],[0.60113484],[0.048193663],[1.5680411],[1.57577],[1.5836853],[1.5711677],[1.5445132],[1.5007596],[1.4529978],[0.047915205],[0.04800539],[0.04808892],[0.04822398],[0.04817677],[0.047881734],[0.047405947]],"model_test_loss":0.019342588260769844,"input_size":18,"current_date_and_time":"2023-08-05_06-09-11","input_mean":[[22.757933],[-0.016342578],[-0.001405228],[-0.014619173],[-0.018091483],[-0.018382493],[-0.019270267],[-0.018759886],[-0.019559544],[-0.017848592],[-0.020014366],[-0.014564899],[-0.01457757],[-0.014600966],[-0.014757987],[-0.014915743],[-0.015121007],[-0.015359475]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.045577038],[-2.8160777],[-0.21418032],[2.9552083],[-0.06852827],[0.029141279],[-0.053249933]],"dense_1_W":[[0.0010664682,1.3813498,-8.153277,-0.009101015,-0.1484779,1.0008405,-1.5930991,-1.195274,0.6136762,0.3560462,-0.30077773,0.93531257,0.14550218,-0.40074933,-0.21716364,0.13834935,-0.0658936,-0.49427196],[-0.7338253,-0.037501235,-0.49922377,-1.0526338,-0.46141604,-1.3649396,0.7309814,-0.91667104,0.044683233,-0.18628967,-0.20878822,-0.2678598,0.48446324,0.51164204,0.09547851,0.41491362,-0.4524314,0.16594785],[-0.0032175342,3.5451593,-0.11421584,-0.2511033,0.27893174,0.6384996,0.80790603,1.3835542,1.8000495,2.249461,1.4026878,0.7851167,-0.35052946,0.041820426,-0.39790183,0.45703772,-0.30459648,0.18287674],[0.72289664,-0.77138704,-0.5025135,-0.49323097,-0.39893976,-0.8779149,0.6848096,-0.58729875,-0.16643623,-0.14427555,-0.15013328,-0.11467142,-0.27280045,0.48508415,0.52080667,-0.0029569597,-0.28170735,0.06367212],[0.00014026981,-2.0353239,0.0060915067,0.4794941,1.1882906,-1.512865,0.80642134,-0.26044324,-0.56213886,-0.365122,0.55078083,-0.34897435,-0.32031298,0.46106994,0.37792024,-0.14116105,0.15597256,-0.32811671],[0.0011494387,1.9060265,8.3941965,-0.3078165,-0.9530427,0.6589645,-1.2487054,-0.541133,0.13451256,0.2805466,-0.33187166,0.9900705,0.2821258,-0.51270896,-0.5130031,-0.28222615,0.042734932,0.27233443],[-0.0014444552,-1.0933362,0.003359957,-0.15052961,0.37165833,1.7087307,-1.4971043,0.673802,-0.108465396,-0.088505454,0.21561684,0.6252258,-0.15019403,-0.34727478,0.08958758,-0.1723621,-0.25557828,0.29778987]],"activation":"σ"},{"dense_2_W":[[0.20810607,-0.13481373,-0.15003344,-0.6067625,-0.31937903,-0.37380016,0.4666911],[0.71834767,0.15669753,-0.11389502,0.27131546,-0.060118295,0.039282244,0.52961355],[-0.27200606,0.43380752,-0.106262706,-0.49363258,0.35659012,-0.01706296,-1.0807354],[-0.79583454,-0.66428274,-0.42157334,-0.52979547,-0.2803004,-0.57637554,0.084149174],[0.1230394,-0.57023287,0.28639448,-0.506744,-0.9920808,0.7510901,0.8584937],[-0.41256845,0.35423175,0.13009231,0.5198333,0.7576573,-0.7053181,-0.3390053],[-0.15090485,0.33375353,-0.64164793,0.57386595,0.25700107,-0.15245672,-0.49020413],[-0.50120676,0.5210849,-0.3282951,0.4276323,1.0257447,-1.0398436,-0.8911315],[0.11402861,-0.41491523,0.247507,-0.39776865,-0.6089287,0.20646147,0.6692102],[0.09411492,0.31819808,0.41330767,0.024243973,0.2314893,0.08529045,0.14911193],[0.5179265,0.20473345,-0.36762783,-0.009045922,-0.19436747,-0.548099,-0.08656279],[-0.69121784,-0.14201449,0.3887621,0.10787631,1.0371574,-0.64747447,-0.73437464],[-0.07860244,-0.597764,0.019551078,0.010199122,-0.7493642,0.66619915,0.121666186]],"activation":"σ","dense_2_b":[[-0.04259814],[-0.053309314],[-0.28045595],[-0.23801634],[0.00944943],[-0.109921046],[-0.037457183],[0.24627604],[-0.0069056284],[-0.04725389],[-0.03361769],[0.044101644],[-0.0009850285]]},{"dense_3_W":[[-0.19631335,-0.47722518,-0.019853225,-0.29363275,-0.625923,0.35145283,0.62401354,0.14337404,-0.25707108,-0.51310915,0.2565006,0.61742216,-0.07952821],[0.5868974,-0.36786363,-0.0177682,-0.45121866,0.68608487,-0.55178356,-0.35236016,-0.7226455,-0.015394288,-0.10650332,0.5391039,0.17791761,0.34196886],[0.16821232,-0.35580373,0.26906335,0.41736925,-0.6979869,0.41801625,0.34707317,0.7784011,-0.10002936,0.32485273,-0.3644285,0.49092358,-0.43872768]],"activation":"identity","dense_3_b":[[-0.019481273],[0.031026587],[-0.03595322]]},{"dense_4_W":[[-0.72340566,0.21244061,-0.67248434]],"dense_4_b":[[0.026229527]],"activation":"identity"}]}
|
||||
@@ -0,0 +1 @@
|
||||
{"input_std":[[9.342214],[1.5915664],[0.60113484],[0.048193663],[1.5680411],[1.57577],[1.5836853],[1.5711677],[1.5445132],[1.5007596],[1.4529978],[0.047915205],[0.04800539],[0.04808892],[0.04822398],[0.04817677],[0.047881734],[0.047405947]],"model_test_loss":0.019342588260769844,"input_size":18,"current_date_and_time":"2023-08-05_06-09-11","input_mean":[[22.757933],[-0.016342578],[-0.001405228],[-0.014619173],[-0.018091483],[-0.018382493],[-0.019270267],[-0.018759886],[-0.019559544],[-0.017848592],[-0.020014366],[-0.014564899],[-0.01457757],[-0.014600966],[-0.014757987],[-0.014915743],[-0.015121007],[-0.015359475]],"input_vars":["v_ego","lateral_accel","lateral_jerk","roll","lateral_accel_m03","lateral_accel_m02","lateral_accel_m01","lateral_accel_p03","lateral_accel_p06","lateral_accel_p10","lateral_accel_p15","roll_m03","roll_m02","roll_m01","roll_p03","roll_p06","roll_p10","roll_p15"],"output_size":1,"layers":[{"dense_1_b":[[-0.045577038],[-2.8160777],[-0.21418032],[2.9552083],[-0.06852827],[0.029141279],[-0.053249933]],"dense_1_W":[[0.0010664682,1.3813498,-8.153277,-0.009101015,-0.1484779,1.0008405,-1.5930991,-1.195274,0.6136762,0.3560462,-0.30077773,0.93531257,0.14550218,-0.40074933,-0.21716364,0.13834935,-0.0658936,-0.49427196],[-0.7338253,-0.037501235,-0.49922377,-1.0526338,-0.46141604,-1.3649396,0.7309814,-0.91667104,0.044683233,-0.18628967,-0.20878822,-0.2678598,0.48446324,0.51164204,0.09547851,0.41491362,-0.4524314,0.16594785],[-0.0032175342,3.5451593,-0.11421584,-0.2511033,0.27893174,0.6384996,0.80790603,1.3835542,1.8000495,2.249461,1.4026878,0.7851167,-0.35052946,0.041820426,-0.39790183,0.45703772,-0.30459648,0.18287674],[0.72289664,-0.77138704,-0.5025135,-0.49323097,-0.39893976,-0.8779149,0.6848096,-0.58729875,-0.16643623,-0.14427555,-0.15013328,-0.11467142,-0.27280045,0.48508415,0.52080667,-0.0029569597,-0.28170735,0.06367212],[0.00014026981,-2.0353239,0.0060915067,0.4794941,1.1882906,-1.512865,0.80642134,-0.26044324,-0.56213886,-0.365122,0.55078083,-0.34897435,-0.32031298,0.46106994,0.37792024,-0.14116105,0.15597256,-0.32811671],[0.0011494387,1.9060265,8.3941965,-0.3078165,-0.9530427,0.6589645,-1.2487054,-0.541133,0.13451256,0.2805466,-0.33187166,0.9900705,0.2821258,-0.51270896,-0.5130031,-0.28222615,0.042734932,0.27233443],[-0.0014444552,-1.0933362,0.003359957,-0.15052961,0.37165833,1.7087307,-1.4971043,0.673802,-0.108465396,-0.088505454,0.21561684,0.6252258,-0.15019403,-0.34727478,0.08958758,-0.1723621,-0.25557828,0.29778987]],"activation":"σ"},{"dense_2_W":[[0.20810607,-0.13481373,-0.15003344,-0.6067625,-0.31937903,-0.37380016,0.4666911],[0.71834767,0.15669753,-0.11389502,0.27131546,-0.060118295,0.039282244,0.52961355],[-0.27200606,0.43380752,-0.106262706,-0.49363258,0.35659012,-0.01706296,-1.0807354],[-0.79583454,-0.66428274,-0.42157334,-0.52979547,-0.2803004,-0.57637554,0.084149174],[0.1230394,-0.57023287,0.28639448,-0.506744,-0.9920808,0.7510901,0.8584937],[-0.41256845,0.35423175,0.13009231,0.5198333,0.7576573,-0.7053181,-0.3390053],[-0.15090485,0.33375353,-0.64164793,0.57386595,0.25700107,-0.15245672,-0.49020413],[-0.50120676,0.5210849,-0.3282951,0.4276323,1.0257447,-1.0398436,-0.8911315],[0.11402861,-0.41491523,0.247507,-0.39776865,-0.6089287,0.20646147,0.6692102],[0.09411492,0.31819808,0.41330767,0.024243973,0.2314893,0.08529045,0.14911193],[0.5179265,0.20473345,-0.36762783,-0.009045922,-0.19436747,-0.548099,-0.08656279],[-0.69121784,-0.14201449,0.3887621,0.10787631,1.0371574,-0.64747447,-0.73437464],[-0.07860244,-0.597764,0.019551078,0.010199122,-0.7493642,0.66619915,0.121666186]],"activation":"σ","dense_2_b":[[-0.04259814],[-0.053309314],[-0.28045595],[-0.23801634],[0.00944943],[-0.109921046],[-0.037457183],[0.24627604],[-0.0069056284],[-0.04725389],[-0.03361769],[0.044101644],[-0.0009850285]]},{"dense_3_W":[[-0.19631335,-0.47722518,-0.019853225,-0.29363275,-0.625923,0.35145283,0.62401354,0.14337404,-0.25707108,-0.51310915,0.2565006,0.61742216,-0.07952821],[0.5868974,-0.36786363,-0.0177682,-0.45121866,0.68608487,-0.55178356,-0.35236016,-0.7226455,-0.015394288,-0.10650332,0.5391039,0.17791761,0.34196886],[0.16821232,-0.35580373,0.26906335,0.41736925,-0.6979869,0.41801625,0.34707317,0.7784011,-0.10002936,0.32485273,-0.3644285,0.49092358,-0.43872768]],"activation":"identity","dense_3_b":[[-0.019481273],[0.031026587],[-0.03595322]]},{"dense_4_W":[[-0.72340566,0.21244061,-0.67248434]],"dense_4_b":[[0.026229527]],"activation":"identity"}]}
|
||||
@@ -37,6 +37,7 @@ HOLIDAY_SLUGS = {
|
||||
}
|
||||
|
||||
THEME_COMPONENT_PARAMS = {
|
||||
"boot_logos": "BootLogoToDownload",
|
||||
"colors": "ColorToDownload",
|
||||
"distance_icons": "DistanceIconToDownload",
|
||||
"icons": "IconToDownload",
|
||||
@@ -93,8 +94,16 @@ class ThemeManager:
|
||||
steering_wheel_save_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(steering_wheel_image_path, steering_wheel_save_path)
|
||||
|
||||
default_boot_logo_path = Path(__file__).parent / "other_images/frogpilot_boot_logo.png"
|
||||
boot_logo_save_path = THEME_SAVE_PATH / "bootlogos/starpilot.png"
|
||||
boot_logo_save_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if not boot_logo_save_path.exists():
|
||||
shutil.copy2(default_boot_logo_path, boot_logo_save_path)
|
||||
|
||||
def download_theme(self, theme_component, theme_name, asset_param, frogpilot_toggles):
|
||||
self.downloading_theme = True
|
||||
allow_unknown_size = theme_component in {"boot_logos", "steering_wheels"}
|
||||
name_candidates = [theme_name]
|
||||
|
||||
repo_url = get_repository_url()
|
||||
if not repo_url:
|
||||
@@ -102,7 +111,12 @@ class ThemeManager:
|
||||
self.downloading_theme = False
|
||||
return
|
||||
|
||||
if theme_component == "distance_icons":
|
||||
if theme_component == "boot_logos":
|
||||
download_link = f"{repo_url}/Themes/bootlogo"
|
||||
download_path = THEME_SAVE_PATH / "bootlogos" / theme_name
|
||||
extensions = [".png", ".jpg", ".jpeg"]
|
||||
name_candidates = list(dict.fromkeys([theme_name, theme_name.replace("_", "-"), theme_name.replace("-", "_")]))
|
||||
elif theme_component == "distance_icons":
|
||||
download_link = f"{repo_url}/Distance-Icons/{theme_name}"
|
||||
download_path = THEME_SAVE_PATH / "theme_packs" / theme_name / theme_component
|
||||
extensions = [".zip"]
|
||||
@@ -117,37 +131,40 @@ class ThemeManager:
|
||||
|
||||
for extension in extensions:
|
||||
theme_path = download_path.with_suffix(extension)
|
||||
theme_url = download_link + extension
|
||||
theme_urls = [f"{download_link}/{candidate}{extension}" for candidate in name_candidates]
|
||||
if theme_component != "boot_logos":
|
||||
theme_urls = [download_link + extension]
|
||||
|
||||
delete_file(theme_path)
|
||||
|
||||
print(f"Downloading theme from GitHub: {theme_name}")
|
||||
download_file(CANCEL_DOWNLOAD_PARAM, theme_path, DOWNLOAD_PROGRESS_PARAM, theme_url, asset_param, params_memory)
|
||||
|
||||
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
|
||||
for theme_url in theme_urls:
|
||||
delete_file(theme_path)
|
||||
handle_error(None, "Download cancelled...", "Download cancelled...", asset_param, DOWNLOAD_PROGRESS_PARAM, params_memory)
|
||||
|
||||
self.downloading_theme = False
|
||||
return
|
||||
print(f"Downloading theme from GitHub: {theme_name}")
|
||||
download_file(CANCEL_DOWNLOAD_PARAM, theme_path, DOWNLOAD_PROGRESS_PARAM, theme_url, asset_param, params_memory, allow_unknown_size=allow_unknown_size, suppress_errors=allow_unknown_size)
|
||||
|
||||
if verify_download(theme_path, theme_url):
|
||||
print(f"Theme {theme_name} downloaded and verified successfully from GitHub!")
|
||||
self.update_theme_size(theme_component, theme_name, theme_path.stat().st_size)
|
||||
if params_memory.get_bool(CANCEL_DOWNLOAD_PARAM):
|
||||
delete_file(theme_path)
|
||||
handle_error(None, "Download cancelled...", "Download cancelled...", asset_param, DOWNLOAD_PROGRESS_PARAM, params_memory)
|
||||
|
||||
if extension == ".zip":
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Unpacking theme...")
|
||||
extract_zip(theme_path, download_path)
|
||||
self.downloading_theme = False
|
||||
return
|
||||
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
|
||||
params_memory.remove(asset_param)
|
||||
if verify_download(theme_path, theme_url, allow_unknown_size=allow_unknown_size):
|
||||
print(f"Theme {theme_name} downloaded and verified successfully from GitHub!")
|
||||
self.update_theme_size(theme_component, theme_name, theme_path.stat().st_size)
|
||||
|
||||
self.downloading_theme = False
|
||||
if extension == ".zip":
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Unpacking theme...")
|
||||
extract_zip(theme_path, download_path)
|
||||
|
||||
self.update_themes(frogpilot_toggles)
|
||||
return
|
||||
elif self.handle_verification_failure(extension, theme_component, theme_name, asset_param, theme_path, download_path, frogpilot_toggles):
|
||||
return
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
|
||||
params_memory.remove(asset_param)
|
||||
|
||||
self.downloading_theme = False
|
||||
|
||||
self.update_themes(frogpilot_toggles)
|
||||
return
|
||||
elif self.handle_verification_failure(extension, theme_component, theme_name, asset_param, theme_path, download_path, frogpilot_toggles):
|
||||
return
|
||||
|
||||
handle_error(download_path, "Download failed...", "Download failed...", asset_param, DOWNLOAD_PROGRESS_PARAM, params_memory)
|
||||
self.downloading_theme = False
|
||||
@@ -158,7 +175,7 @@ class ThemeManager:
|
||||
|
||||
repo_encoded = quote_plus(RESOURCES_REPO)
|
||||
|
||||
assets = {"themes": {}, "wheels": []}
|
||||
assets = {"boot_logos": [], "themes": {}, "wheels": []}
|
||||
try:
|
||||
def list_files(branch):
|
||||
if is_github:
|
||||
@@ -234,6 +251,20 @@ class ThemeManager:
|
||||
theme_name, sub_path = item["path"].split("/", 1)
|
||||
theme_path = sub_path.lower()
|
||||
|
||||
if theme_name.lower() == "bootlogo":
|
||||
if Path(sub_path).suffix.lower() not in (".png", ".jpg", ".jpeg"):
|
||||
continue
|
||||
|
||||
assets["boot_logos"].append(sub_path)
|
||||
logo_name = Path(sub_path).stem
|
||||
local_files = list((THEME_SAVE_PATH / "bootlogos").glob(f"{logo_name}.*"))
|
||||
if local_files and expected_size > 0:
|
||||
local_size = self.theme_sizes.get("boot_logos", {}).get(logo_name)
|
||||
if local_size != expected_size:
|
||||
print(f"boot logo {logo_name} is outdated, redownloading...")
|
||||
self.download_theme("boot_logos", logo_name, THEME_COMPONENT_PARAMS["boot_logos"], frogpilot_toggles)
|
||||
continue
|
||||
|
||||
for key in ("colors", "icons", "signals", "sounds"):
|
||||
if key in theme_path:
|
||||
assets["themes"].setdefault(theme_name, set()).add(key)
|
||||
@@ -246,6 +277,7 @@ class ThemeManager:
|
||||
self.download_theme(key, theme_name, THEME_COMPONENT_PARAMS[key], frogpilot_toggles)
|
||||
break
|
||||
|
||||
assets["boot_logos"].sort()
|
||||
assets["themes"] = {key: sorted(list(value)) for key, value in assets["themes"].items()}
|
||||
assets["wheels"].sort()
|
||||
return assets
|
||||
@@ -265,7 +297,7 @@ class ThemeManager:
|
||||
parts = base.replace("_", "-").split("-")
|
||||
capitalized_parts = [part.capitalize() for part in parts if part]
|
||||
|
||||
if len(capitalized_parts) > 1 and component != "steering_wheels":
|
||||
if len(capitalized_parts) > 1 and component not in {"boot_logos", "steering_wheels"}:
|
||||
display = f"{capitalized_parts[0]} ({' '.join(capitalized_parts[1:])})"
|
||||
else:
|
||||
display = " ".join(capitalized_parts)
|
||||
@@ -321,37 +353,44 @@ class ThemeManager:
|
||||
}
|
||||
|
||||
def handle_verification_failure(self, extension, theme_component, theme_name, asset_param, theme_path, download_path, frogpilot_toggles):
|
||||
if theme_component == "distance_icons":
|
||||
allow_unknown_size = theme_component in {"boot_logos", "steering_wheels"}
|
||||
if theme_component == "boot_logos":
|
||||
download_link = f"{GITLAB_URL}/Themes/bootlogo"
|
||||
name_candidates = list(dict.fromkeys([theme_name, theme_name.replace("_", "-"), theme_name.replace("-", "_")]))
|
||||
elif theme_component == "distance_icons":
|
||||
download_link = f"{GITLAB_URL}/Distance-Icons/{theme_name}"
|
||||
name_candidates = [theme_name]
|
||||
elif theme_component == "steering_wheels":
|
||||
download_link = f"{GITLAB_URL}/Steering-Wheels/{theme_name}"
|
||||
name_candidates = [theme_name]
|
||||
else:
|
||||
download_link = f"{GITLAB_URL}/Themes/{theme_name}/{theme_component}"
|
||||
name_candidates = [theme_name]
|
||||
|
||||
delete_file(theme_path)
|
||||
for candidate in name_candidates:
|
||||
delete_file(theme_path)
|
||||
|
||||
theme_url = download_link + extension
|
||||
print(f"Downloading theme from GitLab: {theme_name}")
|
||||
download_file(CANCEL_DOWNLOAD_PARAM, theme_path, DOWNLOAD_PROGRESS_PARAM, theme_url, asset_param, params_memory)
|
||||
theme_url = f"{download_link}/{candidate}{extension}" if theme_component == "boot_logos" else download_link + extension
|
||||
print(f"Downloading theme from GitLab: {theme_name}")
|
||||
download_file(CANCEL_DOWNLOAD_PARAM, theme_path, DOWNLOAD_PROGRESS_PARAM, theme_url, asset_param, params_memory, allow_unknown_size=allow_unknown_size, suppress_errors=allow_unknown_size)
|
||||
|
||||
if verify_download(theme_path, theme_url):
|
||||
print(f"Theme {theme_name} downloaded and verified successfully from GitLab!")
|
||||
self.update_theme_size(theme_component, theme_name, theme_path.stat().st_size)
|
||||
if verify_download(theme_path, theme_url, allow_unknown_size=allow_unknown_size):
|
||||
print(f"Theme {theme_name} downloaded and verified successfully from GitLab!")
|
||||
self.update_theme_size(theme_component, theme_name, theme_path.stat().st_size)
|
||||
|
||||
if extension == ".zip":
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Unpacking theme...")
|
||||
extract_zip(theme_path, download_path)
|
||||
if extension == ".zip":
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Unpacking theme...")
|
||||
extract_zip(theme_path, download_path)
|
||||
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
|
||||
params_memory.remove(asset_param)
|
||||
params_memory.put(DOWNLOAD_PROGRESS_PARAM, "Downloaded!")
|
||||
params_memory.remove(asset_param)
|
||||
|
||||
self.downloading_theme = False
|
||||
self.downloading_theme = False
|
||||
|
||||
self.update_themes(frogpilot_toggles)
|
||||
return True
|
||||
self.update_themes(frogpilot_toggles)
|
||||
return True
|
||||
|
||||
handle_error(None, "Download failed...", "Download failed...", asset_param, DOWNLOAD_PROGRESS_PARAM, params_memory)
|
||||
self.downloading_theme = False
|
||||
# Let the caller continue trying alternate extensions before surfacing failure.
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
@@ -415,6 +454,8 @@ class ThemeManager:
|
||||
return random.choice(candidates) if candidates else "stock"
|
||||
|
||||
def update_active_theme(self, time_validated, frogpilot_toggles, boot_run=False, randomize_theme=False):
|
||||
boot_logo = getattr(frogpilot_toggles, "boot_logo", "stock")
|
||||
|
||||
if time_validated and frogpilot_toggles.holiday_themes:
|
||||
self.holiday_theme = self.update_holiday()
|
||||
else:
|
||||
@@ -422,6 +463,7 @@ class ThemeManager:
|
||||
|
||||
if self.holiday_theme != "stock":
|
||||
asset_mappings = {
|
||||
"boot_logo": ("boot_logo", boot_logo),
|
||||
"color_scheme": ("colors", self.holiday_theme),
|
||||
"distance_icons": ("distance_icons", self.holiday_theme),
|
||||
"icon_pack": ("icons", self.holiday_theme),
|
||||
@@ -434,6 +476,7 @@ class ThemeManager:
|
||||
selected_theme = self.randomize_theme_asset(available_themes)
|
||||
|
||||
asset_mappings = {
|
||||
"boot_logo": ("boot_logo", boot_logo),
|
||||
"color_scheme": ("colors", selected_theme.replace("-animated", "")),
|
||||
"distance_icons": ("distance_icons", self.randomize_distance_icons(available_themes, selected_theme.replace("-animated", ""))),
|
||||
"icon_pack": ("icons", selected_theme),
|
||||
@@ -444,6 +487,7 @@ class ThemeManager:
|
||||
|
||||
elif not frogpilot_toggles.random_themes:
|
||||
asset_mappings = {
|
||||
"boot_logo": ("boot_logo", boot_logo),
|
||||
"color_scheme": ("colors", frogpilot_toggles.color_scheme),
|
||||
"distance_icons": ("distance_icons", frogpilot_toggles.distance_icons),
|
||||
"icon_pack": ("icons", frogpilot_toggles.icon_pack),
|
||||
@@ -458,7 +502,9 @@ class ThemeManager:
|
||||
for asset, (asset_type, current_value) in asset_mappings.items():
|
||||
print(f"Updating {asset}: {asset_type} with value {current_value}")
|
||||
|
||||
if asset_type == "wheel_image":
|
||||
if asset_type == "boot_logo":
|
||||
self.update_boot_logo(current_value)
|
||||
elif asset_type == "wheel_image":
|
||||
self.update_wheel_image(current_value, boot_run=boot_run)
|
||||
else:
|
||||
self.update_theme_asset(asset_type, current_value, boot_run=boot_run)
|
||||
@@ -499,9 +545,16 @@ class ThemeManager:
|
||||
save_location.symlink_to(asset_location, target_is_directory=True)
|
||||
print(f"Linked {save_location} to {asset_location}")
|
||||
|
||||
def update_theme_params(self, downloadable_colors, downloadable_distance_icons, downloadable_icons, downloadable_signals, downloadable_sounds, downloadable_wheels):
|
||||
def update_theme_params(self, downloadable_boot_logos, downloadable_colors, downloadable_distance_icons, downloadable_icons, downloadable_signals, downloadable_sounds, downloadable_wheels):
|
||||
def update_param(key, assets, subfolder):
|
||||
if subfolder == "steering_wheels":
|
||||
if subfolder == "boot_logos":
|
||||
themes_path = THEME_SAVE_PATH / "bootlogos"
|
||||
existing_assets = {item.stem.lower() for item in themes_path.glob("*") if item.is_file()}
|
||||
pending_assets = [asset for asset in assets if asset.lower() not in existing_assets]
|
||||
params.put(key, ",".join(sorted(set(pending_assets))))
|
||||
print(f"{key} updated successfully")
|
||||
return
|
||||
elif subfolder == "steering_wheels":
|
||||
themes_path = THEME_SAVE_PATH / subfolder
|
||||
existing_assets = {self.format_name(item.name, "steering_wheels") for item in themes_path.glob("*") if item.is_file()}
|
||||
else:
|
||||
@@ -511,6 +564,7 @@ class ThemeManager:
|
||||
params.put(key, ",".join(sorted(set(assets) - existing_assets)))
|
||||
print(f"{key} updated successfully")
|
||||
|
||||
update_param("DownloadableBootLogos", downloadable_boot_logos, "boot_logos")
|
||||
update_param("DownloadableColors", downloadable_colors, "colors")
|
||||
update_param("DownloadableDistanceIcons", downloadable_distance_icons, "distance_icons")
|
||||
update_param("DownloadableIcons", downloadable_icons, "icons")
|
||||
@@ -529,12 +583,20 @@ class ThemeManager:
|
||||
theme_name = self.format_name(theme_dir.name, "theme_packs")
|
||||
downloaded_themes[theme_name] = sorted(components)
|
||||
|
||||
(THEME_SAVE_PATH / "bootlogos").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
downloaded_boot_logos = []
|
||||
for boot_logo_file in (THEME_SAVE_PATH / "bootlogos").iterdir():
|
||||
if boot_logo_file.is_file():
|
||||
downloaded_boot_logos.append(self.format_name(boot_logo_file.name, "boot_logos"))
|
||||
|
||||
downloaded_wheels = []
|
||||
for wheel_file in (THEME_SAVE_PATH / "steering_wheels").iterdir():
|
||||
if wheel_file.is_file():
|
||||
downloaded_wheels.append(self.format_name(wheel_file.name, "steering_wheels"))
|
||||
|
||||
params.put("ThemesDownloaded", json.dumps({
|
||||
"boot_logos": sorted(downloaded_boot_logos),
|
||||
"themes": {key: downloaded_themes[key] for key in sorted(downloaded_themes)},
|
||||
"steering_wheels": sorted(downloaded_wheels)
|
||||
}))
|
||||
@@ -542,7 +604,9 @@ class ThemeManager:
|
||||
print("ThemesDownloaded updated successfully")
|
||||
|
||||
def update_theme_size(self, theme_component, theme_name, file_size):
|
||||
if theme_component == "steering_wheels":
|
||||
if theme_component == "boot_logos":
|
||||
key = "boot_logos"
|
||||
elif theme_component == "steering_wheels":
|
||||
key = "wheels"
|
||||
else:
|
||||
key = "themes"
|
||||
@@ -550,7 +614,7 @@ class ThemeManager:
|
||||
if key not in self.theme_sizes:
|
||||
self.theme_sizes[key] = {}
|
||||
|
||||
if key == "wheels":
|
||||
if key in {"boot_logos", "wheels"}:
|
||||
self.theme_sizes[key][theme_name] = file_size
|
||||
else:
|
||||
if theme_name not in self.theme_sizes[key]:
|
||||
@@ -572,6 +636,7 @@ class ThemeManager:
|
||||
if not assets:
|
||||
return
|
||||
|
||||
downloadable_boot_logos = []
|
||||
downloadable_colors = []
|
||||
downloadable_distance_icons = []
|
||||
downloadable_icons = []
|
||||
@@ -593,8 +658,10 @@ class ThemeManager:
|
||||
if "sounds" in available_assets:
|
||||
downloadable_sounds.append(theme_name)
|
||||
|
||||
downloadable_boot_logos = [Path(boot_logo).stem for boot_logo in assets["boot_logos"]]
|
||||
downloadable_wheels = [self.format_name(wheel, "steering_wheels") for wheel in assets["wheels"]]
|
||||
|
||||
print(f"Downloadable Boot Logos: {downloadable_boot_logos}")
|
||||
print(f"Downloadable Colors: {downloadable_colors}")
|
||||
print(f"Downloadable Icons: {downloadable_icons}")
|
||||
print(f"Downloadable Signals: {downloadable_signals}")
|
||||
@@ -605,7 +672,21 @@ class ThemeManager:
|
||||
if boot_run:
|
||||
self.validate_themes(downloadable_colors, downloadable_distance_icons, downloadable_icons, downloadable_signals, downloadable_sounds, downloadable_wheels, frogpilot_toggles)
|
||||
|
||||
self.update_theme_params(downloadable_colors, downloadable_distance_icons, downloadable_icons, downloadable_signals, downloadable_sounds, downloadable_wheels)
|
||||
self.update_theme_params(downloadable_boot_logos, downloadable_colors, downloadable_distance_icons, downloadable_icons, downloadable_signals, downloadable_sounds, downloadable_wheels)
|
||||
|
||||
@staticmethod
|
||||
def update_boot_logo(image):
|
||||
boot_logo_location = Path(__file__).parent / "other_images/frogpilot_boot_logo.png"
|
||||
image_name = image.replace(" ", "_").lower()
|
||||
source_file = next((file for file in (THEME_SAVE_PATH / "bootlogos").glob("*") if file.is_file() and file.stem.lower() == image_name), boot_logo_location)
|
||||
|
||||
# Avoid SameFileError when the selected/fallback source is already the active boot logo.
|
||||
if source_file.resolve() == boot_logo_location.resolve():
|
||||
print(f"Boot logo unchanged: {boot_logo_location}")
|
||||
return
|
||||
|
||||
shutil.copy2(source_file, boot_logo_location)
|
||||
print(f"Copied {source_file} to {boot_logo_location}")
|
||||
|
||||
def update_wheel_image(self, image, boot_run=False, random_event=False):
|
||||
wheel_save_location = ACTIVE_THEME_PATH / "steering_wheel"
|
||||
@@ -641,6 +722,15 @@ class ThemeManager:
|
||||
def validate_themes(self, downloadable_colors, downloadable_distance_icons, downloadable_icons, downloadable_signals, downloadable_sounds, downloadable_wheels, frogpilot_toggles):
|
||||
downloaded_data = json.loads(params.get("ThemesDownloaded") or "{}")
|
||||
|
||||
boot_logos_path = THEME_SAVE_PATH / "bootlogos"
|
||||
for display_name in downloaded_data.get("boot_logos", []):
|
||||
file_stem = display_name.replace(" ", "_").lower()
|
||||
matching_files = list(boot_logos_path.glob(f"{file_stem}.*"))
|
||||
if not matching_files:
|
||||
print(f"Missing boot logo '{display_name}'. Downloading...")
|
||||
self.download_theme("boot_logos", file_stem, THEME_COMPONENT_PARAMS["boot_logos"], frogpilot_toggles)
|
||||
self.update_active_theme(True, frogpilot_toggles)
|
||||
|
||||
for display_name, components in downloaded_data.get("themes", {}).items():
|
||||
raw_name = display_name.lower().replace(" ", "_").replace("(", "").replace(")", "")
|
||||
theme_folder_name = raw_name.replace("_animated", "-animated")
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -2,6 +2,7 @@
|
||||
import json
|
||||
import math
|
||||
import numpy as np
|
||||
import os
|
||||
import requests
|
||||
import shutil
|
||||
import subprocess
|
||||
@@ -22,7 +23,7 @@ from opendbc.can.parser import CANParser
|
||||
from openpilot.common.realtime import DT_DMON, DT_HW
|
||||
from openpilot.selfdrive.car.toyota.carcontroller import LOCK_CMD
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
from panda import Panda
|
||||
from panda import Panda, FW_PATH
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_variables import EARTH_RADIUS, KONIK_PATH, MAPD_PATH, MAPS_PATH, params, params_cache, params_memory
|
||||
|
||||
@@ -151,11 +152,21 @@ def extract_zip(zip_file, extract_path):
|
||||
print(f"Extraction completed: {zip_file} has been removed")
|
||||
|
||||
def flash_panda():
|
||||
remote_start = params.get_bool("RemoteStartBootsComma")
|
||||
for serial in Panda.list():
|
||||
try:
|
||||
panda = Panda(serial)
|
||||
flash_fn = None
|
||||
if remote_start:
|
||||
app_fn = panda.get_mcu_type().config.app_fn
|
||||
remote_fn = "panda_h7_remote.bin.signed" if app_fn == "panda_h7.bin.signed" else "panda_remote.bin.signed"
|
||||
candidate = os.path.join(FW_PATH, remote_fn)
|
||||
if os.path.isfile(candidate):
|
||||
flash_fn = candidate
|
||||
else:
|
||||
print(f"Remote-start panda firmware missing: {candidate}. Falling back to default firmware.")
|
||||
panda.reset(enter_bootstub=True)
|
||||
panda.flash()
|
||||
panda.flash(fn=flash_fn)
|
||||
panda.close()
|
||||
except Exception as exception:
|
||||
print(f"Error flashing Panda {serial}: {exception}")
|
||||
|
||||
@@ -13,7 +13,8 @@ from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.selfdrive.car import gen_empty_fingerprint
|
||||
from openpilot.selfdrive.car.car_helpers import interfaces
|
||||
from openpilot.selfdrive.car.gm.values import GMFlags
|
||||
from openpilot.selfdrive.car.gm.values import EV_CAR as GM_EV_CAR, GMFlags
|
||||
from openpilot.selfdrive.car.hyundai.values import EV_CAR as HYUNDAI_EV_CAR
|
||||
from openpilot.selfdrive.car.interfaces import CarInterfaceBase
|
||||
from openpilot.selfdrive.car.mock.interface import CarInterface
|
||||
from openpilot.selfdrive.car.mock.values import CAR as MOCK
|
||||
@@ -32,6 +33,7 @@ params_memory = Params("/dev/shm/params")
|
||||
|
||||
GearShifter = car.CarState.GearShifter
|
||||
SafetyModel = car.CarParams.SafetyModel
|
||||
TransmissionType = car.CarParams.TransmissionType
|
||||
|
||||
CITY_SPEED_LIMIT = 25 # 55mph is typically the minimum speed for highways
|
||||
CRUISING_SPEED = 5 # Roughly the speed cars go when not touching the gas while in drive
|
||||
@@ -94,6 +96,8 @@ EXCLUDED_KEYS = {
|
||||
}
|
||||
|
||||
TINYGRAD_FILES = [
|
||||
("driving_off_policy_metadata.pkl", "off-policy metadata"),
|
||||
("driving_off_policy_tinygrad.pkl", "off-policy model"),
|
||||
("driving_policy_metadata.pkl", "policy metadata"),
|
||||
("driving_policy_tinygrad.pkl", "policy model"),
|
||||
("driving_vision_metadata.pkl", "vision metadata"),
|
||||
@@ -126,9 +130,12 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("AdjacentPath", "0", 3, "0"),
|
||||
("AdjacentPathMetrics", "0", 3, "0"),
|
||||
("AdvancedCustomUI", "0", 2, "0"),
|
||||
("AdvancedLateralTune", "0", 2, "0"),
|
||||
("AdvancedLateralTune", "1", 2, "0"),
|
||||
("AdvancedLongitudinalTune", "0", 3, "0"),
|
||||
("EVTuning", "", 3, "0"),
|
||||
("TruckTuning", "0", 3, "0"),
|
||||
("AggressiveFollow", "1.25", 2, "1.25"),
|
||||
("AggressiveFollowHigh", "1.25", 2, "1.25"),
|
||||
("AggressiveJerkAcceleration", "50", 3, "50"),
|
||||
("AggressiveJerkDanger", "100", 3, "100"),
|
||||
("AggressiveJerkDeceleration", "50", 3, "50"),
|
||||
@@ -153,7 +160,8 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("BlacklistedModels", "", 2, ""),
|
||||
("BlindSpotMetrics", "1", 3, "0"),
|
||||
("BlindSpotPath", "1", 1, "0"),
|
||||
("BorderMetrics", "0", 3, "0"),
|
||||
("BootLogo", "starpilot", 0, "stock"),
|
||||
("BorderMetrics", "1", 3, "0"),
|
||||
("CalibratedLateralAcceleration", str(DEFAULT_LATERAL_ACCELERATION), 2, str(DEFAULT_LATERAL_ACCELERATION)),
|
||||
("CalibrationProgress", "0", 3, "0"),
|
||||
("CameraView", "3", 2, "0"),
|
||||
@@ -192,7 +200,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("CustomUI", "1", 1, "0"),
|
||||
("DecelerationProfile", "1", 2, "0"),
|
||||
("DeveloperMetrics", "1", 3, "0"),
|
||||
("DeveloperSidebar", "1", 3, "0"),
|
||||
("DeveloperSidebar", "0", 3, "0"),
|
||||
("DeveloperSidebarMetric1", "1", 3, "0"),
|
||||
("DeveloperSidebarMetric2", "2", 3, "0"),
|
||||
("DeveloperSidebarMetric3", "3", 3, "0"),
|
||||
@@ -201,7 +209,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("DeveloperSidebarMetric6", "6", 3, "0"),
|
||||
("DeveloperSidebarMetric7", "7", 3, "0"),
|
||||
("DeveloperWidgets", "1", 3, "0"),
|
||||
("DeveloperUI", "0", 3, "0"),
|
||||
("DeveloperUI", "1", 3, "0"),
|
||||
("DeviceManagement", "1", 1, "0"),
|
||||
("DeviceShutdown", "9", 1, "33"),
|
||||
("DisableOnroadUploads", "0", 2, "0"),
|
||||
@@ -219,7 +227,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("Fahrenheit", "0", 3, "0"),
|
||||
("FavoriteDestinations", "", 0, ""),
|
||||
("ForceAutoTune", "0", 2, "0"),
|
||||
("ForceAutoTuneOff", "0", 2, "0"),
|
||||
("ForceAutoTuneOff", "1", 2, "0"),
|
||||
("ForceFingerprint", "0", 2, "0"),
|
||||
("ForceMPHDashboard", "0", 2, "0"),
|
||||
("ForceStops", "0", 2, "0"),
|
||||
@@ -231,6 +239,8 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("FullMap", "0", 2, "0"),
|
||||
("GasRegenCmd", "1", 2, "0"),
|
||||
("GMPedalLongitudinal", "1", 2, "1"),
|
||||
("RedPanda", "0", 3, "0"),
|
||||
("RemoteStartBootsComma", "0", 3, "0"),
|
||||
("GithubSshKeys", "", 0, ""),
|
||||
("GithubUsername", "", 0, ""),
|
||||
("GoatScream", "0", 1, "0"),
|
||||
@@ -270,6 +280,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("LongitudinalActuatorDelay", "", 3, ""),
|
||||
("LongitudinalActuatorDelayStock", "", 3, ""),
|
||||
("LongitudinalTune", "1", 0, "0"),
|
||||
("TrailerLoad", "0", 2, "0"),
|
||||
("LongPitch", "1", 2, "0"),
|
||||
("LoudBlindspotAlert", "0", 0, "0"),
|
||||
("LowVoltageShutdown", str(VBATT_PAUSE_CHARGING), 2, str(VBATT_PAUSE_CHARGING)),
|
||||
@@ -329,13 +340,14 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("RecordFront", "0", 0, "0"),
|
||||
("RefuseVolume", "101", 2, "101"),
|
||||
("RelaxedFollow", "1.75", 2, "1.75"),
|
||||
("RelaxedFollowHigh", "1.75", 2, "1.75"),
|
||||
("RelaxedJerkAcceleration", "50", 3, "50"),
|
||||
("RelaxedJerkDanger", "100", 3, "100"),
|
||||
("RelaxedJerkDeceleration", "50", 3, "50"),
|
||||
("RelaxedJerkSpeed", "50", 3, "50"),
|
||||
("RelaxedJerkSpeedDecrease", "50", 3, "50"),
|
||||
("RelaxedPersonalityProfile", "1", 2, "0"),
|
||||
("ReverseCruise", "0", 1, "0"),
|
||||
("ReverseCruise", "1", 1, "0"),
|
||||
("RoadEdgesWidth", "2", 2, "2"),
|
||||
("RoadNameUI", "1", 2, "0"),
|
||||
("RotatingWheel", "1", 1, "0"),
|
||||
@@ -359,7 +371,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("ShownToggleDescriptions", "", 0, ""),
|
||||
("ShowSLCOffset", "1", 0, "0"),
|
||||
("ShowSpeedLimits", "1", 1, "0"),
|
||||
("ShowSteering", "0", 3, "0"),
|
||||
("ShowSteering", "1", 3, "0"),
|
||||
("ShowStoppingPoint", "0", 2, "0"),
|
||||
("ShowStoppingPointMetrics", "0", 2, "0"),
|
||||
("ShowStorageLeft", "0", 3, "0"),
|
||||
@@ -386,6 +398,7 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("StartupMessageBottom", "Human-tested, frog-approved 🐸", 0, "Always keep hands on wheel and eyes on road"),
|
||||
("StartupMessageTop", "Hop in and buckle up!", 0, "Be ready to take over at any time"),
|
||||
("StandardFollow", "1.45", 2, "1.45"),
|
||||
("StandardFollowHigh", "1.45", 2, "1.45"),
|
||||
("StandardJerkAcceleration", "50", 3, "50"),
|
||||
("StandardJerkDanger", "100", 3, "100"),
|
||||
("StandardJerkDeceleration", "50", 3, "50"),
|
||||
@@ -400,6 +413,8 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("SteerDelayStock", "", 3, ""),
|
||||
("SteerFriction", "", 3, ""),
|
||||
("SteerFrictionStock", "", 3, ""),
|
||||
("SteerOffset", "", 3, ""),
|
||||
("SteerOffsetStock", "", 3, ""),
|
||||
("SteerKP", "", 3, ""),
|
||||
("SteerKPStock", "", 3, ""),
|
||||
("SteerLatAccel", "", 3, ""),
|
||||
@@ -422,8 +437,8 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("TrafficJerkSpeed", "50", 3, "50"),
|
||||
("TrafficJerkSpeedDecrease", "50", 3, "50"),
|
||||
("TrafficPersonalityProfile", "1", 2, "0"),
|
||||
("TuningLevel", "0", 0, "0"),
|
||||
("TuningLevelConfirmed", "0", 0, "0"),
|
||||
("TuningLevel", "3", 0, "0"),
|
||||
("TuningLevelConfirmed", "1", 0, "0"),
|
||||
("TurnDesires", "0", 2, "0"),
|
||||
("UnlimitedLength", "1", 2, "0"),
|
||||
("UnlockDoors", "1", 0, "0"),
|
||||
@@ -441,7 +456,8 @@ frogpilot_default_params: list[tuple[str, str | bytes, int, str]] = [
|
||||
("WarningSoftVolume", "101", 2, "101"),
|
||||
("WheelIcon", "frog", 0, "stock"),
|
||||
("WheelSpeed", "0", 2, "0"),
|
||||
("StopDistance", "6", 3, "6")
|
||||
("StopDistance", "6", 3, "6"),
|
||||
("RecoveryPower", "1.0", 2, "1.0")
|
||||
]
|
||||
|
||||
misc_tuning_levels: list[tuple[str, str | bytes, int, str]] = [
|
||||
@@ -561,6 +577,7 @@ class FrogPilotVariables:
|
||||
toggle.has_pedal = CP.enableGasInterceptor
|
||||
has_radar = not CP.radarUnavailable
|
||||
toggle.has_sdsu = toggle.car_make == "toyota" and bool(CP.flags & ToyotaFlags.SMART_DSU.value)
|
||||
toggle.has_sascm = toggle.car_make == "gm" and bool(CP.flags & GMFlags.SASCM.value)
|
||||
has_sng = CP.autoResumeSng
|
||||
toggle.has_zss = toggle.car_make == "toyota" and bool(FPCP.fpFlags & ToyotaFrogPilotFlags.ZSS.value)
|
||||
is_angle_car = CP.steerControlType == car.CarParams.SteerControlType.angle
|
||||
@@ -604,14 +621,37 @@ class FrogPilotVariables:
|
||||
toggle.steerActuatorDelay = np.clip(params.get_float("SteerDelay"), 0.01, 1.0) if advanced_lateral_tuning and tuning_level >= level["SteerDelay"] else steerActuatorDelay
|
||||
toggle.use_custom_steerActuatorDelay = bool(round(toggle.steerActuatorDelay, 2) != round(steerActuatorDelay, 2))
|
||||
toggle.friction = np.clip(params.get_float("SteerFriction"), 0, 0.5) if advanced_lateral_tuning and tuning_level >= level["SteerFriction"] else friction
|
||||
toggle.steer_offset = np.clip(params.get_float("SteerOffset"), -0.2, 0.2) if advanced_lateral_tuning and tuning_level >= level["SteerOffset"] and toggle.car_make == "gm" else 0.0
|
||||
toggle.use_custom_friction = bool(round(toggle.friction, 2) != round(friction, 2)) and is_torque_car and not toggle.force_auto_tune or toggle.force_auto_tune_off
|
||||
toggle.steerKp = [[0], [np.clip(params.get_float("SteerKP"), steerKp * 0.5, steerKp * 1.5) if advanced_lateral_tuning and is_torque_car and tuning_level >= level["SteerKP"] else steerKp]]
|
||||
toggle.latAccelFactor = np.clip(params.get_float("SteerLatAccel"), latAccelFactor * 0.75, latAccelFactor * 1.25) if advanced_lateral_tuning and tuning_level >= level["SteerLatAccel"] else latAccelFactor
|
||||
toggle.latAccelFactor = np.clip(params.get_float("SteerLatAccel"), latAccelFactor * 0.5, latAccelFactor * 1.25) if advanced_lateral_tuning and tuning_level >= level["SteerLatAccel"] else latAccelFactor
|
||||
toggle.use_custom_latAccelFactor = bool(round(toggle.latAccelFactor, 2) != round(latAccelFactor, 2)) and is_torque_car and not toggle.force_auto_tune or toggle.force_auto_tune_off
|
||||
toggle.steerRatio = np.clip(params.get_float("SteerRatio"), steerRatio * 0.5, steerRatio * 1.5) if advanced_lateral_tuning and tuning_level >= level["SteerRatio"] else steerRatio
|
||||
toggle.steerRatio = np.clip(params.get_float("SteerRatio"), steerRatio * 0.25, steerRatio * 1.5) if advanced_lateral_tuning and tuning_level >= level["SteerRatio"] else steerRatio
|
||||
toggle.use_custom_steerRatio = bool(round(toggle.steerRatio, 2) != round(steerRatio, 2)) and not toggle.force_auto_tune or toggle.force_auto_tune_off
|
||||
|
||||
advanced_longitudinal_tuning = params.get_bool("AdvancedLongitudinalTune") if tuning_level >= level["AdvancedLongitudinalTune"] else default.get_bool("AdvancedLongitudinalTune")
|
||||
gm_ev_vehicle = toggle.car_make == "gm" and CP.carFingerprint in GM_EV_CAR
|
||||
gm_ev_vehicle &= not (toggle.car_model.startswith("CHEVROLET_VOLT") and not toggle.car_model.endswith("_CC"))
|
||||
gm_ev_vehicle &= toggle.car_model != "CHEVROLET_MALIBU_HYBRID_CC"
|
||||
ev_vehicle = gm_ev_vehicle or (toggle.car_make == "hyundai" and CP.carFingerprint in HYUNDAI_EV_CAR)
|
||||
ev_vehicle |= CP.transmissionType == TransmissionType.direct
|
||||
|
||||
if params.get("EVTuning") == b"":
|
||||
params.put_bool("EVTuning", ev_vehicle)
|
||||
|
||||
if params.get("TruckTuning") == b"":
|
||||
params.put_bool("TruckTuning", False)
|
||||
|
||||
ev_tuning_param = params.get_bool("EVTuning")
|
||||
truck_tuning_param = params.get_bool("TruckTuning")
|
||||
|
||||
# Enforce exclusivity between EV and Truck tuning.
|
||||
if truck_tuning_param and ev_tuning_param:
|
||||
ev_tuning_param = False
|
||||
params.put_bool("EVTuning", False)
|
||||
|
||||
toggle.ev_tuning = ev_tuning_param if advanced_longitudinal_tuning and tuning_level >= level["EVTuning"] else ev_vehicle
|
||||
toggle.truck_tuning = truck_tuning_param if advanced_longitudinal_tuning and tuning_level >= level["TruckTuning"] else False
|
||||
toggle.longitudinalActuatorDelay = np.clip(params.get_float("LongitudinalActuatorDelay"), 0, 1) if advanced_longitudinal_tuning and tuning_level >= level["LongitudinalActuatorDelay"] else longitudinalActuatorDelay
|
||||
toggle.startAccel = np.clip(params.get_float("StartAccel"), 0, 4) if advanced_longitudinal_tuning and tuning_level >= level["StartAccel"] else startAccel
|
||||
toggle.stopAccel = np.clip(params.get_float("StopAccel"), -4, 0) if advanced_longitudinal_tuning and tuning_level >= level["StopAccel"] else stopAccel
|
||||
@@ -621,6 +661,8 @@ class FrogPilotVariables:
|
||||
|
||||
toggle.stop_distance = params.get_float("StopDistance") if advanced_longitudinal_tuning and tuning_level >= level["StopDistance"] else 6.0
|
||||
|
||||
toggle.recovery_power = np.clip(params.get_float("RecoveryPower"), 0.5, 2.0) if advanced_longitudinal_tuning and tuning_level >= level["RecoveryPower"] else 1.0
|
||||
|
||||
toggle.alert_volume_controller = params.get_bool("AlertVolumeControl") if tuning_level >= level["AlertVolumeControl"] else default.get_bool("AlertVolumeControl")
|
||||
toggle.disengage_volume = params.get_int("DisengageVolume") if toggle.alert_volume_controller and tuning_level >= level["DisengageVolume"] else default.get_int("DisengageVolume")
|
||||
toggle.engage_volume = params.get_int("EngageVolume") if toggle.alert_volume_controller and tuning_level >= level["EngageVolume"] else default.get_int("EngageVolume")
|
||||
@@ -676,28 +718,31 @@ class FrogPilotVariables:
|
||||
toggle.aggressive_jerk_danger = np.clip(params.get_int("AggressiveJerkDanger") / 100, 0.25, 2) if aggressive_profile and tuning_level >= level["AggressiveJerkDanger"] else default.get_int("AggressiveJerkDanger") / 100
|
||||
toggle.aggressive_jerk_speed = np.clip(params.get_int("AggressiveJerkSpeed") / 100, 0.25, 2) if aggressive_profile and tuning_level >= level["AggressiveJerkSpeed"] else default.get_int("AggressiveJerkSpeed") / 100
|
||||
toggle.aggressive_jerk_speed_decrease = np.clip(params.get_int("AggressiveJerkSpeedDecrease") / 100, 0.25, 2) if aggressive_profile and tuning_level >= level["AggressiveJerkSpeedDecrease"] else default.get_int("AggressiveJerkSpeedDecrease") / 100
|
||||
toggle.aggressive_follow = np.clip(params.get_float("AggressiveFollow"), 1, MAX_T_FOLLOW) if aggressive_profile and tuning_level >= level["AggressiveFollow"] else default.get_float("AggressiveFollow")
|
||||
toggle.aggressive_follow = [np.clip(params.get_float("AggressiveFollow"), 1, MAX_T_FOLLOW) if aggressive_profile and tuning_level >= level["AggressiveFollow"] else default.get_float("AggressiveFollow"),
|
||||
np.clip(params.get_float("AggressiveFollowHigh"), 1, MAX_T_FOLLOW) if aggressive_profile and tuning_level >= level["AggressiveFollowHigh"] else default.get_float("AggressiveFollowHigh")]
|
||||
standard_profile = toggle.custom_personalities and (params.get_bool("StandardPersonalityProfile") if tuning_level >= level["StandardPersonalityProfile"] else default.get_bool("StandardPersonalityProfile"))
|
||||
toggle.standard_jerk_acceleration = np.clip(params.get_int("StandardJerkAcceleration") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkAcceleration"] else default.get_int("StandardJerkAcceleration") / 100
|
||||
toggle.standard_jerk_deceleration = np.clip(params.get_int("StandardJerkDeceleration") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkDeceleration"] else default.get_int("StandardJerkDeceleration") / 100
|
||||
toggle.standard_jerk_danger = np.clip(params.get_int("StandardJerkDanger") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkDanger"] else default.get_int("StandardJerkDanger") / 100
|
||||
toggle.standard_jerk_speed = np.clip(params.get_int("StandardJerkSpeed") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkSpeed"] else default.get_int("StandardJerkSpeed") / 100
|
||||
toggle.standard_jerk_speed_decrease = np.clip(params.get_int("StandardJerkSpeedDecrease") / 100, 0.25, 2) if standard_profile and tuning_level >= level["StandardJerkSpeedDecrease"] else default.get_int("StandardJerkSpeedDecrease") / 100
|
||||
toggle.standard_follow = np.clip(params.get_float("StandardFollow"), 1, MAX_T_FOLLOW) if standard_profile and tuning_level >= level["StandardFollow"] else default.get_float("StandardFollow")
|
||||
toggle.standard_follow = [np.clip(params.get_float("StandardFollow"), 1, MAX_T_FOLLOW) if standard_profile and tuning_level >= level["StandardFollow"] else default.get_float("StandardFollow"),
|
||||
np.clip(params.get_float("StandardFollowHigh"), 1, MAX_T_FOLLOW) if standard_profile and tuning_level >= level["StandardFollowHigh"] else default.get_float("StandardFollowHigh")]
|
||||
relaxed_profile = toggle.custom_personalities and (params.get_bool("RelaxedPersonalityProfile") if tuning_level >= level["RelaxedPersonalityProfile"] else default.get_bool("RelaxedPersonalityProfile"))
|
||||
toggle.relaxed_jerk_acceleration = np.clip(params.get_int("RelaxedJerkAcceleration") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkAcceleration"] else default.get_int("RelaxedJerkAcceleration") / 100
|
||||
toggle.relaxed_jerk_deceleration = np.clip(params.get_int("RelaxedJerkDeceleration") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkDeceleration"] else default.get_int("RelaxedJerkDeceleration") / 100
|
||||
toggle.relaxed_jerk_danger = np.clip(params.get_int("RelaxedJerkDanger") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkDanger"] else default.get_int("RelaxedJerkDanger") / 100
|
||||
toggle.relaxed_jerk_speed = np.clip(params.get_int("RelaxedJerkSpeed") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkSpeed"] else default.get_int("RelaxedJerkSpeed") / 100
|
||||
toggle.relaxed_jerk_speed_decrease = np.clip(params.get_int("RelaxedJerkSpeedDecrease") / 100, 0.25, 2) if relaxed_profile and tuning_level >= level["RelaxedJerkSpeedDecrease"] else default.get_int("RelaxedJerkSpeedDecrease") / 100
|
||||
toggle.relaxed_follow = np.clip(params.get_float("RelaxedFollow"), 1, MAX_T_FOLLOW) if relaxed_profile and tuning_level >= level["RelaxedFollow"] else default.get_float("RelaxedFollow")
|
||||
toggle.relaxed_follow = [np.clip(params.get_float("RelaxedFollow"), 1, MAX_T_FOLLOW) if relaxed_profile and tuning_level >= level["RelaxedFollow"] else default.get_float("RelaxedFollow"),
|
||||
np.clip(params.get_float("RelaxedFollowHigh"), 1, MAX_T_FOLLOW) if relaxed_profile and tuning_level >= level["RelaxedFollowHigh"] else default.get_float("RelaxedFollowHigh")]
|
||||
traffic_profile = toggle.custom_personalities and (params.get_bool("TrafficPersonalityProfile") if tuning_level >= level["TrafficPersonalityProfile"] else default.get_bool("TrafficPersonalityProfile"))
|
||||
toggle.traffic_mode_jerk_acceleration = [np.clip(params.get_int("TrafficJerkAcceleration") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkAcceleration"] else default.get_int("TrafficJerkAcceleration") / 100, toggle.aggressive_jerk_acceleration]
|
||||
toggle.traffic_mode_jerk_deceleration = [np.clip(params.get_int("TrafficJerkDeceleration") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkDeceleration"] else default.get_int("TrafficJerkDeceleration") / 100, toggle.aggressive_jerk_deceleration]
|
||||
toggle.traffic_mode_jerk_danger = [np.clip(params.get_int("TrafficJerkDanger") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkDanger"] else default.get_int("TrafficJerkDanger") / 100, toggle.aggressive_jerk_danger]
|
||||
toggle.traffic_mode_jerk_speed = [np.clip(params.get_int("TrafficJerkSpeed") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkSpeed"] else default.get_int("TrafficJerkSpeed") / 100, toggle.aggressive_jerk_speed]
|
||||
toggle.traffic_mode_jerk_speed_decrease = [np.clip(params.get_int("TrafficJerkSpeedDecrease") / 100, 0.25, 2) if traffic_profile and tuning_level >= level["TrafficJerkSpeedDecrease"] else default.get_int("TrafficJerkSpeedDecrease") / 100, toggle.aggressive_jerk_speed_decrease]
|
||||
toggle.traffic_mode_follow = [np.clip(params.get_float("TrafficFollow"), 0.5, MAX_T_FOLLOW) if traffic_profile and tuning_level >= level["TrafficFollow"] else default.get_float("TrafficFollow"), toggle.aggressive_follow]
|
||||
toggle.traffic_mode_follow = [np.clip(params.get_float("TrafficFollow"), 0.5, MAX_T_FOLLOW) if traffic_profile and tuning_level >= level["TrafficFollow"] else default.get_float("TrafficFollow"), toggle.aggressive_follow[0]]
|
||||
|
||||
custom_ui = params.get_bool("CustomUI") if tuning_level >= level["CustomUI"] else default.get_bool("CustomUI")
|
||||
toggle.acceleration_path = toggle.openpilot_longitudinal and (custom_ui and (params.get_bool("AccelerationPath") if tuning_level >= level["AccelerationPath"] else default.get_bool("AccelerationPath")) or toggle.debug_mode)
|
||||
@@ -782,6 +827,9 @@ class FrogPilotVariables:
|
||||
toggle.vEgoStarting = 0.15 if toggle.experimental_gm_tune else toggle.vEgoStarting
|
||||
toggle.vEgoStopping = 0.15 if toggle.experimental_gm_tune else toggle.vEgoStopping
|
||||
|
||||
toggle.red_panda = toggle.car_make == "gm" and (params.get_bool("RedPanda") if tuning_level >= level["RedPanda"] else default.get_bool("RedPanda"))
|
||||
toggle.remote_start_boots_comma = toggle.car_make == "gm" and (params.get_bool("RemoteStartBootsComma") if tuning_level >= level["RemoteStartBootsComma"] else default.get_bool("RemoteStartBootsComma"))
|
||||
|
||||
toggle.force_fingerprint = (params.get_bool("ForceFingerprint") if tuning_level >= level["ForceFingerprint"] else default.get_bool("ForceFingerprint")) and toggle.car_model is not None
|
||||
|
||||
toggle.frogsgomoo_tweak = toggle.openpilot_longitudinal and toggle.car_make == "toyota" and (params.get_bool("FrogsGoMoosTweak") if tuning_level >= level["FrogsGoMoosTweak"] else default.get_bool("FrogsGoMoosTweak"))
|
||||
@@ -826,6 +874,7 @@ class FrogPilotVariables:
|
||||
toggle.human_following = longitudinal_tuning and (params.get_bool("HumanFollowing") if tuning_level >= level["HumanFollowing"] else default.get_bool("HumanFollowing"))
|
||||
toggle.lead_detection_probability = np.clip(params.get_int("LeadDetectionThreshold") / 100, 0.25, 0.50) if longitudinal_tuning and tuning_level >= level["LeadDetectionThreshold"] else default.get_int("LeadDetectionThreshold") / 100
|
||||
toggle.max_desired_acceleration = np.clip(params.get_float("MaxDesiredAcceleration"), 0.1, 4.0) if longitudinal_tuning and tuning_level >= level["MaxDesiredAcceleration"] else default.get_float("MaxDesiredAcceleration")
|
||||
toggle.trailer_load_kg = (np.clip(params.get_int("TrailerLoad"), 0, 15000) if longitudinal_tuning and tuning_level >= level["TrailerLoad"] else default.get_int("TrailerLoad")) * CV.LB_TO_KG
|
||||
toggle.taco_tune = longitudinal_tuning and (params.get_bool("TacoTune") if tuning_level >= level["TacoTune"] else default.get_bool("TacoTune"))
|
||||
|
||||
toggle.available_models = params.get("AvailableModels", encoding="utf-8") or ""
|
||||
@@ -866,7 +915,7 @@ class FrogPilotVariables:
|
||||
toggle.model_version = DEFAULT_MODEL_VERSION
|
||||
toggle.classic_longitudinal = toggle.model_version in {"v1", "v2", "v3", "v4"}
|
||||
toggle.classic_model = toggle.model_version in {"v1", "v2", "v3", "v4"}
|
||||
toggle.tinygrad_model = toggle.model_version in {"v8", "v9", "v10", "v11"}
|
||||
toggle.tinygrad_model = toggle.model_version in {"v8", "v9", "v10", "v11", "v12"}
|
||||
toggle.tomb_raider = toggle.model == "space-lab"
|
||||
|
||||
toggle.model_ui = params.get_bool("ModelUI") if tuning_level >= level["ModelUI"] else default.get_bool("ModelUI")
|
||||
@@ -890,6 +939,7 @@ class FrogPilotVariables:
|
||||
toggle.old_long_api |= toggle.openpilot_longitudinal and toggle.car_make == "hyundai" and not (params.get_bool("NewLongAPI") if tuning_level >= level["NewLongAPI"] else default.get_bool("NewLongAPI"))
|
||||
|
||||
personalize_openpilot = params.get_bool("PersonalizeOpenpilot") if tuning_level >= level["PersonalizeOpenpilot"] else default.get_bool("PersonalizeOpenpilot")
|
||||
toggle.boot_logo = params.get("BootLogo", encoding="utf-8") or "starpilot"
|
||||
toggle.color_scheme = toggle.current_holiday_theme if toggle.current_holiday_theme != "stock" else params.get("CustomColors", encoding="utf-8") if personalize_openpilot else "stock"
|
||||
toggle.distance_icons = toggle.current_holiday_theme if toggle.current_holiday_theme != "stock" else params.get("CustomDistanceIcons", encoding="utf-8") if personalize_openpilot else "stock"
|
||||
toggle.icon_pack = toggle.current_holiday_theme if toggle.current_holiday_theme != "stock" else params.get("CustomIcons", encoding="utf-8") if personalize_openpilot else "stock"
|
||||
@@ -979,7 +1029,14 @@ class FrogPilotVariables:
|
||||
toggle.lock_doors = toyota_doors and (params.get_bool("LockDoors") if tuning_level >= level["LockDoors"] else default.get_bool("LockDoors"))
|
||||
toggle.unlock_doors = toyota_doors and (params.get_bool("UnlockDoors") if tuning_level >= level["UnlockDoors"] else default.get_bool("UnlockDoors"))
|
||||
|
||||
toggle.volt_sng = toggle.car_model == "CHEVROLET_VOLT" and (params.get_bool("VoltSNG") if tuning_level >= level["VoltSNG"] else default.get_bool("VoltSNG"))
|
||||
volt_models = {
|
||||
"CHEVROLET_VOLT",
|
||||
"CHEVROLET_VOLT_2019",
|
||||
"CHEVROLET_VOLT_ASCM",
|
||||
"CHEVROLET_VOLT_CAMERA",
|
||||
}
|
||||
|
||||
toggle.volt_sng = toggle.car_model in volt_models and (params.get_bool("VoltSNG") if tuning_level >= level["VoltSNG"] else default.get_bool("VoltSNG"))
|
||||
|
||||
toggle.gm_pedal_longitudinal = params.get_bool("GMPedalLongitudinal") if tuning_level >= level["GMPedalLongitudinal"] else default.get_bool("GMPedalLongitudinal")
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
import time
|
||||
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.common.realtime import DT_MDL
|
||||
from openpilot.common.numpy_fast import interp
|
||||
@@ -32,6 +34,10 @@ class ConditionalExperimentalMode:
|
||||
LIGHT_BOOST_LOW = 1.15
|
||||
LIGHT_BOOST_HIGH = 1.2
|
||||
|
||||
# Small latch to avoid frame-to-frame mode chatter.
|
||||
CEM_TRANSITION_GUARD_TIME = 0.50
|
||||
CEM_TRANSITION_BUFFER_TIME = 0.25
|
||||
|
||||
@staticmethod
|
||||
def get_speed_based_param(speed_mph, param_array):
|
||||
"""Get parameter value based on current speed using smooth interpolation between breakpoints [0, 35, 55, 70]"""
|
||||
@@ -49,8 +55,12 @@ class ConditionalExperimentalMode:
|
||||
self.experimental_mode = False
|
||||
self.stop_light_detected = False
|
||||
self.prev_experimental_mode = False # For hysteresis
|
||||
self.mode_hold_until = 0.0
|
||||
self.mode_false_since = 0.0
|
||||
|
||||
def update(self, v_ego, sm, frogpilot_toggles):
|
||||
now = time.monotonic()
|
||||
|
||||
if frogpilot_toggles.experimental_mode_via_press:
|
||||
self.status_value = params_memory.get_int("CEStatus")
|
||||
else:
|
||||
@@ -58,28 +68,23 @@ class ConditionalExperimentalMode:
|
||||
|
||||
if self.status_value not in {1, 2} and not sm["carState"].standstill:
|
||||
self.update_conditions(v_ego, sm, frogpilot_toggles)
|
||||
new_experimental_mode = self.check_conditions(v_ego, sm, frogpilot_toggles)
|
||||
|
||||
# Add hysteresis to prevent rapid toggling
|
||||
if new_experimental_mode and not self.prev_experimental_mode:
|
||||
# Require weaker conditions to turn on
|
||||
hysteresis_factor = 0.9
|
||||
elif not new_experimental_mode and self.prev_experimental_mode:
|
||||
# Require stronger conditions to turn off
|
||||
hysteresis_factor = 1.2
|
||||
else:
|
||||
hysteresis_factor = 1.0
|
||||
triggered = self.check_conditions(v_ego, sm, frogpilot_toggles)
|
||||
if triggered:
|
||||
self.mode_hold_until = now + self.CEM_TRANSITION_GUARD_TIME
|
||||
self.mode_false_since = 0.0
|
||||
elif self.mode_false_since == 0.0:
|
||||
self.mode_false_since = now
|
||||
|
||||
# Apply hysteresis to key conditions
|
||||
if hasattr(self, 'slow_lead_detected'):
|
||||
self.slow_lead_detected = self.slow_lead_detected if hysteresis_factor == 1.0 else (self.slow_lead_filter.x >= scale_threshold(v_ego) * hysteresis_factor)
|
||||
if hasattr(self, 'curve_detected'):
|
||||
self.curve_detected = self.curve_detected if hysteresis_factor == 1.0 else (self.curvature_filter.x >= THRESHOLD * hysteresis_factor)
|
||||
hold_active = now < self.mode_hold_until
|
||||
transition_buffer_active = self.mode_false_since != 0.0 and (now - self.mode_false_since) < self.CEM_TRANSITION_BUFFER_TIME
|
||||
|
||||
self.experimental_mode = self.check_conditions(v_ego, sm, frogpilot_toggles)
|
||||
self.experimental_mode = triggered or hold_active or transition_buffer_active
|
||||
self.prev_experimental_mode = self.experimental_mode
|
||||
params_memory.put_int("CEStatus", self.status_value if self.experimental_mode else 0)
|
||||
else:
|
||||
self.mode_hold_until = 0.0
|
||||
self.mode_false_since = 0.0
|
||||
self.experimental_mode = self.status_value == 2 or sm["carState"].standstill and self.experimental_mode and self.frogpilot_planner.model_stopped
|
||||
self.stop_light_detected &= self.status_value not in {1, 2}
|
||||
self.stop_light_filter.x = 0
|
||||
|
||||
@@ -53,14 +53,41 @@ A_CRUISE_MIN_SPORT = A_CRUISE_MIN * 2
|
||||
|
||||
# MPH = [0.0, 11, 22, 34, 45, 56, 89]
|
||||
A_CRUISE_MAX_BP_CUSTOM = [0.0, 5., 10., 15., 20., 25., 40.]
|
||||
A_CRUISE_MAX_VALS_ECO = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
|
||||
A_CRUISE_MAX_VALS_SPORT = [1.25, 1.25, 1.25, 1.25, 1.5, 1.5, 2.0]
|
||||
A_CRUISE_MAX_VALS_ECO_EV = [1.0, 1.0, 1.0, 1.0, 1.12, 1.12, 1.45]
|
||||
A_CRUISE_MAX_VALS_STANDARD_EV = [1.15, 1.15, 1.15, 1.15, 1.30, 1.30, 1.72]
|
||||
A_CRUISE_MAX_VALS_SPORT_EV = [1.25, 1.25, 1.25, 1.25, 1.45, 1.5, 2.0]
|
||||
A_CRUISE_MAX_VALS_SPORT_PLUS_EV = [1.35, 1.35, 1.35, 1.35, 1.60, 1.60, 2.10]
|
||||
A_CRUISE_MAX_VALS_ECO_GAS = [2.0, 1.5, 1.0, 0.8, 0.6, 0.4, 0.2]
|
||||
A_CRUISE_MAX_VALS_SPORT_GAS = [3.0, 2.5, 2.0, 1.5, 1.0, 0.8, 0.6]
|
||||
A_CRUISE_MAX_VALS_ECO_TRUCK = [3.00, 1.05, 0.60, 0.50, 0.50, 0.45, 0.35]
|
||||
A_CRUISE_MAX_VALS_STANDARD_TRUCK = [6.00, 1.10, 0.70, 0.60, 0.55, 0.45, 0.35]
|
||||
A_CRUISE_MAX_VALS_SPORT_TRUCK = [6.00, 1.15, 0.75, 0.70, 0.60, 0.50, 0.40]
|
||||
A_CRUISE_MAX_VALS_SPORT_PLUS_TRUCK = [6.00, 1.30, 0.90, 0.80, 0.70, 0.60, 0.45]
|
||||
|
||||
def get_max_accel_eco(v_ego):
|
||||
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_ECO))
|
||||
def get_max_accel_eco(v_ego, ev_tuning=True, truck_tuning=False):
|
||||
if ev_tuning:
|
||||
cruise_vals = A_CRUISE_MAX_VALS_ECO_EV
|
||||
elif truck_tuning:
|
||||
cruise_vals = A_CRUISE_MAX_VALS_ECO_TRUCK
|
||||
else:
|
||||
cruise_vals = A_CRUISE_MAX_VALS_ECO_GAS
|
||||
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, cruise_vals))
|
||||
|
||||
def get_max_accel_sport(v_ego):
|
||||
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_SPORT))
|
||||
def get_max_accel_sport(v_ego, ev_tuning=True, truck_tuning=False):
|
||||
if ev_tuning:
|
||||
cruise_vals = A_CRUISE_MAX_VALS_SPORT_EV
|
||||
elif truck_tuning:
|
||||
cruise_vals = A_CRUISE_MAX_VALS_SPORT_TRUCK
|
||||
else:
|
||||
cruise_vals = A_CRUISE_MAX_VALS_SPORT_GAS
|
||||
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, cruise_vals))
|
||||
|
||||
def get_max_accel_standard(v_ego, ev_tuning=True, truck_tuning=False):
|
||||
if ev_tuning:
|
||||
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_STANDARD_EV))
|
||||
if truck_tuning:
|
||||
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_STANDARD_TRUCK))
|
||||
return get_max_accel(v_ego)
|
||||
|
||||
def get_max_accel_low_speeds(max_accel, v_cruise):
|
||||
return float(akima_interp(v_cruise, [0., CITY_SPEED_LIMIT / 2, CITY_SPEED_LIMIT], [max_accel / 4, max_accel / 2, max_accel]))
|
||||
@@ -68,7 +95,11 @@ def get_max_accel_low_speeds(max_accel, v_cruise):
|
||||
def get_max_accel_ramp_off(max_accel, v_cruise, v_ego):
|
||||
return float(akima_interp(v_cruise - v_ego, [0., 1., 5., 10.], [0., 0.5, 1.0, max_accel]))
|
||||
|
||||
def get_max_allowed_accel(v_ego):
|
||||
def get_max_allowed_accel(v_ego, ev_tuning=True, truck_tuning=False):
|
||||
if ev_tuning:
|
||||
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_SPORT_PLUS_EV))
|
||||
if truck_tuning:
|
||||
return float(akima_interp(v_ego, A_CRUISE_MAX_BP_CUSTOM, A_CRUISE_MAX_VALS_SPORT_PLUS_TRUCK))
|
||||
return float(akima_interp(v_ego, [0., 5., 20.], [4.0, 4.0, 2.0])) # ISO 15622:2018
|
||||
|
||||
class FrogPilotAcceleration:
|
||||
@@ -81,26 +112,28 @@ class FrogPilotAcceleration:
|
||||
def update(self, v_ego, sm, frogpilot_toggles):
|
||||
eco_gear = sm["frogpilotCarState"].ecoGear
|
||||
sport_gear = sm["frogpilotCarState"].sportGear
|
||||
ev_tuning = frogpilot_toggles.ev_tuning
|
||||
truck_tuning = frogpilot_toggles.truck_tuning
|
||||
|
||||
if sm["frogpilotCarState"].trafficModeEnabled:
|
||||
self.max_accel = get_max_accel(v_ego)
|
||||
self.max_accel = get_max_accel_standard(v_ego, ev_tuning, truck_tuning)
|
||||
elif frogpilot_toggles.map_acceleration and (eco_gear or sport_gear):
|
||||
if eco_gear:
|
||||
self.max_accel = get_max_accel_eco(v_ego)
|
||||
self.max_accel = get_max_accel_eco(v_ego, ev_tuning, truck_tuning)
|
||||
else:
|
||||
if frogpilot_toggles.acceleration_profile == 2:
|
||||
self.max_accel = get_max_accel_sport(v_ego)
|
||||
self.max_accel = get_max_accel_sport(v_ego, ev_tuning, truck_tuning)
|
||||
else:
|
||||
self.max_accel = get_max_allowed_accel(v_ego)
|
||||
self.max_accel = get_max_allowed_accel(v_ego, ev_tuning, truck_tuning)
|
||||
else:
|
||||
if frogpilot_toggles.acceleration_profile == 1:
|
||||
self.max_accel = get_max_accel_eco(v_ego)
|
||||
self.max_accel = get_max_accel_eco(v_ego, ev_tuning, truck_tuning)
|
||||
elif frogpilot_toggles.acceleration_profile == 2:
|
||||
self.max_accel = get_max_accel_sport(v_ego)
|
||||
self.max_accel = get_max_accel_sport(v_ego, ev_tuning, truck_tuning)
|
||||
elif frogpilot_toggles.acceleration_profile == 3:
|
||||
self.max_accel = get_max_allowed_accel(v_ego)
|
||||
self.max_accel = get_max_allowed_accel(v_ego, ev_tuning, truck_tuning)
|
||||
else:
|
||||
self.max_accel = get_max_accel(v_ego)
|
||||
self.max_accel = get_max_accel_standard(v_ego, ev_tuning, truck_tuning)
|
||||
|
||||
if frogpilot_toggles.human_acceleration:
|
||||
self.max_accel = min(get_max_accel_low_speeds(self.max_accel, self.frogpilot_planner.v_cruise), self.max_accel)
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#!/usr/bin/env python3
|
||||
import numpy as np
|
||||
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import COMFORT_BRAKE, desired_follow_distance, get_jerk_factor, get_T_FOLLOW
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_variables import CITY_SPEED_LIMIT
|
||||
|
||||
TRAFFIC_MODE_BP = [0., CITY_SPEED_LIMIT]
|
||||
PERSONALITY_BP = [20. * CV.KPH_TO_MS, 90. * CV.KPH_TO_MS]
|
||||
|
||||
class FrogPilotFollowing:
|
||||
def __init__(self, FrogPilotPlanner):
|
||||
@@ -48,12 +50,16 @@ class FrogPilotFollowing:
|
||||
frogpilot_toggles.custom_personalities, sm["controlsState"].personality
|
||||
)
|
||||
|
||||
self.t_follow = get_T_FOLLOW(
|
||||
t_follow_param = get_T_FOLLOW(
|
||||
frogpilot_toggles.aggressive_follow,
|
||||
frogpilot_toggles.standard_follow,
|
||||
frogpilot_toggles.relaxed_follow,
|
||||
frogpilot_toggles.custom_personalities, sm["controlsState"].personality
|
||||
)
|
||||
if isinstance(t_follow_param, list):
|
||||
self.t_follow = float(np.interp(v_ego, PERSONALITY_BP, t_follow_param))
|
||||
else:
|
||||
self.t_follow = float(t_follow_param)
|
||||
else:
|
||||
self.base_acceleration_jerk = 0
|
||||
self.base_danger_jerk = 0
|
||||
|
||||
@@ -51,7 +51,7 @@ class FrogPilotTracking:
|
||||
self.sound = FrogPilotAudibleAlert.none
|
||||
self.state = State.disabled
|
||||
|
||||
self.model_name = clean_model_name(dict(zip(frogpilot_toggles.available_models.split(","), frogpilot_toggles.available_model_names.split(",")))[frogpilot_toggles.model])
|
||||
self.model_name = clean_model_name(frogpilot_toggles.model_name)
|
||||
|
||||
def update(self, now, time_validated, sm, frogpilot_toggles):
|
||||
v_cruise = min(sm["controlsState"].vCruiseCluster, V_CRUISE_MAX) * CV.KPH_TO_MS
|
||||
|
||||
@@ -128,7 +128,7 @@ def get_nn_model_path(car, eps_firmware) -> str | None:
|
||||
def find_valid_model(*queries):
|
||||
for query in queries:
|
||||
path, score = best_model_path(query)
|
||||
if path and candidate in path and score >= 0.9:
|
||||
if path and score >= 0.9:
|
||||
return path
|
||||
return None
|
||||
|
||||
|
||||
Regular → Executable
BIN
Binary file not shown.
Binary file not shown.
@@ -20,6 +20,30 @@ from openpilot.frogpilot.common.frogpilot_variables import get_frogpilot_toggles
|
||||
|
||||
BASE_URL = "https://nominatim.openstreetmap.org"
|
||||
MINIMUM_POPULATION = 100_000
|
||||
SEARCH_RADIUS_DEGREES = 1.45
|
||||
|
||||
def get_population_value(population_str):
|
||||
if population_str is None:
|
||||
return None
|
||||
try:
|
||||
return int(str(population_str).replace(",", "").split(";")[0].strip())
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def search_nearby_major_cities(lat, lon, session, state_name, country_name):
|
||||
viewbox = f"{lon - SEARCH_RADIUS_DEGREES},{lat + SEARCH_RADIUS_DEGREES},{lon + SEARCH_RADIUS_DEGREES},{lat - SEARCH_RADIUS_DEGREES}"
|
||||
cities = (session.get(f"{BASE_URL}/search", params={
|
||||
"addressdetails": 1, "bounded": 1, "extratags": 1, "format": "jsonv2", "limit": 20, "q": "city", "viewbox": viewbox
|
||||
}, timeout=10).json() or [])
|
||||
|
||||
qualifying = [c for c in cities if (get_population_value((c.get("extratags") or {}).get("population")) or 0) >= MINIMUM_POPULATION]
|
||||
if not qualifying:
|
||||
return None
|
||||
|
||||
nearest = min(qualifying, key=lambda c: (float(c["lat"]) - lat) ** 2 + (float(c["lon"]) - lon) ** 2)
|
||||
addr = nearest.get("address") or {}
|
||||
return float(nearest["lat"]), float(nearest["lon"]), addr.get("city") or addr.get("town") or nearest.get("display_name", "").split(",")[0], state_name, country_name
|
||||
|
||||
|
||||
def get_city_center(latitude, longitude):
|
||||
try:
|
||||
@@ -44,14 +68,7 @@ def get_city_center(latitude, longitude):
|
||||
|
||||
if data:
|
||||
tags = data[0]
|
||||
population = (tags.get("extratags") or {}).get("population")
|
||||
|
||||
population_value = None
|
||||
if population is not None:
|
||||
try:
|
||||
population_value = int(str(population).replace(",", "").split(";")[0].strip())
|
||||
except Exception:
|
||||
population_value = None
|
||||
population_value = get_population_value((tags.get("extratags") or {}).get("population"))
|
||||
|
||||
if population_value is not None and population_value >= MINIMUM_POPULATION:
|
||||
latitude_value = float(tags["lat"])
|
||||
@@ -62,6 +79,10 @@ def get_city_center(latitude, longitude):
|
||||
|
||||
return latitude_value, longitude_value, city_label, state_name, country_name
|
||||
|
||||
nearby_result = search_nearby_major_cities(latitude, longitude, session, state_name, country_name)
|
||||
if nearby_result:
|
||||
return nearby_result
|
||||
|
||||
query = f"{state_name} state capital" if country_code == "us" else f"capital of {state_name}, {country_name}"
|
||||
response = session.get(f"{BASE_URL}/search", params={"addressdetails": 1, "extratags": 1, "format": "jsonv2", "limit": 5, "q": query}, timeout=10)
|
||||
response.raise_for_status()
|
||||
@@ -99,14 +120,14 @@ def get_city_center(latitude, longitude):
|
||||
|
||||
def update_branch_commits(now):
|
||||
points = []
|
||||
for branch in ["FrogPilot", "FrogPilot-Staging", "FrogPilot-Testing"]:
|
||||
try:
|
||||
response = requests.get(f"https://api.github.com/repos/FrogAi/FrogPilot/commits/{branch}")
|
||||
response.raise_for_status()
|
||||
sha = response.json()["sha"]
|
||||
points.append(Point("branch_commits").field("commit", sha).tag("branch", branch).time(now))
|
||||
except Exception as e:
|
||||
print(f"Failed to fetch commit for {branch}: {e}")
|
||||
branch = get_build_metadata().channel # Current running branch
|
||||
try:
|
||||
response = requests.get(f"https://api.github.com/repos/firestar5683/StarPilot/commits/{branch}")
|
||||
response.raise_for_status()
|
||||
sha = response.json()["sha"]
|
||||
points.append(Point("branch_commits").field("commit", sha).tag("branch", branch).time(now))
|
||||
except Exception as e:
|
||||
print(f"Failed to fetch commit for {branch}: {e}")
|
||||
|
||||
return points
|
||||
|
||||
@@ -129,11 +150,9 @@ def send_stats():
|
||||
if frogpilot_toggles.car_make == "mock":
|
||||
return
|
||||
|
||||
bucket = os.environ.get("STATS_BUCKET", "")
|
||||
org_ID = os.environ.get("STATS_ORG_ID", "")
|
||||
token = os.environ.get("STATS_TOKEN", "")
|
||||
url = os.environ.get("STATS_URL", "")
|
||||
|
||||
bucket = "StarPilot"
|
||||
org_ID = "StarPilot"
|
||||
url = "https://stats.firestar.link"
|
||||
frogpilot_stats = json.loads(params.get("FrogPilotStats") or "{}")
|
||||
|
||||
location = json.loads(params.get("LastGPSPosition") or "{}")
|
||||
@@ -161,14 +180,19 @@ def send_stats():
|
||||
|
||||
user_point = (
|
||||
Point("user_stats")
|
||||
.tag("car_make", "GM" if frogpilot_toggles.car_make == "gm" else frogpilot_toggles.car_make.title())
|
||||
.tag("car_model", frogpilot_toggles.car_model)
|
||||
.tag("city", city)
|
||||
.tag("country", country)
|
||||
.tag("device", HARDWARE.get_device_type())
|
||||
.tag("driving_model", clean_model_name(frogpilot_toggles.model_name))
|
||||
.tag("state", state)
|
||||
.tag("theme", selected_theme.title())
|
||||
.tag("branch", build_metadata.channel)
|
||||
.tag("dongle_id", params.get("FrogPilotDongleId", encoding="utf-8"))
|
||||
|
||||
.field("blocked_user", frogpilot_toggles.block_user)
|
||||
.field("car_make", "GM" if frogpilot_toggles.car_make == "gm" else frogpilot_toggles.car_make.title())
|
||||
.field("car_model", frogpilot_toggles.car_model)
|
||||
.field("city", city)
|
||||
.field("country", country)
|
||||
.field("current_months_kilometers", int(frogpilot_stats.get("CurrentMonthsKilometers", 0)))
|
||||
.field("device", HARDWARE.get_device_type())
|
||||
.field("driving_model", clean_model_name(frogpilot_toggles.model_name))
|
||||
.field("event", 1)
|
||||
.field("frogpilot_drives", int(frogpilot_stats.get("FrogPilotDrives", 0)))
|
||||
.field("frogpilot_hours", float(frogpilot_stats.get("FrogPilotSeconds", 0)) / (60 * 60))
|
||||
@@ -178,13 +202,12 @@ def send_stats():
|
||||
.field("has_openpilot_longitudinal", frogpilot_toggles.openpilot_longitudinal)
|
||||
.field("has_pedal", frogpilot_toggles.has_pedal)
|
||||
.field("has_sdsu", frogpilot_toggles.has_sdsu)
|
||||
.field("has_sascm", frogpilot_toggles.has_sascm)
|
||||
.field("has_zss", frogpilot_toggles.has_zss)
|
||||
.field("latitude", latitude)
|
||||
.field("longitude", longitude)
|
||||
.field("rainbow_path", frogpilot_toggles.rainbow_path)
|
||||
.field("random_events", frogpilot_toggles.random_events)
|
||||
.field("state", state)
|
||||
.field("theme", selected_theme.title())
|
||||
.field("total_aol_seconds", float(frogpilot_stats.get("AOLTime", 0)))
|
||||
.field("total_lateral_seconds", float(frogpilot_stats.get("LateralTime", 0)))
|
||||
.field("total_longitudinal_seconds", float(frogpilot_stats.get("LongitudinalTime", 0)))
|
||||
@@ -193,15 +216,12 @@ def send_stats():
|
||||
.field("up_to_date", is_up_to_date(build_metadata))
|
||||
.field("using_stock_acc", not (frogpilot_toggles.has_cc_long or frogpilot_toggles.openpilot_longitudinal))
|
||||
|
||||
.tag("branch", build_metadata.channel)
|
||||
.tag("dongle_id", params.get("FrogPilotDongleId", encoding="utf-8"))
|
||||
|
||||
.time(now)
|
||||
)
|
||||
|
||||
all_points = [user_point] + update_branch_commits(now)
|
||||
|
||||
client = InfluxDBClient(org=org_ID, token=token, url=url)
|
||||
client = InfluxDBClient(org=org_ID, token=org_ID, url=url)
|
||||
client.write_api(write_options=SYNCHRONOUS).write(bucket=bucket, org=org_ID, record=all_points)
|
||||
print("Successfully sent FrogPilot stats!")
|
||||
except Exception as exception:
|
||||
|
||||
@@ -32,7 +32,10 @@ lenvCython.Program('models/commonmodel_pyx.so', 'models/commonmodel_pyx.pyx', LI
|
||||
tinygrad_files = ["#"+x for x in glob.glob(env.Dir("#tinygrad_repo").relpath + "/**", recursive=True, root_dir=env.Dir("#").abspath) if 'pycache' not in x]
|
||||
|
||||
# Get model metadata
|
||||
for model_name in ['driving_vision', 'driving_policy']:
|
||||
model_metadata_names = ['driving_vision', 'driving_policy']
|
||||
if File("models/driving_off_policy.onnx").exists():
|
||||
model_metadata_names.append('driving_off_policy')
|
||||
for model_name in model_metadata_names:
|
||||
fn = File(f"models/{model_name}").abspath
|
||||
script_files = [File(Dir("#frogpilot/tinygrad_modeld").File("get_model_metadata.py").abspath)]
|
||||
cmd = f'python3 {Dir("#frogpilot/tinygrad_modeld").abspath}/get_model_metadata.py {fn}.onnx'
|
||||
@@ -48,7 +51,10 @@ def tg_compile(flags, model_name):
|
||||
)
|
||||
|
||||
# Compile small models
|
||||
for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']:
|
||||
model_compile_names = ['driving_vision', 'driving_policy', 'dmonitoring_model']
|
||||
if File("models/driving_off_policy.onnx").exists():
|
||||
model_compile_names.append('driving_off_policy')
|
||||
for model_name in model_compile_names:
|
||||
flags = {
|
||||
'larch64': 'DEV=QCOM',
|
||||
'Darwin': 'DEV=CPU IMAGE=0',
|
||||
|
||||
@@ -11,16 +11,15 @@ ConfidenceClass = log.ModelDataV2.ConfidenceClass
|
||||
|
||||
# Return curvature for lateral action. If the model outputs desired_curvature and we're not in mlsim mode,
|
||||
# use it directly; otherwise derive from the plan using yaw and yaw-rate.
|
||||
def get_curvature_from_output(output: dict, v_ego: float, lat_action_t: float, mlsim: bool) -> float:
|
||||
def get_curvature_from_output(output: dict, plan: np.ndarray, v_ego: float, lat_action_t: float, mlsim: bool) -> float:
|
||||
if not mlsim:
|
||||
desired = output.get('desired_curvature')
|
||||
if desired is not None:
|
||||
return float(desired[0, 0])
|
||||
|
||||
plan_out = output['plan'][0]
|
||||
# Use yaw (index 2) and yaw_rate (index 2)
|
||||
theta = plan_out[:, Plan.T_FROM_CURRENT_EULER][:, 2]
|
||||
theta_dot = plan_out[:, Plan.ORIENTATION_RATE][:, 2]
|
||||
theta = plan[:, Plan.T_FROM_CURRENT_EULER][:, 2]
|
||||
theta_dot = plan[:, Plan.ORIENTATION_RATE][:, 2]
|
||||
return float(get_curvature_from_plan(theta, theta_dot, ModelConstants.T_IDXS, v_ego, lat_action_t))
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -97,12 +97,12 @@ class Parser:
|
||||
self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION,
|
||||
out_shape=(ModelConstants.LEAD_TRAJ_LEN, ModelConstants.LEAD_WIDTH))
|
||||
if 'plan' in outs:
|
||||
if outs['plan'].shape[1] == 2 * ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH:
|
||||
self.parse_mdn('plan', outs, in_N=0, out_N=0,
|
||||
out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH))
|
||||
else:
|
||||
self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION,
|
||||
out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH))
|
||||
plan_mhp = outs['plan'].shape[1] != 2 * ModelConstants.IDX_N * ModelConstants.PLAN_WIDTH
|
||||
plan_in_N, plan_out_N = (ModelConstants.PLAN_MHP_N, ModelConstants.PLAN_MHP_SELECTION) if plan_mhp else (0, 0)
|
||||
self.parse_mdn('plan', outs, in_N=plan_in_N, out_N=plan_out_N,
|
||||
out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH))
|
||||
if 'planplus' in outs:
|
||||
self.parse_mdn('planplus', outs, in_N=plan_in_N, out_N=plan_out_N, out_shape=(ModelConstants.IDX_N, ModelConstants.PLAN_WIDTH))
|
||||
if 'lane_lines' in outs:
|
||||
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0,
|
||||
out_shape=(ModelConstants.NUM_LANE_LINES, ModelConstants.IDX_N, ModelConstants.LANE_LINES_WIDTH))
|
||||
|
||||
@@ -35,14 +35,16 @@ PROCESS_NAME = "frogpilot.tinygrad_modeld.tinygrad_modeld"
|
||||
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
|
||||
|
||||
|
||||
LAT_SMOOTH_SECONDS = 0.1
|
||||
LAT_SMOOTH_SECONDS = 0.0
|
||||
LONG_SMOOTH_SECONDS = 0.3
|
||||
MIN_LAT_CONTROL_SPEED = 0.3
|
||||
|
||||
|
||||
def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log.ModelDataV2.Action,
|
||||
lat_action_t: float, long_action_t: float, v_ego: float, mlsim: bool, is_v9: bool) -> log.ModelDataV2.Action:
|
||||
lat_action_t: float, long_action_t: float, v_ego: float, mlsim: bool, is_v9: bool, frogpilot_toggles) -> log.ModelDataV2.Action:
|
||||
plan = model_output['plan'][0]
|
||||
if 'planplus' in model_output:
|
||||
plan = plan + frogpilot_toggles.recovery_power*model_output['planplus'][0]
|
||||
desired_accel, should_stop = get_accel_from_plan_tomb_raider(plan[:,Plan.VELOCITY][:,0],
|
||||
plan[:,Plan.ACCELERATION][:,0],
|
||||
ModelConstants.T_IDXS,
|
||||
@@ -56,7 +58,7 @@ def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log.
|
||||
else:
|
||||
desired_curvature = prev_action.desiredCurvature
|
||||
else:
|
||||
desired_curvature = get_curvature_from_output(model_output, v_ego, lat_action_t, mlsim=mlsim)
|
||||
desired_curvature = get_curvature_from_output(model_output, plan, v_ego, lat_action_t, mlsim=mlsim)
|
||||
if v_ego > MIN_LAT_CONTROL_SPEED:
|
||||
desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, LAT_SMOOTH_SECONDS)
|
||||
else:
|
||||
@@ -81,6 +83,34 @@ class ModelState:
|
||||
output: np.ndarray
|
||||
prev_desire: np.ndarray # for tracking the rising edge of the pulse
|
||||
|
||||
def _build_policy_inputs(self, input_shapes: dict[str, tuple[int, ...]]) -> tuple[dict[str, np.ndarray], str | None]:
|
||||
numpy_inputs: dict[str, np.ndarray] = {}
|
||||
|
||||
# Always-supported inputs (if model expects them)
|
||||
desire_key_init = next((k for k in input_shapes if k.startswith('desire')), None)
|
||||
if desire_key_init:
|
||||
numpy_inputs[desire_key_init] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.DESIRE_LEN), dtype=np.float32)
|
||||
if 'traffic_convention' in input_shapes:
|
||||
numpy_inputs['traffic_convention'] = np.zeros((1, ModelConstants.TRAFFIC_CONVENTION_LEN), dtype=np.float32)
|
||||
if 'features_buffer' in input_shapes:
|
||||
numpy_inputs['features_buffer'] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32)
|
||||
|
||||
# Optional inputs for non-v11 (and some v10/v9 variants)
|
||||
# Lateral control params
|
||||
if 'lateral_control_params' in input_shapes:
|
||||
numpy_inputs['lateral_control_params'] = np.zeros((1, ModelConstants.LATERAL_CONTROL_PARAMS_LEN), dtype=np.float32)
|
||||
|
||||
# Previous desired curvature: handle both singular and plural key names across model versions
|
||||
prev_desired_curv_key = None
|
||||
if 'prev_desired_curv' in input_shapes:
|
||||
prev_desired_curv_key = 'prev_desired_curv'
|
||||
numpy_inputs['prev_desired_curv'] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32)
|
||||
elif 'prev_desired_curvs' in input_shapes:
|
||||
prev_desired_curv_key = 'prev_desired_curvs'
|
||||
numpy_inputs['prev_desired_curvs'] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32)
|
||||
|
||||
return numpy_inputs, prev_desired_curv_key
|
||||
|
||||
def __init__(self, context: CLContext):
|
||||
# Dynamically build paths based on current model ID
|
||||
params = Params()
|
||||
@@ -99,13 +129,17 @@ class ModelState:
|
||||
models_dir = Path(__file__).parent / "models"
|
||||
VISION_PKL_PATH = models_dir / "driving_vision_tinygrad.pkl"
|
||||
POLICY_PKL_PATH = models_dir / "driving_policy_tinygrad.pkl"
|
||||
OFF_POLICY_PKL_PATH = models_dir / "driving_off_policy_tinygrad.pkl"
|
||||
VISION_METADATA_PATH = models_dir / "driving_vision_metadata.pkl"
|
||||
POLICY_METADATA_PATH = models_dir / "driving_policy_metadata.pkl"
|
||||
OFF_POLICY_METADATA_PATH = models_dir / "driving_off_policy_metadata.pkl"
|
||||
else:
|
||||
VISION_PKL_PATH = model_dir / f"{model_id}_driving_vision_tinygrad.pkl"
|
||||
POLICY_PKL_PATH = model_dir / f"{model_id}_driving_policy_tinygrad.pkl"
|
||||
OFF_POLICY_PKL_PATH = model_dir / f"{model_id}_driving_off_policy_tinygrad.pkl"
|
||||
VISION_METADATA_PATH = model_dir / f"{model_id}_driving_vision_metadata.pkl"
|
||||
POLICY_METADATA_PATH = model_dir / f"{model_id}_driving_policy_metadata.pkl"
|
||||
OFF_POLICY_METADATA_PATH = model_dir / f"{model_id}_driving_off_policy_metadata.pkl"
|
||||
|
||||
# If ModelVersion is not set or not available, try to determine it from available model data
|
||||
if not model_version:
|
||||
@@ -156,8 +190,10 @@ class ModelState:
|
||||
# Add policy_generation attribute after loading policy_metadata
|
||||
self.policy_generation = model_version or "v8"
|
||||
self.is_v11 = (self.policy_generation == "v11")
|
||||
self.is_v10 = (self.policy_generation == "v10")
|
||||
self.is_v12 = (self.policy_generation == "v12")
|
||||
self.is_v9 = (self.policy_generation == "v9")
|
||||
self.mlsim = (self.policy_generation in ("v8", "v10", "v11"))
|
||||
self.mlsim = (self.policy_generation in ("v8", "v10", "v11", "v12"))
|
||||
|
||||
self.frames = {name: DrivingModelFrame(context, ModelConstants.TEMPORAL_SKIP) for name in self.vision_input_names}
|
||||
self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32)
|
||||
@@ -168,33 +204,50 @@ class ModelState:
|
||||
|
||||
|
||||
# policy inputs (built dynamically to support all generations)
|
||||
self.numpy_inputs = {}
|
||||
self.numpy_inputs, self.prev_desired_curv_key = self._build_policy_inputs(self.policy_input_shapes)
|
||||
|
||||
# Always-supported inputs (if model expects them)
|
||||
desire_key_init = next((k for k in self.policy_input_shapes if k.startswith('desire')), None)
|
||||
if desire_key_init:
|
||||
self.numpy_inputs[desire_key_init] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.DESIRE_LEN), dtype=np.float32)
|
||||
if 'traffic_convention' in self.policy_input_shapes:
|
||||
self.numpy_inputs['traffic_convention'] = np.zeros((1, ModelConstants.TRAFFIC_CONVENTION_LEN), dtype=np.float32)
|
||||
if 'features_buffer' in self.policy_input_shapes:
|
||||
self.numpy_inputs['features_buffer'] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32)
|
||||
# Off-policy model (optional)
|
||||
self.off_policy_enabled = False
|
||||
self.off_policy_input_shapes: dict[str, tuple[int, ...]] = {}
|
||||
self.off_policy_output_slices: dict[str, slice] = {}
|
||||
self.off_policy_numpy_inputs: dict[str, np.ndarray] = {}
|
||||
self.off_policy_prev_desired_curv_key: str | None = None
|
||||
self.off_policy_desire_key: str | None = None
|
||||
self.off_policy_inputs: dict[str, Tensor] | None = None
|
||||
self.off_policy_output: np.ndarray | None = None
|
||||
|
||||
# Optional inputs for non-v11 (and some v10/v9 variants)
|
||||
# Lateral control params
|
||||
if 'lateral_control_params' in self.policy_input_shapes:
|
||||
self.numpy_inputs['lateral_control_params'] = np.zeros((1, ModelConstants.LATERAL_CONTROL_PARAMS_LEN), dtype=np.float32)
|
||||
off_policy_metadata = None
|
||||
if self.policy_generation == "v12" or OFF_POLICY_METADATA_PATH.is_file() or OFF_POLICY_PKL_PATH.is_file():
|
||||
try:
|
||||
with open(OFF_POLICY_METADATA_PATH, 'rb') as f:
|
||||
off_policy_metadata = pickle.load(f)
|
||||
except FileNotFoundError:
|
||||
cloudlog.error(f"Missing metadata {OFF_POLICY_METADATA_PATH}, downloading...")
|
||||
from openpilot.frogpilot.assets.model_manager import ModelManager
|
||||
ModelManager().download_model(model_id)
|
||||
try:
|
||||
with open(OFF_POLICY_METADATA_PATH, 'rb') as f:
|
||||
off_policy_metadata = pickle.load(f)
|
||||
except FileNotFoundError:
|
||||
cloudlog.warning(f"Off-policy metadata still missing: {OFF_POLICY_METADATA_PATH}")
|
||||
|
||||
# Previous desired curvature: handle both singular and plural key names across model versions
|
||||
self.prev_desired_curv_key = None
|
||||
if 'prev_desired_curv' in self.policy_input_shapes:
|
||||
self.prev_desired_curv_key = 'prev_desired_curv'
|
||||
self.numpy_inputs['prev_desired_curv'] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32)
|
||||
elif 'prev_desired_curvs' in self.policy_input_shapes:
|
||||
self.prev_desired_curv_key = 'prev_desired_curvs'
|
||||
self.numpy_inputs['prev_desired_curvs'] = np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32)
|
||||
if off_policy_metadata is not None:
|
||||
self.off_policy_input_shapes = off_policy_metadata['input_shapes']
|
||||
self.off_policy_output_slices = off_policy_metadata['output_slices']
|
||||
off_policy_output_size = off_policy_metadata['output_shapes']['outputs'][1]
|
||||
self.off_policy_numpy_inputs, self.off_policy_prev_desired_curv_key = self._build_policy_inputs(self.off_policy_input_shapes)
|
||||
self.off_policy_desire_key = next((k for k in self.off_policy_numpy_inputs if k.startswith('desire')), None)
|
||||
self.off_policy_inputs = {k: Tensor(v, device='NPY').realize() for k, v in self.off_policy_numpy_inputs.items()}
|
||||
self.off_policy_output = np.zeros(off_policy_output_size, dtype=np.float32)
|
||||
try:
|
||||
with open(OFF_POLICY_PKL_PATH, "rb") as f:
|
||||
self.off_policy_run = pickle.load(f)
|
||||
self.off_policy_enabled = True
|
||||
except FileNotFoundError:
|
||||
cloudlog.warning(f"Missing off-policy model {OFF_POLICY_PKL_PATH}, skipping off-policy")
|
||||
|
||||
# Optional temporal buffer for previous desired curvature (allocate only if the policy expects it)
|
||||
if getattr(self, 'prev_desired_curv_key', None) is not None:
|
||||
# Optional temporal buffer for previous desired curvature (allocate only if any model expects it)
|
||||
if self.prev_desired_curv_key is not None or self.off_policy_prev_desired_curv_key is not None:
|
||||
self.full_prev_desired_curv = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32)
|
||||
|
||||
|
||||
@@ -204,6 +257,7 @@ class ModelState:
|
||||
self.policy_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()}
|
||||
self.policy_output = np.zeros(policy_output_size, dtype=np.float32)
|
||||
self.parser = Parser()
|
||||
self.off_policy_parser = Parser(ignore_missing=True)
|
||||
|
||||
with open(VISION_PKL_PATH, "rb") as f:
|
||||
self.vision_run = pickle.load(f)
|
||||
@@ -229,10 +283,18 @@ class ModelState:
|
||||
self.full_desire[0,:-1] = self.full_desire[0,1:]
|
||||
self.full_desire[0,-1] = new_desire
|
||||
self.numpy_inputs[self.desire_key][:] = self.full_desire.reshape((1,ModelConstants.INPUT_HISTORY_BUFFER_LEN,ModelConstants.TEMPORAL_SKIP,-1)).max(axis=2)
|
||||
if self.off_policy_enabled and self.off_policy_desire_key is not None:
|
||||
self.off_policy_numpy_inputs[self.off_policy_desire_key][:] = self.numpy_inputs[self.desire_key]
|
||||
|
||||
if 'traffic_convention' in self.numpy_inputs:
|
||||
self.numpy_inputs['traffic_convention'][:] = inputs['traffic_convention']
|
||||
if self.off_policy_enabled and 'traffic_convention' in self.off_policy_numpy_inputs:
|
||||
self.off_policy_numpy_inputs['traffic_convention'][:] = inputs['traffic_convention']
|
||||
|
||||
self.numpy_inputs['traffic_convention'][:] = inputs['traffic_convention']
|
||||
if 'lateral_control_params' in self.numpy_inputs:
|
||||
self.numpy_inputs['lateral_control_params'][:] = inputs['lateral_control_params']
|
||||
if self.off_policy_enabled and 'lateral_control_params' in self.off_policy_numpy_inputs:
|
||||
self.off_policy_numpy_inputs['lateral_control_params'][:] = inputs['lateral_control_params']
|
||||
|
||||
if prepare_only:
|
||||
return None
|
||||
@@ -254,7 +316,10 @@ class ModelState:
|
||||
|
||||
self.full_features_buffer[0,:-1] = self.full_features_buffer[0,1:]
|
||||
self.full_features_buffer[0,-1] = vision_outputs_dict['hidden_state'][0, :]
|
||||
self.numpy_inputs['features_buffer'][:] = self.full_features_buffer[0, self.temporal_idxs]
|
||||
if 'features_buffer' in self.numpy_inputs:
|
||||
self.numpy_inputs['features_buffer'][:] = self.full_features_buffer[0, self.temporal_idxs]
|
||||
if self.off_policy_enabled and 'features_buffer' in self.off_policy_numpy_inputs:
|
||||
self.off_policy_numpy_inputs['features_buffer'][:] = self.full_features_buffer[0, self.temporal_idxs]
|
||||
|
||||
self.policy_output = self.policy_run(**self.policy_inputs).contiguous().realize().uop.base.buffer.numpy()
|
||||
policy_outputs_dict = self.parser.parse_policy_outputs(self.slice_outputs(self.policy_output, self.policy_output_slices))
|
||||
@@ -265,15 +330,30 @@ class ModelState:
|
||||
self.full_prev_desired_curv[0,-1,:] = policy_outputs_dict['desired_curvature'][0, :]
|
||||
|
||||
if self.prev_desired_curv_key is not None:
|
||||
# v9 models expect zeros for prev_desired_curv(s); others use history
|
||||
if self.is_v9:
|
||||
# v9/v10/v11/v12 models expect zeros for prev_desired_curv(s); others use history
|
||||
if self.is_v9 or self.is_v10 or self.is_v11 or self.is_v12:
|
||||
self.numpy_inputs[self.prev_desired_curv_key][:] = 0 * self.full_prev_desired_curv[0, self.temporal_idxs]
|
||||
else:
|
||||
self.numpy_inputs[self.prev_desired_curv_key][:] = self.full_prev_desired_curv[0, self.temporal_idxs]
|
||||
|
||||
if self.off_policy_enabled and self.off_policy_prev_desired_curv_key is not None:
|
||||
if self.is_v9 or self.is_v12:
|
||||
self.off_policy_numpy_inputs[self.off_policy_prev_desired_curv_key][:] = 0 * self.full_prev_desired_curv[0, self.temporal_idxs]
|
||||
else:
|
||||
self.off_policy_numpy_inputs[self.off_policy_prev_desired_curv_key][:] = self.full_prev_desired_curv[0, self.temporal_idxs]
|
||||
|
||||
combined_outputs_dict = {**vision_outputs_dict, **policy_outputs_dict}
|
||||
if self.off_policy_enabled:
|
||||
self.off_policy_output = self.off_policy_run(**self.off_policy_inputs).contiguous().realize().uop.base.buffer.numpy()
|
||||
off_policy_outputs_dict = self.off_policy_parser.parse_policy_outputs(
|
||||
self.slice_outputs(self.off_policy_output, self.off_policy_output_slices)
|
||||
)
|
||||
combined_outputs_dict.update(off_policy_outputs_dict)
|
||||
if SEND_RAW_PRED:
|
||||
combined_outputs_dict['raw_pred'] = np.concatenate([self.vision_output.copy(), self.policy_output.copy()])
|
||||
raw_pred = [self.vision_output.copy(), self.policy_output.copy()]
|
||||
if self.off_policy_enabled and self.off_policy_output is not None:
|
||||
raw_pred.append(self.off_policy_output.copy())
|
||||
combined_outputs_dict['raw_pred'] = np.concatenate(raw_pred)
|
||||
|
||||
return combined_outputs_dict
|
||||
|
||||
@@ -442,7 +522,7 @@ def main(demo=False):
|
||||
drivingdata_send = messaging.new_message('drivingModelData')
|
||||
posenet_send = messaging.new_message('cameraOdometry')
|
||||
|
||||
action = get_action_from_model(model_output, prev_action, lat_delay + DT_MDL, long_delay + DT_MDL, v_ego, model.mlsim, model.is_v9)
|
||||
action = get_action_from_model(model_output, prev_action, lat_delay + DT_MDL, long_delay + DT_MDL, v_ego, model.mlsim, model.is_v9, frogpilot_toggles)
|
||||
prev_action = action
|
||||
fill_model_msg(drivingdata_send, modelv2_send, model_output, action,
|
||||
publish_state, meta_main.frame_id, meta_extra.frame_id, frame_id,
|
||||
@@ -481,4 +561,4 @@ if __name__ == "__main__":
|
||||
cloudlog.warning(f"child {PROCESS_NAME} got SIGINT")
|
||||
except Exception:
|
||||
sentry.capture_exception()
|
||||
raise
|
||||
raise
|
||||
|
||||
@@ -269,10 +269,14 @@ void FrogPilotSettingsWindow::updateVariables() {
|
||||
hasPedal = CP.getEnableGasInterceptor();
|
||||
hasRadar = !CP.getRadarUnavailable();
|
||||
hasSDSU = frogpilot_toggles.value("has_sdsu").toBool();
|
||||
hasSASCM = frogpilot_toggles.value("has_sascm").toBool();
|
||||
hasSNG = hasOpenpilotLongitudinal && CP.getAutoResumeSng();
|
||||
hasZSS = frogpilot_toggles.value("has_zss").toBool();
|
||||
isAngleCar = CP.getSteerControlType() == cereal::CarParams::SteerControlType::ANGLE;
|
||||
isBolt = carFingerprint == "CHEVROLET_BOLT_CC" || carFingerprint == "CHEVROLET_BOLT_EUV";
|
||||
isBolt = carFingerprint == "CHEVROLET_BOLT_ACC_2022_2023" ||
|
||||
carFingerprint == "CHEVROLET_BOLT_CC_2022_2023" ||
|
||||
carFingerprint == "CHEVROLET_BOLT_CC_2019_2021" ||
|
||||
carFingerprint == "CHEVROLET_BOLT_CC_2017";
|
||||
isGM = carMake == "gm";
|
||||
isHKG = carMake == "hyundai";
|
||||
isHKGCanFd = isHKG && safetyModel == cereal::CarParams::SafetyModel::HYUNDAI_CANFD;
|
||||
@@ -280,10 +284,12 @@ void FrogPilotSettingsWindow::updateVariables() {
|
||||
isTorqueCar = CP.getLateralTuning().which() == cereal::CarParams::LateralTuning::TORQUE;
|
||||
isToyota = carMake == "toyota";
|
||||
isTSK = CP.getSecOcRequired();
|
||||
isVolt = carFingerprint == "CHEVROLET_VOLT";
|
||||
isVolt = carFingerprint.find("CHEVROLET_VOLT") == 0;
|
||||
if (isVolt) hasSNG = false;
|
||||
longitudinalActuatorDelay = CP.getLongitudinalActuatorDelay();
|
||||
startAccel = CP.getStartAccel();
|
||||
steerActuatorDelay = CP.getSteerActuatorDelay();
|
||||
steerOffset = 0.0f;
|
||||
steerKp = CP.getLateralTuning().which() == cereal::CarParams::LateralTuning::PID ? CP.getLateralTuning().getPid().getKpV()[0] : 0.6;
|
||||
steerRatio = CP.getSteerRatio();
|
||||
stopAccel = CP.getStopAccel();
|
||||
@@ -295,6 +301,7 @@ void FrogPilotSettingsWindow::updateVariables() {
|
||||
|
||||
float currentDelayStock = params.getFloat("SteerDelayStock");
|
||||
float currentFrictionStock = params.getFloat("SteerFrictionStock");
|
||||
float currentSteerOffsetStock = params.getFloat("SteerOffsetStock");
|
||||
float currentKPStock = params.getFloat("SteerKPStock");
|
||||
float currentLatAccelStock = params.getFloat("SteerLatAccelStock");
|
||||
float currentLongDelayStock = params.getFloat("LongitudinalActuatorDelayStock");
|
||||
@@ -319,6 +326,13 @@ void FrogPilotSettingsWindow::updateVariables() {
|
||||
params.putFloat("SteerFrictionStock", friction);
|
||||
}
|
||||
|
||||
if (currentSteerOffsetStock != steerOffset) {
|
||||
if (params.getFloat("SteerOffset") == currentSteerOffsetStock) {
|
||||
params.putFloat("SteerOffset", steerOffset);
|
||||
}
|
||||
params.putFloat("SteerOffsetStock", steerOffset);
|
||||
}
|
||||
|
||||
if (currentKPStock != steerKp && steerKp != 0) {
|
||||
if (params.getFloat("SteerKP") == currentKPStock || currentKPStock == 0) {
|
||||
params.putFloat("SteerKP", steerKp);
|
||||
@@ -391,6 +405,7 @@ void FrogPilotSettingsWindow::updateVariables() {
|
||||
|
||||
canUsePedal = FPCP.getCanUsePedal();
|
||||
canUseSDSU = FPCP.getCanUseSDSU();
|
||||
canUseSASCM = FPCP.getCanUseSASCM();
|
||||
openpilotLongitudinalControlDisabled = FPCP.getOpenpilotLongitudinalControlDisabled();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ public:
|
||||
|
||||
bool canUsePedal = false;
|
||||
bool canUseSDSU = false;
|
||||
bool canUseSASCM = false;
|
||||
bool forceOpenDescriptions = false;
|
||||
bool hasAutoTune = true;
|
||||
bool hasBSM = true;
|
||||
@@ -24,6 +25,7 @@ public:
|
||||
bool hasPedal = false;
|
||||
bool hasRadar = true;
|
||||
bool hasSDSU = false;
|
||||
bool hasSASCM = false;
|
||||
bool hasSNG = false;
|
||||
bool hasZSS = false;
|
||||
bool isAngleCar = false;
|
||||
@@ -45,6 +47,7 @@ public:
|
||||
float longitudinalActuatorDelay;
|
||||
float startAccel;
|
||||
float steerActuatorDelay;
|
||||
float steerOffset;
|
||||
float steerKp;
|
||||
float steerRatio;
|
||||
float stopAccel;
|
||||
|
||||
@@ -41,6 +41,7 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
|
||||
{"AdvancedLateralTune", tr("Advanced Lateral Tuning"), tr("<b>Advanced steering control changes to fine-tune how openpilot drives.</b>"), "../../frogpilot/assets/toggle_icons/icon_advanced_lateral_tune.png"},
|
||||
{"SteerDelay", parent->steerActuatorDelay != 0 ? QString(tr("Actuator Delay (Default: %1)")).arg(QString::number(parent->steerActuatorDelay, 'f', 2)) : tr("Actuator Delay"), tr("<b>The time between openpilot's steering command and the vehicle's response.</b> Increase if the vehicle reacts late; decrease if it feels jumpy. Auto-learned by default."), ""},
|
||||
{"SteerFriction", parent->friction != 0 ? QString(tr("Friction (Default: %1)")).arg(QString::number(parent->friction, 'f', 2)) : tr("Friction"), tr("<b>Compensates for steering friction.</b> Increase if the wheel sticks near center; decrease if it jitters. Auto-learned by default."), ""},
|
||||
{"SteerOffset", parent->steerOffset != 0 ? QString(tr("Steer Offset (Default: %1)")).arg(QString::number(parent->steerOffset, 'f', 3)) : tr("Steer Offset"), tr("<b>Offsets steering torque to help compensate for alignment or tire issues.</b> More negative pulls the car right; more positive pulls it left. Most users should not need to touch this."), ""},
|
||||
{"SteerKP", parent->steerKp != 0 ? QString(tr("Kp Factor (Default: %1)")).arg(QString::number(parent->steerKp, 'f', 2)) : tr("Kp Factor"), tr("<b>How strongly openpilot corrects lane position.</b> Higher is tighter but twitchier; lower is smoother but slower. Auto-learned by default."), ""},
|
||||
{"SteerLatAccel", parent->latAccelFactor != 0 ? QString(tr("Lateral Acceleration (Default: %1)")).arg(QString::number(parent->latAccelFactor, 'f', 2)) : tr("Lateral Acceleration"), tr("<b>Maps steering torque to turning response.</b> Increase for sharper turns; decrease for gentler steering. Auto-learned by default."), ""},
|
||||
{"SteerRatio", parent->steerRatio != 0 ? QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(parent->steerRatio, 'f', 2)) : tr("Steer Ratio"), tr("<b>The relationship between steering wheel rotation and road wheel angle.</b> Increase if steering feels too quick or twitchy; decrease if it feels too slow or weak. Auto-learned by default."), ""},
|
||||
@@ -87,15 +88,18 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
|
||||
} else if (param == "SteerFriction") {
|
||||
std::vector<QString> steerFrictionButton{"Reset"};
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0, 0.5, QString(), std::map<float, QString>(), 0.01, false, {}, steerFrictionButton, false, false);
|
||||
} else if (param == "SteerOffset") {
|
||||
std::vector<QString> steerOffsetButton{"Reset"};
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, -0.2, 0.2, QString(), std::map<float, QString>(), 0.005, false, {}, steerOffsetButton, false, false);
|
||||
} else if (param == "SteerKP") {
|
||||
std::vector<QString> steerKPButton{"Reset"};
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, parent->steerKp * 0.5, parent->steerKp * 1.5, QString(), std::map<float, QString>(), 0.01, false, {}, steerKPButton, false, false);
|
||||
} else if (param == "SteerLatAccel") {
|
||||
std::vector<QString> steerLatAccelButton{"Reset"};
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, parent->latAccelFactor * 0.75, parent->latAccelFactor * 1.25, QString(), std::map<float, QString>(), 0.01, false, {}, steerLatAccelButton, false, false);
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, parent->latAccelFactor * 0.5, parent->latAccelFactor * 1.25, QString(), std::map<float, QString>(), 0.01, false, {}, steerLatAccelButton, false, false);
|
||||
} else if (param == "SteerRatio") {
|
||||
std::vector<QString> steerRatioButton{"Reset"};
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, parent->steerRatio * 0.5, parent->steerRatio * 1.5, QString(), std::map<float, QString>(), 0.01, false, {}, steerRatioButton, false, false);
|
||||
lateralToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, parent->steerRatio * 0.25, parent->steerRatio * 1.5, QString(), std::map<float, QString>(), 0.01, false, {}, steerRatioButton, false, false);
|
||||
|
||||
} else if (param == "AlwaysOnLateral") {
|
||||
FrogPilotManageControl *aolToggle = new FrogPilotManageControl(param, title, desc, icon);
|
||||
@@ -216,6 +220,14 @@ FrogPilotLateralPanel::FrogPilotLateralPanel(FrogPilotSettingsWindow *parent) :
|
||||
}
|
||||
});
|
||||
|
||||
steerOffsetToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerOffset"]);
|
||||
QObject::connect(steerOffsetToggle, &FrogPilotParamValueButtonControl::buttonClicked, [parent, this]() {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Reset <b>Steer Offset</b> to its default value?"), this)) {
|
||||
params.putFloat("SteerOffset", parent->steerOffset);
|
||||
steerOffsetToggle->refresh();
|
||||
}
|
||||
});
|
||||
|
||||
steerKPToggle = static_cast<FrogPilotParamValueButtonControl*>(toggles["SteerKP"]);
|
||||
QObject::connect(steerKPToggle, &FrogPilotParamValueButtonControl::buttonClicked, [parent, this]() {
|
||||
if (FrogPilotConfirmationDialog::yesorno(tr("Reset <b>Kp Factor</b> to its default value?"), this)) {
|
||||
@@ -255,12 +267,13 @@ void FrogPilotLateralPanel::showEvent(QShowEvent *event) {
|
||||
|
||||
steerDelayToggle->setTitle(QString(tr("Actuator Delay (Default: %1)")).arg(QString::number(parent->steerActuatorDelay, 'f', 2)));
|
||||
steerFrictionToggle->setTitle(QString(tr("Friction (Default: %1)")).arg(QString::number(parent->friction, 'f', 2)));
|
||||
steerOffsetToggle->setTitle(QString(tr("Steer Offset (Default: %1)")).arg(QString::number(parent->steerOffset, 'f', 3)));
|
||||
steerKPToggle->setTitle(QString(tr("Kp Factor (Default: %1)")).arg(QString::number(parent->steerKp, 'f', 2)));
|
||||
steerKPToggle->updateControl(parent->steerKp * 0.5, parent->steerKp * 1.5);
|
||||
steerLatAccelToggle->setTitle(QString(tr("Lateral Accel (Default: %1)")).arg(QString::number(parent->latAccelFactor, 'f', 2)));
|
||||
steerLatAccelToggle->updateControl(parent->latAccelFactor * 0.75, parent->latAccelFactor * 1.25);
|
||||
steerLatAccelToggle->updateControl(parent->latAccelFactor * 0.5, parent->latAccelFactor * 1.25);
|
||||
steerRatioToggle->setTitle(QString(tr("Steer Ratio (Default: %1)")).arg(QString::number(parent->steerRatio, 'f', 2)));
|
||||
steerRatioToggle->updateControl(parent->steerRatio * 0.5, parent->steerRatio * 1.5);
|
||||
steerRatioToggle->updateControl(parent->steerRatio * 0.25, parent->steerRatio * 1.5);
|
||||
|
||||
updateToggles();
|
||||
}
|
||||
@@ -340,7 +353,6 @@ void FrogPilotLateralPanel::updateToggles() {
|
||||
}
|
||||
}
|
||||
|
||||
bool forcingAutoTune = !parent->hasAutoTune && params.getBool("ForceAutoTune");
|
||||
bool forcingAutoTuneOff = parent->hasAutoTune && params.getBool("ForceAutoTuneOff");
|
||||
bool forcingTorqueController = !parent->isAngleCar && params.getBool("ForceTorqueController");
|
||||
bool usingNNFF = parent->hasNNFFLog && params.getBool("LateralTune") && params.getBool("NNFF");
|
||||
@@ -401,7 +413,13 @@ void FrogPilotLateralPanel::updateToggles() {
|
||||
|
||||
else if (key == "SteerFriction") {
|
||||
setVisible &= parent->friction != 0;
|
||||
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : !forcingAutoTune;
|
||||
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : true;
|
||||
setVisible &= parent->isTorqueCar || forcingTorqueController;
|
||||
setVisible &= !usingNNFF;
|
||||
}
|
||||
|
||||
else if (key == "SteerOffset") {
|
||||
setVisible &= parent->isGM;
|
||||
setVisible &= parent->isTorqueCar || forcingTorqueController;
|
||||
setVisible &= !usingNNFF;
|
||||
}
|
||||
@@ -413,14 +431,14 @@ void FrogPilotLateralPanel::updateToggles() {
|
||||
|
||||
else if (key == "SteerLatAccel") {
|
||||
setVisible &= parent->latAccelFactor != 0;
|
||||
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : !forcingAutoTune;
|
||||
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : true;
|
||||
setVisible &= parent->isTorqueCar || forcingTorqueController;
|
||||
setVisible &= !usingNNFF;
|
||||
}
|
||||
|
||||
else if (key == "SteerRatio") {
|
||||
setVisible &= parent->steerRatio != 0;
|
||||
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : !forcingAutoTune;
|
||||
setVisible &= parent->hasAutoTune ? forcingAutoTuneOff : true;
|
||||
}
|
||||
|
||||
toggle->setVisible(setVisible);
|
||||
|
||||
@@ -31,6 +31,7 @@ private:
|
||||
float friction;
|
||||
float latAccelFactor;
|
||||
float steerActuatorDelay;
|
||||
float steerOffset;
|
||||
float steerKp;
|
||||
float steerRatio;
|
||||
|
||||
@@ -38,7 +39,7 @@ private:
|
||||
|
||||
std::map<QString, AbstractControl*> toggles;
|
||||
|
||||
QSet<QString> advancedLateralTuneKeys = {"ForceAutoTune", "ForceAutoTuneOff", "ForceTorqueController", "SteerDelay", "SteerFriction", "SteerLatAccel", "SteerKP", "SteerRatio"};
|
||||
QSet<QString> advancedLateralTuneKeys = {"ForceAutoTune", "ForceAutoTuneOff", "ForceTorqueController", "SteerDelay", "SteerFriction", "SteerOffset", "SteerLatAccel", "SteerKP", "SteerRatio"};
|
||||
QSet<QString> aolKeys = {"AlwaysOnLateralLKAS", "AlwaysOnLateralMain", "PauseAOLOnBrake"};
|
||||
QSet<QString> laneChangeKeys = {"LaneChangeTime", "LaneDetectionWidth", "MinimumLaneChangeSpeed", "NudgelessLaneChange", "OneLaneChange"};
|
||||
QSet<QString> lateralTuneKeys = {"NNFF", "NNFFLite", "TurnDesires"};
|
||||
@@ -48,6 +49,7 @@ private:
|
||||
|
||||
FrogPilotParamValueButtonControl *steerDelayToggle;
|
||||
FrogPilotParamValueButtonControl *steerFrictionToggle;
|
||||
FrogPilotParamValueButtonControl *steerOffsetToggle;
|
||||
FrogPilotParamValueButtonControl *steerLatAccelToggle;
|
||||
FrogPilotParamValueButtonControl *steerKPToggle;
|
||||
FrogPilotParamValueButtonControl *steerRatioToggle;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -40,19 +40,19 @@ private:
|
||||
|
||||
std::map<QString, AbstractControl*> toggles;
|
||||
|
||||
QSet<QString> advancedLongitudinalTuneKeys = {"LongitudinalActuatorDelay", "StartAccel", "StopAccel", "StoppingDecelRate", "VEgoStarting", "VEgoStopping"};
|
||||
QSet<QString> aggressivePersonalityKeys = {"AggressiveFollow", "AggressiveJerkAcceleration", "AggressiveJerkDeceleration", "AggressiveJerkDanger", "AggressiveJerkSpeed", "AggressiveJerkSpeedDecrease", "ResetAggressivePersonality"};
|
||||
QSet<QString> advancedLongitudinalTuneKeys = {"EVTuning", "TruckTuning", "LongitudinalActuatorDelay", "StartAccel", "StopAccel", "StoppingDecelRate", "VEgoStarting", "VEgoStopping"};
|
||||
QSet<QString> aggressivePersonalityKeys = {"AggressiveFollow", "AggressiveFollowHigh", "AggressiveJerkAcceleration", "AggressiveJerkDeceleration", "AggressiveJerkDanger", "AggressiveJerkSpeed", "AggressiveJerkSpeedDecrease", "ResetAggressivePersonality"};
|
||||
QSet<QString> conditionalExperimentalKeys = {"CESpeed", "CESpeedLead", "CECurves", "CELead", "CEModelStopTime", "CENavigation", "CESignalSpeed", "ShowCEMStatus"};
|
||||
QSet<QString> curveSpeedKeys = {"CalibratedLateralAcceleration", "CalibrationProgress", "ResetCurveData", "ShowCSCStatus"};
|
||||
QSet<QString> customDrivingPersonalityKeys = {"AggressivePersonalityProfile", "RelaxedPersonalityProfile", "StandardPersonalityProfile", "TrafficPersonalityProfile"};
|
||||
QSet<QString> longitudinalTuneKeys = {"AccelerationProfile", "DecelerationProfile", "HumanAcceleration", "HumanFollowing", "LeadDetectionThreshold", "MaxDesiredAcceleration", "TacoTune"};
|
||||
QSet<QString> longitudinalTuneKeys = {"AccelerationProfile", "DecelerationProfile", "HumanAcceleration", "HumanFollowing", "LeadDetectionThreshold", "MaxDesiredAcceleration", "TrailerLoad", "TacoTune"};
|
||||
QSet<QString> qolKeys = {"CustomCruise", "CustomCruiseLong", "ForceStops", "IncreasedStoppedDistance", "MapGears", "ReverseCruise", "SetSpeedOffset"};
|
||||
QSet<QString> relaxedPersonalityKeys = {"RelaxedFollow", "RelaxedJerkAcceleration", "RelaxedJerkDeceleration", "RelaxedJerkDanger", "RelaxedJerkSpeed", "RelaxedJerkSpeedDecrease", "ResetRelaxedPersonality"};
|
||||
QSet<QString> relaxedPersonalityKeys = {"RelaxedFollow", "RelaxedFollowHigh", "RelaxedJerkAcceleration", "RelaxedJerkDeceleration", "RelaxedJerkDanger", "RelaxedJerkSpeed", "RelaxedJerkSpeedDecrease", "ResetRelaxedPersonality"};
|
||||
QSet<QString> speedLimitControllerKeys = {"SLCOffsets", "SLCFallback", "SLCOverride", "SLCPriority", "SLCQOL", "SLCVisuals"};
|
||||
QSet<QString> speedLimitControllerOffsetsKeys = {"Offset1", "Offset2", "Offset3", "Offset4", "Offset5", "Offset6", "Offset7"};
|
||||
QSet<QString> speedLimitControllerQOLKeys = {"ForceMPHDashboard", "SetSpeedLimit", "SLCConfirmation", "SLCLookaheadHigher", "SLCLookaheadLower", "SLCMapboxFiller"};
|
||||
QSet<QString> speedLimitControllerVisualKeys = {"ShowSLCOffset", "SpeedLimitSources"};
|
||||
QSet<QString> standardPersonalityKeys = {"StandardFollow", "StandardJerkAcceleration", "StandardJerkDeceleration", "StandardJerkDanger", "StandardJerkSpeed", "StandardJerkSpeedDecrease", "ResetStandardPersonality"};
|
||||
QSet<QString> standardPersonalityKeys = {"StandardFollow", "StandardFollowHigh", "StandardJerkAcceleration", "StandardJerkDeceleration", "StandardJerkDanger", "StandardJerkSpeed", "StandardJerkSpeedDecrease", "ResetStandardPersonality"};
|
||||
QSet<QString> trafficPersonalityKeys = {"TrafficFollow", "TrafficJerkAcceleration", "TrafficJerkDeceleration", "TrafficJerkDanger", "TrafficJerkSpeed", "TrafficJerkSpeedDecrease", "ResetTrafficPersonality"};
|
||||
|
||||
QSet<QString> parentKeys;
|
||||
|
||||
@@ -30,12 +30,14 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
|
||||
{"DeleteModel", tr("Delete Driving Models"), tr("Delete driving models from the device."), ""},
|
||||
{"DownloadModel", tr("Download Driving Models"), tr("Download driving models to the device."), ""},
|
||||
{"ModelRandomizer", tr("Model Randomizer"), tr("Driving models are chosen at random each drive and feedback prompts are used to find the model that best suits your needs."), ""},
|
||||
{"RecoveryPower", tr("Recovery Power"), tr("Adjust the strength of planplus lane recovery corrections (0.5 to 2.0)."), ""},
|
||||
{"StopDistance", tr("Stop Distance"), tr("Adjust the model's stopping distance in meters (minimum 4 for safety). Most users prefer 6."), ""},
|
||||
{"ManageBlacklistedModels", tr("Manage Model Blacklist"), tr("Add or remove models from the <b>Model Randomizer</b>'s blacklist list."), ""},
|
||||
{"ManageScores", tr("Manage Model Ratings"), tr("Reset or view the saved ratings for the driving models."), ""},
|
||||
{"SelectModel", tr("Select Driving Model"), tr("Select the active driving model."), ""},
|
||||
};
|
||||
|
||||
FrogPilotParamValueButtonControl *recoveryPowerToggle = nullptr;
|
||||
FrogPilotParamValueButtonControl *stopDistanceToggle = nullptr;
|
||||
|
||||
for (const auto &[param, title, desc, icon] : modelToggles) {
|
||||
@@ -431,6 +433,10 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
|
||||
});
|
||||
modelToggle = selectModelButton;
|
||||
|
||||
} else if (param == "RecoveryPower") {
|
||||
std::vector<QString> recoveryPowerButton{"Reset"};
|
||||
modelToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 0.5, 2.0, QString(), std::map<float, QString>(), 0.1, false, {}, recoveryPowerButton, false, false);
|
||||
recoveryPowerToggle = static_cast<FrogPilotParamValueButtonControl*>(modelToggle);
|
||||
} else if (param == "StopDistance") {
|
||||
std::vector<QString> stopDistanceButton{"Reset"};
|
||||
modelToggle = new FrogPilotParamValueButtonControl(param, title, desc, icon, 4.0, 10.0, QString(), std::map<float, QString>(), 0.1, false, {}, stopDistanceButton, false, false);
|
||||
@@ -463,6 +469,16 @@ FrogPilotModelPanel::FrogPilotModelPanel(FrogPilotSettingsWindow *parent) : Frog
|
||||
}
|
||||
});
|
||||
|
||||
if (recoveryPowerToggle) {
|
||||
QObject::connect(recoveryPowerToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this, recoveryPowerToggle]() {
|
||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to reset your <b>Recovery Power</b> to the default of 1.0?"), tr("Reset"), this)) {
|
||||
params.putFloat("RecoveryPower", 1.0);
|
||||
recoveryPowerToggle->refresh();
|
||||
updateFrogPilotToggles();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (stopDistanceToggle) {
|
||||
QObject::connect(stopDistanceToggle, &FrogPilotParamValueButtonControl::buttonClicked, [this, stopDistanceToggle]() {
|
||||
if (ConfirmationDialog::confirm(tr("Are you sure you want to reset your <b>Stop Distance</b> to the default of 6 meters?"), tr("Reset"), this)) {
|
||||
@@ -487,6 +503,8 @@ bool FrogPilotModelPanel::isModelInstalled(const QString &key) const {
|
||||
bool has_policy_tg = false;
|
||||
bool has_vision_meta = false;
|
||||
bool has_vision_tg = false;
|
||||
bool has_off_policy_meta = false;
|
||||
bool has_off_policy_tg = false;
|
||||
bool foundAny = false;
|
||||
|
||||
for (const QString &file : modelDir.entryList(QDir::Files)) {
|
||||
@@ -505,6 +523,10 @@ bool FrogPilotModelPanel::isModelInstalled(const QString &key) const {
|
||||
has_policy_meta = true;
|
||||
} else if (base.contains("_driving_policy_tinygrad")) {
|
||||
has_policy_tg = true;
|
||||
} else if (base.contains("_driving_off_policy_metadata")) {
|
||||
has_off_policy_meta = true;
|
||||
} else if (base.contains("_driving_off_policy_tinygrad")) {
|
||||
has_off_policy_tg = true;
|
||||
} else if (base.contains("_driving_vision_metadata")) {
|
||||
has_vision_meta = true;
|
||||
} else if (base.contains("_driving_vision_tinygrad")) {
|
||||
@@ -518,6 +540,9 @@ bool FrogPilotModelPanel::isModelInstalled(const QString &key) const {
|
||||
}
|
||||
|
||||
if (has_policy_meta && has_policy_tg && has_vision_meta && has_vision_tg) {
|
||||
if (has_off_policy_meta || has_off_policy_tg) {
|
||||
return has_off_policy_meta && has_off_policy_tg;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -740,6 +765,8 @@ void FrogPilotModelPanel::updateToggles() {
|
||||
setVisible &= params.getBool("ModelRandomizer");
|
||||
} else if (key == "SelectModel") {
|
||||
setVisible &= !params.getBool("ModelRandomizer");
|
||||
} else if (key == "RecoveryPower") {
|
||||
setVisible &= (tuningLevel == 3); // Only visible in developer tuning level
|
||||
} else if (key == "StopDistance") {
|
||||
setVisible &= (tuningLevel == 3); // Only visible in developer tuning level
|
||||
}
|
||||
|
||||
@@ -66,6 +66,11 @@ void deleteThemeAsset(QDir &directory, const QString &subFolder, const QString &
|
||||
}
|
||||
|
||||
void downloadThemeAsset(const QString &input, const std::string ¶mKey, const QString &assetParam, Params ¶ms, Params ¶ms_memory) {
|
||||
if (paramKey == "BootLogoToDownload") {
|
||||
params_memory.put(paramKey, input.trimmed().toStdString());
|
||||
return;
|
||||
}
|
||||
|
||||
QString output = input;
|
||||
int tilde = output.indexOf("~");
|
||||
if (tilde >= 0) {
|
||||
@@ -228,6 +233,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr
|
||||
|
||||
const std::vector<std::tuple<QString, QString, QString, QString>> themeToggles {
|
||||
{"PersonalizeOpenpilot", tr("Custom Themes"), tr("<b>The overall look and feel of openpilot.</b> Use the \"Theme Maker\" in \"The Pond\" to create and share your own themes!"), "../../frogpilot/assets/toggle_icons/icon_frog.png"},
|
||||
{"BootLogo", tr("Boot Logo"), tr("<b>The boot logo shown while the device starts.</b>"), ""},
|
||||
{"CustomColors", tr("Color Scheme"), tr("<b>The color scheme used throughout openpilot.</b> Use the \"Theme Maker\" in \"The Pond\" to create and share your own themes!"), ""},
|
||||
{"CustomDistanceIcons", tr("Distance Button"), tr("<b>The distance button icons shown on the driving screen.</b> Use the \"Theme Maker\" in \"The Pond\" to create and share your own themes!"), ""},
|
||||
{"CustomIcons", tr("Icon Pack"), tr("<b>The icon style used across openpilot.</b> Use the \"Theme Maker\" in \"The Pond\" to create and share your own themes!"), ""},
|
||||
@@ -252,6 +258,57 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr
|
||||
themesLayout->setCurrentWidget(customThemesPanel);
|
||||
});
|
||||
themeToggle = personalizeOpenpilotToggle;
|
||||
} else if (param == "BootLogo") {
|
||||
manageBootLogosButton = new FrogPilotButtonsControl(title, desc, icon, {tr("DELETE"), tr("DOWNLOAD"), tr("SELECT")});
|
||||
QObject::connect(manageBootLogosButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
|
||||
// Show all downloaded boot logos, including the currently selected one.
|
||||
QStringList bootLogos = getThemeList(true, QDir(bootLogosDirectory.path()), "", "BootLogo", params);
|
||||
|
||||
if (id == 0) {
|
||||
QString bootLogoToDelete = MultiOptionDialog::getSelection(tr("Select a boot logo to delete"), bootLogos, "", this);
|
||||
if (!bootLogoToDelete.isEmpty() && ConfirmationDialog::confirm(tr("Delete the \"%1\" boot logo?").arg(bootLogoToDelete), tr("Delete"), this)) {
|
||||
bootLogosDownloaded = false;
|
||||
|
||||
deleteThemeAsset(bootLogosDirectory, "", "DownloadableBootLogos", bootLogoToDelete, params);
|
||||
}
|
||||
} else if (id == 1) {
|
||||
if (bootLogoDownloading) {
|
||||
cancellingDownload = true;
|
||||
|
||||
params_memory.putBool("CancelThemeDownload", true);
|
||||
|
||||
QTimer::singleShot(2500, [this]() {
|
||||
bootLogoDownloading = false;
|
||||
cancellingDownload = false;
|
||||
themeDownloading = false;
|
||||
|
||||
params_memory.putBool("CancelThemeDownload", false);
|
||||
});
|
||||
} else {
|
||||
QStringList downloadableBootLogos = QString::fromStdString(params.get("DownloadableBootLogos")).split(",");
|
||||
bootLogoToDownload = MultiOptionDialog::getSelection(tr("Select a boot logo to download"), downloadableBootLogos, "", this);
|
||||
if (!bootLogoToDownload.isEmpty()) {
|
||||
manageBootLogosButton->setValue(storeThemeName(bootLogoToDownload, "BootLogo", params));
|
||||
|
||||
bootLogoDownloading = true;
|
||||
themeDownloading = true;
|
||||
|
||||
params_memory.put("ThemeDownloadProgress", "Downloading...");
|
||||
|
||||
downloadThemeAsset(bootLogoToDownload, "BootLogoToDownload", "DownloadableBootLogos", params, params_memory);
|
||||
|
||||
downloadStatusLabel->setText("Downloading...");
|
||||
}
|
||||
}
|
||||
} else if (id == 2) {
|
||||
QString bootLogoToSelect = MultiOptionDialog::getSelection(tr("Select a boot logo"), bootLogos, getThemeName("BootLogo", params), this);
|
||||
if (!bootLogoToSelect.isEmpty()) {
|
||||
manageBootLogosButton->setValue(storeThemeName(bootLogoToSelect, "BootLogo", params));
|
||||
}
|
||||
}
|
||||
});
|
||||
manageBootLogosButton->setValue(getThemeName(param.toStdString(), params));
|
||||
themeToggle = manageBootLogosButton;
|
||||
} else if (param == "CustomColors") {
|
||||
manageCustomColorsButton = new FrogPilotButtonsControl(title, desc, icon, {tr("DELETE"), tr("DOWNLOAD"), tr("SELECT")});
|
||||
QObject::connect(manageCustomColorsButton, &FrogPilotButtonsControl::buttonClicked, [this](int id) {
|
||||
@@ -704,6 +761,7 @@ FrogPilotThemesPanel::FrogPilotThemesPanel(FrogPilotSettingsWindow *parent) : Fr
|
||||
}
|
||||
|
||||
void FrogPilotThemesPanel::showEvent(QShowEvent *event) {
|
||||
bootLogosDownloaded = params.get("DownloadableBootLogos").empty();
|
||||
colorsDownloaded = params.get("DownloadableColors").empty();
|
||||
distanceIconsDownloaded = params.get("DownloadableDistanceIcons").empty();
|
||||
iconsDownloaded = params.get("DownloadableIcons").empty();
|
||||
@@ -756,6 +814,7 @@ void FrogPilotThemesPanel::updateState(const UIState &s, const FrogPilotUIState
|
||||
finalizingDownload = true;
|
||||
|
||||
QTimer::singleShot(2500, [this]() {
|
||||
bootLogoDownloading = false;
|
||||
colorDownloading = false;
|
||||
distanceIconDownloading = false;
|
||||
finalizingDownload = false;
|
||||
@@ -765,6 +824,7 @@ void FrogPilotThemesPanel::updateState(const UIState &s, const FrogPilotUIState
|
||||
themeDownloading = false;
|
||||
wheelDownloading = false;
|
||||
|
||||
bootLogosDownloaded = params.get("DownloadableBootLogos").empty();
|
||||
colorsDownloaded = params.get("DownloadableColors").empty();
|
||||
distanceIconsDownloaded = params.get("DownloadableDistanceIcons").empty();
|
||||
iconsDownloaded = params.get("DownloadableIcons").empty();
|
||||
@@ -782,6 +842,11 @@ void FrogPilotThemesPanel::updateState(const UIState &s, const FrogPilotUIState
|
||||
|
||||
bool parked = !s.scene.started || fs.frogpilot_scene.parked || fs.frogpilot_toggles.value("frogs_go_moo").toBool();
|
||||
|
||||
manageBootLogosButton->setText(1, bootLogoDownloading ? tr("CANCEL") : tr("DOWNLOAD"));
|
||||
manageBootLogosButton->setEnabledButtons(0, !themeDownloading);
|
||||
manageBootLogosButton->setEnabledButtons(1, fs.frogpilot_scene.online && (!themeDownloading || bootLogoDownloading) && !cancellingDownload && !finalizingDownload && !bootLogosDownloaded && parked);
|
||||
manageBootLogosButton->setEnabledButtons(2, !themeDownloading);
|
||||
|
||||
manageCustomColorsButton->setText(1, colorDownloading ? tr("CANCEL") : tr("DOWNLOAD"));
|
||||
manageCustomColorsButton->setEnabledButtons(0, !themeDownloading);
|
||||
manageCustomColorsButton->setEnabledButtons(1, fs.frogpilot_scene.online && (!themeDownloading || colorDownloading) && !cancellingDownload && !finalizingDownload && !colorsDownloaded && parked);
|
||||
|
||||
@@ -19,6 +19,8 @@ private:
|
||||
void updateToggles();
|
||||
|
||||
bool cancellingDownload;
|
||||
bool bootLogoDownloading = false;
|
||||
bool bootLogosDownloaded = false;
|
||||
bool colorDownloading;
|
||||
bool colorsDownloaded;
|
||||
bool distanceIconDownloading;
|
||||
@@ -40,10 +42,11 @@ private:
|
||||
|
||||
std::map<QString, AbstractControl*> toggles;
|
||||
|
||||
QSet<QString> customThemeKeys = {"CustomColors", "CustomDistanceIcons", "CustomIcons", "CustomSignals", "CustomSounds", "DownloadStatusLabel", "WheelIcon"};
|
||||
QSet<QString> customThemeKeys = {"BootLogo", "CustomColors", "CustomDistanceIcons", "CustomIcons", "CustomSignals", "CustomSounds", "DownloadStatusLabel", "WheelIcon"};
|
||||
|
||||
QSet<QString> parentKeys;
|
||||
|
||||
FrogPilotButtonsControl *manageBootLogosButton;
|
||||
FrogPilotButtonsControl *manageCustomColorsButton;
|
||||
FrogPilotButtonsControl *manageCustomIconsButton;
|
||||
FrogPilotButtonsControl *manageCustomSignalsButton;
|
||||
@@ -55,11 +58,13 @@ private:
|
||||
|
||||
LabelControl *downloadStatusLabel;
|
||||
|
||||
QDir bootLogosDirectory{"/data/themes/bootlogos/"};
|
||||
QDir themePacksDirectory{"/data/themes/theme_packs/"};
|
||||
QDir wheelsDirectory{"/data/themes/steering_wheels/"};
|
||||
|
||||
QJsonObject frogpilotToggleLevels;
|
||||
|
||||
QString bootLogoToDownload;
|
||||
QString colorSchemeToDownload;
|
||||
QString distanceIconPackToDownload;
|
||||
QString iconPackToDownload;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include <QRegularExpression>
|
||||
#include <QTextStream>
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "frogpilot/ui/qt/offroad/vehicle_settings.h"
|
||||
|
||||
QStringList getCarNames(const QString &carMake, QMap<QString, QString> &carModels) {
|
||||
@@ -170,6 +173,8 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
|
||||
{"ExperimentalGMTune", tr("FrogsGoMoo's Experimental Tune"), tr("<b>Experimental GM tune by FrogsGoMoo</b> that attempts to smoothen stopping and takeoff control. Use at your own risk!"), ""},
|
||||
{"GMPedalLongitudinal", tr("Use Pedal for Longitudinal Control"), tr("<b>Use the pedal interceptor for longitudinal control</b> instead of camera ACC/Redneck when available."), ""},
|
||||
{"LongPitch", tr("Smooth Pedal Response on Hills"), tr("<b>Smoothen acceleration and braking</b> when driving downhill/uphill."), ""},
|
||||
{"RedPanda", tr("Red Panda"), tr("<b>Enable Red Panda behavior</b> for GM (alternate safety config and bus numbering). Requires a reboot to take effect."), ""},
|
||||
{"RemoteStartBootsComma", tr("Remote Start Boots Comma"), tr("<b>Use GM C9 SystemPowerMode</b> for ignition detection. Toggle requires a panda firmware update and a reboot to take effect."), ""},
|
||||
{"VoltSNG", tr("Stop-and-Go Hack"), tr("<b>Force stop-and-go</b> on the 2017 Chevy Volt."), ""},
|
||||
|
||||
{"HKGToggles", tr("Hyundai/Kia/Genesis Settings"), tr("<b>FrogPilot features for Genesis, Hyundai, and Kia vehicles.</b>"), ""},
|
||||
@@ -189,6 +194,7 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
|
||||
{"PedalSupport", tr("comma Pedal Support"), tr("<b>Does your vehicle support the \"comma pedal\"?</b>"), ""},
|
||||
{"OpenpilotLongitudinal", tr("openpilot Longitudinal Support"), tr("<b>Can openpilot control the vehicle's acceleration and braking?</b>"), ""},
|
||||
{"RadarSupport", tr("Radar Support"), tr("<b>Does openpilot use the vehicle's radar data</b> alongside the device's camera for tracking lead vehicles?"), ""},
|
||||
{"SASCMSupport", tr("SASCM Support"), tr("<b>Does your vehicle support \"SASCMs\"?</b>"), ""},
|
||||
{"SDSUSupport", tr("SDSU Support"), tr("<b>Does your vehicle support \"SDSUs\"?</b>"), ""},
|
||||
{"SNGSupport", tr("Stop-and-Go Support"), tr("<b>Does your vehicle support stop-and-go driving?</b>"), ""}
|
||||
};
|
||||
@@ -299,6 +305,25 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent)
|
||||
});
|
||||
}
|
||||
|
||||
ParamControl *remoteStartToggle = static_cast<ParamControl*>(toggles["RemoteStartBootsComma"]);
|
||||
QObject::connect(remoteStartToggle, &ToggleControl::toggleFlipped, [parent, remoteStartToggle, this](bool state) {
|
||||
const QString prompt = tr("Remote Start requires a Panda firmware update. Flash the Panda now?");
|
||||
if (!FrogPilotConfirmationDialog::yesorno(prompt, this)) {
|
||||
params.putBool("RemoteStartBootsComma", !state);
|
||||
remoteStartToggle->refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
std::thread([parent, this]() {
|
||||
parent->keepScreenOn = true;
|
||||
params_memory.putBool("FlashPanda", true);
|
||||
while (params_memory.getBool("FlashPanda")) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
Hardware::reboot();
|
||||
}).detach();
|
||||
});
|
||||
|
||||
openDescriptions(forceOpenDescriptions, toggles);
|
||||
|
||||
QObject::connect(uiState(), &UIState::offroadTransition, [selectMakeButton, selectModelButton, this]() {
|
||||
@@ -342,6 +367,7 @@ void FrogPilotVehiclesPanel::showEvent(QShowEvent *event) {
|
||||
|
||||
QStringList detected;
|
||||
if (hasPedal) detected << "comma Pedal";
|
||||
if (parent->hasSASCM) detected << "SASCM";
|
||||
if (parent->hasSDSU) detected << "SDSU";
|
||||
if (parent->hasZSS) detected << "ZSS";
|
||||
static_cast<LabelControl*>(toggles["HardwareDetected"])->setText(detected.isEmpty() ? tr("None") : detected.join(", "));
|
||||
@@ -350,6 +376,7 @@ void FrogPilotVehiclesPanel::showEvent(QShowEvent *event) {
|
||||
static_cast<LabelControl*>(toggles["OpenpilotLongitudinal"])->setText(hasOpenpilotLongitudinal ? tr("Yes") : tr("No"));
|
||||
static_cast<LabelControl*>(toggles["PedalSupport"])->setText(parent->canUsePedal ? tr("Yes") : tr("No"));
|
||||
static_cast<LabelControl*>(toggles["RadarSupport"])->setText(parent->hasRadar ? tr("Yes") : tr("No"));
|
||||
static_cast<LabelControl*>(toggles["SASCMSupport"])->setText(parent->canUseSASCM ? tr("Yes") : tr("No"));
|
||||
static_cast<LabelControl*>(toggles["SDSUSupport"])->setText(parent->canUseSDSU ? tr("Yes") : tr("No"));
|
||||
static_cast<LabelControl*>(toggles["SNGSupport"])->setText(hasSNG ? tr("Yes") : tr("No"));
|
||||
|
||||
|
||||
@@ -36,11 +36,11 @@ private:
|
||||
|
||||
std::map<QString, AbstractControl*> toggles;
|
||||
|
||||
QSet<QString> gmKeys = {"ExperimentalGMTune", "GMPedalLongitudinal", "LongPitch", "VoltSNG"};
|
||||
QSet<QString> gmKeys = {"ExperimentalGMTune", "GMPedalLongitudinal", "LongPitch", "RedPanda", "RemoteStartBootsComma", "VoltSNG"};
|
||||
QSet<QString> hkgKeys = {"NewLongAPI", "TacoTuneHacks"};
|
||||
QSet<QString> longitudinalKeys = {"ExperimentalGMTune", "FrogsGoMoosTweak", "LongPitch", "NewLongAPI", "SNGHack", "VoltSNG"};
|
||||
QSet<QString> toyotaKeys = {"ClusterOffset", "FrogsGoMoosTweak", "LockDoorsTimer", "SNGHack", "ToyotaDoors"};
|
||||
QSet<QString> vehicleInfoKeys = {"BlindSpotSupport", "HardwareDetected", "OpenpilotLongitudinal", "PedalSupport", "RadarSupport", "SDSUSupport", "SNGSupport"};
|
||||
QSet<QString> vehicleInfoKeys = {"BlindSpotSupport", "HardwareDetected", "OpenpilotLongitudinal", "PedalSupport", "RadarSupport", "SASCMSupport", "SDSUSupport", "SNGSupport"};
|
||||
|
||||
QSet<QString> parentKeys;
|
||||
|
||||
@@ -50,6 +50,7 @@ private:
|
||||
ParamControl *forceFingerprint;
|
||||
|
||||
Params params;
|
||||
Params params_memory{"/dev/shm/params"};
|
||||
Params params_default{"/dev/shm/params_default"};
|
||||
|
||||
QJsonObject frogpilotToggleLevels;
|
||||
|
||||
@@ -171,6 +171,7 @@ BO_ 481 ASCMSteeringButton: 7 K124_ASCM
|
||||
SG_ DistanceButton : 22|1@0+ (1,0) [0|0] "" NEO
|
||||
SG_ LKAButton : 23|1@0+ (1,0) [0|0] "" NEO
|
||||
SG_ ACCAlwaysOne : 24|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ ACCHiddenBit : 30|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ ACCButtons : 46|3@0+ (1,0) [0|0] "" NEO
|
||||
SG_ DriveModeButton : 39|1@0+ (1,0) [0|1] "" XXX
|
||||
SG_ RollingCounter : 33|2@0+ (1,0) [0|3] "" NEO
|
||||
|
||||
@@ -6,6 +6,8 @@ Import('build_project', 'base_project_f4', 'base_project_h7')
|
||||
build_projects = {
|
||||
"panda": base_project_f4,
|
||||
"panda_h7": base_project_h7,
|
||||
"panda_remote": base_project_f4,
|
||||
"panda_h7_remote": base_project_h7,
|
||||
}
|
||||
|
||||
for project_name, project in build_projects.items():
|
||||
@@ -18,4 +20,7 @@ for project_name, project in build_projects.items():
|
||||
if "H723" in os.environ:
|
||||
flags.append('-DSTM32H723')
|
||||
|
||||
if "remote" in project_name:
|
||||
flags.append('-DPANDA_GM_REMOTE_START_C9')
|
||||
|
||||
build_project(project_name, project, flags)
|
||||
|
||||
@@ -33,6 +33,7 @@ extern bool can_loopback;
|
||||
// Ignition detected from CAN meessages
|
||||
bool ignition_can = false;
|
||||
uint32_t ignition_can_cnt = 0U;
|
||||
extern bool gm_remote_start_boots_comma;
|
||||
|
||||
#define ALL_CAN_SILENT 0xFF
|
||||
#define ALL_CAN_LIVE 0
|
||||
@@ -202,10 +203,22 @@ void ignition_can_hook(CANPacket_t *to_push) {
|
||||
int len = GET_LEN(to_push);
|
||||
|
||||
// GM exception
|
||||
if ((addr == 0xC9) && (len == 8)) {
|
||||
// Matches SystemPowerMode (1=Run, 0=Off)
|
||||
ignition_can = (GET_BYTE(to_push, 6) & 0x10U) != 0U;
|
||||
ignition_can_cnt = 0U;
|
||||
#ifdef PANDA_GM_REMOTE_START_C9
|
||||
if (true) {
|
||||
#else
|
||||
if (gm_remote_start_boots_comma) {
|
||||
#endif
|
||||
if ((addr == 0xC9) && (len == 8)) {
|
||||
// Matches SystemPowerMode (1=Run, 0=Off)
|
||||
ignition_can = (GET_BYTE(to_push, 6) & 0x10U) != 0U;
|
||||
ignition_can_cnt = 0U;
|
||||
}
|
||||
} else {
|
||||
if ((addr == 0x1F1) && (len == 8)) {
|
||||
// SystemPowerMode (2=Run, 3=Crank Request)
|
||||
ignition_can = (GET_BYTE(to_push, 0) & 0x2U) != 0U;
|
||||
ignition_can_cnt = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
// Tesla exception
|
||||
|
||||
+199
-49
@@ -9,20 +9,32 @@ const SteeringLimits GM_STEERING_LIMITS = {
|
||||
.type = TorqueDriverLimited,
|
||||
};
|
||||
|
||||
const SteeringLimits GM_BOLT_2017_STEERING_LIMITS = {
|
||||
.max_steer = 450,
|
||||
.max_rate_up = 15,
|
||||
.max_rate_down = 34,
|
||||
.driver_torque_allowance = 78,
|
||||
.driver_torque_factor = 6,
|
||||
.max_rt_delta = 345,
|
||||
.max_rt_interval = 200000,
|
||||
.type = TorqueDriverLimited,
|
||||
};
|
||||
|
||||
const LongitudinalLimits GM_ASCM_LONG_LIMITS = {
|
||||
.max_gas = 7168,
|
||||
.max_gas = 8191,
|
||||
.min_gas = 5500,
|
||||
.inactive_gas = 5500,
|
||||
.max_brake = 400,
|
||||
};
|
||||
|
||||
const LongitudinalLimits GM_CAM_LONG_LIMITS = {
|
||||
.max_gas = 7496,
|
||||
.max_gas = 8848,
|
||||
.min_gas = 5610,
|
||||
.inactive_gas = 5650,
|
||||
.max_brake = 400,
|
||||
};
|
||||
|
||||
const SteeringLimits *gm_steer_limits;
|
||||
const LongitudinalLimits *gm_long_limits;
|
||||
|
||||
const int GM_STANDSTILL_THRSLD = 10; // 0.311kph
|
||||
@@ -36,16 +48,16 @@ const CanMsg GM_ASCM_TX_MSGS[] = {{0x180, 0, 4}, {0x409, 0, 7}, {0x40A, 0, 7}, {
|
||||
{0xA1, 1, 7}, {0x306, 1, 8}, {0x308, 1, 7}, {0x310, 1, 2}, // obs bus
|
||||
{0x315, 2, 5}}; // ch bus
|
||||
|
||||
const CanMsg GM_CAM_TX_MSGS[] = {{0x180, 0, 4}, {0x200, 0, 6}, {0x1E1, 0, 7}, {0xBD, 0, 7}, {0x1F5, 0, 8}, // pt bus
|
||||
const CanMsg GM_CAM_TX_MSGS[] = {{0x180, 0, 4}, {0x370, 0, 6}, {0x200, 0, 6}, {0x1E1, 0, 7}, {0x3D1, 0, 8}, {0xBD, 0, 7}, {0x1F5, 0, 8}, // pt bus
|
||||
{0x1E1, 2, 7}, {0x184, 2, 8}}; // camera bus
|
||||
|
||||
const CanMsg GM_CAM_LONG_TX_MSGS[] = {{0x180, 0, 4}, {0x315, 0, 5}, {0x2CB, 0, 8}, {0x370, 0, 6}, {0x200, 0, 6}, {0xBD, 0, 7}, {0x1F5, 0, 8}, // pt bus
|
||||
{0x1E1, 2, 7}, {0x184, 2, 8}}; // camera bus
|
||||
const CanMsg GM_CAM_LONG_TX_MSGS[] = {{0x180, 0, 4}, {0x315, 0, 5}, {0x2CB, 0, 8}, {0x370, 0, 6}, {0x200, 0, 6}, {0x3D1, 0, 8}, {0xBD, 0, 7}, {0x1F5, 0, 8}, // pt bus
|
||||
{0x315, 2, 5}, {0x1E1, 2, 7}, {0x184, 2, 8}}; // camera bus
|
||||
|
||||
const CanMsg GM_SDGM_TX_MSGS[] = {{0x180, 0, 4}, {0x1E1, 0, 7}, {0xBD, 0, 7}, {0x1F5, 0, 8}, // pt bus
|
||||
{0x184, 2, 8}}; // camera bus
|
||||
|
||||
const CanMsg GM_CC_LONG_TX_MSGS[] = {{0x180, 0, 4}, {0x1E1, 0, 7}, {0xBD, 0, 7}, {0x1F5, 0, 8}, // pt bus
|
||||
const CanMsg GM_CC_LONG_TX_MSGS[] = {{0x180, 0, 4}, {0x370, 0, 6}, {0x1E1, 0, 7}, {0x3D1, 0, 8}, {0xBD, 0, 7}, {0x1F5, 0, 8}, // pt bus
|
||||
{0x184, 2, 8}, {0x1E1, 2, 7}}; // camera bus
|
||||
|
||||
// TODO: do checksum and counter checks. Add correct timestep, 0.1s for now.
|
||||
@@ -62,13 +74,26 @@ RxCheck gm_rx_checks[] = {
|
||||
|
||||
const uint16_t GM_PARAM_HW_CAM = 1;
|
||||
const uint16_t GM_PARAM_HW_CAM_LONG = 2;
|
||||
const uint16_t GM_PARAM_HW_SDGM = 4;
|
||||
const uint16_t GM_PARAM_CC_LONG = 8;
|
||||
const uint16_t GM_PARAM_HW_ASCM_LONG = 16;
|
||||
const uint16_t GM_PARAM_NO_CAMERA = 32;
|
||||
const uint16_t GM_PARAM_NO_ACC = 64;
|
||||
const uint16_t GM_PARAM_PEDAL_LONG = 128; // TODO: this can be inferred
|
||||
const uint16_t GM_PARAM_PEDAL_INTERCEPTOR = 256;
|
||||
const uint16_t GM_PARAM_CC_LONG = 4;
|
||||
const uint16_t GM_PARAM_HW_ASCM_LONG = 8;
|
||||
const uint16_t GM_PARAM_NO_CAMERA = 16;
|
||||
const uint16_t GM_PARAM_NO_ACC = 32;
|
||||
const uint16_t GM_PARAM_PEDAL_LONG = 64; // TODO: this can be inferred
|
||||
const uint16_t GM_PARAM_PEDAL_INTERCEPTOR = 128;
|
||||
const uint16_t GM_PARAM_ASCM_INT = 256;
|
||||
const uint16_t GM_PARAM_FORCE_BRAKE_C9 = 512;
|
||||
const uint16_t GM_PARAM_HW_SDGM = 1024;
|
||||
const uint16_t GM_PARAM_BOLT_2017 = 2048;
|
||||
const uint16_t GM_PARAM_BOLT_2022_PEDAL = 4096;
|
||||
const uint16_t GM_PARAM_REMOTE_START_BOOTS_COMMA = 8192;
|
||||
const uint16_t GM_PARAM_PANDA_3D1_SCHED = 16384;
|
||||
|
||||
const uint32_t GM_3D1_PERIOD_US = 100000U;
|
||||
const uint32_t GM_3D1_TX_OFFSET_US = 0U;
|
||||
const uint32_t GM_3D1_LOCK_TOLERANCE_US = 20000U;
|
||||
|
||||
void can_send(CANPacket_t *to_push, uint8_t bus_number, bool skip_tx_hook);
|
||||
void can_set_checksum(CANPacket_t *packet);
|
||||
|
||||
enum {
|
||||
GM_BTN_UNPRESS = 1,
|
||||
@@ -90,30 +115,47 @@ bool gm_pedal_long = false;
|
||||
bool gm_cc_long = false;
|
||||
bool gm_skip_relay_check = false;
|
||||
bool gm_force_ascm = false;
|
||||
bool gm_bolt_2022_pedal = false;
|
||||
bool gm_ascm_int = false;
|
||||
bool gm_force_brake_c9 = false;
|
||||
bool gm_remote_start_boots_comma = false;
|
||||
bool gm_panda_3d1_sched = false;
|
||||
|
||||
static void handle_gm_wheel_buttons(const CANPacket_t *to_push) {
|
||||
int button = (GET_BYTE(to_push, 5) & 0x70U) >> 4;
|
||||
bool gm_3d1_spoof_valid = false;
|
||||
bool gm_3d1_internal_tx = false;
|
||||
uint8_t gm_3d1_spoof_data[8] = {0U};
|
||||
uint32_t gm_3d1_next_tx_us = 0U;
|
||||
uint32_t gm_3d1_expected_stock_us = 0U;
|
||||
uint32_t gm_3d1_last_stock_us = 0U;
|
||||
bool gm_3d1_phase_locked = false;
|
||||
|
||||
// enter controls on falling edge of set or rising edge of resume (avoids fault)
|
||||
bool set = (button != GM_BTN_SET) && (cruise_button_prev == GM_BTN_SET);
|
||||
bool res = (button == GM_BTN_RESUME) && (cruise_button_prev != GM_BTN_RESUME);
|
||||
if (set || res) {
|
||||
controls_allowed = true;
|
||||
static void gm_try_send_3d1_spoof(uint32_t now_us) {
|
||||
if (!(gm_panda_3d1_sched && gm_3d1_spoof_valid && (gm_3d1_next_tx_us != 0U))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// exit controls on cancel press
|
||||
if (button == GM_BTN_CANCEL) {
|
||||
controls_allowed = false;
|
||||
if ((int32_t)(now_us - gm_3d1_next_tx_us) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
cruise_button_prev = button;
|
||||
CANPacket_t to_send = {0};
|
||||
to_send.returned = 0U;
|
||||
to_send.rejected = 0U;
|
||||
to_send.extended = 0U;
|
||||
to_send.addr = 0x3D1U;
|
||||
to_send.bus = 0U;
|
||||
to_send.data_len_code = 8U;
|
||||
(void)memcpy(to_send.data, gm_3d1_spoof_data, 8U);
|
||||
can_set_checksum(&to_send);
|
||||
|
||||
gm_3d1_internal_tx = true;
|
||||
can_send(&to_send, 0U, false);
|
||||
gm_3d1_internal_tx = false;
|
||||
|
||||
gm_3d1_next_tx_us += GM_3D1_PERIOD_US;
|
||||
}
|
||||
|
||||
static void gm_rx_hook(const CANPacket_t *to_push) {
|
||||
if ((GET_BUS(to_push) == 2U) && (GET_ADDR(to_push) == 0x1E1) && (gm_hw == GM_SDGM)) {
|
||||
// SDGM buttons are on bus 2
|
||||
handle_gm_wheel_buttons(to_push);
|
||||
}
|
||||
if (GET_BUS(to_push) == 0U) {
|
||||
int addr = GET_ADDR(to_push);
|
||||
|
||||
@@ -131,18 +173,33 @@ static void gm_rx_hook(const CANPacket_t *to_push) {
|
||||
vehicle_moving = (left_rear_speed > GM_STANDSTILL_THRSLD) || (right_rear_speed > GM_STANDSTILL_THRSLD);
|
||||
}
|
||||
|
||||
// ACC steering wheel buttons (GM_CAM and GM_SDGM are tied to the PCM)
|
||||
if ((addr == 0x1E1) && (!gm_pcm_cruise || gm_cc_long) && (gm_hw != GM_SDGM)) {
|
||||
handle_gm_wheel_buttons(to_push);
|
||||
// ACC steering wheel buttons (GM_CAM is tied to the PCM)
|
||||
if ((addr == 0x1E1) && (!gm_pcm_cruise || gm_cc_long)) {
|
||||
int button = (GET_BYTE(to_push, 5) & 0x70U) >> 4;
|
||||
|
||||
// enter controls on falling edge of set or rising edge of resume (avoids fault)
|
||||
bool set = (button != GM_BTN_SET) && (cruise_button_prev == GM_BTN_SET);
|
||||
bool res = (button == GM_BTN_RESUME) && (cruise_button_prev != GM_BTN_RESUME);
|
||||
if (set || res) {
|
||||
controls_allowed = true;
|
||||
}
|
||||
|
||||
// exit controls on cancel press
|
||||
if (button == GM_BTN_CANCEL) {
|
||||
controls_allowed = false;
|
||||
}
|
||||
|
||||
cruise_button_prev = button;
|
||||
}
|
||||
|
||||
// Reference for brake pressed signals:
|
||||
// https://github.com/commaai/openpilot/blob/master/selfdrive/car/gm/carstate.py
|
||||
if ((addr == 0xBE) && (gm_hw == GM_ASCM)) {
|
||||
// Prefer 0xC9 (ECMEngineStatus) when gm_force_brake_c9 is set, otherwise keep legacy behavior.
|
||||
if ((addr == 0xC9) && gm_force_brake_c9) {
|
||||
brake_pressed = GET_BIT(to_push, 40U) != 0U;
|
||||
} else if ((addr == 0xBE) && ((gm_hw == GM_ASCM) || (gm_hw == GM_SDGM))) {
|
||||
brake_pressed = GET_BYTE(to_push, 1) >= 8U;
|
||||
}
|
||||
|
||||
if ((addr == 0xC9) && ((gm_hw == GM_CAM) || (gm_hw == GM_SDGM))) {
|
||||
} else if ((addr == 0xC9) && (gm_hw == GM_CAM)) {
|
||||
brake_pressed = GET_BIT(to_push, 40U) != 0U;
|
||||
}
|
||||
|
||||
@@ -162,14 +219,42 @@ static void gm_rx_hook(const CANPacket_t *to_push) {
|
||||
}
|
||||
}
|
||||
|
||||
// Cruise check for ACC models with pedal interceptor - block stock ACC
|
||||
if ((addr == 0x1C4) && gm_has_acc && enable_gas_interceptor && gm_bolt_2022_pedal) {
|
||||
cruise_engaged_prev = false;
|
||||
}
|
||||
|
||||
// Cruise check for CC only cars
|
||||
if ((addr == 0x3D1) && !gm_has_acc) {
|
||||
uint32_t now_us = microsecond_timer_get();
|
||||
gm_3d1_last_stock_us = now_us;
|
||||
bool cruise_engaged = (GET_BYTE(to_push, 4) >> 7) != 0U;
|
||||
if (gm_cc_long) {
|
||||
pcm_cruise_check(cruise_engaged);
|
||||
} else {
|
||||
cruise_engaged_prev = cruise_engaged;
|
||||
}
|
||||
|
||||
if (gm_panda_3d1_sched) {
|
||||
if (!gm_3d1_phase_locked) {
|
||||
gm_3d1_phase_locked = true;
|
||||
gm_3d1_expected_stock_us = now_us + GM_3D1_PERIOD_US;
|
||||
} else {
|
||||
int32_t phase_err_us = (int32_t)(now_us - gm_3d1_expected_stock_us);
|
||||
if (phase_err_us < 0) {
|
||||
phase_err_us = -phase_err_us;
|
||||
}
|
||||
|
||||
if ((uint32_t)phase_err_us <= GM_3D1_LOCK_TOLERANCE_US) {
|
||||
gm_3d1_expected_stock_us += GM_3D1_PERIOD_US;
|
||||
} else {
|
||||
gm_3d1_expected_stock_us = now_us + GM_3D1_PERIOD_US;
|
||||
}
|
||||
}
|
||||
|
||||
gm_3d1_next_tx_us = now_us + GM_3D1_TX_OFFSET_US;
|
||||
gm_try_send_3d1_spoof(now_us);
|
||||
}
|
||||
}
|
||||
|
||||
if (addr == 0xBD) {
|
||||
@@ -192,6 +277,19 @@ static void gm_rx_hook(const CANPacket_t *to_push) {
|
||||
}
|
||||
generic_rx_checks(stock_ecu_detected);
|
||||
}
|
||||
|
||||
// Cruise check for ASCMActiveCruiseControlStatus on bus 2.
|
||||
// Keep kaofui behavior for non-Bolt paths; Bolt pedal path keeps local tracking.
|
||||
if ((GET_ADDR(to_push) == 0x370) && (GET_BUS(to_push) == 2U)) {
|
||||
bool cruise_engaged = (GET_BYTE(to_push, 2) >> 7) != 0U; // ACCCmdActive
|
||||
if (gm_bolt_2022_pedal) {
|
||||
cruise_engaged_prev = cruise_engaged;
|
||||
} else if (gm_pcm_cruise && gm_has_acc) {
|
||||
pcm_cruise_check(cruise_engaged);
|
||||
} else {
|
||||
cruise_engaged_prev = cruise_engaged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool gm_tx_hook(const CANPacket_t *to_send) {
|
||||
@@ -214,7 +312,7 @@ static bool gm_tx_hook(const CANPacket_t *to_send) {
|
||||
|
||||
bool steer_req = GET_BIT(to_send, 3U);
|
||||
|
||||
if (steer_torque_cmd_checks(desired_torque, steer_req, GM_STEERING_LIMITS)) {
|
||||
if (steer_torque_cmd_checks(desired_torque, steer_req, *gm_steer_limits)) {
|
||||
tx = false;
|
||||
}
|
||||
}
|
||||
@@ -246,8 +344,11 @@ static bool gm_tx_hook(const CANPacket_t *to_send) {
|
||||
int button = (GET_BYTE(to_send, 5) >> 4) & 0x7U;
|
||||
|
||||
bool allowed_btn = (button == GM_BTN_CANCEL) && cruise_engaged_prev;
|
||||
// For standard CC, allow spamming of SET / RESUME
|
||||
if (gm_cc_long) {
|
||||
if (gm_hw == GM_CAM && enable_gas_interceptor && gm_bolt_2022_pedal && button == GM_BTN_CANCEL) {
|
||||
allowed_btn = true;
|
||||
}
|
||||
// For CC_LONG or PCM cruise vehicles, allow SET/RESUME when cruise is engaged
|
||||
if (gm_cc_long || gm_pcm_cruise) {
|
||||
allowed_btn |= cruise_engaged_prev && (button == GM_BTN_SET || button == GM_BTN_RESUME || button == GM_BTN_UNPRESS);
|
||||
}
|
||||
|
||||
@@ -256,6 +357,28 @@ static bool gm_tx_hook(const CANPacket_t *to_send) {
|
||||
}
|
||||
}
|
||||
|
||||
// Cruise status spoofing only for non-ACC (CC-only) paths
|
||||
if (addr == 0x3D1) {
|
||||
bool allowed_cruise_status = !gm_has_acc;
|
||||
if (!allowed_cruise_status) {
|
||||
tx = false;
|
||||
} else if (gm_panda_3d1_sched) {
|
||||
if (gm_3d1_internal_tx) {
|
||||
tx = true;
|
||||
} else {
|
||||
uint32_t now_us = microsecond_timer_get();
|
||||
(void)memcpy(gm_3d1_spoof_data, to_send->data, 8U);
|
||||
gm_3d1_spoof_valid = true;
|
||||
if (gm_3d1_next_tx_us == 0U) {
|
||||
gm_3d1_next_tx_us = now_us + GM_3D1_TX_OFFSET_US;
|
||||
}
|
||||
bool stock_stale = (gm_3d1_last_stock_us == 0U) || (get_ts_elapsed(now_us, gm_3d1_last_stock_us) > 300000U);
|
||||
bool scheduler_ready = gm_3d1_phase_locked && !stock_stale;
|
||||
tx = !scheduler_ready;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// REGEN PADDLE
|
||||
if (addr == 0xBD) {
|
||||
bool regen_apply = GET_BIT(to_send, 7) || GET_BIT(to_send, 6) || GET_BIT(to_send, 5) || GET_BIT(to_send, 4);
|
||||
@@ -282,16 +405,32 @@ static int gm_fwd_hook(int bus_num, int addr) {
|
||||
if (bus_num == 0) {
|
||||
// block PSCMStatus; forwarded through openpilot to hide an alert from the camera
|
||||
bool is_pscm_msg = (addr == 0x184);
|
||||
if (!is_pscm_msg) {
|
||||
// For non-ACC camera/SDGM paths, keep stock ECMCruiseControl off camera side
|
||||
// so openpilot's spoofed 0x3D1 is the only cruise-status source there.
|
||||
bool is_ecm_cruise_status_msg = (addr == 0x3D1) && !gm_has_acc;
|
||||
if (!is_pscm_msg && !is_ecm_cruise_status_msg) {
|
||||
bus_fwd = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (bus_num == 2) {
|
||||
// block lkas message and acc messages if gm_cam_long, forward all others
|
||||
bool is_lkas_msg = (addr == 0x180);
|
||||
bool is_acc_msg = (addr == 0x315) || (addr == 0x2CB) || (addr == 0x370);
|
||||
bool block_msg = is_lkas_msg || (is_acc_msg && gm_cam_long);
|
||||
bool is_acc_status_msg = (addr == 0x370);
|
||||
bool is_acc_actuation_msg = (addr == 0x315) || (addr == 0x2CB);
|
||||
|
||||
// Block steering if we are controlling LKA
|
||||
bool block_msg = is_lkas_msg;
|
||||
|
||||
// Block Dashboard Status if we are in Native Long OR Pedal Long
|
||||
if (gm_cam_long || gm_pedal_long) {
|
||||
block_msg |= is_acc_status_msg;
|
||||
}
|
||||
|
||||
// Block Native Actuation ONLY if we are in Native Long (not Pedal)
|
||||
if (gm_cam_long) {
|
||||
block_msg |= is_acc_actuation_msg;
|
||||
}
|
||||
|
||||
if (!block_msg) {
|
||||
bus_fwd = 0;
|
||||
}
|
||||
@@ -302,17 +441,19 @@ static int gm_fwd_hook(int bus_num, int addr) {
|
||||
}
|
||||
|
||||
static safety_config gm_init(uint16_t param) {
|
||||
if GET_FLAG(param, GM_PARAM_HW_CAM) {
|
||||
gm_ascm_int = GET_FLAG(param, GM_PARAM_ASCM_INT);
|
||||
if (GET_FLAG(param, GM_PARAM_HW_CAM)) {
|
||||
gm_hw = GM_CAM;
|
||||
} else if GET_FLAG(param, GM_PARAM_HW_SDGM) {
|
||||
} else if (GET_FLAG(param, GM_PARAM_HW_SDGM)) {
|
||||
gm_hw = GM_SDGM;
|
||||
} else {
|
||||
gm_hw = GM_ASCM;
|
||||
}
|
||||
|
||||
gm_force_ascm = GET_FLAG(param, GM_PARAM_HW_ASCM_LONG);
|
||||
gm_steer_limits = GET_FLAG(param, GM_PARAM_BOLT_2017) ? &GM_BOLT_2017_STEERING_LIMITS : &GM_STEERING_LIMITS;
|
||||
|
||||
if (gm_hw == GM_ASCM || gm_force_ascm) {
|
||||
if (gm_hw == GM_ASCM || gm_force_ascm || gm_ascm_int) {
|
||||
gm_long_limits = &GM_ASCM_LONG_LIMITS;
|
||||
} else if ((gm_hw == GM_CAM) || (gm_hw == GM_SDGM)) {
|
||||
gm_long_limits = &GM_CAM_LONG_LIMITS;
|
||||
@@ -321,11 +462,22 @@ static safety_config gm_init(uint16_t param) {
|
||||
|
||||
gm_pedal_long = GET_FLAG(param, GM_PARAM_PEDAL_LONG);
|
||||
gm_cc_long = GET_FLAG(param, GM_PARAM_CC_LONG);
|
||||
enable_gas_interceptor = GET_FLAG(param, GM_PARAM_PEDAL_INTERCEPTOR);
|
||||
gm_cam_long = GET_FLAG(param, GM_PARAM_HW_CAM_LONG) && !gm_cc_long;
|
||||
gm_pcm_cruise = ((gm_hw == GM_CAM) && (!gm_cam_long || gm_cc_long) && !gm_force_ascm && !gm_pedal_long) || (gm_hw == GM_SDGM);
|
||||
gm_bolt_2022_pedal = GET_FLAG(param, GM_PARAM_BOLT_2022_PEDAL);
|
||||
gm_pcm_cruise = (((gm_hw == GM_CAM) || (gm_hw == GM_SDGM)) && (!gm_cam_long || gm_cc_long) && !gm_force_ascm && !gm_pedal_long);
|
||||
gm_skip_relay_check = GET_FLAG(param, GM_PARAM_NO_CAMERA);
|
||||
gm_has_acc = !GET_FLAG(param, GM_PARAM_NO_ACC);
|
||||
enable_gas_interceptor = GET_FLAG(param, GM_PARAM_PEDAL_INTERCEPTOR);
|
||||
gm_force_brake_c9 = GET_FLAG(param, GM_PARAM_FORCE_BRAKE_C9);
|
||||
gm_remote_start_boots_comma = GET_FLAG(param, GM_PARAM_REMOTE_START_BOOTS_COMMA);
|
||||
gm_panda_3d1_sched = GET_FLAG(param, GM_PARAM_PANDA_3D1_SCHED) && gm_pedal_long && !gm_has_acc && !gm_bolt_2022_pedal;
|
||||
|
||||
gm_3d1_spoof_valid = false;
|
||||
gm_3d1_internal_tx = false;
|
||||
gm_3d1_next_tx_us = 0U;
|
||||
gm_3d1_expected_stock_us = 0U;
|
||||
gm_3d1_last_stock_us = 0U;
|
||||
gm_3d1_phase_locked = false;
|
||||
|
||||
safety_config ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_ASCM_TX_MSGS);
|
||||
if (gm_hw == GM_CAM) {
|
||||
@@ -336,8 +488,6 @@ static safety_config gm_init(uint16_t param) {
|
||||
} else {
|
||||
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CAM_TX_MSGS);
|
||||
}
|
||||
} else if (gm_hw == GM_SDGM) {
|
||||
ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_SDGM_TX_MSGS);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
+32
-22
@@ -231,13 +231,19 @@ class Panda:
|
||||
|
||||
FLAG_GM_HW_CAM = 1
|
||||
FLAG_GM_HW_CAM_LONG = 2
|
||||
FLAG_GM_HW_SDGM = 4
|
||||
FLAG_GM_CC_LONG = 8
|
||||
FLAG_GM_HW_ASCM_LONG = 16
|
||||
FLAG_GM_NO_CAMERA = 32
|
||||
FLAG_GM_NO_ACC = 64
|
||||
FLAG_GM_PEDAL_LONG = 128 # TODO: This can be inferred
|
||||
FLAG_GM_GAS_INTERCEPTOR = 256
|
||||
FLAG_GM_CC_LONG = 4
|
||||
FLAG_GM_HW_ASCM_LONG = 8
|
||||
FLAG_GM_NO_CAMERA = 16
|
||||
FLAG_GM_NO_ACC = 32
|
||||
FLAG_GM_PEDAL_LONG = 64 # TODO: This can be inferred
|
||||
FLAG_GM_GAS_INTERCEPTOR = 128
|
||||
FLAG_GM_ASCM_INT = 256
|
||||
FLAG_GM_FORCE_BRAKE_C9 = 512
|
||||
FLAG_GM_HW_SDGM = 1024
|
||||
FLAG_GM_BOLT_2017 = 2048
|
||||
FLAG_GM_BOLT_2022_PEDAL = 4096
|
||||
FLAG_GM_REMOTE_START_BOOTS_COMMA = 8192
|
||||
FLAG_GM_PANDA_3D1_SCHED = 16384
|
||||
|
||||
FLAG_FORD_LONG_CONTROL = 1
|
||||
FLAG_FORD_CANFD = 2
|
||||
@@ -804,7 +810,8 @@ class Panda:
|
||||
# The panda will NAK CAN writes when there is CAN congestion.
|
||||
# libusb will try to send it again, with a max timeout.
|
||||
# Timeout is in ms. If set to 0, the timeout is infinite.
|
||||
CAN_SEND_TIMEOUT_MS = 10
|
||||
CAN_SEND_TIMEOUT_MS = 5
|
||||
CAN_MAX_RETRIES = 3
|
||||
|
||||
def can_reset_communications(self):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xc0, 0, 0, b'')
|
||||
@@ -812,18 +819,18 @@ class Panda:
|
||||
@ensure_can_packet_version
|
||||
def can_send_many(self, arr, timeout=CAN_SEND_TIMEOUT_MS):
|
||||
snds = pack_can_buffer(arr)
|
||||
while True:
|
||||
try:
|
||||
for tx in snds:
|
||||
while True:
|
||||
bs = self._handle.bulkWrite(3, tx, timeout=timeout)
|
||||
tx = tx[bs:]
|
||||
if len(tx) == 0:
|
||||
break
|
||||
logging.error("CAN: PARTIAL SEND MANY, RETRYING")
|
||||
break
|
||||
except (usb1.USBErrorIO, usb1.USBErrorOverflow):
|
||||
logging.error("CAN: BAD SEND MANY, RETRYING")
|
||||
for tx in snds:
|
||||
retries = 0
|
||||
while len(tx) > 0:
|
||||
bs = self._handle.bulkWrite(3, tx, timeout=timeout)
|
||||
if bs == 0:
|
||||
retries += 1
|
||||
if retries > self.CAN_MAX_RETRIES:
|
||||
logging.warning("CAN send: no progress after retries, dropping")
|
||||
break
|
||||
else:
|
||||
retries = 0
|
||||
tx = tx[bs:]
|
||||
|
||||
def can_send(self, addr, dat, bus, timeout=CAN_SEND_TIMEOUT_MS):
|
||||
self.can_send_many([[addr, None, dat, bus]], timeout=timeout)
|
||||
@@ -831,13 +838,16 @@ class Panda:
|
||||
@ensure_can_packet_version
|
||||
def can_recv(self):
|
||||
dat = bytearray()
|
||||
while True:
|
||||
for _ in range(self.CAN_MAX_RETRIES):
|
||||
try:
|
||||
dat = self._handle.bulkRead(1, 16384) # Max receive batch size + 2 extra reserve frames
|
||||
break
|
||||
except (usb1.USBErrorIO, usb1.USBErrorOverflow):
|
||||
logging.error("CAN: BAD RECV, RETRYING")
|
||||
time.sleep(0.1)
|
||||
time.sleep(0.01)
|
||||
else:
|
||||
logging.error("CAN: recv failed after retries")
|
||||
return []
|
||||
msgs, self.can_rx_overflow_buffer = unpack_can_buffer(self.can_rx_overflow_buffer + dat)
|
||||
return msgs
|
||||
|
||||
|
||||
+12
-1
@@ -27,7 +27,10 @@ NACK = 0x1F
|
||||
CHECKSUM_START = 0xAB
|
||||
|
||||
MIN_ACK_TIMEOUT_MS = 100
|
||||
MAX_ACK_TIMEOUT_MS = 500 # like C++ SPI_ACK_TIMEOUT
|
||||
DEFAULT_TIMEOUT_MS = 500 # default when timeout=0
|
||||
MAX_XFER_RETRY_COUNT = 5
|
||||
MAX_TIMEOUT_RETRIES = 5 # like C++
|
||||
|
||||
XFER_SIZE = 0x40*31
|
||||
|
||||
@@ -152,6 +155,8 @@ class PandaSpiHandle(BaseHandle):
|
||||
return cksum
|
||||
|
||||
def _wait_for_ack(self, spi, ack_val: int, timeout: int, tx: int, length: int = 1) -> bytes:
|
||||
# Original behavior preserved - timeout=0 means wait forever within this function
|
||||
# The caller (_transfer) handles the overall timeout
|
||||
timeout_s = max(MIN_ACK_TIMEOUT_MS, timeout) * 1e-3
|
||||
|
||||
start = time.monotonic()
|
||||
@@ -225,10 +230,15 @@ class PandaSpiHandle(BaseHandle):
|
||||
logging.debug("starting transfer: endpoint=%d, max_rx_len=%d", endpoint, max_rx_len)
|
||||
logging.debug("==============================================")
|
||||
|
||||
# Fix timeout=0 infinite loop: default to DEFAULT_TIMEOUT_MS
|
||||
if timeout == 0:
|
||||
timeout = DEFAULT_TIMEOUT_MS
|
||||
|
||||
n = 0
|
||||
start_time = time.monotonic()
|
||||
exc = PandaSpiException()
|
||||
while (timeout == 0) or (time.monotonic() - start_time) < timeout*1e-3:
|
||||
# Use the timeout for the overall loop, matching original behavior but with timeout=0 fixed
|
||||
while (time.monotonic() - start_time) < timeout * 1e-3:
|
||||
n += 1
|
||||
logging.debug("\ntry #%d", n)
|
||||
with self.dev.acquire() as spi:
|
||||
@@ -238,6 +248,7 @@ class PandaSpiHandle(BaseHandle):
|
||||
exc = e
|
||||
logging.debug("SPI transfer failed, retrying", exc_info=True)
|
||||
|
||||
logging.error("SPI transfer failed after %d tries, %.2fms", n, (time.monotonic() - start_time) * 1000)
|
||||
raise exc
|
||||
|
||||
def get_protocol_version(self) -> bytes:
|
||||
|
||||
@@ -386,7 +386,7 @@ class TestGmInterceptorSafety(common.GasInterceptorSafetyTest, TestGmCameraSafet
|
||||
|
||||
|
||||
class TestGmCcLongitudinalSafety(TestGmCameraSafety):
|
||||
TX_MSGS = [[384, 0], [481, 0], [0x1F5, 0], [388, 2]]
|
||||
TX_MSGS = [[384, 0], [481, 0], [0x3D1, 0], [0x1F5, 0], [388, 2]]
|
||||
FWD_BLACKLISTED_ADDRS = {2: [384], 0: [388]} # block LKAS message and PSCMStatus
|
||||
BUTTONS_BUS = 0 # tx only
|
||||
|
||||
|
||||
@@ -11,12 +11,14 @@ from openpilot.selfdrive.car.fingerprints import eliminate_incompatible_cars, al
|
||||
from openpilot.selfdrive.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN
|
||||
from openpilot.selfdrive.car.fw_versions import get_fw_versions_ordered, get_present_ecus, match_fw_to_car, set_obd_multiplexing
|
||||
from openpilot.selfdrive.car.mock.values import CAR as MOCK
|
||||
from openpilot.selfdrive.car.gm.values import CAR as GM_CAR, CanBus as GMCanBus
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
import cereal.messaging as messaging
|
||||
from openpilot.selfdrive.car import gen_empty_fingerprint
|
||||
from openpilot.system.version import get_build_metadata
|
||||
|
||||
FRAME_FINGERPRINT = 100 # 1s
|
||||
SOURCE_BRANCH_FILE = "/data/media/0/starpilot_source_branch"
|
||||
|
||||
EventName = car.CarEvent.EventName
|
||||
FrogPilotEventName = custom.FrogPilotCarEvent.EventName
|
||||
@@ -68,8 +70,9 @@ interface_names = _get_interface_names()
|
||||
interfaces = load_interfaces(interface_names)
|
||||
|
||||
|
||||
def can_fingerprint(next_can: Callable) -> tuple[str | None, dict[int, dict]]:
|
||||
def can_fingerprint(next_can: Callable) -> tuple[str | None, dict[int, dict], dict[int, set[int]]]:
|
||||
finger = gen_empty_fingerprint()
|
||||
nonzero_addrs = {bus: set() for bus in finger}
|
||||
candidate_cars = {i: all_legacy_fingerprint_cars() for i in [0, 1]} # attempt fingerprint on both bus 0 and 1
|
||||
frame = 0
|
||||
car_fingerprint = None
|
||||
@@ -84,7 +87,10 @@ def can_fingerprint(next_can: Callable) -> tuple[str | None, dict[int, dict]]:
|
||||
if can.src < 128:
|
||||
if can.src not in finger:
|
||||
finger[can.src] = {}
|
||||
nonzero_addrs[can.src] = set()
|
||||
finger[can.src][can.address] = len(can.dat)
|
||||
if any(can.dat):
|
||||
nonzero_addrs[can.src].add(can.address)
|
||||
|
||||
for b in candidate_cars:
|
||||
# Ignore extended messages and VIN query response.
|
||||
@@ -105,7 +111,7 @@ def can_fingerprint(next_can: Callable) -> tuple[str | None, dict[int, dict]]:
|
||||
|
||||
frame += 1
|
||||
|
||||
return car_fingerprint, finger
|
||||
return car_fingerprint, finger, nonzero_addrs
|
||||
|
||||
|
||||
# **** for use live only ****
|
||||
@@ -162,7 +168,7 @@ def fingerprint(logcan, sendcan, num_pandas):
|
||||
# CAN fingerprint
|
||||
# drain CAN socket so we get the latest messages
|
||||
messaging.drain_sock_raw(logcan)
|
||||
car_fingerprint, finger = can_fingerprint(lambda: get_one_can(logcan))
|
||||
car_fingerprint, finger, nonzero_addrs = can_fingerprint(lambda: get_one_can(logcan))
|
||||
|
||||
exact_match = True
|
||||
source = car.CarParams.FingerprintSource.can
|
||||
@@ -181,16 +187,74 @@ def fingerprint(logcan, sendcan, num_pandas):
|
||||
fw_count=len(car_fw), ecu_responses=list(ecu_rx_addrs), vin_rx_addr=vin_rx_addr, vin_rx_bus=vin_rx_bus,
|
||||
fingerprints=repr(finger), fw_query_time=fw_query_time, error=True)
|
||||
|
||||
return car_fingerprint, finger, vin, car_fw, source, exact_match
|
||||
return car_fingerprint, finger, nonzero_addrs, vin, car_fw, source, exact_match
|
||||
|
||||
|
||||
def get_car_interface(CP, FPCP):
|
||||
CarInterface, CarController, CarState = interfaces[CP.carFingerprint]
|
||||
return CarInterface(CP, FPCP, CarController, CarState)
|
||||
|
||||
def get_cached_car_fingerprint(params: Params) -> str | None:
|
||||
for key in ("CarParamsPersistent", "CarParamsCache", "CarParams"):
|
||||
cp_bytes = params.get(key)
|
||||
if cp_bytes is None:
|
||||
continue
|
||||
try:
|
||||
with car.CarParams.from_bytes(cp_bytes) as cached_cp:
|
||||
if cached_cp.carFingerprint:
|
||||
return cached_cp.carFingerprint
|
||||
except Exception:
|
||||
continue
|
||||
return None
|
||||
|
||||
def clear_stale_car_params(params: Params, candidate: str) -> None:
|
||||
cached_fingerprint = get_cached_car_fingerprint(params)
|
||||
if cached_fingerprint is None or cached_fingerprint == candidate:
|
||||
return
|
||||
|
||||
stale_keys = (
|
||||
"CarParams",
|
||||
"CarParamsCache",
|
||||
"CarParamsPersistent",
|
||||
"FrogPilotCarParams",
|
||||
"FrogPilotCarParamsPersistent",
|
||||
"CarModelName",
|
||||
)
|
||||
for key in stale_keys:
|
||||
params.remove(key)
|
||||
|
||||
cloudlog.warning("cleared stale car params after fingerprint change: %s -> %s", cached_fingerprint, candidate)
|
||||
|
||||
def migrate_legacy_bolt_candidate(candidate: str) -> str:
|
||||
source_branch = ""
|
||||
try:
|
||||
with open(SOURCE_BRANCH_FILE, encoding="utf-8") as f:
|
||||
source_branch = f.read().strip()
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
migration_branch = source_branch or get_build_metadata().channel
|
||||
replacements = {}
|
||||
if migration_branch in {"TorqueTune", "TorquePedal"}:
|
||||
replacements = {
|
||||
"CHEVROLET_BOLT_EUV": GM_CAR.CHEVROLET_BOLT_ACC_2022_2023,
|
||||
"CHEVROLET_BOLT_CC": GM_CAR.CHEVROLET_BOLT_CC_2022_2023,
|
||||
}
|
||||
elif migration_branch in {"TotallyTune", "StarPilot-2017", "StarPilot 2017"}:
|
||||
replacements = {
|
||||
"CHEVROLET_BOLT_CC": GM_CAR.CHEVROLET_BOLT_CC_2017,
|
||||
}
|
||||
elif migration_branch in {"StarPilot"}:
|
||||
replacements = {
|
||||
"CHEVROLET_BOLT_CC": GM_CAR.CHEVROLET_BOLT_CC_2019_2021,
|
||||
}
|
||||
|
||||
normalized_candidate = candidate[4:] if candidate.startswith("CAR.") else candidate
|
||||
return replacements.get(normalized_candidate, normalized_candidate)
|
||||
|
||||
|
||||
def get_car(logcan, sendcan, experimental_long_allowed, params, num_pandas=1, frogpilot_toggles=None):
|
||||
candidate, fingerprints, vin, car_fw, source, exact_match = fingerprint(logcan, sendcan, num_pandas)
|
||||
candidate, fingerprints, nonzero_addrs, vin, car_fw, source, exact_match = fingerprint(logcan, sendcan, num_pandas)
|
||||
|
||||
if candidate is None or frogpilot_toggles.force_fingerprint:
|
||||
if frogpilot_toggles.car_model is not None:
|
||||
@@ -202,10 +266,80 @@ def get_car(logcan, sendcan, experimental_long_allowed, params, num_pandas=1, fr
|
||||
params.put_nonblocking("CarMake", candidate.split('_')[0].title())
|
||||
params.put_nonblocking("CarModel", candidate)
|
||||
|
||||
# Branch migration can leave legacy Bolt candidate names active in params/cache.
|
||||
# Remap the selected candidate itself so fingerprint selection and params stay in sync.
|
||||
migrated_candidate = migrate_legacy_bolt_candidate(candidate)
|
||||
if candidate != migrated_candidate:
|
||||
cloudlog.warning("legacy Bolt candidate migration: %s -> %s", candidate, migrated_candidate)
|
||||
candidate = migrated_candidate
|
||||
params.put_nonblocking("CarMake", candidate.split('_')[0].title())
|
||||
params.put_nonblocking("CarModel", candidate)
|
||||
params.remove("CarModelName")
|
||||
|
||||
# VIN-based Bolt year mapping (selfdrive-only, bolt variants only)
|
||||
if not frogpilot_toggles.force_fingerprint and is_valid_vin(vin):
|
||||
bolt_variants = {
|
||||
"CHEVROLET_BOLT_EUV",
|
||||
"CHEVROLET_BOLT_CC",
|
||||
"CAR.CHEVROLET_BOLT_EUV",
|
||||
"CAR.CHEVROLET_BOLT_CC",
|
||||
GM_CAR.CHEVROLET_BOLT_ACC_2022_2023,
|
||||
GM_CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
GM_CAR.CHEVROLET_BOLT_CC_2022_2023,
|
||||
GM_CAR.CHEVROLET_BOLT_CC_2019_2021,
|
||||
GM_CAR.CHEVROLET_BOLT_CC_2017,
|
||||
}
|
||||
if candidate in bolt_variants:
|
||||
year_code = vin[9:10]
|
||||
year_map = {
|
||||
"H": GM_CAR.CHEVROLET_BOLT_CC_2017, # 2017
|
||||
"J": GM_CAR.CHEVROLET_BOLT_CC_2019_2021, # 2018
|
||||
"K": GM_CAR.CHEVROLET_BOLT_CC_2019_2021, # 2019
|
||||
"L": GM_CAR.CHEVROLET_BOLT_CC_2019_2021, # 2020
|
||||
"M": GM_CAR.CHEVROLET_BOLT_CC_2019_2021, # 2021
|
||||
"N": GM_CAR.CHEVROLET_BOLT_ACC_2022_2023, # 2022
|
||||
"P": GM_CAR.CHEVROLET_BOLT_ACC_2022_2023, # 2023
|
||||
}
|
||||
if year_code in year_map:
|
||||
vin_candidate = year_map[year_code]
|
||||
if vin_candidate == GM_CAR.CHEVROLET_BOLT_ACC_2022_2023:
|
||||
has_acc_data = (
|
||||
0x370 in nonzero_addrs.get(GMCanBus.CAMERA, set()) or
|
||||
0x370 in nonzero_addrs.get(GMCanBus.POWERTRAIN, set())
|
||||
)
|
||||
has_pedal_msg = 0x201 in fingerprints.get(GMCanBus.POWERTRAIN, {})
|
||||
if has_acc_data:
|
||||
vin_candidate = GM_CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL if has_pedal_msg else GM_CAR.CHEVROLET_BOLT_ACC_2022_2023
|
||||
else:
|
||||
vin_candidate = GM_CAR.CHEVROLET_BOLT_CC_2022_2023
|
||||
if candidate != vin_candidate:
|
||||
prev_candidate = candidate
|
||||
candidate = vin_candidate
|
||||
params.put_nonblocking("CarMake", candidate.split('_')[0].title())
|
||||
params.put_nonblocking("CarModel", candidate)
|
||||
params.remove("CarModelName")
|
||||
cloudlog.warning("VIN Bolt override: %s -> %s", prev_candidate, candidate)
|
||||
|
||||
# Always prefer live fingerprint naming for Bolt variants to avoid stale manual labels.
|
||||
if candidate in {
|
||||
GM_CAR.CHEVROLET_BOLT_ACC_2022_2023,
|
||||
GM_CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
GM_CAR.CHEVROLET_BOLT_CC_2022_2023,
|
||||
GM_CAR.CHEVROLET_BOLT_CC_2019_2021,
|
||||
GM_CAR.CHEVROLET_BOLT_CC_2017,
|
||||
"CHEVROLET_BOLT_EUV",
|
||||
"CHEVROLET_BOLT_CC",
|
||||
"CAR.CHEVROLET_BOLT_EUV",
|
||||
"CAR.CHEVROLET_BOLT_CC",
|
||||
}:
|
||||
params.remove("CarModelName")
|
||||
|
||||
if frogpilot_toggles.block_user:
|
||||
candidate = MOCK.MOCK
|
||||
sentry.capture_block()
|
||||
|
||||
clear_stale_car_params(params, candidate)
|
||||
|
||||
CarInterface, _, _ = interfaces[candidate]
|
||||
CP = CarInterface.get_params(candidate, fingerprints, car_fw, experimental_long_allowed, frogpilot_toggles, docs=False)
|
||||
FPCP = CarInterface.get_frogpilot_params(candidate, fingerprints, car_fw, CP, frogpilot_toggles)
|
||||
|
||||
@@ -158,7 +158,7 @@ MIGRATION = {
|
||||
"CADILLAC ESCALADE 2017": GM.CADILLAC_ESCALADE,
|
||||
"CADILLAC ESCALADE ESV 2016": GM.CADILLAC_ESCALADE_ESV,
|
||||
"CADILLAC ESCALADE ESV 2019": GM.CADILLAC_ESCALADE_ESV_2019,
|
||||
"CHEVROLET BOLT EUV 2022": GM.CHEVROLET_BOLT_EUV,
|
||||
"CHEVROLET BOLT EUV 2022": GM.CHEVROLET_BOLT_ACC_2022_2023,
|
||||
"CHEVROLET SILVERADO 1500 2020": GM.CHEVROLET_SILVERADO,
|
||||
"CHEVROLET EQUINOX 2019": GM.CHEVROLET_EQUINOX,
|
||||
"CHEVROLET TRAILBLAZER 2021": GM.CHEVROLET_TRAILBLAZER,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from typing import Tuple
|
||||
import time
|
||||
import math
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from cereal import car
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
@@ -9,7 +11,7 @@ from openpilot.common.params_pyx import Params
|
||||
from opendbc.can.packer import CANPacker
|
||||
from openpilot.selfdrive.car import apply_driver_steer_torque_limits, create_gas_interceptor_command
|
||||
from openpilot.selfdrive.car.gm import gmcan
|
||||
from openpilot.selfdrive.car.gm.values import DBC, AccState, CanBus, CarControllerParams, CruiseButtons, GMFlags, CC_ONLY_CAR, SDGM_CAR, EV_CAR, CC_REGEN_PADDLE_CAR
|
||||
from openpilot.selfdrive.car.gm.values import CAR, DBC, AccState, CanBus, CarControllerParams, CruiseButtons, GMFlags, CC_ONLY_CAR, SDGM_CAR, ASCM_INT, EV_CAR, CC_REGEN_PADDLE_CAR
|
||||
from openpilot.selfdrive.car.interfaces import CarControllerBase
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import apply_deadzone
|
||||
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
|
||||
@@ -62,15 +64,29 @@ class CarController(CarControllerBase):
|
||||
self.lka_icon_status_last = (False, False)
|
||||
|
||||
self.params = CarControllerParams(self.CP)
|
||||
self.is_volt = self.CP.carFingerprint in (CAR.CHEVROLET_VOLT, CAR.CHEVROLET_VOLT_2019, CAR.CHEVROLET_VOLT_ASCM, CAR.CHEVROLET_VOLT_CAMERA, CAR.CHEVROLET_VOLT_CC)
|
||||
self.pedal_scale = 1.0
|
||||
self.params_ = Params()
|
||||
|
||||
self.mass = CP.mass
|
||||
self.tireRadius = 0.075 * CP.wheelbase + 0.1453
|
||||
self.frontalArea = 1.05 * CP.wheelbase + 0.0679
|
||||
self.coeffDrag = 0.30
|
||||
self.airDensity = 1.225
|
||||
|
||||
|
||||
|
||||
self.malibu_cancel_phase = 0
|
||||
self.malibu_cancel_last_ts = 0.0
|
||||
self.malibu_cancel_frame = 0
|
||||
self.malibu_button_phase = 0
|
||||
|
||||
self.packer_pt = CANPacker(DBC[self.CP.carFingerprint]['pt'])
|
||||
self.packer_obj = CANPacker(DBC[self.CP.carFingerprint]['radar'])
|
||||
self.packer_ch = CANPacker(DBC[self.CP.carFingerprint]['chassis'])
|
||||
|
||||
# FrogPilot variables
|
||||
self.accel_g = 0.0
|
||||
|
||||
self.pitch = FirstOrderFilter(0., 0.09 * 4, DT_CTRL * 4) # runs at 25 Hz
|
||||
self.accel_g = 0.0
|
||||
self.regen_paddle_pressed = False
|
||||
@@ -137,6 +153,22 @@ class CarController(CarControllerBase):
|
||||
actuators = CC.actuators
|
||||
accel = brake_accel = actuators.accel
|
||||
press_regen_paddle = False
|
||||
kaofui_cars = SDGM_CAR | ASCM_INT | {
|
||||
CAR.CHEVROLET_VOLT,
|
||||
CAR.CHEVROLET_VOLT_2019,
|
||||
CAR.CHEVROLET_VOLT_ASCM,
|
||||
CAR.CHEVROLET_VOLT_CAMERA,
|
||||
CAR.CHEVROLET_VOLT_CC,
|
||||
CAR.CHEVROLET_MALIBU_CC,
|
||||
CAR.CHEVROLET_MALIBU_HYBRID_CC,
|
||||
}
|
||||
volt_like = {
|
||||
CAR.CHEVROLET_VOLT,
|
||||
CAR.CHEVROLET_VOLT_2019,
|
||||
CAR.CHEVROLET_VOLT_ASCM,
|
||||
CAR.CHEVROLET_VOLT_CAMERA,
|
||||
CAR.CHEVROLET_VOLT_CC,
|
||||
}
|
||||
|
||||
# Planner-driven regen hold: gate by car support and OP long active, use commanded accel thresholds
|
||||
if (self.CP.enableGasInterceptor and self.CP.carFingerprint in CC_REGEN_PADDLE_CAR
|
||||
@@ -162,6 +194,13 @@ class CarController(CarControllerBase):
|
||||
can_sends = []
|
||||
paddle_sends = []
|
||||
|
||||
if self.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC:
|
||||
phase_map = gmcan.malibu_phase_map_for_acc(CS.cruise_buttons)
|
||||
if phase_map and CS.steering_button_checksum in phase_map:
|
||||
phase = (phase_map[CS.steering_button_checksum] + 1) % 4
|
||||
self.malibu_cancel_phase = phase
|
||||
self.malibu_button_phase = phase
|
||||
|
||||
raw_regen_active = (
|
||||
self.CP.carFingerprint in CC_REGEN_PADDLE_CAR and
|
||||
self.CP.openpilotLongitudinalControl and
|
||||
@@ -214,7 +253,7 @@ class CarController(CarControllerBase):
|
||||
and delta_before_ns >= gap_ns):
|
||||
# Non-blocking 1 ms spacing for paddle frames
|
||||
if now_nanos - self.last_paddle_ts_ns >= PADDLE_NONBLOCK_GAP_NS:
|
||||
paddle_sends.append(gmcan.create_prndl2_command(self.packer_pt, CanBus.POWERTRAIN, True))
|
||||
paddle_sends.append(gmcan.create_prndl2_command(self.packer_pt, CanBus.POWERTRAIN, True, self.CP))
|
||||
paddle_sends.append(gmcan.create_regen_paddle_command(self.packer_pt, CanBus.POWERTRAIN, True))
|
||||
self.last_paddle_ts_ns = now_nanos
|
||||
self.last_spoof_ts_ns = now_nanos
|
||||
@@ -233,7 +272,7 @@ class CarController(CarControllerBase):
|
||||
and delta_before_ns >= gap_ns):
|
||||
# Non-blocking 1 ms spacing for paddle frames
|
||||
if now_nanos - self.last_paddle_ts_ns >= PADDLE_NONBLOCK_GAP_NS:
|
||||
paddle_sends.append(gmcan.create_prndl2_command(self.packer_pt, CanBus.POWERTRAIN, True))
|
||||
paddle_sends.append(gmcan.create_prndl2_command(self.packer_pt, CanBus.POWERTRAIN, True, self.CP))
|
||||
paddle_sends.append(gmcan.create_regen_paddle_command(self.packer_pt, CanBus.POWERTRAIN, True))
|
||||
self.last_paddle_ts_ns = now_nanos
|
||||
self.last_spoof_ts_ns = now_nanos
|
||||
@@ -267,7 +306,7 @@ class CarController(CarControllerBase):
|
||||
if (delta_after_ns >= gap_ns and delta_before_ns >= gap_ns):
|
||||
# Non-blocking 1 ms spacing for paddle frames
|
||||
if now_nanos - self.last_paddle_ts_ns >= PADDLE_NONBLOCK_GAP_NS:
|
||||
paddle_sends.append(gmcan.create_prndl2_command(self.packer_pt, CanBus.POWERTRAIN, False))
|
||||
paddle_sends.append(gmcan.create_prndl2_command(self.packer_pt, CanBus.POWERTRAIN, False, self.CP))
|
||||
paddle_sends.append(gmcan.create_regen_paddle_command(self.packer_pt, CanBus.POWERTRAIN, False))
|
||||
self.last_paddle_ts_ns = now_nanos
|
||||
self.off_sent[i] = True
|
||||
@@ -329,19 +368,25 @@ class CarController(CarControllerBase):
|
||||
if now_nanos - self.last_steer_ts_ns >= flush_gap_ns:
|
||||
can_sends.extend(paddle_sends)
|
||||
|
||||
spoof_ecm_cruise_cars = {
|
||||
CAR.CHEVROLET_BOLT_CC_2017,
|
||||
CAR.CHEVROLET_BOLT_CC_2019_2021,
|
||||
CAR.CHEVROLET_BOLT_CC_2022_2023,
|
||||
CAR.CHEVROLET_MALIBU_HYBRID_CC,
|
||||
}
|
||||
non_acc_pedal_long = (self.CP.flags & GMFlags.PEDAL_LONG.value) and self.CP.carFingerprint in spoof_ecm_cruise_cars and self.CP.enableGasInterceptor
|
||||
if non_acc_pedal_long and self.frame % 4 == 0:
|
||||
spoof_enabled = True
|
||||
spoof_set_speed_kph = hud_v_cruise * CV.MS_TO_KPH
|
||||
can_sends.append(gmcan.create_ecm_cruise_control_command(
|
||||
self.packer_pt, CanBus.POWERTRAIN, spoof_enabled, spoof_set_speed_kph))
|
||||
|
||||
if self.CP.openpilotLongitudinalControl:
|
||||
|
||||
# Gas/regen, brakes, and UI commands - all at 25Hz
|
||||
if self.frame % 4 == 0:
|
||||
stopping = actuators.longControlState == LongCtrlState.stopping
|
||||
|
||||
# Pitch compensated acceleration;
|
||||
# TODO: include future pitch (sm['modelDataV2'].orientation.y) to account for long actuator delay
|
||||
if frogpilot_toggles.long_pitch and len(CC.orientationNED) > 1:
|
||||
self.pitch.update(CC.orientationNED[1])
|
||||
self.accel_g = ACCELERATION_DUE_TO_GRAVITY * apply_deadzone(self.pitch.x, PITCH_DEADZONE) # driving uphill is positive pitch
|
||||
accel += self.accel_g
|
||||
brake_accel = actuators.accel + self.accel_g * interp(CS.out.vEgo, BRAKE_PITCH_FACTOR_BP, BRAKE_PITCH_FACTOR_V)
|
||||
|
||||
at_full_stop = CC.longActive and CS.out.standstill
|
||||
near_stop = CC.longActive and (CS.out.vEgo < self.params.NEAR_STOP_BRAKE_PHASE)
|
||||
interceptor_gas_cmd = 0
|
||||
@@ -353,9 +398,54 @@ class CarController(CarControllerBase):
|
||||
self.apply_gas = self.params.INACTIVE_REGEN
|
||||
self.apply_brake = int(min(-100 * frogpilot_toggles.stopAccel, self.params.MAX_BRAKE))
|
||||
else:
|
||||
# Normal operation
|
||||
self.apply_gas = int(round(interp(accel, self.params.GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V)))
|
||||
self.apply_brake = int(round(interp(brake_accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
|
||||
if self.is_volt:
|
||||
if len(CC.orientationNED) == 3 and CS.out.vEgo > self.CP.vEgoStopping:
|
||||
volt_pitch_accel = math.sin(CC.orientationNED[1]) * ACCELERATION_DUE_TO_GRAVITY
|
||||
else:
|
||||
volt_pitch_accel = 0.0
|
||||
|
||||
aero_drag_accel = (0.5 * self.coeffDrag * self.frontalArea * self.airDensity * CS.out.vEgo ** 2) / self.mass
|
||||
accel += aero_drag_accel + volt_pitch_accel
|
||||
brake_accel = actuators.accel + aero_drag_accel + volt_pitch_accel * interp(CS.out.vEgo, BRAKE_PITCH_FACTOR_BP, BRAKE_PITCH_FACTOR_V)
|
||||
accel = clip(accel, self.params.ACCEL_MIN, self.params.ACCEL_MAX)
|
||||
brake_accel = clip(brake_accel, self.params.ACCEL_MIN, self.params.ACCEL_MAX)
|
||||
|
||||
if self.CP.carFingerprint in EV_CAR:
|
||||
self.params.update_ev_gas_brake_threshold(CS.out.vEgo)
|
||||
self.apply_gas = int(round(interp(accel, self.params.EV_GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V)))
|
||||
self.apply_brake = int(round(interp(brake_accel, self.params.EV_BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
|
||||
else:
|
||||
self.apply_gas = int(round(interp(accel, self.params.GAS_LOOKUP_BP, self.params.GAS_LOOKUP_V)))
|
||||
self.apply_brake = int(round(interp(brake_accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
|
||||
|
||||
# Clamp within message-valid ranges to avoid ASCM faults from overshoot or rounding
|
||||
self.apply_gas = int(round(clip(self.apply_gas, self.params.MAX_ACC_REGEN, self.params.MAX_GAS)))
|
||||
self.apply_brake = int(round(clip(self.apply_brake, 0, self.params.MAX_BRAKE)))
|
||||
|
||||
if self.apply_brake > 0:
|
||||
# Volt should never present positive torque alongside friction braking
|
||||
self.apply_gas = self.params.INACTIVE_REGEN
|
||||
else:
|
||||
if len(CC.orientationNED) == 3 and CS.out.vEgo > self.CP.vEgoStopping:
|
||||
accel_due_to_pitch = math.sin(CC.orientationNED[1]) * ACCELERATION_DUE_TO_GRAVITY
|
||||
else:
|
||||
accel_due_to_pitch = 0.0
|
||||
|
||||
gas_max = self.params.MAX_GAS
|
||||
accel_max = self.params.ACCEL_MAX
|
||||
|
||||
accel = clip(actuators.accel + accel_due_to_pitch, self.params.ACCEL_MIN, accel_max)
|
||||
torque = self.tireRadius * ((self.mass*accel) + (0.5*self.coeffDrag*self.frontalArea*self.airDensity*CS.out.vEgo**2))
|
||||
|
||||
scaled_torque = torque + self.params.ZERO_GAS
|
||||
apply_gas_torque = clip(scaled_torque, self.params.MAX_ACC_REGEN, gas_max)
|
||||
BRAKE_SWITCH = int(round(interp(CS.out.vEgo, self.params.BRAKE_SWITCH_LOOKUP_BP, self.params.BRAKE_SWITCH_LOOKUP_V)))
|
||||
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(interp(brake_accel, self.params.BRAKE_LOOKUP_BP, self.params.BRAKE_LOOKUP_V)))
|
||||
if self.apply_brake > 0:
|
||||
self.apply_gas = self.params.INACTIVE_REGEN
|
||||
|
||||
# Don't allow any gas above inactive regen while stopping
|
||||
# FIXME: brakes aren't applied immediately when enabling at a stop
|
||||
if stopping:
|
||||
@@ -373,20 +463,29 @@ class CarController(CarControllerBase):
|
||||
idx = (self.frame // 4) % 4
|
||||
|
||||
if self.CP.flags & GMFlags.CC_LONG.value:
|
||||
if CC.longActive and CS.out.vEgo > self.CP.minEnableSpeed:
|
||||
if CC.longActive and CS.out.cruiseState.enabled and CS.out.vEgo > self.CP.minEnableSpeed:
|
||||
# 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, frogpilot_toggles))
|
||||
elif 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:
|
||||
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.DECEL_SET))
|
||||
elif (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.enableGasInterceptor:
|
||||
can_sends.append(create_gas_interceptor_command(self.packer_pt, interceptor_gas_cmd, idx))
|
||||
if self.CP.carFingerprint not in CC_ONLY_CAR:
|
||||
friction_brake_bus = CanBus.CHASSIS
|
||||
# GM Camera exceptions
|
||||
# TODO: can we always check the longControlState?
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera and self.CP.carFingerprint not in CC_ONLY_CAR:
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
at_full_stop = at_full_stop and stopping
|
||||
friction_brake_bus = CanBus.POWERTRAIN
|
||||
if self.CP.carFingerprint in SDGM_CAR:
|
||||
friction_brake_bus = CanBus.CAMERA
|
||||
|
||||
if self.CP.autoResumeSng:
|
||||
resume = actuators.longControlState != LongCtrlState.starting or CC.cruiseControl.resume
|
||||
@@ -398,14 +497,16 @@ class CarController(CarControllerBase):
|
||||
acc_engaged = CC.enabled
|
||||
|
||||
# GasRegenCmdActive needs to be 1 to avoid cruise faults. It describes the ACC state, not actuation
|
||||
can_sends.append(gmcan.create_gas_regen_command(self.packer_pt, CanBus.POWERTRAIN, self.apply_gas, idx, acc_engaged, at_full_stop))
|
||||
can_sends.append(gmcan.create_gas_regen_command(self.packer_pt, CanBus.POWERTRAIN, self.apply_gas, idx, acc_engaged, at_full_stop,
|
||||
include_always_one3=self.CP.carFingerprint in kaofui_cars))
|
||||
can_sends.append(gmcan.create_friction_brake_command(self.packer_ch, friction_brake_bus, self.apply_brake,
|
||||
idx, CC.enabled, near_stop, at_full_stop, self.CP))
|
||||
|
||||
# Send dashboard UI commands (ACC status)
|
||||
is_bolt_acc_pedal = self.CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL
|
||||
if self.CP.carFingerprint not in CC_ONLY_CAR or is_bolt_acc_pedal:
|
||||
send_fcw = hud_alert == VisualAlert.fcw
|
||||
can_sends.append(gmcan.create_acc_dashboard_command(self.packer_pt, CanBus.POWERTRAIN, CC.enabled,
|
||||
hud_v_cruise * CV.MS_TO_KPH, hud_control, send_fcw))
|
||||
can_sends.append(gmcan.create_acc_dashboard_command(
|
||||
self.packer_pt, CanBus.POWERTRAIN, CC.enabled, hud_v_cruise * CV.MS_TO_KPH, hud_control, send_fcw))
|
||||
else:
|
||||
# to keep accel steady for logs when not sending gas
|
||||
accel += self.accel_g
|
||||
@@ -413,26 +514,59 @@ class CarController(CarControllerBase):
|
||||
# Radar needs to know current speed and yaw rate (50hz),
|
||||
# and that ADAS is alive (5hz, previously 10hz)
|
||||
if not self.CP.radarUnavailable:
|
||||
tt = self.frame * DT_CTRL
|
||||
time_and_headlights_step = 20
|
||||
if self.frame % time_and_headlights_step == 0:
|
||||
idx = (self.frame // time_and_headlights_step) % 4
|
||||
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
|
||||
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
|
||||
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
|
||||
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
|
||||
send_adas = True
|
||||
if self.CP.carFingerprint in kaofui_cars:
|
||||
if self.CP.carFingerprint in ASCM_INT:
|
||||
send_adas = True
|
||||
else:
|
||||
send_adas = (self.CP.networkLocation != NetworkLocation.fwdCamera) and (self.CP.carFingerprint not in SDGM_CAR)
|
||||
|
||||
if self.CP.networkLocation == NetworkLocation.gateway and self.frame % (self.params.ADAS_KEEPALIVE_STEP * 2) == 0:
|
||||
if send_adas:
|
||||
tt = self.frame * DT_CTRL
|
||||
if self.CP.carFingerprint in kaofui_cars:
|
||||
time_and_headlights_step = 10
|
||||
speed_and_accelerometer_step = 2
|
||||
if self.frame % time_and_headlights_step == 0:
|
||||
idx = (self.frame // time_and_headlights_step) % 4
|
||||
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
|
||||
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
|
||||
if self.frame % speed_and_accelerometer_step == 0:
|
||||
idx = (self.frame // speed_and_accelerometer_step) % 4
|
||||
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
|
||||
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
|
||||
else:
|
||||
time_and_headlights_step = 20
|
||||
if self.frame % time_and_headlights_step == 0:
|
||||
idx = (self.frame // time_and_headlights_step) % 4
|
||||
can_sends.append(gmcan.create_adas_time_status(CanBus.OBSTACLE, int((tt - self.start_time) * 60), idx))
|
||||
can_sends.append(gmcan.create_adas_headlights_status(self.packer_obj, CanBus.OBSTACLE))
|
||||
can_sends.append(gmcan.create_adas_steering_status(CanBus.OBSTACLE, idx))
|
||||
can_sends.append(gmcan.create_adas_accelerometer_speed_status(CanBus.OBSTACLE, CS.out.vEgo, idx))
|
||||
|
||||
if self.CP.networkLocation == NetworkLocation.gateway and (self.frame % (self.params.ADAS_KEEPALIVE_STEP if self.CP.carFingerprint in kaofui_cars else self.params.ADAS_KEEPALIVE_STEP * 2)) == 0:
|
||||
can_sends += gmcan.create_adas_keepalive(CanBus.POWERTRAIN)
|
||||
|
||||
# TODO: integrate this with the code block below?
|
||||
stock_cc_active = CS.out.cruiseState.enabled or CS.pcm_acc_status != AccState.OFF
|
||||
if self.CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL:
|
||||
# Match TorquePedal behavior for ACC+pedal path: gate cancel on camera ACC active state.
|
||||
stock_cc_active = CS.out.cruiseState.enabled
|
||||
if (
|
||||
(self.CP.flags & GMFlags.PEDAL_LONG.value) # Always cancel stock CC when using pedal interceptor
|
||||
or (self.CP.flags & GMFlags.CC_LONG.value and not CC.enabled) # Cancel stock CC if OP is not active
|
||||
) and CS.out.cruiseState.enabled:
|
||||
if (self.frame - self.last_button_frame) * DT_CTRL > 0.04:
|
||||
self.last_button_frame = self.frame
|
||||
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, (CS.buttons_counter + 1) % 4, CruiseButtons.CANCEL))
|
||||
) and stock_cc_active:
|
||||
if self.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC:
|
||||
# Match 33 Hz cadence (every 3 frames) and align phase to the last seen checksum.
|
||||
if self.malibu_cancel_frame % 3 == 0:
|
||||
can_sends.append(gmcan.create_buttons_malibu_cancel(
|
||||
CanBus.POWERTRAIN, self.malibu_cancel_phase, CS.steering_button_prefix))
|
||||
self.malibu_cancel_phase = (self.malibu_cancel_phase + 1) % 4
|
||||
self.malibu_cancel_frame += 1
|
||||
else:
|
||||
if (self.frame - self.last_button_frame) * DT_CTRL > 0.04:
|
||||
self.last_button_frame = self.frame
|
||||
cancel_bus = CanBus.CAMERA if self.CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL else CanBus.POWERTRAIN
|
||||
can_sends.append(gmcan.create_buttons(self.packer_pt, cancel_bus, (CS.buttons_counter + 1) % 4, CruiseButtons.CANCEL))
|
||||
|
||||
else:
|
||||
# While car is braking, cancel button causes ECM to enter a soft disable state with a fault status.
|
||||
@@ -443,14 +577,20 @@ class CarController(CarControllerBase):
|
||||
if (self.frame - self.last_button_frame) * DT_CTRL > 0.04:
|
||||
if self.cancel_counter > CAMERA_CANCEL_DELAY_FRAMES:
|
||||
self.last_button_frame = self.frame
|
||||
if self.CP.carFingerprint in SDGM_CAR:
|
||||
if self.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC:
|
||||
if self.malibu_cancel_frame % 3 == 0:
|
||||
can_sends.append(gmcan.create_buttons_malibu_cancel(
|
||||
CanBus.POWERTRAIN, self.malibu_cancel_phase, CS.steering_button_prefix))
|
||||
self.malibu_cancel_phase = (self.malibu_cancel_phase + 1) % 4
|
||||
self.malibu_cancel_frame += 1
|
||||
elif self.CP.carFingerprint in SDGM_CAR and self.CP.carFingerprint not in (volt_like | {CAR.CHEVROLET_BLAZER, CAR.CHEVROLET_MALIBU_SDGM, CAR.CHEVROLET_TRAVERSE}):
|
||||
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.POWERTRAIN, CS.buttons_counter, CruiseButtons.CANCEL))
|
||||
else:
|
||||
can_sends.append(gmcan.create_buttons(self.packer_pt, CanBus.CAMERA, CS.buttons_counter, CruiseButtons.CANCEL))
|
||||
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
# Silence "Take Steering" alert sent by camera, forward PSCMStatus with HandsOffSWlDetectionStatus=1
|
||||
if self.frame % 20 == 0:
|
||||
if self.frame % 10 == 0:
|
||||
can_sends.append(gmcan.create_pscm_status(self.packer_pt, CanBus.CAMERA, CS.pscm_status))
|
||||
|
||||
new_actuators = actuators.as_builder()
|
||||
|
||||
@@ -5,7 +5,7 @@ from openpilot.common.numpy_fast import mean
|
||||
from opendbc.can.can_define import CANDefine
|
||||
from opendbc.can.parser import CANParser
|
||||
from openpilot.selfdrive.car.interfaces import CarStateBase
|
||||
from openpilot.selfdrive.car.gm.values import DBC, AccState, CanBus, STEER_THRESHOLD, GMFlags, CC_ONLY_CAR, CAMERA_ACC_CAR, SDGM_CAR, CC_REGEN_PADDLE_CAR
|
||||
from openpilot.selfdrive.car.gm.values import DBC, AccState, CanBus, STEER_THRESHOLD, GMFlags, CC_ONLY_CAR, CAMERA_ACC_CAR, SDGM_CAR, CC_REGEN_PADDLE_CAR, ASCM_INT, CAR
|
||||
|
||||
TransmissionType = car.CarParams.TransmissionType
|
||||
NetworkLocation = car.CarParams.NetworkLocation
|
||||
@@ -26,23 +26,39 @@ class CarState(CarStateBase):
|
||||
self.pt_lka_steering_cmd_counter = 0
|
||||
self.cam_lka_steering_cmd_counter = 0
|
||||
self.buttons_counter = 0
|
||||
self.steering_button_checksum = 0
|
||||
self.steering_button_prefix = 0x01
|
||||
|
||||
self.prev_distance_button = 0
|
||||
self.distance_button = 0
|
||||
|
||||
self.single_pedal_mode = False
|
||||
self.pedal_steady = 0.
|
||||
self.ecm_cruise_control_ts_nanos = 0
|
||||
self.accelerator_pedal2_ts_nanos = 0
|
||||
|
||||
def update(self, pt_cp, cam_cp, loopback_cp, frogpilot_toggles):
|
||||
ret = car.CarState.new_message()
|
||||
fp_ret = custom.FrogPilotCarState.new_message()
|
||||
volt_like = {CAR.CHEVROLET_VOLT, CAR.CHEVROLET_VOLT_2019, CAR.CHEVROLET_VOLT_ASCM, CAR.CHEVROLET_VOLT_CAMERA, CAR.CHEVROLET_VOLT_CC}
|
||||
kaofui_state_cars = volt_like | SDGM_CAR | ASCM_INT | {
|
||||
CAR.CHEVROLET_BLAZER,
|
||||
CAR.CHEVROLET_MALIBU_SDGM,
|
||||
CAR.CHEVROLET_MALIBU_HYBRID_CC,
|
||||
}
|
||||
sdgm_non_volt = self.CP.carFingerprint in SDGM_CAR and \
|
||||
self.CP.carFingerprint not in kaofui_state_cars
|
||||
|
||||
self.prev_cruise_buttons = self.cruise_buttons
|
||||
self.prev_distance_button = self.distance_button
|
||||
if self.CP.carFingerprint not in SDGM_CAR:
|
||||
if not sdgm_non_volt:
|
||||
self.cruise_buttons = pt_cp.vl["ASCMSteeringButton"]["ACCButtons"]
|
||||
self.distance_button = pt_cp.vl["ASCMSteeringButton"]["DistanceButton"]
|
||||
self.buttons_counter = pt_cp.vl["ASCMSteeringButton"]["RollingCounter"]
|
||||
self.steering_button_checksum = pt_cp.vl["ASCMSteeringButton"]["SteeringButtonChecksum"]
|
||||
acc_always_one = pt_cp.vl["ASCMSteeringButton"]["ACCAlwaysOne"]
|
||||
acc_hidden_bit = pt_cp.vl["ASCMSteeringButton"].get("ACCHiddenBit", 0)
|
||||
self.steering_button_prefix = (int(acc_always_one) & 1) | ((int(acc_hidden_bit) & 1) << 6)
|
||||
else:
|
||||
self.cruise_buttons = cam_cp.vl["ASCMSteeringButton"]["ACCButtons"]
|
||||
self.distance_button = cam_cp.vl["ASCMSteeringButton"]["DistanceButton"]
|
||||
@@ -78,25 +94,43 @@ class CarState(CarStateBase):
|
||||
# sample rear wheel speeds, standstill=True if ECM allows engagement with brake
|
||||
ret.standstill = ret.wheelSpeeds.rl <= STANDSTILL_THRESHOLD and ret.wheelSpeeds.rr <= STANDSTILL_THRESHOLD
|
||||
|
||||
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(pt_cp.vl["ECMPRDNL2"]["PRNDL2"], None))
|
||||
|
||||
if self.CP.flags & GMFlags.NO_ACCELERATOR_POS_MSG.value:
|
||||
ret.brake = pt_cp.vl["EBCMBrakePedalPosition"]["BrakePedalPosition"] / 0xd0
|
||||
if pt_cp.vl["ECMPRDNL2"]["ManualMode"] == 1:
|
||||
ret.gearShifter = self.parse_gear_shifter("T")
|
||||
else:
|
||||
ret.brake = pt_cp.vl["ECMAcceleratorPos"]["BrakePedalPos"]
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera:
|
||||
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(pt_cp.vl["ECMPRDNL2"]["PRNDL2"], None))
|
||||
|
||||
no_accel_pos = bool(self.CP.flags & GMFlags.NO_ACCELERATOR_POS_MSG.value)
|
||||
|
||||
if no_accel_pos:
|
||||
if self.CP.carFingerprint in kaofui_state_cars:
|
||||
ret.brake = pt_cp.vl.get("EBCMBrakePedalPosition", {}).get("BrakePedalPosition", 0) / 0xd0
|
||||
else:
|
||||
ret.brake = pt_cp.vl["EBCMBrakePedalPosition"]["BrakePedalPosition"] / 0xd0
|
||||
else:
|
||||
if self.CP.carFingerprint in kaofui_state_cars:
|
||||
ret.brake = pt_cp.vl.get("ECMAcceleratorPos", {}).get("BrakePedalPos", 0)
|
||||
else:
|
||||
ret.brake = pt_cp.vl["ECMAcceleratorPos"]["BrakePedalPos"]
|
||||
|
||||
if self.CP.carFingerprint in {CAR.CHEVROLET_MALIBU_CC} or (self.CP.carFingerprint == CAR.CHEVROLET_BLAZER and not no_accel_pos):
|
||||
ret.brakePressed = ret.brake >= 8
|
||||
elif (self.CP.flags & GMFlags.FORCE_BRAKE_C9.value) or ((self.CP.networkLocation == NetworkLocation.fwdCamera) and (self.CP.carFingerprint != CAR.CHEVROLET_BLAZER)):
|
||||
ret.brakePressed = pt_cp.vl["ECMEngineStatus"]["BrakePressed"] != 0
|
||||
else:
|
||||
# Some Volt 2016-17 have loose brake pedal push rod retainers which causes the ECM to believe
|
||||
# that the brake is being intermittently pressed without user interaction.
|
||||
# To avoid a cruise fault we need to use a conservative brake position threshold
|
||||
# https://static.nhtsa.gov/odi/tsbs/2017/MC-10137629-9999.pdf
|
||||
ret.brakePressed = ret.brake >= 8
|
||||
analog_thresh = 0.10 if no_accel_pos else 8
|
||||
ret.brakePressed = ret.brake >= analog_thresh
|
||||
|
||||
# Regen braking is braking
|
||||
if self.CP.transmissionType == TransmissionType.direct:
|
||||
ret.regenBraking = pt_cp.vl["EBCMRegenPaddle"]["RegenPaddle"] != 0
|
||||
self.single_pedal_mode = ret.gearShifter == GearShifter.low or pt_cp.vl["EVDriveMode"]["SinglePedalModeActive"] == 1 or (ret.regenBraking and GearShifter.manumatic)
|
||||
self.single_pedal_mode = (ret.gearShifter == GearShifter.low or
|
||||
pt_cp.vl["EVDriveMode"]["SinglePedalModeActive"] == 1 or
|
||||
(ret.regenBraking and GearShifter.manumatic) or
|
||||
(self.CP.carFingerprint in (CAR.CHEVROLET_BOLT_ACC_2022_2023, CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL, CAR.CHEVROLET_BOLT_CC_2022_2023) and self.CP.enableGasInterceptor))
|
||||
|
||||
if self.CP.enableGasInterceptor:
|
||||
ret.gas = (pt_cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS"] + pt_cp.vl["GAS_SENSOR"]["INTERCEPTOR_GAS2"]) / 2.
|
||||
@@ -117,7 +151,7 @@ class CarState(CarStateBase):
|
||||
ret.steerFaultTemporary = self.lkas_status == 2
|
||||
ret.steerFaultPermanent = self.lkas_status == 3
|
||||
|
||||
if self.CP.carFingerprint not in SDGM_CAR:
|
||||
if not sdgm_non_volt:
|
||||
# 1 - open, 0 - closed
|
||||
ret.doorOpen = (pt_cp.vl["BCMDoorBeltStatus"]["FrontLeftDoor"] == 1 or
|
||||
pt_cp.vl["BCMDoorBeltStatus"]["FrontRightDoor"] == 1 or
|
||||
@@ -153,29 +187,44 @@ class CarState(CarStateBase):
|
||||
if self.CP.networkLocation == NetworkLocation.fwdCamera and not self.CP.flags & GMFlags.NO_CAMERA.value:
|
||||
if self.CP.carFingerprint not in CC_ONLY_CAR:
|
||||
ret.cruiseState.speed = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCSpeedSetpoint"] * CV.KPH_TO_MS
|
||||
if self.CP.carFingerprint not in SDGM_CAR:
|
||||
if self.CP.carFingerprint not in (SDGM_CAR | ASCM_INT):
|
||||
ret.stockAeb = cam_cp.vl["AEBCmd"]["AEBCmdActive"] != 0
|
||||
else:
|
||||
ret.stockAeb = False
|
||||
# openpilot controls nonAdaptive when not pcmCruise
|
||||
if self.CP.pcmCruise:
|
||||
if self.CP.pcmCruise and self.CP.carFingerprint not in ASCM_INT:
|
||||
ret.cruiseState.nonAdaptive = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCruiseState"] not in (2, 3)
|
||||
if self.CP.carFingerprint in CC_ONLY_CAR:
|
||||
self.ecm_cruise_control_ts_nanos = pt_cp.ts_nanos["ECMCruiseControl"]["CruiseActive"]
|
||||
self.accelerator_pedal2_ts_nanos = pt_cp.ts_nanos["AcceleratorPedal2"]["CruiseState"]
|
||||
ret.accFaulted = False
|
||||
ret.cruiseState.speed = pt_cp.vl["ECMCruiseControl"]["CruiseSetSpeed"] * CV.KPH_TO_MS
|
||||
ret.cruiseState.enabled = pt_cp.vl["ECMCruiseControl"]["CruiseActive"] != 0
|
||||
if self.CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL:
|
||||
try:
|
||||
ret.cruiseState.enabled = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCmdActive"] != 0
|
||||
except:
|
||||
ret.cruiseState.enabled = pt_cp.vl["ECMCruiseControl"]["CruiseActive"] != 0
|
||||
else:
|
||||
# Most CC paths use ECM first.
|
||||
try:
|
||||
ret.cruiseState.enabled = pt_cp.vl["ECMCruiseControl"]["CruiseActive"] != 0
|
||||
except:
|
||||
ret.cruiseState.enabled = cam_cp.vl["ASCMActiveCruiseControlStatus"]["ACCCmdActive"] != 0
|
||||
else:
|
||||
self.ecm_cruise_control_ts_nanos = 0
|
||||
self.accelerator_pedal2_ts_nanos = 0
|
||||
|
||||
if self.CP.enableBsm:
|
||||
if self.CP.carFingerprint not in SDGM_CAR:
|
||||
if not sdgm_non_volt:
|
||||
ret.leftBlindspot = pt_cp.vl["BCMBlindSpotMonitor"]["LeftBSM"] == 1
|
||||
ret.rightBlindspot = pt_cp.vl["BCMBlindSpotMonitor"]["RightBSM"] == 1
|
||||
else:
|
||||
ret.leftBlindspot = cam_cp.vl["BCMBlindSpotMonitor"]["LeftBSM"] == 1
|
||||
ret.rightBlindspot = cam_cp.vl["BCMBlindSpotMonitor"]["RightBSM"] == 1
|
||||
|
||||
|
||||
# FrogPilot CarState functions
|
||||
self.lkas_previously_enabled = self.lkas_enabled
|
||||
if self.CP.carFingerprint in SDGM_CAR:
|
||||
if sdgm_non_volt:
|
||||
self.lkas_enabled = cam_cp.vl["ASCMSteeringButton"]["LKAButton"]
|
||||
else:
|
||||
self.lkas_enabled = pt_cp.vl["ASCMSteeringButton"]["LKAButton"]
|
||||
@@ -190,10 +239,18 @@ class CarState(CarStateBase):
|
||||
def get_cam_can_parser(CP, FPCP):
|
||||
messages = []
|
||||
if CP.networkLocation == NetworkLocation.fwdCamera and not CP.flags & GMFlags.NO_CAMERA.value:
|
||||
volt_like = {CAR.CHEVROLET_VOLT, CAR.CHEVROLET_VOLT_2019, CAR.CHEVROLET_VOLT_ASCM, CAR.CHEVROLET_VOLT_CAMERA, CAR.CHEVROLET_VOLT_CC}
|
||||
kaofui_state_cars = volt_like | SDGM_CAR | ASCM_INT | {
|
||||
CAR.CHEVROLET_BLAZER,
|
||||
CAR.CHEVROLET_MALIBU_SDGM,
|
||||
CAR.CHEVROLET_MALIBU_HYBRID_CC,
|
||||
}
|
||||
sdgm_non_volt = CP.carFingerprint in SDGM_CAR and \
|
||||
CP.carFingerprint not in kaofui_state_cars
|
||||
messages += [
|
||||
("ASCMLKASteeringCmd", 10),
|
||||
]
|
||||
if CP.carFingerprint in SDGM_CAR:
|
||||
if sdgm_non_volt:
|
||||
messages += [
|
||||
("BCMTurnSignals", 1),
|
||||
("BCMDoorBeltStatus", 10),
|
||||
@@ -202,11 +259,11 @@ class CarState(CarStateBase):
|
||||
]
|
||||
if CP.enableBsm:
|
||||
messages.append(("BCMBlindSpotMonitor", 10))
|
||||
else:
|
||||
elif CP.carFingerprint not in (SDGM_CAR | ASCM_INT):
|
||||
messages += [
|
||||
("AEBCmd", 10),
|
||||
]
|
||||
if CP.carFingerprint not in CC_ONLY_CAR:
|
||||
if CP.carFingerprint not in CC_ONLY_CAR or CP.carFingerprint == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL:
|
||||
messages += [
|
||||
("ASCMActiveCruiseControlStatus", 25),
|
||||
]
|
||||
@@ -226,15 +283,24 @@ class CarState(CarStateBase):
|
||||
("SportMode", 0),
|
||||
]
|
||||
|
||||
if CP.carFingerprint in SDGM_CAR:
|
||||
volt_like = {CAR.CHEVROLET_VOLT, CAR.CHEVROLET_VOLT_2019, CAR.CHEVROLET_VOLT_ASCM, CAR.CHEVROLET_VOLT_CAMERA, CAR.CHEVROLET_VOLT_CC}
|
||||
kaofui_state_cars = volt_like | SDGM_CAR | ASCM_INT | {
|
||||
CAR.CHEVROLET_BLAZER,
|
||||
CAR.CHEVROLET_MALIBU_SDGM,
|
||||
CAR.CHEVROLET_MALIBU_HYBRID_CC,
|
||||
}
|
||||
prndl2_rate = 10 if CP.carFingerprint in kaofui_state_cars else 40
|
||||
sdgm_non_volt = CP.carFingerprint in SDGM_CAR and \
|
||||
CP.carFingerprint not in kaofui_state_cars
|
||||
if sdgm_non_volt:
|
||||
messages += [
|
||||
("ECMPRDNL2", 40),
|
||||
("ECMPRDNL2", prndl2_rate),
|
||||
("AcceleratorPedal2", 40),
|
||||
("ECMEngineStatus", 80),
|
||||
]
|
||||
else:
|
||||
messages += [
|
||||
("ECMPRDNL2", 40),
|
||||
("ECMPRDNL2", prndl2_rate),
|
||||
("AcceleratorPedal2", 33),
|
||||
("ECMEngineStatus", 100),
|
||||
("BCMTurnSignals", 1),
|
||||
@@ -255,8 +321,9 @@ class CarState(CarStateBase):
|
||||
messages.append(("EBCMBrakePedalPosition", 100))
|
||||
|
||||
if CP.transmissionType == TransmissionType.direct:
|
||||
regen_paddle_rate = 50 if CP.carFingerprint in kaofui_state_cars else 40
|
||||
messages += [
|
||||
("EBCMRegenPaddle", 40),
|
||||
("EBCMRegenPaddle", regen_paddle_rate),
|
||||
("EVDriveMode", 0),
|
||||
]
|
||||
|
||||
|
||||
@@ -26,6 +26,20 @@ FINGERPRINTS = {
|
||||
{
|
||||
170: 8, 171: 8, 189: 7, 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 389: 2, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 4, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 5, 567: 3, 568: 1, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1273: 3, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1922: 7, 1927: 7, 1930: 7, 2017: 8, 2020: 8, 2025: 8, 2028: 8
|
||||
}],
|
||||
CAR.CHEVROLET_VOLT_ASCM: [
|
||||
# Causes errors with normal OBD install
|
||||
# {
|
||||
# 189: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 497: 8, 500: 6, 501: 8, 528: 4, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 761: 7, 767: 4, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 880: 6, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1267: 1, 1280: 4, 1296: 4, 1300: 8, 1922: 7, 1930: 7
|
||||
# }
|
||||
],
|
||||
CAR.CHEVROLET_VOLT_CAMERA: [
|
||||
# Volt Premier 2017 w/ flashed firmware, cam harness + pedal (no 0x170/0x171 on PT bus)
|
||||
{
|
||||
# 189: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 497: 8, 500: 6, 501: 8, 513: 6, 528: 4, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1922: 7
|
||||
}],
|
||||
CAR.GMC_ACADIA_ASCM: [
|
||||
# Causes errors with normal OBD install
|
||||
],
|
||||
CAR.BUICK_LACROSSE: [{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 386: 8, 388: 8, 393: 7, 398: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 510: 8, 528: 5, 532: 6, 534: 2, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 5, 707: 8, 753: 5, 761: 7, 801: 8, 804: 3, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 872: 1, 882: 8, 890: 1, 892: 2, 893: 1, 894: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1904: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1914: 7, 1916: 7, 1918: 7, 1919: 7, 1937: 8, 1953: 8, 1968: 8, 2001: 8, 2017: 8, 2018: 8, 2020: 8, 2026: 8
|
||||
}],
|
||||
@@ -52,6 +66,9 @@ FINGERPRINTS = {
|
||||
CAR.CHEVROLET_MALIBU: [{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 456: 8, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1930: 7, 2016: 8, 2024: 8
|
||||
}],
|
||||
CAR.CHEVROLET_MALIBU_ASCM: [{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 456: 8, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1930: 7, 2016: 8, 2024: 8
|
||||
}],
|
||||
CAR.GMC_ACADIA: [{
|
||||
190: 6, 192: 5, 193: 8, 197: 8, 199: 4, 201: 6, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 289: 1, 290: 1, 298: 8, 304: 8, 309: 8, 313: 8, 320: 8, 322: 7, 328: 1, 352: 7, 368: 8, 381: 8, 384: 8, 386: 8, 388: 8, 393: 8, 398: 8, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 458: 8, 460: 4, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 489: 5, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 512: 3, 530: 8, 532: 6, 534: 2, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 567: 5, 568: 2, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 801: 8, 803: 8, 804: 3, 805: 8, 832: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1003: 5, 1005: 6, 1009: 8, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1225: 8, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1906: 7, 1907: 7, 1912: 7, 1914: 7, 1918: 7, 1919: 7, 1920: 7, 1930: 7
|
||||
},
|
||||
@@ -67,14 +84,34 @@ FINGERPRINTS = {
|
||||
CAR.CADILLAC_ESCALADE_ESV_2019: [{
|
||||
715: 8, 840: 5, 717: 5, 869: 4, 880: 6, 289: 8, 454: 8, 842: 5, 460: 5, 463: 3, 801: 8, 170: 8, 190: 6, 241: 6, 201: 8, 417: 7, 211: 2, 419: 1, 398: 8, 426: 7, 487: 8, 442: 8, 451: 8, 452: 8, 453: 6, 479: 3, 311: 8, 500: 6, 647: 6, 193: 8, 707: 8, 197: 8, 209: 7, 199: 4, 455: 7, 313: 8, 481: 7, 485: 8, 489: 8, 249: 8, 393: 7, 407: 7, 413: 8, 422: 4, 431: 8, 501: 8, 499: 3, 810: 8, 508: 8, 381: 8, 462: 4, 532: 6, 562: 8, 386: 8, 761: 7, 573: 1, 554: 3, 719: 5, 560: 8, 1279: 4, 388: 8, 288: 5, 1005: 6, 497: 8, 844: 8, 961: 8, 967: 4, 977: 8, 979: 8, 985: 5, 1001: 8, 1017: 8, 1019: 2, 1020: 8, 1217: 8, 510: 8, 866: 4, 304: 1, 969: 8, 384: 4, 1033: 7, 1009: 8, 1034: 7, 1296: 4, 1930: 7, 1105: 5, 1013: 5, 1225: 7, 1919: 7, 320: 3, 534: 2, 352: 5, 298: 8, 1223: 2, 1233: 8, 608: 8, 1265: 8, 609: 6, 1267: 1, 1417: 8, 610: 6, 1906: 7, 611: 6, 612: 8, 613: 8, 208: 8, 564: 5, 309: 8, 1221: 5, 1280: 4, 1249: 8, 1907: 7, 1257: 6, 1300: 8, 1920: 7, 563: 5, 1322: 6, 1323: 4, 1328: 4, 1917: 7, 328: 1, 1912: 7, 1914: 7, 804: 3, 1918: 7
|
||||
}],
|
||||
CAR.CHEVROLET_BOLT_EUV: [{
|
||||
CAR.CHEVROLET_BOLT_ACC_2022_2023: [{
|
||||
189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 8, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1280: 4, 1296: 4, 1300: 8, 1611: 8, 1930: 7
|
||||
}],
|
||||
CAR.CHEVROLET_BOLT_CC: [
|
||||
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL: [{
|
||||
189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 8, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1280: 4, 1296: 4, 1300: 8, 1611: 8, 1930: 7
|
||||
}],
|
||||
CAR.CHEVROLET_BOLT_CC_2022_2023: [{
|
||||
189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 528: 5, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 8, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 869: 4, 880: 6, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1280: 4, 1296: 4, 1300: 8, 1611: 8, 1930: 7
|
||||
}],
|
||||
CAR.CHEVROLET_BOLT_CC_2017: [
|
||||
# Bolt Premier w/o ACC 2017
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 201: 6, 209: 7, 211: 2, 241: 6, 289: 1, 290: 1, 298: 8, 304: 8, 309: 8, 311: 8, 313: 8, 320: 8, 322: 7, 328: 1, 352: 5, 353: 3, 368: 8, 381: 6, 384: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 512: 3, 514: 2, 516: 4, 519: 2, 521: 3, 528: 5, 530: 8, 532: 7, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 8, 566: 6, 567: 5, 568: 1, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 832: 8, 840: 6, 842: 6, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8
|
||||
},
|
||||
# Bolt EV Premier 2017
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 201: 6, 209: 7, 211: 2, 241: 6, 289: 1, 290: 1, 298: 8, 304: 8, 309: 8, 311: 8, 313: 8, 320: 8, 322: 7, 328: 1, 352: 5, 353: 3, 368: 8, 381: 6, 384: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 512: 3, 514: 2, 516: 4, 519: 2, 521: 3, 528: 5, 530: 8, 532: 7, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 8, 566: 6, 567: 5, 568: 1, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 832: 8, 840: 6, 842: 6, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8
|
||||
},
|
||||
# Bolt EV Premier 2017 w Pedal
|
||||
{ # pylint: disable=duplicate-key
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 201: 6, 209: 7, 211: 2, 241: 6, 289: 1, 290: 1, 298: 8, 304: 8, 309: 8, 311: 8, 313: 8, 320: 8, 322: 7, 328: 1, 352: 5, 353: 3, 368: 8, 381: 6, 384: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 512: 3, 512: 6, 513: 6, 514: 2, 516: 4, 519: 2, 521: 3, 528: 5, 530: 8, 532: 7, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 8, 566: 6, 567: 5, 568: 1, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 832: 8, 840: 6, 842: 6, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8 # pylint: disable=duplicate-key # noqa: F601
|
||||
},
|
||||
# Bolt EV Premier 2017 2 w Pedal
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 384: 4, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 6, 567: 5, 568: 1, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7
|
||||
}],
|
||||
CAR.CHEVROLET_BOLT_CC_2019_2021: [
|
||||
# Chevy Bolt EV 2018-2021
|
||||
# Bolt Premier no ACC 2018 + Pedal
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 384: 4, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 6, 567: 5, 568: 1, 573: 1, 577: 8, 592: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1616: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7, 2020: 8, 2023: 8, 2028: 8, 2031: 8
|
||||
@@ -95,18 +132,6 @@ FINGERPRINTS = {
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 368: 3, 381: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 512: 6, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 7, 567: 5, 568: 2, 569: 3, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 753: 5, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 866: 4, 872: 1, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1037: 5, 1105: 5, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1279: 4, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7
|
||||
},
|
||||
# Bolt EV Premier 2017
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 201: 6, 209: 7, 211: 2, 241: 6, 289: 1, 290: 1, 298: 8, 304: 8, 309: 8, 311: 8, 313: 8, 320: 8, 322: 7, 328: 1, 352: 5, 353: 3, 368: 8, 381: 6, 384: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 512: 3, 514: 2, 516: 4, 519: 2, 521: 3, 528: 5, 530: 8, 532: 7, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 8, 566: 6, 567: 5, 568: 1, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 832: 8, 840: 6, 842: 6, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8
|
||||
},
|
||||
# Bolt EV Premier 2017 w Pedal
|
||||
{ # pylint: disable=duplicate-key
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 201: 6, 209: 7, 211: 2, 241: 6, 289: 1, 290: 1, 298: 8, 304: 8, 309: 8, 311: 8, 313: 8, 320: 8, 322: 7, 328: 1, 352: 5, 353: 3, 368: 8, 381: 6, 384: 8, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 512: 3, 512: 6, 513: 6, 514: 2, 516: 4, 519: 2, 521: 3, 528: 5, 530: 8, 532: 7, 537: 5, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 4, 563: 5, 564: 5, 565: 8, 566: 6, 567: 5, 568: 1, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 832: 8, 840: 6, 842: 6, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1601: 8, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1927: 7, 2016: 8, 2020: 8, 2024: 8, 2028: 8 # pylint: disable=duplicate-key # noqa: F601
|
||||
},
|
||||
# Bolt EV Premier 2017 2 w Pedal
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 384: 4, 386: 8, 388: 8, 390: 7, 407: 7, 417: 7, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 513: 6, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 6, 567: 5, 568: 1, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 717: 5, 753: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 7, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1904: 7, 1905: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7
|
||||
},
|
||||
# Bolt EV Premier no ACC 2023
|
||||
{
|
||||
170: 8, 188: 8, 189: 7, 190: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 3, 241: 6, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 308: 4, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 353: 3, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 390: 7, 398: 8, 407: 7, 417: 8, 419: 1, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 5, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 8, 567: 5, 568: 2, 569: 3, 573: 1, 577: 8, 592: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 711: 6, 715: 8, 717: 5, 753: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 848: 4, 866: 4, 869: 4, 872: 1, 880: 6, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1010: 8, 1013: 6, 1015: 1, 1017: 8, 1019: 2, 1020: 8, 1037: 5, 1105: 5, 1187: 5, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1265: 8, 1275: 3, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1601: 8, 1616: 8, 1618: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1913: 7, 1922: 7, 1927: 7, 1930: 7, 2016: 8, 2020: 8, 2023: 8, 2024: 8, 2028: 8, 2031: 8
|
||||
@@ -178,31 +203,51 @@ FINGERPRINTS = {
|
||||
CAR.CADILLAC_XT4: [
|
||||
# Cadillac XT4 w/ ACC 2023
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 353: 3, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 401: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 719: 5, 761: 7, 806: 1, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 880: 6, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 5, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1037: 5, 1105: 5, 1187: 5, 1195: 3, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1273: 3, 1276: 2, 1277: 7, 1278: 4, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1417: 8, 1512: 8, 1517: 8, 1601: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1920: 8, 1924: 8, 1930: 7, 1937: 8, 1953: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1984: 8, 1988: 8, 2000: 8, 2001: 8, 2002: 8, 2016: 8, 2017: 8, 2018: 8, 2020: 8, 2021: 8, 2024: 8, 2026: 8
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 353: 3, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 401: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 719: 5, 761: 7, 767: 4, 806: 1, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 880: 6, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 5, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1037: 5, 1105: 5, 1187: 5, 1195: 3, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1273: 3, 1276: 2, 1277: 7, 1278: 4, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1417: 8, 1512: 8, 1517: 8, 1601: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1920: 8, 1924: 8, 1930: 7, 1937: 8, 1953: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1984: 8, 1988: 8, 2000: 8, 2001: 8, 2002: 8, 2016: 8, 2017: 8, 2018: 8, 2020: 8, 2021: 8, 2024: 8, 2026: 8
|
||||
}],
|
||||
CAR.CADILLAC_XT5_CC: [
|
||||
# TRain's 2017 XT5
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 510: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 567: 5, 647: 3, 707: 8, 717: 5, 723: 2, 753: 5, 761: 7, 800: 6, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 872: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1904: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1914: 7, 1919: 7, 1920: 7
|
||||
}],
|
||||
CAR.CADILLAC_XT6: [
|
||||
#{}
|
||||
],
|
||||
CAR.CHEVROLET_BLAZER: [{
|
||||
190: 6, 193: 8, 197: 8, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 289: 8, 298: 8, 304: 3, 309: 8, 313: 8, 322: 7, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 510: 8, 532: 6, 560: 8, 562: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 767: 4, 840: 5, 842: 5, 844: 8, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1296: 4
|
||||
}],
|
||||
CAR.CHEVROLET_TRAVERSE: [
|
||||
# Chevy Traverse w/ ACC 2023
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 401: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 567: 5, 573: 1, 577: 8, 578: 8, 579: 8, 587: 8, 603: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 723: 4, 730: 4, 753: 5, 761: 7, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 5, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1105: 5, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1346: 8, 1347: 8, 1355: 8, 1362: 8, 1417: 8, 1512: 8, 1514: 8, 1601: 8, 1602: 8, 1603: 7, 1609: 8, 1611: 8, 1613: 8, 1618: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1799: 8, 1810: 8, 1813: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1856: 8, 1858: 8, 1859: 8, 1860: 8, 1862: 8, 1863: 8, 1871: 8, 1872: 8, 1875: 8, 1879: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1920: 7, 1927: 8, 1930: 7, 1937: 8, 1953: 8, 1954: 8, 1955: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1988: 8, 1990: 8, 2000: 8, 2001: 8, 2004: 8, 2016: 8, 2017: 8, 2018: 8, 2019: 8, 2020: 8, 2024: 8, 2026: 8
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 401: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 510: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 567: 5, 573: 1, 577: 8, 578: 8, 579: 8, 587: 8, 603: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 723: 4, 730: 4, 753: 5, 761: 7, 767: 4, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 5, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1105: 5, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1346: 8, 1347: 8, 1355: 8, 1362: 8, 1417: 8, 1512: 8, 1514: 8, 1601: 8, 1602: 8, 1603: 7, 1609: 8, 1611: 8, 1613: 8, 1618: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1799: 8, 1810: 8, 1813: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1856: 8, 1858: 8, 1859: 8, 1860: 8, 1862: 8, 1863: 8, 1871: 8, 1872: 8, 1875: 8, 1879: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7, 1920: 7, 1927: 8, 1930: 7, 1937: 8, 1953: 8, 1954: 8, 1955: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1988: 8, 1990: 8, 2000: 8, 2001: 8, 2004: 8, 2016: 8, 2017: 8, 2018: 8, 2019: 8, 2020: 8, 2024: 8, 2026: 8
|
||||
}],
|
||||
CAR.CHEVROLET_MALIBU_SDGM: [
|
||||
# Chevy Malibu w/ SDGM Harness 2019
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 5, 288: 5, 289: 8, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 587: 8, 707: 8, 715: 8, 717: 5, 761: 7, 767: 4, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 880: 6, 882: 8, 890: 1, 892: 2, 893: 2, 894: 1, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1353: 8, 1355: 8, 1611: 8, 1792: 8, 1793: 8, 1798: 8, 1799: 8, 1810: 8, 1813: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1843: 8, 1856: 8, 1858: 8, 1859: 8, 1860: 8, 1862: 8, 1863: 8, 1871: 8, 1872: 8, 1875: 8, 1879: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1916: 7, 1920: 8, 1927: 8, 1930: 7, 1937: 8, 1953: 8, 1954: 8, 1955: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1988: 8, 1990: 8, 2000: 8, 2001: 8, 2002: 8, 2004: 8, 2017: 8, 2018: 8, 2020: 8
|
||||
}],
|
||||
CAR.BUICK_BABYENCLAVE: [
|
||||
# Buick Baby Enclave w/ ACC 2020-23
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 353: 3, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 394: 7, 398: 8, 401: 8, 405: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 450: 4, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 456: 8, 457: 6, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 723: 4, 730: 4, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 872: 1, 880: 6, 882: 8, 890: 1, 892: 2, 893: 2, 894: 1, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1037: 5, 1105: 5, 1187: 5, 1195: 3, 1201: 3, 1217: 8, 1218: 3, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1273: 3, 1276: 2, 1277: 7, 1278: 4, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1417: 8, 1512: 8, 1514: 8, 1517: 8, 1601: 8, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1914: 7, 1916: 7, 1919: 7, 1927: 7, 1930: 7, 2018: 8, 2020: 8, 2021: 8, 2028: 8
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 208: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 331: 3, 352: 5, 353: 3, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 394: 7, 398: 8, 401: 8, 405: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 450: 4, 451: 8, 452: 8, 453: 6, 454: 8, 455: 7, 456: 8, 457: 6, 462: 4, 463: 3, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 503: 2, 508: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 569: 3, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 715: 8, 717: 5, 723: 4, 730: 4, 761: 7, 767: 4, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 872: 1, 880: 6, 882: 8, 890: 1, 892: 2, 893: 2, 894: 1, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 6, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1037: 5, 1105: 5, 1187: 5, 1195: 3, 1201: 3, 1217: 8, 1218: 3, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1273: 3, 1276: 2, 1277: 7, 1278: 4, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1345: 8, 1417: 8, 1512: 8, 1514: 8, 1517: 8, 1601: 8, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1914: 7, 1916: 7, 1919: 7, 1927: 7, 1930: 7, 2018: 8, 2020: 8, 2021: 8, 2028: 8
|
||||
}],
|
||||
CAR.CHEVROLET_MALIBU_CC: [
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 257: 8, 288: 5, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 328: 1, 352: 5, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 401: 8, 407: 7, 409: 8, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 577: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 6, 707: 8, 717: 5, 730: 4, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 961: 8, 969: 8, 975: 2, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 6, 1017: 8, 1020: 8, 1037: 5, 1105: 5, 1187: 6, 1189: 1, 1195: 3, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1268: 2, 1271: 8, 1273: 3, 1279: 4, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1906: 7, 1907: 7, 1912: 7, 1919: 7
|
||||
}],
|
||||
CAR.CHEVROLET_MALIBU_HYBRID_CC: [
|
||||
{
|
||||
193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 352: 5, 386: 8, 451: 8, 452: 8, 453: 6, 481: 7, 485: 8, 489: 8, 493: 8, 500: 6, 560: 8, 562: 8, 566: 6, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 717: 5, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4
|
||||
}],
|
||||
CAR.CHEVROLET_TRAX: [
|
||||
{
|
||||
190: 6, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 3, 309: 8, 311: 8, 313: 8, 320: 4, 322: 7, 328: 1, 352: 5, 381: 8, 384: 4, 386: 8, 388: 8, 413: 8, 451: 8, 452: 8, 453: 6, 455: 7, 479: 3, 481: 7, 485: 8, 489: 8, 497: 8, 500: 6, 501: 8, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 715: 8, 717: 5, 761: 7, 789: 5, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 880: 6, 977: 8, 1001: 8, 1011: 6, 1017: 8, 1020: 8, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1930: 7
|
||||
}],
|
||||
CAR.CHEVROLET_VOLT_2019: [
|
||||
# Chevy Volt w/ ACC 2019
|
||||
{
|
||||
170: 8, 189: 7, 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 257: 8, 288: 5, 289: 8, 292: 2, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 331: 3, 352: 5, 368: 3, 381: 8, 384: 4, 386: 8, 388: 8, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 508: 8, 528: 5, 532: 6, 546: 7, 550: 8, 554: 3, 558: 8, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 566: 7, 567: 5, 573: 1, 577: 8, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 711: 6, 715: 8, 717: 5, 761: 7, 767: 4, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 961: 8, 967: 4, 969: 8, 975: 2, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 8, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 5, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1236: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1268: 2, 1273: 3, 1275: 3, 1279: 4, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1328: 4, 1345: 8, 1417: 8, 1512: 8, 1513: 8, 1516: 8, 1517: 8, 1601: 8, 1609: 8, 1611: 8, 1618: 8, 1613: 8, 1649: 8, 1792: 8, 1793: 8, 1798: 8, 1799: 8, 1810: 8, 1813: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1856: 8, 1858: 8, 1859: 8, 1860: 8, 1862: 8, 1863: 8, 1871: 8, 1872: 8, 1875: 8, 1879: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1905: 7, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1920: 8, 1922: 7, 1927: 7, 1930: 7, 1937: 8, 1953: 8, 1954: 8, 1955: 8, 1968: 8, 1969: 8, 1971: 8, 1975: 8, 1988: 8, 1990: 8, 2000: 8, 2001: 8, 2004: 8, 2017: 8, 2018: 8, 2020: 8, 2021: 8, 2023: 8, 2025: 8, 2028: 8, 2031: 8
|
||||
}],
|
||||
}
|
||||
|
||||
FW_VERSIONS: dict[str, dict[tuple, list[bytes]]] = {
|
||||
|
||||
@@ -6,6 +6,56 @@ from openpilot.common.realtime import DT_CTRL
|
||||
from openpilot.selfdrive.car import make_can_msg
|
||||
from openpilot.selfdrive.car.gm.values import CAR, CruiseButtons, CanBus
|
||||
|
||||
MALIBU_BUTTON_TABLE = {
|
||||
0: [0x2FBC, 0x25DE, 0x15EE, 0x1FCC],
|
||||
1: [0x55AE, 0x5F8C, 0x6F7C, 0x659E],
|
||||
4: [0x2ACD, 0x20EF, 0x1ADD, 0x10FF],
|
||||
5: [0x50BF, 0x5A9D, 0x60AF, 0x6A8D],
|
||||
}
|
||||
|
||||
MALIBU_BUTTON_MAP = {
|
||||
CruiseButtons.UNPRESS: 0,
|
||||
CruiseButtons.RES_ACCEL: 1,
|
||||
CruiseButtons.MAIN: 4,
|
||||
CruiseButtons.CANCEL: 5,
|
||||
}
|
||||
|
||||
|
||||
def malibu_phase_map_for_button(button):
|
||||
key = MALIBU_BUTTON_MAP.get(button, None)
|
||||
if key is None or key not in MALIBU_BUTTON_TABLE:
|
||||
return None
|
||||
return {v: i for i, v in enumerate(MALIBU_BUTTON_TABLE[key])}
|
||||
|
||||
|
||||
def malibu_phase_map_for_acc(acc_value):
|
||||
seq = MALIBU_BUTTON_TABLE.get(acc_value)
|
||||
if not seq:
|
||||
return None
|
||||
return {v: i for i, v in enumerate(seq)}
|
||||
|
||||
|
||||
def create_buttons_malibu(packer, bus, button, phase, prefix=0x41):
|
||||
key = MALIBU_BUTTON_MAP.get(button, None)
|
||||
if key is None or key not in MALIBU_BUTTON_TABLE:
|
||||
# fallback to standard checksum for unsupported buttons
|
||||
return create_buttons(packer, bus, 0, button)
|
||||
|
||||
values = {
|
||||
"ACCButtons": button,
|
||||
"RollingCounter": 0,
|
||||
"ACCAlwaysOne": 1,
|
||||
"DistanceButton": 0,
|
||||
}
|
||||
dat = packer.make_can_msg("ASCMSteeringButton", bus, values)[2]
|
||||
data = bytearray(dat)
|
||||
data[3] = prefix & 0xFF
|
||||
|
||||
seq = MALIBU_BUTTON_TABLE[key]
|
||||
val = seq[phase % len(seq)]
|
||||
data[5] = (val >> 8) & 0xFF
|
||||
data[6] = val & 0xFF
|
||||
return make_can_msg(0x1e1, bytes(data), bus)
|
||||
|
||||
def create_buttons(packer, bus, idx, button):
|
||||
values = {
|
||||
@@ -23,6 +73,17 @@ def create_buttons(packer, bus, idx, button):
|
||||
values["SteeringButtonChecksum"] = checksum
|
||||
return packer.make_can_msg("ASCMSteeringButton", bus, values)
|
||||
|
||||
def create_buttons_malibu_cancel(bus, phase, prefix=0x41):
|
||||
# Malibu Hybrid CC cancel frames use a 4-value pattern in the last 2 bytes.
|
||||
data = bytearray(7)
|
||||
data[3] = prefix & 0xFF
|
||||
data[4] = 0x00
|
||||
cancel_bytes = (0x60, 0xAF, 0x65, 0x9E, 0x6A, 0x8D, 0x6F, 0x7C)
|
||||
idx = ((phase + 2) % 4) * 2
|
||||
data[5] = cancel_bytes[idx]
|
||||
data[6] = cancel_bytes[idx + 1]
|
||||
return make_can_msg(0x1e1, bytes(data), bus)
|
||||
|
||||
|
||||
def create_pscm_status(packer, bus, pscm_status):
|
||||
values = {s: pscm_status[s] for s in [
|
||||
@@ -57,7 +118,7 @@ def create_adas_keepalive(bus):
|
||||
return [make_can_msg(0x409, dat, bus), make_can_msg(0x40a, dat, bus)]
|
||||
|
||||
|
||||
def create_gas_regen_command(packer, bus, throttle, idx, enabled, at_full_stop):
|
||||
def create_gas_regen_command(packer, bus, throttle, idx, enabled, at_full_stop, include_always_one3=False):
|
||||
values = {
|
||||
"GasRegenCmdActive": enabled,
|
||||
"RollingCounter": idx,
|
||||
@@ -66,8 +127,9 @@ def create_gas_regen_command(packer, bus, throttle, idx, enabled, at_full_stop):
|
||||
"GasRegenFullStopActive": at_full_stop,
|
||||
"GasRegenAlwaysOne": 1,
|
||||
"GasRegenAlwaysOne2": 1,
|
||||
"GasRegenAlwaysOne3": 1,
|
||||
}
|
||||
if include_always_one3:
|
||||
values["GasRegenAlwaysOne3"] = 1
|
||||
|
||||
dat = packer.make_can_msg("ASCMGasRegenCmd", bus, values)[2]
|
||||
values["GasRegenChecksum"] = (((0xff - dat[1]) & 0xff) << 16) | \
|
||||
@@ -81,7 +143,7 @@ def create_friction_brake_command(packer, bus, apply_brake, idx, enabled, near_s
|
||||
mode = 0x1
|
||||
|
||||
# TODO: Understand this better. Volts and ICE Camera ACC cars are 0x1 when enabled with no brake
|
||||
if enabled and CP.carFingerprint in (CAR.CHEVROLET_BOLT_EUV,):
|
||||
if enabled and CP.carFingerprint in (CAR.CHEVROLET_BOLT_ACC_2022_2023,):
|
||||
mode = 0x9
|
||||
|
||||
if apply_brake > 0:
|
||||
@@ -123,6 +185,23 @@ def create_acc_dashboard_command(packer, bus, enabled, target_speed_kph, hud_con
|
||||
|
||||
return packer.make_can_msg("ASCMActiveCruiseControlStatus", bus, values)
|
||||
|
||||
def create_ecm_cruise_control_command(packer, bus, enabled, target_speed_kph):
|
||||
dat = bytearray(8)
|
||||
dat[0] = 0x01
|
||||
# Match observed stock shape on non-ACC CC paths: byte4 is usually 0x00
|
||||
# (with occasional 0x80 from stock state transitions). Keep this spoofed
|
||||
# path at 0x00 to avoid plausibility mismatch on non-speed bits.
|
||||
dat[4] = 0x00
|
||||
|
||||
set_speed_raw = 0
|
||||
if enabled:
|
||||
set_speed_raw = int(round(max(0., target_speed_kph) / 0.0625))
|
||||
set_speed_raw = max(0, min(set_speed_raw, 0x0FFF))
|
||||
|
||||
dat[2] = (set_speed_raw >> 8) & 0xFF
|
||||
dat[3] = set_speed_raw & 0xFF
|
||||
return make_can_msg(0x3D1, bytes(dat), bus)
|
||||
|
||||
|
||||
def create_adas_time_status(bus, tt, idx):
|
||||
dat = [(tt >> 20) & 0xff, (tt >> 12) & 0xff, (tt >> 4) & 0xff,
|
||||
@@ -177,8 +256,11 @@ def create_lka_icon_command(bus, active, critical, steer):
|
||||
dat = b"\x00\x00\x00"
|
||||
return make_can_msg(0x104c006c, dat, bus)
|
||||
|
||||
def create_prndl2_command(packer, bus, press_regen_paddle):
|
||||
prndl2_value = 7 if press_regen_paddle else 6
|
||||
def create_prndl2_command(packer, bus, press_regen_paddle, CP):
|
||||
if CP.carFingerprint in (CAR.CHEVROLET_BOLT_ACC_2022_2023, CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL, CAR.CHEVROLET_BOLT_CC_2022_2023):
|
||||
prndl2_value = 5 if press_regen_paddle else 6
|
||||
else:
|
||||
prndl2_value = 7 if press_regen_paddle else 6
|
||||
manual_mode = 1 if press_regen_paddle else 0
|
||||
values = {
|
||||
"Byte0": 0x0C,
|
||||
@@ -242,6 +324,13 @@ def create_gm_cc_spam_command(packer, controller, CS, actuators, frogpilot_toggl
|
||||
# TODO: Cleanup the timing - normal is every 30ms...
|
||||
if (cruiseBtn != CruiseButtons.INIT) and ((controller.frame - controller.last_button_frame) * DT_CTRL > rate):
|
||||
controller.last_button_frame = controller.frame
|
||||
if CS.CP.carFingerprint == CAR.CHEVROLET_MALIBU_HYBRID_CC:
|
||||
phase_map = malibu_phase_map_for_button(cruiseBtn)
|
||||
if phase_map:
|
||||
msgs = [create_buttons_malibu(packer, CanBus.POWERTRAIN, cruiseBtn, controller.malibu_button_phase,
|
||||
CS.steering_button_prefix)]
|
||||
controller.malibu_button_phase = (controller.malibu_button_phase + 1) % 4
|
||||
return msgs
|
||||
idx = (CS.buttons_counter + 1) % 4 # Need to predict the next idx for '22-23 EUV
|
||||
return [create_buttons(packer, CanBus.POWERTRAIN, idx, cruiseBtn)]
|
||||
else:
|
||||
|
||||
+313
-71
@@ -7,8 +7,8 @@ from panda import Panda
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
from openpilot.selfdrive.car import create_button_events, get_safety_config
|
||||
from openpilot.selfdrive.car.gm.radar_interface import RADAR_HEADER_MSG
|
||||
from openpilot.selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus, GMFlags, CC_ONLY_CAR, SDGM_CAR
|
||||
from openpilot.selfdrive.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD, LateralAccelFromTorqueCallbackType
|
||||
from openpilot.selfdrive.car.gm.values import CAR, CruiseButtons, CarControllerParams, EV_CAR, CAMERA_ACC_CAR, CanBus, GMFlags, CC_ONLY_CAR, SDGM_CAR, ASCM_INT, set_red_panda_canbus
|
||||
from openpilot.selfdrive.car.interfaces import CarInterfaceBase, TorqueFromLateralAccelCallbackType, FRICTION_THRESHOLD, LateralAccelFromTorqueCallbackType, get_friction_threshold
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import get_friction
|
||||
|
||||
ButtonType = car.CarState.ButtonEvent.Type
|
||||
@@ -26,15 +26,56 @@ CAM_MSG = 0x320 # AEBCmd
|
||||
# TODO: Is this always linked to camera presence?
|
||||
ACCELERATOR_POS_MSG = 0xbe
|
||||
|
||||
VOLT_LIKE_CARS = {
|
||||
CAR.CHEVROLET_VOLT,
|
||||
CAR.CHEVROLET_VOLT_2019,
|
||||
CAR.CHEVROLET_VOLT_CC,
|
||||
CAR.CHEVROLET_VOLT_CAMERA,
|
||||
CAR.CHEVROLET_VOLT_ASCM,
|
||||
CAR.CHEVROLET_MALIBU,
|
||||
CAR.CHEVROLET_MALIBU_ASCM,
|
||||
CAR.CHEVROLET_MALIBU_SDGM,
|
||||
CAR.CHEVROLET_MALIBU_CC,
|
||||
CAR.CHEVROLET_MALIBU_HYBRID_CC,
|
||||
}
|
||||
|
||||
NON_LINEAR_TORQUE_PARAMS = {
|
||||
CAR.CHEVROLET_BOLT_EUV: [1.8, 1.1, 0.27, 0.0],
|
||||
CAR.CHEVROLET_BOLT_CC: [1.8, 1.1, 0.27, 0.0],
|
||||
CAR.GMC_ACADIA: [4.78003305, 1.0, 0.3122, 0.05591772],
|
||||
CAR.CHEVROLET_SILVERADO: [3.29974374, 1.0, 0.25571356, 0.0465122]
|
||||
CAR.CHEVROLET_BOLT_ACC_2022_2023: {
|
||||
"left": [2.6531724862969748, 1.1, 0.1919764879840985, 0.0],
|
||||
"right": [2.7031724862969748, 1.0, 0.1469764879840985, 0.0],
|
||||
},
|
||||
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL: {
|
||||
"left": [2.6531724862969748, 1.1, 0.1919764879840985, 0.0],
|
||||
"right": [2.7031724862969748, 1.0, 0.1469764879840985, 0.0],
|
||||
},
|
||||
CAR.CHEVROLET_BOLT_CC_2022_2023: {
|
||||
"left": [2.6531724862969748, 1.1, 0.1919764879840985, 0.0],
|
||||
"right": [2.7031724862969748, 1.0, 0.1469764879840985, 0.0],
|
||||
},
|
||||
CAR.CHEVROLET_BOLT_CC_2019_2021: {
|
||||
"left": [1.8, 1.1, 0.27, 0.0],
|
||||
"right": [2.0, 1.0, 0.205, 0.0],
|
||||
},
|
||||
CAR.CHEVROLET_BOLT_CC_2017: {
|
||||
"left": [2.15, 1.0, 0.21, 0.0],
|
||||
"right": [2.15, 1.0, 0.21, 0.0],
|
||||
},
|
||||
CAR.GMC_ACADIA: {
|
||||
"left": [4.78003305, 1.0, 0.3122, 0.05591772],
|
||||
"right": [4.78003305, 1.0, 0.3122, 0.05591772],
|
||||
},
|
||||
CAR.CHEVROLET_SILVERADO: {
|
||||
"left": [3.8, 0.81, 0.24, 0.0465122],
|
||||
"right": [3.8, 0.81, 0.24, 0.0465122],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class CarInterface(CarInterfaceBase):
|
||||
def __init__(self, CP, FPCP, CarController, CarState):
|
||||
super().__init__(CP, FPCP, CarController, CarState)
|
||||
self.steer_offset = 0.0
|
||||
|
||||
@staticmethod
|
||||
def get_pid_accel_limits(CP, current_speed, cruise_speed):
|
||||
return CarControllerParams.ACCEL_MIN, CarControllerParams.ACCEL_MAX
|
||||
@@ -47,7 +88,7 @@ class CarInterface(CarInterfaceBase):
|
||||
return 0.10006696 * sigmoid * (v_ego + 3.12485927)
|
||||
|
||||
def get_steer_feedforward_function(self):
|
||||
if self.CP.carFingerprint in (CAR.CHEVROLET_VOLT, CAR.CHEVROLET_VOLT_CC):
|
||||
if self.CP.carFingerprint in VOLT_LIKE_CARS:
|
||||
return self.get_steer_feedforward_volt
|
||||
else:
|
||||
return CarInterfaceBase.get_steer_feedforward_default
|
||||
@@ -60,7 +101,9 @@ class CarInterface(CarInterfaceBase):
|
||||
# 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)
|
||||
assert non_linear_torque_params, "The params are not defined"
|
||||
a, b, c, d = non_linear_torque_params
|
||||
# Left is positive
|
||||
side_key = "left" if lateral_acceleration >= 0 else "right"
|
||||
a, b, c, d = non_linear_torque_params[side_key]
|
||||
sig_input = a * lateral_acceleration
|
||||
sig = np.sign(sig_input) * (1 / (1 + exp(-fabs(sig_input))) - 0.5)
|
||||
steer_torque = (sig * b) + (lateral_acceleration * c) + d
|
||||
@@ -76,46 +119,111 @@ class CarInterface(CarInterfaceBase):
|
||||
torque_values, lataccel_values = self.get_lataccel_torque_siglin()
|
||||
|
||||
def torque_from_lateral_accel_siglin(lateral_acceleration: float, torque_params: car.CarParams.LateralTorqueTuning):
|
||||
return np.interp(lateral_acceleration, lataccel_values, torque_values)
|
||||
return float(np.interp(lateral_acceleration, lataccel_values, torque_values) + self.steer_offset)
|
||||
return torque_from_lateral_accel_siglin
|
||||
else:
|
||||
return self.torque_from_lateral_accel_linear
|
||||
def torque_from_lateral_accel_linear(lateral_acceleration: float, torque_params: car.CarParams.LateralTorqueTuning):
|
||||
return self.torque_from_lateral_accel_linear(lateral_acceleration, torque_params) + self.steer_offset
|
||||
return torque_from_lateral_accel_linear
|
||||
|
||||
def lateral_accel_from_torque(self) -> LateralAccelFromTorqueCallbackType:
|
||||
if self.CP.carFingerprint in NON_LINEAR_TORQUE_PARAMS:
|
||||
torque_values, lataccel_values = self.get_lataccel_torque_siglin()
|
||||
|
||||
def lateral_accel_from_torque_siglin(torque: float, torque_params: car.CarParams.LateralTorqueTuning):
|
||||
return np.interp(torque, torque_values, lataccel_values)
|
||||
return np.interp(torque - self.steer_offset, torque_values, lataccel_values)
|
||||
return lateral_accel_from_torque_siglin
|
||||
else:
|
||||
return self.lateral_accel_from_torque_linear
|
||||
def lateral_accel_from_torque_linear(torque: float, torque_params: car.CarParams.LateralTorqueTuning):
|
||||
return self.lateral_accel_from_torque_linear(torque - self.steer_offset, torque_params)
|
||||
return lateral_accel_from_torque_linear
|
||||
|
||||
def update(self, c: car.CarControl, can_strings: list[bytes], frogpilot_toggles) -> car.CarState:
|
||||
self.steer_offset = float(getattr(frogpilot_toggles, "steer_offset", 0.0))
|
||||
return super().update(c, can_strings, frogpilot_toggles)
|
||||
|
||||
@staticmethod
|
||||
def _get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs, frogpilot_toggles):
|
||||
ret.carName = "gm"
|
||||
red_panda = getattr(frogpilot_toggles, "red_panda", False)
|
||||
set_red_panda_canbus(red_panda)
|
||||
|
||||
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.gm)]
|
||||
if red_panda:
|
||||
ret.safetyConfigs = [get_safety_config(car.CarParams.SafetyModel.noOutput), ret.safetyConfigs[0]]
|
||||
gm_safety_cfg = ret.safetyConfigs[-1] if red_panda else ret.safetyConfigs[0]
|
||||
ret.autoResumeSng = False
|
||||
ret.enableBsm = 0x142 in fingerprint[CanBus.POWERTRAIN]
|
||||
if PEDAL_MSG in fingerprint[0]:
|
||||
ret.enableBsm = 0x142 in fingerprint.get(CanBus.POWERTRAIN, {})
|
||||
|
||||
def has_sascm(fingerprint):
|
||||
return 0x2FF in fingerprint.get(CanBus.POWERTRAIN, {})
|
||||
|
||||
# Detect Beartech SASCM allows openpilot longitudinal control on SDGM and ASCM_INT vehicles
|
||||
if has_sascm(fingerprint):
|
||||
ret.flags |= GMFlags.SASCM.value
|
||||
|
||||
if PEDAL_MSG in fingerprint.get(CanBus.POWERTRAIN, {}):
|
||||
ret.enableGasInterceptor = True
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_GAS_INTERCEPTOR
|
||||
# When a pedal interceptor is present, always use normal longitudinal (block stock cruise)
|
||||
experimental_long = False
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_GAS_INTERCEPTOR
|
||||
if candidate == CAR.CHEVROLET_BOLT_ACC_2022_2023:
|
||||
# Hard-block pedal interceptor for ACC fingerprinted Bolts
|
||||
ret.enableGasInterceptor = False
|
||||
gm_safety_cfg.safetyParam &= ~Panda.FLAG_GM_GAS_INTERCEPTOR
|
||||
else:
|
||||
# When a pedal interceptor is present, always use normal longitudinal (block stock cruise)
|
||||
experimental_long = False
|
||||
|
||||
if candidate in EV_CAR:
|
||||
ret.transmissionType = TransmissionType.direct
|
||||
else:
|
||||
ret.transmissionType = TransmissionType.automatic
|
||||
|
||||
ret.longitudinalTuning.kiBP = [5., 35., 60.]
|
||||
kaofui_cars = SDGM_CAR | ASCM_INT | VOLT_LIKE_CARS | {CAR.CHEVROLET_MALIBU_HYBRID_CC}
|
||||
ret.longitudinalTuning.kiBP = [5., 35.] if candidate in kaofui_cars else [5., 35., 60.]
|
||||
|
||||
if candidate in CAMERA_ACC_CAR:
|
||||
ret.experimentalLongitudinalAvailable = candidate not in CC_ONLY_CAR
|
||||
is_bolt_2022_2023_pedal = candidate == CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL and ret.enableGasInterceptor
|
||||
|
||||
kaofui_camera_cars = {
|
||||
CAR.CHEVROLET_VOLT_CAMERA,
|
||||
CAR.CHEVROLET_VOLT_CC,
|
||||
CAR.CHEVROLET_MALIBU_HYBRID_CC,
|
||||
}
|
||||
bolt_cc_camera_cars = {
|
||||
CAR.CHEVROLET_BOLT_CC_2017,
|
||||
CAR.CHEVROLET_BOLT_CC_2019_2021,
|
||||
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
CAR.CHEVROLET_BOLT_CC_2022_2023,
|
||||
}
|
||||
is_camera_acc = candidate in CAMERA_ACC_CAR and candidate not in kaofui_cars and \
|
||||
(candidate not in CC_ONLY_CAR or candidate in bolt_cc_camera_cars)
|
||||
if candidate in kaofui_camera_cars:
|
||||
# Keep Volt/Malibu camera path functionally aligned with kaofui.
|
||||
ret.experimentalLongitudinalAvailable = candidate not in (CC_ONLY_CAR | ASCM_INT | SDGM_CAR) or has_sascm(fingerprint)
|
||||
ret.networkLocation = NetworkLocation.fwdCamera
|
||||
ret.radarUnavailable = 0x460 not in fingerprint.get(CanBus.OBSTACLE, {})
|
||||
ret.pcmCruise = True
|
||||
ret.minEnableSpeed = 5 * CV.KPH_TO_MS
|
||||
ret.minSteerSpeed = 10 * CV.KPH_TO_MS
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM
|
||||
|
||||
# Tuning for experimental long
|
||||
ret.longitudinalTuning.kiV = [0.5, 0.5]
|
||||
ret.stoppingDecelRate = 1.0 # reach brake quickly after enabling
|
||||
ret.vEgoStopping = 0.25
|
||||
ret.vEgoStarting = 0.25
|
||||
ret.stopAccel = -0.25
|
||||
|
||||
if ret.experimentalLongitudinalAvailable and experimental_long:
|
||||
ret.pcmCruise = False
|
||||
ret.openpilotLongitudinalControl = True
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
|
||||
elif is_camera_acc:
|
||||
# TorqueTune camera-ACC behavior
|
||||
ret.experimentalLongitudinalAvailable = (candidate not in CC_ONLY_CAR) and not ret.enableGasInterceptor
|
||||
ret.networkLocation = NetworkLocation.fwdCamera
|
||||
ret.radarUnavailable = True # no radar
|
||||
ret.pcmCruise = True
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM
|
||||
ret.pcmCruise = not ret.enableGasInterceptor
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM
|
||||
ret.minEnableSpeed = 5 * CV.KPH_TO_MS
|
||||
ret.minSteerSpeed = 10 * CV.KPH_TO_MS
|
||||
|
||||
@@ -132,33 +240,90 @@ class CarInterface(CarInterfaceBase):
|
||||
if ret.experimentalLongitudinalAvailable and experimental_long:
|
||||
ret.pcmCruise = False
|
||||
ret.openpilotLongitudinalControl = True
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
|
||||
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
|
||||
elif candidate in SDGM_CAR:
|
||||
ret.longitudinalTuning.kiV = [0., 0., 0.] # TODO: tuning
|
||||
ret.experimentalLongitudinalAvailable = False
|
||||
# kaofui parity: SDGM cars require SASCM for experimental long
|
||||
ret.experimentalLongitudinalAvailable = candidate not in (CC_ONLY_CAR | ASCM_INT | SDGM_CAR) or has_sascm(fingerprint)
|
||||
ret.networkLocation = NetworkLocation.fwdCamera
|
||||
ret.radarUnavailable = 0x460 not in fingerprint.get(CanBus.OBSTACLE, {})
|
||||
ret.pcmCruise = True
|
||||
ret.radarUnavailable = True
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by ASCM
|
||||
ret.minSteerSpeed = 30 * CV.MPH_TO_MS
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_SDGM
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
ret.minSteerSpeed = 7 * CV.MPH_TO_MS
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_SDGM
|
||||
# Use C9 brake bit on Blazer and SDGM variants that lack 0xBE (ECMAcceleratorPos),
|
||||
# so panda brake_pressed source matches carstate on light taps.
|
||||
if candidate == CAR.CHEVROLET_BLAZER or ACCELERATOR_POS_MSG not in fingerprint.get(CanBus.POWERTRAIN, {}):
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_FORCE_BRAKE_C9
|
||||
ret.flags |= GMFlags.FORCE_BRAKE_C9.value
|
||||
|
||||
# Tuning for experimental long
|
||||
ret.longitudinalTuning.kiV = [0.5, 0.5] if candidate in kaofui_cars else [0.5, 0.5, 0.5]
|
||||
ret.vEgoStopping = 0.1
|
||||
ret.vEgoStarting = 0.1
|
||||
|
||||
ret.stoppingDecelRate = 1.0 # reach brake quickly after enabling
|
||||
ret.vEgoStopping = 0.25
|
||||
ret.vEgoStarting = 0.25
|
||||
ret.stopAccel = -0.25
|
||||
|
||||
if ret.experimentalLongitudinalAvailable and experimental_long:
|
||||
ret.pcmCruise = False
|
||||
ret.openpilotLongitudinalControl = True
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
|
||||
if is_bolt_2022_2023_pedal:
|
||||
ret.experimentalLongitudinalAvailable = False
|
||||
ret.pcmCruise = False
|
||||
elif candidate in ASCM_INT:
|
||||
# kaofui parity: ASCM_INT cars require SASCM for experimental long
|
||||
ret.experimentalLongitudinalAvailable = candidate not in (CC_ONLY_CAR | ASCM_INT | SDGM_CAR) or has_sascm(fingerprint)
|
||||
ret.networkLocation = NetworkLocation.fwdCamera
|
||||
ret.radarUnavailable = 0x460 not in fingerprint.get(CanBus.OBSTACLE, {})
|
||||
ret.pcmCruise = True
|
||||
ret.minEnableSpeed = 5 * CV.KPH_TO_MS
|
||||
ret.minSteerSpeed = 7 * CV.MPH_TO_MS
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_ASCM_INT
|
||||
|
||||
# Tuning for experimental long
|
||||
ret.longitudinalTuning.kiV = [0.5, 0.5] if candidate in kaofui_cars else [0.5, 0.5, 0.5]
|
||||
ret.vEgoStopping = 0.1
|
||||
ret.vEgoStarting = 0.1
|
||||
|
||||
ret.stoppingDecelRate = 1.0 # reach brake quickly after enabling
|
||||
ret.vEgoStopping = 0.25
|
||||
ret.vEgoStarting = 0.25
|
||||
ret.stopAccel = -0.25
|
||||
|
||||
if ret.experimentalLongitudinalAvailable and experimental_long:
|
||||
ret.pcmCruise = False
|
||||
ret.openpilotLongitudinalControl = True
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
|
||||
if is_bolt_2022_2023_pedal:
|
||||
ret.experimentalLongitudinalAvailable = False
|
||||
ret.pcmCruise = False
|
||||
else: # ASCM, OBD-II harness
|
||||
ret.openpilotLongitudinalControl = not frogpilot_toggles.disable_openpilot_long
|
||||
ret.networkLocation = NetworkLocation.gateway
|
||||
ret.radarUnavailable = RADAR_HEADER_MSG not in fingerprint[CanBus.OBSTACLE] and not docs
|
||||
ret.radarUnavailable = RADAR_HEADER_MSG not in fingerprint.get(CanBus.OBSTACLE, {}) and not docs
|
||||
ret.pcmCruise = False # stock non-adaptive cruise control is kept off
|
||||
# supports stop and go, but initial engage must (conservatively) be above 18mph
|
||||
ret.minEnableSpeed = 18 * CV.MPH_TO_MS
|
||||
ret.minSteerSpeed = 7 * CV.MPH_TO_MS
|
||||
|
||||
# Tuning
|
||||
ret.longitudinalTuning.kiV = [0.5, 0.5, 0.5]
|
||||
ret.longitudinalTuning.kiV = [0.5, 0.5] if candidate in kaofui_cars else [0.5, 0.5, 0.5]
|
||||
if candidate in kaofui_cars:
|
||||
ret.stoppingDecelRate = 3
|
||||
ret.vEgoStopping = 0.75
|
||||
ret.vEgoStarting = 0.75
|
||||
ret.stopAccel = -1.5
|
||||
|
||||
if ret.enableGasInterceptor:
|
||||
# Need to set ASCM long limits when using pedal interceptor, instead of camera ACC long limits
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_ASCM_LONG
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_ASCM_LONG
|
||||
|
||||
if getattr(frogpilot_toggles, "remote_start_boots_comma", False):
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_REMOTE_START_BOOTS_COMMA
|
||||
|
||||
# Start with a baseline tuning for all GM vehicles. Override tuning as needed in each model section below.
|
||||
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0.], [0.]]
|
||||
@@ -172,14 +337,17 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.radarTimeStep = 0.0667 # GM radar runs at 15Hz instead of standard 20Hz
|
||||
ret.longitudinalActuatorDelay = 0.5 # large delay to initially start braking
|
||||
|
||||
if candidate in (CAR.CHEVROLET_VOLT, CAR.CHEVROLET_VOLT_CC):
|
||||
if candidate in (CAR.CHEVROLET_VOLT, CAR.CHEVROLET_VOLT_CC, CAR.CHEVROLET_VOLT_CAMERA):
|
||||
ret.minEnableSpeed = -1
|
||||
ret.lateralTuning.pid.kpBP = [0., 40.]
|
||||
ret.lateralTuning.pid.kpV = [0., 0.17]
|
||||
ret.lateralTuning.pid.kiBP = [0.]
|
||||
ret.lateralTuning.pid.kiV = [0.]
|
||||
ret.lateralTuning.pid.kf = 1. # get_steer_feedforward_volt()
|
||||
|
||||
if candidate == CAR.CHEVROLET_VOLT_2019 and not ret.openpilotLongitudinalControl:
|
||||
ret.minEnableSpeed = -1
|
||||
|
||||
if candidate in VOLT_LIKE_CARS:
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
ret.steerActuatorDelay = 0.2
|
||||
if candidate == CAR.CHEVROLET_MALIBU_HYBRID_CC and ret.enableGasInterceptor:
|
||||
ret.flags |= GMFlags.PEDAL_LONG.value
|
||||
|
||||
elif candidate == CAR.GMC_ACADIA:
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
@@ -204,20 +372,41 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate in (CAR.CHEVROLET_BOLT_EUV, CAR.CHEVROLET_BOLT_CC):
|
||||
elif candidate in (CAR.CHEVROLET_BOLT_ACC_2022_2023, CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL, CAR.CHEVROLET_BOLT_CC_2022_2023, CAR.CHEVROLET_BOLT_CC_2019_2021, CAR.CHEVROLET_BOLT_CC_2017):
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
# Bolt-only lateral tuning overrides
|
||||
ret.lateralTuning.torque.kp = 1.03
|
||||
ret.lateralTuning.torque.ki = 1.07
|
||||
ret.lateralTuning.torque.kd = 0.93
|
||||
ret.lateralTuning.torque.kfDEPRECATED = 0.02
|
||||
|
||||
if candidate in (CAR.CHEVROLET_BOLT_CC_2019_2021,
|
||||
CAR.CHEVROLET_BOLT_ACC_2022_2023,
|
||||
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
CAR.CHEVROLET_BOLT_CC_2022_2023):
|
||||
# Apply 2019-style negative FF and Ki-mult tweaks to 2019-2021 and 2022 variants.
|
||||
ret.lateralTuning.torque.ki *= 1.07
|
||||
ret.lateralTuning.torque.kd *= 0.93
|
||||
|
||||
if candidate == CAR.CHEVROLET_BOLT_CC_2017:
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_BOLT_2017
|
||||
|
||||
if ret.enableGasInterceptor:
|
||||
# ACC Bolts use pedal for full longitudinal control, not just sng
|
||||
ret.flags |= GMFlags.PEDAL_LONG.value
|
||||
|
||||
elif candidate == CAR.CHEVROLET_SILVERADO:
|
||||
# Enable pedal interceptor for ACC models when detected
|
||||
if is_bolt_2022_2023_pedal:
|
||||
ret.flags |= GMFlags.PEDAL_LONG.value
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_NO_ACC
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_BOLT_2022_PEDAL
|
||||
|
||||
if candidate == CAR.CHEVROLET_SILVERADO:
|
||||
# On the Bolt, the ECM and camera independently check that you are either above 5 kph or at a stop
|
||||
# with foot on brake to allow engagement, but this platform only has that check in the camera.
|
||||
# TODO: check if this is split by EV/ICE with more platforms in the future
|
||||
if ret.openpilotLongitudinalControl:
|
||||
ret.minEnableSpeed = -1.
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate in (CAR.CHEVROLET_EQUINOX, CAR.CHEVROLET_EQUINOX_CC):
|
||||
@@ -235,22 +424,34 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.CADILLAC_XT6:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
ret.minSteerSpeed = 7 * CV.MPH_TO_MS
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.CADILLAC_XT4:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
if not ret.openpilotLongitudinalControl:
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
ret.minSteerSpeed = 30 * CV.MPH_TO_MS
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.CADILLAC_XT5_CC:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.CHEVROLET_TRAVERSE:
|
||||
elif candidate in (CAR.CHEVROLET_TRAVERSE, CAR.CHEVROLET_BLAZER):
|
||||
ret.steerActuatorDelay = 0.2
|
||||
ret.minSteerSpeed = 10 * CV.KPH_TO_MS
|
||||
if not ret.openpilotLongitudinalControl:
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
if candidate == CAR.CHEVROLET_BLAZER:
|
||||
ret.minEnableSpeed = 5 * CV.KPH_TO_MS
|
||||
|
||||
elif candidate == CAR.BUICK_BABYENCLAVE:
|
||||
ret.steerActuatorDelay = 0.2
|
||||
ret.minSteerSpeed = 10 * CV.KPH_TO_MS
|
||||
if not ret.openpilotLongitudinalControl:
|
||||
ret.minEnableSpeed = -1. # engage speed is decided by pcm
|
||||
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
|
||||
|
||||
elif candidate == CAR.CADILLAC_CT6_CC:
|
||||
@@ -265,30 +466,50 @@ class CarInterface(CarInterfaceBase):
|
||||
|
||||
if ret.enableGasInterceptor and frogpilot_toggles.gm_pedal_longitudinal:
|
||||
ret.networkLocation = NetworkLocation.fwdCamera
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM
|
||||
ret.minEnableSpeed = -1
|
||||
ret.pcmCruise = False
|
||||
ret.openpilotLongitudinalControl = not frogpilot_toggles.disable_openpilot_long
|
||||
ret.stoppingControl = True
|
||||
ret.autoResumeSng = True
|
||||
|
||||
if candidate in CC_ONLY_CAR: #pedal interceptor tuning
|
||||
if candidate in CC_ONLY_CAR or (candidate in CAMERA_ACC_CAR and ret.enableGasInterceptor): #pedal interceptor tuning
|
||||
ret.flags |= GMFlags.PEDAL_LONG.value
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_PEDAL_LONG
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_PEDAL_LONG
|
||||
# Note: Low speed, stop and go not tested. Should be fairly smooth on highway
|
||||
ret.longitudinalTuning.kiBP = [0., 3., 6., 35.]
|
||||
ret.longitudinalTuning.kiV = [0.125, 0.175, 0.225, 0.33]
|
||||
ret.longitudinalTuning.kfDEPRECATED = 0.25
|
||||
ret.stoppingDecelRate = 0.8
|
||||
if candidate in (CAR.CHEVROLET_MALIBU_CC, CAR.CHEVROLET_MALIBU_HYBRID_CC):
|
||||
ret.longitudinalTuning.kiBP = [0.0, 5., 35.]
|
||||
ret.longitudinalTuning.kiV = [0.0, 0.35, 0.5]
|
||||
ret.longitudinalTuning.kfDEPRECATED = 0.15
|
||||
ret.stoppingDecelRate = 0.8
|
||||
ret.minEnableSpeed = -1
|
||||
ret.pcmCruise = False
|
||||
ret.openpilotLongitudinalControl = not frogpilot_toggles.disable_openpilot_long
|
||||
else:
|
||||
ret.longitudinalTuning.kiBP = [0., 3., 6., 35.]
|
||||
ret.longitudinalTuning.kiV = [0.125, 0.175, 0.225, 0.33]
|
||||
ret.longitudinalTuning.kfDEPRECATED = 0.25
|
||||
ret.stoppingDecelRate = 0.8
|
||||
else: # Pedal used for SNG, ACC for longitudinal control otherwise
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_HW_CAM_LONG
|
||||
ret.startingState = True
|
||||
ret.vEgoStopping = 0.25
|
||||
ret.vEgoStarting = 0.25
|
||||
|
||||
elif candidate in CC_ONLY_CAR:
|
||||
if ret.enableGasInterceptor and candidate == CAR.CHEVROLET_MALIBU_HYBRID_CC:
|
||||
ret.flags |= GMFlags.PEDAL_LONG.value
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_PEDAL_LONG
|
||||
ret.longitudinalTuning.kiBP = [0.0, 5., 35.]
|
||||
ret.longitudinalTuning.kiV = [0.0, 0.18, 0.25]
|
||||
ret.longitudinalTuning.kfDEPRECATED = 0.15
|
||||
ret.stoppingDecelRate = 0.8
|
||||
ret.minEnableSpeed = -1
|
||||
ret.pcmCruise = False
|
||||
ret.openpilotLongitudinalControl = not frogpilot_toggles.disable_openpilot_long
|
||||
|
||||
elif candidate in CC_ONLY_CAR and not ret.enableGasInterceptor:
|
||||
ret.flags |= GMFlags.CC_LONG.value
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_CC_LONG
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_CC_LONG
|
||||
ret.radarUnavailable = True
|
||||
ret.experimentalLongitudinalAvailable = False
|
||||
ret.minEnableSpeed = 24 * CV.MPH_TO_MS
|
||||
@@ -296,26 +517,41 @@ class CarInterface(CarInterfaceBase):
|
||||
ret.pcmCruise = False
|
||||
|
||||
if not ret.enableGasInterceptor and candidate in CC_ONLY_CAR: #redneck tuning
|
||||
ret.longitudinalTuning.kpBP = [10.7, 10.8, 28.] # 10.7 m/s == 24 mph
|
||||
ret.longitudinalTuning.kpV = [0., 5., 2.] # set lower end to 0 since we can't drive below that speed
|
||||
ret.longitudinalTuning.deadzoneBP = [0.]
|
||||
ret.longitudinalTuning.deadzoneV = [0.9] # == 2 km/h/s, 1.25 mph/s
|
||||
ret.longitudinalActuatorDelay = 1. # TODO: measure this
|
||||
ret.longitudinalTuning.kiBP = [0.]
|
||||
ret.longitudinalTuning.kiV = [0.1]
|
||||
ret.stoppingDecelRate = 11.18 # == 25 mph/s (.04 rate)
|
||||
if candidate == CAR.CHEVROLET_MALIBU_HYBRID_CC:
|
||||
pass
|
||||
else:
|
||||
ret.longitudinalTuning.kpBP = [10.7, 10.8, 28.] # 10.7 m/s == 24 mph
|
||||
ret.longitudinalTuning.kpV = [0., 5., 2.] # set lower end to 0 since we can't drive below that speed
|
||||
ret.longitudinalTuning.deadzoneBP = [0., 1.]
|
||||
ret.longitudinalTuning.deadzoneV = [0.9, 0.9] # == 2 km/h/s, 1.25 mph/s
|
||||
ret.longitudinalActuatorDelay = 1. # TODO: measure this
|
||||
if candidate == CAR.CHEVROLET_MALIBU_CC:
|
||||
ret.longitudinalTuning.kpV = [0., 20., 20.]
|
||||
ret.longitudinalTuning.kiBP = [0.]
|
||||
ret.longitudinalTuning.kiV = [0.1]
|
||||
ret.stoppingDecelRate = 11.18 # == 25 mph/s (.04 rate)
|
||||
|
||||
if candidate in CC_ONLY_CAR:
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_NO_ACC
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_NO_ACC
|
||||
|
||||
# Exception for flashed cars, or cars whose camera was removed
|
||||
if (ret.networkLocation == NetworkLocation.fwdCamera or candidate in CC_ONLY_CAR) and CAM_MSG not in fingerprint[CanBus.CAMERA] and not candidate in SDGM_CAR:
|
||||
if (ret.networkLocation == NetworkLocation.fwdCamera or candidate in CC_ONLY_CAR) and CAM_MSG not in fingerprint.get(CanBus.CAMERA, {}) and not candidate in (SDGM_CAR | ASCM_INT):
|
||||
ret.flags |= GMFlags.NO_CAMERA.value
|
||||
ret.safetyConfigs[0].safetyParam |= Panda.FLAG_GM_NO_CAMERA
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_NO_CAMERA
|
||||
|
||||
if ACCELERATOR_POS_MSG not in fingerprint[CanBus.POWERTRAIN]:
|
||||
if ACCELERATOR_POS_MSG not in fingerprint.get(CanBus.POWERTRAIN, {}):
|
||||
ret.flags |= GMFlags.NO_ACCELERATOR_POS_MSG.value
|
||||
|
||||
use_panda_3d1_sched = (
|
||||
ret.openpilotLongitudinalControl and
|
||||
ret.enableGasInterceptor and
|
||||
bool(ret.flags & GMFlags.PEDAL_LONG.value) and
|
||||
candidate in CC_ONLY_CAR and
|
||||
candidate != CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL
|
||||
)
|
||||
if use_panda_3d1_sched:
|
||||
gm_safety_cfg.safetyParam |= Panda.FLAG_GM_PANDA_3D1_SCHED
|
||||
|
||||
return ret
|
||||
|
||||
# returns a car.CarState
|
||||
@@ -345,22 +581,28 @@ class CarInterface(CarInterfaceBase):
|
||||
# TODO: verify 17 Volt can enable for the first time at a stop and allow for all GMs
|
||||
below_min_enable_speed = ret.vEgo < self.CP.minEnableSpeed or self.CS.moving_backward
|
||||
if below_min_enable_speed and not (ret.standstill and ret.brake >= 20 and
|
||||
(self.CP.networkLocation == NetworkLocation.fwdCamera and not self.CP.carFingerprint in SDGM_CAR)):
|
||||
(self.CP.networkLocation == NetworkLocation.fwdCamera and
|
||||
(self.CP.carFingerprint in VOLT_LIKE_CARS or self.CP.carFingerprint in {CAR.CHEVROLET_BLAZER, CAR.CHEVROLET_MALIBU_SDGM, CAR.CHEVROLET_TRAVERSE} or self.CP.carFingerprint not in SDGM_CAR))):
|
||||
events.add(EventName.belowEngageSpeed)
|
||||
if ret.cruiseState.standstill and not self.CP.autoResumeSng:
|
||||
events.add(EventName.resumeRequired)
|
||||
if ret.vEgo < self.CP.minSteerSpeed:
|
||||
events.add(EventName.belowSteerSpeed)
|
||||
|
||||
if (self.CP.flags & GMFlags.CC_LONG.value) and ret.vEgo < self.CP.minEnableSpeed and ret.cruiseState.enabled:
|
||||
events.add(EventName.speedTooLow)
|
||||
if (self.CP.flags & GMFlags.CC_LONG.value) and ret.vEgo < self.CP.minEnableSpeed:
|
||||
if ret.cruiseState.enabled or self.CS.out.cruiseState.enabled:
|
||||
events.add(EventName.speedTooLow)
|
||||
|
||||
if (self.CP.flags & GMFlags.PEDAL_LONG.value) and \
|
||||
self.CP.transmissionType == TransmissionType.direct and \
|
||||
self.CP.carFingerprint != CAR.CHEVROLET_MALIBU_HYBRID_CC and \
|
||||
not self.CS.single_pedal_mode and \
|
||||
c.longActive:
|
||||
events.add(FrogPilotEventName.pedalInterceptorNoBrake)
|
||||
|
||||
if self.CS.lkas_status == 3:
|
||||
events.add(EventName.steerUnavailable)
|
||||
|
||||
ret.events = events.to_msg()
|
||||
|
||||
return ret, fp_ret
|
||||
|
||||
+237
-43
@@ -37,44 +37,123 @@ class CarControllerParams:
|
||||
ACCEL_MIN = -4. # m/s^2
|
||||
|
||||
def __init__(self, CP):
|
||||
self.STEER_MAX = CarControllerParams.STEER_MAX
|
||||
self.STEER_STEP = CarControllerParams.STEER_STEP
|
||||
self.INACTIVE_STEER_STEP = CarControllerParams.INACTIVE_STEER_STEP
|
||||
self.STEER_DELTA_UP = CarControllerParams.STEER_DELTA_UP
|
||||
self.STEER_DELTA_DOWN = CarControllerParams.STEER_DELTA_DOWN
|
||||
self.STEER_DRIVER_ALLOWANCE = CarControllerParams.STEER_DRIVER_ALLOWANCE
|
||||
self.STEER_DRIVER_MULTIPLIER = CarControllerParams.STEER_DRIVER_MULTIPLIER
|
||||
self.STEER_DRIVER_FACTOR = CarControllerParams.STEER_DRIVER_FACTOR
|
||||
|
||||
if CP.carFingerprint == CAR.CHEVROLET_BOLT_CC_2017:
|
||||
self.STEER_MAX = 450
|
||||
self.STEER_DELTA_UP = 15
|
||||
self.STEER_DELTA_DOWN = 34
|
||||
self.STEER_DRIVER_ALLOWANCE = 78
|
||||
self.STEER_DRIVER_MULTIPLIER = 6
|
||||
self.STEER_DRIVER_FACTOR = 100
|
||||
|
||||
# Gas/brake lookups
|
||||
self.ZERO_GAS = 6144 # Coasting
|
||||
self.ZERO_GAS = 6150 # Coasting
|
||||
self.MAX_BRAKE = 400 # ~ -4.0 m/s^2 with regen
|
||||
|
||||
if CP.carFingerprint in CAMERA_ACC_CAR and CP.carFingerprint not in CC_ONLY_CAR and CP.carFingerprint != CAR.CHEVROLET_BOLT_EUV:
|
||||
self.MAX_GAS = 7496
|
||||
self.MAX_GAS_PLUS = 8848
|
||||
self.MAX_ACC_REGEN = 5610
|
||||
self.INACTIVE_REGEN = 5650
|
||||
# Camera ACC vehicles have no regen while enabled.
|
||||
# Camera transitions to MAX_ACC_REGEN from ZERO_GAS and uses friction brakes instantly
|
||||
self.max_regen_acceleration = 0.
|
||||
kaofui_cars = SDGM_CAR | ASCM_INT | {
|
||||
CAR.CHEVROLET_VOLT,
|
||||
CAR.CHEVROLET_VOLT_2019,
|
||||
CAR.CHEVROLET_VOLT_ASCM,
|
||||
CAR.CHEVROLET_VOLT_CAMERA,
|
||||
CAR.CHEVROLET_VOLT_CC,
|
||||
CAR.CHEVROLET_MALIBU_CC,
|
||||
CAR.CHEVROLET_MALIBU_HYBRID_CC,
|
||||
}
|
||||
volt_like = {
|
||||
CAR.CHEVROLET_VOLT,
|
||||
CAR.CHEVROLET_VOLT_ASCM,
|
||||
CAR.CHEVROLET_VOLT_CAMERA,
|
||||
CAR.CHEVROLET_VOLT_CC,
|
||||
}
|
||||
|
||||
elif CP.carFingerprint in SDGM_CAR:
|
||||
self.MAX_GAS = 7496
|
||||
self.MAX_GAS_PLUS = 7496
|
||||
self.MAX_ACC_REGEN = 7110
|
||||
self.INACTIVE_REGEN = 5650
|
||||
self.max_regen_acceleration = 0.
|
||||
if CP.carFingerprint in kaofui_cars:
|
||||
if (CP.carFingerprint in (CAMERA_ACC_CAR | SDGM_CAR) and
|
||||
CP.carFingerprint not in CC_ONLY_CAR and
|
||||
CP.carFingerprint != CAR.CHEVROLET_BOLT_ACC_2022_2023):
|
||||
self.MAX_GAS = 8848
|
||||
self.MAX_GAS_PLUS = 8848
|
||||
self.MAX_ACC_REGEN = 5610
|
||||
self.INACTIVE_REGEN = 5650
|
||||
# Camera ACC vehicles have no regen while enabled.
|
||||
# Camera transitions to MAX_ACC_REGEN from ZERO_GAS and uses friction brakes instantly
|
||||
max_regen_acceleration = 0.
|
||||
else:
|
||||
self.MAX_GAS = 8191 # Safety limit, not ACC max. Stock ACC >8192 from standstill.
|
||||
self.MAX_GAS_PLUS = 8191
|
||||
self.MAX_ACC_REGEN = 5500 # Max ACC regen is slightly less than max paddle regen
|
||||
self.INACTIVE_REGEN = 5500
|
||||
# ICE has much less engine braking force compared to regen in EVs,
|
||||
# lower threshold removes some braking deadzone
|
||||
max_regen_acceleration = -1. if CP.carFingerprint in EV_CAR else -0.1
|
||||
|
||||
self.BRAKE_SWITCH_MAX = self.MAX_ACC_REGEN if CP.carFingerprint in EV_CAR else self.ZERO_GAS
|
||||
if CP.carFingerprint in volt_like:
|
||||
self.BRAKE_LOOKUP_BP = [self.ACCEL_MIN, 0.]
|
||||
else:
|
||||
self.BRAKE_LOOKUP_BP = [self.ACCEL_MIN, max_regen_acceleration]
|
||||
|
||||
else:
|
||||
self.MAX_GAS = 7168 # Safety limit, not ACC max. Stock ACC >8192 from standstill.
|
||||
self.MAX_GAS_PLUS = 8191 # 8292 uses new bit, possible but not tested. Matches Twilsonco tw-main max
|
||||
self.MAX_ACC_REGEN = 7110 # Increased for stronger regen braking
|
||||
self.INACTIVE_REGEN = 5500
|
||||
# ICE has much less engine braking force compared to regen in EVs,
|
||||
# lower threshold removes some braking deadzone
|
||||
self.max_regen_acceleration = -3. if CP.carFingerprint in EV_CAR else -0.1 # More aggressive regen for EVs
|
||||
if CP.carFingerprint in CAMERA_ACC_CAR and CP.carFingerprint not in CC_ONLY_CAR:
|
||||
self.MAX_GAS = 8848
|
||||
self.MAX_GAS_PLUS = 8848
|
||||
self.MAX_ACC_REGEN = 5610
|
||||
self.INACTIVE_REGEN = 5650
|
||||
# Camera ACC vehicles have no regen while enabled.
|
||||
# Camera transitions to MAX_ACC_REGEN from ZERO_GAS and uses friction brakes instantly
|
||||
max_regen_acceleration = 0.
|
||||
self.BRAKE_SWITCH_MAX = self.MAX_ACC_REGEN if CP.carFingerprint in EV_CAR else self.ZERO_GAS
|
||||
|
||||
elif CP.carFingerprint in SDGM_CAR:
|
||||
self.MAX_GAS = 8191
|
||||
self.MAX_GAS_PLUS = 8191
|
||||
self.MAX_ACC_REGEN = 5500
|
||||
self.INACTIVE_REGEN = 5500
|
||||
max_regen_acceleration = 0.
|
||||
self.BRAKE_SWITCH_MAX = self.ZERO_GAS
|
||||
|
||||
else:
|
||||
self.MAX_GAS = 7168 # Safety limit, not ACC max. Stock ACC >8192 from standstill.
|
||||
self.MAX_GAS_PLUS = 7168 # 8292 uses new bit, possible but not tested. Matches Twilsonco tw-main max
|
||||
self.MAX_ACC_REGEN = 5500 # Max ACC regen is slightly less than max paddle regen
|
||||
self.INACTIVE_REGEN = 5500
|
||||
# ICE has much less engine braking force compared to regen in EVs,
|
||||
# lower threshold removes some braking deadzone
|
||||
max_regen_acceleration = -3. if CP.carFingerprint in EV_CAR else -0.1
|
||||
self.BRAKE_SWITCH_MAX = self.MAX_ACC_REGEN if CP.carFingerprint in EV_CAR else self.ZERO_GAS
|
||||
|
||||
self.BRAKE_LOOKUP_BP = [self.ACCEL_MIN, 0.]
|
||||
|
||||
self.max_regen_acceleration = max_regen_acceleration
|
||||
self.GAS_LOOKUP_BP = [self.max_regen_acceleration, 0., self.ACCEL_MAX]
|
||||
self.GAS_LOOKUP_BP_PLUS = [self.max_regen_acceleration, 0., self.ACCEL_MAX_PLUS]
|
||||
self.GAS_LOOKUP_V = [self.MAX_ACC_REGEN, self.ZERO_GAS, self.MAX_GAS]
|
||||
self.GAS_LOOKUP_V_PLUS = [self.MAX_ACC_REGEN, self.ZERO_GAS, self.MAX_GAS_PLUS]
|
||||
|
||||
self.BRAKE_LOOKUP_BP = [self.ACCEL_MIN, self.max_regen_acceleration]
|
||||
self.BRAKE_LOOKUP_V = [self.MAX_BRAKE, 0.]
|
||||
|
||||
self.BRAKE_SWITCH_LOOKUP_BP = [0.5, 10]
|
||||
self.BRAKE_SWITCH_LOOKUP_V = [self.ZERO_GAS, self.BRAKE_SWITCH_MAX]
|
||||
|
||||
# determined by letting Volt regen to a stop in L gear from 89mph,
|
||||
# and by letting off gas and allowing car to creep, for determining
|
||||
# the positive threshold values at very low speed
|
||||
EV_GAS_BRAKE_THRESHOLD_BP = [1.29, 1.52, 1.55, 1.6, 1.7, 1.8, 2.0, 2.2, 2.5, 5.52, 9.6, 20.5, 23.5, 35.0] # [m/s]
|
||||
EV_GAS_BRAKE_THRESHOLD_V = [0.0, -0.14, -0.16, -0.18, -0.215, -0.255, -0.32, -0.41, -0.5, -0.72, -0.895, -1.125, -1.145, -1.16] # [m/s^s]
|
||||
|
||||
def update_ev_gas_brake_threshold(self, v_ego):
|
||||
gas_brake_threshold = interp(v_ego, self.EV_GAS_BRAKE_THRESHOLD_BP, self.EV_GAS_BRAKE_THRESHOLD_V)
|
||||
self.GAS_LOOKUP_BP_PLUS = [self.max_regen_acceleration, 0., self.ACCEL_MAX_PLUS]
|
||||
self.EV_GAS_LOOKUP_BP = [gas_brake_threshold, max(0., gas_brake_threshold), self.ACCEL_MAX]
|
||||
self.EV_GAS_LOOKUP_BP_PLUS = [gas_brake_threshold, max(0., gas_brake_threshold), self.ACCEL_MAX_PLUS]
|
||||
self.EV_BRAKE_LOOKUP_BP = [self.ACCEL_MIN, gas_brake_threshold]
|
||||
|
||||
@dataclass
|
||||
class GMCarDocs(CarDocs):
|
||||
@@ -114,6 +193,16 @@ class CAR(Platforms):
|
||||
GMCarSpecs(mass=1607, wheelbase=2.69, steerRatio=17.7, centerToFrontRatio=0.45, tireStiffnessFactor=0.469, minEnableSpeed=-1),
|
||||
dbc_dict=dbc_dict('gm_global_a_powertrain_volt', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis')
|
||||
)
|
||||
CHEVROLET_VOLT_ASCM = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Volt 2017-18 ASCM Harness", min_enable_speed=0, video_link="https://youtu.be/QeMCN_4TFfQ")],
|
||||
GMCarSpecs(mass=1607, wheelbase=2.69, steerRatio=17.7, centerToFrontRatio=0.45, tireStiffnessFactor=0.469, minEnableSpeed=-1),
|
||||
dbc_dict=dbc_dict('gm_global_a_powertrain_volt', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis')
|
||||
)
|
||||
CHEVROLET_VOLT_CAMERA = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Volt 2017-18 Camera Harness", "Flashed camera-forward integration with ACC")],
|
||||
CHEVROLET_VOLT.specs,
|
||||
dbc_dict=dbc_dict('gm_global_a_powertrain_volt', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis')
|
||||
)
|
||||
CADILLAC_ATS = GMASCMPlatformConfig(
|
||||
[GMCarDocs("Cadillac ATS Premium Performance 2018")],
|
||||
GMCarSpecs(mass=1601, wheelbase=2.78, steerRatio=15.3),
|
||||
@@ -122,10 +211,19 @@ class CAR(Platforms):
|
||||
[GMCarDocs("Chevrolet Malibu Premier 2017")],
|
||||
GMCarSpecs(mass=1496, wheelbase=2.83, steerRatio=15.8, centerToFrontRatio=0.4),
|
||||
)
|
||||
CHEVROLET_MALIBU_ASCM = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Malibu 2017-19 ASCM Harness")],
|
||||
CHEVROLET_MALIBU.specs,
|
||||
)
|
||||
GMC_ACADIA = GMASCMPlatformConfig(
|
||||
[GMCarDocs("GMC Acadia 2018", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo")],
|
||||
GMCarSpecs(mass=1975, wheelbase=2.86, steerRatio=14.4, centerToFrontRatio=0.4),
|
||||
)
|
||||
GMC_ACADIA_ASCM = GMPlatformConfig(
|
||||
[GMCarDocs("GMC Acadia 2018 ASCM Harness", video_link="https://www.youtube.com/watch?v=0ZN6DdsBUZo")],
|
||||
GMCarSpecs(mass=1975, wheelbase=2.86, steerRatio=14.4, centerToFrontRatio=0.4),
|
||||
dbc_dict=dbc_dict('gm_global_a_powertrain_generated', 'gm_global_a_object', chassis_dbc='gm_global_a_chassis')
|
||||
)
|
||||
BUICK_LACROSSE = GMASCMPlatformConfig(
|
||||
[GMCarDocs("Buick LaCrosse 2017-19", "Driver Confidence Package 2")],
|
||||
GMCarSpecs(mass=1712, wheelbase=2.91, steerRatio=15.8, centerToFrontRatio=0.4),
|
||||
@@ -146,10 +244,10 @@ class CAR(Platforms):
|
||||
[GMCarDocs("Cadillac Escalade ESV 2019", "Adaptive Cruise Control (ACC) & LKAS")],
|
||||
CADILLAC_ESCALADE_ESV.specs,
|
||||
)
|
||||
CHEVROLET_BOLT_EUV = GMPlatformConfig(
|
||||
CHEVROLET_BOLT_ACC_2022_2023 = GMPlatformConfig(
|
||||
[
|
||||
GMCarDocs("Chevrolet Bolt EUV 2022-23", "Premier or Premier Redline Trim without Super Cruise Package", video_link="https://youtu.be/xvwzGMUA210"),
|
||||
GMCarDocs("Chevrolet Bolt EV 2022-23", "2LT Trim with Adaptive Cruise Control Package"),
|
||||
GMCarDocs("Chevrolet Bolt ACC 2022-2023", "Premier or Premier Redline Trim without Super Cruise Package", video_link="https://youtu.be/xvwzGMUA210"),
|
||||
GMCarDocs("Chevrolet Bolt EV ACC 2022-2023", "2LT Trim with Adaptive Cruise Control Package"),
|
||||
],
|
||||
GMCarSpecs(mass=1669, wheelbase=2.63779, steerRatio=16.8, centerToFrontRatio=0.4, tireStiffnessFactor=1.0),
|
||||
)
|
||||
@@ -158,7 +256,7 @@ class CAR(Platforms):
|
||||
GMCarDocs("Chevrolet Silverado 1500 2020-21", "Safety Package II"),
|
||||
GMCarDocs("GMC Sierra 1500 2020-21", "Driver Alert Package II", video_link="https://youtu.be/5HbNoBLzRwE"),
|
||||
],
|
||||
GMCarSpecs(mass=2450, wheelbase=3.75, steerRatio=16.3, tireStiffnessFactor=1.0),
|
||||
GMCarSpecs(mass=2994, wheelbase=3.75, steerRatio=16.3, tireStiffnessFactor=1.0),
|
||||
)
|
||||
CHEVROLET_EQUINOX = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Equinox 2019-22")],
|
||||
@@ -174,12 +272,25 @@ class CAR(Platforms):
|
||||
[GMCarDocs("Chevrolet Volt 2017-18 - No-ACC", min_enable_speed=0)],
|
||||
CHEVROLET_VOLT.specs,
|
||||
)
|
||||
CHEVROLET_BOLT_CC = GMPlatformConfig(
|
||||
CHEVROLET_BOLT_CC_2019_2021 = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Bolt EV 2018-2021 - No-ACC")],
|
||||
CHEVROLET_BOLT_ACC_2022_2023.specs,
|
||||
)
|
||||
CHEVROLET_BOLT_ACC_2022_2023_PEDAL = GMPlatformConfig(
|
||||
[
|
||||
GMCarDocs("Chevrolet Bolt EUV 2022-23 - No-ACC"),
|
||||
GMCarDocs("Chevrolet Bolt EV 2017-23 - No-ACC"),
|
||||
GMCarDocs("Chevrolet Bolt EV 2022-2023 ACC w Pedal"),
|
||||
],
|
||||
CHEVROLET_BOLT_EUV.specs,
|
||||
CHEVROLET_BOLT_ACC_2022_2023.specs,
|
||||
)
|
||||
CHEVROLET_BOLT_CC_2022_2023 = GMPlatformConfig(
|
||||
[
|
||||
GMCarDocs("Chevrolet Bolt EV 2022-2023 - No-ACC"),
|
||||
],
|
||||
CHEVROLET_BOLT_ACC_2022_2023.specs,
|
||||
)
|
||||
CHEVROLET_BOLT_CC_2017 = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Bolt EV 2017 - No-ACC")],
|
||||
CHEVROLET_BOLT_ACC_2022_2023.specs,
|
||||
)
|
||||
CHEVROLET_EQUINOX_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Equinox 2019-22 - No-ACC")],
|
||||
@@ -194,15 +305,15 @@ class CAR(Platforms):
|
||||
CHEVROLET_SUBURBAN.specs,
|
||||
)
|
||||
GMC_YUKON_CC = GMPlatformConfig(
|
||||
[GMCarDocs("GMC Yukon No ACC")],
|
||||
[GMCarDocs("GMC Yukon - No-ACC")],
|
||||
CarSpecs(mass=2541, wheelbase=2.95, steerRatio=16.3, centerToFrontRatio=0.4),
|
||||
)
|
||||
CADILLAC_CT6_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Cadillac CT6 No ACC")],
|
||||
[GMCarDocs("Cadillac CT6 - No-ACC")],
|
||||
CarSpecs(mass=2358, wheelbase=3.11, steerRatio=17.7, centerToFrontRatio=0.4),
|
||||
)
|
||||
CHEVROLET_TRAILBLAZER_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Trailblazer 2021-22")],
|
||||
[GMCarDocs("Chevrolet Trailblazer 2021-22 - No-ACC")],
|
||||
CHEVROLET_TRAILBLAZER.specs,
|
||||
)
|
||||
CADILLAC_XT4 = GMPlatformConfig(
|
||||
@@ -210,25 +321,45 @@ class CAR(Platforms):
|
||||
CarSpecs(mass=1660, wheelbase=2.78, steerRatio=14.4, centerToFrontRatio=0.4),
|
||||
)
|
||||
CADILLAC_XT5_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Cadillac XT5 No ACC")],
|
||||
[GMCarDocs("Cadillac XT5 - No-ACC")],
|
||||
CarSpecs(mass=1810, wheelbase=2.86, steerRatio=16.34, centerToFrontRatio=0.5),
|
||||
)
|
||||
CHEVROLET_BLAZER = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Blazer 2019-2025", "Driver Assist Package")],
|
||||
CarSpecs(mass=1850, wheelbase=3.10, steerRatio=17.9, centerToFrontRatio=0.4),
|
||||
)
|
||||
CHEVROLET_TRAVERSE = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Traverse 2023", "Driver Assist Package")],
|
||||
CarSpecs(mass=1955, wheelbase=3.07, steerRatio=17.9, centerToFrontRatio=0.4),
|
||||
)
|
||||
CHEVROLET_MALIBU_SDGM = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Malibu 2019", "SDGM Harness (Optional SASCM)")],
|
||||
CHEVROLET_MALIBU.specs,
|
||||
)
|
||||
BUICK_BABYENCLAVE = GMPlatformConfig(
|
||||
[GMCarDocs("Buick Baby Enclave 2020-23", "Driver Assist Package")],
|
||||
CarSpecs(mass=2050, wheelbase=2.86, steerRatio=16.0, centerToFrontRatio=0.5),
|
||||
)
|
||||
CHEVROLET_MALIBU_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Malibu 2023 No ACC")],
|
||||
[GMCarDocs("Chevrolet Malibu 2023 - No-ACC")],
|
||||
CarSpecs(mass=1450, wheelbase=2.8, steerRatio=18.25, centerToFrontRatio=0.4, tireStiffnessFactor=0.997),
|
||||
)
|
||||
CHEVROLET_MALIBU_HYBRID_CC = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Malibu Hybrid 2017 - No-ACC")],
|
||||
CarSpecs(mass=1450, wheelbase=2.8, steerRatio=15.8, centerToFrontRatio=0.4),
|
||||
)
|
||||
CHEVROLET_TRAX = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet TRAX 2024")],
|
||||
CarSpecs(mass=1365, wheelbase=2.7, steerRatio=16.4, centerToFrontRatio=0.4),
|
||||
)
|
||||
CHEVROLET_VOLT_2019 = GMPlatformConfig(
|
||||
[GMCarDocs("Chevrolet Volt 2019")],
|
||||
GMCarSpecs(mass=1607, wheelbase=2.69, steerRatio=15.7, centerToFrontRatio=0.45),
|
||||
)
|
||||
CADILLAC_XT6 = GMPlatformConfig(
|
||||
[GMCarDocs("Cadillac XT6 2020", "Driver Assist Package")],
|
||||
GMCarSpecs(mass=2050, wheelbase=2.86, steerRatio=16.5, centerToFrontRatio=0.4),
|
||||
)
|
||||
|
||||
|
||||
class CruiseButtons:
|
||||
@@ -253,11 +384,29 @@ class CanBus:
|
||||
LOOPBACK = 128
|
||||
DROPPED = 192
|
||||
|
||||
def set_red_panda_canbus(enabled: bool) -> None:
|
||||
if enabled:
|
||||
CanBus.POWERTRAIN = 4
|
||||
CanBus.OBSTACLE = 5
|
||||
CanBus.CAMERA = 6
|
||||
CanBus.CHASSIS = 6
|
||||
CanBus.LOOPBACK = 132
|
||||
CanBus.DROPPED = 196
|
||||
else:
|
||||
CanBus.POWERTRAIN = 0
|
||||
CanBus.OBSTACLE = 1
|
||||
CanBus.CAMERA = 2
|
||||
CanBus.CHASSIS = 2
|
||||
CanBus.LOOPBACK = 128
|
||||
CanBus.DROPPED = 192
|
||||
|
||||
class GMFlags(IntFlag):
|
||||
PEDAL_LONG = 1
|
||||
CC_LONG = 2
|
||||
NO_CAMERA = 4
|
||||
NO_ACCELERATOR_POS_MSG = 8
|
||||
FORCE_BRAKE_C9 = 16
|
||||
SASCM = 32
|
||||
|
||||
|
||||
# In a Data Module, an identifier is a string used to recognize an object,
|
||||
@@ -309,17 +458,62 @@ FW_QUERY_CONFIG = FwQueryConfig(
|
||||
extra_ecus=[(Ecu.fwdCamera, 0x24b, None)],
|
||||
)
|
||||
|
||||
EV_CAR = {CAR.CHEVROLET_VOLT, CAR.CHEVROLET_BOLT_EUV, CAR.CHEVROLET_VOLT_CC, CAR.CHEVROLET_BOLT_CC}
|
||||
CC_ONLY_CAR = {CAR.CHEVROLET_VOLT_CC, CAR.CHEVROLET_BOLT_CC, CAR.CHEVROLET_EQUINOX_CC, CAR.CHEVROLET_SUBURBAN_CC, CAR.GMC_YUKON_CC, CAR.CADILLAC_CT6_CC, CAR.CHEVROLET_TRAILBLAZER_CC, CAR.CADILLAC_XT5_CC, CAR.CHEVROLET_MALIBU_CC}
|
||||
CC_REGEN_PADDLE_CAR = {CAR.CHEVROLET_BOLT_CC, CAR.CHEVROLET_BOLT_EUV}
|
||||
# CC_ONLY_CAR = set(c for c in CAR if str(c).endswith('_CC'))
|
||||
EV_CAR = {
|
||||
CAR.CHEVROLET_VOLT,
|
||||
CAR.CHEVROLET_VOLT_2019,
|
||||
CAR.CHEVROLET_VOLT_ASCM,
|
||||
CAR.CHEVROLET_VOLT_CAMERA,
|
||||
CAR.CHEVROLET_VOLT_CC,
|
||||
CAR.CHEVROLET_BOLT_ACC_2022_2023,
|
||||
CAR.CHEVROLET_BOLT_CC_2019_2021,
|
||||
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
CAR.CHEVROLET_BOLT_CC_2022_2023,
|
||||
CAR.CHEVROLET_BOLT_CC_2017,
|
||||
CAR.CHEVROLET_MALIBU_HYBRID_CC,
|
||||
}
|
||||
CC_ONLY_CAR = {
|
||||
CAR.CHEVROLET_VOLT_CC,
|
||||
CAR.CHEVROLET_BOLT_CC_2019_2021,
|
||||
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
CAR.CHEVROLET_BOLT_CC_2022_2023,
|
||||
CAR.CHEVROLET_BOLT_CC_2017,
|
||||
CAR.CHEVROLET_EQUINOX_CC,
|
||||
CAR.CHEVROLET_SUBURBAN_CC,
|
||||
CAR.GMC_YUKON_CC,
|
||||
CAR.CADILLAC_CT6_CC,
|
||||
CAR.CHEVROLET_TRAILBLAZER_CC,
|
||||
CAR.CADILLAC_XT5_CC,
|
||||
CAR.CHEVROLET_MALIBU_CC,
|
||||
CAR.CHEVROLET_MALIBU_HYBRID_CC,
|
||||
}
|
||||
CC_REGEN_PADDLE_CAR = {
|
||||
CAR.CHEVROLET_BOLT_CC_2019_2021,
|
||||
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
CAR.CHEVROLET_BOLT_CC_2022_2023,
|
||||
CAR.CHEVROLET_BOLT_CC_2017,
|
||||
}
|
||||
|
||||
# We're integrated at the Safety Data Gateway Module on these cars
|
||||
SDGM_CAR = {CAR.CADILLAC_XT4, CAR.CHEVROLET_TRAVERSE, CAR.BUICK_BABYENCLAVE}
|
||||
SDGM_CAR = {CAR.CADILLAC_XT4, CAR.CADILLAC_XT6, CAR.CHEVROLET_TRAVERSE, CAR.CHEVROLET_BLAZER, CAR.CHEVROLET_MALIBU_SDGM, CAR.BUICK_BABYENCLAVE, CAR.CHEVROLET_VOLT_2019}
|
||||
|
||||
ASCM_INT = {CAR.CHEVROLET_VOLT_ASCM, CAR.GMC_ACADIA_ASCM, CAR.CHEVROLET_MALIBU_ASCM}
|
||||
|
||||
# We're integrated at the camera with VOACC on these cars (instead of ASCM w/ OBD-II harness)
|
||||
CAMERA_ACC_CAR = {CAR.CHEVROLET_BOLT_EUV, CAR.CHEVROLET_SILVERADO, CAR.CHEVROLET_EQUINOX, CAR.CHEVROLET_TRAILBLAZER, CAR.CHEVROLET_TRAX}
|
||||
CAMERA_ACC_CAR.update({CAR.CHEVROLET_VOLT_CC, CAR.CHEVROLET_BOLT_CC, CAR.CHEVROLET_EQUINOX_CC, CAR.GMC_YUKON_CC, CAR.CADILLAC_CT6_CC, CAR.CHEVROLET_TRAILBLAZER_CC, CAR.CADILLAC_XT5_CC, CAR.CHEVROLET_MALIBU_CC})
|
||||
CAMERA_ACC_CAR = {CAR.CHEVROLET_BOLT_ACC_2022_2023, CAR.CHEVROLET_SILVERADO, CAR.CHEVROLET_EQUINOX, CAR.CHEVROLET_TRAILBLAZER, CAR.CHEVROLET_TRAX, CAR.CHEVROLET_VOLT_CAMERA, CAR.CHEVROLET_BLAZER}
|
||||
CAMERA_ACC_CAR.update({
|
||||
CAR.CHEVROLET_VOLT_CC,
|
||||
CAR.CHEVROLET_BOLT_CC_2019_2021,
|
||||
CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
CAR.CHEVROLET_BOLT_CC_2022_2023,
|
||||
CAR.CHEVROLET_BOLT_CC_2017,
|
||||
CAR.CHEVROLET_EQUINOX_CC,
|
||||
CAR.GMC_YUKON_CC,
|
||||
CAR.CADILLAC_CT6_CC,
|
||||
CAR.CHEVROLET_TRAILBLAZER_CC,
|
||||
CAR.CADILLAC_XT5_CC,
|
||||
CAR.CHEVROLET_MALIBU_CC,
|
||||
CAR.CHEVROLET_MALIBU_HYBRID_CC,
|
||||
})
|
||||
# CAMERA_ACC_CAR.update(CC_ONLY_CAR)
|
||||
|
||||
STEER_THRESHOLD = 1.0
|
||||
|
||||
@@ -260,11 +260,11 @@ class CarController(CarControllerBase):
|
||||
self.gas = pcm_accel / self.params.NIDEC_GAS_MAX
|
||||
|
||||
new_actuators = actuators.as_builder()
|
||||
new_actuators.speed = self.speed
|
||||
new_actuators.accel = self.accel
|
||||
new_actuators.gas = self.gas
|
||||
new_actuators.brake = self.brake
|
||||
new_actuators.steer = self.last_steer
|
||||
new_actuators.speed = float(self.speed)
|
||||
new_actuators.accel = float(self.accel)
|
||||
new_actuators.gas = float(self.gas)
|
||||
new_actuators.brake = float(self.brake)
|
||||
new_actuators.steer = float(self.last_steer)
|
||||
new_actuators.steerOutputCan = apply_steer
|
||||
|
||||
self.frame += 1
|
||||
|
||||
@@ -76,14 +76,14 @@ def create_brake_command(packer, CAN, apply_brake, pump_on, pcm_override, pcm_ca
|
||||
return packer.make_can_msg("BRAKE_COMMAND", CAN.pt, values)
|
||||
|
||||
|
||||
def create_acc_commands(packer, CAN, enabled, active, accel, gas, stopping_counter, car_fingerprint):
|
||||
def create_acc_commands(packer, CAN, enabled, active, accel, gas, stopping_counter, car_fingerprint, gas_force):
|
||||
commands = []
|
||||
min_gas_accel = CarControllerParams.BOSCH_GAS_LOOKUP_BP[0]
|
||||
|
||||
control_on = 5 if enabled else 0
|
||||
gas_command = gas if active and accel > min_gas_accel else -30000
|
||||
gas_command = gas if active and gas_force > min_gas_accel else -30000
|
||||
accel_command = accel if active else 0
|
||||
braking = 1 if active and accel < min_gas_accel else 0
|
||||
braking = 1 if active and gas_force < min_gas_accel else 0
|
||||
standstill = 1 if active and stopping_counter > 0 else 0
|
||||
standstill_release = 1 if active and stopping_counter == 0 else 0
|
||||
|
||||
|
||||
@@ -37,7 +37,12 @@ EventName = car.CarEvent.EventName
|
||||
MAX_CTRL_SPEED = (V_CRUISE_MAX + 4) * CV.KPH_TO_MS
|
||||
ACCEL_MAX = 2.0
|
||||
ACCEL_MIN = -3.5
|
||||
FRICTION_THRESHOLD = 0.3
|
||||
FRICTION_THRESHOLD = 0.12
|
||||
|
||||
def get_friction_threshold(v_ego):
|
||||
# Interpolate friction threshold
|
||||
from openpilot.common.numpy_fast import interp
|
||||
return interp(v_ego, [1 * CV.MPH_TO_MS, 75 * CV.MPH_TO_MS], [0.12, 0.3])
|
||||
|
||||
TORQUE_PARAMS_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/params.toml')
|
||||
TORQUE_OVERRIDE_PATH = os.path.join(BASEDIR, 'selfdrive/car/torque_data/override.toml')
|
||||
@@ -147,8 +152,11 @@ class CarInterfaceBase(ABC):
|
||||
|
||||
ret = cls._get_params(ret, candidate, fingerprint, car_fw, experimental_long, docs, frogpilot_toggles)
|
||||
|
||||
trailer_load_kg = getattr(frogpilot_toggles, "trailer_load_kg", 0)
|
||||
|
||||
# Vehicle mass is published curb weight plus assumed payload such as a human driver; notCars have no assumed payload
|
||||
if not ret.notCar:
|
||||
ret.mass = ret.mass + trailer_load_kg
|
||||
ret.mass = ret.mass + STD_CARGO_KG
|
||||
|
||||
# Set params dependent on values set by the car interface
|
||||
@@ -179,6 +187,7 @@ class CarInterfaceBase(ABC):
|
||||
|
||||
elif platform in GMCAR:
|
||||
fp_ret.canUsePedal = True
|
||||
fp_ret.canUseSASCM = True
|
||||
|
||||
elif platform in HondaCAR:
|
||||
if candidate == HondaCAR.HONDA_CLARITY:
|
||||
|
||||
@@ -66,8 +66,8 @@ routes = [
|
||||
CarTestRoute("46460f0da08e621e|2021-10-26--07-21-46", GM.CADILLAC_ESCALADE_ESV),
|
||||
CarTestRoute("168f8b3be57f66ae|2023-09-12--21-44-42", GM.CADILLAC_ESCALADE_ESV_2019),
|
||||
CarTestRoute("c950e28c26b5b168|2018-05-30--22-03-41", GM.CHEVROLET_VOLT),
|
||||
CarTestRoute("f08912a233c1584f|2022-08-11--18-02-41", GM.CHEVROLET_BOLT_EUV, segment=1),
|
||||
CarTestRoute("555d4087cf86aa91|2022-12-02--12-15-07", GM.CHEVROLET_BOLT_EUV, segment=14), # Bolt EV
|
||||
CarTestRoute("f08912a233c1584f|2022-08-11--18-02-41", GM.CHEVROLET_BOLT_ACC_2022_2023, segment=1),
|
||||
CarTestRoute("555d4087cf86aa91|2022-12-02--12-15-07", GM.CHEVROLET_BOLT_ACC_2022_2023, segment=14), # Bolt EV
|
||||
CarTestRoute("38aa7da107d5d252|2022-08-15--16-01-12", GM.CHEVROLET_SILVERADO),
|
||||
CarTestRoute("5085c761395d1fe6|2023-04-07--18-20-06", GM.CHEVROLET_TRAILBLAZER),
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class TestCanFingerprint:
|
||||
|
||||
fingerprint_iter = iter([can])
|
||||
empty_can = messaging.new_message('can', 0)
|
||||
car_fingerprint, finger = can_fingerprint(lambda: next(fingerprint_iter, empty_can)) # noqa: B023
|
||||
car_fingerprint, finger, _ = can_fingerprint(lambda: next(fingerprint_iter, empty_can)) # noqa: B023
|
||||
|
||||
assert car_fingerprint == car_model
|
||||
assert finger[0] == fingerprint
|
||||
@@ -26,7 +26,7 @@ class TestCanFingerprint:
|
||||
|
||||
def test_timing(self, subtests):
|
||||
# just pick any CAN fingerprinting car
|
||||
car_model = "CHEVROLET_BOLT_EUV"
|
||||
car_model = "CHEVROLET_BOLT_ACC_2022_2023"
|
||||
fingerprint = FINGERPRINTS[car_model][0]
|
||||
|
||||
cases = []
|
||||
@@ -56,6 +56,6 @@ class TestCanFingerprint:
|
||||
frames += 1
|
||||
return can # noqa: B023
|
||||
|
||||
car_fingerprint, _ = can_fingerprint(test)
|
||||
car_fingerprint, _, _ = can_fingerprint(test)
|
||||
assert car_fingerprint == car_model
|
||||
assert frames == expected_frames + 2# TODO: fix extra frames
|
||||
|
||||
@@ -43,8 +43,12 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"CADILLAC_ESCALADE" = [1.899999976158142, 1.842270016670227, 0.1120000034570694]
|
||||
"CADILLAC_ESCALADE_ESV_2019" = [1.15, 1.3, 0.2]
|
||||
"CADILLAC_XT4" = [1.45, 1.6, 0.2]
|
||||
"CHEVROLET_BOLT_EUV" = [2.0, 2.0, 0.05]
|
||||
"CHEVROLET_MALIBU_CC" = [1.85, 1.85, 0.075]
|
||||
"CADILLAC_XT6" = [1.33, 1.9, 0.16]
|
||||
"CHEVROLET_BOLT_ACC_2022_2023" = [2.0, 2.0, 0.13]
|
||||
"CHEVROLET_BOLT_CC_2017" = [1.5, 2.0, 0.245]
|
||||
"CHEVROLET_BOLT_CC_2019_2021" = [2.0, 2.0, 0.13]
|
||||
"CHEVROLET_BLAZER" = [1.33, 1.33, 0.18]
|
||||
"CHEVROLET_MALIBU_CC" = [1.58, 1.8422651988094612, 0.205]
|
||||
"CHEVROLET_SILVERADO" = [1.9, 1.9, 0.112]
|
||||
"CHEVROLET_TRAILBLAZER" = [1.33, 1.9, 0.16]
|
||||
"CHEVROLET_TRAVERSE" = [1.33, 1.33, 0.18]
|
||||
|
||||
@@ -5,6 +5,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"AUDI_A3_MK3" = [1.5122414863077502, 1.7443517531719404, 0.15194151892450905]
|
||||
"AUDI_Q3_MK2" = [1.4439223359448605, 1.2254955789112076, 0.1413798895978097]
|
||||
"CHEVROLET_VOLT" = [1.5961527626411784, 1.8422651988094612, 0.1572393918005158]
|
||||
"CHEVROLET_MALIBU_HYBRID_CC" = [1.5961527626411784, 1.8422651988094612, 0.1572393918005158]
|
||||
"CHRYSLER_PACIFICA_2018" = [2.07140, 1.3366521181047952, 0.13776367250652022]
|
||||
"CHRYSLER_PACIFICA_2020" = [1.86206, 1.509076559398423, 0.14328246159386085]
|
||||
"CHRYSLER_PACIFICA_2017_HYBRID" = [1.79422, 1.06831764583744, 0.116237]
|
||||
|
||||
@@ -56,9 +56,16 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
|
||||
"CADILLAC_ESCALADE_ESV" = "CHEVROLET_VOLT"
|
||||
"CADILLAC_ATS" = "CHEVROLET_VOLT"
|
||||
"CHEVROLET_MALIBU" = "CHEVROLET_VOLT"
|
||||
"CHEVROLET_MALIBU_ASCM" = "CHEVROLET_VOLT"
|
||||
"CHEVROLET_MALIBU_SDGM" = "CHEVROLET_VOLT"
|
||||
"HOLDEN_ASTRA" = "CHEVROLET_VOLT"
|
||||
"CHEVROLET_VOLT_CC" = "CHEVROLET_VOLT"
|
||||
"CHEVROLET_BOLT_CC" = "CHEVROLET_BOLT_EUV"
|
||||
"CHEVROLET_VOLT_CAMERA" = "CHEVROLET_VOLT"
|
||||
"CHEVROLET_VOLT_ASCM" = "CHEVROLET_VOLT"
|
||||
"GMC_ACADIA_ASCM" = "GMC_ACADIA"
|
||||
"CHEVROLET_VOLT_2019" = "CHEVROLET_VOLT"
|
||||
"CHEVROLET_BOLT_ACC_2022_2023_PEDAL" = "CHEVROLET_BOLT_ACC_2022_2023"
|
||||
"CHEVROLET_BOLT_CC_2022_2023" = "CHEVROLET_BOLT_ACC_2022_2023"
|
||||
"CHEVROLET_EQUINOX_CC" = "CHEVROLET_EQUINOX"
|
||||
"CHEVROLET_SUBURBAN" = "CHEVROLET_SILVERADO"
|
||||
"CHEVROLET_SUBURBAN_CC" = "CHEVROLET_SILVERADO"
|
||||
|
||||
@@ -199,7 +199,7 @@ class Controls:
|
||||
|
||||
self.event_names_to_clear = set()
|
||||
|
||||
self.has_menu = self.CP.carName == "gm" and not (self.CP.flags & GMFlags.NO_CAMERA.value or self.CP.carFingerprint in CC_ONLY_CAR)
|
||||
self.has_menu = self.CP.carName == "gm" and not (self.CP.flags & GMFlags.NO_CAMERA.value)
|
||||
|
||||
self.frogpilot_AM = AlertManager()
|
||||
self.frogpilot_events = Events(frogpilot=True)
|
||||
@@ -623,9 +623,31 @@ class Controls:
|
||||
# Update Torque Params
|
||||
if self.CP.lateralTuning.which() == 'torque':
|
||||
torque_params = self.sm['liveTorqueParameters']
|
||||
if self.sm.all_checks(['liveTorqueParameters']) and (torque_params.useParams or self.frogpilot_toggles.force_auto_tune):
|
||||
self.LaC.update_live_torque_params(torque_params.latAccelFactorFiltered, torque_params.latAccelOffsetFiltered,
|
||||
torque_params.frictionCoefficientFiltered)
|
||||
|
||||
allow_lat_accel_learning = self.CP.carName in ['toyota', 'hyundai']
|
||||
allow_friction_learning = (allow_lat_accel_learning or self.CP.carName in ['gm'])
|
||||
use_live_params = self.sm.all_checks(['liveTorqueParameters']) and (torque_params.useParams or self.frogpilot_toggles.force_auto_tune)
|
||||
|
||||
# Defaults pulled from manual tuning values
|
||||
lat_accel_factor = self.params.get_float("SteerLatAccel")
|
||||
friction = self.params.get_float("SteerFriction")
|
||||
lat_accel_offset = self.CP.lateralTuning.torque.latAccelOffset
|
||||
|
||||
# Apply user overrides first
|
||||
if self.frogpilot_toggles.use_custom_latAccelFactor:
|
||||
lat_accel_factor = self.frogpilot_toggles.latAccelFactor
|
||||
if self.frogpilot_toggles.use_custom_friction:
|
||||
friction = self.frogpilot_toggles.friction
|
||||
|
||||
# Layer in live values only for parameters the platform allows to learn and only when not overridden
|
||||
if use_live_params:
|
||||
if allow_lat_accel_learning and not self.frogpilot_toggles.use_custom_latAccelFactor:
|
||||
lat_accel_factor = torque_params.latAccelFactorFiltered
|
||||
lat_accel_offset = torque_params.latAccelOffsetFiltered
|
||||
if allow_friction_learning and not self.frogpilot_toggles.use_custom_friction:
|
||||
friction = torque_params.frictionCoefficientFiltered
|
||||
|
||||
self.LaC.update_live_torque_params(lat_accel_factor, lat_accel_offset, friction)
|
||||
|
||||
if self.sm.updated['liveDelay'] and hasattr(self.LaC, "update_live_delay"):
|
||||
self.LaC.update_live_delay(self.sm['liveDelay'].lateralDelay)
|
||||
|
||||
@@ -147,8 +147,7 @@ class VCruiseHelper:
|
||||
# initializing is handled by the PCM
|
||||
if self.CP.pcmCruise:
|
||||
return
|
||||
|
||||
initial = V_CRUISE_INITIAL_EXPERIMENTAL_MODE if experimental_mode and not frogpilot_toggles.conditional_experimental_mode else V_CRUISE_INITIAL
|
||||
engage_floor_kph = max(V_CRUISE_MIN, 7.0 * CV.MPH_TO_KPH)
|
||||
|
||||
# 250kph or above probably means we never had a set speed
|
||||
if any(b.type in (ButtonType.accelCruise, ButtonType.resumeCruise) for b in CS.buttonEvents) and self.v_cruise_kph_last < 250:
|
||||
@@ -157,7 +156,7 @@ class VCruiseHelper:
|
||||
if desired_speed_limit != 0 and frogpilot_toggles.set_speed_limit:
|
||||
self.v_cruise_kph = int(round(desired_speed_limit * CV.MS_TO_KPH))
|
||||
else:
|
||||
self.v_cruise_kph = int(round(clip(CS.vEgo * CV.MS_TO_KPH, initial, V_CRUISE_MAX)))
|
||||
self.v_cruise_kph = int(round(clip(CS.vEgo * CV.MS_TO_KPH, engage_floor_kph, V_CRUISE_MAX)))
|
||||
|
||||
self.v_cruise_cluster_kph = self.v_cruise_kph
|
||||
|
||||
|
||||
@@ -357,8 +357,8 @@ def forcing_stop_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMas
|
||||
def holiday_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, frogpilot_toggles: SimpleNamespace) -> Alert:
|
||||
holiday_messages = {
|
||||
"new_years": "Happy New Year! 🎉",
|
||||
"valentines": "Happy Valentine's Day! ❤️",
|
||||
"st_patricks": "Happy St. Patrick's Day! 🍀",
|
||||
"valentines_day": "Happy Valentine's Day! ❤️",
|
||||
"st_patricks_day": "Happy St. Patrick's Day! 🍀",
|
||||
"world_frog_day": "Happy World Frog Day! 🐸",
|
||||
"april_fools": "Happy April Fool's Day! 🤡",
|
||||
"easter_week": "Happy Easter! 🐰",
|
||||
@@ -372,7 +372,7 @@ def holiday_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster,
|
||||
}
|
||||
|
||||
return Alert(
|
||||
holiday_messages.get(frogpilot_toggles.current_holiday_theme),
|
||||
holiday_messages.get(frogpilot_toggles.current_holiday_theme, "Happy Holidays!"),
|
||||
"",
|
||||
AlertStatus.normal, AlertSize.small,
|
||||
Priority.LOWEST, VisualAlert.none, FrogPilotAudibleAlert.startup, 5.)
|
||||
@@ -982,8 +982,8 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
|
||||
"",
|
||||
AlertStatus.normal, AlertSize.full,
|
||||
Priority.LOWEST, VisualAlert.none, AudibleAlert.none, .2, creation_delay=0.5),
|
||||
ET.USER_DISABLE: ImmediateDisableAlert("Reverse Gear"),
|
||||
ET.NO_ENTRY: NoEntryAlert("Reverse Gear"),
|
||||
ET.USER_DISABLE: ImmediateDisableAlert("Wrong Gear"),
|
||||
ET.NO_ENTRY: NoEntryAlert("Wrong Gear"),
|
||||
},
|
||||
|
||||
# On cars that use stock ACC the car can decide to cancel ACC for various reasons.
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import math
|
||||
|
||||
from cereal import log
|
||||
from openpilot.selfdrive.controls.lib.latcontrol import LatControl
|
||||
from openpilot.selfdrive.controls.lib.latcontrol import LatControl, MIN_LATERAL_CONTROL_SPEED
|
||||
from openpilot.selfdrive.controls.lib.pid import PIDController
|
||||
|
||||
|
||||
class LatControlPID(LatControl):
|
||||
def __init__(self, CP, CI, dt):
|
||||
super().__init__(CP, CI, dt)
|
||||
self.steer_release_i_decay = 0.8
|
||||
self.prev_steering_pressed = False
|
||||
self.pid = PIDController((CP.lateralTuning.pid.kpBP, CP.lateralTuning.pid.kpV),
|
||||
(CP.lateralTuning.pid.kiBP, CP.lateralTuning.pid.kiV),
|
||||
pos_limit=self.steer_max, neg_limit=-self.steer_max)
|
||||
self.ff_factor = CP.lateralTuning.pid.kf
|
||||
self.get_steer_feedforward = CI.get_steer_feedforward_function()
|
||||
self.low_speed_reset_threshold = max(CP.minSteerSpeed, MIN_LATERAL_CONTROL_SPEED)
|
||||
|
||||
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay, llk, model_data, frogpilot_toggles):
|
||||
pid_log = log.ControlsState.LateralPIDState.new_message()
|
||||
@@ -28,11 +31,17 @@ class LatControlPID(LatControl):
|
||||
if not active:
|
||||
output_torque = 0.0
|
||||
pid_log.active = False
|
||||
self.pid.reset()
|
||||
|
||||
else:
|
||||
if self.prev_steering_pressed and not CS.steeringPressed:
|
||||
self.pid.i *= self.steer_release_i_decay
|
||||
|
||||
# offset does not contribute to resistive torque
|
||||
ff = self.ff_factor * self.get_steer_feedforward(angle_steers_des_no_offset, CS.vEgo)
|
||||
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5
|
||||
if CS.vEgo < self.low_speed_reset_threshold:
|
||||
self.pid.reset()
|
||||
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < self.low_speed_reset_threshold
|
||||
|
||||
output_torque = self.pid.update(error,
|
||||
feedforward=ff,
|
||||
@@ -46,4 +55,6 @@ class LatControlPID(LatControl):
|
||||
pid_log.output = float(output_torque)
|
||||
pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited))
|
||||
|
||||
self.prev_steering_pressed = CS.steeringPressed
|
||||
|
||||
return output_torque, angle_steers_des, pid_log
|
||||
|
||||
@@ -3,10 +3,11 @@ import numpy as np
|
||||
from collections import deque
|
||||
|
||||
from cereal import log
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.selfdrive.car.interfaces import FRICTION_THRESHOLD
|
||||
from openpilot.selfdrive.car.interfaces import FRICTION_THRESHOLD, get_friction_threshold
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import MIN_SPEED, get_friction
|
||||
from openpilot.selfdrive.controls.lib.latcontrol import LatControl
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.selfdrive.controls.lib.latcontrol import LatControl, MIN_LATERAL_CONTROL_SPEED
|
||||
from openpilot.selfdrive.car.gm.values import CAR as GM_CAR
|
||||
from openpilot.selfdrive.controls.lib.pid import PIDController
|
||||
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
|
||||
|
||||
@@ -16,29 +17,85 @@ from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_G
|
||||
# wheel slip, or to speed.
|
||||
|
||||
# This controller applies torque to achieve desired lateral
|
||||
# accelerations. To compensate for the low speed effects we
|
||||
# use a LOW_SPEED_FACTOR in the error. Additionally, there is
|
||||
# friction in the steering wheel that needs to be overcome to
|
||||
# move it at all, this is compensated for too.
|
||||
# accelerations. To compensate for the low speed effects the
|
||||
# proportional gain is increased at low speeds by the PID controller.
|
||||
# Additionally, there is friction in the steering wheel that needs
|
||||
# to be overcome to move it at all, this is compensated for too.
|
||||
|
||||
KP = 0.7
|
||||
KI = 0.35
|
||||
|
||||
INTERP_SPEEDS = [1, 1.5, 2.0, 3.0, 5, 7.5, 10, 15, 30]
|
||||
KP_INTERP = [250, 120, 65, 30, 11.5, 5.5, 3.5, 2.0, KP]
|
||||
|
||||
LOW_SPEED_X = [0, 10, 20, 30]
|
||||
LOW_SPEED_Y = [15, 13, 10, 5]
|
||||
|
||||
LOW_SPEED_Y = [12, 10.5, 8, 5]
|
||||
MAX_LAT_JERK_UP = 2.5 # m/s^3
|
||||
|
||||
LP_FILTER_CUTOFF_HZ = 1.2
|
||||
JERK_LOOKAHEAD_SECONDS = 0.19
|
||||
JERK_GAIN = 0.22
|
||||
LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0
|
||||
VERSION = 2
|
||||
DEBUG_TORQUE_TUNE = False
|
||||
FF_SCALE_BLEND_LAT_ACCEL = 0.05
|
||||
DEADZONE_BOOST_LAT_ACCEL = 0.08
|
||||
UNWIND_D_DES_THRESHOLD = -1.0
|
||||
UNWIND_LAT_ACCEL_NEAR_ZERO = 0.3
|
||||
|
||||
BOLT_2022_2023_CARS = (
|
||||
GM_CAR.CHEVROLET_BOLT_ACC_2022_2023,
|
||||
GM_CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
GM_CAR.CHEVROLET_BOLT_CC_2022_2023,
|
||||
)
|
||||
BOLT_2019_2021_CARS = (
|
||||
GM_CAR.CHEVROLET_BOLT_CC_2019_2021,
|
||||
)
|
||||
BOLT_2017_CARS = (
|
||||
GM_CAR.CHEVROLET_BOLT_CC_2017,
|
||||
)
|
||||
BOLT_CARS = BOLT_2022_2023_CARS + BOLT_2019_2021_CARS + BOLT_2017_CARS
|
||||
|
||||
class LatControlTorque(LatControl):
|
||||
def __init__(self, CP, CI, dt):
|
||||
super().__init__(CP, CI, dt)
|
||||
self.steer_release_i_decay = 0.8
|
||||
self.prev_steering_pressed = False
|
||||
self.torque_params = CP.lateralTuning.torque
|
||||
self.torque_from_lateral_accel = CI.torque_from_lateral_accel()
|
||||
self.lateral_accel_from_torque = CI.lateral_accel_from_torque()
|
||||
self.pid = PIDController(self.torque_params.kp, self.torque_params.ki, rate=1/self.dt)
|
||||
self.pid = PIDController([INTERP_SPEEDS, KP_INTERP], KI, rate=1/self.dt)
|
||||
self.update_limits()
|
||||
self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg
|
||||
self.LATACCEL_REQUEST_BUFFER_NUM_FRAMES = int(1 / self.dt)
|
||||
self.requested_lateral_accel_buffer = deque([0.] * self.LATACCEL_REQUEST_BUFFER_NUM_FRAMES , maxlen=self.LATACCEL_REQUEST_BUFFER_NUM_FRAMES)
|
||||
self.lat_accel_request_buffer_len = int(LAT_ACCEL_REQUEST_BUFFER_SECONDS / self.dt)
|
||||
self.lat_accel_request_buffer = deque([0.] * self.lat_accel_request_buffer_len , maxlen=self.lat_accel_request_buffer_len)
|
||||
self.lookahead_frames = int(JERK_LOOKAHEAD_SECONDS / self.dt)
|
||||
self.jerk_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * LP_FILTER_CUTOFF_HZ), self.dt)
|
||||
self.previous_measurement = 0.0
|
||||
self.measurement_rate_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * (MAX_LAT_JERK_UP - 0.5)), self.dt)
|
||||
self.low_speed_reset_threshold = max(CP.minSteerSpeed, MIN_LATERAL_CONTROL_SPEED)
|
||||
self.debug_counter = 0
|
||||
self.prev_desired_lateral_accel = 0.0
|
||||
|
||||
self.is_bolt = CP.carFingerprint in BOLT_CARS
|
||||
self.is_bolt_2022_2023 = CP.carFingerprint in BOLT_2022_2023_CARS
|
||||
self.is_bolt_2019_2021 = CP.carFingerprint in BOLT_2019_2021_CARS
|
||||
self.is_bolt_2017 = CP.carFingerprint in BOLT_2017_CARS
|
||||
# Keep Bolt-specific FF controls isolated by generation.
|
||||
self.use_bolt_ff_scaling = self.is_bolt_2022_2023 or self.is_bolt_2019_2021
|
||||
self.use_bolt_deadzone_boost = self.is_bolt_2022_2023 or self.is_bolt_2019_2021
|
||||
self.use_bolt_ki_multiplier = self.is_bolt_2022_2023 or self.is_bolt_2019_2021
|
||||
self.torque_ff_scale_pos = 1.0
|
||||
self.torque_ff_scale_neg = 1.0
|
||||
self.torque_deadzone_boost_neg = 0.0
|
||||
self.torque_ki_mult = 1.0
|
||||
if self.is_bolt:
|
||||
self.torque_ff_scale_pos = float(self.torque_params.kp)
|
||||
self.torque_ff_scale_neg = float(self.torque_params.ki)
|
||||
self.torque_ki_mult = float(self.torque_params.kd)
|
||||
self.torque_deadzone_boost_neg = float(getattr(self.torque_params, "kfDEPRECATED", 0.0))
|
||||
if self.use_bolt_ki_multiplier and self.torque_ki_mult > 0.0 and self.torque_ki_mult != 1.0:
|
||||
self.pid._k_i = [self.pid._k_i[0], [k * self.torque_ki_mult for k in self.pid._k_i[1]]]
|
||||
|
||||
def update_live_torque_params(self, latAccelFactor, latAccelOffset, friction):
|
||||
self.torque_params.latAccelFactor = latAccelFactor
|
||||
@@ -52,46 +109,71 @@ class LatControlTorque(LatControl):
|
||||
|
||||
def update(self, active, CS, VM, params, steer_limited_by_safety, desired_curvature, curvature_limited, lat_delay, llk, model_data, frogpilot_toggles):
|
||||
pid_log = log.ControlsState.LateralTorqueState.new_message()
|
||||
pid_log.version = VERSION
|
||||
if not active:
|
||||
output_torque = 0.0
|
||||
pid_log.active = False
|
||||
self.pid.reset()
|
||||
self.previous_measurement = 0.0
|
||||
self.measurement_rate_filter.x = 0.0
|
||||
self.lat_accel_request_buffer = deque([0.] * self.lat_accel_request_buffer_len , maxlen=self.lat_accel_request_buffer_len)
|
||||
self.prev_desired_lateral_accel = 0.0
|
||||
else:
|
||||
if self.prev_steering_pressed and not CS.steeringPressed:
|
||||
self.pid.i *= self.steer_release_i_decay
|
||||
|
||||
measured_curvature = -VM.calc_curvature(math.radians(CS.steeringAngleDeg - params.angleOffsetDeg), CS.vEgo, params.roll)
|
||||
roll_compensation = params.roll * ACCELERATION_DUE_TO_GRAVITY
|
||||
curvature_deadzone = abs(VM.calc_curvature(math.radians(self.steering_angle_deadzone_deg), CS.vEgo, 0.0))
|
||||
lateral_accel_deadzone = curvature_deadzone * CS.vEgo ** 2
|
||||
|
||||
delay_frames = int(np.clip(lat_delay / self.dt, 1, self.LATACCEL_REQUEST_BUFFER_NUM_FRAMES))
|
||||
expected_lateral_accel = self.requested_lateral_accel_buffer[-delay_frames]
|
||||
# TODO factor out lateral jerk from error to later replace it with delay independent alternative
|
||||
delay_frames = int(np.clip(lat_delay / self.dt, 1, self.lat_accel_request_buffer_len))
|
||||
expected_lateral_accel = self.lat_accel_request_buffer[-delay_frames]
|
||||
future_desired_lateral_accel = desired_curvature * CS.vEgo ** 2
|
||||
self.requested_lateral_accel_buffer.append(future_desired_lateral_accel)
|
||||
self.lat_accel_request_buffer.append(future_desired_lateral_accel)
|
||||
raw_lateral_jerk = (future_desired_lateral_accel - expected_lateral_accel) / max(lat_delay, self.dt)
|
||||
raw_lateral_jerk = np.clip(raw_lateral_jerk, -MAX_LAT_JERK_UP, MAX_LAT_JERK_UP)
|
||||
desired_lateral_jerk = np.clip(self.jerk_filter.update(raw_lateral_jerk), -MAX_LAT_JERK_UP, MAX_LAT_JERK_UP)
|
||||
gravity_adjusted_future_lateral_accel = future_desired_lateral_accel - roll_compensation
|
||||
desired_lateral_jerk = (future_desired_lateral_accel - expected_lateral_accel) / lat_delay
|
||||
setpoint = expected_lateral_accel + desired_lateral_jerk * lat_delay
|
||||
desired_lateral_accel_rate = (setpoint - self.prev_desired_lateral_accel) / self.dt
|
||||
unwind_detected = (desired_lateral_accel_rate < UNWIND_D_DES_THRESHOLD and
|
||||
abs(setpoint) < UNWIND_LAT_ACCEL_NEAR_ZERO)
|
||||
self.prev_desired_lateral_accel = setpoint
|
||||
|
||||
measurement = measured_curvature * CS.vEgo ** 2
|
||||
measurement_rate = self.measurement_rate_filter.update((measurement - self.previous_measurement) / self.dt)
|
||||
measurement_rate = np.clip(measurement_rate, -MAX_LAT_JERK_UP, MAX_LAT_JERK_UP)
|
||||
self.previous_measurement = measurement
|
||||
|
||||
low_speed_factor = (np.interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y) / max(CS.vEgo, MIN_SPEED)) ** 2
|
||||
setpoint = lat_delay * desired_lateral_jerk + expected_lateral_accel
|
||||
current_kp = np.interp(CS.vEgo, self.pid._k_p[0], self.pid._k_p[1])
|
||||
error = setpoint - measurement
|
||||
error_lsf = error + low_speed_factor / self.torque_params.kp * error
|
||||
error_with_lsf = error * (1 + low_speed_factor / max(current_kp, 1e-3))
|
||||
|
||||
# do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly
|
||||
pid_log.error = float(error_lsf)
|
||||
pid_log.error = float(error_with_lsf)
|
||||
ff = gravity_adjusted_future_lateral_accel
|
||||
# latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll
|
||||
ff -= self.torque_params.latAccelOffset
|
||||
# TODO jerk is weighted by lat_delay for legacy reasons, but should be made independent of it
|
||||
ff += get_friction(error, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params)
|
||||
ff_scale = 1.0
|
||||
if self.use_bolt_ff_scaling:
|
||||
ff_scale = np.interp(ff, [-FF_SCALE_BLEND_LAT_ACCEL, 0.0, FF_SCALE_BLEND_LAT_ACCEL],
|
||||
[self.torque_ff_scale_neg, 1.0, self.torque_ff_scale_pos])
|
||||
ff *= ff_scale
|
||||
ff += get_friction(error_with_lsf + JERK_GAIN * desired_lateral_jerk, lateral_accel_deadzone, get_friction_threshold(CS.vEgo), self.torque_params)
|
||||
deadzone_boost_active = False
|
||||
if self.use_bolt_deadzone_boost and self.torque_deadzone_boost_neg > 0.0 and gravity_adjusted_future_lateral_accel < 0.0:
|
||||
if abs(gravity_adjusted_future_lateral_accel) < DEADZONE_BOOST_LAT_ACCEL:
|
||||
boost_scale = np.interp(abs(gravity_adjusted_future_lateral_accel), [0.0, DEADZONE_BOOST_LAT_ACCEL], [1.0, 0.0])
|
||||
ff -= self.torque_deadzone_boost_neg * boost_scale
|
||||
deadzone_boost_active = True
|
||||
|
||||
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5
|
||||
output_lataccel = self.pid.update(pid_log.error,
|
||||
-measurement_rate,
|
||||
feedforward=ff,
|
||||
speed=CS.vEgo,
|
||||
freeze_integrator=freeze_integrator)
|
||||
if CS.vEgo < self.low_speed_reset_threshold:
|
||||
self.pid.reset()
|
||||
freeze_integrator = (steer_limited_by_safety or CS.steeringPressed or
|
||||
CS.vEgo < self.low_speed_reset_threshold or unwind_detected)
|
||||
output_lataccel = self.pid.update(pid_log.error, error_rate=-measurement_rate, speed=CS.vEgo, feedforward=ff, freeze_integrator=freeze_integrator)
|
||||
output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params)
|
||||
|
||||
pid_log.active = True
|
||||
@@ -102,7 +184,16 @@ class LatControlTorque(LatControl):
|
||||
pid_log.output = float(-output_torque) # TODO: log lat accel?
|
||||
pid_log.actualLateralAccel = float(measurement)
|
||||
pid_log.desiredLateralAccel = float(setpoint)
|
||||
pid_log.desiredLateralJerk = float(desired_lateral_jerk)
|
||||
pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited))
|
||||
|
||||
if DEBUG_TORQUE_TUNE and self.is_bolt:
|
||||
self.debug_counter += 1
|
||||
if self.debug_counter % 50 == 0:
|
||||
print(f"bolt_torque ff_scale={ff_scale:.3f} pos={self.torque_ff_scale_pos:.3f} "
|
||||
f"neg={self.torque_ff_scale_neg:.3f} deadzone_boost_active={deadzone_boost_active}")
|
||||
|
||||
self.prev_steering_pressed = CS.steeringPressed
|
||||
|
||||
# TODO left is positive in this convention
|
||||
return -output_torque, 0.0, pid_log
|
||||
return -output_torque, 0.0, pid_log
|
||||
|
||||
@@ -179,6 +179,26 @@ class LongControl:
|
||||
else:
|
||||
output_accel = raw_output_accel
|
||||
|
||||
if self.long_control_state == LongCtrlState.pid:
|
||||
# Smooth acceleration and deceleration with urgency-based rate limiting
|
||||
base_rate = 1.0
|
||||
|
||||
if output_accel < self.last_output_accel: # Deceleration requested
|
||||
decel_needed = self.last_output_accel - output_accel
|
||||
# Use a safe default for ACCEL_MIN if not available, to prevent division by zero
|
||||
max_decel = abs(CarControllerParams.ACCEL_MIN) if CarControllerParams.ACCEL_MIN != 0 else 4.0
|
||||
urgency = min(1.0, decel_needed / max_decel)
|
||||
|
||||
# Adjust rate based on urgency (1.0 m/s^3 for low urgency, up to 4.0 m/s^3 for high urgency)
|
||||
max_rate = 1.0 + 3.0 * urgency
|
||||
else:
|
||||
max_rate = base_rate # Acceleration is always smooth
|
||||
|
||||
max_accel_change = max_rate * DT_CTRL
|
||||
output_accel = clip(output_accel,
|
||||
self.last_output_accel - max_accel_change,
|
||||
self.last_output_accel + max_accel_change)
|
||||
|
||||
self.last_output_accel = clip(output_accel, accel_limits[0], accel_limits[1])
|
||||
return self.last_output_accel
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import numpy as np
|
||||
from cereal import log
|
||||
from openpilot.common.numpy_fast import clip, interp
|
||||
from openpilot.common.realtime import DT_MDL
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.common.conversions import Conversions as CV
|
||||
# WARNING: imports outside of constants will not trigger a rebuild
|
||||
@@ -422,9 +421,6 @@ class LongitudinalMpc:
|
||||
scale = float(np.interp(uncertainty, [0.45, 0.60], [1.2, 1.5]))
|
||||
speed_jerk *= scale
|
||||
|
||||
if abs(filter_time_factor - prev_filter_time_factor) > 1e-3:
|
||||
cloudlog.error(f"LON_FILTER; filter_time_factor={filter_time_factor:.2f}; uncertainty={uncertainty:.3f}; v_ego={v_ego:.2f} mps; lead_dist={lead_dist:.2f} m; accel_reengage={accel_reengage}")
|
||||
|
||||
if self.mode == 'acc':
|
||||
a_change_cost = acceleration_jerk if prev_accel_constraint else 0
|
||||
cost_weights = [self.current_x_ego_cost, X_EGO_COST, V_EGO_COST, A_EGO_COST, a_change_cost, speed_jerk]
|
||||
@@ -627,11 +623,7 @@ class LongitudinalMpc:
|
||||
|
||||
self.prev_a = np.interp(T_IDXS + self.dt, T_IDXS, self.a_solution)
|
||||
|
||||
t = time.monotonic()
|
||||
if self.solution_status != 0:
|
||||
if t > self.last_cloudlog_t + 5.0:
|
||||
self.last_cloudlog_t = t
|
||||
cloudlog.warning(f"Long mpc reset, solution_status: {self.solution_status}")
|
||||
self.reset()
|
||||
# reset = 1
|
||||
# print(f"long_mpc timings: total internal {self.solve_time:.2e}, external: {(time.monotonic() - t0):.2e} qp {self.time_qp_solution:.2e}, \
|
||||
|
||||
@@ -111,9 +111,6 @@ class LongitudinalPlanner:
|
||||
self.a_desired_trajectory = np.zeros(CONTROL_N)
|
||||
self.j_desired_trajectory = np.zeros(CONTROL_N)
|
||||
self.solverExecutionTime = 0.0
|
||||
# logging cadence & state
|
||||
self.last_uncert_log_t = 0.0
|
||||
self.prev_uncert_over = False
|
||||
|
||||
# ---- Rubberband mitigation state ----
|
||||
# Two uncertainty tracks (slow/fast) for asymmetric gating
|
||||
@@ -138,7 +135,7 @@ class LongitudinalPlanner:
|
||||
|
||||
@property
|
||||
def mlsim(self):
|
||||
return self.generation in ("v8", "v10", "v11")
|
||||
return self.generation in ("v8", "v10", "v11", "v12")
|
||||
|
||||
def get_mpc_mode(self) -> str:
|
||||
"""
|
||||
@@ -344,19 +341,6 @@ class LongitudinalPlanner:
|
||||
|
||||
# now_t defined earlier
|
||||
over = uncertainty > 1.0
|
||||
# Log on threshold edge or at ~1 Hz
|
||||
if over != self.prev_uncert_over or (now_t - self.last_uncert_log_t) > 1.0:
|
||||
try:
|
||||
cloudlog.error(
|
||||
f"LON_UNCERT; v_ego={v_ego:.2f} mps; desireEntropy={desire_entropy:.3f}; "
|
||||
f"brakeRawMax={(raw_brake_max if 'raw_brake_max' in locals() else -1.0):.3f}; "
|
||||
f"brakeDecayed={(disengage_risk if 'disengage_risk' in locals() else -1.0):.3f}; "
|
||||
f"lam={(lam if 'lam' in locals() else -1.0):.2f}; uncertainty={uncertainty:.3f}; over={over}"
|
||||
)
|
||||
except Exception as e:
|
||||
cloudlog.warning(f"LON_UNCERT log error: {e}")
|
||||
self.prev_uncert_over = over
|
||||
self.last_uncert_log_t = now_t
|
||||
|
||||
# Asymmetric accel release with hysteresis + dwell to prevent on/off pulsing
|
||||
rise_dwell_s, fall_dwell_s = 0.6, 0.4
|
||||
@@ -364,7 +348,8 @@ class LongitudinalPlanner:
|
||||
(self.a_desired > 0.0) and
|
||||
self.stable_lead and
|
||||
(uncertainty <= 0.425) and
|
||||
(desire_entropy < 0.41)
|
||||
(desire_entropy < 0.41) and
|
||||
(v_ego > 5.0)
|
||||
)
|
||||
|
||||
# dwell timers for robust gating
|
||||
@@ -443,7 +428,8 @@ class LongitudinalPlanner:
|
||||
self.a_desired = float(self.a_desired - pre_brake)
|
||||
|
||||
# Apply tiny feed-forward nudge when released and safe
|
||||
if now_t < self.accel_nudge_until and self.a_desired > -0.1:
|
||||
close_lead = self.lead_one.status and self.lead_one.dRel < 10.0
|
||||
if now_t < self.accel_nudge_until and self.a_desired > -0.1 and not close_lead:
|
||||
self.a_desired = float(min(self.a_desired + 0.12, get_max_accel(v_ego)))
|
||||
|
||||
# Small deadzone around zero accel to kill micro-dithers
|
||||
|
||||
@@ -135,7 +135,7 @@ def main():
|
||||
CP = msg
|
||||
cloudlog.info("paramsd got CarParams")
|
||||
|
||||
min_sr, max_sr = 0.5 * CP.steerRatio, 2.0 * CP.steerRatio
|
||||
min_sr, max_sr = 0.25 * CP.steerRatio, 2.0 * CP.steerRatio
|
||||
|
||||
params = params_reader.get("LiveParameters")
|
||||
|
||||
|
||||
@@ -34,7 +34,8 @@ MIN_BUCKET_POINTS = np.array([100, 300, 500, 500, 500, 500, 300, 100])
|
||||
MIN_ENGAGE_BUFFER = 2 # secs
|
||||
|
||||
VERSION = 1 # bump this to invalidate old parameter caches
|
||||
ALLOWED_CARS = ['toyota', 'hyundai']
|
||||
FULL_AUTO_CARS = ['toyota', 'hyundai']
|
||||
FRICTION_ONLY_CARS = ['gm']
|
||||
|
||||
|
||||
def slope2rot(slope):
|
||||
@@ -72,7 +73,10 @@ class TorqueEstimator(ParameterEstimator):
|
||||
self.offline_friction = 0.0
|
||||
self.offline_latAccelFactor = 0.0
|
||||
self.resets = 0.0
|
||||
self.use_params = CP.carName in ALLOWED_CARS and CP.lateralTuning.which() == 'torque'
|
||||
self.allow_lat_accel_learning = CP.carName in FULL_AUTO_CARS and CP.lateralTuning.which() == 'torque'
|
||||
self.allow_friction_learning = (self.allow_lat_accel_learning or CP.carName in FRICTION_ONLY_CARS) \
|
||||
and CP.lateralTuning.which() == 'torque'
|
||||
self.use_params = self.allow_friction_learning
|
||||
|
||||
if CP.lateralTuning.which() == 'torque':
|
||||
self.offline_friction = CP.lateralTuning.torque.friction
|
||||
@@ -104,11 +108,11 @@ class TorqueEstimator(ParameterEstimator):
|
||||
cache_CP = msg
|
||||
if self.get_restore_key(cache_CP, cache_ltp.version) == self.get_restore_key(CP, VERSION):
|
||||
if cache_ltp.liveValid:
|
||||
initial_params = {
|
||||
'latAccelFactor': cache_ltp.latAccelFactorFiltered,
|
||||
'latAccelOffset': cache_ltp.latAccelOffsetFiltered,
|
||||
'frictionCoefficient': cache_ltp.frictionCoefficientFiltered
|
||||
}
|
||||
if self.allow_lat_accel_learning:
|
||||
initial_params['latAccelFactor'] = cache_ltp.latAccelFactorFiltered
|
||||
initial_params['latAccelOffset'] = cache_ltp.latAccelOffsetFiltered
|
||||
if self.allow_friction_learning:
|
||||
initial_params['frictionCoefficient'] = cache_ltp.frictionCoefficientFiltered
|
||||
initial_params['points'] = cache_ltp.points
|
||||
self.decay = cache_ltp.decay
|
||||
self.filtered_points.load_points(initial_params['points'])
|
||||
@@ -155,6 +159,10 @@ class TorqueEstimator(ParameterEstimator):
|
||||
def update_params(self, params):
|
||||
self.decay = min(self.decay + DT_MDL, MAX_FILTER_DECAY)
|
||||
for param, value in params.items():
|
||||
if param.startswith('latAccel') and not self.allow_lat_accel_learning:
|
||||
continue
|
||||
if param == 'frictionCoefficient' and not self.allow_friction_learning:
|
||||
continue
|
||||
self.filtered_params[param].update(value)
|
||||
self.filtered_params[param].update_alpha(self.decay)
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ class DRIVER_MONITOR_SETTINGS:
|
||||
self._SG_THRESHOLD = 0.9
|
||||
self._BLINK_THRESHOLD = 0.865
|
||||
|
||||
self._EE_THRESH11 = 0.4
|
||||
self._EE_THRESH11 = 0.6
|
||||
self._EE_THRESH12 = 15.0
|
||||
self._EE_MAX_OFFSET1 = 0.06
|
||||
self._EE_MIN_OFFSET1 = 0.025
|
||||
@@ -44,9 +44,9 @@ class DRIVER_MONITOR_SETTINGS:
|
||||
self._POSE_YAW_THRESHOLD = 0.4020
|
||||
self._POSE_YAW_THRESHOLD_SLACK = 0.5042
|
||||
self._POSE_YAW_THRESHOLD_STRICT = self._POSE_YAW_THRESHOLD
|
||||
self._PITCH_NATURAL_OFFSET = 0.029 # initial value before offset is learned
|
||||
self._PITCH_NATURAL_OFFSET = 0.011 # initial value before offset is learned
|
||||
self._PITCH_NATURAL_THRESHOLD = 0.449
|
||||
self._YAW_NATURAL_OFFSET = 0.097 # initial value before offset is learned
|
||||
self._YAW_NATURAL_OFFSET = 0.075 # initial value before offset is learned
|
||||
self._PITCH_MAX_OFFSET = 0.124
|
||||
self._PITCH_MIN_OFFSET = -0.0881
|
||||
self._YAW_MAX_OFFSET = 0.289
|
||||
|
||||
@@ -118,7 +118,7 @@ std::optional<std::string> Panda::get_serial() {
|
||||
|
||||
bool Panda::up_to_date() {
|
||||
if (auto fw_sig = get_firmware_version()) {
|
||||
for (auto fn : { "panda.bin.signed", "panda_h7.bin.signed" }) {
|
||||
for (auto fn : { "panda.bin.signed", "panda_h7.bin.signed", "panda_remote.bin.signed", "panda_h7_remote.bin.signed" }) {
|
||||
auto content = util::read_file(std::string("../../panda/board/obj/") + fn);
|
||||
if (content.size() >= fw_sig->size() &&
|
||||
memcmp(content.data() + content.size() - fw_sig->size(), fw_sig->data(), fw_sig->size()) == 0) {
|
||||
|
||||
@@ -13,15 +13,25 @@ from openpilot.system.hardware import HARDWARE
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
|
||||
def get_expected_signature(panda: Panda) -> bytes:
|
||||
def get_expected_firmware_path(panda: Panda, remote_start: bool) -> str:
|
||||
app_fn = panda.get_mcu_type().config.app_fn
|
||||
if remote_start:
|
||||
remote_fn = "panda_h7_remote.bin.signed" if app_fn == "panda_h7.bin.signed" else "panda_remote.bin.signed"
|
||||
remote_path = os.path.join(FW_PATH, remote_fn)
|
||||
if os.path.isfile(remote_path):
|
||||
return remote_path
|
||||
cloudlog.warning(f"Remote-start panda firmware not found: {remote_path}, falling back to default")
|
||||
return os.path.join(FW_PATH, app_fn)
|
||||
|
||||
def get_expected_signature(panda: Panda, remote_start: bool) -> bytes:
|
||||
try:
|
||||
fn = os.path.join(FW_PATH, panda.get_mcu_type().config.app_fn)
|
||||
fn = get_expected_firmware_path(panda, remote_start)
|
||||
return Panda.get_signature_from_firmware(fn)
|
||||
except Exception:
|
||||
cloudlog.exception("Error computing expected signature")
|
||||
return b""
|
||||
|
||||
def flash_panda(panda_serial: str) -> Panda:
|
||||
def flash_panda(panda_serial: str, remote_start: bool) -> Panda:
|
||||
try:
|
||||
panda = Panda(panda_serial)
|
||||
except PandaProtocolMismatch:
|
||||
@@ -29,7 +39,8 @@ def flash_panda(panda_serial: str) -> Panda:
|
||||
HARDWARE.recover_internal_panda()
|
||||
raise
|
||||
|
||||
fw_signature = get_expected_signature(panda)
|
||||
fw_path = get_expected_firmware_path(panda, remote_start)
|
||||
fw_signature = get_expected_signature(panda, remote_start)
|
||||
internal_panda = panda.is_internal()
|
||||
|
||||
panda_version = "bootstub" if panda.bootstub else panda.get_version()
|
||||
@@ -38,7 +49,7 @@ def flash_panda(panda_serial: str) -> Panda:
|
||||
|
||||
if panda.bootstub or panda_signature != fw_signature:
|
||||
cloudlog.info("Panda firmware out of date, update required")
|
||||
panda.flash()
|
||||
panda.flash(fn=fw_path)
|
||||
cloudlog.info("Done flashing")
|
||||
|
||||
if panda.bootstub:
|
||||
@@ -105,8 +116,9 @@ def main() -> NoReturn:
|
||||
|
||||
# Flash pandas
|
||||
pandas: list[Panda] = []
|
||||
remote_start = params.get_bool("RemoteStartBootsComma")
|
||||
for serial in panda_serials:
|
||||
pandas.append(flash_panda(serial))
|
||||
pandas.append(flash_panda(serial, remote_start))
|
||||
|
||||
# Ensure internal panda is present if expected
|
||||
internal_pandas = [panda for panda in pandas if panda.is_internal()]
|
||||
|
||||
@@ -30,7 +30,7 @@ source_segments = [
|
||||
("RAM", "17fc16d840fe9d21|2023-04-26--13-28-44--5"), # CHRYSLER.RAM_1500_5TH_GEN
|
||||
("SUBARU", "341dccd5359e3c97|2022-09-12--10-35-33--3"), # SUBARU.SUBARU_OUTBACK
|
||||
("GM", "0c58b6a25109da2b|2021-02-23--16-35-50--11"), # GM.CHEVROLET_VOLT
|
||||
("GM2", "376bf99325883932|2022-10-27--13-41-22--1"), # GM.CHEVROLET_BOLT_EUV
|
||||
("GM2", "376bf99325883932|2022-10-27--13-41-22--1"), # GM.CHEVROLET_BOLT_ACC_2022_2023
|
||||
("NISSAN", "35336926920f3571|2021-02-12--18-38-48--46"), # NISSAN.NISSAN_XTRAIL
|
||||
("VOLKSWAGEN", "de9592456ad7d144|2021-06-29--11-00-15--6"), # VOLKSWAGEN.VOLKSWAGEN_GOLF
|
||||
("MAZDA", "bd6a637565e91581|2021-10-30--15-14-53--4"), # MAZDA.MAZDA_CX9_2021
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QMouseEvent>
|
||||
#include <QSet>
|
||||
#include <QStackedWidget>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
@@ -172,6 +173,22 @@ OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) {
|
||||
|
||||
main_layout->addLayout(header_layout);
|
||||
|
||||
branch_merge_banner = new QLabel(this);
|
||||
branch_merge_banner->setAlignment(Qt::AlignCenter);
|
||||
branch_merge_banner->setWordWrap(true);
|
||||
branch_merge_banner->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
branch_merge_banner->setVisible(false);
|
||||
branch_merge_banner->setStyleSheet(R"(
|
||||
background-color: #E22C2C;
|
||||
color: white;
|
||||
border-radius: 16px;
|
||||
padding: 24px;
|
||||
font-size: 44px;
|
||||
font-weight: 700;
|
||||
)");
|
||||
branch_merge_banner->setText(tr("Branch Merge Notice: This branch is deprecated and has been merged into StarPilot. Please switch to the StarPilot branch."));
|
||||
main_layout->addWidget(branch_merge_banner);
|
||||
|
||||
// main content
|
||||
main_layout->addSpacing(25);
|
||||
center_layout = new QStackedLayout();
|
||||
@@ -268,10 +285,21 @@ void OffroadHome::hideEvent(QHideEvent *event) {
|
||||
}
|
||||
|
||||
void OffroadHome::refresh() {
|
||||
static const QSet<QString> deprecated_branches = {
|
||||
"TorqueTune",
|
||||
"TorquePedal",
|
||||
"Kaofui",
|
||||
"Red-Kao",
|
||||
"TotallyTune",
|
||||
"StarPilot-2017",
|
||||
"TRX",
|
||||
};
|
||||
|
||||
date->setText(QLocale(uiState()->language.mid(5)).toString(QDateTime::currentDateTime(), "dddd, MMMM d"));
|
||||
date->setVisible(util::system_time_valid());
|
||||
|
||||
version->setText(getBrand() + " v" + getVersion().left(14).trimmed() + " - " + processModelName(frogpilotUIState()->frogpilot_toggles.value("model_name").toString()));
|
||||
branch_merge_banner->setVisible(deprecated_branches.contains(QString::fromStdString(params.get("GitBranch"))));
|
||||
|
||||
bool updateAvailable = update_widget->refresh();
|
||||
int alerts = alerts_widget->refresh();
|
||||
|
||||
@@ -41,6 +41,7 @@ private:
|
||||
OffroadAlert* alerts_widget;
|
||||
QPushButton* alert_notif;
|
||||
QPushButton* update_notif;
|
||||
QLabel* branch_merge_banner;
|
||||
|
||||
// FrogPilot variables
|
||||
ElidedLabel* date;
|
||||
|
||||
@@ -92,6 +92,99 @@ def manager_init() -> None:
|
||||
params.put_bool("IsTestedBranch", build_metadata.tested_channel)
|
||||
params.put_bool("IsReleaseBranch", build_metadata.release_channel)
|
||||
|
||||
# Legacy Bolt fingerprint migration after branch consolidation
|
||||
bolt_source_branch_file = "/data/media/0/starpilot_source_branch"
|
||||
bolt_fingerprint_migration_flag_file = "/data/media/0/frogpilot_bolt_fingerprint_migrated.flag"
|
||||
if not os.path.exists(bolt_fingerprint_migration_flag_file):
|
||||
source_branch = ""
|
||||
try:
|
||||
if os.path.exists(bolt_source_branch_file):
|
||||
with open(bolt_source_branch_file, encoding="utf-8") as f:
|
||||
source_branch = f.read().strip()
|
||||
except OSError:
|
||||
cloudlog.exception("failed reading StarPilot source branch file")
|
||||
|
||||
migration_branch = source_branch or build_metadata.channel
|
||||
replacements = {}
|
||||
if migration_branch in {"TorqueTune", "TorquePedal"}:
|
||||
replacements = {
|
||||
"CHEVROLET_BOLT_EUV": "CHEVROLET_BOLT_ACC_2022_2023",
|
||||
"CHEVROLET_BOLT_CC": "CHEVROLET_BOLT_CC_2022_2023",
|
||||
}
|
||||
elif migration_branch in {"TotallyTune", "StarPilot-2017", "StarPilot 2017"}:
|
||||
replacements = {
|
||||
"CHEVROLET_BOLT_CC": "CHEVROLET_BOLT_CC_2017",
|
||||
}
|
||||
elif migration_branch in {"StarPilot"}:
|
||||
replacements = {
|
||||
"CHEVROLET_BOLT_CC": "CHEVROLET_BOLT_CC_2019_2021",
|
||||
}
|
||||
|
||||
migrated_values = []
|
||||
if replacements:
|
||||
for param_key in ("CarModel", "CarModelName"):
|
||||
current_value = params.get(param_key, encoding='utf-8')
|
||||
normalized_value = current_value[4:] if current_value is not None and current_value.startswith("CAR.") else current_value
|
||||
if normalized_value in replacements:
|
||||
new_value = replacements[normalized_value]
|
||||
params.put(param_key, new_value)
|
||||
params_cache.put(param_key, new_value)
|
||||
migrated_values.append(f"{param_key}: {current_value} -> {new_value}")
|
||||
|
||||
if migrated_values:
|
||||
cloudlog.info(f"migrated legacy bolt fingerprint values from branch '{migration_branch}' (source='{source_branch}'): {', '.join(migrated_values)}")
|
||||
|
||||
# Keep Bolt display label aligned with live fingerprint selection
|
||||
bolt_models = {
|
||||
"CHEVROLET_BOLT_EUV",
|
||||
"CHEVROLET_BOLT_CC",
|
||||
"CHEVROLET_BOLT_ACC_2022_2023",
|
||||
"CHEVROLET_BOLT_ACC_2022_2023_PEDAL",
|
||||
"CHEVROLET_BOLT_CC_2022_2023",
|
||||
"CHEVROLET_BOLT_CC_2019_2021",
|
||||
"CHEVROLET_BOLT_CC_2017",
|
||||
}
|
||||
if (params.get("CarModel", encoding='utf-8') or "") in bolt_models:
|
||||
params.remove("CarModelName")
|
||||
params_cache.remove("CarModelName")
|
||||
|
||||
with open(bolt_fingerprint_migration_flag_file, "w") as f:
|
||||
f.write(migration_branch or "unknown")
|
||||
|
||||
# One-time migration to align FrogPilot defaults after install
|
||||
frogpilot_migration_flag_file = "/data/media/0/frogpilot_migrated.flag"
|
||||
if not os.path.exists(frogpilot_migration_flag_file):
|
||||
params.put_bool("NNFF", False)
|
||||
params.put_bool("NNFFLite", False)
|
||||
params.put_bool("AdvancedLateralTune", True)
|
||||
params.put_bool("ForceAutoTuneOff", True)
|
||||
params.put_bool("ForceAutoTune", False)
|
||||
|
||||
params.put_bool("CECurves", False)
|
||||
params.put_bool("CENavigation", False)
|
||||
params.put_bool("ShowCEMStatus", True)
|
||||
params.put_bool("CESlowerLead", True)
|
||||
params.put_bool("CEStoppedLead", True)
|
||||
params.put_int("CEModelStopTime", 8)
|
||||
|
||||
params.put_bool("ReverseCruise", True)
|
||||
params.put_bool("HumanFollowing", False)
|
||||
params.put_bool("HumanAcceleration", False)
|
||||
|
||||
params.put_int("TuningLevel", 3)
|
||||
params.put_bool("TuningLevelConfirmed", True)
|
||||
|
||||
params.put_bool("DeveloperUI", True)
|
||||
params.put_bool("DeveloperWidgets", True)
|
||||
params.put_bool("DeveloperSidebar", False)
|
||||
params.put_bool("LeadInfo", True)
|
||||
params.put_bool("BorderMetrics", True)
|
||||
params.put_bool("ShowSteering", True)
|
||||
params.put_bool("BlindSpotMetrics", True)
|
||||
|
||||
with open(frogpilot_migration_flag_file, "w") as f:
|
||||
f.write("migrated")
|
||||
|
||||
# One-time migration for HumanAcceleration and HumanFollowing to off
|
||||
migration_flag_file = "/data/media/0/frogpilot_human_toggles_migrated.flag"
|
||||
if not os.path.exists(migration_flag_file):
|
||||
@@ -134,6 +227,18 @@ def manager_init() -> None:
|
||||
with open(nnff_migration_flag_file, "w") as f:
|
||||
f.write("migrated")
|
||||
|
||||
# One-time migration for lateral tuning/auto-tune preferences
|
||||
lateral_tuning_migration_flag_file = "/data/media/0/frogpilot_lateral_tuning_migrated.flag"
|
||||
if not os.path.exists(lateral_tuning_migration_flag_file):
|
||||
if not params.get_bool("AdvancedLateralTune"):
|
||||
params.put_bool("AdvancedLateralTune", True)
|
||||
if params.get_bool("ForceAutoTune"):
|
||||
params.put_bool("ForceAutoTune", False)
|
||||
if not params.get_bool("ForceAutoTuneOff"):
|
||||
params.put_bool("ForceAutoTuneOff", True)
|
||||
with open(lateral_tuning_migration_flag_file, "w") as f:
|
||||
f.write("migrated")
|
||||
|
||||
# set dongle id
|
||||
reg_res = register(show_spinner=True)
|
||||
if reg_res:
|
||||
|
||||
+23
-19
@@ -1,5 +1,6 @@
|
||||
"""Install exception handler for process crash."""
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import sentry_sdk
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
@@ -9,15 +10,16 @@ from sentry_sdk.integrations.threading import ThreadingIntegration
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.system.hardware import HARDWARE, PC
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
from openpilot.system.version import get_build_metadata, get_version
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_variables import ERROR_LOGS_PATH, params
|
||||
|
||||
class SentryProject(Enum):
|
||||
# python project
|
||||
SELFDRIVE = os.environ.get("SENTRY_DSN", "")
|
||||
SELFDRIVE = "https://7305139359a548fcb348ec09497dc389@bugsink.firestar.link/1"
|
||||
# native project
|
||||
SELFDRIVE_NATIVE = os.environ.get("SENTRY_DSN", "")
|
||||
SELFDRIVE_NATIVE = "https://7305139359a548fcb348ec09497dc389@bugsink.firestar.link/1"
|
||||
|
||||
|
||||
def report_tombstone(fn: str, message: str, contents: str) -> None:
|
||||
@@ -26,6 +28,12 @@ def report_tombstone(fn: str, message: str, contents: str) -> None:
|
||||
with sentry_sdk.configure_scope() as scope:
|
||||
scope.set_extra("tombstone_fn", fn)
|
||||
scope.set_extra("tombstone", contents)
|
||||
|
||||
# Attach qlog for debugging context
|
||||
qlogs = glob.glob(f"{Paths.log_root()}/*/qlog")
|
||||
if qlogs:
|
||||
scope.add_attachment(path=max(qlogs, key=os.path.getmtime), filename="qlog")
|
||||
|
||||
sentry_sdk.capture_message(message=message)
|
||||
sentry_sdk.flush()
|
||||
|
||||
@@ -51,8 +59,14 @@ def capture_exception(*args, crash_log=True, **kwargs) -> None:
|
||||
cloudlog.error("crash", exc_info=kwargs.get('exc_info', 1))
|
||||
|
||||
try:
|
||||
sentry_sdk.capture_exception(*args, **kwargs)
|
||||
sentry_sdk.flush() # https://github.com/getsentry/sentry-python/issues/291
|
||||
with sentry_sdk.push_scope() as scope:
|
||||
# Attach qlog for debugging context
|
||||
qlogs = glob.glob(f"{Paths.log_root()}/*/qlog")
|
||||
if qlogs:
|
||||
scope.add_attachment(path=max(qlogs, key=os.path.getmtime), filename="qlog")
|
||||
|
||||
sentry_sdk.capture_exception(*args, **kwargs)
|
||||
sentry_sdk.flush() # https://github.com/getsentry/sentry-python/issues/291
|
||||
except Exception:
|
||||
cloudlog.exception("sentry exception")
|
||||
|
||||
@@ -90,25 +104,15 @@ def save_exception(exc_text: str, crash_log) -> None:
|
||||
|
||||
|
||||
def init(project: SentryProject) -> bool:
|
||||
build_metadata = get_build_metadata()
|
||||
FrogPilot = "frogai" in build_metadata.openpilot.git_origin.lower()
|
||||
if not FrogPilot or PC:
|
||||
if PC:
|
||||
return False
|
||||
|
||||
build_metadata = get_build_metadata()
|
||||
short_branch = build_metadata.channel
|
||||
|
||||
if short_branch in ["COMMA", "HEAD"]:
|
||||
return
|
||||
elif short_branch == "FrogPilot-Development":
|
||||
env = "Development"
|
||||
elif build_metadata.release_channel:
|
||||
env = "Release"
|
||||
elif short_branch == "FrogPilot-Testing":
|
||||
env = short_branch
|
||||
if re.search("test", short_branch, re.IGNORECASE):
|
||||
env = "Testing"
|
||||
elif build_metadata.tested_channel:
|
||||
env = "Staging"
|
||||
else:
|
||||
env = short_branch
|
||||
|
||||
dongle_id = params.get("DongleId", encoding="utf-8")
|
||||
installed = params.get("InstallDate", encoding="utf-8")
|
||||
|
||||
+104
-3
@@ -36,6 +36,19 @@ OVERLAY_INIT = Path(os.path.join(BASEDIR, ".overlay_init"))
|
||||
|
||||
DAYS_NO_CONNECTIVITY_MAX = 14 # do not allow to engage after this many days
|
||||
DAYS_NO_CONNECTIVITY_PROMPT = 10 # send an offroad prompt after this many days
|
||||
MIGRATED_TARGET_BRANCH = "StarPilot"
|
||||
MIGRATION_DONE_FILE = "/data/starpilot_branch_migrated"
|
||||
MIGRATION_SOURCE_BRANCH_FILE = "/data/media/0/starpilot_source_branch"
|
||||
MIGRATION_EXCLUDED_BRANCHES = {"Dom"}
|
||||
MIGRATION_SOURCE_BRANCHES = {
|
||||
"TorqueTune",
|
||||
"TorquePedal",
|
||||
"Kaofui",
|
||||
"Red-Kao",
|
||||
"TotallyTune",
|
||||
"StarPilot-2017",
|
||||
"TRX",
|
||||
}
|
||||
|
||||
class UserRequest:
|
||||
NONE = 0
|
||||
@@ -184,6 +197,39 @@ def finalize_update(params) -> None:
|
||||
"""Take the current OverlayFS merged view and finalize a copy outside of
|
||||
OverlayFS, ready to be swapped-in at BASEDIR. Copy using shutil.copytree"""
|
||||
|
||||
def get_directory_size(path: str) -> int:
|
||||
size = 0
|
||||
for root, _, files in os.walk(path):
|
||||
for name in files:
|
||||
file_path = os.path.join(root, name)
|
||||
if not os.path.islink(file_path):
|
||||
try:
|
||||
size += os.lstat(file_path).st_size
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return size
|
||||
|
||||
total_size = get_directory_size(OVERLAY_MERGED)
|
||||
copied_size = 0
|
||||
|
||||
def copy_with_progress(src, dst, *, follow_symlinks=True):
|
||||
nonlocal copied_size
|
||||
|
||||
if os.path.islink(src):
|
||||
linkto = os.readlink(src)
|
||||
os.symlink(linkto, dst)
|
||||
return dst
|
||||
|
||||
result = shutil.copy2(src, dst, follow_symlinks=follow_symlinks)
|
||||
try:
|
||||
copied_size += os.lstat(src).st_size
|
||||
if total_size > 0:
|
||||
progress = min(int((copied_size / total_size) * 100), 100)
|
||||
params.put("UpdaterState", f"finalizing update... {progress}%")
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return result
|
||||
|
||||
# Remove the update ready flag and any old updates
|
||||
cloudlog.info("creating finalized version of the overlay")
|
||||
set_consistent_flag(False)
|
||||
@@ -191,7 +237,12 @@ def finalize_update(params) -> None:
|
||||
# Copy the merged overlay view and set the update ready flag
|
||||
if os.path.exists(FINALIZED):
|
||||
shutil.rmtree(FINALIZED)
|
||||
shutil.copytree(OVERLAY_MERGED, FINALIZED, symlinks=True)
|
||||
if total_size == 0:
|
||||
shutil.copytree(OVERLAY_MERGED, FINALIZED, symlinks=True)
|
||||
else:
|
||||
params.put("UpdaterState", "finalizing update... 0%")
|
||||
shutil.copytree(OVERLAY_MERGED, FINALIZED, symlinks=True, copy_function=copy_with_progress)
|
||||
params.put("UpdaterState", "finalizing update... 100%")
|
||||
|
||||
run(["git", "reset", "--hard"], FINALIZED)
|
||||
run(["git", "submodule", "foreach", "--recursive", "git", "reset", "--hard"], FINALIZED)
|
||||
@@ -242,6 +293,7 @@ class Updater:
|
||||
self.params = Params()
|
||||
self.branches = defaultdict(str)
|
||||
self._has_internet: bool = False
|
||||
self._migrate_target_branch()
|
||||
|
||||
@property
|
||||
def has_internet(self) -> bool:
|
||||
@@ -254,6 +306,31 @@ class Updater:
|
||||
b = self.get_branch(BASEDIR)
|
||||
return b
|
||||
|
||||
def _migrate_target_branch(self) -> None:
|
||||
target_branch: str | None = self.params.get("UpdaterTargetBranch", encoding='utf-8')
|
||||
current_branch = self.get_branch(BASEDIR)
|
||||
if current_branch in MIGRATION_EXCLUDED_BRANCHES or target_branch in MIGRATION_EXCLUDED_BRANCHES:
|
||||
cloudlog.info(f"skipping StarPilot branch migration on excluded branch: current={current_branch}, target={target_branch}")
|
||||
return
|
||||
if current_branch not in MIGRATION_SOURCE_BRANCHES and target_branch not in MIGRATION_SOURCE_BRANCHES:
|
||||
cloudlog.info(f"skipping StarPilot branch migration on unmanaged branch: current={current_branch}, target={target_branch}")
|
||||
return
|
||||
|
||||
if current_branch != MIGRATED_TARGET_BRANCH:
|
||||
try:
|
||||
Path(MIGRATION_SOURCE_BRANCH_FILE).write_text(current_branch, encoding='utf-8')
|
||||
except OSError:
|
||||
cloudlog.exception(f"failed to persist source branch for migration: {MIGRATION_SOURCE_BRANCH_FILE}")
|
||||
|
||||
if target_branch != MIGRATED_TARGET_BRANCH or current_branch != MIGRATED_TARGET_BRANCH:
|
||||
self.params.put("UpdaterTargetBranch", MIGRATED_TARGET_BRANCH)
|
||||
cloudlog.info(f"migrated updater target branch to {MIGRATED_TARGET_BRANCH} from target={target_branch}, current={current_branch}")
|
||||
|
||||
try:
|
||||
Path(MIGRATION_DONE_FILE).touch()
|
||||
except OSError:
|
||||
cloudlog.exception(f"failed to write migration flag file: {MIGRATION_DONE_FILE}")
|
||||
|
||||
@property
|
||||
def update_ready(self) -> bool:
|
||||
consistent_file = Path(os.path.join(FINALIZED, ".overlay_consistent"))
|
||||
@@ -377,10 +454,34 @@ class Updater:
|
||||
else:
|
||||
cloudlog.info(f"up to date on {cur_branch} ({str(cur_commit)[:7]})")
|
||||
|
||||
def _git_fetch_with_progress(self, branch: str) -> str:
|
||||
fetch_cmd = ["git", "fetch", "--progress", "origin", branch]
|
||||
progress = 0
|
||||
output_lines: list[str] = []
|
||||
|
||||
with subprocess.Popen(fetch_cmd, cwd=OVERLAY_MERGED, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1) as proc:
|
||||
assert proc.stdout is not None
|
||||
for line in proc.stdout:
|
||||
output_lines.append(line)
|
||||
matches = re.findall(r"(\d+)%", line)
|
||||
if matches:
|
||||
progress = max(progress, int(matches[-1]))
|
||||
self.params.put("UpdaterState", f"downloading... {progress}%")
|
||||
|
||||
proc.wait()
|
||||
|
||||
if proc.returncode != 0:
|
||||
raise subprocess.CalledProcessError(proc.returncode, fetch_cmd, output=''.join(output_lines))
|
||||
|
||||
if progress < 100:
|
||||
self.params.put("UpdaterState", "downloading... 100%")
|
||||
|
||||
return ''.join(output_lines)
|
||||
|
||||
def fetch_update(self) -> None:
|
||||
cloudlog.info("attempting git fetch inside staging overlay")
|
||||
|
||||
self.params.put("UpdaterState", "downloading...")
|
||||
self.params.put("UpdaterState", "downloading... 0%")
|
||||
|
||||
# TODO: cleanly interrupt this and invalidate old update
|
||||
set_consistent_flag(False)
|
||||
@@ -389,7 +490,7 @@ class Updater:
|
||||
setup_git_options(OVERLAY_MERGED)
|
||||
|
||||
branch = self.target_branch
|
||||
git_fetch_output = run(["git", "fetch", "origin", branch], OVERLAY_MERGED)
|
||||
git_fetch_output = self._git_fetch_with_progress(branch)
|
||||
cloudlog.info("git fetch success: %s", git_fetch_output)
|
||||
|
||||
cloudlog.info("git reset in progress")
|
||||
|
||||
@@ -0,0 +1,342 @@
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <cassert>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QListWidget>
|
||||
#include <QStackedLayout>
|
||||
#include <QLabel>
|
||||
#include <QProgressBar>
|
||||
#include <QProcess>
|
||||
#include <QWidget>
|
||||
#include <QPair>
|
||||
#include <QVector>
|
||||
|
||||
// --- Configuration ---
|
||||
#define GIT_URL "https://github.com/27182818284590452353602874713526624977572470936999595"
|
||||
#define BRANCH "161803398874989484820458683436563811772030917980576286213544862270526046281890244970720720418939113748475408807538689175212663386222353693179318006076672635443338908659593958290563832266131992829026788067520876689250171169620703222104321626954862629631361"
|
||||
#define LOADING_MSG "314159265358979323846264338327950288419"
|
||||
#define GIT_SSH_URL "git@github.com:commaai/openpilot.git"
|
||||
#define CONTINUE_PATH "/data/continue.sh"
|
||||
#define CACHE_PATH "/usr/comma/openpilot"
|
||||
#define INSTALL_PATH "/data/openpilot"
|
||||
#define TMP_INSTALL_PATH "/data/tmppilot"
|
||||
|
||||
// --- Embedded continue.sh ---
|
||||
const char str_continue_data[] = R"(#!/usr/bin/env sh
|
||||
exec /data/openpilot/launch_openpilot.sh
|
||||
)";
|
||||
const uint8_t *str_continue = (const uint8_t*)str_continue_data;
|
||||
const uint8_t *str_continue_end = str_continue + sizeof(str_continue_data) - 1;
|
||||
|
||||
// --- Helper Functions ---
|
||||
bool time_valid() {
|
||||
time_t rawtime;
|
||||
time(&rawtime);
|
||||
struct tm * sys_time = gmtime(&rawtime);
|
||||
return (1900 + sys_time->tm_year) >= 2020;
|
||||
}
|
||||
|
||||
void run(const char* cmd) {
|
||||
int err = std::system(cmd);
|
||||
assert(err == 0);
|
||||
}
|
||||
|
||||
float lerp(float a, float b, float f) {
|
||||
return (a * (1.0 - f)) + (b * f);
|
||||
}
|
||||
|
||||
// --- Installer Class Definition ---
|
||||
class Installer : public QWidget {
|
||||
public:
|
||||
Installer(QWidget *parent = nullptr);
|
||||
|
||||
void updateProgress(int percent);
|
||||
void doInstall();
|
||||
void freshClone();
|
||||
void cachedFetch();
|
||||
void readProgress();
|
||||
void cloneFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
|
||||
private:
|
||||
QProgressBar *bar;
|
||||
QLabel *val;
|
||||
QLabel *title;
|
||||
QProcess proc;
|
||||
};
|
||||
|
||||
// --- Globals ---
|
||||
static QString targetGitUrl = GIT_URL;
|
||||
static QString targetBranch = BRANCH;
|
||||
|
||||
// --- Installer Implementation ---
|
||||
Installer::Installer(QWidget *parent) : QWidget(parent) {
|
||||
QStackedLayout *stack = new QStackedLayout(this);
|
||||
stack->setMargin(0);
|
||||
|
||||
// --- Page 0: Intro ---
|
||||
QWidget *introPage = new QWidget;
|
||||
QVBoxLayout *introLayout = new QVBoxLayout(introPage);
|
||||
introLayout->setContentsMargins(50, 50, 50, 50);
|
||||
introLayout->setSpacing(20);
|
||||
|
||||
QLabel *introTitle = new QLabel("Select Installation Mode");
|
||||
introTitle->setStyleSheet("font-size: 60px; font-weight: 600; padding-bottom: 50px;");
|
||||
introTitle->setAlignment(Qt::AlignCenter);
|
||||
introLayout->addWidget(introTitle);
|
||||
|
||||
QPushButton *btnDefault = new QPushButton("Default Installation");
|
||||
btnDefault->setFixedHeight(120);
|
||||
btnDefault->setStyleSheet("font-size: 40px; background-color: #333; border-radius: 10px;");
|
||||
introLayout->addWidget(btnDefault);
|
||||
|
||||
QPushButton *btnBranch = new QPushButton("StarPilot Branch");
|
||||
btnBranch->setFixedHeight(120);
|
||||
btnBranch->setStyleSheet("font-size: 40px; background-color: #333; border-radius: 10px;");
|
||||
introLayout->addWidget(btnBranch);
|
||||
|
||||
stack->addWidget(introPage);
|
||||
|
||||
// --- Page 1: Branch Selection ---
|
||||
QWidget *branchPage = new QWidget;
|
||||
QVBoxLayout *branchLayout = new QVBoxLayout(branchPage);
|
||||
branchLayout->setContentsMargins(50, 50, 50, 50);
|
||||
|
||||
QLabel *branchTitle = new QLabel("Select Branch");
|
||||
branchTitle->setStyleSheet("font-size: 50px; font-weight: 600;");
|
||||
branchLayout->addWidget(branchTitle);
|
||||
|
||||
QListWidget *branchList = new QListWidget;
|
||||
branchList->setStyleSheet("font-size: 35px; background-color: #222;");
|
||||
branchLayout->addWidget(branchList);
|
||||
|
||||
QHBoxLayout *btnLayout = new QHBoxLayout;
|
||||
QPushButton *btnCancel = new QPushButton("Cancel");
|
||||
btnCancel->setFixedHeight(100);
|
||||
btnCancel->setStyleSheet("font-size: 35px; background-color: #444; border-radius: 10px;");
|
||||
|
||||
QPushButton *btnInstall = new QPushButton("Install");
|
||||
btnInstall->setFixedHeight(100);
|
||||
btnInstall->setStyleSheet("font-size: 35px; background-color: #007aff; border-radius: 10px;");
|
||||
|
||||
btnLayout->addWidget(btnCancel);
|
||||
btnLayout->addWidget(btnInstall);
|
||||
branchLayout->addLayout(btnLayout);
|
||||
|
||||
stack->addWidget(branchPage);
|
||||
|
||||
// --- Page 2: Progress ---
|
||||
QWidget *progressPage = new QWidget;
|
||||
QVBoxLayout *layout = new QVBoxLayout(progressPage);
|
||||
layout->setContentsMargins(150, 290, 150, 150);
|
||||
layout->setSpacing(0);
|
||||
|
||||
title = new QLabel("Installing " LOADING_MSG);
|
||||
title->setStyleSheet("font-size: 90px; font-weight: 600;");
|
||||
layout->addWidget(title, 0, Qt::AlignTop);
|
||||
|
||||
layout->addSpacing(170);
|
||||
|
||||
bar = new QProgressBar();
|
||||
bar->setRange(0, 100);
|
||||
bar->setTextVisible(false);
|
||||
bar->setFixedHeight(72);
|
||||
layout->addWidget(bar, 0, Qt::AlignTop);
|
||||
|
||||
layout->addSpacing(30);
|
||||
|
||||
val = new QLabel("0%");
|
||||
val->setStyleSheet("font-size: 70px; font-weight: 300;");
|
||||
layout->addWidget(val, 0, Qt::AlignTop);
|
||||
|
||||
layout->addStretch();
|
||||
stack->addWidget(progressPage);
|
||||
|
||||
// --- Logic Wiring ---
|
||||
|
||||
// Default Button
|
||||
QObject::connect(btnDefault, &QPushButton::clicked, [=]() {
|
||||
stack->setCurrentWidget(progressPage);
|
||||
QTimer::singleShot(100, this, &Installer::doInstall);
|
||||
});
|
||||
|
||||
// StarPilot Button
|
||||
QObject::connect(btnBranch, &QPushButton::clicked, [=]() {
|
||||
stack->setCurrentWidget(branchPage);
|
||||
branchList->clear();
|
||||
branchList->addItem("Loading branches...");
|
||||
|
||||
QProcess *p = new QProcess(this);
|
||||
QString url = "https://github.com/firestar5683/StarPilot";
|
||||
p->start("git", {"ls-remote", "--heads", url});
|
||||
|
||||
QObject::connect(p, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), [=](int exitCode) {
|
||||
branchList->clear();
|
||||
if (exitCode == 0) {
|
||||
QString out = p->readAllStandardOutput();
|
||||
QStringList lines = out.split('\n');
|
||||
for (const QString &line : lines) {
|
||||
if (line.isEmpty()) continue;
|
||||
QStringList parts = line.split('\t');
|
||||
if (parts.size() < 2) continue;
|
||||
QString ref = parts[1];
|
||||
if (ref.startsWith("refs/heads/")) {
|
||||
branchList->addItem(ref.mid(11));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
branchList->addItem("Error fetching branches");
|
||||
}
|
||||
p->deleteLater();
|
||||
});
|
||||
});
|
||||
|
||||
// Cancel Button
|
||||
QObject::connect(btnCancel, &QPushButton::clicked, [=]() {
|
||||
stack->setCurrentWidget(introPage);
|
||||
});
|
||||
|
||||
// Install Button
|
||||
QObject::connect(btnInstall, &QPushButton::clicked, [=]() {
|
||||
if (branchList->currentItem()) {
|
||||
targetGitUrl = "https://github.com/firestar5683/StarPilot";
|
||||
targetBranch = branchList->currentItem()->text();
|
||||
stack->setCurrentWidget(progressPage);
|
||||
title->setText("Installing " + targetBranch);
|
||||
QTimer::singleShot(100, this, &Installer::doInstall);
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(&proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &Installer::cloneFinished);
|
||||
QObject::connect(&proc, &QProcess::readyReadStandardError, this, &Installer::readProgress);
|
||||
|
||||
setStyleSheet(R"(
|
||||
* {
|
||||
font-family: Inter;
|
||||
color: white;
|
||||
background-color: black;
|
||||
}
|
||||
QProgressBar {
|
||||
border: none;
|
||||
background-color: #292929;
|
||||
}
|
||||
QListWidget {
|
||||
border: 1px solid #444;
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
void Installer::updateProgress(int percent) {
|
||||
int h = (int)(lerp(233, 360 + 131, percent / 100.)) % 360;
|
||||
int s = lerp(78, 62, percent / 100.);
|
||||
int b = lerp(94, 87, percent / 100.);
|
||||
|
||||
bar->setValue(percent);
|
||||
bar->setStyleSheet(QString(R"(
|
||||
QProgressBar::chunk {
|
||||
background-color: hsb(%1, %2%, %3%);
|
||||
})").arg(h).arg(s).arg(b));
|
||||
val->setText(QString("%1%").arg(percent));
|
||||
update();
|
||||
}
|
||||
|
||||
void Installer::doInstall() {
|
||||
while (!time_valid()) {
|
||||
usleep(500 * 1000);
|
||||
qDebug() << "Waiting for valid time";
|
||||
}
|
||||
|
||||
run("rm -rf " TMP_INSTALL_PATH " " INSTALL_PATH);
|
||||
|
||||
if (QDir(CACHE_PATH).exists()) {
|
||||
cachedFetch();
|
||||
} else {
|
||||
freshClone();
|
||||
}
|
||||
}
|
||||
|
||||
void Installer::freshClone() {
|
||||
qDebug() << "Doing fresh clone";
|
||||
proc.start("git", {"clone", "--progress", targetGitUrl, "-b", targetBranch,
|
||||
"--depth=1", "--recurse-submodules", TMP_INSTALL_PATH});
|
||||
}
|
||||
|
||||
void Installer::cachedFetch() {
|
||||
qDebug() << "Fetching with cache";
|
||||
|
||||
run("cp -rp " CACHE_PATH " " TMP_INSTALL_PATH);
|
||||
int err = chdir(TMP_INSTALL_PATH);
|
||||
assert(err == 0);
|
||||
run(("git remote set-branches --add origin " + targetBranch.toStdString()).c_str());
|
||||
|
||||
updateProgress(10);
|
||||
|
||||
proc.setWorkingDirectory(TMP_INSTALL_PATH);
|
||||
proc.start("git", {"fetch", "--progress", "origin", targetBranch});
|
||||
}
|
||||
|
||||
void Installer::readProgress() {
|
||||
const QVector<QPair<QString, int>> stages = {
|
||||
{"Receiving objects: ", 95},
|
||||
{"Filtering content: ", 5},
|
||||
};
|
||||
|
||||
auto line = QString(proc.readAllStandardError());
|
||||
|
||||
int base = 0;
|
||||
for (const QPair<QString, int> kv : stages) {
|
||||
if (line.startsWith(kv.first)) {
|
||||
auto perc = line.split(kv.first)[1].split("%")[0];
|
||||
int p = base + int(perc.toFloat() / 100. * kv.second);
|
||||
updateProgress(p);
|
||||
break;
|
||||
}
|
||||
base += kv.second;
|
||||
}
|
||||
}
|
||||
|
||||
void Installer::cloneFinished(int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
qDebug() << "git finished with " << exitCode;
|
||||
assert(exitCode == 0);
|
||||
|
||||
title->setText("Installation complete");
|
||||
updateProgress(100);
|
||||
|
||||
int err = chdir(TMP_INSTALL_PATH);
|
||||
assert(err == 0);
|
||||
run(("git checkout " + targetBranch.toStdString()).c_str());
|
||||
run(("git reset --hard origin/" + targetBranch.toStdString()).c_str());
|
||||
|
||||
run("mv " TMP_INSTALL_PATH " " INSTALL_PATH);
|
||||
|
||||
// Embedded continue.sh write logic
|
||||
FILE *of = fopen("/data/continue.sh.new", "wb");
|
||||
assert(of != NULL);
|
||||
|
||||
size_t num = str_continue_end - str_continue;
|
||||
size_t num_written = fwrite(str_continue, 1, num, of);
|
||||
assert(num == num_written);
|
||||
fclose(of);
|
||||
|
||||
run("chmod +x /data/continue.sh.new");
|
||||
run("mv /data/continue.sh.new " CONTINUE_PATH);
|
||||
|
||||
QTimer::singleShot(60 * 1000, &QCoreApplication::quit);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Simplest main setup
|
||||
QApplication a(argc, argv);
|
||||
Installer installer;
|
||||
installer.showFullScreen();
|
||||
return a.exec();
|
||||
}
|
||||
Reference in New Issue
Block a user