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
This commit is contained in:
82
tools/re-capture/flight_law3.py
Normal file
82
tools/re-capture/flight_law3.py
Normal file
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Is the `_Min`/`_Max` pair a LERP on speed, and is roll really speed-independent?
|
||||
|
||||
Pitch measured 87 deg/s at minimum speed and 54 at maximum, against
|
||||
`AV_PitchMinus_{Min,Max}` 75/40 — consistent with either a two-state switch or an
|
||||
interpolation. A THIRD point settles it: at half throttle the craft sits midway
|
||||
between cruise and maximum, so an interpolation must put the pitch rate between the
|
||||
two, while a switch cannot.
|
||||
|
||||
Roll measured ~144 and ~150 in the two regimes, but only 2 s apart — not enough
|
||||
settle for a 126 -> 1342 speed change. Here every phase settles 5 s.
|
||||
|
||||
Usage: flight_law3.py <out.csv> [dwell_s]
|
||||
"""
|
||||
import json, math, os, struct, subprocess, sys, time
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
import speed_law
|
||||
|
||||
SETTLE = 5.0
|
||||
|
||||
def pad(*a):
|
||||
subprocess.run(["vgamepad", *a], capture_output=True)
|
||||
|
||||
def row_at(fd, off, cfg, row):
|
||||
at = off + cfg["rot_delta"] + row * cfg["rot_stride"]
|
||||
v = struct.unpack(">3f", os.pread(fd, 12, at))
|
||||
n = math.sqrt(sum(c * c for c in v)) or 1.0
|
||||
return tuple(c / n for c in v)
|
||||
|
||||
def ang(a, b):
|
||||
return math.degrees(math.acos(max(-1.0, min(1.0, sum(a[i] * b[i] for i in range(3))))))
|
||||
|
||||
def main():
|
||||
out_csv = sys.argv[1]
|
||||
dwell = float(sys.argv[2]) if len(sys.argv) > 2 else 4.0
|
||||
cfg = json.load(open("/tmp/nav-live.json"))
|
||||
w, off, nm = speed_law.find_player()
|
||||
if not w:
|
||||
sys.exit("player entity not found after retries")
|
||||
print(f"# locked on {nm}")
|
||||
fwd, other = cfg.get("fwd_row", 0), (1 if cfg.get("fwd_row", 0) != 1 else 2)
|
||||
rows = []
|
||||
phases = [
|
||||
# label, throttle, stick, matrix row to watch
|
||||
("pitch_slow", ("LT", 1.0), ("LY", 1.0), fwd),
|
||||
("pitch_mid", ("RT", 0.5), ("LY", 1.0), fwd),
|
||||
("pitch_fast", ("RT", 1.0), ("LY", 1.0), fwd),
|
||||
("roll_slow", ("LT", 1.0), ("LX", 1.0), other),
|
||||
("roll_fast", ("RT", 1.0), ("LX", 1.0), other),
|
||||
]
|
||||
for label, (trig, tv), (axis, av), row in phases:
|
||||
pad("trig", "RT", "0.0"); pad("trig", "LT", "0.0")
|
||||
pad("axis", "LX", "0.0"); pad("axis", "LY", "0.0")
|
||||
pad("trig", trig, str(tv))
|
||||
time.sleep(SETTLE) # 5 s: a full 126 -> 1342 change needs ~2 s
|
||||
# speed at the moment of the turn, so the regime is recorded not assumed
|
||||
p0 = speed_law.pos_at(w.fd, off); time.sleep(0.6); p1 = speed_law.pos_at(w.fd, off)
|
||||
v_at_turn = math.dist(p0, p1) / 0.6
|
||||
pad("axis", axis, str(av))
|
||||
seq, t0 = [], time.time()
|
||||
while time.time() - t0 < dwell:
|
||||
seq.append((round(time.time() - t0, 3), *row_at(w.fd, off, cfg, row)))
|
||||
time.sleep(0.05)
|
||||
pad("axis", axis, "0.0")
|
||||
rows += [(label, *s) for s in seq]
|
||||
rates = []
|
||||
for t in range(int(dwell) - 1):
|
||||
a = [s for s in seq if s[0] >= t][0]
|
||||
b = [s for s in seq if s[0] <= t + 1][-1]
|
||||
rates.append(ang(a[1:], b[1:]) / (b[0] - a[0]))
|
||||
mean = sum(rates) / len(rates)
|
||||
print(f"# {label:<11} speed {v_at_turn:6.0f}/s rate {mean:6.1f} deg/s " +
|
||||
" ".join(f"{r:5.0f}" for r in rates))
|
||||
pad("trig", "RT", "0.0"); pad("trig", "LT", "0.0"); pad("reset")
|
||||
with open(out_csv, "w") as f:
|
||||
f.write("phase,t,ax,ay,az\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