debug: report the achieved geometry, and catch stalls that keep a full span
CI for csaxs_bec / test (push) Successful in 1m45s

The first clean run said both scans were fine, which conflicts with scan 473
being classified as having the artefact. Two gaps explain how that verdict
could be wrong.

**The file was never checked against the intended scan.** Measured fast-axis
travel implies roughly 6.2 um per point in S00473 and 9.2 um in S00254, while
the scans were run at 0.65 um and 1.00 um -- both about 9.3x larger. Until that
is resolved the analysis rests on an unverified assumption about which axis is
which and in what units. The check now prints the fast and slow axis ranges,
the per-line slow step, and the implied step size, so the file can be compared
directly against the scan command. A mismatch means the wrong axis, the wrong
units, or motion that was not commanded, and all three matter more than
anything downstream.

**Whole-line spans cannot see a partial stall.** An axis that freezes part-way
through a line and then continues still covers its full range, so the span test
passes. Since the report describes intermittent motion loss rather than whole
lines going missing, that is the more likely shape of the fault. Lines are now
also scanned for runs of consecutive position samples that did not advance,
reported with the same start/middle/end distribution.

Verified against a file whose stalls leave every span intact: the span test
sees nothing, the new one finds all eight and places them in the final tenth.

Note the sampling limit still applies -- 74 position samples against 428 data
points in S00473, and about 8 against 205 in S00254. A stall shorter than the
sampling interval remains invisible, so a clean result here is not proof.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
menzelandClaude Opus 5 committed 2026-08-25 12:24:01 +02:00
1 parent 95409036bb
commit f0f8f620de
1 file changed
+45
+45
View File
@@ -429,6 +429,51 @@ def check_line_motion_from_motor(signals, motors, meta) -> None:
return
ratio = spans / median
print(f" samples per line : ~{per_line} median line span : {median:.6g}")
# Enough geometry to check this file against the scan that was intended.
# A step size that disagrees with the scan command means either the wrong
# axis has been identified or the motion was not what was asked for -- and
# both make everything below meaningless.
grid_pts = int(meta.get("frames_per_trigger") or 0)
print(f" fast axis range : {arr.min():.6g} .. {arr.max():.6g}")
if slow_name is not None:
sarr = signals[slow_name]
print(f" slow axis range : {sarr.min():.6g} .. {sarr.max():.6g}"
f" ({found} lines -> {(sarr.max() - sarr.min()) / max(1, found - 1):.4g} per line)")
if grid_pts:
print(f" implied step : {median / grid_pts:.4g} per point"
f" ({median:.4g} over {grid_pts} points)")
print(" ^ compare with the step size in the scan command. A mismatch means")
print(" the wrong axis, the wrong units, or motion that was not commanded.")
# Whole-line spans only catch a line that never moved. An axis that stalls
# part-way and then continues still spans the full range, so look for runs of
# samples that did not advance.
stalled = []
for i, sg in enumerate(segs):
if sg.size < 4:
continue
step = np.abs(np.diff(sg))
typical = float(np.median(step))
if typical <= 0:
continue
frozen = step < 0.1 * typical
run, longest = 0, 0
for f in frozen:
run = run + 1 if f else 0
longest = max(longest, run)
if longest >= 3:
stalled.append((i, longest, sg.size))
if stalled:
print(f"\n within-line stalls: {len(stalled)} lines contain a run of >=3"
" consecutive samples that did not advance")
report_distribution(np.array([i for i, _, _ in stalled]), found, "within-line stalls")
for i, longest, size in stalled[:10]:
print(f" line {i:>5} {longest} frozen of {size} samples")
print(" A line can span its full range and still have stopped part-way;")
print(" this is the partial-stall case the span check cannot see.")
else:
print(" no within-line stalls either (no run of >=3 frozen samples).")
if per_line < 5:
print(f" WARNING: only ~{per_line} position samples per line. The motor trace")
print(" is far sparser than the data, so a partial stall can hide between")