added ReadingMode member to RawMasterFile adapted error in Matterhorntransformer

This commit is contained in:
2026-02-03 16:49:04 +01:00
parent 4edebf4671
commit 8c8d635b0c
9 changed files with 146 additions and 75 deletions

View File

@@ -2,11 +2,11 @@
#pragma once #pragma once
#include "aare/defs.hpp" #include "aare/defs.hpp"
#include <algorithm> #include <algorithm>
#include <chrono>
#include <filesystem> #include <filesystem>
#include <fmt/format.h> #include <fmt/format.h>
#include <fstream> #include <fstream>
#include <optional> #include <optional>
#include <chrono>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
using json = nlohmann::json; using json = nlohmann::json;
@@ -97,9 +97,9 @@ class RawMasterFile {
size_t m_frame_padding{}; size_t m_frame_padding{};
// TODO! should these be bool? // TODO! should these be bool?
uint8_t m_analog_flag{}; bool m_analog_flag{};
uint8_t m_digital_flag{}; bool m_digital_flag{};
uint8_t m_transceiver_flag{}; bool m_transceiver_flag{};
ScanParameters m_scan_parameters; ScanParameters m_scan_parameters;
@@ -135,6 +135,8 @@ class RawMasterFile {
size_t n_modules() const; size_t n_modules() const;
uint8_t quad() const; uint8_t quad() const;
ReadingMode get_reading_mode() const;
std::optional<size_t> analog_samples() const; std::optional<size_t> analog_samples() const;
std::optional<size_t> digital_samples() const; std::optional<size_t> digital_samples() const;
std::optional<size_t> transceiver_samples() const; std::optional<size_t> transceiver_samples() const;
@@ -145,7 +147,9 @@ class RawMasterFile {
ScanParameters scan_parameters() const; ScanParameters scan_parameters() const;
std::optional<std::chrono::nanoseconds> exptime() const { return m_exptime; } std::optional<std::chrono::nanoseconds> exptime() const {
return m_exptime;
}
std::chrono::nanoseconds period() const { return m_period; } std::chrono::nanoseconds period() const { return m_period; }
private: private:

View File

@@ -188,6 +188,15 @@ struct ROI {
} }
}; };
enum ReadingMode : uint8_t {
Analog = 0,
Digital = 1,
AnalogAndDigital = 2,
Transceiver = 3,
DigitalAndTransceiver = 4,
Unknown = 5
};
using dynamic_shape = std::vector<ssize_t>; using dynamic_shape = std::vector<ssize_t>;
// TODO! Can we uniform enums between the libraries? // TODO! Can we uniform enums between the libraries?
@@ -353,16 +362,15 @@ using DataTypeVariants = std::variant<uint16_t, uint32_t>;
constexpr uint16_t ADC_MASK = constexpr uint16_t ADC_MASK =
0x3FFF; // used to mask out the gain bits in Jungfrau 0x3FFF; // used to mask out the gain bits in Jungfrau
class BitOffset {
class BitOffset{
uint8_t m_offset{}; uint8_t m_offset{};
public: public:
BitOffset() = default; BitOffset() = default;
explicit BitOffset(uint32_t offset); explicit BitOffset(uint32_t offset);
uint8_t value() const {return m_offset;} uint8_t value() const { return m_offset; }
bool operator==(const BitOffset& other) const; bool operator==(const BitOffset &other) const;
bool operator<(const BitOffset& other) const; bool operator<(const BitOffset &other) const;
}; };
} // namespace aare } // namespace aare

View File

@@ -5,7 +5,7 @@ from . import _aare
from ._aare import File, RawMasterFile, RawSubFile, JungfrauDataFile from ._aare import File, RawMasterFile, RawSubFile, JungfrauDataFile
from ._aare import Pedestal_d, Pedestal_f, ClusterFinder_Cluster3x3i, VarClusterFinder from ._aare import Pedestal_d, Pedestal_f, ClusterFinder_Cluster3x3i, VarClusterFinder
from ._aare import DetectorType from ._aare import DetectorType, ReadingMode
from ._aare import hitmap from ._aare import hitmap
from ._aare import ROI from ._aare import ROI
from ._aare import corner from ._aare import corner

