mirror of
https://github.com/slsdetectorgroup/aare.git
synced 2026-09-07 12:12:37 +02:00
Dev/filereading for disabled udp (#342)
- handles file reading of disabled udp ports - adds members disabled_udp_ports in master file as an optional - adds member diasbled_udp_port_types in master file as an optional - Treats enabled udp ports as ROIs - merges/combines consecutive ROis into one ### To discuss: - right now can only handle either ROI or disabled udp ports (disabled udp ports has higher precedence) - Should Frames in File be removed from Master File as value is nonsense for disabled udp ports --------- Co-authored-by: Erik Fröjdh <erik.frojdh@psi.ch>
This commit is contained in:
@@ -366,6 +366,7 @@ set(PUBLICHEADERS
|
||||
include/aare/hist/PixelHistogramImpl.hpp
|
||||
include/aare/hist/PedestalTrackingPixelHistogram.hpp
|
||||
include/aare/GainMap.hpp
|
||||
include/aare/ROI.hpp
|
||||
include/aare/ROIGeometry.hpp
|
||||
include/aare/DetectorGeometry.hpp
|
||||
include/aare/JungfrauDataFile.hpp
|
||||
@@ -389,6 +390,7 @@ set(SourceFiles
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/CtbRawFile.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/decode.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/defs.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ROI.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ROIGeometry.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/DetectorGeometry.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/hist/PedestalTrackingPixelHistogram.cpp
|
||||
@@ -477,6 +479,7 @@ if(AARE_TESTS)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyHelpers.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RawFile.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RawSubFile.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/ROI.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/task.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/to_string.test.cpp)
|
||||
target_sources(tests PRIVATE ${TestSources})
|
||||
|
||||
@@ -37,6 +37,10 @@
|
||||
- ``NDView<T, Ndim>`` now converts to ``NDView<const T, Ndim>``;
|
||||
``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
|
||||
|
||||
@@ -54,6 +58,7 @@
|
||||
- ``aare.transfrom.Matterhorn10Transform`` reshapes data such that first dimension is number of counters
|
||||
- Added support for len() for files. Returns the number of frames
|
||||
- Added support for direct subtraction of Pedestal from numpy array
|
||||
- Added support to read files with disabled udp ports
|
||||
|
||||
### Bugfixes:
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "aare/ClusterVector.hpp"
|
||||
#include "aare/GainMap.hpp"
|
||||
#include "aare/NDArray.hpp"
|
||||
#include "aare/defs.hpp"
|
||||
#include "aare/ROI.hpp"
|
||||
#include "aare/logger.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
#pragma once
|
||||
#include "aare/ROI.hpp"
|
||||
#include "aare/ROIGeometry.hpp"
|
||||
#include "aare/RawMasterFile.hpp" //ROI refactor away
|
||||
#include "aare/defs.hpp"
|
||||
#include <iostream>
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -114,6 +115,8 @@ class DetectorGeometry {
|
||||
size_t modules_x() const;
|
||||
size_t modules_y() const;
|
||||
|
||||
xy udp_interfaces_per_module() const;
|
||||
|
||||
const std::vector<ModuleGeometry> &get_module_geometries() const;
|
||||
|
||||
const ModuleGeometry &get_module_geometries(const size_t index) const;
|
||||
@@ -125,6 +128,7 @@ class DetectorGeometry {
|
||||
size_t m_modules_y{};
|
||||
size_t m_pixels_x{};
|
||||
size_t m_pixels_y{};
|
||||
xy m_udp_interfaces_per_module{};
|
||||
static constexpr ModuleConfig cfg{0, 0};
|
||||
|
||||
// TODO: maybe remove - should be a member in ROIGeometry - in particular
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
#pragma once
|
||||
#include "aare/defs.hpp"
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace aare {
|
||||
|
||||
class ROIGeometry; // forward declaration to avoid circular dependency
|
||||
class DetectorGeometry; // forward declaration to avoid circular dependency
|
||||
|
||||
struct ROI {
|
||||
ssize_t xmin{};
|
||||
ssize_t xmax{};
|
||||
ssize_t ymin{};
|
||||
ssize_t ymax{};
|
||||
|
||||
ssize_t height() const { return ymax - ymin; }
|
||||
ssize_t width() const { return xmax - xmin; }
|
||||
bool contains(ssize_t x, ssize_t y) const {
|
||||
return x >= xmin && x < xmax && y >= ymin && y < ymax;
|
||||
}
|
||||
|
||||
bool operator==(const ROI &other) const {
|
||||
return xmin == other.xmin && xmax == other.xmax && ymin == other.ymin &&
|
||||
ymax == other.ymax;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Merge all consecutive ROIs in a vector into a single ROI.
|
||||
* @param rois vector of ROIs to merge
|
||||
* @return vector of merged ROIs
|
||||
* @tparam horizontally_aligned true if the ROIs are horizontally aligned, false
|
||||
* otherwise
|
||||
* @tparam vertically_aligned true if the ROIs are vertically aligned, false
|
||||
* otherwise
|
||||
*/
|
||||
template <bool horizontally_aligned = false, bool vertically_aligned = false>
|
||||
std::vector<ROI> merge_consecutive_rois(std::vector<ROI> &rois) {
|
||||
|
||||
if constexpr (horizontally_aligned && vertically_aligned) {
|
||||
throw std::runtime_error(
|
||||
LOCATION + "Vector of the same ROI? Cannot merge ROIs both "
|
||||
"horizontally and vertically at the same time.");
|
||||
}
|
||||
|
||||
if (rois.empty()) {
|
||||
return {};
|
||||
}
|
||||
if (rois.size() == 1) {
|
||||
return rois;
|
||||
}
|
||||
|
||||
auto merge_along_x = [](std::vector<ROI> in_rois) {
|
||||
std::sort(in_rois.begin(), in_rois.end(),
|
||||
[](const ROI &a, const ROI &b) {
|
||||
return (a.ymin != b.ymin) ? (a.ymin < b.ymin)
|
||||
: (a.xmin < b.xmin);
|
||||
}); // N log (N)
|
||||
|
||||
std::vector<ROI> merged_rois;
|
||||
merged_rois.reserve(in_rois.size());
|
||||
merged_rois.push_back(in_rois[0]);
|
||||
|
||||
for (size_t i = 1; i < in_rois.size(); ++i) {
|
||||
auto &last = merged_rois.back();
|
||||
const auto ¤t = in_rois[i];
|
||||
if (last.ymin == current.ymin && last.ymax == current.ymax &&
|
||||
last.xmax == current.xmin) {
|
||||
// merge
|
||||
last.xmax = current.xmax;
|
||||
} else {
|
||||
merged_rois.push_back(current);
|
||||
}
|
||||
}
|
||||
return merged_rois;
|
||||
};
|
||||
|
||||
auto merge_along_y = [](std::vector<ROI> in_rois) {
|
||||
std::sort(in_rois.begin(), in_rois.end(),
|
||||
[](const ROI &a, const ROI &b) {
|
||||
return (a.xmin != b.xmin) ? (a.xmin < b.xmin)
|
||||
: (a.ymin < b.ymin);
|
||||
});
|
||||
|
||||
std::vector<ROI> merged_rois;
|
||||
merged_rois.reserve(in_rois.size());
|
||||
merged_rois.push_back(in_rois[0]);
|
||||
|
||||
for (size_t i = 1; i < in_rois.size(); ++i) {
|
||||
auto &last = merged_rois.back();
|
||||
const auto ¤t = in_rois[i];
|
||||
if (last.xmin == current.xmin && last.xmax == current.xmax &&
|
||||
last.ymax == current.ymin) {
|
||||
last.ymax = current.ymax;
|
||||
} else {
|
||||
merged_rois.push_back(current);
|
||||
}
|
||||
}
|
||||
return merged_rois;
|
||||
};
|
||||
|
||||
if constexpr (horizontally_aligned) {
|
||||
return merge_along_y(rois); // one sort + one pass
|
||||
} else if constexpr (vertically_aligned) {
|
||||
return merge_along_x(rois); // one sort + one pass
|
||||
} else {
|
||||
return merge_along_y(merge_along_x(rois)); // generic case: two passes
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the ROI covers the entire detector geometry
|
||||
* @param roi Region of interest
|
||||
* @param geometry Detector geometry
|
||||
* @return true if the ROI covers the entire detector geometry, false otherwise
|
||||
*/
|
||||
bool complete_ROI(const ROI &roi, const DetectorGeometry &geometry);
|
||||
|
||||
bool complete_ROI(const std::vector<ROI> &rois,
|
||||
const DetectorGeometry &geometry);
|
||||
|
||||
bool complete_ROI(const ROIGeometry &roi, const DetectorGeometry &geometry);
|
||||
|
||||
bool complete_ROI(const std::vector<ROIGeometry> &rois,
|
||||
const DetectorGeometry &geometry);
|
||||
|
||||
} // namespace aare
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include "aare/DetectorGeometry.hpp"
|
||||
#include "aare/defs.hpp"
|
||||
#include "aare/ROI.hpp"
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace aare {
|
||||
|
||||
@@ -17,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;
|
||||
|
||||
|
||||
@@ -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,17 +29,16 @@ 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<ROIGeometry> m_ROI_geometries;
|
||||
|
||||
/// @brief total number of frames in file
|
||||
size_t m_frames_in_file{};
|
||||
|
||||
public:
|
||||
/**
|
||||
* @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;
|
||||
@@ -122,7 +120,7 @@ class RawFile : public FileInterface {
|
||||
size_t n_modules() const;
|
||||
|
||||
/**
|
||||
* @brief number of ROIs defined
|
||||
* @brief number of ROIs defined (always 1 for complete ROI)
|
||||
*/
|
||||
size_t num_rois() const;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
#pragma once
|
||||
#include "aare/defs.hpp"
|
||||
#include "aare/DetectorGeometry.hpp"
|
||||
#include "aare/ROI.hpp"
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
@@ -88,7 +89,8 @@ class RawMasterFile {
|
||||
std::optional<std::chrono::nanoseconds> 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{};
|
||||
@@ -109,7 +111,20 @@ class RawMasterFile {
|
||||
std::optional<size_t> m_number_of_rows;
|
||||
std::optional<uint8_t> m_counter_mask;
|
||||
|
||||
std::optional<std::vector<ROI>> m_rois;
|
||||
/// @brief index of disabled UDP ports - index relative to UDP_port_types
|
||||
std::vector<size_t> m_disabled_udp_ports{};
|
||||
|
||||
/// @brief udp port types
|
||||
std::optional<std::vector<UDPPortPosition>> m_udp_port_types{};
|
||||
|
||||
/// @brief ROIs defined in master file or derived from disabled UDP ports
|
||||
std::vector<ROI> m_rois;
|
||||
|
||||
/// @brief Detector geometry - geometry for each module
|
||||
DetectorGeometry m_geometry{};
|
||||
|
||||
/// @brief ROI geometries
|
||||
std::vector<ROIGeometry> m_ROI_geometries;
|
||||
|
||||
public:
|
||||
RawMasterFile(const std::filesystem::path &fpath);
|
||||
@@ -131,10 +146,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<ROIGeometry> &roi_geometries() const;
|
||||
|
||||
ReadoutMode get_reading_mode() const;
|
||||
|
||||
std::optional<size_t> analog_samples() const;
|
||||
@@ -143,9 +162,21 @@ class RawMasterFile {
|
||||
std::optional<size_t> number_of_rows() const;
|
||||
std::optional<uint8_t> counter_mask() const;
|
||||
|
||||
std::optional<std::vector<ROI>> rois() const;
|
||||
/// @brief Get the types of UDP ports
|
||||
/// @return Optional vector of UDP port types as strings (only present for
|
||||
/// masterfile version >= 8.1)
|
||||
std::optional<std::vector<UDPPortPosition>> udp_port_types() const;
|
||||
|
||||
std::optional<ROI> roi() const;
|
||||
/// @brief Get the indices of disabled UDP ports
|
||||
/// @return vector of indices of disabled UDP ports (empty if none are
|
||||
/// disabled)
|
||||
std::vector<size_t> disabled_udp_ports() const;
|
||||
|
||||
std::vector<ROI> rois() 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;
|
||||
|
||||
@@ -157,6 +188,7 @@ 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();
|
||||
};
|
||||
|
||||
|
||||
+7
-13
@@ -94,19 +94,6 @@ template <typename T> struct t_xy {
|
||||
};
|
||||
using xy = t_xy<uint32_t>;
|
||||
|
||||
struct ROI {
|
||||
ssize_t xmin{};
|
||||
ssize_t xmax{};
|
||||
ssize_t ymin{};
|
||||
ssize_t ymax{};
|
||||
|
||||
ssize_t height() const { return ymax - ymin; }
|
||||
ssize_t width() const { return xmax - xmin; }
|
||||
bool contains(ssize_t x, ssize_t y) const {
|
||||
return x >= xmin && x < xmax && y >= ymin && y < ymax;
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief Chip specifications for Matterhorn1
|
||||
struct Matterhorn10 {
|
||||
constexpr static size_t nRows = 256;
|
||||
@@ -315,6 +302,13 @@ enum class corner : int {
|
||||
cBottomRight = 3
|
||||
};
|
||||
|
||||
enum class UDPPortPosition : uint8_t {
|
||||
LEFT = 0,
|
||||
RIGHT = 1,
|
||||
TOP = 2,
|
||||
BOTTOM = 3
|
||||
};
|
||||
|
||||
enum class TimingMode { Auto, Trigger };
|
||||
enum class FrameDiscardPolicy { NoDiscard, Discard, DiscardPartial };
|
||||
|
||||
|
||||
@@ -27,6 +27,8 @@ from ._aare import hitmap
|
||||
from ._aare import ROI
|
||||
from ._aare import corner
|
||||
|
||||
from ._aare import UDPPortPosition
|
||||
|
||||
# from ._aare import ClusterFinderMT, ClusterCollector, ClusterFileSink, ClusterVector_i
|
||||
|
||||
from ._version import __version__
|
||||
|
||||
@@ -27,4 +27,49 @@ void define_defs_bindings(py::module &m) {
|
||||
moench05.attr("nRows") = Moench05::nRows;
|
||||
moench05.attr("nCols") = Moench05::nCols;
|
||||
moench05.attr("adcNumbers") = Moench05::adcNumbers;
|
||||
|
||||
py::class_<ROI>(m, "ROI")
|
||||
.def(py::init<>())
|
||||
.def(py::init<ssize_t, ssize_t, ssize_t, ssize_t>(), py::arg("xmin"),
|
||||
py::arg("xmax"), py::arg("ymin"), py::arg("ymax"))
|
||||
.def_readwrite("xmin", &ROI::xmin)
|
||||
.def_readwrite("xmax", &ROI::xmax)
|
||||
.def_readwrite("ymin", &ROI::ymin)
|
||||
.def_readwrite("ymax", &ROI::ymax)
|
||||
.def("__str__",
|
||||
[](const ROI &self) {
|
||||
return fmt::format("ROI: xmin: {} xmax: {} ymin: {} ymax: {}",
|
||||
self.xmin, self.xmax, self.ymin, self.ymax);
|
||||
})
|
||||
.def("__repr__",
|
||||
[](const ROI &self) {
|
||||
return fmt::format(
|
||||
"<ROI: xmin: {} xmax: {} ymin: {} ymax: {}>", self.xmin,
|
||||
self.xmax, self.ymin, self.ymax);
|
||||
})
|
||||
.def("__iter__",
|
||||
[](const ROI &self) {
|
||||
return py::make_iterator(&self.xmin, &self.ymax + 1); // NOLINT
|
||||
})
|
||||
|
||||
.def("__eq__", [](const ROI &self, const ROI &other) {
|
||||
return self.xmin == other.xmin && self.xmax == other.xmax &&
|
||||
self.ymin == other.ymin && self.ymax == other.ymax;
|
||||
});
|
||||
|
||||
py::enum_<DetectorType>(m, "DetectorType")
|
||||
.value("Jungfrau", DetectorType::Jungfrau)
|
||||
.value("Eiger", DetectorType::Eiger)
|
||||
.value("Mythen3", DetectorType::Mythen3)
|
||||
.value("Moench", DetectorType::Moench)
|
||||
.value("Moench03", DetectorType::Moench03)
|
||||
.value("Moench03_old", DetectorType::Moench03_old)
|
||||
.value("ChipTestBoard", DetectorType::ChipTestBoard)
|
||||
.value("Unknown", DetectorType::Unknown);
|
||||
|
||||
py::enum_<UDPPortPosition>(m, "UDPPortPosition")
|
||||
.value("LEFT", UDPPortPosition::LEFT)
|
||||
.value("RIGHT", UDPPortPosition::RIGHT)
|
||||
.value("TOP", UDPPortPosition::TOP)
|
||||
.value("BOTTOM", UDPPortPosition::BOTTOM);
|
||||
}
|
||||
|
||||
+1
-33
@@ -2,6 +2,7 @@
|
||||
#include "aare/CtbRawFile.hpp"
|
||||
#include "aare/File.hpp"
|
||||
#include "aare/Frame.hpp"
|
||||
#include "aare/ROI.hpp"
|
||||
#include "aare/RawFile.hpp"
|
||||
#include "aare/RawMasterFile.hpp"
|
||||
#include "aare/RawSubFile.hpp"
|
||||
@@ -27,16 +28,6 @@ using namespace ::aare;
|
||||
|
||||
void define_file_io_bindings(py::module &m) {
|
||||
|
||||
py::enum_<DetectorType>(m, "DetectorType")
|
||||
.value("Jungfrau", DetectorType::Jungfrau)
|
||||
.value("Eiger", DetectorType::Eiger)
|
||||
.value("Mythen3", DetectorType::Mythen3)
|
||||
.value("Moench", DetectorType::Moench)
|
||||
.value("Moench03", DetectorType::Moench03)
|
||||
.value("Moench03_old", DetectorType::Moench03_old)
|
||||
.value("ChipTestBoard", DetectorType::ChipTestBoard)
|
||||
.value("Unknown", DetectorType::Unknown);
|
||||
|
||||
PYBIND11_NUMPY_DTYPE(DetectorHeader, frameNumber, expLength, packetNumber,
|
||||
bunchId, timestamp, modId, row, column, reserved,
|
||||
debug, roundRNumber, detType, version, packetMask);
|
||||
@@ -170,28 +161,5 @@ void define_file_io_bindings(py::module &m) {
|
||||
.def_property_readonly("stop", &ScanParameters::stop)
|
||||
.def_property_readonly("step", &ScanParameters::step);
|
||||
|
||||
py::class_<ROI>(m, "ROI")
|
||||
.def(py::init<>())
|
||||
.def(py::init<ssize_t, ssize_t, ssize_t, ssize_t>(), py::arg("xmin"),
|
||||
py::arg("xmax"), py::arg("ymin"), py::arg("ymax"))
|
||||
.def_readwrite("xmin", &ROI::xmin)
|
||||
.def_readwrite("xmax", &ROI::xmax)
|
||||
.def_readwrite("ymin", &ROI::ymin)
|
||||
.def_readwrite("ymax", &ROI::ymax)
|
||||
.def("__str__",
|
||||
[](const ROI &self) {
|
||||
return fmt::format("ROI: xmin: {} xmax: {} ymin: {} ymax: {}",
|
||||
self.xmin, self.xmax, self.ymin, self.ymax);
|
||||
})
|
||||
.def("__repr__",
|
||||
[](const ROI &self) {
|
||||
return fmt::format(
|
||||
"<ROI: xmin: {} xmax: {} ymin: {} ymax: {}>", self.xmin,
|
||||
self.xmax, self.ymin, self.ymax);
|
||||
})
|
||||
.def("__iter__", [](const ROI &self) {
|
||||
return py::make_iterator(&self.xmin, &self.ymax + 1); // NOLINT
|
||||
});
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
@@ -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,
|
||||
@@ -107,6 +108,33 @@ void define_raw_master_file_bindings(py::module &m) {
|
||||
return std::nullopt;
|
||||
}
|
||||
})
|
||||
.def_property_readonly("rois", &RawMasterFile::rois, R"(
|
||||
Get the ROIs defined in the master file
|
||||
|
||||
Returns
|
||||
----------
|
||||
List[ROI]
|
||||
List of ROIs (default complete ROI)
|
||||
)")
|
||||
.def_property_readonly("udp_port_types", &RawMasterFile::udp_port_types,
|
||||
R"(
|
||||
Get the types of UDP ports
|
||||
|
||||
Returns
|
||||
----------
|
||||
Optional[List[UDPPortPosition]]
|
||||
Optional vector of UDP port types as strings (only present for
|
||||
masterfile version >= 8.1)
|
||||
)")
|
||||
.def_property_readonly("disabled_udp_ports",
|
||||
&RawMasterFile::disabled_udp_ports, R"(
|
||||
Get the indices of disabled UDP ports
|
||||
|
||||
Returns
|
||||
----------
|
||||
List[int]
|
||||
Vector of disabled UDP port indices relative to UDP port types (empty if none are disabled)
|
||||
)")
|
||||
.def_property_readonly("period", [](RawMasterFile &self) {
|
||||
double seconds =
|
||||
std::chrono::duration<double>(self.period()).count();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
import pytest
|
||||
from aare import RawFile
|
||||
from aare import RawFile, ROI, UDPPortPosition
|
||||
import numpy as np
|
||||
|
||||
@pytest.mark.withdata
|
||||
@@ -110,3 +110,41 @@ def test_read_rawfile_eiger_and_compare_to_numpy(test_data_path):
|
||||
header, image1 = f.read_frame()
|
||||
|
||||
assert (image == image1).all()
|
||||
|
||||
|
||||
@pytest.mark.withdata
|
||||
def test_read_eiger_udp_port_disabled(test_data_path):
|
||||
with RawFile(test_data_path / "raw/eiger/one_udp_port_disabled_master_0.json") as f:
|
||||
_, frame = f.read_rois()
|
||||
|
||||
assert(len(frame) == 2)
|
||||
assert frame[0].shape == (256, 512)
|
||||
assert frame[1].shape == (256, 1024)
|
||||
assert f.master.udp_port_types == [UDPPortPosition.LEFT, UDPPortPosition.RIGHT]
|
||||
rois = f.master.rois
|
||||
assert len(rois) == 2
|
||||
assert rois[0] == ROI(512, 1024, 0, 256)
|
||||
assert rois[1] == ROI(0, 1024, 256, 512)
|
||||
|
||||
with RawFile(test_data_path / "raw/eiger/quad_eiger_disabled_bottom_port_master_0.json") as f:
|
||||
_, frame = f.read_frame()
|
||||
|
||||
assert frame.shape == (256, 512)
|
||||
assert(f.master.disabled_udp_ports == [1])
|
||||
assert f.master.udp_port_types == [UDPPortPosition.TOP, UDPPortPosition.BOTTOM]
|
||||
rois = f.master.rois
|
||||
assert len(rois) == 1
|
||||
assert rois[0] == ROI(0, 512, 256, 512)
|
||||
|
||||
with RawFile(test_data_path / "raw/eiger/2_modules_eiger_disabled_udp_port_master_0.json") as f:
|
||||
_, frame = f.read_rois()
|
||||
|
||||
assert(len(frame) == 2)
|
||||
assert frame[0].shape == (512, 512)
|
||||
assert frame[1].shape == (512, 512)
|
||||
assert (f.master.disabled_udp_ports == [1, 3, 5, 7])
|
||||
assert f.master.udp_port_types == [UDPPortPosition.LEFT, UDPPortPosition.RIGHT]
|
||||
rois = f.master.rois
|
||||
assert len(rois) == 2
|
||||
assert rois[0] == ROI(0, 512, 0, 512)
|
||||
assert rois[1] == ROI(1024, 1536, 0, 512)
|
||||
@@ -11,7 +11,8 @@ 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) {
|
||||
const bool quad)
|
||||
: m_udp_interfaces_per_module(udp_interfaces_per_module) {
|
||||
|
||||
size_t num_modules = geometry.col * geometry.row;
|
||||
module_geometries.reserve(num_modules);
|
||||
@@ -43,6 +44,10 @@ DetectorGeometry::DetectorGeometry(const xy &geometry,
|
||||
m_pixels_y += static_cast<size_t>((geometry.row - 1) * cfg.module_gap_row);
|
||||
}
|
||||
|
||||
xy DetectorGeometry::udp_interfaces_per_module() const {
|
||||
return m_udp_interfaces_per_module;
|
||||
}
|
||||
|
||||
size_t DetectorGeometry::n_modules() const { return m_modules_x * m_modules_y; }
|
||||
|
||||
size_t DetectorGeometry::pixels_x() const { return m_pixels_x; }
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
#include "aare/ROI.hpp"
|
||||
#include "aare/DetectorGeometry.hpp"
|
||||
#include "aare/ROIGeometry.hpp"
|
||||
|
||||
namespace aare {
|
||||
|
||||
/**
|
||||
* @brief Check if the ROI covers the entire detector geometry
|
||||
* @param roi Region of interest
|
||||
* @param geometry Detector geometry
|
||||
* @return true if the ROI covers the entire detector geometry, false otherwise
|
||||
*/
|
||||
bool complete_ROI(const ROI &roi, const DetectorGeometry &geometry) {
|
||||
return roi.xmin == 0 &&
|
||||
roi.xmax == static_cast<ssize_t>(geometry.pixels_x()) &&
|
||||
roi.ymin == 0 &&
|
||||
roi.ymax == static_cast<ssize_t>(geometry.pixels_y());
|
||||
}
|
||||
|
||||
bool complete_ROI(const std::vector<ROI> &rois,
|
||||
const DetectorGeometry &geometry) {
|
||||
if (rois.empty() or rois.size() > 1) {
|
||||
return false;
|
||||
} else {
|
||||
return complete_ROI(rois[0], geometry);
|
||||
}
|
||||
}
|
||||
|
||||
bool complete_ROI(const ROIGeometry &roi, const DetectorGeometry &geometry) {
|
||||
return roi.pixels_x() == geometry.pixels_x() &&
|
||||
roi.pixels_y() == geometry.pixels_y();
|
||||
}
|
||||
|
||||
bool complete_ROI(const std::vector<ROIGeometry> &rois,
|
||||
const DetectorGeometry &geometry) {
|
||||
if (rois.empty() or rois.size() > 1) {
|
||||
return false;
|
||||
} else {
|
||||
return complete_ROI(rois[0], geometry);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace aare
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "aare/ROI.hpp"
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
namespace aare {
|
||||
|
||||
TEST_CASE("merge ROIs", "[utility_functions]") {
|
||||
|
||||
SECTION("not fully contiguous") {
|
||||
|
||||
std::vector<ROI> rois = {ROI{20, 30, 50, 60}, ROI{20, 30, 40, 50},
|
||||
ROI{10, 20, 40, 50}};
|
||||
|
||||
auto merged_rois = merge_consecutive_rois<false, false>(rois);
|
||||
|
||||
REQUIRE(merged_rois.size() == 2);
|
||||
|
||||
REQUIRE(merged_rois[0] == ROI{10, 30, 40, 50});
|
||||
REQUIRE(merged_rois[1] == ROI{20, 30, 50, 60});
|
||||
}
|
||||
SECTION("complex merge") {
|
||||
|
||||
std::vector<ROI> rois = {ROI{40, 50, 20, 30}, ROI{10, 20, 30, 40},
|
||||
ROI{10, 20, 50, 60}, ROI{20, 30, 30, 40},
|
||||
ROI{60, 70, 30, 40}, ROI{60, 70, 20, 30},
|
||||
ROI{20, 30, 20, 30}, ROI{10, 20, 20, 30}};
|
||||
|
||||
auto merged_rois = merge_consecutive_rois<false, false>(rois);
|
||||
|
||||
REQUIRE(merged_rois.size() == 4);
|
||||
|
||||
REQUIRE(merged_rois[0] == ROI{10, 30, 20, 40});
|
||||
REQUIRE(merged_rois[1] == ROI{10, 20, 50, 60});
|
||||
REQUIRE(merged_rois[2] == ROI{40, 50, 20, 30});
|
||||
REQUIRE(merged_rois[3] == ROI{60, 70, 20, 40});
|
||||
}
|
||||
SECTION("horizontally aligned") {
|
||||
std::vector<ROI> rois = {ROI{10, 20, 30, 40}, ROI{10, 20, 50, 60},
|
||||
ROI{10, 20, 20, 30}};
|
||||
|
||||
auto merged_rois = merge_consecutive_rois<true, false>(rois);
|
||||
|
||||
REQUIRE(merged_rois.size() == 2);
|
||||
REQUIRE(merged_rois[0] == ROI{10, 20, 20, 40});
|
||||
REQUIRE(merged_rois[1] == ROI{10, 20, 50, 60});
|
||||
}
|
||||
SECTION("vertically aligned") {
|
||||
std::vector<ROI> rois = {ROI{10, 20, 30, 40}, ROI{30, 40, 30, 40},
|
||||
ROI{20, 30, 30, 40}};
|
||||
|
||||
auto merged_rois = merge_consecutive_rois<false, true>(rois);
|
||||
|
||||
REQUIRE(merged_rois.size() == 1);
|
||||
REQUIRE(merged_rois[0] == ROI{10, 40, 30, 40});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace aare
|
||||
+14
-15
@@ -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();
|
||||
}
|
||||
|
||||
+108
-109
@@ -2,11 +2,13 @@
|
||||
#include "aare/RawFile.hpp"
|
||||
#include "aare/DetectorGeometry.hpp"
|
||||
#include "aare/PixelMap.hpp"
|
||||
#include "aare/ROI.hpp"
|
||||
#include "aare/ROIGeometry.hpp"
|
||||
#include "aare/algorithm.hpp"
|
||||
#include "aare/defs.hpp"
|
||||
#include "aare/logger.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fmt/format.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -15,34 +17,44 @@ using json = nlohmann::json;
|
||||
namespace aare {
|
||||
|
||||
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_master(fname), m_frames_in_file(m_master.frames_in_file()) {
|
||||
|
||||
m_mode = mode;
|
||||
|
||||
m_subfiles.resize(m_master.rois().has_value() ? m_master.rois()->size()
|
||||
: 1);
|
||||
|
||||
if (mode == "r") {
|
||||
if (m_master.rois().has_value()) {
|
||||
m_ROI_geometries.reserve(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;
|
||||
}
|
||||
m_subfiles.resize(m_master.roi_geometries().size());
|
||||
// iterate over all ROIS
|
||||
const size_t num_rois = m_master.roi_geometries().size();
|
||||
|
||||
} else {
|
||||
// no ROI use full detector
|
||||
m_ROI_geometries.reserve(1);
|
||||
m_ROI_geometries.push_back(ROIGeometry(m_geometry));
|
||||
open_subfiles(0);
|
||||
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().empty()) {
|
||||
// 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
|
||||
std::vector<size_t> min_subfiles_per_roi(m_subfiles.size());
|
||||
std::transform(
|
||||
m_subfiles.begin(), m_subfiles.end(),
|
||||
min_subfiles_per_roi.begin(), [](const auto &subfiles) {
|
||||
return std::min_element(
|
||||
subfiles.begin(), subfiles.end(),
|
||||
[](const auto &raw_subfile1,
|
||||
const auto &raw_subfile2) {
|
||||
return raw_subfile1->frames_in_file() <
|
||||
raw_subfile2->frames_in_file();
|
||||
})
|
||||
->get()
|
||||
->frames_in_file();
|
||||
});
|
||||
m_frames_in_file = *std::min_element(min_subfiles_per_roi.begin(),
|
||||
min_subfiles_per_roi.end());
|
||||
|
||||
LOG(logDEBUG) << "Frames in file: " << m_frames_in_file;
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error(LOCATION +
|
||||
@@ -52,24 +64,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<Frame> 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<Frame> frames;
|
||||
frames.reserve(num_rois);
|
||||
@@ -83,18 +85,17 @@ std::vector<Frame> RawFile::read_rois() {
|
||||
}
|
||||
|
||||
Frame RawFile::read_frame() {
|
||||
if (m_master.rois().has_value() && m_master.rois()->size() > 1) {
|
||||
throw std::runtime_error(LOCATION +
|
||||
"Multiple ROIs defined in the master file. "
|
||||
"Use read_ROIs() instead.");
|
||||
if (m_master.roi_geometries().size() > 1) {
|
||||
throw std::runtime_error(LOCATION + "Multiple ROIs present in file. "
|
||||
"Use read_ROIs() instead.");
|
||||
}
|
||||
return get_frame(m_current_frame++);
|
||||
}
|
||||
|
||||
Frame RawFile::read_frame(size_t frame_number) {
|
||||
if (m_master.rois().has_value() && m_master.rois()->size() > 1) {
|
||||
if (m_master.roi_geometries().size() > 1) {
|
||||
throw std::runtime_error(
|
||||
LOCATION + "Multiple ROIs defined in the master file. "
|
||||
LOCATION + "Multiple ROIs present in file. "
|
||||
"Use read_ROIs(const size_t frame_number) instead.");
|
||||
}
|
||||
seek(frame_number);
|
||||
@@ -103,7 +104,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_master.rois().has_value() && m_master.rois()->size() > 1) {
|
||||
if (m_master.roi_geometries().size() > 1) {
|
||||
throw std::runtime_error(LOCATION +
|
||||
"Cannot use read_into for multiple ROIs.");
|
||||
}
|
||||
@@ -115,7 +116,7 @@ void RawFile::read_into(std::byte *image_buf, size_t n_frames) {
|
||||
}
|
||||
|
||||
void RawFile::read_into(std::byte *image_buf) {
|
||||
if (m_master.rois().has_value() && m_master.rois()->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.");
|
||||
@@ -125,10 +126,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_master.rois().has_value()) {
|
||||
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.");
|
||||
}
|
||||
@@ -136,7 +133,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_master.rois().has_value() && m_master.rois()->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.");
|
||||
@@ -148,25 +145,28 @@ 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_master.rois().has_value() && m_master.rois()->size() > 1) {
|
||||
if (m_master.roi_geometries().size() > 1) {
|
||||
throw std::runtime_error(
|
||||
LOCATION +
|
||||
"Cannot use read_into for multiple ROIs."); // TODO: maybe pass
|
||||
// roi_index so one can
|
||||
// use read_into for a
|
||||
// specific ROI
|
||||
"Cannot use read_into for multiple ROIs."); // TODO: maybe
|
||||
// pass
|
||||
// roi_index so
|
||||
// one can use
|
||||
// read_into for
|
||||
// a specific
|
||||
// ROI
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < n_frames; i++) {
|
||||
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_master.rois().has_value() && m_master.rois()->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. ");
|
||||
@@ -175,13 +175,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_master.rois().has_value() && m_master.rois()->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. ");
|
||||
@@ -190,8 +190,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(); }
|
||||
@@ -210,10 +210,10 @@ 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::total_frames() const { return m_frames_in_file; }
|
||||
|
||||
size_t RawFile::rows() const {
|
||||
if (m_master.rois().has_value() && m_master.rois()->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. ");
|
||||
@@ -221,10 +221,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_master.rois().has_value() && m_master.rois()->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. ");
|
||||
@@ -232,50 +232,41 @@ 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<uint32_t>(m_geometry.modules_y()),
|
||||
static_cast<uint32_t>(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 {
|
||||
if (m_master.rois().has_value()) {
|
||||
return m_master.rois()->size();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
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<size_t> RawFile::n_modules_in_roi() const {
|
||||
|
||||
std::vector<size_t> results(m_ROI_geometries.size());
|
||||
std::vector<size_t> 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;
|
||||
}
|
||||
|
||||
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 :
|
||||
m_ROI_geometries[roi_index].module_indices_in_roi()) {
|
||||
const auto pos = m_geometry.get_module_geometries(i);
|
||||
for (const size_t i : module_indices) {
|
||||
const auto pos = m_master.geometry().get_module_geometries(i);
|
||||
m_subfiles[roi_index].emplace_back(std::make_unique<RawSubFile>(
|
||||
m_master.data_fname(i, 0), m_master.detector_type(), pos.height,
|
||||
pos.width, m_master.bitdepth(), pos.row_index, pos.col_index));
|
||||
@@ -306,8 +297,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);
|
||||
@@ -323,16 +314,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<size_t> frame_numbers(
|
||||
m_ROI_geometries[roi_index].num_modules_in_roi());
|
||||
m_master.roi_geometries().at(roi_index).num_modules_in_roi());
|
||||
std::vector<size_t> 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);
|
||||
@@ -362,32 +355,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 +
|
||||
@@ -420,10 +416,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);
|
||||
@@ -437,7 +435,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 +
|
||||
|
||||
+299
-6
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/generators/catch_generators.hpp>
|
||||
#include <catch2/matchers/catch_matchers_string.hpp>
|
||||
#include <filesystem>
|
||||
|
||||
#include "test_config.hpp"
|
||||
@@ -263,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);
|
||||
@@ -308,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);
|
||||
|
||||
@@ -400,8 +402,299 @@ 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);
|
||||
}
|
||||
|
||||
TEST_CASE("Read Jungfrau frame with disabled UDP ports",
|
||||
"[.with-data][RawFile][disabled_udp_ports]") {
|
||||
SECTION("disabled top port") {
|
||||
auto fpath = test_data_path() / "raw/jungfrau" /
|
||||
"2_interfaces_top_disabled_master_6.json";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
RawFile f(fpath);
|
||||
REQUIRE(f.master().disabled_udp_ports() == std::vector<size_t>{1});
|
||||
REQUIRE(f.master().udp_port_types().value() ==
|
||||
std::vector<UDPPortPosition>{UDPPortPosition::BOTTOM,
|
||||
UDPPortPosition::TOP});
|
||||
auto frame = f.read_frame();
|
||||
REQUIRE(frame.cols() == 1024);
|
||||
REQUIRE(frame.rows() == 256);
|
||||
|
||||
auto rois = f.master().rois();
|
||||
|
||||
REQUIRE(rois.size() == 1);
|
||||
REQUIRE(rois[0] == ROI{0, 1024, 0, 256});
|
||||
}
|
||||
|
||||
SECTION("disabled bottom port") {
|
||||
auto fpath = test_data_path() / "raw/jungfrau" /
|
||||
"2_interfaces_bottom_port_disabled_master_0.json";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
RawFile f(fpath);
|
||||
REQUIRE(f.master().disabled_udp_ports() == std::vector<size_t>{0});
|
||||
REQUIRE(f.master().udp_port_types().value() ==
|
||||
std::vector<UDPPortPosition>{UDPPortPosition::BOTTOM,
|
||||
UDPPortPosition::TOP});
|
||||
auto frame = f.read_frame();
|
||||
REQUIRE(frame.cols() == 1024);
|
||||
REQUIRE(frame.rows() == 256);
|
||||
auto rois = f.master().rois();
|
||||
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" /
|
||||
"2_modules_2_interfaces_top_ports_disabled_master_2.json";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
RawFile f(fpath);
|
||||
REQUIRE(f.master().disabled_udp_ports() == std::vector<size_t>{1, 3});
|
||||
REQUIRE(f.master().udp_port_types().value() ==
|
||||
std::vector<UDPPortPosition>{UDPPortPosition::BOTTOM,
|
||||
UDPPortPosition::TOP});
|
||||
REQUIRE_THROWS_WITH(
|
||||
f.read_frame(),
|
||||
Catch::Matchers::ContainsSubstring(
|
||||
"Multiple ROIs present in file. Use read_ROIs() "
|
||||
"instead")); // cannot read frame
|
||||
// because multiple rois
|
||||
|
||||
auto frame = f.read_rois();
|
||||
|
||||
REQUIRE(frame.size() == 2);
|
||||
REQUIRE(frame[0].cols() == 1024);
|
||||
REQUIRE(frame[0].rows() == 256);
|
||||
REQUIRE(frame[1].cols() == 1024);
|
||||
REQUIRE(frame[1].rows() == 256);
|
||||
auto rois = f.master().rois();
|
||||
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" /
|
||||
"2_modules_2_interfaces_disabled_ports_master_1.json";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
RawFile f(fpath);
|
||||
REQUIRE(f.master().disabled_udp_ports() == std::vector<size_t>{0, 3});
|
||||
REQUIRE(f.master().udp_port_types().value() ==
|
||||
std::vector<UDPPortPosition>{UDPPortPosition::BOTTOM,
|
||||
UDPPortPosition::TOP});
|
||||
auto frame = f.read_frame();
|
||||
REQUIRE(frame.cols() == 1024);
|
||||
REQUIRE(frame.rows() == 512);
|
||||
auto rois = f.master().rois();
|
||||
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" /
|
||||
"4_modules_udp_disabled_master_0.json";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
RawFile f(fpath);
|
||||
REQUIRE(f.master().disabled_udp_ports() ==
|
||||
std::vector<size_t>{1, 3, 4, 6});
|
||||
REQUIRE(f.master().udp_port_types().value() ==
|
||||
std::vector<UDPPortPosition>{UDPPortPosition::BOTTOM,
|
||||
UDPPortPosition::TOP});
|
||||
REQUIRE_THROWS_WITH(
|
||||
f.read_frame(),
|
||||
Catch::Matchers::ContainsSubstring(
|
||||
"Multiple ROIs present in file. Use read_ROIs() "
|
||||
"instead"));
|
||||
auto frames = f.read_rois();
|
||||
REQUIRE(frames.size() == 4);
|
||||
REQUIRE(frames[0].cols() == 1024);
|
||||
REQUIRE(frames[0].rows() == 256);
|
||||
REQUIRE(frames[1].cols() == 1024);
|
||||
REQUIRE(frames[1].rows() == 256);
|
||||
REQUIRE(frames[2].cols() == 1024);
|
||||
REQUIRE(frames[2].rows() == 256);
|
||||
REQUIRE(frames[3].cols() == 1024);
|
||||
REQUIRE(frames[3].rows() == 256);
|
||||
auto rois = f.master().rois();
|
||||
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});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Read Moench frame with disabled UDP ports",
|
||||
"[.with-data][RawFile][disabled_udp_ports]") {
|
||||
SECTION("disabled top port") {
|
||||
auto fpath = test_data_path() / "raw/moench" /
|
||||
"2_interfaces_top_port_disabled_master_0.json";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
RawFile f(fpath);
|
||||
REQUIRE(f.master().disabled_udp_ports() == std::vector<size_t>{1});
|
||||
REQUIRE(f.master().udp_port_types().value() ==
|
||||
std::vector<UDPPortPosition>{UDPPortPosition::BOTTOM,
|
||||
UDPPortPosition::TOP});
|
||||
auto frame = f.read_frame();
|
||||
REQUIRE(frame.cols() == 400);
|
||||
REQUIRE(frame.rows() == 200);
|
||||
auto rois = f.master().rois();
|
||||
|
||||
REQUIRE(rois.size() == 1);
|
||||
REQUIRE(rois[0] == ROI{0, 400, 0, 200});
|
||||
}
|
||||
|
||||
SECTION("disabled bottom port") {
|
||||
auto fpath = test_data_path() / "raw/moench" /
|
||||
"2_interfaces_bottom_port_disabled_master_6.json";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
RawFile f(fpath);
|
||||
REQUIRE(f.master().disabled_udp_ports() == std::vector<size_t>{0});
|
||||
REQUIRE(f.master().udp_port_types().value() ==
|
||||
std::vector<UDPPortPosition>{UDPPortPosition::BOTTOM,
|
||||
UDPPortPosition::TOP});
|
||||
auto frame = f.read_frame();
|
||||
REQUIRE(frame.cols() == 400);
|
||||
REQUIRE(frame.rows() == 200);
|
||||
auto rois = f.master().rois();
|
||||
|
||||
REQUIRE(rois.size() == 1);
|
||||
REQUIRE(rois[0] == ROI{0, 400, 200, 400});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Read Eiger frame with disabled UDP ports",
|
||||
"[.with-data][RawFile][disabled_udp_ports]") {
|
||||
SECTION("disabled left port (bottom and top half module)") {
|
||||
auto fpath = test_data_path() / "raw/eiger" /
|
||||
"left_udp_port_disabled_master_2.json";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
RawFile f(fpath);
|
||||
REQUIRE(f.master().disabled_udp_ports() == std::vector<size_t>{0, 2});
|
||||
REQUIRE(f.master().udp_port_types().value() ==
|
||||
std::vector<UDPPortPosition>{UDPPortPosition::LEFT,
|
||||
UDPPortPosition::RIGHT});
|
||||
|
||||
auto frame = f.read_frame();
|
||||
|
||||
REQUIRE(frame.cols() == 512);
|
||||
REQUIRE(frame.rows() == 512);
|
||||
|
||||
auto rois = f.master().rois();
|
||||
REQUIRE(rois.size() == 1);
|
||||
REQUIRE(rois[0] == ROI{512, 1024, 0, 512});
|
||||
}
|
||||
SECTION("disabled right port") {
|
||||
auto fpath = test_data_path() / "raw/eiger" /
|
||||
"right_udp_port_disabled_master_3.json";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
RawFile f(fpath);
|
||||
REQUIRE(f.master().disabled_udp_ports() == std::vector<size_t>{1, 3});
|
||||
REQUIRE(f.master().udp_port_types().value() ==
|
||||
std::vector<UDPPortPosition>{UDPPortPosition::LEFT,
|
||||
UDPPortPosition::RIGHT});
|
||||
auto frame = f.read_frame();
|
||||
REQUIRE(frame.cols() == 512);
|
||||
REQUIRE(frame.rows() == 512);
|
||||
auto rois = f.master().rois();
|
||||
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" /
|
||||
"2_modules_eiger_disabled_udp_port_master_0.json";
|
||||
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
RawFile f(fpath);
|
||||
REQUIRE(f.master().disabled_udp_ports() ==
|
||||
std::vector<size_t>{1, 3, 5, 7});
|
||||
REQUIRE(f.master().udp_port_types().value() ==
|
||||
std::vector<UDPPortPosition>{UDPPortPosition::LEFT,
|
||||
UDPPortPosition::RIGHT});
|
||||
REQUIRE_THROWS_WITH(
|
||||
f.read_frame(),
|
||||
Catch::Matchers::ContainsSubstring(
|
||||
"Multiple ROIs present in file. Use read_ROIs() "
|
||||
"instead"));
|
||||
auto frames = f.read_rois();
|
||||
REQUIRE(frames.size() == 2);
|
||||
REQUIRE(frames[0].cols() == 512);
|
||||
REQUIRE(frames[0].rows() == 512);
|
||||
REQUIRE(frames[1].cols() == 512);
|
||||
REQUIRE(frames[1].rows() == 512);
|
||||
auto rois = f.master().rois();
|
||||
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" /
|
||||
"quad_eiger_disabled_bottom_port_master_0.json";
|
||||
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
RawFile f(fpath);
|
||||
REQUIRE(f.master().disabled_udp_ports() == std::vector<size_t>{1});
|
||||
REQUIRE(f.master().udp_port_types().value() ==
|
||||
std::vector<UDPPortPosition>{UDPPortPosition::TOP,
|
||||
UDPPortPosition::BOTTOM});
|
||||
auto frame = f.read_frame();
|
||||
REQUIRE(frame.cols() == 512);
|
||||
REQUIRE(frame.rows() == 256);
|
||||
auto rois = f.master().rois();
|
||||
REQUIRE(rois.size() == 1);
|
||||
REQUIRE(rois[0] == ROI{0, 512, 256, 512});
|
||||
}
|
||||
SECTION("only bottom left port disabled") {
|
||||
auto fpath = test_data_path() / "raw/eiger" /
|
||||
"one_udp_port_disabled_master_0.json";
|
||||
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
RawFile f(fpath);
|
||||
REQUIRE(f.master().disabled_udp_ports() == std::vector<size_t>{0});
|
||||
REQUIRE(f.master().udp_port_types().value() ==
|
||||
std::vector<UDPPortPosition>{UDPPortPosition::LEFT,
|
||||
UDPPortPosition::RIGHT});
|
||||
|
||||
REQUIRE_THROWS_WITH(
|
||||
f.read_frame(),
|
||||
Catch::Matchers::ContainsSubstring(
|
||||
"Multiple ROIs present in file. Use read_ROIs() instead"));
|
||||
|
||||
auto frame = f.read_rois();
|
||||
REQUIRE(frame.size() == 2);
|
||||
REQUIRE(frame[0].cols() == 512);
|
||||
REQUIRE(frame[0].rows() == 256);
|
||||
REQUIRE(frame[1].cols() == 1024);
|
||||
REQUIRE(frame[1].rows() == 256);
|
||||
auto rois = f.master().rois();
|
||||
REQUIRE(rois.size() == 2);
|
||||
|
||||
REQUIRE(rois[0] == ROI{512, 1024, 0, 256});
|
||||
REQUIRE(rois[1] == ROI{0, 1024, 256, 512});
|
||||
}
|
||||
SECTION("No udp ports disabled") {
|
||||
auto fpath = test_data_path() /
|
||||
"raw/eiger_virtual_500k_disabled_ports" /
|
||||
"all_active_master_0.json";
|
||||
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
RawFile f(fpath);
|
||||
|
||||
auto disabled_udp_ports = f.master().disabled_udp_ports();
|
||||
REQUIRE(disabled_udp_ports.empty());
|
||||
|
||||
auto udp_port_types = f.master().udp_port_types();
|
||||
REQUIRE(udp_port_types.has_value());
|
||||
REQUIRE(udp_port_types.value() ==
|
||||
std::vector<UDPPortPosition>{UDPPortPosition::LEFT,
|
||||
UDPPortPosition::RIGHT});
|
||||
REQUIRE(f.total_frames() == 5);
|
||||
|
||||
auto frame = f.read_frame();
|
||||
REQUIRE(frame.cols() == 1024);
|
||||
REQUIRE(frame.rows() == 512);
|
||||
|
||||
auto rois = f.master().rois();
|
||||
REQUIRE(rois.size() == 1);
|
||||
REQUIRE(rois[0] == ROI{0, 1024, 0, 512});
|
||||
}
|
||||
}
|
||||
+246
-31
@@ -115,6 +115,32 @@ RawMasterFile::RawMasterFile(const std::filesystem::path &fpath)
|
||||
} else {
|
||||
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() = {UDPPortPosition::TOP,
|
||||
UDPPortPosition::BOTTOM};
|
||||
}
|
||||
|
||||
if (!m_disabled_udp_ports.empty()) {
|
||||
// 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)
|
||||
@@ -164,10 +190,16 @@ std::optional<uint8_t> 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<ROIGeometry> &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 {
|
||||
@@ -193,26 +225,30 @@ ScanParameters RawMasterFile::scan_parameters() const {
|
||||
return m_scan_parameters;
|
||||
}
|
||||
|
||||
std::optional<ROI> RawMasterFile::roi() const {
|
||||
if (!m_rois) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<std::vector<UDPPortPosition>>
|
||||
RawMasterFile::udp_port_types() const {
|
||||
return m_udp_port_types;
|
||||
}
|
||||
|
||||
if (m_rois->empty()) {
|
||||
std::vector<size_t> RawMasterFile::disabled_udp_ports() const {
|
||||
return m_disabled_udp_ports;
|
||||
}
|
||||
|
||||
ROI RawMasterFile::roi() const {
|
||||
|
||||
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<ROI>(m_rois.value().at(0))
|
||||
: std::nullopt; // TODO: maybe throw if no roi exists
|
||||
return m_rois.at(0);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::vector<ROI>> RawMasterFile::rois() const { return m_rois; }
|
||||
std::vector<ROI> RawMasterFile::rois() const { return m_rois; }
|
||||
|
||||
ReadoutMode RawMasterFile::get_reading_mode() const {
|
||||
|
||||
@@ -248,10 +284,11 @@ void RawMasterFile::parse_json(std::istream &is) {
|
||||
m_type = string_to<DetectorType>(j["Detector Type"].get<std::string>());
|
||||
m_timing_mode = string_to<TimingMode>(j["Timing Mode"].get<std::string>());
|
||||
|
||||
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"];
|
||||
@@ -301,6 +338,9 @@ void RawMasterFile::parse_json(std::istream &is) {
|
||||
if (j.contains("Number of rows") && j["Number of rows"].is_number()) {
|
||||
m_number_of_rows = j["Number of rows"];
|
||||
}
|
||||
if (j.contains("Number of Rows") && j["Number of Rows"].is_number()) {
|
||||
m_number_of_rows = j["Number of Rows"]; // changed at some point
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Special treatment of analog flag because of Moench03.
|
||||
@@ -377,7 +417,26 @@ void RawMasterFile::parse_json(std::istream &is) {
|
||||
} catch (const json::out_of_range &e) {
|
||||
// not a scan
|
||||
}
|
||||
|
||||
try {
|
||||
auto json_list_obj = j.at("UDP Ports Type");
|
||||
m_udp_port_types.emplace();
|
||||
for (auto &elem : json_list_obj) {
|
||||
m_udp_port_types.value().push_back(
|
||||
string_to<UDPPortPosition>(elem));
|
||||
}
|
||||
} catch (const json::out_of_range &e) {
|
||||
// leave the optional empty
|
||||
}
|
||||
try {
|
||||
auto json_list_obj = j.at("UDP Ports Disabled");
|
||||
m_disabled_udp_ports.clear();
|
||||
for (auto &elem : json_list_obj) {
|
||||
m_disabled_udp_ports.push_back(static_cast<size_t>(elem));
|
||||
}
|
||||
} catch (const json::out_of_range &e) {
|
||||
m_disabled_udp_ports
|
||||
.clear(); // empty list if not present in master file
|
||||
}
|
||||
try {
|
||||
m_udp_interfaces_per_module = {j.at("Number of UDP Interfaces"), 1};
|
||||
} catch (const json::out_of_range &e) {
|
||||
@@ -397,14 +456,21 @@ 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<ssize_t>(obj.at("xmax")) + 1,
|
||||
obj.at("ymin"), static_cast<ssize_t>(obj.at("ymax")) + 1});
|
||||
m_rois.push_back({static_cast<ssize_t>(obj.at("xmin")),
|
||||
static_cast<ssize_t>(obj.at("xmax")) + 1,
|
||||
static_cast<ssize_t>(obj.at("ymin")),
|
||||
static_cast<ssize_t>(obj.at("ymax")) + 1});
|
||||
} else {
|
||||
// fill ROI with full detector size if not present in master
|
||||
// file
|
||||
m_rois.push_back(
|
||||
{0,
|
||||
m_detector_layout.col * static_cast<ssize_t>(m_pixels_x),
|
||||
0,
|
||||
m_detector_layout.row * static_cast<ssize_t>(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) {
|
||||
@@ -412,15 +478,18 @@ void RawMasterFile::parse_json(std::istream &is) {
|
||||
elem.at("ymax") = 0;
|
||||
}
|
||||
|
||||
m_rois.value().push_back(ROI{
|
||||
elem.at("xmin"), static_cast<ssize_t>(elem.at("xmax")) + 1,
|
||||
elem.at("ymin"),
|
||||
static_cast<ssize_t>(elem.at("ymax")) + 1});
|
||||
m_rois.push_back({static_cast<ssize_t>(elem.at("xmin")),
|
||||
static_cast<ssize_t>(elem.at("xmax")) + 1,
|
||||
static_cast<ssize_t>(elem.at("ymin")),
|
||||
static_cast<ssize_t>(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, m_detector_layout.col * static_cast<ssize_t>(m_pixels_x), 0,
|
||||
m_detector_layout.row * static_cast<ssize_t>(m_pixels_y)});
|
||||
}
|
||||
|
||||
if (j.contains("Counter Mask")) {
|
||||
@@ -528,7 +597,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<uint32_t>(std::stoi(value.substr(1, pos))),
|
||||
static_cast<uint32_t>(std::stoi(value.substr(pos + 1)))};
|
||||
} else if (key == "Number of UDP Interfaces") {
|
||||
@@ -548,11 +617,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
|
||||
@@ -578,7 +647,153 @@ 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();
|
||||
|
||||
size_t first_port = m_disabled_udp_ports[0] % num_udp_port_types;
|
||||
|
||||
bool all_ports_equal =
|
||||
std::all_of(m_disabled_udp_ports.begin(), m_disabled_udp_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 =
|
||||
m_disabled_udp_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] == UDPPortPosition::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<ssize_t>(m_geometry.pixels_y())};
|
||||
});
|
||||
}
|
||||
if (m_udp_port_types.value()[first_port] == UDPPortPosition::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<ssize_t>(m_geometry.pixels_y())};
|
||||
});
|
||||
}
|
||||
if (m_udp_port_types.value()[first_port] == UDPPortPosition::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<ssize_t>(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] == UDPPortPosition::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<ssize_t>(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<size_t> enabled_ports(m_geometry.n_modules());
|
||||
std::iota(enabled_ports.begin(), enabled_ports.end(), 0);
|
||||
std::for_each(m_disabled_udp_ports.begin(), m_disabled_udp_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<UDPPortPosition>{UDPPortPosition::LEFT,
|
||||
UDPPortPosition::RIGHT}) {
|
||||
m_rois = merge_consecutive_rois<false, true>(m_rois);
|
||||
} else if (m_udp_port_types ==
|
||||
std::vector<UDPPortPosition>{UDPPortPosition::BOTTOM,
|
||||
UDPPortPosition::TOP} ||
|
||||
m_udp_port_types ==
|
||||
std::vector<UDPPortPosition>{UDPPortPosition::TOP,
|
||||
UDPPortPosition::BOTTOM}) {
|
||||
m_rois = merge_consecutive_rois<true, false>(m_rois);
|
||||
} else {
|
||||
throw std::runtime_error(LOCATION + "Unsupported UDP port types");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace aare
|
||||
|
||||
+142
-35
@@ -68,7 +68,7 @@ TEST_CASE("A disabled scan") {
|
||||
REQUIRE(s.step() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Parse a master file in .json format", "[.integration]") {
|
||||
TEST_CASE("Parse a master file in .json format", "[.with-data]") {
|
||||
auto fpath =
|
||||
test_data_path() / "raw" / "jungfrau" / "jungfrau_single_master_0.json";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
@@ -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);
|
||||
@@ -151,7 +151,7 @@ TEST_CASE("Parse a master file in .json format", "[.integration]") {
|
||||
}
|
||||
|
||||
TEST_CASE("Parse a master file in old .raw format",
|
||||
"[.integration][.with-data][.rawmasterfile]") {
|
||||
"[.with-data][rawmasterfile]") {
|
||||
auto fpath = test_data_path() /
|
||||
"raw/jungfrau_2modules_version6.1.2/run_master_0.raw";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
@@ -159,11 +159,11 @@ TEST_CASE("Parse a master file in old .raw format",
|
||||
|
||||
CHECK(f.udp_interfaces_per_module() == xy{1, 1});
|
||||
CHECK(f.n_modules() == 2);
|
||||
CHECK(f.geometry().row == 2);
|
||||
CHECK(f.geometry().col == 1);
|
||||
CHECK(f.detector_layout().row == 2);
|
||||
CHECK(f.detector_layout().col == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("Parse a master file in .raw format", "[.integration]") {
|
||||
TEST_CASE("Parse a master file in .raw format", "[.with-data]") {
|
||||
|
||||
auto fpath =
|
||||
test_data_path() /
|
||||
@@ -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]
|
||||
@@ -232,8 +232,7 @@ TEST_CASE("Parse a master file in .raw format", "[.integration]") {
|
||||
// Packets Caught Mask : 64 bytes
|
||||
}
|
||||
|
||||
TEST_CASE("Parse a master file in new .json format",
|
||||
"[.integration][.with-data]") {
|
||||
TEST_CASE("Parse a master file in new .json format", "[.with-data]") {
|
||||
|
||||
auto file_path =
|
||||
test_data_path() / "raw" / "newmythen03" / "run_87_master_0.json";
|
||||
@@ -247,9 +246,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);
|
||||
|
||||
@@ -260,14 +259,14 @@ TEST_CASE("Parse a master file in new .json format",
|
||||
REQUIRE(f.scan_parameters().step() == 0);
|
||||
REQUIRE(f.scan_parameters().settleTime() == 0);
|
||||
|
||||
auto roi = f.roi().value();
|
||||
auto roi = f.roi();
|
||||
REQUIRE(roi.xmin == 0);
|
||||
REQUIRE(roi.xmax == 2560);
|
||||
REQUIRE(roi.ymin == 0);
|
||||
REQUIRE(roi.ymax == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("Read eiger master file", "[.integration]") {
|
||||
TEST_CASE("Read eiger master file", "[.with-data]") {
|
||||
auto fpath = test_data_path() / "raw/eiger/eiger_500k_32bit_master_0.json";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
RawMasterFile f(fpath);
|
||||
@@ -400,8 +399,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 +476,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 +559,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 +637,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 +709,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 +780,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 +862,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 +943,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 +1010,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 +1100,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 +1185,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 +1267,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);
|
||||
@@ -1286,3 +1285,111 @@ TEST_CASE("Parse old Moench03 from stream") {
|
||||
REQUIRE(f.period() == std::chrono::microseconds(600));
|
||||
REQUIRE(f.analog_samples() == 5000);
|
||||
}
|
||||
|
||||
TEST_CASE("Parse Eiger json v8.1 all ports active") {
|
||||
std::string master_content = R"(
|
||||
{
|
||||
"Version": 8.1,
|
||||
"Timestamp": "Mon Aug 10 17:44:46 2026",
|
||||
"Detector Type": "Eiger",
|
||||
"Timing Mode": "auto",
|
||||
"Geometry": {
|
||||
"x": 2,
|
||||
"y": 2
|
||||
},
|
||||
"Image Size": 262144,
|
||||
"Pixels": {
|
||||
"x": 512,
|
||||
"y": 256
|
||||
},
|
||||
"Max Frames Per File": 10000,
|
||||
"Frame Discard Policy": "nodiscard",
|
||||
"Frame Padding": 1,
|
||||
"Scan Parameters": {
|
||||
"enable": 0,
|
||||
"dacInd": 0,
|
||||
"start offset": 0,
|
||||
"stop offset": 0,
|
||||
"step size": 0,
|
||||
"dac settle time ns": 0
|
||||
},
|
||||
"Total Frames": 5,
|
||||
"Receiver Rois": [
|
||||
{
|
||||
"xmin": 0,
|
||||
"xmax": 1023,
|
||||
"ymin": 0,
|
||||
"ymax": 511
|
||||
}
|
||||
],
|
||||
"Dynamic Range": 16,
|
||||
"Ten Giga": 0,
|
||||
"Exposure Time": "1s",
|
||||
"Acquisition Period": "1s",
|
||||
"Threshold Energy": -1,
|
||||
"Sub Exposure Time": "2.62144ms",
|
||||
"Sub Acquisition Period": "2.62144ms",
|
||||
"Quad": 0,
|
||||
"UDP Ports Type": [
|
||||
"left",
|
||||
"right"
|
||||
],
|
||||
"UDP Ports Disabled": [],
|
||||
"Number of Rows": 256,
|
||||
"Rate Corrections": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"Readout Speed": "full_speed",
|
||||
"Frames in File": 5,
|
||||
"Additional JSON Header": {}
|
||||
}
|
||||
|
||||
)";
|
||||
|
||||
std::istringstream iss(master_content);
|
||||
RawMasterFile f(iss, "test_master_0.json");
|
||||
|
||||
REQUIRE(f.version() == "8.1");
|
||||
REQUIRE(f.detector_type() == DetectorType::Eiger);
|
||||
REQUIRE(f.timing_mode() == TimingMode::Auto);
|
||||
REQUIRE(f.detector_layout() == xy{2, 2});
|
||||
REQUIRE(f.n_modules() == 4);
|
||||
REQUIRE(f.image_size_in_bytes() == 262144);
|
||||
REQUIRE(f.pixels_x() == 512);
|
||||
REQUIRE(f.pixels_y() == 256);
|
||||
REQUIRE(f.max_frames_per_file() == 10000);
|
||||
REQUIRE(f.frame_discard_policy() == FrameDiscardPolicy::NoDiscard);
|
||||
REQUIRE(f.frame_padding() == 1);
|
||||
REQUIRE(f.total_frames_expected() == 5);
|
||||
REQUIRE(f.frames_in_file() == 5);
|
||||
REQUIRE(f.bitdepth() == 16);
|
||||
REQUIRE(f.exptime() == std::chrono::seconds(1));
|
||||
REQUIRE(f.period() == std::chrono::seconds(1));
|
||||
REQUIRE(f.quad() == 0);
|
||||
// rows return a std::optional, so we need to check if it has a value before
|
||||
// using it
|
||||
auto rows = f.number_of_rows();
|
||||
REQUIRE(rows.has_value());
|
||||
REQUIRE(rows.value() == 256);
|
||||
REQUIRE(f.udp_interfaces_per_module() == xy{1, 2});
|
||||
|
||||
auto scan_parameters = f.scan_parameters();
|
||||
REQUIRE_FALSE(scan_parameters.enabled());
|
||||
REQUIRE(scan_parameters.dac() == DACIndex::DAC_0);
|
||||
REQUIRE(scan_parameters.start() == 0);
|
||||
REQUIRE(scan_parameters.stop() == 0);
|
||||
REQUIRE(scan_parameters.step() == 0);
|
||||
REQUIRE(scan_parameters.settleTime() == 0);
|
||||
|
||||
REQUIRE(f.udp_port_types().has_value());
|
||||
REQUIRE(f.udp_port_types().value() ==
|
||||
std::vector<UDPPortPosition>{UDPPortPosition::LEFT,
|
||||
UDPPortPosition::RIGHT});
|
||||
|
||||
REQUIRE(f.disabled_udp_ports().empty());
|
||||
|
||||
auto rois = f.rois();
|
||||
REQUIRE(rois.size() == 1);
|
||||
REQUIRE(rois[0] == ROI{0, 1024, 0, 512});
|
||||
}
|
||||
|
||||
@@ -231,4 +231,4 @@ TEST_CASE("Bilinear interpolation", "[algorithm]") {
|
||||
0.75);
|
||||
REQUIRE(interpolated_value == 5.25);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -235,6 +235,19 @@ template <> DACIndex string_to(const std::string &arg) {
|
||||
"\"");
|
||||
}
|
||||
|
||||
template <> UDPPortPosition string_to(const std::string &arg) {
|
||||
if (arg == "left")
|
||||
return UDPPortPosition::LEFT;
|
||||
if (arg == "right")
|
||||
return UDPPortPosition::RIGHT;
|
||||
if (arg == "top")
|
||||
return UDPPortPosition::TOP;
|
||||
if (arg == "bottom")
|
||||
return UDPPortPosition::BOTTOM;
|
||||
throw std::runtime_error("Could not decode UDPPortPosition from: \"" + arg +
|
||||
"\"");
|
||||
}
|
||||
|
||||
std::string remove_unit(std::string &str) {
|
||||
auto it = str.begin();
|
||||
while (it != str.end()) {
|
||||
|
||||
@@ -55,6 +55,14 @@ template <typename T> T string_to(const std::string &arg) {
|
||||
*/
|
||||
template <> DetectorType string_to(const std::string &arg);
|
||||
|
||||
/**
|
||||
* @brief Convert a string to UDPPortPosition
|
||||
* @param name string representation of the UDPPortPosition
|
||||
* @return UDPPortPosition
|
||||
* @throw runtime_error if the string does not match any UDPPortPosition
|
||||
*/
|
||||
template <> UDPPortPosition string_to(const std::string &arg);
|
||||
|
||||
/**
|
||||
* @brief Convert a string to TimingMode
|
||||
* @param mode string representation of the TimingMode
|
||||
|
||||
Reference in New Issue
Block a user