Skip to content

Ford: read TSR speed limit on Q3 (CAN) cars, not just Q4 (CAN FD)#160

Open
jrneulight wants to merge 1 commit into
BluePilotDev:bp-devfrom
jrneulight:bp-ford-q3-speed-limit
Open

Ford: read TSR speed limit on Q3 (CAN) cars, not just Q4 (CAN FD)#160
jrneulight wants to merge 1 commit into
BluePilotDev:bp-devfrom
jrneulight:bp-ford-q3-speed-limit

Conversation

@jrneulight

Copy link
Copy Markdown
Contributor

Read the posted speed limit from the IPMA traffic-sign-recognition broadcast on Q3
(CAN) Fords, not just Q4 (CAN FD). No new signals, no DBC changes, no behavior change on
cars that don't broadcast it.

The evidence that motivated this

Speed limit never populated on a Q3 Ford (e.g. Explorer MK6): carStateSP.speedLimit
sat at 0 for an entire highway drive. The feature was gated behind FordFlags.CANFD in
three places, so the whole TSR path was a dead branch on CAN cars — not a decode bug, no
implementation at all.

But the data is on the wire. Scanning rlogs from a 2021 Explorer, Traffic_RecognitnData
(CAN msg 973) is broadcast on the camera bus (bus 2, plus 0/128), and its speed-limit
signals decode cleanly with the DBC (ford_lincoln_base_pt) both generations already
share:

Route TsrVLim1MsgTxt_D_Rq TsrVlUnitMsgTxt_D_Rq decoded
00000031--71******** 45 (6062/6132 frames) 2 (mph) 45 mph
0000003b--f0******** 65 (5966/6056 frames) 2 (mph) 65 mph

Two independent drives, two different posted limits, both mph, both sane against the ~54–64
mph vEgo — this is a live TSR feed the whole drive, not a stray frame. The IPMA camera
absolutely sends speed limit on Q3; only the code path refused to read it.

Why the fix is shaped this way

  • Non-critical registration. Traffic_RecognitnData joins the Q3 camera parser at
    float('nan') frequency — the BCM_Lamp_Stat_FD1 / HEV-overlay convention — because TSR
    is an optional Co-Pilot360 camera feature. Trims without it simply never broadcast the
    message, and it must never invalidate the rest of carState. Its signals default to
    0/255, which update_traffic_signals already maps to "no limit", so absent hardware
    yields 0 cleanly.
  • No IPMA_Data2 on the Q3 path. The unit (mph/kph) rides inside msg 973 itself
    (TsrVlUnitMsgTxt_D_Rq), so the CAN FD-only IPMA_Data2 lookup isn't needed and stays
    CAN FD-only.
  • The decode body is unchanged. Dropping the FordFlags.CANFD early-return lets the
    existing math (unit factor, skip on 0/255) run for both generations verbatim — the
    identical logic that already ships for Q4.

The evidence afterwards

Driving the real CarState.update() pipeline over both routes with this change:

Route carStateSP.speedLimit (rounded)
00000031--71******** 45 mph (5862/5938 frames; rest startup/no-sign)
0000003b--f0******** 65 mph (5866/5929 frames)

Matches the raw-CAN decode exactly. The speed-limit resolver reads
sm['carStateSP'].speedLimit directly and is car-agnostic, so populating the field is
sufficient to feed the car source for Speed Limit Assist.

What each change is

  • opendbc/car/ford/carstate.py: register Traffic_RecognitnData on the non-CANFD camera
    bus (non-critical); drop the CANFD gate on the ret_sp.speedLimit assignment.
  • opendbc/sunnypilot/car/ford/carstate_ext.py: drop the CANFD early-return in
    update_traffic_signals; the decode body is unchanged. Docstring updated.

Test it yourself

pytest opendbc_repo/opendbc/car/ford/tests/test_ford.py -n0

On a Q3 Ford with a Co-Pilot360 TSR camera: drive past a posted-limit sign and confirm
carStateSP.speedLimit reports the sign value; cross-check against raw CAN msg 973
(TsrVLim1MsgTxt_D_Rq × unit from TsrVlUnitMsgTxt_D_Rq). Enable Speed Limit Assist with a
car-source policy to see it consumed.

Risks / limits

  • On-screen display is unverified on-device. Populating carStateSP.speedLimit feeds
    the car-agnostic resolver (confirmed), but whether it renders also depends on Speed
    Limit Mode / policy settings and the UI — untested here.
  • Q3 trims without the TSR camera: the message is never broadcast; non-critical
    registration + the 0/255 default keep the path inert (no CAN-validity impact, speedLimit
    stays 0). Verified in the DBC default handling, not yet on a non-TSR Q3 car.
  • Q4 (CAN FD) behavior is unchanged — same message, same decode, same result.

Speed limit was gated behind FordFlags.CANFD in three places, so Q3 Fords
(e.g. Explorer MK6) never populated carStateSP.speedLimit even though their
IPMA camera broadcasts the same Traffic_RecognitnData message the CAN FD cars
read. Confirmed on a 2021 Explorer over two rlog routes: msg 973
(Traffic_RecognitnData) is present on the camera bus, and its TsrVLim1MsgTxt_D_Rq
/ TsrVlUnitMsgTxt_D_Rq signals decode cleanly to 45 mph and 65 mph respectively,
using the identical DBC (ford_lincoln_base_pt) both generations already share.
Driving the real CarState.update() pipeline over both routes with this change
yields speedLimit = 45 / 65 mph, matching the raw decode.

Changes:
- Register Traffic_RecognitnData on the Q3 camera bus. Marked non-critical
  (nan frequency, matching the BCM/HEV overlay convention) because TSR is an
  optional Co-Pilot360 camera feature: trims without it never send the message,
  and it must not invalidate the rest of carState. Its signals default to
  0/255, which update_traffic_signals already maps to "no limit".
- Drop the CANFD gate on the ret_sp.speedLimit assignment in carstate.
- Drop the CANFD early-return in update_traffic_signals; the existing decode
  body (unit from TsrVlUnitMsgTxt_D_Rq within the message, skip on 0/255) works
  unchanged for both generations. IPMA_Data2 stays CAN FD-only and is not needed
  for the TSR path since the unit rides inside msg 973 itself.

The speed-limit resolver reads sm['carStateSP'].speedLimit directly and is
car-agnostic, so populating the field is sufficient to feed Speed Limit Assist.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant