diff --git a/CMakeLists.txt b/CMakeLists.txt index a1ec6fe..6215482 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -324,13 +324,10 @@ if(AARE_ASAN) ) endif() - - - - if(AARE_TESTS) enable_testing() add_subdirectory(tests) + target_compile_definitions(tests PRIVATE AARE_TESTS) endif() ###------------------------------------------------------------------------------MAIN LIBRARY @@ -353,7 +350,7 @@ set(PUBLICHEADERS include/aare/FilePtr.hpp include/aare/Frame.hpp include/aare/GainMap.hpp - include/aare/geo_helpers.hpp + include/aare/DetectorGeometry.hpp include/aare/JungfrauDataFile.hpp include/aare/logger.hpp include/aare/NDArray.hpp @@ -379,7 +376,7 @@ set(SourceFiles ${CMAKE_CURRENT_SOURCE_DIR}/src/File.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/FilePtr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Fit.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/geo_helpers.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/DetectorGeometry.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/JungfrauDataFile.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyFile.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyHelpers.cpp @@ -414,6 +411,9 @@ target_link_libraries( ) +if(AARE_TESTS) + target_compile_definitions(aare_core PRIVATE AARE_TESTS) +endif() if(AARE_VERBOSE) target_compile_definitions(aare_core PUBLIC AARE_VERBOSE) target_compile_definitions(aare_core PUBLIC AARE_LOG_LEVEL=aare::logDEBUG5) @@ -441,7 +441,7 @@ if(AARE_TESTS) ${CMAKE_CURRENT_SOURCE_DIR}/src/decode.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Dtype.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Frame.test.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/geo_helpers.test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/DetectorGeometry.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/RawMasterFile.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/NDArray.test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/NDView.test.cpp @@ -463,11 +463,6 @@ if(AARE_TESTS) target_sources(tests PRIVATE ${TestSources} ) endif() - - -###------------------------------------------------------------------------------------------ -###------------------------------------------------------------------------------------------ - if(AARE_MASTER_PROJECT) install(TARGETS aare_core aare_compiler_flags EXPORT "${TARGETS_EXPORT_NAME}" diff --git a/include/aare/DetectorGeometry.hpp b/include/aare/DetectorGeometry.hpp new file mode 100644 index 0000000..0511319 --- /dev/null +++ b/include/aare/DetectorGeometry.hpp @@ -0,0 +1,81 @@ +#pragma once +#include "aare/RawMasterFile.hpp" //ROI refactor away +#include "aare/defs.hpp" +namespace aare { + +struct ModuleConfig { + int module_gap_row{}; + int module_gap_col{}; + + bool operator==(const ModuleConfig &other) const { + if (module_gap_col != other.module_gap_col) + return false; + if (module_gap_row != other.module_gap_row) + return false; + return true; + } +}; + +/** + * @brief Class to hold the geometry of a module. Where pixel 0 is located and + * the size of the module + */ +struct ModuleGeometry { + int origin_x{}; + int origin_y{}; + int height{}; + int width{}; + int row_index{}; + int col_index{}; +}; + +/** + * @brief Class to hold the geometry of a detector. Number of modules, their + * size and where pixel 0 for each module is located + */ +class DetectorGeometry { + public: + DetectorGeometry(const xy &geometry, const ssize_t module_pixels_x, + const ssize_t module_pixels_y, + const xy udp_interfaces_per_module = xy{1, 1}, + const bool quad = false); + + ~DetectorGeometry() = default; + + /** + * @brief Update the detector geometry given a region of interest + * + * @param roi + * @return DetectorGeometry + */ + void update_geometry_with_roi(ROI roi); + + size_t n_modules() const; + + size_t n_modules_in_roi() const; + + size_t pixels_x() const; + size_t pixels_y() const; + + size_t modules_x() const; + size_t modules_y() const; + + const std::vector &get_modules_in_roi() const; + + ssize_t get_modules_in_roi(const size_t index) const; + + const std::vector &get_module_geometries() const; + + const ModuleGeometry &get_module_geometries(const size_t index) const; + + private: + size_t m_modules_x{}; + size_t m_modules_y{}; + size_t m_pixels_x{}; + size_t m_pixels_y{}; + static constexpr ModuleConfig cfg{0, 0}; + std::vector module_geometries{}; + std::vector modules_in_roi{}; +}; + +} // namespace aare \ No newline at end of file diff --git a/include/aare/RawFile.hpp b/include/aare/RawFile.hpp index 9ffdb7c..b63095a 100644 --- a/include/aare/RawFile.hpp +++ b/include/aare/RawFile.hpp @@ -1,27 +1,19 @@ #pragma once +#include "aare/DetectorGeometry.hpp" #include "aare/FileInterface.hpp" #include "aare/Frame.hpp" #include "aare/NDArray.hpp" //for pixel map #include "aare/RawMasterFile.hpp" #include "aare/RawSubFile.hpp" +#ifdef AARE_TESTS +#include "../tests/friend_test.hpp" +#endif + #include namespace aare { -struct ModuleConfig { - int module_gap_row{}; - int module_gap_col{}; - - bool operator==(const ModuleConfig &other) const { - if (module_gap_col != other.module_gap_col) - return false; - if (module_gap_row != other.module_gap_row) - return false; - return true; - } -}; - /** * @brief Class to read .raw files. The class will parse the master file * to find the correct geometry for the frames. @@ -29,11 +21,12 @@ struct ModuleConfig { * Consider using that unless you need raw file specific functionality. */ class RawFile : public FileInterface { + std::vector> m_subfiles; - ModuleConfig cfg{0, 0}; + RawMasterFile m_master; size_t m_current_frame{}; - size_t m_current_subfile{}; + DetectorGeometry m_geometry; public: @@ -67,13 +60,21 @@ class RawFile : public FileInterface { size_t rows() const override; size_t cols() const override; size_t bitdepth() const override; - xy geometry(); size_t n_modules() const; + size_t n_modules_in_roi() const; + xy geometry() const; RawMasterFile master() const; DetectorType detector_type() const override; + /** + * @brief read the header of the file + * @param fname path to the data subfile + * @return DetectorHeader + */ + static DetectorHeader read_header(const std::filesystem::path &fname); + private: /** * @brief read the frame at the given frame index into the image buffer @@ -91,15 +92,7 @@ class RawFile : public FileInterface { */ Frame get_frame(size_t frame_index); - /** - * @brief read the header of the file - * @param fname path to the data subfile - * @return DetectorHeader - */ - static DetectorHeader read_header(const std::filesystem::path &fname); - void open_subfiles(); - void find_geometry(); }; } // namespace aare \ No newline at end of file diff --git a/include/aare/RawMasterFile.hpp b/include/aare/RawMasterFile.hpp index 2c64a90..57189f7 100644 --- a/include/aare/RawMasterFile.hpp +++ b/include/aare/RawMasterFile.hpp @@ -1,5 +1,6 @@ #pragma once #include "aare/defs.hpp" +#include #include #include #include @@ -77,8 +78,10 @@ class RawMasterFile { size_t m_pixels_y{}; size_t m_pixels_x{}; size_t m_bitdepth{}; + uint8_t m_quad = 0; xy m_geometry{}; + xy m_udp_interfaces_per_module{1, 1}; size_t m_max_frames_per_file{}; // uint32_t m_adc_mask{}; // TODO! implement reading @@ -96,7 +99,6 @@ class RawMasterFile { std::optional m_digital_samples; std::optional m_transceiver_samples; std::optional m_number_of_rows; - std::optional m_quad; std::optional m_roi; @@ -115,17 +117,18 @@ class RawMasterFile { size_t max_frames_per_file() const; size_t bitdepth() const; size_t frame_padding() const; + xy udp_interfaces_per_module() const; const FrameDiscardPolicy &frame_discard_policy() const; size_t total_frames_expected() const; xy geometry() const; size_t n_modules() const; + uint8_t quad() const; std::optional analog_samples() const; std::optional digital_samples() const; std::optional transceiver_samples() const; std::optional number_of_rows() const; - std::optional quad() const; std::optional roi() const; @@ -134,6 +137,7 @@ class RawMasterFile { private: void parse_json(const std::filesystem::path &fpath); void parse_raw(const std::filesystem::path &fpath); + void retrieve_geometry(); }; } // namespace aare \ No newline at end of file diff --git a/include/aare/defs.hpp b/include/aare/defs.hpp index 71d8c49..9bb8cfa 100644 --- a/include/aare/defs.hpp +++ b/include/aare/defs.hpp @@ -174,35 +174,6 @@ template struct t_xy { }; using xy = t_xy; -/** - * @brief Class to hold the geometry of a module. Where pixel 0 is located and - * the size of the module - */ -struct ModuleGeometry { - int origin_x{}; - int origin_y{}; - int height{}; - int width{}; - int row_index{}; - int col_index{}; -}; - -/** - * @brief Class to hold the geometry of a detector. Number of modules, their - * size and where pixel 0 for each module is located - */ -struct DetectorGeometry { - int modules_x{}; - int modules_y{}; - int pixels_x{}; - int pixels_y{}; - int module_gap_row{}; - int module_gap_col{}; - std::vector module_pixel_0; - - auto size() const { return module_pixel_0.size(); } -}; - struct ROI { ssize_t xmin{}; ssize_t xmax{}; diff --git a/include/aare/geo_helpers.hpp b/include/aare/geo_helpers.hpp deleted file mode 100644 index c6454a5..0000000 --- a/include/aare/geo_helpers.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "aare/RawMasterFile.hpp" //ROI refactor away -#include "aare/defs.hpp" -namespace aare { - -/** - * @brief Update the detector geometry given a region of interest - * - * @param geo - * @param roi - * @return DetectorGeometry - */ -DetectorGeometry update_geometry_with_roi(DetectorGeometry geo, ROI roi); - -} // namespace aare \ No newline at end of file diff --git a/python/src/raw_file.hpp b/python/src/raw_file.hpp index 689b84e..83f1110 100644 --- a/python/src/raw_file.hpp +++ b/python/src/raw_file.hpp @@ -65,7 +65,7 @@ void define_raw_file_io_bindings(py::module &m) { header = py::array_t(n_frames); } else { header = py::array_t( - {self.n_modules(), n_frames}); + {self.n_modules_in_roi(), n_frames}); } // py::array_t header({self.n_mod(), n_frames}); @@ -101,7 +101,8 @@ void define_raw_file_io_bindings(py::module &m) { .def_property_readonly("cols", &RawFile::cols) .def_property_readonly("bitdepth", &RawFile::bitdepth) .def_property_readonly("geometry", &RawFile::geometry) - .def_property_readonly("n_modules", &RawFile::n_modules) .def_property_readonly("detector_type", &RawFile::detector_type) - .def_property_readonly("master", &RawFile::master); + .def_property_readonly("master", &RawFile::master) + .def_property_readonly("n_modules", &RawFile::n_modules) + .def_property_readonly("n_modules_in_roi", &RawFile::n_modules_in_roi); } \ No newline at end of file diff --git a/python/src/raw_master_file.hpp b/python/src/raw_master_file.hpp index 9c2bd17..34db4d3 100644 --- a/python/src/raw_master_file.hpp +++ b/python/src/raw_master_file.hpp @@ -57,6 +57,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("udp_interfaces_per_module", + &RawMasterFile::udp_interfaces_per_module) .def_property_readonly("analog_samples", &RawMasterFile::analog_samples, R"( Number of analog samples diff --git a/python/tests/test_RawFile.py b/python/tests/test_RawFile.py new file mode 100644 index 0000000..5edfa84 --- /dev/null +++ b/python/tests/test_RawFile.py @@ -0,0 +1,73 @@ +import pytest +from aare import RawFile +import numpy as np + +@pytest.mark.files +def test_read_rawfile_with_roi(test_data_path): + + with RawFile(test_data_path / "raw/SingleChipROI/Data_master_0.json") as f: + headers, frames = f.read() + + assert headers.size == 10100 + assert frames.shape == (10100, 256, 256) + +@pytest.mark.files +def test_read_rawfile_quad_eiger_and_compare_to_numpy(test_data_path): + + d0 = test_data_path/'raw/eiger_quad_data/W13_vrpreampscan_m21C_300V_800eV_vthre2000_d0_f0_0.raw' + d1 = test_data_path/'raw/eiger_quad_data/W13_vrpreampscan_m21C_300V_800eV_vthre2000_d1_f0_0.raw' + + image = np.zeros((512,512), dtype=np.uint32) + + with open(d0) as f: + raw = np.fromfile(f, dtype=np.uint32, count = 256*512, offset = 20*256*512*4 + 112*21).reshape(256,512) + + image[256:,:] = raw + + with open(d1) as f: + raw = np.fromfile(f, dtype=np.uint32, count = 256*512, offset = 20*256*512*4 + 112*21).reshape(256,512) + + image[0:256,:] = raw[::-1,:] + + with RawFile(test_data_path/'raw/eiger_quad_data/W13_vrpreampscan_m21C_300V_800eV_vthre2000_master_0.json') as f: + f.seek(20) + header, image1 = f.read_frame() + + assert (image == image1).all() + + +@pytest.mark.files +def test_read_rawfile_eiger_and_compare_to_numpy(test_data_path): + d0 = test_data_path/'raw/eiger/Lab6_20500eV_2deg_20240629_d0_f0_7.raw' + d1 = test_data_path/'raw/eiger/Lab6_20500eV_2deg_20240629_d1_f0_7.raw' + d2 = test_data_path/'raw/eiger/Lab6_20500eV_2deg_20240629_d2_f0_7.raw' + d3 = test_data_path/'raw/eiger/Lab6_20500eV_2deg_20240629_d3_f0_7.raw' + + image = np.zeros((512,1024), dtype=np.uint32) + + #TODO why is there no header offset? + with open(d0) as f: + raw = np.fromfile(f, dtype=np.uint32, count = 256*512, offset=112).reshape(256,512) + + image[0:256,0:512] = raw[::-1] + + with open(d1) as f: + raw = np.fromfile(f, dtype=np.uint32, count = 256*512, offset=112).reshape(256,512) + + image[0:256,512:] = raw[::-1] + + with open(d2) as f: + raw = np.fromfile(f, dtype=np.uint32, count = 256*512, offset=112).reshape(256,512) + + image[256:,0:512] = raw + + with open(d3) as f: + raw = np.fromfile(f, dtype=np.uint32, count = 256*512, offset=112).reshape(256,512) + + image[256:,512:] = raw + + + with RawFile(test_data_path/'raw/eiger/Lab6_20500eV_2deg_20240629_master_7.json') as f: + header, image1 = f.read_frame() + + assert (image == image1).all() diff --git a/src/ClusterFinderMT.test.cpp b/src/ClusterFinderMT.test.cpp index 9289592..f4abd58 100644 --- a/src/ClusterFinderMT.test.cpp +++ b/src/ClusterFinderMT.test.cpp @@ -58,8 +58,10 @@ class ClusterFinderMTWrapper }; TEST_CASE("multithreaded cluster finder", "[.files][.ClusterFinder]") { - auto fpath = "/mnt/sls_det_storage/matterhorn_data/aare_test_data/" - "Moench03new/cu_half_speed_master_4.json"; + auto fpath = + test_data_path() / "clust/Moench03new/cu_half_speed_master_4.json"; + + REQUIRE(std::filesystem::exists(fpath)); File file(fpath); diff --git a/src/DetectorGeometry.cpp b/src/DetectorGeometry.cpp new file mode 100644 index 0000000..9658aad --- /dev/null +++ b/src/DetectorGeometry.cpp @@ -0,0 +1,151 @@ + +#include "aare/DetectorGeometry.hpp" +#include "fmt/core.h" +#include +#include + +namespace aare { + +DetectorGeometry::DetectorGeometry(const xy &geometry, + const ssize_t module_pixels_x, + const ssize_t module_pixels_y, + const xy udp_interfaces_per_module, + const bool quad) { + + size_t num_modules = geometry.col * geometry.row; + module_geometries.reserve(num_modules); + for (size_t col = 0; col < geometry.col; + col += udp_interfaces_per_module.col) + for (size_t row = 0; row < geometry.row; + row += udp_interfaces_per_module.row) { + for (size_t port_row = 0; port_row < udp_interfaces_per_module.row; + ++port_row) + for (size_t port_col = 0; + port_col < udp_interfaces_per_module.col; ++port_col) { + ModuleGeometry g; + g.row_index = + quad ? (row + port_row + 1) % 2 : (row + port_row); + g.col_index = col + port_col; + g.origin_x = g.col_index * module_pixels_x; + g.origin_y = g.row_index * module_pixels_y; + + g.width = module_pixels_x; + g.height = module_pixels_y; + module_geometries.push_back(g); + } + } + + m_pixels_y = (geometry.row * module_pixels_y); + m_pixels_x = (geometry.col * module_pixels_x); + m_modules_x = geometry.col; + m_modules_y = geometry.row; + m_pixels_y += static_cast((geometry.row - 1) * cfg.module_gap_row); + + modules_in_roi.resize(num_modules); + std::iota(modules_in_roi.begin(), modules_in_roi.end(), 0); +} + +size_t DetectorGeometry::n_modules() const { return m_modules_x * m_modules_y; } + +size_t DetectorGeometry::n_modules_in_roi() const { + return modules_in_roi.size(); +}; + +size_t DetectorGeometry::pixels_x() const { return m_pixels_x; } +size_t DetectorGeometry::pixels_y() const { return m_pixels_y; } + +size_t DetectorGeometry::modules_x() const { return m_modules_x; }; +size_t DetectorGeometry::modules_y() const { return m_modules_y; }; + +const std::vector &DetectorGeometry::get_modules_in_roi() const { + return modules_in_roi; +} + +ssize_t DetectorGeometry::get_modules_in_roi(const size_t index) const { + return modules_in_roi[index]; +} + +const std::vector & +DetectorGeometry::get_module_geometries() const { + return module_geometries; +} + +const ModuleGeometry & +DetectorGeometry::get_module_geometries(const size_t index) const { + return module_geometries[index]; +} + +void DetectorGeometry::update_geometry_with_roi(ROI roi) { +#ifdef AARE_VERBOSE + fmt::println("update_geometry_with_roi() called with ROI: {} {} {} {}", + roi.xmin, roi.xmax, roi.ymin, roi.ymax); + fmt::println("Geometry: {} {} {} {} {} {}", m_modules_x, m_modules_y, + m_pixels_x, m_pixels_y, cfg.module_gap_row, + cfg.module_gap_col); + +#endif + + modules_in_roi.clear(); + modules_in_roi.reserve(m_modules_x * m_modules_y); + int pos_y = 0; + int pos_y_increment = 0; + for (size_t row = 0; row < m_modules_y; row++) { + int pos_x = 0; + for (size_t col = 0; col < m_modules_x; col++) { + auto &m = module_geometries[row * m_modules_x + col]; + + auto original_height = m.height; + auto original_width = m.width; + + // module is to the left of the roi + if (m.origin_x + m.width < roi.xmin) { + m.width = 0; + + // roi is in module + } else { + // here we only arrive when the roi is in or to the left of + // the module + if (roi.xmin > m.origin_x) { + m.width -= roi.xmin - m.origin_x; + } + if (roi.xmax < m.origin_x + original_width) { + m.width -= m.origin_x + original_width - roi.xmax; + } + m.origin_x = pos_x; + pos_x += m.width; + } + + if (m.origin_y + m.height < roi.ymin) { + m.height = 0; + } else { + if ((roi.ymin > m.origin_y) && + (roi.ymin < m.origin_y + m.height)) { + m.height -= roi.ymin - m.origin_y; + } + if (roi.ymax < m.origin_y + original_height) { + m.height -= m.origin_y + original_height - roi.ymax; + } + m.origin_y = pos_y; + pos_y_increment = m.height; + } + + if (m.height != 0 && m.width != 0) { + modules_in_roi.push_back(row * m_modules_x + col); + } + +#ifdef AARE_VERBOSE + fmt::println("Module {} {} {} {}", m.origin_x, m.origin_y, m.width, + m.height); +#endif + } + // increment pos_y + pos_y += pos_y_increment; + } + + // m_rows = roi.height(); + // m_cols = roi.width(); + m_pixels_x = roi.width(); + m_pixels_y = roi.height(); +} + +} // namespace aare \ No newline at end of file diff --git a/src/DetectorGeometry.test.cpp b/src/DetectorGeometry.test.cpp new file mode 100644 index 0000000..1424f0a --- /dev/null +++ b/src/DetectorGeometry.test.cpp @@ -0,0 +1,194 @@ +#include "aare/File.hpp" +#include "aare/RawFile.hpp" +#include "aare/RawMasterFile.hpp" //needed for ROI + +#include +#include + +#include "aare/DetectorGeometry.hpp" +#include "test_config.hpp" + +TEST_CASE("Simple ROIs on one module") { + // DetectorGeometry update_geometry_with_roi(DetectorGeometry geo, aare::ROI + // roi) + aare::DetectorGeometry geo(aare::xy{1, 1}, 1024, 512); + + REQUIRE(geo.get_module_geometries(0).origin_x == 0); + REQUIRE(geo.get_module_geometries(0).origin_y == 0); + REQUIRE(geo.get_module_geometries(0).width == 1024); + REQUIRE(geo.get_module_geometries(0).height == 512); + + SECTION("ROI is the whole module") { + aare::ROI roi; + roi.xmin = 0; + roi.xmax = 1024; + roi.ymin = 0; + roi.ymax = 512; + geo.update_geometry_with_roi(roi); + + REQUIRE(geo.pixels_x() == 1024); + REQUIRE(geo.pixels_y() == 512); + REQUIRE(geo.modules_x() == 1); + REQUIRE(geo.modules_y() == 1); + REQUIRE(geo.get_module_geometries(0).height == 512); + REQUIRE(geo.get_module_geometries(0).width == 1024); + } + SECTION("ROI is the top left corner of the module") { + aare::ROI roi; + roi.xmin = 100; + roi.xmax = 200; + roi.ymin = 150; + roi.ymax = 200; + geo.update_geometry_with_roi(roi); + + REQUIRE(geo.pixels_x() == 100); + REQUIRE(geo.pixels_y() == 50); + REQUIRE(geo.modules_x() == 1); + REQUIRE(geo.modules_y() == 1); + REQUIRE(geo.get_module_geometries(0).height == 50); + REQUIRE(geo.get_module_geometries(0).width == 100); + } + + SECTION("ROI is a small square") { + aare::ROI roi; + roi.xmin = 1000; + roi.xmax = 1010; + roi.ymin = 500; + roi.ymax = 510; + geo.update_geometry_with_roi(roi); + + REQUIRE(geo.pixels_x() == 10); + REQUIRE(geo.pixels_y() == 10); + REQUIRE(geo.modules_x() == 1); + REQUIRE(geo.modules_y() == 1); + REQUIRE(geo.get_module_geometries(0).height == 10); + REQUIRE(geo.get_module_geometries(0).width == 10); + } + SECTION("ROI is a few columns") { + aare::ROI roi; + roi.xmin = 750; + roi.xmax = 800; + roi.ymin = 0; + roi.ymax = 512; + geo.update_geometry_with_roi(roi); + + REQUIRE(geo.pixels_x() == 50); + REQUIRE(geo.pixels_y() == 512); + REQUIRE(geo.modules_x() == 1); + REQUIRE(geo.modules_y() == 1); + REQUIRE(geo.get_module_geometries(0).height == 512); + REQUIRE(geo.get_module_geometries(0).width == 50); + } +} + +TEST_CASE("Two modules side by side") { + // DetectorGeometry update_geometry_with_roi(DetectorGeometry geo, aare::ROI + // roi) + aare::DetectorGeometry geo(aare::xy{1, 2}, 1024, 512); + + REQUIRE(geo.get_module_geometries(0).origin_x == 0); + REQUIRE(geo.get_module_geometries(0).origin_y == 0); + REQUIRE(geo.get_module_geometries(0).width == 1024); + REQUIRE(geo.get_module_geometries(0).height == 512); + REQUIRE(geo.get_module_geometries(1).origin_x == 1024); + REQUIRE(geo.get_module_geometries(1).origin_y == 0); + + SECTION("ROI is the whole image") { + aare::ROI roi; + roi.xmin = 0; + roi.xmax = 2048; + roi.ymin = 0; + roi.ymax = 512; + geo.update_geometry_with_roi(roi); + + REQUIRE(geo.pixels_x() == 2048); + REQUIRE(geo.pixels_y() == 512); + REQUIRE(geo.modules_x() == 2); + REQUIRE(geo.modules_y() == 1); + } + SECTION("rectangle on both modules") { + aare::ROI roi; + roi.xmin = 800; + roi.xmax = 1300; + roi.ymin = 200; + roi.ymax = 499; + geo.update_geometry_with_roi(roi); + + REQUIRE(geo.pixels_x() == 500); + REQUIRE(geo.pixels_y() == 299); + REQUIRE(geo.modules_x() == 2); + REQUIRE(geo.modules_y() == 1); + REQUIRE(geo.get_module_geometries(0).height == 299); + REQUIRE(geo.get_module_geometries(0).width == 224); + REQUIRE(geo.get_module_geometries(1).height == 299); + REQUIRE(geo.get_module_geometries(1).width == 276); + } +} + +TEST_CASE("Three modules side by side") { + // DetectorGeometry update_geometry_with_roi(DetectorGeometry geo, aare::ROI + // roi) + aare::DetectorGeometry geo(aare::xy{1, 3}, 1024, 512); + aare::ROI roi; + roi.xmin = 700; + roi.xmax = 2500; + roi.ymin = 0; + roi.ymax = 123; + + REQUIRE(geo.get_module_geometries(0).origin_x == 0); + REQUIRE(geo.get_module_geometries(0).origin_y == 0); + REQUIRE(geo.get_module_geometries(0).width == 1024); + REQUIRE(geo.get_module_geometries(0).height == 512); + REQUIRE(geo.get_module_geometries(1).origin_x == 1024); + REQUIRE(geo.get_module_geometries(2).origin_x == 2048); + + geo.update_geometry_with_roi(roi); + + REQUIRE(geo.pixels_x() == 1800); + REQUIRE(geo.pixels_y() == 123); + REQUIRE(geo.modules_x() == 3); + REQUIRE(geo.modules_y() == 1); + REQUIRE(geo.get_module_geometries(0).height == 123); + REQUIRE(geo.get_module_geometries(0).width == 324); + REQUIRE(geo.get_module_geometries(1).height == 123); + REQUIRE(geo.get_module_geometries(1).width == 1024); + REQUIRE(geo.get_module_geometries(2).height == 123); + REQUIRE(geo.get_module_geometries(2).width == 452); +} + +TEST_CASE("Four modules as a square") { + // DetectorGeometry update_geometry_with_roi(DetectorGeometry geo, aare::ROI + // roi) + aare::DetectorGeometry geo(aare::xy{2, 2}, 1024, 512, aare::xy{1, 2}); + aare::ROI roi; + roi.xmin = 500; + roi.xmax = 2000; + roi.ymin = 500; + roi.ymax = 600; + + REQUIRE(geo.get_module_geometries(0).origin_x == 0); + REQUIRE(geo.get_module_geometries(0).origin_y == 0); + REQUIRE(geo.get_module_geometries(0).width == 1024); + REQUIRE(geo.get_module_geometries(0).height == 512); + REQUIRE(geo.get_module_geometries(1).origin_x == 1024); + REQUIRE(geo.get_module_geometries(1).origin_y == 0); + REQUIRE(geo.get_module_geometries(2).origin_x == 0); + REQUIRE(geo.get_module_geometries(2).origin_y == 512); + REQUIRE(geo.get_module_geometries(3).origin_x == 1024); + REQUIRE(geo.get_module_geometries(3).origin_y == 512); + + geo.update_geometry_with_roi(roi); + + REQUIRE(geo.pixels_x() == 1500); + REQUIRE(geo.pixels_y() == 100); + REQUIRE(geo.modules_x() == 2); + REQUIRE(geo.modules_y() == 2); + REQUIRE(geo.get_module_geometries(0).height == 12); + REQUIRE(geo.get_module_geometries(0).width == 524); + REQUIRE(geo.get_module_geometries(1).height == 12); + REQUIRE(geo.get_module_geometries(1).width == 976); + REQUIRE(geo.get_module_geometries(2).height == 88); + REQUIRE(geo.get_module_geometries(2).width == 524); + REQUIRE(geo.get_module_geometries(3).height == 88); + REQUIRE(geo.get_module_geometries(3).width == 976); +} \ No newline at end of file diff --git a/src/RawFile.cpp b/src/RawFile.cpp index 22bf8dd..fab8150 100644 --- a/src/RawFile.cpp +++ b/src/RawFile.cpp @@ -1,8 +1,8 @@ #include "aare/RawFile.hpp" +#include "aare/DetectorGeometry.hpp" #include "aare/PixelMap.hpp" #include "aare/algorithm.hpp" #include "aare/defs.hpp" -#include "aare/geo_helpers.hpp" #include "aare/logger.hpp" #include @@ -13,13 +13,14 @@ using json = nlohmann::json; namespace aare { RawFile::RawFile(const std::filesystem::path &fname, const std::string &mode) - : m_master(fname) { + : 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_mode = mode; + if (mode == "r") { - find_geometry(); if (m_master.roi()) { - m_geometry = - update_geometry_with_roi(m_geometry, m_master.roi().value()); + m_geometry.update_geometry_with_roi(m_master.roi().value()); } open_subfiles(); } else { @@ -61,26 +62,25 @@ void RawFile::read_into(std::byte *image_buf, size_t n_frames, this->get_frame_into(m_current_frame++, image_buf, header); image_buf += bytes_per_frame(); if (header) - header += n_modules(); + header += m_geometry.n_modules_in_roi(); } } -size_t RawFile::n_modules() const { return m_master.n_modules(); } - size_t RawFile::bytes_per_frame() { - return m_geometry.pixels_x * m_geometry.pixels_y * m_master.bitdepth() / + return m_geometry.pixels_x() * m_geometry.pixels_y() * m_master.bitdepth() / bits_per_byte; } size_t RawFile::pixels_per_frame() { // return m_rows * m_cols; - return m_geometry.pixels_x * m_geometry.pixels_y; + return m_geometry.pixels_x() * m_geometry.pixels_y(); } DetectorType RawFile::detector_type() const { return m_master.detector_type(); } void RawFile::seek(size_t frame_index) { // check if the frame number is greater than the total frames - // if frame_number == total_frames, then the next read will throw an error + // if frame_number == total_frames, then the next read will throw an + // error if (frame_index > total_frames()) { throw std::runtime_error( fmt::format("frame number {} is greater than total frames {}", @@ -92,15 +92,23 @@ void RawFile::seek(size_t frame_index) { size_t RawFile::tell() { return m_current_frame; } size_t RawFile::total_frames() const { return m_master.frames_in_file(); } -size_t RawFile::rows() const { return m_geometry.pixels_y; } -size_t RawFile::cols() const { return m_geometry.pixels_x; } +size_t RawFile::rows() const { return m_geometry.pixels_y(); } +size_t RawFile::cols() const { return m_geometry.pixels_x(); } size_t RawFile::bitdepth() const { return m_master.bitdepth(); } -xy RawFile::geometry() { return m_master.geometry(); } +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(); }; +size_t RawFile::n_modules_in_roi() const { + return m_geometry.n_modules_in_roi(); +}; void RawFile::open_subfiles() { if (m_mode == "r") - for (size_t i = 0; i != n_modules(); ++i) { - auto pos = m_geometry.module_pixel_0[i]; + for (size_t i : m_geometry.get_modules_in_roi()) { + auto pos = m_geometry.get_module_geometries(i); m_subfiles.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)); @@ -130,45 +138,8 @@ DetectorHeader RawFile::read_header(const std::filesystem::path &fname) { RawMasterFile RawFile::master() const { return m_master; } -/** - * @brief Find the geometry of the detector by opening all the subfiles and - * reading the headers. - */ -void RawFile::find_geometry() { - - // Hold the maximal row and column number found - // Later used for calculating the total number of rows and columns - uint16_t r{}; - uint16_t c{}; - - for (size_t i = 0; i < n_modules(); i++) { - auto h = read_header(m_master.data_fname(i, 0)); - r = std::max(r, h.row); - c = std::max(c, h.column); - // positions.push_back({h.row, h.column}); - - ModuleGeometry g; - g.origin_x = h.column * m_master.pixels_x(); - g.origin_y = h.row * m_master.pixels_y(); - g.row_index = h.row; - g.col_index = h.column; - g.width = m_master.pixels_x(); - g.height = m_master.pixels_y(); - m_geometry.module_pixel_0.push_back(g); - } - - r++; - c++; - - m_geometry.pixels_y = (r * m_master.pixels_y()); - m_geometry.pixels_x = (c * m_master.pixels_x()); - m_geometry.modules_x = c; - m_geometry.modules_y = r; - m_geometry.pixels_y += static_cast((r - 1) * cfg.module_gap_row); -} - Frame RawFile::get_frame(size_t frame_index) { - auto f = Frame(m_geometry.pixels_y, m_geometry.pixels_x, + auto f = Frame(m_geometry.pixels_y(), m_geometry.pixels_x(), Dtype::from_bitdepth(m_master.bitdepth())); std::byte *frame_buffer = f.data(); get_frame_into(frame_index, frame_buffer); @@ -183,13 +154,15 @@ void RawFile::get_frame_into(size_t frame_index, std::byte *frame_buffer, if (frame_index >= total_frames()) { throw std::runtime_error(LOCATION + "Frame number out of range"); } - std::vector frame_numbers(n_modules()); - std::vector frame_indices(n_modules(), frame_index); + std::vector frame_numbers(m_geometry.n_modules_in_roi()); + std::vector frame_indices(m_geometry.n_modules_in_roi(), + frame_index); // sync the frame numbers - if (n_modules() != 1) { // if we have more than one module - for (size_t part_idx = 0; part_idx != n_modules(); ++part_idx) { + if (m_geometry.n_modules() != 1) { // if we have more than one module + for (size_t part_idx = 0; part_idx != m_geometry.n_modules_in_roi(); + ++part_idx) { frame_numbers[part_idx] = m_subfiles[part_idx]->frame_number(frame_index); } @@ -219,19 +192,32 @@ void RawFile::get_frame_into(size_t frame_index, std::byte *frame_buffer, if (m_master.geometry().col == 1) { // get the part from each subfile and copy it to the frame - for (size_t part_idx = 0; part_idx != n_modules(); ++part_idx) { + for (size_t part_idx = 0; part_idx != m_geometry.n_modules_in_roi(); + ++part_idx) { auto corrected_idx = frame_indices[part_idx]; // This is where we start writing - auto offset = (m_geometry.module_pixel_0[part_idx].origin_y * - m_geometry.pixels_x + - m_geometry.module_pixel_0[part_idx].origin_x) * + auto offset = (m_geometry + .get_module_geometries( + m_geometry.get_modules_in_roi(part_idx)) + .origin_y * + m_geometry.pixels_x() + + m_geometry + .get_module_geometries( + m_geometry.get_modules_in_roi(part_idx)) + .origin_x) * m_master.bitdepth() / 8; - if (m_geometry.module_pixel_0[part_idx].origin_x != 0) - throw std::runtime_error(LOCATION + - " Implementation error. x pos not 0."); - + if (m_geometry + .get_module_geometries( + m_geometry.get_modules_in_roi(part_idx)) + .origin_x != 0) + throw std::runtime_error( + LOCATION + + " Implementation error. x pos not 0."); // TODO: origin + // can still + // change if roi + // changes // TODO! What if the files don't match? m_subfiles[part_idx]->seek(corrected_idx); m_subfiles[part_idx]->read_into(frame_buffer + offset, header); @@ -249,11 +235,13 @@ void RawFile::get_frame_into(size_t frame_index, std::byte *frame_buffer, auto *part_buffer = new std::byte[bytes_per_part]; - // TODO! if we have many submodules we should reorder them on the module - // level + // TODO! if we have many submodules we should reorder them on the + // module level - for (size_t part_idx = 0; part_idx != n_modules(); ++part_idx) { - auto pos = m_geometry.module_pixel_0[part_idx]; + for (size_t part_idx = 0; part_idx != m_geometry.n_modules_in_roi(); + ++part_idx) { + auto pos = m_geometry.get_module_geometries( + m_geometry.get_modules_in_roi(part_idx)); auto corrected_idx = frame_indices[part_idx]; m_subfiles[part_idx]->seek(corrected_idx); @@ -266,7 +254,7 @@ 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 * this->m_geometry.pixels_x + icol); + auto dest = (irow * this->m_geometry.pixels_x() + icol); dest = dest * m_master.bitdepth() / 8; memcpy(frame_buffer + dest, part_buffer + diff --git a/src/RawFile.test.cpp b/src/RawFile.test.cpp index 1fb441f..4e09ead 100644 --- a/src/RawFile.test.cpp +++ b/src/RawFile.test.cpp @@ -3,11 +3,15 @@ #include "aare/RawMasterFile.hpp" //needed for ROI #include +#include #include #include "test_config.hpp" +#include "test_macros.hpp" using aare::File; +using aare::RawFile; +using namespace aare; TEST_CASE("Read number of frames from a jungfrau raw file", "[.integration]") { @@ -107,25 +111,65 @@ TEST_CASE("Read frame numbers from a raw file", "[.integration]") { } TEST_CASE("Compare reading from a numpy file with a raw file", "[.files]") { - auto fpath_raw = - test_data_path() / "raw/jungfrau" / "jungfrau_single_master_0.json"; - REQUIRE(std::filesystem::exists(fpath_raw)); - auto fpath_npy = - test_data_path() / "raw/jungfrau" / "jungfrau_single_0.npy"; - REQUIRE(std::filesystem::exists(fpath_npy)); + SECTION("jungfrau data") { + auto fpath_raw = + test_data_path() / "raw/jungfrau" / "jungfrau_single_master_0.json"; + REQUIRE(std::filesystem::exists(fpath_raw)); - File raw(fpath_raw, "r"); - File npy(fpath_npy, "r"); + auto fpath_npy = + test_data_path() / "raw/jungfrau" / "jungfrau_single_0.npy"; + REQUIRE(std::filesystem::exists(fpath_npy)); - CHECK(raw.total_frames() == 10); - CHECK(npy.total_frames() == 10); + File raw(fpath_raw, "r"); + File npy(fpath_npy, "r"); - for (size_t i = 0; i < 10; ++i) { - CHECK(raw.tell() == i); + CHECK(raw.total_frames() == 10); + CHECK(npy.total_frames() == 10); + + for (size_t i = 0; i < 10; ++i) { + CHECK(raw.tell() == i); + auto raw_frame = raw.read_frame(); + auto npy_frame = npy.read_frame(); + CHECK((raw_frame.view() == npy_frame.view())); + } + } + + SECTION("eiger quad data") { + auto fpath_raw = + test_data_path() / "raw/eiger_quad_data" / + "W13_vrpreampscan_m21C_300V_800eV_vthre2000_master_0.json"; + REQUIRE(std::filesystem::exists(fpath_raw)); + + auto fpath_npy = test_data_path() / "raw/eiger_quad_data" / + "W13_vrpreampscan_m21C_300V_800eV_vthre2000.npy"; + REQUIRE(std::filesystem::exists(fpath_npy)); + + File raw(fpath_raw, "r"); + File npy(fpath_npy, "r"); + + raw.seek(20); auto raw_frame = raw.read_frame(); + auto npy_frame = npy.read_frame(); - CHECK((raw_frame.view() == npy_frame.view())); + CHECK((raw_frame.view() == npy_frame.view())); + } + SECTION("eiger data") { + auto fpath_raw = test_data_path() / "raw/eiger" / + "Lab6_20500eV_2deg_20240629_master_7.json"; + REQUIRE(std::filesystem::exists(fpath_raw)); + + auto fpath_npy = + test_data_path() / "raw/eiger" / "Lab6_20500eV_2deg_20240629_7.npy"; + REQUIRE(std::filesystem::exists(fpath_npy)); + + File raw(fpath_raw, "r"); + File npy(fpath_npy, "r"); + + auto raw_frame = raw.read_frame(); + + auto npy_frame = npy.read_frame(); + CHECK((raw_frame.view() == npy_frame.view())); } } @@ -160,6 +204,114 @@ TEST_CASE("Read multipart files", "[.integration]") { } } +struct TestParameters { + const std::string master_filename{}; + const uint8_t num_ports{}; + const size_t modules_x{}; + const size_t modules_y{}; + const size_t pixels_x{}; + const size_t pixels_y{}; + std::vector module_geometries{}; +}; + +TEST_CASE("check find_geometry", "[.integration][.files][.rawfile]") { + + auto test_parameters = GENERATE( + TestParameters{"raw/jungfrau_2modules_version6.1.2/run_master_0.raw", 2, + 1, 2, 1024, 1024, + std::vector{ + ModuleGeometry{0, 0, 512, 1024, 0, 0}, + ModuleGeometry{0, 512, 512, 1024, 0, 1}}}, + TestParameters{ + "raw/eiger_1_module_version7.0.0/eiger_1mod_master_7.json", 4, 2, 2, + 1024, 512, + std::vector{ + ModuleGeometry{0, 0, 256, 512, 0, 0}, + ModuleGeometry{512, 0, 256, 512, 0, 1}, + ModuleGeometry{0, 256, 256, 512, 1, 0}, + ModuleGeometry{512, 256, 256, 512, 1, 1}}}, + + TestParameters{"raw/jungfrau_2modules_2interfaces/run_master_0.json", 4, + 1, 4, 1024, 1024, + std::vector{ + ModuleGeometry{0, 0, 256, 1024, 0, 0}, + ModuleGeometry{0, 256, 256, 1024, 1, 0}, + ModuleGeometry{0, 512, 256, 1024, 2, 0}, + ModuleGeometry{0, 768, 256, 1024, 3, 0}}}, + TestParameters{ + "raw/eiger_quad_data/" + "W13_vthreshscan_m21C_300V_800eV_vrpre3400_master_0.json", + 2, 1, 2, 512, 512, + std::vector{ModuleGeometry{0, 256, 256, 512, 1, 0}, + ModuleGeometry{0, 0, 256, 512, 0, 0}}}); + + auto fpath = test_data_path() / test_parameters.master_filename; + + REQUIRE(std::filesystem::exists(fpath)); + + 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()); + + CHECK(geometry.modules_x() == test_parameters.modules_x); + CHECK(geometry.modules_y() == test_parameters.modules_y); + CHECK(geometry.pixels_x() == test_parameters.pixels_x); + CHECK(geometry.pixels_y() == test_parameters.pixels_y); + + REQUIRE(geometry.get_module_geometries().size() == + test_parameters.num_ports); + + // compare to data stored in header + RawFile f(fpath, "r"); + for (size_t i = 0; i < test_parameters.num_ports; ++i) { + + auto subfile1_path = f.master().data_fname(i, 0); + REQUIRE(std::filesystem::exists(subfile1_path)); + + auto header = RawFile::read_header(subfile1_path); + + CHECK(header.column == geometry.get_module_geometries(i).col_index); + CHECK(header.row == geometry.get_module_geometries(i).row_index); + + CHECK(geometry.get_module_geometries(i).height == + test_parameters.module_geometries[i].height); + CHECK(geometry.get_module_geometries(i).width == + test_parameters.module_geometries[i].width); + + CHECK(geometry.get_module_geometries(i).origin_x == + test_parameters.module_geometries[i].origin_x); + CHECK(geometry.get_module_geometries(i).origin_y == + test_parameters.module_geometries[i].origin_y); + } +} + +TEST_CASE("Open multi module file with ROI", + "[.integration][.files][.rawfile]") { + + auto fpath = test_data_path() / "raw/SingleChipROI/Data_master_0.json"; + REQUIRE(std::filesystem::exists(fpath)); + + RawFile f(fpath, "r"); + + SECTION("read 2 frames") { + REQUIRE(f.master().roi().value().width() == 256); + REQUIRE(f.master().roi().value().height() == 256); + + CHECK(f.n_modules() == 2); + + CHECK(f.n_modules_in_roi() == 1); + + auto frames = f.read_n(2); + + CHECK(frames.size() == 2); + + CHECK(frames[0].rows() == 256); + CHECK(frames[1].cols() == 256); + } +} + TEST_CASE("Read file with unordered frames", "[.integration]") { // TODO! Better explanation and error message auto fpath = test_data_path() / "mythen" / "scan242_master_3.raw"; diff --git a/src/RawMasterFile.cpp b/src/RawMasterFile.cpp index 508b396..f12b7e0 100644 --- a/src/RawMasterFile.cpp +++ b/src/RawMasterFile.cpp @@ -1,5 +1,8 @@ #include "aare/RawMasterFile.hpp" +#include "aare/RawFile.hpp" +#include "aare/logger.hpp" #include + namespace aare { RawFileNameComponents::RawFileNameComponents( @@ -138,7 +141,11 @@ size_t RawMasterFile::n_modules() const { return m_geometry.row * m_geometry.col; } -std::optional RawMasterFile::quad() const { return m_quad; } +xy RawMasterFile::udp_interfaces_per_module() const { + return m_udp_interfaces_per_module; +} + +uint8_t RawMasterFile::quad() const { return m_quad; } // optional values, these may or may not be present in the master file // and are therefore modeled as std::optional @@ -169,7 +176,10 @@ void RawMasterFile::parse_json(const std::filesystem::path &fpath) { m_type = StringTo(j["Detector Type"].get()); m_timing_mode = StringTo(j["Timing Mode"].get()); - m_geometry = {j["Geometry"]["y"], j["Geometry"]["x"]}; + 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 = j["Image Size in bytes"]; m_frames_in_file = j["Frames in File"]; @@ -258,6 +268,15 @@ void RawMasterFile::parse_json(const std::filesystem::path &fpath) { } catch (const json::out_of_range &e) { // not a scan } + try { + m_udp_interfaces_per_module = {j.at("Number of UDP Interfaces"), 1}; + } catch (const json::out_of_range &e) { + if (m_type == DetectorType::Eiger && m_quad == 1) + m_udp_interfaces_per_module = {2, 1}; + else if (m_type == DetectorType::Eiger) { + m_udp_interfaces_per_module = {1, 2}; + } + } try { ROI tmp_roi; @@ -272,14 +291,14 @@ void RawMasterFile::parse_json(const std::filesystem::path &fpath) { tmp_roi.ymin != 4294967295 || tmp_roi.ymax != 4294967295) { if (v < 7.21) { - tmp_roi.xmax++; + tmp_roi.xmax++; // why is it updated tmp_roi.ymax++; } - m_roi = tmp_roi; } } catch (const json::out_of_range &e) { + std::cout << e.what() << std::endl; // leave the optional empty } @@ -389,21 +408,54 @@ void RawMasterFile::parse_raw(const std::filesystem::path &fpath) { m_geometry = { static_cast(std::stoi(value.substr(1, pos))), static_cast(std::stoi(value.substr(pos + 1)))}; + } else if (key == "Number of UDP Interfaces") { + m_udp_interfaces_per_module = { + static_cast(std::stoi(value)), 1}; } } } + + if (m_type == DetectorType::Eiger && m_quad == 1) { + m_udp_interfaces_per_module = {2, 1}; + } else if (m_type == DetectorType::Eiger) { + m_udp_interfaces_per_module = {1, 2}; + } + if (m_pixels_x == 400 && m_pixels_y == 400) { m_type = DetectorType::Moench03_old; } - // TODO! Look for d0, d1...dn and update geometry if (m_geometry.col == 0 && m_geometry.row == 0) { - m_geometry = {1, 1}; - fmt::print("Warning: No geometry found in master file. Assuming 1x1\n"); + retrieve_geometry(); + LOG(TLogLevel::logWARNING) + << "No geometry found in master file. Retrieved geometry of " + << m_geometry.row << " x " << m_geometry.col << "\n "; } // TODO! Read files and find actual frames if (m_frames_in_file == 0) m_frames_in_file = m_total_frames_expected; } + +void RawMasterFile::retrieve_geometry() { + uint32_t module_index = 0; + uint16_t rows = 0; + uint16_t cols = 0; + // TODO use case for Eiger + + while (std::filesystem::exists(data_fname(module_index, 0))) { + + auto header = RawFile::read_header(data_fname(module_index, 0)); + + rows = std::max(rows, header.row); + cols = std::max(cols, header.column); + + ++module_index; + } + ++rows; + ++cols; + + m_geometry = {rows, cols}; +} + } // namespace aare diff --git a/src/RawMasterFile.test.cpp b/src/RawMasterFile.test.cpp index 0e75ec4..c623935 100644 --- a/src/RawMasterFile.test.cpp +++ b/src/RawMasterFile.test.cpp @@ -2,6 +2,7 @@ #include "test_config.hpp" #include +#include using namespace aare; @@ -145,6 +146,19 @@ TEST_CASE("Parse a master file in .json format", "[.integration]") { REQUIRE_FALSE(f.digital_samples()); } +TEST_CASE("Parse a master file in old .raw format", + "[.integration][.files][.rawmasterfile]") { + auto fpath = test_data_path() / + "raw/jungfrau_2modules_version6.1.2/run_master_0.raw"; + REQUIRE(std::filesystem::exists(fpath)); + RawMasterFile f(fpath); + + CHECK(f.udp_interfaces_per_module() == xy{1, 1}); + CHECK(f.n_modules() == 2); + CHECK(f.geometry().row == 2); + CHECK(f.geometry().col == 1); +} + TEST_CASE("Parse a master file in .raw format", "[.integration]") { auto fpath = diff --git a/src/geo_helpers.cpp b/src/geo_helpers.cpp deleted file mode 100644 index 96a9056..0000000 --- a/src/geo_helpers.cpp +++ /dev/null @@ -1,72 +0,0 @@ - -#include "aare/geo_helpers.hpp" -#include "fmt/core.h" - -namespace aare { - -DetectorGeometry update_geometry_with_roi(DetectorGeometry geo, aare::ROI roi) { -#ifdef AARE_VERBOSE - fmt::println("update_geometry_with_roi() called with ROI: {} {} {} {}", - roi.xmin, roi.xmax, roi.ymin, roi.ymax); - fmt::println("Geometry: {} {} {} {} {} {}", geo.modules_x, geo.modules_y, - geo.pixels_x, geo.pixels_y, geo.module_gap_row, - geo.module_gap_col); -#endif - int pos_y = 0; - int pos_y_increment = 0; - for (int row = 0; row < geo.modules_y; row++) { - int pos_x = 0; - for (int col = 0; col < geo.modules_x; col++) { - auto &m = geo.module_pixel_0[row * geo.modules_x + col]; - auto original_height = m.height; - auto original_width = m.width; - - // module is to the left of the roi - if (m.origin_x + m.width < roi.xmin) { - m.width = 0; - - // roi is in module - } else { - // here we only arrive when the roi is in or to the left of - // the module - if (roi.xmin > m.origin_x) { - m.width -= roi.xmin - m.origin_x; - } - if (roi.xmax < m.origin_x + original_width) { - m.width -= m.origin_x + original_width - roi.xmax; - } - m.origin_x = pos_x; - pos_x += m.width; - } - - if (m.origin_y + m.height < roi.ymin) { - m.height = 0; - } else { - if ((roi.ymin > m.origin_y) && - (roi.ymin < m.origin_y + m.height)) { - m.height -= roi.ymin - m.origin_y; - } - if (roi.ymax < m.origin_y + original_height) { - m.height -= m.origin_y + original_height - roi.ymax; - } - m.origin_y = pos_y; - pos_y_increment = m.height; - } -#ifdef AARE_VERBOSE - fmt::println("Module {} {} {} {}", m.origin_x, m.origin_y, m.width, - m.height); -#endif - } - // increment pos_y - pos_y += pos_y_increment; - } - - // m_rows = roi.height(); - // m_cols = roi.width(); - geo.pixels_x = roi.width(); - geo.pixels_y = roi.height(); - - return geo; -} - -} // namespace aare \ No newline at end of file diff --git a/src/geo_helpers.test.cpp b/src/geo_helpers.test.cpp deleted file mode 100644 index 48ae9cf..0000000 --- a/src/geo_helpers.test.cpp +++ /dev/null @@ -1,228 +0,0 @@ -#include "aare/File.hpp" -#include "aare/RawFile.hpp" -#include "aare/RawMasterFile.hpp" //needed for ROI - -#include -#include - -#include "aare/geo_helpers.hpp" -#include "test_config.hpp" - -TEST_CASE("Simple ROIs on one module") { - // DetectorGeometry update_geometry_with_roi(DetectorGeometry geo, aare::ROI - // roi) - aare::DetectorGeometry geo; - - aare::ModuleGeometry mod; - mod.origin_x = 0; - mod.origin_y = 0; - mod.width = 1024; - mod.height = 512; - - geo.pixels_x = 1024; - geo.pixels_y = 512; - geo.modules_x = 1; - geo.modules_y = 1; - geo.module_pixel_0.push_back(mod); - - SECTION("ROI is the whole module") { - aare::ROI roi; - roi.xmin = 0; - roi.xmax = 1024; - roi.ymin = 0; - roi.ymax = 512; - auto updated_geo = aare::update_geometry_with_roi(geo, roi); - - REQUIRE(updated_geo.pixels_x == 1024); - REQUIRE(updated_geo.pixels_y == 512); - REQUIRE(updated_geo.modules_x == 1); - REQUIRE(updated_geo.modules_y == 1); - REQUIRE(updated_geo.module_pixel_0[0].height == 512); - REQUIRE(updated_geo.module_pixel_0[0].width == 1024); - } - SECTION("ROI is the top left corner of the module") { - aare::ROI roi; - roi.xmin = 100; - roi.xmax = 200; - roi.ymin = 150; - roi.ymax = 200; - auto updated_geo = aare::update_geometry_with_roi(geo, roi); - - REQUIRE(updated_geo.pixels_x == 100); - REQUIRE(updated_geo.pixels_y == 50); - REQUIRE(updated_geo.modules_x == 1); - REQUIRE(updated_geo.modules_y == 1); - REQUIRE(updated_geo.module_pixel_0[0].height == 50); - REQUIRE(updated_geo.module_pixel_0[0].width == 100); - } - - SECTION("ROI is a small square") { - aare::ROI roi; - roi.xmin = 1000; - roi.xmax = 1010; - roi.ymin = 500; - roi.ymax = 510; - auto updated_geo = aare::update_geometry_with_roi(geo, roi); - - REQUIRE(updated_geo.pixels_x == 10); - REQUIRE(updated_geo.pixels_y == 10); - REQUIRE(updated_geo.modules_x == 1); - REQUIRE(updated_geo.modules_y == 1); - REQUIRE(updated_geo.module_pixel_0[0].height == 10); - REQUIRE(updated_geo.module_pixel_0[0].width == 10); - } - SECTION("ROI is a few columns") { - aare::ROI roi; - roi.xmin = 750; - roi.xmax = 800; - roi.ymin = 0; - roi.ymax = 512; - auto updated_geo = aare::update_geometry_with_roi(geo, roi); - - REQUIRE(updated_geo.pixels_x == 50); - REQUIRE(updated_geo.pixels_y == 512); - REQUIRE(updated_geo.modules_x == 1); - REQUIRE(updated_geo.modules_y == 1); - REQUIRE(updated_geo.module_pixel_0[0].height == 512); - REQUIRE(updated_geo.module_pixel_0[0].width == 50); - } -} - -TEST_CASE("Two modules side by side") { - // DetectorGeometry update_geometry_with_roi(DetectorGeometry geo, aare::ROI - // roi) - aare::DetectorGeometry geo; - - aare::ModuleGeometry mod; - mod.origin_x = 0; - mod.origin_y = 0; - mod.width = 1024; - mod.height = 512; - - geo.pixels_x = 2048; - geo.pixels_y = 512; - geo.modules_x = 2; - geo.modules_y = 1; - - geo.module_pixel_0.push_back(mod); - mod.origin_x = 1024; - geo.module_pixel_0.push_back(mod); - - SECTION("ROI is the whole image") { - aare::ROI roi; - roi.xmin = 0; - roi.xmax = 2048; - roi.ymin = 0; - roi.ymax = 512; - auto updated_geo = aare::update_geometry_with_roi(geo, roi); - - REQUIRE(updated_geo.pixels_x == 2048); - REQUIRE(updated_geo.pixels_y == 512); - REQUIRE(updated_geo.modules_x == 2); - REQUIRE(updated_geo.modules_y == 1); - } - SECTION("rectangle on both modules") { - aare::ROI roi; - roi.xmin = 800; - roi.xmax = 1300; - roi.ymin = 200; - roi.ymax = 499; - auto updated_geo = aare::update_geometry_with_roi(geo, roi); - - REQUIRE(updated_geo.pixels_x == 500); - REQUIRE(updated_geo.pixels_y == 299); - REQUIRE(updated_geo.modules_x == 2); - REQUIRE(updated_geo.modules_y == 1); - REQUIRE(updated_geo.module_pixel_0[0].height == 299); - REQUIRE(updated_geo.module_pixel_0[0].width == 224); - REQUIRE(updated_geo.module_pixel_0[1].height == 299); - REQUIRE(updated_geo.module_pixel_0[1].width == 276); - } -} - -TEST_CASE("Three modules side by side") { - // DetectorGeometry update_geometry_with_roi(DetectorGeometry geo, aare::ROI - // roi) - aare::DetectorGeometry geo; - aare::ROI roi; - roi.xmin = 700; - roi.xmax = 2500; - roi.ymin = 0; - roi.ymax = 123; - - aare::ModuleGeometry mod; - mod.origin_x = 0; - mod.origin_y = 0; - mod.width = 1024; - mod.height = 512; - - geo.pixels_x = 3072; - geo.pixels_y = 512; - geo.modules_x = 3; - geo.modules_y = 1; - - geo.module_pixel_0.push_back(mod); - mod.origin_x = 1024; - geo.module_pixel_0.push_back(mod); - mod.origin_x = 2048; - geo.module_pixel_0.push_back(mod); - - auto updated_geo = aare::update_geometry_with_roi(geo, roi); - - REQUIRE(updated_geo.pixels_x == 1800); - REQUIRE(updated_geo.pixels_y == 123); - REQUIRE(updated_geo.modules_x == 3); - REQUIRE(updated_geo.modules_y == 1); - REQUIRE(updated_geo.module_pixel_0[0].height == 123); - REQUIRE(updated_geo.module_pixel_0[0].width == 324); - REQUIRE(updated_geo.module_pixel_0[1].height == 123); - REQUIRE(updated_geo.module_pixel_0[1].width == 1024); - REQUIRE(updated_geo.module_pixel_0[2].height == 123); - REQUIRE(updated_geo.module_pixel_0[2].width == 452); -} - -TEST_CASE("Four modules as a square") { - // DetectorGeometry update_geometry_with_roi(DetectorGeometry geo, aare::ROI - // roi) - aare::DetectorGeometry geo; - aare::ROI roi; - roi.xmin = 500; - roi.xmax = 2000; - roi.ymin = 500; - roi.ymax = 600; - - aare::ModuleGeometry mod; - mod.origin_x = 0; - mod.origin_y = 0; - mod.width = 1024; - mod.height = 512; - - geo.pixels_x = 2048; - geo.pixels_y = 1024; - geo.modules_x = 2; - geo.modules_y = 2; - - geo.module_pixel_0.push_back(mod); - mod.origin_x = 1024; - geo.module_pixel_0.push_back(mod); - mod.origin_x = 0; - mod.origin_y = 512; - geo.module_pixel_0.push_back(mod); - mod.origin_x = 1024; - geo.module_pixel_0.push_back(mod); - - auto updated_geo = aare::update_geometry_with_roi(geo, roi); - - REQUIRE(updated_geo.pixels_x == 1500); - REQUIRE(updated_geo.pixels_y == 100); - REQUIRE(updated_geo.modules_x == 2); - REQUIRE(updated_geo.modules_y == 2); - REQUIRE(updated_geo.module_pixel_0[0].height == 12); - REQUIRE(updated_geo.module_pixel_0[0].width == 524); - REQUIRE(updated_geo.module_pixel_0[1].height == 12); - REQUIRE(updated_geo.module_pixel_0[1].width == 976); - REQUIRE(updated_geo.module_pixel_0[2].height == 88); - REQUIRE(updated_geo.module_pixel_0[2].width == 524); - REQUIRE(updated_geo.module_pixel_0[3].height == 88); - REQUIRE(updated_geo.module_pixel_0[3].width == 976); -} \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1906508..994dc47 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -40,5 +40,8 @@ target_sources(tests PRIVATE ${TestSources} ) #configure a header to pass test file paths get_filename_component(TEST_FILE_PATH ${PROJECT_SOURCE_DIR}/data ABSOLUTE) configure_file(test_config.hpp.in test_config.hpp) -target_include_directories(tests PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) +target_include_directories(tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) + + + diff --git a/tests/friend_test.hpp b/tests/friend_test.hpp new file mode 100644 index 0000000..7d16000 --- /dev/null +++ b/tests/friend_test.hpp @@ -0,0 +1,4 @@ +#define FRIEND_TEST(test_name) friend void test_name##_impl(); + +#define TEST_CASE_PRIVATE_FWD(test_name) \ + void test_name##_impl(); // foward declaration diff --git a/tests/test_macros.hpp b/tests/test_macros.hpp new file mode 100644 index 0000000..0d9f59d --- /dev/null +++ b/tests/test_macros.hpp @@ -0,0 +1,20 @@ +#include + +#include +#include +#include + +#define TEST_CASE_PRIVATE(namespace_name, test_name, test_name_str, \ + test_tags_str) \ + namespace namespace_name { \ + void test_name##_impl(); \ + \ + struct test_name##_Invoker : Catch::ITestInvoker { \ + void invoke() const override { test_name##_impl(); } \ + }; \ + Catch::AutoReg \ + autoReg_##test_name(Catch::Detail::make_unique(), \ + Catch::SourceLineInfo(__FILE__, __LINE__), "", \ + Catch::NameAndTags{test_name_str, test_tags_str}); \ + \ + void test_name##_impl()