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>
53 lines
2.1 KiB
C++
53 lines
2.1 KiB
C++
// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#pragma once
|
|
|
|
#include <optional>
|
|
#include <vector>
|
|
|
|
#include "Coord.h"
|
|
|
|
class GoniometerAxis {
|
|
std::string name;
|
|
float start;
|
|
float increment;
|
|
Coord axis;
|
|
std::optional<Coord> helical_step;
|
|
std::optional<float> screening_wedge;
|
|
public:
|
|
GoniometerAxis(const std::string& name,
|
|
float start,
|
|
float increment,
|
|
const Coord &axis,
|
|
const std::optional<Coord> &helical_step);
|
|
|
|
GoniometerAxis& Axis(const Coord &input);
|
|
GoniometerAxis& ScreeningWedge(const std::optional<float>& input);
|
|
|
|
[[nodiscard]] std::string GetName() const;
|
|
[[nodiscard]] float GetStart_deg() const;
|
|
[[nodiscard]] float GetIncrement_deg() const;
|
|
// Whether the axis actually turns during the acquisition. An axis can be present and stationary
|
|
// - a grid scan or a still taken at a particular head position - which is a different question
|
|
// from whether one is defined at all, and the two used to be conflated.
|
|
[[nodiscard]] bool IsScanning() const;
|
|
[[nodiscard]] std::optional<float> GetScreeningWedge() const;
|
|
[[nodiscard]] float GetWedge_deg() const;
|
|
[[nodiscard]] float GetAngle_deg(float image_number) const;
|
|
|
|
[[nodiscard]] Coord GetAxis() const;
|
|
[[nodiscard]] std::optional<Coord> GetHelicalStep() const;
|
|
[[nodiscard]] Coord GetPosition(int64_t image_number) const;
|
|
|
|
[[nodiscard]] std::vector<double> GetAngleContainer(int64_t max_image_number) const;
|
|
[[nodiscard]] std::vector<double> GetAngleContainerEnd(int64_t max_image_number) const;
|
|
[[nodiscard]] std::vector<double> GetXContainer_m(int64_t max_image_number) const;
|
|
[[nodiscard]] std::vector<double> GetYContainer_m(int64_t max_image_number) const;
|
|
[[nodiscard]] std::vector<double> GetZContainer_m(int64_t max_image_number) const;
|
|
|
|
[[nodiscard]] std::vector<double> GetAxisVector() const;
|
|
|
|
[[nodiscard]] RotMatrix GetTransformationAngle(float angle_deg) const;
|
|
};
|