Screenshotting the HUD speed readout at each throttle step, beside the position-derived measurement of the same moment: RT 0.00 HUD 350 (= CruisingVelocity) position ~447 ratio 1.28 RT 0.25 HUD 507 position ~652 ratio 1.29 RT 0.75 HUD 963 position ~1141 ratio 1.19 So (a) the HUD speaks the definition's units — exactly CruisingVelocity at neutral, 963 at three-quarters against the 987 the interpolation predicts — confirming the throttle law in the game's own numbers without any position sampling; and (b) world displacement runs ~1.2x the displayed speed. Since settled angular rates need no such factor, this is a unit difference between the position triple and the velocity fields, not a clock effect: a reimplementation moving entities at MaximumVelocity in world coordinates will be ~20% slow. Also fixes speed_law.find_player: a mission holds more than one *_Player object and at least one never moves, so the finder now samples each candidate twice and keeps the one that displaces. Locking onto the static one is what produced a run of exact zeros while the game was visibly flying. 🟡 The ratio is 1.19-1.29 rather than a clean constant and every sample was taken in a firefight; pinning it wants a quiet map. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NptfmpjdpNCKEez6d2xvA9
60 lines
2.6 KiB
Python
60 lines
2.6 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(6.0) # settle: 5 s+ is what the turn-rate fix showed is needed
|
|
# Screenshot the HUD at the settled speed: its readout is in the
|
|
# DEFINITION's unit (it shows 350 at neutral = CruisingVelocity), so it is
|
|
# the ground truth the position-derived number has to be compared against.
|
|
subprocess.run(["screenshot", f"/sylph-home/re/shots/hud_{axis}_{v}.png"],
|
|
capture_output=True)
|
|
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()
|