Files
Syplheed-Reborn/tools/re-capture/throttle_curve.py
Claude (auto-RE) 4dbd4f6cef re(flight): the throttle is analogue — target speed interpolates cruise -> maximum
RT is an analogue trigger, so "held" was one point on a curve. Walking it 0.00 ->
1.00 (throttle_curve.py) gives a straight ramp: 438, 626, 879, 1094, 1342 units/s.
Dividing by the ~1.2 time-base factor, the endpoints land on the definition's own
numbers (365 vs CruisingVelocity 350; 1118 vs MaximumVelocity 1200) and the midpoint
follows, so

  target speed = CruisingVelocity + RT * (MaximumVelocity - CruisingVelocity)

which refines the earlier "selects one of three targets" reading: those three are the
curve's endpoints.

It also refutes the standing afterburner hypothesis that full RT is the burner: the
curve is smooth through full deflection with no step, and the shield does not move.

The LT half is not measured yet — the entity scan needs the craft moving when it
runs, so a mission left idling drops out of it. Bind early.

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

55 lines
2.2 KiB
Python

#!/usr/bin/env python3
"""Is the throttle ANALOGUE, and is full deflection the afterburner?
speed_law.py measured a settled speed of ~1 530 under full `RT` against a
`MaximumVelocity` of 1 200 — attributed to the emulated time base because the same
~1.2x appears in the turn rates. But `RT` is an analogue trigger, so there is a
second reading worth excluding: that partial deflection reaches MaximumVelocity and
FULL deflection is the afterburner (which would also explain why no button toggles
it). This walks the trigger 0.00 -> 1.00 and reports the settled speed at each step.
Usage: throttle_curve.py <out.csv> [dwell_s]
"""
import math, os, subprocess, sys, time
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import speed_law
def pad(*a):
subprocess.run(["vgamepad", *a], capture_output=True)
def windowed(seq, lo, hi):
a = [s for s in seq if s[0] >= lo][0]
b = [s for s in seq if s[0] <= hi][-1]
return math.dist(a[1:], b[1:]) / (b[0] - a[0])
def main():
out_csv = sys.argv[1]
dwell = float(sys.argv[2]) if len(sys.argv) > 2 else 6.0
axis = sys.argv[3] if len(sys.argv) > 3 else "RT" # RT ramps up, LT ramps down
w, off, nm = speed_law.find_player()
if not w:
sys.exit("player entity not found after retries")
print(f"# locked on {nm}")
rows = []
for v in (0.0, 0.25, 0.5, 0.75, 1.0):
pad("trig", "RT", "0.0"); pad("trig", "LT", "0.0")
pad("trig", axis, str(v))
time.sleep(2.5) # let the speed converge before sampling
seq, t0 = [], time.time()
while time.time() - t0 < dwell:
seq.append((round(time.time() - t0, 3), *speed_law.pos_at(w.fd, off)))
time.sleep(0.05)
for s in seq:
rows.append((v, *s))
wins = [windowed(seq, t, t + 1.0) for t in range(int(dwell) - 1)]
print(f"# {axis}={v:<4} settled speed per 1 s window: " + " ".join(f"{x:6.0f}" for x in wins))
pad("trig", "RT", "0.0"); pad("reset")
with open(out_csv, "w") as f:
f.write("rt,t,x,y,z\n")
for r in rows:
f.write(",".join(str(x) for x in r) + "\n")
print(f"# wrote {out_csv}")
if __name__ == "__main__":
main()