re: the rate is 56.8 units per guest second -- both predictions hold, control passes

The capture reached the title AFTER the harness stopped classifying, so the
plate's ramp was in the log with tick stamps after all.

ptbtn00: alpha 11->231 over 334.4 guest ms = 657.9 alpha/s. With its
independently attested T=22 that is 56.8 units per guest second, inside the
pre-registered 55-65 band. The final step of every ramp is excluded because
it clamps at 255 and reports more elapsed time than it consumed -- including
it drags the plate to 633.0 alpha/s, a 4% error entirely inside the clamp.

Control passes at 1.15%: ptcopyright gives 650.4 alpha/s, and at one shared
clock that makes its own segment T=22.25. Two elements agreeing on a rate
AND independently landing on a round declared length is stronger than either.

Why my earlier 29.9 was wrong, and it is the failure the page predicted: it
used T=15 borrowed from a row that is about A element with a 15-unit fade,
generalised to splash B's quads, which is not what that row says. At 56.8 the
splash elements' implied T is 23-34, none of them 15. And the step quantum
does not rescue 15: splash steps are multiples of 17 = 255/15, which is what
made 15 look confirmed, but at T=28.5 a step of 17 is simply TWO units of
8.9. A quantum fixes T only if you already know the step is one unit.

60 is NOT refuted -- 5.6% away against ~5% quantisation resolution -- so the
port keeps it. But this DOES eliminate the unit constant as a cause of a late
plate: at 56.8 units/s t=236 lands at 4.15 s against the port's 3.93 s, so
the port is fractionally early.
This commit is contained in:
sylph-decoder
2026-09-01 17:11:19 +00:00
parent 1ac388b0c1
commit d67aaaf30b
3 changed files with 108 additions and 1 deletions

View File

