Compare commits

..

14 Commits

Author SHA1 Message Date
mikkeli e47468fc98 UPS: ups_pct_source and ups_charge_ah (counted charge); host.ndjson in capture sessions
/ crosscheck (push) Successful in 1s
Rust consumers (L4T) / Clippy, test, build, glibc for every service that compiles contracts in (push) Successful in 22s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
2026-10-16 00:17:34 +09:00
mikkeli 9452708e80 Checklist v5: step 32, temperature dial; step 14 notes the overnight soak
/ crosscheck (push) Successful in 1s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
2026-10-15 23:17:48 +09:00
mikkeli cff32a7190 Signals: cam_up and vision_up (orin-status), for the app's status bar
/ crosscheck (push) Successful in 1s
Rust consumers (L4T) / Clippy, test, build, glibc for every service that compiles contracts in (push) Successful in 21s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 23:01:03 +09:00
mikkeli 15c7d6ce84 Checklist v4: step 31, each wheel by hand on stands; DECODE-3: 449 bytes 0, 1, 3 as climate-control sensor candidates
/ crosscheck (push) Successful in 1s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
2026-10-15 22:30:40 +09:00
mikkeli c31af2d5e3 Z27AG-DECODE-3: 570 bytes 2-3 slosh with motion, so fuel (not temperature); odometer agrees with the cluster
/ crosscheck (push) Successful in 2s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
2026-10-15 22:23:47 +09:00
mikkeli e8ba581985 Z27AG decode pass 3: wheel positions from corners, odometer (570, 24-bit); layouts can be 24 bits
/ crosscheck (push) Successful in 1s
Rust consumers (L4T) / Clippy, test, build, glibc for every service that compiles contracts in (push) Successful in 25s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
2026-10-15 22:13:34 +09:00
mikkeli 642be2a5e1 Steering angle (0C0, signed 0.1°), main beam, front fogs; layouts can be signed; dipped beam doubted
/ crosscheck (push) Successful in 2s
Rust consumers (L4T) / Clippy, test, build, glibc for every service that compiles contracts in (push) Successful in 22s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
2026-10-15 21:21:48 +09:00
mikkeli 0af2c956e4 Z27AG decode pass 2: vehicle speed, coolant, front wiper; wheel speeds little-endian; IMU checked against GNSS
/ crosscheck (push) Successful in 1s
Rust consumers (L4T) / Clippy, test, build, glibc for every service that compiles contracts in (push) Successful in 22s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
2026-10-15 20:15:07 +09:00
mikkeli b52a7dcedb Checklist v3: IMU calibration (level twice, straight pull-aways and stops); yukart/raw/imu
/ crosscheck (push) Successful in 1s
Rust consumers (L4T) / Clippy, test, build, glibc for every service that compiles contracts in (push) Successful in 19s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
2026-10-14 13:28:40 +09:00
mikkeli 4cbac36fbe IMU signals in the car's frame (attitude, gravity-free g, yaw rate) on yukart/imu/<signal>
/ crosscheck (push) Successful in 0s
Rust consumers (L4T) / Clippy, test, build, glibc for every service that compiles contracts in (push) Successful in 17s
The WT61's publisher owns mounting and gravity; displays get car-frame values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
2026-10-13 23:23:31 +09:00
mikkeli 5d186bfd5d Captures plan: OBD-II through the ELM on the K-line, obd_* names, what each poller state means
/ crosscheck (push) Successful in 1s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
2026-10-13 22:57:31 +09:00
mikkeli 229d796cfd OBD-II signals renamed obd_*, plus timing, trims, O2; GNSS signals; elm_connected; reboot command
/ crosscheck (push) Successful in 1s
Rust consumers (L4T) / Clippy, test, build, glibc for every service that compiles contracts in (push) Failing after 6s
Decision 30: CAN feeds displays, OBD-II (K-line via the ELM) calibrates, so every
obd2 signal carries the obd_ prefix and the plain names are left for decoded ones.
yukart/gnss/<signal> gives displays the fix as single values; gnss/fix stays for
navigation. Boundary: reboot beside poweroff, same guards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
2026-10-13 22:50:03 +09:00
mikkeli 5feba8b354 Decode report: clutch and transmission not on CAN, stated once; no camera trigger plan
/ crosscheck (push) Successful in 0s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
2026-10-13 21:48:07 +09:00
mikkeli 0eab0e660e Decode report: reverse rechecked, gear inference dropped for a recommended gear
/ crosscheck (push) Successful in 0s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
2026-10-13 21:44:52 +09:00
9 changed files with 833 additions and 116 deletions
+98 -2
View File
@@ -1,6 +1,6 @@
{ {
"id": "z27ag-captures", "id": "z27ag-captures",
"version": 2, "version": 5,
"title": "Z27AG decoding captures", "title": "Z27AG decoding captures",
"about": "docs/impl/plans/Z27AG-CAPTURES.md", "about": "docs/impl/plans/Z27AG-CAPTURES.md",
"mark_format": "<step> <action>[ #<n>] start|end for a span, <step> <action>[ #<n>] for a point", "mark_format": "<step> <action>[ #<n>] start|end for a span, <step> <action>[ #<n>] for a point",
@@ -254,7 +254,7 @@
"id": "14", "id": "14",
"title": "Engine start", "title": "Engine start",
"do": "Cold start if possible: mark as the starter turns, then idle 60 s.", "do": "Cold start if possible: mark as the starter turns, then idle 60 s.",
"finds": "starting, coolant and oil warming, idle control", "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": [ "actions": [
{ {
"label": "engine start", "label": "engine start",
@@ -320,6 +320,25 @@
"point": true "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
}
]
} }
] ]
}, },
@@ -445,6 +464,83 @@
"hold_s": 10 "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
}
]
} }
] ]
} }
+9 -7
View File
@@ -37,7 +37,7 @@ The two channels from decision 24 share the socket but keep different guarantees
| `ack` | reply to a `cmd` | `id`, `ok`, `detail`, `dup` (true when answered from the cache) | | `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 With `source: bus`, `signals` is the newest envelope of every signal svc-gw has
seen on `yukart/vehicle/+` and `yukart/host/+`. svc-gw marks one 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 `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 the broker's retained copy (which may predate a reboot). A signal once seen is
never removed. never removed.
@@ -66,18 +66,20 @@ tablet, or an Orin behind a replugged hub, looks connected at the link layer.
|---|---|---| |---|---|---|
| `ping` | — | `ack` with `detail: "pong"` | | `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 | | `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-poweroff` (the Orin's unit only), and when `boot_id` is not the current boot's | | `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 A repeated `id` returns the original `ack` (last 256 kept, shared across
connections) and never re-runs the command. connections) and never re-runs the command.
`poweroff` is the exception to retry-until-acked: the ack cache does not survive `poweroff` and `reboot` are the exception to retry-until-acked: the ack cache does
the reboot it causes, so a resend could shut the next boot down too. The client not survive the restart they cause, so a resend could shut the next boot down (or
sends it once and does not resend it; `boot_id` makes svc-gw refuse one from a reboot it again). The client sends them once and does not resend; `boot_id` makes
previous boot regardless. svc-gw refuse one from a previous boot regardless.
## Not yet ## Not yet
- TTS utterances (id, chunk sequence, terminator, expiry) — when there is a TTS. - TTS utterances (id, chunk sequence, terminator, expiry) — when there is a TTS.
- `gnss`: its fix is an object, which the envelope's `value` does not allow yet. - `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. - Any version beyond `proto: 0`. A breaking change bumps it; the client checks it.
+1 -1
View File
@@ -9,7 +9,7 @@ type: guidelines
- ⚠ **The unit appears in both the topic name and the payload, so they can - ⚠ **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 contradict each other.** Generating both from one entry is what stops that; a
hand-written `"unit"` reintroduces the failure it was meant to prevent. 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. mismatch is visible at the call site rather than one registry lookup away.
## 2. Two clocks, and they answer different questions ## 2. Two clocks, and they answer different questions
+42 -16
View File
@@ -1,7 +1,7 @@
--- ---
type: plan type: plan
status: ready status: ready
updated: 2026-10-13 updated: 2026-10-15
--- ---
# Z27AG decoding captures: what to do in the car # Z27AG decoding captures: what to do in the car
@@ -13,10 +13,12 @@ ground truth. Prior art and first candidates: [the prior-art report](../reports/
| In the session directory | From | Use | | In the session directory | From | Use |
|---|---|---| |---|---|---|
| `can-*.log(.zst)` | the PEAK, every frame, kernel timestamps | what gets decoded; also holds every OBD request (`7DF`) and reply (`7E8`) | | `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` | `obd-poller` via the bus | the same OBD replies as values (rpm, speed, throttle, load, MAP, MAF ~10 Hz; coolant, intake air, fuel 1 Hz); `env.timestamp` equals the reply frame's timestamp | | `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 | | `events.ndjson` | marks from the app | when each step happened |
| `gnss.ndjson` | gpsd (SIM7600) | speed and position for the drive | | `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 ## Operating it from the app
@@ -33,8 +35,9 @@ ground truth. Prior art and first candidates: [the prior-art report](../reports/
session` when there is no traffic yet (ignition on, PEAK plugged). A mark sent with 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 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. - The **Mark** button on the main screen is still there for anything off-plan.
- **OBD is working** when `engine_rpm`, `vehicle_speed_kph` and the rest appear in - **OBD is working** when `obd_engine_rpm`, `obd_coolant_temp_c` and the rest appear
the signal list and move. If they never appear, see *If OBD stays silent*. 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 ## The steps
@@ -52,14 +55,22 @@ recorder refuses marks with "no session" otherwise); engine off, doors shut.
- **Engine running, parked** (14–18): start and idle, held revs in neutral (no OBD - **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. 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, - **Driving** (19–22): steady speeds, stops, corners, each gear. A passenger presses,
or skip the marks: GNSS speed is recorded continuously, and gear comes from or skip the marks: GNSS speed is recorded continuously. The gear is not on CAN and
rpm ÷ speed. is not inferred (decode report).
- **On jack stands** (23–26, since v2): front wheels up, rears chocked, handbrake - **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, 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 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. 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 2 (2026-10-13) adds the stands section and writes down the conditions the 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 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 handbrake step, neutral with the clutch up for the revs.
@@ -80,18 +91,33 @@ The parked steps can be split over several sessions; the drive is the important
## If OBD stays silent ## If OBD stays silent
`yukart/system/obd-poller` says why (visible in `mosquitto_sub -v -t 'yukart/system/#'` `yukart/system/obd-poller` says why (visible in `mosquitto_sub -v -t 'yukart/system/#'`
on the Orin): on the Orin), and the signal list's `elm_connected` shows the Bluetooth link:
- `bus idle: not polling`: no traffic: ignition off or PEAK unplugged. - `ELM …: … (unplugged, asleep or out of range)`: no Bluetooth link. The ELM in the
- `no ECU answers 01 00`: the engine ECU does not do standard mode 01 on this bus. port? Its light on? A phone app holding it (it takes one connection)? Retried
Then OBDb's MUT-III (`21 xx`) requests are the next thing to try. 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`, - To see what answers, on the Orin: `sudo systemctl stop yukart-obd-poller`,
`yukart-obd-poller --once` (supported PIDs and one value each, with reply times), `yukart-obd-poller --elm $(. /etc/default/yukart-elm; echo $ELM_ADDR) --once`
`sudo systemctl start yukart-obd-poller`. Never two pollers at once: their (adapter, protocol, supported PIDs and one value each, with reply times), then
replies would cross. `sudo systemctl start yukart-obd-poller`.
## Afterwards ## 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 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 `rsync -a mikkeli-orin-nano:/var/lib/yukart/recordings/<session>/ …` into
`yukart-data`, with a line in its README of what was done. `yukart-data`, with a line in its README of what was done.
+9 -4
View File
@@ -46,10 +46,15 @@ jack-stands session and a GNSS-matched drive settle both the byte order and the
## Not on this bus (or not seen) ## Not on this bus (or not seen)
- **Clutch pedal:** nothing changed in 3 presses, engine off. - **Clutch and transmission are not on this bus.** Nothing changed for the clutch
- **Gear position:** nothing in 5 gears, reverse or neutral. Expected for a manual. The (3 presses), any of the 5 gears, neutral or reverse: nothing on the car consumes
gear can be computed from rpm ÷ wheel speed once the scale is known. them, so no frame carries them. Reverse was rechecked against both reverse
- **Reverse:** no reverse bit found. The reverse lamp is probably wired directly. 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). - **Steering angle:** not analysed yet (step 5 was done with the engine on, at the end).
## Not analysed yet ## Not analysed yet
+78
View File
@@ -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).
+98
View File
@@ -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.
+476 -85
View File
@@ -1,6 +1,136 @@
{ {
"$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=host is the Orin itself (orin-status), not the car. 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 or 16), bit (length 1 only: 0 = LSB of that byte), order (length 16: big = byte first is most significant, little = the reverse), scale and offset (value = raw * scale + offset; booleans are raw != 0). 'evidence' names the contracts report that found it.", "$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": { "signals": {
"obd_vehicle_speed_kph": {
"unit": "kph",
"type": "number",
"source": "obd2",
"description": "Road speed",
"range": [
0,
255
]
},
"obd_coolant_temp_c": {
"unit": "celsius",
"type": "number",
"source": "obd2",
"description": "Engine coolant temperature",
"range": [
-40,
215
]
},
"obd_intake_air_temp_c": {
"unit": "celsius",
"type": "number",
"source": "obd2",
"description": "Intake air temperature",
"range": [
-40,
215
]
},
"obd_throttle_pct": {
"unit": "percent",
"type": "number",
"source": "obd2",
"description": "Throttle position",
"range": [
0,
100
]
},
"obd_engine_load_pct": {
"unit": "percent",
"type": "number",
"source": "obd2",
"description": "Calculated engine load",
"range": [
0,
100
]
},
"obd_fuel_level_pct": {
"unit": "percent",
"type": "number",
"source": "obd2",
"description": "Fuel tank level",
"range": [
0,
100
]
},
"obd_intake_pressure_kpa": {
"unit": "kpa",
"type": "number",
"source": "obd2",
"description": "Intake manifold absolute pressure",
"range": [
0,
255
]
},
"obd_maf_gps": {
"unit": "g/s",
"type": "number",
"source": "obd2",
"description": "Mass air flow rate",
"range": [
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": { "engine_rpm": {
"unit": "rpm", "unit": "rpm",
"type": "number", "type": "number",
@@ -20,86 +150,6 @@
"evidence": "Z27AG-DECODE-1" "evidence": "Z27AG-DECODE-1"
} }
}, },
"vehicle_speed_kph": {
"unit": "kph",
"type": "number",
"source": "obd2",
"description": "Road speed",
"range": [
0,
255
]
},
"coolant_temp_c": {
"unit": "celsius",
"type": "number",
"source": "obd2",
"description": "Engine coolant temperature",
"range": [
-40,
215
]
},
"intake_air_temp_c": {
"unit": "celsius",
"type": "number",
"source": "obd2",
"description": "Intake air temperature",
"range": [
-40,
215
]
},
"throttle_pct": {
"unit": "percent",
"type": "number",
"source": "obd2",
"description": "Throttle position",
"range": [
0,
100
]
},
"engine_load_pct": {
"unit": "percent",
"type": "number",
"source": "obd2",
"description": "Calculated engine load",
"range": [
0,
100
]
},
"fuel_level_pct": {
"unit": "percent",
"type": "number",
"source": "obd2",
"description": "Fuel tank level",
"range": [
0,
100
]
},
"intake_pressure_kpa": {
"unit": "kpa",
"type": "number",
"source": "obd2",
"description": "Intake manifold absolute pressure",
"range": [
0,
255
]
},
"maf_gps": {
"unit": "g/s",
"type": "number",
"source": "obd2",
"description": "Mass air flow rate",
"range": [
0,
656
]
},
"accel_pedal_pct": { "accel_pedal_pct": {
"unit": "percent", "unit": "percent",
"type": "number", "type": "number",
@@ -118,6 +168,26 @@
"evidence": "Z27AG-DECODE-1" "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": { "brake_pedal": {
"unit": "", "unit": "",
"type": "boolean", "type": "boolean",
@@ -187,7 +257,7 @@
"unit": "", "unit": "",
"type": "boolean", "type": "boolean",
"source": "z27ag", "source": "z27ag",
"description": "Dipped headlights on", "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": { "can": {
"id": "0x423", "id": "0x423",
"byte": 3, "byte": 3,
@@ -196,11 +266,37 @@
"evidence": "Z27AG-DECODE-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": { "door_open": {
"unit": "", "unit": "",
"type": "boolean", "type": "boolean",
"source": "z27ag", "source": "z27ag",
"description": "Any door open", "description": "Any door open, the tailgate included (the bonnet has no switch)",
"can": { "can": {
"id": "0x423", "id": "0x423",
"byte": 2, "byte": 2,
@@ -213,7 +309,7 @@
"unit": "", "unit": "",
"type": "boolean", "type": "boolean",
"source": "z27ag", "source": "z27ag",
"description": "Driver's door open", "description": "Driver's door open: the only door with its own bit",
"can": { "can": {
"id": "0x423", "id": "0x423",
"byte": 0, "byte": 0,
@@ -235,6 +331,151 @@
"evidence": "Z27AG-DECODE-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": { "orin_cpu_temp_c": {
"unit": "celsius", "unit": "celsius",
"type": "number", "type": "number",
@@ -477,6 +718,24 @@
10000 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": { "ups_battery_v": {
"unit": "V", "unit": "V",
"type": "number", "type": "number",
@@ -501,17 +760,149 @@
"unit": "percent", "unit": "percent",
"type": "number", "type": "number",
"source": "host", "source": "host",
"description": "UPS charge estimate from cell voltage, corrected for load. An estimate, steadiest at rest", "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": [ "range": [
0, 0,
100 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": { "ups_state": {
"unit": "", "unit": "",
"type": "string", "type": "string",
"source": "host", "source": "host",
"description": "charging, discharging (running on battery) or idle (on input power, not charging)" "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
]
} }
} }
} }
+22 -1
View File
@@ -12,7 +12,7 @@
"payload": "envelope", "payload": "envelope",
"retained": true, "retained": true,
"rate": "1-50/s", "rate": "1-50/s",
"publisher": "decoder, obd-poller", "publisher": "decoder (source z27ag), obd-poller (source obd2)",
"description": "One topic per signal, named exactly as in signals.json." "description": "One topic per signal, named exactly as in signals.json."
}, },
"yukart/gnss/fix": { "yukart/gnss/fix": {
@@ -22,6 +22,27 @@
"publisher": "gnss", "publisher": "gnss",
"description": "value is an object: lat, lon, alt_m, speed_kph, heading_deg, fix, satellites." "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>": { "yukart/host/<signal>": {
"payload": "envelope", "payload": "envelope",
"retained": true, "retained": true,