diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 49aee755..067b1e34 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -16,7 +16,7 @@ This is an UNSTABLE release. It includes many experimental features, as well as * HDF5: a still or grid scan recorded with a Smargon head position is no longer read back as a single image. * 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 can carry the sample transformation chain (`transformations`, in the END message) in mounting order, so a goniometer axis, the Smargon chi/phi and a grid stage are described together and unambiguously. It is optional - a producer that does not send it gets the same chain built by the writer - so no metadata version change is needed. +* The image stream can carry the sample transformation chain (`transformations`, in the END message) in mounting order, so a goniometer axis, the Smargon chi/phi and a grid stage are described together and unambiguously. It is optional - a producer that does not send it gets the same chain built by the writer - so no metadata version change is needed. The rotation width and end angles are derived by the writer rather than sent. * 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. * The image stream and HDF5 now record `mirror_y`, whether the assembled image is mirrored in Y relative to the detector's raw readout, and it is read back. * rugnux: an image integrated in pyFAI through the `.poni` file written by `--mode calibration` now comes out with the correct azimuth; it was 180 degrees out. Radial integration is unchanged. diff --git a/tests/JFJochReaderTest.cpp b/tests/JFJochReaderTest.cpp index 783cfad6..1c8393da 100644 --- a/tests/JFJochReaderTest.cpp +++ b/tests/JFJochReaderTest.cpp @@ -2991,6 +2991,20 @@ TEST_CASE("JFJochReader_TransformationChain_SentAndBuilt", "[HDF5][Full]") { CHECK(sent.GetDatasetSettings().GetSmargonPosition()->phi_deg == Catch::Approx(-7.25f).margin(1e-3)); + // Compared on the files, not through the reader: the reader reads neither AXISNAME_end nor the + // rotation width, so it cannot see the two routes diverge - and it did, until the writer started + // deriving them for a chain it was handed. + { + HDF5ReadOnlyFile built_file("test_chain_built_master.h5"); + HDF5ReadOnlyFile sent_file("test_chain_sent_master.h5"); + CHECK(built_file.FindLeafs("/entry/sample/transformations") + == sent_file.FindLeafs("/entry/sample/transformations")); + CHECK(sent_file.ReadVector("/entry/sample/transformations/omega_end").size() + == static_cast(x.GetImageNum())); + CHECK(sent_file.ReadVector("/entry/sample/transformations/omega_range_average").at(0) + == Catch::Approx(0.1).margin(1e-4)); + } + remove("test_chain_built_master.h5"); remove("test_chain_sent_master.h5"); REQUIRE(H5Fget_obj_count(H5F_OBJ_ALL, H5F_OBJ_ALL) == 0); diff --git a/writer/HDF5NXmx.cpp b/writer/HDF5NXmx.cpp index 36418408..e0f1f2ae 100644 --- a/writer/HDF5NXmx.cpp +++ b/writer/HDF5NXmx.cpp @@ -825,6 +825,27 @@ void NXmx::Sample(const StartMessage &start, const EndMessage &end) { axis.GetEquipment(), axis.GetEquipmentComponent(), type, vector, offset, ""); depends_on = base + axis.GetName(); + + // AXISNAME_end and the rotation width are derived here rather than carried on the wire. + // They follow from the values themselves for a constant step, which is every case there + // is today, so sending them would cost another array per axis and say nothing new. + const auto &values = axis.GetValues(); + // Over the endpoints rather than the first pair: the values arrive as floats, and a + // single difference carries that noise straight into the reported rotation width. + const double step = (values.size() > 1) + ? (values.back() - values.front()) / static_cast(values.size() - 1) + : 0.0; + if (axis.IsRotation() && (step != 0.0)) { + std::vector end(values.size()); + for (size_t i = 0; i < values.size(); i++) + end[i] = values[i] + step; + SaveVector(transformations, axis.GetName() + "_end", end)->Units(axis.GetUnits()); + SaveScalar(transformations, axis.GetName() + "_range_average", + static_cast(step))->Units(axis.GetUnits()); + SaveScalar(transformations, axis.GetName() + "_range_total", + static_cast(step * static_cast(values.size()))) + ->Units(axis.GetUnits()); + } } group.SaveScalar("depends_on", depends_on); return;