Compare commits
35 Commits
73eff1c5d6
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| e47468fc98 | |||
| 9452708e80 | |||
| cff32a7190 | |||
| 15c7d6ce84 | |||
| c31af2d5e3 | |||
| e8ba581985 | |||
| 642be2a5e1 | |||
| 0af2c956e4 | |||
| b52a7dcedb | |||
| 4cbac36fbe | |||
| 5d186bfd5d | |||
| 229d796cfd | |||
| 5feba8b354 | |||
| 0eab0e660e | |||
| 34140cc666 | |||
| 77d6b79c6b | |||
| 140ad0d3ba | |||
| e6a1d7f79f | |||
| b79820dfce | |||
| aff9ae3f9f | |||
| 75fec3b4c2 | |||
| 635e2d1304 | |||
| f7c7a3533a | |||
| a372f16ebd | |||
| ac09363b04 | |||
| 7dd6a4c467 | |||
| 0862eb5423 | |||
| f07e8071e4 | |||
| 4a69d1506a | |||
| dbdaa901cc | |||
| 1f91b7ee16 | |||
| d9d942745b | |||
| 7653cb609c | |||
| 88c926c58a | |||
| b55ccba268 |
@@ -0,0 +1,17 @@
|
||||
# Cross-repo consistency (yukart/tool-crosscheck): decisions, topics, doc references,
|
||||
# service lists and front matter checked against their source in every yukart repo.
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
crosscheck:
|
||||
# Gitea dispatches a reusable-workflow call to a runner chosen by the CALLER's runs-on;
|
||||
# without one, any runner may take it (the instance's pi5-runner did, and failed).
|
||||
runs-on: halogen
|
||||
uses: yukart/tool-crosscheck/.gitea/workflows/crosscheck.yml@master
|
||||
with:
|
||||
repo: ${{ github.event.repository.name }}
|
||||
sha: ${{ github.sha }}
|
||||
secrets: inherit
|
||||
@@ -0,0 +1,75 @@
|
||||
name: Rust consumers (L4T)
|
||||
|
||||
# A contracts change can break a service that compiles contracts in (include_str! of
|
||||
# ../contracts/...), and that service's own CI only runs when the service changes.
|
||||
# So: on a change to what services compile in, build every one of them, at its master,
|
||||
# against the pushed contracts. README, docs and checklists are not compiled into any
|
||||
# service (checklists reach the app as a synced copy, which crosscheck watches).
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
paths:
|
||||
- 'schema/**'
|
||||
- '.gitea/workflows/rust-consumers.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
consumers:
|
||||
name: Clippy, test, build, glibc for every service that compiles contracts in
|
||||
# The development Orin (host mode): the same checks as each service's rust.yml.
|
||||
runs-on: l4t-rust
|
||||
steps:
|
||||
- name: Checkout contracts at the push, and every consumer at master
|
||||
env:
|
||||
SERVER: ${{ gitea.server_url }}
|
||||
SHA: ${{ github.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
rm -rf ws && mkdir ws
|
||||
git init -q ws/contracts
|
||||
git -C ws/contracts fetch -q --depth 1 "$SERVER/yukart/contracts.git" "$SHA"
|
||||
git -C ws/contracts checkout -q FETCH_HEAD
|
||||
repos=$(curl -fsS "$SERVER/api/v1/orgs/yukart/repos?limit=50" \
|
||||
| python3 -c 'import json,sys; print(" ".join(r["name"] for r in json.load(sys.stdin) if not r["archived"]))')
|
||||
consumers=""
|
||||
for r in $repos; do
|
||||
[ "$r" = contracts ] && continue
|
||||
# Only Rust repos are cloned in full; the API says whether Cargo.toml exists.
|
||||
curl -fsS -o /dev/null "$SERVER/api/v1/repos/yukart/$r/contents/Cargo.toml" 2>/dev/null || continue
|
||||
git clone -q --depth 1 "$SERVER/yukart/$r.git" "ws/$r"
|
||||
if grep -rqsF '../contracts/' "ws/$r/src" "ws/$r/build.rs"; then
|
||||
consumers="$consumers $r"
|
||||
else
|
||||
rm -rf "ws/$r"
|
||||
fi
|
||||
done
|
||||
[ -n "$consumers" ] || { echo "no Rust repo compiles contracts in"; exit 1; }
|
||||
echo "CONSUMERS=$consumers" >> "$GITHUB_ENV"
|
||||
echo "contracts $(git -C ws/contracts log --oneline -1); consumers:$consumers"
|
||||
rustc --version; head -1 /etc/nv_tegra_release
|
||||
|
||||
- name: Build each consumer
|
||||
run: |
|
||||
set -euo pipefail
|
||||
failed=""
|
||||
for r in $CONSUMERS; do
|
||||
echo "::group::$r $(git -C ws/$r log --oneline -1)"
|
||||
# Each repo's own cache, shared with its rust.yml: the runner runs one job at a time.
|
||||
export CARGO_TARGET_DIR="$HOME/target/$r"
|
||||
if ( cd "ws/$r" \
|
||||
&& cargo clippy --release --all-targets --locked -- -D warnings \
|
||||
&& cargo test --release --locked \
|
||||
&& cargo build --release --locked \
|
||||
&& for b in $(cargo metadata --format-version 1 --no-deps | python3 -c '
|
||||
import json, sys
|
||||
for p in json.load(sys.stdin)["packages"]:
|
||||
for t in p["targets"]:
|
||||
if "bin" in t["kind"]: print(t["name"])'); do
|
||||
scripts/check-glibc.sh "$CARGO_TARGET_DIR/release/$b" || exit 1
|
||||
done ); then
|
||||
echo "::endgroup::"; echo "$r: ok"
|
||||
else
|
||||
echo "::endgroup::"; echo "$r: FAILED"; failed="$failed $r"
|
||||
fi
|
||||
done
|
||||
[ -z "$failed" ] || { echo "failed:$failed"; exit 1; }
|
||||
@@ -17,9 +17,14 @@ definitions from here.
|
||||
|---|---|
|
||||
| [`schema/topics.json`](schema/topics.json) | Topic namespace: payload kind, retention, rate, publisher |
|
||||
| [`schema/envelope.json`](schema/envelope.json) | JSON Schema for the decoded-signal envelope |
|
||||
| [`schema/signals.json`](schema/signals.json) | Signal registry — the single source for name, unit, type and range |
|
||||
| [`schema/signals.json`](schema/signals.json) | Signal registry — the single source for name, unit, type and range, and for where each decoded Z27AG signal sits in its CAN frame (`can` block) |
|
||||
| [`docs/BOUNDARY.md`](docs/BOUNDARY.md) | Display boundary protocol: what crosses between `svc-gw` and the app |
|
||||
| [`checklists/`](checklists/) | Recording checklists as data; the app bundles exact copies (its **Captures** page) |
|
||||
|
||||
The Z27AG DBC lands here too, once Phase 2 produces one.
|
||||
Decoded Z27AG signals (source `z27ag`) carry their frame layout in `signals.json` itself,
|
||||
so name, unit and bits cannot drift apart. A DBC for tools like SavvyCAN or cantools,
|
||||
if one is wanted, is generated from it, never edited by hand. Findings behind each
|
||||
layout: [`docs/impl/reports/`](docs/impl/reports/).
|
||||
|
||||
- For implementation practice guidelines, refer to the [guidelines](docs/GUIDELINES.md);
|
||||
- For implementation plans and history, refer to [them](docs/impl/);
|
||||
|
||||
@@ -0,0 +1,548 @@
|
||||
{
|
||||
"id": "z27ag-captures",
|
||||
"version": 5,
|
||||
"title": "Z27AG decoding captures",
|
||||
"about": "docs/impl/plans/Z27AG-CAPTURES.md",
|
||||
"mark_format": "<step> <action>[ #<n>] start|end for a span, <step> <action>[ #<n>] for a point",
|
||||
"sections": [
|
||||
{
|
||||
"title": "Parked, ignition on, engine off, handbrake on",
|
||||
"steps": [
|
||||
{
|
||||
"id": "1",
|
||||
"title": "Baseline",
|
||||
"do": "Touch nothing.",
|
||||
"finds": "the resting value of everything",
|
||||
"actions": [
|
||||
{
|
||||
"label": "baseline",
|
||||
"hold_s": 20
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"title": "Accelerator",
|
||||
"do": "Slowly to the floor over 5 s, hold 3 s, slowly back. Start as the foot moves, end when it is off.",
|
||||
"finds": "pedal position, with no rpm to confuse it",
|
||||
"actions": [
|
||||
{
|
||||
"label": "accel"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"title": "Brake",
|
||||
"do": "Start as you press, hold 3 s, end as you release.",
|
||||
"finds": "brake switch, maybe pressure (engine off: the pedal goes hard after a press or two, so pressure fades over the repeats)",
|
||||
"actions": [
|
||||
{
|
||||
"label": "brake",
|
||||
"hold_s": 3,
|
||||
"repeat": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "4",
|
||||
"title": "Clutch",
|
||||
"do": "Start as you press, hold 3 s, end as you release.",
|
||||
"finds": "clutch switch",
|
||||
"actions": [
|
||||
{
|
||||
"label": "clutch",
|
||||
"hold_s": 3,
|
||||
"repeat": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "5",
|
||||
"title": "Steering",
|
||||
"do": "Centre, full left, full right, back to centre, slowly. Engine off is cleaner but heavy without power steering: step 23 on the jack stands does it properly.",
|
||||
"finds": "steering angle",
|
||||
"actions": [
|
||||
{
|
||||
"label": "steer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "6",
|
||||
"title": "Gears",
|
||||
"do": "Engine off, clutch down throughout. Start when the gear is in, end as you take it out.",
|
||||
"finds": "reverse (likely as the reverse light); no other gearbox signal expected",
|
||||
"actions": [
|
||||
{
|
||||
"label": "gear 1",
|
||||
"hold_s": 3
|
||||
},
|
||||
{
|
||||
"label": "gear 2",
|
||||
"hold_s": 3
|
||||
},
|
||||
{
|
||||
"label": "gear 3",
|
||||
"hold_s": 3
|
||||
},
|
||||
{
|
||||
"label": "gear 4",
|
||||
"hold_s": 3
|
||||
},
|
||||
{
|
||||
"label": "gear 5",
|
||||
"hold_s": 3
|
||||
},
|
||||
{
|
||||
"label": "gear R",
|
||||
"hold_s": 3
|
||||
},
|
||||
{
|
||||
"label": "neutral",
|
||||
"hold_s": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "7",
|
||||
"title": "Handbrake",
|
||||
"do": "Hold the foot brake. Start as you release the handbrake, end as you pull it back up.",
|
||||
"finds": "handbrake switch",
|
||||
"actions": [
|
||||
{
|
||||
"label": "handbrake",
|
||||
"hold_s": 3,
|
||||
"repeat": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "8",
|
||||
"title": "Indicators",
|
||||
"do": "Start when switched on, end when off.",
|
||||
"finds": "indicator states",
|
||||
"actions": [
|
||||
{
|
||||
"label": "left",
|
||||
"hold_s": 5
|
||||
},
|
||||
{
|
||||
"label": "right",
|
||||
"hold_s": 5
|
||||
},
|
||||
{
|
||||
"label": "hazard",
|
||||
"hold_s": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "9",
|
||||
"title": "Lights",
|
||||
"do": "Start when switched on, end when off.",
|
||||
"finds": "light states",
|
||||
"actions": [
|
||||
{
|
||||
"label": "side lights",
|
||||
"hold_s": 3
|
||||
},
|
||||
{
|
||||
"label": "dipped",
|
||||
"hold_s": 3
|
||||
},
|
||||
{
|
||||
"label": "main beam",
|
||||
"hold_s": 3
|
||||
},
|
||||
{
|
||||
"label": "flash",
|
||||
"point": true
|
||||
},
|
||||
{
|
||||
"label": "front fogs",
|
||||
"hold_s": 3
|
||||
},
|
||||
{
|
||||
"label": "rear fog",
|
||||
"hold_s": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "10",
|
||||
"title": "Doors",
|
||||
"do": "Start as it opens, end when it is shut. Skip what the car does not have.",
|
||||
"finds": "door switches",
|
||||
"actions": [
|
||||
{
|
||||
"label": "driver door"
|
||||
},
|
||||
{
|
||||
"label": "passenger door"
|
||||
},
|
||||
{
|
||||
"label": "rear left door"
|
||||
},
|
||||
{
|
||||
"label": "rear right door"
|
||||
},
|
||||
{
|
||||
"label": "boot"
|
||||
},
|
||||
{
|
||||
"label": "bonnet"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "11",
|
||||
"title": "Wipers",
|
||||
"do": "Start when switched on, end when off.",
|
||||
"finds": "wiper states",
|
||||
"actions": [
|
||||
{
|
||||
"label": "wipers int",
|
||||
"hold_s": 5
|
||||
},
|
||||
{
|
||||
"label": "wipers low",
|
||||
"hold_s": 5
|
||||
},
|
||||
{
|
||||
"label": "wipers high",
|
||||
"hold_s": 5
|
||||
},
|
||||
{
|
||||
"label": "washer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "12",
|
||||
"title": "A/C and blower",
|
||||
"do": "A/C: start on, end off. Blower: start as you turn it up, end when it is back down.",
|
||||
"finds": "A/C request, blower",
|
||||
"actions": [
|
||||
{
|
||||
"label": "ac",
|
||||
"hold_s": 5
|
||||
},
|
||||
{
|
||||
"label": "blower"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "13",
|
||||
"title": "Seatbelt",
|
||||
"do": "Start as it clicks in, end as it comes out.",
|
||||
"finds": "belt switch",
|
||||
"actions": [
|
||||
{
|
||||
"label": "belt",
|
||||
"hold_s": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Engine running, parked",
|
||||
"steps": [
|
||||
{
|
||||
"id": "14",
|
||||
"title": "Engine start",
|
||||
"do": "Cold start if possible: mark as the starter turns, then idle 60 s.",
|
||||
"finds": "starting, coolant and oil warming, idle control; after standing overnight, every temperature sensor reads the outside temperature at the first frames, which scales 449 bytes 0, 1, 3 against OBD-II intake and coolant",
|
||||
"actions": [
|
||||
{
|
||||
"label": "engine start",
|
||||
"point": true
|
||||
},
|
||||
{
|
||||
"label": "idle",
|
||||
"hold_s": 60
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "15",
|
||||
"title": "Revs",
|
||||
"do": "Neutral, clutch up. Start when the revs are steady at the target, end as you leave it.",
|
||||
"finds": "engine speed against OBD rpm, throttle against pedal",
|
||||
"actions": [
|
||||
{
|
||||
"label": "rpm 1500",
|
||||
"hold_s": 5
|
||||
},
|
||||
{
|
||||
"label": "rpm 2000",
|
||||
"hold_s": 5
|
||||
},
|
||||
{
|
||||
"label": "rpm 3000",
|
||||
"hold_s": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "16",
|
||||
"title": "Blips",
|
||||
"do": "Three sharp blips of the throttle.",
|
||||
"finds": "response timing of throttle, rpm, pressure",
|
||||
"actions": [
|
||||
{
|
||||
"label": "blips"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "17",
|
||||
"title": "A/C under load",
|
||||
"do": "A/C on for 20 s, then off.",
|
||||
"finds": "compressor clutch, idle-up",
|
||||
"actions": [
|
||||
{
|
||||
"label": "ac",
|
||||
"hold_s": 20
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "18",
|
||||
"title": "Radiator fan",
|
||||
"do": "Idle until the fan cuts in, if it will; mark when you hear it.",
|
||||
"finds": "fan state, coolant threshold",
|
||||
"actions": [
|
||||
{
|
||||
"label": "fan",
|
||||
"point": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "32",
|
||||
"title": "Temperature dial",
|
||||
"do": "Engine warm (the heater needs it), blower on, A/C as it is. Sweep: from coldest to hottest a click or two at a time, a few seconds at each, then back; start at coldest, end back at coldest. Then hold hottest 5 min, and coldest 5 min, windows shut.",
|
||||
"finds": "the dial setting on CAN (a byte stepping with each click); whether 449 byte 0 is the cabin temperature (it should drift up over the hot hold, down over the cold one)",
|
||||
"actions": [
|
||||
{
|
||||
"label": "dial sweep"
|
||||
},
|
||||
{
|
||||
"label": "hold hottest",
|
||||
"hold_s": 300
|
||||
},
|
||||
{
|
||||
"label": "hold coldest",
|
||||
"hold_s": 300
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Driving (a passenger presses, or skip the marks)",
|
||||
"steps": [
|
||||
{
|
||||
"id": "19",
|
||||
"title": "Steady speeds",
|
||||
"do": "Where safe, hold each speed 10 s. Or do step 24 on the jack stands instead.",
|
||||
"finds": "road and wheel speeds against OBD and GNSS speed",
|
||||
"actions": [
|
||||
{
|
||||
"label": "steady 20",
|
||||
"hold_s": 10
|
||||
},
|
||||
{
|
||||
"label": "steady 40",
|
||||
"hold_s": 10
|
||||
},
|
||||
{
|
||||
"label": "steady 60",
|
||||
"hold_s": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "20",
|
||||
"title": "Stops",
|
||||
"do": "Start as you brake, end when stopped.",
|
||||
"finds": "brake pressure, deceleration",
|
||||
"actions": [
|
||||
{
|
||||
"label": "gentle stop"
|
||||
},
|
||||
{
|
||||
"label": "firm stop"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "21",
|
||||
"title": "Corners",
|
||||
"do": "A few corners each way.",
|
||||
"finds": "yaw, lateral g, wheel speed differences",
|
||||
"actions": [
|
||||
{
|
||||
"label": "corners"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "22",
|
||||
"title": "Through the box",
|
||||
"do": "Each gear in turn.",
|
||||
"finds": "gear from rpm / speed",
|
||||
"actions": [
|
||||
{
|
||||
"label": "gears"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "On jack stands (front wheels up, rears chocked, handbrake off)",
|
||||
"steps": [
|
||||
{
|
||||
"id": "23",
|
||||
"title": "Steering, unpowered",
|
||||
"do": "Engine off, wheels in the air. Centre, full left, full right, back to centre, slowly.",
|
||||
"finds": "steering angle with nothing else moving",
|
||||
"actions": [
|
||||
{
|
||||
"label": "steer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "24",
|
||||
"title": "Wheel speeds",
|
||||
"do": "Engine running. Hold each speedometer reading 10 s; start when it is steady, end as you leave it.",
|
||||
"finds": "vehicle and wheel speeds (fronts turning, rears still), speedometer against CAN, rpm against gear. The ABS light may come on; that is a find too.",
|
||||
"actions": [
|
||||
{
|
||||
"label": "2nd 20",
|
||||
"hold_s": 10
|
||||
},
|
||||
{
|
||||
"label": "2nd 40",
|
||||
"hold_s": 10
|
||||
},
|
||||
{
|
||||
"label": "3rd 40",
|
||||
"hold_s": 10
|
||||
},
|
||||
{
|
||||
"label": "3rd 60",
|
||||
"hold_s": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "25",
|
||||
"title": "Brake from speed",
|
||||
"do": "3rd at 40 on the speedometer, clutch down, brake to a stop. Start as you brake, end when the wheels stop.",
|
||||
"finds": "brake with the engine running (vacuum assist), wheel deceleration",
|
||||
"actions": [
|
||||
{
|
||||
"label": "brake from 40",
|
||||
"repeat": 2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "26",
|
||||
"title": "Reverse",
|
||||
"do": "Reverse gear, wheels turning gently, 10 s.",
|
||||
"finds": "reverse light, wheel direction (if the speeds are signed)",
|
||||
"actions": [
|
||||
{
|
||||
"label": "reverse",
|
||||
"hold_s": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "31",
|
||||
"title": "One wheel at a time, by hand",
|
||||
"do": "Ignition on, engine off, neutral. A front: hold the other front still (open differential: in neutral the turned wheel spins the gearbox, not its partner) and spin this one briskly by hand, forward, about one turn a second; start as it turns, end as you stop. A rear, if you raise it: rears turn freely, no holding needed. Fronts are enough if the rears are not up.",
|
||||
"finds": "which word of 235 is which wheel, one at a time (the drives already point to bytes 0-1 front right, 2-3 front left, 4-5 rear right, 6-7 rear left); also the slowest speed the ABS reports",
|
||||
"actions": [
|
||||
{
|
||||
"label": "front left by hand",
|
||||
"hold_s": 10
|
||||
},
|
||||
{
|
||||
"label": "front right by hand",
|
||||
"hold_s": 10
|
||||
},
|
||||
{
|
||||
"label": "rear left by hand",
|
||||
"hold_s": 10
|
||||
},
|
||||
{
|
||||
"label": "rear right by hand",
|
||||
"hold_s": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "IMU calibration (module fixed in its final place)",
|
||||
"steps": [
|
||||
{
|
||||
"id": "27",
|
||||
"title": "IMU level, facing one way",
|
||||
"do": "Parked on the flattest ground at hand, engine off or idling, nobody moving inside. Start, keep still 15 s, end.",
|
||||
"finds": "which way is down for the IMU (with step 28, cancelling the ground's slope)",
|
||||
"actions": [
|
||||
{
|
||||
"label": "imu level A",
|
||||
"hold_s": 15
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "28",
|
||||
"title": "IMU level, turned round",
|
||||
"do": "The same spot, nose the other way: a rough 180° will do. Still again, 15 s.",
|
||||
"finds": "the second half of the level: the slope's tilt reverses, the mounting's does not",
|
||||
"actions": [
|
||||
{
|
||||
"label": "imu level B",
|
||||
"hold_s": 15
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "29",
|
||||
"title": "IMU pull-away, straight",
|
||||
"do": "A straight, flat section. From standstill, a firm pull-away in 1st to about 30. Start as the clutch bites, end as you lift.",
|
||||
"finds": "which way is forward for the IMU",
|
||||
"actions": [
|
||||
{
|
||||
"label": "imu pull away",
|
||||
"repeat": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "30",
|
||||
"title": "IMU braking, straight",
|
||||
"do": "The same section. From about 40, a firm (not emergency) stop. Start as you brake, end when stopped.",
|
||||
"finds": "forward again, from the other side; also braking g",
|
||||
"actions": [
|
||||
{
|
||||
"label": "imu brake",
|
||||
"repeat": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
type: reference
|
||||
---
|
||||
# Display boundary protocol — v0
|
||||
|
||||
Between a display client (`app-flutter`) and [svc-gw](gitea:yukart/svc-gw). The
|
||||
bus topics in `schema/` stay inside the Orin; this is what crosses to the tablet.
|
||||
|
||||
## Transport
|
||||
|
||||
One WebSocket, `ws://<orin>:7700/`, JSON text messages, each with a `"t"` type.
|
||||
The tablet reaches the car Orin at `192.168.55.1` over USB NCM; the app binds to
|
||||
that network itself (see `app-flutter`'s initial plan).
|
||||
|
||||
svc-gw caps its socket's send rate (`SO_MAX_PACING_RATE`, ~5 MB/s; project-yukart
|
||||
decision 28, proposed). Unpaced bursts toward the tablet stall the USB link for
|
||||
seconds; paced traffic does not (app-flutter `docs/impl/reports/LINK-TEST-2026-10-11.md`).
|
||||
State messages are far below the cap; it matters for utterances and anything bulky.
|
||||
|
||||
The client always connects and owns reconnection. svc-gw refuses peers outside
|
||||
loopback, RFC 1918 and the tailnet's 100.64.0.0/10.
|
||||
|
||||
The two channels from decision 24 share the socket but keep different guarantees:
|
||||
|
||||
| | Down (state) | Up (commands) |
|
||||
|---|---|---|
|
||||
| Loss | tolerated; svc-gw drops the oldest when a client lags (64-message queue) | not tolerated; retry until acked |
|
||||
| On reconnect | `hello`, then current state on the next tick | resend anything unacked, same `id` |
|
||||
|
||||
## Down: svc-gw → client
|
||||
|
||||
| `t` | When | Fields |
|
||||
|---|---|---|
|
||||
| `hello` | first message | `proto` (0), `server`, `host`, `boot_id` (the host's, changes every boot), `source` (`bus` \| `mock` \| `none`), `mock`, `hb_ms`, `timeout_ms` |
|
||||
| `hb` | every `hb_ms` | `ts` (Orin epoch ms), `clients` |
|
||||
| `state` | 5 Hz, when there is a source | `seq`, `mock`, `signals`: name → envelope (`schema/envelope.json`) |
|
||||
| `ack` | reply to a `cmd` | `id`, `ok`, `detail`, `dup` (true when answered from the cache) |
|
||||
|
||||
With `source: bus`, `signals` is the newest envelope of every signal svc-gw has
|
||||
seen on `yukart/vehicle/+`, `yukart/host/+`, `yukart/gnss/+` and `yukart/imu/+`. svc-gw marks one
|
||||
`quality: "stale"` when it has not been refreshed for 5 s, or is known only from
|
||||
the broker's retained copy (which may predate a reboot). A signal once seen is
|
||||
never removed.
|
||||
|
||||
`signals` holds only what is currently known. A missing signal is unknown, a
|
||||
`null` value is known-without-value, `quality: "stale"` is an old value. All three
|
||||
are normal and a display must render each distinctly.
|
||||
|
||||
⚠ **`mock: true` must be visible on the display surface** whenever it appears in
|
||||
`hello` or in any envelope. Not in a settings page, not in a log.
|
||||
|
||||
## Up: client → svc-gw
|
||||
|
||||
| `t` | When | Fields |
|
||||
|---|---|---|
|
||||
| `hb` | every `hb_ms` | — |
|
||||
| `cmd` | on user action | `id` (unique, the idempotency key), `cmd`, `args` |
|
||||
|
||||
svc-gw drops a client silent for `timeout_ms` (5 s). The client should treat
|
||||
`timeout_ms` without any downstream message the same way and reconnect: an asleep
|
||||
tablet, or an Orin behind a replugged hub, looks connected at the link layer.
|
||||
|
||||
### Commands
|
||||
|
||||
| `cmd` | `args` | Effect |
|
||||
|---|---|---|
|
||||
| `ping` | — | `ack` with `detail: "pong"` |
|
||||
| `mark` | `text`, `delay_ms` (optional: how long the command waited before this send) | stamped into the open recording; fails with no session open |
|
||||
| `poweroff` | `confirm: "poweroff"`, `boot_id` (from this connection's `hello`) | acks, then shuts the host down cleanly (`systemctl poweroff`) about 2 s later. Refused unless svc-gw runs with `--allow-power` (the Orin's unit only), and when `boot_id` is not the current boot's |
|
||||
| `reboot` | `confirm: "reboot"`, `boot_id` | the same, with `systemctl reboot`; the same flag and refusals |
|
||||
|
||||
A repeated `id` returns the original `ack` (last 256 kept, shared across
|
||||
connections) and never re-runs the command.
|
||||
|
||||
`poweroff` and `reboot` are the exception to retry-until-acked: the ack cache does
|
||||
not survive the restart they cause, so a resend could shut the next boot down (or
|
||||
reboot it again). The client sends them once and does not resend; `boot_id` makes
|
||||
svc-gw refuse one from a previous boot regardless.
|
||||
|
||||
## Not yet
|
||||
|
||||
- TTS utterances (id, chunk sequence, terminator, expiry) — when there is a TTS.
|
||||
- `gnss/fix` as one object (navigation's input): the envelope's `value` does not allow
|
||||
one yet. Displays get the fix as single values on `yukart/gnss/<signal>` instead.
|
||||
- Any version beyond `proto: 0`. A breaking change bumps it; the client checks it.
|
||||
+12
-2
@@ -9,7 +9,7 @@ type: guidelines
|
||||
- ⚠ **The unit appears in both the topic name and the payload, so they can
|
||||
contradict each other.** Generating both from one entry is what stops that; a
|
||||
hand-written `"unit"` reintroduces the failure it was meant to prevent.
|
||||
- Signal names carry their unit (`vehicle_speed_kph`, `coolant_temp_c`), so a unit
|
||||
- Signal names carry their unit (`gnss_speed_kph`, `obd_coolant_temp_c`), so a unit
|
||||
mismatch is visible at the call site rather than one registry lookup away.
|
||||
|
||||
## 2. Two clocks, and they answer different questions
|
||||
@@ -33,4 +33,14 @@ type: guidelines
|
||||
- There is no version in any topic or payload. A recorded session names the
|
||||
contracts tag it was captured under, which is what keeps old captures readable.
|
||||
|
||||
## 6. To be added
|
||||
## 6. A decoded signal's layout lives only in its registry entry
|
||||
- Every `source: z27ag` signal has a `can` block in `signals.json`: frame ID, byte,
|
||||
bit or length, byte order, scale and offset. The decoder reads those blocks and
|
||||
nothing else, so a correction lands in one file and every consumer sees it.
|
||||
- ⚠ **No CAN ID or bit position is written in any service's code.** A layout
|
||||
hard-coded downstream is a second source that silently disagrees the day the
|
||||
registry is corrected.
|
||||
- Each block names the report with its evidence (`evidence`). A layout without
|
||||
evidence is a guess and does not go in.
|
||||
|
||||
## 7. To be added
|
||||
|
||||
@@ -20,20 +20,10 @@ observed traffic. They arrive as DBC entries plus registry rows with
|
||||
`system/<service>` have their fields named in `topics.json` but no JSON Schema
|
||||
yet. They get one when a publisher exists to constrain.
|
||||
|
||||
**The app ↔ `svc-gw` boundary protocol.** Bus topics are defined here, but the
|
||||
two channels across the boundary (latest-wins state down, acknowledged commands
|
||||
up) have no wire format yet. Project-yukart decision 24 (proposed) already
|
||||
fixes what it must carry, because the USB link drops for ~3 s at every power
|
||||
change and a sleeping tablet looks alive at the link layer:
|
||||
|
||||
- a **heartbeat** in both directions, with an agreed interval and timeout, so the
|
||||
Orin reaps a silent client instead of trusting TCP or link state;
|
||||
- an **idempotency key** on every upstream command, so retries across a
|
||||
reconnect never act twice;
|
||||
- for TTS, **utterance id + chunk sequence + terminator + an expiry**, so a
|
||||
reconnect can resume or drop an utterance rather than replay it blindly.
|
||||
|
||||
Defined when `svc-gw` and `app-flutter` first talk, not before.
|
||||
**The app ↔ `svc-gw` boundary protocol** is at v0 in [BOUNDARY.md](../../BOUNDARY.md):
|
||||
heartbeat both ways, idempotency key on every command, mock flagged per message.
|
||||
Still missing from it: the TTS utterance shape (id, chunk sequence, terminator,
|
||||
expiry), defined when a TTS exists to send one.
|
||||
|
||||
## Order
|
||||
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
---
|
||||
type: plan
|
||||
status: ready
|
||||
updated: 2026-10-15
|
||||
---
|
||||
# Z27AG decoding captures: what to do in the car
|
||||
|
||||
Recordings that let the broadcast frames be decoded: one thing changes at a time,
|
||||
each change is marked from the app's **Captures** page, and OBD-II mode 01 values are recorded alongside as the
|
||||
ground truth. Prior art and first candidates: [the prior-art report](../reports/Z27AG-CAN-PRIOR-ART.md).
|
||||
|
||||
## What records what
|
||||
|
||||
| In the session directory | From | Use |
|
||||
|---|---|---|
|
||||
| `can-*.log(.zst)` | the PEAK, every frame, kernel timestamps | what gets decoded (nothing transmits on CAN: OBD-II is on the K-line) |
|
||||
| `vehicle.ndjson` | `decoder` and `obd-poller` via the bus | decoded CAN signals, and the `obd_*` references from the K-line through the ELM327 (rpm, speed, throttle, load, MAF, timing, O2 every round; coolant, intake air, trims every 5 s); `env.timestamp` is the reply's arrival at the Orin |
|
||||
| `events.ndjson` | marks from the app | when each step happened |
|
||||
| `gnss.ndjson` | gpsd (SIM7600) | speed and position for the drive |
|
||||
| `host.ndjson` | `orin-status` via the bus (`yukart/host/*`) | the Orin's own state beside the drive: temperatures, load, power, links, UPS (since 2026-10-16) |
|
||||
| `imu.bin` (`imu.ndjson`) | `imu` via the bus (`yukart/raw/imu`) | every IMU sample, packed (svc-core `imu --export` prints it as JSON lines; sessions before 2026-10-16 and anything that does not pack are in `imu.ndjson`), raw module axes, Orin receive time: calibration (27–30) and, once calibrated, motion for the drive |
|
||||
|
||||
## Operating it from the app
|
||||
|
||||
- **Sessions start by themselves** when the PEAK sees traffic (ignition on) and end
|
||||
15 s after it stops. There is no start/stop button; nothing to do.
|
||||
- **Captures** (top bar) opens the checklist. Each step shows what to do and one big
|
||||
button: **Start** as the action begins, **End** as it ends (a running timer shows
|
||||
the suggested hold); a single moment is one **Mark**. When a step's actions are done
|
||||
the button says **Next step**: a natural pause, about 3 s, before the next.
|
||||
**Redo step** and **Skip step** say so in the recording; the step list (left, or
|
||||
the list icon on a narrow screen) jumps anywhere. Leaving a step mid-action marks
|
||||
that action aborted.
|
||||
- **Check it landed:** under the button, the last mark and `✓`, or `✗ error: no
|
||||
session` when there is no traffic yet (ignition on, PEAK plugged). A mark sent with
|
||||
the link down waits (`unacked`) and goes when it is back, with its delay noted.
|
||||
- The **Mark** button on the main screen is still there for anything off-plan.
|
||||
- **OBD is working** when `obd_engine_rpm`, `obd_coolant_temp_c` and the rest appear
|
||||
in the signal list and move (the ELM on the OBD port, through the Y harness beside
|
||||
the PEAK). If they never appear, see *If OBD stays silent*.
|
||||
|
||||
## The steps
|
||||
|
||||
They are data: [`checklists/z27ag-captures.json`](../../../checklists/z27ag-captures.json),
|
||||
bundled into the app as an exact copy (app-flutter `tool/sync-contracts.sh`;
|
||||
crosscheck fails if the copy drifts). Edit them there; bump `version` when steps
|
||||
change meaning, since every recording names the version it was made with. Before
|
||||
starting: PEAK on the OBD port, Orin up, tablet connected, and `can_pcan` UP (the
|
||||
recorder refuses marks with "no session" otherwise); engine off, doors shut.
|
||||
|
||||
- **Parked, ignition on, engine off, handbrake on** (1–13): baseline, pedals, steering, gears
|
||||
(reverse likely shows as the reverse light: the diesel forfour carries it in
|
||||
`423` byte 1 bit 3; no other gearbox signal expected), handbrake, indicators,
|
||||
lights, doors, wipers, A/C, belt.
|
||||
- **Engine running, parked** (14–18): start and idle, held revs in neutral (no OBD
|
||||
on this car's CAN: the revs are approximate, read off the tacho), blips, A/C load, radiator fan.
|
||||
- **Driving** (19–22): steady speeds, stops, corners, each gear. A passenger presses,
|
||||
or skip the marks: GNSS speed is recorded continuously. The gear is not on CAN and
|
||||
is not inferred (decode report).
|
||||
- **On jack stands** (23–26, since v2): front wheels up, rears chocked, handbrake
|
||||
off. Unpowered steering; speedometer readings held in 2nd and 3rd (fronts turn,
|
||||
rears stay still, which tells the wheels apart); braking from speed with vacuum
|
||||
assist; reverse. Stands replace the driving speeds where a drive is not practical. Step 31
|
||||
(since v4, kept in this section, numbered after the IMU steps so earlier numbers stay): each
|
||||
wheel spun by hand, the other front held still; the rears only if they are raised too.
|
||||
- **IMU calibration** (27–30, since v3): once the IMU is fixed in its final place. Level
|
||||
twice on the same spot, facing one way and then turned round (a rough 180° is fine:
|
||||
the ground's slope reverses and cancels, the mounting's tilt does not); then three
|
||||
firm pull-aways and three firm stops on a straight, flat section (forward, signed by
|
||||
which kind of stretch it is). Any session holds raw IMU samples (`imu.bin`), so
|
||||
these can go in their own short session or with anything else.
|
||||
|
||||
Version 5 (2026-10-15) adds step 32, the temperature dial (engine running section), and the overnight soak to step 14. Version 4 (2026-10-15) adds step 31, one wheel at a time by hand. Version 3 (2026-10-14) adds the IMU calibration section. Version 2 (2026-10-13) adds the stands section and writes down the conditions the
|
||||
first run showed matter: engine off and handbrake on for 1–13, foot brake held for
|
||||
the handbrake step, neutral with the clutch up for the revs.
|
||||
|
||||
The parked steps can be split over several sessions; the drive is the important part.
|
||||
|
||||
## Marks, as recorded
|
||||
|
||||
`events.ndjson` holds each mark's text, stamped on arrival at the recorder:
|
||||
|
||||
| Text | Meaning |
|
||||
|---|---|
|
||||
| `checklist z27ag-captures v1` | before the first mark from the page: which steps these are |
|
||||
| `3 brake #2 start` / `3 brake #2 end` | a span; `#n` only on repeated actions |
|
||||
| `14 engine start` | a point |
|
||||
| `3 brake #2 abort` | a span left open (redo, skip, another step) |
|
||||
| `3 redo`, `3 skip` | the step is being done again / was skipped: use the last attempt |
|
||||
|
||||
## If OBD stays silent
|
||||
|
||||
`yukart/system/obd-poller` says why (visible in `mosquitto_sub -v -t 'yukart/system/#'`
|
||||
on the Orin), and the signal list's `elm_connected` shows the Bluetooth link:
|
||||
|
||||
- `ELM …: … (unplugged, asleep or out of range)`: no Bluetooth link. The ELM in the
|
||||
port? Its light on? A phone app holding it (it takes one connection)? Retried
|
||||
every minute.
|
||||
- `ECU not answering (…): ignition off?`: the ELM is there, the car is not. The
|
||||
adapter's own words are in the brackets (`BUS INIT: ...ERROR`, `NO DATA`).
|
||||
- `ECU silent for 60 s: adapter released`: ignition off for a minute; the poller let
|
||||
go so the ELM can sleep, and tries again every minute.
|
||||
- To see what answers, on the Orin: `sudo systemctl stop yukart-obd-poller`,
|
||||
`yukart-obd-poller --elm $(. /etc/default/yukart-elm; echo $ELM_ADDR) --once`
|
||||
(adapter, protocol, supported PIDs and one value each, with reply times), then
|
||||
`sudo systemctl start yukart-obd-poller`.
|
||||
|
||||
## Afterwards
|
||||
|
||||
**IMU calibration**, on the car Orin, from the session that holds steps 27–30:
|
||||
|
||||
```bash
|
||||
yukart-imu --calibrate /var/lib/yukart/recordings/<session> # print: down, forward, slope, spread
|
||||
yukart-imu --calibrate /var/lib/yukart/recordings/<session> --write # and save /var/lib/yukart/imu/mount.json
|
||||
```
|
||||
|
||||
The service picks the new mounting up within seconds; roll and pitch should then read
|
||||
about 0 parked on level ground. Steps 27–28 alone also work: forward then stays the
|
||||
`--forward` axis, laid flat, until a session with 29–30 is calibrated.
|
||||
|
||||
Sessions are in `/var/lib/yukart/recordings/` on the car Orin. Copy one off with
|
||||
`rsync -a mikkeli-orin-nano:/var/lib/yukart/recordings/<session>/ …` into
|
||||
`yukart-data`, with a line in its README of what was done.
|
||||
@@ -0,0 +1,123 @@
|
||||
---
|
||||
type: report
|
||||
date: 2026-10-09
|
||||
updated: 2026-10-13
|
||||
---
|
||||
# Z27AG CAN: prior art and a first look
|
||||
|
||||
What exists publicly for decoding a Mitsubishi of this age, and what the first
|
||||
10 s capture already shows. The input to Phase 2: not inventing what someone has
|
||||
already published.
|
||||
|
||||
## Short version
|
||||
|
||||
- **Nothing public decodes the Z27 Colt's broadcast frames.** No DBC, no ID list,
|
||||
for the Colt, Colt Ralliart, CZT or Version-R. The broadcast IDs have to be
|
||||
reverse-engineered against our own recordings.
|
||||
- **The smart forfour 454 is the useful sibling.** A sniffed 2006 forfour diesel
|
||||
shares our body/chassis frames (`0C0`, `119`, `312`, …) but not the engine ones;
|
||||
its notes are the first hypothesis for every shared ID.
|
||||
- **Diagnostic requests are well covered.** Mitsubishi's MUT-III protocol is
|
||||
KWP2000 over ISO 15765 (`0x21 <local id>` "mode 21" requests), and OBDb publishes
|
||||
open, machine-readable definitions for many Mitsubishi ECUs. Those are the fast
|
||||
path to named signals, and the ground truth for decoding the broadcast ones.
|
||||
- **Standard OBD-II mode 01** (RPM 0x0C, speed 0x0D, coolant 0x05, …) works on any
|
||||
car of this age and is what `obd-poller` should start with — all nine signals in
|
||||
`schema/signals.json` are mode 01 PIDs.
|
||||
|
||||
## Sources
|
||||
|
||||
### Broadcast frames
|
||||
|
||||
| Source | What it has | Use to us |
|
||||
|---|---|---|
|
||||
| [ECUMASTER ADU application note, Evo X](https://www.ecumaster.cz/download/evoX.pdf) | Evo X OBD port at 500 kbps, pins 6/14, no terminator; a channel list (rpm, speed, tps, clt, wheel speeds, indicators, lights, gear) but the IDs only inside their `.canx` file | Same bus conventions as ours; no IDs |
|
||||
| [EvolutionM "Evo X real CAN packets"](https://www.evolutionm.net/forums/evo-x-engine-management-tuning-forums/552550-evo-x-real-can-packets.html) (forum, blocks automated fetch) | Evo X: RPM in `0x308` bytes 1–2; throttle in `0x210` bytes 0–1 | `0x210` exists on our bus too, but with a **different layout** (6 bytes; the RPM-like value is in bytes 4–5). Evo X IDs do not transfer |
|
||||
| [awesome-automotive-can-id](https://github.com/iDoka/awesome-automotive-can-id) | Index of public CAN ID collections; Mitsubishi: Lancer 2008 ([habr, RU](https://habr.com/ru/post/448658/)), i-MiEV | Lancer article covers diagnostics, not broadcast (below) |
|
||||
| [billyjack2/CAN-DBC-Collection](https://github.com/billyjack2/CAN-DBC-Collection) `MITSUBISHI/COLT/can1v3.dbc` | AiM logger driver "Mitsubishi Colt" as a DBC: wheel speeds `200`/`208`, pedal `210` byte 2, brake switch `300`, rpm `308`, speed and fuel `412`, battery `416`, coolant `608` | The EU Colt / forfour engine-side set (it matches the forfour notes below), **not the Z27's**: of its IDs only `210` and `300` exist here, and neither fits — `210` byte 2 stays `08` through a throttle blip while bytes 4–5 follow rpm; `300` is 4 bytes, not 8 |
|
||||
| [I2000C/SmartCruiseControl](https://github.com/I2000C/SmartCruiseControl) `doc/CAN bus reverse engineering.md` (MIT) | Smart forfour 454, 2006, **1.5 CDI diesel**, sniffed: ~30 IDs with lengths and byte notes. Decoded: `09E` odometer (bytes 4–6, 0.1 km), `200` handbrake + three speeds (×0.05 km/h, big-endian), `208` brake intensity + speeds, `210` pedal (byte 2), `212` pedal sensor 2, `308` rpm (bytes 1–2), `408` fuel litres, `40C`/`412` dash speed, `416` battery (0.1 V), `423` ignition / lights / indicators / doors bitfield, `608` coolant (−40) and fuel rate | **The closest relative found.** Body and chassis frames match ours: `0C0` (5 bytes, byte 3 always `07`, byte 4 counter; ours `6DFF0007xx`), `119` (byte 5 `08`), `312` (four 16-bit words), and `2F1`, `300`, `443`, `423` exist on both. The engine-side frames do not (`308`, `608`, `200`/`208` absent here; `210`, `423` other lengths and layouts) — plausibly because that car's engine ECU is Mercedes' diesel one and ours is Mitsubishi's 4G15T |
|
||||
|
||||
The Z27 Colt is a 2002 platform shared with the smart forfour (W454); the CZT /
|
||||
Ralliart's 4G15T is also the forfour Brabus engine, and parts listings show the
|
||||
sharing reaches the electrics (Z30 engine ECUs carry Mercedes-style `A135…` part
|
||||
numbers; the steering rack is the 454's). The forfour notes bear that out for the
|
||||
body bus. Their layouts are the first hypothesis for every shared ID, and their
|
||||
engine-side IDs a hint of what a petrol forfour or EU Colt would add.
|
||||
|
||||
**The forfour Brabus is the sibling to look for.** EcuTek's [Colt CZT tuning
|
||||
manual](https://ecutek.zendesk.com/hc/en-gb/article_attachments/204299089/Mitsubishi_Colt_CZT_-_Tuning.pdf)
|
||||
(2005-on "CAN ECUs"): "The Smart ForFour Brabus edition is basically a Colt CZT
|
||||
with the fibreglass body"; one ProECU package tunes both. Brabus owners run CZT
|
||||
ECUs (MH8304F; ECUFlash definitions exist for CZT ROM 47110032 and Brabus
|
||||
49100033, [EvolutionM](https://www.evolutionm.net/forums/ecu-flash/767782-xml-file-smart-brabus-w454.html)).
|
||||
So a Brabus's engine-ECU frames should be ours (if the JDM Ralliart's ECU
|
||||
matches the EU CZT's), while its body frames come from smart's SAM module: on the
|
||||
454 "the SAM … sits at the awkward edge of Mitsubishi engine management and
|
||||
Mercedes body electronics" ([smartcarmanuals](https://smartcarmanuals.com/models/454-forfour/)).
|
||||
Expect, from a Brabus capture: engine frames equal, chassis frames (ABS, the shared
|
||||
electric steering) probably equal, body frames different. No Brabus or CZT capture
|
||||
is public, and none is being sought: the Z27's own recordings are the source. The same manual notes that the **Euro CZT has no MAP sensor**, so OBD PID
|
||||
`0B` may be unsupported on the Z27 (`obd-poller` discovers that by itself). The smart
|
||||
fortwo 451 ([hackaday.io](https://hackaday.io/project/19931-smart-fortwo-can-bus-reverse-engineering))
|
||||
shares `423` as the cluster status frame, little else.
|
||||
|
||||
### Diagnostic requests
|
||||
|
||||
| Source | What it has |
|
||||
|---|---|
|
||||
| [OBDb/Mitsubishi](https://github.com/OBDb/Mitsubishi) (CC BY-SA 4.0) | Make-level signal set: request header, response header, request, bit position, scaling. E.g. combination meter `6A0`→`514`: `21 A3` fuel level (byte 2, −16, %), `21 AD` odometer (24 bit, km), `21 AF` dashboard voltage (÷10, V). A/C `688`→`511`: `21 13` engine RPM and vehicle speed. TPMS `600`/`773` |
|
||||
| [OBDb/Mitsubishi-Lancer](https://github.com/OBDb/Mitsubishi-Lancer) and others | Per-model overrides; the Lancer one is empty. No Colt repo |
|
||||
| [habr 448658](https://habr.com/ru/post/448658/), Lancer X 2008 | MUT-III over CAN: ESP `21 02` → steering angle, bytes 2–3, int16 × 0.04375 °; security access `27 01` / `27 02` gates writes, not reads |
|
||||
| [EvoScan](https://evoscan.com/) | Commercial logger with MUT-III support for the EU Colt CZT / Ralliart (4G15T). Its parameter definitions are the most complete source for engine mode-21 requests on our exact engine; not open |
|
||||
| [EcuTek Colt suite](https://www.tpwengineering.com/products/ecutek-vehicle-tuning-mitsubishi-colt) | Confirms the 2005+ EU Colt CZT / Ralliart ECU speaks CAN for flashing; nothing published |
|
||||
|
||||
OBDb's definitions are from newer Mitsubishis (roughly 2008 on). Whether the Z27's
|
||||
meter and A/C units answer at the same addresses is a question one listen-only
|
||||
look at the bus during a MUT-III session — or one careful request — would settle.
|
||||
|
||||
## What the first capture shows
|
||||
|
||||
`z27ag-20261009-163736-ign.log`: engine idling, parked, a light touch of
|
||||
throttle around t ≈ 4–5 s. 27 periodic IDs, ~893 frames/s. Every observation
|
||||
below is a candidate until checked against OBD-II mode 01 in the same recording.
|
||||
|
||||
| ID | Period | Candidate |
|
||||
|---|---|---|
|
||||
| `210` | 20 ms | **Engine speed**: bytes 4–5, big-endian, 1 rpm/bit — ~845 at idle, rising to ~900 with the throttle blip and back |
|
||||
| `13D` | 20 ms | **Engine speed again**: bytes 0–1, little-endian, ≈ 2× `210`'s value (0.5 rpm/bit?) — moves in lockstep with it |
|
||||
| `312` | 20 ms | Four 16-bit big-endian words near 0x07B0–0x0940, all noisy, two follow the blip. Sensor values; load or pressure, not wheel speed (parked) |
|
||||
| `449` | 200 ms | Bytes 0, 1, 3 = 0x6F, 0x5F, 0x71: with the usual −40 offset, 71, 55, 73 °C — temperatures of a warm engine |
|
||||
| `0C0` | 10 ms | Byte 4 low nibble a 4-bit rolling counter; high nibble looks like a checksum |
|
||||
| `119`, `149`, `156` | 20 ms | Byte 6 low nibble a rolling counter, byte 7 a checksum (neither plain sum nor XOR) |
|
||||
| `149` | 20 ms | Bytes 0 and 4 noisy around a centre: an inertial sensor (yaw / lateral g) is the usual owner of such a frame |
|
||||
| `710`–`713` | 200 ms | Slow, mostly constant: body/meter status |
|
||||
|
||||
Counters and checksums matter only for transmitting, which is out of scope. The
|
||||
decoder ignores those bits.
|
||||
|
||||
## Automated decoding, in the literature
|
||||
|
||||
Published tools do what the capture checklist sets up: record known values next to
|
||||
the raw frames and search the frames for them.
|
||||
[LibreCAN](https://rtcl.eecs.umich.edu/rtclweb/assets/publications/2019/fv075-peseA.pdf)
|
||||
fits OBD-II replies for powertrain signals and phone sensors for body ones (GNSS
|
||||
plays that part here); [CAN-D](https://www.ornl.gov/publication/can-d-modular-four-step-pipeline-comprehensively-decoding-controller-area-network-data)
|
||||
and [ByCAN](https://arxiv.org/html/2408.09265v1) find signal boundaries from bit
|
||||
flip rates, then byte order and signedness, then match. None reads our logs; the
|
||||
method — classify bits (counter, constant, live), cut signals, try both
|
||||
endiannesses, fit against OBD values at matching timestamps, rank — is the
|
||||
analysis tool to write.
|
||||
|
||||
## What would move Phase 2 fastest
|
||||
|
||||
1. **Record mode 01 alongside the broadcast.** One `obd-poller` session polling
|
||||
RPM, speed, coolant and throttle at ~2 Hz confirms or kills the `210` / `13D` /
|
||||
`449` candidates in an afternoon.
|
||||
2. **Try OBDb's meter and A/C requests** (`6A0`, `688`) once. If the Z27 answers,
|
||||
fuel level and odometer come for free — neither is easy to find in broadcast.
|
||||
3. **A drive with GNSS and marks.** Speed against GNSS speed finds the wheel-speed
|
||||
frame; marked indicators, lights and brake find the body frames.
|
||||
|
||||
Steps 1 and 2 transmit, so they wait for the deliberate move of `can_pcan` out of
|
||||
listen-only (svc-core's initial plan). Step 3 needs only the recorder, which is
|
||||
running on the car unit.
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
type: report
|
||||
date: 2026-10-13
|
||||
---
|
||||
# Z27AG CAN: first decoding pass
|
||||
|
||||
Session `z27ag-20261013-170050-checklist` (yukart-data `can-captures/`), checklist v1,
|
||||
117 marks. Method: list every transition of every rarely-changing bit and read it
|
||||
against the marks; for blinking signals, count toggles inside each marked range
|
||||
against the seconds before it; for values, dump bytes across the marked range; for
|
||||
speed, correlate every 16-bit word with GNSS speed over the crawls. Marks lag the
|
||||
action by a fraction of a second to a few seconds (the operator presses after doing),
|
||||
so a change shortly *before* a start mark is normal.
|
||||
|
||||
Bit numbering: `ID.byte.bit`, byte 0 first, bit 7 the MSB.
|
||||
|
||||
## Found
|
||||
|
||||
| Signal | Where | Evidence | Confidence |
|
||||
|---|---|---|---|
|
||||
| Engine speed | `210` bytes 4–5, BE, 1 rpm | idle 740–870; holds at ~1410 and ~1900 for "1500" / "2000" (operator: revs not exact); blip peak 2887 | High |
|
||||
| Engine speed, finer | `13D` bytes 0–1, LE, ≈ rpm × 2.048 | moves in lockstep with `210` | High |
|
||||
| Accelerator pedal | `210` byte 0, 0x00 released … 0xFA full (÷2.5 → %) | engine-off sweep: 0 → 0xFA → 0 | High |
|
||||
| Accelerator sensors 1 and 2 | `139` bytes 0 and 1 (raw, 0x33 rest … 0xEE full, track each other) | same sweep | High |
|
||||
| Throttle-related | `139` byte 2 (0x21 … 0xD5) | same sweep; plate or target | Medium |
|
||||
| Pedal-pressed flag | `210` byte 3 bit 7 | set exactly while the pedal is off rest | Medium |
|
||||
| Brake pedal switch | `231.4.1` (1 = pressed) | 3/3 presses, plus the foot-brake holds around the handbrake step and the crawls | High |
|
||||
| Handbrake | `239.0.2` (1 = applied) | set through every engaged stretch, clear during the crawls and the handbrake releases | High |
|
||||
| Left indicator | `423.2.6` blinking | 2/2 | High |
|
||||
| Right indicator | `423.2.5` blinking | 2/2 | High |
|
||||
| Hazards | `423.2.6` only blinks, 2/2 | odd: `2.5` stays still. Maybe `2.6` is "left or hazard", maybe the hazard has its own bit elsewhere at a rate this method missed | Low |
|
||||
| Side lights | `423.2.2` and `570.7.3` (1 = on) | on/off and during dipped | High |
|
||||
| Dipped beam | `423.3.1` (1 = on) | 1/1 | Medium (one sample) |
|
||||
| Any door open | `423.2.1` | driver and passenger | Medium |
|
||||
| Driver door | `423.0.4` | driver only, not passenger | Medium |
|
||||
| Driver belt unbuckled | `573.5.7` (1 = unbuckled) | set at ignition on, cleared at buckle; no release seen | Low |
|
||||
| A/C request / blower | `443` bytes 1–2 and `711` bytes 5–6, multi-bit | change with A/C and blower; not separated yet | Low |
|
||||
| A/C compressor engaged | `13D.6.7` | engine running, A/C on; set ~0.5 s after the request, cleared with A/C off | Medium |
|
||||
| Four wheel speeds | `235` four 16-bit words (bytes 0–1, 2–3, 4–5, 6–7) | zero parked, all four move together in the crawls | High (signal), **scale open** |
|
||||
| Vehicle speed | `231` byte 6 (and probably byte 7) | tracks the wheel words | Medium |
|
||||
|
||||
**Wheel-speed scale.** Crawls topped out at ~6 km/h, and so far only the first byte of each word moved
|
||||
(0x4C at GNSS 3.5 km/h). Read as little-endian ×0.05 km/h (the forfour's `200`
|
||||
scale), that is 3.8 km/h, which is plausible given GNSS lag. Big-endian is not ruled out. The
|
||||
jack-stands session and a GNSS-matched drive settle both the byte order and the scale.
|
||||
|
||||
## Not on this bus (or not seen)
|
||||
|
||||
- **Clutch and transmission are not on this bus.** Nothing changed for the clutch
|
||||
(3 presses), any of the 5 gears, neutral or reverse: nothing on the car consumes
|
||||
them, so no frame carries them. Reverse was rechecked against both reverse
|
||||
stretches (engine off and engine on) versus every other position: no bit
|
||||
separates them.
|
||||
- **Gear is not inferred from rpm ÷ wheel speed** (owner's call, 2026-10-13): the
|
||||
clutch is invisible, so the ratio lies whenever it is down or slipping, and the
|
||||
gear matters most at a standstill, where the ratio says nothing. A *recommended*
|
||||
gear from speed and rpm is the useful form, if any.
|
||||
- **Steering angle:** not analysed yet (step 5 was done with the engine on, at the end).
|
||||
|
||||
## Not analysed yet
|
||||
|
||||
- `312` (four noisy 16-bit words that follow the throttle: MAP/load/air candidates)
|
||||
- `449` temperatures (coolant candidate), `710`–`713` slow body/meter frames
|
||||
- `0C0`, `119`, `149`, `156` (counters/checksums; `149` inertial candidate)
|
||||
|
||||
## Scripts
|
||||
|
||||
Scratch analysis for now: a session loader, a transition timeline, a blink counter and a
|
||||
GNSS correlator, in Python with numpy. They become a tool once a DBC exists to check
|
||||
against.
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
type: report
|
||||
date: 2026-10-15
|
||||
---
|
||||
# Z27AG CAN: second pass, against OBD-II and GNSS
|
||||
|
||||
Sessions `000007-20261015T192938` and `000008-20261015T200202` (car Orin): doors, tailgate,
|
||||
bonnet and wipers (checklist step 10–11), then a short drive with GNSS (3D fix, helix antenna)
|
||||
and OBD-II through the ELM327 on the K-line, all on the Orin's clock. The first time CAN, OBD-II
|
||||
and GNSS were recorded together. About 30 minutes; 1190 OBD-II samples of each fast value.
|
||||
|
||||
## Confirmed
|
||||
|
||||
| Signal | Where | Evidence | Registered as |
|
||||
|---|---|---|---|
|
||||
| Engine speed | `210` bytes 4–5, BE, 1 rpm (pass 1) | equal to OBD-II `0C`: median ratio 1.0000, median difference 4 rpm, 1190 samples | `engine_rpm` (unchanged) |
|
||||
| Coolant temperature | `449` byte 2, −40 °C | equal to OBD-II `05` within ±1 °C (median 0.0) over 86–97 °C; no other byte tracks it | `coolant_temp_c` |
|
||||
| Vehicle speed | `231` bytes 6–7, **little-endian**, **0.0577 km/h per count** | steady cruising (> 15 km/h, GNSS change < 1 km/h over 2 s, 117 fixes): spread 2.6 % against GNSS | `vehicle_speed_kph` |
|
||||
| Four wheel speeds | `235` four words, **little-endian** (pass 1 left the byte order open) | big-endian misses by 28 km/h rms; little-endian fits. Words 0, 1: 0.0578; words 2, 3: 0.0581 km/h per count against GNSS | not yet: which word is which wheel waits for the jack-stands run (fronts turn, rears stay) |
|
||||
|
||||
Scales are against **GNSS**, so they include this car's tyres today. **OBD-II speed reads 3 % low**
|
||||
against GNSS (0.9706): the ECU's figure is no better a reference than the ABS frames. The two
|
||||
word pairs of `235` differ by 0.5 %, the usual size of a front/rear tyre difference.
|
||||
|
||||
## Doors, tailgate, bonnet, wipers
|
||||
|
||||
| Thing | Bit | Notes |
|
||||
|---|---|---|
|
||||
| Driver's door | `423.0.4` | its own bit (pass 1); also sets any-door |
|
||||
| Passenger, rear left, rear right | only `423.2.1` (any door) | no bit of their own |
|
||||
| Tailgate | only `423.2.1` | with the driver's door open, `423.0.4` cleared on closing it while `423.2.1` stayed set until the tailgate shut |
|
||||
| Bonnet | nothing | a mechanical latch with no switch, as expected |
|
||||
| Front wiper running | `423.3.3` | set through "low" and "high"; nothing in a 4 s "interval" window. Medium confidence; also explains pass 1's unexplained `423.3.3` stretch |
|
||||
|
||||
## IMU calibration, checked against GNSS
|
||||
|
||||
Level from session `000006` (two still stretches, the ground's 1.9° slope cancelled), forward from
|
||||
`000008` (three stops, one launch; 3512 samples, mean spread 12.7°). The module sits upside down,
|
||||
about 24° off level, turned so its X axis points forward. Calibrated, the marked stretches read:
|
||||
|
||||
| Stretch | IMU longitudinal (0.25 s mean, peak) | GNSS |
|
||||
|---|---|---|
|
||||
| Stop, 48 → 0 km/h | −0.35 g | −0.28 g |
|
||||
| Stop, 45 → 0 | −0.17 g | −0.17 g |
|
||||
| Stop, 40 → 0 | −0.21 g | −0.18 g |
|
||||
| Launch | +0.32 g | +0.36 g |
|
||||
|
||||
Lateral stays at about 0 in all four: forward is straight ahead. GNSS reports about once a second,
|
||||
which smooths short peaks.
|
||||
|
||||
## Session 000009: lights at night, corners
|
||||
|
||||
`z27ag-20261015-203117-lights-corners-drive`: step 9 at night, then a 35-minute errand drive
|
||||
(no OBD-II: the obd-poller was off after the freeze). References: GNSS and the calibrated IMU.
|
||||
|
||||
| Signal | Where | Evidence | Registered as |
|
||||
|---|---|---|---|
|
||||
| Main beam | `423.2.7` | set exactly through the marked stretch; also fired once with the other lights off: flash-to-pass | `main_beam` |
|
||||
| Front fog lamps | `423.2.4` | set exactly through the marked stretch (lights on) | `front_fogs` |
|
||||
| **Steering wheel angle** | `0C0` bytes 0–1, little-endian, **signed**, **0.1° per count**, positive left | correlation **+0.995** with how sharply the car turns (IMU yaw rate ÷ speed), 1021 s of driving. Lock to lock −479…+471°; with a 2.5 m wheelbase that is a 16.3:1 steering ratio, typical. Straight driving reads −2.7° (camber or alignment) | `steering_angle_deg` (the layout format gained `signed`) |
|
||||
| Dipped beam? | `423.3.1` | clean in daylight on the 13th; at night it flickers on for 0.1–0.2 s at each switch movement. The cluster has **no dipped-beam lamp** (only side lights, main beam and fogs, no rear fogs), and `423` matches the cluster's lamps, so this bit is probably something else that moves with the lights (illumination, dimming). A walk through Off → Auto → Dim → Main settles it | `dipped_beam` kept, description says so |
|
||||
|
||||
**IMU, lateral axis:** lateral g against speed × yaw rate gives a slope of **−0.998** (correlation
|
||||
−0.91; the sign is right: yaw counts a left turn positive, lateral g counts right positive). With the
|
||||
stops above, both horizontal axes of the calibration are confirmed.
|
||||
|
||||
`149` bytes 1–2 is not a yaw-rate sensor (correlation 0.02 with the IMU's yaw rate).
|
||||
|
||||
## Not found
|
||||
|
||||
- **Intake air temperature:** no byte of `449` tracks OBD-II `0F` (29–51 °C here).
|
||||
- `449` bytes 0, 1, 3 move like temperatures but match nothing on OBD-II.
|
||||
|
||||
## Notes on the recording
|
||||
|
||||
- **The Orin froze mid-drive** (19:59; hardware watchdog reset at 20:01:47), splitting the drive
|
||||
over two sessions. orin-nano-env-setup has the investigation.
|
||||
- OBD-II through the ELM gave each fast value about every 2 s (K-line, ~250 ms per request).
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
type: report
|
||||
date: 2026-10-15
|
||||
---
|
||||
# Z27AG CAN: third pass, the drive back
|
||||
|
||||
Sessions `000011-20261015T212904` and `000013-20261015T214544` (the drive back, about 13 minutes,
|
||||
no marks) together with pass 2's `000008` and `000009`: 20 km of driving with GNSS and the calibrated
|
||||
IMU, no OBD-II. Nothing was marked, so this pass uses what any drive has: corners and distance.
|
||||
|
||||
## Confirmed
|
||||
|
||||
| Signal | Where | Evidence | Registered as |
|
||||
|---|---|---|---|
|
||||
| Wheel speeds | `235` four words, little-endian (pass 2): bytes 0–1 **front right**, 2–3 **front left**, 4–5 **rear right**, 6–7 **rear left** | from corners, below | `wheel_speed_fr_kph`, `_fl_`, `_rr_`, `_rl_` |
|
||||
| Odometer | `570` bytes 4–6, little-endian, **1 km** | steps once per kilometre: 12 steps over 11.9 km (`000009`), 3 over 3.0 km (`000011`); 167966 on the 13th, 167988 at the end of the drive back; valid with the engine off too. Agrees with the cluster's (digital) odometer | `odometer_km` (the layout format gained 24-bit fields) |
|
||||
| Distance counter | `14D` bytes 6–7, little-endian, **about 0.40 m** per count | tracks distance from the wheel speeds with correlation 1.0000 in both drives (103.2 and 103.6 m per step of byte 7); wraps every ~26 km | not registered. The cluster's two trip meters count 0.1 km and may run from this; it is also the finest distance on the bus (slow creeping, short manoeuvres) |
|
||||
|
||||
### Which word is which wheel
|
||||
|
||||
In a corner the outer wheels run faster than the inner ones, by about half the track width times the
|
||||
curvature; and the fronts run on a wider arc than the rears, by about half the wheelbase squared times
|
||||
the curvature squared. Fitting each word's share of the four (its speed ÷ their mean) against the
|
||||
curvature from the steering angle (`steering_angle_deg` + 2.7° for the straight-ahead offset, ÷ 16.3,
|
||||
over a 2.5 m wheelbase), 74 476 frames above 15 km/h:
|
||||
|
||||
| Word | Linear term (track) | Square term (wheelbase) | So |
|
||||
|---|---|---|---|
|
||||
| 0 | +0.72 m | +1.4 m² | right, front |
|
||||
| 1 | −0.72 m | +1.4 m² | left, front |
|
||||
| 2 | +0.75 m | −0.9 m² | right, rear |
|
||||
| 3 | −0.75 m | −1.9 m² | left, rear |
|
||||
|
||||
Curvature is positive turning left, so a positive linear term is a wheel on the right (outside in a left
|
||||
turn). The size, ±0.72–0.75 m, is half the Colt's track (1.45–1.46 m). The square terms split the words
|
||||
into a pair above the mean and a pair below it, ±1.5 m² against an expected ±1.6 m² (2.5² ÷ 4). Medians
|
||||
by curvature agree: in left turns tighter than 20 m (`κ > 0.05`) the words read 1.068, 0.956, 1.044,
|
||||
0.932 of their mean.
|
||||
|
||||
Straight ahead the fronts read 0.6 % more counts than the rears, matching pass 2's per-word scales
|
||||
(0.0578 against 0.0581 km/h per count). Driven wheels or tyre sizes, either is ordinary; each pair is
|
||||
registered with its own scale, so all four read GNSS speed when cruising.
|
||||
|
||||
Jack stands (checklist 23–26) would still confirm this, but are no longer needed to find it.
|
||||
|
||||
## Candidates
|
||||
|
||||
- **`570` bytes 2 and 3: fuel, most likely.** Both fall slowly: byte 2 read 87 on the 13th and 76
|
||||
at the end of the drive back, 22 km later. Both read `7F` with the engine off. Outside temperature
|
||||
was the other reading, but the car shows it nowhere, and the bytes **slosh**. Take away their own
|
||||
60 s average, and what is left follows the car's motion in all three drives:
|
||||
|
||||
| Byte | Lateral g | Longitudinal g | Lag |
|
||||
|---|---|---|---|
|
||||
| 3 | +0.70 to +0.81 | −0.42 to −0.48 | none |
|
||||
| 2 | +0.64 to +0.75 | −0.48 to −0.50 | about 3 s |
|
||||
|
||||
Lateral g here is from the wheel speeds and the steering angle, longitudinal from the change in
|
||||
wheel speed. A temperature would not do this; liquid in a tank would. Byte 3 looks like the sender
|
||||
as it is, and byte 2 a damped copy (a gauge needle is damped the same way). The "rise while parked"
|
||||
(78 → 81) was the same thing: the drive before ended at 80, parked in the same spot (the same
|
||||
IMU tilt to three decimals). It read 76 a minute earlier, while still moving. The reading moves about
|
||||
±4 with tilt and motion, so it needs averaging over minutes.
|
||||
Scale unknown: full is unlikely to be 100 or 255. A refuel gives two points (before and after,
|
||||
parked level, with the litres put in). Not registered until then.
|
||||
|
||||
- **`449` bytes 0, 1, 3: climate-control temperatures, most likely raw sensor readings.** Pass 2 looked
|
||||
for a byte that equals OBD-II intake air temperature less 40 and found none. Read the other way,
|
||||
bytes 1 and 3 run *against* it (correlation −0.96 and −0.98 in `000007`), the way a thermistor's raw
|
||||
reading falls as it warms. They follow it over tens of minutes, not second to second, so they are
|
||||
other sensors that warm and cool with the air around the engine and car:
|
||||
- **Byte 3: the A/C evaporator.** At each first switch-on of the compressor (`ac_compressor`) it climbs
|
||||
from 115–145 to about 185 within 30–60 s, then stays there while the compressor cycles on and off
|
||||
every 10–20 s. That is the evaporator being held just above freezing, the job its sensor exists
|
||||
for. It keeps climbing a few counts after each switch-off, as an evaporator does while the last
|
||||
refrigerant boils off. With the A/C off it drifts with the air (the `000007` correlation above).
|
||||
Higher count, colder.
|
||||
- **Byte 1: outside air, probably.** Slow; 89–91 on the afternoon of the 13th, 99–117 in the evening
|
||||
of the 15th (higher count, colder, as byte 3). It fell from 114 to 99 over 30 minutes parked and
|
||||
idling, and rose from 112 to 117 over 8 minutes of driving. A sensor behind the grille does this:
|
||||
radiator heat soaks it while standing, and the airflow cools it once moving. That is why climate
|
||||
controls filter it.
|
||||
- **Byte 0: unclear.** 119–134, slow, unmoved by the engine starting or stopping (so not the
|
||||
charging voltage), and its sign against the other two is not consistent. A cabin sensor is one
|
||||
guess.
|
||||
|
||||
An auto climate control has these three: evaporator, outside air, cabin. The way to scale them is
|
||||
an overnight soak: a cold start after the car has stood all night gives one reading of each with
|
||||
everything at the outside temperature (OBD-II intake and coolant say what that is). With a
|
||||
thermometer and the A/C steps (12, 17), it gives a second. A mid-session frame with byte 6 = 24 instead
|
||||
of 51 (end of `000009`, engine stopping) has every byte halved or changed; left out of the above.
|
||||
|
||||
## Notes on the recording
|
||||
|
||||
- `000012` is a 25-second parked session in between; `000013` ends with the PEAK unplugged.
|
||||
- The recorder kept IMU samples as JSON lines (34 MB for `000009`). From this pass on it packs them
|
||||
(`imu.bin`, svc-core `imu/packed.rs`): the same samples are 8.5 MB, and all of `000009`'s samples fit
|
||||
the packed form exactly.
|
||||
@@ -2,7 +2,7 @@
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://yukart/schema/envelope.json",
|
||||
"title": "Signal envelope",
|
||||
"description": "Every decoded signal published on yukart/vehicle/* and yukart/gnss/*. Raw CAN uses the binary frame encoding instead.",
|
||||
"description": "Every decoded signal published on yukart/vehicle/*, yukart/gnss/* and yukart/host/*. Raw CAN uses the binary frame encoding instead.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"timestamp",
|
||||
|
||||
+832
-19
@@ -1,17 +1,7 @@
|
||||
{
|
||||
"$comment": "Single source for signal identity. Topic name and envelope unit are both derived from these entries and are never authored separately. source=obd2 is standardised; source=z27ag is reverse-engineered and lives in the DBC.",
|
||||
"$comment": "Single source for signal identity. Topic name and envelope unit are both derived from these entries and are never authored separately. source=obd2 is standardised (OBD-II mode 01 over the K-line, through the ELM327): calibration references, every name prefixed obd_ (project-yukart decision 30); source=host is the Orin itself (orin-status), not the car. source=gnss is the receiver (svc-core gnss), on yukart/gnss/<signal>. source=imu is the WT61 IMU, already turned into the car's frame (mounting and gravity handled by its publisher), on yukart/imu/<signal>. source=z27ag is reverse-engineered from the car's own broadcast frames, and its 'can' block is the only place its layout is written down: id (11-bit, hex string), byte (first data byte, 0-7), length in bits (1, 8, 16 or 24), bit (length 1 only: 0 = LSB of that byte), order (lengths 16 and 24: big = byte first is most significant, little = the reverse), signed (lengths 8 and up: two's complement; default unsigned), scale and offset (value = raw * scale + offset; booleans are raw != 0). 'evidence' names the contracts report that found it.",
|
||||
"signals": {
|
||||
"engine_rpm": {
|
||||
"unit": "rpm",
|
||||
"type": "number",
|
||||
"source": "obd2",
|
||||
"description": "Engine speed",
|
||||
"range": [
|
||||
0,
|
||||
8000
|
||||
]
|
||||
},
|
||||
"vehicle_speed_kph": {
|
||||
"obd_vehicle_speed_kph": {
|
||||
"unit": "kph",
|
||||
"type": "number",
|
||||
"source": "obd2",
|
||||
@@ -21,7 +11,7 @@
|
||||
255
|
||||
]
|
||||
},
|
||||
"coolant_temp_c": {
|
||||
"obd_coolant_temp_c": {
|
||||
"unit": "celsius",
|
||||
"type": "number",
|
||||
"source": "obd2",
|
||||
@@ -31,7 +21,7 @@
|
||||
215
|
||||
]
|
||||
},
|
||||
"intake_air_temp_c": {
|
||||
"obd_intake_air_temp_c": {
|
||||
"unit": "celsius",
|
||||
"type": "number",
|
||||
"source": "obd2",
|
||||
@@ -41,7 +31,7 @@
|
||||
215
|
||||
]
|
||||
},
|
||||
"throttle_pct": {
|
||||
"obd_throttle_pct": {
|
||||
"unit": "percent",
|
||||
"type": "number",
|
||||
"source": "obd2",
|
||||
@@ -51,7 +41,7 @@
|
||||
100
|
||||
]
|
||||
},
|
||||
"engine_load_pct": {
|
||||
"obd_engine_load_pct": {
|
||||
"unit": "percent",
|
||||
"type": "number",
|
||||
"source": "obd2",
|
||||
@@ -61,7 +51,7 @@
|
||||
100
|
||||
]
|
||||
},
|
||||
"fuel_level_pct": {
|
||||
"obd_fuel_level_pct": {
|
||||
"unit": "percent",
|
||||
"type": "number",
|
||||
"source": "obd2",
|
||||
@@ -71,7 +61,7 @@
|
||||
100
|
||||
]
|
||||
},
|
||||
"intake_pressure_kpa": {
|
||||
"obd_intake_pressure_kpa": {
|
||||
"unit": "kpa",
|
||||
"type": "number",
|
||||
"source": "obd2",
|
||||
@@ -81,7 +71,7 @@
|
||||
255
|
||||
]
|
||||
},
|
||||
"maf_gps": {
|
||||
"obd_maf_gps": {
|
||||
"unit": "g/s",
|
||||
"type": "number",
|
||||
"source": "obd2",
|
||||
@@ -90,6 +80,829 @@
|
||||
0,
|
||||
656
|
||||
]
|
||||
},
|
||||
"obd_engine_rpm": {
|
||||
"unit": "rpm",
|
||||
"type": "number",
|
||||
"source": "obd2",
|
||||
"description": "Engine speed (OBD-II 0C): the reference for the decoded engine_rpm",
|
||||
"range": [
|
||||
0,
|
||||
8000
|
||||
]
|
||||
},
|
||||
"obd_timing_advance_deg": {
|
||||
"unit": "degree",
|
||||
"type": "number",
|
||||
"source": "obd2",
|
||||
"description": "Ignition timing advance before TDC, cylinder 1",
|
||||
"range": [
|
||||
-64,
|
||||
64
|
||||
]
|
||||
},
|
||||
"obd_stft_pct": {
|
||||
"unit": "percent",
|
||||
"type": "number",
|
||||
"source": "obd2",
|
||||
"description": "Short-term fuel trim, bank 1: positive adds fuel",
|
||||
"range": [
|
||||
-100,
|
||||
100
|
||||
]
|
||||
},
|
||||
"obd_ltft_pct": {
|
||||
"unit": "percent",
|
||||
"type": "number",
|
||||
"source": "obd2",
|
||||
"description": "Long-term fuel trim, bank 1: positive adds fuel",
|
||||
"range": [
|
||||
-100,
|
||||
100
|
||||
]
|
||||
},
|
||||
"obd_o2_b1s1_v": {
|
||||
"unit": "V",
|
||||
"type": "number",
|
||||
"source": "obd2",
|
||||
"description": "Upstream oxygen sensor voltage (narrowband: ~0.1 lean … ~0.9 rich)",
|
||||
"range": [
|
||||
0,
|
||||
1.275
|
||||
]
|
||||
},
|
||||
"engine_rpm": {
|
||||
"unit": "rpm",
|
||||
"type": "number",
|
||||
"source": "z27ag",
|
||||
"description": "Engine speed",
|
||||
"range": [
|
||||
0,
|
||||
8000
|
||||
],
|
||||
"can": {
|
||||
"id": "0x210",
|
||||
"byte": 4,
|
||||
"length": 16,
|
||||
"order": "big",
|
||||
"scale": 1,
|
||||
"offset": 0,
|
||||
"evidence": "Z27AG-DECODE-1"
|
||||
}
|
||||
},
|
||||
"accel_pedal_pct": {
|
||||
"unit": "percent",
|
||||
"type": "number",
|
||||
"source": "z27ag",
|
||||
"description": "Accelerator pedal position (0x00 at rest, 0xFA flat)",
|
||||
"range": [
|
||||
0,
|
||||
100
|
||||
],
|
||||
"can": {
|
||||
"id": "0x210",
|
||||
"byte": 0,
|
||||
"length": 8,
|
||||
"scale": 0.4,
|
||||
"offset": 0,
|
||||
"evidence": "Z27AG-DECODE-1"
|
||||
}
|
||||
},
|
||||
"steering_angle_deg": {
|
||||
"unit": "degree",
|
||||
"type": "number",
|
||||
"source": "z27ag",
|
||||
"description": "Steering wheel angle: positive to the left. About -2.7° driving straight; lock to lock about ±475° (ratio about 16:1)",
|
||||
"range": [
|
||||
-600,
|
||||
600
|
||||
],
|
||||
"can": {
|
||||
"id": "0x0C0",
|
||||
"byte": 0,
|
||||
"length": 16,
|
||||
"order": "little",
|
||||
"signed": true,
|
||||
"scale": 0.1,
|
||||
"offset": 0,
|
||||
"evidence": "Z27AG-DECODE-2"
|
||||
}
|
||||
},
|
||||
"brake_pedal": {
|
||||
"unit": "",
|
||||
"type": "boolean",
|
||||
"source": "z27ag",
|
||||
"description": "Brake pedal switch: pressed",
|
||||
"can": {
|
||||
"id": "0x231",
|
||||
"byte": 4,
|
||||
"bit": 1,
|
||||
"length": 1,
|
||||
"evidence": "Z27AG-DECODE-1"
|
||||
}
|
||||
},
|
||||
"handbrake": {
|
||||
"unit": "",
|
||||
"type": "boolean",
|
||||
"source": "z27ag",
|
||||
"description": "Handbrake applied",
|
||||
"can": {
|
||||
"id": "0x239",
|
||||
"byte": 0,
|
||||
"bit": 2,
|
||||
"length": 1,
|
||||
"evidence": "Z27AG-DECODE-1"
|
||||
}
|
||||
},
|
||||
"indicator_left": {
|
||||
"unit": "",
|
||||
"type": "boolean",
|
||||
"source": "z27ag",
|
||||
"description": "Left indicator lamp, as it flashes (on and off at the flasher's rate)",
|
||||
"can": {
|
||||
"id": "0x423",
|
||||
"byte": 2,
|
||||
"bit": 6,
|
||||
"length": 1,
|
||||
"evidence": "Z27AG-DECODE-1"
|
||||
}
|
||||
},
|
||||
"indicator_right": {
|
||||
"unit": "",
|
||||
"type": "boolean",
|
||||
"source": "z27ag",
|
||||
"description": "Right indicator lamp, as it flashes. Hazards were seen on the left bit only: open",
|
||||
"can": {
|
||||
"id": "0x423",
|
||||
"byte": 2,
|
||||
"bit": 5,
|
||||
"length": 1,
|
||||
"evidence": "Z27AG-DECODE-1"
|
||||
}
|
||||
},
|
||||
"side_lights": {
|
||||
"unit": "",
|
||||
"type": "boolean",
|
||||
"source": "z27ag",
|
||||
"description": "Side (position) lights on; also on with dipped beam",
|
||||
"can": {
|
||||
"id": "0x423",
|
||||
"byte": 2,
|
||||
"bit": 2,
|
||||
"length": 1,
|
||||
"evidence": "Z27AG-DECODE-1"
|
||||
}
|
||||
},
|
||||
"dipped_beam": {
|
||||
"unit": "",
|
||||
"type": "boolean",
|
||||
"source": "z27ag",
|
||||
"description": "Probably not dipped beam: clean in daylight (2026-10-13) but flickering at each switch movement at night (2026-10-15), and the Z27AG's cluster has no dipped-beam lamp (side lights, main beam and fogs only), so frame 423, the cluster's status, has little reason to carry it. Kept under this name until a light-switch walk shows what it is",
|
||||
"can": {
|
||||
"id": "0x423",
|
||||
"byte": 3,
|
||||
"bit": 1,
|
||||
"length": 1,
|
||||
"evidence": "Z27AG-DECODE-1"
|
||||
}
|
||||
},
|
||||
"main_beam": {
|
||||
"unit": "",
|
||||
"type": "boolean",
|
||||
"source": "z27ag",
|
||||
"description": "Main beam lamp on, flash-to-pass included",
|
||||
"can": {
|
||||
"id": "0x423",
|
||||
"byte": 2,
|
||||
"bit": 7,
|
||||
"length": 1,
|
||||
"evidence": "Z27AG-DECODE-2"
|
||||
}
|
||||
},
|
||||
"front_fogs": {
|
||||
"unit": "",
|
||||
"type": "boolean",
|
||||
"source": "z27ag",
|
||||
"description": "Front fog lamps on (they need side lights or dipped beam)",
|
||||
"can": {
|
||||
"id": "0x423",
|
||||
"byte": 2,
|
||||
"bit": 4,
|
||||
"length": 1,
|
||||
"evidence": "Z27AG-DECODE-2"
|
||||
}
|
||||
},
|
||||
"door_open": {
|
||||
"unit": "",
|
||||
"type": "boolean",
|
||||
"source": "z27ag",
|
||||
"description": "Any door open, the tailgate included (the bonnet has no switch)",
|
||||
"can": {
|
||||
"id": "0x423",
|
||||
"byte": 2,
|
||||
"bit": 1,
|
||||
"length": 1,
|
||||
"evidence": "Z27AG-DECODE-1"
|
||||
}
|
||||
},
|
||||
"driver_door_open": {
|
||||
"unit": "",
|
||||
"type": "boolean",
|
||||
"source": "z27ag",
|
||||
"description": "Driver's door open: the only door with its own bit",
|
||||
"can": {
|
||||
"id": "0x423",
|
||||
"byte": 0,
|
||||
"bit": 4,
|
||||
"length": 1,
|
||||
"evidence": "Z27AG-DECODE-1"
|
||||
}
|
||||
},
|
||||
"ac_compressor": {
|
||||
"unit": "",
|
||||
"type": "boolean",
|
||||
"source": "z27ag",
|
||||
"description": "A/C compressor engaged (engine running)",
|
||||
"can": {
|
||||
"id": "0x13D",
|
||||
"byte": 6,
|
||||
"bit": 7,
|
||||
"length": 1,
|
||||
"evidence": "Z27AG-DECODE-1"
|
||||
}
|
||||
},
|
||||
"vehicle_speed_kph": {
|
||||
"unit": "kph",
|
||||
"type": "number",
|
||||
"source": "z27ag",
|
||||
"description": "Road speed from the ABS (CAN), scaled against GNSS on this car's tyres: OBD-II speed reads 3 % lower",
|
||||
"range": [
|
||||
0,
|
||||
250
|
||||
],
|
||||
"can": {
|
||||
"id": "0x231",
|
||||
"byte": 6,
|
||||
"length": 16,
|
||||
"order": "little",
|
||||
"scale": 0.0577,
|
||||
"offset": 0,
|
||||
"evidence": "Z27AG-DECODE-2"
|
||||
}
|
||||
},
|
||||
"wheel_speed_fr_kph": {
|
||||
"unit": "kph",
|
||||
"type": "number",
|
||||
"source": "z27ag",
|
||||
"description": "Front right wheel speed from the ABS (CAN), scaled against GNSS while cruising. Which word is which wheel: from cornering (outer wheels faster, fronts on a wider arc than rears), not yet checked on jack stands",
|
||||
"range": [
|
||||
0,
|
||||
250
|
||||
],
|
||||
"can": {
|
||||
"id": "0x235",
|
||||
"byte": 0,
|
||||
"length": 16,
|
||||
"order": "little",
|
||||
"scale": 0.0578,
|
||||
"offset": 0,
|
||||
"evidence": "Z27AG-DECODE-3"
|
||||
}
|
||||
},
|
||||
"wheel_speed_fl_kph": {
|
||||
"unit": "kph",
|
||||
"type": "number",
|
||||
"source": "z27ag",
|
||||
"description": "Front left wheel speed from the ABS (CAN), scaled against GNSS while cruising. Which word is which wheel: from cornering (outer wheels faster, fronts on a wider arc than rears), not yet checked on jack stands",
|
||||
"range": [
|
||||
0,
|
||||
250
|
||||
],
|
||||
"can": {
|
||||
"id": "0x235",
|
||||
"byte": 2,
|
||||
"length": 16,
|
||||
"order": "little",
|
||||
"scale": 0.0578,
|
||||
"offset": 0,
|
||||
"evidence": "Z27AG-DECODE-3"
|
||||
}
|
||||
},
|
||||
"wheel_speed_rr_kph": {
|
||||
"unit": "kph",
|
||||
"type": "number",
|
||||
"source": "z27ag",
|
||||
"description": "Rear right wheel speed from the ABS (CAN), scaled against GNSS while cruising. Which word is which wheel: from cornering (outer wheels faster, fronts on a wider arc than rears), not yet checked on jack stands",
|
||||
"range": [
|
||||
0,
|
||||
250
|
||||
],
|
||||
"can": {
|
||||
"id": "0x235",
|
||||
"byte": 4,
|
||||
"length": 16,
|
||||
"order": "little",
|
||||
"scale": 0.0581,
|
||||
"offset": 0,
|
||||
"evidence": "Z27AG-DECODE-3"
|
||||
}
|
||||
},
|
||||
"wheel_speed_rl_kph": {
|
||||
"unit": "kph",
|
||||
"type": "number",
|
||||
"source": "z27ag",
|
||||
"description": "Rear left wheel speed from the ABS (CAN), scaled against GNSS while cruising. Which word is which wheel: from cornering (outer wheels faster, fronts on a wider arc than rears), not yet checked on jack stands",
|
||||
"range": [
|
||||
0,
|
||||
250
|
||||
],
|
||||
"can": {
|
||||
"id": "0x235",
|
||||
"byte": 6,
|
||||
"length": 16,
|
||||
"order": "little",
|
||||
"scale": 0.0581,
|
||||
"offset": 0,
|
||||
"evidence": "Z27AG-DECODE-3"
|
||||
}
|
||||
},
|
||||
"odometer_km": {
|
||||
"unit": "km",
|
||||
"type": "number",
|
||||
"source": "z27ag",
|
||||
"description": "Total distance (CAN), whole kilometres: steps once per kilometre driven (12 steps over 11.9 km). Not yet compared with the cluster's display",
|
||||
"range": [
|
||||
0,
|
||||
16777215
|
||||
],
|
||||
"can": {
|
||||
"id": "0x570",
|
||||
"byte": 4,
|
||||
"length": 24,
|
||||
"order": "little",
|
||||
"scale": 1,
|
||||
"offset": 0,
|
||||
"evidence": "Z27AG-DECODE-3"
|
||||
}
|
||||
},
|
||||
"coolant_temp_c": {
|
||||
"unit": "celsius",
|
||||
"type": "number",
|
||||
"source": "z27ag",
|
||||
"description": "Engine coolant temperature (CAN): equal to OBD-II's within 1 °C over 86-97 °C",
|
||||
"range": [
|
||||
-40,
|
||||
215
|
||||
],
|
||||
"can": {
|
||||
"id": "0x449",
|
||||
"byte": 2,
|
||||
"length": 8,
|
||||
"scale": 1,
|
||||
"offset": -40,
|
||||
"evidence": "Z27AG-DECODE-2"
|
||||
}
|
||||
},
|
||||
"front_wiper": {
|
||||
"unit": "",
|
||||
"type": "boolean",
|
||||
"source": "z27ag",
|
||||
"description": "Front wiper running (low or high; interval not seen in a 4 s window). Medium confidence",
|
||||
"can": {
|
||||
"id": "0x423",
|
||||
"byte": 3,
|
||||
"bit": 3,
|
||||
"length": 1,
|
||||
"evidence": "Z27AG-DECODE-2"
|
||||
}
|
||||
},
|
||||
"orin_cpu_temp_c": {
|
||||
"unit": "celsius",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "Orin CPU cluster temperature",
|
||||
"range": [
|
||||
-40,
|
||||
110
|
||||
]
|
||||
},
|
||||
"orin_gpu_temp_c": {
|
||||
"unit": "celsius",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "Orin GPU temperature",
|
||||
"range": [
|
||||
-40,
|
||||
110
|
||||
]
|
||||
},
|
||||
"orin_soc_temp_c": {
|
||||
"unit": "celsius",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "Orin SoC temperature, hottest of its sensors",
|
||||
"range": [
|
||||
-40,
|
||||
110
|
||||
]
|
||||
},
|
||||
"orin_tj_temp_c": {
|
||||
"unit": "celsius",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "Orin junction temperature: the hottest point on the die, which throttling acts on",
|
||||
"range": [
|
||||
-40,
|
||||
110
|
||||
]
|
||||
},
|
||||
"orin_cpu_pct": {
|
||||
"unit": "percent",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "CPU utilisation, all cores",
|
||||
"range": [
|
||||
0,
|
||||
100
|
||||
]
|
||||
},
|
||||
"orin_gpu_pct": {
|
||||
"unit": "percent",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "GPU utilisation",
|
||||
"range": [
|
||||
0,
|
||||
100
|
||||
]
|
||||
},
|
||||
"orin_ram_used_mb": {
|
||||
"unit": "MB",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "Memory in use, CPU and GPU together (one shared pool): total minus available",
|
||||
"range": [
|
||||
0,
|
||||
8192
|
||||
]
|
||||
},
|
||||
"orin_ram_used_pct": {
|
||||
"unit": "percent",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "Memory in use as a share of the total",
|
||||
"range": [
|
||||
0,
|
||||
100
|
||||
]
|
||||
},
|
||||
"orin_swap_used_mb": {
|
||||
"unit": "MB",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "Swap in use",
|
||||
"range": [
|
||||
0,
|
||||
16384
|
||||
]
|
||||
},
|
||||
"orin_power_w": {
|
||||
"unit": "W",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "Orin module input power (VDD_IN), from the onboard INA3221",
|
||||
"range": [
|
||||
0,
|
||||
30
|
||||
]
|
||||
},
|
||||
"orin_fan_rpm": {
|
||||
"unit": "rpm",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "Cooling fan speed",
|
||||
"range": [
|
||||
0,
|
||||
10000
|
||||
]
|
||||
},
|
||||
"net_wifi_up": {
|
||||
"unit": "",
|
||||
"type": "boolean",
|
||||
"source": "host",
|
||||
"description": "WiFi associated and up"
|
||||
},
|
||||
"net_wifi_ssid": {
|
||||
"unit": "",
|
||||
"type": "string",
|
||||
"source": "host",
|
||||
"description": "Network name (SSID) of the WiFi the Orin is associated with; not sent while WiFi is down"
|
||||
},
|
||||
"net_wifi_signal_dbm": {
|
||||
"unit": "dBm",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "WiFi signal level",
|
||||
"range": [
|
||||
-100,
|
||||
0
|
||||
]
|
||||
},
|
||||
"net_wifi_rx_kbps": {
|
||||
"unit": "kbit/s",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "WiFi receive rate (into the Orin)",
|
||||
"range": [
|
||||
0,
|
||||
1000000
|
||||
]
|
||||
},
|
||||
"net_wifi_tx_kbps": {
|
||||
"unit": "kbit/s",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "WiFi transmit rate (out of the Orin)",
|
||||
"range": [
|
||||
0,
|
||||
1000000
|
||||
]
|
||||
},
|
||||
"net_lte_state": {
|
||||
"unit": "",
|
||||
"type": "string",
|
||||
"source": "host",
|
||||
"description": "Modem state as ModemManager reports it (connected, registered, searching, disabled, …), or absent with no modem"
|
||||
},
|
||||
"net_lte_tech": {
|
||||
"unit": "",
|
||||
"type": "string",
|
||||
"source": "host",
|
||||
"description": "Access technology in use (lte, umts, …); empty when not registered"
|
||||
},
|
||||
"net_lte_signal_pct": {
|
||||
"unit": "percent",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "Modem signal quality",
|
||||
"range": [
|
||||
0,
|
||||
100
|
||||
]
|
||||
},
|
||||
"net_lte_rx_kbps": {
|
||||
"unit": "kbit/s",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "LTE receive rate (into the Orin)",
|
||||
"range": [
|
||||
0,
|
||||
150000
|
||||
]
|
||||
},
|
||||
"net_lte_tx_kbps": {
|
||||
"unit": "kbit/s",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "LTE transmit rate (out of the Orin)",
|
||||
"range": [
|
||||
0,
|
||||
50000
|
||||
]
|
||||
},
|
||||
"net_tablet_up": {
|
||||
"unit": "",
|
||||
"type": "boolean",
|
||||
"source": "host",
|
||||
"description": "USB link to the tablet has carrier"
|
||||
},
|
||||
"net_tablet_rx_kbps": {
|
||||
"unit": "kbit/s",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "USB tablet link, tablet to Orin",
|
||||
"range": [
|
||||
0,
|
||||
480000
|
||||
]
|
||||
},
|
||||
"net_tablet_tx_kbps": {
|
||||
"unit": "kbit/s",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "USB tablet link, Orin to tablet",
|
||||
"range": [
|
||||
0,
|
||||
480000
|
||||
]
|
||||
},
|
||||
"can_up": {
|
||||
"unit": "",
|
||||
"type": "boolean",
|
||||
"source": "host",
|
||||
"description": "The recorder's CAN interface (the PEAK, can_pcan) is present and up; false when absent or down"
|
||||
},
|
||||
"can_bus_state": {
|
||||
"unit": "",
|
||||
"type": "string",
|
||||
"source": "host",
|
||||
"description": "CAN controller state: ERROR-ACTIVE (normal), ERROR-WARNING, ERROR-PASSIVE, BUS-OFF, STOPPED (down), or absent with no adapter"
|
||||
},
|
||||
"can_rx_fps": {
|
||||
"unit": "frames/s",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "CAN frames received per second on that interface",
|
||||
"range": [
|
||||
0,
|
||||
10000
|
||||
]
|
||||
},
|
||||
"elm_connected": {
|
||||
"unit": "",
|
||||
"type": "boolean",
|
||||
"source": "host",
|
||||
"description": "Bluetooth link to the OBD-II adapter (the ELM327 on the K-line) is up: obd-poller holds it while the ECU answers"
|
||||
},
|
||||
"cam_up": {
|
||||
"unit": "",
|
||||
"type": "boolean",
|
||||
"source": "host",
|
||||
"description": "A USB (UVC) camera is present for svc-vision: any V4L2 device on USB"
|
||||
},
|
||||
"vision_up": {
|
||||
"unit": "",
|
||||
"type": "boolean",
|
||||
"source": "host",
|
||||
"description": "yukart-vision (the camera's live view) is running"
|
||||
},
|
||||
"ups_battery_v": {
|
||||
"unit": "V",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "UPS battery pack voltage (3S Li-ion, 9.0–12.6 V)",
|
||||
"range": [
|
||||
0,
|
||||
16
|
||||
]
|
||||
},
|
||||
"ups_current_a": {
|
||||
"unit": "A",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "UPS battery current: positive charging, negative discharging",
|
||||
"range": [
|
||||
-5,
|
||||
5
|
||||
]
|
||||
},
|
||||
"ups_battery_pct": {
|
||||
"unit": "percent",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "UPS charge: counted from the current since the charger last stopped at full when ups_pct_source is 'counted'; otherwise the estimate from cell voltage, corrected for load, steadiest at rest",
|
||||
"range": [
|
||||
0,
|
||||
100
|
||||
]
|
||||
},
|
||||
"ups_pct_source": {
|
||||
"unit": "",
|
||||
"type": "string",
|
||||
"source": "host",
|
||||
"description": "Where ups_battery_pct comes from: 'counted' (current integrated since the last full charge, against the configured capacity) or 'voltage' (no full charge seen since the count was lost: pack out, Orin off a while, capacity changed)"
|
||||
},
|
||||
"ups_charge_ah": {
|
||||
"unit": "Ah",
|
||||
"type": "number",
|
||||
"source": "host",
|
||||
"description": "UPS charge counted since the last full charge; absent until there has been one",
|
||||
"range": [
|
||||
0,
|
||||
20
|
||||
]
|
||||
},
|
||||
"ups_state": {
|
||||
"unit": "",
|
||||
"type": "string",
|
||||
"source": "host",
|
||||
"description": "charging, discharging (running on battery) or idle (on input power, not charging)"
|
||||
},
|
||||
"gnss_fix": {
|
||||
"unit": "",
|
||||
"type": "string",
|
||||
"source": "gnss",
|
||||
"description": "Fix quality: none, 2d or 3d (gpsd TPV mode)"
|
||||
},
|
||||
"gnss_speed_kph": {
|
||||
"unit": "kph",
|
||||
"type": "number",
|
||||
"source": "gnss",
|
||||
"description": "Speed over ground; null without a fix",
|
||||
"range": [
|
||||
0,
|
||||
300
|
||||
]
|
||||
},
|
||||
"gnss_heading_deg": {
|
||||
"unit": "degree",
|
||||
"type": "number",
|
||||
"source": "gnss",
|
||||
"description": "Course over ground, true north; null without a fix or at a standstill",
|
||||
"range": [
|
||||
0,
|
||||
360
|
||||
]
|
||||
},
|
||||
"gnss_lat": {
|
||||
"unit": "degree",
|
||||
"type": "number",
|
||||
"source": "gnss",
|
||||
"description": "Latitude, WGS84",
|
||||
"range": [
|
||||
-90,
|
||||
90
|
||||
]
|
||||
},
|
||||
"gnss_lon": {
|
||||
"unit": "degree",
|
||||
"type": "number",
|
||||
"source": "gnss",
|
||||
"description": "Longitude, WGS84",
|
||||
"range": [
|
||||
-180,
|
||||
180
|
||||
]
|
||||
},
|
||||
"gnss_alt_m": {
|
||||
"unit": "m",
|
||||
"type": "number",
|
||||
"source": "gnss",
|
||||
"description": "Altitude above mean sea level; null without a 3D fix",
|
||||
"range": [
|
||||
-500,
|
||||
9000
|
||||
]
|
||||
},
|
||||
"gnss_satellites": {
|
||||
"unit": "",
|
||||
"type": "number",
|
||||
"source": "gnss",
|
||||
"description": "Satellites used in the fix",
|
||||
"range": [
|
||||
0,
|
||||
64
|
||||
]
|
||||
},
|
||||
"imu_roll_deg": {
|
||||
"unit": "degree",
|
||||
"type": "number",
|
||||
"source": "imu",
|
||||
"description": "Body roll in the car's frame: positive leaning right (right side down)",
|
||||
"range": [
|
||||
-180,
|
||||
180
|
||||
]
|
||||
},
|
||||
"imu_pitch_deg": {
|
||||
"unit": "degree",
|
||||
"type": "number",
|
||||
"source": "imu",
|
||||
"description": "Body pitch in the car's frame: positive nose up",
|
||||
"range": [
|
||||
-90,
|
||||
90
|
||||
]
|
||||
},
|
||||
"imu_accel_lon_g": {
|
||||
"unit": "g",
|
||||
"type": "number",
|
||||
"source": "imu",
|
||||
"description": "Longitudinal acceleration in the car's frame, gravity removed: positive forward (speeding up), negative braking",
|
||||
"range": [
|
||||
-4,
|
||||
4
|
||||
]
|
||||
},
|
||||
"imu_accel_lat_g": {
|
||||
"unit": "g",
|
||||
"type": "number",
|
||||
"source": "imu",
|
||||
"description": "Lateral acceleration in the car's frame, gravity removed: positive to the right (a right turn reads positive)",
|
||||
"range": [
|
||||
-4,
|
||||
4
|
||||
]
|
||||
},
|
||||
"imu_yaw_rate_dps": {
|
||||
"unit": "degree/s",
|
||||
"type": "number",
|
||||
"source": "imu",
|
||||
"description": "Yaw rate: positive turning left (counter-clockwise from above)",
|
||||
"range": [
|
||||
-500,
|
||||
500
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+29
-1
@@ -12,7 +12,7 @@
|
||||
"payload": "envelope",
|
||||
"retained": true,
|
||||
"rate": "1-50/s",
|
||||
"publisher": "decoder",
|
||||
"publisher": "decoder (source z27ag), obd-poller (source obd2)",
|
||||
"description": "One topic per signal, named exactly as in signals.json."
|
||||
},
|
||||
"yukart/gnss/fix": {
|
||||
@@ -22,6 +22,34 @@
|
||||
"publisher": "gnss",
|
||||
"description": "value is an object: lat, lon, alt_m, speed_kph, heading_deg, fix, satellites."
|
||||
},
|
||||
"yukart/gnss/<signal>": {
|
||||
"payload": "envelope",
|
||||
"retained": true,
|
||||
"rate": "1-10/s",
|
||||
"publisher": "gnss",
|
||||
"description": "The receiver's fix as single values, one topic per signal, named exactly as in signals.json (source=gnss). For displays; yukart/gnss/fix stays the composite for navigation."
|
||||
},
|
||||
"yukart/imu/<signal>": {
|
||||
"payload": "envelope",
|
||||
"retained": true,
|
||||
"rate": "10-50/s",
|
||||
"publisher": "imu",
|
||||
"description": "The IMU (WT61) in the car's frame: attitude, gravity-free acceleration, yaw rate; one topic per signal, named exactly as in signals.json (source=imu)."
|
||||
},
|
||||
"yukart/raw/imu": {
|
||||
"payload": "json",
|
||||
"retained": false,
|
||||
"rate": "10-100/s",
|
||||
"publisher": "imu",
|
||||
"description": "Every IMU sample, raw: module axes, not calibrated. {mono (ns, CLOCK_MONOTONIC), t (µs, realtime), accel [x,y,z] g, gyro [x,y,z] deg/s, angle [roll,pitch,yaw] deg (the module's own)}. Not an envelope; for the recorder (imu.ndjson), so a later calibration can reprocess a session. Displays use yukart/imu/<signal>."
|
||||
},
|
||||
"yukart/host/<signal>": {
|
||||
"payload": "envelope",
|
||||
"retained": true,
|
||||
"rate": "1/s",
|
||||
"publisher": "orin-status",
|
||||
"description": "The Orin itself: temperatures, load, memory, network links, UPS battery. One topic per signal, named exactly as in signals.json (source=host)."
|
||||
},
|
||||
"yukart/system/<service>": {
|
||||
"payload": "json",
|
||||
"retained": true,
|
||||
|
||||
Reference in New Issue
Block a user