diff --git a/reader/JFJochCBFReader.cpp b/reader/JFJochCBFReader.cpp index 7d554119f..2556df484 100644 --- a/reader/JFJochCBFReader.cpp +++ b/reader/JFJochCBFReader.cpp @@ -152,16 +152,21 @@ std::optional ImageOrientation(const minicbf::Header &h) { // scan turns about the base axis however the cradle is set - which is why a header carrying a large // fixed chi still comes out as the base axis here - and only a phi scan is carried by chi and by // omega. Chi turns about the beam, as the imgCIF axis convention has it, pointing back at the -// source; internal z points the other way, hence the minus. A kappa arm cannot be expressed at all: -// its inclination is a property of the hardware that no miniCBF header states. +// source; internal z points the other way, hence the minus. A head whose inner circle is inclined - +// a fixed-chi stage, a kappa arm - states that inclination in its axis table and nowhere else. Coord RotationAxis(const minicbf::Header &h) { const Coord base = BaseAxis(h); if (!minicbf::ScansPhi(h)) return base; const auto rad = [](double deg) { return static_cast(deg * PI / 180.0); }; + // Where the table states the inner axis outright, that IS the inclination, and the driven-circle + // angles do not describe it: a fixed-chi stage carries phi at a standing angle to the base with + // no chi circle to report, so "# Chi" reads zero while the axis is tens of degrees away. const Coord chi_axis(0.0f, 0.0f, -1.0f); - const Coord tilted = RotMatrix(rad(h.chi_deg.value_or(0.0)), chi_axis) * base; + const Coord tilted = h.inner_spindle_axis.has_value() + ? ImgCIFToInternal(*h.inner_spindle_axis) + : RotMatrix(rad(h.chi_deg.value_or(0.0)), chi_axis) * base; return RotMatrix(rad(h.omega_deg.value_or(0.0)), base) * tilted; } diff --git a/reader/MiniCBF.cpp b/reader/MiniCBF.cpp index 14d6b8208..3b4626b03 100644 --- a/reader/MiniCBF.cpp +++ b/reader/MiniCBF.cpp @@ -194,6 +194,8 @@ void ParseAxisTable(const std::string &text, Header &h) { h.spindle_axis = v; else if (equipment == "detector") h.detector_axis = v; + } else if ((type == "rotation") && (equipment == "goniometer")) { + h.inner_spindle_axis = v; // carried by the base axis: a fixed-chi or kappa inclination } } diff --git a/reader/MiniCBF.h b/reader/MiniCBF.h index dd8cb92a1..9cb3fd96d 100644 --- a/reader/MiniCBF.h +++ b/reader/MiniCBF.h @@ -54,6 +54,11 @@ struct Header { std::optional> fast_direction; // laboratory direction of a +1 column step std::optional> slow_direction; // ... of a +1 row step std::optional> spindle_axis; // the base goniometer rotation axis + // The goniometer rotation axis carried by the base one, stated in the base axis' own frame. A + // head whose inner circle is INCLINED - a fixed-chi stage, a kappa arm - says so only here: its + // "# Chi" and "# Kappa" lines read zero because no such circle is driven. Empty where the table + // states one rotation axis only, which is every Eulerian head. + std::optional> inner_spindle_axis; std::optional> detector_axis; // the detector's own rotation axis: a 2theta arm // "# Oscillation_axis X.CW +SLOW". Some writers say which of the two image directions the spindle // runs along instead of stating a vector. The axis NAME on that line is a poor discriminator (see