re(flight): axis probe pins the rows and checks liveness; fly_stage waits, not sleeps
Both fixes the previous run's caveats asked for, plus one the run itself forced. axis_probe.py now: - PINS which non-forward row is up and which is right, by comparing world-Y across the rows in level flight, and says CONFIDENT or WEAK. entities2 measures row 2 = forward against velocity, but the other two were labelled by the D3D convention, and yaw/pitch SWAP if that is wrong -- so the previous run's last two columns were named on an assumption. - checks the craft is ALIVE between inputs, and ABORTS with a message instead of reporting the clean zeros a destroyed craft produces. The first run ended on GAME OVER and only said so afterwards. - measures the UNKNOWN inputs (rx, ry, LB, RB) first while the craft is healthy, keeping the established lx/ly as controls at the end. fly_stage.sh now WAITS for the stage load instead of sleeping a fixed guess. The fixed sleeps worked until they didn't: one load ran long, the script pressed START into a black screen, and every later step went to nothing while the screenshots recorded a plausible-looking sequence. It now polls for a non-black frame and aborts with a pointer to the log if the load hangs (PhysicalHeap::Release failures) rather than continuing blind. The probe itself did not run this iteration -- the stage load hung -- so there is no new axis data, and none is claimed.
This commit is contained in:
@@ -52,13 +52,16 @@ PAD_FILE = os.environ.get("XENIA_PAD_FILE", "/tmp/xenia_pad.txt")
|
||||
|
||||
# Candidates. Sticks at full deflection, shoulders as buttons. Triggers are the
|
||||
# throttle (already settled) and are left out.
|
||||
# Order matters: the craft can be shot down mid-run, so the UNKNOWN inputs go
|
||||
# first while it is healthy and the already-established ones (lx = roll) go last
|
||||
# as controls. A row measured after death looks like a clean zero.
|
||||
CANDIDATES = [
|
||||
("lx+", {"lx": 32767}),
|
||||
("ly+", {"ly": 32767}),
|
||||
("rx+", {"rx": 32767}),
|
||||
("ry+", {"ry": 32767}),
|
||||
("LB", {"press": "LB"}),
|
||||
("RB", {"press": "RB"}),
|
||||
("lx+", {"lx": 32767}),
|
||||
("ly+", {"ly": 32767}),
|
||||
]
|
||||
|
||||
|
||||
@@ -84,6 +87,46 @@ def dot(a, b):
|
||||
return sum(a[i] * b[i] for i in range(3))
|
||||
|
||||
|
||||
def alive(w, off, cfg, secs=1.0):
|
||||
"""Is the craft still flying? A destroyed craft stops moving, and every
|
||||
subsequent input then measures a clean, meaningless zero — the failure mode
|
||||
that invalidated this probe's first run (it ended on GAME OVER and only said
|
||||
so afterwards)."""
|
||||
p0 = struct.unpack(">3f", os.pread(w.fd, 12, off))
|
||||
time.sleep(secs)
|
||||
p1 = struct.unpack(">3f", os.pread(w.fd, 12, off))
|
||||
return math.dist(p0, p1) > 1.0
|
||||
|
||||
|
||||
def pin_rows(w, off, cfg):
|
||||
"""Which non-forward row is UP and which is RIGHT?
|
||||
|
||||
`entities2` measures row 2 = forward against the velocity vector, but the other
|
||||
two are assigned by the D3D convention rather than evidence — and yaw and pitch
|
||||
SWAP if that is wrong, so naming them without this test is a guess.
|
||||
|
||||
Discriminator: in level flight the craft's up-vector points along world +Y and
|
||||
its right-vector lies near the horizontal plane. Sample at neutral and compare
|
||||
|world-Y| across the rows."""
|
||||
f_i = cfg["fwd_row"]
|
||||
acc = [0.0, 0.0, 0.0]
|
||||
n = 0
|
||||
for _ in range(20):
|
||||
time.sleep(0.05)
|
||||
rs = rows_at(w.fd, off, cfg)
|
||||
for r in range(3):
|
||||
acc[r] += rs[r][1] # world Y component
|
||||
n += 1
|
||||
ys = [a / n for a in acc]
|
||||
cand = [r for r in (0, 1, 2) if r != f_i]
|
||||
up_i = max(cand, key=lambda r: abs(ys[r]))
|
||||
right_i = [r for r in cand if r != up_i][0]
|
||||
print(f"# row world-Y means: {[round(y,3) for y in ys]} (forward = row {f_i})")
|
||||
print(f"# -> up = row {up_i}, right = row {right_i}"
|
||||
f" {'CONFIDENT' if abs(ys[up_i]) - abs(ys[right_i]) > 0.3 else 'WEAK — craft may not be level'}")
|
||||
return up_i, right_i
|
||||
|
||||
|
||||
def main():
|
||||
out_csv = sys.argv[1]
|
||||
dwell = float(sys.argv[2]) if len(sys.argv) > 2 else 5.0
|
||||
@@ -93,7 +136,9 @@ def main():
|
||||
sys.exit("player entity not found")
|
||||
print(f"# locked on {nm}")
|
||||
f_i = cfg["fwd_row"]
|
||||
u_i, w_i = [r for r in (0, 1, 2) if r != f_i]
|
||||
if not alive(w, off, cfg):
|
||||
sys.exit("craft is not moving at the start — not in flight, or already dead")
|
||||
u_i, w_i = pin_rows(w, off, cfg)
|
||||
|
||||
rows = []
|
||||
print(f"# {'input':<6} {'roll':>9} {'yaw':>9} {'pitch':>9} (deg over the dwell)")
|
||||
@@ -115,6 +160,10 @@ def main():
|
||||
prev = cur
|
||||
pad_state()
|
||||
wall = time.time() - t0
|
||||
if not alive(w, off, cfg):
|
||||
print(f"# CRAFT STOPPED MOVING after {label} — everything from here is "
|
||||
f"meaningless, aborting rather than reporting zeros")
|
||||
break
|
||||
rate = [v / wall for v in sw]
|
||||
rows.append((label, *[round(v, 2) for v in rate]))
|
||||
print(f"# {label:<6} {rate[0]:9.1f} {rate[1]:9.1f} {rate[2]:9.1f} deg/wall-s")
|
||||
|
||||
Reference in New Issue
Block a user