Do not let a marCCD file name 2-theta as the scanned circle
The scanned circle was read as the first start/end pair that differs, and two_theta is the first pair in the goniostat block. A file that leaves its end block at zero while the detector sits on a swung-out 2-theta arm therefore named the arm as the spindle and stopped before reaching phi; with no rotation range recorded either, the increment fell back to -start_angle, which differs per frame, and the sweep was refused outright. The header's own rotation_axis index (offset 732) is taken where it names a spindle circle, and the pair scan is the fallback for files that never filled it in - read past two_theta, which no sweep turns. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013nW6FNRP1bBJJ8pfHiByAT
This commit is contained in:
+25
-12
@@ -38,6 +38,7 @@ constexpr size_t OFF_END_XTAL_TO_DETECTOR = 728; // ... and at its end
|
||||
constexpr size_t OFF_EXPOSURE_TIME = 656; // milliseconds
|
||||
constexpr size_t OFF_START_TWOTHETA = 668; // millidegrees, and the seven that follow it
|
||||
constexpr size_t OFF_END_TWOTHETA = 700; // the same seven, at the end of the exposure
|
||||
constexpr size_t OFF_ROTATION_AXIS = 732; // which circle the sweep turned, as an index below
|
||||
constexpr size_t OFF_ROTATION_RANGE = 736; // millidegrees
|
||||
constexpr size_t OFF_PIXELSIZE_X = 772; // nanometres
|
||||
constexpr size_t OFF_PIXELSIZE_Y = 776;
|
||||
@@ -146,21 +147,33 @@ Header Parse(const std::vector<uint8_t> &h, const std::string &path) {
|
||||
out.exposure_s = I32(h, OFF_EXPOSURE_TIME) * 1e-3;
|
||||
out.saturated_value = I32(h, OFF_SATURATED);
|
||||
|
||||
// Which circle moved. The header also carries a rotation_axis index, but the start/end pair
|
||||
// that differs is the same answer read off the angles themselves, and it stays right on a file
|
||||
// whose index field was never filled in.
|
||||
// Which circle moved. The header names it as an index into the circle list, and that is taken
|
||||
// where it names a spindle circle: reading the start/end pairs alone takes the FIRST pair that
|
||||
// differs, and two_theta is the first of them, so a file that left its end block at zero while
|
||||
// the detector sat on a swung-out 2-theta arm had the arm named as the spindle - and, with no
|
||||
// rotation range recorded either, an increment of -start_angle that differs on every frame,
|
||||
// which SweepLayout then refuses the whole sweep over.
|
||||
//
|
||||
// The index is not always filled in, and a file that leaves it at zero cannot be told from one
|
||||
// that means two_theta by it, so the fallback is the pairs as before - read past two_theta,
|
||||
// which is a detector circle and never the one a sweep turns.
|
||||
out.angle_increment_deg = I32(h, OFF_ROTATION_RANGE) * 1e-3;
|
||||
for (size_t i = 0; i < N_CIRCLES; i++) {
|
||||
const auto take_circle = [&](size_t i) {
|
||||
const double start = I32(h, OFF_START_TWOTHETA + 4 * i) * 1e-3;
|
||||
const double end = I32(h, OFF_END_TWOTHETA + 4 * i) * 1e-3;
|
||||
if (start != end) {
|
||||
out.axis_name = CIRCLE_NAMES[i];
|
||||
out.start_angle_deg = start;
|
||||
if (out.angle_increment_deg == 0.0)
|
||||
out.angle_increment_deg = end - start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (start == end)
|
||||
return false;
|
||||
out.axis_name = CIRCLE_NAMES[i];
|
||||
out.start_angle_deg = start;
|
||||
if (out.angle_increment_deg == 0.0)
|
||||
out.angle_increment_deg = end - start;
|
||||
return true;
|
||||
};
|
||||
const int32_t named = I32(h, OFF_ROTATION_AXIS);
|
||||
if (named <= 0 || static_cast<size_t>(named) >= N_CIRCLES || !take_circle(static_cast<size_t>(named)))
|
||||
for (size_t i = 1; i < N_CIRCLES; i++)
|
||||
if (take_circle(i))
|
||||
break;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user