View File

@@ -1,7 +1,7 @@
# SPDX-License-Identifier: MPL-2.0 # SPDX-License-Identifier: MPL-2.0
import numpy as np import numpy as np
from . import _aare from . import _aare
from aare import ReadingMode
class AdcSar04Transform64to16: class AdcSar04Transform64to16:
def __call__(self, data): def __call__(self, data):
@@ -71,20 +71,16 @@ class Matterhorn10Transform:
self.dynamic_range = dynamic_range self.dynamic_range = dynamic_range
self.num_counters = num_counters self.num_counters = num_counters
def compatibility(self, num_digital_samples : int, num_analog_samples : int): def compatibility(self, readingmode : ReadingMode):
""" """
checks if Matterhorn10Transform is compatible with given parameters checks if Matterhorn10Transform is compatible with given parameters
:param num_digital_samples: Number of digital samples set :param readingmode: Reading mode set
:type num_digital_samples: int :type readingmode: ReadingMode
:param num_analog_samples: Number of analog samples set
:type num_analog_samples: int
:raises ValueError: if not compatible :raises ValueError: if not compatible
""" """
if(num_digital_samples != 0 and num_digital_samples is not None): if(readingmode != ReadingMode.Transceiver):
raise ValueError(f"Incompatible Transformation. Matterhorn10Transform does not support digital samples, but num_digital_samples is {num_digital_samples}.") raise ValueError(f"Incompatible Transformation. Matterhorn10Transform only requires transceiver samples. However reading mode is {readingmode}.")
if(num_analog_samples != 0 and num_analog_samples is not None):
raise ValueError(f"Incompatible Transformation. Matterhorn10Transform does not support analog samples, but num_analog_samples is {num_analog_samples}.")
pass pass

View File

