Ford: log interesting module configuration data (#31569)

* Ford: log interesting module configuration data

Ford ECUs have what is called "As-Built Data" which is configured at the
factory/workshop to set what packages/features are enabled on the car.
But they also contain vehicle specific information (VIN, make, model,
weight, wheel base...), DTC information and driver preferences.

I dumped the CAN traffic for the FORScan diagnostic tool to see how it
requests this information from the ECUs.

<details>
<summary>FORScan communication with IPMA (camera)</summary>
<pre>
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': '0200'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'TESTER_PRESENT', 'hex': '00'}
{'addr': 1806, 'type': 'positive_response', 'service': 'TESTER_PRESENT', 'hex': '00'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f190'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de00'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de00020799dbaa10296516a440000000000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f113'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1\x13JX7T-19H406-CH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1134a5837542d3139483430362d434800000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f188'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1\x88JX7T-14F397-AH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1884a5837542d3134463339372d414800000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f120'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1 JX7T-14F397-BF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1204a5837542d3134463339372d424600000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f121'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f124'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1$JX7T-14F398-AG\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1244a5837542d3134463339382d414700000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f125'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1%JX7T-14F398-BF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1254a5837542d3134463339382d424600000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f126'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f10a'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f111'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1\x11JX7T-14F403-CA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1114a5837542d3134463430332d434100000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f18c'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1\x8c182762191\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f18c31383237363231393100000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f162'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f110'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1\x10DS-JX7T-19H406-AD\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f11044532d4a5837542d3139483430362d414400000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': '0202'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'd100'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'd10001'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'd700'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'd70001010101'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'd701'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'd70101020000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'dd01'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'dd010102f8'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'f113'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'data': b'\xf1\x13JX7T-19H406-CH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 'hex': 'f1134a5837542d3139483430362d434800000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'fd08'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'fd0800000500500100300000000000000000000000300000000000000000200100400300100200001200f00300500000000000000300c00b00400200000000000000000000000000000000200f01201e01501400a00200200400700d02501d01700700e06405005e05503401100a000000002002002001000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'fd09'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'fd09ffec0001fef60002'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de00'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de00020799dbaa10296516a440000000000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DTC_INFORMATION', 'hex': '028f'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DTC_INFORMATION', 'hex': '02ff50019768c253002cc401862cc418862c'}
... skip DTC requests ...
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de00'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de00020799dbaa10296516a440000000000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de01'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de01fd5616db5fffff557fe1f842080000000800000008000000080000000819bfe00f7c00000000000000000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de02'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de02800000008000000080000000800000008337fc00800000008000000080000000800000008337fc0000000000000000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de03'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de03fffc26c3800000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de04'}
{'addr': 1806, 'type': 'positive_response', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de04546a8c0000000000'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de05'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de06'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de07'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de08'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de09'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de0a'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de0b'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de0c'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de0d'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
{'addr': 1798, 'type': 'request', 'service': 'READ_DATA_BY_IDENTIFIER', 'hex': 'de0e'}
{'addr': 1806, 'type': 'negative_response', 'hex': '2231'}
</pre>
</details>

Using UDS service `READ_DATA_BY_IDENTIFIER`, we can read the As-Built
blocks from `0xDExx` with no diagnostic session/security access necessary.

I used [Online As-Built databases](https://cyanlabs.net/asbuilt-db/) and
various coding spreadsheets shared online to find values we might be
interested in using for fingerprinting (both vehicle parameters and
identifying the platform).

ABS:
- Payload tier (Base, Mid Payload Upgrade, Heavy Duty Payload
  Upgrade...)
- Wheelbase
- Steering ratio
- Cruise Control Mode (Normal, Adaptive)
- Enable Stop and Go

PSCM:
- Enable Lane Keeping Aid
- Enable Traffic Jam Assist
- Enable Lane Centering Assist

IPMA (Q4):
- Steering ratio
- Wheelbase

APIM (Sync 3 and Sync 4):
- Steering ratio
- Vehicle weight
- Wheelbase

There are more potentially useful signals which I haven't included
although they might not be necessary:
- Vehicle (Ford platform code, like "C344" or "C519" - although the
  source of the mapping from index to code is FORScan and not Ford
themselves unless we can find a better source).
- Fuel type
- Vehicle length/height/front track/rear track
- Tire circumference (could be useful for converting wheel speed rad/s
  to m/s)
- Steering angle source (Pinion, Wheel)
- Country code (letters, e.g. US, CA or UK)
- Transmission type
- CAN network architecture
- More feature flags (the APIM also stores settings for ACC, LCA, BLIS)

The full list of settings I have found is
[here](https://github.com/incognitojam/op-notebooks/blob/main/ford/settings.py).

* FwQueryConfig: add data_requests

* add car_data to CarInterface get_params

* Revert "add car_data to CarInterface get_params"

This reverts commit aa161a6b82.

* test_ford: add APIM ecu address

* Revert "FwQueryConfig: add data_requests"

This reverts commit dc5484a9b8.

* fix block numbers and add extra queries

* bump test_fw_query_timing

* add missing query whitelists

* simplify asbuilt requests

* use forscan block ids

* formatting

---------

Co-authored-by: Shane Smiskol <shane@smiskol.com>
pull/31609/head
Cameron Clough 1 year ago committed by GitHub
parent 9ecff49118
commit c724d1c86c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      selfdrive/car/ford/tests/test_ford.py
  2. 46
      selfdrive/car/ford/values.py
  3. 6
      selfdrive/car/tests/test_fw_fingerprint.py

@ -19,6 +19,7 @@ ECU_ADDRESSES = {
Ecu.fwdCamera: 0x706, # Image Processing Module A (IPMA)
Ecu.engine: 0x7E0, # Powertrain Control Module (PCM)
Ecu.shiftByWire: 0x732, # Gear Shift Module (GSM)
Ecu.debug: 0x7D0, # Accessory Protocol Interface Module (APIM)
}

@ -1,11 +1,12 @@
from dataclasses import dataclass, field
from enum import Enum
import panda.python.uds as uds
from cereal import car
from openpilot.selfdrive.car import AngleRateLimit, CarSpecs, dbc_dict, DbcDict, PlatformConfig, Platforms
from openpilot.selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column, \
Device
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
from openpilot.selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16
Ecu = car.CarParams.Ecu
@ -140,6 +141,33 @@ class CAR(Platforms):
CANFD_CAR = {CAR.F_150_MK14, CAR.F_150_LIGHTNING_MK1, CAR.MUSTANG_MACH_E_MK1}
DATA_IDENTIFIER_FORD_ASBUILT = 0xDE
ASBUILT_BLOCKS: list[tuple[int, list]] = [
(1, [Ecu.debug, Ecu.fwdCamera, Ecu.eps]),
(2, [Ecu.abs, Ecu.debug, Ecu.eps]),
(3, [Ecu.abs, Ecu.debug, Ecu.eps]),
(4, [Ecu.debug, Ecu.fwdCamera]),
(5, [Ecu.debug]),
(6, [Ecu.debug]),
(7, [Ecu.debug]),
(8, [Ecu.debug]),
(9, [Ecu.debug]),
(16, [Ecu.debug, Ecu.fwdCamera]),
(18, [Ecu.fwdCamera]),
(20, [Ecu.fwdCamera]),
(21, [Ecu.fwdCamera]),
]
def ford_asbuilt_block_request(block_id: int):
return bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER]) + p16(DATA_IDENTIFIER_FORD_ASBUILT + block_id - 1)
def ford_asbuilt_block_response(block_id: int):
return bytes([uds.SERVICE_TYPE.READ_DATA_BY_IDENTIFIER + 0x40]) + p16(DATA_IDENTIFIER_FORD_ASBUILT + block_id - 1)
FW_QUERY_CONFIG = FwQueryConfig(
requests=[
# CAN and CAN FD queries are combined.
@ -147,19 +175,29 @@ FW_QUERY_CONFIG = FwQueryConfig(
Request(
[StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST],
[StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE],
whitelist_ecus=[Ecu.abs, Ecu.debug, Ecu.engine, Ecu.eps, Ecu.fwdCamera, Ecu.fwdRadar, Ecu.shiftByWire],
logging=True,
),
Request(
[StdQueries.TESTER_PRESENT_REQUEST, StdQueries.MANUFACTURER_SOFTWARE_VERSION_REQUEST],
[StdQueries.TESTER_PRESENT_RESPONSE, StdQueries.MANUFACTURER_SOFTWARE_VERSION_RESPONSE],
whitelist_ecus=[Ecu.abs, Ecu.debug, Ecu.engine, Ecu.eps, Ecu.fwdCamera, Ecu.fwdRadar, Ecu.shiftByWire],
bus=0,
auxiliary=True,
),
*[Request(
[StdQueries.TESTER_PRESENT_REQUEST, ford_asbuilt_block_request(block_id)],
[StdQueries.TESTER_PRESENT_RESPONSE, ford_asbuilt_block_response(block_id)],
whitelist_ecus=ecus,
bus=0,
logging=True,
) for block_id, ecus in ASBUILT_BLOCKS],
],
extra_ecus=[
# We are unlikely to get a response from the PCM from behind the gateway
(Ecu.engine, 0x7e0, None),
(Ecu.shiftByWire, 0x732, None),
(Ecu.engine, 0x7e0, None), # Powertrain Control Module
# Note: We are unlikely to get a response from behind the gateway
(Ecu.shiftByWire, 0x732, None), # Gear Shift Module
(Ecu.debug, 0x7d0, None), # Accessory Protocol Interface Module
],
)

@ -263,13 +263,13 @@ class TestFwFingerprintTiming(unittest.TestCase):
print(f'get_vin {name} case, query time={self.total_time / self.N} seconds')
def test_fw_query_timing(self):
total_ref_time = {1: 6.5, 2: 7.4}
total_ref_time = {1: 7.8, 2: 8.7}
brand_ref_times = {
1: {
'gm': 0.5,
'body': 0.1,
'chrysler': 0.3,
'ford': 0.2,
'ford': 1.5,
'honda': 0.55,
'hyundai': 1.05,
'mazda': 0.1,
@ -280,7 +280,7 @@ class TestFwFingerprintTiming(unittest.TestCase):
'volkswagen': 0.65,
},
2: {
'ford': 0.3,
'ford': 1.6,
'hyundai': 1.85,
}
}

Loading…
Cancel
Save