From 2a77674adf04bcff3b38473391f95b3ec04b4b2f Mon Sep 17 00:00:00 2001 From: Alice Date: Tue, 1 Sep 2026 15:35:57 +0200 Subject: [PATCH] moved DetectorGeometry, vector of ROIGeometry to master file --- RELEASE.md | 4 + include/aare/DetectorGeometry.hpp | 3 +- include/aare/ROIGeometry.hpp | 5 - include/aare/RawFile.hpp | 11 - include/aare/RawMasterFile.hpp | 30 ++- python/src/raw_master_file.hpp | 7 +- src/ROIGeometry.cpp | 29 ++- src/RawFile.cpp | 348 +++++++----------------------- src/RawFile.test.cpp | 73 +++---- src/RawMasterFile.cpp | 233 +++++++++++++++++--- src/RawMasterFile.test.cpp | 52 ++--- 11 files changed, 384 insertions(+), 411 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 949460aa..5a33f49b 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -16,6 +16,10 @@ - ``NDView`` now converts to ``NDView``; ``expand4to8bit`` and ``expand24to32bit`` accept const input views. +- ``RawMasterFile::geometry()`` is deprecetad and returns full detector geometry information including module geometry. Use +``RawMasterFile::module_layout()`` to get num_modules in x an y +- ``RawMasterFile::rois()`` always returns a list of rois (no optional). Per default it returns a list of one ROI element spawing the entire detector + ### Bugfixes: - Fixed broken reading of old (pre reordering) Moench03 diff --git a/include/aare/DetectorGeometry.hpp b/include/aare/DetectorGeometry.hpp index f5032482..e6718ffd 100644 --- a/include/aare/DetectorGeometry.hpp +++ b/include/aare/DetectorGeometry.hpp @@ -2,7 +2,6 @@ #pragma once #include "aare/ROI.hpp" #include "aare/ROIGeometry.hpp" -#include "aare/RawMasterFile.hpp" //ROI refactor away #include namespace aare { @@ -97,6 +96,8 @@ class DetectorGeometry { const xy udp_interfaces_per_module = xy{1, 1}, const bool quad = false); + DetectorGeometry() = default; + ~DetectorGeometry() = default; /** diff --git a/include/aare/ROIGeometry.hpp b/include/aare/ROIGeometry.hpp index 2efbba2c..739b24c3 100644 --- a/include/aare/ROIGeometry.hpp +++ b/include/aare/ROIGeometry.hpp @@ -19,11 +19,6 @@ class ROIGeometry { */ ROIGeometry(const ROI &roi, DetectorGeometry &geometry); - /** @brief Constructor for ROI geometry expanding over full detector - * @param geometry general detector geometry - */ - ROIGeometry(DetectorGeometry &geometry); - /// @brief Get number of modules in the ROI size_t num_modules_in_roi() const; diff --git a/include/aare/RawFile.hpp b/include/aare/RawFile.hpp index c2cc006c..d9cf0d71 100644 --- a/include/aare/RawFile.hpp +++ b/include/aare/RawFile.hpp @@ -1,6 +1,5 @@ // SPDX-License-Identifier: MPL-2.0 #pragma once -#include "aare/DetectorGeometry.hpp" #include "aare/FileInterface.hpp" #include "aare/Frame.hpp" #include "aare/NDArray.hpp" //for pixel map @@ -30,9 +29,6 @@ class RawFile : public FileInterface { RawMasterFile m_master; size_t m_current_frame{}; - DetectorGeometry m_geometry; - - /// @brief Geometries e.g. number of modules, size etc. for each ROI std::vector m_ROI_geometries; /// @brief total number of frames in file @@ -43,7 +39,6 @@ class RawFile : public FileInterface { * @brief RawFile constructor * @param fname path to the master file (.json) * @param mode file mode (only "r" is supported at the moment) - */ RawFile(const std::filesystem::path &fname, const std::string &mode = "r"); virtual ~RawFile() override = default; @@ -172,12 +167,6 @@ class RawFile : public FileInterface { Frame get_frame(size_t frame_index, const size_t roi_index = 0); void open_subfiles(const size_t roi_index); - - /** - * @brief set the ROIs in master file. - * @param rois vector of ROIs to set in the RawMasterFile. - */ - void set_ROIs(const std::vector &rois); }; } // namespace aare diff --git a/include/aare/RawMasterFile.hpp b/include/aare/RawMasterFile.hpp index 0b48d657..79f6a425 100644 --- a/include/aare/RawMasterFile.hpp +++ b/include/aare/RawMasterFile.hpp @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 #pragma once +#include "aare/DetectorGeometry.hpp" #include "aare/ROI.hpp" #include #include @@ -13,8 +14,6 @@ using json = nlohmann::json; namespace aare { -class RawFile; // forward declaration - /** * @brief Implementation used in RawMasterFile to parse the file name */ @@ -90,7 +89,8 @@ class RawMasterFile { std::optional m_exptime; std::chrono::nanoseconds m_period{0}; - xy m_geometry{}; + /// @brief modules in x and y direction + xy m_detector_layout{}; xy m_udp_interfaces_per_module{1, 1}; size_t m_max_frames_per_file{}; @@ -118,7 +118,14 @@ class RawMasterFile { std::optional> m_udp_port_types{}; // TODO: UDPPortType? - string_to conversion? - std::optional> m_rois; + /// @brief ROIs defined in master file or derived from disabled UDP ports + std::vector m_rois; + + /// @brief Detector geometry - geometry for each module + DetectorGeometry m_geometry{}; + + /// @brief ROI geometries + std::vector m_ROI_geometries; public: RawMasterFile(const std::filesystem::path &fpath); @@ -140,10 +147,14 @@ class RawMasterFile { const FrameDiscardPolicy &frame_discard_policy() const; size_t total_frames_expected() const; - xy geometry() const; + xy detector_layout() const; size_t n_modules() const; uint8_t quad() const; + const DetectorGeometry &geometry() const; + + const std::vector &roi_geometries() const; + ReadoutMode get_reading_mode() const; std::optional analog_samples() const; @@ -162,9 +173,11 @@ class RawMasterFile { /// for masterfile version >= 8.1) std::optional> disabled_udp_ports() const; - std::optional> rois() const; + std::vector rois() const; - std::optional roi() const; + /// @brief get roi for the case of a single ROI + /// @return ROI object (complete ROI if no roi present in master file) + ROI roi() const; ScanParameters scan_parameters() const; @@ -176,9 +189,8 @@ class RawMasterFile { private: void parse_json(std::istream &is); void parse_raw(std::istream &is); + void update_rois_from_disabled_udp_ports(); void retrieve_geometry(); - - friend class RawFile; }; } // namespace aare \ No newline at end of file diff --git a/python/src/raw_master_file.hpp b/python/src/raw_master_file.hpp index fe248823..0feb857f 100644 --- a/python/src/raw_master_file.hpp +++ b/python/src/raw_master_file.hpp @@ -66,7 +66,8 @@ void define_raw_master_file_bindings(py::module &m) { .def_property_readonly("total_frames_expected", &RawMasterFile::total_frames_expected) - .def_property_readonly("geometry", &RawMasterFile::geometry) + .def_property_readonly("detector_layout", + &RawMasterFile::detector_layout) .def_property_readonly("udp_interfaces_per_module", &RawMasterFile::udp_interfaces_per_module) .def_property_readonly("analog_samples", &RawMasterFile::analog_samples, @@ -112,8 +113,8 @@ void define_raw_master_file_bindings(py::module &m) { Returns ---------- - Optional[List[ROI]] - Optional vector of ROIs (only present for masterfile version >= 8.1) + List[ROI] + List of ROIs (default complete ROI) )") .def_property_readonly("udp_port_types", &RawMasterFile::udp_port_types, R"( diff --git a/src/ROIGeometry.cpp b/src/ROIGeometry.cpp index 04cf9187..f61f9079 100644 --- a/src/ROIGeometry.cpp +++ b/src/ROIGeometry.cpp @@ -5,25 +5,24 @@ namespace aare { ROIGeometry::ROIGeometry(const ROI &roi, DetectorGeometry &geometry) : m_pixels_x(roi.width()), m_pixels_y(roi.height()), m_geometry(geometry) { - m_module_indices_in_roi.reserve(m_geometry.n_modules()); - // determine which modules are in the roi - for (size_t i = 0; i < m_geometry.n_modules(); ++i) { - auto &module_geometry = m_geometry.get_module_geometries(i); - if (module_geometry.module_in_roi(roi)) { - module_geometry.update_geometry_with_roi(roi); - m_module_indices_in_roi.push_back(i); + + if (complete_ROI({roi}, geometry)) { + m_module_indices_in_roi.resize(m_geometry.n_modules()); + std::iota(m_module_indices_in_roi.begin(), + m_module_indices_in_roi.end(), 0); + } else { + m_module_indices_in_roi.reserve(m_geometry.n_modules()); + // determine which modules are in the roi + for (size_t i = 0; i < m_geometry.n_modules(); ++i) { + auto &module_geometry = m_geometry.get_module_geometries(i); + if (module_geometry.module_in_roi(roi)) { + module_geometry.update_geometry_with_roi(roi); + m_module_indices_in_roi.push_back(i); + } } } } -ROIGeometry::ROIGeometry(DetectorGeometry &geometry) - : m_pixels_x(geometry.pixels_x()), m_pixels_y(geometry.pixels_y()), - m_geometry(geometry) { - m_module_indices_in_roi.resize(m_geometry.n_modules()); - std::iota(m_module_indices_in_roi.begin(), m_module_indices_in_roi.end(), - 0); -} - size_t ROIGeometry::num_modules_in_roi() const { return m_module_indices_in_roi.size(); } diff --git a/src/RawFile.cpp b/src/RawFile.cpp index bd556adf..8040da7e 100644 --- a/src/RawFile.cpp +++ b/src/RawFile.cpp @@ -16,212 +16,25 @@ using json = nlohmann::json; namespace aare { -/** - * @brief Get ROIs from disabled UDP ports - * @param disabled_ports vector of disabled UDP ports - * @param udp_port_types vector of UDP port types (e.g. "left", "right", "top", - * "bottom") - * @param pixels_per_module_x number of pixels per module in x direction - * @param pixels_per_module_y number of pixels per module in y direction - * @param modules_x number of modules in x direction - * @param modules_y number of modules in y direction - * @return vector of ROIs corresponding to the disabled UDP ports - */ -std::vector -get_rois_from_disabled_udp_ports(std::vector &disabled_ports, - std::vector &udp_port_types, - const DetectorGeometry &geometry) { - - const size_t num_udp_port_types = udp_port_types.size(); - - size_t first_port = disabled_ports[0] % num_udp_port_types; - - bool all_ports_equal = - std::all_of(disabled_ports.begin(), disabled_ports.end(), - [&num_udp_port_types, first_port](size_t &port) { - return port % num_udp_port_types == first_port; - }); - - std::vector rois; - - const ssize_t udp_ports_per_module = - geometry.udp_interfaces_per_module().col * - geometry.udp_interfaces_per_module().row; - - bool port_disabled_for_all_modules = - disabled_ports.size() == - geometry.modules_x() * geometry.modules_y() / udp_ports_per_module; - - if (all_ports_equal && port_disabled_for_all_modules) { - - if (udp_port_types[first_port] == "left") { - const size_t num_rois = geometry.modules_x() / udp_ports_per_module; - rois.resize(num_rois); - - const ssize_t pixels_per_module_x = - geometry.pixels_x() / geometry.modules_x(); - - std::generate( - rois.begin(), rois.end(), - [n = 0, &geometry, pixels_per_module_x, - udp_ports_per_module]() mutable { - ssize_t idx = n++; - return ROI{ - idx * udp_ports_per_module * pixels_per_module_x + - pixels_per_module_x, - idx * udp_ports_per_module * pixels_per_module_x + - 2 * pixels_per_module_x, - 0, static_cast(geometry.pixels_y())}; - }); - } - if (udp_port_types[first_port] == "right") { - const size_t num_rois = geometry.modules_x() / udp_ports_per_module; - rois.resize(num_rois); - - const ssize_t pixels_per_module_x = - geometry.pixels_x() / geometry.modules_x(); - - std::generate( - rois.begin(), rois.end(), - [n = 0, geometry, pixels_per_module_x, - udp_ports_per_module]() mutable { - ssize_t idx = n++; - return ROI{idx * udp_ports_per_module * pixels_per_module_x, - idx * udp_ports_per_module * - pixels_per_module_x + - pixels_per_module_x, - 0, static_cast(geometry.pixels_y())}; - }); - } - if (udp_port_types[first_port] == "top") { - size_t num_rois = geometry.modules_y() / udp_ports_per_module; - rois.resize(num_rois); - // assumes euclidean coordinate system with origin at bottom - // left corner of the detector - const ssize_t pixels_per_module_y = - geometry.pixels_y() / geometry.modules_y(); - std::generate( - rois.begin(), rois.end(), - [n = 0, &geometry, pixels_per_module_y, - udp_ports_per_module]() mutable { - ssize_t idx = n++; - return ROI{0, static_cast(geometry.pixels_x()), - idx * udp_ports_per_module * pixels_per_module_y, - idx * udp_ports_per_module * - pixels_per_module_y + - pixels_per_module_y}; - }); - } - if (udp_port_types[first_port] == "bottom") { - size_t num_rois = geometry.modules_y() / udp_ports_per_module; - rois.resize(num_rois); - const ssize_t pixels_per_module_y = - geometry.pixels_y() / geometry.modules_y(); - std::generate( - rois.begin(), rois.end(), - [n = 0, &geometry, pixels_per_module_y, - udp_ports_per_module]() mutable { - ssize_t idx = n++; - return ROI{ - 0, static_cast(geometry.pixels_x()), - idx * udp_ports_per_module * pixels_per_module_y + - pixels_per_module_y, - idx * udp_ports_per_module * pixels_per_module_y + - 2 * pixels_per_module_y}; - }); - } - } else { - // iterate over all ports and create ROIs for each disabled port - LOG(logDEBUG) << "Creating ROIs from disabled UDP ports"; - // get the enabled ones: - std::vector enabled_ports(geometry.n_modules()); - std::iota(enabled_ports.begin(), enabled_ports.end(), 0); - std::for_each(disabled_ports.begin(), disabled_ports.end(), - [&enabled_ports](size_t &port) { - enabled_ports.erase(std::remove(enabled_ports.begin(), - enabled_ports.end(), - port), - enabled_ports.end()); - }); - - rois.reserve(enabled_ports.size()); - for (const auto enabled_port : enabled_ports) { - - auto module_geometry = geometry.get_module_geometries(enabled_port); - rois.push_back( - ROI{module_geometry.origin_x, - module_geometry.origin_x + module_geometry.width, - module_geometry.origin_y, - module_geometry.origin_y + module_geometry.height}); - } - - if (udp_port_types == std::vector{"left", "right"}) { - rois = merge_consecutive_rois(rois); - } else if (udp_port_types == - std::vector{"bottom", "top"} || - udp_port_types == - std::vector{"top", "bottom"}) { - rois = merge_consecutive_rois(rois); - } else { - throw std::runtime_error(LOCATION + "Unsupported UDP port types"); - } - } - - return rois; -} - RawFile::RawFile(const std::filesystem::path &fname, const std::string &mode) - : m_master(fname), - m_geometry(m_master.geometry(), m_master.pixels_x(), m_master.pixels_y(), - m_master.udp_interfaces_per_module(), m_master.quad()), - m_frames_in_file(m_master.frames_in_file()) { + : m_master(fname), m_frames_in_file(m_master.frames_in_file()) { m_mode = mode; if (mode == "r") { - if (m_master.rois().has_value() && - !complete_ROI(m_master.rois().value(), m_geometry)) { - LOG(logDEBUG) - << "ROIs defined in master file. Creating subfiles for " - "each ROI."; - m_ROI_geometries.reserve(m_master.rois()->size()); + m_subfiles.resize(m_master.roi_geometries().size()); + // iterate over all ROIS + const size_t num_rois = m_master.roi_geometries().size(); - m_subfiles.resize(m_master.rois()->size()); - // iterate over all ROIS - size_t roi_index = 0; - const auto rois = m_master.rois().value(); - for (const auto &roi : rois) { - m_ROI_geometries.push_back(ROIGeometry(roi, m_geometry)); - // open subfiles - open_subfiles(roi_index); - ++roi_index; - } - } else if (m_master.disabled_udp_ports().has_value() && - m_master.disabled_udp_ports().value().size() > 0) { - - LOG(logDEBUG) << "Disabled UDP ports defined in master file. " - "Creating ROIs from disabled UDP ports."; - - auto disabled_ports = m_master.disabled_udp_ports().value(); - - auto udp_port_types = m_master.udp_port_types().value(); - - std::vector rois = get_rois_from_disabled_udp_ports( - disabled_ports, udp_port_types, m_geometry); - - set_ROIs(rois); - - m_subfiles.resize(rois.size()); - - m_ROI_geometries.reserve(rois.size()); - - for (size_t roi_index = 0; roi_index < rois.size(); ++roi_index) { - m_ROI_geometries.push_back( - ROIGeometry(rois[roi_index], m_geometry)); - open_subfiles(roi_index); - } + for (size_t roi_index = 0; roi_index < num_rois; ++roi_index) { + // open subfiles + open_subfiles(roi_index); + } + // TODO: work around for now - retrieve num_frames from subfiles + if (m_master.disabled_udp_ports().has_value() && + m_master.disabled_udp_ports().value().size() > 0) { // TODO: remove frames_from_file from master file? // retrieve the frame numbers from subfile as frames per file in // master file 0 if dataprocessor 0 was disabled @@ -243,12 +56,6 @@ RawFile::RawFile(const std::filesystem::path &fname, const std::string &mode) min_subfiles_per_roi.end()); LOG(logDEBUG) << "Frames in file: " << m_frames_in_file; - } else { - // no ROI use full detector - m_subfiles.resize(1); - m_ROI_geometries.reserve(1); - m_ROI_geometries.push_back(ROIGeometry(m_geometry)); - open_subfiles(0); } } else { throw std::runtime_error(LOCATION + @@ -258,24 +65,14 @@ RawFile::RawFile(const std::filesystem::path &fname, const std::string &mode) Frame RawFile::read_roi(const size_t roi_index) { - if (!m_master.rois()) { - throw std::runtime_error(LOCATION + - "No ROIs defined in the master file."); - } - if (roi_index >= m_ROI_geometries.size()) { + if (roi_index >= m_master.roi_geometries().size()) { throw std::runtime_error(LOCATION + "ROI index out of range."); } return get_frame(m_current_frame++, roi_index); } std::vector RawFile::read_rois() { - - if (!m_master.rois()) { - throw std::runtime_error(LOCATION + - "No ROIs defined in the master file."); - } - - const size_t num_rois = m_ROI_geometries.size(); + const size_t num_rois = m_master.roi_geometries().size(); std::vector frames; frames.reserve(num_rois); @@ -289,7 +86,7 @@ std::vector RawFile::read_rois() { } Frame RawFile::read_frame() { - if (m_ROI_geometries.size() > 1) { + if (m_master.roi_geometries().size() > 1) { throw std::runtime_error(LOCATION + "Multiple ROIs present in file. " "Use read_ROIs() instead."); } @@ -297,7 +94,7 @@ Frame RawFile::read_frame() { } Frame RawFile::read_frame(size_t frame_number) { - if (m_ROI_geometries.size() > 1) { + if (m_master.roi_geometries().size() > 1) { throw std::runtime_error( LOCATION + "Multiple ROIs present in file. " "Use read_ROIs(const size_t frame_number) instead."); @@ -308,7 +105,7 @@ Frame RawFile::read_frame(size_t frame_number) { void RawFile::read_into(std::byte *image_buf, size_t n_frames) { // TODO: implement this in a more efficient way - if (m_ROI_geometries.size() > 1) { + if (m_master.roi_geometries().size() > 1) { throw std::runtime_error(LOCATION + "Cannot use read_into for multiple ROIs."); } @@ -320,7 +117,7 @@ void RawFile::read_into(std::byte *image_buf, size_t n_frames) { } void RawFile::read_into(std::byte *image_buf) { - if (m_ROI_geometries.size() > 1) { + if (m_master.roi_geometries().size() > 1) { throw std::runtime_error(LOCATION + "Cannot use read_into for multiple ROIs. Use " "read_roi_into() for a single ROI instead."); @@ -330,10 +127,6 @@ void RawFile::read_into(std::byte *image_buf) { void RawFile::read_roi_into(std::byte *image_buf, const size_t roi_index, const size_t frame_number, DetectorHeader *header) { - if (m_ROI_geometries.size() <= 1) { - throw std::runtime_error(LOCATION + - "No ROIs defined in the master file."); - } if (roi_index >= num_rois()) { throw std::runtime_error(LOCATION + "ROI index out of range."); } @@ -341,7 +134,7 @@ void RawFile::read_roi_into(std::byte *image_buf, const size_t roi_index, } void RawFile::read_into(std::byte *image_buf, DetectorHeader *header) { - if (m_ROI_geometries.size() > 1) { + if (m_master.roi_geometries().size() > 1) { throw std::runtime_error(LOCATION + "Cannot use read_into for multiple ROIs. Use " "read_roi_into() for a single ROI instead."); @@ -353,7 +146,7 @@ void RawFile::read_into(std::byte *image_buf, size_t n_frames, DetectorHeader *header) { // return get_frame_into(m_current_frame++, image_buf, header); - if (m_ROI_geometries.size() > 1) { + if (m_master.roi_geometries().size() > 1) { throw std::runtime_error( LOCATION + "Cannot use read_into for multiple ROIs."); // TODO: maybe @@ -369,12 +162,12 @@ void RawFile::read_into(std::byte *image_buf, size_t n_frames, this->get_frame_into(m_current_frame++, image_buf, 0, header); image_buf += bytes_per_frame(); if (header) - header += m_ROI_geometries[0].num_modules_in_roi(); + header += m_master.roi_geometries()[0].num_modules_in_roi(); } } size_t RawFile::bytes_per_frame() { - if (m_ROI_geometries.size() > 1) { + if (m_master.roi_geometries().size() > 1) { throw std::runtime_error( LOCATION + "Pass the desired roi_index to bytes_per_frame to get " "bytes_per_frame for the specific ROI. "); @@ -383,13 +176,13 @@ size_t RawFile::bytes_per_frame() { } size_t RawFile::bytes_per_frame(const size_t roi_index) { - return m_ROI_geometries.at(roi_index).pixels_x() * - m_ROI_geometries.at(roi_index).pixels_y() * m_master.bitdepth() / - bits_per_byte; + return m_master.roi_geometries().at(roi_index).pixels_x() * + m_master.roi_geometries().at(roi_index).pixels_y() * + m_master.bitdepth() / bits_per_byte; } size_t RawFile::pixels_per_frame() { - if (m_ROI_geometries.size() > 1) { + if (m_master.roi_geometries().size() > 1) { throw std::runtime_error( LOCATION + "Pass the desired roi_index to pixels_per_frame to get " "pixels_per_frame for the specific ROI. "); @@ -398,8 +191,8 @@ size_t RawFile::pixels_per_frame() { } size_t RawFile::pixels_per_frame(const size_t roi_index) { - return m_ROI_geometries.at(roi_index).pixels_x() * - m_ROI_geometries.at(roi_index).pixels_y(); + return m_master.roi_geometries().at(roi_index).pixels_x() * + m_master.roi_geometries().at(roi_index).pixels_y(); } DetectorType RawFile::detector_type() const { return m_master.detector_type(); } @@ -421,7 +214,7 @@ size_t RawFile::tell() { return m_current_frame; } size_t RawFile::total_frames() const { return m_frames_in_file; } size_t RawFile::rows() const { - if (m_ROI_geometries.size() > 1) { + if (m_master.roi_geometries().size() > 1) { throw std::runtime_error(LOCATION + "Pass the desired roi_index to rows to get " "rows for the specific ROI. "); @@ -429,10 +222,10 @@ size_t RawFile::rows() const { return rows(0); } size_t RawFile::rows(const size_t roi_index) const { - return m_ROI_geometries.at(roi_index).pixels_y(); + return m_master.roi_geometries().at(roi_index).pixels_y(); } size_t RawFile::cols() const { - if (m_ROI_geometries.size() > 1) { + if (m_master.roi_geometries().size() > 1) { throw std::runtime_error(LOCATION + "Pass the desired roi_index to cols to get " "cols for the specific ROI. "); @@ -440,27 +233,26 @@ size_t RawFile::cols() const { return cols(0); } size_t RawFile::cols(const size_t roi_index) const { - return m_ROI_geometries.at(roi_index).pixels_x(); + return m_master.roi_geometries().at(roi_index).pixels_x(); } size_t RawFile::bitdepth() const { return m_master.bitdepth(); } -xy RawFile::geometry() const { - return xy{static_cast(m_geometry.modules_y()), - static_cast(m_geometry.modules_x())}; -} -size_t RawFile::n_modules() const { return m_geometry.n_modules(); }; +xy RawFile::geometry() const { return m_master.detector_layout(); } -size_t RawFile::num_rois() const { return m_ROI_geometries.size(); } +size_t RawFile::n_modules() const { return m_master.n_modules(); }; + +size_t RawFile::num_rois() const { return m_master.roi_geometries().size(); } const ROIGeometry &RawFile::roi_geometries(size_t roi_index) const { - return m_ROI_geometries[roi_index]; + return m_master.roi_geometries().at(roi_index); } std::vector RawFile::n_modules_in_roi() const { - std::vector results(m_ROI_geometries.size()); + std::vector results(m_master.roi_geometries().size()); std::transform( - m_ROI_geometries.begin(), m_ROI_geometries.end(), results.begin(), + m_master.roi_geometries().begin(), m_master.roi_geometries().end(), + results.begin(), [](const ROIGeometry &roi) { return roi.num_modules_in_roi(); }); return results; } @@ -469,13 +261,13 @@ void RawFile::open_subfiles(const size_t roi_index) { if (m_mode == "r") { m_subfiles[roi_index].reserve( - m_ROI_geometries[roi_index].num_modules_in_roi()); + m_master.roi_geometries().at(roi_index).num_modules_in_roi()); auto module_indices = - m_ROI_geometries[roi_index].module_indices_in_roi(); + m_master.roi_geometries().at(roi_index).module_indices_in_roi(); for (const size_t i : module_indices) { - const auto pos = m_geometry.get_module_geometries(i); + const auto pos = m_master.geometry().get_module_geometries(i); m_subfiles[roi_index].emplace_back(std::make_unique( m_master.data_fname(i, 0), m_master.detector_type(), pos.height, pos.width, m_master.bitdepth(), pos.row_index, pos.col_index)); @@ -506,8 +298,8 @@ DetectorHeader RawFile::read_header(const std::filesystem::path &fname) { RawMasterFile RawFile::master() const { return m_master; } Frame RawFile::get_frame(size_t frame_index, const size_t roi_index) { - auto f = Frame(m_ROI_geometries[roi_index].pixels_y(), - m_ROI_geometries[roi_index].pixels_x(), + auto f = Frame(m_master.roi_geometries().at(roi_index).pixels_y(), + m_master.roi_geometries().at(roi_index).pixels_x(), Dtype::from_bitdepth(m_master.bitdepth())); std::byte *frame_buffer = f.data(); get_frame_into(frame_index, frame_buffer, roi_index); @@ -523,16 +315,18 @@ void RawFile::get_frame_into(size_t frame_index, std::byte *frame_buffer, throw std::runtime_error(LOCATION + "Frame number out of range"); } std::vector frame_numbers( - m_ROI_geometries[roi_index].num_modules_in_roi()); + m_master.roi_geometries().at(roi_index).num_modules_in_roi()); std::vector frame_indices( - m_ROI_geometries[roi_index].num_modules_in_roi(), frame_index); + m_master.roi_geometries().at(roi_index).num_modules_in_roi(), + frame_index); // sync the frame numbers - if (m_ROI_geometries[roi_index].num_modules_in_roi() != + if (m_master.roi_geometries().at(roi_index).num_modules_in_roi() != 1) { // if we have more than one module for (size_t part_idx = 0; - part_idx != m_ROI_geometries[roi_index].num_modules_in_roi(); + part_idx != + m_master.roi_geometries().at(roi_index).num_modules_in_roi(); ++part_idx) { frame_numbers[part_idx] = m_subfiles[roi_index][part_idx]->frame_number(frame_index); @@ -562,32 +356,35 @@ void RawFile::get_frame_into(size_t frame_index, std::byte *frame_buffer, } } - if (m_master.geometry().col == 1) { + if (m_master.detector_layout().col == 1) { // get the part from each subfile and copy it to the frame for (size_t part_idx = 0; - part_idx != m_ROI_geometries[roi_index].num_modules_in_roi(); + part_idx != + m_master.roi_geometries().at(roi_index).num_modules_in_roi(); ++part_idx) { auto corrected_idx = frame_indices[part_idx]; // This is where we start writing auto offset = - (m_geometry + (m_master.geometry() .get_module_geometries( - m_ROI_geometries[roi_index].module_indices_in_roi( - part_idx)) + m_master.roi_geometries() + .at(roi_index) + .module_indices_in_roi(part_idx)) .origin_y * - m_ROI_geometries[roi_index].pixels_x() + - m_geometry + m_master.roi_geometries().at(roi_index).pixels_x() + + m_master.geometry() .get_module_geometries( - m_ROI_geometries[roi_index].module_indices_in_roi( - part_idx)) + m_master.roi_geometries() + .at(roi_index) + .module_indices_in_roi(part_idx)) .origin_x) * m_master.bitdepth() / 8; - if (m_geometry - .get_module_geometries( - m_ROI_geometries[roi_index].module_indices_in_roi( - part_idx)) + if (m_master.geometry() + .get_module_geometries(m_master.roi_geometries() + .at(roi_index) + .module_indices_in_roi(part_idx)) .origin_x != 0) throw std::runtime_error( LOCATION + @@ -620,10 +417,12 @@ void RawFile::get_frame_into(size_t frame_index, std::byte *frame_buffer, // the module level for (size_t part_idx = 0; - part_idx != m_ROI_geometries[roi_index].num_modules_in_roi(); + part_idx != + m_master.roi_geometries().at(roi_index).num_modules_in_roi(); ++part_idx) { - auto pos = m_geometry.get_module_geometries( - m_ROI_geometries[roi_index].module_indices_in_roi(part_idx)); + auto pos = m_master.geometry().get_module_geometries( + m_master.roi_geometries().at(roi_index).module_indices_in_roi( + part_idx)); auto corrected_idx = frame_indices[part_idx]; m_subfiles[roi_index][part_idx]->seek(corrected_idx); @@ -637,7 +436,8 @@ void RawFile::get_frame_into(size_t frame_index, std::byte *frame_buffer, auto irow = (pos.origin_y + cur_row); auto icol = pos.origin_x; auto dest = - (irow * m_ROI_geometries[roi_index].pixels_x() + icol); + (irow * m_master.roi_geometries().at(roi_index).pixels_x() + + icol); dest = dest * m_master.bitdepth() / 8; memcpy(frame_buffer + dest, part_buffer + @@ -691,6 +491,4 @@ size_t RawFile::frame_number(size_t frame_index) { return m_subfiles[0][0]->frame_number(frame_index); } -void RawFile::set_ROIs(const std::vector &rois) { m_master.m_rois = rois; } - } // namespace aare diff --git a/src/RawFile.test.cpp b/src/RawFile.test.cpp index e922dae2..6974272a 100644 --- a/src/RawFile.test.cpp +++ b/src/RawFile.test.cpp @@ -264,8 +264,9 @@ TEST_CASE("check find_geometry", "[.with-data][RawFile]") { RawMasterFile master_file(fpath); auto geometry = DetectorGeometry( - master_file.geometry(), master_file.pixels_x(), master_file.pixels_y(), - master_file.udp_interfaces_per_module(), master_file.quad()); + master_file.detector_layout(), master_file.pixels_x(), + master_file.pixels_y(), master_file.udp_interfaces_per_module(), + master_file.quad()); CHECK(geometry.modules_x() == test_parameters.modules_x); CHECK(geometry.modules_y() == test_parameters.modules_y); @@ -309,8 +310,8 @@ TEST_CASE("Open multi module file with ROI", RawFile f(fpath, "r"); SECTION("read 2 frames") { - REQUIRE(f.master().roi().value().width() == 256); - REQUIRE(f.master().roi().value().height() == 256); + REQUIRE(f.master().roi().width() == 256); + REQUIRE(f.master().roi().height() == 256); CHECK(f.n_modules() == 2); @@ -401,8 +402,8 @@ TEST_CASE("Read Mythenframe", "[.with-data][RawFile]") { auto fpath = test_data_path() / "raw/newmythen03/run_2_master_1.json"; REQUIRE(std::filesystem::exists(fpath)); RawFile f(fpath); - REQUIRE(f.master().roi().value().width() == 2560); - REQUIRE(f.master().roi().value().height() == 1); + REQUIRE(f.master().roi().width() == 2560); + REQUIRE(f.master().roi().height() == 1); auto frame = f.read_frame(); REQUIRE(frame.cols() == 2560); } @@ -424,8 +425,8 @@ TEST_CASE("Read Jungfrau frame with disabled UDP ports", auto rois = f.master().rois(); - REQUIRE(rois.value().size() == 1); - REQUIRE(rois.value()[0] == ROI{0, 1024, 0, 256}); + REQUIRE(rois.size() == 1); + REQUIRE(rois[0] == ROI{0, 1024, 0, 256}); } SECTION("disabled bottom port") { @@ -441,8 +442,8 @@ TEST_CASE("Read Jungfrau frame with disabled UDP ports", REQUIRE(frame.cols() == 1024); REQUIRE(frame.rows() == 256); auto rois = f.master().rois(); - REQUIRE(rois.value().size() == 1); - REQUIRE(rois.value()[0] == ROI{0, 1024, 256, 512}); + REQUIRE(rois.size() == 1); + REQUIRE(rois[0] == ROI{0, 1024, 256, 512}); } SECTION("2 modules - top ports disabled") { auto fpath = test_data_path() / "raw/jungfrau" / @@ -468,9 +469,9 @@ TEST_CASE("Read Jungfrau frame with disabled UDP ports", REQUIRE(frame[1].cols() == 1024); REQUIRE(frame[1].rows() == 256); auto rois = f.master().rois(); - REQUIRE(rois.value().size() == 2); - REQUIRE(rois.value()[0] == ROI{0, 1024, 0, 256}); - REQUIRE(rois.value()[1] == ROI{0, 1024, 512, 768}); + REQUIRE(rois.size() == 2); + REQUIRE(rois[0] == ROI{0, 1024, 0, 256}); + REQUIRE(rois[1] == ROI{0, 1024, 512, 768}); } SECTION("2 modules - top ports disabled - bottom port disabled") { auto fpath = test_data_path() / "raw/jungfrau" / @@ -485,8 +486,8 @@ TEST_CASE("Read Jungfrau frame with disabled UDP ports", REQUIRE(frame.cols() == 1024); REQUIRE(frame.rows() == 512); auto rois = f.master().rois(); - REQUIRE(rois.value().size() == 1); - REQUIRE(rois.value()[0] == ROI{0, 1024, 256, 768}); + REQUIRE(rois.size() == 1); + REQUIRE(rois[0] == ROI{0, 1024, 256, 768}); } SECTION("4 modules- mixed ports disabled") { auto fpath = test_data_path() / "raw/jungfrau" / @@ -513,11 +514,11 @@ TEST_CASE("Read Jungfrau frame with disabled UDP ports", REQUIRE(frames[3].cols() == 1024); REQUIRE(frames[3].rows() == 256); auto rois = f.master().rois(); - REQUIRE(rois.value().size() == 4); - REQUIRE(rois.value()[0] == ROI{0, 1024, 0, 256}); - REQUIRE(rois.value()[1] == ROI{0, 1024, 512, 768}); - REQUIRE(rois.value()[2] == ROI{1024, 2048, 256, 512}); - REQUIRE(rois.value()[3] == ROI{1024, 2048, 768, 1024}); + REQUIRE(rois.size() == 4); + REQUIRE(rois[0] == ROI{0, 1024, 0, 256}); + REQUIRE(rois[1] == ROI{0, 1024, 512, 768}); + REQUIRE(rois[2] == ROI{1024, 2048, 256, 512}); + REQUIRE(rois[3] == ROI{1024, 2048, 768, 1024}); } } @@ -537,8 +538,8 @@ TEST_CASE("Read Moench frame with disabled UDP ports", REQUIRE(frame.rows() == 200); auto rois = f.master().rois(); - REQUIRE(rois.value().size() == 1); - REQUIRE(rois.value()[0] == ROI{0, 400, 0, 200}); + REQUIRE(rois.size() == 1); + REQUIRE(rois[0] == ROI{0, 400, 0, 200}); } SECTION("disabled bottom port") { @@ -555,8 +556,8 @@ TEST_CASE("Read Moench frame with disabled UDP ports", REQUIRE(frame.rows() == 200); auto rois = f.master().rois(); - REQUIRE(rois.value().size() == 1); - REQUIRE(rois.value()[0] == ROI{0, 400, 200, 400}); + REQUIRE(rois.size() == 1); + REQUIRE(rois[0] == ROI{0, 400, 200, 400}); } } @@ -577,8 +578,8 @@ TEST_CASE("Read Eiger frame with disabled UDP ports", REQUIRE(frame.rows() == 512); auto rois = f.master().rois(); - REQUIRE(rois.value().size() == 1); - REQUIRE(rois.value()[0] == ROI{512, 1024, 0, 512}); + REQUIRE(rois.size() == 1); + REQUIRE(rois[0] == ROI{512, 1024, 0, 512}); } SECTION("disabled right port") { auto fpath = test_data_path() / "raw/eiger" / @@ -593,8 +594,8 @@ TEST_CASE("Read Eiger frame with disabled UDP ports", REQUIRE(frame.cols() == 512); REQUIRE(frame.rows() == 512); auto rois = f.master().rois(); - REQUIRE(rois.value().size() == 1); - REQUIRE(rois.value()[0] == ROI{0, 512, 0, 512}); + REQUIRE(rois.size() == 1); + REQUIRE(rois[0] == ROI{0, 512, 0, 512}); } SECTION("2 full modules stacked vertically - right ports disabled") { auto fpath = test_data_path() / "raw/eiger" / @@ -618,9 +619,9 @@ TEST_CASE("Read Eiger frame with disabled UDP ports", REQUIRE(frames[1].cols() == 512); REQUIRE(frames[1].rows() == 512); auto rois = f.master().rois(); - REQUIRE(rois.value().size() == 2); - REQUIRE(rois.value()[0] == ROI{0, 512, 0, 512}); - REQUIRE(rois.value()[1] == ROI{1024, 1536, 0, 512}); + REQUIRE(rois.size() == 2); + REQUIRE(rois[0] == ROI{0, 512, 0, 512}); + REQUIRE(rois[1] == ROI{1024, 1536, 0, 512}); } SECTION("quad module - bottom port disabled") { auto fpath = test_data_path() / "raw/eiger" / @@ -636,8 +637,8 @@ TEST_CASE("Read Eiger frame with disabled UDP ports", REQUIRE(frame.cols() == 512); REQUIRE(frame.rows() == 256); auto rois = f.master().rois(); - REQUIRE(rois.value().size() == 1); - REQUIRE(rois.value()[0] == ROI{0, 512, 256, 512}); + REQUIRE(rois.size() == 1); + REQUIRE(rois[0] == ROI{0, 512, 256, 512}); } SECTION("only one port disabled") { auto fpath = test_data_path() / "raw/eiger" / @@ -656,9 +657,9 @@ TEST_CASE("Read Eiger frame with disabled UDP ports", REQUIRE(frame[1].cols() == 1024); REQUIRE(frame[1].rows() == 256); auto rois = f.master().rois(); - REQUIRE(rois.value().size() == 2); + REQUIRE(rois.size() == 2); - REQUIRE(rois.value()[0] == ROI{512, 1024, 0, 256}); - REQUIRE(rois.value()[1] == ROI{0, 1024, 256, 512}); + REQUIRE(rois[0] == ROI{512, 1024, 0, 256}); + REQUIRE(rois[1] == ROI{0, 1024, 256, 512}); } } diff --git a/src/RawMasterFile.cpp b/src/RawMasterFile.cpp index cdd5d7e6..f2abd419 100644 --- a/src/RawMasterFile.cpp +++ b/src/RawMasterFile.cpp @@ -116,9 +116,30 @@ RawMasterFile::RawMasterFile(const std::filesystem::path &fpath) throw std::runtime_error(LOCATION + "Unsupported file type"); } + m_geometry = DetectorGeometry(m_detector_layout, m_pixels_x, m_pixels_y, + m_udp_interfaces_per_module, m_quad); + if (m_quad == 1 && m_udp_port_types.has_value()) { m_udp_port_types.value() = {"top", "bottom"}; } + + if (m_disabled_udp_ports.has_value()) { + // ROI takes precedence over disabled UDP ports, if both are defined in + // the master file + if (!complete_ROI(m_rois, m_geometry)) { + LOG(logWARNING) + << "ROI and disabled UDP ports defined in master file. ROI " + "will be used and disabled UDP ports will be ignored."; + } else { + update_rois_from_disabled_udp_ports(); + } + } + + m_ROI_geometries.reserve(m_rois.size()); + + for (const auto &roi : m_rois) { + m_ROI_geometries.push_back(ROIGeometry(roi, m_geometry)); + } } RawMasterFile::RawMasterFile(std::istream &is, const std::string &fname) @@ -168,10 +189,16 @@ std::optional RawMasterFile::counter_mask() const { return m_counter_mask; } -xy RawMasterFile::geometry() const { return m_geometry; } +xy RawMasterFile::detector_layout() const { return m_detector_layout; } + +const DetectorGeometry &RawMasterFile::geometry() const { return m_geometry; } + +const std::vector &RawMasterFile::roi_geometries() const { + return m_ROI_geometries; +} size_t RawMasterFile::n_modules() const { - return m_geometry.row * m_geometry.col; + return m_detector_layout.row * m_detector_layout.col; } xy RawMasterFile::udp_interfaces_per_module() const { @@ -205,26 +232,21 @@ std::optional> RawMasterFile::disabled_udp_ports() const { return m_disabled_udp_ports; } -std::optional RawMasterFile::roi() const { - if (!m_rois) { - return std::nullopt; - } +ROI RawMasterFile::roi() const { - if (m_rois->empty()) { + if (m_rois.empty()) { throw std::runtime_error(LOCATION + "Zero ROIs in metadata."); } - if (m_rois.value().size() > 1) { + if (m_rois.size() > 1) { throw std::runtime_error(LOCATION + "Multiple ROIs present, use rois() method."); } else { - return m_rois.has_value() - ? std::optional(m_rois.value().at(0)) - : std::nullopt; // TODO: maybe throw if no roi exists + return m_rois.at(0); } } -std::optional> RawMasterFile::rois() const { return m_rois; } +std::vector RawMasterFile::rois() const { return m_rois; } ReadoutMode RawMasterFile::get_reading_mode() const { @@ -260,10 +282,11 @@ 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_detector_layout = { + 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"]; @@ -426,14 +449,18 @@ void RawMasterFile::parse_json(std::istream &is) { obj.at("ymin") = 0; obj.at("ymax") = 0; } - m_rois.emplace(); - m_rois.value().push_back(ROI{ - obj.at("xmin"), static_cast(obj.at("xmax")) + 1, - obj.at("ymin"), static_cast(obj.at("ymax")) + 1}); + m_rois.push_back({static_cast(obj.at("xmin")), + static_cast(obj.at("xmax")) + 1, + static_cast(obj.at("ymin")), + static_cast(obj.at("ymax")) + 1}); + } else { + // fill ROI with full detector size if not present in master + // file + m_rois.push_back({0, static_cast(m_pixels_x), 0, + static_cast(m_pixels_y)}); } } else { auto obj = j.at("Receiver Rois"); - m_rois.emplace(); for (auto &elem : obj) { // handle Mythen if (elem.at("ymin") == -1 && elem.at("ymax") == -1) { @@ -441,15 +468,17 @@ void RawMasterFile::parse_json(std::istream &is) { elem.at("ymax") = 0; } - m_rois.value().push_back(ROI{ - elem.at("xmin"), static_cast(elem.at("xmax")) + 1, - elem.at("ymin"), - static_cast(elem.at("ymax")) + 1}); + m_rois.push_back({static_cast(elem.at("xmin")), + static_cast(elem.at("xmax")) + 1, + static_cast(elem.at("ymin")), + static_cast(elem.at("ymax")) + 1}); } } } catch (const json::out_of_range &e) { - // leave the optional empty + // fill ROI with full detector size if not present in master file + m_rois.push_back({0, static_cast(m_pixels_x), 0, + static_cast(m_pixels_y)}); } if (j.contains("Counter Mask")) { @@ -557,7 +586,7 @@ void RawMasterFile::parse_raw(std::istream &is) { m_max_frames_per_file = std::stoi(value); } else if (key == "Geometry") { pos = value.find(','); - m_geometry = { + m_detector_layout = { static_cast(std::stoi(value.substr(1, pos))), static_cast(std::stoi(value.substr(pos + 1)))}; } else if (key == "Number of UDP Interfaces") { @@ -577,11 +606,11 @@ void RawMasterFile::parse_raw(std::istream &is) { m_type = DetectorType::Moench03_old; } - if (m_geometry.col == 0 && m_geometry.row == 0) { + if (m_detector_layout.col == 0 && m_detector_layout.row == 0) { retrieve_geometry(); LOG(TLogLevel::logWARNING) << "No geometry found in master file. Retrieved geometry of " - << m_geometry.row << " x " << m_geometry.col << "\n "; + << m_detector_layout.row << " x " << m_detector_layout.col << "\n "; } // TODO! Read files and find actual frames @@ -607,7 +636,151 @@ void RawMasterFile::retrieve_geometry() { ++rows; ++cols; - m_geometry = {rows, cols}; + m_detector_layout = {rows, cols}; +} + +/** + * @brief Update ROIs from disabled UDP ports + */ +void RawMasterFile::update_rois_from_disabled_udp_ports() { + + const size_t num_udp_port_types = m_udp_port_types.value().size(); + + auto disabled_ports = m_disabled_udp_ports.value(); + + size_t first_port = disabled_ports[0] % num_udp_port_types; + + bool all_ports_equal = + std::all_of(disabled_ports.begin(), disabled_ports.end(), + [&num_udp_port_types, first_port](size_t &port) { + return port % num_udp_port_types == first_port; + }); + + m_rois.clear(); // remove global roi + + const ssize_t udp_ports_per_module = + m_geometry.udp_interfaces_per_module().col * + m_geometry.udp_interfaces_per_module().row; + + bool port_disabled_for_all_modules = + disabled_ports.size() == + m_geometry.modules_x() * m_geometry.modules_y() / udp_ports_per_module; + + if (all_ports_equal && port_disabled_for_all_modules) { + + if (m_udp_port_types.value()[first_port] == "left") { + const size_t num_rois = + m_geometry.modules_x() / udp_ports_per_module; + m_rois.resize(num_rois); + + const ssize_t pixels_per_module_x = + m_geometry.pixels_x() / m_geometry.modules_x(); + std::generate( + m_rois.begin(), m_rois.end(), + [n = 0, this, pixels_per_module_x, + udp_ports_per_module]() mutable { + ssize_t idx = n++; + return ROI{ + idx * udp_ports_per_module * pixels_per_module_x + + pixels_per_module_x, + idx * udp_ports_per_module * pixels_per_module_x + + 2 * pixels_per_module_x, + 0, static_cast(m_geometry.pixels_y())}; + }); + } + if (m_udp_port_types.value()[first_port] == "right") { + const size_t num_rois = + m_geometry.modules_x() / udp_ports_per_module; + m_rois.resize(num_rois); + + const ssize_t pixels_per_module_x = + m_geometry.pixels_x() / m_geometry.modules_x(); + std::generate( + m_rois.begin(), m_rois.end(), + [n = 0, this, pixels_per_module_x, + udp_ports_per_module]() mutable { + ssize_t idx = n++; + return ROI{idx * udp_ports_per_module * pixels_per_module_x, + idx * udp_ports_per_module * + pixels_per_module_x + + pixels_per_module_x, + 0, static_cast(m_geometry.pixels_y())}; + }); + } + if (m_udp_port_types.value()[first_port] == "top") { + size_t num_rois = m_geometry.modules_y() / udp_ports_per_module; + m_rois.resize(num_rois); + // assumes euclidean coordinate system with origin at bottom + // left corner of the detector + const ssize_t pixels_per_module_y = + m_geometry.pixels_y() / m_geometry.modules_y(); + std::generate( + m_rois.begin(), m_rois.end(), + [n = 0, this, pixels_per_module_y, + udp_ports_per_module]() mutable { + ssize_t idx = n++; + return ROI{0, static_cast(m_geometry.pixels_x()), + idx * udp_ports_per_module * pixels_per_module_y, + idx * udp_ports_per_module * + pixels_per_module_y + + pixels_per_module_y}; + }); + } + if (m_udp_port_types.value()[first_port] == "bottom") { + size_t num_rois = m_geometry.modules_y() / udp_ports_per_module; + m_rois.resize(num_rois); + const ssize_t pixels_per_module_y = + m_geometry.pixels_y() / m_geometry.modules_y(); + std::generate( + m_rois.begin(), m_rois.end(), + [n = 0, this, pixels_per_module_y, + udp_ports_per_module]() mutable { + ssize_t idx = n++; + return ROI{ + 0, static_cast(m_geometry.pixels_x()), + idx * udp_ports_per_module * pixels_per_module_y + + pixels_per_module_y, + idx * udp_ports_per_module * pixels_per_module_y + + 2 * pixels_per_module_y}; + }); + } + } else { + // iterate over all ports and create ROIs for each disabled port + LOG(logDEBUG) << "Creating ROIs from disabled UDP ports"; + // get the enabled ones: + std::vector enabled_ports(m_geometry.n_modules()); + std::iota(enabled_ports.begin(), enabled_ports.end(), 0); + std::for_each(disabled_ports.begin(), disabled_ports.end(), + [&enabled_ports](size_t &port) { + enabled_ports.erase(std::remove(enabled_ports.begin(), + enabled_ports.end(), + port), + enabled_ports.end()); + }); + + m_rois.reserve(enabled_ports.size()); + for (const auto enabled_port : enabled_ports) { + + auto module_geometry = + m_geometry.get_module_geometries(enabled_port); + m_rois.push_back( + ROI{module_geometry.origin_x, + module_geometry.origin_x + module_geometry.width, + module_geometry.origin_y, + module_geometry.origin_y + module_geometry.height}); + } + + if (m_udp_port_types == std::vector{"left", "right"}) { + m_rois = merge_consecutive_rois(m_rois); + } else if (m_udp_port_types == + std::vector{"bottom", "top"} || + m_udp_port_types == + std::vector{"top", "bottom"}) { + m_rois = merge_consecutive_rois(m_rois); + } else { + throw std::runtime_error(LOCATION + "Unsupported UDP port types"); + } + } } } // namespace aare diff --git a/src/RawMasterFile.test.cpp b/src/RawMasterFile.test.cpp index 59efed71..475e7e72 100644 --- a/src/RawMasterFile.test.cpp +++ b/src/RawMasterFile.test.cpp @@ -86,8 +86,8 @@ TEST_CASE("Parse a master file in .json format", "[.integration]") { // "x": 1, // "y": 1 // }, - REQUIRE(f.geometry().col == 1); - REQUIRE(f.geometry().row == 1); + REQUIRE(f.detector_layout().col == 1); + REQUIRE(f.detector_layout().row == 1); // "Image Size in bytes": 1048576, REQUIRE(f.image_size_in_bytes() == 1048576); @@ -180,9 +180,9 @@ TEST_CASE("Parse a master file in .raw format", "[.integration]") { REQUIRE(f.detector_type() == DetectorType::ChipTestBoard); // Timing Mode : auto REQUIRE(f.timing_mode() == TimingMode::Auto); - // Geometry : [1, 1] - REQUIRE(f.geometry().col == 1); - REQUIRE(f.geometry().row == 1); + // Detector Layout : [1, 1] + REQUIRE(f.detector_layout().col == 1); + REQUIRE(f.detector_layout().row == 1); // Image Size : 360000 bytes REQUIRE(f.image_size_in_bytes() == 360000); // Pixels : [96, 1] @@ -247,9 +247,9 @@ TEST_CASE("Parse a master file in new .json format", REQUIRE(f.detector_type() == DetectorType::Mythen3); // Timing Mode : auto REQUIRE(f.timing_mode() == TimingMode::Auto); - // Geometry : [2, 1] - REQUIRE(f.geometry().col == 2); - REQUIRE(f.geometry().row == 1); + // Detector Layout : [2, 1] + REQUIRE(f.detector_layout().col == 2); + REQUIRE(f.detector_layout().row == 1); // Image Size : 5120 bytes REQUIRE(f.image_size_in_bytes() == 5120); @@ -400,8 +400,8 @@ TEST_CASE("Parse EIGER 7.2 master from string stream") { REQUIRE(f.version() == "7.2"); REQUIRE(f.detector_type() == DetectorType::Eiger); REQUIRE(f.timing_mode() == TimingMode::Auto); - REQUIRE(f.geometry().col == 2); - REQUIRE(f.geometry().row == 2); + REQUIRE(f.detector_layout().col == 2); + REQUIRE(f.detector_layout().row == 2); REQUIRE(f.image_size_in_bytes() == 524288); REQUIRE(f.pixels_x() == 512); @@ -477,8 +477,8 @@ TEST_CASE("Parse JUNGFRAU 7.2 master from string stream") { REQUIRE(f.version() == "7.2"); REQUIRE(f.detector_type() == DetectorType::Jungfrau); REQUIRE(f.timing_mode() == TimingMode::Auto); - REQUIRE(f.geometry().col == 1); - REQUIRE(f.geometry().row == 2); + REQUIRE(f.detector_layout().col == 1); + REQUIRE(f.detector_layout().row == 2); REQUIRE(f.n_modules() == 2); REQUIRE(f.image_size_in_bytes() == 524288); REQUIRE(f.pixels_x() == 1024); @@ -560,7 +560,7 @@ TEST_CASE( REQUIRE(f.version() == "7.2"); REQUIRE(f.detector_type() == DetectorType::ChipTestBoard); REQUIRE(f.timing_mode() == TimingMode::Auto); - REQUIRE(f.geometry() == xy{1, 1}); + REQUIRE(f.detector_layout() == xy{1, 1}); REQUIRE(f.image_size_in_bytes() == 192000); REQUIRE(f.pixels_x() == 32); REQUIRE(f.pixels_y() == 1); @@ -638,7 +638,7 @@ TEST_CASE( REQUIRE(f.version() == "7.2"); REQUIRE(f.detector_type() == DetectorType::ChipTestBoard); REQUIRE(f.timing_mode() == TimingMode::Auto); - REQUIRE(f.geometry() == xy{1, 1}); + REQUIRE(f.detector_layout() == xy{1, 1}); REQUIRE(f.image_size_in_bytes() == 16000); REQUIRE(f.pixels_x() == 64); REQUIRE(f.pixels_y() == 1); @@ -710,7 +710,7 @@ TEST_CASE("Parse Moench 7.2 master (SW 7.0.3) from string stream") { REQUIRE(f.version() == "7.2"); REQUIRE(f.detector_type() == DetectorType::Moench03_old); REQUIRE(f.timing_mode() == TimingMode::Auto); - REQUIRE(f.geometry() == xy{1, 1}); + REQUIRE(f.detector_layout() == xy{1, 1}); REQUIRE(f.image_size_in_bytes() == 320000); REQUIRE(f.pixels_x() == 400); REQUIRE(f.pixels_y() == 400); @@ -781,7 +781,7 @@ TEST_CASE("Parse Moench 7.2 master (SW 8.0.0) from string stream") { REQUIRE(f.version() == "7.2"); REQUIRE(f.detector_type() == DetectorType::Moench03); REQUIRE(f.timing_mode() == TimingMode::Auto); - REQUIRE(f.geometry() == xy{1, 1}); + REQUIRE(f.detector_layout() == xy{1, 1}); REQUIRE(f.image_size_in_bytes() == 320000); REQUIRE(f.pixels_x() == 400); REQUIRE(f.pixels_y() == 400); @@ -863,7 +863,7 @@ TEST_CASE("Parse CTB 7.2 master (SW 8.0.0) from string stream") { REQUIRE(f.version() == "7.2"); REQUIRE(f.detector_type() == DetectorType::ChipTestBoard); REQUIRE(f.timing_mode() == TimingMode::Auto); - REQUIRE(f.geometry() == xy{1, 1}); + REQUIRE(f.detector_layout() == xy{1, 1}); REQUIRE(f.image_size_in_bytes() == 192000); REQUIRE(f.pixels_x() == 32); REQUIRE(f.pixels_y() == 1); @@ -944,7 +944,7 @@ TEST_CASE( REQUIRE(f.version() == "7.2"); REQUIRE(f.detector_type() == DetectorType::ChipTestBoard); REQUIRE(f.timing_mode() == TimingMode::Auto); - REQUIRE(f.geometry() == xy{1, 1}); + REQUIRE(f.detector_layout() == xy{1, 1}); REQUIRE(f.image_size_in_bytes() == 16000); REQUIRE(f.pixels_x() == 64); REQUIRE(f.pixels_y() == 1); @@ -1011,8 +1011,8 @@ TEST_CASE("Parse a CTB file from stream") { REQUIRE(f.version() == "8.0"); REQUIRE(f.detector_type() == DetectorType::ChipTestBoard); REQUIRE(f.timing_mode() == TimingMode::Auto); - REQUIRE(f.geometry().col == 1); - REQUIRE(f.geometry().row == 1); + REQUIRE(f.detector_layout().col == 1); + REQUIRE(f.detector_layout().row == 1); REQUIRE(f.image_size_in_bytes() == 18432); REQUIRE(f.pixels_x() == 2); REQUIRE(f.pixels_y() == 1); @@ -1101,8 +1101,8 @@ TEST_CASE("Parse v8.0 MYTHEN3 from stream") { REQUIRE(f.version() == "8.0"); REQUIRE(f.detector_type() == DetectorType::Mythen3); REQUIRE(f.timing_mode() == TimingMode::Auto); - REQUIRE(f.geometry().col == 2); - REQUIRE(f.geometry().row == 1); + REQUIRE(f.detector_layout().col == 2); + REQUIRE(f.detector_layout().row == 1); REQUIRE(f.image_size_in_bytes() == 5120); REQUIRE(f.pixels_x() == 1280); REQUIRE(f.pixels_y() == 1); @@ -1186,8 +1186,8 @@ TEST_CASE("Parse a v7.1 Mythen3 from stream") { REQUIRE(f.version() == "7.1"); REQUIRE(f.detector_type() == DetectorType::Mythen3); REQUIRE(f.timing_mode() == TimingMode::Auto); - REQUIRE(f.geometry().col == 1); - REQUIRE(f.geometry().row == 1); + REQUIRE(f.detector_layout().col == 1); + REQUIRE(f.detector_layout().row == 1); REQUIRE(f.image_size_in_bytes() == 15360); REQUIRE(f.pixels_x() == 3840); REQUIRE(f.pixels_y() == 1); @@ -1268,8 +1268,8 @@ TEST_CASE("Parse old Moench03 from stream") { REQUIRE(f.version() == "7.1"); REQUIRE(f.detector_type() == DetectorType::Moench03_old); REQUIRE(f.timing_mode() == TimingMode::Auto); - REQUIRE(f.geometry().col == 1); - REQUIRE(f.geometry().row == 1); + REQUIRE(f.detector_layout().col == 1); + REQUIRE(f.detector_layout().row == 1); REQUIRE(f.image_size_in_bytes() == 320000); REQUIRE(f.pixels_x() == 400); REQUIRE(f.pixels_y() == 400);