A 2700 frame, 540 degree sweep was refused at load as "screening images taken at
scattered angles". Nothing about it is irregular: every consecutive difference is
a clean step, and the headers simply write the start angle modulo 360, so the
second and third revolutions repeat angles the first already had.
The unwrap handled that correctly. What failed was the STEP. It was taken to be
the smallest difference between two adjacent frames - a single recorded value, and
of all the samples the one biased furthest low by read-back noise. These headers
are written from 32-bit floats, so that one sample was 1.3e-4 low, and placing a
frame by dividing its distance from the FIRST frame by that step multiplied the
error by the frame number: by frame 1924 the drift had reached a quarter of a step
and the on-grid check refused the rest of the sweep.
The fix separates the two jobs the step was doing. The smallest difference is now
only a guess, used to count how many steps apart each pair of NEIGHBOURS is; a
local difference carries the noise of one reading and nothing accumulates, however
long the sweep. The step itself is then a straight line fitted through (steps,
angle) over every frame, so the noise averages out instead of one unlucky pair
setting the scale for thousands of frames. Over the whole corpus this changes the
step for 4 of 82 numbered series and leaves the other 78 bit-identical; of the 4,
three were drifting by 0.17 to 0.35 of a step across the sweep and one by 1e-8.
Counting steps between neighbours is also what carries a sweep past a full turn:
each fold is one step forward, so the count keeps climbing through 360 and a frame
taken on the second revolution lands beyond the first rather than on top of it. A
series that genuinely goes BACK - two sweeps of one crystal concatenated - folds to
a large negative count in a single pair and still collides, so that refusal keeps
working; its message now says what it means.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nW6FNRP1bBJJ8pfHiByAT