Files
Syplheed-Reborn/docs/re/flight-speed-law.md
Claude (auto-RE) f6974ff3f0 re(flight): settled turn rates match the definition exactly — withdraw the time-base claim
Re-measured with 5 s of settle per phase and the speed recorded at the moment the
turn starts (flight_law3.py):

  pitch @ 130/s    74.9 deg/s   vs AV_PitchMinus_Min 75
  pitch @ 1821/s   41.1         vs AV_PitchMinus_Max 40
  roll  @ 110/s   129.5         vs AV_Roll_Min 200
  roll  @ 1722/s  149.3         vs AV_Roll_Max 125

Pitch lands on the definition's own numbers with NO scale factor, so the ~1.2x I
attributed to the emulated time base two iterations ago was an artefact of
differentiating during the AA_* acceleration ramp with too little settle. That
explanation is withdrawn: AV_* can be used verbatim.

What remains is only on the linear side — settled speeds still read high and vary
between runs (RT full: 1342 in one flight, 1821 in another, vs MaximumVelocity 1200),
consistent with a craft being shoved around in a firefight. The HUD reads exactly
CruisingVelocity at neutral. A clean linear measurement needs a quiet map; no cause
is claimed until then.

Roll re-measured with proper settles confirms the axis difference: no speed
dependence, both regimes near AV_Roll_Max, where pitch moved 75 -> 40.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NptfmpjdpNCKEez6d2xvA9
2026-08-13 16:46:33 +00:00

13 KiB
Raw Blame History

The throttle is a TARGET-SPEED selector — measured against the definition (2026-08-13)

Status: for the shape of the law, 🟡 for the unit scale.

The unit definition gives MinimumVelocity 100, CruisingVelocity 350, MaximumVelocity 1200, Acceleration 600 and Deceleration 500 for UN_f001_TCAF_DeltaSaber_T_Player (values), but not how the game applies them. This measures it.

Method — tools/re-capture/speed_law.py: lock onto the player entity once, then sample its own position triple in guest RAM while holding each throttle input in turn (8 s per phase, 20 Hz). Speed is differentiated over 1-second windows, never per sample.

Result

phase measured speed (1 s windows) settles to definition field
no throttle 367 403 471 365 463 463 365 ~420 CruisingVelocity 350
RT held 1041 1376 1551 1379 1596 1510 1620 ~1 530 MaximumVelocity 1200
release 1314 672 432 452 432 450 408 back to ~440
LT held 203 121 135 121 115 146 122 ~125 MinimumVelocity 100
release 369 451 427 425 445 390 465 back to ~430

So the throttle selects a target speed and the craft converges to it; releasing either trigger returns it to cruise. (Refined below: the trigger is analogue, so the target is a continuous interpolation and these three are its endpoints.) It is not a force model with the throttle adding thrust, which is what a reimplementation would most likely have assumed from Acceleration/Deceleration alone. Those two fields govern the convergence rate: the release phase falls ~1 314 → ~432 in about two seconds (~440 units/s², against Deceleration 500) and the RT phase climbs ~420 → ~1 550 in two to three seconds (~470560 units/s², against Acceleration 600).

🟡 The unit scale

Measured world-space speeds run ≈1.21.3× the definition numbers in all three regimes (cruise 1.21, maximum 1.28, minimum 1.32). The HUD, meanwhile, reads 350 at cruise — the definition value exactly. So the definition's velocity unit is the HUD's, and entity world coordinates are a constant multiple of it, close to 1.25. The spread across regimes is larger than the constant itself is precise, because the craft is manoeuvring under fire while sampled (straight-line displacement per window under-reads a curving path), so this is recorded as a measured range rather than a pinned constant.

Traps

  • RT/LT are analogue triggers, so vgamepad trig RT 1.0 holds the throttle; the button verb hold RT is a silent no-op. The first run of this probe measured the drift of a craft nobody was flying.
  • Do not differentiate per sample. The guest updates the position slower than 20 Hz, so per-sample differences alternate between 0 and a double step — 0, 1519, 1985, 0, 2681… for a craft flying smoothly.
  • Bind late, measure fast. The player entity is not in the typed-entity scan for the first ~15 s of a mission, and the craft dies within a few minutes if nobody is flying it — one earlier attempt ended at GAME OVER mid-probe.

Raw samples: captures/speed-law-throttle-phases.csv.


Turn rates: AV_* are rate caps, and _Min/_Max mean at minimum/maximum speed

