diff --git a/CHANGELOG.md b/CHANGELOG.md
index 00be668571..5cb25994be 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+sunnypilot Version 2025.002.000 (2025-xx-xx)
+========================
+
sunnypilot Version 2025.001.000 (2025-10-25)
========================
* 🛠️ Major rewrite
@@ -17,10 +20,12 @@ sunnypilot Version 2025.001.000 (2025-10-25)
* Intelligent longitudinal control adaptation
* Speed Limit Assist (SLA)
* Comprehensive speed limit integration featuring @pfeiferj's `mapd` for offline map limits downloads, a Speed Limit Resolver for sourcing data (from car, map, combined, etc), on-screen UI for Speed Limit Information/Warning, and Speed Limit Assist (SLA) to adjust cruise speed automatically.
+ * Currently disabled for Tesla with sunnypilot Longitudinal Control in release and Rivian with sunnypilot Longitudinal Control in all branches
+ * May return in future releases
* Intelligent Cruise Button Management (ICBM)
* System designed to manage the vehicle’s speed by sending cruise control button commands to the car’s ECU.
* Smart Cruise Control Map & Vision (SCC-M / SCC-V)
- * When using any form of long control (openpilot longitudinal or ICBM) it will control the speed at which you enter and perform a turn by leveraging map data (SCC-M) and/or by leveraging what the model sees about the curve ahead (SCC-V)
+ * When using any form of long control (sunnypilot longitudinal control or ICBM) it will control the speed at which you enter and perform a turn by leveraging map data (SCC-M) and/or by leveraging what the model sees about the curve ahead (SCC-V)
* Vehicle Selector
* If your vehicle isn’t fingerprinted automatically, you can still use the vehicle selector to get it working
* sunnylink Integration
@@ -132,6 +137,7 @@ sunnypilot Version 2025.001.000 (2025-10-25)
* @michael-was-taken made their first contribution in "Reorder README tables: show -new branches first (#1191)"
* @dzid26 made their first contribution in "params: Fix loading delay on startup (#1297)"
* @HazZelnutz made their first contribution in "Visuals: Turn signals on screen when blinker is used (#1291)"
+ * @sirmuskrat made their first contribution in "ui: openpilot Longitudinal Control → sunnypilot Longitudinal Control (#1422)"
* New Contributors (sunnypilot/opendbc)
* @chrispypatt made their first contribution in "Toyota: SecOC Longitudinal Control (sunnypilot/opendbc#93)"
* @Discountchubbs made their first contribution in "Hyundai: EPS FW For 2022 KIA_NIRO_EV SCC (sunnypilot/opendbc#118)"
diff --git a/docs/CARS.md b/docs/CARS.md
index 25f84b389e..695c2589e3 100644
--- a/docs/CARS.md
+++ b/docs/CARS.md
@@ -4,7 +4,7 @@
A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
-# 337 Supported Cars
+# 339 Supported Cars
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|
Hardware Needed
|Video|Setup Video|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
@@ -21,7 +21,10 @@ A supported vehicle is one that just works when you install a comma device. All
|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |||
|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[1](#footnotes)|3 mph|6 mph|[](##)|[](##)|Parts
- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |
||
|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[1](#footnotes)|3 mph|6 mph|[](##)|[](##)|Parts
- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |||
+|Chevrolet|Bolt EV Non-ACC 2017|Adaptive Cruise Control (ACC)|Stock|24 mph|7 mph|[](##)|[](##)|Parts
- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |||
+|Chevrolet|Bolt EV Non-ACC 2018-21|Adaptive Cruise Control (ACC)|Stock|24 mph|7 mph|[](##)|[](##)|Parts
- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |||
|Chevrolet|Equinox 2019-22|Adaptive Cruise Control (ACC)|openpilot available[1](#footnotes)|3 mph|6 mph|[](##)|[](##)|Parts
- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |||
+|Chevrolet|Malibu Non-ACC 2016-23|Adaptive Cruise Control (ACC)|Stock|24 mph|7 mph|[](##)|[](##)|Parts
- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |||
|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[1](#footnotes)|0 mph|6 mph|[](##)|[](##)|Parts
- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |||
|Chevrolet|Trailblazer 2021-22|Adaptive Cruise Control (ACC)|openpilot available[1](#footnotes)|3 mph|6 mph|[](##)|[](##)|Parts
- 1 GM connector
- 1 comma 3X
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |||
|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|Parts
- 1 FCA connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |||
@@ -236,20 +239,20 @@ A supported vehicle is one that just works when you install a comma device. All
|Rivian|R1T 2022-24|All|openpilot|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Rivian A connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 right angle OBD-C cable (1.5 ft)
Buy Here ||
|
|SEAT|Ateca 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |||
|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |||
-|Subaru|Ascent 2019-21|All[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
-|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |
||
-|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
-|Subaru|Forester 2017-18|EyeSight Driver Assistance[8](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
-|Subaru|Forester 2019-21|All[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
-|Subaru|Impreza 2017-19|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
-|Subaru|Impreza 2020-22|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
-|Subaru|Legacy 2015-18|EyeSight Driver Assistance[8](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
+|Subaru|Ascent 2019-21|All[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
+|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |
||
+|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
+|Subaru|Forester 2017-18|EyeSight Driver Assistance[8](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
+|Subaru|Forester 2019-21|All[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
+|Subaru|Impreza 2017-19|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
+|Subaru|Impreza 2020-22|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
+|Subaru|Legacy 2015-18|EyeSight Driver Assistance[8](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
|Subaru|Legacy 2020-22|All[8](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
-|Subaru|Outback 2015-17|EyeSight Driver Assistance[8](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
-|Subaru|Outback 2018-19|EyeSight Driver Assistance[8](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
+|Subaru|Outback 2015-17|EyeSight Driver Assistance[8](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
+|Subaru|Outback 2018-19|EyeSight Driver Assistance[8](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
|Subaru|Outback 2020-22|All[8](#footnotes)|Stock|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru B connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
-|Subaru|XV 2018-19|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |
||
-|Subaru|XV 2020-21|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
+|Subaru|XV 2018-19|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |
||
+|Subaru|XV 2020-21|EyeSight Driver Assistance[8](#footnotes)|openpilot available[1,9](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Subaru A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy HereTools
- 1 Pry Tool
- 1 Socket Wrench 8mm or 5/16" (deep) |||
|Ĺ koda|Fabia 2022-23[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here [17](#footnotes)|||
|Ĺ koda|Kamiq 2021-23[13,15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here [17](#footnotes)|||
|Ĺ koda|Karoq 2019-23[15](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |||
@@ -308,7 +311,6 @@ A supported vehicle is one that just works when you install a comma device. All
|Toyota|RAV4 Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |
||
|Toyota|RAV4 Hybrid 2023-25|All|openpilot available[1](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |
||
|Toyota|Sienna 2018-20|All|openpilot available[2](#footnotes)|19 mph|0 mph|[](##)|[](##)|Parts
- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |
||
-|Toyota|Wildlander PHEV 2021|All|openpilot|19 mph|0 mph|[](##)|[](##)|Parts
- 1 Toyota A connector
- 1 comma 3X
- 1 comma power v3
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |||
|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |
||
|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |
||
|Volkswagen|Arteon R 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,16](#footnotes)|0 mph|0 mph|[](##)|[](##)|Parts
- 1 USB-C coupler
- 1 VW J533 connector
- 1 comma 3X
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here |
||
diff --git a/opendbc_repo b/opendbc_repo
index 0a2315efd1..efe4ff137f 160000
--- a/opendbc_repo
+++ b/opendbc_repo
@@ -1 +1 @@
-Subproject commit 0a2315efd175176c318aa34d634d55d9dcd9350a
+Subproject commit efe4ff137f839bbd416ac8ff8f5b78550a7d6eec
diff --git a/selfdrive/car/card.py b/selfdrive/car/card.py
index 4c44e43281..6758432b68 100755
--- a/selfdrive/car/card.py
+++ b/selfdrive/car/card.py
@@ -88,6 +88,7 @@ class Car:
self.can_callbacks = can_comm_callbacks(self.can_sock, self.pm.sock['sendcan'])
is_release = self.params.get_bool("IsReleaseBranch")
+ is_release_sp = self.params.get_bool("IsReleaseSpBranch")
if CI is None:
# wait for one pandaState and one CAN packet
@@ -110,7 +111,7 @@ class Car:
init_params_list_sp = sunnypilot_interfaces.initialize_params(self.params)
self.CI = get_car(*self.can_callbacks, obd_callback(self.params), alpha_long_allowed, is_release, num_pandas, cached_params,
- fixed_fingerprint, init_params_list_sp)
+ fixed_fingerprint, init_params_list_sp, is_release_sp)
sunnypilot_interfaces.setup_interfaces(self.CI, self.params)
self.RI = interfaces[self.CI.CP.carFingerprint].RadarInterface(self.CI.CP, self.CI.CP_SP)
self.CP = self.CI.CP
diff --git a/selfdrive/car/tests/test_models.py b/selfdrive/car/tests/test_models.py
index 4f61ad3c35..48da4b1ce2 100644
--- a/selfdrive/car/tests/test_models.py
+++ b/selfdrive/car/tests/test_models.py
@@ -151,7 +151,7 @@ class TestCarModelBase(unittest.TestCase):
cls.CarInterface = interfaces[cls.platform]
cls.CP = cls.CarInterface.get_params(cls.platform, cls.fingerprint, car_fw, alpha_long, False, docs=False)
- cls.CP_SP = cls.CarInterface.get_params_sp(cls.CP, cls.platform, cls.fingerprint, car_fw, alpha_long, docs=False)
+ cls.CP_SP = cls.CarInterface.get_params_sp(cls.CP, cls.platform, cls.fingerprint, car_fw, alpha_long, False, docs=False)
assert cls.CP
assert cls.CP_SP
assert cls.CP.carFingerprint == cls.platform
diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py
index 53eca38356..0501f669f1 100755
--- a/selfdrive/controls/lib/longitudinal_planner.py
+++ b/selfdrive/controls/lib/longitudinal_planner.py
@@ -51,12 +51,12 @@ def limit_accel_in_turns(v_ego, angle_steers, a_target, CP):
class LongitudinalPlanner(LongitudinalPlannerSP):
- def __init__(self, CP, init_v=0.0, init_a=0.0, dt=DT_MDL):
+ def __init__(self, CP, CP_SP, init_v=0.0, init_a=0.0, dt=DT_MDL):
self.CP = CP
self.mpc = LongitudinalMpc(dt=dt)
# TODO remove mpc modes when TR released
self.mpc.mode = 'acc'
- LongitudinalPlannerSP.__init__(self, self.CP, self.mpc)
+ LongitudinalPlannerSP.__init__(self, self.CP, CP_SP, self.mpc)
self.fcw = False
self.dt = dt
self.allow_throttle = True
diff --git a/selfdrive/controls/plannerd.py b/selfdrive/controls/plannerd.py
index 4565a9c047..f7d3370f90 100755
--- a/selfdrive/controls/plannerd.py
+++ b/selfdrive/controls/plannerd.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-from cereal import car
+from cereal import car, custom
from openpilot.common.gps import get_gps_location_service
from openpilot.common.params import Params
from openpilot.common.realtime import Priority, config_realtime_process
@@ -17,10 +17,14 @@ def main():
CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams)
cloudlog.info("plannerd got CarParams: %s", CP.brand)
+ cloudlog.info("plannerd is waiting for CarParamsSP")
+ CP_SP = messaging.log_from_bytes(params.get("CarParamsSP", block=True), custom.CarParamsSP)
+ cloudlog.info("plannerd got CarParamsSP")
+
gps_location_service = get_gps_location_service(params)
ldw = LaneDepartureWarning()
- longitudinal_planner = LongitudinalPlanner(CP)
+ longitudinal_planner = LongitudinalPlanner(CP, CP_SP)
pm = messaging.PubMaster(['longitudinalPlan', 'driverAssistance', 'longitudinalPlanSP'])
sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'liveParameters', 'radarState', 'modelV2', 'selfdriveState',
'liveMapDataSP', 'carStateSP', gps_location_service],
diff --git a/selfdrive/test/longitudinal_maneuvers/plant.py b/selfdrive/test/longitudinal_maneuvers/plant.py
index e466472432..e82ef2f65e 100755
--- a/selfdrive/test/longitudinal_maneuvers/plant.py
+++ b/selfdrive/test/longitudinal_maneuvers/plant.py
@@ -51,7 +51,9 @@ class Plant:
from opendbc.car.honda.values import CAR
from opendbc.car.honda.interface import CarInterface
- self.planner = LongitudinalPlanner(CarInterface.get_non_essential_params(CAR.HONDA_CIVIC), init_v=self.speed)
+ CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
+ CP_SP = CarInterface.get_non_essential_params_sp(CP, CAR.HONDA_CIVIC)
+ self.planner = LongitudinalPlanner(CP, CP_SP, init_v=self.speed)
@property
def current_time(self):
diff --git a/selfdrive/ui/qt/offroad/developer_panel.cc b/selfdrive/ui/qt/offroad/developer_panel.cc
index fbfb16294b..37c5d19272 100644
--- a/selfdrive/ui/qt/offroad/developer_panel.cc
+++ b/selfdrive/ui/qt/offroad/developer_panel.cc
@@ -32,11 +32,11 @@ DeveloperPanel::DeveloperPanel(SettingsWindow *parent) : ListWidget(parent) {
experimentalLongitudinalToggle = new ParamControl(
"AlphaLongitudinalEnabled",
- tr("openpilot Longitudinal Control (Alpha)"),
+ tr("sunnypilot Longitudinal Control (Alpha)"),
QString("%1
%2")
- .arg(tr("WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB)."))
- .arg(tr("On this car, sunnypilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. "
- "Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.")),
+ .arg(tr("WARNING: sunnypilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB)."))
+ .arg(tr("On this car, sunnypilot defaults to the car's built-in ACC instead of sunnypilot's longitudinal control. "
+ "Enable this to switch to sunnypilot longitudinal control. Enabling Experimental mode is recommended when enabling sunnypilot longitudinal control alpha.")),
""
);
experimentalLongitudinalToggle->setConfirmation(true, false);
diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc
index 6f594d939a..9a909ff2d2 100644
--- a/selfdrive/ui/qt/offroad/settings.cc
+++ b/selfdrive/ui/qt/offroad/settings.cc
@@ -188,7 +188,7 @@ void TogglesPanel::updateToggles() {
const QString unavailable = tr("Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control.");
QString long_desc = unavailable + " " + \
- tr("openpilot longitudinal control may come in a future update.");
+ tr("sunnypilot longitudinal control may come in a future update.");
if (CP.getAlphaLongitudinalAvailable()) {
if (is_release) {
long_desc = unavailable + " " + tr("An alpha version of sunnypilot longitudinal control can be tested, along with Experimental mode, on non-release branches.");
diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal/speed_limit/speed_limit_settings.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal/speed_limit/speed_limit_settings.cc
index 1696c7f63a..99c227d233 100644
--- a/selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal/speed_limit/speed_limit_settings.cc
+++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal/speed_limit/speed_limit_settings.cc
@@ -105,13 +105,13 @@ SpeedLimitSettings::SpeedLimitSettings(QWidget *parent) : QStackedWidget(parent)
}
void SpeedLimitSettings::refresh() {
+ bool is_release = params.getBool("IsReleaseSpBranch");
bool is_metric_param = params.getBool("IsMetric");
SpeedLimitMode speed_limit_mode_param = static_cast(std::atoi(params.get("SpeedLimitMode").c_str()));
SpeedLimitOffsetType offset_type_param = static_cast(std::atoi(params.get("SpeedLimitOffsetType").c_str()));
QString offsetLabel = QString::fromStdString(params.get("SpeedLimitValueOffset"));
- bool has_longitudinal_control;
- bool has_icbm;
+ bool sla_available;
auto cp_bytes = params.get("CarParamsPersistent");
auto cp_sp_bytes = params.get("CarParamsSPPersistent");
if (!cp_bytes.empty() && !cp_sp_bytes.empty()) {
@@ -122,17 +122,24 @@ void SpeedLimitSettings::refresh() {
cereal::CarParams::Reader CP = cmsg.getRoot();
cereal::CarParamsSP::Reader CP_SP = cmsg_sp.getRoot();
- has_longitudinal_control = hasLongitudinalControl(CP);
- has_icbm = hasIntelligentCruiseButtonManagement(CP_SP);
+ bool has_longitudinal_control = hasLongitudinalControl(CP);
+ bool has_icbm = hasIntelligentCruiseButtonManagement(CP_SP);
- if (!has_longitudinal_control && !has_icbm) {
- if (speed_limit_mode_param == SpeedLimitMode::ASSIST) {
- params.put("SpeedLimitMode", std::to_string(static_cast(SpeedLimitMode::WARNING)));
- }
+ /*
+ * Speed Limit Assist is available when:
+ * - has_longitudinal_control or has_icbm, and
+ * - is not a release branch or not a disallowed brand, and
+ * - is not always disallowed
+ */
+ bool sla_disallow_in_release = CP.getBrand() == "tesla" && is_release;
+ bool sla_always_disallow = CP.getBrand() == "rivian";
+ sla_available = (has_longitudinal_control || has_icbm) && !sla_disallow_in_release && !sla_always_disallow;
+
+ if (!sla_available && speed_limit_mode_param == SpeedLimitMode::ASSIST) {
+ params.put("SpeedLimitMode", std::to_string(static_cast(SpeedLimitMode::WARNING)));
}
} else {
- has_longitudinal_control = false;
- has_icbm = false;
+ sla_available = false;
}
speed_limit_mode_settings->setDescription(modeDescription(speed_limit_mode_param));
@@ -152,7 +159,7 @@ void SpeedLimitSettings::refresh() {
speed_limit_offset->showDescription();
}
- if (has_longitudinal_control || has_icbm) {
+ if (sla_available) {
speed_limit_mode_settings->setEnableSelectedButtons(true, convertSpeedLimitModeValues(getSpeedLimitModeValues()));
} else {
speed_limit_mode_settings->setEnableSelectedButtons(true, convertSpeedLimitModeValues(
diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.cc
index 2cc59ea333..ab357a2c16 100644
--- a/selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.cc
+++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.cc
@@ -38,7 +38,7 @@ LongitudinalPanel::LongitudinalPanel(QWidget *parent) : QWidget(parent) {
intelligentCruiseButtonManagement = new ParamControlSP(
"IntelligentCruiseButtonManagement",
tr("Intelligent Cruise Button Management (ICBM) (Alpha)"),
- tr("When enabled, sunnypilot will attempt to manage the built-in cruise control buttons by emulating button presses for limited longitudinal control."),
+ "",
"",
this
);
@@ -104,6 +104,8 @@ void LongitudinalPanel::hideEvent(QHideEvent *event) {
}
void LongitudinalPanel::refresh(bool _offroad) {
+ const QString icbm_description = tr("When enabled, sunnypilot will attempt to manage the built-in cruise control buttons by emulating button presses for limited longitudinal control.");
+
auto cp_bytes = params.get("CarParamsPersistent");
auto cp_sp_bytes = params.get("CarParamsSPPersistent");
if (!cp_bytes.empty() && !cp_sp_bytes.empty()) {
@@ -120,9 +122,24 @@ void LongitudinalPanel::refresh(bool _offroad) {
if (CP_SP.getIntelligentCruiseButtonManagementAvailable() && !has_longitudinal_control) {
intelligentCruiseButtonManagement->setEnabled(offroad);
+ intelligentCruiseButtonManagement->setDescription(icbm_description);
} else {
params.remove("IntelligentCruiseButtonManagement");
intelligentCruiseButtonManagement->setEnabled(false);
+
+ const QString icbm_unavaialble = tr("Intelligent Cruise Button Management is currently unavailable on this platform.");
+
+ QString long_desc = icbm_unavaialble;
+ if (has_longitudinal_control) {
+ if (CP.getAlphaLongitudinalAvailable()) {
+ long_desc = icbm_unavaialble + " " + tr("Disable the sunnypilot Longitudinal Control (alpha) toggle to allow Intelligent Cruise Button Management.");
+ } else {
+ long_desc = icbm_unavaialble + " " + tr("sunnypilot Longitudinal Control is the default longitudinal control for this platform.");
+ }
+ }
+
+ intelligentCruiseButtonManagement->setDescription("" + long_desc + "
" + icbm_description);
+ intelligentCruiseButtonManagement->showDescription();
}
if (has_longitudinal_control || has_icbm) {
@@ -151,10 +168,11 @@ void LongitudinalPanel::refresh(bool _offroad) {
has_longitudinal_control = false;
is_pcm_cruise = false;
has_icbm = false;
+ intelligentCruiseButtonManagement->setDescription("" + tr("Start the vehicle to check vehicle compatibility.") + "" + icbm_description);
}
QString accEnabledDescription = tr("Enable custom Short & Long press increments for cruise speed increase/decrease.");
- QString accNoLongDescription = tr("This feature can only be used with openpilot longitudinal control enabled.");
+ QString accNoLongDescription = tr("This feature can only be used with sunnypilot longitudinal control enabled.");
QString accPcmCruiseDisabledDescription = tr("This feature is not supported on this platform due to vehicle limitations.");
QString onroadOnlyDescription = tr("Start the vehicle to check vehicle compatibility.");
diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h
index c94d40cfde..b1ae95a01e 100644
--- a/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h
+++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h
@@ -33,7 +33,7 @@ private:
static QString toggleDisableMsg(bool _offroad, bool _has_longitudinal_control) {
if (!_has_longitudinal_control) {
- return tr("This feature can only be used with openpilot longitudinal control enabled.");
+ return tr("This feature can only be used with sunnypilot longitudinal control enabled.");
}
if (!_offroad) {
@@ -57,7 +57,7 @@ private:
}
return QString("%1
%2
%3
%4
")
- .arg(tr("Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control."))
+ .arg(tr("Fine-tune your driving experience by adjusting acceleration smoothness with sunnypilot longitudinal control."))
.arg(off_str)
.arg(dynamic_str)
.arg(predictive_str);
diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc
index 0e42f777cd..37e982a982 100644
--- a/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc
+++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc
@@ -119,7 +119,7 @@ VisualsPanel::VisualsPanel(QWidget *parent) : QWidget(parent) {
// Visuals: Display Metrics below Chevron
std::vector chevron_info_settings_texts{tr("Off"), tr("Distance"), tr("Speed"), tr("Time"), tr("All")};
chevron_info_settings = new ButtonParamControlSP(
- "ChevronInfo", tr("Display Metrics Below Chevron"), tr("Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control)."),
+ "ChevronInfo", tr("Display Metrics Below Chevron"), tr("Display useful metrics below the chevron that tracks the lead car (only applicable to cars with sunnypilot longitudinal control)."),
"",
chevron_info_settings_texts,
200);
@@ -159,8 +159,8 @@ void VisualsPanel::refreshLongitudinalStatus() {
}
if (chevron_info_settings) {
- QString chevronEnabledDescription = tr("Display useful metrics below the chevron that tracks the lead car (only applicable to cars with openpilot longitudinal control).");
- QString chevronNoLongDescription = tr("This feature requires openpilot longitudinal control to be available.");
+ QString chevronEnabledDescription = tr("Display useful metrics below the chevron that tracks the lead car (only applicable to cars with sunnypilot longitudinal control).");
+ QString chevronNoLongDescription = tr("This feature requires sunnypilot longitudinal control to be available.");
if (has_longitudinal_control) {
chevron_info_settings->setDescription(chevronEnabledDescription);
diff --git a/selfdrive/ui/sunnypilot/qt/widgets/controls.h b/selfdrive/ui/sunnypilot/qt/widgets/controls.h
index e111ce2fb5..71041556b7 100644
--- a/selfdrive/ui/sunnypilot/qt/widgets/controls.h
+++ b/selfdrive/ui/sunnypilot/qt/widgets/controls.h
@@ -390,7 +390,7 @@ class ButtonParamControlSP : public MultiButtonControlSP {
Q_OBJECT
public:
ButtonParamControlSP(const QString ¶m, const QString &title, const QString &desc, const QString &icon,
- const std::vector &button_texts, const int minimum_button_width = 225, const bool inline_layout = false, bool advancedControl = false) : MultiButtonControlSP(title, desc, icon,
+ const std::vector &button_texts, const int minimum_button_width = 380, const bool inline_layout = false, bool advancedControl = false) : MultiButtonControlSP(title, desc, icon,
button_texts, minimum_button_width, inline_layout, advancedControl) {
key = param.toStdString();
int value = atoi(params.get(key).c_str());
diff --git a/sunnypilot/common/version.h b/sunnypilot/common/version.h
index 5086f103f8..b52d984b4d 100644
--- a/sunnypilot/common/version.h
+++ b/sunnypilot/common/version.h
@@ -1 +1 @@
-#define SUNNYPILOT_VERSION "2025.001.000"
+#define SUNNYPILOT_VERSION "2025.002.000"
diff --git a/sunnypilot/models/fetcher.py b/sunnypilot/models/fetcher.py
index 3be6e0b46c..8c9c4eac71 100644
--- a/sunnypilot/models/fetcher.py
+++ b/sunnypilot/models/fetcher.py
@@ -116,7 +116,7 @@ class ModelCache:
class ModelFetcher:
"""Handles fetching and caching of model data from remote source"""
- MODEL_URL = "https://docs.sunnypilot.ai/driving_models_v7.json"
+ MODEL_URL = "https://docs.sunnypilot.ai/driving_models_v8.json"
def __init__(self, params: Params):
self.params = params
diff --git a/sunnypilot/navd/navigation_helpers/nav_instructions.py b/sunnypilot/navd/navigation_helpers/nav_instructions.py
index a734d663ec..7fea0ad4c8 100644
--- a/sunnypilot/navd/navigation_helpers/nav_instructions.py
+++ b/sunnypilot/navd/navigation_helpers/nav_instructions.py
@@ -83,7 +83,7 @@ class NavigationInstructions:
'maneuver': step['maneuver'],
'location': location,
'cumulative_distance': cumulative_distances[closest_idx],
- 'maxspeed': maxspeed[min(closest_idx, len(maxspeed) - 1)],
+ 'maxspeed': maxspeed[min(closest_idx, len(maxspeed) - 1)] if len(maxspeed) > 0 else (0, 'kmh'),
'modifier': string_to_direction(step['modifier']),
'instruction': step['instruction'],
})
diff --git a/sunnypilot/selfdrive/car/interfaces.py b/sunnypilot/selfdrive/car/interfaces.py
index 6a868ab85b..7d63d3c5ba 100644
--- a/sunnypilot/selfdrive/car/interfaces.py
+++ b/sunnypilot/selfdrive/car/interfaces.py
@@ -11,7 +11,7 @@ from opendbc.car.interfaces import CarInterfaceBase
from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
from openpilot.sunnypilot.selfdrive.controls.lib.nnlc.helpers import get_nn_model_path
-from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit.common import Mode as SpeedLimitMode
+from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit.helpers import set_speed_limit_assist_availability
import openpilot.system.sentry as sentry
@@ -87,8 +87,7 @@ def _cleanup_unsupported_params(CP: structs.CarParams, CP_SP: structs.CarParamsS
params.remove("SmartCruiseControlVision")
params.remove("SmartCruiseControlMap")
- if params.get("SpeedLimitMode", return_default=True) == SpeedLimitMode.assist:
- params.put("SpeedLimitMode", int(SpeedLimitMode.warning))
+ set_speed_limit_assist_availability(CP, CP_SP, params)
def setup_interfaces(CI: CarInterfaceBase, params: Params = None) -> None:
diff --git a/sunnypilot/selfdrive/controls/lib/longitudinal_planner.py b/sunnypilot/selfdrive/controls/lib/longitudinal_planner.py
index 4c2443f253..68b50c3e19 100644
--- a/sunnypilot/selfdrive/controls/lib/longitudinal_planner.py
+++ b/sunnypilot/selfdrive/controls/lib/longitudinal_planner.py
@@ -22,13 +22,13 @@ LongitudinalPlanSource = custom.LongitudinalPlanSP.LongitudinalPlanSource
class LongitudinalPlannerSP:
- def __init__(self, CP: structs.CarParams, mpc):
+ def __init__(self, CP: structs.CarParams, CP_SP: structs.CarParamsSP, mpc):
self.events_sp = EventsSP()
self.resolver = SpeedLimitResolver()
self.dec = DynamicExperimentalController(CP, mpc)
self.scc = SmartCruiseControl()
self.resolver = SpeedLimitResolver()
- self.sla = SpeedLimitAssist(CP)
+ self.sla = SpeedLimitAssist(CP, CP_SP)
self.generation = int(model_bundle.generation) if (model_bundle := get_active_bundle()) else None
self.source = LongitudinalPlanSource.cruise
self.e2e_alerts_helper = E2EAlertsHelper()
diff --git a/sunnypilot/selfdrive/controls/lib/speed_limit/helpers.py b/sunnypilot/selfdrive/controls/lib/speed_limit/helpers.py
index 40e8f653ff..4f388befc7 100644
--- a/sunnypilot/selfdrive/controls/lib/speed_limit/helpers.py
+++ b/sunnypilot/selfdrive/controls/lib/speed_limit/helpers.py
@@ -5,7 +5,10 @@ This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
+from cereal import custom, car
from openpilot.common.constants import CV
+from openpilot.common.params import Params
+from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit.common import Mode as SpeedLimitMode
def compare_cluster_target(v_cruise_cluster: float, target_set_speed: float, is_metric: bool) -> tuple[bool, bool]:
@@ -17,3 +20,25 @@ def compare_cluster_target(v_cruise_cluster: float, target_set_speed: float, is_
req_minus = v_cruise_cluster_conv > target_set_speed_conv
return req_plus, req_minus
+
+
+def set_speed_limit_assist_availability(CP: car.CarParams, CP_SP: custom.CarParamsSP, params: Params = None) -> bool:
+ if params is None:
+ params = Params()
+
+ is_release = params.get_bool("IsReleaseSpBranch")
+ disallow_in_release = CP.brand == "tesla" and is_release
+ always_disallow = CP.brand == "rivian"
+ allowed = True
+
+ if disallow_in_release or always_disallow:
+ allowed = False
+
+ if not CP.openpilotLongitudinalControl and CP_SP.pcmCruiseSpeed:
+ allowed = False
+
+ if not allowed:
+ if params.get("SpeedLimitMode", return_default=True) == SpeedLimitMode.assist:
+ params.put("SpeedLimitMode", int(SpeedLimitMode.warning))
+
+ return allowed
diff --git a/sunnypilot/selfdrive/controls/lib/speed_limit/speed_limit_assist.py b/sunnypilot/selfdrive/controls/lib/speed_limit/speed_limit_assist.py
index b948452059..ff7be8a8be 100644
--- a/sunnypilot/selfdrive/controls/lib/speed_limit/speed_limit_assist.py
+++ b/sunnypilot/selfdrive/controls/lib/speed_limit/speed_limit_assist.py
@@ -10,13 +10,13 @@ from cereal import custom, car
from openpilot.common.params import Params
from openpilot.common.constants import CV
from openpilot.common.realtime import DT_MDL
-from openpilot.sunnypilot import PARAMS_UPDATE_PERIOD
from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N
+from openpilot.selfdrive.modeld.constants import ModelConstants
+from openpilot.sunnypilot import PARAMS_UPDATE_PERIOD
from openpilot.sunnypilot.selfdrive.selfdrived.events import EventsSP
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit import PCM_LONG_REQUIRED_MAX_SET_SPEED, CONFIRM_SPEED_THRESHOLD
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit.common import Mode
-from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit.helpers import compare_cluster_target
-from openpilot.selfdrive.modeld.constants import ModelConstants
+from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit.helpers import compare_cluster_target, set_speed_limit_assist_availability
ButtonType = car.CarState.ButtonEvent.Type
EventNameSP = custom.OnroadEventSP.EventName
@@ -38,7 +38,7 @@ LIMIT_MIN_ACC = -1.5 # m/s^2 Maximum deceleration allowed for limit controllers
LIMIT_MAX_ACC = 1.0 # m/s^2 Maximum acceleration allowed for limit controllers to provide while active.
LIMIT_MIN_SPEED = 8.33 # m/s, Minimum speed limit to provide as solution on limit controllers.
LIMIT_SPEED_OFFSET_TH = -1. # m/s Maximum offset between speed limit and current speed for adapting state.
-V_CRUISE_UNSET = 255
+V_CRUISE_UNSET = 255.
CRUISE_BUTTONS_PLUS = (ButtonType.accelCruise, ButtonType.resumeCruise)
CRUISE_BUTTONS_MINUS = (ButtonType.decelCruise, ButtonType.setCruise)
@@ -52,13 +52,15 @@ class SpeedLimitAssist:
a_ego: float
v_offset: float
- def __init__(self, CP):
+ def __init__(self, CP: car.CarParams, CP_SP: custom.CarParamsSP):
self.params = Params()
self.CP = CP
+ self.CP_SP = CP_SP
self.frame = -1
self.long_engaged_timer = 0
self.pre_active_timer = 0
self.is_metric = self.params.get_bool("IsMetric")
+ set_speed_limit_assist_availability(self.CP, self.CP_SP, self.params)
self.enabled = self.params.get("SpeedLimitMode", return_default=True) == Mode.assist
self.long_enabled = False
self.long_enabled_prev = False
@@ -140,6 +142,7 @@ class SpeedLimitAssist:
def update_params(self) -> None:
if self.frame % int(PARAMS_UPDATE_PERIOD / DT_MDL) == 0:
self.is_metric = self.params.get_bool("IsMetric")
+ set_speed_limit_assist_availability(self.CP, self.CP_SP, self.params)
self.enabled = self.params.get("SpeedLimitMode", return_default=True) == Mode.assist
def update_car_state(self, CS: car.CarState) -> None:
diff --git a/sunnypilot/selfdrive/controls/lib/speed_limit/tests/test_speed_limit_assist.py b/sunnypilot/selfdrive/controls/lib/speed_limit/tests/test_speed_limit_assist.py
index 168c53016b..aa6650adda 100644
--- a/sunnypilot/selfdrive/controls/lib/speed_limit/tests/test_speed_limit_assist.py
+++ b/sunnypilot/selfdrive/controls/lib/speed_limit/tests/test_speed_limit_assist.py
@@ -4,17 +4,22 @@ Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
-from cereal import custom
+import pytest
+
+from cereal import custom
from opendbc.car.car_helpers import interfaces
+from opendbc.car.rivian.values import CAR as RIVIAN
+from opendbc.car.tesla.values import CAR as TESLA
from opendbc.car.toyota.values import CAR as TOYOTA
from openpilot.common.constants import CV
from openpilot.common.params import Params
from openpilot.common.realtime import DT_MDL
from openpilot.selfdrive.car.cruise import V_CRUISE_UNSET
+from openpilot.sunnypilot import PARAMS_UPDATE_PERIOD
from openpilot.sunnypilot.selfdrive.car import interfaces as sunnypilot_interfaces
-from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit.common import Mode
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit import PCM_LONG_REQUIRED_MAX_SET_SPEED
+from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit.common import Mode
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit.speed_limit_assist import SpeedLimitAssist, \
PRE_ACTIVE_GUARD_PERIOD, ACTIVE_STATES
from openpilot.sunnypilot.selfdrive.selfdrived.events import EventsSP
@@ -30,15 +35,29 @@ SPEED_LIMITS = {
'freeway': 80 * CV.MPH_TO_MS, # 80 mph
}
+DEFAULT_CAR = TOYOTA.TOYOTA_RAV4_TSS2
+
+
+@pytest.fixture
+def car_name(request):
+ return getattr(request, "param", DEFAULT_CAR)
+
+
+@pytest.fixture(autouse=True)
+def set_car_name_on_instance(request, car_name):
+ instance = getattr(request, "instance", None)
+ if instance:
+ instance.car_name = car_name
+
class TestSpeedLimitAssist:
- def setup_method(self):
+ def setup_method(self, method):
self.params = Params()
self.reset_custom_params()
self.events_sp = EventsSP()
- CI = self._setup_platform(TOYOTA.TOYOTA_RAV4_TSS2)
- self.sla = SpeedLimitAssist(CI.CP)
+ CI = self._setup_platform(self.car_name)
+ self.sla = SpeedLimitAssist(CI.CP, CI.CP_SP)
self.sla.pre_active_timer = int(PRE_ACTIVE_GUARD_PERIOD[self.sla.pcm_op_long] / DT_MDL)
self.pcm_long_max_set_speed = PCM_LONG_REQUIRED_MAX_SET_SPEED[self.sla.is_metric][1] # use 80 MPH for now
self.speed_conv = CV.MS_TO_KPH if self.sla.is_metric else CV.MS_TO_MPH
@@ -51,10 +70,12 @@ class TestSpeedLimitAssist:
CP = CarInterface.get_non_essential_params(car_name)
CP_SP = CarInterface.get_non_essential_params_sp(CP, car_name)
CI = CarInterface(CP, CP_SP)
+ CI.CP.openpilotLongitudinalControl = True # always assume it's openpilot longitudinal
sunnypilot_interfaces.setup_interfaces(CI, self.params)
return CI
def reset_custom_params(self):
+ self.params.put("IsReleaseSpBranch", True)
self.params.put("SpeedLimitMode", int(Mode.assist))
self.params.put_bool("IsMetric", False)
self.params.put("SpeedLimitOffsetType", 0)
@@ -84,6 +105,22 @@ class TestSpeedLimitAssist:
assert not self.sla.is_active
assert V_CRUISE_UNSET == self.sla.get_v_target_from_control()
+ @pytest.mark.parametrize("car_name", [RIVIAN.RIVIAN_R1_GEN1, TESLA.TESLA_MODEL_Y], indirect=True)
+ def test_disallowed_brands(self, car_name):
+ """
+ Speed Limit Assist is disabled for the following brands and conditions:
+ - All Tesla and is a release branch;
+ - All Rivian
+ """
+ assert not self.sla.enabled
+
+ # stay disallowed even when the param may have changed from somewhere else
+ self.params.put("SpeedLimitMode", int(Mode.assist))
+ for _ in range(int(PARAMS_UPDATE_PERIOD / DT_MDL)):
+ self.sla.update(True, False, SPEED_LIMITS['city'], 0, SPEED_LIMITS['highway'], SPEED_LIMITS['city'],
+ SPEED_LIMITS['city'], True, 0, self.events_sp)
+ assert not self.sla.enabled
+
def test_disabled(self):
self.params.put("SpeedLimitMode", int(Mode.off))
for _ in range(int(10. / DT_MDL)):
diff --git a/sunnypilot/selfdrive/locationd/tests/test_locationd.py b/sunnypilot/selfdrive/locationd/tests/test_locationd.py
index c409f5b5a5..877bc821da 100644
--- a/sunnypilot/selfdrive/locationd/tests/test_locationd.py
+++ b/sunnypilot/selfdrive/locationd/tests/test_locationd.py
@@ -1,4 +1,5 @@
import pytest
+import platform
import json
import random
import time
@@ -12,6 +13,10 @@ from openpilot.common.transformations.coordinates import ecef2geodetic
from openpilot.system.manager.process_config import managed_processes
+if platform.system() == 'Darwin':
+ pytest.skip("Skipping locationd test on macOS due to unsupported msgq.", allow_module_level=True)
+
+
class TestLocationdProc:
LLD_MSGS = ['gpsLocationExternal', 'cameraOdometry', 'carState', 'liveCalibration',
'accelerometer', 'gyroscope', 'magnetometer']
diff --git a/system/version.py b/system/version.py
index 268aaf11ea..a3f2e36da5 100755
--- a/system/version.py
+++ b/system/version.py
@@ -10,7 +10,7 @@ from openpilot.common.basedir import BASEDIR
from openpilot.common.swaglog import cloudlog
from openpilot.common.git import get_commit, get_origin, get_branch, get_short_branch, get_commit_date
-RELEASE_SP_BRANCHES = ['release-c3', 'release']
+RELEASE_SP_BRANCHES = ['release-c3', 'release', 'release-tizi', 'release-tici', 'release-tizi-staging', 'release-tici-staging']
TESTED_SP_BRANCHES = ['staging-c3', 'staging-c3-new', 'staging']
MASTER_SP_BRANCHES = ['master']
RELEASE_BRANCHES = ['release3-staging', 'release3', 'release-tici', 'nightly']