diff --git a/include/aare/RawMasterFile.hpp b/include/aare/RawMasterFile.hpp index c672a2e..0c07bf3 100644 --- a/include/aare/RawMasterFile.hpp +++ b/include/aare/RawMasterFile.hpp @@ -2,11 +2,11 @@ #pragma once #include "aare/defs.hpp" #include +#include #include #include #include #include -#include #include using json = nlohmann::json; @@ -97,9 +97,9 @@ class RawMasterFile { size_t m_frame_padding{}; // TODO! should these be bool? - uint8_t m_analog_flag{}; - uint8_t m_digital_flag{}; - uint8_t m_transceiver_flag{}; + bool m_analog_flag{}; + bool m_digital_flag{}; + bool m_transceiver_flag{}; ScanParameters m_scan_parameters; @@ -135,6 +135,8 @@ class RawMasterFile { size_t n_modules() const; uint8_t quad() const; + ReadingMode get_reading_mode() const; + std::optional analog_samples() const; std::optional digital_samples() const; std::optional transceiver_samples() const; @@ -145,7 +147,9 @@ class RawMasterFile { ScanParameters scan_parameters() const; - std::optional exptime() const { return m_exptime; } + std::optional exptime() const { + return m_exptime; + } std::chrono::nanoseconds period() const { return m_period; } private: diff --git a/include/aare/defs.hpp b/include/aare/defs.hpp index 433b3aa..1264bce 100644 --- a/include/aare/defs.hpp +++ b/include/aare/defs.hpp @@ -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; // TODO! Can we uniform enums between the libraries? @@ -353,16 +362,15 @@ using DataTypeVariants = std::variant; constexpr uint16_t ADC_MASK = 0x3FFF; // used to mask out the gain bits in Jungfrau - -class BitOffset{ +class BitOffset { uint8_t m_offset{}; - public: + + public: BitOffset() = default; explicit BitOffset(uint32_t offset); - uint8_t value() const {return m_offset;} - bool operator==(const BitOffset& other) const; - bool operator<(const BitOffset& other) const; - + uint8_t value() const { return m_offset; } + bool operator==(const BitOffset &other) const; + bool operator<(const BitOffset &other) const; }; } // namespace aare \ No newline at end of file diff --git a/python/aare/__init__.py b/python/aare/__init__.py index b69d76e..e687489 100644 --- a/python/aare/__init__.py +++ b/python/aare/__init__.py @@ -5,7 +5,7 @@ from . import _aare from ._aare import File, RawMasterFile, RawSubFile, JungfrauDataFile 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 ROI from ._aare import corner diff --git a/python/aare/transform.py b/python/aare/transform.py index df76425..a0098a3 100644 --- a/python/aare/transform.py +++ b/python/aare/transform.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: MPL-2.0 import numpy as np from . import _aare - +from aare import ReadingMode class AdcSar04Transform64to16: def __call__(self, data): @@ -71,20 +71,16 @@ class Matterhorn10Transform: self.dynamic_range = dynamic_range 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 - :param num_digital_samples: Number of digital samples set - :type num_digital_samples: int - :param num_analog_samples: Number of analog samples set - :type num_analog_samples: int + :param readingmode: Reading mode set + :type readingmode: ReadingMode :raises ValueError: if not compatible """ - if(num_digital_samples != 0 and num_digital_samples is not None): - raise ValueError(f"Incompatible Transformation. Matterhorn10Transform does not support digital samples, but num_digital_samples is {num_digital_samples}.") - 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}.") + if(readingmode != ReadingMode.Transceiver): + raise ValueError(f"Incompatible Transformation. Matterhorn10Transform only requires transceiver samples. However reading mode is {readingmode}.") pass diff --git a/python/src/raw_master_file.hpp b/python/src/raw_master_file.hpp index f8730d9..5322494 100644 --- a/python/src/raw_master_file.hpp +++ b/python/src/raw_master_file.hpp @@ -23,6 +23,16 @@ namespace py = pybind11; using namespace ::aare; void define_raw_master_file_bindings(py::module &m) { + + py::enum_(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_(m, "RawMasterFile") .def(py::init()) .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", &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("quad", &RawMasterFile::quad) .def_property_readonly("scan_parameters", diff --git a/python/tests/test_RawMasterFile.py b/python/tests/test_RawMasterFile.py new file mode 100644 index 0000000..c9eb821 --- /dev/null +++ b/python/tests/test_RawMasterFile.py @@ -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) \ No newline at end of file diff --git a/src/RawMasterFile.cpp b/src/RawMasterFile.cpp index afc9b09..c913735 100644 --- a/src/RawMasterFile.cpp +++ b/src/RawMasterFile.cpp @@ -71,7 +71,7 @@ ScanParameters::ScanParameters(const bool enabled, const DACIndex dac, const int start, const int stop, const int step, const int64_t settleTime) : 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]" ScanParameters::ScanParameters(const std::string &par) { @@ -195,6 +195,30 @@ ScanParameters RawMasterFile::scan_parameters() const { std::optional 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) { json j; is >> j; @@ -205,10 +229,10 @@ void RawMasterFile::parse_json(std::istream &is) { m_type = string_to(j["Detector Type"].get()); m_timing_mode = string_to(j["Timing Mode"].get()); - m_geometry = { - j["Geometry"]["y"], - j["Geometry"]["x"]}; // TODO: isnt it only available for version > 7.1? - // - try block default should be 1x1 + m_geometry = {j["Geometry"]["y"], + j["Geometry"]["x"]}; // TODO: isnt it only available for + // version > 7.1? + // - try block default should be 1x1 m_image_size_in_bytes = v < 8.0 ? j["Image Size in bytes"] : j["Image Size"]; @@ -244,7 +268,7 @@ void RawMasterFile::parse_json(std::istream &is) { // TODO! Not valid for CTB but not changing api right now! // Not all detectors write the bitdepth but in case // 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"]; } else { m_bitdepth = 16; @@ -255,18 +279,18 @@ void RawMasterFile::parse_json(std::istream &is) { m_frame_discard_policy = string_to( j["Frame Discard Policy"].get()); - 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"]; - } + } // ---------------------------------------------------------------- // Special treatment of analog flag because of Moench03 try { - m_analog_flag = j.at("Analog Flag"); + m_analog_flag = static_cast(j.at("Analog Flag").get()); } catch (const json::out_of_range &e) { // if it doesn't work still set it to one // to try to decode analog samples (Old Moench03) - m_analog_flag = 1; + m_analog_flag = true; } try { if (m_analog_flag) { @@ -276,7 +300,7 @@ void RawMasterFile::parse_json(std::istream &is) { } catch (const json::out_of_range &e) { // keep the optional empty // and set analog flag to 0 - m_analog_flag = 0; + m_analog_flag = false; } //----------------------------------------------------------------- try { @@ -291,7 +315,7 @@ void RawMasterFile::parse_json(std::istream &is) { // m_adc_mask = 0; // } try { - int digital_flag = j.at("Digital Flag"); + bool digital_flag = static_cast(j.at("Digital Flag").get()); if (digital_flag) { m_digital_samples = j.at("Digital Samples"); } @@ -299,7 +323,8 @@ void RawMasterFile::parse_json(std::istream &is) { // keep the optional empty } try { - m_transceiver_flag = j.at("Transceiver Flag"); + m_transceiver_flag = + static_cast(j.at("Transceiver Flag").get()); if (m_transceiver_flag) { m_transceiver_samples = j.at("Transceiver Samples"); } @@ -379,7 +404,6 @@ void RawMasterFile::parse_json(std::istream &is) { m_counter_mask = std::stoi(j["Counter Mask"].get(), nullptr, 16); } - // Update detector type for Moench // TODO! How does this work with old .raw master files? @@ -433,9 +457,9 @@ void RawMasterFile::parse_raw(std::istream &is) { // } else if (key == "Number of rows"){ // m_number_of_rows = std::stoi(value); } else if (key == "Analog Flag") { - m_analog_flag = std::stoi(value); + m_analog_flag = static_cast(std::stoi(value)); } else if (key == "Digital Flag") { - m_digital_flag = std::stoi(value); + m_digital_flag = static_cast(std::stoi(value)); } else if (key == "Analog Samples") { if (m_analog_flag == 1) { diff --git a/src/RawMasterFile.test.cpp b/src/RawMasterFile.test.cpp index 2eb7b44..2957ebd 100644 --- a/src/RawMasterFile.test.cpp +++ b/src/RawMasterFile.test.cpp @@ -146,6 +146,8 @@ TEST_CASE("Parse a master file in .json format", "[.integration]") { REQUIRE_FALSE(f.analog_samples()); REQUIRE_FALSE(f.digital_samples()); + + REQUIRE(f.get_reading_mode() == ReadingMode::Unknown); } 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 REQUIRE(f.frames_in_file() == 100); + REQUIRE(f.get_reading_mode() == ReadingMode::Unknown); + // #Frame Header // Frame Number : 8 bytes // SubFrame Number/ExpLength : 4 bytes @@ -398,7 +402,7 @@ TEST_CASE("Parse EIGER 7.2 master from string stream") { REQUIRE(f.timing_mode() == TimingMode::Auto); REQUIRE(f.geometry().col == 2); REQUIRE(f.geometry().row == 2); - + REQUIRE(f.image_size_in_bytes() == 524288); REQUIRE(f.pixels_x() == 512); REQUIRE(f.pixels_y() == 256); @@ -560,6 +564,7 @@ TEST_CASE("Parse a CTB file from stream") { REQUIRE(f.digital_samples() == std::nullopt); // Digital Flag is 0 REQUIRE(f.transceiver_samples() == 1152); REQUIRE(f.frames_in_file() == 40); + REQUIRE(f.get_reading_mode() == ReadingMode::Transceiver); } TEST_CASE("Parse v8.0 MYTHEN3 from stream") { diff --git a/src/decode.test.cpp b/src/decode.test.cpp index d57b2f7..ba7f238 100644 --- a/src/decode.test.cpp +++ b/src/decode.test.cpp @@ -75,35 +75,32 @@ TEST_CASE("test_apply_custom_weights") { CHECK_THAT(output, WithinAbs(6.34, 0.001)); } -TEST_CASE("Mask 32 bit unsigned integer to 24 bit"){ - //any number less than 2**24 (16777216) should be the same - CHECK(aare::mask32to24bits(0)==0); - CHECK(aare::mask32to24bits(19)==19); - CHECK(aare::mask32to24bits(29875)==29875); - CHECK(aare::mask32to24bits(1092177)==1092177); - CHECK(aare::mask32to24bits(0xFFFF)==0xFFFF); - CHECK(aare::mask32to24bits(0xFFFFFFFF)==0xFFFFFF); +TEST_CASE("Mask 32 bit unsigned integer to 24 bit") { + // any number less than 2**24 (16777216) should be the same + CHECK(aare::mask32to24bits(0) == 0); + CHECK(aare::mask32to24bits(19) == 19); + CHECK(aare::mask32to24bits(29875) == 29875); + CHECK(aare::mask32to24bits(1092177) == 1092177); + CHECK(aare::mask32to24bits(0xFFFF) == 0xFFFF); + CHECK(aare::mask32to24bits(0xFFFFFFFF) == 0xFFFFFF); // Offset specifies that the should ignore 0-7 bits // at the start - CHECK(aare::mask32to24bits(0xFFFF, BitOffset(4))==0xFFF); - CHECK(aare::mask32to24bits(0xFF0000d9)==0xd9); - CHECK(aare::mask32to24bits(0xFF000d9F, BitOffset(4))==0xF000d9); - CHECK(aare::mask32to24bits(16777217)==1); - 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); - + CHECK(aare::mask32to24bits(0xFFFF, BitOffset(4)) == 0xFFF); + CHECK(aare::mask32to24bits(0xFF0000d9) == 0xd9); + CHECK(aare::mask32to24bits(0xFF000d9F, BitOffset(4)) == 0xF000d9); + CHECK(aare::mask32to24bits(16777217) == 1); + 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); } -TEST_CASE("Expand container with 24 bit data to 32"){ +TEST_CASE("Expand container with 24 bit data to 32") { { uint8_t buffer[] = { - 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; aare::NDView input(&buffer[0], {9}); @@ -116,9 +113,7 @@ TEST_CASE("Expand container with 24 bit data to 32"){ } { uint8_t buffer[] = { - 0x0F, 0x00, 0x00, - 0xFF, 0x00, 0x00, - 0xFF, 0xFF, 0xFF, + 0x0F, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, }; aare::NDView input(&buffer[0], {9}); @@ -131,9 +126,7 @@ TEST_CASE("Expand container with 24 bit data to 32"){ } { uint8_t buffer[] = { - 0x00, 0x00, 0xFF, - 0xFF, 0xFF, 0x00, - 0x00, 0xFF, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0x00, }; aare::NDView 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))); } { - //For use with offset we need an extra byte - uint8_t buffer[] = { - 0x00, 0x00, 0xFF, - 0xFF, 0xFF, 0x00, - 0x00, 0xFF, 0x00, 0x00 - }; + // For use with offset we need an extra byte + uint8_t buffer[] = {0x00, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0xFF, 0x00, 0x00}; aare::NDView input(&buffer[0], {10}); - aare::NDArray out({3}); //still output.size == 3 + aare::NDArray out({3}); // still output.size == 3 aare::expand24to32bit(input, out.view(), BitOffset(4)); CHECK(out(0) == 0xFFF000); CHECK(out(1) == 0xFFF); 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 input(&buffer[0], {6}); + aare::NDArray 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]); + } + } } \ No newline at end of file