@@ -137,7 +137,7 @@ files, which is how the same ground got covered twice.
| [`structures/stage-mission-tables.md`](structures/stage-mission-tables.md) | The stage table set — phases, routes, sub-objectives and AI parameters | ✅ the table set and how the stage record reaches it, validated across; **`AIParams` disc-wide: 23 objects, one shared 34-profile roster (782 records), loader `sub_8233C368`; `Type`→field-count holds except the two `_Test` templates** | | [`structures/stage-mission-tables.md`](structures/stage-mission-tables.md) | The stage table set — phases, routes, sub-objectives and AI parameters | ✅ the table set and how the stage record reaches it, validated across; **`AIParams` disc-wide: 23 objects, one shared 34-profile roster (782 records), loader `sub_8233C368`; `Type`→field-count holds except the two `_Test` templates** |
| [`structures/texture-color-k8888.md`](structures/texture-color-k8888.md) | Texture colour interpretation — `k_8_8_8_8` (32bpp UI/HUD textures) | — | | [`structures/texture-color-k8888.md`](structures/texture-color-k8888.md) | Texture colour interpretation — `k_8_8_8_8` (32bpp UI/HUD textures) | — |
| [`ui-keyframe-time-unit.md`](ui-keyframe-time-unit.md) | What a keyframe time is worth, and what shape the ramp has | ✅ CONFIRMED from the running game's own draw stream — the ramp is **linear** (a declared 15-unit fade lands on `round(255·k/15)` for all seven samples) and the animation clock advances **2 time units per submitted frame**. 🟡 the seconds conversion (`1 unit = 1/60 s`) rests on a measured 27.6 present-frames/second | | [`ui-keyframe-time-unit.md`](ui-keyframe-time-unit.md) | What a keyframe time is worth, and what shape the ramp has | ✅ CONFIRMED from the running game's own draw stream — the ramp is **linear** (a declared 15-unit fade lands on `round(255·k/15)` for all seven samples) and the animation clock advances **2 time units per submitted frame**. 🟡 the seconds conversion (`1 unit = 1/60 s`) rests on a measured 27.6 present-frames/second |
| [`units-per-second-measured.md`](units-per-second-measured.md) + [pre-reg](units-per-second-preregistration.md) | Is the animation clock frame-counted or time-integrated, and at what rate | 🟡 **half-answered, 2026-09-01, one prediction HELD and one FAILED.****The clock is NOT frame-counted**: the same animation takes 21 labels in one capture and 33 in another, and splash A's logo steps `+136,+34` in one and `+17,+51,+34,+34,+17,+17` in the other. Steps are always integer multiples of **17** (255/15 = one unit), so the clock advances in whole units at a rate set by frame duration. 📌 **This retires "2 units per submitted frame" as a MECHANISM** — 2 was that run's frame pacing, so `units = 2 × frames` computes an emulator artefact. **The rate FAILED its prediction**: predicted 60 units/guest-second, measured **median 29.9** (25.236.6) — but that rests on `T=15`, which I have not read off the disc and whose usual derivation is circular with the thing just retired. **Settles with the plate (`T=22`, attested by two readers with no clock in the chain) captured with tick stamps; that run did not reach the title in 531 s.** Instrument: guest timebase (50 MHz, scalar 1.0), control passed — 123.24 guest s over ~118 wall s. | | [`units-per-second-measured.md`](units-per-second-measured.md) + [pre-reg](units-per-second-preregistration.md) | Is the animation clock frame-counted or time-integrated, and at what rate | 🟡 **half-answered, 2026-09-01, one prediction HELD and one FAILED.****The clock is NOT frame-counted**: the same animation takes 21 labels in one capture and 33 in another, and splash A's logo steps `+136,+34` in one and `+17,+51,+34,+34,+17,+17` in the other. Steps are always integer multiples of **17** (255/15 = one unit), so the clock advances in whole units at a rate set by frame duration. 📌 **This retires "2 units per submitted frame" as a MECHANISM** — 2 was that run's frame pacing, so `units = 2 × frames` computes an emulator artefact. **RESOLVED later the same iteration: 56.8 units per guest second**, on `ptbtn00`'s ramp with the clamped final step excluded, `T=22` attested by two readers with no clock in the chain — **inside the pre-registered 5565 band; 30 and 120 both excluded**. Control passed at **1.15 %**: `ptcopyright` gives 650.4 α/s against the plate's 657.9, implying its own `T=22.25`. The earlier 29.9 was a borrowed `T=15` that does not apply to those elements (implied `T` there is 2334). ⚠️ `60` is **not** refuted — 5.6 % away against ~5 % quantisation — so the port keeps it; but the unit constant is eliminated as a cause of a late plate (t=236 → 4.15 s vs the port's 3.93 s, i.e. fractionally *early*). Instrument: guest timebase (50 MHz, scalar 1.0), control passed — 123.24 guest s over ~118 wall s. |
| [`h3-units-per-frame-measured.md`](h3-units-per-frame-measured.md) | How many keyframe units elapse per guest frame, and which declared time the title's settle anchor is | ✅ **measured against a pre-registration, 2026-09-01. 2 units per guest frame**, on `ptbtn00`'s own declared ramp (`t=214→236`, T=22): three consecutive gap-free steps of **exactly 23** = 255×2/22, ramp span 10 labels against a predicted 11 (±1). The Port's inferred **5 is excluded by >2×**. 🔴 Why 5 appeared: **an alpha step is not a clock rate**`Δα/frame = 255·(units/frame)/T`, so splash B's 34-with-T=15 and the plate's 23-with-T=22 are ONE clock. Also: the **title settle anchor is t≈160**, not t=118 — it is `ptcopyright`, the last build-in element and the only glyph one, full at label 5350 → t≈168176. 🔴 **units/SECOND is NOT settled** and two of my own captures disagree ~2.9× on it; the gap is in frames→seconds, not units→frames. | | [`h3-units-per-frame-measured.md`](h3-units-per-frame-measured.md) | How many keyframe units elapse per guest frame, and which declared time the title's settle anchor is | ✅ **measured against a pre-registration, 2026-09-01. 2 units per guest frame**, on `ptbtn00`'s own declared ramp (`t=214→236`, T=22): three consecutive gap-free steps of **exactly 23** = 255×2/22, ramp span 10 labels against a predicted 11 (±1). The Port's inferred **5 is excluded by >2×**. 🔴 Why 5 appeared: **an alpha step is not a clock rate**`Δα/frame = 255·(units/frame)/T`, so splash B's 34-with-T=15 and the plate's 23-with-T=22 are ONE clock. Also: the **title settle anchor is t≈160**, not t=118 — it is `ptcopyright`, the last build-in element and the only glyph one, full at label 5350 → t≈168176. 🔴 **units/SECOND is NOT settled** and two of my own captures disagree ~2.9× on it; the gap is in frames→seconds, not units→frames. |
| [`h3-units-per-frame-preregistration.md`](h3-units-per-frame-preregistration.md) | The prediction, committed before the capture was read | ✅ kept as the control on the row above | | [`h3-units-per-frame-preregistration.md`](h3-units-per-frame-preregistration.md) | The prediction, committed before the capture was read | ✅ kept as the control on the row above |
| [`input-pad-read-path.md`](input-pad-read-path.md) | What the game asks the console for, on the pad | ✅ **decoded from the image, 2026-09-01.** `sub_82457038` is the only function that reads controller *data*, and it compares **every** field of `XINPUT_GAMEPAD``wButtons` (full 16 bits), both triggers, all four stick axes — against the previous state. **14/14** loads verified byte-for-byte against `/image/sylpheed.pe`, database used as an index only. **Two input paths**: the polled state and an 8-byte-record `XamInputGetKeystrokeEx` ring. ⚠️ This is the **superset the game can see, not the per-screen set** — reading a field is not acting on it. ❔ Which bits each screen tests is open; footholds are the image's own `C_PAD_DECODER` / `C_PAD_RINGBUF` trace strings (`sub_8220B610` / `sub_821A6470`). | | [`input-pad-read-path.md`](input-pad-read-path.md) | What the game asks the console for, on the pad | ✅ **decoded from the image, 2026-09-01.** `sub_82457038` is the only function that reads controller *data*, and it compares **every** field of `XINPUT_GAMEPAD``wButtons` (full 16 bits), both triggers, all four stick axes — against the previous state. **14/14** loads verified byte-for-byte against `/image/sylpheed.pe`, database used as an index only. **Two input paths**: the polled state and an 8-byte-record `XamInputGetKeystrokeEx` ring. ⚠️ This is the **superset the game can see, not the per-screen set** — reading a field is not acting on it. ❔ Which bits each screen tests is open; footholds are the image's own `C_PAD_DECODER` / `C_PAD_RINGBUF` trace strings (`sub_8220B610` / `sub_821A6470`). |

View File

@@ -0,0 +1,26 @@
# The animation clock's RATE, in GUEST seconds, off the title's build-in.
# Capture 2026-09-01 (tickcap), augmented draw logger with the guest timebase
# (50 MHz, guest_time_scalar_=1.0). No host wall clock enters any number here.
# The LAST step of a ramp is excluded everywhere: it clamps at 255 and so
# reports more elapsed time than it actually consumed.
## ptbtn00 (the plate)
ramp (label, alpha): (6709,11), (6710,46), (6711,69), (6712,92), (6713,115), (6715,162), (6716,185), (6717,197), (6718,231), (6719,255)
steps (Δα, ms): (35,31.5), (23,46.0), (23,22.7), (23,69.4), (47,49.4), (23,46.9), (12,35.3), (34,33.2), (24,51.0)
unclamped span: α 11 → 231 (Δα=220) over 334.4 guest ms
-> 657.9 alpha per guest second
-> declared T=22: **56.8 units per guest second**
## ptcopyright
ramp (label, alpha): (6675,34), (6676,57), (6677,69), (6678,92), (6679,115), (6680,139), (6681,162), (6683,231), (6684,255)
steps (Δα, ms): (23,47.8), (12,30.0), (23,33.2), (23,46.1), (24,24.6), (23,68.9), (69,52.4), (24,45.5)
unclamped span: α 34 → 231 (Δα=197) over 302.9 guest ms
-> 650.4 alpha per guest second
-> T not independently attested; at the plate's rate this implies T = 255·rate/(Δα/Δt)
## CONTROL (pre-registered): two independent elements, same screen, same run
ptbtn00 (the plate): 657.9 alpha/s
ptcopyright: 650.4 alpha/s
agreement: 1.15 %
-> they share one ramp length as well as one clock; at the plate's T=22
ptcopyright's implied T is 22.25

View File

@@ -126,3 +126,84 @@ Two captures, the boot splashes in both, one title capture without stamps. The
"not frame-counted" conclusion rests on a **comparison between captures** and is "not frame-counted" conclusion rests on a **comparison between captures** and is
as strong as the two captures being of the same animation, which their element as strong as the two captures being of the same animation, which their element
rects and declared ramps make certain. The rate has no reach at all yet. rects and declared ramps make certain. The rate has no reach at all yet.
---
# 🔴 RESOLVED LATER THE SAME ITERATION — the title capture landed, and the rate is **~57 units per guest second**
Everything above was written while the capture was still running. It then reached
the title **after** the harness had stopped classifying, so the plate's ramp is in
the log with tick stamps after all. Kept above rather than rewritten, because the
sequence is the point: the failed prediction was caused by exactly the `T`
circularity the page names, and the fix is the element whose `T` does not need a
clock.
## The measurement
[`data/units-per-second-rate.txt`](data/units-per-second-rate.txt)
```
ptbtn00 (the plate) α 11 → 231 over 334.4 guest ms 657.9 α/s
ptcopyright α 34 → 231 over 302.9 guest ms 650.4 α/s
```
The **last step of each ramp is excluded**: it clamps at 255 and therefore reports
more elapsed time than it consumed. Including it drags the plate from 657.9 to
633.0 α/s — a 4 % error entirely inside the clamp.
**With `ptbtn00`'s independently attested `T = 22`:**
> ## **56.8 units per guest second**
## ✅ Both pre-registered predictions now hold, and the control passes
| # | prediction | outcome |
|---|---|---|
| 1 | time-integrated | ✅ **held** — and by the between-capture argument above, not the regression |
| 2 | **60 units/s, accept 5565** | ✅ **56.8 — inside the band** |
| 3 | 120 and 30 excluded | ✅ **both excluded.** 30 would need `T = 11.6` for the plate; 120 would need `T = 46.5` |
**The control I pre-registered — two independent elements, same screen, same run —
passes at 1.15 %.** `ptcopyright` gives 650.4 α/s against the plate's 657.9. At
one shared clock that makes `ptcopyright`'s own segment **`T = 22.25`**, i.e. the
same 22-unit ramp; two elements agreeing on a rate *and* independently landing on
a round declared length is a stronger result than either alone.
## Why the earlier ~30 was wrong, and it is the failure the page predicted
`units/s = (Δα/Δt) × T / 255`. The splash estimate used **`T = 15`**, borrowed
from `ui-keyframe-time-unit.md`. That row is about *an* element with a declared
15-unit fade; **I generalised it to splash B's quads, which is not what it says.**
At the measured 56.8 units/s those elements' implied lengths are:
| element | measured α/s | implied `T` |
|---|---|---|
| splash A logo | 622.5 | **23.3** |
| splash B logos ×3 | 508.7 | **28.5** |
| splash B companions ×2 | 428.2 | **33.8** |
None is 15. The page above said the answer would move by exactly the factor the
unknown moved by, and it did — 29.9 × (28.5/15) = 56.8.
⚠️ **The step-quantum argument does not rescue `T = 15` either.** Splash steps are
multiples of 17 and `255/15 = 17`, which is what made 15 look confirmed — but at
`T = 28.5` a step of 17 is simply **two** units of 8.9. A quantum fixes `T` only
if you already know the step is one unit, and nothing said it was.
## Resolution and reach
**Classified: measured.** ⟨capture⟩, guest timebase, control passed.
* **`56.8` is not `60`, and `60` is not refuted.** The span is 19 units at ~11.6 α
per unit, so one unit of quantisation is ~5 %; 60 sits 5.6 % away, at the edge
of this measurement's resolution. **The port keeps 60.**
* **It does eliminate the unit constant as the cause of a late plate.** At 56.8
units/s the plate's `t = 236` lands at **4.15 s** after clock zero against the
port's 3.93 s — the port is fractionally *early*, not late. Whatever the human
saw, this is not it.
* 🟡 **Reach is the title.** The splashes are a different `GamePart` and this does
not establish that they tick at the same rate — it establishes that their `T`
is unknown, which is a different statement. Reading `T` off the disc for the
splash elements is the way to close that, and it is static work.
* 🟡 The **structured residual** — long frames advancing less than a constant rate
predicts — is untouched and still unexplained.