From af2e153e52fea84b59d7b0e265bbff1cf9b953d1 Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Thu, 27 Aug 2026 14:24:43 -0500 Subject: [PATCH] GUM --- docs/CARS.md | 4 +- opendbc_repo/docs/CARS.md | 4 +- opendbc_repo/opendbc/car/gm/fingerprints.py | 29 ++++----- opendbc_repo/opendbc/car/gm/gmcan.py | 10 +-- opendbc_repo/opendbc/car/gm/interface.py | 5 +- opendbc_repo/opendbc/car/gm/tests/test_gm.py | 41 ++++++++++++ opendbc_repo/opendbc/car/gm/values.py | 3 +- opendbc_repo/opendbc/car/interfaces.py | 4 ++ .../opendbc/car/tests/test_car_interfaces.py | 24 +++++++ opendbc_repo/opendbc/safety/modes/gm.h | 9 +++ opendbc_repo/opendbc/safety/tests/test_gm.py | 22 +++++++ .../controls/tests/test_starpilot_vcruise.py | 17 +++++ .../settings/starpilot/longitudinal.py | 4 +- .../ui/onroad/starpilot/starpilot_border.py | 10 +-- .../common/assets/device_settings_layout.json | 24 +++++++ .../controls/lib/curve_speed_controller.py | 9 ++- .../components/tools/device_settings.css | 7 ++ .../components/tools/device_settings.js | 64 ++++++++++++++++++- .../tests/test_device_settings_layout.py | 11 ++++ .../tests/test_navigation_params.py | 32 ++++++++++ starpilot/system/the_galaxy/the_galaxy.py | 9 +++ 21 files changed, 305 insertions(+), 37 deletions(-) diff --git a/docs/CARS.md b/docs/CARS.md index 0cc85940a..05f5084a7 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -76,7 +76,7 @@ A supported vehicle is one that just works when you install a comma device. All |Chevrolet|Volt 2019|Adaptive Cruise Control (ACC) & LKAS|Stock|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM SDGM connector
- 1 OBD-C cable (2 ft)
- 1 USB-C coupler
- 1 comma four
- 1 comma power v3
- 1 harness box
- 1 long OBD-C cable (9.5 ft)
- 1 mount
Buy Here
||| |Chevrolet|Volt ASCM Harness 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 OBD-C cable (2 ft)
- 1 comma four
- 1 harness box
- 1 mount
Buy Here
||| |Chevrolet|Volt Camera Harness 2017-18|Flashed camera-forward integration with ACC|openpilot available[1](#footnotes)|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 OBD-C cable (2 ft)
- 1 comma four
- 1 harness box
- 1 mount
Buy Here
||| -|Chevrolet|Volt No-ACC 2017-18|Adaptive Cruise Control (ACC)|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 GM connector
- 1 OBD-C cable (2 ft)
- 1 comma four
- 1 harness box
- 1 mount
Buy Here
||| +|Chevrolet|Volt No-ACC 2016-18 (OBD-C / L&P Harness)|Redneck ACC|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
Parts- 1 OBD-II connector
- 1 OBD-C cable (2 ft)
- 1 comma four
- 1 harness box
- 1 mount
Buy Here
||| |Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 OBD-C cable (2 ft)
- 1 comma four
- 1 comma power v3
- 1 harness box
- 1 mount
Buy Here
||| |Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 OBD-C cable (2 ft)
- 1 comma four
- 1 comma power v3
- 1 harness box
- 1 mount
Buy Here
||| |Chrysler|Pacifica 2021-23|All|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
Parts- 1 FCA connector
- 1 OBD-C cable (2 ft)
- 1 comma four
- 1 comma power v3
- 1 harness box
- 1 mount
Buy Here
||| @@ -545,4 +545,4 @@ openpilot does not yet support these Toyota models due to a new message authenti * Toyota Camry 2025+ * Lexus NX 2022+ * Toyota bZ4x 2023+ -* Subaru Solterra 2023+ \ No newline at end of file +* Subaru Solterra 2023+ diff --git a/opendbc_repo/docs/CARS.md b/opendbc_repo/docs/CARS.md index b761a8544..644f75940 100644 --- a/opendbc_repo/docs/CARS.md +++ b/opendbc_repo/docs/CARS.md @@ -85,7 +85,7 @@ |Chevrolet|Volt 2019|Adaptive Cruise Control (ACC) & LKAS|[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)| +|Chevrolet|Volt No-ACC 2016-18 (OBD-C / L&P Harness)|Redneck 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)| @@ -578,4 +578,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. \ No newline at end of file +may one day be supported, but we have no immediate plans to support FlexRay. diff --git a/opendbc_repo/opendbc/car/gm/fingerprints.py b/opendbc_repo/opendbc/car/gm/fingerprints.py index 197f9ed0d..87edc5f91 100644 --- a/opendbc_repo/opendbc/car/gm/fingerprints.py +++ b/opendbc_repo/opendbc/car/gm/fingerprints.py @@ -30,20 +30,20 @@ FINGERPRINTS = { CAR.BUICK_LACROSSE: [{ 190: 6, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 2, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 353: 3, 381: 6, 386: 8, 388: 8, 393: 7, 398: 8, 407: 7, 413: 8, 417: 7, 419: 1, 422: 4, 426: 7, 431: 8, 442: 8, 451: 8, 452: 8, 453: 6, 455: 7, 456: 8, 463: 3, 479: 3, 481: 7, 485: 8, 487: 8, 489: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 8, 503: 1, 508: 8, 510: 8, 528: 5, 532: 6, 534: 2, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 573: 1, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 5, 707: 8, 753: 5, 761: 7, 801: 8, 804: 3, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 872: 1, 882: 8, 890: 1, 892: 2, 893: 1, 894: 1, 961: 8, 967: 4, 969: 8, 977: 8, 979: 8, 985: 5, 1001: 8, 1005: 6, 1009: 8, 1011: 6, 1013: 3, 1017: 8, 1019: 2, 1020: 8, 1022: 1, 1105: 6, 1217: 8, 1221: 5, 1223: 2, 1225: 7, 1233: 8, 1243: 3, 1249: 8, 1257: 6, 1259: 8, 1261: 7, 1263: 4, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1609: 8, 1613: 8, 1649: 8, 1792: 8, 1798: 8, 1824: 8, 1825: 8, 1840: 8, 1842: 8, 1858: 8, 1860: 8, 1863: 8, 1872: 8, 1875: 8, 1882: 8, 1888: 8, 1889: 8, 1892: 8, 1904: 7, 1906: 7, 1907: 7, 1912: 7, 1913: 7, 1914: 7, 1916: 7, 1918: 7, 1919: 7, 1937: 8, 1953: 8, 1968: 8, 2001: 8, 2017: 8, 2018: 8, 2020: 8, 2026: 8 }], - # CAR.CHEVROLET_VOLT_CC: [ - # FIXME: Need a message to distinguish flashed from non-flashed - # Volt Premier w/o acc 2016 - # { - # 170: 8, 171: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 199: 4, 201: 6, 209: 7, 211: 2, 241: 6, 288: 5, 289: 1, 290: 1, 298: 2, 304: 8, 308: 4, 309: 8, 311: 8, 313: 8, 320: 8, 328: 1, 352: 5, 368: 8, 381: 6, 384: 8, 386: 5, 388: 8, 389: 2, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 3, 508: 8, 512: 3, 528: 4, 530: 8, 532: 6, 537: 4, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 8, 563: 5, 564: 5, 565: 8, 566: 5, 567: 3, 568: 1, 577: 8, 578: 8, 594: 8, 647: 3, 707: 8, 711: 6, 717: 5, 761: 7, 800: 6, 810: 8, 821: 4, 823: 7, 832: 8, 840: 5, 842: 6, 844: 8, 866: 4, 869: 4, 961: 8, 969: 8, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1273: 3, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1602: 8, 1618: 8, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1922: 7, 1927: 7, 1928: 7, 1930: 7, 2016: 8, 2017: 8, 2018: 8, 2019: 8, 2020: 8, 2024: 8, 2025: 8, 2028: 8 - # }, - # { - # 201: 8, 493: 8, 495: 4, 193: 8, 197: 8, 209: 7, 171: 8, 456: 8, 199: 4, 489: 8, 211: 2, 499: 3, 390: 7, 532: 6, 568: 1, 761: 7, 381: 6, 485: 8, 189: 7, 479: 3, 711: 6, 501: 8, 241: 6, 717: 5, 869: 4, 389: 2, 454: 8, 170: 8, 190: 6, 497: 8, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 500: 6, 508: 8, 528: 4, 647: 3, 1105: 6, 1005: 6, 481: 7, 844: 8, 866: 4, 564: 5, 969: 8, 388: 8, 352: 5, 562: 8, 961: 8, 386: 8, 707: 8, 977: 8, 979: 7, 298: 8, 840: 5, 842: 5, 988: 6, 1001: 8, 560: 8, 546: 7, 558: 8, 309: 8, 995: 7, 311: 8, 566: 5, 567:3, 989: 8, 384: 4, 800: 6, 1033: 7, 1034: 7, 313: 8, 554: 3, 810: 8, 1017: 8, 1019: 2, 1020: 8, 1217: 8, 1223: 3, 1233: 8, 1227: 4, 1417: 8, 1009: 8, 1221: 5, 1275: 3, 1225: 7, 289: 8, 550: 8, 1273: 3, 1928: 7, 1187: 4, 1265: 8, 1927: 7, 1267: 1, 1906: 7, 288: 5, 304: 1, 328: 1, 1912: 7, 320: 3, 1910: 7, 563: 5, 1249: 8, 1930: 7, 1257: 6, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 565: 5, 1280: 4, 1907: 7 - # }, - # # Volt Premier w/o ACC 2018 + Pedal - # { - # 189: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 497: 8, 500: 6, 501: 8, 513: 6, 528: 4, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 717: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1922: 7, 1930: 7 - # } - # ], + CAR.CHEVROLET_VOLT_CC: [ + # Captured no-ACC Volt fingerprints for OBD-C/L&P harness installations + # Volt Premier w/o ACC 2016 + { + 170: 8, 171: 8, 189: 7, 190: 6, 192: 5, 193: 8, 197: 8, 199: 4, 201: 6, 209: 7, 211: 2, 241: 6, 288: 5, 289: 1, 290: 1, 298: 2, 304: 8, 308: 4, 309: 8, 311: 8, 313: 8, 320: 8, 328: 1, 352: 5, 368: 8, 381: 6, 384: 8, 386: 5, 388: 8, 389: 2, 390: 7, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 454: 8, 456: 8, 458: 8, 479: 3, 481: 7, 485: 8, 489: 5, 493: 8, 495: 4, 497: 8, 499: 3, 500: 6, 501: 3, 508: 8, 512: 3, 528: 4, 530: 8, 532: 6, 537: 4, 539: 8, 542: 7, 546: 7, 550: 8, 554: 3, 558: 8, 560: 6, 562: 8, 563: 5, 564: 5, 565: 8, 566: 5, 567: 3, 568: 1, 577: 8, 578: 8, 594: 8, 647: 3, 707: 8, 711: 6, 717: 5, 761: 7, 800: 6, 810: 8, 821: 4, 823: 7, 832: 8, 840: 5, 842: 6, 844: 8, 866: 4, 869: 4, 961: 8, 969: 8, 977: 8, 979: 7, 988: 6, 989: 8, 995: 7, 1001: 5, 1003: 5, 1005: 6, 1009: 8, 1017: 8, 1019: 2, 1020: 8, 1033: 7, 1034: 7, 1105: 6, 1187: 4, 1217: 8, 1221: 5, 1223: 3, 1225: 7, 1227: 4, 1233: 8, 1249: 8, 1257: 6, 1265: 8, 1267: 1, 1273: 3, 1275: 3, 1280: 4, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 1417: 8, 1601: 8, 1602: 8, 1618: 8, 1906: 7, 1907: 7, 1910: 7, 1912: 7, 1922: 7, 1927: 7, 1928: 7, 1930: 7, 2016: 8, 2017: 8, 2018: 8, 2019: 8, 2020: 8, 2024: 8, 2025: 8, 2028: 8 + }, + { + 201: 8, 493: 8, 495: 4, 193: 8, 197: 8, 209: 7, 171: 8, 456: 8, 199: 4, 489: 8, 211: 2, 499: 3, 390: 7, 532: 6, 568: 1, 761: 7, 381: 6, 485: 8, 189: 7, 479: 3, 711: 6, 501: 8, 241: 6, 717: 5, 869: 4, 389: 2, 454: 8, 170: 8, 190: 6, 497: 8, 417: 7, 419: 1, 426: 7, 451: 8, 452: 8, 453: 6, 500: 6, 508: 8, 528: 4, 647: 3, 1105: 6, 1005: 6, 481: 7, 844: 8, 866: 4, 564: 5, 969: 8, 388: 8, 352: 5, 562: 8, 961: 8, 386: 8, 707: 8, 977: 8, 979: 7, 298: 8, 840: 5, 842: 5, 988: 6, 1001: 8, 560: 8, 546: 7, 558: 8, 309: 8, 995: 7, 311: 8, 566: 5, 567:3, 989: 8, 384: 4, 800: 6, 1033: 7, 1034: 7, 313: 8, 554: 3, 810: 8, 1017: 8, 1019: 2, 1020: 8, 1217: 8, 1223: 3, 1233: 8, 1227: 4, 1417: 8, 1009: 8, 1221: 5, 1275: 3, 1225: 7, 289: 8, 550: 8, 1273: 3, 1928: 7, 1187: 4, 1265: 8, 1927: 7, 1267: 1, 1906: 7, 288: 5, 304: 1, 328: 1, 1912: 7, 320: 3, 1910: 7, 563: 5, 1249: 8, 1930: 7, 1257: 6, 1300: 8, 1322: 6, 1323: 4, 1328: 4, 565: 5, 1280: 4, 1907: 7 + }, + # Volt Premier w/o ACC 2018 + Pedal + { + 189: 7, 193: 8, 197: 8, 201: 8, 209: 7, 211: 2, 241: 6, 288: 5, 298: 8, 304: 1, 308: 4, 309: 8, 311: 8, 313: 8, 320: 3, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 451: 8, 452: 8, 453: 6, 479: 3, 481: 7, 485: 8, 489: 8, 493: 8, 497: 8, 500: 6, 501: 8, 513: 6, 528: 4, 532: 6, 560: 8, 562: 8, 563: 5, 565: 5, 566: 5, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 707: 8, 717: 5, 761: 7, 800: 6, 810: 8, 840: 5, 842: 5, 844: 8, 869: 4, 977: 8, 1001: 8, 1017: 8, 1020: 8, 1033: 7, 1034: 7, 1217: 8, 1221: 5, 1233: 8, 1249: 8, 1265: 8, 1267: 1, 1280: 4, 1300: 8, 1922: 7, 1930: 7 + } + ], CAR.BUICK_REGAL: [{ 190: 8, 193: 8, 197: 8, 199: 4, 201: 8, 209: 7, 211: 8, 241: 6, 249: 8, 288: 5, 298: 8, 304: 1, 309: 8, 311: 8, 313: 8, 320: 3, 322: 7, 328: 1, 352: 5, 381: 6, 384: 4, 386: 8, 388: 8, 393: 7, 398: 8, 407: 7, 413: 8, 417: 8, 419: 8, 422: 4, 426: 8, 431: 8, 442: 8, 451: 8, 452: 8, 453: 8, 455: 7, 456: 8, 463: 3, 479: 8, 481: 7, 485: 8, 487: 8, 489: 8, 495: 8, 497: 8, 499: 3, 500: 8, 501: 8, 508: 8, 528: 5, 532: 6, 554: 3, 560: 8, 562: 8, 563: 5, 564: 5, 565: 5, 567: 5, 569: 3, 573: 1, 577: 8, 578: 8, 579: 8, 587: 8, 608: 8, 609: 6, 610: 6, 611: 6, 612: 8, 613: 8, 647: 3, 707: 8, 715: 8, 717: 5, 753: 5, 761: 7, 810: 8, 840: 5, 842: 5, 844: 8, 866: 4, 869: 4, 880: 6, 882: 8, 884: 8, 890: 1, 892: 2, 893: 2, 894: 1, 961: 8, 967: 8, 969: 8, 977: 8, 979: 8, 985: 8, 1001: 8, 1005: 6, 1009: 8, 1011: 8, 1013: 3, 1017: 8, 1020: 8, 1024: 8, 1025: 8, 1026: 8, 1027: 8, 1028: 8, 1029: 8, 1030: 8, 1031: 8, 1032: 2, 1033: 7, 1034: 7, 1105: 6, 1217: 8, 1221: 5, 1223: 8, 1225: 7, 1233: 8, 1249: 8, 1257: 6, 1259: 8, 1261: 8, 1263: 8, 1265: 8, 1267: 8, 1271: 8, 1280: 4, 1296: 4, 1300: 8, 1322: 6, 1328: 4, 1417: 8, 1601: 8, 1602: 8, 1603: 7, 1611: 8, 1618: 8, 1906: 8, 1907: 7, 1912: 7, 1914: 7, 1916: 7, 1919: 7, 1930: 7, 2016: 8, 2018: 8, 2019: 8, 2024: 8, 2026: 8 }], @@ -207,7 +207,6 @@ FINGERPRINTS = { FINGERPRINTS.update({ CAR.CHEVROLET_VOLT_ASCM: FINGERPRINTS[CAR.CHEVROLET_VOLT], CAR.CHEVROLET_VOLT_CAMERA: [{**fp, CAMERA_DIAGNOSTIC_ADDRESS: 8, CAMERA_DIAGNOSTIC_RX_ADDRESS: 8} for fp in FINGERPRINTS[CAR.CHEVROLET_VOLT]], - CAR.CHEVROLET_VOLT_CC: FINGERPRINTS[CAR.CHEVROLET_VOLT], CAR.GMC_ACADIA_ASCM: FINGERPRINTS[CAR.GMC_ACADIA], CAR.CHEVROLET_MALIBU_ASCM: FINGERPRINTS[CAR.CHEVROLET_MALIBU], CAR.CADILLAC_ESCALADE_ASCM: FINGERPRINTS[CAR.CADILLAC_ESCALADE], diff --git a/opendbc_repo/opendbc/car/gm/gmcan.py b/opendbc_repo/opendbc/car/gm/gmcan.py index 552cb6a23..3f8a672c9 100644 --- a/opendbc_repo/opendbc/car/gm/gmcan.py +++ b/opendbc_repo/opendbc/car/gm/gmcan.py @@ -1,4 +1,4 @@ -from opendbc.car import DT_CTRL +from opendbc.car import DT_CTRL, structs from opendbc.car.can_definitions import CanData from opendbc.car.common.conversions import Conversions as CV from opendbc.car.gm.values import CAR, CanBus, CruiseButtons, GMFlags @@ -420,9 +420,11 @@ def create_gm_cc_spam_command(packer, controller, CS, actuators, starpilot_toggl idx = (CS.buttons_counter + 1) % 4 # Need to predict the next idx for '22-23 EUV msgs = [create_buttons(packer, CanBus.POWERTRAIN, idx, cruise_btn)] - # Flashed camera-forward Volt CC installs also need the button spoof on the - # camera side. Removed-camera installs set NO_CAMERA and keep this PT-only. - if CS.CP.carFingerprint == CAR.CHEVROLET_VOLT_CC and not (CS.CP.flags & GMFlags.NO_CAMERA.value): + # A camera-forward Volt CC install needs the button spoof on both sides. + # The OBD-C/L&P gateway variant has no camera bus and remains PT-only. + if (CS.CP.carFingerprint == CAR.CHEVROLET_VOLT_CC and + getattr(CS.CP, "networkLocation", None) == structs.CarParams.NetworkLocation.fwdCamera and + not (CS.CP.flags & GMFlags.NO_CAMERA.value)): msgs.append(create_buttons(packer, CanBus.CAMERA, idx, cruise_btn)) return msgs else: diff --git a/opendbc_repo/opendbc/car/gm/interface.py b/opendbc_repo/opendbc/car/gm/interface.py index 59e26aa49..1c89f5eb3 100755 --- a/opendbc_repo/opendbc/car/gm/interface.py +++ b/opendbc_repo/opendbc/car/gm/interface.py @@ -273,7 +273,6 @@ class CarInterface(CarInterfaceBase): kaofui_camera_cars = { CAR.CHEVROLET_VOLT_CAMERA, - CAR.CHEVROLET_VOLT_CC, CAR.CHEVROLET_MALIBU_HYBRID_CC, } bolt_cc_camera_cars = { @@ -685,6 +684,8 @@ class CarInterface(CarInterfaceBase): if candidate in CC_ONLY_CAR: ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_NO_ACC.value + if candidate == CAR.CHEVROLET_VOLT_CC and ret.networkLocation == NetworkLocation.gateway: + ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_VOLT_CC_GATEWAY.value if candidate in SDGM_CAR and ACCELERATOR_POS_MSG not in fingerprint[CanBus.POWERTRAIN]: ret.flags |= GMFlags.FORCE_BRAKE_C9.value @@ -698,7 +699,7 @@ class CarInterface(CarInterfaceBase): if ACCELERATOR_POS_MSG not in fingerprint[CanBus.POWERTRAIN]: ret.flags |= GMFlags.NO_ACCELERATOR_POS_MSG.value - if candidate == CAR.CHEVROLET_VOLT and ret.networkLocation == NetworkLocation.gateway: + if candidate in (CAR.CHEVROLET_VOLT, CAR.CHEVROLET_VOLT_CC) and ret.networkLocation == NetworkLocation.gateway: # Reuse the no-camera safety bit as an ASCM Volt selector for the alternate EBCM brake path. ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_NO_CAMERA.value diff --git a/opendbc_repo/opendbc/car/gm/tests/test_gm.py b/opendbc_repo/opendbc/car/gm/tests/test_gm.py index efe9331f7..cc34107fa 100644 --- a/opendbc_repo/opendbc/car/gm/tests/test_gm.py +++ b/opendbc_repo/opendbc/car/gm/tests/test_gm.py @@ -201,6 +201,45 @@ class TestGMInterface: assert car_params.flags & GMFlags.NO_CAMERA.value assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_NO_CAMERA.value + def test_volt_cc_obd_gateway_uses_cc_long_no_camera_path(self): + CarInterface = interfaces[CAR.CHEVROLET_VOLT_CC] + car_params = CarInterface.get_params( + CAR.CHEVROLET_VOLT_CC, + _empty_fingerprint(), + [], + alpha_long=False, + is_release=False, + docs=False, + starpilot_toggles=_test_starpilot_toggles(), + ) + + assert car_params.networkLocation == structs.CarParams.NetworkLocation.gateway + assert car_params.flags & GMFlags.CC_LONG.value + assert car_params.flags & GMFlags.NO_CAMERA.value + assert not (car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.HW_CAM.value) + assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_CC_LONG.value + assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_NO_ACC.value + assert car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_NO_CAMERA.value + + parsers = CarInterface.CarState.get_can_parsers(car_params) + assert "ECMCruiseControl" in parsers[Bus.pt].vl + assert not parsers[Bus.cam].vl + + def test_other_cc_only_gateway_does_not_use_volt_cc_safety_path(self): + CarInterface = interfaces[CAR.CHEVROLET_SILVERADO_CC] + car_params = CarInterface.get_params( + CAR.CHEVROLET_SILVERADO_CC, + _empty_fingerprint(), + [], + alpha_long=False, + is_release=False, + docs=False, + starpilot_toggles=_test_starpilot_toggles(), + ) + + assert car_params.networkLocation == structs.CarParams.NetworkLocation.gateway + assert not (car_params.safetyConfigs[0].safetyParam & GMSafetyFlags.FLAG_GM_VOLT_CC_GATEWAY.value) + def test_volt_ascm_sparse_fingerprint_without_camera_does_not_set_no_camera(self): CarInterface = interfaces[CAR.CHEVROLET_VOLT_ASCM] fingerprint = { @@ -533,6 +572,7 @@ class TestGMCarController: CP=SimpleNamespace( carFingerprint=CAR.CHEVROLET_VOLT_CC, flags=0, + networkLocation=structs.CarParams.NetworkLocation.fwdCamera, minEnableSpeed=24 * CV.MPH_TO_MS, ), buttons_counter=2, @@ -554,6 +594,7 @@ class TestGMCarController: CP=SimpleNamespace( carFingerprint=CAR.CHEVROLET_VOLT_CC, flags=GMFlags.NO_CAMERA.value, + networkLocation=structs.CarParams.NetworkLocation.gateway, minEnableSpeed=24 * CV.MPH_TO_MS, ), buttons_counter=2, diff --git a/opendbc_repo/opendbc/car/gm/values.py b/opendbc_repo/opendbc/car/gm/values.py index 3cc981f36..df281ebc4 100644 --- a/opendbc_repo/opendbc/car/gm/values.py +++ b/opendbc_repo/opendbc/car/gm/values.py @@ -175,6 +175,7 @@ class GMSafetyFlags(IntFlag): FLAG_GM_REMOTE_START_BOOTS_COMMA = 8192 FLAG_GM_PANDA_3D1_SCHED = 16384 FLAG_GM_PANDA_PADDLE_SCHED = 32768 + FLAG_GM_VOLT_CC_GATEWAY = 16384 class Footnote(Enum): @@ -247,7 +248,7 @@ class CAR(Platforms): dbc_dict=CHEVROLET_VOLT.dbc_dict, ) CHEVROLET_VOLT_CC = GMPlatformConfig( - [GMCarDocs("Chevrolet Volt No-ACC 2017-18", min_enable_speed=0)], + [GMCarDocs("Chevrolet Volt No-ACC 2016-18 (OBD-C / L&P Harness)", "Redneck ACC", min_enable_speed=0)], CHEVROLET_VOLT.specs, dbc_dict=CHEVROLET_VOLT.dbc_dict, ) diff --git a/opendbc_repo/opendbc/car/interfaces.py b/opendbc_repo/opendbc/car/interfaces.py index c8f4a5c59..77f3171d7 100644 --- a/opendbc_repo/opendbc/car/interfaces.py +++ b/opendbc_repo/opendbc/car/interfaces.py @@ -257,6 +257,10 @@ class CarInterfaceBase(ABC): if getattr(starpilot_toggles, "always_on_lateral_lkas", False): fp_ret.safetyConfigs[-1].safetyParam |= HyundaiStarPilotSafetyFlags.AOL_LKAS_ON_ENGAGE.value + if candidate == HYUNDAI.HYUNDAI_ELANTRA_HEV_2024 and \ + getattr(starpilot_toggles, "always_on_lateral_main", False): + fp_ret.safetyConfigs[-1].safetyParam |= HyundaiStarPilotSafetyFlags.AOL_LKAS_ON_ENGAGE.value + # LKASButtonControl == 9 means BUTTON_FUNCTIONS["AOL_TOGGLE"] in starpilot_variables. if params.get_bool("AlwaysOnLateral") and params.get_int("LKASButtonControl") == 9: fp_ret.safetyConfigs[-1].safetyParam |= HyundaiStarPilotSafetyFlags.AOL_LKAS_ON_ENGAGE.value diff --git a/opendbc_repo/opendbc/car/tests/test_car_interfaces.py b/opendbc_repo/opendbc/car/tests/test_car_interfaces.py index 5676c2ccc..ef0f85764 100644 --- a/opendbc_repo/opendbc/car/tests/test_car_interfaces.py +++ b/opendbc_repo/opendbc/car/tests/test_car_interfaces.py @@ -300,6 +300,30 @@ class TestCarInterfaces: ) assert fp_car_params.safetyConfigs[-1].safetyParam & HyundaiStarPilotSafetyFlags.AOL_LKAS_ON_ENGAGE.value + def test_hyundai_elantra_hev_auto_aol_sets_lkas_on_engage_flag(self): + toggles = get_test_starpilot_toggles() + toggles.always_on_lateral_main = True + fingerprint = {bus: {} for bus in range(8)} + + car_params = HyundaiCarInterface.get_params( + HYUNDAI_CAR.HYUNDAI_ELANTRA_HEV_2024, + fingerprint, + [], + alpha_long=True, + is_release=False, + docs=False, + starpilot_toggles=toggles, + ) + fp_car_params = HyundaiCarInterface.get_starpilot_params( + HYUNDAI_CAR.HYUNDAI_ELANTRA_HEV_2024, + fingerprint, + [], + car_params, + toggles, + ) + + assert fp_car_params.safetyConfigs[-1].safetyParam & HyundaiStarPilotSafetyFlags.AOL_LKAS_ON_ENGAGE.value + def test_toyota_disable_openpilot_long_sets_stock_long_safety_flag(self): CarInterface = interfaces[TOYOTA_CAR.TOYOTA_PRIUS_TSS2] fingerprint = {bus: {} for bus in range(8)} diff --git a/opendbc_repo/opendbc/safety/modes/gm.h b/opendbc_repo/opendbc/safety/modes/gm.h index af1e97e23..69c722c3b 100644 --- a/opendbc_repo/opendbc/safety/modes/gm.h +++ b/opendbc_repo/opendbc/safety/modes/gm.h @@ -559,6 +559,7 @@ static safety_config gm_init(uint16_t param) { const uint16_t GM_PARAM_REMOTE_START_BOOTS_COMMA = 8192; const uint16_t GM_PARAM_PANDA_3D1_SCHED = 16384; const uint16_t GM_PARAM_PANDA_PADDLE_SCHED = 32768U; + const uint16_t GM_PARAM_VOLT_CC_GATEWAY = 16384U; static const LongitudinalLimits GM_ASCM_LONG_LIMITS = { .max_gas = 8191, @@ -706,6 +707,11 @@ static safety_config gm_init(uint16_t param) { {0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}, {0x184, 2, 8, .check_relay = false}, {0x1E1, 2, 7, .check_relay = false}}; // camera bus + static const CanMsg GM_CC_LONG_ASCM_TX_MSGS[] = {{0x180, 0, 4, .check_relay = true}, {0x409, 0, 7, .check_relay = false}, + {0x40A, 0, 7, .check_relay = false}, {0x370, 0, 6, .check_relay = false}, + {0x1E1, 0, 7, .check_relay = false}, {0x3D1, 0, 8, .check_relay = false}, + {0xBD, 0, 7, .check_relay = false}, {0x1F5, 0, 8, .check_relay = false}}; + gm_hw = GET_FLAG(param, GM_PARAM_HW_CAM) ? GM_CAM : GM_ASCM; gm_sdgm = GET_FLAG(param, GM_PARAM_HW_SDGM); gm_ascm_int = GET_FLAG(param, GM_PARAM_HW_ASCM_INT); @@ -714,6 +720,7 @@ static safety_config gm_init(uint16_t param) { gm_cc_long = GET_FLAG(param, GM_PARAM_CC_LONG); gm_has_acc = !GET_FLAG(param, GM_PARAM_NO_ACC); gm_pedal_long = GET_FLAG(param, GM_PARAM_PEDAL_LONG); + const bool gm_volt_cc_gateway = GET_FLAG(param, GM_PARAM_VOLT_CC_GATEWAY) && gm_no_camera && !gm_pedal_long && !gm_has_acc; enable_gas_interceptor = GET_FLAG(param, GM_PARAM_PEDAL_INTERCEPTOR); gm_force_ascm = GET_FLAG(param, GM_PARAM_HW_ASCM_LONG); gm_force_brake_c9 = GET_FLAG(param, GM_PARAM_FORCE_BRAKE_C9); @@ -781,6 +788,8 @@ static safety_config gm_init(uint16_t param) { } else { ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_SDGM_TX_MSGS); } + } else if (gm_cc_long && gm_volt_cc_gateway && (gm_hw == GM_ASCM) && !gm_sdgm) { + ret = BUILD_SAFETY_CFG(gm_rx_checks, GM_CC_LONG_ASCM_TX_MSGS); } else if ((gm_hw == GM_CAM) || gm_sdgm) { // FIXME: cppcheck thinks that gm_cam_long is always false. This is not true // if ALLOW_DEBUG is defined but cppcheck is run without ALLOW_DEBUG diff --git a/opendbc_repo/opendbc/safety/tests/test_gm.py b/opendbc_repo/opendbc/safety/tests/test_gm.py index 73fb608cd..286c441c9 100755 --- a/opendbc_repo/opendbc/safety/tests/test_gm.py +++ b/opendbc_repo/opendbc/safety/tests/test_gm.py @@ -59,6 +59,28 @@ def test_gm_panda_scheduler_paths(): safety.safety_rx_hook(prndl) +def test_gm_cc_longitudinal_ascm_safety_whitelist(): + safety = libsafety_py.libsafety + flags = (GMSafetyFlags.FLAG_GM_NO_CAMERA | GMSafetyFlags.FLAG_GM_NO_ACC | + GMSafetyFlags.FLAG_GM_CC_LONG | GMSafetyFlags.FLAG_GM_VOLT_CC_GATEWAY) + assert safety.set_safety_hooks(CarParams.SafetyModel.gm, flags) == 0 + safety.init_tests() + safety.set_controls_allowed(True) + safety.set_cruise_engaged_prev(True) + + allowed = ((0x180, 0, 4), (0x409, 0, 7), (0x40A, 0, 7), (0x370, 0, 6), + (0x1E1, 0, 7), (0x3D1, 0, 8), (0xBD, 0, 7), (0x1F5, 0, 8)) + for addr, bus, length in allowed: + data = b'\x00' * length + if addr == 0x1E1: + data = data[:5] + b'\x10' + data[6:] + assert safety.safety_tx_hook(common.make_msg(bus, addr, length, data)) + + blocked = ((0x184, 2, 8), (0x200, 0, 6), (0x2CB, 0, 8), (0x306, 1, 8)) + for addr, bus, length in blocked: + assert not safety.safety_tx_hook(common.make_msg(bus, addr, length)) + + def test_gm_bolt_acc_pedal_clears_stock_cruise(): safety = libsafety_py.libsafety flags = GMSafetyFlags.HW_CAM | GMSafetyFlags.FLAG_GM_GAS_INTERCEPTOR | GMSafetyFlags.FLAG_GM_BOLT_2022_PEDAL diff --git a/selfdrive/controls/tests/test_starpilot_vcruise.py b/selfdrive/controls/tests/test_starpilot_vcruise.py index dd0248bd1..b97571850 100644 --- a/selfdrive/controls/tests/test_starpilot_vcruise.py +++ b/selfdrive/controls/tests/test_starpilot_vcruise.py @@ -260,6 +260,23 @@ def test_curve_speed_controller_learns_when_speed_is_manually_controlled(long_ac assert not vcruise.csc_controlling_speed +def test_curve_speed_controller_learns_when_longitudinal_override_event_is_active(): + planner, vcruise = make_vcruise(road_curvature=0.02) + sm = make_sm(standstill=False) + sm["onroadEvents"] = [SimpleNamespace(overrideLongitudinal=True)] + toggles = make_toggles() + toggles.curve_speed_controller = True + planner.driving_in_curve = True + planner.road_curvature_detected = True + planner.lateral_acceleration = 2.4 + vcruise.csc.training_timer = PLANNER_TIME + + update_vcruise(vcruise, sm, toggles, now=50.0, v_ego=20.0) + + assert vcruise.csc.enable_training + assert vcruise.csc.curvature_data["0.02"]["count"] == 1 + + def test_curve_speed_controller_persists_data_after_leaving_curve(): planner, vcruise = make_vcruise(road_curvature=0.02) sm = make_sm(standstill=False) diff --git a/selfdrive/ui/layouts/settings/starpilot/longitudinal.py b/selfdrive/ui/layouts/settings/starpilot/longitudinal.py index a3c2ee7c0..46d7d69c8 100644 --- a/selfdrive/ui/layouts/settings/starpilot/longitudinal.py +++ b/selfdrive/ui/layouts/settings/starpilot/longitudinal.py @@ -686,12 +686,12 @@ class StarPilotLongitudinalLayout(_SettingsPage): self._curve_speed_controller_rows = [ SettingRow("CalibratedLatAccel", "value", tr_noop("Calibrated Lateral Accel"), subtitle=tr_noop("The learned lateral acceleration from collected driving data. Higher values allow faster cornering."), - get_value=lambda: f"{self._params_memory.get_float('CalibratedLateralAcceleration'):.2f} m/s", + get_value=lambda: f"{self._params.get_float('CalibratedLateralAcceleration'):.2f} m/s", on_click=None, visible=csc_on), SettingRow("CalibrationProgress", "value", tr_noop("Calibration Progress"), subtitle=tr_noop("How much curve data has been collected. Normal for the value to stay low."), - get_value=lambda: f"{self._params_memory.get_float('CalibrationProgress'):.2f}%", + get_value=lambda: f"{self._params.get_float('CalibrationProgress'):.2f}%", on_click=None, visible=csc_on), SettingRow("ResetCurve", "action", tr_noop("Reset Curve Data"), diff --git a/selfdrive/ui/onroad/starpilot/starpilot_border.py b/selfdrive/ui/onroad/starpilot/starpilot_border.py index 495a8d5e1..1cae802e8 100644 --- a/selfdrive/ui/onroad/starpilot/starpilot_border.py +++ b/selfdrive/ui/onroad/starpilot/starpilot_border.py @@ -39,6 +39,7 @@ _GREEN = rl.Color(34, 197, 94, 255) _AMBER = rl.Color(251, 191, 36, 255) _ORANGE = rl.Color(234, 88, 12, 255) _RED = rl.Color(201, 34, 49, 255) +_TRAINING = rl.Color(112, 192, 216, 255) _last_was_active = False _activation_start = 0.0 @@ -57,7 +58,7 @@ def _csc_state(): plan = sm["starpilotPlan"] params = ui_state.ui_params - if plan.speedLimitChanged or not params.get_bool("ShowCSCStatus"): + if not params.get_bool("ShowCSCStatus"): return None car_state = sm["carState"] @@ -113,7 +114,8 @@ def _render_csc_glow(border_rect: rl.Rectangle, border_width: float = UI_BORDER_ state = _csc_state() now = rl.get_time() - if state is None or not state['active']: + visible = state is not None and (state['active'] or state['training']) + if not visible: if _last_was_active: _fade_out_start = now _last_was_active = False @@ -131,9 +133,9 @@ def _render_csc_glow(border_rect: rl.Rectangle, border_width: float = UI_BORDER_ _fade_out_start = 0.0 _last_was_active = True - intensity = _intensity(state['curvature']) + intensity = _intensity(state['curvature']) if state['active'] else _GLOW_BASE_INTENSITY period = _glow_period(intensity) - color = _glow_color(intensity) + color = _glow_color(intensity) if state['active'] else _TRAINING t_norm = max(0.0, min(1.0, (intensity - _GLOW_BASE_INTENSITY) / (1.0 - _GLOW_BASE_INTENSITY))) _last_state = (intensity, period, color, t_norm) fade = min(1.0, (now - _activation_start) / _GLOW_FADE_IN_DURATION) diff --git a/starpilot/common/assets/device_settings_layout.json b/starpilot/common/assets/device_settings_layout.json index 61d28f3ec..8aa5cd07d 100644 --- a/starpilot/common/assets/device_settings_layout.json +++ b/starpilot/common/assets/device_settings_layout.json @@ -1043,6 +1043,30 @@ "parent_key": "CurveSpeedController", "settings_tier": "simple" }, + { + "key": "CalibratedLateralAcceleration", + "label": "Calibrated Lateral Accel", + "description": "The learned lateral acceleration from collected driving data. Higher values allow faster cornering.", + "picker_description": "Learned cornering comfort from your driving data.", + "data_type": "float", + "ui_type": "readout", + "precision": 2, + "unit": " m/s²", + "parent_key": "CurveSpeedController", + "settings_tier": "simple" + }, + { + "key": "CalibrationProgress", + "label": "Calibration Progress", + "description": "How much curve data has been collected. Normal for the value to stay low.", + "picker_description": "How much curve data has been collected.", + "data_type": "float", + "ui_type": "readout", + "precision": 2, + "unit": "%", + "parent_key": "CurveSpeedController", + "settings_tier": "simple" + }, { "key": "ResetCurveData", "label": "Reset Curve Data", diff --git a/starpilot/controls/lib/curve_speed_controller.py b/starpilot/controls/lib/curve_speed_controller.py index 4d4bf4480..ad8bb2e1a 100644 --- a/starpilot/controls/lib/curve_speed_controller.py +++ b/starpilot/controls/lib/curve_speed_controller.py @@ -54,7 +54,7 @@ class CurveSpeedController: self.required_curvatures = [str(round(road_curvature, ROUNDING_PRECISION)) for road_curvature in np.arange(MIN_CURVATURE, MAX_CURVATURE + STEP, STEP)] self.update_lateral_acceleration() - self._publish_calibration_progress() + self._publish_calibration_progress(persist=True) @staticmethod def _bucket_curvature(road_curvature): @@ -117,8 +117,11 @@ class CurveSpeedController: progress += min(self.curvature_data[key]["count"] / CALIBRATION_PROGRESS_THRESHOLD, 1.0) return (progress / len(self.required_curvatures)) * 100 - def _publish_calibration_progress(self): - self._put_memory_param("CalibrationProgress", self._calibration_progress()) + def _publish_calibration_progress(self, persist=False): + progress = self._calibration_progress() + if persist: + self.starpilot_planner.params.put_nonblocking("CalibrationProgress", progress) + self._put_memory_param("CalibrationProgress", progress) def _put_memory_param(self, key, value): params_memory = getattr(self.starpilot_planner, "params_memory", None) diff --git a/starpilot/system/the_galaxy/assets/components/tools/device_settings.css b/starpilot/system/the_galaxy/assets/components/tools/device_settings.css index e6a951b2f..074600d98 100644 --- a/starpilot/system/the_galaxy/assets/components/tools/device_settings.css +++ b/starpilot/system/the_galaxy/assets/components/tools/device_settings.css @@ -384,6 +384,13 @@ padding: 0.2rem 0.6rem; } +.ds-row-readout { + background-color: transparent; + border: none; + color: var(--text-muted); + font-style: italic; +} + .ds-stepper-container { width: 100%; } diff --git a/starpilot/system/the_galaxy/assets/components/tools/device_settings.js b/starpilot/system/the_galaxy/assets/components/tools/device_settings.js index 63752f24f..771cf75e7 100644 --- a/starpilot/system/the_galaxy/assets/components/tools/device_settings.js +++ b/starpilot/system/the_galaxy/assets/components/tools/device_settings.js @@ -53,6 +53,8 @@ let flmWorkspaceInflight = null let lastFlmWorkspaceFetch = 0 let favoritePollInflight = null let favoritePollTimer = null +let cscCalibrationPollInflight = null +let cscCalibrationPollTimer = null const DYNAMIC_DEFAULT_DEP_KEYS = new Set(["AccelerationProfile", "EVTuning", "TruckTuning"]) const PANDA_FIRMWARE_TOGGLE_KEYS = new Set(["IgnoreIgnitionLine", "RemoteStartBootsComma", "HKGRemoteStartBootsComma"]) const FLM_ADVANCED_LATERAL_KEYS = new Set([ @@ -479,6 +481,16 @@ function formatSliderValue(val, stepStr, precisionInt, key) { return Number(v.toFixed(dec)).toString() } +function formatReadoutValue(p) { + const raw = state.values[p.key] + const value = parseFloat(raw) + if (raw === undefined || raw === null || Number.isNaN(value)) return "--" + + const precision = p.precision !== undefined && p.precision !== null ? Number(p.precision) : 2 + const formatted = Number(value.toFixed(Math.max(0, precision))).toString() + return p.unit ? `${formatted}${p.unit}` : formatted +} + function formatNumericForInput(value, precision) { const n = Number(value) if (!Number.isFinite(n)) return "" @@ -674,6 +686,51 @@ function ensureFavoriteValuePolling() { }, 1000) } +async function refreshCscCalibrationValues() { + if (cscCalibrationPollInflight || state.loadingValues) return cscCalibrationPollInflight + + cscCalibrationPollInflight = Promise.all( + ["CalibratedLateralAcceleration", "CalibrationProgress"].map(async key => { + const response = await fetch(`/api/params_memory?key=${encodeURIComponent(key)}`, { cache: "no-store" }) + if (!response.ok) return [key, null] + const raw = (await response.text()).trim() + const value = Number(raw) + return [key, Number.isFinite(value) && raw !== "" ? value : null] + }), + ).then(entries => { + const nextValues = { ...state.values } + let changed = false + for (const [key, value] of entries) { + if (value === null || nextValues[key] === value) continue + nextValues[key] = value + changed = true + } + if (changed) { + state.values = nextValues + scheduleSyncInputs() + } + }).catch(() => {}).finally(() => { + cscCalibrationPollInflight = null + }) + + return cscCalibrationPollInflight +} + +function ensureCscCalibrationPolling() { + if (cscCalibrationPollTimer !== null) return + + cscCalibrationPollTimer = setInterval(() => { + if (!window.location.pathname.startsWith("/device_settings")) { + clearInterval(cscCalibrationPollTimer) + cscCalibrationPollTimer = null + return + } + if (document.visibilityState === "visible") { + refreshCscCalibrationValues() + } + }, 1000) +} + async function saveFavoriteSlots(slots) { if (state.favoriteSaving) return @@ -1493,6 +1550,7 @@ function renderSettingRow(p) { const isText = p.ui_type === "text" const isColor = p.ui_type === "color" const isAction = p.ui_type === "action" + const isReadout = p.ui_type === "readout" const isGroup = isGroupParam(p) const isChild = p.parent_key ? "ds-child-modifier" : "" const lockReason = () => getSettingLockReason(p) @@ -1643,7 +1701,7 @@ function renderSettingRow(p) { @click="${() => resetColorParam(p)}">Stock ` - } else if (!isGroup) { + } else if (!isGroup && !isReadout) { if (p.key === "IsRHD") { rowControl = html`
@@ -1713,8 +1771,9 @@ function renderSettingRow(p) {
` : ""} - ${(isNumeric || isColor) ? html`${() => { + ${(isNumeric || isColor || isReadout) ? html`${() => { if (isColor) return formatColorDisplayValue(p) + if (isReadout) return formatReadoutValue(p) const currentValue = state.sliderPreviewValues[p.key] ?? state.values[p.key] const bounds = numericBounds(p) return currentValue !== undefined ? formatSliderValue(currentValue, String(bounds.step), p.precision, p.key) : ".." @@ -1769,6 +1828,7 @@ export function DeviceSettings({ params }) { fetchFlmWorkspace() ensureFavoriteValuePolling() + ensureCscCalibrationPolling() if (!state.fetched) { state.fetched = true diff --git a/starpilot/system/the_galaxy/tests/test_device_settings_layout.py b/starpilot/system/the_galaxy/tests/test_device_settings_layout.py index e26458b5a..c90cfaebf 100644 --- a/starpilot/system/the_galaxy/tests/test_device_settings_layout.py +++ b/starpilot/system/the_galaxy/tests/test_device_settings_layout.py @@ -135,6 +135,17 @@ def test_curve_speed_controller_no_lead_toggle_is_nested_under_csc(): assert _declared_default("CurveSpeedControllerNoLead") == "0" +def test_curve_speed_controller_readouts_are_display_only_and_nested(): + csc = _params_by_section(_layout())["Longitudinal (Speed & Following)"] + + for key, unit in (("CalibratedLateralAcceleration", " m/s²"), ("CalibrationProgress", "%")): + readout = csc[key] + assert readout["ui_type"] == "readout" + assert readout["parent_key"] == "CurveSpeedController" + assert readout["unit"] == unit + assert readout["settings_tier"] == "simple" + + def test_every_galaxy_setting_has_a_shared_settings_tier(): layout = _layout() tiers = { diff --git a/starpilot/system/the_galaxy/tests/test_navigation_params.py b/starpilot/system/the_galaxy/tests/test_navigation_params.py index 072d4c5ed..519348c29 100644 --- a/starpilot/system/the_galaxy/tests/test_navigation_params.py +++ b/starpilot/system/the_galaxy/tests/test_navigation_params.py @@ -256,6 +256,27 @@ def test_device_settings_layout_asset_is_served_from_common_catalog(monkeypatch) assert response.get_json() == the_galaxy.load_settings_catalog() +def test_params_all_exposes_curve_calibration_readouts(monkeypatch): + client, _ = _params_client(monkeypatch, { + "CalibratedLateralAcceleration": 2.73, + "CalibrationProgress": 48.0, + }, "tici") + monkeypatch.setattr( + the_galaxy, + "_params_live_raw", + WritableFakeParams({ + "CalibratedLateralAcceleration": 2.73, + "CalibrationProgress": 48.0, + }), + ) + + response = client.get("/api/params/all") + + assert response.status_code == 200 + assert response.get_json()["CalibratedLateralAcceleration"] == 2.73 + assert response.get_json()["CalibrationProgress"] == 48.0 + + def test_ford_lateral_mode_is_editable_through_galaxy(monkeypatch): client, fake_params = _params_client(monkeypatch, { "CarMake": "Ford", @@ -391,6 +412,12 @@ def test_curve_speed_controller_reset_clears_learned_data_offroad(monkeypatch): "CalibrationProgress": 48.0, "CurvatureData": {"0.01": {"average": 2.73, "count": 12}}, }, "tici") + fake_memory = WritableFakeParams({ + "CalibratedLateralAcceleration": 2.73, + "CalibrationProgress": 48.0, + "CurvatureData": {"0.01": {"average": 2.73, "count": 12}}, + }) + monkeypatch.setattr(the_galaxy, "params_memory", fake_memory) response = client.post("/api/curve_speed_controller/reset") @@ -403,6 +430,11 @@ def test_curve_speed_controller_reset_clears_learned_data_offroad(monkeypatch): assert "CalibrationProgress" not in fake_params.values assert "CurvatureData" not in fake_params.values assert fake_params.removals == ["CalibrationProgress", "CurvatureData"] + assert fake_memory.values == { + "CalibratedLateralAcceleration": 2.0, + "CalibrationProgress": 0.0, + } + assert fake_memory.removals == ["CurvatureData"] def test_curve_speed_controller_reset_rejected_onroad(monkeypatch): diff --git a/starpilot/system/the_galaxy/the_galaxy.py b/starpilot/system/the_galaxy/the_galaxy.py index 276acc71d..b895a5c0a 100644 --- a/starpilot/system/the_galaxy/the_galaxy.py +++ b/starpilot/system/the_galaxy/the_galaxy.py @@ -5456,6 +5456,9 @@ def setup(app): params.put("CalibratedLateralAcceleration", 2.0) params.remove("CalibrationProgress") params.remove("CurvatureData") + params_memory.put("CalibratedLateralAcceleration", 2.0) + params_memory.put("CalibrationProgress", 0.0) + params_memory.remove("CurvatureData") return jsonify({ "message": "Curve Speed Controller data reset. Training will restart on the next drive.", @@ -5484,6 +5487,12 @@ def setup(app): result["AlphaLongitudinalAvailable"] = _get_alpha_longitudinal_available() result["HasRivianAngleHarness"] = _get_has_rivian_angle_harness() + for key in ("CalibratedLateralAcceleration", "CalibrationProgress"): + try: + result[key] = _get_current_param_value(key, float, defaults_lookup) + except Exception: + result[key] = None + return jsonify(_sanitize_json_value(result)), 200 @app.route("/api/params/defaults", methods=["GET"])