Merge branch 'upstream/opendbc/master' into sync-20260201

# Conflicts:
#	.github/workflows/tests.yml
#	opendbc/car/honda/interface.py
This commit is contained in:
Jason Wen
2026-02-02 22:20:53 -05:00
47 changed files with 1603 additions and 160 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

@@ -77,11 +77,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

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,6 +1,6 @@
<!--- AUTOGENERATED FROM selfdrive/car/CARS_template.md, DO NOT EDIT. --->
# Support Information for 384 Known Cars
# Support Information for 387 Known Cars
|Make|Model|Package|Support Level|
|---|---|---|:---:|
@@ -10,7 +10,7 @@
|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|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 +19,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)|
@@ -103,7 +104,7 @@
|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 +115,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)|
@@ -149,7 +151,7 @@
|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|[Upstream](#upstream)|
|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)|
@@ -208,7 +210,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 +220,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)|
@@ -273,7 +276,7 @@
|Subaru|Outback 2018-19|EyeSight Driver Assistance|[Dashcam mode](#dashcam)|
|Subaru|Outback 2020-22|All|[Upstream](#upstream)|
|Subaru|Outback 2023|All|[Dashcam mode](#dashcam)|
|Subaru|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 +301,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 +321,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 +342,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

@@ -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

@@ -55,8 +55,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

@@ -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

@@ -189,6 +189,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 +282,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 +334,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

@@ -19,6 +19,8 @@ class CarState(CarStateBase):
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]
@@ -27,9 +29,6 @@ class CarState(CarStateBase):
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"],
@@ -113,8 +112,19 @@ class CarState(CarStateBase):
self.cam_laneinfo = cp_cam.vl["CAM_LANEINFO"]
ret.steerFaultPermanent = cp_cam.vl["CAM_LKAS"]["ERR_BIT_1"] == 1
# TODO: add button types for inc and dec
ret.buttonEvents = create_button_events(self.distance_button, prev_distance_button, {1: ButtonType.gapAdjustCruise})
# 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"]
ret.buttonEvents = [
*create_button_events(self.distance_button, prev_distance_button, {1: ButtonType.gapAdjustCruise}),
*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

@@ -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

@@ -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()

154
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,34 @@ 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 +51,20 @@ 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]:
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,17 @@ 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:
cwd = Path(__file__).resolve().parents[3]
ref_path = cwd / DIFF_BUCKET
if not update_refs:
@@ -263,7 +265,7 @@ def main(platform=None, segments_per_platform=10, update_refs=False, all_platfor
platforms = get_changed_platforms(cwd, database, interfaces)
if not platforms:
print("No car changes detected", file=sys.stderr)
print("No car changes detected")
return 0
segments = {p: database.get(p, [])[:segments_per_platform] for p in platforms}
@@ -272,16 +274,16 @@ 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")
@@ -291,14 +293,14 @@ def main(platform=None, segments_per_platform=10, update_refs=False, all_platfor
if with_diffs:
print("```")
for plat, seg, diffs in with_diffs:
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):
for line in format_diff(fd, ref, states, field):
print(line)
print("```")

View File

@@ -130,6 +130,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),
@@ -147,6 +148,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

@@ -91,7 +91,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

@@ -974,11 +974,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',
@@ -987,6 +989,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): [
@@ -996,6 +999,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

@@ -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

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

@@ -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

1077
uv.lock generated Normal file

File diff suppressed because it is too large Load Diff