Files
samplez/tunie/research/reference-maps/TABLES.md
uhryniuk 4aa5da53d2 Commit tune maps and research so tunes are reachable from the phone
Removes the *.hex/maps_cache gitignore rule (explicit user call, reversing
the earlier no-redistribution stance) so the official TuneECU catalogue
maps, derived SAI/O2-delete composites, and the checksum/composition
tooling are actually available to pull up on a phone browser when using
the real TuneECU app. Also folds in tonight's KWP2000 fixes (TesterPresent
keep-alive, connect-failure cleanup, slow-init StartCommunication fix) and
the accumulated research docs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FP2GaxS9HkUdL5sLBnjKje
2026-08-27 01:34:05 -05:00

147 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Keihin SH7054 (Triumph 865 twin, mechanical odo) — table map
Reversed from `Nc()`/`Lb()` in `l.java` and validated against the stock reference
maps. All offsets are in the reconstructed **flat ROM** (0x60000). Compute per map:
```
fe = c.a[Qd*48] # Qd from the s.a directory lookup on the map signature
base = (fe[0] & 0x2F0) << 12 # = 0x50000 for the 865 twin family
offset(table) = base + fe[<field>]
```
Main tables are **32 rows (RPM) × 20 cols (throttle), 16-bit big-endian**.
## Axes
| Axis | fe field | Entries | Values (20187) | Meaning |
|---|---|---|---|---|
| RPM (rows) | `fe[8]` | 32 | 0, 500, 900, 1000 … 10000 | engine speed, direct RPM |
| Throttle (cols) | `fe[27]` | 20 | 0, 10, 20 … 780, 1000 | throttle %, ÷10 (0–100.0%) |
## Tables (validated by production 20187 vs aftermarket 20188 diff)
| Table | fe | Offset (20187) | Value range | Notes |
|---|---|---|---|---|
| Main fuel — cyl 1 | 11 | 0x56990 | 951–10480 | **strong**: 75% diff, smooth VE, aftermarket richer |
| Main fuel — cyl 2 | 12 | 0x56E90 | 951–10680 | **strong**: 67% diff |
| Low-throttle fuel — cyl 1 | 15 | 0x55590 | 0–10760 | 70% diff; starts at 0 (closed throttle) |
| Low-throttle fuel — cyl 2 | 16 | 0x55A90 | 0–10810 | 68% diff; cyl-1 pair |
| Fuel — base/idle | 9 | 0x55500 | 0–10760 | 61% diff; lower values |
| Ignition advance — gear 1 | 19 | 0x587D0 | 13–600 | 4 evenly-spaced (0x500) 20×32 tables |
| Ignition advance — gears 2–5 | 20 | 0x58CD0 | 13–600 | main operating map |
| Ignition advance — gear 6 | 21 | 0x591D0 | 13–600 | overdrive |
| Ignition advance — neutral | 22 | 0x596D0 | 60–600 | idle/free-rev |
## AFR / target lambda
`fe[2]` @ 0x52260 is an interleaved (value, breakpoint) curve where **128 = λ1.00
(stoich, 14.7 AFR)** — e.g. `… 128, 500, 128, 400, 128, 300 …`. The `128` cells are
the closed-loop lambda targets; disabling closed loop (O2 delete) lets you set
these richer. Exact dimensions still being confirmed.
## Scaling (confidence varies)
- **Fuel** values are VE/fuel-mass in the ECU's internal units (≈ 0–10800). Real
units (injector µs / VE %) need the ECU's fuel constant; relative changes are
exact, absolute scaling TBD.
- **Ignition** 13–600 is advance in an internal unit (candidate: value ÷ 10 = °BTDC,
giving ~1.3–60°; the low-RPM row 90→14→20 fits a retard-then-advance curve). TBD.
- **AFR** 128 = λ1.00 is solid (standard Keihin convention).
## Fuel/ignition trim array — RPM-indexed, 32 entries, `0x53690`-`0x536AF`
**Located and axis-mapped (Aug 2026).** A 32-byte array sitting just before
the device-flags region (`0x53801`, `TABLES.md` above / `DEVICES.md`), one
byte per **RPM breakpoint on the same 32-point RPM axis** as the main
tables (`fe[8]`: 0, 500, 900, 1000, ..., 10000). Not addressed via a `fe[]`
pointer for the maps checked (no `fe` field in `c.a[Qd*48]` equals `0x3690`
for `20187`'s `Qd=32`) — likely a fixed offset relative to the device-flags
struct rather than an independently relocatable table, unconfirmed.
Evidence for the RPM-axis mapping: diffing `20188Map2009AIRBOXBONNY.hex`
(the Bonneville SAI/O2 delete map) against stock `20188` shows **exactly 2
of the 32 cells changed — at RPM=2400 and RPM=7000** — with all 30 other
RPM-indexed cells byte-identical. That's `0x5369C` (index 12, RPM 2400) and
`0x536AB` (index 27, RPM 7000) from earlier sessions, now understood as two
specific points on this curve rather than free-floating bytes.
Cross-checked against `20184dynoTuneSteveO2-Disable.hex` (America,
open-pipe + airbox-snorkel-removed + O2-disable, real dyno tune) vs. stock
`20186`: **nearly every one of the 32 cells changed**, by large,
non-monotonic swings (e.g. RPM=1000: 231→63, RPM=4400: 235→5, RPM=5200:
25→204). Not a smooth curve shift — looks like cell-by-cell dyno-session
adjustment (consistent with a human tuner nudging individual RPM points
after successive pulls) rather than a single global correction. Confirms
this is a real, independently-tunable-per-cell array, not a scalar.
**Identity: confirmed as "Idle Fuel Trim (CO)".** Crawled tuneecu.net's full
docs site (`../tuneecu_site_mirror/`) and found the exact match in
`TuneECU_En/tests.html`, TuneECU's live-diagnostics parameter list:
> "Idle Fuel Trim (CO): lets you adjust the fuel richness at idle."
> Available for "**Triumph without O²-Sensor only**."
> (Counterpart: "Long Term Fuel Trim... Triumph models **with** O²-Sensor
> only" — the closed-loop equivalent for bikes that still have the sensor.)
This is a direct, official match to what we observed empirically: the table
is specifically the O2-delete-relevant trim, exactly why it's what the real
delete/dyno maps touch and the stock maps don't need to. `co_trim`/`ift_co`
string resources (`strings.xml`: "CO Trim" / "Idle Fuel Trim (CO)") back
this — "CO" = idle mixture richness, the classic idle-adjustment convention
carried over from carbureted-era tuning terminology.
**Encoding, upgraded confidence (Aug 2026):** found TuneECU's actual "CO
Trim" edit dialog (`MainActivity.java`'s `La()`) and its value-scaling
logic. Two independent code paths converge on the same answer:
1. The dialog's clamp/display logic (`j8()`) treats the value as a plain
signed range: `if (v < -128) v = -128; else if (v > 127) v = 127;`,
displayed as `Integer.toString(v)` — no fractional scaling.
2. A separate live-value dispatcher (`n.java`, a large switch handling
various PID-style codes) has a case doing manual two's-complement
conversion — `if (raw > 127) raw -= 256;` — immediately before storing
into the same field (`MainActivity.Mc`) the CO Trim dialog reads.
**Conclusion: the raw byte is signed two's-complement (-128 to 127), used
directly with no multiply/divide scaling factor** — not the unsigned 0-255
interpretation this project used everywhere earlier. **This retroactively
resolves the "255 looks like a sentinel" puzzle** from the exhaust-family
composability analysis (`COMMUNITY_TUNING.md`/`PERMUTATIONS.md`): unsigned
`255` is simply signed `-1` — an entirely ordinary, near-neutral value, not
a special case. Re-reading the earlier data with correct signs: the stock
aftermarket-silencer family sits near `-1` (neutral), production/Arrow
sits around `+69` to `+125` (already noticeably rich), and the real
SAI/O2 delete map pushes to `+117` — a large, deliberate enrichment,
exactly the physically-sensible story this parameter's name would predict.
The composability conclusion (can't cleanly merge Arrow's delta with the
delete map's delta) still holds — redone with correct signs, the combined
correction still saturates past the `+127` clamp — but now for an
intuitive reason (the needed correction is genuinely large) instead of an
unexplained sentinel.
**Not fully closed:** the *encoding* (signed int8, no scaling) is now
well-supported by two independent sites; the exact real-world *unit* (is
`+1` precisely "+1% CO", or a nearby-but-not-identical ECU-internal unit)
isn't separately confirmed by either site. Raised from low to medium-high
confidence — upgraded, not fully resolved.
**Practical implication for composing tunes:** don't treat this array as
"2 conflicting bytes" the way `compose_arrow_delete.py` originally did —
it's a full 32-cell curve. A conservative mod (mufflers only) may touch 2
cells; a more aggressive one (open pipes + airbox) touches nearly all 32.
Composing two independent tunes' curves here needs the same per-cell
arithmetic treatment as the main VE tables (`PERMUTATIONS.md`), not a
handful of spot fixes.
## SAI / O2 / lambda flags
Now findable via the localised flat-ROM diff (20187 vs 20188), which isolates small
single-byte changes (e.g. 0x50C13, 0x534AD, 0x59759) as prime toggle candidates.
Confirming which is SAI vs O2 needs either bench testing or the finer `Nc` device
logic — a follow-up.
## Status
Fuel and ignition table **locations** are validated end-to-end. Remaining: exact
scaling constants, AFR dimensions, and confirming the individual device flags.