Both firmware fixes are now confirmed over the air, with the sniffer board capturing and asn1tools judging the result. GN lifetime: our station transmits 0x05 (1 s), the value both bench stations use, where the August captures show 0x83 (3200 s) for the same frames. 397 CAMs from station 999999 decode and re-encode byte-identically, so the whole transmit chain is right on the wire, not only in the host tests. yawRateConfidence: the bench beacon, flashed to a spare board, sends CAMs that decode and re-encode byte-identically as well (72 of 72 from station 0x0BADC0DE). NOTES.md now says how to flash that beacon and how to check what it sends, including that it shares the phone pinger's MAC and the two are told apart by station ID. The new firmware also runs on the OBU with the phone attached: CAM, DENM and SPATEM from the bench stations all keep decoding in the app now that messages are cut to the length their header declares. Signed reception stays open. The CiT One transmits unsigned and nothing else here signs, so it needs real roadside traffic, the CiT One switched to signed mode, or a replay firmware on a spare board. Two bench facts worth not rediscovering are recorded too: opening COM3's console resets the OBU and drops the phone's USB link, and every capture taken before today is truncated at its first corrupted record, because the receiver's console inserts a CR before every 0x0a byte of the binary pcap stream. live_capture.py now undoes that, a change that lives in the receiver repo and is not part of this commit.
126 lines
8.0 KiB
Markdown
126 lines
8.0 KiB
Markdown
# TODO
|
|
|
|
Engineering to-do list. The reviewer-facing open items live in
|
|
`docs/01-requirements-traceability.md` ("Open items"); this file is the working list behind them.
|
|
|
|
## Waiting on hardware
|
|
|
|
### Over-the-air check of the GN lifetime fix (added 2026-09-11)
|
|
|
|
`geonet.c` now writes GN lifetime `0x05` (1 s) instead of `0x83`, which decoded to 3200 s. Changed
|
|
in both `obu-firmware` and `obu-cam-transmistter`. Both still build (IDF 6.1 / 5.5.4), and the
|
|
compiled `geonet_wrap_shb` stores the new byte. Confirmed on air 2026-09-14. Nothing else
|
|
reads this byte (`gn_unwrap.c` ignores it, the app never sees GN headers), so the app does not
|
|
need updating alongside the firmware.
|
|
|
|
Needs: the phone with the app, the OBU ESP32-C5, and a **second** ESP32-C5 running
|
|
`its-g5-receiver-firmware` to capture with.
|
|
|
|
- [x] Flash `obu-firmware` (done 2026-09-14 on COM3; flash backed up first to
|
|
`Documents/micrOBU_workspace/firmware-backups/COM3-2026-09-14-before-secured-rx.bin`).
|
|
- [x] Capture with the receiver (COM8) into `its-g5-receiver-firmware/recordings/`.
|
|
- [x] `pcap_gn_tally.py` on capture_20260914_132126.pcap: our station sends SHB, port 2001,
|
|
lifetime `0x05`, same as both bench stations. It was `0x83` in the August captures.
|
|
- [x] Real-station CAMs/DENMs/SPATEM still reach the app (logcat: `handleCamUper`,
|
|
`handleDenmUper`, `handleSpatUper` all decoding, 2026-09-14).
|
|
- [x] Our own CAMs decode on air: 397 frames from station 999999 decode with asn1tools and
|
|
re-encode byte-identically.
|
|
- [ ] Confirm the CAM Pinger card's `tx fail` / oversize / CRC counters are 0 (needs a look at the
|
|
phone; not readable from the PC).
|
|
|
|
Partial check possible with one board and no phone: flash it, `idf.py -p COMx monitor`, and look
|
|
for `OCB @ 5900 MHz - TX/RX armed`. That proves the new build boots and brings the radio up, not
|
|
that it transmits correctly.
|
|
|
|
### obu-cam-transmistter yawRateConfidence fix (added 2026-09-11)
|
|
|
|
Its `cam.c` (compiled into that firmware) wrote `yawRateConfidence` as 3 bits / 7 instead of
|
|
4 bits / unavailable(8), the bug the app fixed on 2026-08-20. Fixed in it and in obu-firmware's
|
|
reference copy; asn1tools now decodes the CAM and re-encodes it byte-identically, and it builds on
|
|
IDF 5.5.4. Since 2026-09-14 the spare board on COM10 runs it as a bench beacon:
|
|
|
|
- [x] Done 2026-09-14: flashed on COM10 and captured on COM8. All 72 CAMs from station
|
|
195936478 (0x0BADC0DE) decode with asn1tools and re-encode byte-identically, so the
|
|
4-bit yawRateConfidence is right on air. COM10 now runs this beacon rather than
|
|
obu-firmware - reflash it if the spare is needed as an OBU again.
|
|
|
|
### Signed-message reception and exact payloads (added 2026-09-11)
|
|
|
|
obu-firmware's `gn_unwrap.c` now unwraps TS 103 097 signed packets (signature not verified,
|
|
reported as V2X_RX flags bit1) and cuts every message to the length its header declares, dropping
|
|
the 8 bytes the chip's RX appends to each frame, which were forwarded to the phone until now.
|
|
Verified on the host (`obu-firmware/test/host`: chain, replay of all recordings against asn1tools,
|
|
50M-iteration fuzz) and flashed to the production OBU on 2026-09-14. The remaining gap is signed
|
|
traffic to receive: real vehicles or
|
|
RSUs, since the bench CiT One sends unsigned. A second ESP32 running the receiver firmware is
|
|
optional, but shows what was on air at the time.
|
|
|
|
- [x] Flash obu-firmware (done 2026-09-14, COM3).
|
|
- [x] Unsigned bench traffic still decodes in the app, with messages now cut to their declared
|
|
length (CAM, DENM and SPATEM all decoding in logcat after the flash).
|
|
- [ ] Near signed traffic: signed CAMs/DENMs appear in the app, and a simultaneous capture shows
|
|
them on air (`pcap_gn_tally.py` lists them as `secured`). NOT possible at this bench: the
|
|
CiT One transmits unsigned (`ItsGnSecurity = 0`) and nothing else here signs. Needs a drive
|
|
past real RSUs, the CiT One switched to signed mode if its API allows, or a replay firmware
|
|
on a spare board that re-transmits the recorded signed frames.
|
|
- [ ] The heartbeat's oversize counter still counts over-long messages. Not exercised at the
|
|
bench: the SPATEMs here are ~340 bytes on air, far below the cap.
|
|
|
|
## Set up host testing
|
|
|
|
- [x] Install MSYS2 UCRT64 gcc (done 2026-09-11: gcc 16.2.0, GNU Make 4.4.1; chosen over WSL,
|
|
vanetza is not going to be built). Setup and the PATH gotcha: `obu-firmware/test/host/README.md`.
|
|
- [x] Host round-trip test `obu-firmware/test/host/test_chain.c` (`geonet_wrap_shb` ->
|
|
`dot11p_build_frame` -> `gn_unwrap_its`, byte-checked against the standard). Done
|
|
2026-09-11: 491 checks, 0 failed. Run `make` in that folder before flashing any firmware fix.
|
|
- [x] Replay of the recorded captures (`test_replay.c` + `check_replay.py`, independent asn1tools
|
|
check). Done 2026-09-11: C and Python agree on all 15 145 records.
|
|
- [x] Mutation fuzzer `fuzz_gn_unwrap.c`, inputs against a no-access guard page. Done 2026-09-11:
|
|
50 000 000 iterations, no crash. `make` runs a 2 000 000-iteration pass every time.
|
|
|
|
## Firmware ideas from the vanetza review (2026-09-11, not started)
|
|
|
|
Suggested order after the host tests exist:
|
|
|
|
- [x] **Read secured packets (GN NextHeader=2) without verifying them.** Done 2026-09-11 in
|
|
`gn_unwrap.c`, host-verified; flagged to the phone as V2X_RX flags bit1. On-air check under
|
|
"Waiting on hardware".
|
|
- [ ] **Forward the full GeoBroadcast area**: shape (circle/rectangle/ellipse), DistanceB, angle,
|
|
appended to the V2X_RX prefix behind a capability bit. Port vanetza's `geonet/areas.cpp`
|
|
`inside_or_at_border` to the app, which currently treats every area as a circle.
|
|
- [ ] **RX filtering before the serial link**: duplicate detection for GBC (last 8 sequence numbers
|
|
per source, as vanetza does), drop our own frames, reject GN version != 1.
|
|
- [ ] **Read the DCC-MCO field** (the 4 "reserved" bytes of an SHB header): neighbours' channel
|
|
busy ratio for free.
|
|
- [ ] **Minimum TX gap in firmware** as a DCC safety net (vanetza reactive table: 60 ms relaxed ...
|
|
460 ms restrictive), with a CBR estimate in the heartbeat.
|
|
- [ ] **Generic V2X_TX message** (BTP port, SHB/GBC, traffic class, lifetime, area) so the phone can
|
|
send DENM and VAM without reflashing. Consider QoS Data frames: vanetza's Cohda receive path
|
|
drops non-QoS ones.
|
|
|
|
Dropped: building vanetza as a GN/BTP oracle. Real captures (`pcap_gn_tally.py`), the host
|
|
round-trip test and `asn1tools` for UPER cover what it would have checked.
|
|
|
|
## Follow-ups found 2026-09-14
|
|
|
|
- [ ] **Commit the `live_capture.py` fix in the receiver repo.** Its console inserts a CR before
|
|
every LF, which also hits every 0x0a byte of the binary pcap stream, shifting pcap record
|
|
headers and frames. A 787 KB capture parsed cleanly for only 82 of ~2000 records, and DENMs
|
|
showed up on nonsense BTP ports. `undo_crlf()` now reverses it on the raw stream before
|
|
framing; afterwards a capture parsed to EOF and DENMs read as port 2002. This is the
|
|
"occasional byte inserted mid-frame" in the older recordings, so **every capture taken before
|
|
2026-09-14 is truncated at its first corrupted record** - re-measure anything derived from
|
|
them. The change is in the `its-g5-receiver-firmware` repo, uncommitted.
|
|
- [ ] **Do not open COM3's console while the phone is attached.** Opening it toggles DTR/RTS on the
|
|
CH343 and resets the OBU, which drops the phone's USB link and needs a manual Connect.
|
|
|
|
## Follow-ups found 2026-09-11
|
|
|
|
- [ ] **App: show the signed flag.** `V2xRxFrame.parse` in `SerialFrame.kt` only reads bit0 of
|
|
the flags byte; read bit1 (signed, not verified) and show it where messages are listed.
|
|
- [ ] **Messages that do not decode with asn1tools.** In the recordings, 56 from the CiT One
|
|
(`aa:f8:76:7d:bd:ad`: 54 CAMs of 245 bytes, 2 DENMs of 402 bytes) and one 218-byte CAM from
|
|
`6e:94:03:1b:05:26` fail against `cam_1_4_1`/`denm_1_3_1` + `cdd_1_3_1_1`, with or without the
|
|
old trailing bytes. A newer module version on the sender, or a sender bug; check what the
|
|
app's decoders make of them (`check_replay.py` lists the records).
|