tools/re-capture/turn_law.py pins the speed regime with a throttle, holds a stick axis, and differentiates the craft's own forward vector over 1-second windows.

Control mapping, measuredLX is roll (the forward vector barely moves under it: 35 °/s residual), LY is pitch (vgamepad documents LY: -1 = up, so +1 is nose down = the PitchMinus family), and the right stick does not steer at all (0 °/s).

phase measured (1 s windows) definition
slow + nose down 85 · 96 · 82 → ~87 AV_PitchMinus_Min 75
fast + nose down 55 · 45 · 63 → ~54 AV_PitchMinus_Max 40
slow + nose up 181 · 164 · 181 → ~175 AV_PitchPlus_Min 150
fast + nose up 103 · 130 · 175 → ~136 AV_PitchPlus_Max 70

Two claims of the field names are confirmed:

  • Agility falls with speed — every rate drops when the throttle goes from LT to RT, so _Min/_Max are at minimum / maximum speed, not floor/ceiling of the rate.
  • Pitching up is about twice as fast as pitching down, exactly as the pair 150/75 (slow) and 70/40 (fast) says.

The ~1.2× factor — ⚠️ WITHDRAWN, see "Settled rates match exactly" below

The speed law measured 1.21 / 1.28 / 1.32 × its definition values; the pitch rates measure 1.16 / 1.35 / 1.17 / 1.94 × theirs. A unit scale would have to differ between metres-per-second and degrees-per-second — it cannot explain both. A time base does: if the guest's simulated second is shorter than the wall-clock second this probe measures against, every rate reads high by the same factor. So the definition numbers are self-consistent and a reimplementation should take them at face value; these measurements confirm the shape of the law (which field governs what, and how the regimes switch), not a scale correction.

🟡 No yaw input was found. AV_Yaw_{Min,Max} (45 / 25 °/s) exists, but neither stick yaws the craft. With MaximumBank_Normal (60) and roll on LX, a bank-to-turn model is the obvious reading — sustained turns come from rolling and pitching — but the yaw fields may equally belong to the AI or to an input this probe has not driven.

Raw samples: captures/turn-law-pitch-phases.csv.


The afterburner is not on the obvious buttons — a bounded negative

The definition describes the burner without naming its input: AB_ConsumeShield_Begin 50, AB_ConsumeShield 10, and a set of AB_AV_* turn caps far below the normal ones (roll 40 vs 125200 °/s, pitch-up 30 vs 70150). Notably there is no AB_*Velocity, so the burner may not raise the speed target at all.

Two independent probes, both negative for A, B, X, LB (and LS/RS on the first):

  • tools/re-capture/ab_probe.py — hold RT for a max-speed baseline, then hold each candidate: speed stayed inside the baseline's own noise band (1 2001 580 units/s) for every one.
  • tools/re-capture/ab_state_probe.py — sample a window of the player object while each candidate is held and report any float that falls during the hold: nothing fell.
  • And the cheapest oracle of the three, needing no offsets at all: count the green pixels of the HUD's SHIELD bar before and after each hold, on a freshly spawned craft with a full shield. AB_ConsumeShield_Begin 50 should take a visible bite; the bar read 156, 156, 156, 157, 157 across baseline and all four buttons.

So the burner is not a simple hold on A/B/X/LB/LS/RS. What remains: a chord (something plus RT), an input this pad cannot reach, a craft variant that has it, or fields that belong to the AI rather than the player. Worth knowing before anyone re-probes the obvious buttons.

(RB is the nose gun, Y the missile mount and the d-pad the tactical map — see flight controls — so those were not held here.)


The throttle is ANALOGUE: the target speed interpolates cruise → maximum

RT is an analogue trigger, so "held" was only ever one point on a curve. Walking it in five steps (tools/re-capture/throttle_curve.py, 2.5 s to converge then 5 s of sampling per step):

RT settled speed (1 s windows) mean
0.00 465 · 411 · 373 · 504 438
0.25 648 · 554 · 678 · 622 626
0.50 739 · 961 · 897 · 919 879
0.75 1028 · 1086 · 977 · 1283 1094
1.00 1359 · 1306 · 1517 · 1186 1342

A straight ramp. Dividing by the ~1.2 time-base factor established above, the endpoints land on the definition's own numbers — 438/1.2 ≈ 365 against CruisingVelocity 350, and 1342/1.2 ≈ 1118 against MaximumVelocity 1200 — and the midpoint follows: 879/1.2 ≈ 733 against the predicted 350 + 0.5·(1200350) = 775. So

