This commit is contained in:
firestar5683
2026-08-09 20:54:41 -05:00
parent 98f856a548
commit d4d06e5561
34 changed files with 731 additions and 131 deletions
+1 -1
View File
@@ -208,7 +208,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"CECurves", {PERSISTENT, BOOL, "0", "0", 1, SETTINGS_SIMPLE}},
{"CECurvesLead", {PERSISTENT, BOOL, "0", "0", 1}},
{"CELead", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
{"CEModelStopTime", {PERSISTENT, FLOAT, "9.0", "0.0", 2, SETTINGS_SIMPLE}},
{"CEModelStopTime", {PERSISTENT, FLOAT, "7.7", "0.0", 2, SETTINGS_SIMPLE}},
{"CESignalLaneDetection", {PERSISTENT, BOOL, "1", "0", 2}},
{"CESignalSpeed", {PERSISTENT, FLOAT, "0.0", "0.0", 2, SETTINGS_SIMPLE}},
{"CESlowerLead", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}},
+115 -10
View File
@@ -1,24 +1,36 @@
<!--- AUTOGENERATED FROM selfdrive/car/CARS_template.md, DO NOT EDIT. --->
# Support Information for 384 Known Cars
# Support Information for 489 Known Cars
|Make|Model|Package|Support Level|
|---|---|---|:---:|
|Acura|ADX 2025-26|All|[Upstream](#upstream)|
|Acura|ILX 2016-18|Technology Plus Package or AcuraWatch Plus|[Upstream](#upstream)|
|Acura|ILX 2019|All|[Upstream](#upstream)|
|Acura|Integra 2023-25|All|[Community](#community)|
|Acura|Integra 2023-26|All|[Upstream](#upstream)|
|Acura|MDX 2014-16|Advance Package|[Upstream](#upstream)|
|Acura|MDX 2015-16|Advance Package|[Community](#community)|
|Acura|MDX 2017-19|All|[Upstream](#upstream)|
|Acura|MDX 2017-20|All|[Community](#community)|
|Acura|MDX 2020|All|[Upstream](#upstream)|
|Acura|MDX 2022-24|All|[Upstream](#upstream)|
|Acura|MDX 2022-24|All|[Community](#community)|
|Acura|MDX 2025|All except Type S|[Upstream](#upstream)|
|Acura|MDX Hybrid 2017-19|All|[Upstream](#upstream)|
|Acura|MDX Hybrid 2020|All|[Upstream](#upstream)|
|Acura|RDX 2016-18|AcuraWatch Plus or Advance Package|[Upstream](#upstream)|
|Acura|RDX 2019-21|All|[Upstream](#upstream)|
|Acura|RDX 2022-25|All|[Community](#community)|
|Acura|RDX 2022-26|All|[Upstream](#upstream)|
|Acura|RLX 2017|Advance Package or Technology Package|[Community](#community)|
|Acura|TLX 2015-17|Advance Package|[Upstream](#upstream)|
|Acura|TLX 2015-17|Advance Package|[Community](#community)|
|Acura|TLX 2018-20|All|[Upstream](#upstream)|
|Acura|TLX 2018-20|All|[Community](#community)|
|Acura|TLX 2021|All|[Upstream](#upstream)|
|Acura|TLX 2022-23|All|[Community](#community)|
|Acura|TLX 2024-25|All|[Upstream](#upstream)|
|Acura|ZDX 2024|All|[Not compatible](#can-bus-security)|
|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
@@ -29,11 +41,37 @@
|Audi|Q5 2017-24|All|[Not compatible](#flexray)|
|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim, without Super Cruise Package|[Upstream](#upstream)|
|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|[Upstream](#upstream)|
|Buick|Baby Enclave 2020-23|Driver Assist Package|[Upstream](#upstream)|
|Buick|LaCrosse ASCM Harness 2017-19|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Buick|LaCrosse US ASCM Harness 2019|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Cadillac|CT6 No-ACC 2016-20|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Cadillac|Escalade ASCM Harness 2018|Driver Assist Package|[Upstream](#upstream)|
|Cadillac|Escalade ESV Platinum ASCM Harness 2019|Adaptive Cruise Control (ACC) & LKAS|[Upstream](#upstream)|
|Cadillac|XT4 No-ACC 2023|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Cadillac|XT5 No-ACC 2022|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Cadillac|XT6 2020|Driver Assist Package|[Upstream](#upstream)|
|Chevrolet|Blazer 2019-25|Driver Assist Package|[Upstream](#upstream)|
|Chevrolet|Bolt EV & EUV ACC 2022-23|Premier or Premier Redline Trim without Super Cruise Package|[Upstream](#upstream)|
|Chevrolet|Bolt EV & EUV ACC w Pedal 2022-23|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Bolt EV & EUV No-ACC 2022-23|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Bolt EV No-ACC 2017|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Bolt EV No-ACC 2018-21|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Equinox 2019-22|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Equinox No-ACC 2019-22|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Malibu 2019|SDGM Harness (Optional SASCM)|[Upstream](#upstream)|
|Chevrolet|Malibu ASCM Harness 2017-19|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Malibu Hybrid No-ACC 2017|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Malibu No-ACC 2023|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Silverado 1500 2020-21|Safety Package II|[Upstream](#upstream)|
|Chevrolet|Silverado 1500 No-ACC 2020-21|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Suburban Premier 2016-20|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Suburban Premier No-ACC 2016-20|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Trailblazer 2021-22|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Trailblazer No-ACC 2021-22|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|TRAX 2024|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Volt ASCM Harness 2017-18|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Volt Camera Harness 2017-18|Flashed camera-forward integration with ACC|[Upstream](#upstream)|
|Chevrolet|Volt No-ACC 2017-18|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chrysler|Pacifica 2021-23|All|[Upstream](#upstream)|
@@ -67,9 +105,11 @@
|Ford|Maverick Hybrid 2023-24|Co-Pilot360 Assist|[Upstream](#upstream)|
|Ford|Mustang Mach-E 2021-24|All|[Upstream](#upstream)|
|Ford|Ranger 2024|Adaptive Cruise Control with Lane Centering|[Upstream](#upstream)|
|Ford|Transit 2025|Co-Pilot360 Assist+|[Upstream](#upstream)|
|Genesis|G70 2018|All|[Upstream](#upstream)|
|Genesis|G70 2019-21|All|[Upstream](#upstream)|
|Genesis|G70 2022-23|All|[Upstream](#upstream)|
|Genesis|G70 Non-SCC 2021|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Genesis|G80 2017|All|[Upstream](#upstream)|
|Genesis|G80 2018-19|All|[Upstream](#upstream)|
|Genesis|G80 (2.5T Advanced Trim, with HDA II) 2024|Highway Driving Assist II|[Upstream](#upstream)|
@@ -78,14 +118,22 @@
|Genesis|GV60 (Performance Trim) 2022-23|All|[Upstream](#upstream)|
|Genesis|GV70 (2.5T Trim, without HDA II) 2022-24|All|[Upstream](#upstream)|
|Genesis|GV70 (3.5T Trim, without HDA II) 2022-23|All|[Upstream](#upstream)|
|Genesis|GV70 Electrified 2026|All|[Upstream](#upstream)|
|Genesis|GV70 Electrified (Australia Only) 2022|All|[Upstream](#upstream)|
|Genesis|GV70 Electrified (with HDA II) 2023-24|Highway Driving Assist II|[Upstream](#upstream)|
|Genesis|GV80 2023|All|[Upstream](#upstream)|
|Genesis|GV80 (3.5T Prestige Trim, with HDA II & LFA2) 2025|Highway Driving Assist II & Lane Follow Assist 2|[Upstream](#upstream)|
|Genesis|GV80 Coupe (with HDA II & LFA2) 2025|Highway Driving Assist II & Lane Follow Assist 2|[Upstream](#upstream)|
|GMC|Acadia ASCM Harness 2018|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|GMC|Sierra 1500 2020-21|Driver Alert Package II|[Upstream](#upstream)|
|GMC|Yukon 2019-20|Adaptive Cruise Control (ACC) & LKAS|[Dashcam mode](#dashcam)|
|GMC|Sierra 1500 No-ACC 2020-21|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|GMC|Yukon 2019-20|Adaptive Cruise Control (ACC) & LKAS|[Upstream](#upstream)|
|GMC|Yukon No-ACC 2019-20|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Honda|Accord 2016-17|Honda Sensing|[Upstream](#upstream)|
|Honda|Accord 2016-17|Honda Sensing|[Community](#community)|
|Honda|Accord 2018-22|All|[Upstream](#upstream)|
|Honda|Accord 2023-25|All|[Upstream](#upstream)|
|Honda|Accord Hybrid 2017|All|[Upstream](#upstream)|
|Honda|Accord Hybrid 2018-22|All|[Upstream](#upstream)|
|Honda|Accord Hybrid 2023-25|All|[Upstream](#upstream)|
|Honda|City (Brazil only) 2023|All|[Upstream](#upstream)|
@@ -98,6 +146,7 @@
|Honda|Civic Hatchback Hybrid 2025-26|All|[Upstream](#upstream)|
|Honda|Civic Hatchback Hybrid (Europe only) 2023|All|[Upstream](#upstream)|
|Honda|Civic Hybrid 2025-26|All|[Upstream](#upstream)|
|Honda|Clarity 2018-21|All|[Upstream](#upstream)|
|Honda|Clarity 2018-21|All|[Community](#community)|
|Honda|CR-V 2015-16|Touring Trim|[Upstream](#upstream)|
|Honda|CR-V 2017-22|Honda Sensing|[Upstream](#upstream)|
@@ -106,6 +155,8 @@
|Honda|CR-V Hybrid 2023-25|All|[Upstream](#upstream)|
|Honda|e 2020|All|[Upstream](#upstream)|
|Honda|Fit 2018-20|Honda Sensing|[Upstream](#upstream)|
|Honda|Fit (Taiwan) 2021|All|[Upstream](#upstream)|
|Honda|Fit (Taiwan) 2024-25|All|[Upstream](#upstream)|
|Honda|Freed 2020|Honda Sensing|[Upstream](#upstream)|
|Honda|HR-V 2019-22|Honda Sensing|[Upstream](#upstream)|
|Honda|HR-V 2023-25|All|[Upstream](#upstream)|
@@ -114,27 +165,40 @@
|Honda|N-Box 2018|All|[Upstream](#upstream)|
|Honda|Odyssey 2018-20|Honda Sensing|[Upstream](#upstream)|
|Honda|Odyssey 2021-26|All|[Upstream](#upstream)|
|Honda|Odyssey (Singapore) 2021|Honda Sensing|[Upstream](#upstream)|
|Honda|Odyssey (Taiwan) 2018-19|Honda Sensing|[Upstream](#upstream)|
|Honda|Passport 2019-25|All|[Upstream](#upstream)|
|Honda|Passport 2026|All|[Upstream](#upstream)|
|Honda|Pilot 2016-22|Honda Sensing|[Upstream](#upstream)|
|Honda|Pilot 2023-25|All|[Upstream](#upstream)|
|Honda|Prelude 2026|All|[Upstream](#upstream)|
|Honda|Prologue 2024-25|All|[Not compatible](#can-bus-security)|
|Honda|Ridgeline 2017-25|Honda Sensing|[Upstream](#upstream)|
|Hyundai|Azera 2022|All|[Upstream](#upstream)|
|Hyundai|Azera Hybrid 2019|All|[Upstream](#upstream)|
|Hyundai|Azera Hybrid 2020|All|[Upstream](#upstream)|
|Hyundai|Azera Hybrid (with HDA II & LFA2) 2025|Highway Driving Assist II & Lane Follow Assist 2|[Upstream](#upstream)|
|Hyundai|Bayon Non-SCC 2021|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Hyundai|Custin 2023|All|[Upstream](#upstream)|
|Hyundai|Elantra 2017-18|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Elantra 2019|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Elantra 2024-25|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Elantra GT 2017-20|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Elantra Hybrid 2024-26|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Elantra Hybrid Non-SCC 2022|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Hyundai|Elantra Non-SCC 2022|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|i30 Hybrid 2024|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Ioniq 5 (Southeast Asia and Europe only) 2022-24|All|[Upstream](#upstream)|
|Hyundai|Ioniq 5 (with HDA II) 2022-24|Highway Driving Assist II|[Upstream](#upstream)|
|Hyundai|Ioniq 5 (without HDA II) 2022-24|Highway Driving Assist|[Upstream](#upstream)|
|Hyundai|Ioniq 5 N (with HDA II) 2024|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Ioniq 5 PE (with HDA II & LFA2) 2025-26|Highway Driving Assist II & Lane Follow Assist 2|[Upstream](#upstream)|
|Hyundai|Ioniq 6 (with HDA II) 2023-24|Highway Driving Assist II|[Upstream](#upstream)|
|Hyundai|Ioniq 9 (with HDA II & LFA2) 2025-26|Highway Driving Assist II & Lane Follow Assist 2|[Upstream](#upstream)|
|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Ioniq Electric 2020|All|[Upstream](#upstream)|
|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|[Upstream](#upstream)|
@@ -143,40 +207,68 @@
|Hyundai|Ioniq Plug-in Hybrid 2020-22|All|[Upstream](#upstream)|
|Hyundai|Kona 2020|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona 2022-23|Smart Cruise Control (SCC)|[Dashcam mode](#dashcam)|
|Hyundai|Kona (without HDA II) 2024-25|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona Electric 2022-23|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona Electric (with HDA II, Korea only) 2023|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona Electric (without HDA II) 2024|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona Electric Non-SCC 2019|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona Hybrid (without HDA II) 2024|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Kona Non-SCC 2019|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Hyundai|Nexo 2021|All|[Upstream](#upstream)|
|Hyundai|Palisade 2020-22|All|[Upstream](#upstream)|
|Hyundai|Palisade 2023-24|HDA2|[Community](#community)|
|Hyundai|Palisade (with HDA II) 2023-24|Highway Driving Assist II|[Upstream](#upstream)|
|Hyundai|Palisade (without HDA II) 2023-25|Highway Driving Assist|[Upstream](#upstream)|
|Hyundai|Santa Cruz 2022-24|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Santa Cruz (without HDA II) 2025|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Santa Fe 2019-20|All|[Upstream](#upstream)|
|Hyundai|Santa Fe 2021-23|All|[Upstream](#upstream)|
|Hyundai|Santa Fe Hybrid 2022-23|All|[Upstream](#upstream)|
|Hyundai|Santa Fe Hybrid (with HDA II & LFA2) 2024-25|Highway Driving Assist II & Lane Follow Assist 2|[Upstream](#upstream)|
|Hyundai|Santa Fe Hybrid (without HDA II, LFA2) 2025-26|Lane Follow Assist 2|[Upstream](#upstream)|
|Hyundai|Santa Fe Plug-in Hybrid 2022-23|All|[Upstream](#upstream)|
|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Sonata 2020-23|All|[Upstream](#upstream)|
|Hyundai|Sonata (without HDA II) 2024-25|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Sonata Hybrid 2020-23|All|[Upstream](#upstream)|
|Hyundai|Sonata Hybrid (without HDA II) 2024-25|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Staria 2023|All|[Upstream](#upstream)|
|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Tucson 2022|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Tucson 2023-24|All|[Upstream](#upstream)|
|Hyundai|Tucson (without HDA II) 2025-26|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Tucson Hybrid 2022-24|All|[Upstream](#upstream)|
|Hyundai|Tucson Hybrid (without HDA II) 2025-26|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Tucson Plug-in Hybrid 2024|All|[Upstream](#upstream)|
|Hyundai|Tucson Plug-in Hybrid (without HDA II) 2025|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Kia|Carnival 2022-24|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Carnival 2025|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Carnival (China only) 2023|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Carnival (with HDA II) 2025|Highway Driving Assist II|[Upstream](#upstream)|
|Kia|Carnival Hybrid 2025|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Carnival Hybrid 2026|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Carnival Hybrid (with HDA II) 2025-26|Highway Driving Assist II|[Upstream](#upstream)|
|Kia|Ceed 2019-21|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Ceed Plug-in Hybrid Non-SCC 2022|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Kia|EV6 (Southeast Asia only) 2022-24|All|[Upstream](#upstream)|
|Kia|EV6 (with HDA I) 2025|Highway Driving Assist I|[Upstream](#upstream)|
|Kia|EV6 (with HDA II) 2022-24|Highway Driving Assist II|[Upstream](#upstream)|
|Kia|EV6 (without HDA II) 2022-24|Highway Driving Assist|[Upstream](#upstream)|
|Kia|EV9 2025-26|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Forte 2019-21|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Forte 2022-23|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Forte Non-SCC 2019|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Kia|Forte Non-SCC 2021|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Kia|K4 (with HDA II) 2025|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|K4 (without HDA II) 2025|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|K5 2021-24|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|K5 (without HDA II) 2025|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|K5 Hybrid 2020-22|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|K8 Hybrid (with HDA II) 2023|Highway Driving Assist II|[Upstream](#upstream)|
|Kia|Niro EV 2019|All|[Upstream](#upstream)|
@@ -198,17 +290,25 @@
|Kia|Optima Hybrid 2017|Advanced Smart Cruise Control|[Dashcam mode](#dashcam)|
|Kia|Optima Hybrid 2019|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Seltos 2021|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Seltos Non-SCC 2023-24|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Kia|Sorento 2018|Advanced Smart Cruise Control & LKAS|[Upstream](#upstream)|
|Kia|Sorento 2019|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Sorento 2021-23|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Sorento (without HDA II) 2024-25|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Sorento Hybrid 2021-23|All|[Upstream](#upstream)|
|Kia|Sorento Hybrid 2026|All|[Upstream](#upstream)|
|Kia|Sorento Plug-in Hybrid 2022-23|All|[Upstream](#upstream)|
|Kia|Sportage 2023-24|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Sportage (without HDA II) 2026|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Sportage Hybrid 2023|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Sportage Hybrid 2026|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Stinger 2022-23|All|[Upstream](#upstream)|
|Kia|Telluride 2020-22|All|[Upstream](#upstream)|
|Kia|Telluride 2023-24|HDA2|[Community](#community)|
|Kia|Telluride (with HDA II) 2023-24|Highway Driving Assist II|[Upstream](#upstream)|
|Kia|Telluride (without HDA II) 2023-25|Highway Driving Assist|[Upstream](#upstream)|
|Kia|XCeed Plug-in Hybrid 2021|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Lexus|CT Hybrid 2017-18|Lexus Safety System+|[Upstream](#upstream)|
|Lexus|ES 2017-18|All|[Upstream](#upstream)|
|Lexus|ES 2019-25|All|[Upstream](#upstream)|
@@ -244,6 +344,7 @@
|Mazda|CX-9 2021-23|All|[Upstream](#upstream)|
|Nissan|Altima 2019-20, 2024|ProPILOT Assist|[Upstream](#upstream)|
|Nissan|Leaf 2018-25|ProPILOT Assist|[Upstream](#upstream)|
|Nissan|Leaf Instrument Cluster 2018-25|ProPILOT Assist|[Upstream](#upstream)|
|Nissan|Rogue 2018-20|ProPILOT Assist|[Upstream](#upstream)|
|Nissan|X-Trail 2017|ProPILOT Assist|[Upstream](#upstream)|
|Peugeot|208 2019-25|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
@@ -257,22 +358,24 @@
|SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Subaru|Ascent 2019-21|All|[Upstream](#upstream)|
|Subaru|Ascent 2023|All|[Dashcam mode](#dashcam)|
|Subaru|Ascent 2023-25|All|[Upstream](#upstream)|
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Crosstrek 2025|All|[Upstream](#upstream)|
|Subaru|Crosstrek Hybrid 2020|EyeSight Driver Assistance|[Dashcam mode](#dashcam)|
|Subaru|Forester 2017-18|EyeSight Driver Assistance|[Dashcam mode](#dashcam)|
|Subaru|Forester 2019-21|All|[Upstream](#upstream)|
|Subaru|Forester 2022-24|All|[Dashcam mode](#dashcam)|
|Subaru|Forester 2022-24|All|[Upstream](#upstream)|
|Subaru|Forester Hybrid 2020|EyeSight Driver Assistance|[Dashcam mode](#dashcam)|
|Subaru|Impreza 2017-19|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Impreza 2020-22|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Legacy 2015-18|EyeSight Driver Assistance|[Dashcam mode](#dashcam)|
|Subaru|Legacy 2020-22|All|[Upstream](#upstream)|
|Subaru|Legacy 2025|All|[Upstream](#upstream)|
|Subaru|Outback 2015-17|EyeSight Driver Assistance|[Dashcam mode](#dashcam)|
|Subaru|Outback 2018-19|EyeSight Driver Assistance|[Dashcam mode](#dashcam)|
|Subaru|Outback 2020-22|All|[Upstream](#upstream)|
|Subaru|Outback 2023|All|[Dashcam mode](#dashcam)|
|Subaru|Outback 2023-24|All|[Upstream](#upstream)|
|Subaru|Solterra 2023-25|Any|[Not compatible](#can-bus-security)|
|Subaru|XV 2018-19|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|XV 2020-21|EyeSight Driver Assistance|[Upstream](#upstream)|
@@ -286,7 +389,8 @@
|Škoda|Scala 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Škoda|Superb 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Tesla|Model 3 (with HW3) 2019-23|All|[Upstream](#upstream)|
|Tesla|Model 3 (with HW4) 2024-25|All|[Upstream](#upstream)|
|Tesla|Model 3 (with HW4) 2024-26|All|[Upstream](#upstream)|
|Tesla|Model S (Pre-AP) 2012-14|All|[Community](#community)|
|Tesla|Model X (with HW4) 2024|All|[Dashcam mode](#dashcam)|
|Tesla|Model Y (with HW3) 2020-23|All|[Upstream](#upstream)|
|Tesla|Model Y (with HW4) 2024-25|All|[Upstream](#upstream)|
@@ -321,6 +425,7 @@
|Toyota|Highlander 2025|Any|[Not compatible](#can-bus-security)|
|Toyota|Highlander Hybrid 2017-19|All|[Upstream](#upstream)|
|Toyota|Highlander Hybrid 2020-23|All|[Upstream](#upstream)|
|Toyota|Matrix Retrofit 2005|Custom retrofit|[Community](#community)|
|Toyota|Mirai 2021|All|[Upstream](#upstream)|
|Toyota|Prius 2016|Toyota Safety Sense P|[Upstream](#upstream)|
|Toyota|Prius 2017-20|All|[Upstream](#upstream)|
@@ -342,7 +447,7 @@
|Toyota|RAV4 Prime 2024-25|Any|[Not compatible](#can-bus-security)|
|Toyota|Sequoia 2023-25|Any|[Not compatible](#can-bus-security)|
|Toyota|Sienna 2018-20|All|[Upstream](#upstream)|
|Toyota|Sienna 2021-23|All|[Community](#community)|
|Toyota|Sienna 2021-25|All|[Community](#community)|
|Toyota|Sienna 2024-25|Any|[Not compatible](#can-bus-security)|
|Toyota|Tundra 2022-25|Any|[Not compatible](#can-bus-security)|
|Toyota|Venza 2021-25|Any|[Not compatible](#can-bus-security)|
@@ -444,4 +549,4 @@ Toyota, and the GM Global B platform.
All the cars that openpilot supports use a [CAN bus](https://en.wikipedia.org/wiki/CAN_bus) for communication between all the car's computers, however a
CAN bus isn't the only way that the computers in your car can communicate. Most, if not all, vehicles from the following
manufacturers use [FlexRay](https://en.wikipedia.org/wiki/FlexRay) instead of a CAN bus: **BMW, Mercedes, Audi, Land Rover, and some Volvo**. These cars
may one day be supported, but we have no immediate plans to support FlexRay.
may one day be supported, but we have no immediate plans to support FlexRay.
@@ -671,7 +671,7 @@ class CarController(CarControllerBase):
stopping, hud_control, CS, CC, starpilot_toggles, lka_icon, lfa_icon))
else:
can_sends.extend(self.create_can_msgs(apply_steer_req, apply_torque, torque_fault, set_speed_in_units, accel,
stopping, hud_control, actuators, CS, CC, lfa_icon))
stopping, hud_control, actuators, CS, CC, lka_icon, lfa_icon))
new_actuators = actuators.as_builder()
if self.CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING:
@@ -686,7 +686,7 @@ class CarController(CarControllerBase):
self.frame += 1
return new_actuators, can_sends
def create_can_msgs(self, apply_steer_req, apply_torque, torque_fault, set_speed_in_units, accel, stopping, hud_control, actuators, CS, CC, lfa_icon):
def create_can_msgs(self, apply_steer_req, apply_torque, torque_fault, set_speed_in_units, accel, stopping, hud_control, actuators, CS, CC, lka_icon, lfa_icon):
can_sends = []
can_canfd_blended = bool(self.CP.flags & HyundaiFlags.CAN_CANFD_BLENDED)
@@ -711,7 +711,7 @@ class CarController(CarControllerBase):
can_sends.append(hyundaican.create_lkas11(self.packer, self.frame, self.CP, apply_torque, apply_steer_req,
torque_fault, CS.lkas11, sys_warning, sys_state, CC.enabled,
hud_control.leftLaneVisible, hud_control.rightLaneVisible,
left_lane_warning, right_lane_warning))
left_lane_warning, right_lane_warning, lka_icon))
# Button messages
if not self.long_active_ecu:
+11 -1
View File
@@ -49,6 +49,15 @@ def calculate_canfd_speed_limit(CP, FPCP, cp, cp_cam, speed_factor):
return 0.0
def get_canfd_cruise_available(CP, cp, scc_available: bool) -> bool:
# The EV9 fallback keeps stock ACC active when ECU disable is skipped. Its
# SCC status remains inactive in that mode, while TCS still reports whether
# ACC is fault-free and available.
if CP.carFingerprint == CAR.KIA_EV9 and not CP.openpilotLongitudinalControl:
return cp.vl["TCS"]["ACCEnable"] == 0
return scc_available
def decode_ioniq_6_blindspot_radar_state(state: int) -> tuple[bool, bool]:
state_int = int(state)
return bool(state_int & IONIQ_6_BLINDSPOT_LEFT_MASK), bool(state_int & IONIQ_6_BLINDSPOT_RIGHT_MASK)
@@ -522,7 +531,8 @@ class CarState(CarStateBase):
ret.cruiseState.standstill = False
else:
cp_cruise_info = cp_cam if self.CP.flags & HyundaiFlags.CANFD_CAMERA_SCC else cp
ret.cruiseState.available = cp_cruise_info.vl["SCC_CONTROL"]["MainMode_ACC"] == 1
ret.cruiseState.available = get_canfd_cruise_available(
self.CP, cp, cp_cruise_info.vl["SCC_CONTROL"]["MainMode_ACC"] == 1)
ret.cruiseState.enabled = cp_cruise_info.vl["SCC_CONTROL"]["ACCMode"] in (1, 2)
ret.cruiseState.standstill = cp_cruise_info.vl["SCC_CONTROL"]["CRUISE_STANDSTILL"] == 1
ret.cruiseState.speed = cp_cruise_info.vl["SCC_CONTROL"]["VSetDis"] * speed_factor
@@ -8,7 +8,7 @@ hyundai_checksum = crcmod.mkCrcFun(0x11D, initCrc=0xFD, rev=False, xorOut=0xdf)
def create_lkas11(packer, frame, CP, apply_torque, steer_req,
torque_fault, lkas11, sys_warning, sys_state, enabled,
left_lane, right_lane,
left_lane_depart, right_lane_depart):
left_lane_depart, right_lane_depart, lka_icon):
values = {s: lkas11[s] for s in [
"CF_Lkas_LdwsActivemode",
"CF_Lkas_LdwsSysState",
@@ -51,7 +51,7 @@ def create_lkas11(packer, frame, CP, apply_torque, steer_req,
# FcwOpt_USM 2 = Green car + lanes
# FcwOpt_USM 1 = White car + lanes
# FcwOpt_USM 0 = No car + lanes
values["CF_Lkas_FcwOpt_USM"] = 2 if enabled else 1
values["CF_Lkas_FcwOpt_USM"] = lka_icon if CP.carFingerprint == CAR.GENESIS_G70_2020 else 2 if enabled else 1
# SysWarning 4 = keep hands on wheel
# SysWarning 5 = keep hands on wheel (red)
@@ -17,7 +17,8 @@ from opendbc.car.hyundai.carcontroller import CarController, Ioniq6LongitudinalT
should_reset_ev6_gt_line_longitudinal_tuning, reset_ev6_gt_line_longitudinal_tuning, \
direct_angle_request_allowed, get_angle_smoothing_alpha, \
should_use_ev6_gt_line_stop_direct_tracking
from opendbc.car.hyundai.carstate import CarState, decode_canfd_camera_lead, decode_ioniq_6_blindspot_radar_state
from opendbc.car.hyundai.carstate import CarState, decode_canfd_camera_lead, decode_ioniq_6_blindspot_radar_state, \
get_canfd_cruise_available
from opendbc.car.hyundai.interface import CarInterface, KIA_EV9_ACCEL_MAX
from opendbc.car.hyundai import hyundaican, hyundaicanfd
from opendbc.car.hyundai.hyundaicanfd import CanBus
@@ -480,13 +481,35 @@ class TestHyundaiFingerprint:
CC = SimpleNamespace(enabled=True, cruiseControl=SimpleNamespace(cancel=False, resume=False))
actuators = SimpleNamespace(longControlState=LongCtrlState.off)
msgs = controller.create_can_msgs(True, 100, False, 0.0, 0.0, False, hud_control, actuators, CS, CC, 2)
msgs = controller.create_can_msgs(True, 100, False, 0.0, 0.0, False, hud_control, actuators, CS, CC, 2, 2)
msg_addrs_buses = {(addr, bus) for addr, _, bus in msgs}
assert (0x50, 0) in msg_addrs_buses
assert (0x2A4, 0) in msg_addrs_buses
assert not ({0x340, 0x364} & {addr for addr, _, _ in msgs})
def test_g70_aol_uses_active_lkas_icon(self):
CP = CarInterface.get_params(CAR.GENESIS_G70_2020, gen_empty_fingerprint(), [], False, False, False, None)
controller = CarController(DBC[CP.carFingerprint], CP)
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("LKAS11", 0)], 0)
hud_control = SimpleNamespace(
visualAlert=CarControl.HUDControl.VisualAlert.none,
leftLaneVisible=True,
rightLaneVisible=True,
leftLaneDepart=False,
rightLaneDepart=False,
)
CS = SimpleNamespace(lkas11=parser.vl["LKAS11"])
CC = SimpleNamespace(enabled=False, cruiseControl=SimpleNamespace(cancel=False, resume=False))
actuators = SimpleNamespace(longControlState=LongCtrlState.off)
msgs = controller.create_can_msgs(True, 100, False, 0.0, 0.0, False, hud_control, actuators, CS, CC, 2, 0)
lkas11 = next(msg for msg in msgs if msg[0] == 0x340)
parser.update([(1, [lkas11])])
assert parser.vl["LKAS11"]["CF_Lkas_FcwOpt_USM"] == 2
@pytest.mark.parametrize("candidate", (CAR.HYUNDAI_ELANTRA_2024, CAR.HYUNDAI_ELANTRA_HEV_2024))
def test_hyundai_can_refresh_platforms_use_refresh_dbc_and_safety_param(self, candidate):
CP = CarInterface.get_params(candidate, gen_empty_fingerprint(), [], False, False, False, None)
@@ -728,6 +751,18 @@ class TestHyundaiFingerprint:
ret.buttonEvents = [structs.CarState.ButtonEvent(pressed=True, type=ButtonType.mainCruise)]
assert not CarState.update_main_cruise(car_state, ret)
def test_ev9_stock_fallback_uses_tcs_cruise_availability(self):
CP = SimpleNamespace(carFingerprint=CAR.KIA_EV9, openpilotLongitudinalControl=False)
cp = SimpleNamespace(vl={"TCS": {"ACCEnable": 0}})
assert get_canfd_cruise_available(CP, cp, False)
cp.vl["TCS"]["ACCEnable"] = 1
assert not get_canfd_cruise_available(CP, cp, True)
other_cp = SimpleNamespace(carFingerprint=CAR.HYUNDAI_IONIQ_6, openpilotLongitudinalControl=False)
assert not get_canfd_cruise_available(other_cp, cp, False)
def test_palisade_2023_cancel_release_enables_from_standby(self):
toggles = get_test_toggles()
CP = CarInterface.get_params(CAR.HYUNDAI_PALISADE_2023, gen_empty_fingerprint(), [], True, False, False, toggles)
@@ -35,7 +35,7 @@ class CarController(CarControllerBase):
self.packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
self.main_bus = CanBus.main_for_cp(CP)
self.angle_bus = CanBus.angle_for_cp(CP)
self.status_bus = CanBus.camera if CP.flags & SubaruFlags.D_PLATFORM else CanBus.main
self.status_bus = CanBus.main
if CP.flags & SubaruFlags.LKAS_ANGLE:
self.VM = VehicleModel(get_safety_CP())
+1 -1
View File
@@ -21,7 +21,7 @@ class CarState(CarStateBase):
cp_cam = can_parsers[Bus.cam]
cp_alt = can_parsers[Bus.alt]
cp_main = can_parsers[Bus.main] if self.CP.flags & SubaruFlags.D_PLATFORM else cp
cp_angle = cp_cam if self.CP.flags & SubaruFlags.D_PLATFORM else cp
cp_angle = cp_main if self.CP.flags & SubaruFlags.D_PLATFORM else cp
ret = structs.CarState()
throttle_msg = cp.vl["Throttle"] if not (self.CP.flags & SubaruFlags.HYBRID) else cp_alt.vl["Throttle_Hybrid"]
@@ -37,15 +37,19 @@ FW_VERSIONS = {
CAR.SUBARU_ASCENT_2023: {
(Ecu.abs, 0x7b0, None): [
b'\xa5 #\x03\x00',
b'\xa5 %\x03\x01',
],
(Ecu.eps, 0x746, None): [
b'%\xc0\xd0\x11',
b'\x55\xc0\xd0\x10',
],
(Ecu.fwdCamera, 0x787, None): [
b'\x05!\x08\x1dK\x05!\x08\x01/',
b'\x17!\x08\x01A\x12!\x08\x00;',
],
(Ecu.engine, 0x7a2, None): [
b'\xe5,\xa0P\x07',
b'\x11,\xa00\x07',
],
(Ecu.transmission, 0x7a3, None): [
b'\x04\xfe\xf3\x00\x00',
@@ -99,6 +99,17 @@ class TestSubaruFingerprint:
assert exact
assert matches == {CAR.SUBARU_LEGACY_2025}
def test_ascent_2025_firmware(self):
car_fw = [
CarParams.CarFw(ecu=CarParams.Ecu.abs, fwVersion=b'\xa5 %\x03\x01', address=0x7b0, brand="subaru"),
CarParams.CarFw(ecu=CarParams.Ecu.eps, fwVersion=b'\x55\xc0\xd0\x10', address=0x746, brand="subaru"),
CarParams.CarFw(ecu=CarParams.Ecu.fwdCamera, fwVersion=b'\x17!\x08\x01A\x12!\x08\x00;', address=0x787, brand="subaru"),
CarParams.CarFw(ecu=CarParams.Ecu.engine, fwVersion=b'\x11,\xa00\x07', address=0x7a2, brand="subaru"),
]
exact, matches = match_fw_to_car(car_fw, "4S4WMAAD9S3414980", allow_fuzzy=False, log=False)
assert exact
assert matches == {CAR.SUBARU_ASCENT_2023}
ANGLE_PLATFORMS = (
CAR.SUBARU_FORESTER_2022,
@@ -136,13 +147,13 @@ def test_outback_2023_uses_d_platform_bus_layout():
assert CP.flags & SubaruFlags.D_PLATFORM
assert CP.safetyConfigs[0].safetyParam & SubaruSafetyFlags.D_PLATFORM
assert CanBus.main_for_cp(CP) == CanBus.alt
assert CanBus.angle_for_cp(CP) == CanBus.camera
assert CanBus.angle_for_cp(CP) == CanBus.main
assert parsers[Bus.pt].bus == CanBus.alt
assert parsers[Bus.cam].bus == CanBus.camera
assert parsers[Bus.alt].bus == CanBus.alt
assert parsers[Bus.main].bus == CanBus.main
assert controller.angle_bus == CanBus.camera
assert controller.status_bus == CanBus.camera
assert controller.angle_bus == CanBus.main
assert controller.status_bus == CanBus.main
def test_legacy_2025_uses_d_platform_bus_layout():
@@ -153,13 +164,30 @@ def test_legacy_2025_uses_d_platform_bus_layout():
assert CP.flags & SubaruFlags.D_PLATFORM
assert CP.safetyConfigs[0].safetyParam & SubaruSafetyFlags.D_PLATFORM
assert CanBus.main_for_cp(CP) == CanBus.alt
assert CanBus.angle_for_cp(CP) == CanBus.camera
assert CanBus.angle_for_cp(CP) == CanBus.main
assert parsers[Bus.pt].bus == CanBus.alt
assert parsers[Bus.cam].bus == CanBus.camera
assert parsers[Bus.alt].bus == CanBus.alt
assert parsers[Bus.main].bus == CanBus.main
assert controller.angle_bus == CanBus.camera
assert controller.status_bus == CanBus.camera
assert controller.angle_bus == CanBus.main
assert controller.status_bus == CanBus.main
def test_ascent_2023_uses_d_platform_bus_layout():
CP = CarInterface.get_non_essential_params(CAR.SUBARU_ASCENT_2023)
parsers = CarState.get_can_parsers(CP)
controller = CarController({}, CP)
assert CP.flags & SubaruFlags.D_PLATFORM
assert CP.safetyConfigs[0].safetyParam & SubaruSafetyFlags.D_PLATFORM
assert CanBus.main_for_cp(CP) == CanBus.alt
assert CanBus.angle_for_cp(CP) == CanBus.main
assert parsers[Bus.pt].bus == CanBus.alt
assert parsers[Bus.cam].bus == CanBus.camera
assert parsers[Bus.alt].bus == CanBus.alt
assert parsers[Bus.main].bus == CanBus.main
assert controller.angle_bus == CanBus.main
assert controller.status_bus == CanBus.main
def test_other_angle_platforms_keep_existing_bus_layout():
+5 -4
View File
@@ -113,8 +113,9 @@ class CanBus:
@staticmethod
def angle_for_cp(CP):
# D-platform angle LKAS is exchanged with the camera ECU on the camera bus.
return CanBus.camera if CP.flags & SubaruFlags.D_PLATFORM else CanBus.main
# D-platform angle commands reach the EPS through the main bus. The camera
# bus still carries the stock angle message and remains receive-only.
return CanBus.main
class Footnote(Enum):
@@ -244,9 +245,9 @@ class CAR(Platforms):
flags=SubaruFlags.LKAS_ANGLE | SubaruFlags.D_PLATFORM,
)
SUBARU_ASCENT_2023 = SubaruGen2PlatformConfig(
[SubaruCarDocs("Subaru Ascent 2023", "All", car_parts=CarParts.common([CarHarness.subaru_d]))],
[SubaruCarDocs("Subaru Ascent 2023-25", "All", car_parts=CarParts.common([CarHarness.subaru_d]))],
SUBARU_ASCENT.specs,
flags=SubaruFlags.LKAS_ANGLE,
flags=SubaruFlags.LKAS_ANGLE | SubaruFlags.D_PLATFORM,
)
SUBARU_CROSSTREK_2025 = SubaruGen2PlatformConfig(
[SubaruCarDocs("Subaru Crosstrek 2025", "All", car_parts=CarParts.common([CarHarness.subaru_d]))],
+7 -7
View File
@@ -56,10 +56,10 @@
{MSG_SUBARU_ES_Distance, alt_bus, 8, .check_relay = false}, \
#define SUBARU_D_PLATFORM_ANGLE_TX_MSGS() \
{MSG_SUBARU_ES_LKAS_ANGLE, SUBARU_CAM_BUS, 8, .check_relay = true}, \
{MSG_SUBARU_ES_DashStatus, SUBARU_CAM_BUS, 8, .check_relay = true}, \
{MSG_SUBARU_ES_LKAS_State, SUBARU_CAM_BUS, 8, .check_relay = true}, \
{MSG_SUBARU_ES_Infotainment, SUBARU_CAM_BUS, 8, .check_relay = true}, \
{MSG_SUBARU_ES_LKAS_ANGLE, SUBARU_MAIN_BUS, 8, .check_relay = true}, \
{MSG_SUBARU_ES_DashStatus, SUBARU_MAIN_BUS, 8, .check_relay = true}, \
{MSG_SUBARU_ES_LKAS_State, SUBARU_MAIN_BUS, 8, .check_relay = true}, \
{MSG_SUBARU_ES_Infotainment, SUBARU_MAIN_BUS, 8, .check_relay = true}, \
#define SUBARU_COMMON_LONG_TX_MSGS(alt_bus) \
{MSG_SUBARU_ES_Distance, alt_bus, 8, .check_relay = true}, \
@@ -93,8 +93,8 @@
#define SUBARU_D_PLATFORM_ANGLE_RX_CHECKS() \
{.msg = {{MSG_SUBARU_Throttle, SUBARU_ALT_BUS, 8, 100U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{MSG_SUBARU_Steering_Torque, SUBARU_CAM_BUS, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{MSG_SUBARU_Steering_2, SUBARU_CAM_BUS, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{MSG_SUBARU_Steering_Torque, SUBARU_MAIN_BUS, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{MSG_SUBARU_Steering_2, SUBARU_MAIN_BUS, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{MSG_SUBARU_Wheel_Speeds, SUBARU_ALT_BUS, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{MSG_SUBARU_Brake_Status, SUBARU_ALT_BUS, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
{.msg = {{MSG_SUBARU_ES_Brake, SUBARU_ALT_BUS, 8, 50U, .max_counter = 15U, .ignore_quality_flag = true}, { 0 }, { 0 }}}, \
@@ -127,7 +127,7 @@ static uint32_t subaru_compute_checksum(const CANPacket_t *msg) {
static void subaru_rx_hook(const CANPacket_t *msg) {
const unsigned int alt_main_bus = subaru_gen2 ? SUBARU_ALT_BUS : SUBARU_MAIN_BUS;
const unsigned int status_bus = subaru_gen2 ? SUBARU_ALT_BUS : SUBARU_CAM_BUS;
const unsigned int steering_bus = subaru_d_platform ? SUBARU_CAM_BUS : SUBARU_MAIN_BUS;
const unsigned int steering_bus = SUBARU_MAIN_BUS;
const unsigned int main_bus = subaru_d_platform ? SUBARU_ALT_BUS : SUBARU_MAIN_BUS;
if ((msg->addr == MSG_SUBARU_Steering_Torque) && (msg->bus == steering_bus)) {
@@ -970,6 +970,10 @@ class SafetyTest(SafetyTestBase):
if 'TestSubaruDPlatformAngleSafety' in {attr, current_test} and \
'Angle' in attr and 'Angle' in current_test:
continue
if 'TestSubaruDPlatformAngleSafety' in {attr, current_test}:
# D-platform uses the same main-bus HUD messages as the other
# Subaru modes, so those modes cannot be distinguished by ID.
tx = list(filter(lambda m: not (m[1] == 0 and m[0] in [0x321, 0x322, 0x323]), tx))
if attr.startswith('TestSubaruPreglobal') and current_test.startswith('TestSubaruPreglobal'):
continue
if {attr, current_test}.issubset({'TestVolkswagenPqSafety', 'TestVolkswagenPqStockSafety', 'TestVolkswagenPqLongSafety'}):
@@ -343,15 +343,16 @@ class TestSubaruGen2AngleStockLongitudinalSafety(TestSubaruStockLongitudinalSafe
class TestSubaruDPlatformAngleSafety(TestSubaruStockLongitudinalSafetyBase, TestSubaruAngleSafetyBase):
FLAGS = SubaruSafetyFlags.GEN2 | SubaruSafetyFlags.LKAS_ANGLE | SubaruSafetyFlags.D_PLATFORM
ALT_MAIN_BUS = SUBARU_ALT_BUS
TX_MSGS = [[SubaruMsg.ES_LKAS_ANGLE, SUBARU_CAM_BUS],
[SubaruMsg.ES_DashStatus, SUBARU_CAM_BUS],
[SubaruMsg.ES_LKAS_State, SUBARU_CAM_BUS],
[SubaruMsg.ES_Infotainment, SUBARU_CAM_BUS],
TX_MSGS = [[SubaruMsg.ES_LKAS_ANGLE, SUBARU_MAIN_BUS],
[SubaruMsg.ES_DashStatus, SUBARU_MAIN_BUS],
[SubaruMsg.ES_LKAS_State, SUBARU_MAIN_BUS],
[SubaruMsg.ES_Infotainment, SUBARU_MAIN_BUS],
[SubaruMsg.ES_Distance, SUBARU_ALT_BUS]]
RELAY_MALFUNCTION_ADDRS = {SUBARU_CAM_BUS: (SubaruMsg.ES_LKAS_ANGLE, SubaruMsg.ES_DashStatus,
RELAY_MALFUNCTION_ADDRS = {SUBARU_MAIN_BUS: (SubaruMsg.ES_LKAS_ANGLE,
SubaruMsg.ES_DashStatus,
SubaruMsg.ES_LKAS_State, SubaruMsg.ES_Infotainment)}
FWD_BLACKLISTED_ADDRS = {
SUBARU_MAIN_BUS: [SubaruMsg.ES_LKAS_ANGLE, SubaruMsg.ES_DashStatus, SubaruMsg.ES_LKAS_State, SubaruMsg.ES_Infotainment],
SUBARU_CAM_BUS: [SubaruMsg.ES_LKAS_ANGLE, SubaruMsg.ES_DashStatus, SubaruMsg.ES_LKAS_State, SubaruMsg.ES_Infotainment],
}
def _torque_driver_msg(self, torque):
@@ -365,10 +366,10 @@ class TestSubaruDPlatformAngleSafety(TestSubaruStockLongitudinalSafetyBase, Test
self.safety.set_timer(self.angle_cmd_cnt * int(1e6 / self.LATERAL_FREQUENCY))
self.angle_cmd_cnt += 1
values = {"LKAS_Output": angle, "LKAS_Request": enabled, "SET_3": 3}
return self.packer.make_can_msg_safety("ES_LKAS_ANGLE", SUBARU_CAM_BUS, values)
return self.packer.make_can_msg_safety("ES_LKAS_ANGLE", SUBARU_MAIN_BUS, values)
def _angle_meas_msg(self, angle):
return self.packer.make_can_msg_safety("Steering_2", SUBARU_CAM_BUS, {"Steering_Angle": angle})
return self.packer.make_can_msg_safety("Steering_2", SUBARU_MAIN_BUS, {"Steering_Angle": angle})
class TestSubaruGen2LongitudinalSafety(TestSubaruLongitudinalSafetyBase, TestSubaruGen2TorqueSafetyBase):
+57 -30
View File
@@ -1,6 +1,8 @@
#!/usr/bin/env python3
import argparse
import codecs
import ctypes
import glob
import hashlib
import json
import os
@@ -42,8 +44,17 @@ MODEL_RUN_FREQ = 20
MODEL_CONTEXT_FREQ = 5
REPOSITORY_FILE_LIMIT = 100 * 1024 * 1024
DEFAULT_MULTIPART_SIZE = 95 * 1024 * 1024
USBGPU_PROBE_ATTEMPTS = 3
USBGPU_PROBE_TIMEOUT = 10
USBGPU_PROBE_ATTEMPTS = 10
USBGPU_PROBE_TIMEOUT = 2
USBDEVFS_CONTROL = 0xC0185500
USBGPU_VID_PIDS = (("add1", "0001"), ("3801", "0001"))
class _UsbdevfsControl(ctypes.Structure):
_fields_ = [("request_type", ctypes.c_uint8), ("request", ctypes.c_uint8),
("value", ctypes.c_uint16), ("index", ctypes.c_uint16),
("length", ctypes.c_uint16), ("timeout", ctypes.c_uint32),
("data", ctypes.c_void_p)]
def build_compile_env() -> dict[str, str]:
@@ -65,49 +76,65 @@ def build_compile_env() -> dict[str, str]:
return env
def _probe_external_gpu_link_once() -> tuple[bool, str]:
"""Probe the bridge without initializing tinygrad or resetting the USB device."""
import fcntl
diagnostics: list[str] = []
for path in glob.glob("/sys/bus/usb/devices/*"):
try:
vendor = Path(path, "idVendor").read_text().strip().lower()
product = Path(path, "idProduct").read_text().strip().lower()
if (vendor, product) not in USBGPU_VID_PIDS:
continue
bus = int(Path(path, "busnum").read_text())
device = int(Path(path, "devnum").read_text())
location = f"usb:{bus}-{device}"
fd = os.open(f"/dev/bus/usb/{bus:03d}/{device:03d}", os.O_RDWR)
except (OSError, ValueError) as exc:
diagnostics.append(f"{path}: open failed ({exc})")
continue
try:
fcntl.ioctl(fd, USBDEVFS_CONTROL, _UsbdevfsControl(0x40, 0xF3, 1, 0, 0, USBGPU_PROBE_TIMEOUT * 1000, None))
state = (ctypes.c_ubyte * 1)()
fcntl.ioctl(fd, USBDEVFS_CONTROL, _UsbdevfsControl(0xC0, 0xE4, 0xB450, 0, 1, 1000, ctypes.cast(state, ctypes.c_void_p)))
if state[0] == 0x78:
return True, f"{location}: LTSSM=0x78"
diagnostics.append(f"{location}: LTSSM=0x{state[0]:02X}")
except OSError as exc:
diagnostics.append(f"{location}: control probe failed ({exc})")
finally:
os.close(fd)
return False, diagnostics[-1] if diagnostics else "no ASM2464PD device found"
def wait_for_external_gpu(compile_env: dict[str, str]) -> bool:
"""Wait for the USB GPU's PCIe link before starting the large model build.
The dock can enumerate on USB before its PCIe link has finished training.
OpenPilot probes the tinygrad device in a short-lived process and retries;
doing the same here avoids making the model compiler lose its one chance at
initialization while keeping all non-GPU builds unchanged.
Probe the bridge's control endpoint directly, like upstream openpilot. Do
not instantiate tinygrad here: opening the GPU resets/claims the USB
interface, and doing that in a probe process can leave the bridge in a state
where the authoritative compiler cannot train the link.
"""
probe = [sys.executable, "-c", "from tinygrad.device import Device; Device[Device.DEFAULT]; import os; os._exit(0)"]
probe_env = {**compile_env, "DEV": "USB+AMD"}
del compile_env # retained in the public helper signature for callers/tests
diagnostics: list[str] = []
for attempt in range(USBGPU_PROBE_ATTEMPTS):
if attempt:
time.sleep(1)
try:
result = subprocess.run(
probe,
cwd=REPO_ROOT,
env=probe_env,
capture_output=True,
text=True,
timeout=USBGPU_PROBE_TIMEOUT,
check=False,
)
except subprocess.TimeoutExpired as exc:
partial = exc.stderr or exc.stdout or ""
if isinstance(partial, bytes):
partial = partial.decode(errors="replace")
partial = partial.strip()
diagnostics.append(
f"probe timed out after {USBGPU_PROBE_TIMEOUT}s" + (f": {partial[-2000:]}" if partial else "")
)
continue
if result.returncode == 0:
ready, detail = _probe_external_gpu_link_once()
except Exception as exc: # probe is advisory; compile_modeld remains authoritative
ready, detail = False, str(exc)
if ready:
return True
detail = (result.stderr or result.stdout).strip()
diagnostics.append((detail[-2000:] if detail else f"probe exited with status {result.returncode}"))
diagnostics.append(detail)
detail = diagnostics[-1] if diagnostics else "unknown error"
print(
f"Warning: external GPU probe did not become ready after {USBGPU_PROBE_ATTEMPTS} probes: {detail}\n"
f"Warning: external GPU link did not become ready after {USBGPU_PROBE_ATTEMPTS} probes: {detail}\n"
" Continuing; compile_modeld will perform the authoritative link wait and initialization."
)
return False
+15
View File
@@ -49,6 +49,9 @@ class VCruiseHelper:
}
self.button_hard_states = dict.fromkeys(self.button_timers, False)
self.button_change_states = {btn: {"standstill": False, "enabled": False} for btn in self.button_timers}
# Keep the confirmation button from leaking into cruise-speed handling when the
# planner clears the confirmation state between the press and release events.
self.confirmation_button_suppressed = set()
self.gm_cc_only = self.CP.carFingerprint in CC_ONLY_CAR and self.CP.flags & GMFlags.CC_LONG.value
self.redneck_non_pcm = bool(FPCP is not None and
@@ -120,6 +123,15 @@ class VCruiseHelper:
v_cruise_delta = 1. if is_metric else IMPERIAL_INCREMENT
for b in CS.buttonEvents:
event_button_type = b.type.raw
if event_button_type in self.button_timers:
if speed_limit_changed and b.pressed:
self.confirmation_button_suppressed.add(event_button_type)
elif not b.pressed and event_button_type in self.confirmation_button_suppressed:
self.confirmation_button_suppressed.remove(event_button_type)
return
for b in CS.buttonEvents:
if b.type.raw in self.button_timers and not b.pressed:
if self.button_timers[b.type.raw] > CRUISE_LONG_PRESS:
@@ -138,6 +150,9 @@ class VCruiseHelper:
if button_type is None:
return
if button_type in self.confirmation_button_suppressed:
return
# Don't adjust speed when pressing to confirm or deny speed limit changes
if speed_limit_changed:
return
+5
View File
@@ -2,6 +2,7 @@ from cereal import car
from opendbc.car import apply_hysteresis
from openpilot.common.constants import CV
from openpilot.common.realtime import DT_CTRL
from openpilot.selfdrive.car.cruise import V_CRUISE_UNSET
ButtonType = car.CarState.ButtonEvent.Type
@@ -50,6 +51,10 @@ def select_redneck_target_speed(v_cruise_kph: float, speed_cluster_ms: float,
target_speed_ms = float(speed_cluster_ms)
if slc_target_speed_ms > 0:
target_speed_ms = float(slc_target_speed_ms)
# SLC is an upper bound for the button-spammed stock setpoint. A driver-set
# speed below the posted target must still be able to slow the car down.
if 0 < v_cruise_kph < V_CRUISE_UNSET:
target_speed_ms = min(target_speed_ms, float(v_cruise_kph) * CV.KPH_TO_MS)
elif v_cruise_kph > 0:
target_speed_ms = float(v_cruise_kph) * CV.KPH_TO_MS
elif starpilot_target_speed_ms > 0:
+29
View File
@@ -330,6 +330,35 @@ class TestVCruiseHelper:
assert self.v_cruise_helper.v_cruise_kph == initial_v_cruise_kph
def test_speed_limit_confirmation_press_release_does_not_leak_after_acceptance(self):
for button_type in (ButtonType.accelCruise, ButtonType.decelCruise):
self.enable(V_CRUISE_INITIAL * CV.KPH_TO_MS, False)
initial_v_cruise_kph = self.v_cruise_helper.v_cruise_kph
pressed_cs = car.CarState(cruiseState={"available": True})
pressed_cs.buttonEvents = [ButtonEvent(type=button_type, pressed=True)]
self.v_cruise_helper.update_v_cruise(
pressed_cs,
enabled=True,
is_metric=False,
speed_limit_changed=True,
starpilot_toggles=self.starpilot_toggles,
)
# The planner may have consumed the confirmation by the time the physical
# button release arrives. The release must remain confirmation-only.
released_cs = car.CarState(cruiseState={"available": True})
released_cs.buttonEvents = [ButtonEvent(type=button_type, pressed=False)]
self.v_cruise_helper.update_v_cruise(
released_cs,
enabled=True,
is_metric=False,
speed_limit_changed=False,
starpilot_toggles=self.starpilot_toggles,
)
assert self.v_cruise_helper.v_cruise_kph == initial_v_cruise_kph
def test_stale_speed_limit_change_does_adjust_cruise(self):
self.enable(V_CRUISE_INITIAL * CV.KPH_TO_MS, False)
initial_v_cruise_kph = self.v_cruise_helper.v_cruise_kph
+16 -3
View File
@@ -170,8 +170,8 @@ class TestRedneckCruise(unittest.TestCase):
)
self.assertAlmostEqual(104.4 * CV.KPH_TO_MS, target_speed)
def test_target_speed_follows_resolved_slc_target(self):
for internal_mph, slc_mph in ((55.0, 65.0), (65.0, 55.0)):
def test_target_speed_respects_manual_lower_set_speed_with_slc(self):
for internal_mph, slc_mph, expected_mph in ((55.0, 65.0, 55.0), (65.0, 55.0, 55.0)):
with self.subTest(internal_mph=internal_mph, slc_mph=slc_mph):
target_speed = select_redneck_target_speed(
internal_mph * CV.MPH_TO_KPH,
@@ -182,7 +182,20 @@ class TestRedneckCruise(unittest.TestCase):
allow_plan_decrease=False,
slc_target_speed_ms=slc_mph * CV.MPH_TO_MS,
)
self.assertAlmostEqual(slc_mph * CV.MPH_TO_MS, target_speed)
self.assertAlmostEqual(expected_mph * CV.MPH_TO_MS, target_speed)
def test_target_speed_keeps_slc_limit_when_manual_set_speed_is_higher(self):
target_speed = select_redneck_target_speed(
75.0 * CV.MPH_TO_KPH,
65.0 * CV.MPH_TO_MS,
0.0,
[],
10,
allow_plan_decrease=False,
slc_target_speed_ms=65.0 * CV.MPH_TO_MS,
)
self.assertAlmostEqual(65.0 * CV.MPH_TO_MS, target_speed)
def test_card_target_speed_uses_longitudinal_acceleration(self):
sm = MagicMock()
+19 -3
View File
@@ -113,6 +113,7 @@ class LatControlTorque(LatControl):
self.is_ioniq_5 = CP.carFingerprint in IONIQ_5_CARS
self.is_ioniq_ev_old = CP.carFingerprint in IONIQ_EV_OLD_CARS
self.is_ioniq_6 = CP.carFingerprint in IONIQ_6_CARS
self.is_ioniq_6_2025 = is_ioniq_6_2025_model(CP)
self.is_sonata = CP.carFingerprint in SONATA_CARS
self.is_sonata_hybrid = CP.carFingerprint in SONATA_HYBRID_CARS
self.is_elantra_non_scc = CP.carFingerprint in ELANTRA_NON_SCC_CARS
@@ -251,9 +252,10 @@ class LatControlTorque(LatControl):
delay_frames = int(np.clip(lat_delay / self.dt, 1, self.request_buffer_len))
expected_lateral_accel = self.curvature_request_buffer[-delay_frames] * CS.vEgo ** 2
self.curvature_request_buffer.append(desired_curvature)
lateral_jerk_limit = RAM_1500_MAX_LAT_JERK_UP if self.is_ram_1500 else MAX_LAT_JERK_UP
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)
raw_lateral_jerk = np.clip(raw_lateral_jerk, -lateral_jerk_limit, lateral_jerk_limit)
desired_lateral_jerk = np.clip(self.jerk_filter.update(raw_lateral_jerk), -lateral_jerk_limit, lateral_jerk_limit)
gravity_adjusted_future_lateral_accel = future_desired_lateral_accel - roll_compensation
setpoint = expected_lateral_accel + desired_lateral_jerk * lat_delay
desired_lateral_accel_rate = (setpoint - self.prev_desired_lateral_accel) / self.dt
@@ -393,6 +395,8 @@ class LatControlTorque(LatControl):
friction_scale = get_ioniq_6_friction_scale(CS.vEgo, setpoint, desired_lateral_jerk)
friction_scale = 1.0 + ((friction_scale - 1.0) * ioniq_6_center_taper)
friction_scale *= get_ioniq_6_friction_center_fade_scale(setpoint, CS.vEgo)
if self.is_ioniq_6_2025:
friction_scale *= IONIQ_6_2025_FRICTION_SCALE_MULT
elif sonata_active:
ff *= get_sonata_ff_scale(setpoint, desired_lateral_jerk, CS.vEgo) * sonata_center_taper
elif sonata_hybrid_active:
@@ -416,6 +420,8 @@ class LatControlTorque(LatControl):
elif kia_carnival_active:
friction_threshold = get_kia_carnival_friction_threshold(CS.vEgo, setpoint, desired_lateral_jerk)
friction_scale *= get_kia_carnival_friction_center_fade_scale(setpoint, CS.vEgo)
elif self.is_kona_non_scc:
friction_threshold = get_kona_non_scc_friction_threshold(CS.vEgo, setpoint, desired_lateral_jerk)
elif tucson_4th_gen_active:
friction_threshold = get_tucson_4th_gen_friction_threshold(CS.vEgo, setpoint, desired_lateral_jerk)
elif self.is_silverado:
@@ -442,7 +448,14 @@ class LatControlTorque(LatControl):
if trailer_load_kg > 0.0:
ff *= get_trailer_lateral_ff_scale(trailer_load_kg, CS.vEgo, setpoint)
friction_scale *= get_trailer_lateral_friction_scale(trailer_load_kg, CS.vEgo, setpoint)
vehicle_friction_jerk_deadzone = IONIQ_6_FRICTION_JERK_DEADZONE if ioniq_6_active else 0.0
if ioniq_6_active:
vehicle_friction_jerk_deadzone = (
IONIQ_6_2025_FRICTION_JERK_DEADZONE if self.is_ioniq_6_2025 else IONIQ_6_FRICTION_JERK_DEADZONE
)
elif prius_active:
vehicle_friction_jerk_deadzone = get_prius_friction_jerk_deadzone(CS.vEgo, setpoint)
else:
vehicle_friction_jerk_deadzone = 0.0
friction_jerk_deadzone = get_center_chatter_friction_jerk_deadzone(
CS.vEgo, setpoint, vehicle_friction_jerk_deadzone
)
@@ -486,6 +499,8 @@ class LatControlTorque(LatControl):
if ioniq_6_active:
output_torque *= get_ioniq_6_highway_output_taper_scale(setpoint, CS.vEgo)
output_torque *= get_ioniq_6_highway_transition_output_taper_scale(setpoint, desired_lateral_jerk, CS.vEgo)
if self.is_ioniq_6_2025:
output_torque *= get_ioniq_6_2025_center_output_scale(setpoint, CS.vEgo)
elif self.is_ram_1500 and output_torque * setpoint > 0.0:
output_torque *= get_ram_1500_transition_output_scale(setpoint, desired_lateral_jerk, CS.vEgo)
elif self.is_kona_non_scc:
@@ -500,6 +515,7 @@ class LatControlTorque(LatControl):
output_torque *= get_sienna_4th_gen_high_speed_output_taper_scale(CS.vEgo)
elif prius_active:
output_torque *= prius_center_taper
output_torque *= get_prius_high_speed_output_taper_scale(setpoint, CS.vEgo)
elif volt_standard_test_active:
output_torque *= volt_standard_center_taper
elif volt_plexy_test_active:
@@ -104,6 +104,24 @@ IONIQ_EV_OLD_CARS = (
IONIQ_6_CARS = (
HYUNDAI_CAR.HYUNDAI_IONIQ_6,
)
def is_ioniq_6_2025_model(CP) -> bool:
"""Identify the newer Ioniq 6 firmware without changing the legacy 2023 path."""
if getattr(CP, "carFingerprint", None) not in IONIQ_6_CARS:
return False
versions = []
try:
for fw in CP.carFw:
value = fw.fwVersion
versions.append(value.decode("ascii", errors="ignore") if isinstance(value, bytes) else str(value))
except (AttributeError, TypeError, ValueError):
return False
return any("230915" in version for version in versions) and any("240206" in version for version in versions)
SONATA_HYBRID_CARS = (
HYUNDAI_CAR.HYUNDAI_SONATA_HYBRID,
)
@@ -722,6 +740,14 @@ IONIQ_6_FRICTION_CENTER_FADE_LAT = 0.15
IONIQ_6_FRICTION_CENTER_FADE_LAT_WIDTH = 0.06
IONIQ_6_FRICTION_CENTER_FADE_SPEED = 18.0
IONIQ_6_FRICTION_CENTER_FADE_SPEED_WIDTH = 2.5
# Newer Ioniq 6 highway center-chatter correction; activation is firmware-gated.
IONIQ_6_2025_FRICTION_SCALE_MULT = 0.80
IONIQ_6_2025_FRICTION_JERK_DEADZONE = 0.45
IONIQ_6_2025_CENTER_OUTPUT_TAPER_MAX = 0.18
IONIQ_6_2025_CENTER_OUTPUT_TAPER_LAT = 0.35
IONIQ_6_2025_CENTER_OUTPUT_TAPER_LAT_WIDTH = 0.10
IONIQ_6_2025_CENTER_OUTPUT_TAPER_SPEED = 22.0
IONIQ_6_2025_CENTER_OUTPUT_TAPER_SPEED_WIDTH = 2.5
IONIQ_6_HEAVY_DIRECTIONAL_TAPER_LAT_START = 0.90
IONIQ_6_HEAVY_DIRECTIONAL_TAPER_LAT_WIDTH = 0.18
IONIQ_6_HEAVY_DIRECTIONAL_TAPER_BASE_LEFT = 0.03
@@ -774,7 +800,7 @@ KIA_EV6_CENTER_TAPER_LAT = 0.16
KIA_EV6_CENTER_TAPER_LAT_WIDTH = 0.04
KIA_EV6_CENTER_TAPER_SPEED = 17.0
KIA_EV6_CENTER_TAPER_SPEED_WIDTH = 2.8
KIA_EV6_CENTER_FRICTION_THRESHOLD_GAIN = 0.08
KIA_EV6_CENTER_FRICTION_THRESHOLD_GAIN = 0.14
KIA_EV6_CENTER_FRICTION_THRESHOLD_LAT = 0.30
KIA_EV6_CENTER_FRICTION_THRESHOLD_LAT_WIDTH = 0.07
KIA_EV6_CENTER_FRICTION_THRESHOLD_SPEED = 18.0
@@ -816,7 +842,7 @@ PRIUS_TURN_IN_FRICTION_BOOST_RIGHT = 0.06
PRIUS_UNWIND_FRICTION_REDUCTION_LEFT = 0.22
PRIUS_UNWIND_FRICTION_REDUCTION_RIGHT = 0.30
PRIUS_CENTER_TAPER_MAX = 0.15
PRIUS_CENTER_TAPER_LAT = 0.16
PRIUS_CENTER_TAPER_LAT = 0.24
PRIUS_CENTER_TAPER_LAT_WIDTH = 0.035
PRIUS_CENTER_TAPER_SPEED = 18.0
PRIUS_CENTER_TAPER_SPEED_WIDTH = 2.2
@@ -825,6 +851,16 @@ PRIUS_CENTER_FRICTION_THRESHOLD_LAT = 0.30
PRIUS_CENTER_FRICTION_THRESHOLD_LAT_WIDTH = 0.07
PRIUS_CENTER_FRICTION_THRESHOLD_SPEED = 18.0
PRIUS_CENTER_FRICTION_THRESHOLD_SPEED_WIDTH = 2.2
PRIUS_FRICTION_JERK_DEADZONE_MAX = 0.24
PRIUS_FRICTION_JERK_DEADZONE_LAT = 0.30
PRIUS_FRICTION_JERK_DEADZONE_LAT_WIDTH = 0.07
PRIUS_FRICTION_JERK_DEADZONE_SPEED = 18.0
PRIUS_FRICTION_JERK_DEADZONE_SPEED_WIDTH = 2.2
PRIUS_HIGH_SPEED_OUTPUT_TAPER_MAX = 0.06
PRIUS_HIGH_SPEED_OUTPUT_TAPER_LAT = 0.30
PRIUS_HIGH_SPEED_OUTPUT_TAPER_LAT_WIDTH = 0.35
PRIUS_HIGH_SPEED_OUTPUT_TAPER_SPEED = 22.0
PRIUS_HIGH_SPEED_OUTPUT_TAPER_SPEED_WIDTH = 2.5
CAMRY_CENTER_FRICTION_THRESHOLD_GAIN = 0.09
CAMRY_CENTER_FRICTION_THRESHOLD_LAT = 0.22
@@ -873,6 +909,9 @@ SIENNA_4TH_GEN_FRICTION_SPEED_ONSET = 3.0
SIENNA_4TH_GEN_FRICTION_SPEED_WIDTH = 1.5
SIENNA_4TH_GEN_FRICTION_SPEED_MAX = 14.0
SIENNA_4TH_GEN_FRICTION_SPEED_MAX_WIDTH = 2.0
SIENNA_4TH_GEN_HIGH_SPEED_FRICTION_THRESHOLD_GAIN = 0.14
SIENNA_4TH_GEN_HIGH_SPEED_FRICTION_SPEED_ONSET = 18.0
SIENNA_4TH_GEN_HIGH_SPEED_FRICTION_SPEED_WIDTH = 2.5
SIENNA_4TH_GEN_CENTER_TAPER_MAX = 0.12
SIENNA_4TH_GEN_CENTER_TAPER_LAT = 0.20
SIENNA_4TH_GEN_CENTER_TAPER_LAT_WIDTH = 0.06
@@ -910,13 +949,14 @@ RAM_1500_TRANSITION_JERK_ONSET = 0.35
RAM_1500_TRANSITION_JERK_FULL = 1.10
RAM_1500_TRANSITION_LAT_FADE_START = 0.65
RAM_1500_TRANSITION_LAT_FADE_END = 1.85
RAM_1500_MAX_LAT_JERK_UP = 2.10
RAM_1500_PHASE_SCALE = 0.12
RAM_1500_PHASE_SPEED_ONSET = 8.0
RAM_1500_PHASE_SPEED_FULL = 15.0
RAM_1500_PHASE_LAT_ONSET = 0.25
RAM_1500_PHASE_LAT_WIDTH = 0.12
RAM_1500_TURN_IN_FF_BOOST = 0.06
RAM_1500_UNWIND_FF_REDUCTION = 0.10
RAM_1500_UNWIND_FF_REDUCTION = 0.05
# The Kona route is exceptionally accurate below highway speed, but Pop V2
# reverses the requested lateral acceleration roughly once per second at
@@ -934,6 +974,11 @@ KONA_NON_SCC_CENTER_TAPER_MAX = 0.14
KONA_NON_SCC_CENTER_TAPER_LAT = 0.28
KONA_NON_SCC_CENTER_TAPER_SPEED_ONSET = 12.0
KONA_NON_SCC_CENTER_TAPER_SPEED_FULL = 24.0
KONA_NON_SCC_CENTER_FRICTION_THRESHOLD_GAIN = 0.14
KONA_NON_SCC_CENTER_FRICTION_THRESHOLD_LAT = 0.28
KONA_NON_SCC_CENTER_FRICTION_THRESHOLD_LAT_WIDTH = 0.07
KONA_NON_SCC_CENTER_FRICTION_THRESHOLD_SPEED_ONSET = 11.0
KONA_NON_SCC_CENTER_FRICTION_THRESHOLD_SPEED_WIDTH = 2.5
TRAILER_LOAD_FULL_ASSIST_KG = 15000.0 * CV.LB_TO_KG
TRAILER_LATERAL_MIN_SPEED = 15.0 * CV.MPH_TO_MS
@@ -1194,6 +1239,22 @@ def get_prius_center_taper_scale(desired_lateral_accel: float, v_ego: float) ->
return 1.0 - reduction
def get_prius_friction_jerk_deadzone(v_ego: float, desired_lateral_accel: float) -> float:
speed_weight = _prius_sigmoid((v_ego - PRIUS_FRICTION_JERK_DEADZONE_SPEED) /
PRIUS_FRICTION_JERK_DEADZONE_SPEED_WIDTH)
center_weight = _prius_sigmoid((PRIUS_FRICTION_JERK_DEADZONE_LAT - abs(desired_lateral_accel)) /
PRIUS_FRICTION_JERK_DEADZONE_LAT_WIDTH)
return PRIUS_FRICTION_JERK_DEADZONE_MAX * speed_weight * center_weight
def get_prius_high_speed_output_taper_scale(desired_lateral_accel: float, v_ego: float) -> float:
speed_weight = _prius_sigmoid((v_ego - PRIUS_HIGH_SPEED_OUTPUT_TAPER_SPEED) /
PRIUS_HIGH_SPEED_OUTPUT_TAPER_SPEED_WIDTH)
curve_weight = _prius_sigmoid((abs(desired_lateral_accel) - PRIUS_HIGH_SPEED_OUTPUT_TAPER_LAT) /
PRIUS_HIGH_SPEED_OUTPUT_TAPER_LAT_WIDTH)
return 1.0 - PRIUS_HIGH_SPEED_OUTPUT_TAPER_MAX * speed_weight * curve_weight
def get_camry_friction_threshold(v_ego: float, desired_lateral_accel: float = 0.0,
desired_lateral_jerk: float = 0.0) -> float:
del desired_lateral_jerk
@@ -1312,8 +1373,13 @@ def get_sienna_4th_gen_friction_threshold(v_ego: float, desired_lateral_accel: f
del desired_lateral_jerk
center_weight = _sigmoid((SIENNA_4TH_GEN_FRICTION_CENTER_LAT - abs(desired_lateral_accel)) /
SIENNA_4TH_GEN_FRICTION_CENTER_LAT_WIDTH)
high_speed_weight = _sigmoid((v_ego - SIENNA_4TH_GEN_HIGH_SPEED_FRICTION_SPEED_ONSET) /
SIENNA_4TH_GEN_HIGH_SPEED_FRICTION_SPEED_WIDTH)
return get_standard_friction_threshold(v_ego) * (
1.0 + SIENNA_4TH_GEN_FRICTION_THRESHOLD_GAIN * center_weight * _sienna_4th_gen_friction_speed_weight(v_ego)
1.0 + center_weight * (
SIENNA_4TH_GEN_FRICTION_THRESHOLD_GAIN * _sienna_4th_gen_friction_speed_weight(v_ego) +
SIENNA_4TH_GEN_HIGH_SPEED_FRICTION_THRESHOLD_GAIN * high_speed_weight
)
)
@@ -1392,6 +1458,18 @@ def get_kona_non_scc_highway_transition_output_scale(desired_lateral_accel: floa
return 1.0 - (taper_max * speed_weight * jerk_weight * lat_weight)
def get_kona_non_scc_friction_threshold(v_ego: float, desired_lateral_accel: float = 0.0,
desired_lateral_jerk: float = 0.0) -> float:
del desired_lateral_jerk
speed_weight = _sigmoid((v_ego - KONA_NON_SCC_CENTER_FRICTION_THRESHOLD_SPEED_ONSET) /
KONA_NON_SCC_CENTER_FRICTION_THRESHOLD_SPEED_WIDTH)
center_weight = _sigmoid((KONA_NON_SCC_CENTER_FRICTION_THRESHOLD_LAT - abs(desired_lateral_accel)) /
KONA_NON_SCC_CENTER_FRICTION_THRESHOLD_LAT_WIDTH)
return get_standard_friction_threshold(v_ego) * (
1.0 + KONA_NON_SCC_CENTER_FRICTION_THRESHOLD_GAIN * speed_weight * center_weight
)
def get_kona_non_scc_center_taper_scale(desired_lateral_accel: float, v_ego: float) -> float:
speed_weight = float(np.interp(v_ego, [KONA_NON_SCC_CENTER_TAPER_SPEED_ONSET, KONA_NON_SCC_CENTER_TAPER_SPEED_FULL], [0.0, 1.0]))
center_weight = float(np.interp(abs(desired_lateral_accel), [0.0, KONA_NON_SCC_CENTER_TAPER_LAT], [1.0, 0.0]))
@@ -2684,6 +2762,14 @@ def get_ioniq_6_friction_center_fade_scale(desired_lateral_accel: float, v_ego:
return 1.0 - IONIQ_6_FRICTION_CENTER_FADE_MAX * speed_weight * center_weight
def get_ioniq_6_2025_center_output_scale(desired_lateral_accel: float, v_ego: float) -> float:
speed_weight = _ioniq_6_sigmoid((v_ego - IONIQ_6_2025_CENTER_OUTPUT_TAPER_SPEED) /
IONIQ_6_2025_CENTER_OUTPUT_TAPER_SPEED_WIDTH)
center_weight = _ioniq_6_sigmoid((IONIQ_6_2025_CENTER_OUTPUT_TAPER_LAT - abs(desired_lateral_accel)) /
IONIQ_6_2025_CENTER_OUTPUT_TAPER_LAT_WIDTH)
return 1.0 - IONIQ_6_2025_CENTER_OUTPUT_TAPER_MAX * speed_weight * center_weight
def get_ioniq_6_center_taper_scale(desired_lateral_accel: float, v_ego: float) -> float:
speed_weight = _ioniq_6_sigmoid((v_ego - IONIQ_6_CENTER_TAPER_SPEED) / IONIQ_6_CENTER_TAPER_SPEED_WIDTH)
center_weight = _ioniq_6_sigmoid((IONIQ_6_CENTER_TAPER_LAT - abs(desired_lateral_accel)) / IONIQ_6_CENTER_TAPER_LAT_WIDTH)
+3
View File
@@ -286,6 +286,9 @@ class LongControl:
else: # LongCtrlState.pid
a_target = self.vehicle_tuning.shape_gm_truck_accel_target(a_target, CS.vEgo, should_stop)
a_target = self.vehicle_tuning.shape_toyota_corolla_accel_target(
a_target, CS.vEgo, should_stop, self.last_output_accel,
)
a_target = self.vehicle_tuning.shape_toyota_sienna_accel_target(
a_target, CS.vEgo, should_stop, leads=leads,
)
@@ -34,6 +34,11 @@ TOYOTA_SIENNA_COMFORT_FILTER_MIN_TTC = 4.5
TOYOTA_SIENNA_COMFORT_FILTER_MAX_CLOSING_SPEED = 4.0
TOYOTA_SIENNA_COMFORT_FILTER_MAX_LEAD_BRAKE = 2.5
TOYOTA_SIENNA_COMFORT_FILTER_BRAKE_BYPASS = -2.5
TOYOTA_COROLLA_TARGET_FILTER_MAX_SPEED = 3.0
TOYOTA_COROLLA_TARGET_FILTER_UP_TAU = 0.30
TOYOTA_COROLLA_TARGET_FILTER_DOWN_TAU = 0.18
TOYOTA_COROLLA_TARGET_FILTER_BRAKE_BYPASS = -0.75
TOYOTA_COROLLA_TARGET_FILTER_DROP_BYPASS = 0.45
VOLT_CRUISE_INTEGRATOR_MIN_SPEED = 8.0
VOLT_CRUISE_INTEGRATOR_TARGET_MAX = 0.12
VOLT_CRUISE_INTEGRATOR_ERROR_MAX = 0.12
@@ -106,6 +111,10 @@ class LongControlVehicleTuning:
CP.brand == "toyota" and
getattr(CP, "carFingerprint", None) == TOYOTA_CAR.TOYOTA_SIENNA_4TH_GEN
)
self.is_toyota_corolla_tss2 = bool(
CP.brand == "toyota" and
getattr(CP, "carFingerprint", None) == TOYOTA_CAR.TOYOTA_COROLLA_TSS2
)
self.is_bolt_acc_pedal_friction_car = bool(
CP.brand == "gm" and
CP.enableGasInterceptorDEPRECATED and
@@ -121,6 +130,8 @@ class LongControlVehicleTuning:
self.gm_truck_target_filter_initialized = False
self.toyota_sienna_filtered_a_target = 0.0
self.toyota_sienna_target_filter_initialized = False
self.toyota_corolla_filtered_a_target = 0.0
self.toyota_corolla_target_filter_initialized = False
self.bolt_start_handoff_frames = 0
def apply_bolt_start_handoff_floor(self, output_accel, last_output_accel, a_target, v_ego,
@@ -226,6 +237,31 @@ class LongControlVehicleTuning:
self.toyota_sienna_filtered_a_target += alpha * (float(a_target) - self.toyota_sienna_filtered_a_target)
return self.toyota_sienna_filtered_a_target
def shape_toyota_corolla_accel_target(self, a_target, v_ego, should_stop, last_output_accel):
"""Smooth low-speed Corolla TSS2 stop releases without delaying hard braking."""
if not self.is_toyota_corolla_tss2 or should_stop or v_ego >= TOYOTA_COROLLA_TARGET_FILTER_MAX_SPEED:
self.toyota_corolla_target_filter_initialized = False
return a_target
if not self.toyota_corolla_target_filter_initialized:
self.toyota_corolla_filtered_a_target = float(last_output_accel)
self.toyota_corolla_target_filter_initialized = True
bypass_filter = (
a_target <= TOYOTA_COROLLA_TARGET_FILTER_BRAKE_BYPASS or
a_target < self.toyota_corolla_filtered_a_target - TOYOTA_COROLLA_TARGET_FILTER_DROP_BYPASS
)
if bypass_filter:
self.toyota_corolla_filtered_a_target = float(a_target)
return float(a_target)
tau = (TOYOTA_COROLLA_TARGET_FILTER_DOWN_TAU
if a_target < self.toyota_corolla_filtered_a_target
else TOYOTA_COROLLA_TARGET_FILTER_UP_TAU)
alpha = DT_CTRL / (tau + DT_CTRL)
self.toyota_corolla_filtered_a_target += alpha * (float(a_target) - self.toyota_corolla_filtered_a_target)
return self.toyota_corolla_filtered_a_target
def get_integrator_freeze(self, last_output_accel, a_target, error, v_ego, accel_limits):
volt_test_tune_handoff = self.is_volt and testing_ground.use_2
@@ -786,7 +786,7 @@ class LongitudinalMpc:
def get_vision_follow_cruise_hold(self, prev_source, lead_one, lead_two,
lead_0_obstacle, lead_1_obstacle, cruise_obstacle,
v_ego, t_follow, tracking_lead):
v_ego, t_follow, tracking_lead, *, early_follow=False):
if not tracking_lead or prev_source not in ("lead0", "lead1"):
return None
@@ -795,7 +795,24 @@ class LongitudinalMpc:
return None
if float(getattr(prev_lead, "modelProb", 0.0)) < VISION_FOLLOW_CRUISE_HOLD_MIN_MODEL_PROB:
return None
if self.get_stable_follow_cruise_hysteresis(prev_lead, v_ego, t_follow) <= 0.0:
if early_follow:
# Silverado admits a credible centered vision lead before it reaches the
# normal matched-follow window. Hold that lead through the harmless
# cruise/lead crossover, but never through a closing or braking lead.
relative_speed = float(v_ego) - float(prev_lead.vLead)
actual_headway = float(prev_lead.dRel) / max(float(v_ego), 1e-3)
if (
float(v_ego) < 18.0 or
abs(relative_speed) > 2.5 or
actual_headway < 0.95 or
actual_headway > 2.35 or
float(prev_lead.dRel) > 130.0 or
abs(float(getattr(prev_lead, "yRel", 0.0))) > 1.2 or
max(0.0, -float(getattr(prev_lead, "aLeadK", 0.0))) > 0.35 or
float(getattr(prev_lead, "modelProb", 0.0)) < 0.95
):
return None
elif self.get_stable_follow_cruise_hysteresis(prev_lead, v_ego, t_follow) <= 0.0:
return None
prev_lead_obstacle = float(lead_0_obstacle if prev_source == "lead0" else lead_1_obstacle)
@@ -845,7 +862,7 @@ class LongitudinalMpc:
def update(self, radarstate, v_cruise, x, v, a, j, danger_factor, t_follow,
personality=log.LongitudinalPersonality.standard, tracking_lead=True,
optional_far_lead_comfort=True, smooth_duplicate_vision=False,
stop_x=None):
stop_x=None, silverado_early_follow=False):
v_ego = self.x0[1]
lead_one = radarstate.leadOne
lead_two = radarstate.leadTwo
@@ -939,6 +956,7 @@ class LongitudinalMpc:
v_ego,
t_follow,
tracking_lead,
early_follow=silverado_early_follow,
)
self.source = sticky_source or candidate_source
@@ -20,6 +20,7 @@ from openpilot.selfdrive.controls.lib.lead_follow_policy import apply as apply_f
from openpilot.selfdrive.controls.lib.lead_follow_policy import is_nonurgent_duplicate_vision_follow
from openpilot.selfdrive.controls.lib.longitudinal_vehicle_tunes import (
get_far_follow_output_slew_rates,
get_follow_prebrake_min_headway,
is_gm_silverado_early_follow_lead,
get_toyota_sienna_post_departure_restop_cap,
get_untracked_slow_lead_decel_scale,
@@ -2183,7 +2184,8 @@ class LongitudinalPlanner:
personality=personality, tracking_lead=lead_control_active,
optional_far_lead_comfort=True,
smooth_duplicate_vision=nonurgent_duplicate_vision_follow and not panic_bypass,
stop_x=force_stop_x)
stop_x=force_stop_x,
silverado_early_follow=early_truck_follow)
self.a_desired_trajectory_full = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.a_solution)
self.v_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.v_solution)
@@ -2212,7 +2214,7 @@ class LongitudinalPlanner:
if lead_one_active:
rel_v = max(0.0, v_ego - self.lead_one.vLead)
# dynamic time headway adds a small buffer when uncertainty is elevated
base_th = max(1.6, effective_t_follow)
base_th = get_follow_prebrake_min_headway(self.CP, effective_t_follow)
th = base_th + 0.6 * max(0.0, uncertainty - 0.42)
desired_gap = th * v_ego
if (self.lead_dist_f is not None and self.lead_dist_f < desired_gap and rel_v > 0.5):
@@ -8,6 +8,7 @@ GM_SILVERADO_EARLY_FOLLOW_MIN_EGO_SPEED = 18.0
GM_SILVERADO_EARLY_FOLLOW_MAX_DISTANCE = 130.0
GM_SILVERADO_EARLY_FOLLOW_MIN_MODEL_PROB = 0.85
GM_SILVERADO_EARLY_FOLLOW_MAX_LATERAL_OFFSET = 1.2
GM_SILVERADO_FOLLOW_PREBRAKE_MIN_HEADWAY = 1.25
TOYOTA_SIENNA_POST_DEPARTURE_RESTOP_MAX_EGO_SPEED = 2.0
TOYOTA_SIENNA_POST_DEPARTURE_RESTOP_MAX_LEAD_SPEED = 0.45
TOYOTA_SIENNA_POST_DEPARTURE_RESTOP_MAX_LEAD_DELTA = 0.35
@@ -47,6 +48,13 @@ def is_gm_silverado_early_follow_lead(CP, lead, v_ego):
return True
def get_follow_prebrake_min_headway(CP, t_follow):
"""Return the comfort pre-brake floor without changing lead safety distance."""
if CP.brand == "gm" and str(CP.carFingerprint) in ("CHEVROLET_SILVERADO", "CHEVROLET_SILVERADO_CC"):
return max(float(t_follow), GM_SILVERADO_FOLLOW_PREBRAKE_MIN_HEADWAY)
return max(float(t_follow), 1.6)
def get_toyota_sienna_post_departure_restop_cap(CP, lead, v_ego, accel_min,
stop_distance, now_t, departure_latch_until):
"""Re-arm a stop if a Sienna's lead twitches forward and stops again."""
+54 -1
View File
@@ -28,9 +28,11 @@ from openpilot.selfdrive.controls.lib.latcontrol_vehicle_tunes import (
get_flm_runtime_overrides,
get_hkg_canfd_base_friction_threshold,
get_kona_non_scc_center_taper_scale,
get_kona_non_scc_friction_threshold,
get_kona_non_scc_highway_transition_output_scale,
KIA_FORTE_BASE_LAT_ACCEL_FACTOR_MULT,
RAM_1500_BASE_LAT_ACCEL_FACTOR_MULT,
RAM_1500_MAX_LAT_JERK_UP,
get_ram_1500_transition_output_scale,
get_ram_1500_ff_scale,
get_subaru_impreza_pid_output_scale,
@@ -72,6 +74,8 @@ from openpilot.selfdrive.controls.lib.latcontrol_torque import (
get_prius_ff_scale,
get_prius_friction_scale,
get_prius_friction_threshold,
get_prius_friction_jerk_deadzone,
get_prius_high_speed_output_taper_scale,
get_camry_friction_threshold,
get_rav4_prime_ff_scale,
get_rav4_prime_friction_scale,
@@ -97,6 +101,8 @@ from openpilot.selfdrive.controls.lib.latcontrol_torque import (
get_ioniq_6_friction_scale,
get_ioniq_6_friction_threshold,
get_ioniq_6_low_speed_angle_assist_torque,
get_ioniq_6_2025_center_output_scale,
is_ioniq_6_2025_model,
get_kia_forte_center_taper_scale,
get_kia_forte_ff_scale,
get_kia_carnival_center_taper_scale,
@@ -702,6 +708,11 @@ class TestLatControl:
assert right_turn_in_scale == left_turn_in_scale > base_scale
assert base_scale > left_unwind_scale == right_unwind_scale
assert get_prius_friction_jerk_deadzone(30.0, 0.0) > get_prius_friction_jerk_deadzone(30.0, 0.8)
assert get_prius_friction_jerk_deadzone(8.0, 0.0) < 0.05
assert get_prius_high_speed_output_taper_scale(30.0, 0.0) > get_prius_high_speed_output_taper_scale(30.0, 0.8)
assert get_prius_high_speed_output_taper_scale(15.0, 0.8) > 0.99
def test_camry_friction_threshold_only_fades_in_for_calm_high_speed(self):
low_speed_center = get_camry_friction_threshold(10.0, 0.0)
high_speed_center = get_camry_friction_threshold(32.0, 0.0)
@@ -811,7 +822,12 @@ class TestLatControl:
base = get_standard_friction_threshold(9.0)
center = get_sienna_4th_gen_friction_threshold(9.0, 0.0)
turn = get_sienna_4th_gen_friction_threshold(9.0, 0.8)
highway_base = get_standard_friction_threshold(28.0)
highway_center = get_sienna_4th_gen_friction_threshold(28.0, 0.0)
highway_turn = get_sienna_4th_gen_friction_threshold(28.0, 0.8)
assert center > turn >= base
assert highway_center > highway_base
assert highway_turn < highway_center
calm = get_sienna_4th_gen_center_taper_scale(0.0, 8.0)
turn_taper = get_sienna_4th_gen_center_taper_scale(0.8, 8.0)
@@ -856,6 +872,18 @@ class TestLatControl:
assert get_ram_1500_ff_scale(1.2, -1.1, 17.0) < 1.0
assert get_ram_1500_ff_scale(1.2, 1.1, 6.0) < get_ram_1500_ff_scale(1.2, 1.1, 17.0)
def test_ram_1500_jerk_limit_update_path(self):
controller, VM, CS, params, starpilot_toggles = self._build_torque_controller(CHRYSLER.RAM_1500_5TH_GEN)
jerk_samples = []
for i in range(100):
_, _, lac_log = controller.update(
True, CS, VM, params, False, 0.0025 * i, False, 0.2, None, None, starpilot_toggles,
)
jerk_samples.append(abs(lac_log.desiredLateralJerk))
assert max(jerk_samples) <= RAM_1500_MAX_LAT_JERK_UP + 1e-6
assert max(jerk_samples) > RAM_1500_MAX_LAT_JERK_UP - 0.05
def test_ram_1500_transition_taper_update_path(self, monkeypatch):
controller, VM, CS, params, starpilot_toggles = self._build_torque_controller(CHRYSLER.RAM_1500_5TH_GEN)
base_output, _, lac_log = controller.update(
@@ -910,6 +938,16 @@ class TestLatControl:
assert get_kona_non_scc_center_taper_scale(0.28, 25.0) == pytest.approx(1.0)
assert get_kona_non_scc_center_taper_scale(0.10, 25.0) < get_kona_non_scc_center_taper_scale(0.10, 15.0)
def test_kona_non_scc_center_friction_threshold_is_speed_and_center_gated(self):
low_speed = get_kona_non_scc_friction_threshold(3.0, 0.0)
highway_base = get_standard_friction_threshold(25.0)
highway_center = get_kona_non_scc_friction_threshold(25.0, 0.0)
highway_curve = get_kona_non_scc_friction_threshold(25.0, 0.8)
assert low_speed == pytest.approx(get_standard_friction_threshold(3.0), abs=0.002)
assert highway_center > highway_base
assert highway_curve < highway_center
def test_kona_non_scc_highway_transition_taper_update_path(self, monkeypatch):
controller, VM, CS, params, starpilot_toggles = self._build_torque_controller(HYUNDAI.HYUNDAI_KONA_NON_SCC)
CS.vEgo = 30.0
@@ -1049,6 +1087,21 @@ class TestLatControl:
assert get_ioniq_6_friction_center_fade_scale(-0.5, 30.0) > 0.95
assert get_ioniq_6_friction_center_fade_scale(0.0, 8.0) > 0.95
def test_ioniq_6_2025_variant_is_firmware_gated(self):
old_cp = SimpleNamespace(
carFingerprint=HYUNDAI.HYUNDAI_IONIQ_6,
carFw=[SimpleNamespace(fwVersion=b"99211-KL000 221213"), SimpleNamespace(fwVersion=b"ADR 1.03 221205")],
)
new_cp = SimpleNamespace(
carFingerprint=HYUNDAI.HYUNDAI_IONIQ_6,
carFw=[SimpleNamespace(fwVersion=b"99211-KL000 230915"), SimpleNamespace(fwVersion=b"ADR 1.05 240206")],
)
assert not is_ioniq_6_2025_model(old_cp)
assert is_ioniq_6_2025_model(new_cp)
assert get_ioniq_6_2025_center_output_scale(0.0, 28.0) < get_ioniq_6_2025_center_output_scale(0.5, 28.0)
assert get_ioniq_6_2025_center_output_scale(0.0, 15.0) > 0.98
def test_ioniq_6_center_taper_curve(self):
assert get_ioniq_6_center_taper_scale(0.0, 10.0) > get_ioniq_6_center_taper_scale(0.0, 30.0)
assert get_ioniq_6_center_taper_scale(0.0, 30.0) < get_ioniq_6_center_taper_scale(0.2, 30.0)
@@ -1079,7 +1132,7 @@ class TestLatControl:
high_speed_center = get_kia_ev6_friction_threshold(34.0, 0.0, 0.0)
high_speed_curve = get_kia_ev6_friction_threshold(34.0, 0.55, 0.0)
assert low_speed == pytest.approx(get_hkg_canfd_base_friction_threshold(10.0), abs=0.002)
assert low_speed == pytest.approx(get_hkg_canfd_base_friction_threshold(10.0), abs=0.004)
assert high_speed_center > get_hkg_canfd_base_friction_threshold(34.0)
assert high_speed_curve < high_speed_center
@@ -555,6 +555,63 @@ def test_update_releases_stopping_on_small_sustained_positive_target():
assert lc.long_control_state == LongCtrlState.starting
def test_corolla_tss2_stop_release_ramps_positive_target():
CP = make_longcontrol_cp(
brand="toyota",
carFingerprint=TOYOTA_CAR.TOYOTA_COROLLA_TSS2,
)
tuning = vehicle_tunes.LongControlVehicleTuning(CP)
tuning.reset()
first_target = tuning.shape_toyota_corolla_accel_target(1.5, 0.0, False, -0.15)
assert first_target < 0.0
assert first_target < 1.5
target = first_target
for _ in range(100):
target = tuning.shape_toyota_corolla_accel_target(1.5, 0.0, False, target)
assert target > 1.4
for _ in range(100):
target = tuning.shape_toyota_corolla_accel_target(1.5, 0.0, False, target)
assert target == pytest.approx(1.5, abs=0.01)
def test_corolla_tss2_target_filter_does_not_delay_hard_braking():
CP = make_longcontrol_cp(
brand="toyota",
carFingerprint=TOYOTA_CAR.TOYOTA_COROLLA_TSS2,
)
tuning = vehicle_tunes.LongControlVehicleTuning(CP)
tuning.shape_toyota_corolla_accel_target(1.0, 1.0, False, 0.0)
assert tuning.shape_toyota_corolla_accel_target(-1.0, 1.0, False, 0.5) == -1.0
def test_corolla_tss2_longcontrol_release_does_not_step_to_full_accel():
CP = make_longcontrol_cp(
brand="toyota",
carFingerprint=TOYOTA_CAR.TOYOTA_COROLLA_TSS2,
)
lc = LongControl(CP)
lc.long_control_state = LongCtrlState.stopping
lc.last_output_accel = -0.15
CS = car.CarState.new_message(vEgo=0.0, aEgo=0.0, brakePressed=False)
CS.cruiseState.standstill = False
output_accel = lc.update(
active=True,
CS=CS,
a_target=1.5,
should_stop=False,
accel_limits=(-3.0, 2.0),
starpilot_toggles=make_toggles(vEgoStarting=0.1),
)
assert lc.long_control_state == LongCtrlState.pid
assert output_accel < 0.0
def test_update_releases_stopping_immediately_after_confirmed_lead_departure():
CP = car.CarParams.new_message(startingState=True, vEgoStarting=0.5)
CP.longitudinalTuning.kpBP = [0.0]
@@ -19,6 +19,7 @@ from openpilot.selfdrive.controls.lib.longitudinal_planner import LongitudinalPl
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import LongitudinalMpc, soften_far_radar_lead_accel, should_trigger_planner_fcw
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import T_IDXS as T_IDXS_MPC
from openpilot.selfdrive.controls.lib.longitudinal_vehicle_tunes import (
get_follow_prebrake_min_headway,
get_toyota_sienna_post_departure_restop_cap,
is_gm_silverado_early_follow_lead,
)
@@ -498,6 +499,31 @@ def test_gm_silverado_early_follow_requires_a_credible_centered_vision_lead(kwar
assert not is_gm_silverado_early_follow_lead(CP, lead, 30.0)
def test_silverado_prebrake_floor_is_vehicle_specific():
silverado = SimpleNamespace(brand="gm", carFingerprint=GM_CAR.CHEVROLET_SILVERADO)
honda = SimpleNamespace(brand="honda", carFingerprint=CAR.HONDA_CIVIC)
assert get_follow_prebrake_min_headway(silverado, 1.0) == pytest.approx(1.25)
assert get_follow_prebrake_min_headway(honda, 1.0) == pytest.approx(1.6)
def test_silverado_vision_follow_hold_survives_nonurgent_far_lead_crossover():
v_ego = 32.0
t_follow = 1.0
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
planner = LongitudinalPlanner(CP, init_v=v_ego)
lead_one = make_lead(status=True, d_rel=70.0, v_lead=31.2, a_lead=-0.02, radar=False, model_prob=1.0, y_rel=0.1)
lead_two = make_lead(status=False)
assert planner.mpc.get_vision_follow_cruise_hold(
"lead0", lead_one, lead_two, 101.0, 200.0, 100.0, v_ego, t_follow, True,
) is None
assert planner.mpc.get_vision_follow_cruise_hold(
"lead0", lead_one, lead_two, 101.0, 200.0, 100.0, v_ego, t_follow, True,
early_follow=True,
) == "lead0"
@pytest.mark.parametrize("model_version", ["v11", "v12", "v13", "v14", "v15"])
def test_acc_mode_uses_far_near_stopped_radar_lead_before_tracking(model_version):
v_ego = 24.6
+12 -17
View File
@@ -1,5 +1,4 @@
import io
from types import SimpleNamespace
import numpy as np
@@ -25,31 +24,27 @@ def test_out_of_band_artifact_round_trip():
def test_external_gpu_probe_retries_until_pcie_is_ready(monkeypatch):
results = [SimpleNamespace(returncode=1, stdout="", stderr="LTSSM=0x00"),
SimpleNamespace(returncode=0, stdout="", stderr="")]
calls = []
probe_count = 0
def probe():
nonlocal probe_count
probe_count += 1
calls.append("probe")
return (False, "LTSSM=0x00") if probe_count < 3 else (True, "LTSSM=0x78")
monkeypatch.setattr(
model_compiler.subprocess,
"run",
lambda *args, **kwargs: calls.append((args, kwargs)) or results.pop(0),
model_compiler,
"_probe_external_gpu_link_once",
probe,
)
monkeypatch.setattr(model_compiler.time, "sleep", lambda seconds: calls.append(("sleep", seconds)))
model_compiler.wait_for_external_gpu({"PYTHONPATH": "/tmp/openpilot"})
assert len(calls) == 3
assert calls[0][1]["env"]["DEV"] == "USB+AMD"
assert calls[1] == ("sleep", 1)
assert calls == ["probe", ("sleep", 1), "probe", ("sleep", 1), "probe"]
def test_external_gpu_probe_reports_failure(monkeypatch):
result = SimpleNamespace(returncode=1, stdout="", stderr="link unavailable")
monkeypatch.setattr(model_compiler.subprocess, "run", lambda *args, **kwargs: result)
monkeypatch.setattr(model_compiler, "_probe_external_gpu_link_once", lambda: (False, "link unavailable"))
monkeypatch.setattr(model_compiler.time, "sleep", lambda _: None)
try:
model_compiler.wait_for_external_gpu({})
except RuntimeError as error:
assert "link unavailable" in str(error)
else:
raise AssertionError("external GPU probe unexpectedly succeeded")
assert model_compiler.wait_for_external_gpu({}) is False
@@ -272,7 +272,7 @@ class ConditionalDriveModeView(AdjustorTogglesPanelView):
"CESpeed": {"title": tr("Below Speed"), "subtitle": "", "min": 0, "max": max_speed, "step": 1.0, "unit": speed_unit, "presets": [0, 20, 35, 55, 75], "labels": {}, "get": lambda: float(self._controller._params.get_int("CESpeed"))},
"CESpeedLead": {"title": tr("Speed w/ Lead"), "subtitle": "", "min": 0, "max": max_speed, "step": 1.0, "unit": speed_unit, "presets": [0, 20, 35, 55, 75], "labels": {}, "get": lambda: float(self._controller._params.get_int("CESpeedLead"))},
"CESignalSpeed": {"title": tr("Turn Signal Below"), "subtitle": "", "min": 0, "max": max_speed, "step": 1.0, "unit": speed_unit, "presets": [0, 20, 35, 55, 75], "labels": {0.0: tr("Off")}, "get": lambda: float(self._controller._params.get_int("CESignalSpeed"))},
"CEModelStopTime": {"title": tr("Predicted Stop In"), "subtitle": "", "min": 0, "max": 10.0, "step": 1.0, "unit": "s", "presets": [0, 3, 5, 9, 10], "labels": {0.0: tr("Off")}, "get": lambda: float(self._controller._params.get_int("CEModelStopTime"))},
"CEModelStopTime": {"title": tr("Predicted Stop In"), "subtitle": "", "min": 0, "max": 10.0, "step": 0.1, "unit": "s", "presets": [0, 3, 5, 7.7, 10], "labels": {0.0: tr("Off")}, "get": lambda: float(self._controller._params.get_float("CEModelStopTime"))},
"CCMSpeed": {"title": tr("Above Speed"), "subtitle": "", "min": 0, "max": max_speed, "step": 1.0, "unit": speed_unit, "presets": [0, 35, 55, 65, 80], "labels": {}, "get": lambda: float(self._controller._params.get_int("CCMSpeed"))},
"CCMSpeedLead": {"title": tr("Speed w/ Lead"), "subtitle": "", "min": 0, "max": max_speed, "step": 1.0, "unit": speed_unit, "presets": [0, 35, 55, 65, 80], "labels": {}, "get": lambda: float(self._controller._params.get_int("CCMSpeedLead"))},
"CCMSetSpeedMargin": {"title": tr("Set Speed Margin"), "subtitle": "", "min": 0, "max": 30.0 if is_metric else 15.0, "step": 1.0, "unit": speed_unit, "presets": [0, 5, 10, 15], "labels": {}, "get": lambda: float(self._controller._params.get_int("CCMSetSpeedMargin"))},
@@ -306,22 +306,26 @@ class ConditionalDriveModeView(AdjustorTogglesPanelView):
"CESpeed": {"title": tr("Below Speed"), "min": 0, "max": max_speed, "unit": speed_unit, "labels": {}, "presets": [0, 20, 35, 55, 75]},
"CESpeedLead": {"title": tr("Speed w/ Lead"), "min": 0, "max": max_speed, "unit": speed_unit, "labels": {}, "presets": [0, 20, 35, 55, 75]},
"CESignalSpeed": {"title": tr("Turn Signal Below"), "min": 0, "max": max_speed, "unit": speed_unit, "labels": {0.0: tr("Off")}, "presets": [0, 20, 35, 55, 75]},
"CEModelStopTime": {"title": tr("Predicted Stop In"), "min": 0, "max": 10.0, "unit": "s", "labels": {0.0: tr("Off")}, "presets": [0, 3, 5, 9, 10]},
"CEModelStopTime": {"title": tr("Predicted Stop In"), "min": 0, "max": 10.0, "unit": "s", "labels": {0.0: tr("Off")}, "presets": [0, 3, 5, 7.7, 10]},
"CCMSpeed": {"title": tr("Above Speed"), "min": 0, "max": max_speed, "unit": speed_unit, "labels": {}, "presets": [0, 35, 55, 65, 80]},
"CCMSpeedLead": {"title": tr("Speed w/ Lead"), "min": 0, "max": max_speed, "unit": speed_unit, "labels": {}, "presets": [0, 35, 55, 65, 80]},
"CCMSetSpeedMargin": {"title": tr("Set Speed Margin"), "min": 0, "max": 30.0 if is_metric else 15.0, "unit": speed_unit, "labels": {}, "presets": [0, 5, 10, 15]},
}
spec = specs[key]
original_val = float(self._controller._params.get_int(key))
is_float = key == "CEModelStopTime"
original_val = float(self._controller._params.get_float(key) if is_float else self._controller._params.get_int(key))
def on_close(res, val):
if res == DialogResult.CONFIRM:
self._controller._params.put_int(key, int(val))
if is_float:
self._controller._params.put_float(key, float(val))
else:
self._controller._params.put_int(key, int(val))
gui_app.push_widget(AetherSliderDialog(
title=spec["title"],
min_val=float(spec["min"]), max_val=float(spec["max"]), step=1.0,
min_val=float(spec["min"]), max_val=float(spec["max"]), step=0.1 if is_float else 1.0,
current_val=original_val,
on_close=on_close, presets=[float(p) for p in spec["presets"]],
unit=spec["unit"], labels=spec["labels"], color=PANEL_STYLE.accent
@@ -291,11 +291,8 @@ StarPilotLongitudinalPanel::StarPilotLongitudinalPanel(StarPilotSettingsWindow *
} else if (param == "CCMSetSpeedMargin") {
longitudinalToggle = new StarPilotParamValueControl(param, title, desc, icon, 0, 15, tr(" mph"), std::map<float, QString>(), 1, true, 175);
} else if (param == "CEModelStopTime") {
std::map<float, QString> stopTimeLabels;
for (int i = 0; i <= 10; ++i) {
stopTimeLabels[i] = i == 0 ? tr("Off") : i == 1 ? QString::number(i) + tr(" second") : QString::number(i) + tr(" seconds");
}
longitudinalToggle = new StarPilotParamValueControl(param, title, desc, icon, 0, 9, QString(), stopTimeLabels);
std::map<float, QString> stopTimeLabels{{0.0f, tr("Off")}};
longitudinalToggle = new StarPilotParamValueControl(param, title, desc, icon, 0, 9, tr(" seconds"), stopTimeLabels, 0.1);
} else if (param == "CESignalSpeed") {
std::vector<QString> ceSignalToggles{"CESignalLaneDetection"};
std::vector<QString> ceSignalToggleNames{tr("Not For Detected Lanes")};
+8 -8
View File
@@ -68,7 +68,7 @@ STARPILOT_PARAM_CANONICALIZATION_MIGRATION_FLAG = Path("/data") / "starpilot_par
STARPILOT_PC_ROOT_MIGRATION_FLAG = Path("/data") / "starpilot_pc_root_v1"
STARPILOT_PARAMS_CACHE_MIGRATION_FLAG = Path("/data") / "starpilot_params_cache_v1"
STARPILOT_DEFAULT_MODEL_MIGRATION_FLAG = Path("/data") / "starpilot_default_model_rdf_v1"
STARPILOT_CE_MODEL_STOP_TIME_MIGRATION_FLAG = Path("/data") / "starpilot_ce_model_stop_time_v1"
STARPILOT_CE_MODEL_STOP_TIME_MIGRATION_FLAG = Path("/data") / "starpilot_ce_model_stop_time_v2"
STARPILOT_LEGACY_CACHE_MARKER_KEYS = ("RemapCancelToDistance",)
STARPILOT_REMOVED_PARAM_KEYS = ("CoastUpToLeads", "HumanAcceleration", "HumanFollowing", "PrioritizeSmoothFollowing")
LEGACY_CARMODEL_MIGRATIONS = {
@@ -561,8 +561,8 @@ def migrate_starpilot_default_parity(params: Params, params_cache: Params) -> No
seeded_keys.append(key)
if not _has_persisted_param_file(params, "CEModelStopTime") and not _has_persisted_param_file(params_cache, "CEModelStopTime"):
params.put_float("CEModelStopTime", 9.0)
params_cache.put_float("CEModelStopTime", 9.0)
params.put_float("CEModelStopTime", 7.7)
params_cache.put_float("CEModelStopTime", 7.7)
seeded_keys.append("CEModelStopTime")
# Rebase default regression fix:
@@ -628,7 +628,7 @@ def migrate_starpilot_default_model(params: Params, params_cache: Params) -> Non
def migrate_starpilot_ce_model_stop_time(params: Params, params_cache: Params) -> None:
"""Move the old persisted 7-second stop prediction default to 9 seconds once."""
"""Move persisted users of the old 9-second stop prediction threshold to 7.7 once."""
if STARPILOT_CE_MODEL_STOP_TIME_MIGRATION_FLAG.exists():
return
@@ -643,14 +643,14 @@ def migrate_starpilot_ce_model_stop_time(params: Params, params_cache: Params) -
except Exception:
continue
if abs(parsed_value - 7.0) < 1e-6:
if abs(parsed_value - 9.0) < 1e-6:
legacy_default_detected = True
break
if legacy_default_detected:
params.put_float("CEModelStopTime", 9.0)
params_cache.put_float("CEModelStopTime", 9.0)
cloudlog.warning("Migrated CEModelStopTime from 7 seconds to 9 seconds")
params.put_float("CEModelStopTime", 7.7)
params_cache.put_float("CEModelStopTime", 7.7)
cloudlog.warning("Migrated CEModelStopTime from 9 seconds to 7.7 seconds")
try:
STARPILOT_CE_MODEL_STOP_TIME_MIGRATION_FLAG.parent.mkdir(parents=True, exist_ok=True)
+26 -4
View File
@@ -240,6 +240,17 @@ class TestManager:
assert params.get("CEModelStopTime") == "3.5"
assert params_cache.get_bool("NNFF")
def test_migrate_starpilot_default_parity_seeds_new_model_stop_time_default(self, tmp_path, monkeypatch):
monkeypatch.setattr(manager, "STARPILOT_DEFAULTS_PARITY_MIGRATION_FLAG", tmp_path / "starpilot_defaults_parity_v1")
params = FileBackedFakeParams(tmp_path / "params")
params_cache = FileBackedFakeParams(tmp_path / "cache")
manager.migrate_starpilot_default_parity(params, params_cache)
assert params.get("CEModelStopTime") == "7.7"
assert params_cache.get("CEModelStopTime") == "7.7"
def test_migrate_starpilot_default_model(self, tmp_path, monkeypatch):
monkeypatch.setattr(manager, "STARPILOT_DEFAULT_MODEL_MIGRATION_FLAG", tmp_path / "starpilot_default_model_rdf_v1")
@@ -262,17 +273,28 @@ class TestManager:
assert manager.STARPILOT_DEFAULT_MODEL_MIGRATION_FLAG.exists()
def test_migrate_starpilot_ce_model_stop_time(self, tmp_path, monkeypatch):
monkeypatch.setattr(manager, "STARPILOT_CE_MODEL_STOP_TIME_MIGRATION_FLAG", tmp_path / "starpilot_ce_model_stop_time_v1")
monkeypatch.setattr(manager, "STARPILOT_CE_MODEL_STOP_TIME_MIGRATION_FLAG", tmp_path / "starpilot_ce_model_stop_time_v2")
params = FileBackedFakeParams(tmp_path / "params", {"CEModelStopTime": 7.0})
params = FileBackedFakeParams(tmp_path / "params", {"CEModelStopTime": 9.0})
params_cache = FileBackedFakeParams(tmp_path / "cache")
manager.migrate_starpilot_ce_model_stop_time(params, params_cache)
assert params.get("CEModelStopTime") == "9.0"
assert params_cache.get("CEModelStopTime") == "9.0"
assert params.get("CEModelStopTime") == "7.7"
assert params_cache.get("CEModelStopTime") == "7.7"
assert manager.STARPILOT_CE_MODEL_STOP_TIME_MIGRATION_FLAG.exists()
def test_migrate_starpilot_ce_model_stop_time_preserves_custom_value(self, tmp_path, monkeypatch):
monkeypatch.setattr(manager, "STARPILOT_CE_MODEL_STOP_TIME_MIGRATION_FLAG", tmp_path / "starpilot_ce_model_stop_time_v2")
params = FileBackedFakeParams(tmp_path / "params", {"CEModelStopTime": 8.0})
params_cache = FileBackedFakeParams(tmp_path / "cache", {"CEModelStopTime": 8.0})
manager.migrate_starpilot_ce_model_stop_time(params, params_cache)
assert params.get("CEModelStopTime") == "8.0"
assert params_cache.get("CEModelStopTime") == "8.0"
def test_migrate_disable_humanlike_defaults(self, tmp_path, monkeypatch):
monkeypatch.setattr(manager, "STARPILOT_HUMANLIKE_DISABLE_MIGRATION_FLAG", tmp_path / "starpilot_humanlike_disable_v1")