diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 083c0a13..a7f2ede4 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -15,6 +15,7 @@ This is an UNSTABLE release. It includes many experimental features, as well as * rugnux: the detector geometry is also logged in XDS's convention (`ORGX`/`ORGY`, detector axis vectors, rotation axis), so it can be compared directly with an XDS refinement. * HDF5: a data file missing next to a VDS master now reads as the error-pixel marker instead of zero counts, so those frames are masked rather than silently integrated as blank. * The writer refuses a stream whose start message declares a different pixel format than its images carry, instead of writing a master that does not describe its own data. +* HDF5: `module_offset` is written as a float with a proper unit vector, and every transformation offset declares `offset_units`, so a reader does not fall back to the axis's own units - degrees on a rotation - when interpreting a length. * The image stream carries the sample transformation chain (`transformations`) in mounting order, so a goniometer axis, the Smargon chi/phi and a grid stage can be described together and unambiguously. * Smargon chi/phi are written for a still as well, and are read back from HDF5; before, they were dropped unless the run also had a rotation axis or a grid scan, and nothing read them. diff --git a/writer/HDF5NXmx.cpp b/writer/HDF5NXmx.cpp index 0f729e81..fde0c1f6 100644 --- a/writer/HDF5NXmx.cpp +++ b/writer/HDF5NXmx.cpp @@ -584,9 +584,13 @@ void NXmx::DetectorModule(const std::string &name, const std::vector &o "", "", "translation", slow_axis, {0,0,0}, ""); - SaveScalar(module_group, "module_offset", 0)-> + // The module origin coincides with the detector origin, so the offset is zero - but it is still + // a translation, and NXmx types module_offset NX_FLOAT. Write a float with a proper unit vector + // rather than an integer with a zero-length one, which is degenerate: the direction of a + // zero-magnitude translation is arbitrary, not absent. + SaveScalar(module_group, "module_offset", 0.0f)-> Transformation("m", "/entry/instrument/detector/transformations/" + nx_axis, - "", "", "translation", {0,0,0}); + "", "", "translation", {0, 0, 1}); } void NXmx::Facility(const StartMessage &start) { diff --git a/writer/HDF5Objects.cpp b/writer/HDF5Objects.cpp index ae5f5258..1dac248a 100644 --- a/writer/HDF5Objects.cpp +++ b/writer/HDF5Objects.cpp @@ -672,7 +672,11 @@ HDF5Object& HDF5Object::Transformation(const std::string& units, const std::stri const std::vector &offset, const std::string& offset_units) { Transformation(units, depends_on, equipment, equipment_component, transformation_type, vector); Attr("offset", offset); - if (!offset_units.empty()) Attr("offset_units", offset_units); + // An offset is a length, whatever the axis is. Without this attribute a reader falls back to + // `units`, which on a rotation axis is degrees - NXmx readers then try to convert degrees to + // millimetres and raise. Harmless while every offset we write is zero, and wrong the moment one + // is not, so state it always rather than only when a caller remembers to. + Attr("offset_units", offset_units.empty() ? std::string("m") : offset_units); return *this; }