diff --git a/04-transmit-setup.md b/04-transmit-setup.md new file mode 100644 index 0000000..b2476fe --- /dev/null +++ b/04-transmit-setup.md @@ -0,0 +1,280 @@ +# Step 4 - Build, flash, and validate the HLN-SV DENM transmitter + +## Radio-config fix (2026-07-15) - read this first + +Symptom: nothing on air. Proof it was a radio-config problem, not a +frame-format problem: the 2026-07-15 sniffer capture (`recordings/ +its5_20260715_131448.pcap`) contains 13,584 ITS-G5 frames on 5.9 GHz but +**zero** from our source MAC `02:00:00:00:00:01`. A sniffer records a +station's frames even if the payload is malformed, so the frame was never +leaving the radio - the DENM/GeoNet/802.11 encoding was never the issue. + +Three stacking causes, all in `main.c`'s Wi-Fi init, now fixed: + +1. **Plain STA, no promiscuous, power-save on.** ESP-IDF only actually emits + raw frames when the MAC is in promiscuous mode or associated to an AP, and + default STA power-save sleeps the radio between beacons and drops outbound + frames. The *working sniffer* runs promiscuous - the OBU didn't. Fixed: + `esp_wifi_set_ps(WIFI_PS_NONE)` + `esp_wifi_set_promiscuous(true)` after + `esp_wifi_start()`. +2. **Never entered 5 GHz band mode.** The stuck `esp_wifi_get_channel() + primary=1` was the dual-band C5 still in 2.4 GHz band mode, so + `esp_wifi_set_channel(<5G channel>)` failed silently and the HMAC TX path + keyed the 2.4 GHz PHY on ch1 - inaudible to a 5.9 GHz sniffer. Fixed: + `esp_wifi_set_band_mode(WIFI_BAND_MODE_5G_ONLY)` before start. Channel prime + changed from 140 (5700 MHz) to **177 (5885 MHz)** - see cause 3. +3. **5900 MHz is out of the C5's spec range.** The datasheet 5 GHz range is + **5180-5885 MHz**; our target 5900 MHz (ITS-G5 G5-CCH) is 15 MHz above it. + RX tolerates 15 MHz over (that's why the sniffer works at 5900); TX may be + PA-calibration-gated at an uncalibrated frequency. Priming the driver to + channel 177 (5885, the top legal channel) keeps TX on the closest real + power table before `phy_change_channel(5900,...)` nudges the LO. + +### If it STILL shows nothing after these fixes - the 5900 MHz isolation test + +Causes 1 and 2 are high-confidence. Cause 3 is the remaining unknown, so +isolate it before touching anything else. Temporarily change **both** ends to +a channel inside the C5's spec range and see if the OBU appears: + +- In `main.c`, change the target frequency from `5900` to `5885` in *both* + `phy_change_channel()` calls (boot + per-TX), matching the channel-177 prime. +- Put your sniffer on 5885 MHz too. +- Ground GPIO4 and watch the dashboard/PCAP for source MAC + `02:00:00:00:00:01`. + +If the OBU now appears at 5885 but not at 5900: TX genuinely can't key the PA +at the out-of-spec 5900 MHz, and no amount of frame-format work will change +that - you'd need a chip whose 5 GHz range covers the ITS band, or to accept +operating one channel down. If it appears at neither, the problem is still in +the TX-enable path (recheck that the three calls above returned ESP_OK in the +boot log), not the frequency. + +Also note: **nothing transmits until GPIO4 is grounded** (active-low hazard +input). If you were bench-testing without grounding GPIO4, the TX path was +never even entered - ground it (jumper GPIO4 to GND) before concluding +anything. + +--- + +Profile: **HLN-SV** (aftermarket stationary recovery vehicle) - causeCode 94 +(stationaryVehicle), subCauseCode 0, transmits only while the hazard-light +GPIO reads active. No location/alacarte containers, no optional fields. + +Code lives in `obu-firmware/main/`: `main.c` (entry, PHY hack, GPIO, TX loop), +`denm.c/.h` (ASN.1 UPER encoding), `geonet.c/.h` (GeoNetworking + BTP-B +wrapping), `dot11p.c/.h` (802.11 OCB frame assembly), `tx_custom.c/.h` (the +actual raw-frame transmit path - see "The TX bypass" below). + +## The TX bypass (tx_custom.c) + +`esp_wifi_80211_tx()` - ESP-IDF's public raw-frame API - rejects QoS Data +frames outright (`esp_err 258` / "unsupport QoS frame type"), and there's no +supported way to override that from application code (a linker-level symbol +override attempt, `main/wifi_patches.c`, is kept around only for history - +confirmed not to work). + +`tx_custom.c` was pulled from +[opentrafficmap/its-g5-receiver-firmware_txenabled](https://codeberg.org/opentrafficmap/its-g5-receiver-firmware_txenabled) +- a TX-enabled fork of the exact receiver firmware (V2X2MAP) already used on +the RX side of this project, same authors, same chip. Instead of trying to +disable the gate, it skips the code path that contains it entirely: it calls +`ic_ebuf_alloc()` and `ieee80211_post_hmac_tx()` - undocumented internal +driver functions - directly, submitting straight to the MAC. `main.c` now +calls `esp_wifi_80211_tx_custom()` instead of `esp_wifi_80211_tx()`, with the +one proven-working parameter set (`WIFI_PHY_MODE_11A`, `WIFI_PHY_RATE_12M`, +`WIFI_BAND_5G`, `WIFI_BW20`) copied from the only call site in the upstream +repo (`mqtt.cpp`, triggered by an incoming MQTT message there - ours is +triggered by the GPIO4 hazard-light logic instead). + +**What this unlocks**: `dot11p.c` is back to real QoS Data frames (subtype 8) +since the gate that forced the non-QoS downgrade no longer applies. Frame +size is now 117 bytes (was 115 with non-QoS Data's 2-byte-shorter header) - +expect `sent (117 bytes)` in the log now, not 115. + +**What's NOT yet independently verified** - things worth checking as you go: +- Whether `ieee80211_post_hmac_tx`, `ic_ebuf_alloc`, `ic_get_default_sched`, + `g_osi_funcs_p`, and `g_wifi_global_lock` actually exist as symbols in + *your* IDF version's `libnet80211.a`/`libpp.a` for esp32c5 - we only + previously confirmed `ieee80211_raw_frame_sanity_check` exists via `nm`, + not these. If the build fails to link with `undefined reference`, this is + the first thing to check - same `nm` approach as the `phy_11p_set` section + below, just against these symbol names. +- `tx_custom.c`'s internal struct layouts (`x_eb_txdesc_t`, `x_ebuf_t`) are + reverse-engineered from the closed WiFi driver, pinned only by a `sizeof()` + assert - that catches a total-size mismatch across IDF versions but not a + field-order mismatch that happens to keep the same total size. If your IDF + version differs meaningfully from whatever the upstream repo has pinned + (check their `esp-idf` git submodule commit vs. `idf.py --version`), this + could compile and link cleanly but write to the wrong internal offsets. + Worth keeping in mind as a possible explanation if you get a crash/hang + right when TX fires rather than a clean error. +- Same "skips ALL sanity checking" risk as the old override attempt: a + malformed frame from a bug anywhere in `denm.c`/`geonet.c`/`dot11p.c` could + now behave worse (crash, silent corruption) than a clean rejection. +- `esp_wifi_set_channel(140, ...)` was added in `main.c` right before the + `phy_11p_set`/`phy_change_channel` pair, copied from upstream's + `cmd_sniffer.c` (their own comment on it: "not sure if strictly needed"). + Motivation: our own `esp_wifi_get_channel()` diagnostic was reporting a + stuck `primary=1` regardless of what the PHY hack was told, consistent + with the driver's channel bookkeeping never being touched by anything it + tracks. This is a "worth trying," not a confirmed fix - watch whether + `esp_wifi_get_channel()`'s reported value changes at all now. + +## Confidence levels - read this before debugging blind + +Updated after pulling the actual specs (EN 302 636-4-1, EN 302 636-5-1) and +the real ASN.1 modules from forge.etsi.org (EN 302 637-3, TS 102 894-2 CDD) - +this isn't guesswork anymore for the parts listed as "verified" below. + +- **GeoNetworking Basic/Common/SHB headers, BTP-B header** (`geonet.c`): + verified field-by-field against EN 302 636-4-1. This caught three real + bugs in the previous version: wrong header type (was encoded as + GeoUnicast, HT=2 - now correctly TSB/SINGLE_HOP, HT=5/HST=0), wrong + payload-length calculation (was including the 24-byte extended header, + which it shouldn't), and GN_ADDR being an arbitrary byte string instead of + its actual structure (M-flag + 5-bit station type + reserved + 48-bit + MID = the same link-layer address used in the 802.11 header). +- **BTP-B header** (`geonet.c`): verified against EN 302 636-5-1 - unchanged + from before, structure was already correct. +- **802.11 header, LLC/SNAP** (`dot11p.c`): back to real QoS Data (subtype 8, + 26-byte header with a QoS Control field), matching actual ITS-G5 hardware. + This required abandoning `esp_wifi_80211_tx()` entirely in favor of + `esp_wifi_80211_tx_custom()` (`tx_custom.c`) - see "The TX bypass" above for + the full story and the list of things about it that aren't independently + verified yet for our exact toolchain. +- **DENM ASN.1 UPER payload** (`denm.c`): verified against the real ASN.1 + modules (DENM-PDU-Descriptions.asn, ITS-Container.asn). This caught real + bugs too: `ManagementContainer`, `SituationContainer`, and the inner + `CauseCode` SEQUENCE are all declared with a trailing `...` (extensible), + each of which needs its own leading extension bit that the previous + version omitted entirely; `SituationContainer`'s optional-presence bits + were encoded in the wrong position (at the end instead of the start); and + three field widths were wrong (latitude is 31 bits not 32, the two + position-confidence fields and orientation are 12 bits not 16, altitude + value is 20 bits not 24). All fixed now, with the exact ASN.1 type and + constraint range cited in comments next to each field. +- **Known-missing, by design, not bugs**: `detectionTime`/`referenceTime`/GN + timestamp are hardcoded to 0 (no RTC/NTP wired up - will decode as + 2004-01-01), and `latitude`/`longitude` are hardcoded to 0 (no GNSS wired + up). Both are called out with `TODO` comments in the source. +- **Privacy pseudonym**: the source MAC/GN_ADDR MID is a fixed placeholder, + not rotated. Fine for bench testing; real stacks rotate this every 5-15 min. +- **Unsecured** (no IEEE 1609.2 signing) - matches "no additional + parameters"/easiest, and your sniffer already handles unsecured frames fine + (that's how it decodes RSU SPATEM/MAPEM today). +- **Still a deliberate simplification, not a bug**: single-hop broadcast + (TSB/SINGLE_HOP) instead of GeoBroadcast. Real DENM dissemination + typically uses GeoBroadcast so RSUs/OBUs can forward it across an area - + upgrading to that needs a sequence number + circular geo-area fields in + the GN extended header that this skeleton doesn't build. Fine for a + single-vehicle beacon; revisit if you need multi-hop forwarding. + +## Build + +```powershell +. $env:IDF_PATH\export.ps1 # or use the "ESP-IDF PowerShell" Start Menu shortcut instead +cd C:\Users\Ashin\Documents\micrOBU_workspace\v2x-obu-esp32c5\obu-firmware +idf.py set-target esp32c5 +idf.py build +``` + +### If the linker fails on `phy_11p_set` / `phy_change_channel` + +These are undocumented, reverse-engineered symbols pulled straight from +`libphy.a` - not a public API, so exact names/signatures can shift between +ESP-IDF versions. If you get `undefined reference`, check what's actually +exported for your IDF version: + +```powershell +riscv32-esp-elf-nm $env:IDF_PATH\components\esp_phy\lib\esp32c5\libphy.a | Select-String -Pattern "11p|change_channel" +``` + +Adjust the `extern` declarations at the top of `main.c` to match whatever you +find. + +## Flash + +```powershell +idf.py -p COM5 -b 921600 flash monitor +``` + +You should see in the log: +``` +OCB mode requested @ 5900 MHz - HLN-SV DENM beacon armed, waiting on GPIO4 +``` +Nothing transmits yet - GPIO4 is pulled up (inactive) until you ground it. + +## Wire up the hazard-light input + +For bench testing: a jumper wire or push button between GPIO4 and GND is +enough (active-low - grounding it = "hazard lights on" = beacon active). +For the real thing later: tap whatever signal your recovery vehicle's hazard +switch drives (through a level shifter / opto-isolator if it's 12V vehicle +wiring - don't feed vehicle voltage directly into a GPIO). + +## Event lifecycle (added after cross-checking the Cohda UCA MQTT schema) + +The Cohda "Use Case App" schema (`v2x-uca/output/json/denm`) documents a +`termination` key: "present if the DENM is cancelled or negated." The +firmware now implements this properly: + +- `actionID` (station ID + sequence number) is assigned once per event, on + the rising edge of the hazard-light GPIO, and stays constant across every + repeat of that same event - it does **not** increment every second like an + earlier version of this code did. +- On the falling edge (hazard lights go off), exactly one DENM is sent with + `termination = isCancellation`, referencing that same actionID, then + transmission stops until the next rising edge. + +Expect your sniffer to show the same station+sequence number repeating for +the duration of the event, then one final frame with a termination flag. + +## Validate against your own sniffer + +This is the important part, since a few of the payload bit widths are +best-effort: with the transmitter running (GPIO4 grounded) and your Phase 1 +sniffer board powered on nearby, check the V2X2MAP dashboard. + +Expect to see: +- A new station appear, sending DENM (and only DENM - this skeleton doesn't + send CAM). +- `causeCode` decoding to **stationaryVehicle**, `subCauseCode` **0**. +- `stationType` decoding to **passengerCar**. + +If instead you get "unknown message type," garbage station type, or the +dashboard just doesn't show anything: capture a PCAP from the dashboard's +record button and open it in Wireshark - compare byte-by-byte against a real +captured DENM (like the one from your Cohda OBU) to see exactly where the +two diverge. That's a much faster debug loop than staring at the C code. + +## Validate against the real RSU + +Your own sniffer receiving the frame only tells you the RF/PHY side works - +it doesn't tell you whether a standards-strict stack (Cohda) will accept it. +With the transmitter running and both your sniffer and the Cohda-based RSU +mqtt monitor watching: + +- If **both** see it, and it decodes as a real DENM (not "Type ?"): done, + move on to the "once this round-trips cleanly" list below. +- If your sniffer sees it but doesn't classify it, or the RSU doesn't see it + at all: worth first ruling out the receiver's own reliability (checked + separately - the receiver has shown signs of hanging independent of the + transmitter) before concluding anything about frame format. +- If **neither** sees it: still worth re-checking `esp_wifi_get_channel()`'s + reported value (logged before every TX) - if it's still stuck regardless of + the new `esp_wifi_set_channel(140, ...)` call, that's a stronger signal + the PHY genuinely isn't moving to 5900MHz, independent of frame format. + +Note the receiver firmware (V2X2MAP) never faces the frame-type problem this +project spent a while on: receiving never calls `esp_wifi_80211_tx()`, so +there was never a gate on that side to work around. The `phy_11p_set`/ +`phy_change_channel` PHY setup in `main.c` already mirrors what the receiver +does for getting onto 5.9GHz OCB mode. + +## Once this round-trips cleanly + +Next reasonable steps, in rough order: wire in real GNSS (replaces the 0/0 +lat-long and the GN timestamp), wire in SNTP or GNSS-derived UTC time +(replaces detectionTime/referenceTime), then decide whether you actually +need GeoBroadcast/multi-hop forwarding instead of SHB, then - only if you +need it - look at IEEE 1609.2 signing. diff --git a/docs/01-requirements-traceability.md b/docs/01-requirements-traceability.md new file mode 100644 index 0000000..3e94bee --- /dev/null +++ b/docs/01-requirements-traceability.md @@ -0,0 +1,229 @@ +# Requirements traceability matrix + +Maps every section of `V2X_MicrOBU_Android_App_Requirements_v15.pdf` (37 pages, chapters 0–13) to +its implementation and to the evidence that it works. + +**Status as of 2026-08-25**, commit `cc35994`. Test counts refer to `app/src/test/` (41 tests, +0 failures); bench measurements refer to `05-obu-bench-test-2026-08-25.md`. + +## How to read this + +The project began against the **consider it CiT One OBU** and later added the **ESP32-C5** as a +second hardware path (requirements chapter 13). That transition is the single biggest source of +divergence in this table, and it is deliberate rather than drift: chapter 13 was written to describe +it. Where a requirement was authored assuming the CiT One, the ESP32-C5 path may satisfy it by a +different mechanism, satisfy it only partly, or make it inapplicable. + +Status values: + +| status | meaning | +|---|---| +| **Done** | implemented and exercised on both hardware paths | +| **Done (CiT One)** | implemented; applies only to the CiT One path by design | +| **Done (ESP32-C5)** | implemented; applies only to the ESP32-C5 path by design | +| **Partial** | implemented in part, with the remainder identified | +| **Backlog** | explicitly deferred in the requirements themselves | +| **Superseded** | the ESP32-C5 transition changed the answer; see the note | +| **Not started** | no implementation | + +"Cited" means a source file names the section in a KDoc comment — 23 files do. Absence of a citation +is **not** absence of implementation; several well-covered areas were written before that convention +and are mapped here by inspection. + +--- + +## 0. Project Context & Main Goal + +| § | Title | Status | Implementation | Evidence | +|---|---|---|---|---| +| 0.1 | Technological Innovation Context | n/a | context, not a requirement | — | +| 0.2 | Bicycle Safety Use Case Foundation (C2C-CC) | **Done** | `DenmUseCase.kt` *(cited)*, `UseCaseType.kt` | use cases 1.1/1.2 below | + +**Note.** 0.2 is the origin of the project's central architectural rule: the CAM-based use cases +never generate DENM. `DenmUseCase.kt` records this, and it survived the ESP32 transition unchanged. + +## 1. Phase 01 — Data Collection + +| § | Title | Status | Implementation | Evidence | +|---|---|---|---|---| +| 1.1 | Test Intersections & Use Cases | **Done** | `UseCaseDetectionEngine.kt`, `UseCaseType.kt` *(both cited)* | IMA-S observed firing on hardware, 2026-08-25 | +| 1.2 | C2C-CC Use Case Mapping & Roadmap | **Done** | `UseCaseType.kt`, `UseCaseDetectionConfig.kt` *(both cited)* | 5 use cases enumerated: IMA-B, IMA-S, RTW-B, LTW-B, SMVA/BCW-B | + +## 2. Functional Requirements + +| § | Title | Status | Implementation | Evidence | +|---|---|---|---|---| +| 2.1 | Screens & Features | **Done** | `ui/screens/` — Dashboard, Record, Trips, V2X, Settings | bench screenshots | +| 2.2 | User Interactions | **Done** | `UseCaseAlertPreferences.kt` *(cited)*, Settings screen | per-use-case toggles | +| 2.3 | Backend, Database & Login | **Partial** | `data/db/` (Room: sessions, trips, events, V2X messages) | no login/backend; local-only by design | + +**Gap.** 2.3's backend and login have no implementation. Everything is on-device. Worth stating +explicitly in the thesis as a scope boundary rather than leaving it to be discovered. + +## 3. Non-Functional Requirements + +| § | Title | Status | Implementation | Evidence | +|---|---|---|---|---| +| 3.1 | Android Version & Target Devices | **Done** | `minSdk 29`, `targetSdk 36` | runs on Pixel 9 Pro | +| 3.2 | Performance & Offline | **Done** | offline by construction on the ESP32-C5 path | 210 MB PSS, 43 threads, no GC pressure attributable to the app | + +**ESP32-C5 note.** 3.2's offline requirement is *more* satisfied after the transition: the ESP32-C5 +path needs no MQTT broker and no network at all. + +## 4. Technical Requirements + +| § | Title | Status | Implementation | Evidence | +|---|---|---|---|---| +| 4.1 | Development Environment | **Done** | Gradle 8.10.2, AGP, Android Studio JBR | builds from CLI | +| 4.2 | Architecture | **Done** | `UseCaseDetectionEngine.kt` *(cited)*; MVVM + Hilt + repositories | pure-domain classes unit-tested without Android | +| 4.3 | Key Libraries | **Done** | Compose, Room, Hilt, osmdroid, usb-serial-for-android, Paho MQTT | — | +| 4.4 | Security Requirements (future) | **Backlog** | — | see note | + +**Security note (4.4).** ETSI TS 103 097 message signing is not implemented and is out of scope. The +firmware rejects secured packets (GN `NextHeader=2`) rather than mis-parsing them; 75 such frames +were observed on the bench. The OBU under test runs `ItsGnSecurity = 0`, so this has not blocked +anything. **This is the most likely reviewer question and the answer should be pre-written.** + +## 5. Design Requirements + +| § | Title | Status | Implementation | Evidence | +|---|---|---|---|---| +| 5.1–5.4 | Visual language, system bars, UX, wireframes | **Done** | Compose Material 3, edge-to-edge | screenshots | +| 5.5 | Alert Level Model (C2C-CC three-tier) | **Done** | `AlertLevel.kt`, `UseCaseDetectionConfig.kt`, `UseCaseDetectionEngine.kt` *(all cited)* | Info / Awareness / Warning; observed WARNING on hardware | + +## 6. Sensor Data Streams (Phase 01) + +| § | Title | Status | Implementation | Evidence | +|---|---|---|---|---| +| 6 | Sensor Data Streams | **Done** | `SensorRepository.kt`, `CamUseCaseRepository.kt` *(cited)* | GNSS, accel, gyro, magnetometer, barometer | + +## 7. Recording Session & Data Export + +| § | Title | Status | Implementation | Evidence | +|---|---|---|---|---| +| 7.1 | Recording Mode | **Done** | `TripRecordingService.kt`, `RecordingScreen.kt` | — | +| 7.2 | CSV Export Format | **Done** | `CsvExporter.kt`, `TripExporter.kt` | export includes V2X messages and RSSI | + +## 8. Connectivity + +| § | Title | Status | Implementation | Evidence | +|---|---|---|---|---| +| 8.1 | Connection Phase Roadmap | **Superseded** | `ObuHardware.kt`, `TransportType.kt` | chapter 13 replaced the roadmap | +| 8.2 | Transport Methods Detail | **Partial** | `UsbSerialTransport.kt`, `UsbNetworkDetector.kt`, `MqttRepository.kt` | USB-C both paths; **Bluetooth not implemented** (13.9 keeps it open) | + +## 9. Phase 01 Key Design Principles + +| § | Title | Status | Implementation | Evidence | +|---|---|---|---|---| +| 9 | Key Design Principles | **Done** | pure-domain `domain/` packages, no Android imports | `EventDetectorTest`, `UseCaseDetectionEngine` unit-testable | + +## 10. Phase 02 — OBU Communication (CAM-based use cases) + +| § | Title | Status | Implementation | Evidence | +|---|---|---|---|---| +| 10.1 | USB-C Wired Transport | **Done (CiT One)** | `UsbNetworkDetector.kt` (IP over USB tethering) | superseded on the ESP32-C5 path by USB serial | +| 10.2 | CAM Reception & Use Case Detection | **Done** | `Cam.kt`, `CamUseCaseRepository.kt`, `MqttViewModel.kt`, `MqttTopicViewerScreen.kt` *(all cited)* | 1244 CAMs decoded in 305 s, 0 failures | +| 10.2.1 | Detection Algorithm Detail | **Done** | `UseCaseDetectionEngine.kt`, `UseCaseDetectionConfig.kt` | IMA-S fired with TTC 4.8 s on hardware | +| 10.3 | Test and Verification Procedure | **Done** | `CamParser.kt`, `UseCaseDetectionConfig.kt`, `UseCaseDetectionEngine.kt` *(all cited)* | extended well beyond the original procedure — see chapter 14 below | +| 10.4 | New/Updated UI Elements | **Done** | `MqttTopicViewerScreen.kt` *(cited)* | alert panel, list/topics/map | +| 10.5 | Phase 02 Key Technical Decisions | **Done** | `DenmUseCase.kt` | DENM decoupled from sensor triggers | + +**Transition note (10.1).** Phase 02 assumed IP-over-USB tethering to the CiT One. The ESP32-C5 path +uses a custom framed serial protocol over USB CDC instead. Both are "USB-C wired transport", but they +share no code. The requirement is satisfied twice, by different means. + +## 11. Phase A — Trip Recording & Cyclist Event Detection + +| § | Title | Status | Implementation | Evidence | +|---|---|---|---|---| +| 11.1 | Orientation-Independent Sensor Strategy | **Done** | `SensorRepository.kt` (magnitude-based) | — | +| 11.2 | Running Standard Deviation Event Detector | **Done** | `EventDetector.kt`, `RunningStats.kt` | **18 unit tests, 0 failures** | +| 11.3 | Trip Recording Architecture | **Done** | `TripRepository.kt`, `TripRecordingService.kt` *(cited)* | — | +| 11.4 | Data Model | **Done** | `data/db/` Room entities *(cited)* | — | +| 11.5 | New UI Elements for Phase A | **Done** | `TripHistoryScreen.kt`, `TripReviewScreen.kt` | — | +| 11.6 | Phase A Success Criteria | **Partial** | — | needs a real ride; see Open Items | + +**Correction note (11.2).** Four `EventDetectorTest` cases had been failing since the initial commit. +Investigation (2026-08-25) established the **detector was correct and the tests were wrong**: they +described stimuli the detector cannot physically see, because they ignored the settling time of the +rolling standard-deviation window. Tests corrected, assertions unchanged, detector untouched. This is +worth reporting — it is a finding about test design, not a defect. + +## 12. Future Architecture & Open Design Questions + +| § | Title | Status | Notes | +|---|---|---|---| +| 12.1 | Multi-Vehicle Handling & Notification Limits | **Partial** | engine tracks per-station history; no notification cap | +| 12.2 | Intersection Ambiguity: Traffic-Light State | **Partial — advanced** | **SPATEM now decoded** (`SpatemUperCodec.kt`), see below | +| 12.3 | Shared-Road / Bike Lane Awareness Dataset | **Not started** | would need MAPEM lane geometry | +| 12.4 | Geo-Server for Crowdsourced Trajectory Data | **Not started** | related to the 2.3 backend gap | +| 12.5 | Geofencing Around High-Risk Intersections | **Not started** | — | +| 12.6 | Sensor Fusion Roadmap (Kalman Filter) | **Not started** | — | +| 12.7 | EventDetector Evolution | **Not started** | — | + +**12.2 is the notable movement.** The requirements listed traffic-light state as future/backlog. It +is now partly delivered: SPATEM is received over the air and decoded to per-signal-group phase and +timing, validated against 79,042 real messages. What remains is the *association* problem — knowing +which signal group applies to the rider's lane — which needs MAPEM geometry (12.3). Worth presenting +as a backlog item advanced ahead of schedule, with the remaining half named precisely. + +## 13. Phase 03 — ESP32-C5 Dual-OBU & Phone-Generated CAM + +The transition chapter. Cited by **16 source files**, more than any other. + +| § | Title | Status | Implementation | Evidence | +|---|---|---|---|---| +| 13.1 | ESP32-C5 as a Second OBU Option | **Done** | `obu-firmware/`, `ObuHardware.kt` | bench campaign T1–T9 | +| 13.2 | Transport: USB-C to ESP32 | **Done** | `SerialFrame.kt`, `serial_link.c` | 2868 frames / 305 s, 0 errors | +| 13.3 | Settings: OBU Hardware Selection | **Done** | `ObuHardwarePreferences.kt`, Settings | both paths selectable | +| 13.4 | DENM Trigger Retained for CiT One Only | **Done (CiT One)** | `MqttTopicViewerScreen.kt` gating | trigger hidden on the ESP32-C5 path | +| 13.5 | Phone-Generated CAM | **Done (ESP32-C5)** | `PhoneCamBuilder.kt`, `CamUperCodec.kt`, `CamTransmitLoop.kt` | **golden-byte test**; 26 own CAMs verified off-air by an independent decoder | +| 13.6 | Adaptive CAM Transmission Rate | **Partial** | `CamTransmitConfig.kt` | fixed 1 Hz pinger; adaptive rate not implemented | +| 13.7 | UI Updates: Map View & Dashboard | **Done** | `V2xLiveMapView.kt`, `DashboardScreen.kt` | screenshots | +| 13.8 | Detection Engine: Unchanged for Reception | **Done** | `UseCaseDetectionEngine.kt` | engine is transport-agnostic; confirmed by inspection — zero SPATEM/MAPEM references | +| 13.9 | Bluetooth Transport (Open Discussion) | **Not started** | — | remains open, as the requirement says | + +**13.8 is the load-bearing claim of the transition** and it holds: the same detection engine serves +both hardware paths, fed by `CamUseCaseRepository` from either MQTT or serial. That is what makes the +ESP32-C5 a drop-in second OBU rather than a fork of the application. + +--- + +## Beyond the requirements + +Work delivered that chapter 13 does not cover, because it postdates v15 of the document. For a +publication these are contributions rather than scope creep, and they need their own section. + +| area | what | evidence | +|---|---|---| +| DENM over-the-air receive | GeoBroadcast unwrapping, `DenmUperCodec` | 1885 DENMs cross-checked, 0 mismatches | +| SPATEM receive | `SpatemUperCodec`, live signal phase UI | 79,042 messages cross-checked, 0 mismatches | +| RSU CAM support | `rsuContainerHighFrequency` decoding | 611 RSU CAMs decoded on the bench | +| Verification methodology | independent ETSI oracle, golden bytes, real captures | three encoding bugs found that self-consistent tests structurally cannot catch | + +## Open items + +Ranked by what a reviewer is most likely to probe. + +1. **11.6 Phase A success criteria** — needs a real ride. The bench proves reception; it cannot + prove the use case behaves correctly with two genuinely moving stations. This is the main + remaining evidence gap for the central claim. +2. **4.4 Security** — no message signing. Pre-write the answer. +3. **2.3 Backend & login** — not implemented; state as a scope boundary. +4. **13.6 Adaptive CAM rate** — fixed 1 Hz. +5. **8.2 / 13.9 Bluetooth** — not implemented; the requirements leave it open. +6. **12.2 remainder** — signal-group-to-lane association needs MAPEM. + +## Known limitations carried deliberately + +Documented decisions, not oversights. Each has its reasoning recorded in the commit history and in +`05-obu-bench-test-2026-08-25.md`. + +- **512-byte serial payload cap.** ~70% of *road* RSU SPATEMs would be dropped. Accepted: the + intersection use case is CAM-driven and needs none of it, and raising the cap would put a + measured, zero-failure chain at risk for an add-on. CAM 26–211 B and DENM 402 B both fit. +- **No MAPEM decoder.** Nothing on air transmits it; it exists only in recorded drive data. +- **Secured messages rejected**, not mis-parsed. +- **No auto-reconnect** after USB re-enumeration; requires a manual Connect. +- **Station IDs rotate** (observed twice within one session), so they cannot identify a physical + unit over time. diff --git a/docs/MicrOBU-Technical-Documentation.docx b/docs/MicrOBU-Technical-Documentation.docx new file mode 100644 index 0000000..7a13134 Binary files /dev/null and b/docs/MicrOBU-Technical-Documentation.docx differ diff --git a/docs/MicrOBU-User-Guide.docx b/docs/MicrOBU-User-Guide.docx new file mode 100644 index 0000000..2678d82 Binary files /dev/null and b/docs/MicrOBU-User-Guide.docx differ diff --git a/docs/references.bib b/docs/references.bib new file mode 100644 index 0000000..4b394fe --- /dev/null +++ b/docs/references.bib @@ -0,0 +1,151 @@ +% MicrOBU standards and source references. +% Collected for the technical documentation and any later publication. +% Keep entry keys stable once cited. + +@techreport{etsi_en_302_637_2, + author = {{ETSI}}, + title = {Intelligent Transport Systems (ITS); Vehicular Communications; Basic Set of Applications; Part 2: Specification of Cooperative Awareness Basic Service}, + institution = {European Telecommunications Standards Institute}, + type = {{EN}}, + number = {302 637-2}, + note = {CAM encode and decode}, +} + +@techreport{etsi_en_302_637_3, + author = {{ETSI}}, + title = {Intelligent Transport Systems (ITS); Vehicular Communications; Basic Set of Applications; Part 3: Specifications of Decentralized Environmental Notification Basic Service}, + institution = {European Telecommunications Standards Institute}, + type = {{EN}}, + number = {302 637-3}, + note = {DENM decode and the HLN-SV transmit profile}, +} + +@techreport{etsi_ts_103_301, + author = {{ETSI}}, + title = {Intelligent Transport Systems (ITS); Vehicular Communications; Basic Set of Applications; Facilities layer protocols and communication requirements for infrastructure services}, + institution = {European Telecommunications Standards Institute}, + type = {{TS}}, + number = {103 301}, + note = {SPATEM and MAPEM}, +} + +@techreport{sae_j2735, + author = {{SAE International}}, + title = {V2X Communications Message Set Dictionary}, + institution = {SAE International}, + type = {Standard}, + number = {J2735}, + note = {SPATEM and MAPEM message content}, +} + +@techreport{etsi_ts_102_894_2, + author = {{ETSI}}, + title = {Intelligent Transport Systems (ITS); Users and applications requirements; Part 2: Applications and facilities layer common data dictionary}, + institution = {European Telecommunications Standards Institute}, + type = {{TS}}, + number = {102 894-2}, + note = {Common data dictionary for all messages}, +} + +@techreport{etsi_en_302_636_4_1, + author = {{ETSI}}, + title = {Intelligent Transport Systems (ITS); Vehicular Communications; GeoNetworking; Part 4: Geographical addressing and forwarding for point-to-point and point-to-multipoint communications; Sub-part 1: Media-Independent Functionality}, + institution = {European Telecommunications Standards Institute}, + type = {{EN}}, + number = {302 636-4-1}, + note = {GeoNetworking Basic, Common and SHB headers; verified field by field}, +} + +@techreport{etsi_en_302_636_5_1, + author = {{ETSI}}, + title = {Intelligent Transport Systems (ITS); Vehicular Communications; GeoNetworking; Part 5: Transport Protocols; Sub-part 1: Basic Transport Protocol}, + institution = {European Telecommunications Standards Institute}, + type = {{EN}}, + number = {302 636-5-1}, + note = {BTP-B header}, +} + +@techreport{etsi_ts_103_248, + author = {{ETSI}}, + title = {Intelligent Transport Systems (ITS); GeoNetworking; Port Numbers for the Basic Transport Protocol (BTP)}, + institution = {European Telecommunications Standards Institute}, + type = {{TS}}, + number = {103 248}, + note = {BTP destination ports 2001, 2002, 2003, 2004}, +} + +@techreport{etsi_ts_103_097, + author = {{ETSI}}, + title = {Intelligent Transport Systems (ITS); Security; Security header and certificate formats}, + institution = {European Telecommunications Standards Institute}, + type = {{TS}}, + number = {103 097}, + note = {Message signing; not implemented in this project}, +} + +@techreport{ieee_802_11_ocb, + author = {{IEEE}}, + title = {IEEE Standard for Information Technology; Telecommunications and Information Exchange between Systems; Local and Metropolitan Area Networks; Specific Requirements; Part 11: Wireless LAN Medium Access Control (MAC) and Physical Layer (PHY) Specifications}, + institution = {Institute of Electrical and Electronics Engineers}, + type = {Standard}, + number = {802.11}, + note = {Operation outside the context of a BSS (OCB), the frame layer used by ITS-G5}, +} + +@techreport{c2ccc_wp_2324, + author = {{Car 2 Car Communication Consortium}}, + title = {Bicycle Safety Use Cases}, + institution = {Car 2 Car Communication Consortium}, + type = {White Paper}, + number = {C2CCC\_WP\_2324}, + version = {1.0}, + year = {2026}, + month = {5}, + note = {Source of the five in-scope use cases: IMA-B, IMA-S, RTW-B, LTW-B, SMVA/BCW-B}, +} + +@techreport{microbu_requirements_v15, + author = {{HAW Hamburg}}, + title = {V2X MicrOBU Android App Requirements}, + institution = {HAW Hamburg, Urban Mobility Lab}, + version = {15}, + note = {The requirements baseline, chapters 0 to 13, 37 pages}, +} + +@manual{considerit_mqtt_api_v6, + author = {{consider it GmbH}}, + title = {CiT MQTT API Documentation}, + version = {6}, + year = {2025}, + month = {2}, + note = {The MQTT API used on the CiT One hardware path}, +} + +@manual{considerit_tx_use_cases_v4, + author = {{consider it GmbH}}, + title = {CiT Transmit Use Cases}, + version = {4}, + year = {2025}, + month = {2}, + note = {Transmit use case definitions including HLN-SV, causeCode 94}, +} + +@misc{considerit_cits_parser, + author = {{consider it GmbH}}, + title = {{C-ITS-Parser}}, + howpublished = {\url{https://github.com/consider-it/C-ITS-Parser}}, + note = {Commit f457426efc2486fac49a02fc9a1c8c7762d160e9, MIT licensed. Source of the seven vendored ASN.1 modules in \texttt{asn1/}}, +} + +@misc{asn1tools, + title = {{asn1tools}}, + howpublished = {Python package, version 0.167.0}, + note = {The independent ASN.1 implementation used as the verification oracle}, +} + +@misc{its_g5_receiver_firmware_txenabled, + author = {{opentrafficmap}}, + title = {{its-g5-receiver-firmware\_txenabled}}, + howpublished = {\url{https://codeberg.org/opentrafficmap/its-g5-receiver-firmware_txenabled}}, + note = {Transmit-enabled fork of the receiver firmware; source of the raw transmit bypass in \texttt{tx\_custom.c}}, +}