Merge branch 'master' of https://github.com/sunnypilot/opendbc into sync

This commit is contained in:
infiniteCable2
2026-02-04 19:22:19 +01:00
71 changed files with 1823 additions and 366 deletions

36
.github/ISSUE_TEMPLATE/misc.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
name: Miscellaneous
description: For tools or test bugs, or enhancements
labels: ["bug"]
body:
- type: markdown
attributes:
value: >
* If the issue likely only affects your car model or make, go back and open a **car bug report** instead.
Before creating a **miscellaneous bug report**, please check the following:
* Ensure there isn't an existing issue for your bug. If there is, leave a comment on the existing issue.
- type: textarea
attributes:
label: Describe the bug or enhancement
description: Also include a description of how to reproduce the bug or how the enhancement would work
validations:
required: true
- type: input
id: route
attributes:
label: Provide a route where the issue occurs if applicable
description: Ensure the route is fully uploaded at https://useradmin.comma.ai
placeholder: 77611a1fac303767|2020-05-11--16-37-07
- type: input
id: version
attributes:
label: openpilot version if applicable
description: If you're not on release, provide the commit hash
placeholder: 0.8.10
- type: textarea
attributes:
label: Additional info

45
.github/workflows/car_diff.yml vendored Normal file
View File

@@ -0,0 +1,45 @@
name: car diff
on:
pull_request_target:
types: [opened, synchronize, reopened]
jobs:
comment:
name: comment
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
pull-requests: write
actions: read
steps:
- name: Wait for car diff
id: wait
continue-on-error: true
uses: lewagon/wait-on-check-action@v1.3.4
with:
ref: ${{ github.event.pull_request.head.sha }}
check-name: car diff
repo-token: ${{ secrets.GITHUB_TOKEN }}
allowed-conclusions: success
wait-interval: 20
- name: Download car diff
if: steps.wait.outcome == 'success'
uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
workflow: tests.yml
workflow_conclusion: ''
pr: ${{ github.event.number }}
name: car_diff_${{ github.event.number }}
path: .
allow_forks: true
- name: Comment car diff on PR
if: steps.wait.outcome == 'success'
uses: thollander/actions-comment-pull-request@v2
with:
filePath: diff.txt
comment_tag: car_diff
pr_number: ${{ github.event.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -62,6 +62,7 @@ jobs:
car_diff:
name: car diff
if: false
runs-on: ${{ github.repository == 'commaai/opendbc' && 'namespace-profile-amd64-8x16' || 'ubuntu-latest' }}
env:
GIT_REF: ${{ github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && github.event.before || format('origin/{0}', github.event.repository.default_branch) }}
@@ -77,11 +78,12 @@ jobs:
- name: Test car diff
if: github.event_name == 'pull_request'
run: source setup.sh && python opendbc/car/tests/car_diff.py | tee diff.txt
- name: Comment PR
- name: Upload diff
if: always() && github.event_name == 'pull_request' && github.repository == 'commaai/opendbc'
env:
GH_TOKEN: ${{ github.token }}
run: '[ -s diff.txt ] && gh pr comment ${{ github.event.pull_request.number }} --repo ${{ github.repository }} -F diff.txt || true'
uses: actions/upload-artifact@v4
with:
name: car_diff_${{ github.event.number }}
path: diff.txt
- name: Update refs
if: github.repository == 'commaai/opendbc' && github.ref == 'refs/heads/master'
run: source setup.sh && python opendbc/car/tests/car_diff.py --update-refs

View File

@@ -16,7 +16,7 @@ jobs:
run: |
pip install -e .
scons -c && scons -j$(nproc)
python -m pip install jinja2==3.1.4 natsort==8.4.0
python -m pip install jinja2==3.1.4
python opendbc/car/docs.py
- uses: stefanzweifel/git-auto-commit-action@8621497c8c39c72f3e2a999a26b4ca1b5058a842
with:

30
.github/workflows/update-uv-lock.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
name: Update uv.lock
on:
schedule:
- cron: "0 14 * * 1" # every Monday at 2am UTC (6am PST)
workflow_dispatch:
jobs:
update-uv-lock:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- name: Update uv.lock
run: uv lock --upgrade
- name: Create Pull Request
uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83
with:
author: Vehicle Researcher <user@comma.ai>
token: ${{ secrets.ACTIONS_CREATE_PR_PAT }}
commit-message: "[bot] Update uv.lock"
title: "[bot] Update uv.lock"
body: "Weekly update of uv.lock dependencies"
branch: "update-uv-lock"
base: "master"
delete-branch: true
labels: bot

1
.gitignore vendored
View File

@@ -17,7 +17,6 @@
*.gcno
*.dump
*.gcov
uv.lock
/dist/
.vscode/
__pycache__/

View File

@@ -1,16 +1,18 @@
<!--- AUTOGENERATED FROM selfdrive/car/CARS_template.md, DO NOT EDIT. --->
# Support Information for 384 Known Cars
# Support Information for 410 Known Cars
|Make|Model|Package|Support Level|
|---|---|---|:---:|
|Acura|ADX 2025-26|All|[Community](#community)|
|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|MDX 2015-16|Advance Package|[Community](#community)|
|Acura|MDX 2017-20|All|[Community](#community)|
|Acura|MDX 2022-24|All|[Community](#community)|
|Acura|MDX 2025|All except Type S|[Upstream](#upstream)|
|Acura|MDX 2025-26|All except Type S|[Upstream](#upstream)|
|Acura|MDX Hybrid 2017-20|All|[Community](#community)|
|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)|
@@ -19,6 +21,7 @@
|Acura|TLX 2018-20|All|[Community](#community)|
|Acura|TLX 2021|All|[Upstream](#upstream)|
|Acura|TLX 2022-23|All|[Community](#community)|
|Acura|TLX 2025|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 +32,21 @@
|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)|
|Cadillac|CT6 Non-ACC 2017-18|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Cadillac|XT5 Non-ACC 2018|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim, without Super Cruise Package|[Upstream](#upstream)|
|Chevrolet|Bolt EUV LT Non-ACC 2022-23|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|[Upstream](#upstream)|
|Chevrolet|Bolt EV LT Non-ACC 2022-23|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Chevrolet|Bolt EV Non-ACC 2017|No Adaptive Cruise Control (Non-ACC)|[Community](community)|
|Chevrolet|Bolt EV Non-ACC 2018-21|No Adaptive Cruise Control (Non-ACC)|[Community](community)|
|Chevrolet|Equinox 2019-22|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Equinox Non-ACC 2019-22|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Chevrolet|Malibu Non-ACC 2016-23|No Adaptive Cruise Control (Non-ACC)|[Community](community)|
|Chevrolet|Silverado 1500 2020-21|Safety Package II|[Upstream](#upstream)|
|Chevrolet|Suburban Non-ACC 2016-20|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|Chevrolet|Trailblazer 2021-22|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Chevrolet|Trailblazer Non-ACC 2021-22|No Adaptive Cruise Control (Non-ACC)|[Dashcam mode](#dashcam)|
|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)|
@@ -70,6 +83,7 @@
|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)|[Dashcam mode](#dashcam)|
|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)|
@@ -86,6 +100,7 @@
|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|[Community](#community)|
|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,12 +113,13 @@
|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|Honda Sensing|[Community](community)|
|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)|
|Honda|CR-V 2023-26|All|[Upstream](#upstream)|
|Honda|CR-V Hybrid 2017-22|Honda Sensing|[Upstream](#upstream)|
|Honda|CR-V Hybrid 2023-25|All|[Upstream](#upstream)|
|Honda|CR-V Hybrid 2023-26|All|[Upstream](#upstream)|
|Honda|e 2020|All|[Upstream](#upstream)|
|Honda|Fit 2018-20|Honda Sensing|[Upstream](#upstream)|
|Honda|Freed 2020|Honda Sensing|[Upstream](#upstream)|
@@ -114,6 +130,7 @@
|Honda|N-Box 2018|All|[Upstream](#upstream)|
|Honda|Odyssey 2018-20|Honda Sensing|[Upstream](#upstream)|
|Honda|Odyssey 2021-26|All|[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)|
@@ -123,12 +140,14 @@
|Hyundai|Azera 2022|All|[Upstream](#upstream)|
|Hyundai|Azera Hybrid 2019|All|[Upstream](#upstream)|
|Hyundai|Azera Hybrid 2020|All|[Upstream](#upstream)|
|Hyundai|Bayon Non-SCC 2021|No Smart Cruise Control (Non-SCC)|[Dashcam mode](#dashcam)|
|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 GT 2017-20|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|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|Ioniq 5 (Southeast Asia and Europe only) 2022-24|All|[Upstream](#upstream)|
@@ -142,14 +161,16 @@
|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|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 2022-23|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 Non-SCC 2019|No Smart Cruise Control (Non-SCC)|[Community](community)|
|Hyundai|Kona Hybrid 2020|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 2023-24|Highway Driving Assist II|[Community](#community)|
|Hyundai|Santa Cruz 2022-24|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Hyundai|Santa Fe 2019-20|All|[Upstream](#upstream)|
|Hyundai|Santa Fe 2021-23|All|[Upstream](#upstream)|
@@ -171,11 +192,14 @@
|Kia|Carnival 2022-24|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|Kia|Carnival (China only) 2023|Smart Cruise Control (SCC)|[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 II) 2022-24|Highway Driving Assist II|[Upstream](#upstream)|
|Kia|EV6 (without HDA II) 2022-24|Highway Driving Assist|[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)|[Dashcam mode](#dashcam)|
|Kia|K5 2021-24|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)|
@@ -198,6 +222,7 @@
|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)|[Dashcam mode](#dashcam)|
|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)|
@@ -208,7 +233,7 @@
|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 2023-24|Highway Driving Assist II|[Community](#community)|
|Lexus|CT Hybrid 2017-18|Lexus Safety System+|[Upstream](#upstream)|
|Lexus|ES 2017-18|All|[Upstream](#upstream)|
|Lexus|ES 2019-25|All|[Upstream](#upstream)|
@@ -218,7 +243,8 @@
|Lexus|IS 2017-19|All|[Upstream](#upstream)|
|Lexus|IS 2022-24|All|[Upstream](#upstream)|
|Lexus|LC 2024-25|All|[Upstream](#upstream)|
|Lexus|NS 2022-25|Any|[Not compatible](#can-bus-security)|
|Lexus|LS 2018|All except Lexus Safety System+ A|[Upstream](#upstream)|
|Lexus|NS 2022-25|All|[Not compatible](#can-bus-security)|
|Lexus|NX 2018-19|All|[Upstream](#upstream)|
|Lexus|NX 2020-21|All|[Upstream](#upstream)|
|Lexus|NX Hybrid 2018-19|All|[Upstream](#upstream)|
@@ -249,8 +275,8 @@
|Peugeot|208 2019-25|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|Porsche|Macan 2017-24|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
|Ram|1500 2019-24|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Ram|2500 2020-24|Adaptive Cruise Control (ACC)|[Dashcam mode](#dashcam)|
|Ram|3500 2019-22|Adaptive Cruise Control (ACC)|[Dashcam mode](#dashcam)|
|Ram|2500 2020-24|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Ram|3500 2019-22|Adaptive Cruise Control (ACC)|[Upstream](#upstream)|
|Rivian|R1S 2022-24|All|[Upstream](#upstream)|
|Rivian|R1T 2022-24|All|[Upstream](#upstream)|
|SEAT|Alhambra 2018-20|Adaptive Cruise Control (ACC) & Lane Assist|[Dashcam mode](#dashcam)|
@@ -261,19 +287,19 @@
|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Crosstrek Hybrid 2020|EyeSight Driver Assistance|[Dashcam mode](#dashcam)|
|Subaru|Forester 2017-18|EyeSight Driver Assistance|[Dashcam mode](#dashcam)|
|Subaru|Forester 2017-18|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Forester 2019-21|All|[Upstream](#upstream)|
|Subaru|Forester 2022-24|All|[Dashcam mode](#dashcam)|
|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 2015-18|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Legacy 2020-22|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 2015-17|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Outback 2018-19|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|Outback 2020-22|All|[Upstream](#upstream)|
|Subaru|Outback 2023|All|[Dashcam mode](#dashcam)|
|Subaru|Solterra 2023-25|Any|[Not compatible](#can-bus-security)|
|Subaru|Solterra 2023-25|All|[Not compatible](#can-bus-security)|
|Subaru|XV 2018-19|EyeSight Driver Assistance|[Upstream](#upstream)|
|Subaru|XV 2020-21|EyeSight Driver Assistance|[Upstream](#upstream)|
|Škoda|Fabia 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
@@ -298,19 +324,19 @@
|Toyota|Avalon 2022|All|[Upstream](#upstream)|
|Toyota|Avalon Hybrid 2019-21|All|[Upstream](#upstream)|
|Toyota|Avalon Hybrid 2022|All|[Upstream](#upstream)|
|Toyota|bZ4x 2023-25|Any|[Not compatible](#can-bus-security)|
|Toyota|bZ4x 2023-25|All|[Not compatible](#can-bus-security)|
|Toyota|C-HR 2017-20|All|[Upstream](#upstream)|
|Toyota|C-HR 2021|All|[Upstream](#upstream)|
|Toyota|C-HR Hybrid 2017-20|All|[Upstream](#upstream)|
|Toyota|C-HR Hybrid 2021-22|All|[Upstream](#upstream)|
|Toyota|Camry 2018-20|All|[Upstream](#upstream)|
|Toyota|Camry 2021-24|All|[Upstream](#upstream)|
|Toyota|Camry 2025|Any|[Not compatible](#can-bus-security)|
|Toyota|Camry 2025|All|[Not compatible](#can-bus-security)|
|Toyota|Camry Hybrid 2018-20|All|[Upstream](#upstream)|
|Toyota|Camry Hybrid 2021-24|All|[Upstream](#upstream)|
|Toyota|Corolla 2017-19|All|[Upstream](#upstream)|
|Toyota|Corolla 2020-22|All|[Upstream](#upstream)|
|Toyota|Corolla Cross 2022-25|Any|[Not compatible](#can-bus-security)|
|Toyota|Corolla Cross 2022-25|All|[Not compatible](#can-bus-security)|
|Toyota|Corolla Cross (Non-US only) 2020-23|All|[Upstream](#upstream)|
|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|[Upstream](#upstream)|
|Toyota|Corolla Hatchback 2019-22|All|[Upstream](#upstream)|
@@ -318,7 +344,7 @@
|Toyota|Corolla Hybrid (South America only) 2020-23|All|[Upstream](#upstream)|
|Toyota|Highlander 2017-19|All|[Upstream](#upstream)|
|Toyota|Highlander 2020-23|All|[Upstream](#upstream)|
|Toyota|Highlander 2025|Any|[Not compatible](#can-bus-security)|
|Toyota|Highlander 2025|All|[Not compatible](#can-bus-security)|
|Toyota|Highlander Hybrid 2017-19|All|[Upstream](#upstream)|
|Toyota|Highlander Hybrid 2020-23|All|[Upstream](#upstream)|
|Toyota|Mirai 2021|All|[Upstream](#upstream)|
@@ -339,13 +365,13 @@
|Toyota|RAV4 Hybrid 2022|All|[Upstream](#upstream)|
|Toyota|RAV4 Hybrid 2023-25|All|[Upstream](#upstream)|
|Toyota|RAV4 Prime 2021-23|All|[Custom](#secoc-cars-with-recoverable-keys)|
|Toyota|RAV4 Prime 2024-25|Any|[Not compatible](#can-bus-security)|
|Toyota|Sequoia 2023-25|Any|[Not compatible](#can-bus-security)|
|Toyota|RAV4 Prime 2024-25|All|[Not compatible](#can-bus-security)|
|Toyota|Sequoia 2023-25|All|[Not compatible](#can-bus-security)|
|Toyota|Sienna 2018-20|All|[Upstream](#upstream)|
|Toyota|Sienna 2021-23|All|[Custom](#secoc-cars-with-recoverable-keys)|
|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)|
|Toyota|Sienna 2024-25|All|[Not compatible](#can-bus-security)|
|Toyota|Tundra 2022-25|All|[Not compatible](#can-bus-security)|
|Toyota|Venza 2021-25|All|[Not compatible](#can-bus-security)|
|Toyota|Yaris (Non-US only) 2020, 2023|All|[Custom](#secoc-cars-with-recoverable-keys)|
|Volkswagen|Arteon 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|
|Volkswagen|Arteon eHybrid 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|[Upstream](#upstream)|

View File

@@ -130,17 +130,15 @@ class CanSignalRateCalculator:
Calculates the instantaneous rate of a CAN signal by using the counter
variable and the known frequency of the CAN message that contains it.
"""
def __init__(self, frequency):
def __init__(self, frequency: int):
self.frequency = frequency
self.previous_counter = 0
self.previous_value = 0
self.rate = 0
def update(self, current_value, current_counter):
if current_counter != self.previous_counter:
def update(self, current_value: float, updated: bool):
if updated:
self.rate = (current_value - self.previous_value) * self.frequency
self.previous_counter = current_counter
self.previous_value = current_value
return self.rate

View File

@@ -1,3 +1,6 @@
from opendbc.car.crc import CRC8BODY
def create_control(packer, torque_l, torque_r):
values = {
"TORQUE_L": torque_l,
@@ -9,12 +12,6 @@ def create_control(packer, torque_l, torque_r):
def body_checksum(address: int, sig, d: bytearray) -> int:
crc = 0xFF
poly = 0xD5
for i in range(len(d) - 2, -1, -1):
crc ^= d[i]
for _ in range(8):
if crc & 0x80:
crc = ((crc << 1) ^ poly) & 0xFF
else:
crc = (crc << 1) & 0xFF
crc = CRC8BODY[crc ^ d[i]]
return crc

View File

@@ -217,7 +217,7 @@ class CcpClient:
resp = self._recv_dto(0.025)
# mta_addr_ext = resp[0]
mta_addr = struct.unpack(f"{self.byte_order.value}I", resp[1:5])[0]
return mta_addr # type: ignore
return mta_addr
def download_6_bytes(self, data: bytes) -> int:
if len(data) != 6:
@@ -226,7 +226,7 @@ class CcpClient:
resp = self._recv_dto(0.025)
# mta_addr_ext = resp[0]
mta_addr = struct.unpack(f"{self.byte_order.value}I", resp[1:5])[0]
return mta_addr # type: ignore
return mta_addr
def upload(self, size: int) -> bytes:
if size > 5:
@@ -325,7 +325,7 @@ class CcpClient:
resp = self._recv_dto(0.1)
# mta_addr_ext = resp[0]
mta_addr = struct.unpack(f"{self.byte_order.value}I", resp[1:5])[0]
return mta_addr # type: ignore
return mta_addr
def program_6_bytes(self, data: bytes) -> int:
if len(data) != 6:
@@ -334,7 +334,7 @@ class CcpClient:
resp = self._recv_dto(0.1)
# mta_addr_ext = resp[0]
mta_addr = struct.unpack(f"{self.byte_order.value}I", resp[1:5])[0]
return mta_addr # type: ignore
return mta_addr
def move_memory_block(self, size: int) -> None:
self._send_cro(COMMAND_CODE.MOVE, struct.pack(f"{self.byte_order.value}I", size))

View File

@@ -73,8 +73,8 @@ class RadarInterface(RadarInterfaceBase):
if 'LONG_DIST' in cpt: # c_* message
self.pts[trackId].dRel = cpt['LONG_DIST'] # from front of car
# our lat_dist is positive to the right in car's frame.
# TODO what does yRel want?
self.pts[trackId].yRel = cpt['LAT_DIST'] # in car frame's y axis, left is positive
# LAT_DIST is right-positive, yRel is left-positive
self.pts[trackId].yRel = -cpt['LAT_DIST'] # in car frame's y axis, left is positive
else: # d_* message
self.pts[trackId].vRel = cpt['REL_SPEED']

View File

@@ -27,4 +27,5 @@ def _gen_crc16_table(poly: int) -> list[int]:
CRC8H2F = _gen_crc8_table(0x2F)
CRC8J1850 = _gen_crc8_table(0x1D)
CRC8BODY = _gen_crc8_table(0xD5)
CRC16_XMODEM = _gen_crc16_table(0x1021)

View File

@@ -1,12 +1,13 @@
#!/usr/bin/env python3
import argparse
import re
import os
import jinja2
import argparse
import unicodedata
from typing import get_args
from collections import defaultdict
import jinja2
from enum import Enum
from natsort import natsorted
from collections import defaultdict
from opendbc.car.common.basedir import BASEDIR
from opendbc.car import gen_empty_fingerprint
@@ -47,6 +48,12 @@ def get_all_footnotes() -> dict[Enum, int]:
return {fn: idx + 1 for idx, fn in enumerate(all_footnotes)}
def _natural_sort_key(s):
# NFKD normalization ensures accented characters sort with their base letter (e.g., Š sorts with S)
normalized = unicodedata.normalize('NFKD', s)
return [int(t) if t.isdigit() else t.lower() for t in re.split(r'(\d+)', normalized) if t]
def build_sorted_car_docs_list(platforms, footnotes=None):
collected_car_docs: list[CarDocs | ExtraCarDocs] = []
for platform in platforms.values():
@@ -64,7 +71,7 @@ def build_sorted_car_docs_list(platforms, footnotes=None):
collected_car_docs.append(_car_docs)
# Sort cars by make and model + year
sorted_cars = natsorted(collected_car_docs, key=lambda car: car.name.lower())
sorted_cars = sorted(collected_car_docs, key=lambda car: _natural_sort_key(car.name))
return sorted_cars

View File

@@ -37,9 +37,11 @@ class CAR(Platforms):
EXTRA_HONDA = ExtraPlatformConfig(
[
CommunityCarDocs("Acura ADX 2025-26"),
CommunityCarDocs("Acura Integra 2023-25"),
CommunityCarDocs("Acura MDX 2015-16", "Advance Package"),
CommunityCarDocs("Acura MDX 2017-20"),
CommunityCarDocs("Acura MDX Hybrid 2017-20"),
CommunityCarDocs("Acura MDX 2022-24"),
CommunityCarDocs("Acura RDX 2022-25"),
CommunityCarDocs("Acura RLX 2017", "Advance Package or Technology Package"),
@@ -48,6 +50,7 @@ class CAR(Platforms):
CommunityCarDocs("Acura TLX 2022-23"),
GMSecurityCarDocs("Acura ZDX 2024"),
CommunityCarDocs("Honda Accord 2016-17", "Honda Sensing"),
CommunityCarDocs("Honda Accord Hybrid 2017"),
CommunityCarDocs("Honda Clarity 2018-21"),
GMSecurityCarDocs("Honda Prologue 2024-25"),
],
@@ -55,8 +58,8 @@ class CAR(Platforms):
EXTRA_HYUNDAI = ExtraPlatformConfig(
[
CommunityCarDocs("Hyundai Palisade 2023-24", package="HDA2"),
CommunityCarDocs("Kia Telluride 2023-24", package="HDA2"),
CommunityCarDocs("Hyundai Palisade 2023-24", "Highway Driving Assist II"),
CommunityCarDocs("Kia Telluride 2023-24", "Highway Driving Assist II"),
],
)

View File

@@ -3,7 +3,7 @@ from enum import Enum, IntFlag
from opendbc.car import Bus, PlatformConfig, DbcDict, Platforms, CarSpecs
from opendbc.car.structs import CarParams
from opendbc.car.docs_definitions import CarDocs, CarFootnote, CarHarness, CarParts, Column
from opendbc.car.docs_definitions import CarDocs, CarFootnote, CarHarness, CarParts, Column, SupportType
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
from opendbc.sunnypilot.car.gm.values_ext import GMFlagsSP
@@ -88,6 +88,13 @@ class GMCarDocs(CarDocs):
self.car_parts = CarParts.common([CarHarness.obd_ii])
@dataclass
class GMNonAccCarDocs(GMCarDocs):
package: str = "No Adaptive Cruise Control (Non-ACC)"
support_type: SupportType = SupportType.COMMUNITY
support_link: str = "community"
@dataclass(frozen=True, kw_only=True)
class GMCarSpecs(CarSpecs):
tireStiffnessFactor: float = 0.444 # not optimized yet
@@ -116,6 +123,12 @@ class GMSDGMPlatformConfig(GMPlatformConfig):
self.car_docs = []
@dataclass
class GMNonSccPlatformConfig(GMPlatformConfig):
def init(self):
self.sp_flags |= GMFlagsSP.NON_ACC
class CAR(Platforms):
HOLDEN_ASTRA = GMASCMPlatformConfig(
[GMCarDocs("Holden Astra 2017")],
@@ -199,58 +212,48 @@ class CAR(Platforms):
# port extensions
# Separate car def is required when there is no ASCM
# (for now) unless there is a way to detect it when it has been unplugged...
# CHEVROLET_VOLT_CC = GMPlatformConfig(
# [GMCarDocs("Chevrolet Volt LT 2017-18")],
# CHEVROLET_VOLT_CC = GMNonSccPlatformConfig(
# [GMNonAccCarDocs("Chevrolet Volt LT 2017-18")],
# CHEVROLET_VOLT.specs,
# sp_flags=GMFlagsSP.NON_ACC,
# )
CHEVROLET_BOLT_NON_ACC = GMPlatformConfig(
[GMCarDocs("Chevrolet Bolt EV Non-ACC 2017")],
CHEVROLET_BOLT_NON_ACC = GMNonSccPlatformConfig(
[GMNonAccCarDocs("Chevrolet Bolt EV Non-ACC 2017")],
CHEVROLET_BOLT_EUV.specs,
sp_flags=GMFlagsSP.NON_ACC,
)
CHEVROLET_BOLT_NON_ACC_1ST_GEN = GMPlatformConfig(
[GMCarDocs("Chevrolet Bolt EV Non-ACC 2018-21")],
CHEVROLET_BOLT_NON_ACC_1ST_GEN = GMNonSccPlatformConfig(
[GMNonAccCarDocs("Chevrolet Bolt EV Non-ACC 2018-21")],
CHEVROLET_BOLT_EUV.specs,
sp_flags=GMFlagsSP.NON_ACC,
)
CHEVROLET_BOLT_NON_ACC_2ND_GEN = GMPlatformConfig(
CHEVROLET_BOLT_NON_ACC_2ND_GEN = GMNonSccPlatformConfig(
[
GMCarDocs("Chevrolet Bolt EUV LT Non-ACC 2022-23"),
GMCarDocs("Chevrolet Bolt EV LT Non-ACC 2022-23"),
GMNonAccCarDocs("Chevrolet Bolt EUV LT Non-ACC 2022-23"),
GMNonAccCarDocs("Chevrolet Bolt EV LT Non-ACC 2022-23"),
],
CHEVROLET_BOLT_EUV.specs,
sp_flags=GMFlagsSP.NON_ACC,
)
CHEVROLET_EQUINOX_NON_ACC_3RD_GEN = GMPlatformConfig(
[GMCarDocs("Chevrolet Equinox Non-ACC 2019-22")],
CHEVROLET_EQUINOX_NON_ACC_3RD_GEN = GMNonSccPlatformConfig(
[GMNonAccCarDocs("Chevrolet Equinox Non-ACC 2019-22")],
CHEVROLET_EQUINOX.specs,
sp_flags=GMFlagsSP.NON_ACC,
)
CHEVROLET_SUBURBAN_NON_ACC_11TH_GEN = GMPlatformConfig(
[GMCarDocs("Chevrolet Suburban Non-ACC 2016-20")],
CHEVROLET_SUBURBAN_NON_ACC_11TH_GEN = GMNonSccPlatformConfig(
[GMNonAccCarDocs("Chevrolet Suburban Non-ACC 2016-20")],
CarSpecs(mass=2731, wheelbase=3.302, steerRatio=17.3, centerToFrontRatio=0.49),
sp_flags=GMFlagsSP.NON_ACC,
)
CADILLAC_CT6_NON_ACC_1ST_GEN = GMPlatformConfig(
[GMCarDocs("Cadillac CT6 Non-ACC 2017-18")],
CADILLAC_CT6_NON_ACC_1ST_GEN = GMNonSccPlatformConfig(
[GMNonAccCarDocs("Cadillac CT6 Non-ACC 2017-18")],
CarSpecs(mass=2358, wheelbase=3.11, steerRatio=17.7, centerToFrontRatio=0.4),
sp_flags=GMFlagsSP.NON_ACC,
)
CHEVROLET_TRAILBLAZER_NON_ACC_2ND_GEN = GMPlatformConfig(
[GMCarDocs("Chevrolet Trailblazer Non-ACC 2021-22")],
CHEVROLET_TRAILBLAZER_NON_ACC_2ND_GEN = GMNonSccPlatformConfig(
[GMNonAccCarDocs("Chevrolet Trailblazer Non-ACC 2021-22")],
CHEVROLET_TRAILBLAZER.specs,
sp_flags=GMFlagsSP.NON_ACC,
)
CHEVROLET_MALIBU_NON_ACC_9TH_GEN = GMPlatformConfig(
[GMCarDocs("Chevrolet Malibu Non-ACC 2016-23")],
CHEVROLET_MALIBU_NON_ACC_9TH_GEN = GMNonSccPlatformConfig(
[GMNonAccCarDocs("Chevrolet Malibu Non-ACC 2016-23")],
CarSpecs(mass=1450, wheelbase=2.8, steerRatio=15.8, centerToFrontRatio=0.4),
sp_flags=GMFlagsSP.NON_ACC,
)
CADILLAC_XT5_NON_ACC_1ST_GEN = GMPlatformConfig(
[GMCarDocs("Cadillac XT5 Non-ACC 2018")],
CADILLAC_XT5_NON_ACC_1ST_GEN = GMNonSccPlatformConfig(
[GMNonAccCarDocs("Cadillac XT5 Non-ACC 2018")],
CarSpecs(mass=1810, wheelbase=2.86, steerRatio=16.34, centerToFrontRatio=0.5),
sp_flags=GMFlagsSP.NON_ACC,
)

View File

@@ -51,6 +51,8 @@ class CarState(CarStateBase, CarStateExt):
# When available we use cp.vl["CAR_SPEED"]["ROUGH_CAR_SPEED_2"] to populate vEgoCluster
# However, on cars without a digital speedometer this is not always present (HRV, FIT, CRV 2016, ILX and RDX)
self.dash_speed_seen = False
self.is_metric = False
self.v_cruise_factor = 1.
def update(self, can_parsers) -> tuple[structs.CarState, structs.CarStateSP]:
cp = can_parsers[Bus.pt]
@@ -72,7 +74,8 @@ class CarState(CarStateBase, CarStateExt):
self.cruise_buttons = cp.vl["SCM_BUTTONS"]["CRUISE_BUTTONS"]
# used for car hud message
self.is_metric = not cp.vl["CAR_SPEED"]["IMPERIAL_UNIT"]
# TODO: find CAR_SPEED for HONDA_ODYSSEY_TWN or use ACC_HUD w/ detection
self.is_metric = self.CP.carFingerprint in (CAR.HONDA_ODYSSEY_TWN,) or not cp.vl["CAR_SPEED"]["IMPERIAL_UNIT"]
self.v_cruise_factor = CV.MPH_TO_MS if self.dynamic_v_cruise_units and not self.is_metric else CV.KPH_TO_MS
# ******************* parse out can *******************
@@ -131,10 +134,11 @@ class CarState(CarStateBase, CarStateExt):
ret.espDisabled = cp.vl["VSA_STATUS"]["ESP_DISABLED"] != 0
self.dash_speed_seen = self.dash_speed_seen or cp.vl["CAR_SPEED"]["ROUGH_CAR_SPEED_2"] > 1e-3
if self.dash_speed_seen:
conversion = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
ret.vEgoCluster = cp.vl["CAR_SPEED"]["ROUGH_CAR_SPEED_2"] * conversion
if self.CP.carFingerprint not in (CAR.HONDA_ODYSSEY_TWN,):
self.dash_speed_seen = self.dash_speed_seen or cp.vl["CAR_SPEED"]["ROUGH_CAR_SPEED_2"] > 1e-3
if self.dash_speed_seen:
conversion = CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS
ret.vEgoCluster = cp.vl["CAR_SPEED"]["ROUGH_CAR_SPEED_2"] * conversion
ret.steeringAngleDeg = cp.vl["STEERING_SENSORS"]["STEER_ANGLE"]
ret.steeringRateDeg = cp.vl["STEERING_SENSORS"]["STEER_ANGLE_RATE"]

View File

@@ -223,10 +223,12 @@ FW_VERSIONS = {
b'57114-TBG-A340\x00\x00',
b'57114-TBG-A350\x00\x00',
b'57114-TGG-A340\x00\x00',
b'57114-TGG-C120\x00\x00',
b'57114-TGG-C320\x00\x00',
b'57114-TGG-G320\x00\x00',
b'57114-TGG-L320\x00\x00',
b'57114-TGG-L330\x00\x00',
b'57114-TGH-A130\x00\x00',
b'57114-TGH-L130\x00\x00',
b'57114-TGJ-Q330\x00\x00',
b'57114-TGK-T320\x00\x00',
@@ -241,6 +243,7 @@ FW_VERSIONS = {
b'39990-TGG-A020\x00\x00',
b'39990-TGG-A120\x00\x00',
b'39990-TGG-J510\x00\x00',
b'39990-TGH-J020\x00\x00',
b'39990-TGH-J530\x00\x00',
b'39990-TGL-E130\x00\x00',
b'39990-TGN-E120\x00\x00',
@@ -257,6 +260,7 @@ FW_VERSIONS = {
b'77959-TGG-J320\x00\x00',
b'77959-TGG-Q810\x00\x00',
b'77959-TGG-Z820\x00\x00',
b'77959-TGH-E220\x00\x00',
b'77959-TGH-J110\x00\x00',
],
(Ecu.fwdRadar, 0x18dab0f1, None): [
@@ -269,6 +273,7 @@ FW_VERSIONS = {
b'36802-TGG-A130\x00\x00',
b'36802-TGG-G040\x00\x00',
b'36802-TGG-G130\x00\x00',
b'36802-TGH-A030\x00\x00',
b'36802-TGH-A140\x00\x00',
b'36802-TGK-Q030\x00\x00',
b'36802-TGK-Q120\x00\x00',
@@ -285,6 +290,7 @@ FW_VERSIONS = {
b'36161-TGG-G070\x00\x00',
b'36161-TGG-G130\x00\x00',
b'36161-TGG-G140\x00\x00',
b'36161-TGH-A020\x00\x00',
b'36161-TGH-A140\x00\x00',
b'36161-TGK-Q040\x00\x00',
b'36161-TGK-Q120\x00\x00',
@@ -580,6 +586,17 @@ FW_VERSIONS = {
b'54008-THR-A020\x00\x00',
],
},
CAR.HONDA_ODYSSEY_TWN: {
(Ecu.eps, 0x18da30f1, None): [
b'39990-T6A-J210\x00\x00',
],
(Ecu.srs, 0x18da53f1, None): [
b'77959-T6A-P110\x00\x00',
],
(Ecu.fwdRadar, 0x18dab0f1, None): [
b'36161-T6A-P040\x00\x00',
],
},
CAR.HONDA_ODYSSEY_5G_MMR: {
(Ecu.vsa, 0x18da28f1, None): [
b'57114-THR-A240\x00\x00',
@@ -858,6 +875,7 @@ FW_VERSIONS = {
b'77959-3V0-A910\x00\x00',
],
(Ecu.fwdRadar, 0x18dab0f1, None): [
b'8S102-3M1-T050\x00\x00',
b'8S102-3M3-T050\x00\x00',
b'8S102-3M6-P030\x00\x00',
b'8S102-3M6-PA20\x00\x00',
@@ -1070,6 +1088,14 @@ FW_VERSIONS = {
b'36161-TGV-A030\x00\x00',
],
},
CAR.ACURA_TLX_2G_MMR: {
(Ecu.fwdRadar, 0x18dab0f1, None): [
b'8S302-TGV-A030\x00\x00',
],
(Ecu.fwdCamera, 0x18dab5f1, None): [
b'8S102-TGV-A030\x00\x00',
],
},
}
FW_VERSIONS = merge_fw_versions(FW_VERSIONS, FW_VERSIONS_EXT)

View File

@@ -167,6 +167,10 @@ class CarInterface(CarInterfaceBase):
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.28], [0.08]]
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
elif candidate == CAR.HONDA_ODYSSEY_TWN:
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.28], [0.08]]
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 32767], [0, 32767]] # TODO: determine if there is a dead zone at the top end
elif candidate == CAR.HONDA_PILOT:
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 4096], [0, 4096]] # TODO: determine if there is a dead zone at the top end
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.38], [0.11]]
@@ -195,6 +199,11 @@ class CarInterface(CarInterfaceBase):
# When using stock ACC, the radar intercepts and filters steering commands the EPS would otherwise accept
ret.minSteerSpeed = 70. * CV.KPH_TO_MS
elif candidate == CAR.ACURA_TLX_2G_MMR:
ret.steerActuatorDelay = 0.15
ret.lateralParams.torqueBP, ret.lateralParams.torqueV = [[0, 3840], [0, 3840]]
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
# TODO-SP: remove when https://github.com/commaai/opendbc/pull/2687 is merged
elif candidate == CAR.HONDA_CLARITY:
pass
@@ -205,7 +214,7 @@ class CarInterface(CarInterfaceBase):
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
# These cars use alternate user brake msg (0x1BE)
if 0x1BE in fingerprint[CAN.pt] and candidate in (CAR.HONDA_ACCORD, CAR.HONDA_HRV_3G, *HONDA_BOSCH_CANFD):
if 0x1BE in fingerprint[CAN.pt] and candidate in (CAR.HONDA_ACCORD, CAR.HONDA_HRV_3G, CAR.ACURA_RDX_3G, *HONDA_BOSCH_CANFD):
ret.flags |= HondaFlags.BOSCH_ALT_BRAKE.value
if ret.flags & HondaFlags.BOSCH_ALT_BRAKE:
@@ -223,7 +232,12 @@ class CarInterface(CarInterfaceBase):
# to a negative value, so it won't matter. Otherwise, add 0.5 mph margin to not
# conflict with PCM acc
ret.autoResumeSng = candidate in (HONDA_BOSCH | {CAR.HONDA_CIVIC})
ret.minEnableSpeed = -1. if ret.autoResumeSng else 25.51 * CV.MPH_TO_MS
if ret.autoResumeSng:
ret.minEnableSpeed = -1.
elif candidate == CAR.HONDA_ODYSSEY_TWN:
ret.minEnableSpeed = 19. * CV.MPH_TO_MS
else:
ret.minEnableSpeed = 25.51 * CV.MPH_TO_MS
ret.steerLimitTimer = 0.8
ret.radarDelay = 0.1

View File

@@ -3,7 +3,7 @@ from enum import Enum, IntFlag
from opendbc.car import Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, structs, uds
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column
from opendbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column, SupportType
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16
Ecu = structs.CarParams.Ecu
@@ -114,6 +114,8 @@ class HondaCarDocs(CarDocs):
if CP.carFingerprint in (CAR.HONDA_CLARITY,):
self.car_parts = CarParts.common([CarHarness.honda_clarity])
self.car_parts.custom_parts_url = "https://shop.retropilot.org/product/honda-clarity-proxy-board-kit"
self.support_type: SupportType = SupportType.COMMUNITY
self.support_link: str = "community"
class Footnote(Enum):
@@ -189,6 +191,7 @@ class CAR(Platforms):
],
CarSpecs(mass=1326, wheelbase=2.7, steerRatio=15.38, centerToFrontRatio=0.4), # steerRatio: 10.93 is end-to-end spec
{Bus.pt: 'honda_civic_hatchback_ex_2017_can_generated'},
flags=HondaFlags.ALLOW_MANUAL_TRANS,
)
HONDA_CIVIC_BOSCH_DIESEL = HondaBoschPlatformConfig(
[], # don't show in docs
@@ -281,6 +284,11 @@ class CAR(Platforms):
{Bus.pt: 'honda_civic_hatchback_ex_2017_can_generated'},
flags=HondaFlags.BOSCH_ALT_RADAR,
)
# mid-model refresh
ACURA_TLX_2G_MMR = HondaBoschCANFDPlatformConfig(
[HondaCarDocs("Acura TLX 2025", "All")],
CarSpecs(mass=3990 * CV.LB_TO_KG, wheelbase=2.87, centerToFrontRatio=0.43, steerRatio=13.7),
)
# Nidec Cars
ACURA_ILX = HondaNidecPlatformConfig(
@@ -328,6 +336,12 @@ class CAR(Platforms):
radar_dbc_dict('honda_odyssey_exl_2018_generated'),
flags=HondaFlags.NIDEC_ALT_PCM_ACCEL | HondaFlags.HAS_ALL_DOOR_STATES,
)
HONDA_ODYSSEY_TWN = HondaNidecPlatformConfig(
[HondaCarDocs("Honda Odyssey (Taiwan) 2018-19")],
CarSpecs(mass=1865, wheelbase=2.9, steerRatio=14.35, centerToFrontRatio=0.44, tireStiffnessFactor=0.82),
radar_dbc_dict('honda_odyssey_twn_2018_generated'),
flags=HondaFlags.NIDEC_ALT_SCM_MESSAGES,
)
ACURA_RDX = HondaNidecPlatformConfig(
[HondaCarDocs("Acura RDX 2016-18", "AcuraWatch Plus or Advance Package", min_steer_speed=12. * CV.MPH_TO_MS)],
CarSpecs(mass=3925 * CV.LB_TO_KG, wheelbase=2.68, steerRatio=15.0, centerToFrontRatio=0.38, tireStiffnessFactor=0.444), # as spec

View File

@@ -701,6 +701,7 @@ FW_VERSIONS = {
b'\xf1\x00OSP MDPS C 1.00 1.02 56310-K4271 4OEPC102',
b'\xf1\x00OSP MDPS C 1.00 1.02 56310/K4271 4OEPC102',
b'\xf1\x00OSP MDPS C 1.00 1.02 56310/K4970 4OEPC102',
b'\xf1\x00OSP MDPS C 1.00 1.02 56310/K4971 4OEPC102',
b'\xf1\x00OSP MDPS C 1.00 1.02 56310K4260\x00 4OEPC102',
b'\xf1\x00OSP MDPS C 1.00 1.02 56310K4261\x00 4OEPC102',
b'\xf1\x00OSP MDPS C 1.00 1.02 56310K4971\x00 4OEPC102',
@@ -1087,12 +1088,14 @@ FW_VERSIONS = {
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.00 99211-N9260 14Y',
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.01 99211-N9100 14A',
b'\xf1\x00NX4 FR_CMR AT USA LHD 1.00 1.01 99211-N9240 14T',
b'\xf1\x00NX4 FR_CMR AT EUR LHD 1.00 1.00 99211-N9240 14Q',
],
(Ecu.fwdRadar, 0x7d0, None): [
b'\xf1\x00NX4__ 1.00 1.00 99110-N9100 ',
b'\xf1\x00NX4__ 1.00 1.01 99110-N9000 ',
b'\xf1\x00NX4__ 1.00 1.02 99110-N9000 ',
b'\xf1\x00NX4__ 1.01 1.00 99110-N9100 ',
b'\xf1\x00NX4__ 1.01 1.02 99110-N9000 ',
],
},
CAR.HYUNDAI_SANTA_CRUZ_1ST_GEN: {

View File

@@ -5,7 +5,7 @@ from enum import IntFlag
from opendbc.car import Bus, CarSpecs, DbcDict, PlatformConfig, Platforms, uds
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.structs import CarParams
from opendbc.car.docs_definitions import CarHarness, CarDocs, CarParts
from opendbc.car.docs_definitions import CarHarness, CarDocs, CarParts, SupportType
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, p16
from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP
@@ -134,6 +134,13 @@ class HyundaiCarDocs(CarDocs):
package: str = "Smart Cruise Control (SCC)"
@dataclass
class HyundaiNonSccCarDocs(CarDocs):
package: str = "No Smart Cruise Control (Non-SCC)"
support_type: SupportType = SupportType.COMMUNITY
support_link: str = "community"
@dataclass
class HyundaiPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "hyundai_kia_generic"})
@@ -146,6 +153,17 @@ class HyundaiPlatformConfig(PlatformConfig):
self.specs = self.specs.override(minSteerSpeed=32 * CV.MPH_TO_MS)
@dataclass
class HyundaiNonSccPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "hyundai_kia_generic"})
def init(self):
self.sp_flags |= HyundaiFlagsSP.NON_SCC
if self.flags & HyundaiFlags.MIN_STEER_32_MPH:
self.specs = self.specs.override(minSteerSpeed=32 * CV.MPH_TO_MS)
@dataclass
class HyundaiCanFDPlatformConfig(PlatformConfig):
dbc_dict: DbcDict = field(default_factory=lambda: {Bus.pt: "hyundai_canfd_generated"})
@@ -589,57 +607,50 @@ class CAR(Platforms):
)
# port extensions
HYUNDAI_BAYON_1ST_GEN_NON_SCC = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Bayon Non-SCC 2021", "No Smart Cruise Control (Non-SCC)", car_parts=CarParts.common([CarHarness.hyundai_n]))],
HYUNDAI_BAYON_1ST_GEN_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Hyundai Bayon Non-SCC 2021", car_parts=CarParts.common([CarHarness.hyundai_n]))],
CarSpecs(mass=1150, wheelbase=2.58, steerRatio=13.27 * 1.15),
flags=HyundaiFlags.CHECKSUM_CRC8,
sp_flags=HyundaiFlagsSP.NON_SCC,
)
HYUNDAI_ELANTRA_2022_NON_SCC = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Elantra Non-SCC 2022", "No Smart Cruise Control (Non-SCC)", car_parts=CarParts.common([CarHarness.hyundai_k]))],
HYUNDAI_ELANTRA_2022_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Hyundai Elantra Non-SCC 2022", car_parts=CarParts.common([CarHarness.hyundai_k]))],
HYUNDAI_ELANTRA_2021.specs,
flags=HyundaiFlags.CHECKSUM_CRC8,
sp_flags=HyundaiFlagsSP.NON_SCC,
)
HYUNDAI_KONA_NON_SCC = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Kona Non-SCC 2019", "No Smart Cruise Control (Non-SCC)", car_parts=CarParts.common([CarHarness.hyundai_b]))],
HYUNDAI_KONA_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Hyundai Kona Non-SCC 2019", car_parts=CarParts.common([CarHarness.hyundai_b]))],
HYUNDAI_KONA.specs,
flags=HyundaiFlags.ALT_LIMITS,
sp_flags=HyundaiFlagsSP.NON_SCC,
)
HYUNDAI_KONA_EV_NON_SCC = HyundaiPlatformConfig(
[HyundaiCarDocs("Hyundai Kona Electric Non-SCC 2019", "No Smart Cruise Control (Non-SCC)", car_parts=CarParts.common([CarHarness.hyundai_g]))],
HYUNDAI_KONA_EV_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Hyundai Kona Electric Non-SCC 2019", car_parts=CarParts.common([CarHarness.hyundai_g]))],
HYUNDAI_KONA_EV.specs,
flags=HyundaiFlags.EV | HyundaiFlags.ALT_LIMITS,
sp_flags=HyundaiFlagsSP.NON_SCC,
)
KIA_CEED_PHEV_2022_NON_SCC = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia Ceed Plug-in Hybrid Non-SCC 2022", "No Smart Cruise Control (Non-SCC)", car_parts=CarParts.common([CarHarness.hyundai_i]))],
KIA_CEED_PHEV_2022_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Kia Ceed Plug-in Hybrid Non-SCC 2022", car_parts=CarParts.common([CarHarness.hyundai_i]))],
CarSpecs(mass=1650, wheelbase=2.65, steerRatio=13.75, tireStiffnessFactor=0.5),
flags=HyundaiFlags.HYBRID,
sp_flags=HyundaiFlagsSP.NON_SCC,
)
KIA_FORTE_2019_NON_SCC = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia Forte Non-SCC 2019", "No Smart Cruise Control (Non-SCC)", car_parts=CarParts.common([CarHarness.hyundai_g]))],
KIA_FORTE_2019_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Kia Forte Non-SCC 2019", car_parts=CarParts.common([CarHarness.hyundai_g]))],
KIA_FORTE.specs,
sp_flags=HyundaiFlagsSP.NON_SCC | HyundaiFlagsSP.NON_SCC_NO_FCA,
sp_flags=HyundaiFlagsSP.NON_SCC_NO_FCA,
)
KIA_FORTE_2021_NON_SCC = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia Forte Non-SCC 2021", "No Smart Cruise Control (Non-SCC)", car_parts=CarParts.common([CarHarness.hyundai_g]))],
KIA_FORTE_2021_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Kia Forte Non-SCC 2021", car_parts=CarParts.common([CarHarness.hyundai_g]))],
KIA_FORTE.specs,
sp_flags=HyundaiFlagsSP.NON_SCC,
)
KIA_SELTOS_2023_NON_SCC = HyundaiPlatformConfig(
[HyundaiCarDocs("Kia Seltos Non-SCC 2023-24", "No Smart Cruise Control (Non-SCC)", car_parts=CarParts.common([CarHarness.hyundai_l]))],
KIA_SELTOS_2023_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Kia Seltos Non-SCC 2023-24", car_parts=CarParts.common([CarHarness.hyundai_l]))],
KIA_SELTOS.specs,
flags=HyundaiFlags.CHECKSUM_CRC8,
sp_flags=HyundaiFlagsSP.NON_SCC,
)
GENESIS_G70_2021_NON_SCC = HyundaiPlatformConfig(
[HyundaiCarDocs("Genesis G70 Non-SCC 2021", "No Smart Cruise Control (Non-SCC)", car_parts=CarParts.common([CarHarness.hyundai_f]))],
GENESIS_G70_2021_NON_SCC = HyundaiNonSccPlatformConfig(
[HyundaiNonSccCarDocs("Genesis G70 Non-SCC 2021", car_parts=CarParts.common([CarHarness.hyundai_f]))],
GENESIS_G70_2020.specs,
flags=HyundaiFlags.CHECKSUM_CRC8,
sp_flags=HyundaiFlagsSP.NON_SCC | HyundaiFlagsSP.NON_SCC_RADAR_FCA,
sp_flags=HyundaiFlagsSP.NON_SCC_RADAR_FCA,
)

View File

@@ -5,7 +5,7 @@ from opendbc.car import structs, rate_limit, DT_CTRL, ACCELERATION_DUE_TO_GRAVIT
from opendbc.car.vehicle_model import VehicleModel
from typing import Tuple
FRICTION_THRESHOLD = 0.3
FRICTION_THRESHOLD = 0.2
# ISO 11270
ISO_LATERAL_ACCEL = 3.0 # m/s^2

View File

@@ -4,15 +4,12 @@ from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.interfaces import CarStateBase
from opendbc.car.mazda.values import DBC, LKAS_LIMITS
from opendbc.sunnypilot.car.mazda.carstate_ext import CarStateExt
ButtonType = structs.CarState.ButtonEvent.Type
class CarState(CarStateBase, CarStateExt):
class CarState(CarStateBase):
def __init__(self, CP, CP_SP):
CarStateBase.__init__(self, CP, CP_SP)
CarStateExt.__init__(self, CP, CP_SP)
super().__init__(CP, CP_SP)
can_define = CANDefine(DBC[CP.carFingerprint][Bus.pt])
self.shifter_values = can_define.dv["GEAR"]["GEAR"]
@@ -22,6 +19,8 @@ class CarState(CarStateBase, CarStateExt):
self.lkas_allowed_speed = False
self.distance_button = 0
self.accel_button = 0
self.decel_button = 0
def update(self, can_parsers) -> tuple[structs.CarState, structs.CarStateSP]:
cp = can_parsers[Bus.pt]
@@ -30,9 +29,6 @@ class CarState(CarStateBase, CarStateExt):
ret = structs.CarState()
ret_sp = structs.CarStateSP()
prev_distance_button = self.distance_button
self.distance_button = cp.vl["CRZ_BTNS"]["DISTANCE_LESS"]
self.parse_wheel_speeds(ret,
cp.vl["WHEEL_SPEEDS"]["FL"],
cp.vl["WHEEL_SPEEDS"]["FR"],
@@ -116,12 +112,18 @@ class CarState(CarStateBase, CarStateExt):
self.cam_laneinfo = cp_cam.vl["CAM_LANEINFO"]
ret.steerFaultPermanent = cp_cam.vl["CAM_LKAS"]["ERR_BIT_1"] == 1
CarStateExt.update(self, ret, ret_sp, can_parsers)
# cruise control button events: distance, inc, and dec
prev_distance_button = self.distance_button
prev_accel_button = self.accel_button
prev_decel_button = self.decel_button
self.distance_button = cp.vl["CRZ_BTNS"]["DISTANCE_LESS"]
self.accel_button = cp.vl["CRZ_BTNS"]["RES"]
self.decel_button = cp.vl["CRZ_BTNS"]["SET_M"]
# TODO: add button types for inc and dec
ret.buttonEvents = [
*create_button_events(self.distance_button, prev_distance_button, {1: ButtonType.gapAdjustCruise}),
*self.button_events,
*create_button_events(self.accel_button, prev_accel_button, {1: ButtonType.accelCruise}),
*create_button_events(self.decel_button, prev_decel_button, {1: ButtonType.decelCruise}),
]
return ret, ret_sp

View File

@@ -16,6 +16,7 @@ class CarInterface(CarInterfaceBase):
ret.brand = "nissan"
ret.safetyConfigs = [get_safety_config(structs.CarParams.SafetyModel.nissan)]
ret.autoResumeSng = False
ret.steerAtStandstill = True
ret.steerLimitTimer = 1.0

View File

@@ -66,11 +66,19 @@ class CarState(CarStateBase, MadsCarState, SnGCarState):
can_gear = int(cp_transmission.vl["Transmission"]["Gear"])
ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(can_gear, None))
ret.steeringAngleDeg = cp.vl["Steering_Torque"]["Steering_Angle"]
if not (self.CP.flags & SubaruFlags.LKAS_ANGLE):
ret.steeringAngleDeg = cp.vl["Steering_Torque"]["Steering_Angle"]
steering_updated = len(cp.vl_all["Steering_Torque"]["Steering_Angle"]) > 0
else:
# Steering_Torque->Steering_Angle exists on SUBARU_FORESTER_2022, SUBARU_OUTBACK_2023, SUBARU_ASCENT_2023 where
# it is identical to Steering_2's signal. However, it is always zero on newer LKAS_ANGLE cars
# such as 2024+ Crosstrek, 2023+ Ascent, etc. Use a universal signal for LKAS_ANGLE cars.
ret.steeringAngleDeg = cp.vl["Steering_2"]["Steering_Angle"]
steering_updated = len(cp.vl_all["Steering_2"]["Steering_Angle"]) > 0
if not (self.CP.flags & SubaruFlags.PREGLOBAL):
# ideally we get this from the car, but unclear if it exists. diagnostic software doesn't even have it
ret.steeringRateDeg = self.angle_rate_calulator.update(ret.steeringAngleDeg, cp.vl["Steering_Torque"]["COUNTER"])
ret.steeringRateDeg = self.angle_rate_calulator.update(ret.steeringAngleDeg, steering_updated)
ret.steeringTorque = cp.vl["Steering_Torque"]["Steer_Torque_Sensor"]
ret.steeringTorqueEps = cp.vl["Steering_Torque"]["Steer_Torque_Output"]
@@ -79,8 +87,16 @@ class CarState(CarStateBase, MadsCarState, SnGCarState):
ret.steeringPressed = abs(ret.steeringTorque) > steer_threshold
cp_cruise = cp_alt if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp
if self.CP.flags & SubaruFlags.HYBRID:
ret.cruiseState.enabled = cp_cam.vl["ES_DashStatus"]['Cruise_Activated'] != 0
cp_es_brake = cp_alt if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp_cam
if self.CP.flags & (SubaruFlags.HYBRID | SubaruFlags.LKAS_ANGLE):
# ES_DashStatus->Cruise_Activated_Dash is likely intended for the dash display only, as it falls
# during user gas override and at standstill. ES_Status is missing on hybrid, so we use ES_Brake instead
# TODO: ES_Brake->Cruise_Activated has been seen staying high when Crosstrek 2025 angle LKAS user pressed
# brake while engaged at a stop. ES_Status and ES_DashStatus->Signal7 correctly fell, but is either missing or
# always zero on hybrids. Probably need to split angle & hybrid. 0x27 and 0x225 on hybrids may work for them.
ret.cruiseState.enabled = cp_es_brake.vl["ES_Brake"]['Cruise_Activated'] != 0
ret.cruiseState.available = cp_cam.vl["ES_DashStatus"]['Cruise_On'] != 0
else:
ret.cruiseState.enabled = cp_cruise.vl["CruiseControl"]["Cruise_Activated"] != 0
@@ -109,9 +125,7 @@ class CarState(CarStateBase, MadsCarState, SnGCarState):
(cp_cam.vl["ES_LKAS_State"]["LKAS_Alert"] == 2)
self.es_lkas_state_msg = copy.copy(cp_cam.vl["ES_LKAS_State"])
cp_es_brake = cp_alt if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp_cam
self.es_brake_msg = copy.copy(cp_es_brake.vl["ES_Brake"])
cp_es_status = cp_alt if self.CP.flags & SubaruFlags.GLOBAL_GEN2 else cp_cam
# TODO: Hybrid cars don't have ES_Distance, need a replacement
if not (self.CP.flags & SubaruFlags.HYBRID):
@@ -119,7 +133,7 @@ class CarState(CarStateBase, MadsCarState, SnGCarState):
ret.stockAeb = (cp_es_distance.vl["ES_Brake"]["AEB_Status"] == 8) and \
(cp_es_distance.vl["ES_Brake"]["Brake_Pressure"] != 0)
self.es_status_msg = copy.copy(cp_es_status.vl["ES_Status"])
self.es_status_msg = copy.copy(cp_es_brake.vl["ES_Status"])
self.cruise_control_msg = copy.copy(cp_cruise.vl["CruiseControl"])
if not (self.CP.flags & SubaruFlags.HYBRID):

View File

@@ -146,6 +146,7 @@ FW_VERSIONS = {
b'\xc5!ap\x07',
b'\xc5!ar\x07',
b'\xc5!as\x07',
b'\xc5!au\x07',
b'\xc5!dr\x07',
b'\xc5!ds\x07',
b'\xca\x01b0\x07',

View File

@@ -37,12 +37,13 @@ class CarInterface(CarInterfaceBase):
ret.steerLimitTimer = 0.4
ret.steerActuatorDelay = 0.1
if ret.flags & SubaruFlags.LKAS_ANGLE:
ret.steerControlType = structs.CarParams.SteerControlType.angle
else:
if not (ret.flags & SubaruFlags.LKAS_ANGLE):
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
if candidate in (CAR.SUBARU_ASCENT, CAR.SUBARU_ASCENT_2023):
if ret.flags & SubaruFlags.LKAS_ANGLE:
ret.steerControlType = structs.CarParams.SteerControlType.angle
elif candidate == CAR.SUBARU_ASCENT:
ret.steerActuatorDelay = 0.3 # end-to-end angle controller
ret.lateralTuning.init('pid')
ret.lateralTuning.pid.kf = 0.00003
@@ -65,13 +66,13 @@ class CarInterface(CarInterfaceBase):
elif candidate == CAR.SUBARU_CROSSTREK_HYBRID:
ret.steerActuatorDelay = 0.1
elif candidate in (CAR.SUBARU_FORESTER, CAR.SUBARU_FORESTER_2022, CAR.SUBARU_FORESTER_HYBRID):
elif candidate in (CAR.SUBARU_FORESTER, CAR.SUBARU_FORESTER_HYBRID):
ret.lateralTuning.init('pid')
ret.lateralTuning.pid.kf = 0.000038
ret.lateralTuning.pid.kiBP, ret.lateralTuning.pid.kpBP = [[0., 14., 23.], [0., 14., 23.]]
ret.lateralTuning.pid.kpV, ret.lateralTuning.pid.kiV = [[0.01, 0.065, 0.2], [0.001, 0.015, 0.025]]
elif candidate in (CAR.SUBARU_OUTBACK, CAR.SUBARU_LEGACY, CAR.SUBARU_OUTBACK_2023):
elif candidate in (CAR.SUBARU_OUTBACK, CAR.SUBARU_LEGACY):
ret.steerActuatorDelay = 0.1
elif candidate in (CAR.SUBARU_FORESTER_PREGLOBAL, CAR.SUBARU_OUTBACK_PREGLOBAL_2018):

View File

@@ -147,8 +147,8 @@ def create_es_dashstatus(packer, frame, dashstatus_msg, enabled, long_enabled, l
"Signal4",
"Conventional_Cruise",
"Signal5",
"Cruise_Disengaged",
"Cruise_Activated",
"Cruise_Disengaged_Dash",
"Cruise_Activated_Dash",
"Signal6",
"Cruise_Set_Speed",
"Cruise_Fault",
@@ -165,8 +165,9 @@ def create_es_dashstatus(packer, frame, dashstatus_msg, enabled, long_enabled, l
if long_enabled:
values["Cruise_State"] = 0
values["Cruise_Activated"] = enabled
values["Cruise_Disengaged"] = 0
# TODO: Cruise_Activated_dash should respect gas pressed and standstill stock behavior
values["Cruise_Activated_Dash"] = enabled
values["Cruise_Disengaged_Dash"] = 0
values["Car_Follow"] = int(lead_visible)
values["PCB_Off"] = 1 # AEB is not presevered, so show the PCB_Off on dash

View File

@@ -1,10 +1,11 @@
import copy
from opendbc.can import CANDefine, CANParser
from opendbc.car import Bus, structs
from opendbc.car.carlog import carlog
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.interfaces import CarStateBase
from opendbc.car.tesla.teslacan import get_steer_ctrl_type
from opendbc.car.tesla.values import DBC, CANBUS, GEAR_MAP, STEER_THRESHOLD, CAR
from opendbc.car.tesla.values import DBC, CANBUS, GEAR_MAP, STEER_THRESHOLD, TeslaFlags
from opendbc.sunnypilot.car.tesla.carstate_ext import CarStateExt
@@ -21,6 +22,8 @@ class CarState(CarStateBase, CarStateExt):
self.autopark = False
self.autopark_prev = False
self.cruise_enabled_prev = False
self.fsd14_error_logged = False
self.suspected_fsd14 = False
self.hands_on_level = 0
self.das_control = None
@@ -117,10 +120,23 @@ class CarState(CarStateBase, CarStateExt):
ret.stockLkas = cp_ap_party.vl["DAS_steeringControl"]["DAS_steeringControlType"] == lkas_ctrl_type # LANE_KEEP_ASSIST
# Stock Autosteer should be off (includes FSD)
if self.CP.carFingerprint in (CAR.TESLA_MODEL_3, CAR.TESLA_MODEL_Y):
# TODO: find for TESLA_MODEL_X and HW2.5 vehicles
if not (self.CP.flags & TeslaFlags.MISSING_DAS_SETTINGS):
ret.invalidLkasSetting = cp_ap_party.vl["DAS_settings"]["DAS_autosteerEnabled"] != 0
else:
pass
# Because we don't have FSD 14 detection outside of a set of FW, we should check if this FW is accidentally missing from FSD_14_FW
# 1. If in Autosteer or FSD, already caught by invalidLkasSetting
# 2. If in TACC and DAS ever sends ANGLE_CONTROL (1), we can infer it's trying to do LKAS on FSD 14+
angle_control = cp_ap_party.vl["DAS_steeringControl"]["DAS_steeringControlType"] == 1 # ANGLE_CONTROL
if not ret.invalidLkasSetting and angle_control and not self.CP.flags & TeslaFlags.FSD_14:
self.suspected_fsd14 = True
if self.suspected_fsd14:
ret.invalidLkasSetting = True
if not self.fsd14_error_logged:
carlog.error("FSD 14 detected, but FW not in FSD_14_FW set")
self.fsd14_error_logged = True
# Buttons # ToDo: add Gap adjust button
# Messages needed by carcontroller

View File

@@ -18,6 +18,7 @@ FW_VERSIONS = {
b'TeMYG4_Main_0.0.0 (59),E4H014.29.0',
b'TeMYG4_Main_0.0.0 (65),E4H015.01.0',
b'TeMYG4_Main_0.0.0 (67),E4H015.02.1',
b'TeMYG4_Main_0.0.0 (77),E4H015.04.5',
b'TeMYG4_SingleECU_0.0.0 (33),E4S014.27',
],
},

View File

@@ -1,8 +1,9 @@
from opendbc.car import get_safety_config, structs
from opendbc.car import Bus, get_safety_config, structs
from opendbc.car.interfaces import CarInterfaceBase
from opendbc.car.tesla.carcontroller import CarController
from opendbc.car.tesla.carstate import CarState
from opendbc.car.tesla.values import TeslaSafetyFlags, TeslaFlags, CAR, FSD_14_FW, Ecu
from opendbc.car.tesla.values import TeslaSafetyFlags, TeslaFlags, CANBUS, CAR, DBC, FSD_14_FW, Ecu
from opendbc.car.tesla.radar_interface import RadarInterface, RADAR_START_ADDR
from opendbc.sunnypilot.car.tesla.values import TeslaFlagsSP, TeslaSafetyFlagsSP
@@ -10,6 +11,7 @@ from opendbc.sunnypilot.car.tesla.values import TeslaFlagsSP, TeslaSafetyFlagsSP
class CarInterface(CarInterfaceBase):
CarState = CarState
CarController = CarController
RadarInterface = RadarInterface
@staticmethod
def _get_params(ret: structs.CarParams, candidate, fingerprint, car_fw, alpha_long, is_release, docs) -> structs.CarParams:
@@ -22,7 +24,17 @@ class CarInterface(CarInterfaceBase):
ret.steerAtStandstill = True
ret.steerControlType = structs.CarParams.SteerControlType.angle
ret.radarUnavailable = True
# Model X and HW 2.5 vehicles are missing DAS_settings
if 0x293 not in fingerprint[CANBUS.autopilot_party]:
ret.flags |= TeslaFlags.MISSING_DAS_SETTINGS.value
# Radar support is intended to work for:
# - Tesla Model 3 vehicles built approximately mid-2017 through early-2021
# - Tesla Model Y vehicles built approximately mid-2020 through early-2021
# - Vehicles equipped with the Continental ARS4-B radar (used on HW2 / HW2.5 / early HW3)
# - Radar CAN lines must be tapped and connected to CAN bus 1 (normally not used for tesla vehicles)
ret.radarUnavailable = RADAR_START_ADDR not in fingerprint[1] or Bus.radar not in DBC[candidate]
ret.alphaLongitudinalAvailable = True
if alpha_long:

View File

@@ -0,0 +1,89 @@
from opendbc.can import CANParser
from opendbc.car import Bus, structs
from opendbc.car.interfaces import RadarInterfaceBase
from opendbc.car.tesla.values import DBC
RADAR_START_ADDR = 0x410
RADAR_MSG_COUNT = 80 # 40 points * 2 messages each
def get_radar_can_parser(CP):
if Bus.radar not in DBC[CP.carFingerprint]:
return None
messages = [('RadarStatus', 16)]
for i in range(RADAR_MSG_COUNT // 2):
messages.extend([
(f'RadarPoint{i}_A', 16),
(f'RadarPoint{i}_B', 16),
])
return CANParser(DBC[CP.carFingerprint][Bus.radar], messages, 1)
class RadarInterface(RadarInterfaceBase):
def __init__(self, CP, CP_SP):
super().__init__(CP, CP_SP)
self.updated_messages = set()
self.trigger_msg = RADAR_START_ADDR + RADAR_MSG_COUNT - 1
self.track_id = 0
self.radar_off_can = CP.radarUnavailable
self.rcp = get_radar_can_parser(CP)
def update(self, can_strings):
if self.radar_off_can or self.rcp is None:
return super().update(None)
vls = self.rcp.update(can_strings)
self.updated_messages.update(vls)
if self.trigger_msg not in self.updated_messages:
return None
rr = self._update(self.updated_messages)
self.updated_messages.clear()
return rr
def _update(self, updated_messages):
ret = structs.RadarData()
if self.rcp is None:
return ret
if not self.rcp.can_valid:
ret.errors.canError = True
radar_status = self.rcp.vl['RadarStatus']
if radar_status['shortTermUnavailable']:
ret.errors.radarUnavailableTemporary = True
if radar_status['sensorBlocked'] or radar_status['vehDynamicsError']:
ret.errors.radarFault = True
for i in range(RADAR_MSG_COUNT // 2):
msg_a = self.rcp.vl[f'RadarPoint{i}_A']
msg_b = self.rcp.vl[f'RadarPoint{i}_B']
# Make sure msg A and B are together
if msg_a['Index'] != msg_b['Index2']:
continue
if not msg_a['Tracked']:
if i in self.pts:
del self.pts[i]
continue
if i not in self.pts:
self.pts[i] = structs.RadarData.RadarPoint()
self.pts[i].trackId = self.track_id
self.track_id += 1
self.pts[i].dRel = msg_a['LongDist']
self.pts[i].yRel = msg_a['LatDist']
self.pts[i].vRel = msg_a['LongSpeed']
self.pts[i].aRel = msg_a['LongAccel']
self.pts[i].yvRel = msg_b['LatSpeed']
self.pts[i].measured = bool(msg_a['Meas'])
ret.points = list(self.pts.values())
return ret

View File

View File

@@ -0,0 +1,24 @@
from opendbc.car import gen_empty_fingerprint
from opendbc.car.tesla.interface import CarInterface
from opendbc.car.tesla.radar_interface import RADAR_START_ADDR
from opendbc.car.tesla.values import CAR
class TestTeslaFingerprint:
def test_radar_detection(self):
# Test radar availability detection for cars with radar DBC defined
for radar in (True, False):
fingerprint = gen_empty_fingerprint()
if radar:
fingerprint[1][RADAR_START_ADDR] = 8
CP = CarInterface.get_params(CAR.TESLA_MODEL_3, fingerprint, [], False, False, False)
assert CP.radarUnavailable != radar
def test_no_radar_car(self):
# Model X doesn't have radar DBC defined, should always be unavailable
for radar in (True, False):
fingerprint = gen_empty_fingerprint()
if radar:
fingerprint[1][RADAR_START_ADDR] = 8
CP = CarInterface.get_params(CAR.TESLA_MODEL_X, fingerprint, [], False, False, False)
assert CP.radarUnavailable # Always unavailable since no radar DBC

View File

@@ -48,6 +48,7 @@ class CAR(Platforms):
TeslaCarDocsHW4("Tesla Model 3 (with HW4) 2024-25"),
],
CarSpecs(mass=1899., wheelbase=2.875, steerRatio=12.0),
{Bus.party: 'tesla_model3_party', Bus.radar: 'tesla_radar_continental_generated', Bus.adas: 'tesla_model3_vehicle'},
)
TESLA_MODEL_Y = TeslaPlatformConfig(
[
@@ -55,6 +56,7 @@ class CAR(Platforms):
TeslaCarDocsHW4("Tesla Model Y (with HW4) 2024-25"),
],
CarSpecs(mass=2072., wheelbase=2.890, steerRatio=12.0),
{Bus.party: 'tesla_model3_party', Bus.radar: 'tesla_radar_continental_generated', Bus.adas: 'tesla_model3_vehicle'},
)
TESLA_MODEL_X = TeslaPlatformConfig(
[TeslaCarDocsHW4("Tesla Model X (with HW4) 2024")],
@@ -133,6 +135,7 @@ class TeslaSafetyFlags(IntFlag):
class TeslaFlags(IntFlag):
LONG_CONTROL = 1
FSD_14 = 2
MISSING_DAS_SETTINGS = 4
DBC = CAR.create_dbc_map()

170
opendbc/car/tests/car_diff.py Normal file → Executable file
View File

@@ -1,3 +1,4 @@
#!/usr/bin/env python3
import argparse
import os
import pickle
@@ -5,23 +6,33 @@ import re
import subprocess
import sys
import tempfile
import traceback
import zstandard as zstd
from tqdm import tqdm
from tqdm.contrib.concurrent import process_map
from urllib.request import urlopen
from collections import defaultdict
from concurrent.futures import ProcessPoolExecutor
from pathlib import Path
from typing import Any
from comma_car_segments import get_comma_car_segments_database, get_url
from opendbc.car import structs
from opendbc.car.can_definitions import CanData
from opendbc.car.car_helpers import can_fingerprint, interfaces
from opendbc.car.logreader import LogReader, decompress_stream
TOLERANCE = 1e-4
DIFF_BUCKET = "car_diff"
IGNORE_FIELDS = ["cumLagMs", "canErrorCounter"]
PADDING = 5
Diff = tuple[str, int, tuple[Any, Any], int]
Ref = tuple[int, structs.CarState]
Result = tuple[str, str, list[Diff], list[Ref] | None, list[structs.CarState] | None, str | None]
def dict_diff(d1, d2, path="", ignore=None, tolerance=0):
def dict_diff(d1: dict[str, Any], d2: dict[str, Any], path: str = "", ignore: list[str] | None = None, tolerance: float = 0) -> list[tuple]:
ignore = ignore or []
diffs = []
for key in d1.keys() | d2.keys():
@@ -39,23 +50,21 @@ def dict_diff(d1, d2, path="", ignore=None, tolerance=0):
return diffs
def load_can_messages(seg):
def load_can_messages(seg: str) -> list[Any]:
from comma_car_segments import get_url
parts = seg.split("/")
url = get_url(f"{parts[0]}/{parts[1]}", parts[2])
msgs = LogReader(url, only_union_types=True)
msgs = LogReader(url, only_union_types=True, sort_by_time=True)
return [m for m in msgs if m.which() == 'can']
def replay_segment(platform, can_msgs):
from opendbc.car import gen_empty_fingerprint, structs
from opendbc.car.can_definitions import CanData
from opendbc.car.car_helpers import FRAME_FINGERPRINT, interfaces
def replay_segment(platform: str, can_msgs: list[Any]) -> tuple[structs.CarParams, list[structs.CarState], list[int]]:
_can_msgs = ([CanData(can.address, can.dat, can.src) for can in m.can] for m in can_msgs)
fingerprint = gen_empty_fingerprint()
for msg in can_msgs[:FRAME_FINGERPRINT]:
for m in msg.can:
if m.src < 64:
fingerprint[m.src][m.address] = len(m.dat)
def can_recv(wait_for_one: bool = False) -> list[list[CanData]]:
return [next(_can_msgs, [])]
_, fingerprint = can_fingerprint(can_recv)
CarInterface = interfaces[platform]
CP = CarInterface.get_params(platform, fingerprint, [], False, False, False)
@@ -69,35 +78,39 @@ def replay_segment(platform, can_msgs):
states.append(CI.update([(msg.logMonoTime, frames)]))
CI.apply(CC, msg.logMonoTime)
timestamps.append(msg.logMonoTime)
return states, timestamps
return CP, states, timestamps
def process_segment(args):
def process_segment(args: tuple) -> Result:
platform, seg, ref_path, update = args
try:
can_msgs = load_can_messages(seg)
states, timestamps = replay_segment(platform, can_msgs)
CP, states, timestamps = replay_segment(platform, can_msgs)
ref_file = Path(ref_path) / f"{platform}_{seg.replace('/', '_')}.zst"
if update:
data = list(zip(timestamps, states, strict=True))
data = {"cp": CP.to_dict(), "frames": list(zip(timestamps, states, strict=True))}
ref_file.write_bytes(zstd.compress(pickle.dumps(data), 10))
return (platform, seg, [], None)
return (platform, seg, [], None, None, None)
if not ref_file.exists():
return (platform, seg, [], "no ref")
return (platform, seg, [], None, None, "no ref")
ref = pickle.loads(decompress_stream(ref_file.read_bytes()))
ref_data = pickle.loads(decompress_stream(ref_file.read_bytes()))
cp: dict[str, Any] = ref_data["cp"]
ref: list[Ref] = ref_data["frames"]
diffs = []
for diff in dict_diff(cp, CP.to_dict(), path="carParams", ignore=IGNORE_FIELDS, tolerance=TOLERANCE):
diffs.append((diff[1], -1, diff[2], 0))
for i, ((ts, ref_state), state) in enumerate(zip(ref, states, strict=True)):
for diff in dict_diff(ref_state.to_dict(), state.to_dict(), ignore=IGNORE_FIELDS, tolerance=TOLERANCE):
diffs.append((diff[1], i, diff[2], ts))
return (platform, seg, diffs, None)
except Exception as e:
return (platform, seg, [], str(e))
return (platform, seg, diffs, ref, states, None)
except Exception:
return (platform, seg, [], None, None, traceback.format_exc())
def get_changed_platforms(cwd, database, interfaces):
def get_changed_platforms(cwd: Path, database: dict[str, Any], interfaces: dict[str, Any]) -> list[str]:
git_ref = os.environ.get("GIT_REF", "origin/master")
changed = subprocess.check_output(["git", "diff", "--name-only", f"{git_ref}...HEAD"], cwd=cwd, encoding='utf8').strip()
brands = set()
@@ -110,30 +123,26 @@ def get_changed_platforms(cwd, database, interfaces):
return [p for p in interfaces if any(b in p.lower() for b in brands) and p in database]
def download_refs(ref_path, platforms, segments):
def download_refs(ref_path: Path, platforms: list[str], segments: dict[str, list[str]]) -> None:
base_url = f"https://raw.githubusercontent.com/commaai/ci-artifacts/refs/heads/{DIFF_BUCKET}"
for platform in platforms:
for platform in tqdm(platforms):
for seg in segments.get(platform, []):
filename = f"{platform}_{seg.replace('/', '_')}.zst"
try:
with urlopen(f"{base_url}/{filename}") as resp:
(Path(ref_path) / filename).write_bytes(resp.read())
except Exception:
pass
with urlopen(f"{base_url}/{filename}") as resp:
(Path(ref_path) / filename).write_bytes(resp.read())
def run_replay(platforms, segments, ref_path, update, workers=4):
def run_replay(platforms: list[str], segments: dict[str, list[str]], ref_path: Path, update: bool, workers: int = 4) -> list[Result]:
work = [(platform, seg, ref_path, update)
for platform in platforms for seg in segments.get(platform, [])]
with ProcessPoolExecutor(max_workers=workers) as pool:
return list(pool.map(process_segment, work))
return process_map(process_segment, work, max_workers=workers)
# ASCII waveforms helpers
def find_edges(vals, init):
def find_edges(vals: list[bool]) -> tuple[list[int], list[int]]:
rises = []
falls = []
prev = init
prev = vals[0]
for i, val in enumerate(vals):
if val and not prev:
rises.append(i)
@@ -143,10 +152,10 @@ def find_edges(vals, init):
return rises, falls
def render_waveform(label, vals, init):
def render_waveform(label: str, vals: list[bool]) -> str:
wave = {(False, False): "_", (True, True): "", (False, True): "/", (True, False): "\\"}
line = f" {label}:".ljust(12)
prev = init
prev = vals[0]
for val in vals:
line += wave[(prev, val)]
prev = val
@@ -155,7 +164,7 @@ def render_waveform(label, vals, init):
return line
def format_timing(edge_type, master_edges, pr_edges, ms_per_frame):
def format_timing(edge_type: str, master_edges: list[int], pr_edges: list[int], ms_per_frame: float) -> str | None:
if not master_edges or not pr_edges:
return None
delta = pr_edges[0] - master_edges[0]
@@ -166,7 +175,7 @@ def format_timing(edge_type, master_edges, pr_edges, ms_per_frame):
return " " * 12 + f"{edge_type}: PR {direction} by {abs(delta)} frames ({ms}ms)"
def group_frames(diffs, max_gap=15):
def group_frames(diffs: list[Diff], max_gap: int = 15) -> list[list[Diff]]:
groups = []
current = [diffs[0]]
for diff in diffs[1:]:
@@ -181,28 +190,25 @@ def group_frames(diffs, max_gap=15):
return groups
def build_signals(group):
def build_signals(group: list[Diff], ref: list[Ref], states: list[structs.CarState], field: str) -> tuple[list[Any], list[Any], int, int]:
_, first_frame, _, _ = group[0]
_, last_frame, (final_master, _), _ = group[-1]
start = max(0, first_frame - 5)
end = last_frame + 6
init = not final_master
diff_at = {frame: (m, p) for _, frame, (m, p), _ in group}
_, last_frame, _, _ = group[-1]
start = max(0, first_frame - PADDING)
end = min(last_frame + PADDING + 1, len(ref))
master_vals = []
pr_vals = []
master = init
pr = init
for frame in range(start, end):
if frame in diff_at:
master, pr = diff_at[frame]
elif frame > last_frame:
master = pr = final_master
master_vals.append(master)
pr_vals.append(pr)
return master_vals, pr_vals, init, start, end
mval = ref[frame][1].to_dict()
pval = states[frame].to_dict()
for k in field.split("."):
mval = mval.get(k) if isinstance(mval, dict) else None
pval = pval.get(k) if isinstance(pval, dict) else None
master_vals.append(mval)
pr_vals.append(pval)
return master_vals, pr_vals, start, end
def format_numeric_diffs(diffs):
def format_numeric_diffs(diffs: list[Diff]) -> list[str]:
lines = []
for _, frame, (old_val, new_val), _ in diffs[:10]:
lines.append(f" frame {frame}: {old_val} -> {new_val}")
@@ -211,7 +217,7 @@ def format_numeric_diffs(diffs):
return lines
def format_boolean_diffs(diffs):
def format_boolean_diffs(diffs: list[Diff], ref: list[Ref], states: list[structs.CarState], field: str) -> list[str]:
_, first_frame, _, first_ts = diffs[0]
_, last_frame, _, last_ts = diffs[-1]
frame_time = last_frame - first_frame
@@ -219,14 +225,12 @@ def format_boolean_diffs(diffs):
ms = time_ms / frame_time if frame_time else 10.0
lines = []
for group in group_frames(diffs):
master_vals, pr_vals, init, start, end = build_signals(group)
master_rises, master_falls = find_edges(master_vals, init)
pr_rises, pr_falls = find_edges(pr_vals, init)
if bool(master_rises) != bool(pr_rises) or bool(master_falls) != bool(pr_falls):
continue
master_vals, pr_vals, start, end = build_signals(group, ref, states, field)
master_rises, master_falls = find_edges(master_vals)
pr_rises, pr_falls = find_edges(pr_vals)
lines.append(f"\n frames {start}-{end - 1}")
lines.append(render_waveform("master", master_vals, init))
lines.append(render_waveform("PR", pr_vals, init))
lines.append(render_waveform("master", master_vals))
lines.append(render_waveform("PR", pr_vals))
for edge_type, master_edges, pr_edges in [("rise", master_rises, pr_rises), ("fall", master_falls, pr_falls)]:
msg = format_timing(edge_type, master_edges, pr_edges, ms)
if msg:
@@ -234,19 +238,18 @@ def format_boolean_diffs(diffs):
return lines
def format_diff(diffs):
def format_diff(diffs: list[Diff], ref: list[Ref], states: list[structs.CarState], field: str) -> list[str]:
if not diffs:
return []
_, _, (old, new), _ = diffs[0]
is_bool = isinstance(old, bool) and isinstance(new, bool)
if is_bool:
return format_boolean_diffs(diffs)
return format_boolean_diffs(diffs, ref, states, field)
return format_numeric_diffs(diffs)
def main(platform=None, segments_per_platform=10, update_refs=False, all_platforms=False):
from opendbc.car.car_helpers import interfaces
def main(platform: str | None = None, segments_per_platform: int = 10, update_refs: bool = False, all_platforms: bool = False) -> int:
from comma_car_segments import get_comma_car_segments_database
cwd = Path(__file__).resolve().parents[3]
ref_path = cwd / DIFF_BUCKET
if not update_refs:
@@ -262,8 +265,12 @@ def main(platform=None, segments_per_platform=10, update_refs=False, all_platfor
else:
platforms = get_changed_platforms(cwd, database, interfaces)
print("## Car behavior report")
print("Replays driving segments through this PR and compares the behavior to master.")
print("Please review any changes carefully to ensure they are expected.\n")
if not platforms:
print("No car changes detected", file=sys.stderr)
print("No changes detected")
return 0
segments = {p: database.get(p, [])[:segments_per_platform] for p in platforms}
@@ -272,35 +279,36 @@ def main(platform=None, segments_per_platform=10, update_refs=False, all_platfor
if update_refs:
results = run_replay(platforms, segments, ref_path, update=True)
errors = [e for _, _, _, e in results if e]
errors = [e for _, _, _, _, _, e in results if e]
assert len(errors) == 0, f"Segment failures: {errors}"
print(f"Generated {n_segments} refs to {ref_path}")
return 0
download_refs(ref_path, platforms, segments)
results = run_replay(platforms, segments, ref_path, update=False)
with_diffs = [(p, s, d) for p, s, d, e in results if d]
errors = [(p, s, e) for p, s, d, e in results if e]
with_diffs = [(platform, seg, diffs, ref, states)
for platform, seg, diffs, ref, states, err in results if diffs]
errors = [(platform, seg, err) for platform, seg, diffs, ref, states, err in results if err]
n_passed = len(results) - len(with_diffs) - len(errors)
print(f"\nResults: {n_passed} passed, {len(with_diffs)} with diffs, {len(errors)} errors")
icon = "⚠️" if with_diffs else ""
print(f"\n{icon} {len(with_diffs)} changed, {n_passed} passed, {len(errors)} errors")
for plat, seg, err in errors:
print(f"\nERROR {plat} - {seg}: {err}")
if with_diffs:
print("```")
for plat, seg, diffs in with_diffs:
print("<details><summary><b>Show changes</b></summary>\n\n```")
for plat, seg, diffs, ref, states in with_diffs:
print(f"\n{plat} - {seg}")
by_field = defaultdict(list)
for d in diffs:
by_field[d[0]].append(d)
for field, fd in sorted(by_field.items()):
print(f" {field} ({len(fd)} diffs)")
for line in format_diff(fd):
print(f"\n {field} ({len(fd)} diffs)")
for line in format_diff(fd, ref, states, field):
print(line)
print("```")
print("```\n</details>")
return 1 if errors else 0

View File

@@ -140,6 +140,7 @@ routes = [
CarTestRoute("f29e2b57a55e7ad5/2021-03-24--20-52-38", HONDA.HONDA_ACCORD), # hybrid, 2021 with new style HUD msgs
CarTestRoute("1ad763dd22ef1a0e/2020-02-29--18-37-03", HONDA.HONDA_CRV_5G),
CarTestRoute("0a96f86fcfe35964/2020-02-05--07-25-51", HONDA.HONDA_ODYSSEY),
CarTestRoute("7817fe954aff07b8/00000001--fdaaf36c4f", HONDA.HONDA_ODYSSEY_TWN),
CarTestRoute("d7233a428eb7d0b5/00000001--9b99b04d43", HONDA.HONDA_ODYSSEY_5G_MMR),
CarTestRoute("d83f36766f8012a5/2020-02-05--18-42-21", HONDA.HONDA_CIVIC_BOSCH_DIESEL),
CarTestRoute("f0890d16a07a236b/2021-05-25--17-27-22", HONDA.HONDA_INSIGHT),
@@ -157,6 +158,7 @@ routes = [
# CarTestRoute("56b2cf1dacdcd033/00000017--d24ffdb376", HONDA.HONDA_CITY_7G), # Brazilian model
CarTestRoute("2dc4489d7e1410ca/00000001--bbec3f5117", HONDA.HONDA_CRV_6G),
CarTestRoute("a703d058f4e05aeb/00000008--f169423024", HONDA.HONDA_PASSPORT_4G),
CarTestRoute("ad9840558640c31d/000001f2--026c4f6275", HONDA.ACURA_TLX_2G_MMR),
CarTestRoute("87d7f06ade479c2e/2023-09-11--23-30-11", HYUNDAI.HYUNDAI_AZERA_6TH_GEN),
CarTestRoute("66189dd8ec7b50e6/2023-09-20--07-02-12", HYUNDAI.HYUNDAI_AZERA_HEV_6TH_GEN),

View File

@@ -2,6 +2,7 @@ import os
import math
import hypothesis.strategies as st
import pytest
from functools import cache
from hypothesis import Phase, given, settings
from collections.abc import Callable
from typing import Any
@@ -27,7 +28,8 @@ DLC_TO_LEN = [0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64]
MAX_EXAMPLES = int(os.environ.get('MAX_EXAMPLES', '15'))
def get_fuzzy_car_interface(car_name: str, draw: DrawType) -> CarInterfaceBase:
@cache
def get_fuzzy_strategy():
# Fuzzy CAN fingerprints and FW versions to test more states of the CarInterface
fingerprint_strategy = st.fixed_dictionaries({0: st.dictionaries(st.integers(min_value=0, max_value=0x800),
st.sampled_from(DLC_TO_LEN))})
@@ -44,8 +46,11 @@ def get_fuzzy_car_interface(car_name: str, draw: DrawType) -> CarInterfaceBase:
'car_fw': car_fw_strategy,
'alpha_long': st.booleans(),
})
return params_strategy
params: dict = draw(params_strategy)
def get_fuzzy_car_interface(car_name: str, draw: DrawType) -> CarInterfaceBase:
params: dict = draw(get_fuzzy_strategy())
# reduce search space by duplicating CAN fingerprints across all buses
params['fingerprints'] |= {key + 1: params['fingerprints'][0] for key in range(6)}

View File

@@ -52,7 +52,7 @@ class TestCarDocs:
for car in self.all_cars:
with subtests.test(car=car.name):
# honda sanity check, it's the definition of a no torque star
if car.car_fingerprint in (HONDA.HONDA_ACCORD, HONDA.HONDA_CIVIC, HONDA.HONDA_CRV, HONDA.HONDA_ODYSSEY, HONDA.HONDA_PILOT):
if car.car_fingerprint in (HONDA.HONDA_ACCORD, HONDA.HONDA_CIVIC, HONDA.HONDA_CRV, HONDA.HONDA_ODYSSEY, HONDA.HONDA_ODYSSEY_TWN, HONDA.HONDA_PILOT):
assert car.row[Column.STEERING_TORQUE] == Star.EMPTY, f"{car.name} has full torque star"
elif car.brand in ("toyota", "hyundai"):
assert car.row[Column.STEERING_TORQUE] != Star.EMPTY, f"{car.name} has no torque star"

View File

@@ -101,7 +101,11 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"HONDA_ODYSSEY_5G_MMR" = [0.9, 0.9, 0.2]
"HONDA_NBOX_2G" = [1.2, 1.2, 0.2]
"ACURA_TLX_2G" = [1.2, 1.2, 0.15]
"ACURA_TLX_2G_MMR" = [1.7, 1.7, 0.16]
"PORSCHE_MACAN_MK1" = [2.0, 2.0, 0.2]
"LEXUS_LS" = [1.35, 1.7, 0.17]
"TOYOTA_RAV4_PRIME" = [1.7, 2.0, 0.14]
"TOYOTA_RAV4_TSS2_2022" = [1.9, 1.9304407208090029, 0.112174]
# Dashcam or fallback configured as ideal car
"MOCK" = [10.0, 10, 0.0]

View File

@@ -69,9 +69,8 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"TOYOTA_PRIUS" = [1.60, 1.5023147650693636, 0.151515]
"TOYOTA_PRIUS_TSS2" = [1.972600, 1.9104337425537743, 0.170968]
"TOYOTA_RAV4" = [2.085695074355425, 2.2142832316984733, 0.13339165270103975]
"TOYOTA_RAV4_TSS2" = [2.279239424615458, 2.087101966779332, 0.13682208413446817]
"TOYOTA_RAV4_TSS2" = [1.9557514786720276, 2.087101966779332, 0.12075843289494514]
"TOYOTA_RAV4H" = [1.9796257271652042, 1.7503987331707576, 0.14628860048885406]
"TOYOTA_RAV4_TSS2_2022" = [2.241883248393209, 1.9304407208090029, 0.112174]
"TOYOTA_SIENNA" = [1.689726, 1.3208264576110418, 0.140456]
"TOYOTA_YARIS" = [2.22984, 1.86145, 0.168189]
"VOLKSWAGEN_ARTEON_MK1" = [1.45136518053819, 1.3639364049316804, 0.23806361745695032]

View File

@@ -9,15 +9,13 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"TOYOTA_ALPHARD_TSS2" = "TOYOTA_SIENNA"
"TOYOTA_PRIUS_V" = "TOYOTA_PRIUS"
"TOYOTA_RAV4_PRIME" = "TOYOTA_RAV4_TSS2"
"TOYOTA_SIENNA_4TH_GEN" = "TOYOTA_RAV4_TSS2"
"TOYOTA_SIENNA_4TH_GEN" = "TOYOTA_RAV4_PRIME"
"LEXUS_IS" = "LEXUS_NX"
"LEXUS_CTH" = "LEXUS_NX"
"LEXUS_ES" = "TOYOTA_CAMRY"
"LEXUS_RC" = "LEXUS_NX_TSS2"
"LEXUS_RC_TSS2" = "LEXUS_NX_TSS2"
"LEXUS_LC_TSS2" = "LEXUS_NX_TSS2"
"LEXUS_LS" = "LEXUS_NX"
"KIA_OPTIMA_G4" = "HYUNDAI_SONATA"
"KIA_OPTIMA_G4_FL" = "HYUNDAI_SONATA"
@@ -52,6 +50,7 @@ legend = ["LAT_ACCEL_FACTOR", "MAX_LAT_ACCEL_MEASURED", "FRICTION"]
"HONDA_CRV_EU" = "HONDA_CRV"
"HONDA_CIVIC_BOSCH_DIESEL" = "HONDA_CIVIC_BOSCH"
"HONDA_E" = "HONDA_CIVIC_BOSCH"
"HONDA_ODYSSEY_TWN" = "HONDA_ODYSSEY"
"BUICK_LACROSSE" = "CHEVROLET_VOLT"
"BUICK_REGAL" = "CHEVROLET_VOLT"

View File

@@ -31,7 +31,7 @@ MAX_PITCH_COMPENSATION = 1.5 # m/s^2
# LKA limits
# EPS faults if you apply torque while the steering rate is above 100 deg/s for too long
MAX_STEER_RATE = 100 # deg/s
MAX_STEER_RATE_FRAMES = 18 # tx control frames needed before torque can be cut
MAX_STEER_RATE_FRAMES = 17 # tx control frames needed before torque can be cut
# EPS allows user torque above threshold for 50 frames before permanently faulting
MAX_USER_TORQUE = 500

View File

@@ -1442,21 +1442,26 @@ FW_VERSIONS = {
CAR.LEXUS_GS_F: {
(Ecu.engine, 0x7e0, None): [
b'\x0233075200\x00\x00\x00\x00\x00\x00\x00\x00530B9000\x00\x00\x00\x00\x00\x00\x00\x00',
b'\x02330Y8000\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00',
],
(Ecu.abs, 0x7b0, None): [
b'F152630700\x00\x00\x00\x00\x00\x00',
b'F152630400\x00\x00\x00\x00\x00\x00',
],
(Ecu.dsu, 0x791, None): [
b'881513016200\x00\x00\x00\x00',
b'881513011300\x00\x00\x00\x00',
],
(Ecu.eps, 0x7a1, None): [
b'8965B30551\x00\x00\x00\x00\x00\x00',
b'8965B30431\x00\x00\x00\x00\x00\x00',
],
(Ecu.fwdRadar, 0x750, 0xf): [
b'8821F4702000\x00\x00\x00\x00',
],
(Ecu.fwdCamera, 0x750, 0x6d): [
b'8646F3002100\x00\x00\x00\x00',
b'8646F3001200\x00\x00\x00\x00',
],
},
CAR.LEXUS_NX: {

View File

@@ -76,24 +76,7 @@ class CarInterface(CarInterfaceBase):
# https://engage.toyota.com/static/images/toyota_safety_sense/TSS_Applicability_Chart.pdf
stop_and_go = candidate != CAR.TOYOTA_AVALON
elif candidate in (CAR.TOYOTA_RAV4_TSS2, CAR.TOYOTA_RAV4_TSS2_2022, CAR.TOYOTA_RAV4_TSS2_2023, CAR.TOYOTA_RAV4_PRIME, CAR.TOYOTA_SIENNA_4TH_GEN):
ret.lateralTuning.init('pid')
ret.lateralTuning.pid.kiBP = [0.0]
ret.lateralTuning.pid.kpBP = [0.0]
ret.lateralTuning.pid.kpV = [0.6]
ret.lateralTuning.pid.kiV = [0.1]
ret.lateralTuning.pid.kf = 0.00007818594
# 2019+ RAV4 TSS2 uses two different steering racks and specific tuning seems to be necessary.
# See https://github.com/commaai/openpilot/pull/21429#issuecomment-873652891
for fw in car_fw:
if fw.ecu == "eps" and (fw.fwVersion.startswith(b'\x02') or fw.fwVersion in [b'8965B42181\x00\x00\x00\x00\x00\x00']):
ret.lateralTuning.pid.kpV = [0.15]
ret.lateralTuning.pid.kiV = [0.05]
ret.lateralTuning.pid.kf = 0.00004
break
elif candidate in (CAR.TOYOTA_CHR, CAR.TOYOTA_CAMRY, CAR.TOYOTA_SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_NX):
elif candidate in (CAR.TOYOTA_CHR, CAR.TOYOTA_CAMRY, CAR.TOYOTA_SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_LS, CAR.LEXUS_NX):
# TODO: Some of these platforms are not advertised to have full range ACC, do they really all have sng?
stop_and_go = True

View File

@@ -219,7 +219,7 @@ def dyn_ss_sol(sa: float, u: float, roll: float, VM: VehicleModel) -> np.ndarray
"""
A, B = create_dyn_state_matrices(u, VM)
inp = np.array([[sa], [roll]])
return -solve(A, B) @ inp # type: ignore
return -solve(A, B) @ inp
def calc_slip_factor(VM: VehicleModel) -> float:

View File

@@ -1128,11 +1128,13 @@ FW_VERSIONS = {
b'\xf1\x8795B906259BM\xf1\x890001',
b'\xf1\x8795B90652013\xf1\x893485',
b'\xf1\x8795B90654002\xf1\x893495',
b'\xf1\x8795B907551AD\xf1\x890001',
b'\xf1\x8795B907551D \xf1\x890006',
],
(Ecu.transmission, 0x7e1, None): [
b'\xf1\x8795B927156CE\xf1\x890022',
b'\xf1\x8795B927156JH\xf1\x890001',
b'\xf1\x8795B927156KD\xf1\x890001',
b'\xf1\x8795B927156KK\xf1\x890001',
b'\xf1\x8795B927156KP\xf1\x890001',
b'\xf1\x8795B927156R \xf1\x890021',
@@ -1141,6 +1143,7 @@ FW_VERSIONS = {
b'\xf1\x8795B959655F \xf1\x890130\xf1\x82\x05065Q033513',
b'\xf1\x8795B959655F \xf1\x890130\xf1\x82\x050682033514',
b'\xf1\x8795B959655G \xf1\x890150\xf1\x82\x0506B1033514',
b'\xf1\x8795B959655G \xf1\x890150\xf1\x82\x0506CG033517',
b'\xf1\x8795B959655G \xf1\x890150\xf1\x82\x0506CJ02D417',
],
(Ecu.eps, 0x712, None): [
@@ -1150,6 +1153,7 @@ FW_VERSIONS = {
(Ecu.fwdRadar, 0x757, None): [
b'\xf1\x8795B907567B\x00\xf1\x890800\xf1\x82108',
b'\xf1\x8795B907567G\x00\xf1\x890410\xf1\x82104',
b'\xf1\x8795B907567H\x00\xf1\x890430\xf1\x82104',
b'\xf1\x8795B907567J \xf1\x890440\xf1\x82104',
],
},

View File

@@ -85,6 +85,13 @@ BO_ 490 VEHICLE_DYNAMICS: 8 VSA
SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON
SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON
BO_ 547 BRAKE_HOLD_HYBRID_ALT: 6 XXX
SG_ BRAKE_HOLD_FAULT_BIT : 33|1@0+ (1,0) [0|1] "" XXX
SG_ BRAKE_HOLD_ENABLED : 37|2@0+ (1,0) [0|7] "" XXX
SG_ BRAKE_HOLD_ACTIVE : 38|1@0+ (1,0) [0|1] "" XXX
SG_ CHECKSUM : 43|4@0+ (1,0) [0|15] "" XXX
SG_ COUNTER : 45|2@0+ (1,0) [0|3] "" XXX
BO_ 597 ROUGH_WHEEL_SPEED: 8 VSA
SG_ WHEEL_SPEED_FL : 7|8@0+ (1,0) [0|255] "kph" EON
SG_ WHEEL_SPEED_FR : 15|8@0+ (1,0) [0|255] "kph" EON

View File

@@ -35,13 +35,6 @@ BO_ 506 BRAKE_COMMAND: 8 ADAS
SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EBCM
SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EBCM
BO_ 547 BRAKE_HOLD_HYBRID_ALT: 6 XXX
SG_ BRAKE_HOLD_FAULT_BIT : 33|1@0+ (1,0) [0|1] "" XXX
SG_ BRAKE_HOLD_ENABLED : 37|2@0+ (1,0) [0|7] "" XXX
SG_ BRAKE_HOLD_ACTIVE : 38|1@0+ (1,0) [0|1] "" XXX
SG_ CHECKSUM : 43|4@0+ (1,0) [0|15] "" XXX
SG_ COUNTER : 45|2@0+ (1,0) [0|3] "" XXX
BO_ 892 CRUISE_PARAMS: 8 PCM
SG_ CRUISE_SPEED_OFFSET : 31|8@0- (0.1,0) [-128|127] "kph" EON
SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON

View File

@@ -0,0 +1,17 @@
BO_ 404 STEERING_CONTROL: 4 EON
SG_ SET_ME_X00 : 22|7@0+ (1,0) [0|127] "" EPS
SG_ STEER_TORQUE_REQUEST : 23|1@0+ (1,0) [0|1] "" EPS
SG_ COUNTER : 29|2@0+ (1,0) [0|15] "" EPS
SG_ CHECKSUM : 27|4@0+ (1,0) [0|3] "" EPS
SG_ STEER_TORQUE : 7|16@0- (-1,0) [-32767|32767] "" EPS
BO_ 399 STEER_STATUS: 7 EPS
SG_ STEER_TORQUE_SENSOR : 7|16@0- (-1,0) [-2985|2985] "tbd" EON
SG_ STEER_ANGLE_RATE : 23|16@0- (-0.1,0) [-31000|31000] "deg/s" EON
SG_ STEER_STATUS : 43|4@0+ (1,0) [0|15] "" EON
SG_ STEER_CONTROL_ACTIVE : 35|1@0+ (1,0) [0|1] "" EON
SG_ STEER_CONFIG_INDEX : 43|4@0+ (1,0) [0|15] "" EON
SG_ CHECKSUM : 51|4@0+ (1,0) [0|15] "" EON
SG_ COUNTER : 53|2@0+ (1,0) [0|3] "" EON
VAL_ 399 STEER_STATUS 7 "permanent_fault" 6 "tmp_fault" 5 "fault_1" 4 "no_torque_alert_2" 3 "low_speed_lockout" 2 "no_torque_alert_1" 1 "driver_steering" 0 "normal" ;

View File

@@ -0,0 +1,7 @@
CM_ "steer_angle_rate is negative vs _steering_sensors_b.dbc";
BO_ 342 STEERING_SENSORS: 6 EPS
SG_ STEER_ANGLE : 7|16@0- (-0.1,0) [-500|500] "deg" EON
SG_ STEER_ANGLE_RATE : 23|16@0- (1,0) [-3000|3000] "deg/s" EON
SG_ COUNTER : 45|2@0+ (1,0) [0|3] "" EON
SG_ CHECKSUM : 43|4@0+ (1,0) [0|15] "" EON

View File

@@ -0,0 +1,7 @@
CM_ "IMPORT _honda_common.dbc";
CM_ "IMPORT _nidec_common.dbc";
CM_ "IMPORT _lkas_hud_5byte.dbc";
CM_ "IMPORT _nidec_scm_group_a.dbc";
CM_ "IMPORT _steering_sensors_c.dbc";
CM_ "IMPORT _steering_control_c.dbc";
CM_ "IMPORT _gearbox_common.dbc";

View File

@@ -45,8 +45,8 @@ BO_ 64 Throttle: 8 XXX
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
SG_ Signal1 : 12|4@1+ (1,0) [0|15] "" XXX
SG_ Engine_RPM : 16|12@1+ (1,0) [0|4095] "" XXX
SG_ Signal2 : 28|4@1+ (1,0) [0|15] "" XXX
SG_ Engine_RPM : 16|13@1+ (1,0) [0|8191] "" XXX
SG_ Neutral : 31|1@1+ (1,0) [0|1] "" XXX
SG_ Throttle_Pedal : 32|8@1+ (1,0) [0|255] "" XXX
SG_ Throttle_Cruise : 40|8@1+ (1,0) [0|255] "" XXX
SG_ Throttle_Combo : 48|8@1+ (1,0) [0|255] "" XXX
@@ -110,6 +110,7 @@ BO_ 282 Steering_2: 8 XXX
BO_ 312 Brake_Pressure_L_R: 8 XXX
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
SG_ Steering_Angle : 16|16@1- (-0.1,0) [-3276.8|3276.7] "" XXX
SG_ Brake_1 : 48|8@1+ (1,0) [0|255] "" XXX
SG_ Brake_2 : 56|8@1+ (1,0) [0|255] "" XXX
@@ -158,6 +159,7 @@ BO_ 544 ES_Brake: 8 XXX
BO_ 577 Cruise_Status: 8 XXX
SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" XXX
SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" XXX
SG_ Clutch_Depressed : 47|1@0+ (1,0) [0|1] "" XXX
SG_ Cruise_Set_Speed : 51|12@0+ (1,0) [0|120] "" XXX
SG_ Cruise_On : 54|1@1+ (1,0) [0|1] "" XXX
SG_ Cruise_Activated : 55|1@1+ (1,0) [0|1] "" XXX
@@ -211,8 +213,8 @@ BO_ 801 ES_DashStatus: 8 XXX
SG_ Signal4 : 31|1@1+ (1,0) [0|1] "" XXX
SG_ Conventional_Cruise : 32|1@1+ (1,0) [0|1] "" XXX
SG_ Signal5 : 33|2@1+ (1,0) [0|3] "" XXX
SG_ Cruise_Disengaged : 35|1@1+ (1,0) [0|1] "" XXX
SG_ Cruise_Activated : 36|1@1+ (1,0) [0|1] "" XXX
SG_ Cruise_Disengaged_Dash : 35|1@1+ (1,0) [0|1] "" XXX
SG_ Cruise_Activated_Dash : 36|1@1+ (1,0) [0|1] "" XXX
SG_ Signal6 : 37|3@1+ (1,0) [0|1] "" XXX
SG_ Cruise_Set_Speed : 40|8@1+ (1,0) [0|255] "" XXX
SG_ Cruise_Fault : 48|1@1+ (1,0) [0|1] "" XXX
@@ -283,6 +285,7 @@ CM_ SG_ 544 Cruise_Brake_Lights "1 = switch on brake lights";
CM_ SG_ 544 Brake_Pressure "Winds down after cruise disabled. Also can be non-zero when likely preparing for AEB";
CM_ SG_ 544 Signal3 "Usually goes to 2 if AEB_Status is 4";
CM_ SG_ 544 AEB_Status "Occasionally is 4 instead of 8 while Brake_Pressure is non-zero, unsure why";
CM_ SG_ 801 Cruise_Activated_Dash "Falls during user gas or standstill. This is likely only a UI signal to the dash to show if ACC is actually actuating";
CM_ SG_ 801 PCB_Off "Pre-Collision Braking off";
CM_ SG_ 801 Brake_Lights "Driver or Cruise brake on";
CM_ SG_ 801 Cruise_State "0 = Normal, 1 = Hold+User Brake, 2 = Ready, 3 = Hold";

View File

@@ -3,7 +3,7 @@ CM_ "IMPORT _subaru_preglobal_2015.dbc";
BO_ 355 ES_DashStatus: 8 XXX
SG_ Not_Ready_Startup : 4|2@1+ (1,0) [0|3] "" XXX
SG_ Cruise_On : 16|1@1+ (1,0) [0|1] "" XXX
SG_ Cruise_Activated : 17|1@0+ (1,0) [0|1] "" XXX
SG_ Cruise_Activated_Dash : 17|1@0+ (1,0) [0|1] "" XXX
SG_ Cruise_Set_Speed : 24|8@1+ (1,0) [0|255] "" XXX
SG_ COUNTER : 40|3@1+ (1,0) [0|7] "" XXX
SG_ Brake : 43|1@1+ (1,0) [0|1] "" XXX

View File

@@ -5,7 +5,7 @@ BO_ 358 ES_DashStatus: 8 XXX
SG_ Seatbelt_Disengage : 12|2@1+ (1,0) [0|3] "" XXX
SG_ Disengage_Alert : 14|2@1+ (1,0) [0|3] "" XXX
SG_ Cruise_On : 16|1@1+ (1,0) [0|1] "" XXX
SG_ Cruise_Activated : 17|1@1+ (1,0) [0|1] "" XXX
SG_ Cruise_Activated_Dash : 17|1@1+ (1,0) [0|1] "" XXX
SG_ Signal1 : 18|1@1+ (1,0) [0|1] "" XXX
SG_ WHEELS_MOVING_2015 : 19|1@1+ (1,0) [0|1] "" XXX
SG_ Driver_Input : 20|1@1+ (1,0) [0|1] "" XXX

View File

@@ -5,7 +5,7 @@ BO_ 358 ES_DashStatus: 8 XXX
SG_ Seatbelt_Disengage : 12|2@1+ (1,0) [0|3] "" XXX
SG_ Disengage_Alert : 14|2@1+ (1,0) [0|3] "" XXX
SG_ Cruise_On : 16|1@1+ (1,0) [0|1] "" XXX
SG_ Cruise_Activated : 17|1@1+ (1,0) [0|1] "" XXX
SG_ Cruise_Activated_Dash : 17|1@1+ (1,0) [0|1] "" XXX
SG_ Signal1 : 18|1@1+ (1,0) [0|1] "" XXX
SG_ WHEELS_MOVING_2015 : 19|1@1+ (1,0) [0|1] "" XXX
SG_ Driver_Input : 20|1@1+ (1,0) [0|1] "" XXX

View File

@@ -23,8 +23,9 @@ BO_ 147 IMPRECISE_SPEED_INFORMATION: 8 XXX
BO_ 161 STEER_AND_AP_STALK: 8 XXX
SG_ CRC : 7|8@0+ (1,0) [0|255] "" XXX
SG_ STEERING_ANGLE : 15|16@0+ (0.05,0) [0|65535] "degs" XXX
SG_ STEERING_TORQUE : 31|16@0+ (1,0) [0|65535] "??" XXX
SG_ STEERING_ANGLE : 13|13@0+ (0.05,0) [0|65535] "degs" XXX
SG_ STEERING_DIRECTION : 16|1@0+ (1,0) [0|1] "" XXX
SG_ STEERING_TORQUE : 29|14@0+ (1,0) [0|65535] "??" XXX
SG_ AP_REDUCE_DISTANCE_COMMAND : 44|1@0+ (1,0) [0|1] "" XXX
SG_ AP_INCREASE_DISTANCE_COMMAND : 45|1@0+ (1,0) [0|1] "AUTOPILOT_STALK" XXX
SG_ AP_CANCEL_COMMAND : 46|1@0+ (1,0) [0|1] "" XXX

View File

@@ -37,9 +37,9 @@
#define MSG_SUBARU_ES_UDS_Request 0x787U
#define MSG_SUBARU_ES_HighBeamAssist 0x121U
#define MSG_SUBARU_ES_STATIC_1 0x22aU
#define MSG_SUBARU_ES_STATIC_2 0x325U
#define MSG_SUBARU_ES_HighBeamAssist 0x22AU
#define MSG_SUBARU_ES_STATIC_1 0x325U
#define MSG_SUBARU_ES_STATIC_2 0x121U
#define SUBARU_MAIN_BUS 0U
#define SUBARU_ALT_BUS 1U
@@ -105,11 +105,6 @@ static void subaru_rx_hook(const CANPacket_t *msg) {
torque_driver_new = ((GET_BYTES(msg, 0, 4) >> 16) & 0x7FFU);
torque_driver_new = -1 * to_signed(torque_driver_new, 11);
update_sample(&torque_driver, torque_driver_new);
int angle_meas_new = (GET_BYTES(msg, 4, 2) & 0xFFFFU);
// convert Steering_Torque -> Steering_Angle to centidegrees, to match the ES_LKAS_ANGLE angle request units
angle_meas_new = ROUND(to_signed(angle_meas_new, 16) * -2.17);
update_sample(&angle_meas, angle_meas_new);
}
if ((msg->addr == MSG_SUBARU_ES_LKAS_State) && (msg->bus == SUBARU_CAM_BUS)) {

View File

@@ -209,9 +209,9 @@ static bool toyota_tx_hook(const CANPacket_t *msg) {
// the EPS faults when the steering angle rate is above a certain threshold for too long. to prevent this,
// we allow setting STEER_REQUEST bit to 0 while maintaining the requested torque value for a single frame
.min_valid_request_frames = 18,
.min_valid_request_frames = 17,
.max_invalid_request_frames = 1,
.min_valid_request_rt_interval = 171000, // 171ms; a ~10% buffer on cutting every 19 frames
.min_valid_request_rt_interval = 162000, // 162ms; a ~10% buffer on cutting every 18 frames
.has_steer_req_tolerance = true,
};

View File

@@ -9,7 +9,7 @@
2.6 X (Cppcheck)
2.7 X (Addon)
3.1 X (Addon)
3.2 X (Addon)
3.2
4.1 X (Addon)
4.2 X (Addon)
5.1 X (Addon)

View File

@@ -15,7 +15,7 @@ fi
cd $CPPCHECK_DIR
VERS="2.18.3"
VERS="2.19.1"
if [ "$(git describe --tags --always)" != "$VERS" ]; then
git fetch --all --tags --force
git checkout $VERS

View File

@@ -37,7 +37,7 @@ cppcheck() {
OPENDBC_ROOT=${OPENDBC_ROOT:-$BASEDIR}
$CPPCHECK_DIR/cppcheck --inline-suppr -I $OPENDBC_ROOT \
-I "$(gcc -print-file-name=include)" --suppress=*:/usr/lib/gcc/* --suppress=*:*/usr/lib/clang/* \
--suppress=missingIncludeSystem \
--suppressions-list=$DIR/suppressions.txt \
--error-exitcode=2 --check-level=exhaustive --safety \
--platform=arm32-wchar_t4 $COMMON_DEFINES --checkers-report=$CHECKLIST.tmp \

View File

@@ -25,9 +25,9 @@ class SubaruMsg(enum.IntEnum):
ES_LKAS_State = 0x322
ES_Infotainment = 0x323
ES_UDS_Request = 0x787
ES_HighBeamAssist = 0x121
ES_STATIC_1 = 0x22a
ES_STATIC_2 = 0x325
ES_HighBeamAssist = 0x22A
ES_STATIC_1 = 0x325
ES_STATIC_2 = 0x121
SUBARU_MAIN_BUS = 0
@@ -95,10 +95,6 @@ class TestSubaruSafetyBase(common.CarSafetyTest):
values = {s: speed for s in ["FR", "FL", "RR", "RL"]}
return self.packer.make_can_msg_safety("Wheel_Speeds", self.ALT_MAIN_BUS, values)
def _angle_meas_msg(self, angle):
values = {"Steering_Angle": angle}
return self.packer.make_can_msg_safety("Steering_Torque", 0, values)
def _user_brake_msg(self, brake):
values = {"Brake": brake}
return self.packer.make_can_msg_safety("Brake_Status", self.ALT_MAIN_BUS, values)

View File

@@ -178,7 +178,7 @@ class TestToyotaSafetyTorque(TestToyotaSafetyBase, common.MotorTorqueSteeringSaf
TORQUE_MEAS_TOLERANCE = 1 # toyota safety adds one to be conservative for rounding
# Safety around steering req bit
MIN_VALID_STEERING_FRAMES = 18
MIN_VALID_STEERING_FRAMES = 17
MAX_INVALID_STEERING_FRAMES = 1
@classmethod

View File

@@ -66,6 +66,16 @@
],
"package": "All"
},
"Acura TLX 2025": {
"platform": "ACURA_TLX_2G_MMR",
"make": "Acura",
"brand": "honda",
"model": "TLX",
"year": [
"2025"
],
"package": "All"
},
"Audi A3 2014-19": {
"platform": "AUDI_A3_MK3",
"make": "Audi",
@@ -244,7 +254,7 @@
"year": [
"2017"
],
"package": "Adaptive Cruise Control (ACC)"
"package": "No Adaptive Cruise Control (Non-ACC)"
},
"Chevrolet Bolt EV Non-ACC 2018-21": {
"platform": "CHEVROLET_BOLT_NON_ACC_1ST_GEN",
@@ -257,7 +267,7 @@
"2020",
"2021"
],
"package": "Adaptive Cruise Control (ACC)"
"package": "No Adaptive Cruise Control (Non-ACC)"
},
"Chevrolet Equinox 2019-22": {
"platform": "CHEVROLET_EQUINOX",
@@ -287,7 +297,7 @@
"2022",
"2023"
],
"package": "Adaptive Cruise Control (ACC)"
"package": "No Adaptive Cruise Control (Non-ACC)"
},
"Chevrolet Silverado 1500 2020-21": {
"platform": "CHEVROLET_SILVERADO",
@@ -1300,6 +1310,17 @@
],
"package": "All"
},
"Honda Odyssey (Taiwan) 2018-19": {
"platform": "HONDA_ODYSSEY_TWN",
"make": "Honda",
"brand": "honda",
"model": "Odyssey (Taiwan)",
"year": [
"2018",
"2019"
],
"package": "Honda Sensing"
},
"Honda Passport 2019-25": {
"platform": "HONDA_PILOT",
"make": "Honda",

View File

@@ -55,6 +55,7 @@ FW_VERSIONS_EXT = {
CAR.KIA_FORTE_2019_NON_SCC: {
(Ecu.eps, 0x7D4, None): [
b'\xf1\x00BD MDPS C 1.00 1.04 56310/M6000 4BDDC104',
b'\xf1\x00BD MDPS C 1.00 1.05 56310/M6000 4BDDC105',
],
(Ecu.fwdCamera, 0x7C4, None): [
b'\xf1\x00BD LKAS AT USA LHD 1.00 1.02 95740-M6000 J31',

View File

@@ -1,36 +0,0 @@
"""
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 enum import StrEnum
from opendbc.car import Bus, structs
from opendbc.can.parser import CANParser
from opendbc.sunnypilot.car.mazda.values import BUTTONS
class CarStateExt:
def __init__(self, CP, CP_SP):
self.CP = CP
self.CP_SP = CP_SP
self.button_events = []
self.button_states = {button.event_type: False for button in BUTTONS}
def update(self, ret: structs.CarState, ret_sp: structs.CarStateSP, can_parsers: dict[StrEnum, CANParser]):
cp = can_parsers[Bus.pt]
button_events = []
for button in BUTTONS:
state = (cp.vl[button.can_addr][button.can_msg] in button.values)
if self.button_states[button.event_type] != state:
event = structs.CarState.ButtonEvent.new_message()
event.type = button.event_type
event.pressed = state
button_events.append(event)
self.button_states[button.event_type] = state
self.button_events = button_events

View File

@@ -1,20 +0,0 @@
"""
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 collections import namedtuple
from opendbc.car import structs
ButtonType = structs.CarState.ButtonEvent.Type
Button = namedtuple('Button', ['event_type', 'can_addr', 'can_msg', 'values'])
BUTTONS = [
Button(ButtonType.accelCruise, "CRZ_BTNS", "SET_P", [1]),
Button(ButtonType.decelCruise, "CRZ_BTNS", "SET_M", [1]),
Button(ButtonType.cancel, "CRZ_BTNS", "CAN_OFF", [1]),
Button(ButtonType.resumeCruise, "CRZ_BTNS", "RES", [1]),
]

View File

@@ -1,6 +1,7 @@
import re
import json
import os
from natsort import natsorted
import unicodedata
from opendbc.car.common.basedir import BASEDIR
from opendbc.car.docs import get_all_footnotes, get_params_for_docs
@@ -15,6 +16,12 @@ def get_car_list() -> dict[str, dict[str, list[str] | str]]:
return sorted_list
def _natural_sort_key(s):
# NFKD normalization ensures accented characters sort with their base letter (e.g., Š sorts with S)
normalized = unicodedata.normalize('NFKD', s)
return [int(t) if t.isdigit() else t.lower() for t in re.split(r'(\d+)', normalized) if t]
def build_sorted_car_list(platforms, footnotes) -> dict[str, dict[str, list[str] | str]]:
cars: dict[str, dict[str, list[str] | str]] = {}
for model, platform in platforms.items():
@@ -49,7 +56,7 @@ def build_sorted_car_list(platforms, footnotes) -> dict[str, dict[str, list[str]
}
# Sort cars by make and model + year
sorted_cars = natsorted(cars.keys(), key=lambda car: car.lower())
sorted_cars = sorted(cars.keys(), key=lambda car: _natural_sort_key(car))
sorted_car_list = {car: cars[car] for car in sorted_cars}
return sorted_car_list

View File

@@ -44,7 +44,6 @@ testing = [
]
docs = [
"Jinja2",
"natsort",
]
examples = [
"inputs",

1066
uv.lock generated Normal file

File diff suppressed because it is too large Load Diff