target speed = CruisingVelocity + RT · (MaximumVelocity  CruisingVelocity)

and LT mirrors it down to MinimumVelocitymeasured, see below.

This also refutes a live hypothesis about the afterburner. Full RT producing more than MaximumVelocity looked like it might be the burner; it is not — the curve is smooth through full deflection, with no step, and the shield does not move. Full throttle is simply full throttle.

Raw samples: captures/throttle-curve-rt.csv.

(The entity scan that locks onto the player searches changing position triples, so it only sees the craft while it still has speed — bind early, or a mission left idling drops out of the scan entirely and every tool reports "player entity not found" while the game is visibly flying.)


LT mirrors RT, and roll does NOT depend on speed

One flight, both measurements (tools/re-capture/flight_law2.py).

The braking half of the curve

LT 0.00 0.25 0.50 0.75 1.00
speed 436 379 289 209 126

A straight ramp down from cruise to ~126, and dividing by the ~1.2 time-base factor the endpoints are the definition's own numbers again — 363 against CruisingVelocity 350 and 105 against MinimumVelocity 100. So the throttle law is symmetric:

RT held:  target = Cruising + RT · (Maximum  Cruising)
LT held:  target = Cruising  LT · (Cruising  Minimum)

Roll

Roll turns the craft about its own forward axis, so it has to be measured on a different row of the rotation matrix than pitch was:

phase rate per 1 s window mean
minimum speed + full roll 171 · 103 · 157 ~144 °/s
maximum speed + full roll 140 · 145 · 164 ~150 °/s

Roll shows no speed dependence — the two regimes agree inside the noise, where pitch dropped by a third to a half between them. Against the definition (AV_Roll_Min 200, AV_Roll_Max 125) the measured ~145 °/s is ~121 after the time-base factor, i.e. the Max value in both regimes.

So the _Min/_Max pair does not mean the same thing for every axis: pitch interpolates between them with speed, roll appears pinned at Max. A reimplementation that applies one rule to all axes would get roll wrong at low speed by ~60 %.

🟡 Caveat kept: the roll phases were 2 s of settling apart, which is marginal for a full 126 → 1 342 speed change, so "no dependence" is a measurement over a real but not perfectly separated pair of regimes.

Raw samples: captures/throttle-curve-lt.csv · captures/turn-law-roll.csv.


⚠️ Settled rates match the definition EXACTLY — the "time base" reading is withdrawn

The earlier turn measurements settled each phase for 1.5 s and then differentiated from the first second. Re-running with 5 s of settle and the craft's speed recorded at the moment the turn starts (tools/re-capture/flight_law3.py):

phase speed at turn measured rate definition
pitch, minimum speed 130 /s 74.9 °/s AV_PitchMinus_Min 75
pitch, half throttle 388 /s 73.7 °/s (between, see below)
pitch, maximum speed 1 821 /s 41.1 °/s AV_PitchMinus_Max 40
roll, minimum speed 110 /s 129.5 °/s AV_Roll_Min 200
roll, maximum speed 1 722 /s 149.3 °/s AV_Roll_Max 125

Pitch lands on 75 and 40 — the definition's own numbers, with no scale factor. So the ~1.2× I attributed to the emulated time base was an artefact of measuring during the AA_* acceleration ramp with too little settle, and that explanation is withdrawn: angular rates need no correction, and a reimplementation should use AV_* verbatim.

What remains unexplained is only on the linear side: settled speeds still read high and vary between runs (RT full measured 1 342 in one flight and 1 821 in another, against MaximumVelocity 1 200), which is what a craft being shoved around in a firefight looks like. The HUD, meanwhile, reads exactly CruisingVelocity at neutral throttle. A clean linear measurement wants a quiet corner of a map, not this one — until then, no cause is claimed.

Roll, re-measured with proper settles, confirms the axis difference: 129.5 at 110 /s and 149.3 at 1 722 /s — no speed dependence, both near AV_Roll_Max 125, where pitch moved 75 → 40 across the same range. The mid-throttle pitch point does not discriminate lerp-versus-switch, because at half throttle the craft only reached 388 /s, where an interpolation would predict ~66 °/s against a measured 73.7 — inside this probe's noise.

Raw samples: captures/turn-law-settled.csv.