NXmx has no field for the depth of the stored image - only bit_depth_readout, "how many bits the electronics record per pixel". The two diverge exactly when summation is used: the readout keeps the detector's native width while the summed image must be wider to hold the sum. Every NXmx reader nonetheless takes bit_depth_readout as the width of the stored pixel. dxtbx ignores the non-standard bit_depth_image entirely for a generic NXmx file, derives its masking markers from bit_depth_readout, and raises "Unsupported integer dtype uint32" for a 32-bit image when the field is absent. Reporting the electronic value there would mislead precisely where it differs. So report the image depth in both fields, and drop the machinery that existed to carry the electronic one for a DECTRIS detector: the SIMPLON read, the DetectorSetup setter, and the receiver-side propagation of a key that the DECTRIS stream2 protocol does not even define. JUNGFRAU and PSI EIGER keep their readout depth, which the FPGA acquisition genuinely needs. Also write NXmx underload_value, the lowest valid value. Without it a reader takes the trusted minimum to be -0x7FFFFFFF, so the error-pixel marker sits inside the trusted range and is consumed as an intensity. Measured with DIALS 3.27 on a written file: trusted_range goes from (-2147483647, 32766) to (-32767, 32766), so the INT16_MIN gap pixels are now masked. Third fix in the same area: JFJochReceiverLite::Configure took the image width from the incoming stream but not the sign, while the image itself is forwarded byte-for-byte. A detector sending int32 was re-declared uint32, and the VDS master was typed unsigned over signed data files. Take pixel_signed from the stream too - it and the width are both carried by the one image_dtype key. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
153 lines
7.2 KiB
C++
153 lines
7.2 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;
|
|
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& 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]] 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);
|