@@ -23,6 +23,16 @@ namespace py = pybind11;
using namespace ::aare; using namespace ::aare;
void define_raw_master_file_bindings(py::module &m) { void define_raw_master_file_bindings(py::module &m) {
py::enum_<ReadingMode>(m, "ReadingMode")
.value("Analog", ReadingMode::Analog)
.value("Digital", ReadingMode::Digital)
.value("AnalogAndDigital", ReadingMode::AnalogAndDigital)
.value("Transceiver", ReadingMode::Transceiver)
.value("DigitalAndTransceiver", ReadingMode::DigitalAndTransceiver)
.value("Unknown", ReadingMode::Unknown)
.export_values();
py::class_<RawMasterFile>(m, "RawMasterFile") py::class_<RawMasterFile>(m, "RawMasterFile")
.def(py::init<const std::filesystem::path &>()) .def(py::init<const std::filesystem::path &>())
.def("data_fname", &RawMasterFile::data_fname, R"( .def("data_fname", &RawMasterFile::data_fname, R"(
@@ -81,6 +91,7 @@ void define_raw_master_file_bindings(py::module &m) {
.def_property_readonly("transceiver_samples", .def_property_readonly("transceiver_samples",
&RawMasterFile::transceiver_samples) &RawMasterFile::transceiver_samples)
.def_property_readonly("reading_mode", &RawMasterFile::get_reading_mode)
.def_property_readonly("number_of_rows", &RawMasterFile::number_of_rows) .def_property_readonly("number_of_rows", &RawMasterFile::number_of_rows)
.def_property_readonly("quad", &RawMasterFile::quad) .def_property_readonly("quad", &RawMasterFile::quad)
.def_property_readonly("scan_parameters", .def_property_readonly("scan_parameters",

View File

@@ -0,0 +1,14 @@
import pytest
from aare import RawMasterFile, ReadingMode, DetectorType
@pytest.mark.withdata
def test_read_rawfile_quad_eiger_and_compare_to_numpy(test_data_path):
file_name = test_data_path/'raw/jungfrau/jungfrau_single_master_0.json'
f = RawMasterFile(file_name)
assert(f.reading_mode == ReadingMode.Unknown)
assert(f.detector_type == DetectorType.Jungfrau)

View File

@@ -71,7 +71,7 @@ ScanParameters::ScanParameters(const bool enabled, const DACIndex dac,
const int start, const int stop, const int step, const int start, const int stop, const int step,
const int64_t settleTime) const int64_t settleTime)
: m_enabled(enabled), m_dac(dac), m_start(start), m_stop(stop), : m_enabled(enabled), m_dac(dac), m_start(start), m_stop(stop),
m_step(step), m_settleTime(settleTime) {}; m_step(step), m_settleTime(settleTime){};
// "[enabled\ndac dac 4\nstart 500\nstop 2200\nstep 5\nsettleTime 100us\n]" // "[enabled\ndac dac 4\nstart 500\nstop 2200\nstep 5\nsettleTime 100us\n]"
ScanParameters::ScanParameters(const std::string &par) { ScanParameters::ScanParameters(const std::string &par) {
@@ -195,6 +195,30 @@ ScanParameters RawMasterFile::scan_parameters() const {
std::optional<ROI> RawMasterFile::roi() const { return m_roi; } std::optional<ROI> RawMasterFile::roi() const { return m_roi; }
ReadingMode RawMasterFile::get_reading_mode() const {
if (m_type != DetectorType::ChipTestBoard &&
m_type != DetectorType::Xilinx_ChipTestBoard) {
LOG(TLogLevel::logINFO)
<< "reading mode is only available for CTB detectors.";
return ReadingMode::Unknown;
}
if (m_analog_flag && m_digital_flag) {
return ReadingMode::AnalogAndDigital;
} else if (m_analog_flag) {
return ReadingMode::Analog;
} else if (m_digital_flag && m_transceiver_flag) {
return ReadingMode::DigitalAndTransceiver;
} else if (m_digital_flag) {
return ReadingMode::Digital;
} else if (m_transceiver_flag) {
return ReadingMode::Transceiver;
} else {
return ReadingMode::Unknown;
}
}
void RawMasterFile::parse_json(std::istream &is) { void RawMasterFile::parse_json(std::istream &is) {
json j; json j;
is >> j; is >> j;
@@ -205,9 +229,9 @@ void RawMasterFile::parse_json(std::istream &is) {
m_type = string_to<DetectorType>(j["Detector Type"].get<std::string>()); m_type = string_to<DetectorType>(j["Detector Type"].get<std::string>());
m_timing_mode = string_to<TimingMode>(j["Timing Mode"].get<std::string>()); m_timing_mode = string_to<TimingMode>(j["Timing Mode"].get<std::string>());
m_geometry = { m_geometry = {j["Geometry"]["y"],
j["Geometry"]["y"], j["Geometry"]["x"]}; // TODO: isnt it only available for
j["Geometry"]["x"]}; // TODO: isnt it only available for version > 7.1? // version > 7.1?
// - try block default should be 1x1 // - try block default should be 1x1
m_image_size_in_bytes = m_image_size_in_bytes =
@@ -244,7 +268,7 @@ void RawMasterFile::parse_json(std::istream &is) {
// TODO! Not valid for CTB but not changing api right now! // TODO! Not valid for CTB but not changing api right now!
// Not all detectors write the bitdepth but in case // Not all detectors write the bitdepth but in case
// its not there it is 16 // its not there it is 16
if(j.contains("Dynamic Range") && j["Dynamic Range"].is_number()){ if (j.contains("Dynamic Range") && j["Dynamic Range"].is_number()) {
m_bitdepth = j["Dynamic Range"]; m_bitdepth = j["Dynamic Range"];
} else { } else {
m_bitdepth = 16; m_bitdepth = 16;
@@ -255,18 +279,18 @@ void RawMasterFile::parse_json(std::istream &is) {
m_frame_discard_policy = string_to<FrameDiscardPolicy>( m_frame_discard_policy = string_to<FrameDiscardPolicy>(
j["Frame Discard Policy"].get<std::string>()); j["Frame Discard Policy"].get<std::string>());
if(j.contains("Number of rows") && j["Number of rows"].is_number()){ if (j.contains("Number of rows") && j["Number of rows"].is_number()) {
m_number_of_rows = j["Number of rows"]; m_number_of_rows = j["Number of rows"];
} }
// ---------------------------------------------------------------- // ----------------------------------------------------------------
// Special treatment of analog flag because of Moench03 // Special treatment of analog flag because of Moench03
try { try {
m_analog_flag = j.at("Analog Flag"); m_analog_flag = static_cast<bool>(j.at("Analog Flag").get<int>());
} catch (const json::out_of_range &e) { } catch (const json::out_of_range &e) {
// if it doesn't work still set it to one // if it doesn't work still set it to one
// to try to decode analog samples (Old Moench03) // to try to decode analog samples (Old Moench03)
m_analog_flag = 1; m_analog_flag = true;
} }
try { try {
if (m_analog_flag) { if (m_analog_flag) {
@@ -276,7 +300,7 @@ void RawMasterFile::parse_json(std::istream &is) {
} catch (const json::out_of_range &e) { } catch (const json::out_of_range &e) {
// keep the optional empty // keep the optional empty
// and set analog flag to 0 // and set analog flag to 0
m_analog_flag = 0; m_analog_flag = false;
} }
//----------------------------------------------------------------- //-----------------------------------------------------------------
try { try {
@@ -291,7 +315,7 @@ void RawMasterFile::parse_json(std::istream &is) {
// m_adc_mask = 0; // m_adc_mask = 0;
// } // }
try { try {
int digital_flag = j.at("Digital Flag"); bool digital_flag = static_cast<bool>(j.at("Digital Flag").get<int>());
if (digital_flag) { if (digital_flag) {
m_digital_samples = j.at("Digital Samples"); m_digital_samples = j.at("Digital Samples");
} }
@@ -299,7 +323,8 @@ void RawMasterFile::parse_json(std::istream &is) {
// keep the optional empty // keep the optional empty
} }
try { try {
m_transceiver_flag = j.at("Transceiver Flag"); m_transceiver_flag =
static_cast<bool>(j.at("Transceiver Flag").get<int>());
if (m_transceiver_flag) { if (m_transceiver_flag) {
m_transceiver_samples = j.at("Transceiver Samples"); m_transceiver_samples = j.at("Transceiver Samples");
} }
@@ -380,7 +405,6 @@ void RawMasterFile::parse_json(std::istream &is) {
std::stoi(j["Counter Mask"].get<std::string>(), nullptr, 16); std::stoi(j["Counter Mask"].get<std::string>(), nullptr, 16);
} }
// Update detector type for Moench // Update detector type for Moench
// TODO! How does this work with old .raw master files? // TODO! How does this work with old .raw master files?
if (m_type == DetectorType::Moench && !m_analog_samples && if (m_type == DetectorType::Moench && !m_analog_samples &&
@@ -433,9 +457,9 @@ void RawMasterFile::parse_raw(std::istream &is) {
// } else if (key == "Number of rows"){ // } else if (key == "Number of rows"){
// m_number_of_rows = std::stoi(value); // m_number_of_rows = std::stoi(value);
} else if (key == "Analog Flag") { } else if (key == "Analog Flag") {
m_analog_flag = std::stoi(value); m_analog_flag = static_cast<bool>(std::stoi(value));
} else if (key == "Digital Flag") { } else if (key == "Digital Flag") {
m_digital_flag = std::stoi(value); m_digital_flag = static_cast<bool>(std::stoi(value));
} else if (key == "Analog Samples") { } else if (key == "Analog Samples") {
if (m_analog_flag == 1) { if (m_analog_flag == 1) {

View File

@@ -146,6 +146,8 @@ TEST_CASE("Parse a master file in .json format", "[.integration]") {
REQUIRE_FALSE(f.analog_samples()); REQUIRE_FALSE(f.analog_samples());
REQUIRE_FALSE(f.digital_samples()); REQUIRE_FALSE(f.digital_samples());
REQUIRE(f.get_reading_mode() == ReadingMode::Unknown);
} }
TEST_CASE("Parse a master file in old .raw format", TEST_CASE("Parse a master file in old .raw format",
@@ -211,6 +213,8 @@ TEST_CASE("Parse a master file in .raw format", "[.integration]") {
// Frames in File : 100 // Frames in File : 100
REQUIRE(f.frames_in_file() == 100); REQUIRE(f.frames_in_file() == 100);
REQUIRE(f.get_reading_mode() == ReadingMode::Unknown);
// #Frame Header // #Frame Header
// Frame Number : 8 bytes // Frame Number : 8 bytes
// SubFrame Number/ExpLength : 4 bytes // SubFrame Number/ExpLength : 4 bytes
@@ -560,6 +564,7 @@ TEST_CASE("Parse a CTB file from stream") {
REQUIRE(f.digital_samples() == std::nullopt); // Digital Flag is 0 REQUIRE(f.digital_samples() == std::nullopt); // Digital Flag is 0
REQUIRE(f.transceiver_samples() == 1152); REQUIRE(f.transceiver_samples() == 1152);
REQUIRE(f.frames_in_file() == 40); REQUIRE(f.frames_in_file() == 40);
REQUIRE(f.get_reading_mode() == ReadingMode::Transceiver);
} }
TEST_CASE("Parse v8.0 MYTHEN3 from stream") { TEST_CASE("Parse v8.0 MYTHEN3 from stream") {

View File

@@ -75,35 +75,32 @@ TEST_CASE("test_apply_custom_weights") {
CHECK_THAT(output, WithinAbs(6.34, 0.001)); CHECK_THAT(output, WithinAbs(6.34, 0.001));
} }
TEST_CASE("Mask 32 bit unsigned integer to 24 bit"){ TEST_CASE("Mask 32 bit unsigned integer to 24 bit") {
//any number less than 2**24 (16777216) should be the same // any number less than 2**24 (16777216) should be the same
CHECK(aare::mask32to24bits(0)==0); CHECK(aare::mask32to24bits(0) == 0);
CHECK(aare::mask32to24bits(19)==19); CHECK(aare::mask32to24bits(19) == 19);
CHECK(aare::mask32to24bits(29875)==29875); CHECK(aare::mask32to24bits(29875) == 29875);
CHECK(aare::mask32to24bits(1092177)==1092177); CHECK(aare::mask32to24bits(1092177) == 1092177);
CHECK(aare::mask32to24bits(0xFFFF)==0xFFFF); CHECK(aare::mask32to24bits(0xFFFF) == 0xFFFF);
CHECK(aare::mask32to24bits(0xFFFFFFFF)==0xFFFFFF); CHECK(aare::mask32to24bits(0xFFFFFFFF) == 0xFFFFFF);
// Offset specifies that the should ignore 0-7 bits // Offset specifies that the should ignore 0-7 bits
// at the start // at the start
CHECK(aare::mask32to24bits(0xFFFF, BitOffset(4))==0xFFF); CHECK(aare::mask32to24bits(0xFFFF, BitOffset(4)) == 0xFFF);
CHECK(aare::mask32to24bits(0xFF0000d9)==0xd9); CHECK(aare::mask32to24bits(0xFF0000d9) == 0xd9);
CHECK(aare::mask32to24bits(0xFF000d9F, BitOffset(4))==0xF000d9); CHECK(aare::mask32to24bits(0xFF000d9F, BitOffset(4)) == 0xF000d9);
CHECK(aare::mask32to24bits(16777217)==1); CHECK(aare::mask32to24bits(16777217) == 1);
CHECK(aare::mask32to24bits(15,BitOffset(7))==0); CHECK(aare::mask32to24bits(15, BitOffset(7)) == 0);
//Highest bit set to 1 should just be excluded
//lowest 4 bits set to 1
CHECK(aare::mask32to24bits(0x8000000f,BitOffset(7))==0);
// Highest bit set to 1 should just be excluded
// lowest 4 bits set to 1
CHECK(aare::mask32to24bits(0x8000000f, BitOffset(7)) == 0);
} }
TEST_CASE("Expand container with 24 bit data to 32"){ TEST_CASE("Expand container with 24 bit data to 32") {
{ {
uint8_t buffer[] = { uint8_t buffer[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
0x00, 0x00, 0x00,
}; };
aare::NDView<uint8_t, 1> input(&buffer[0], {9}); aare::NDView<uint8_t, 1> input(&buffer[0], {9});
@@ -116,9 +113,7 @@ TEST_CASE("Expand container with 24 bit data to 32"){
} }
{ {
uint8_t buffer[] = { uint8_t buffer[] = {
0x0F, 0x00, 0x00, 0x0F, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
0xFF, 0x00, 0x00,
0xFF, 0xFF, 0xFF,
}; };
aare::NDView<uint8_t, 1> input(&buffer[0], {9}); aare::NDView<uint8_t, 1> input(&buffer[0], {9});
@@ -131,9 +126,7 @@ TEST_CASE("Expand container with 24 bit data to 32"){
} }
{ {
uint8_t buffer[] = { uint8_t buffer[] = {
0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00,
0xFF, 0xFF, 0x00,
0x00, 0xFF, 0x00,
}; };
aare::NDView<uint8_t, 1> input(&buffer[0], {9}); aare::NDView<uint8_t, 1> input(&buffer[0], {9});
@@ -147,20 +140,36 @@ TEST_CASE("Expand container with 24 bit data to 32"){
REQUIRE_THROWS(aare::expand24to32bit(input, out.view(), BitOffset(4))); REQUIRE_THROWS(aare::expand24to32bit(input, out.view(), BitOffset(4)));
} }
{ {
//For use with offset we need an extra byte // For use with offset we need an extra byte
uint8_t buffer[] = { uint8_t buffer[] = {0x00, 0x00, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00};
0xFF, 0xFF, 0x00,
0x00, 0xFF, 0x00, 0x00
};
aare::NDView<uint8_t, 1> input(&buffer[0], {10}); aare::NDView<uint8_t, 1> input(&buffer[0], {10});
aare::NDArray<uint32_t, 1> out({3}); //still output.size == 3 aare::NDArray<uint32_t, 1> out({3}); // still output.size == 3
aare::expand24to32bit(input, out.view(), BitOffset(4)); aare::expand24to32bit(input, out.view(), BitOffset(4));
CHECK(out(0) == 0xFFF000); CHECK(out(0) == 0xFFF000);
CHECK(out(1) == 0xFFF); CHECK(out(1) == 0xFFF);
CHECK(out(2) == 0xFF0); CHECK(out(2) == 0xFF0);
} }
}
TEST_CASE("Expand 4 bit values packed into 8 bit to 8 bit values") {
{
uint8_t buffer[] = {
0x00, 0xF0, 0xFF, 0x00, 0xF0, 0xFF,
};
aare::NDView<uint8_t, 1> input(&buffer[0], {6});
aare::NDArray<uint8_t, 1> out({12});
aare::expand4to8bit(input, out.view());
uint8_t expected_output[] = {
0x0, 0x0, 0x0, 0xF, 0xF, 0xF,
0x0, 0x0, 0x0, 0xF, 0xF, 0xF}; // is it first little or big endian?
for (size_t i = 0; i < 12; ++i) {
CHECK(out(i) == expected_output[i]);
}
}
} }