Carry the sample transformation chain as DetectorTransformation
Replaces the start-message TransformationAxis of the previous commit, which was the wrong shape in two ways. DetectorTransformation (common/) mirrors a NeXus NXtransformations axis and holds nothing else: name, type, units, vector, offset, depends_on and the positions themselves. Deliberately without cleverness - the values are either a single number for an axis that does not move or one per image, and nothing derives a position from a start and an increment. That is the point: a producer will later want to report where a stage actually WENT rather than where it was told to go, and a structure that stores start+increment cannot express that. A million images cost 4 MB per axis, which is not a reason to be clever. Hence also the move to the END message: measured positions are only known once the run is over. And hence no metadata version bump, which the previous commit did make. The chain is optional; when it is absent the writer builds the identical chain from the start message, exactly as before. Nothing on the wire changes for a producer that does not send it, so a broker and a writer of different releases still interwork - the constraint the previous version stated is withdrawn. The writer transcribes a chain it is given, without recomputing an angle, which is what makes measured positions possible end to end. JFJochReader_TransformationChain_SentAndBuilt writes the same run both ways and checks the two files read back the same, chi/phi included. CBORSerialize_End_Transformations covers the wire 1:1, asserting the order survives and that a moving axis keeps one value per image while a stationary one keeps a single value. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1015,10 +1015,9 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
// The ordered sample chain (see TransformationAxis). An array, so the mounting order survives -
|
||||
// a CBOR map's order carries no guarantee. Sent alongside the goniometer map, which says the same
|
||||
// thing without the order for consumers that only know DECTRIS stream2.
|
||||
void ProcessTransformations(StartMessage &message, CborValue &value) {
|
||||
// The ordered sample chain, 1:1 with DetectorTransformation. An array, so the mounting order
|
||||
// survives; a CBOR map carries no order a consumer may rely on.
|
||||
void ProcessTransformations(std::vector<DetectorTransformation> &chain, CborValue &value) {
|
||||
CborValue array_value;
|
||||
cborErr(cbor_value_enter_container(&value, &array_value));
|
||||
|
||||
@@ -1026,28 +1025,39 @@ namespace {
|
||||
CborValue map_value;
|
||||
cborErr(cbor_value_enter_container(&array_value, &map_value));
|
||||
|
||||
TransformationAxis axis;
|
||||
DetectorTransformation axis;
|
||||
std::vector<float> values;
|
||||
while (!cbor_value_at_end(&map_value)) {
|
||||
const auto key = GetCBORString(map_value);
|
||||
if (key == "name")
|
||||
axis.name = GetCBORString(map_value);
|
||||
else if (key == "type")
|
||||
axis.rotation = (GetCBORString(map_value) == "rotation");
|
||||
axis.Name(GetCBORString(map_value));
|
||||
else if (key == "transformation_type")
|
||||
axis.Type(GetCBORString(map_value) == "rotation" ? TransformationType::Rotation
|
||||
: TransformationType::Translation);
|
||||
else if (key == "units")
|
||||
axis.Units(GetCBORString(map_value));
|
||||
else if (key == "vector")
|
||||
axis.vector = GetCoord(map_value);
|
||||
else if (key == "start")
|
||||
axis.start = GetCBORFloat(map_value);
|
||||
else if (key == "increment")
|
||||
axis.increment = GetCBORFloat(map_value);
|
||||
axis.Vector(GetCoord(map_value));
|
||||
else if (key == "offset")
|
||||
axis.Offset(GetCoord(map_value));
|
||||
else if (key == "depends_on")
|
||||
axis.DependsOn(GetCBORString(map_value));
|
||||
else if (key == "equipment")
|
||||
axis.Equipment(GetCBORString(map_value));
|
||||
else if (key == "equipment_component")
|
||||
axis.EquipmentComponent(GetCBORString(map_value));
|
||||
else if (key == "values")
|
||||
GetCBORFloatArray(map_value, values);
|
||||
else
|
||||
cbor_value_advance(&map_value);
|
||||
}
|
||||
cborErr(cbor_value_leave_container(&array_value, &map_value));
|
||||
|
||||
if (axis.name.empty())
|
||||
if (axis.GetName().empty())
|
||||
throw JFJochException(JFJochExceptionCategory::CBORError,
|
||||
"Transformation axis without a name");
|
||||
message.transformations.push_back(axis);
|
||||
axis.Values(values);
|
||||
chain.push_back(axis);
|
||||
}
|
||||
cborErr(cbor_value_leave_container(&value, &array_value));
|
||||
}
|
||||
@@ -1298,8 +1308,6 @@ namespace {
|
||||
ProcessAxis(value, message.detector_translation);
|
||||
else if (key == "goniometer")
|
||||
ProcessGoniometerMap(message, value);
|
||||
else if (key == "transformations")
|
||||
ProcessTransformations(message, value);
|
||||
else if (key == "grid_scan")
|
||||
message.grid_scan = ProcessGridScan(value);
|
||||
else if (key == "pixel_mask_enabled")
|
||||
@@ -1435,6 +1443,8 @@ namespace {
|
||||
message.run_name = GetCBORString(value);
|
||||
else if (key == "series_id")
|
||||
message.run_number = GetCBORUInt(value);
|
||||
else if (key == "transformations")
|
||||
ProcessTransformations(message.transformations, value);
|
||||
else if (key == "max_image_number")
|
||||
message.max_image_number = GetCBORUInt(value);
|
||||
else if (key == "images_collected")
|
||||
|
||||
Reference in New Issue
Block a user