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
This commit is contained in:
54
tools/re-capture/throttle_curve.py
Normal file
54
tools/re-capture/throttle_curve.py
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user