Which way the detector's rows run was decided once, in the module assembly, and never stated again: not on the wire, not in the file, nowhere a consumer could read it. mirror_y was consumed inside the DetectorGeometryModular constructor and discarded. It is now a declared property of the detector setup, carried into the start message, written to HDF5 under detectorSpecific, and read back. Absence means true, which is the MX convention and the only thing Jungfraujoch has ever produced. Deliberately a boolean and not a corner enum: the assembled image can only be flipped in Y, so a four-corner value would encode states that cannot occur. DECTRIS stream2 has no field for this - checked against the specification - so the key is new rather than an extension of theirs, and a consumer that does not know it skips it and behaves exactly as before. The .poni file gains pyFAI's orientation. Without it pyFAI applies its own default, 3 (bottom left), and believes increasing row means physically upwards. The numbers still agreed - a mirror preserves 2theta, so radial integration was never affected - but the azimuth came out with the opposite sense, which matters for cake and sector integration. Declaring orientation 2 is not a one-line addition: it re-anchors Poni1 to the top edge and reverses rot2 and rot3, a row flip being improper. Measured against pyFAI 2026.5.0 by searching all four orientations, both Poni1 anchorings and all eight sign combinations: exactly two combinations reproduce the lab position DiffractionGeometry computes to 1.4e-17 m - the unlabelled form written before, and (orientation 2, Poni1 = height-1-beam_y, +rot1/+rot2/-rot3), which is now written. Calibration_PoniFileAxisConvention pins it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
159 lines
7.5 KiB
C++
159 lines
7.5 KiB
C++
// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#pragma once
|
|
|
|
#include <memory>
|
|
#include <chrono>
|
|
#include <optional>
|
|
|
|
#include "DetectorGeometry.h"
|
|
#include "../jungfrau/JFModuleGainCalibration.h"
|
|
#include "DetectorGeometryFixed.h"
|
|
#include "DetectorGeometryModular.h"
|
|
#include "DetectorSettings.h"
|
|
|
|
constexpr uint16_t SimplonStream2Port = 31001;
|
|
|
|
enum class DetectorType {EIGER, JUNGFRAU, DECTRIS};
|
|
|
|
class DetectorSetup {
|
|
std::string description;
|
|
std::string serial_number;
|
|
std::shared_ptr<DetectorGeometry> geometry;
|
|
std::vector<std::string> det_modules_hostname;
|
|
std::vector<std::string> gain_file_names;
|
|
std::vector<std::string> trim_file_names;
|
|
std::string trim_file_directory;
|
|
std::vector<int> trim_energy_eV_values;
|
|
std::shared_ptr<JFGainCalibration> gain_calibration;
|
|
int64_t udp_interface_count = 2;
|
|
float pixel_size_um = 75.0f;
|
|
std::string sensor_material = "Si";
|
|
float sensor_thickness_um = 320.0f;
|
|
std::vector<int64_t> tx_delay;
|
|
DetectorType detector_type;
|
|
int32_t high_voltage = 120.0;
|
|
uint32_t ipv4_base_addr = 0x010a0a0a;
|
|
bool module_sync = true;
|
|
// Whether the assembled image is mirrored in Y relative to the detector's raw readout order.
|
|
// A property of the configuration, not something derivable from the assembled image: it says
|
|
// how the modules were laid out to reach the MX convention of row 0 at the top.
|
|
bool mirror_y = true;
|
|
std::chrono::nanoseconds read_out_time;
|
|
std::chrono::nanoseconds min_count_time;
|
|
std::chrono::nanoseconds min_frame_time;
|
|
float min_energy_threshold_keV = 2.7f;
|
|
int32_t temperature_thresold_degC = 55;
|
|
|
|
std::string dectris_roi;
|
|
|
|
std::optional<int64_t> bit_depth_image;
|
|
std::optional<int64_t> bit_depth_readout;
|
|
std::optional<int64_t> saturation_limit;
|
|
std::optional<DetectorSettings> settings;
|
|
|
|
DetectorSetup(std::shared_ptr<DetectorGeometry> geom,
|
|
DetectorType detector_type,
|
|
const std::string &description = "Detector",
|
|
const std::vector<std::string> &det_modules_hostname = {});
|
|
public:
|
|
DetectorSetup(const DetectorGeometryFixed& geom,
|
|
DetectorType detector_type,
|
|
const std::string &description = "Detector",
|
|
const std::vector<std::string> &det_modules_hostname = {});
|
|
DetectorSetup(const DetectorGeometryModular& geom,
|
|
DetectorType detector_type,
|
|
const std::string &description = "Detector",
|
|
const std::vector<std::string> &det_modules_hostname = {});
|
|
void LoadGain(const std::vector<std::string> &filenames);
|
|
void SetTrimFiles(const std::vector<std::string> &filenames);
|
|
|
|
DetectorSetup& TxDelay(const std::vector<int64_t> &v);
|
|
DetectorSetup& UDPInterfaceCount(int64_t input);
|
|
DetectorSetup& SensorMaterial(const std::string &input);
|
|
DetectorSetup& SensorThickness_um(float input);
|
|
DetectorSetup& PixelSize_um(float input);
|
|
DetectorSetup& HighVoltage(int32_t input);
|
|
DetectorSetup& SerialNumber(const std::string &input);
|
|
DetectorSetup& BaseIPv4Addr(const std::string &input);
|
|
DetectorSetup& ModuleSync(bool input);
|
|
DetectorSetup& MirrorY(bool input);
|
|
DetectorSetup& ReadOutTime(std::chrono::nanoseconds input);
|
|
DetectorSetup& Geometry(const DetectorGeometryFixed& input);
|
|
DetectorSetup& BitDepthImage(int64_t input);
|
|
DetectorSetup& MinFrameTime(std::chrono::nanoseconds input);
|
|
DetectorSetup& MinCountTime(std::chrono::nanoseconds input);
|
|
DetectorSetup& MinThreshold_keV(float input);
|
|
DetectorSetup& SaturationLimit(std::optional<int64_t> input);
|
|
DetectorSetup& Description(const std::string &input);
|
|
DetectorSetup& DECTRISROI(const std::string &input);
|
|
DetectorSetup& DefaultSettings(const std::optional<DetectorSettings> &input);
|
|
DetectorSetup& TempThreshold_degC(int64_t input);
|
|
DetectorSetup& TrimEnergies_eV(std::vector<int> input);
|
|
|
|
[[nodiscard]] DetectorType GetDetectorType() const;
|
|
[[nodiscard]] const DetectorGeometry& GetGeometry() const;
|
|
[[nodiscard]] const std::vector<std::string>& GetDetectorModuleHostname() const;
|
|
[[nodiscard]] uint64_t GetModulesNum() const;
|
|
[[nodiscard]] std::string GetDescription() const;
|
|
[[nodiscard]] float GetPixelSize_mm() const;
|
|
[[nodiscard]] float GetSensorThickness_um() const;
|
|
[[nodiscard]] std::string GetSensorMaterial() const;
|
|
[[nodiscard]] const std::vector<JFModuleGainCalibration> &GetGainCalibration() const;
|
|
[[nodiscard]] int64_t GetUDPInterfaceCount() const;
|
|
[[nodiscard]] const std::vector<int64_t> &GetTxDelay() const; // can be empty for default
|
|
[[nodiscard]] const std::vector<std::string> &GetGainFileNames() const;
|
|
[[nodiscard]] const std::vector<std::string> &GetTrimFileNames() const;
|
|
[[nodiscard]] std::string GetTrimFileDirectory() const;
|
|
[[nodiscard]] int32_t GetHighVoltage() const;
|
|
[[nodiscard]] std::string GetSerialNumber() const;
|
|
[[nodiscard]] uint32_t GetSrcIPv4Addr(uint32_t half_module) const;
|
|
[[nodiscard]] std::string GetBaseIPv4Addr() const;
|
|
[[nodiscard]] bool IsModuleSync() const;
|
|
[[nodiscard]] bool IsMirrorY() const;
|
|
[[nodiscard]] std::chrono::nanoseconds GetReadOutTime() const;
|
|
[[nodiscard]] std::chrono::nanoseconds GetMinFrameTime() const;
|
|
[[nodiscard]] std::chrono::nanoseconds GetMinCountTime() const;
|
|
[[nodiscard]] std::optional<int64_t> GetBitDepthReadout() const;
|
|
[[nodiscard]] std::optional<int64_t> GetBitDepthImage() const;
|
|
[[nodiscard]] std::string GetDECTRISStream2Addr() const;
|
|
[[nodiscard]] float GetMinThreshold_keV() const;
|
|
[[nodiscard]] std::optional<int64_t> GetSaturationLimit() const;
|
|
[[nodiscard]] std::string GetDECTRISROI() const;
|
|
[[nodiscard]] std::optional<DetectorSettings> GetDefaultSettings() const;
|
|
[[nodiscard]] int32_t GetTempThreshold_degC() const;
|
|
[[nodiscard]] std::vector<int> GetTrimEnergies_eV() const;
|
|
};
|
|
|
|
DetectorSetup DetJF4M(const std::string &description = "Detector",
|
|
const std::vector<std::string> &det_modules_hostname = {});
|
|
DetectorSetup DetJF9M(const std::string &description = "Detector",
|
|
const std::vector<std::string> &det_modules_hostname = {});
|
|
|
|
DetectorSetup DetJF(int32_t nmodules,
|
|
int32_t horizontal_stacking = 1,
|
|
int32_t gap_x = 0,
|
|
int32_t gap_y = 0,
|
|
bool mirror_y = true,
|
|
const std::string &description = "Detector",
|
|
const std::vector<std::string> &det_modules_hostname = {});
|
|
DetectorSetup DetJF(const DetectorGeometryModular &geom,
|
|
const std::string &description = "Detector",
|
|
const std::vector<std::string> &det_modules_hostname = {});
|
|
|
|
DetectorSetup DetEIGER(int32_t nmodules,
|
|
int32_t horizontal_stacking = 1,
|
|
int32_t gap_x = 0,
|
|
int32_t gap_y = 0,
|
|
bool mirror_y = true,
|
|
const std::string &description = "Detector",
|
|
const std::vector<std::string> &det_modules_hostname = {});
|
|
DetectorSetup DetEIGER(const DetectorGeometryModular &geom,
|
|
const std::string &description = "Detector",
|
|
const std::vector<std::string> &det_modules_hostname = {});
|
|
|
|
DetectorSetup DetDECTRIS(int64_t width, int64_t height,
|
|
const std::string &description,
|
|
const std::string &addr);
|