36 Commits

Author SHA1 Message Date
766cba296b updated release notes 2025-07-03 11:00:08 +02:00
17f942d216 reordered .cpp files according to filename 2025-07-03 10:58:44 +02:00
d8952eccc6 Update RELEASE.md
All checks were successful
Build on RHEL9 / build (push) Successful in 2m47s
Build on RHEL8 / build (push) Successful in 3m0s
2025-06-27 17:19:14 +02:00
97dae4ac60 added empty() to ClusterVector and fixed docs (#209)
- added ClusterVector::empty() to check if the vector is empty
- Fixed generation of missing docs for ClusterVector
2025-06-27 17:00:46 +02:00
e3f4b34b72 Const element access and fixed comparing bug (#208)
- Added const element access
- Added const data*
- Fixed bug comparing two Views of same size but different shapes

closes #207
2025-06-27 14:13:51 +02:00
6ec8fbee72 migrated tags for tests and added missing raw files (#206)
All checks were successful
Build on RHEL8 / build (push) Successful in 2m57s
Build on RHEL9 / build (push) Successful in 2m59s
- No changes or evaluation of existing tests
- Tags for including tests that require data is changed to
**[.with-data]** and **--with-data** for C++ and python respectively
- Minor update to docs
- Added missing files to the test data repo
2025-06-26 17:11:20 +02:00
30822d9c5f Dev/fix/rawfilereader with roi (#192)
All checks were successful
Build on RHEL8 / build (push) Successful in 2m54s
Build on RHEL9 / build (push) Successful in 3m0s
geometry is calculated from master file.
2025-06-24 16:41:28 +02:00
ff7312f45d replaced fmt with LOG 2025-06-24 16:24:25 +02:00
8e7c9eadff fixed cmake merge 2025-06-24 13:49:05 +02:00
d35b7762b4 Merge branch 'main' into dev/fix/rawfilereader_with_roi 2025-06-24 13:43:26 +02:00
df4dbb8fd0 fixed numpy test 2025-06-24 13:39:32 +02:00
c92be4bca2 added eiger quad test
All checks were successful
Build on RHEL8 / build (push) Successful in 2m53s
Build on RHEL9 / build (push) Successful in 3m0s
2025-06-24 11:29:25 +02:00
664055de92 fixed quad structure
All checks were successful
Build on RHEL9 / build (push) Successful in 2m50s
Build on RHEL8 / build (push) Successful in 3m2s
2025-06-23 17:27:13 +02:00
318e640639 only test over the public interface 2025-06-23 17:27:13 +02:00
c6990dabad deleted unused variables 2025-06-23 17:27:13 +02:00
c9fe16b4c2 use target_compile_definitions (#203)
All checks were successful
Build on RHEL9 / build (push) Successful in 2m54s
Build on RHEL8 / build (push) Successful in 2m55s
use target_compile_definition instead of add_compile_definition to use
macros across projects
2025-06-23 09:06:25 +02:00
64438c8803 Merge branch 'main' into dev/fix/rawfilereader_with_roi
All checks were successful
Build on RHEL9 / build (push) Successful in 2m58s
Build on RHEL8 / build (push) Successful in 2m58s
2025-06-23 08:21:39 +02:00
9f8eee5d08 fixed python bindings - only read headers of modules that are in the roi
All checks were successful
Build on RHEL8 / build (push) Successful in 2m51s
Build on RHEL9 / build (push) Successful in 2m53s
2025-06-16 11:07:00 +02:00
35114cde9d fatal error did not open any subfiles
All checks were successful
Build on RHEL8 / build (push) Successful in 2m56s
Build on RHEL9 / build (push) Successful in 2m59s
2025-06-13 18:12:47 +02:00
b13f864b2b need n_modules 2025-06-13 17:01:13 +02:00
05828baa54 removed n_modules from python bindings 2025-06-13 16:38:46 +02:00
0f56846e3d deleted some commented lines 2025-06-13 16:33:25 +02:00
be67bbab6b extended DetectorGeometry class with find_geometry, update_geometry (refactoring) 2025-06-13 16:16:23 +02:00
8354439605 droping version spec on sphinx (#202)
All checks were successful
Build on RHEL8 / build (push) Successful in 2m56s
Build on RHEL9 / build (push) Successful in 2m58s
- Removing the version requirement on sphinx since the latest version
works again
- added numpy and matplotlib do the etc/dev-env.yml since they are
needed to import aare
2025-06-13 15:25:43 +02:00
11fa95b23c Improved documentation for ClusterFile on the python side (#201)
- Fixed CI job not doing python docs
- added more docs on cluster file 
- fixed generating docs on cluster vector
2025-06-13 10:41:39 +02:00
bd7870e75a review comments
All checks were successful
Build on RHEL9 / build (push) Successful in 2m53s
Build on RHEL8 / build (push) Successful in 2m55s
2025-06-12 18:14:48 +02:00
75f63607fc friend_test macro 2025-06-12 17:46:10 +02:00
cfe7c31fe4 changed data path of test data
All checks were successful
Build on RHEL9 / build (push) Successful in 2m54s
Build on RHEL8 / build (push) Successful in 2m55s
2025-06-12 09:53:15 +02:00
4976ec1651 added back chunk_size in python (#199)
All checks were successful
Build on RHEL9 / build (push) Successful in 2m52s
Build on RHEL8 / build (push) Successful in 2m57s
When refactoring the dispatch of the python binding for ClusterFile I
forgot chunk_size. Adding it back in.
Excluded from release notes since the bug was introduced after the last
release and now fixed before the next release.

1. added back chunk_size
2. removed a few commented out lines

closes #197
2025-06-12 09:32:42 +02:00
a9a55fb27d Merge branch 'main' into dev/fix/rawfilereader_with_roi
All checks were successful
Build on RHEL8 / build (push) Successful in 2m57s
Build on RHEL9 / build (push) Successful in 3m1s
2025-06-11 13:23:01 +02:00
19ecc82fff solved merge conflict
All checks were successful
Build on RHEL8 / build (push) Successful in 2m52s
Build on RHEL9 / build (push) Successful in 3m12s
2025-06-10 17:01:40 +02:00
923f7d22b8 Merge branch 'main' into dev/fix/rawfilereader_with_roi 2025-06-10 15:59:52 +02:00
6438a4bef1 updated python bindings
All checks were successful
Build on RHEL9 / build (push) Successful in 2m21s
Build on RHEL8 / build (push) Successful in 2m29s
2025-06-10 12:00:07 +02:00
ad7525cd02 considered num_udp_interafces for jungfrau and quad structure for eiger 2025-06-10 11:35:15 +02:00
87d8682b1e added test cases 2025-06-06 11:31:39 +02:00
9c6e629298 only files within the ROI are opened & geometry always read in RawMasterFile 2025-06-04 16:34:40 +02:00
78 changed files with 1347 additions and 3637 deletions

View File

@ -43,7 +43,7 @@ jobs:
run: |
mkdir build
cd build
cmake .. -DAARE_SYSTEM_LIBRARIES=ON -DAARE_DOCS=ON
cmake .. -DAARE_SYSTEM_LIBRARIES=ON -DAARE_PYTHON_BINDINGS=ON -DAARE_DOCS=ON
make -j 2
make docs

View File

@ -53,7 +53,6 @@ option(AARE_DOCS "Build documentation" OFF)
option(AARE_VERBOSE "Verbose output" OFF)
option(AARE_CUSTOM_ASSERT "Use custom assert" OFF)
option(AARE_INSTALL_PYTHONEXT "Install the python extension in the install tree under CMAKE_INSTALL_PREFIX/aare/" OFF)
option(AARE_HDF5 "Hdf5 File Format" OFF)
option(AARE_ASAN "Enable AddressSanitizer" OFF)
# Configure which of the dependencies to use FetchContent for
@ -78,17 +77,6 @@ if(AARE_SYSTEM_LIBRARIES)
# on conda-forge
endif()
if(AARE_VERBOSE)
add_compile_definitions(AARE_VERBOSE)
add_compile_definitions(AARE_LOG_LEVEL=aare::logDEBUG5)
else()
add_compile_definitions(AARE_LOG_LEVEL=aare::logINFOBLUE)
endif()
if(AARE_CUSTOM_ASSERT)
add_compile_definitions(AARE_CUSTOM_ASSERT)
endif()
if(AARE_BENCHMARKS)
add_subdirectory(benchmarks)
endif()
@ -336,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
@ -357,10 +342,6 @@ set(PUBLICHEADERS
include/aare/CtbRawFile.hpp
include/aare/ClusterVector.hpp
include/aare/decode.hpp
include/aare/type_traits.hpp
include/aare/scan_parameters.hpp
include/aare/to_string.hpp
include/aare/string_utils.hpp
include/aare/defs.hpp
include/aare/Dtype.hpp
include/aare/File.hpp
@ -369,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
@ -388,44 +369,26 @@ set(PUBLICHEADERS
set(SourceFiles
${CMAKE_CURRENT_SOURCE_DIR}/src/CtbRawFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/defs.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/to_string.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/string_utils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Dtype.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/decode.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Frame.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/defs.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/DetectorGeometry.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Dtype.cpp
${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/Frame.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Interpolator.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/JungfrauDataFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyHelpers.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Interpolator.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/PixelMap.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RawFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RawSubFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RawMasterFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/RawSubFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/task.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/ifstream_helpers.cpp
)
# HDF5
if (AARE_HDF5)
find_package(HDF5 1.10 COMPONENTS CXX REQUIRED)
add_definitions(
${HDF5_DEFINITIONS}
)
list (APPEND PUBLICHEADERS
include/aare/Hdf5File.hpp
include/aare/Hdf5MasterFile.hpp
)
list (APPEND SourceFiles
${CMAKE_CURRENT_SOURCE_DIR}/src/Hdf5File.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Hdf5MasterFile.cpp
)
endif (AARE_HDF5)
add_library(aare_core STATIC ${SourceFiles})
target_include_directories(aare_core PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
@ -448,14 +411,18 @@ target_link_libraries(
)
if (AARE_HDF5 AND HDF5_FOUND)
add_definitions(-DHDF5_FOUND)
target_link_libraries(aare_core PUBLIC
${HDF5_LIBRARIES}
)
target_include_directories(aare_core PUBLIC
${HDF5_INCLUDE_DIRS}
)
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)
else()
target_compile_definitions(aare_core PUBLIC AARE_LOG_LEVEL=aare::logERROR)
endif()
if(AARE_CUSTOM_ASSERT)
target_compile_definitions(aare_core PUBLIC AARE_CUSTOM_ASSERT)
endif()
set_target_properties(aare_core PROPERTIES
@ -471,12 +438,10 @@ if(AARE_TESTS)
set(TestSources
${CMAKE_CURRENT_SOURCE_DIR}/src/algorithm.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/defs.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/to_string.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/scan_parameters.test.cpp
${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
@ -495,22 +460,9 @@ if(AARE_TESTS)
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/task.test.cpp
)
if(HDF5_FOUND)
list (APPEND TestSources
${CMAKE_CURRENT_SOURCE_DIR}/src/Hdf5MasterFile.test.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/Hdf5File.test.cpp
)
endif()
target_sources(tests PRIVATE ${TestSources} )
endif()
###------------------------------------------------------------------------------------------
###------------------------------------------------------------------------------------------
if(AARE_MASTER_PROJECT)
install(TARGETS aare_core aare_compiler_flags
EXPORT "${TARGETS_EXPORT_NAME}"

View File

@ -6,7 +6,11 @@
Features:
- Cluster finder now works with 5x5, 7x7 and 9x9 clusters
- Added ClusterVector::empty() member
Bugfixes:
- Fixed reading RawFiles with ROI fully excluding some sub files.
- Removed unused file: ClusterFile.cpp
### 2025.05.22
@ -20,3 +24,4 @@ Bugfixes:

View File

@ -14,6 +14,7 @@ set(SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR})
file(GLOB SPHINX_SOURCE_FILES CONFIGURE_DEPENDS "src/*.rst")
foreach(filename ${SPHINX_SOURCE_FILES})
get_filename_component(fname ${filename} NAME)
message(STATUS "Copying ${filename} to ${SPHINX_BUILD}/src/${fname}")
@ -44,12 +45,3 @@ add_custom_target(
COMMENT "Generating documentation with Sphinx"
)
add_custom_target(
rst
COMMAND ${SPHINX_EXECUTABLE} -a -b html
-Dbreathe_projects.aare=${CMAKE_CURRENT_BINARY_DIR}/xml
-c "${SPHINX_BUILD}"
${SPHINX_BUILD}/src
${SPHINX_BUILD}/html
COMMENT "Generating documentation with Sphinx"
)

View File

@ -886,7 +886,7 @@ EXCLUDE_SYMLINKS = NO
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories for example use the pattern */test/*
EXCLUDE_PATTERNS = */docs/* */tests/* */python/* */manual */slsDetectorServers/* */libs/* */integrationTests *README* */slsDetectorGui/* */ctbGui/* */slsDetectorCalibration/* *TobiSchluter*
EXCLUDE_PATTERNS = *build* */docs/* */tests/* *.test.cpp* */python/* */manual */slsDetectorServers/* */libs/* */integrationTests *README* *_deps* *TobiSchluter*
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
# (namespaces, classes, functions, etc.) that should be excluded from the

View File

@ -5,3 +5,4 @@ ClusterFile
:members:
:undoc-members:
:private-members:

View File

@ -4,3 +4,12 @@ ClusterVector
.. doxygenclass:: aare::ClusterVector
:members:
:undoc-members:
:private-members:
.. doxygenclass:: aare::ClusterVector< Cluster< T, ClusterSizeX, ClusterSizeY, CoordType > >
:members:
:undoc-members:
:private-members:

View File

@ -1,8 +0,0 @@
Hdf5File
===============
.. doxygenclass:: aare::Hdf5File
:members:
:undoc-members:
:private-members:

View File

@ -1,14 +0,0 @@
Hdf5MasterFile
===============
.. doxygenclass:: aare::Hdf5MasterFile
:members:
:undoc-members:
:private-members:
.. doxygenclass:: aare::Hdf5FileNameComponents
:members:
:undoc-members:
:private-members:

View File

@ -2,7 +2,7 @@
Tests
****************
We test the code both from the C++ and Python API. By default only tests that does not require image data is run.
We test the code both from C++ and Python. By default only tests that does not require additional data are run.
C++
~~~~~~~~~~~~~~~~~~
@ -15,7 +15,7 @@ C++
make -j 4
export AARE_TEST_DATA=/path/to/test/data
./run_test [.files] #or using ctest, [.files] is the option to include tests needing data
./run_test [.with-data] #or using ctest, [.with-data] is the option to include tests needing data
@ -25,7 +25,7 @@ Python
.. code-block:: bash
#From the root dir of the library
python -m pytest python/tests --files # passing --files will run the tests needing data
python -m pytest python/tests --with-data # passing --with-data will run the tests needing data
@ -35,7 +35,7 @@ Getting the test data
.. attention ::
The tests needing the test data are not run by default. To make the data available, you need to set the environment variable
AARE_TEST_DATA to the path of the test data directory. Then pass either [.files] for the C++ tests or --files for Python
AARE_TEST_DATA to the path of the test data directory. Then pass either [.with-data] for the C++ tests or --files for Python
The image files needed for the test are large and are not included in the repository. They are stored
using GIT LFS in a separate repository. To get the test data, you need to clone the repository.

View File

@ -37,7 +37,7 @@ unfamiliar steps.
Checklists for deployment
~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Feature:**

View File

@ -31,8 +31,6 @@ AARE
pyJungfrauDataFile
pyRawFile
pyRawMasterFile
pyHdf5File
pyHdf5MasterFile
pyVarClusterFinder
pyFit
@ -57,8 +55,6 @@ AARE
RawFile
RawSubFile
RawMasterFile
Hdf5File
Hdf5MasterFile
VarClusterFinder

View File

@ -2,9 +2,24 @@
ClusterFile
============
The :class:`ClusterFile` class is the main interface to read and write clusters in aare. Unfortunately the
old file format does not include metadata like the cluster size and the data type. This means that the
user has to know this information from other sources. Specifying the wrong cluster size or data type
will lead to garbage data being read.
.. py:currentmodule:: aare
.. autoclass:: ClusterFile
:members:
:undoc-members:
:inherited-members:
Below is the API of the ClusterFile_Cluster3x3i but all variants share the same API.
.. autoclass:: aare._aare.ClusterFile_Cluster3x3i
:special-members: __init__
:members:
:undoc-members:
:show-inheritance:

View File

@ -2,8 +2,10 @@ ClusterVector
================
The ClusterVector, holds clusters from the ClusterFinder. Since it is templated
in C++ we use a suffix indicating the data type in python. The suffix is
``_i`` for integer, ``_f`` for float, and ``_d`` for double.
in C++ we use a suffix indicating the type of cluster it holds. The suffix follows
the same pattern as for ClusterFile i.e. ``ClusterVector_Cluster3x3i``
for a vector holding 3x3 integer clusters.
At the moment the functionality from python is limited and it is not supported
to push_back clusters to the vector. The intended use case is to pass it to
@ -26,7 +28,8 @@ C++ functions that support the ClusterVector or to view it as a numpy array.
.. py:currentmodule:: aare
.. autoclass:: ClusterVector_i
.. autoclass:: aare._aare.ClusterVector_Cluster3x3i
:special-members: __init__
:members:
:undoc-members:
:show-inheritance:

View File

@ -1,10 +0,0 @@
Hdf5File
===================
.. py:currentmodule:: aare
.. autoclass:: Hdf5File
:members:
:undoc-members:
:show-inheritance:
:inherited-members:

View File

@ -1,10 +0,0 @@
Hdf5MasterFile
===================
.. py:currentmodule:: aare
.. autoclass:: Hdf5MasterFile
:members:
:undoc-members:
:show-inheritance:
:inherited-members:

View File

@ -5,9 +5,12 @@ dependencies:
- anaconda-client
- conda-build
- doxygen
- sphinx=7.1.2
- sphinx
- breathe
- sphinx_rtd_theme
- furo
- zeromq
- pybind11
- numpy
- matplotlib

View File

@ -14,7 +14,7 @@
namespace aare {
/*
Binary cluster file. Expects data to be layed out as:
Binary cluster file. Expects data to be laid out as:
int32_t frame_number
uint32_t number_of_clusters
int16_t x, int16_t y, int32_t data[9] x number_of_clusters

View File

@ -32,7 +32,8 @@ class ClusterVector; // Forward declaration
*/
template <typename T, uint8_t ClusterSizeX, uint8_t ClusterSizeY,
typename CoordType>
class ClusterVector<Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>> {
class ClusterVector<Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>>
{
std::vector<Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>> m_data{};
int32_t m_frame_number{0}; // TODO! Check frame number size and type
@ -122,6 +123,11 @@ class ClusterVector<Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>> {
*/
size_t size() const { return m_data.size(); }
/**
* @brief Check if the vector is empty
*/
bool empty() const { return m_data.empty(); }
uint8_t cluster_size_x() const { return ClusterSizeX; }
uint8_t cluster_size_y() const { return ClusterSizeY; }

View File

@ -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<ssize_t> &get_modules_in_roi() const;
ssize_t get_modules_in_roi(const size_t index) const;
const std::vector<ModuleGeometry> &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<ModuleGeometry> module_geometries{};
std::vector<ssize_t> modules_in_roi{};
};
} // namespace aare

View File

@ -7,7 +7,7 @@ namespace aare {
/**
* @brief RAII File class for reading, and in the future potentially writing
* image files in various formats. Minimal generic interface. For specail
* fuctions plase use the RawFile, NumpyFile or Hdf5File classes directly. Wraps
* fuctions plase use the RawFile or NumpyFile classes directly. Wraps
* FileInterface to abstract the underlying file format
* @note **frame_number** refers the the frame number sent by the detector while
* **frame_index** is the position of the frame in the file

View File

@ -1,7 +1,7 @@
#pragma once
#include "aare/Dtype.hpp"
#include "aare/Frame.hpp"
#include "aare/to_string.hpp"
#include "aare/defs.hpp"
#include <filesystem>
#include <vector>
@ -46,7 +46,7 @@ struct FileConfig {
/**
* @brief FileInterface class to define the interface for file operations
* @note parent class for NumpyFile, RawFile and Hdf5File
* @note parent class for NumpyFile and RawFile
* @note all functions are pure virtual and must be implemented by the derived
* classes
*/

View File

@ -1,211 +0,0 @@
#pragma once
#include "aare/FileInterface.hpp"
#include "aare/Frame.hpp"
#include "aare/Hdf5MasterFile.hpp"
#include "aare/NDArray.hpp" //for pixel map
#include <optional>
namespace aare {
class H5Handles {
std::string file_name;
std::string dataset_name;
H5::H5File file;
H5::DataSet dataset;
H5::DataSpace dataspace;
H5::DataType datatype;
std::unique_ptr<H5::DataSpace> memspace;
std::vector<hsize_t> dims;
std::vector<hsize_t> count;
std::vector<hsize_t> offset;
public:
H5Handles(const std::string &fname, const std::string &dname)
: file_name(fname), dataset_name(dname), file(fname, H5F_ACC_RDONLY),
dataset(file.openDataSet(dname)), dataspace(dataset.getSpace()),
datatype(dataset.getDataType()) {
intialize_dimensions();
initialize_memspace();
}
std::vector<hsize_t> get_dims() const { return dims; }
void seek(size_t frame_index) {
if (frame_index >= dims[0]) {
throw std::runtime_error(LOCATION + "Invalid frame number");
}
offset[0] = static_cast<hsize_t>(frame_index);
}
void get_data_into(size_t frame_index, std::byte *frame_buffer,
size_t n_frames = 1) {
seek(frame_index);
count[0] = static_cast<hsize_t>(n_frames);
// std::cout << "offset:" << ToString(offset) << " count:" <<
// ToString(count) << std::endl;
dataspace.selectHyperslab(H5S_SELECT_SET, count.data(), offset.data());
dataset.read(frame_buffer, datatype, *memspace, dataspace);
}
void get_header_into(size_t frame_index, int part_index,
std::byte *header_buffer) {
seek(frame_index);
offset[1] = static_cast<hsize_t>(part_index);
// std::cout << "offset:" << ToString(offset) << " count:" <<
// ToString(count) << std::endl;
dataspace.selectHyperslab(H5S_SELECT_SET, count.data(), offset.data());
dataset.read(header_buffer, datatype, *memspace, dataspace);
}
private:
void intialize_dimensions() {
int rank = dataspace.getSimpleExtentNdims();
dims.resize(rank);
dataspace.getSimpleExtentDims(dims.data(), nullptr);
}
void initialize_memspace() {
int rank = dataspace.getSimpleExtentNdims();
count.clear();
offset.clear();
// header datasets or header virtual datasets
if (rank == 1 || rank == 2) {
count = std::vector<hsize_t>(rank, 1); // slice 1 value
offset = std::vector<hsize_t>(rank, 0);
memspace = std::make_unique<H5::DataSpace>(H5S_SCALAR);
} else if (rank >= 3) {
// data dataset (frame x height x width)
count = {1, dims[1], dims[2]};
offset = {0, 0, 0};
hsize_t dims_image[2] = {dims[1], dims[2]};
memspace = std::make_unique<H5::DataSpace>(2, dims_image);
} else {
throw std::runtime_error(
LOCATION + "Invalid rank for dataset: " + std::to_string(rank));
}
}
};
template <typename Fn>
void read_hdf5_header_fields(DetectorHeader *header, Fn &&fn_read_field) {
fn_read_field(0, reinterpret_cast<std::byte *>(&(header->frameNumber)));
fn_read_field(1, reinterpret_cast<std::byte *>(&(header->expLength)));
fn_read_field(2, reinterpret_cast<std::byte *>(&(header->packetNumber)));
fn_read_field(3, reinterpret_cast<std::byte *>(&(header->bunchId)));
fn_read_field(4, reinterpret_cast<std::byte *>(&(header->timestamp)));
fn_read_field(5, reinterpret_cast<std::byte *>(&(header->modId)));
fn_read_field(6, reinterpret_cast<std::byte *>(&(header->row)));
fn_read_field(7, reinterpret_cast<std::byte *>(&(header->column)));
fn_read_field(8, reinterpret_cast<std::byte *>(&(header->reserved)));
fn_read_field(9, reinterpret_cast<std::byte *>(&(header->debug)));
fn_read_field(10, reinterpret_cast<std::byte *>(&(header->roundRNumber)));
fn_read_field(11, reinterpret_cast<std::byte *>(&(header->detType)));
fn_read_field(12, reinterpret_cast<std::byte *>(&(header->version)));
fn_read_field(13, reinterpret_cast<std::byte *>(&(header->packetMask)));
}
/**
* @brief Class to read .h5 files. The class will parse the master file
* to find the correct geometry for the frames.
* @note A more generic interface is available in the aare::File class.
* Consider using that unless you need hdf5 file specific functionality.
*/
class Hdf5File : public FileInterface {
Hdf5MasterFile m_master;
size_t m_current_frame{};
size_t m_total_frames{};
size_t m_rows{};
size_t m_cols{};
static const std::string metadata_group_name;
static const std::vector<std::string> header_dataset_names;
std::unique_ptr<H5Handles> m_data_dataset{nullptr};
std::vector<std::unique_ptr<H5Handles>> m_header_datasets{};
public:
/**
* @brief Hdf5File constructor
* @param fname path to the master file (.json)
* @param mode file mode (only "r" is supported at the moment)
*/
Hdf5File(const std::filesystem::path &fname, const std::string &mode = "r");
virtual ~Hdf5File() override;
Frame read_frame() override;
Frame read_frame(size_t frame_number) override;
std::vector<Frame> read_n(size_t n_frames) override;
void read_into(std::byte *image_buf) override;
void read_into(std::byte *image_buf, size_t n_frames) override;
// TODO! do we need to adapt the API?
void read_into(std::byte *image_buf, DetectorHeader *header);
void read_into(std::byte *image_buf, size_t n_frames,
DetectorHeader *header);
size_t frame_number(size_t frame_index) override;
size_t bytes_per_frame() override;
size_t pixels_per_frame() override;
size_t bytes_per_pixel() const;
void seek(size_t frame_index) override;
size_t tell() override;
size_t total_frames() const override;
size_t rows() const override;
size_t cols() const override;
size_t bitdepth() const override;
xy geometry();
size_t n_modules() const;
Hdf5MasterFile master() const;
DetectorType detector_type() const override;
private:
/**
* @brief get the frame at the given frame index
* @param frame_number frame number to read
* @return Frame
*/
Frame get_frame(size_t frame_index);
/**
* @brief read the frame at the given frame index into the image buffer
* @param frame_number frame number to read
* @param n_frames number of frames to read (default is 1)
* @param image_buf buffer to store the frame
*/
void get_frame_into(size_t frame_index, std::byte *frame_buffer,
size_t n_frames = 1, DetectorHeader *header = nullptr);
/**
* @brief read the frame at the given frame index into the image buffer
* @param frame_index frame number to read
* @param n_frames number of frames to read (default is 1)
* @param frame_buffer buffer to store the frame
*/
void get_data_into(size_t frame_index, std::byte *frame_buffer,
size_t n_frames = 1);
/**
* @brief read the header at the given frame index into the header buffer
* @param frame_index frame number to read
* @param part_index part index to read (for virtual datasets)
* @param header buffer to store the header
*/
void get_header_into(size_t frame_index, int part_index,
DetectorHeader *header);
/**
* @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_data_file();
void open_header_files();
};
} // namespace aare

View File

@ -1,135 +0,0 @@
#pragma once
#include "aare/defs.hpp"
#include "aare/scan_parameters.hpp"
#include "H5Cpp.h"
#include <filesystem>
#include <fmt/format.h>
#include <fstream>
#include <optional>
namespace aare {
using ns = std::chrono::nanoseconds;
/**
* @brief Class for parsing a master file either in our .json format or the old
* .Hdf5 format
*/
class Hdf5MasterFile {
std::filesystem::path m_file_name{};
std::string m_version;
DetectorType m_type;
TimingMode m_timing_mode;
xy m_geometry{};
int m_image_size_in_bytes{};
int m_pixels_y{};
int m_pixels_x{};
int m_max_frames_per_file{};
FrameDiscardPolicy m_frame_discard_policy{};
int m_frame_padding{};
std::optional<ScanParameters> m_scan_parameters{};
size_t m_total_frames_expected{};
std::optional<ns> m_exptime{};
std::optional<ns> m_period{};
std::optional<BurstMode> m_burst_mode{};
std::optional<int> m_number_of_udp_interfaces{};
int m_bitdepth{};
std::optional<bool> m_ten_giga{};
std::optional<int> m_threshold_energy{};
std::optional<std::vector<int>> m_threshold_energy_all{};
std::optional<ns> m_subexptime{};
std::optional<ns> m_subperiod{};
std::optional<bool> m_quad{};
std::optional<int> m_number_of_rows{};
std::optional<std::vector<size_t>> m_rate_corrections{};
std::optional<uint32_t> m_adc_mask{};
bool m_analog_flag{};
std::optional<int> m_analog_samples{};
bool m_digital_flag{};
std::optional<int> m_digital_samples{};
std::optional<int> m_dbit_offset{};
std::optional<size_t> m_dbit_list{};
std::optional<int> m_transceiver_mask{};
bool m_transceiver_flag{};
std::optional<int> m_transceiver_samples{};
// g1 roi - will not be implemented?
std::optional<ROI> m_roi{};
std::optional<int> m_counter_mask{};
std::optional<std::vector<ns>> m_exptime_array{};
std::optional<std::vector<ns>> m_gate_delay_array{};
std::optional<int> m_gates{};
std::optional<std::map<std::string, std::string>>
m_additional_json_header{};
size_t m_frames_in_file{};
// TODO! should these be bool?
public:
Hdf5MasterFile(const std::filesystem::path &fpath);
std::filesystem::path file_name() const;
const std::string &version() const; //!< For example "7.2"
const DetectorType &detector_type() const;
const TimingMode &timing_mode() const;
xy geometry() const;
int image_size_in_bytes() const;
int pixels_y() const;
int pixels_x() const;
int max_frames_per_file() const;
const FrameDiscardPolicy &frame_discard_policy() const;
int frame_padding() const;
std::optional<ScanParameters> scan_parameters() const;
size_t total_frames_expected() const;
std::optional<ns> exptime() const;
std::optional<ns> period() const;
std::optional<BurstMode> burst_mode() const;
std::optional<int> number_of_udp_interfaces() const;
int bitdepth() const;
std::optional<bool> ten_giga() const;
std::optional<int> threshold_energy() const;
std::optional<std::vector<int>> threshold_energy_all() const;
std::optional<ns> subexptime() const;
std::optional<ns> subperiod() const;
std::optional<bool> quad() const;
std::optional<int> number_of_rows() const;
std::optional<std::vector<size_t>> rate_corrections() const;
std::optional<uint32_t> adc_mask() const;
bool analog_flag() const;
std::optional<int> analog_samples() const;
bool digital_flag() const;
std::optional<int> digital_samples() const;
std::optional<int> dbit_offset() const;
std::optional<size_t> dbit_list() const;
std::optional<int> transceiver_mask() const;
bool transceiver_flag() const;
std::optional<int> transceiver_samples() const;
// g1 roi - will not be implemented?
std::optional<ROI> roi() const;
std::optional<int> counter_mask() const;
std::optional<std::vector<ns>> exptime_array() const;
std::optional<std::vector<ns>> gate_delay_array() const;
std::optional<int> gates() const;
std::optional<std::map<std::string, std::string>>
additional_json_header() const;
size_t frames_in_file() const;
size_t n_modules() const;
private:
static const std::string metadata_group_name;
void parse_acquisition_metadata(const std::filesystem::path &fpath);
template <typename T>
T h5_read_scalar_dataset(const H5::DataSet &dataset,
const H5::DataType &data_type);
template <typename T>
T h5_get_scalar_dataset(const H5::H5File &file,
const std::string &dataset_name);
};
template <>
std::string Hdf5MasterFile::h5_read_scalar_dataset<std::string>(
const H5::DataSet &dataset, const H5::DataType &data_type);
} // namespace aare

View File

@ -67,20 +67,13 @@ class NDView : public ArrayExpr<NDView<T, Ndim>, Ndim> {
size_(std::accumulate(std::begin(shape), std::end(shape), 1,
std::multiplies<>())) {}
// NDView(T *buffer, const std::vector<ssize_t> &shape)
// : buffer_(buffer),
// strides_(c_strides<Ndim>(make_array<Ndim>(shape))),
// shape_(make_array<Ndim>(shape)),
// size_(std::accumulate(std::begin(shape), std::end(shape), 1,
// std::multiplies<>())) {}
template <typename... Ix>
std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) {
return buffer_[element_offset(strides_, index...)];
}
template <typename... Ix>
std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) const {
const std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) const {
return buffer_[element_offset(strides_, index...)];
}
@ -92,13 +85,17 @@ class NDView : public ArrayExpr<NDView<T, Ndim>, Ndim> {
T *end() { return buffer_ + size_; }
T const *begin() const { return buffer_; }
T const *end() const { return buffer_ + size_; }
T &operator()(ssize_t i) const { return buffer_[i]; }
T &operator[](ssize_t i) const { return buffer_[i]; }
T &operator()(ssize_t i) { return buffer_[i]; }
T &operator[](ssize_t i) { return buffer_[i]; }
const T &operator()(ssize_t i) const { return buffer_[i]; }
const T &operator[](ssize_t i) const { return buffer_[i]; }
bool operator==(const NDView &other) const {
if (size_ != other.size_)
return false;
for (uint64_t i = 0; i != size_; ++i) {
if (shape_ != other.shape_)
return false;
for (size_t i = 0; i != size_; ++i) {
if (buffer_[i] != other.buffer_[i])
return false;
}
@ -157,6 +154,7 @@ class NDView : public ArrayExpr<NDView<T, Ndim>, Ndim> {
auto shape(ssize_t i) const { return shape_[i]; }
T *data() { return buffer_; }
const T *data() const { return buffer_; }
void print_all() const;
private:
@ -180,6 +178,7 @@ class NDView : public ArrayExpr<NDView<T, Ndim>, Ndim> {
return *this;
}
};
template <typename T, ssize_t Ndim> void NDView<T, Ndim>::print_all() const {
for (auto row = 0; row < shape_[0]; ++row) {
for (auto col = 0; col < shape_[1]; ++col) {

View File

@ -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 <optional>
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<std::unique_ptr<RawSubFile>> 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

View File

@ -1,7 +1,6 @@
#pragma once
#include "aare/defs.hpp"
#include "aare/scan_parameters.hpp"
#include <algorithm>
#include <filesystem>
#include <fmt/format.h>
#include <fstream>
@ -41,6 +40,28 @@ class RawFileNameComponents {
void set_old_scheme(bool old_scheme);
};
class ScanParameters {
bool m_enabled = false;
std::string m_dac;
int m_start = 0;
int m_stop = 0;
int m_step = 0;
// TODO! add settleTime, requires string to time conversion
public:
ScanParameters(const std::string &par);
ScanParameters() = default;
ScanParameters(const ScanParameters &) = default;
ScanParameters &operator=(const ScanParameters &) = default;
ScanParameters(ScanParameters &&) = default;
int start() const;
int stop() const;
int step() const;
const std::string &dac() const;
bool enabled() const;
void increment_stop();
};
/**
* @brief Class for parsing a master file either in our .json format or the old
* .raw format
@ -57,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
@ -76,7 +99,6 @@ class RawMasterFile {
std::optional<size_t> m_digital_samples;
std::optional<size_t> m_transceiver_samples;
std::optional<size_t> m_number_of_rows;
std::optional<uint8_t> m_quad;
std::optional<ROI> m_roi;
@ -95,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<size_t> analog_samples() const;
std::optional<size_t> digital_samples() const;
std::optional<size_t> transceiver_samples() const;
std::optional<size_t> number_of_rows() const;
std::optional<uint8_t> quad() const;
std::optional<ROI> roi() const;
@ -114,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

View File

@ -1,15 +1,11 @@
#pragma once
#include "aare/Dtype.hpp"
#include "aare/type_traits.hpp"
#include <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <string_view>
@ -178,35 +174,6 @@ template <typename T> struct t_xy {
};
using xy = t_xy<uint32_t>;
/**
* @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<ModuleGeometry> module_pixel_0;
auto size() const { return module_pixel_0.size(); }
};
struct ROI {
ssize_t xmin{};
ssize_t xmax{};
@ -250,12 +217,17 @@ enum class DetectorType {
enum class TimingMode { Auto, Trigger };
enum class FrameDiscardPolicy { NoDiscard, Discard, DiscardPartial };
enum class BurstMode {
Burst_Interal,
Burst_External,
Continuous_Internal,
Continuous_External
};
template <class T> T StringTo(const std::string &arg) { return T(arg); }
template <class T> std::string ToString(T arg) { return T(arg); }
template <> DetectorType StringTo(const std::string & /*name*/);
template <> std::string ToString(DetectorType arg);
template <> TimingMode StringTo(const std::string & /*mode*/);
template <> FrameDiscardPolicy StringTo(const std::string & /*mode*/);
using DataTypeVariants = std::variant<uint16_t, uint32_t>;

View File

@ -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

View File

@ -1,51 +0,0 @@
#pragma once
#include <string>
#include <sstream>
namespace aare {
class ScanParameters {
bool m_enabled = false;
std::string m_dac;
int m_start = 0;
int m_stop = 0;
int m_step = 0;
// ns m_dac_settle_time{0};
// TODO! add settleTime, requires string to time conversion
public:
// "[enabled\ndac dac 4\nstart 500\nstop 2200\nstep 5\nsettleTime 100us\n]"
// TODO: use StringTo<ScanParameters> and move this to to_string
// add ways of setting the members of the class
ScanParameters(const std::string &par) {
std::istringstream iss(par.substr(1, par.size() - 2));
std::string line;
while (std::getline(iss, line)) {
if (line == "enabled") {
m_enabled = true;
} else if (line.find("dac") != std::string::npos) {
m_dac = line.substr(4);
} else if (line.find("start") != std::string::npos) {
m_start = std::stoi(line.substr(6));
} else if (line.find("stop") != std::string::npos) {
m_stop = std::stoi(line.substr(5));
} else if (line.find("step") != std::string::npos) {
m_step = std::stoi(line.substr(5));
}
}
};
ScanParameters() = default;
ScanParameters(const ScanParameters &) = default;
ScanParameters &operator=(const ScanParameters &) = default;
ScanParameters(ScanParameters &&) = default;
int start() const { return m_start; };
int stop() const { return m_stop; };
int step() const { return m_step; };
const std::string &dac() const { return m_dac; };
bool enabled() const { return m_enabled; };
void increment_stop() { m_stop += 1; };
};
} // namespace aare

View File

@ -1,11 +0,0 @@
#pragma once
#include <string>
namespace aare {
std::string RemoveUnit(std::string &str);
void TrimWhiteSpaces(std::string &s);
} // namespace aare

View File

@ -1,288 +0,0 @@
#pragma once
#include "aare/defs.hpp"
#include "aare/scan_parameters.hpp"
#include "aare/string_utils.hpp"
#include <optional>
#include <chrono>
namespace aare {
// generic
template <class T, typename = std::enable_if_t<!is_duration<T>::value>>
std::string ToString(T arg) {
return T(arg);
}
template <typename T,
std::enable_if_t<!is_duration<T>::value && !is_container<T>::value,
int> = 0>
T StringTo(const std::string &arg) {
return T(arg);
}
// time
/** Convert std::chrono::duration with specified output unit */
template <typename T, typename Rep = double>
typename std::enable_if<is_duration<T>::value, std::string>::type
ToString(T t, const std::string &unit) {
using std::chrono::duration;
using std::chrono::duration_cast;
std::ostringstream os;
if (unit == "ns")
os << duration_cast<duration<Rep, std::nano>>(t).count() << unit;
else if (unit == "us")
os << duration_cast<duration<Rep, std::micro>>(t).count() << unit;
else if (unit == "ms")
os << duration_cast<duration<Rep, std::milli>>(t).count() << unit;
else if (unit == "s")
os << duration_cast<duration<Rep>>(t).count() << unit;
else
throw std::runtime_error("Unknown unit: " + unit);
return os.str();
}
/** Convert std::chrono::duration automatically selecting the unit */
template <typename From>
typename std::enable_if<is_duration<From>::value, std::string>::type
ToString(From t) {
using std::chrono::abs;
using std::chrono::duration_cast;
using std::chrono::microseconds;
using std::chrono::milliseconds;
using std::chrono::nanoseconds;
auto tns = duration_cast<nanoseconds>(t);
if (abs(tns) < microseconds(1)) {
return ToString(tns, "ns");
} else if (abs(tns) < milliseconds(1)) {
return ToString(tns, "us");
} else if (abs(tns) < milliseconds(99)) {
return ToString(tns, "ms");
} else {
return ToString(tns, "s");
}
}
template <class Rep, class Period>
std::ostream &operator<<(std::ostream &os,
const std::chrono::duration<Rep, Period> &d) {
return os << ToString(d);
}
template <typename T>
T StringTo(const std::string &t, const std::string &unit) {
double tval{0};
try {
tval = std::stod(t);
} catch (const std::invalid_argument &e) {
throw std::invalid_argument("[ERROR] Could not convert string to time");
}
using std::chrono::duration;
using std::chrono::duration_cast;
if (unit == "ns") {
return duration_cast<T>(duration<double, std::nano>(tval));
} else if (unit == "us") {
return duration_cast<T>(duration<double, std::micro>(tval));
} else if (unit == "ms") {
return duration_cast<T>(duration<double, std::milli>(tval));
} else if (unit == "s" || unit.empty()) {
return duration_cast<T>(std::chrono::duration<double>(tval));
} else {
throw std::invalid_argument("[ERROR] Invalid unit in conversion from "
"string to std::chrono::duration");
}
}
template <typename T, std::enable_if_t<is_duration<T>::value, int> = 0>
T StringTo(const std::string &t) {
std::string tmp{t};
auto unit = RemoveUnit(tmp);
return StringTo<T>(tmp, unit);
}
template <> inline bool StringTo(const std::string &s) {
int i = std::stoi(s, nullptr, 10);
switch (i) {
case 0:
return false;
case 1:
return true;
default:
throw std::runtime_error("Unknown boolean. Expecting be 0 or 1.");
}
}
template <> inline uint8_t StringTo(const std::string &s) {
int base = s.find("0x") != std::string::npos ? 16 : 10;
int value = std::stoi(s, nullptr, base);
if (value < std::numeric_limits<uint8_t>::min() ||
value > std::numeric_limits<uint8_t>::max()) {
throw std::runtime_error("Cannot scan uint8_t from string '" + s +
"'. Value must be in range 0 - 255.");
}
return static_cast<uint8_t>(value);
}
template <> inline uint16_t StringTo(const std::string &s) {
int base = s.find("0x") != std::string::npos ? 16 : 10;
int value = std::stoi(s, nullptr, base);
if (value < std::numeric_limits<uint16_t>::min() ||
value > std::numeric_limits<uint16_t>::max()) {
throw std::runtime_error("Cannot scan uint16_t from string '" + s +
"'. Value must be in range 0 - 65535.");
}
return static_cast<uint16_t>(value);
}
template <> inline uint32_t StringTo(const std::string &s) {
int base = s.find("0x") != std::string::npos ? 16 : 10;
return std::stoul(s, nullptr, base);
}
template <> inline uint64_t StringTo(const std::string &s) {
int base = s.find("0x") != std::string::npos ? 16 : 10;
return std::stoull(s, nullptr, base);
}
template <> inline int StringTo(const std::string &s) {
int base = s.find("0x") != std::string::npos ? 16 : 10;
return std::stoi(s, nullptr, base);
}
/*template <> inline size_t StringTo(const std::string &s) {
int base = s.find("0x") != std::string::npos ? 16 : 10;
return std::stoull(s, nullptr, base);
}*/
// vector
template <typename T> std::string ToString(const std::vector<T> &vec) {
std::ostringstream oss;
oss << "[";
for (size_t i = 0; i < vec.size(); ++i) {
oss << vec[i];
if (i != vec.size() - 1)
oss << ", ";
}
oss << "]";
return oss.str();
}
template <typename T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
return os << ToString(v);
}
template <typename Container,
std::enable_if_t<is_container<Container>::value &&
!is_std_string_v<Container> /*&&
!is_map_v<Container>*/
,
int> = 0>
Container StringTo(const std::string &s) {
using Value = typename Container::value_type;
// strip outer brackets
std::string str = s;
str.erase(
std::remove_if(str.begin(), str.end(),
[](unsigned char c) { return c == '[' || c == ']'; }),
str.end());
std::stringstream ss(str);
std::string item;
Container result;
while (std::getline(ss, item, ',')) {
TrimWhiteSpaces(item);
if (!item.empty()) {
result.push_back(StringTo<Value>(item));
}
}
return result;
}
// map
template <typename KeyType, typename ValueType>
std::string ToString(const std::map<KeyType, ValueType> &m) {
std::ostringstream os;
os << '{';
if (!m.empty()) {
auto it = m.cbegin();
os << ToString(it->first) << ": " << ToString(it->second);
it++;
while (it != m.cend()) {
os << ", " << ToString(it->first) << ": " << ToString(it->second);
it++;
}
}
os << '}';
return os.str();
}
template <>
inline std::map<std::string, std::string> StringTo(const std::string &s) {
std::map<std::string, std::string> result;
std::string str = s;
// Remove outer braces if present
if (!str.empty() && str.front() == '{' && str.back() == '}') {
str = str.substr(1, str.size() - 2);
}
std::stringstream ss(str);
std::string item;
while (std::getline(ss, item, ',')) {
auto colon_pos = item.find(':');
if (colon_pos == std::string::npos)
throw std::runtime_error("Missing ':' in item: " + item);
std::string key = item.substr(0, colon_pos);
std::string value = item.substr(colon_pos + 1);
TrimWhiteSpaces(key);
TrimWhiteSpaces(value);
result[key] = value;
}
return result;
}
// optional
template <class T> std::string ToString(const std::optional<T> &opt) {
return opt ? ToString(*opt) : "nullopt";
}
template <typename T>
std::ostream &operator<<(std::ostream &os, const std::optional<T> &opt) {
if (opt)
os << *opt;
else
os << "nullopt";
return os;
}
// enums
template <> std::string ToString(DetectorType arg);
template <> DetectorType StringTo(const std::string & /*name*/);
template <> std::string ToString(TimingMode arg);
template <> TimingMode StringTo(const std::string & /*mode*/);
template <> std::string ToString(FrameDiscardPolicy arg);
template <> FrameDiscardPolicy StringTo(const std::string & /*mode*/);
template <> std::string ToString(BurstMode arg);
template <> BurstMode StringTo(const std::string & /*mode*/);
template <> std::string ToString(ROI arg);
std::ostream &operator<<(std::ostream &os, const ROI &roi);
template <> std::string ToString(ScanParameters arg);
std::ostream &operator<<(std::ostream &os, const ScanParameters &r);
} // namespace aare

View File

@ -1,72 +0,0 @@
#pragma once
#include <type_traits>
namespace aare {
/**
* Type trait to check if a template parameter is a std::chrono::duration
*/
template <typename T, typename _ = void>
struct is_duration : std::false_type {};
template <typename... Ts> struct is_duration_helper {};
template <typename T>
struct is_duration<T,
typename std::conditional<
false,
is_duration_helper<typename T::rep, typename T::period,
decltype(std::declval<T>().min()),
decltype(std::declval<T>().max()),
decltype(std::declval<T>().zero())>,
void>::type> : public std::true_type {};
/**
* Type trait to evaluate if template parameter is
* complying with a standard container
*/
template <typename T, typename _ = void>
struct is_container : std::false_type {};
template <typename... Ts> struct is_container_helper {};
template <typename T>
struct is_container<
T, typename std::conditional<
false,
is_container_helper<
typename std::remove_reference<T>::type::value_type,
typename std::remove_reference<T>::type::size_type,
typename std::remove_reference<T>::type::iterator,
typename std::remove_reference<T>::type::const_iterator,
decltype(std::declval<T>().size()),
decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end()),
decltype(std::declval<T>().cbegin()),
decltype(std::declval<T>().cend()),
decltype(std::declval<T>().empty())>,
void>::type> : public std::true_type {};
/**
* Type trait to evaluate if template parameter is
* complying with a std::string
*/
template <typename T>
inline constexpr bool is_std_string_v =
std::is_same_v<std::decay_t<T>, std::string>;
/**
* Type trait to evaluate if template parameter is
* complying with std::map
*/
template <typename T> struct is_map : std::false_type {};
template <typename K, typename V, typename... Args>
struct is_map<std::map<K, V, Args...>> : std::true_type {};
template <typename T>
inline constexpr bool is_map_v = is_map<std::decay_t<T>>::value;
} // namespace aare

View File

@ -1,16 +1,8 @@
# from ._aare import ClusterFinder_Cluster3x3i, ClusterFinder_Cluster2x2i, ClusterFinderMT_Cluster3x3i, ClusterFinderMT_Cluster2x2i, ClusterCollector_Cluster3x3i, ClusterCollector_Cluster2x2i
# from ._aare import ClusterFileSink_Cluster3x3i, ClusterFileSink_Cluster2x2i
from . import _aare
import numpy as np
_supported_cluster_sizes = [(2,2), (3,3), (5,5), (7,7), (9,9),]
# def _get_class()
def _type_to_char(dtype):
if dtype == np.int32:
return 'i'
@ -74,11 +66,22 @@ def ClusterFileSink(clusterfindermt, cluster_file, dtype=np.int32):
return cls(clusterfindermt, cluster_file)
def ClusterFile(fname, cluster_size=(3,3), dtype=np.int32):
def ClusterFile(fname, cluster_size=(3,3), dtype=np.int32, chunk_size = 1000):
"""
Factory function to create a ClusterFile object. Provides a cleaner syntax for
the templated ClusterFile in C++.
.. code-block:: python
from aare import ClusterFile
with ClusterFile("clusters.clust", cluster_size=(3,3), dtype=np.int32) as cf:
# cf is now a ClusterFile_Cluster3x3i object but you don't need to know that.
for clusters in cf:
# Loop over clusters in chunks of 1000
# The type of clusters will be a ClusterVector_Cluster3x3i in this case
"""
cls = _get_class("ClusterFile", cluster_size, dtype)
return cls(fname)
return cls(fname, chunk_size=chunk_size)

View File

@ -1,66 +0,0 @@
from . import _aare
import numpy as np
#from .ScanParameters import ScanParameters
class Hdf5File(_aare.Hdf5File):
def __init__(self, fname, chunk_size = 1):
super().__init__(fname)
self._chunk_size = chunk_size
def read(self) -> tuple:
"""Read the entire file.
Seeks to the beginning of the file before reading.
Returns:
tuple: header, data
"""
self.seek(0)
return self.read_n(self.total_frames)
# @property
# def scan_parameters(self):
# """Return the scan parameters.
# Returns:
# ScanParameters: Scan parameters.
# """
# return ScanParameters(self.master.scan_parameters)
@property
def master(self):
"""Return the master file.
Returns:
Hdf5MasterFile: Master file.
"""
return super().master
def __len__(self) -> int:
"""Return the number of frames in the file.
Returns:
int: Number of frames in file.
"""
return super().frames_in_file
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def __iter__(self):
return self
def __next__(self):
try:
if self._chunk_size == 1:
return self.read_frame()
else:
return self.read_n(self._chunk_size)
except RuntimeError:
# TODO! find a good way to check that we actually have the right exception
raise StopIteration

View File

@ -2,7 +2,7 @@
from . import _aare
from ._aare import File, RawMasterFile, RawSubFile, Hdf5MasterFile, JungfrauDataFile
from ._aare import File, RawMasterFile, RawSubFile, JungfrauDataFile
from ._aare import Pedestal_d, Pedestal_f, ClusterFinder_Cluster3x3i, VarClusterFinder
from ._aare import DetectorType
from ._aare import hitmap
@ -23,7 +23,6 @@ from ._aare import apply_custom_weights
from .CtbRawFile import CtbRawFile
from .RawFile import RawFile
from .Hdf5File import Hdf5File
from .ScanParameters import ScanParameters
from .utils import random_pixels, random_pixel, flat_list, add_colorbar

View File

@ -38,19 +38,20 @@ void define_ClusterFile(py::module &m, const std::string &typestr) {
self.read_clusters(n_clusters));
return v;
},
py::return_value_policy::take_ownership)
py::return_value_policy::take_ownership, py::arg("n_clusters"))
.def("read_frame",
[](ClusterFile<ClusterType> &self) {
auto v = new ClusterVector<ClusterType>(self.read_frame());
return v;
})
.def("set_roi", &ClusterFile<ClusterType>::set_roi)
.def("set_roi", &ClusterFile<ClusterType>::set_roi,
py::arg("roi"))
.def(
"set_noise_map",
[](ClusterFile<ClusterType> &self, py::array_t<int32_t> noise_map) {
auto view = make_view_2d(noise_map);
self.set_noise_map(view);
})
}, py::arg("noise_map"))
.def("set_gain_map",
[](ClusterFile<ClusterType> &self, py::array_t<double> gain_map) {

View File

@ -5,11 +5,6 @@
#include "aare/RawMasterFile.hpp"
#include "aare/RawSubFile.hpp"
#ifdef HDF5_FOUND
#include "aare/Hdf5File.hpp"
#include "aare/Hdf5MasterFile.hpp"
#endif
#include "aare/defs.hpp"
// #include "aare/fClusterFileV2.hpp"

View File

@ -1,106 +0,0 @@
#include "H5Cpp.h"
#include "aare/File.hpp"
#include "aare/Frame.hpp"
#include "aare/Hdf5File.hpp"
#include "aare/Hdf5MasterFile.hpp"
#include "aare/defs.hpp"
// #include "aare/fClusterFileV2.hpp"
#include <cstdint>
#include <filesystem>
#include <pybind11/iostream.h>
#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl/filesystem.h>
#include <string>
namespace py = pybind11;
using namespace ::aare;
void define_hdf5_file_io_bindings(py::module &m) {
py::class_<Hdf5File>(m, "Hdf5File")
.def(py::init<const std::filesystem::path &>())
.def("read_frame",
[](Hdf5File &self) {
py::array image;
std::vector<ssize_t> shape;
shape.reserve(2);
shape.push_back(self.rows());
shape.push_back(self.cols());
// return headers from all subfiles
py::array_t<DetectorHeader> header(self.n_mod());
const uint8_t item_size = self.bytes_per_pixel();
if (item_size == 1) {
image = py::array_t<uint8_t>(shape);
} else if (item_size == 2) {
image = py::array_t<uint16_t>(shape);
} else if (item_size == 4) {
image = py::array_t<uint32_t>(shape);
}
self.read_into(
reinterpret_cast<std::byte *>(image.mutable_data()),
header.mutable_data());
return py::make_tuple(header, image);
})
.def(
"read_n",
[](Hdf5File &self, size_t n_frames) {
// adjust for actual frames left in the file
n_frames =
std::min(n_frames, self.total_frames() - self.tell());
if (n_frames == 0) {
throw std::runtime_error("No frames left in file");
}
std::vector<size_t> shape{n_frames, self.rows(), self.cols()};
// return headers from all subfiles
py::array_t<DetectorHeader> header;
if (self.n_mod() == 1) {
header = py::array_t<DetectorHeader>(n_frames);
} else {
header =
py::array_t<DetectorHeader>({self.n_mod(), n_frames});
}
// py::array_t<DetectorHeader> header({self.n_mod(), n_frames});
py::array image;
const uint8_t item_size = self.bytes_per_pixel();
if (item_size == 1) {
image = py::array_t<uint8_t>(shape);
} else if (item_size == 2) {
image = py::array_t<uint16_t>(shape);
} else if (item_size == 4) {
image = py::array_t<uint32_t>(shape);
}
self.read_into(
reinterpret_cast<std::byte *>(image.mutable_data()),
n_frames, header.mutable_data());
return py::make_tuple(header, image);
},
R"(
Read n frames from the file.
)")
.def("frame_number", &Hdf5File::frame_number)
.def_property_readonly("bytes_per_frame", &Hdf5File::bytes_per_frame)
.def_property_readonly("pixels_per_frame", &Hdf5File::pixels_per_frame)
.def_property_readonly("bytes_per_pixel", &Hdf5File::bytes_per_pixel)
.def("seek", &Hdf5File::seek, R"(
Seek to a frame index in file.
)")
.def("tell", &Hdf5File::tell, R"(
Return the current frame number.)")
.def_property_readonly("total_frames", &Hdf5File::total_frames)
.def_property_readonly("rows", &Hdf5File::rows)
.def_property_readonly("cols", &Hdf5File::cols)
.def_property_readonly("bitdepth", &Hdf5File::bitdepth)
.def_property_readonly("geometry", &Hdf5File::geometry)
.def_property_readonly("n_mod", &Hdf5File::n_mod)
.def_property_readonly("detector_type", &Hdf5File::detector_type)
.def_property_readonly("master", &Hdf5File::master);
}

View File

@ -1,86 +0,0 @@
#include "aare/File.hpp"
#include "aare/Frame.hpp"
#include "aare/Hdf5File.hpp"
#include "aare/Hdf5MasterFile.hpp"
#include "aare/defs.hpp"
// #include "aare/fClusterFileV2.hpp"
#include <cstdint>
#include <filesystem>
#include <pybind11/iostream.h>
#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl/filesystem.h>
#include <string>
namespace py = pybind11;
using namespace ::aare;
void define_hdf5_master_file_bindings(py::module &m) {
py::class_<Hdf5MasterFile>(m, "Hdf5MasterFile")
.def(py::init<const std::filesystem::path &>())
.def("data_fname", &Hdf5MasterFile::data_fname, R"(
Parameters
------------
module_index : int
module index (d0, d1 .. dN)
file_index : int
file index (f0, f1 .. fN)
Returns
----------
os.PathLike
The name of the data file.
)")
.def_property_readonly("version", &Hdf5MasterFile::version)
.def_property_readonly("detector_type", &Hdf5MasterFile::detector_type)
.def_property_readonly("timing_mode", &Hdf5MasterFile::timing_mode)
.def_property_readonly("image_size_in_bytes",
&Hdf5MasterFile::image_size_in_bytes)
.def_property_readonly("frames_in_file",
&Hdf5MasterFile::frames_in_file)
.def_property_readonly("pixels_y", &Hdf5MasterFile::pixels_y)
.def_property_readonly("pixels_x", &Hdf5MasterFile::pixels_x)
.def_property_readonly("max_frames_per_file",
&Hdf5MasterFile::max_frames_per_file)
.def_property_readonly("bitdepth", &Hdf5MasterFile::bitdepth)
.def_property_readonly("frame_padding", &Hdf5MasterFile::frame_padding)
.def_property_readonly("frame_discard_policy",
&Hdf5MasterFile::frame_discard_policy)
.def_property_readonly("total_frames_expected",
&Hdf5MasterFile::total_frames_expected)
.def_property_readonly("geometry", &Hdf5MasterFile::geometry)
.def_property_readonly("analog_samples",
&Hdf5MasterFile::analog_samples, R"(
Number of analog samples
Returns
----------
int | None
The number of analog samples in the file (or None if not enabled)
)")
.def_property_readonly("digital_samples",
&Hdf5MasterFile::digital_samples, R"(
Number of digital samples
Returns
----------
int | None
The number of digital samples in the file (or None if not enabled)
)")
.def_property_readonly("transceiver_samples",
&Hdf5MasterFile::transceiver_samples)
.def_property_readonly("number_of_rows",
&Hdf5MasterFile::number_of_rows)
.def_property_readonly("quad", &Hdf5MasterFile::quad);
//.def_property_readonly("scan_parameters",
// &Hdf5MasterFile::scan_parameters)
//.def_property_readonly("roi", &Hdf5MasterFile::roi);
}

View File

@ -14,10 +14,6 @@
#include "file.hpp"
#include "fit.hpp"
#include "interpolation.hpp"
#ifdef HDF5_FOUND
#include "hdf5_file.hpp"
#include "hdf5_master_file.hpp"
#endif
#include "jungfrau_data_file.hpp"
#include "pedestal.hpp"
#include "pixel_map.hpp"
@ -58,10 +54,6 @@ PYBIND11_MODULE(_aare, m) {
define_raw_sub_file_io_bindings(m);
define_ctb_raw_file_io_bindings(m);
define_raw_master_file_bindings(m);
#ifdef HDF5_FOUND
define_hdf5_file_io_bindings(m);
define_hdf5_master_file_bindings(m);
#endif
define_var_cluster_finder_bindings(m);
define_pixel_map_bindings(m);
define_pedestal_bindings<double>(m, "Pedestal_d");

View File

@ -65,7 +65,7 @@ void define_raw_file_io_bindings(py::module &m) {
header = py::array_t<DetectorHeader>(n_frames);
} else {
header = py::array_t<DetectorHeader>(
{self.n_modules(), n_frames});
{self.n_modules_in_roi(), n_frames});
}
// py::array_t<DetectorHeader> 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);
}

View File

@ -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

View File

@ -6,20 +6,20 @@ import pytest
def pytest_addoption(parser):
parser.addoption(
"--files", action="store_true", default=False, help="run slow tests"
"--with-data", action="store_true", default=False, help="Run tests that require additional data"
)
def pytest_configure(config):
config.addinivalue_line("markers", "files: mark test as needing image files to run")
config.addinivalue_line("markers", "withdata: mark test as needing image files to run")
def pytest_collection_modifyitems(config, items):
if config.getoption("--files"):
if config.getoption("--with-data"):
return
skip = pytest.mark.skip(reason="need --files option to run")
skip = pytest.mark.skip(reason="need --with-data option to run")
for item in items:
if "files" in item.keywords:
if "withdata" in item.keywords:
item.add_marker(skip)

View File

@ -9,7 +9,7 @@ import pickle
from aare import ClusterFile
from conftest import test_data_path
@pytest.mark.files
@pytest.mark.withdata
def test_cluster_file(test_data_path):
"""Test ClusterFile"""
f = ClusterFile(test_data_path / "clust/single_frame_97_clustrers.clust")
@ -39,7 +39,7 @@ def test_cluster_file(test_data_path):
for i in range(10):
assert arr[i]['x'] == i+1
@pytest.mark.files
@pytest.mark.withdata
def test_read_clusters_and_fill_histogram(test_data_path):
# Create the histogram
n_bins = 100

View File

@ -0,0 +1,73 @@
import pytest
from aare import RawFile
import numpy as np
@pytest.mark.withdata
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.withdata
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.withdata
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()

View File

@ -3,7 +3,7 @@ import numpy as np
from aare import RawSubFile, DetectorType
@pytest.mark.files
@pytest.mark.withdata
def test_read_a_jungfrau_RawSubFile(test_data_path):
# Starting with f1 there is now 7 frames left in the series of files
@ -23,7 +23,7 @@ def test_read_a_jungfrau_RawSubFile(test_data_path):
data = np.load(test_data_path / "raw/jungfrau/jungfrau_single_0.npy")
assert np.all(data[3:] == frames)
@pytest.mark.files
@pytest.mark.withdata
def test_iterate_over_a_jungfrau_RawSubFile(test_data_path):
data = np.load(test_data_path / "raw/jungfrau/jungfrau_single_0.npy")

View File

@ -2,7 +2,7 @@ import pytest
import numpy as np
from aare import JungfrauDataFile
@pytest.mark.files
@pytest.mark.withdata
def test_jfungfrau_dat_read_number_of_frames(test_data_path):
with JungfrauDataFile(test_data_path / "dat/AldoJF500k_000000.dat") as dat_file:
assert dat_file.total_frames == 24
@ -14,7 +14,7 @@ def test_jfungfrau_dat_read_number_of_frames(test_data_path):
assert dat_file.total_frames == 113
@pytest.mark.files
@pytest.mark.withdata
def test_jfungfrau_dat_read_number_of_file(test_data_path):
with JungfrauDataFile(test_data_path / "dat/AldoJF500k_000000.dat") as dat_file:
assert dat_file.n_files == 4
@ -26,7 +26,7 @@ def test_jfungfrau_dat_read_number_of_file(test_data_path):
assert dat_file.n_files == 7
@pytest.mark.files
@pytest.mark.withdata
def test_read_module(test_data_path):
"""
Read all frames from the series of .dat files. Compare to canned data in npz format.
@ -50,7 +50,7 @@ def test_read_module(test_data_path):
assert np.all(ref_header == header)
assert np.all(ref_data == data)
@pytest.mark.files
@pytest.mark.withdata
def test_read_half_module(test_data_path):
# Read all frames from the .dat file
@ -71,7 +71,7 @@ def test_read_half_module(test_data_path):
assert np.all(ref_data == data)
@pytest.mark.files
@pytest.mark.withdata
def test_read_single_chip(test_data_path):
# Read all frames from the .dat file

View File

@ -1,395 +0,0 @@
#include "aare/ClusterFile.hpp"
#include <algorithm>
namespace aare {
ClusterFile::ClusterFile(const std::filesystem::path &fname, size_t chunk_size,
const std::string &mode)
: m_chunk_size(chunk_size), m_mode(mode) {
if (mode == "r") {
fp = fopen(fname.c_str(), "rb");
if (!fp) {
throw std::runtime_error("Could not open file for reading: " +
fname.string());
}
} else if (mode == "w") {
fp = fopen(fname.c_str(), "wb");
if (!fp) {
throw std::runtime_error("Could not open file for writing: " +
fname.string());
}
} else if (mode == "a") {
fp = fopen(fname.c_str(), "ab");
if (!fp) {
throw std::runtime_error("Could not open file for appending: " +
fname.string());
}
} else {
throw std::runtime_error("Unsupported mode: " + mode);
}
}
void ClusterFile::set_roi(ROI roi) { m_roi = roi; }
void ClusterFile::set_noise_map(const NDView<int32_t, 2> noise_map) {
m_noise_map = NDArray<int32_t, 2>(noise_map);
}
void ClusterFile::set_gain_map(const NDView<double, 2> gain_map) {
m_gain_map = NDArray<double, 2>(gain_map);
// Gain map is passed as ADU/keV to avoid dividing in when applying the gain
// map we invert it here
for (auto &item : m_gain_map->view()) {
item = 1.0 / item;
}
}
ClusterFile::~ClusterFile() { close(); }
void ClusterFile::close() {
if (fp) {
fclose(fp);
fp = nullptr;
}
}
void ClusterFile::write_frame(const ClusterVector<int32_t> &clusters) {
if (m_mode != "w" && m_mode != "a") {
throw std::runtime_error("File not opened for writing");
}
if (!(clusters.cluster_size_x() == 3) &&
!(clusters.cluster_size_y() == 3)) {
throw std::runtime_error("Only 3x3 clusters are supported");
}
// First write the frame number - 4 bytes
int32_t frame_number = clusters.frame_number();
if (fwrite(&frame_number, sizeof(frame_number), 1, fp) != 1) {
throw std::runtime_error(LOCATION + "Could not write frame number");
}
// Then write the number of clusters - 4 bytes
uint32_t n_clusters = clusters.size();
if (fwrite(&n_clusters, sizeof(n_clusters), 1, fp) != 1) {
throw std::runtime_error(LOCATION +
"Could not write number of clusters");
}
// Now write the clusters in the frame
if (fwrite(clusters.data(), clusters.item_size(), clusters.size(), fp) !=
clusters.size()) {
throw std::runtime_error(LOCATION + "Could not write clusters");
}
}
ClusterVector<int32_t> ClusterFile::read_clusters(size_t n_clusters) {
if (m_mode != "r") {
throw std::runtime_error("File not opened for reading");
}
if (m_noise_map || m_roi) {
return read_clusters_with_cut(n_clusters);
} else {
return read_clusters_without_cut(n_clusters);
}
}
ClusterVector<int32_t>
ClusterFile::read_clusters_without_cut(size_t n_clusters) {
if (m_mode != "r") {
throw std::runtime_error("File not opened for reading");
}
ClusterVector<int32_t> clusters(3, 3, n_clusters);
int32_t iframe = 0; // frame number needs to be 4 bytes!
size_t nph_read = 0;
uint32_t nn = m_num_left;
uint32_t nph = m_num_left; // number of clusters in frame needs to be 4
// auto buf = reinterpret_cast<Cluster3x3 *>(clusters.data());
auto buf = clusters.data();
// if there are photons left from previous frame read them first
if (nph) {
if (nph > n_clusters) {
// if we have more photons left in the frame then photons to read we
// read directly the requested number
nn = n_clusters;
} else {
nn = nph;
}
nph_read += fread((buf + nph_read * clusters.item_size()),
clusters.item_size(), nn, fp);
m_num_left = nph - nn; // write back the number of photons left
}
if (nph_read < n_clusters) {
// keep on reading frames and photons until reaching n_clusters
while (fread(&iframe, sizeof(iframe), 1, fp)) {
clusters.set_frame_number(iframe);
// read number of clusters in frame
if (fread(&nph, sizeof(nph), 1, fp)) {
if (nph > (n_clusters - nph_read))
nn = n_clusters - nph_read;
else
nn = nph;
nph_read += fread((buf + nph_read * clusters.item_size()),
clusters.item_size(), nn, fp);
m_num_left = nph - nn;
}
if (nph_read >= n_clusters)
break;
}
}
// Resize the vector to the number of clusters.
// No new allocation, only change bounds.
clusters.resize(nph_read);
if (m_gain_map)
clusters.apply_gain_map(m_gain_map->view());
return clusters;
}
ClusterVector<int32_t> ClusterFile::read_clusters_with_cut(size_t n_clusters) {
ClusterVector<int32_t> clusters(3, 3);
clusters.reserve(n_clusters);
// if there are photons left from previous frame read them first
if (m_num_left) {
while (m_num_left && clusters.size() < n_clusters) {
Cluster3x3 c = read_one_cluster();
if (is_selected(c)) {
clusters.push_back(c.x, c.y,
reinterpret_cast<std::byte *>(c.data));
}
}
}
// we did not have enough clusters left in the previous frame
// keep on reading frames until reaching n_clusters
if (clusters.size() < n_clusters) {
// sanity check
if (m_num_left) {
throw std::runtime_error(
LOCATION + "Entered second loop with clusters left\n");
}
int32_t frame_number = 0; // frame number needs to be 4 bytes!
while (fread(&frame_number, sizeof(frame_number), 1, fp)) {
if (fread(&m_num_left, sizeof(m_num_left), 1, fp)) {
clusters.set_frame_number(
frame_number); // cluster vector will hold the last frame
// number
while (m_num_left && clusters.size() < n_clusters) {
Cluster3x3 c = read_one_cluster();
if (is_selected(c)) {
clusters.push_back(
c.x, c.y, reinterpret_cast<std::byte *>(c.data));
}
}
}
// we have enough clusters, break out of the outer while loop
if (clusters.size() >= n_clusters)
break;
}
}
if (m_gain_map)
clusters.apply_gain_map(m_gain_map->view());
return clusters;
}
Cluster3x3 ClusterFile::read_one_cluster() {
Cluster3x3 c;
auto rc = fread(&c, sizeof(c), 1, fp);
if (rc != 1) {
throw std::runtime_error(LOCATION + "Could not read cluster");
}
--m_num_left;
return c;
}
ClusterVector<int32_t> ClusterFile::read_frame() {
if (m_mode != "r") {
throw std::runtime_error(LOCATION + "File not opened for reading");
}
if (m_noise_map || m_roi) {
return read_frame_with_cut();
} else {
return read_frame_without_cut();
}
}
ClusterVector<int32_t> ClusterFile::read_frame_without_cut() {
if (m_mode != "r") {
throw std::runtime_error("File not opened for reading");
}
if (m_num_left) {
throw std::runtime_error(
"There are still photons left in the last frame");
}
int32_t frame_number;
if (fread(&frame_number, sizeof(frame_number), 1, fp) != 1) {
throw std::runtime_error(LOCATION + "Could not read frame number");
}
int32_t n_clusters; // Saved as 32bit integer in the cluster file
if (fread(&n_clusters, sizeof(n_clusters), 1, fp) != 1) {
throw std::runtime_error(LOCATION +
"Could not read number of clusters");
}
ClusterVector<int32_t> clusters(3, 3, n_clusters);
clusters.set_frame_number(frame_number);
if (fread(clusters.data(), clusters.item_size(), n_clusters, fp) !=
static_cast<size_t>(n_clusters)) {
throw std::runtime_error(LOCATION + "Could not read clusters");
}
clusters.resize(n_clusters);
if (m_gain_map)
clusters.apply_gain_map(m_gain_map->view());
return clusters;
}
ClusterVector<int32_t> ClusterFile::read_frame_with_cut() {
if (m_mode != "r") {
throw std::runtime_error("File not opened for reading");
}
if (m_num_left) {
throw std::runtime_error(
"There are still photons left in the last frame");
}
int32_t frame_number;
if (fread(&frame_number, sizeof(frame_number), 1, fp) != 1) {
throw std::runtime_error("Could not read frame number");
}
if (fread(&m_num_left, sizeof(m_num_left), 1, fp) != 1) {
throw std::runtime_error("Could not read number of clusters");
}
ClusterVector<int32_t> clusters(3, 3);
clusters.reserve(m_num_left);
clusters.set_frame_number(frame_number);
while (m_num_left) {
Cluster3x3 c = read_one_cluster();
if (is_selected(c)) {
clusters.push_back(c.x, c.y, reinterpret_cast<std::byte *>(c.data));
}
}
if (m_gain_map)
clusters.apply_gain_map(m_gain_map->view());
return clusters;
}
bool ClusterFile::is_selected(Cluster3x3 &cl) {
// Should fail fast
if (m_roi) {
if (!(m_roi->contains(cl.x, cl.y))) {
return false;
}
}
if (m_noise_map) {
int32_t sum_1x1 = cl.data[4]; // central pixel
int32_t sum_2x2 = cl.sum_2x2(); // highest sum of 2x2 subclusters
int32_t sum_3x3 = cl.sum(); // sum of all pixels
auto noise =
(*m_noise_map)(cl.y, cl.x); // TODO! check if this is correct
if (sum_1x1 <= noise || sum_2x2 <= 2 * noise || sum_3x3 <= 3 * noise) {
return false;
}
}
// we passed all checks
return true;
}
NDArray<double, 2> calculate_eta2(ClusterVector<int> &clusters) {
// TOTO! make work with 2x2 clusters
NDArray<double, 2> eta2({static_cast<int64_t>(clusters.size()), 2});
if (clusters.cluster_size_x() == 3 || clusters.cluster_size_y() == 3) {
for (size_t i = 0; i < clusters.size(); i++) {
auto e = calculate_eta2(clusters.at<Cluster3x3>(i));
eta2(i, 0) = e.x;
eta2(i, 1) = e.y;
}
} else if (clusters.cluster_size_x() == 2 ||
clusters.cluster_size_y() == 2) {
for (size_t i = 0; i < clusters.size(); i++) {
auto e = calculate_eta2(clusters.at<Cluster2x2>(i));
eta2(i, 0) = e.x;
eta2(i, 1) = e.y;
}
} else {
throw std::runtime_error("Only 3x3 and 2x2 clusters are supported");
}
return eta2;
}
/**
* @brief Calculate the eta2 values for a 3x3 cluster and return them in a Eta2
* struct containing etay, etax and the corner of the cluster.
*/
Eta2 calculate_eta2(Cluster3x3 &cl) {
Eta2 eta{};
std::array<int32_t, 4> tot2;
tot2[0] = cl.data[0] + cl.data[1] + cl.data[3] + cl.data[4];
tot2[1] = cl.data[1] + cl.data[2] + cl.data[4] + cl.data[5];
tot2[2] = cl.data[3] + cl.data[4] + cl.data[6] + cl.data[7];
tot2[3] = cl.data[4] + cl.data[5] + cl.data[7] + cl.data[8];
auto c = std::max_element(tot2.begin(), tot2.end()) - tot2.begin();
eta.sum = tot2[c];
switch (c) {
case cBottomLeft:
if ((cl.data[3] + cl.data[4]) != 0)
eta.x = static_cast<double>(cl.data[4]) / (cl.data[3] + cl.data[4]);
if ((cl.data[1] + cl.data[4]) != 0)
eta.y = static_cast<double>(cl.data[4]) / (cl.data[1] + cl.data[4]);
eta.c = cBottomLeft;
break;
case cBottomRight:
if ((cl.data[2] + cl.data[5]) != 0)
eta.x = static_cast<double>(cl.data[5]) / (cl.data[4] + cl.data[5]);
if ((cl.data[1] + cl.data[4]) != 0)
eta.y = static_cast<double>(cl.data[4]) / (cl.data[1] + cl.data[4]);
eta.c = cBottomRight;
break;
case cTopLeft:
if ((cl.data[7] + cl.data[4]) != 0)
eta.x = static_cast<double>(cl.data[4]) / (cl.data[3] + cl.data[4]);
if ((cl.data[7] + cl.data[4]) != 0)
eta.y = static_cast<double>(cl.data[7]) / (cl.data[7] + cl.data[4]);
eta.c = cTopLeft;
break;
case cTopRight:
if ((cl.data[5] + cl.data[4]) != 0)
eta.x = static_cast<double>(cl.data[5]) / (cl.data[5] + cl.data[4]);
if ((cl.data[7] + cl.data[4]) != 0)
eta.y = static_cast<double>(cl.data[7]) / (cl.data[7] + cl.data[4]);
eta.c = cTopRight;
break;
// no default to allow compiler to warn about missing cases
}
return eta;
}
Eta2 calculate_eta2(Cluster2x2 &cl) {
Eta2 eta{};
if ((cl.data[0] + cl.data[1]) != 0)
eta.x = static_cast<double>(cl.data[1]) / (cl.data[0] + cl.data[1]);
if ((cl.data[0] + cl.data[2]) != 0)
eta.y = static_cast<double>(cl.data[2]) / (cl.data[0] + cl.data[2]);
eta.sum = cl.data[0] + cl.data[1] + cl.data[2] + cl.data[3];
eta.c = cBottomLeft; // TODO! This is not correct, but need to put something
return eta;
}
} // namespace aare

View File

@ -10,7 +10,7 @@ using aare::Cluster;
using aare::ClusterFile;
using aare::ClusterVector;
TEST_CASE("Read one frame from a cluster file", "[.files]") {
TEST_CASE("Read one frame from a cluster file", "[.with-data]") {
// We know that the frame has 97 clusters
auto fpath = test_data_path() / "clust" / "single_frame_97_clustrers.clust";
REQUIRE(std::filesystem::exists(fpath));
@ -26,7 +26,7 @@ TEST_CASE("Read one frame from a cluster file", "[.files]") {
std::begin(expected_cluster_data)));
}
TEST_CASE("Read one frame using ROI", "[.files]") {
TEST_CASE("Read one frame using ROI", "[.with-data]") {
// We know that the frame has 97 clusters
auto fpath = test_data_path() / "clust" / "single_frame_97_clustrers.clust";
REQUIRE(std::filesystem::exists(fpath));
@ -58,7 +58,7 @@ TEST_CASE("Read one frame using ROI", "[.files]") {
std::begin(expected_cluster_data)));
}
TEST_CASE("Read clusters from single frame file", "[.files]") {
TEST_CASE("Read clusters from single frame file", "[.with-data]") {
// frame_number, num_clusters [135] 97
// [ 1 200] [0 1 2 3 4 5 6 7 8]
@ -202,7 +202,7 @@ TEST_CASE("Read clusters from single frame file", "[.files]") {
}
}
TEST_CASE("Read clusters from single frame file with ROI", "[.files]") {
TEST_CASE("Read clusters from single frame file with ROI", "[.with-data]") {
auto fpath = test_data_path() / "clust" / "single_frame_97_clustrers.clust";
REQUIRE(std::filesystem::exists(fpath));
@ -226,7 +226,7 @@ TEST_CASE("Read clusters from single frame file with ROI", "[.files]") {
std::begin(expected_cluster_data)));
}
TEST_CASE("Read cluster from multiple frame file", "[.files]") {
TEST_CASE("Read cluster from multiple frame file", "[.with-data]") {
using ClusterType = Cluster<double, 2, 2>;
@ -279,7 +279,7 @@ TEST_CASE("Read cluster from multiple frame file", "[.files]") {
}
}
TEST_CASE("Write cluster with potential padding", "[.files][.ClusterFile]") {
TEST_CASE("Write cluster with potential padding", "[.with-data][.ClusterFile]") {
using ClusterType = Cluster<double, 3, 3>;
@ -324,7 +324,7 @@ TEST_CASE("Write cluster with potential padding", "[.files][.ClusterFile]") {
}));
}
TEST_CASE("Read frame and modify cluster data", "[.files][.ClusterFile]") {
TEST_CASE("Read frame and modify cluster data", "[.with-data][.ClusterFile]") {
auto fpath = test_data_path() / "clust" / "single_frame_97_clustrers.clust";
REQUIRE(std::filesystem::exists(fpath));

View File

@ -57,9 +57,11 @@ class ClusterFinderMTWrapper
size_t m_sink_size() const { return this->m_sink.sizeGuess(); }
};
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";
TEST_CASE("multithreaded cluster finder", "[.with-data]") {
auto fpath =
test_data_path() / "raw/moench03/cu_half_speed_master_4.json";
REQUIRE(std::filesystem::exists(fpath));
File file(fpath);

View File

@ -7,9 +7,21 @@
using aare::Cluster;
using aare::ClusterVector;
using C1 = Cluster<int32_t, 2, 2>;
TEST_CASE("A newly created ClusterVector is empty") {
ClusterVector<C1> cv(4);
REQUIRE(cv.empty());
}
TEST_CASE("After pushing back one element the ClusterVector is not empty") {
ClusterVector<C1> cv(4);
cv.push_back(C1{1, 2, {3, 4}});
REQUIRE(!cv.empty());
}
TEST_CASE("item_size return the size of the cluster stored") {
using C1 = Cluster<int32_t, 2, 2>;
ClusterVector<C1> cv(4);
CHECK(cv.item_size() == sizeof(C1));
@ -43,8 +55,7 @@ TEST_CASE("item_size return the size of the cluster stored") {
CHECK(cv7.item_size() == sizeof(C7));
}
TEST_CASE("ClusterVector 2x2 int32_t capacity 4, push back then read",
"[.ClusterVector]") {
TEST_CASE("ClusterVector 2x2 int32_t capacity 4, push back then read") {
ClusterVector<Cluster<int32_t, 2, 2>> cv(4);
REQUIRE(cv.capacity() == 4);
@ -70,7 +81,7 @@ TEST_CASE("ClusterVector 2x2 int32_t capacity 4, push back then read",
}
}
TEST_CASE("Summing 3x1 clusters of int64", "[.ClusterVector]") {
TEST_CASE("Summing 3x1 clusters of int64") {
ClusterVector<Cluster<int32_t, 3, 1>> cv(2);
REQUIRE(cv.capacity() == 2);
REQUIRE(cv.size() == 0);
@ -102,7 +113,7 @@ TEST_CASE("Summing 3x1 clusters of int64", "[.ClusterVector]") {
*/
}
TEST_CASE("Storing floats", "[.ClusterVector]") {
TEST_CASE("Storing floats") {
ClusterVector<Cluster<float, 2, 4>> cv(10);
REQUIRE(cv.capacity() == 10);
REQUIRE(cv.size() == 0);
@ -129,7 +140,7 @@ TEST_CASE("Storing floats", "[.ClusterVector]") {
*/
}
TEST_CASE("Push back more than initial capacity", "[.ClusterVector]") {
TEST_CASE("Push back more than initial capacity") {
ClusterVector<Cluster<int32_t, 2, 2>> cv(2);
auto initial_data = cv.data();
@ -162,8 +173,7 @@ TEST_CASE("Push back more than initial capacity", "[.ClusterVector]") {
REQUIRE(initial_data != cv.data());
}
TEST_CASE("Concatenate two cluster vectors where the first has enough capacity",
"[.ClusterVector]") {
TEST_CASE("Concatenate two cluster vectors where the first has enough capacity") {
ClusterVector<Cluster<int32_t, 2, 2>> cv1(12);
Cluster<int32_t, 2, 2> c1 = {1, 2, {3, 4, 5, 6}};
cv1.push_back(c1);
@ -192,8 +202,7 @@ TEST_CASE("Concatenate two cluster vectors where the first has enough capacity",
REQUIRE(ptr[3].y == 17);
}
TEST_CASE("Concatenate two cluster vectors where we need to allocate",
"[.ClusterVector]") {
TEST_CASE("Concatenate two cluster vectors where we need to allocate") {
ClusterVector<Cluster<int32_t, 2, 2>> cv1(2);
Cluster<int32_t, 2, 2> c1 = {1, 2, {3, 4, 5, 6}};
cv1.push_back(c1);
@ -229,7 +238,7 @@ struct ClusterTestData {
std::vector<int64_t> index_map_y;
};
TEST_CASE("Gain Map Calculation Index Map", "[.ClusterVector][.gain_map]") {
TEST_CASE("Gain Map Calculation Index Map") {
auto clustertestdata = GENERATE(
ClusterTestData{3,

151
src/DetectorGeometry.cpp Normal file
View File

@ -0,0 +1,151 @@
#include "aare/DetectorGeometry.hpp"
#include "fmt/core.h"
#include <iostream>
#include <vector>
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<size_t>((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<ssize_t> &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<ModuleGeometry> &
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

View File

@ -0,0 +1,194 @@
#include "aare/File.hpp"
#include "aare/RawFile.hpp"
#include "aare/RawMasterFile.hpp" //needed for ROI
#include <catch2/catch_test_macros.hpp>
#include <filesystem>
#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);
}

View File

@ -1,7 +1,4 @@
#include "aare/File.hpp"
#ifdef HDF5_FOUND
#include "aare/Hdf5File.hpp"
#endif
#include "aare/JungfrauDataFile.hpp"
#include "aare/NumpyFile.hpp"
#include "aare/RawFile.hpp"
@ -30,17 +27,7 @@ File::File(const std::filesystem::path &fname, const std::string &mode,
} else if (fname.extension() == ".npy") {
// file_impl = new NumpyFile(fname, mode, cfg);
file_impl = std::make_unique<NumpyFile>(fname, mode, cfg);
}
#ifdef HDF5_FOUND
else if (fname.extension() == ".h5") {
file_impl = std::make_unique<Hdf5File>(fname, mode);
}
#else
else if (fname.extension() == ".h5") {
throw std::runtime_error("Enable HDF5 compile option: AARE_HDF5=ON");
}
#endif
else if (fname.extension() == ".dat") {
} else if (fname.extension() == ".dat") {
file_impl = std::make_unique<JungfrauDataFile>(fname);
} else {
throw std::runtime_error("Unsupported file type");

View File

@ -1,234 +0,0 @@
#include "aare/Hdf5File.hpp"
#include "aare/PixelMap.hpp"
#include "aare/defs.hpp"
#include "aare/logger.hpp"
#include <fmt/format.h>
namespace aare {
Hdf5File::Hdf5File(const std::filesystem::path &fname, const std::string &mode)
: m_master(fname) {
m_mode = mode;
if (mode == "r") {
open_data_file();
open_header_files();
} else {
throw std::runtime_error(LOCATION +
"Unsupported mode. Can only read Hdf5Files.");
}
}
Frame Hdf5File::read_frame() { return get_frame(m_current_frame++); }
Frame Hdf5File::read_frame(size_t frame_number) {
seek(frame_number);
return read_frame();
}
std::vector<Frame> Hdf5File::read_n(size_t n_frames) {
// TODO: implement this in a more efficient way
std::vector<Frame> frames;
for (size_t i = 0; i < n_frames; i++) {
frames.push_back(this->get_frame(m_current_frame));
m_current_frame++;
}
return frames;
}
void Hdf5File::read_into(std::byte *image_buf, size_t n_frames) {
get_frame_into(m_current_frame++, image_buf, n_frames);
}
void Hdf5File::read_into(std::byte *image_buf) {
get_frame_into(m_current_frame++, image_buf);
}
void Hdf5File::read_into(std::byte *image_buf, DetectorHeader *header) {
get_frame_into(m_current_frame, image_buf, 1, header);
}
void Hdf5File::read_into(std::byte *image_buf, size_t n_frames,
DetectorHeader *header) {
get_frame_into(m_current_frame++, image_buf, n_frames, header);
}
size_t Hdf5File::frame_number(size_t frame_index) {
// TODO: check if it should check total_Frames() at any point
// check why this->read_into.. as in RawFile
// refactor multiple frame reads into a single one using hyperslab
if (frame_index >= m_master.frames_in_file()) {
throw std::runtime_error(LOCATION + " Frame number out of range");
}
uint64_t fnum{0};
int part_index = 0; // assuming first part
m_header_datasets[0]->get_header_into(frame_index, part_index,
reinterpret_cast<std::byte *>(&fnum));
return fnum;
}
size_t Hdf5File::bytes_per_frame() {
return m_rows * m_cols * m_master.bitdepth() / 8;
}
size_t Hdf5File::pixels_per_frame() { return m_rows * m_cols; }
size_t Hdf5File::bytes_per_pixel() const { return m_master.bitdepth() / 8; }
void Hdf5File::seek(size_t frame_index) {
m_data_dataset->seek(frame_index);
for (size_t i = 0; i != header_dataset_names.size(); ++i) {
m_header_datasets[i]->seek(frame_index);
}
m_current_frame = frame_index;
}
size_t Hdf5File::tell() { return m_current_frame; }
size_t Hdf5File::total_frames() const { return m_total_frames; }
size_t Hdf5File::rows() const { return m_rows; }
size_t Hdf5File::cols() const { return m_cols; }
size_t Hdf5File::bitdepth() const { return m_master.bitdepth(); }
xy Hdf5File::geometry() { return m_master.geometry(); }
size_t Hdf5File::n_modules() const { return m_master.n_modules(); }
Hdf5MasterFile Hdf5File::master() const { return m_master; }
DetectorType Hdf5File::detector_type() const {
return m_master.detector_type();
}
Frame Hdf5File::get_frame(size_t frame_index) {
auto f = Frame(m_rows, m_cols, Dtype::from_bitdepth(m_master.bitdepth()));
std::byte *frame_buffer = f.data();
get_frame_into(frame_index, frame_buffer);
return f;
}
void Hdf5File::get_frame_into(size_t frame_index, std::byte *frame_buffer,
size_t n_frames, DetectorHeader *header) {
if ((frame_index + n_frames - 1) >= m_master.frames_in_file()) {
throw std::runtime_error(LOCATION + "Frame number out of range");
}
get_data_into(frame_index, frame_buffer);
m_current_frame += n_frames;
if (header) {
for (size_t i = 0; i < n_frames; i++) {
for (size_t part_idx = 0; part_idx != m_master.n_modules();
++part_idx) {
get_header_into(frame_index + i, part_idx, header);
header++;
}
}
}
}
void Hdf5File::get_data_into(size_t frame_index, std::byte *frame_buffer,
size_t n_frames) {
m_data_dataset->get_data_into(frame_index, frame_buffer, n_frames);
}
void Hdf5File::get_header_into(size_t frame_index, int part_index,
DetectorHeader *header) {
try {
read_hdf5_header_fields(header, [&](size_t iParameter,
std::byte *dest) {
m_header_datasets[iParameter]->get_header_into(frame_index,
part_index, dest);
});
LOG(logDEBUG5) << "Read 1D header for frame " << frame_index;
} catch (const H5::Exception &e) {
fmt::print("Exception type: {}\n", typeid(e).name());
e.printErrorStack();
throw std::runtime_error(
LOCATION + "\nCould not to access header datasets in given file.");
}
}
DetectorHeader Hdf5File::read_header(const std::filesystem::path &fname) {
DetectorHeader h{};
std::vector<std::unique_ptr<H5Handles>> handles;
try {
for (size_t i = 0; i != header_dataset_names.size(); ++i) {
handles.push_back(std::make_unique<H5Handles>(
fname.string(), metadata_group_name + header_dataset_names[i]));
}
read_hdf5_header_fields(&h, [&](size_t iParameter, std::byte *dest) {
handles[iParameter]->get_header_into(0, 0, dest);
});
LOG(logDEBUG5) << "Read 1D header for frame 0";
} catch (const H5::Exception &e) {
handles.clear();
fmt::print("Exception type: {}\n", typeid(e).name());
e.printErrorStack();
throw std::runtime_error(
LOCATION + "\nCould not to access header datasets in given file.");
}
return h;
}
Hdf5File::~Hdf5File() {}
const std::string Hdf5File::metadata_group_name = "/entry/data/";
const std::vector<std::string> Hdf5File::header_dataset_names = {
"frame number",
"exp length or sub exposure time",
"packets caught",
"detector specific 1",
"timestamp",
"mod id",
"row",
"column",
"detector specific 2",
"detector specific 3",
"detector specific 4",
"detector type",
"detector header version",
"packets caught bit mask"};
void Hdf5File::open_data_file() {
if (m_mode != "r")
throw std::runtime_error(LOCATION +
"Unsupported mode. Can only read Hdf5 files.");
try {
m_data_dataset = std::make_unique<H5Handles>(
m_master.file_name().string(), metadata_group_name + "/data");
m_total_frames = m_data_dataset->get_dims()[0];
m_rows = m_data_dataset->get_dims()[1];
m_cols = m_data_dataset->get_dims()[2];
// fmt::print("Data Dataset dimensions: frames = {}, rows = {}, cols =
// {}\n",
// m_total_frames, m_rows, m_cols);
} catch (const H5::Exception &e) {
m_data_dataset.reset();
fmt::print("Exception type: {}\n", typeid(e).name());
e.printErrorStack();
throw std::runtime_error(
LOCATION + "\nCould not to access 'data' dataset in master file.");
}
}
void Hdf5File::open_header_files() {
if (m_mode != "r")
throw std::runtime_error(LOCATION +
"Unsupported mode. Can only read Hdf5 files.");
try {
for (size_t i = 0; i != header_dataset_names.size(); ++i) {
m_header_datasets.push_back(std::make_unique<H5Handles>(
m_master.file_name().string(),
metadata_group_name + header_dataset_names[i]));
LOG(logDEBUG) << header_dataset_names[i]
<< " Dataset dimensions: size = "
<< m_header_datasets[i]->get_dims()[0];
}
} catch (const H5::Exception &e) {
m_header_datasets.clear();
m_data_dataset.reset();
fmt::print("Exception type: {}\n", typeid(e).name());
e.printErrorStack();
throw std::runtime_error(
LOCATION + "\nCould not to access header datasets in master file.");
}
}
} // namespace aare

View File

View File

@ -1,565 +0,0 @@
#include "aare/Hdf5MasterFile.hpp"
#include "aare/logger.hpp"
#include "aare/to_string.hpp"
#include <iomanip>
#include <sstream>
namespace aare {
Hdf5MasterFile::Hdf5MasterFile(const std::filesystem::path &fpath)
: m_file_name(fpath) {
if (!std::filesystem::exists(fpath)) {
throw std::runtime_error(LOCATION + " File does not exist");
}
parse_acquisition_metadata(fpath);
}
std::filesystem::path Hdf5MasterFile::file_name() const {
return m_file_name;
}
const std::string &Hdf5MasterFile::version() const { return m_version; }
const DetectorType &Hdf5MasterFile::detector_type() const { return m_type; }
const TimingMode &Hdf5MasterFile::timing_mode() const { return m_timing_mode; }
xy Hdf5MasterFile::geometry() const { return m_geometry; }
int Hdf5MasterFile::image_size_in_bytes() const {
return m_image_size_in_bytes;
}
int Hdf5MasterFile::pixels_y() const { return m_pixels_y; }
int Hdf5MasterFile::pixels_x() const { return m_pixels_x; }
int Hdf5MasterFile::max_frames_per_file() const {
return m_max_frames_per_file;
}
const FrameDiscardPolicy &Hdf5MasterFile::frame_discard_policy() const {
return m_frame_discard_policy;
}
int Hdf5MasterFile::frame_padding() const { return m_frame_padding; }
std::optional<ScanParameters> Hdf5MasterFile::scan_parameters() const {
return m_scan_parameters;
}
size_t Hdf5MasterFile::total_frames_expected() const {
return m_total_frames_expected;
}
std::optional<ns> Hdf5MasterFile::exptime() const { return m_exptime; }
std::optional<ns> Hdf5MasterFile::period() const { return m_period; }
std::optional<BurstMode> Hdf5MasterFile::burst_mode() const {
return m_burst_mode;
}
std::optional<int> Hdf5MasterFile::number_of_udp_interfaces() const {
return m_number_of_udp_interfaces;
}
int Hdf5MasterFile::bitdepth() const { return m_bitdepth; }
std::optional<bool> Hdf5MasterFile::ten_giga() const { return m_ten_giga; }
std::optional<int> Hdf5MasterFile::threshold_energy() const {
return m_threshold_energy;
}
std::optional<std::vector<int>> Hdf5MasterFile::threshold_energy_all() const {
return m_threshold_energy_all;
}
std::optional<ns> Hdf5MasterFile::subexptime() const { return m_subexptime; }
std::optional<ns> Hdf5MasterFile::subperiod() const { return m_subperiod; }
std::optional<bool> Hdf5MasterFile::quad() const { return m_quad; }
std::optional<int> Hdf5MasterFile::number_of_rows() const {
return m_number_of_rows;
}
std::optional<std::vector<size_t>> Hdf5MasterFile::rate_corrections() const {
return m_rate_corrections;
}
std::optional<uint32_t> Hdf5MasterFile::adc_mask() const { return m_adc_mask; }
bool Hdf5MasterFile::analog_flag() const { return m_analog_flag; }
std::optional<int> Hdf5MasterFile::analog_samples() const {
return m_analog_samples;
}
bool Hdf5MasterFile::digital_flag() const { return m_digital_flag; }
std::optional<int> Hdf5MasterFile::digital_samples() const {
return m_digital_samples;
}
std::optional<int> Hdf5MasterFile::dbit_offset() const { return m_dbit_offset; }
std::optional<size_t> Hdf5MasterFile::dbit_list() const { return m_dbit_list; }
std::optional<int> Hdf5MasterFile::transceiver_mask() const {
return m_transceiver_mask;
}
bool Hdf5MasterFile::transceiver_flag() const { return m_transceiver_flag; }
std::optional<int> Hdf5MasterFile::transceiver_samples() const {
return m_transceiver_samples;
}
// g1 roi
std::optional<ROI> Hdf5MasterFile::roi() const { return m_roi; }
std::optional<int> Hdf5MasterFile::counter_mask() const {
return m_counter_mask;
}
std::optional<std::vector<ns>> Hdf5MasterFile::exptime_array() const {
return m_exptime_array;
}
std::optional<std::vector<ns>> Hdf5MasterFile::gate_delay_array() const {
return m_gate_delay_array;
}
std::optional<int> Hdf5MasterFile::gates() const { return m_gates; }
std::optional<std::map<std::string, std::string>>
Hdf5MasterFile::additional_json_header() const {
return m_additional_json_header;
}
size_t Hdf5MasterFile::frames_in_file() const { return m_frames_in_file; }
size_t Hdf5MasterFile::n_modules() const {
return m_geometry.row * m_geometry.col;
}
// optional values, these may or may not be present in the master file
// and are therefore modeled as std::optional
const std::string Hdf5MasterFile::metadata_group_name =
"/entry/instrument/detector/";
template <typename T>
T Hdf5MasterFile::h5_read_scalar_dataset(const H5::DataSet &dataset,
const H5::DataType &data_type) {
T value;
dataset.read(&value, data_type);
return value;
}
template <>
std::string Hdf5MasterFile::h5_read_scalar_dataset<std::string>(
const H5::DataSet &dataset, const H5::DataType &data_type) {
size_t size = data_type.getSize();
std::vector<char> buffer(size + 1, 0);
dataset.read(buffer.data(), data_type);
return std::string(buffer.data());
}
template <typename T>
T Hdf5MasterFile::h5_get_scalar_dataset(const H5::H5File &file,
const std::string &dataset_name) {
H5::DataSet dataset = file.openDataSet(dataset_name);
H5::DataSpace dataspace = dataset.getSpace();
if (dataspace.getSimpleExtentNdims() != 0) {
throw std::runtime_error(LOCATION + "Expected " + dataset_name +
" to be a scalar dataset");
}
H5::DataType data_type = dataset.getDataType();
return h5_read_scalar_dataset<T>(dataset, data_type);
}
void Hdf5MasterFile::parse_acquisition_metadata(
const std::filesystem::path &fpath) {
try {
H5::H5File file(fpath, H5F_ACC_RDONLY);
// Attribute - version
double dVersion{0.0};
{
H5::Attribute attr = file.openAttribute("version");
H5::DataType attr_type = attr.getDataType();
attr.read(attr_type, &dVersion);
std::ostringstream oss;
oss << std::fixed << std::setprecision(1) << dVersion;
m_version = oss.str();
LOG(logDEBUG) << "Version: " << m_version;
}
// Scalar Dataset
H5::Exception::dontPrint();
// Detector Type
m_type = StringTo<DetectorType>(h5_get_scalar_dataset<std::string>(
file, std::string(metadata_group_name + "Detector Type")));
LOG(logDEBUG) << "Detector Type: " << ToString(m_type);
// Timing Mode
m_timing_mode = StringTo<TimingMode>(h5_get_scalar_dataset<std::string>(
file, std::string(metadata_group_name + "Timing Mode")));
LOG(logDEBUG) << "Timing Mode: " << ToString(m_timing_mode);
// Geometry
m_geometry.row = h5_get_scalar_dataset<int>(
file, std::string(metadata_group_name + "Geometry in y axis"));
m_geometry.col = h5_get_scalar_dataset<int>(
file, std::string(metadata_group_name + "Geometry in x axis"));
LOG(logDEBUG) << "Geometry: " << m_geometry.to_string();
// Image Size
m_image_size_in_bytes = h5_get_scalar_dataset<int>(
file, std::string(metadata_group_name + "Image Size"));
LOG(logDEBUG) << "Image size: " << m_image_size_in_bytes;
// Pixels y
m_pixels_y = h5_get_scalar_dataset<int>(
file,
std::string(metadata_group_name + "Number of pixels in y axis"));
LOG(logDEBUG) << "Pixels in y: " << m_pixels_y;
// Pixels x
m_pixels_x = h5_get_scalar_dataset<int>(
file,
std::string(metadata_group_name + "Number of pixels in x axis"));
LOG(logDEBUG) << "Pixels in x: " << m_pixels_x;
// Max Frames Per File
m_max_frames_per_file = h5_get_scalar_dataset<int>(
file, std::string(metadata_group_name + "Maximum frames per file"));
LOG(logDEBUG) << "Max frames per File: " << m_max_frames_per_file;
// Frame Discard Policy
m_frame_discard_policy =
StringTo<FrameDiscardPolicy>(h5_get_scalar_dataset<std::string>(
file,
std::string(metadata_group_name + "Frame Discard Policy")));
LOG(logDEBUG) << "Frame Discard Policy: "
<< ToString(m_frame_discard_policy);
// Frame Padding
m_frame_padding = h5_get_scalar_dataset<int>(
file, std::string(metadata_group_name + "Frame Padding"));
LOG(logDEBUG) << "Frame Padding: " << m_frame_padding;
// Scan Parameters
try {
std::string scan_parameters = h5_get_scalar_dataset<std::string>(
file, std::string(metadata_group_name + "Scan Parameters"));
m_scan_parameters = ScanParameters(scan_parameters);
if (dVersion < 6.61) {
m_scan_parameters
->increment_stop(); // adjust for endpoint being included
}
LOG(logDEBUG) << "Scan Parameters: " << ToString(m_scan_parameters);
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Total Frames Expected
m_total_frames_expected = h5_get_scalar_dataset<size_t>(
file, std::string(metadata_group_name + "Total Frames"));
LOG(logDEBUG) << "Total Frames: " << m_total_frames_expected;
// Exptime
try {
m_exptime = StringTo<ns>(h5_get_scalar_dataset<std::string>(
file, std::string(metadata_group_name + "Exposure Time")));
LOG(logDEBUG) << "Exptime: " << ToString(m_exptime);
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Period
try {
m_period = StringTo<ns>(h5_get_scalar_dataset<std::string>(
file, std::string(metadata_group_name + "Acquisition Period")));
LOG(logDEBUG) << "Period: " << ToString(m_period);
} catch (H5::FileIException &e) {
// keep the optional empty
}
// burst mode
try {
m_burst_mode =
StringTo<BurstMode>(h5_get_scalar_dataset<std::string>(
file, std::string(metadata_group_name + "Burst Mode")));
LOG(logDEBUG) << "Burst Mode: " << ToString(m_burst_mode);
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Number of UDP Interfaces
// Not all detectors write the Number of UDP Interfaces but in case
try {
m_number_of_udp_interfaces = h5_get_scalar_dataset<int>(
file,
std::string(metadata_group_name + "Number of UDP Interfaces"));
LOG(logDEBUG) << "Number of UDP Interfaces: "
<< m_number_of_udp_interfaces;
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Bit Depth
// Not all detectors write the bitdepth but in case
// its not there it is 16
try {
m_bitdepth = h5_get_scalar_dataset<int>(
file, std::string(metadata_group_name + "Dynamic Range"));
LOG(logDEBUG) << "Bit Depth: " << m_bitdepth;
} catch (H5::FileIException &e) {
m_bitdepth = 16;
}
// Ten Giga
try {
m_ten_giga = h5_get_scalar_dataset<bool>(
file, std::string(metadata_group_name + "Ten Giga Enable"));
LOG(logDEBUG) << "Ten Giga Enable: " << m_ten_giga;
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Threshold Energy
try {
m_threshold_energy = h5_get_scalar_dataset<int>(
file, std::string(metadata_group_name + "Threshold Energy"));
LOG(logDEBUG) << "Threshold Energy: " << m_threshold_energy;
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Threshold All Energy
try {
m_threshold_energy_all =
StringTo<std::vector<int>>(h5_get_scalar_dataset<std::string>(
file,
std::string(metadata_group_name + "Threshold Energies")));
LOG(logDEBUG) << "Threshold Energies: "
<< ToString(m_threshold_energy_all);
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Subexptime
try {
m_subexptime = StringTo<ns>(h5_get_scalar_dataset<std::string>(
file, std::string(metadata_group_name + "Sub Exposure Time")));
LOG(logDEBUG) << "Subexptime: " << ToString(m_subexptime);
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Subperiod
try {
m_subperiod = StringTo<ns>(h5_get_scalar_dataset<std::string>(
file, std::string(metadata_group_name + "Sub Period")));
LOG(logDEBUG) << "Subperiod: " << ToString(m_subperiod);
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Quad
try {
m_quad = h5_get_scalar_dataset<bool>(
file, std::string(metadata_group_name + "Quad"));
LOG(logDEBUG) << "Quad: " << m_quad;
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Number of Rows
// Not all detectors write the Number of rows but in case
try {
m_number_of_rows = h5_get_scalar_dataset<int>(
file, std::string(metadata_group_name + "Number of rows"));
LOG(logDEBUG) << "Number of rows: " << m_number_of_rows;
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Rate Corrections
try {
m_rate_corrections = StringTo<std::vector<size_t>>(
h5_get_scalar_dataset<std::string>(
file,
std::string(metadata_group_name + "Rate Corrections")));
LOG(logDEBUG) << "Rate Corrections: "
<< ToString(m_rate_corrections);
} catch (H5::FileIException &e) {
// keep the optional empty
}
// ADC Mask
try {
m_adc_mask = h5_get_scalar_dataset<uint32_t>(
file, std::string(metadata_group_name + "ADC Mask"));
LOG(logDEBUG) << "ADC Mask: " << m_adc_mask;
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Analog Flag
// ----------------------------------------------------------------
// Special treatment of analog flag because of Moench03
try {
m_analog_flag = h5_get_scalar_dataset<uint8_t>(
file, std::string(metadata_group_name + "Analog Flag"));
LOG(logDEBUG) << "Analog Flag: " << m_analog_flag;
} catch (H5::FileIException &e) {
// if it doesn't work still set it to one
// to try to decode analog samples (Old Moench03)
m_analog_flag = 1;
}
// Analog Samples
try {
if (m_analog_flag) {
m_analog_samples = h5_get_scalar_dataset<int>(
file, std::string(metadata_group_name + "Analog Samples"));
LOG(logDEBUG) << "Analog Samples: " << m_analog_samples;
}
} catch (H5::FileIException &e) {
// keep the optional empty
// and set analog flag to 0
m_analog_flag = false;
}
//-----------------------------------------------------------------
// Digital Flag, Digital Samples
try {
m_digital_flag = h5_get_scalar_dataset<bool>(
file, std::string(metadata_group_name + "Digital Flag"));
LOG(logDEBUG) << "Digital Flag: " << m_digital_flag;
if (m_digital_flag) {
m_digital_samples = h5_get_scalar_dataset<int>(
file, std::string(metadata_group_name + "Digital Samples"));
}
LOG(logDEBUG) << "Digital Samples: " << m_digital_samples;
} catch (H5::FileIException &e) {
m_digital_flag = false;
}
// Dbit Offset
try {
m_dbit_offset = h5_get_scalar_dataset<int>(
file, std::string(metadata_group_name + "Dbit Offset"));
LOG(logDEBUG) << "Dbit Offset: " << m_dbit_offset;
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Dbit List
try {
m_dbit_list = h5_get_scalar_dataset<size_t>(
file, std::string(metadata_group_name + "Dbit Bitset List"));
LOG(logDEBUG) << "Dbit list: " << m_dbit_list;
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Transceiver Mask
try {
m_transceiver_mask = h5_get_scalar_dataset<int>(
file, std::string(metadata_group_name + "Transceiver Mask"));
LOG(logDEBUG) << "Transceiver Mask: " << m_transceiver_mask;
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Transceiver Flag, Transceiver Samples
try {
m_transceiver_flag = h5_get_scalar_dataset<bool>(
file, std::string(metadata_group_name + "Transceiver Flag"));
LOG(logDEBUG) << "Transceiver Flag: " << m_transceiver_flag;
if (m_transceiver_flag) {
m_transceiver_samples = h5_get_scalar_dataset<int>(
file,
std::string(metadata_group_name + "Transceiver Samples"));
LOG(logDEBUG)
<< "Transceiver Samples: " << m_transceiver_samples;
}
} catch (H5::FileIException &e) {
m_transceiver_flag = false;
}
// Rx ROI
try {
ROI tmp_roi;
tmp_roi.xmin = h5_get_scalar_dataset<int>(
file, std::string(metadata_group_name + "receiver roi xmin"));
tmp_roi.xmax = h5_get_scalar_dataset<int>(
file, std::string(metadata_group_name + "receiver roi xmax"));
tmp_roi.ymin = h5_get_scalar_dataset<int>(
file, std::string(metadata_group_name + "receiver roi ymin"));
tmp_roi.ymax = h5_get_scalar_dataset<int>(
file, std::string(metadata_group_name + "receiver roi ymax"));
// if any of the values are set update the roi
if (tmp_roi.xmin != -1 || tmp_roi.xmax != -1 ||
tmp_roi.ymin != -1 || tmp_roi.ymax != -1) {
// why?? TODO
//if (dVersion < 6.6) {
tmp_roi.xmax++;
tmp_roi.ymax++;
//}
m_roi = tmp_roi;
}
LOG(logDEBUG) << "ROI: " << m_roi;
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Update detector type for Moench
// TODO! How does this work with old .h5 master files?
#ifdef AARE_VERBOSE
fmt::print("Detecting Moench03: m_pixels_y: {}, "
"m_analog_samples: {}\n",
m_pixels_y, m_analog_samples.value_or(0));
#endif
if (m_type == DetectorType::Moench && !m_analog_samples &&
m_pixels_y == 400) {
m_type = DetectorType::Moench03;
} else if (m_type == DetectorType::Moench && m_pixels_y == 400 &&
m_analog_samples == 5000) {
m_type = DetectorType::Moench03_old;
}
// Counter Mask
try {
m_counter_mask = h5_get_scalar_dataset<int>(
file, std::string(metadata_group_name + "Counter Mask"));
LOG(logDEBUG) << "Counter Mask: " << m_counter_mask;
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Exposure Time Array
try {
m_exptime_array =
StringTo<std::vector<ns>>(h5_get_scalar_dataset<std::string>(
file, std::string(metadata_group_name + "Exposure Times")));
LOG(logDEBUG) << "Exposure Times: " << ToString(m_exptime_array);
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Gate Delay Array
try {
m_gate_delay_array =
StringTo<std::vector<ns>>(h5_get_scalar_dataset<std::string>(
file, std::string(metadata_group_name + "Gate Delays")));
LOG(logDEBUG) << "Gate Delays: " << ToString(m_gate_delay_array);
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Gates
try {
m_gates = h5_get_scalar_dataset<int>(
file, std::string(metadata_group_name + "Gates"));
LOG(logDEBUG) << "Gates: " << m_gates;
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Additional Json Header
try {
m_additional_json_header =
StringTo<std::map<std::string, std::string>>(
h5_get_scalar_dataset<std::string>(
file, std::string(metadata_group_name +
"Additional JSON Header")));
LOG(logDEBUG) << "Additional JSON Header: "
<< ToString(m_additional_json_header);
} catch (H5::FileIException &e) {
// keep the optional empty
}
// Frames in File
m_frames_in_file = h5_get_scalar_dataset<size_t>(
file, std::string(metadata_group_name + "Frames in File"));
LOG(logDEBUG) << "Frames in File: " << m_frames_in_file;
H5Eset_auto(H5E_DEFAULT, reinterpret_cast<H5E_auto2_t>(H5Eprint2),
stderr);
} catch (const H5::Exception &e) {
fmt::print("Exception type: {}\n", typeid(e).name());
e.printErrorStack();
throw std::runtime_error(LOCATION + "\nCould not parse master file");
}
}
} // namespace aare

View File

@ -1,168 +0,0 @@
#include "aare/Hdf5MasterFile.hpp"
#include "aare/to_string.hpp"
#include "test_config.hpp"
#include <catch2/catch_test_macros.hpp>
using namespace aare;
TEST_CASE("Parse a multi module jungfrau master file in .h5 format", "[.integration][.hdf5]") {
auto fpath = test_data_path() / "hdf5" / "virtual" / "jungfrau" /
"two_modules_master_0.h5";
REQUIRE(std::filesystem::exists(fpath));
Hdf5MasterFile f(fpath);
REQUIRE(f.version() == "6.6");
// "Timestamp": "Tue Feb 20 08:28:24 2024",
REQUIRE(f.detector_type() == DetectorType::Jungfrau);
REQUIRE(f.timing_mode() == TimingMode::Auto);
REQUIRE(f.geometry().col == 1);
REQUIRE(f.geometry().row == 2);
REQUIRE(f.image_size_in_bytes() == 1048576);
REQUIRE(f.pixels_x() == 1024);
REQUIRE(f.pixels_y() == 512);
REQUIRE(f.max_frames_per_file() == 10000);
REQUIRE(f.frame_discard_policy() == FrameDiscardPolicy::NoDiscard);
REQUIRE(f.frame_padding() == 1);
REQUIRE(f.scan_parameters()->enabled() == false);
REQUIRE(f.total_frames_expected() == 5);
REQUIRE(f.exptime() == std::chrono::microseconds(10));
REQUIRE(f.period() == std::chrono::milliseconds(2));
REQUIRE_FALSE(f.burst_mode().has_value());
REQUIRE(f.number_of_udp_interfaces() == 1);
// Jungfrau doesn't write but it is 16
REQUIRE(f.bitdepth() == 16);
REQUIRE_FALSE(f.ten_giga().has_value());
REQUIRE_FALSE(f.threshold_energy().has_value());
REQUIRE_FALSE(f.threshold_energy_all().has_value());
REQUIRE_FALSE(f.subexptime().has_value());
REQUIRE_FALSE(f.subperiod().has_value());
REQUIRE_FALSE(f.quad().has_value());
REQUIRE(f.number_of_rows() == 512);
REQUIRE_FALSE(f.rate_corrections().has_value());
REQUIRE_FALSE(f.adc_mask().has_value());
REQUIRE_FALSE(f.analog_flag());
REQUIRE_FALSE(f.analog_samples().has_value());
REQUIRE_FALSE(f.digital_flag());
REQUIRE_FALSE(f.digital_samples().has_value());
REQUIRE_FALSE(f.dbit_offset().has_value());
REQUIRE_FALSE(f.dbit_list().has_value());
REQUIRE_FALSE(f.transceiver_mask().has_value());
REQUIRE_FALSE(f.transceiver_flag());
REQUIRE_FALSE(f.transceiver_samples().has_value());
REQUIRE_FALSE(f.roi().has_value());
REQUIRE_FALSE(f.counter_mask().has_value());
REQUIRE_FALSE(f.exptime_array().has_value());
REQUIRE_FALSE(f.gate_delay_array().has_value());
REQUIRE_FALSE(f.gates().has_value());
REQUIRE_FALSE(f.additional_json_header().has_value());
REQUIRE(f.frames_in_file() == 5);
REQUIRE(f.n_modules() == 2);
}
TEST_CASE("Parse a single module jungfrau master file in .h5 format", "[.integration][.hdf5]") {
auto fpath = test_data_path() / "hdf5" / "virtual" / "jungfrau" /
"single_module_master_2.h5";
REQUIRE(std::filesystem::exists(fpath));
Hdf5MasterFile f(fpath);
REQUIRE(f.version() == "6.6");
// "Timestamp": "Tue Feb 20 08:28:24 2024",
REQUIRE(f.detector_type() == DetectorType::Jungfrau);
REQUIRE(f.timing_mode() == TimingMode::Auto);
REQUIRE(f.geometry().col == 1);
REQUIRE(f.geometry().row == 1);
REQUIRE(f.image_size_in_bytes() == 1048576);
REQUIRE(f.pixels_x() == 1024);
REQUIRE(f.pixels_y() == 512);
REQUIRE(f.max_frames_per_file() == 10000);
REQUIRE(f.frame_discard_policy() == FrameDiscardPolicy::NoDiscard);
REQUIRE(f.frame_padding() == 1);
REQUIRE(f.scan_parameters()->enabled() == false);
REQUIRE(f.total_frames_expected() == 5);
REQUIRE(f.exptime() == std::chrono::microseconds(10));
REQUIRE(f.period() == std::chrono::milliseconds(2));
REQUIRE_FALSE(f.burst_mode().has_value());
REQUIRE(f.number_of_udp_interfaces() == 1);
// Jungfrau doesn't write but it is 16
REQUIRE(f.bitdepth() == 16);
REQUIRE_FALSE(f.ten_giga().has_value());
REQUIRE_FALSE(f.threshold_energy().has_value());
REQUIRE_FALSE(f.threshold_energy_all().has_value());
REQUIRE_FALSE(f.subexptime().has_value());
REQUIRE_FALSE(f.subperiod().has_value());
REQUIRE_FALSE(f.quad().has_value());
REQUIRE(f.number_of_rows() == 512);
REQUIRE_FALSE(f.rate_corrections().has_value());
REQUIRE_FALSE(f.adc_mask().has_value());
REQUIRE_FALSE(f.analog_flag());
REQUIRE_FALSE(f.analog_samples().has_value());
REQUIRE_FALSE(f.digital_flag());
REQUIRE_FALSE(f.digital_samples().has_value());
REQUIRE_FALSE(f.dbit_offset().has_value());
REQUIRE_FALSE(f.dbit_list().has_value());
REQUIRE_FALSE(f.transceiver_mask().has_value());
REQUIRE_FALSE(f.transceiver_flag());
REQUIRE_FALSE(f.transceiver_samples().has_value());
REQUIRE_FALSE(f.roi().has_value());
REQUIRE_FALSE(f.counter_mask().has_value());
REQUIRE_FALSE(f.exptime_array().has_value());
REQUIRE_FALSE(f.gate_delay_array().has_value());
REQUIRE_FALSE(f.gates().has_value());
REQUIRE_FALSE(f.additional_json_header().has_value());
REQUIRE(f.frames_in_file() == 5);
REQUIRE(f.n_modules() == 1);
}
TEST_CASE("Parse a mythen3 master file in .h5 format", "[.integration][.hdf5]") {
auto fpath = test_data_path() / "hdf5" / "virtual" / "mythen3" /
"one_module_master_0.h5";
REQUIRE(std::filesystem::exists(fpath));
Hdf5MasterFile f(fpath);
REQUIRE(f.version() == "6.7");
// "Timestamp": "Tue Feb 20 08:28:24 2024",
REQUIRE(f.detector_type() == DetectorType::Mythen3);
REQUIRE(f.timing_mode() == TimingMode::Auto);
REQUIRE(f.geometry().col == 1);
REQUIRE(f.geometry().row == 1);
REQUIRE(f.image_size_in_bytes() == 15360);
REQUIRE(f.pixels_x() == 3840);
REQUIRE(f.pixels_y() == 1);
REQUIRE(f.max_frames_per_file() == 10000);
REQUIRE(f.frame_discard_policy() == FrameDiscardPolicy::NoDiscard);
REQUIRE(f.frame_padding() == 1);
REQUIRE(f.scan_parameters()->enabled() == false);
REQUIRE(f.total_frames_expected() == 1);
REQUIRE_FALSE(f.exptime().has_value());
REQUIRE(f.period() == std::chrono::nanoseconds(0));
REQUIRE_FALSE(f.burst_mode().has_value());
REQUIRE_FALSE(f.number_of_udp_interfaces().has_value());
REQUIRE(f.bitdepth() == 32);
REQUIRE(f.ten_giga() == 1);
REQUIRE_FALSE(f.threshold_energy().has_value());
REQUIRE(ToString(f.threshold_energy_all()) == "[-1, -1, -1]");
REQUIRE_FALSE(f.subexptime().has_value());
REQUIRE_FALSE(f.subperiod().has_value());
REQUIRE_FALSE(f.quad().has_value());
REQUIRE_FALSE(f.number_of_rows().has_value());
REQUIRE_FALSE(f.rate_corrections().has_value());
REQUIRE_FALSE(f.adc_mask().has_value());
REQUIRE_FALSE(f.analog_flag());
REQUIRE_FALSE(f.analog_samples().has_value());
REQUIRE_FALSE(f.digital_flag());
REQUIRE_FALSE(f.digital_samples().has_value());
REQUIRE_FALSE(f.dbit_offset().has_value());
REQUIRE_FALSE(f.dbit_list().has_value());
REQUIRE_FALSE(f.transceiver_mask().has_value());
REQUIRE_FALSE(f.transceiver_flag());
REQUIRE_FALSE(f.transceiver_samples().has_value());
REQUIRE_FALSE(f.roi().has_value());
REQUIRE(f.counter_mask() == 0x7);
REQUIRE(ToString(f.exptime_array()) == "[0.1s, 0.1s, 0.1s]");
REQUIRE(ToString(f.gate_delay_array()) == "[0ns, 0ns, 0ns]");
REQUIRE(f.gates() == 1);
REQUIRE_FALSE(f.additional_json_header().has_value());
REQUIRE(f.frames_in_file() == 1);
REQUIRE(f.n_modules() == 1);
}

View File

@ -5,7 +5,7 @@
using aare::JungfrauDataFile;
using aare::JungfrauDataHeader;
TEST_CASE("Open a Jungfrau data file", "[.files]") {
TEST_CASE("Open a Jungfrau data file", "[.with-data]") {
// we know we have 4 files with 7, 7, 7, and 3 frames
// firs frame number if 1 and the bunch id is frame_number**2
// so we can check the header
@ -37,7 +37,7 @@ TEST_CASE("Open a Jungfrau data file", "[.files]") {
}
}
TEST_CASE("Seek in a JungfrauDataFile", "[.files]") {
TEST_CASE("Seek in a JungfrauDataFile", "[.with-data]") {
auto fpath = test_data_path() / "dat" / "AldoJF65k_000000.dat";
REQUIRE(std::filesystem::exists(fpath));
@ -70,7 +70,7 @@ TEST_CASE("Seek in a JungfrauDataFile", "[.files]") {
REQUIRE_THROWS(f.seek(86356)); // out of range
}
TEST_CASE("Open a Jungfrau data file with non zero file index", "[.files]") {
TEST_CASE("Open a Jungfrau data file with non zero file index", "[.with-data]") {
auto fpath = test_data_path() / "dat" / "AldoJF65k_000003.dat";
REQUIRE(std::filesystem::exists(fpath));
@ -94,7 +94,7 @@ TEST_CASE("Open a Jungfrau data file with non zero file index", "[.files]") {
REQUIRE(f.current_file().stem() == "AldoJF65k_000003");
}
TEST_CASE("Read into throws if size doesn't match", "[.files]") {
TEST_CASE("Read into throws if size doesn't match", "[.with-data]") {
auto fpath = test_data_path() / "dat" / "AldoJF65k_000000.dat";
REQUIRE(std::filesystem::exists(fpath));

View File

@ -21,6 +21,57 @@ TEST_CASE("Element reference 1D") {
}
}
TEST_CASE("Assign elements through () and []") {
std::vector<int> vec;
for (int i = 0; i != 10; ++i) {
vec.push_back(i);
}
NDView<int, 1> data(vec.data(), Shape<1>{10});
REQUIRE(vec.size() == static_cast<size_t>(data.size()));
data[3] = 187;
data(4) = 512;
REQUIRE(data(0) == 0);
REQUIRE(data[0] == 0);
REQUIRE(data(1) == 1);
REQUIRE(data[1] == 1);
REQUIRE(data(2) == 2);
REQUIRE(data[2] == 2);
REQUIRE(data(3) == 187);
REQUIRE(data[3] == 187);
REQUIRE(data(4) == 512);
REQUIRE(data[4] == 512);
REQUIRE(data(5) == 5);
REQUIRE(data[5] == 5);
REQUIRE(data(6) == 6);
REQUIRE(data[6] == 6);
REQUIRE(data(7) == 7);
REQUIRE(data[7] == 7);
REQUIRE(data(8) == 8);
REQUIRE(data[8] == 8);
REQUIRE(data(9) == 9);
REQUIRE(data[9] == 9);
}
TEST_CASE("Element reference 1D with a const NDView") {
std::vector<int> vec;
for (int i = 0; i != 10; ++i) {
vec.push_back(i);
}
const NDView<int, 1> data(vec.data(), Shape<1>{10});
REQUIRE(vec.size() == static_cast<size_t>(data.size()));
for (int i = 0; i != 10; ++i) {
REQUIRE(data(i) == vec[i]);
REQUIRE(data[i] == vec[i]);
}
}
TEST_CASE("Element reference 2D") {
std::vector<int> vec(12);
std::iota(vec.begin(), vec.end(), 0);
@ -56,7 +107,7 @@ TEST_CASE("Element reference 3D") {
}
}
TEST_CASE("Plus and miuns with single value") {
TEST_CASE("Plus and minus with single value") {
std::vector<int> vec(12);
std::iota(vec.begin(), vec.end(), 0);
NDView<int, 2> data(vec.data(), Shape<2>{3, 4});
@ -137,16 +188,9 @@ TEST_CASE("iterators") {
}
}
// TEST_CASE("shape from vector") {
// std::vector<int> vec;
// for (int i = 0; i != 12; ++i) {
// vec.push_back(i);
// }
// std::vector<ssize_t> shape{3, 4};
// NDView<int, 2> data(vec.data(), shape);
// }
TEST_CASE("divide with another span") {
TEST_CASE("divide with another NDView") {
std::vector<int> vec0{9, 12, 3};
std::vector<int> vec1{3, 2, 1};
std::vector<int> result{3, 6, 3};
@ -183,6 +227,30 @@ TEST_CASE("compare two views") {
REQUIRE((view1 == view2));
}
TEST_CASE("Compare two views with different size"){
std::vector<int> vec1(12);
std::iota(vec1.begin(), vec1.end(), 0);
NDView<int, 2> view1(vec1.data(), Shape<2>{3, 4});
std::vector<int> vec2(8);
std::iota(vec2.begin(), vec2.end(), 0);
NDView<int, 2> view2(vec2.data(), Shape<2>{2, 4});
REQUIRE_FALSE(view1 == view2);
}
TEST_CASE("Compare two views with same size but different shape"){
std::vector<int> vec1(12);
std::iota(vec1.begin(), vec1.end(), 0);
NDView<int, 2> view1(vec1.data(), Shape<2>{3, 4});
std::vector<int> vec2(12);
std::iota(vec2.begin(), vec2.end(), 0);
NDView<int, 2> view2(vec2.data(), Shape<2>{2, 6});
REQUIRE_FALSE(view1 == view2);
}
TEST_CASE("Create a view over a vector") {
std::vector<int> vec(12);
std::iota(vec.begin(), vec.end(), 0);

View File

@ -6,7 +6,7 @@
using aare::Dtype;
using aare::NumpyFile;
TEST_CASE("Read a 1D numpy file with int32 data type", "[.integration]") {
TEST_CASE("Read a 1D numpy file with int32 data type", "[.with-data]") {
auto fpath = test_data_path() / "numpy" / "test_1d_int32.npy";
REQUIRE(std::filesystem::exists(fpath));
@ -24,7 +24,7 @@ TEST_CASE("Read a 1D numpy file with int32 data type", "[.integration]") {
}
}
TEST_CASE("Read a 3D numpy file with np.double data type", "[.integration]") {
TEST_CASE("Read a 3D numpy file with np.double data type", "[.with-data]") {
auto fpath = test_data_path() / "numpy" / "test_3d_double.npy";
REQUIRE(std::filesystem::exists(fpath));

View File

@ -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 <fmt/format.h>
@ -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<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(); };
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<RawSubFile>(
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<size_t>((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<size_t> frame_numbers(n_modules());
std::vector<size_t> frame_indices(n_modules(), frame_index);
std::vector<size_t> frame_numbers(m_geometry.n_modules_in_roi());
std::vector<size_t> 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 +

View File

@ -3,25 +3,29 @@
#include "aare/RawMasterFile.hpp" //needed for ROI
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <filesystem>
#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]") {
TEST_CASE("Read number of frames from a jungfrau raw file", "[.with-data]") {
auto fpath =
test_data_path() / "jungfrau" / "jungfrau_single_master_0.json";
test_data_path() / "raw/jungfrau/jungfrau_single_master_0.json";
REQUIRE(std::filesystem::exists(fpath));
File f(fpath, "r");
REQUIRE(f.total_frames() == 10);
}
TEST_CASE("Read frame numbers from a jungfrau raw file", "[.integration]") {
TEST_CASE("Read frame numbers from a jungfrau raw file", "[.with-data]") {
auto fpath =
test_data_path() / "jungfrau" / "jungfrau_single_master_0.json";
test_data_path() / "raw/jungfrau/jungfrau_single_master_0.json";
REQUIRE(std::filesystem::exists(fpath));
File f(fpath, "r");
@ -36,9 +40,9 @@ TEST_CASE("Read frame numbers from a jungfrau raw file", "[.integration]") {
}
}
TEST_CASE("Read a frame number too high throws", "[.integration]") {
TEST_CASE("Read a frame number too high throws", "[.with-data]") {
auto fpath =
test_data_path() / "jungfrau" / "jungfrau_single_master_0.json";
test_data_path() / "raw/jungfrau/jungfrau_single_master_0.json";
REQUIRE(std::filesystem::exists(fpath));
File f(fpath, "r");
@ -52,9 +56,10 @@ TEST_CASE("Read a frame number too high throws", "[.integration]") {
}
TEST_CASE("Read a frame numbers where the subfile is missing throws",
"[.integration]") {
auto fpath = test_data_path() / "jungfrau" /
"[.with-data]") {
auto fpath = test_data_path() / "raw/jungfrau" /
"jungfrau_missing_subfile_master_0.json";
REQUIRE(std::filesystem::exists(fpath));
File f(fpath, "r");
@ -74,9 +79,9 @@ TEST_CASE("Read a frame numbers where the subfile is missing throws",
}
TEST_CASE("Read data from a jungfrau 500k single port raw file",
"[.integration]") {
"[.with-data]") {
auto fpath =
test_data_path() / "jungfrau" / "jungfrau_single_master_0.json";
test_data_path() / "raw/jungfrau/jungfrau_single_master_0.json";
REQUIRE(std::filesystem::exists(fpath));
File f(fpath, "r");
@ -93,8 +98,8 @@ TEST_CASE("Read data from a jungfrau 500k single port raw file",
}
}
TEST_CASE("Read frame numbers from a raw file", "[.integration]") {
auto fpath = test_data_path() / "eiger" / "eiger_500k_16bit_master_0.json";
TEST_CASE("Read frame numbers from a raw file", "[.with-data]") {
auto fpath = test_data_path() / "raw/eiger" / "eiger_500k_16bit_master_0.json";
REQUIRE(std::filesystem::exists(fpath));
// we know this file has 3 frames with frame numbers 14, 15, 16
@ -106,7 +111,9 @@ TEST_CASE("Read frame numbers from a raw file", "[.integration]") {
}
}
TEST_CASE("Compare reading from a numpy file with a raw file", "[.files]") {
TEST_CASE("Compare reading from a numpy file with a raw file", "[.with-data]") {
SECTION("jungfrau data") {
auto fpath_raw =
test_data_path() / "raw/jungfrau" / "jungfrau_single_master_0.json";
REQUIRE(std::filesystem::exists(fpath_raw));
@ -129,9 +136,47 @@ TEST_CASE("Compare reading from a numpy file with a raw file", "[.files]") {
}
}
TEST_CASE("Read multipart files", "[.integration]") {
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<uint32_t>() == npy_frame.view<uint32_t>()));
}
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<uint32_t>() == npy_frame.view<uint32_t>()));
}
}
TEST_CASE("Read multipart files", "[.with-data]") {
auto fpath =
test_data_path() / "jungfrau" / "jungfrau_double_master_0.json";
test_data_path() / "raw/jungfrau" / "jungfrau_double_master_0.json";
REQUIRE(std::filesystem::exists(fpath));
File f(fpath, "r");
@ -160,9 +205,117 @@ TEST_CASE("Read multipart files", "[.integration]") {
}
}
TEST_CASE("Read file with unordered frames", "[.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<ModuleGeometry> module_geometries{};
};
TEST_CASE("check find_geometry", "[.with-data]") {
auto test_parameters = GENERATE(
TestParameters{"raw/jungfrau_2modules_version6.1.2/run_master_0.raw", 2,
1, 2, 1024, 1024,
std::vector<ModuleGeometry>{
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>{
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>{
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>{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",
"[.with-data]") {
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", "[.with-data]") {
// TODO! Better explanation and error message
auto fpath = test_data_path() / "mythen" / "scan242_master_3.raw";
auto fpath = test_data_path() / "raw/mythen/scan242_master_3.raw";
REQUIRE(std::filesystem::exists(fpath));
File f(fpath);
REQUIRE_THROWS((f.read_frame()));

View File

@ -1,6 +1,8 @@
#include "aare/RawMasterFile.hpp"
#include "aare/to_string.hpp"
#include "aare/RawFile.hpp"
#include "aare/logger.hpp"
#include <sstream>
namespace aare {
RawFileNameComponents::RawFileNameComponents(
@ -62,6 +64,32 @@ const std::string &RawFileNameComponents::base_name() const {
const std::string &RawFileNameComponents::ext() const { return m_ext; }
int RawFileNameComponents::file_index() const { return m_file_index; }
// "[enabled\ndac dac 4\nstart 500\nstop 2200\nstep 5\nsettleTime 100us\n]"
ScanParameters::ScanParameters(const std::string &par) {
std::istringstream iss(par.substr(1, par.size() - 2));
std::string line;
while (std::getline(iss, line)) {
if (line == "enabled") {
m_enabled = true;
} else if (line.find("dac") != std::string::npos) {
m_dac = line.substr(4);
} else if (line.find("start") != std::string::npos) {
m_start = std::stoi(line.substr(6));
} else if (line.find("stop") != std::string::npos) {
m_stop = std::stoi(line.substr(5));
} else if (line.find("step") != std::string::npos) {
m_step = std::stoi(line.substr(5));
}
}
}
int ScanParameters::start() const { return m_start; }
int ScanParameters::stop() const { return m_stop; }
void ScanParameters::increment_stop() { m_stop += 1; }
int ScanParameters::step() const { return m_step; }
const std::string &ScanParameters::dac() const { return m_dac; }
bool ScanParameters::enabled() const { return m_enabled; }
RawMasterFile::RawMasterFile(const std::filesystem::path &fpath)
: m_fnc(fpath) {
if (!std::filesystem::exists(fpath)) {
@ -113,7 +141,11 @@ size_t RawMasterFile::n_modules() const {
return m_geometry.row * m_geometry.col;
}
std::optional<uint8_t> 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
@ -144,7 +176,10 @@ void RawMasterFile::parse_json(const std::filesystem::path &fpath) {
m_type = StringTo<DetectorType>(j["Detector Type"].get<std::string>());
m_timing_mode = StringTo<TimingMode>(j["Timing Mode"].get<std::string>());
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"];
@ -233,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;
@ -247,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
}
@ -364,21 +408,54 @@ void RawMasterFile::parse_raw(const std::filesystem::path &fpath) {
m_geometry = {
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") {
m_udp_interfaces_per_module = {
static_cast<uint32_t>(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

View File

@ -2,6 +2,7 @@
#include "test_config.hpp"
#include <catch2/catch_test_macros.hpp>
#include <iostream>
using namespace aare;
@ -46,6 +47,25 @@ TEST_CASE("Master file name does not fit pattern") {
REQUIRE_THROWS(RawFileNameComponents("test_master_1.txt"));
}
TEST_CASE("Parse scan parameters") {
ScanParameters s("[enabled\ndac dac 4\nstart 500\nstop 2200\nstep "
"5\nsettleTime 100us\n]");
REQUIRE(s.enabled());
REQUIRE(s.dac() == "dac 4");
REQUIRE(s.start() == 500);
REQUIRE(s.stop() == 2200);
REQUIRE(s.step() == 5);
}
TEST_CASE("A disabled scan") {
ScanParameters s("[disabled]");
REQUIRE_FALSE(s.enabled());
REQUIRE(s.dac() == "");
REQUIRE(s.start() == 0);
REQUIRE(s.stop() == 0);
REQUIRE(s.step() == 0);
}
TEST_CASE("Parse a master file in .json format", "[.integration]") {
auto fpath =
test_data_path() / "jungfrau" / "jungfrau_single_master_0.json";
@ -126,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][.with-data][.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 =

View File

@ -6,7 +6,7 @@
using namespace aare;
TEST_CASE("Read frames directly from a RawSubFile", "[.files]") {
TEST_CASE("Read frames directly from a RawSubFile", "[.with-data]") {
auto fpath_raw =
test_data_path() / "raw/jungfrau" / "jungfrau_single_d0_f0_0.raw";
REQUIRE(std::filesystem::exists(fpath_raw));
@ -40,7 +40,7 @@ TEST_CASE("Read frames directly from a RawSubFile", "[.files]") {
}
TEST_CASE("Read frames directly from a RawSubFile starting at the second file",
"[.files]") {
"[.with-data]") {
// we know this file has 10 frames with frame numbers 1 to 10
// f0 1,2,3
// f1 4,5,6 <-- starting here

View File

@ -1,4 +1,6 @@
#include "aare/defs.hpp"
#include <stdexcept>
#include <string>
#include <fmt/core.h>
namespace aare {
@ -8,4 +10,109 @@ void assert_failed(const std::string &msg) {
exit(1);
}
/**
* @brief Convert a DetectorType to a string
* @param type DetectorType
* @return string representation of the DetectorType
*/
template <> std::string ToString(DetectorType arg) {
switch (arg) {
case DetectorType::Generic:
return "Generic";
case DetectorType::Eiger:
return "Eiger";
case DetectorType::Gotthard:
return "Gotthard";
case DetectorType::Jungfrau:
return "Jungfrau";
case DetectorType::ChipTestBoard:
return "ChipTestBoard";
case DetectorType::Moench:
return "Moench";
case DetectorType::Mythen3:
return "Mythen3";
case DetectorType::Gotthard2:
return "Gotthard2";
case DetectorType::Xilinx_ChipTestBoard:
return "Xilinx_ChipTestBoard";
// Custom ones
case DetectorType::Moench03:
return "Moench03";
case DetectorType::Moench03_old:
return "Moench03_old";
case DetectorType::Unknown:
return "Unknown";
// no default case to trigger compiler warning if not all
// enum values are handled
}
throw std::runtime_error("Could not decode detector to string");
}
/**
* @brief Convert a string to a DetectorType
* @param name string representation of the DetectorType
* @return DetectorType
* @throw runtime_error if the string does not match any DetectorType
*/
template <> DetectorType StringTo(const std::string &arg) {
if (arg == "Generic")
return DetectorType::Generic;
if (arg == "Eiger")
return DetectorType::Eiger;
if (arg == "Gotthard")
return DetectorType::Gotthard;
if (arg == "Jungfrau")
return DetectorType::Jungfrau;
if (arg == "ChipTestBoard")
return DetectorType::ChipTestBoard;
if (arg == "Moench")
return DetectorType::Moench;
if (arg == "Mythen3")
return DetectorType::Mythen3;
if (arg == "Gotthard2")
return DetectorType::Gotthard2;
if (arg == "Xilinx_ChipTestBoard")
return DetectorType::Xilinx_ChipTestBoard;
// Custom ones
if (arg == "Moench03")
return DetectorType::Moench03;
if (arg == "Moench03_old")
return DetectorType::Moench03_old;
if (arg == "Unknown")
return DetectorType::Unknown;
throw std::runtime_error("Could not decode detector from: \"" + arg + "\"");
}
/**
* @brief Convert a string to a TimingMode
* @param mode string representation of the TimingMode
* @return TimingMode
* @throw runtime_error if the string does not match any TimingMode
*/
template <> TimingMode StringTo(const std::string &arg) {
if (arg == "auto")
return TimingMode::Auto;
if (arg == "trigger")
return TimingMode::Trigger;
throw std::runtime_error("Could not decode timing mode from: \"" + arg +
"\"");
}
template <> FrameDiscardPolicy StringTo(const std::string &arg) {
if (arg == "nodiscard")
return FrameDiscardPolicy::NoDiscard;
if (arg == "discard")
return FrameDiscardPolicy::Discard;
if (arg == "discardpartial")
return FrameDiscardPolicy::DiscardPartial;
throw std::runtime_error("Could not decode frame discard policy from: \"" +
arg + "\"");
}
// template <> TimingMode StringTo<TimingMode>(std::string mode);
} // namespace aare

View File

@ -1,6 +1,54 @@
#include "aare/defs.hpp"
#include <catch2/catch_test_macros.hpp>
#include <string>
using aare::StringTo;
using aare::ToString;
TEST_CASE("Enum to string conversion") {
// TODO! By the way I don't think the enum string conversions should be in
// the defs.hpp file but let's use this to show a test
REQUIRE(ToString(aare::DetectorType::Generic) == "Generic");
REQUIRE(ToString(aare::DetectorType::Eiger) == "Eiger");
REQUIRE(ToString(aare::DetectorType::Gotthard) == "Gotthard");
REQUIRE(ToString(aare::DetectorType::Jungfrau) == "Jungfrau");
REQUIRE(ToString(aare::DetectorType::ChipTestBoard) == "ChipTestBoard");
REQUIRE(ToString(aare::DetectorType::Moench) == "Moench");
REQUIRE(ToString(aare::DetectorType::Mythen3) == "Mythen3");
REQUIRE(ToString(aare::DetectorType::Gotthard2) == "Gotthard2");
REQUIRE(ToString(aare::DetectorType::Xilinx_ChipTestBoard) ==
"Xilinx_ChipTestBoard");
REQUIRE(ToString(aare::DetectorType::Moench03) == "Moench03");
REQUIRE(ToString(aare::DetectorType::Moench03_old) == "Moench03_old");
REQUIRE(ToString(aare::DetectorType::Unknown) == "Unknown");
}
TEST_CASE("String to enum") {
REQUIRE(StringTo<aare::DetectorType>("Generic") ==
aare::DetectorType::Generic);
REQUIRE(StringTo<aare::DetectorType>("Eiger") == aare::DetectorType::Eiger);
REQUIRE(StringTo<aare::DetectorType>("Gotthard") ==
aare::DetectorType::Gotthard);
REQUIRE(StringTo<aare::DetectorType>("Jungfrau") ==
aare::DetectorType::Jungfrau);
REQUIRE(StringTo<aare::DetectorType>("ChipTestBoard") ==
aare::DetectorType::ChipTestBoard);
REQUIRE(StringTo<aare::DetectorType>("Moench") ==
aare::DetectorType::Moench);
REQUIRE(StringTo<aare::DetectorType>("Mythen3") ==
aare::DetectorType::Mythen3);
REQUIRE(StringTo<aare::DetectorType>("Gotthard2") ==
aare::DetectorType::Gotthard2);
REQUIRE(StringTo<aare::DetectorType>("Xilinx_ChipTestBoard") ==
aare::DetectorType::Xilinx_ChipTestBoard);
REQUIRE(StringTo<aare::DetectorType>("Moench03") ==
aare::DetectorType::Moench03);
REQUIRE(StringTo<aare::DetectorType>("Moench03_old") ==
aare::DetectorType::Moench03_old);
REQUIRE(StringTo<aare::DetectorType>("Unknown") ==
aare::DetectorType::Unknown);
}
TEST_CASE("Enum values") {
// Since some of the enums are written to file we need to make sure

View File

@ -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

View File

@ -1,228 +0,0 @@
#include "aare/File.hpp"
#include "aare/RawFile.hpp"
#include "aare/RawMasterFile.hpp" //needed for ROI
#include <catch2/catch_test_macros.hpp>
#include <filesystem>
#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);
}

View File

@ -1,26 +0,0 @@
#include "aare/scan_parameters.hpp"
#include <catch2/catch_test_macros.hpp>
using namespace aare;
// TODO: Move these to to_string.test.cpp once ToSTring is implemented
TEST_CASE("Parse scan parameters") {
ScanParameters s("[enabled\ndac dac 4\nstart 500\nstop 2200\nstep "
"5\nsettleTime 100us\n]");
REQUIRE(s.enabled());
REQUIRE(s.dac() == "dac 4");
REQUIRE(s.start() == 500);
REQUIRE(s.stop() == 2200);
REQUIRE(s.step() == 5);
}
TEST_CASE("A disabled scan") {
ScanParameters s("[disabled]");
REQUIRE_FALSE(s.enabled());
REQUIRE(s.dac() == "");
REQUIRE(s.start() == 0);
REQUIRE(s.stop() == 0);
REQUIRE(s.step() == 0);
}

View File

@ -1,30 +0,0 @@
#include "aare/string_utils.hpp"
#include <algorithm>
namespace aare {
std::string RemoveUnit(std::string &str) {
auto it = str.begin();
while (it != str.end()) {
if (std::isalpha(*it))
break;
++it;
}
auto pos = it - str.begin();
auto unit = str.substr(pos);
str.erase(it, end(str));
return unit;
}
void TrimWhiteSpaces(std::string &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char c) {
return !std::isspace(c);
}));
s.erase(std::find_if(s.rbegin(), s.rend(),
[](unsigned char c) { return !std::isspace(c); })
.base(),
s.end());
}
} // namespace aare

View File

@ -1,226 +0,0 @@
#include "aare/to_string.hpp"
namespace aare {
/**
* @brief Convert a DetectorType to a string
* @param type DetectorType
* @return string representation of the DetectorType
*/
template <> std::string ToString(DetectorType arg) {
switch (arg) {
case DetectorType::Generic:
return "Generic";
case DetectorType::Eiger:
return "Eiger";
case DetectorType::Gotthard:
return "Gotthard";
case DetectorType::Jungfrau:
return "Jungfrau";
case DetectorType::ChipTestBoard:
return "ChipTestBoard";
case DetectorType::Moench:
return "Moench";
case DetectorType::Mythen3:
return "Mythen3";
case DetectorType::Gotthard2:
return "Gotthard2";
case DetectorType::Xilinx_ChipTestBoard:
return "Xilinx_ChipTestBoard";
// Custom ones
case DetectorType::Moench03:
return "Moench03";
case DetectorType::Moench03_old:
return "Moench03_old";
case DetectorType::Unknown:
return "Unknown";
// no default case to trigger compiler warning if not all
// enum values are handled
}
throw std::runtime_error("Could not decode detector to string");
}
/**
* @brief Convert a string to a DetectorType
* @param name string representation of the DetectorType
* @return DetectorType
* @throw runtime_error if the string does not match any DetectorType
*/
template <> DetectorType StringTo(const std::string &arg) {
if (arg == "Generic")
return DetectorType::Generic;
if (arg == "Eiger")
return DetectorType::Eiger;
if (arg == "Gotthard")
return DetectorType::Gotthard;
if (arg == "Jungfrau")
return DetectorType::Jungfrau;
if (arg == "ChipTestBoard")
return DetectorType::ChipTestBoard;
if (arg == "Moench")
return DetectorType::Moench;
if (arg == "Mythen3")
return DetectorType::Mythen3;
if (arg == "Gotthard2")
return DetectorType::Gotthard2;
if (arg == "Xilinx_ChipTestBoard")
return DetectorType::Xilinx_ChipTestBoard;
// Custom ones
if (arg == "Moench03")
return DetectorType::Moench03;
if (arg == "Moench03_old")
return DetectorType::Moench03_old;
if (arg == "Unknown")
return DetectorType::Unknown;
throw std::runtime_error("Could not decode detector from: \"" + arg + "\"");
}
/**
* @brief Convert a TimingMode to a string
* @param type TimingMode
* @return string representation of the TimingMode
*/
template <> std::string ToString(TimingMode arg) {
switch (arg) {
case TimingMode::Auto:
return "Auto";
case TimingMode::Trigger:
return "Trigger";
// no default case to trigger compiler warning if not all
// enum values are handled
}
throw std::runtime_error("Could not decode timing mode to string");
}
/**
* @brief Convert a string to a TimingMode
* @param mode string representation of the TimingMode
* @return TimingMode
* @throw runtime_error if the string does not match any TimingMode
*/
template <> TimingMode StringTo(const std::string &arg) {
if (arg == "auto")
return TimingMode::Auto;
if (arg == "trigger")
return TimingMode::Trigger;
throw std::runtime_error("Could not decode timing mode from: \"" + arg +
"\"");
}
/**
* @brief Convert a FrameDiscardPolicy to a string
* @param type FrameDiscardPolicy
* @return string representation of the FrameDiscardPolicy
*/
template <> std::string ToString(FrameDiscardPolicy arg) {
switch (arg) {
case FrameDiscardPolicy::NoDiscard:
return "nodiscard";
case FrameDiscardPolicy::Discard:
return "discard";
case FrameDiscardPolicy::DiscardPartial:
return "discardpartial";
// no default case to trigger compiler warning if not all
// enum values are handled
}
throw std::runtime_error("Could not decode frame discard policy to string");
}
template <> FrameDiscardPolicy StringTo(const std::string &arg) {
if (arg == "nodiscard")
return FrameDiscardPolicy::NoDiscard;
if (arg == "discard")
return FrameDiscardPolicy::Discard;
if (arg == "discardpartial")
return FrameDiscardPolicy::DiscardPartial;
throw std::runtime_error("Could not decode frame discard policy from: \"" +
arg + "\"");
}
/**
* @brief Convert a BurstMode to a string
* @param type BurstMode
* @return string representation of the BurstMode
*/
template <> std::string ToString(BurstMode arg) {
switch (arg) {
case BurstMode::Burst_Interal:
return "burst_internal";
case BurstMode::Burst_External:
return "burst_external";
case BurstMode::Continuous_Internal:
return "continuous_internal";
case BurstMode::Continuous_External:
return "continuous_external";
}
throw std::runtime_error("Could not decode burst mode to string");
}
template <> BurstMode StringTo(const std::string &arg) {
if (arg == "burst_internal")
return BurstMode::Burst_Interal;
if (arg == "burst_external")
return BurstMode::Burst_External;
if (arg == "continuous_internal")
return BurstMode::Continuous_Internal;
if (arg == "continuous_external")
return BurstMode::Continuous_External;
throw std::runtime_error("Could not decode burst mode from: \"" + arg +
"\"");
}
/**
* @brief Convert a ScanParameters to a string
* @param type ScanParameters
* @return string representation of the ScanParameters
*/
template <> std::string ToString(ScanParameters arg) {
std::ostringstream oss;
oss << '[';
if (arg.enabled()) {
oss << "enabled" << std::endl
<< "dac " << arg.dac() << std::endl
<< "start " << arg.start() << std::endl
<< "stop " << arg.stop() << std::endl
<< "step " << arg.step()
<< std::endl
//<< "settleTime "
// << ToString(std::chrono::nanoseconds{arg.dacSettleTime_ns})
<< std::endl;
} else {
oss << "disabled";
}
oss << ']';
return oss.str();
}
std::ostream &operator<<(std::ostream &os, const ScanParameters &r) {
return os << ToString(r);
}
/**
* @brief Convert a ROI to a string
* @param type ROI
* @return string representation of the ROI
*/
template <> std::string ToString(ROI arg) {
std::ostringstream oss;
oss << '[' << arg.xmin << ", " << arg.xmax;
if (arg.ymin != -1 || arg.ymax != -1) {
oss << ", " << arg.ymin << ", " << arg.ymax;
}
oss << ']';
return oss.str();
}
std::ostream &operator<<(std::ostream &os, const ROI &roi) {
return os << ToString(roi);
}
} // namespace aare

View File

@ -1,224 +0,0 @@
#include "aare/to_string.hpp"
#include <catch2/catch_test_macros.hpp>
using aare::StringTo;
using aare::ToString;
TEST_CASE("Detector Type to string conversion") {
// TODO! By the way I don't think the enum string conversions should be in
// the defs.hpp file but let's use this to show a test
REQUIRE(ToString(aare::DetectorType::Generic) == "Generic");
REQUIRE(ToString(aare::DetectorType::Eiger) == "Eiger");
REQUIRE(ToString(aare::DetectorType::Gotthard) == "Gotthard");
REQUIRE(ToString(aare::DetectorType::Jungfrau) == "Jungfrau");
REQUIRE(ToString(aare::DetectorType::ChipTestBoard) == "ChipTestBoard");
REQUIRE(ToString(aare::DetectorType::Moench) == "Moench");
REQUIRE(ToString(aare::DetectorType::Mythen3) == "Mythen3");
REQUIRE(ToString(aare::DetectorType::Gotthard2) == "Gotthard2");
REQUIRE(ToString(aare::DetectorType::Xilinx_ChipTestBoard) ==
"Xilinx_ChipTestBoard");
REQUIRE(ToString(aare::DetectorType::Moench03) == "Moench03");
REQUIRE(ToString(aare::DetectorType::Moench03_old) == "Moench03_old");
REQUIRE(ToString(aare::DetectorType::Unknown) == "Unknown");
}
TEST_CASE("String to Detector Type") {
REQUIRE(StringTo<aare::DetectorType>("Generic") ==
aare::DetectorType::Generic);
REQUIRE(StringTo<aare::DetectorType>("Eiger") == aare::DetectorType::Eiger);
REQUIRE(StringTo<aare::DetectorType>("Gotthard") ==
aare::DetectorType::Gotthard);
REQUIRE(StringTo<aare::DetectorType>("Jungfrau") ==
aare::DetectorType::Jungfrau);
REQUIRE(StringTo<aare::DetectorType>("ChipTestBoard") ==
aare::DetectorType::ChipTestBoard);
REQUIRE(StringTo<aare::DetectorType>("Moench") ==
aare::DetectorType::Moench);
REQUIRE(StringTo<aare::DetectorType>("Mythen3") ==
aare::DetectorType::Mythen3);
REQUIRE(StringTo<aare::DetectorType>("Gotthard2") ==
aare::DetectorType::Gotthard2);
REQUIRE(StringTo<aare::DetectorType>("Xilinx_ChipTestBoard") ==
aare::DetectorType::Xilinx_ChipTestBoard);
REQUIRE(StringTo<aare::DetectorType>("Moench03") ==
aare::DetectorType::Moench03);
REQUIRE(StringTo<aare::DetectorType>("Moench03_old") ==
aare::DetectorType::Moench03_old);
REQUIRE(StringTo<aare::DetectorType>("Unknown") ==
aare::DetectorType::Unknown);
}
TEST_CASE("conversion from duration to string") {
using ns = std::chrono::nanoseconds;
using us = std::chrono::microseconds;
using ms = std::chrono::milliseconds;
using s = std::chrono::seconds;
REQUIRE(ToString(ns(150)) == "150ns");
REQUIRE(ToString(ms(783)) == "0.783s");
REQUIRE(ToString(ms(783), "ms") == "783ms");
REQUIRE(ToString(us(0)) == "0ns"); // Defaults to the lowest unit
REQUIRE(ToString(us(0), "s") == "0s");
REQUIRE(ToString(s(-1)) == "-1s");
REQUIRE(ToString(us(-100)) == "-100us");
}
TEST_CASE("Convert vector of time") {
using ns = std::chrono::nanoseconds;
using us = std::chrono::microseconds;
using ms = std::chrono::milliseconds;
using s = std::chrono::seconds;
std::vector<ns> vec{ns(150), us(10), ns(600)};
REQUIRE(ToString(vec) == "[150ns, 10us, 600ns]");
vec[0] = ms(150);
vec[1] = s(10);
REQUIRE(ToString(vec) == "[0.15s, 10s, 600ns]");
// REQUIRE(ToString(vec, "ns") == "[150ns, 10000ns, 600ns]");
}
TEST_CASE("string to std::chrono::duration") {
using ns = std::chrono::nanoseconds;
using us = std::chrono::microseconds;
using ms = std::chrono::milliseconds;
using s = std::chrono::seconds;
REQUIRE(StringTo<ns>("150", "ns") == ns(150));
REQUIRE(StringTo<ns>("150us") == us(150));
REQUIRE(StringTo<ns>("150ms") == ms(150));
REQUIRE(StringTo<s>("3 s") == s(3));
REQUIRE_THROWS(StringTo<ns>("5xs"));
REQUIRE_THROWS(StringTo<ns>("asvn"));
}
TEST_CASE("Vector of int") {
std::vector<int> vec;
REQUIRE(ToString(vec) == "[]");
vec.push_back(1);
REQUIRE(ToString(vec) == "[1]");
vec.push_back(172);
REQUIRE(ToString(vec) == "[1, 172]");
vec.push_back(5000);
REQUIRE(ToString(vec) == "[1, 172, 5000]");
}
TEST_CASE("Vector of double") {
std::vector<double> vec;
REQUIRE(ToString(vec) == "[]");
vec.push_back(1.3);
REQUIRE(ToString(vec) == "[1.3]");
// vec.push_back(5669.325);
// REQUIRE(ToString(vec) == "[1.3, 5669.325]");
// vec.push_back(-5669.325005);
// REQUIRE(ToString(vec) == "[1.3, 5669.325, -5669.325005]");
}
TEST_CASE("String to string") {
std::string k = "hej";
REQUIRE(ToString(k) == "hej");
}
TEST_CASE("vector of strings") {
std::vector<std::string> vec{"5", "s"};
REQUIRE(ToString(vec) == "[5, s]");
std::vector<std::string> vec2{"some", "strange", "words", "75"};
REQUIRE(ToString(vec2) == "[some, strange, words, 75]");
}
TEST_CASE("uint32 from string") {
REQUIRE(StringTo<uint32_t>("0") == 0);
REQUIRE(StringTo<uint32_t>("5") == 5u);
REQUIRE(StringTo<uint32_t>("16") == 16u);
REQUIRE(StringTo<uint32_t>("20") == 20u);
REQUIRE(StringTo<uint32_t>("0x14") == 20u);
REQUIRE(StringTo<uint32_t>("0x15") == 21u);
REQUIRE(StringTo<uint32_t>("0x15") == 0x15);
REQUIRE(StringTo<uint32_t>("0xffffff") == 0xffffff);
}
TEST_CASE("uint64 from string") {
REQUIRE(StringTo<uint64_t>("0") == 0);
REQUIRE(StringTo<uint64_t>("5") == 5u);
REQUIRE(StringTo<uint64_t>("16") == 16u);
REQUIRE(StringTo<uint64_t>("20") == 20u);
REQUIRE(StringTo<uint64_t>("0x14") == 20u);
REQUIRE(StringTo<uint64_t>("0x15") == 21u);
REQUIRE(StringTo<uint64_t>("0xffffff") == 0xffffff);
}
TEST_CASE("int from string") {
REQUIRE(StringTo<int>("-1") == -1);
REQUIRE(StringTo<int>("-0x1") == -0x1);
REQUIRE(StringTo<int>("-0x1") == -1);
REQUIRE(StringTo<int>("0") == 0);
REQUIRE(StringTo<int>("5") == 5);
REQUIRE(StringTo<int>("16") == 16);
REQUIRE(StringTo<int>("20") == 20);
REQUIRE(StringTo<int>("0x14") == 20);
REQUIRE(StringTo<int>("0x15") == 21);
REQUIRE(StringTo<int>("0xffffff") == 0xffffff);
}
TEST_CASE("std::map of strings") {
std::map<std::string, std::string> m;
m["key"] = "value";
auto s = ToString(m);
REQUIRE(s == "{key: value}");
m["chrusi"] = "musi";
REQUIRE(ToString(m) == "{chrusi: musi, key: value}");
m["test"] = "tree";
REQUIRE(ToString(m) == "{chrusi: musi, key: value, test: tree}");
}
TEST_CASE("Formatting ROI") {
aare::ROI roi;
roi.xmin = 5;
roi.xmax = 159;
roi.ymin = 6;
roi.ymax = 170;
REQUIRE(ToString(roi) == "[5, 159, 6, 170]");
}
TEST_CASE("Streaming of ROI") {
aare::ROI roi;
roi.xmin = 50;
roi.xmax = 109;
roi.ymin = 6;
roi.ymax = 130;
std::ostringstream oss;
oss << roi;
REQUIRE(oss.str() == "[50, 109, 6, 130]");
}
// TODO: After StringTo<scanParameters> is implemented
/*TEST_CASE("Streaming of scanParameters") {
using namespace sls;
{
aare::scanParameters t{};
std::ostringstream oss;
oss << t;
REQUIRE(oss.str() == "[disabled]");
}
{
aare::scanParameters t{defs::VTH2, 500, 1500, 500};
std::ostringstream oss;
oss << t;
REQUIRE(oss.str() == "[enabled\ndac vth2\nstart 500\nstop 1500\nstep "
"500\nsettleTime 1ms\n]");
}
{
aare::scanParameters t{defs::VTH2, 500, 1500, 500,
std::chrono::milliseconds{500}};
std::ostringstream oss;
oss << t;
REQUIRE(oss.str() == "[enabled\ndac vth2\nstart 500\nstop 1500\nstep "
"500\nsettleTime 0.5s\n]");
}
}*/

View File

@ -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})

4
tests/friend_test.hpp Normal file
View File

@ -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

20
tests/test_macros.hpp Normal file
View File

@ -0,0 +1,20 @@
#include <catch2/catch_test_macros.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/internal/catch_test_registry.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#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<test_name##_Invoker>(), \
Catch::SourceLineInfo(__FILE__, __LINE__), "", \
Catch::NameAndTags{test_name_str, test_tags_str}); \
\
void test_name##_impl()