Two things the goniometer handling conflated.
The axis name is free-form everywhere that writes it - the API imposes only
minLength, the CBOR map uses the name as its key, and tests/CBORTest.cpp round
trips one literally called "z" - but the reader looked for exactly
"/entry/sample/transformations/omega". A sweep recorded as "phi" therefore came
back as stills, in the viewer and in rugnux, with nothing to indicate it. The
reader now walks the transformations group and takes whichever axis is a
rotation, preferring one that turns; the grid scan is read independently rather
than as the else-branch of the same test, since a grid scan can be taken at a
given head position.
Second: "an axis is defined" and "the axis is turning" were the same question,
answered inconsistently - GetImagesPerFile checked the increment, IsRotationIndexing
did not, and the CBOR decoder deleted zero-increment axes outright so the
ambiguity could never surface. GoniometerAxis::IsScanning now asks it explicitly
and the call sites go through it, so a stationary axis can be carried without
being mistaken for rotation data. That mistake is not hypothetical:
RotationIndexerCounter leaves its stride at zero for a zero increment, and
Process() then never fires, so indexing would silently never run.
Keeping stationary axes is also what lets the writer state where the head was for
a still or a grid scan, which is the next step.
JFJochReader_Goniometer_NonOmegaName covers the naming case through the writer
and back; nothing did before, because both existing round trips use "omega".
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>