mirror of
https://github.com/slsdetectorgroup/aare.git
synced 2025-07-04 16:54:48 +02:00
Compare commits
38 Commits
dev/reduce
...
dev/hdf5
Author | SHA1 | Date | |
---|---|---|---|
119ca96a52 | |||
053536d135 | |||
286b2888ca | |||
52aa1d4d9b | |||
8556ab6564 | |||
51a87e2a1e | |||
e6dd1f3ec2 | |||
65672d06f3 | |||
bceefe6d64 | |||
cc57cc7c27 | |||
d89530ed22 | |||
7917e6f81a | |||
a26073fb41 | |||
f3f3e2af6a | |||
031d9503d8 | |||
cba2e46e2f | |||
b4a9b4caec | |||
be7f510775 | |||
56fa6f6bfb | |||
ca4d392b2f | |||
3b65e92cb7 | |||
755a8fb2b7 | |||
dc7f6d44f2 | |||
480e28c927 | |||
d7242671b2 | |||
a6a02249bc | |||
a3f831dc9e | |||
76b8872fe6 | |||
55236ce6cc | |||
e7d3e667b0 | |||
d9cbf0f481 | |||
5681e18403 | |||
0b252709bd | |||
e5df929a9a | |||
b7337fc6c5 | |||
09de69c090 | |||
b23e697e26 | |||
4233509615 |
2
.github/workflows/build_docs.yml
vendored
2
.github/workflows/build_docs.yml
vendored
@ -43,7 +43,7 @@ jobs:
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DAARE_SYSTEM_LIBRARIES=ON -DAARE_PYTHON_BINDINGS=ON -DAARE_DOCS=ON
|
||||
cmake .. -DAARE_SYSTEM_LIBRARIES=ON -DAARE_DOCS=ON
|
||||
make -j 2
|
||||
make docs
|
||||
|
||||
|
@ -53,6 +53,7 @@ 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
|
||||
@ -77,6 +78,17 @@ 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()
|
||||
@ -324,10 +336,13 @@ if(AARE_ASAN)
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if(AARE_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
target_compile_definitions(tests PRIVATE AARE_TESTS)
|
||||
endif()
|
||||
|
||||
###------------------------------------------------------------------------------MAIN LIBRARY
|
||||
@ -342,6 +357,10 @@ 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
|
||||
@ -350,7 +369,7 @@ set(PUBLICHEADERS
|
||||
include/aare/FilePtr.hpp
|
||||
include/aare/Frame.hpp
|
||||
include/aare/GainMap.hpp
|
||||
include/aare/DetectorGeometry.hpp
|
||||
include/aare/geo_helpers.hpp
|
||||
include/aare/JungfrauDataFile.hpp
|
||||
include/aare/logger.hpp
|
||||
include/aare/NDArray.hpp
|
||||
@ -370,13 +389,15 @@ 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/File.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/FilePtr.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Fit.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/DetectorGeometry.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/geo_helpers.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/JungfrauDataFile.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyFile.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/NumpyHelpers.cpp
|
||||
@ -389,6 +410,22 @@ set(SourceFiles
|
||||
${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>"
|
||||
@ -411,18 +448,14 @@ target_link_libraries(
|
||||
|
||||
)
|
||||
|
||||
if(AARE_TESTS)
|
||||
target_compile_definitions(aare_core PRIVATE AARE_TESTS)
|
||||
endif()
|
||||
if(AARE_VERBOSE)
|
||||
target_compile_definitions(aare_core PUBLIC AARE_VERBOSE)
|
||||
target_compile_definitions(aare_core PUBLIC AARE_LOG_LEVEL=aare::logDEBUG5)
|
||||
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)
|
||||
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}
|
||||
)
|
||||
endif()
|
||||
|
||||
set_target_properties(aare_core PROPERTIES
|
||||
@ -438,10 +471,12 @@ 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/DetectorGeometry.test.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/geo_helpers.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
|
||||
@ -460,9 +495,22 @@ 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}"
|
||||
|
@ -6,7 +6,6 @@
|
||||
Features:
|
||||
|
||||
- Cluster finder now works with 5x5, 7x7 and 9x9 clusters
|
||||
- Added ClusterVector::empty() member
|
||||
|
||||
|
||||
### 2025.05.22
|
||||
@ -18,7 +17,6 @@ Features:
|
||||
Bugfixes:
|
||||
|
||||
- Fixed crash when opening raw files with large number of data files
|
||||
- Fixed reading RawFiles with ROI fully excluding some sub files.
|
||||
|
||||
|
||||
|
||||
|
@ -14,7 +14,6 @@ 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}")
|
||||
@ -45,3 +44,12 @@ 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"
|
||||
)
|
@ -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 = *build* */docs/* */tests/* *.test.cpp* */python/* */manual */slsDetectorServers/* */libs/* */integrationTests *README* *_deps* *TobiSchluter*
|
||||
EXCLUDE_PATTERNS = */docs/* */tests/* */python/* */manual */slsDetectorServers/* */libs/* */integrationTests *README* */slsDetectorGui/* */ctbGui/* */slsDetectorCalibration/* *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
|
||||
|
@ -4,5 +4,4 @@ ClusterFile
|
||||
.. doxygenclass:: aare::ClusterFile
|
||||
:members:
|
||||
:undoc-members:
|
||||
:private-members:
|
||||
|
||||
:private-members:
|
@ -3,13 +3,4 @@ ClusterVector
|
||||
|
||||
.. doxygenclass:: aare::ClusterVector
|
||||
:members:
|
||||
:undoc-members:
|
||||
:private-members:
|
||||
|
||||
|
||||
|
||||
.. doxygenclass:: aare::ClusterVector< Cluster< T, ClusterSizeX, ClusterSizeY, CoordType > >
|
||||
:members:
|
||||
:undoc-members:
|
||||
:private-members:
|
||||
|
||||
:undoc-members:
|
8
docs/src/Hdf5File.rst
Normal file
8
docs/src/Hdf5File.rst
Normal file
@ -0,0 +1,8 @@
|
||||
Hdf5File
|
||||
===============
|
||||
|
||||
|
||||
.. doxygenclass:: aare::Hdf5File
|
||||
:members:
|
||||
:undoc-members:
|
||||
:private-members:
|
14
docs/src/Hdf5MasterFile.rst
Normal file
14
docs/src/Hdf5MasterFile.rst
Normal file
@ -0,0 +1,14 @@
|
||||
Hdf5MasterFile
|
||||
===============
|
||||
|
||||
|
||||
.. doxygenclass:: aare::Hdf5MasterFile
|
||||
:members:
|
||||
:undoc-members:
|
||||
:private-members:
|
||||
|
||||
|
||||
.. doxygenclass:: aare::Hdf5FileNameComponents
|
||||
:members:
|
||||
:undoc-members:
|
||||
:private-members:
|
@ -2,7 +2,7 @@
|
||||
Tests
|
||||
****************
|
||||
|
||||
We test the code both from C++ and Python. By default only tests that does not require additional data are run.
|
||||
We test the code both from the C++ and Python API. By default only tests that does not require image data is run.
|
||||
|
||||
C++
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
@ -15,7 +15,7 @@ C++
|
||||
make -j 4
|
||||
|
||||
export AARE_TEST_DATA=/path/to/test/data
|
||||
./run_test [.with-data] #or using ctest, [.with-data] is the option to include tests needing data
|
||||
./run_test [.files] #or using ctest, [.files] 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 --with-data # passing --with-data will run the tests needing data
|
||||
python -m pytest python/tests --files # passing --files 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 [.with-data] for the C++ tests or --files for Python
|
||||
AARE_TEST_DATA to the path of the test data directory. Then pass either [.files] 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.
|
||||
|
@ -37,7 +37,7 @@ unfamiliar steps.
|
||||
|
||||
|
||||
Checklists for deployment
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
**Feature:**
|
||||
|
||||
|
@ -31,6 +31,8 @@ AARE
|
||||
pyJungfrauDataFile
|
||||
pyRawFile
|
||||
pyRawMasterFile
|
||||
pyHdf5File
|
||||
pyHdf5MasterFile
|
||||
pyVarClusterFinder
|
||||
|
||||
pyFit
|
||||
@ -55,6 +57,8 @@ AARE
|
||||
RawFile
|
||||
RawSubFile
|
||||
RawMasterFile
|
||||
Hdf5File
|
||||
Hdf5MasterFile
|
||||
VarClusterFinder
|
||||
|
||||
|
||||
|
@ -2,24 +2,9 @@
|
||||
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:
|
||||
|
@ -2,10 +2,8 @@ ClusterVector
|
||||
================
|
||||
|
||||
The ClusterVector, holds clusters from the ClusterFinder. Since it is templated
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
@ -28,8 +26,7 @@ C++ functions that support the ClusterVector or to view it as a numpy array.
|
||||
|
||||
.. py:currentmodule:: aare
|
||||
|
||||
.. autoclass:: aare._aare.ClusterVector_Cluster3x3i
|
||||
:special-members: __init__
|
||||
.. autoclass:: ClusterVector_i
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
10
docs/src/pyHdf5File.rst
Normal file
10
docs/src/pyHdf5File.rst
Normal file
@ -0,0 +1,10 @@
|
||||
Hdf5File
|
||||
===================
|
||||
|
||||
.. py:currentmodule:: aare
|
||||
|
||||
.. autoclass:: Hdf5File
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:inherited-members:
|
10
docs/src/pyHdf5MasterFile.rst
Normal file
10
docs/src/pyHdf5MasterFile.rst
Normal file
@ -0,0 +1,10 @@
|
||||
Hdf5MasterFile
|
||||
===================
|
||||
|
||||
.. py:currentmodule:: aare
|
||||
|
||||
.. autoclass:: Hdf5MasterFile
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:inherited-members:
|
@ -5,12 +5,9 @@ dependencies:
|
||||
- anaconda-client
|
||||
- conda-build
|
||||
- doxygen
|
||||
- sphinx
|
||||
- sphinx=7.1.2
|
||||
- breathe
|
||||
- sphinx_rtd_theme
|
||||
- furo
|
||||
- zeromq
|
||||
- pybind11
|
||||
- numpy
|
||||
- matplotlib
|
||||
|
||||
|
@ -74,110 +74,6 @@ struct Cluster {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
Cluster<T, 2, 2, uint16_t> reduce_3x3_to_2x2(const Cluster<T, 3, 3, uint16_t> &c) {
|
||||
Cluster<T, 2, 2, uint16_t> result;
|
||||
|
||||
auto [s, i] = c.max_sum_2x2();
|
||||
switch (i) {
|
||||
case 0:
|
||||
result.x = c.x-1;
|
||||
result.y = c.y+1;
|
||||
result.data = {c.data[0], c.data[1], c.data[3], c.data[4]};
|
||||
break;
|
||||
case 1:
|
||||
result.x = c.x;
|
||||
result.y = c.y + 1;
|
||||
result.data = {c.data[1], c.data[2], c.data[4], c.data[5]};
|
||||
break;
|
||||
case 2:
|
||||
result.x = c.x -1;
|
||||
result.y = c.y;
|
||||
result.data = {c.data[3], c.data[4], c.data[6], c.data[7]};
|
||||
break;
|
||||
case 3:
|
||||
result.x = c.x;
|
||||
result.y = c.y;
|
||||
result.data = {c.data[4], c.data[5], c.data[7], c.data[8]};
|
||||
break;
|
||||
}
|
||||
|
||||
// do some stuff
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Cluster<T, 3, 3, uint16_t> reduce_5x5_to_3x3(const Cluster<T, 5, 5, uint16_t> &c) {
|
||||
Cluster<T, 3, 3, uint16_t> result;
|
||||
|
||||
// Reduce the 5x5 cluster to a 3x3 cluster by selecting the 3x3 block with the highest sum
|
||||
std::array<T, 9> sum_3x3_subclusters;
|
||||
|
||||
//Write out the sums in the hope that the compiler can optimize this
|
||||
sum_3x3_subclusters[0] = c.data[0] + c.data[1] + c.data[2] + c.data[5] + c.data[6] + c.data[7] + c.data[10] + c.data[11] + c.data[12];
|
||||
sum_3x3_subclusters[1] = c.data[1] + c.data[2] + c.data[3] + c.data[6] + c.data[7] + c.data[8] + c.data[11] + c.data[12] + c.data[13];
|
||||
sum_3x3_subclusters[2] = c.data[2] + c.data[3] + c.data[4] + c.data[7] + c.data[8] + c.data[9] + c.data[12] + c.data[13] + c.data[14];
|
||||
sum_3x3_subclusters[3] = c.data[5] + c.data[6] + c.data[7] + c.data[10] + c.data[11] + c.data[12] + c.data[15] + c.data[16] + c.data[17];
|
||||
sum_3x3_subclusters[4] = c.data[6] + c.data[7] + c.data[8] + c.data[11] + c.data[12] + c.data[13] + c.data[16] + c.data[17] + c.data[18];
|
||||
sum_3x3_subclusters[5] = c.data[7] + c.data[8] + c.data[9] + c.data[12] + c.data[13] + c.data[14] + c.data[17] + c.data[18] + c.data[19];
|
||||
sum_3x3_subclusters[6] = c.data[10] + c.data[11] + c.data[12] + c.data[15] + c.data[16] + c.data[17] + c.data[20] + c.data[21] + c.data[22];
|
||||
sum_3x3_subclusters[7] = c.data[11] + c.data[12] + c.data[13] + c.data[16] + c.data[17] + c.data[18] + c.data[21] + c.data[22] + c.data[23];
|
||||
sum_3x3_subclusters[8] = c.data[12] + c.data[13] + c.data[14] + c.data[17] + c.data[18] + c.data[19] + c.data[22] + c.data[23] + c.data[24];
|
||||
|
||||
auto index = std::max_element(sum_3x3_subclusters.begin(), sum_3x3_subclusters.end()) - sum_3x3_subclusters.begin();
|
||||
|
||||
switch (index) {
|
||||
case 0:
|
||||
result.x = c.x - 1;
|
||||
result.y = c.y + 1;
|
||||
result.data = {c.data[0], c.data[1], c.data[2], c.data[5], c.data[6], c.data[7], c.data[10], c.data[11], c.data[12]};
|
||||
break;
|
||||
case 1:
|
||||
result.x = c.x;
|
||||
result.y = c.y + 1;
|
||||
result.data = {c.data[1], c.data[2], c.data[3], c.data[6], c.data[7], c.data[8], c.data[11], c.data[12], c.data[13]};
|
||||
break;
|
||||
case 2:
|
||||
result.x = c.x + 1;
|
||||
result.y = c.y + 1;
|
||||
result.data = {c.data[2], c.data[3], c.data[4], c.data[7], c.data[8], c.data[9], c.data[12], c.data[13], c.data[14]};
|
||||
break;
|
||||
case 3:
|
||||
result.x = c.x - 1;
|
||||
result.y = c.y;
|
||||
result.data = {c.data[5], c.data[6], c.data[7], c.data[10], c.data[11], c.data[12], c.data[15], c.data[16], c.data[17]};
|
||||
break;
|
||||
case 4:
|
||||
result.x = c.x + 1;
|
||||
result.y = c.y;
|
||||
result.data = {c.data[6], c.data[7], c.data[8], c.data[11], c.data[12], c.data[13], c.data[16], c.data[17], c.data[18]};
|
||||
break;
|
||||
case 5:
|
||||
result.x = c.x + 1;
|
||||
result.y = c.y;
|
||||
result.data = {c.data[7], c.data[8], c.data[9], c.data[12], c.data[13], c.data[14], c.data[17], c.data[18], c.data[19]};
|
||||
break;
|
||||
case 6:
|
||||
result.x = c.x + 1;
|
||||
result.y = c.y -1;
|
||||
result.data = {c.data[10], c.data[11], c.data[12], c.data[15], c.data[16], c.data[17], c.data[20], c.data[21], c.data[22]};
|
||||
break;
|
||||
case 7:
|
||||
result.x = c.x + 1;
|
||||
result.y = c.y-1;
|
||||
result.data = {c.data[11], c.data[12], c.data[13], c.data[16], c.data[17], c.data[18], c.data[21], c.data[22], c.data[23]};
|
||||
break;
|
||||
case 8:
|
||||
result.x = c.x + 1;
|
||||
result.y = c.y-1;
|
||||
result.data = {c.data[12], c.data[13], c.data[14], c.data[17], c.data[18], c.data[19], c.data[22], c.data[23], c.data[24]};
|
||||
break;
|
||||
}
|
||||
|
||||
// do some stuff
|
||||
return result;
|
||||
}
|
||||
|
||||
// Type Traits for is_cluster_type
|
||||
template <typename T>
|
||||
struct is_cluster : std::false_type {}; // Default case: Not a Cluster
|
||||
|
@ -14,7 +14,7 @@
|
||||
namespace aare {
|
||||
|
||||
/*
|
||||
Binary cluster file. Expects data to be laid out as:
|
||||
Binary cluster file. Expects data to be layed 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
|
||||
|
@ -32,8 +32,7 @@ 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
|
||||
@ -123,11 +122,6 @@ 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; }
|
||||
@ -173,22 +167,4 @@ class ClusterVector<Cluster<T, ClusterSizeX, ClusterSizeY, CoordType>>
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
ClusterVector<Cluster<T, 2, 2, uint16_t>> reduce_3x3_to_2x2(const ClusterVector<Cluster<T, 3, 3, uint16_t>> &cv) {
|
||||
ClusterVector<Cluster<T, 2, 2, uint16_t>> result;
|
||||
for (const auto &c : cv) {
|
||||
result.push_back(reduce_3x3_to_2x2(c));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ClusterVector<Cluster<T, 3, 3, uint16_t>> reduce_5x5_to_3x3(const ClusterVector<Cluster<T, 5, 5, uint16_t>> &cv) {
|
||||
ClusterVector<Cluster<T, 3, 3, uint16_t>> result;
|
||||
for (const auto &c : cv) {
|
||||
result.push_back(reduce_5x5_to_3x3(c));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace aare
|
@ -1,81 +0,0 @@
|
||||
#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
|
@ -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 or NumpyFile classes directly. Wraps
|
||||
* fuctions plase use the RawFile, NumpyFile or Hdf5File 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
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include "aare/Dtype.hpp"
|
||||
#include "aare/Frame.hpp"
|
||||
#include "aare/defs.hpp"
|
||||
#include "aare/to_string.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 and RawFile
|
||||
* @note parent class for NumpyFile, RawFile and Hdf5File
|
||||
* @note all functions are pure virtual and must be implemented by the derived
|
||||
* classes
|
||||
*/
|
||||
|
211
include/aare/Hdf5File.hpp
Normal file
211
include/aare/Hdf5File.hpp
Normal file
@ -0,0 +1,211 @@
|
||||
#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
|
135
include/aare/Hdf5MasterFile.hpp
Normal file
135
include/aare/Hdf5MasterFile.hpp
Normal file
@ -0,0 +1,135 @@
|
||||
#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
|
@ -67,13 +67,20 @@ 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>
|
||||
const std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) const {
|
||||
std::enable_if_t<sizeof...(Ix) == Ndim, T &> operator()(Ix... index) const {
|
||||
return buffer_[element_offset(strides_, index...)];
|
||||
}
|
||||
|
||||
@ -85,17 +92,13 @@ 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) { 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]; }
|
||||
T &operator()(ssize_t i) const { return buffer_[i]; }
|
||||
T &operator[](ssize_t i) const { return buffer_[i]; }
|
||||
|
||||
bool operator==(const NDView &other) const {
|
||||
if (size_ != other.size_)
|
||||
return false;
|
||||
if (shape_ != other.shape_)
|
||||
return false;
|
||||
for (size_t i = 0; i != size_; ++i) {
|
||||
for (uint64_t i = 0; i != size_; ++i) {
|
||||
if (buffer_[i] != other.buffer_[i])
|
||||
return false;
|
||||
}
|
||||
@ -154,7 +157,6 @@ 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:
|
||||
@ -178,7 +180,6 @@ 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) {
|
||||
|
@ -1,19 +1,27 @@
|
||||
#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.
|
||||
@ -21,12 +29,11 @@ namespace aare {
|
||||
* 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:
|
||||
@ -60,21 +67,13 @@ 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
|
||||
@ -92,7 +91,15 @@ 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
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "aare/defs.hpp"
|
||||
#include <algorithm>
|
||||
#include "aare/scan_parameters.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fmt/format.h>
|
||||
#include <fstream>
|
||||
@ -40,28 +41,6 @@ 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
|
||||
@ -78,10 +57,8 @@ 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
|
||||
@ -99,6 +76,7 @@ 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;
|
||||
|
||||
@ -117,18 +95,17 @@ 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;
|
||||
|
||||
@ -137,7 +114,6 @@ class RawMasterFile {
|
||||
private:
|
||||
void parse_json(const std::filesystem::path &fpath);
|
||||
void parse_raw(const std::filesystem::path &fpath);
|
||||
void retrieve_geometry();
|
||||
};
|
||||
|
||||
} // namespace aare
|
@ -1,11 +1,15 @@
|
||||
#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>
|
||||
@ -174,6 +178,35 @@ 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{};
|
||||
@ -217,17 +250,12 @@ enum class DetectorType {
|
||||
|
||||
enum class TimingMode { Auto, Trigger };
|
||||
enum class FrameDiscardPolicy { NoDiscard, Discard, DiscardPartial };
|
||||
|
||||
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*/);
|
||||
enum class BurstMode {
|
||||
Burst_Interal,
|
||||
Burst_External,
|
||||
Continuous_Internal,
|
||||
Continuous_External
|
||||
};
|
||||
|
||||
using DataTypeVariants = std::variant<uint16_t, uint32_t>;
|
||||
|
||||
|
15
include/aare/geo_helpers.hpp
Normal file
15
include/aare/geo_helpers.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#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
|
51
include/aare/scan_parameters.hpp
Normal file
51
include/aare/scan_parameters.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
#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
|
11
include/aare/string_utils.hpp
Normal file
11
include/aare/string_utils.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace aare {
|
||||
|
||||
std::string RemoveUnit(std::string &str);
|
||||
|
||||
void TrimWhiteSpaces(std::string &s);
|
||||
|
||||
} // namespace aare
|
288
include/aare/to_string.hpp
Normal file
288
include/aare/to_string.hpp
Normal file
@ -0,0 +1,288 @@
|
||||
#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
|
72
include/aare/type_traits.hpp
Normal file
72
include/aare/type_traits.hpp
Normal file
@ -0,0 +1,72 @@
|
||||
#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
|
@ -1,8 +1,16 @@
|
||||
|
||||
# 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'
|
||||
@ -66,22 +74,11 @@ def ClusterFileSink(clusterfindermt, cluster_file, dtype=np.int32):
|
||||
return cls(clusterfindermt, cluster_file)
|
||||
|
||||
|
||||
def ClusterFile(fname, cluster_size=(3,3), dtype=np.int32, chunk_size = 1000):
|
||||
def ClusterFile(fname, cluster_size=(3,3), dtype=np.int32):
|
||||
"""
|
||||
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, chunk_size=chunk_size)
|
||||
return cls(fname)
|
||||
|
66
python/aare/Hdf5File.py
Normal file
66
python/aare/Hdf5File.py
Normal file
@ -0,0 +1,66 @@
|
||||
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
|
@ -2,7 +2,7 @@
|
||||
from . import _aare
|
||||
|
||||
|
||||
from ._aare import File, RawMasterFile, RawSubFile, JungfrauDataFile
|
||||
from ._aare import File, RawMasterFile, RawSubFile, Hdf5MasterFile, JungfrauDataFile
|
||||
from ._aare import Pedestal_d, Pedestal_f, ClusterFinder_Cluster3x3i, VarClusterFinder
|
||||
from ._aare import DetectorType
|
||||
from ._aare import hitmap
|
||||
@ -23,6 +23,7 @@ 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
|
||||
|
@ -38,20 +38,19 @@ void define_ClusterFile(py::module &m, const std::string &typestr) {
|
||||
self.read_clusters(n_clusters));
|
||||
return v;
|
||||
},
|
||||
py::return_value_policy::take_ownership, py::arg("n_clusters"))
|
||||
py::return_value_policy::take_ownership)
|
||||
.def("read_frame",
|
||||
[](ClusterFile<ClusterType> &self) {
|
||||
auto v = new ClusterVector<ClusterType>(self.read_frame());
|
||||
return v;
|
||||
})
|
||||
.def("set_roi", &ClusterFile<ClusterType>::set_roi,
|
||||
py::arg("roi"))
|
||||
.def("set_roi", &ClusterFile<ClusterType>::set_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) {
|
||||
|
@ -104,14 +104,4 @@ void define_ClusterVector(py::module &m, const std::string &typestr) {
|
||||
});
|
||||
}
|
||||
|
||||
void define_reduction(py::module &m) {
|
||||
m.def("reduce_3x3_to_2x2", [](const ClusterVector<Cluster<int, 3, 3, uint16_t>> &cv) {
|
||||
return new ClusterVector<Cluster<int, 2, 2, uint16_t>>(reduce_3x3_to_2x2(cv));
|
||||
// return new ClusterVector<Cluster<int, 3, 3>>();
|
||||
})
|
||||
.def("reduce_5x5_to_3x3", [](const ClusterVector<Cluster<int, 5, 5, uint16_t>> &cv) {
|
||||
return new ClusterVector<Cluster<int, 3, 3, uint16_t>>(reduce_5x5_to_3x3(cv));
|
||||
});
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
@ -5,6 +5,11 @@
|
||||
#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"
|
||||
|
||||
|
106
python/src/hdf5_file.hpp
Normal file
106
python/src/hdf5_file.hpp
Normal file
@ -0,0 +1,106 @@
|
||||
#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);
|
||||
}
|
86
python/src/hdf5_master_file.hpp
Normal file
86
python/src/hdf5_master_file.hpp
Normal file
@ -0,0 +1,86 @@
|
||||
|
||||
#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);
|
||||
}
|
@ -14,6 +14,10 @@
|
||||
#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"
|
||||
@ -54,6 +58,10 @@ 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");
|
||||
@ -81,8 +89,4 @@ PYBIND11_MODULE(_aare, m) {
|
||||
DEFINE_CLUSTER_BINDINGS(int, 9, 9, uint16_t, i);
|
||||
DEFINE_CLUSTER_BINDINGS(double, 9, 9, uint16_t, d);
|
||||
DEFINE_CLUSTER_BINDINGS(float, 9, 9, uint16_t, f);
|
||||
|
||||
|
||||
define_reduction(m);
|
||||
|
||||
}
|
||||
|
@ -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_in_roi(), n_frames});
|
||||
{self.n_modules(), n_frames});
|
||||
}
|
||||
// py::array_t<DetectorHeader> header({self.n_mod(), n_frames});
|
||||
|
||||
@ -101,8 +101,7 @@ 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("detector_type", &RawFile::detector_type)
|
||||
.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);
|
||||
.def_property_readonly("detector_type", &RawFile::detector_type)
|
||||
.def_property_readonly("master", &RawFile::master);
|
||||
}
|
@ -57,8 +57,6 @@ 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
|
||||
|
@ -6,20 +6,20 @@ import pytest
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption(
|
||||
"--with-data", action="store_true", default=False, help="Run tests that require additional data"
|
||||
"--files", action="store_true", default=False, help="run slow tests"
|
||||
)
|
||||
|
||||
|
||||
def pytest_configure(config):
|
||||
config.addinivalue_line("markers", "withdata: mark test as needing image files to run")
|
||||
config.addinivalue_line("markers", "files: mark test as needing image files to run")
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
if config.getoption("--with-data"):
|
||||
if config.getoption("--files"):
|
||||
return
|
||||
skip = pytest.mark.skip(reason="need --with-data option to run")
|
||||
skip = pytest.mark.skip(reason="need --files option to run")
|
||||
for item in items:
|
||||
if "withdata" in item.keywords:
|
||||
if "files" in item.keywords:
|
||||
item.add_marker(skip)
|
||||
|
||||
|
||||
|
@ -9,7 +9,7 @@ import pickle
|
||||
from aare import ClusterFile
|
||||
from conftest import test_data_path
|
||||
|
||||
@pytest.mark.withdata
|
||||
@pytest.mark.files
|
||||
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.withdata
|
||||
@pytest.mark.files
|
||||
def test_read_clusters_and_fill_histogram(test_data_path):
|
||||
# Create the histogram
|
||||
n_bins = 100
|
||||
|
@ -1,73 +0,0 @@
|
||||
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()
|
@ -3,7 +3,7 @@ import numpy as np
|
||||
from aare import RawSubFile, DetectorType
|
||||
|
||||
|
||||
@pytest.mark.withdata
|
||||
@pytest.mark.files
|
||||
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.withdata
|
||||
@pytest.mark.files
|
||||
def test_iterate_over_a_jungfrau_RawSubFile(test_data_path):
|
||||
|
||||
data = np.load(test_data_path / "raw/jungfrau/jungfrau_single_0.npy")
|
||||
|
@ -2,7 +2,7 @@ import pytest
|
||||
import numpy as np
|
||||
from aare import JungfrauDataFile
|
||||
|
||||
@pytest.mark.withdata
|
||||
@pytest.mark.files
|
||||
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.withdata
|
||||
@pytest.mark.files
|
||||
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.withdata
|
||||
@pytest.mark.files
|
||||
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.withdata
|
||||
@pytest.mark.files
|
||||
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.withdata
|
||||
@pytest.mark.files
|
||||
def test_read_single_chip(test_data_path):
|
||||
|
||||
# Read all frames from the .dat file
|
||||
|
@ -10,7 +10,7 @@ using aare::Cluster;
|
||||
using aare::ClusterFile;
|
||||
using aare::ClusterVector;
|
||||
|
||||
TEST_CASE("Read one frame from a cluster file", "[.with-data]") {
|
||||
TEST_CASE("Read one frame from a cluster file", "[.files]") {
|
||||
// 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", "[.with-data]") {
|
||||
std::begin(expected_cluster_data)));
|
||||
}
|
||||
|
||||
TEST_CASE("Read one frame using ROI", "[.with-data]") {
|
||||
TEST_CASE("Read one frame using ROI", "[.files]") {
|
||||
// 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", "[.with-data]") {
|
||||
std::begin(expected_cluster_data)));
|
||||
}
|
||||
|
||||
TEST_CASE("Read clusters from single frame file", "[.with-data]") {
|
||||
TEST_CASE("Read clusters from single frame file", "[.files]") {
|
||||
|
||||
// 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", "[.with-data]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Read clusters from single frame file with ROI", "[.with-data]") {
|
||||
TEST_CASE("Read clusters from single frame file with ROI", "[.files]") {
|
||||
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", "[.with-data]") {
|
||||
std::begin(expected_cluster_data)));
|
||||
}
|
||||
|
||||
TEST_CASE("Read cluster from multiple frame file", "[.with-data]") {
|
||||
TEST_CASE("Read cluster from multiple frame file", "[.files]") {
|
||||
|
||||
using ClusterType = Cluster<double, 2, 2>;
|
||||
|
||||
@ -279,7 +279,7 @@ TEST_CASE("Read cluster from multiple frame file", "[.with-data]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Write cluster with potential padding", "[.with-data][.ClusterFile]") {
|
||||
TEST_CASE("Write cluster with potential padding", "[.files][.ClusterFile]") {
|
||||
|
||||
using ClusterType = Cluster<double, 3, 3>;
|
||||
|
||||
@ -324,7 +324,7 @@ TEST_CASE("Write cluster with potential padding", "[.with-data][.ClusterFile]")
|
||||
}));
|
||||
}
|
||||
|
||||
TEST_CASE("Read frame and modify cluster data", "[.with-data][.ClusterFile]") {
|
||||
TEST_CASE("Read frame and modify cluster data", "[.files][.ClusterFile]") {
|
||||
auto fpath = test_data_path() / "clust" / "single_frame_97_clustrers.clust";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
|
||||
|
@ -57,11 +57,9 @@ class ClusterFinderMTWrapper
|
||||
size_t m_sink_size() const { return this->m_sink.sizeGuess(); }
|
||||
};
|
||||
|
||||
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));
|
||||
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";
|
||||
|
||||
File file(fpath);
|
||||
|
||||
|
@ -7,21 +7,9 @@
|
||||
|
||||
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));
|
||||
|
||||
@ -55,7 +43,8 @@ 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") {
|
||||
TEST_CASE("ClusterVector 2x2 int32_t capacity 4, push back then read",
|
||||
"[.ClusterVector]") {
|
||||
|
||||
ClusterVector<Cluster<int32_t, 2, 2>> cv(4);
|
||||
REQUIRE(cv.capacity() == 4);
|
||||
@ -81,7 +70,7 @@ TEST_CASE("ClusterVector 2x2 int32_t capacity 4, push back then read") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Summing 3x1 clusters of int64") {
|
||||
TEST_CASE("Summing 3x1 clusters of int64", "[.ClusterVector]") {
|
||||
ClusterVector<Cluster<int32_t, 3, 1>> cv(2);
|
||||
REQUIRE(cv.capacity() == 2);
|
||||
REQUIRE(cv.size() == 0);
|
||||
@ -113,7 +102,7 @@ TEST_CASE("Summing 3x1 clusters of int64") {
|
||||
*/
|
||||
}
|
||||
|
||||
TEST_CASE("Storing floats") {
|
||||
TEST_CASE("Storing floats", "[.ClusterVector]") {
|
||||
ClusterVector<Cluster<float, 2, 4>> cv(10);
|
||||
REQUIRE(cv.capacity() == 10);
|
||||
REQUIRE(cv.size() == 0);
|
||||
@ -140,7 +129,7 @@ TEST_CASE("Storing floats") {
|
||||
*/
|
||||
}
|
||||
|
||||
TEST_CASE("Push back more than initial capacity") {
|
||||
TEST_CASE("Push back more than initial capacity", "[.ClusterVector]") {
|
||||
|
||||
ClusterVector<Cluster<int32_t, 2, 2>> cv(2);
|
||||
auto initial_data = cv.data();
|
||||
@ -173,7 +162,8 @@ TEST_CASE("Push back more than initial capacity") {
|
||||
REQUIRE(initial_data != cv.data());
|
||||
}
|
||||
|
||||
TEST_CASE("Concatenate two cluster vectors where the first has enough capacity") {
|
||||
TEST_CASE("Concatenate two cluster vectors where the first has enough capacity",
|
||||
"[.ClusterVector]") {
|
||||
ClusterVector<Cluster<int32_t, 2, 2>> cv1(12);
|
||||
Cluster<int32_t, 2, 2> c1 = {1, 2, {3, 4, 5, 6}};
|
||||
cv1.push_back(c1);
|
||||
@ -202,7 +192,8 @@ 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") {
|
||||
TEST_CASE("Concatenate two cluster vectors where we need to allocate",
|
||||
"[.ClusterVector]") {
|
||||
ClusterVector<Cluster<int32_t, 2, 2>> cv1(2);
|
||||
Cluster<int32_t, 2, 2> c1 = {1, 2, {3, 4, 5, 6}};
|
||||
cv1.push_back(c1);
|
||||
@ -238,7 +229,7 @@ struct ClusterTestData {
|
||||
std::vector<int64_t> index_map_y;
|
||||
};
|
||||
|
||||
TEST_CASE("Gain Map Calculation Index Map") {
|
||||
TEST_CASE("Gain Map Calculation Index Map", "[.ClusterVector][.gain_map]") {
|
||||
|
||||
auto clustertestdata = GENERATE(
|
||||
ClusterTestData{3,
|
||||
|
@ -1,151 +0,0 @@
|
||||
|
||||
#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
|
@ -1,194 +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/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);
|
||||
}
|
15
src/File.cpp
15
src/File.cpp
@ -1,4 +1,7 @@
|
||||
#include "aare/File.hpp"
|
||||
#ifdef HDF5_FOUND
|
||||
#include "aare/Hdf5File.hpp"
|
||||
#endif
|
||||
#include "aare/JungfrauDataFile.hpp"
|
||||
#include "aare/NumpyFile.hpp"
|
||||
#include "aare/RawFile.hpp"
|
||||
@ -27,7 +30,17 @@ 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);
|
||||
} else if (fname.extension() == ".dat") {
|
||||
}
|
||||
#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") {
|
||||
file_impl = std::make_unique<JungfrauDataFile>(fname);
|
||||
} else {
|
||||
throw std::runtime_error("Unsupported file type");
|
||||
|
234
src/Hdf5File.cpp
Normal file
234
src/Hdf5File.cpp
Normal file
@ -0,0 +1,234 @@
|
||||
#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
|
0
src/Hdf5File.test.cpp
Normal file
0
src/Hdf5File.test.cpp
Normal file
565
src/Hdf5MasterFile.cpp
Normal file
565
src/Hdf5MasterFile.cpp
Normal file
@ -0,0 +1,565 @@
|
||||
#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
|
168
src/Hdf5MasterFile.test.cpp
Normal file
168
src/Hdf5MasterFile.test.cpp
Normal file
@ -0,0 +1,168 @@
|
||||
#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);
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
|
||||
using aare::JungfrauDataFile;
|
||||
using aare::JungfrauDataHeader;
|
||||
TEST_CASE("Open a Jungfrau data file", "[.with-data]") {
|
||||
TEST_CASE("Open a Jungfrau data file", "[.files]") {
|
||||
// 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", "[.with-data]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Seek in a JungfrauDataFile", "[.with-data]") {
|
||||
TEST_CASE("Seek in a JungfrauDataFile", "[.files]") {
|
||||
auto fpath = test_data_path() / "dat" / "AldoJF65k_000000.dat";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
|
||||
@ -70,7 +70,7 @@ TEST_CASE("Seek in a JungfrauDataFile", "[.with-data]") {
|
||||
REQUIRE_THROWS(f.seek(86356)); // out of range
|
||||
}
|
||||
|
||||
TEST_CASE("Open a Jungfrau data file with non zero file index", "[.with-data]") {
|
||||
TEST_CASE("Open a Jungfrau data file with non zero file index", "[.files]") {
|
||||
|
||||
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", "[.with-data]")
|
||||
REQUIRE(f.current_file().stem() == "AldoJF65k_000003");
|
||||
}
|
||||
|
||||
TEST_CASE("Read into throws if size doesn't match", "[.with-data]") {
|
||||
TEST_CASE("Read into throws if size doesn't match", "[.files]") {
|
||||
auto fpath = test_data_path() / "dat" / "AldoJF65k_000000.dat";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
|
||||
|
@ -21,57 +21,6 @@ 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);
|
||||
@ -107,7 +56,7 @@ TEST_CASE("Element reference 3D") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Plus and minus with single value") {
|
||||
TEST_CASE("Plus and miuns 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});
|
||||
@ -188,9 +137,16 @@ 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 NDView") {
|
||||
TEST_CASE("divide with another span") {
|
||||
std::vector<int> vec0{9, 12, 3};
|
||||
std::vector<int> vec1{3, 2, 1};
|
||||
std::vector<int> result{3, 6, 3};
|
||||
@ -227,30 +183,6 @@ 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);
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
using aare::Dtype;
|
||||
using aare::NumpyFile;
|
||||
TEST_CASE("Read a 1D numpy file with int32 data type", "[.with-data]") {
|
||||
TEST_CASE("Read a 1D numpy file with int32 data type", "[.integration]") {
|
||||
|
||||
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", "[.with-data]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Read a 3D numpy file with np.double data type", "[.with-data]") {
|
||||
TEST_CASE("Read a 3D numpy file with np.double data type", "[.integration]") {
|
||||
|
||||
auto fpath = test_data_path() / "numpy" / "test_3d_double.npy";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
|
130
src/RawFile.cpp
130
src/RawFile.cpp
@ -1,8 +1,8 @@
|
||||
#include "aare/RawFile.hpp"
|
||||
#include "aare/DetectorGeometry.hpp"
|
||||
#include "aare/PixelMap.hpp"
|
||||
#include "aare/algorithm.hpp"
|
||||
#include "aare/defs.hpp"
|
||||
#include "aare/geo_helpers.hpp"
|
||||
#include "aare/logger.hpp"
|
||||
|
||||
#include <fmt/format.h>
|
||||
@ -13,14 +13,13 @@ using json = nlohmann::json;
|
||||
namespace aare {
|
||||
|
||||
RawFile::RawFile(const std::filesystem::path &fname, const std::string &mode)
|
||||
: m_master(fname),
|
||||
m_geometry(m_master.geometry(), m_master.pixels_x(), m_master.pixels_y(),
|
||||
m_master.udp_interfaces_per_module(), m_master.quad()) {
|
||||
: m_master(fname) {
|
||||
m_mode = mode;
|
||||
|
||||
if (mode == "r") {
|
||||
find_geometry();
|
||||
if (m_master.roi()) {
|
||||
m_geometry.update_geometry_with_roi(m_master.roi().value());
|
||||
m_geometry =
|
||||
update_geometry_with_roi(m_geometry, m_master.roi().value());
|
||||
}
|
||||
open_subfiles();
|
||||
} else {
|
||||
@ -62,25 +61,26 @@ 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 += m_geometry.n_modules_in_roi();
|
||||
header += n_modules();
|
||||
}
|
||||
}
|
||||
|
||||
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,23 +92,15 @@ 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() 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();
|
||||
};
|
||||
xy RawFile::geometry() { return m_master.geometry(); }
|
||||
|
||||
void RawFile::open_subfiles() {
|
||||
if (m_mode == "r")
|
||||
for (size_t i : m_geometry.get_modules_in_roi()) {
|
||||
auto pos = m_geometry.get_module_geometries(i);
|
||||
for (size_t i = 0; i != n_modules(); ++i) {
|
||||
auto pos = m_geometry.module_pixel_0[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));
|
||||
@ -138,8 +130,45 @@ 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);
|
||||
@ -154,15 +183,13 @@ 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(m_geometry.n_modules_in_roi());
|
||||
std::vector<size_t> frame_indices(m_geometry.n_modules_in_roi(),
|
||||
frame_index);
|
||||
std::vector<size_t> frame_numbers(n_modules());
|
||||
std::vector<size_t> frame_indices(n_modules(), frame_index);
|
||||
|
||||
// sync the frame numbers
|
||||
|
||||
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) {
|
||||
if (n_modules() != 1) { // if we have more than one module
|
||||
for (size_t part_idx = 0; part_idx != n_modules(); ++part_idx) {
|
||||
frame_numbers[part_idx] =
|
||||
m_subfiles[part_idx]->frame_number(frame_index);
|
||||
}
|
||||
@ -192,32 +219,19 @@ 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 != m_geometry.n_modules_in_roi();
|
||||
++part_idx) {
|
||||
for (size_t part_idx = 0; part_idx != n_modules(); ++part_idx) {
|
||||
auto corrected_idx = frame_indices[part_idx];
|
||||
|
||||
// This is where we start writing
|
||||
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) *
|
||||
auto offset = (m_geometry.module_pixel_0[part_idx].origin_y *
|
||||
m_geometry.pixels_x +
|
||||
m_geometry.module_pixel_0[part_idx].origin_x) *
|
||||
m_master.bitdepth() / 8;
|
||||
|
||||
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
|
||||
if (m_geometry.module_pixel_0[part_idx].origin_x != 0)
|
||||
throw std::runtime_error(LOCATION +
|
||||
" Implementation error. x pos not 0.");
|
||||
|
||||
// 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);
|
||||
@ -235,13 +249,11 @@ 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 != m_geometry.n_modules_in_roi();
|
||||
++part_idx) {
|
||||
auto pos = m_geometry.get_module_geometries(
|
||||
m_geometry.get_modules_in_roi(part_idx));
|
||||
for (size_t part_idx = 0; part_idx != n_modules(); ++part_idx) {
|
||||
auto pos = m_geometry.module_pixel_0[part_idx];
|
||||
auto corrected_idx = frame_indices[part_idx];
|
||||
|
||||
m_subfiles[part_idx]->seek(corrected_idx);
|
||||
@ -254,7 +266,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 +
|
||||
|
@ -3,29 +3,25 @@
|
||||
#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", "[.with-data]") {
|
||||
TEST_CASE("Read number of frames from a jungfrau raw file", "[.integration]") {
|
||||
|
||||
auto fpath =
|
||||
test_data_path() / "raw/jungfrau/jungfrau_single_master_0.json";
|
||||
test_data_path() / "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", "[.with-data]") {
|
||||
TEST_CASE("Read frame numbers from a jungfrau raw file", "[.integration]") {
|
||||
auto fpath =
|
||||
test_data_path() / "raw/jungfrau/jungfrau_single_master_0.json";
|
||||
test_data_path() / "jungfrau" / "jungfrau_single_master_0.json";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
|
||||
File f(fpath, "r");
|
||||
@ -40,9 +36,9 @@ TEST_CASE("Read frame numbers from a jungfrau raw file", "[.with-data]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Read a frame number too high throws", "[.with-data]") {
|
||||
TEST_CASE("Read a frame number too high throws", "[.integration]") {
|
||||
auto fpath =
|
||||
test_data_path() / "raw/jungfrau/jungfrau_single_master_0.json";
|
||||
test_data_path() / "jungfrau" / "jungfrau_single_master_0.json";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
|
||||
File f(fpath, "r");
|
||||
@ -56,10 +52,9 @@ TEST_CASE("Read a frame number too high throws", "[.with-data]") {
|
||||
}
|
||||
|
||||
TEST_CASE("Read a frame numbers where the subfile is missing throws",
|
||||
"[.with-data]") {
|
||||
auto fpath = test_data_path() / "raw/jungfrau" /
|
||||
"[.integration]") {
|
||||
auto fpath = test_data_path() / "jungfrau" /
|
||||
"jungfrau_missing_subfile_master_0.json";
|
||||
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
|
||||
File f(fpath, "r");
|
||||
@ -79,9 +74,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",
|
||||
"[.with-data]") {
|
||||
"[.integration]") {
|
||||
auto fpath =
|
||||
test_data_path() / "raw/jungfrau/jungfrau_single_master_0.json";
|
||||
test_data_path() / "jungfrau" / "jungfrau_single_master_0.json";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
|
||||
File f(fpath, "r");
|
||||
@ -98,8 +93,8 @@ TEST_CASE("Read data from a jungfrau 500k single port raw file",
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Read frame numbers from a raw file", "[.with-data]") {
|
||||
auto fpath = test_data_path() / "raw/eiger" / "eiger_500k_16bit_master_0.json";
|
||||
TEST_CASE("Read frame numbers from a raw file", "[.integration]") {
|
||||
auto fpath = test_data_path() / "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
|
||||
@ -111,72 +106,32 @@ TEST_CASE("Read frame numbers from a raw file", "[.with-data]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Compare reading from a numpy file with a raw file", "[.with-data]") {
|
||||
TEST_CASE("Compare reading from a numpy file with a raw file", "[.files]") {
|
||||
auto fpath_raw =
|
||||
test_data_path() / "raw/jungfrau" / "jungfrau_single_master_0.json";
|
||||
REQUIRE(std::filesystem::exists(fpath_raw));
|
||||
|
||||
SECTION("jungfrau data") {
|
||||
auto fpath_raw =
|
||||
test_data_path() / "raw/jungfrau" / "jungfrau_single_master_0.json";
|
||||
REQUIRE(std::filesystem::exists(fpath_raw));
|
||||
auto fpath_npy =
|
||||
test_data_path() / "raw/jungfrau" / "jungfrau_single_0.npy";
|
||||
REQUIRE(std::filesystem::exists(fpath_npy));
|
||||
|
||||
auto fpath_npy =
|
||||
test_data_path() / "raw/jungfrau" / "jungfrau_single_0.npy";
|
||||
REQUIRE(std::filesystem::exists(fpath_npy));
|
||||
File raw(fpath_raw, "r");
|
||||
File npy(fpath_npy, "r");
|
||||
|
||||
File raw(fpath_raw, "r");
|
||||
File npy(fpath_npy, "r");
|
||||
CHECK(raw.total_frames() == 10);
|
||||
CHECK(npy.total_frames() == 10);
|
||||
|
||||
CHECK(raw.total_frames() == 10);
|
||||
CHECK(npy.total_frames() == 10);
|
||||
|
||||
for (size_t i = 0; i < 10; ++i) {
|
||||
CHECK(raw.tell() == i);
|
||||
auto raw_frame = raw.read_frame();
|
||||
auto npy_frame = npy.read_frame();
|
||||
CHECK((raw_frame.view<uint16_t>() == npy_frame.view<uint16_t>()));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
for (size_t i = 0; i < 10; ++i) {
|
||||
CHECK(raw.tell() == i);
|
||||
auto raw_frame = raw.read_frame();
|
||||
|
||||
auto npy_frame = npy.read_frame();
|
||||
CHECK((raw_frame.view<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>()));
|
||||
CHECK((raw_frame.view<uint16_t>() == npy_frame.view<uint16_t>()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Read multipart files", "[.with-data]") {
|
||||
TEST_CASE("Read multipart files", "[.integration]") {
|
||||
auto fpath =
|
||||
test_data_path() / "raw/jungfrau" / "jungfrau_double_master_0.json";
|
||||
test_data_path() / "jungfrau" / "jungfrau_double_master_0.json";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
|
||||
File f(fpath, "r");
|
||||
@ -205,117 +160,9 @@ TEST_CASE("Read multipart files", "[.with-data]") {
|
||||
}
|
||||
}
|
||||
|
||||
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]") {
|
||||
TEST_CASE("Read file with unordered frames", "[.integration]") {
|
||||
// TODO! Better explanation and error message
|
||||
auto fpath = test_data_path() / "raw/mythen/scan242_master_3.raw";
|
||||
auto fpath = test_data_path() / "mythen" / "scan242_master_3.raw";
|
||||
REQUIRE(std::filesystem::exists(fpath));
|
||||
File f(fpath);
|
||||
REQUIRE_THROWS((f.read_frame()));
|
||||
|
@ -1,8 +1,6 @@
|
||||
#include "aare/RawMasterFile.hpp"
|
||||
#include "aare/RawFile.hpp"
|
||||
#include "aare/logger.hpp"
|
||||
#include "aare/to_string.hpp"
|
||||
#include <sstream>
|
||||
|
||||
namespace aare {
|
||||
|
||||
RawFileNameComponents::RawFileNameComponents(
|
||||
@ -64,32 +62,6 @@ 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)) {
|
||||
@ -141,11 +113,7 @@ size_t RawMasterFile::n_modules() const {
|
||||
return m_geometry.row * m_geometry.col;
|
||||
}
|
||||
|
||||
xy RawMasterFile::udp_interfaces_per_module() const {
|
||||
return m_udp_interfaces_per_module;
|
||||
}
|
||||
|
||||
uint8_t RawMasterFile::quad() const { return m_quad; }
|
||||
std::optional<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
|
||||
@ -176,10 +144,7 @@ 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"]}; // TODO: isnt it only available for version > 7.1?
|
||||
// - try block default should be 1x1
|
||||
m_geometry = {j["Geometry"]["y"], j["Geometry"]["x"]};
|
||||
|
||||
m_image_size_in_bytes = j["Image Size in bytes"];
|
||||
m_frames_in_file = j["Frames in File"];
|
||||
@ -268,15 +233,6 @@ 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;
|
||||
@ -291,14 +247,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++; // why is it updated
|
||||
tmp_roi.xmax++;
|
||||
tmp_roi.ymax++;
|
||||
}
|
||||
|
||||
m_roi = tmp_roi;
|
||||
}
|
||||
|
||||
} catch (const json::out_of_range &e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
// leave the optional empty
|
||||
}
|
||||
|
||||
@ -408,54 +364,21 @@ 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) {
|
||||
retrieve_geometry();
|
||||
LOG(TLogLevel::logWARNING)
|
||||
<< "No geometry found in master file. Retrieved geometry of "
|
||||
<< m_geometry.row << " x " << m_geometry.col << "\n ";
|
||||
m_geometry = {1, 1};
|
||||
fmt::print("Warning: No geometry found in master file. Assuming 1x1\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
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
#include "test_config.hpp"
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using namespace aare;
|
||||
|
||||
@ -47,25 +46,6 @@ 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";
|
||||
@ -146,19 +126,6 @@ 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 =
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
using namespace aare;
|
||||
|
||||
TEST_CASE("Read frames directly from a RawSubFile", "[.with-data]") {
|
||||
TEST_CASE("Read frames directly from a RawSubFile", "[.files]") {
|
||||
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", "[.with-data]") {
|
||||
}
|
||||
|
||||
TEST_CASE("Read frames directly from a RawSubFile starting at the second file",
|
||||
"[.with-data]") {
|
||||
"[.files]") {
|
||||
// we know this file has 10 frames with frame numbers 1 to 10
|
||||
// f0 1,2,3
|
||||
// f1 4,5,6 <-- starting here
|
||||
|
107
src/defs.cpp
107
src/defs.cpp
@ -1,6 +1,4 @@
|
||||
#include "aare/defs.hpp"
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <fmt/core.h>
|
||||
namespace aare {
|
||||
@ -10,109 +8,4 @@ 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
|
@ -1,54 +1,6 @@
|
||||
#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
|
||||
|
72
src/geo_helpers.cpp
Normal file
72
src/geo_helpers.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
|
||||
#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
|
228
src/geo_helpers.test.cpp
Normal file
228
src/geo_helpers.test.cpp
Normal file
@ -0,0 +1,228 @@
|
||||
#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);
|
||||
}
|
26
src/scan_parameters.test.cpp
Normal file
26
src/scan_parameters.test.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#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);
|
||||
}
|
||||
|
30
src/string_utils.cpp
Normal file
30
src/string_utils.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#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
|
226
src/to_string.cpp
Normal file
226
src/to_string.cpp
Normal file
@ -0,0 +1,226 @@
|
||||
#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
|
224
src/to_string.test.cpp
Normal file
224
src/to_string.test.cpp
Normal file
@ -0,0 +1,224 @@
|
||||
#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]");
|
||||
}
|
||||
}*/
|
@ -40,8 +40,5 @@ 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_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
|
||||
|
||||
target_include_directories(tests PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
|
@ -1,4 +0,0 @@
|
||||
#define FRIEND_TEST(test_name) friend void test_name##_impl();
|
||||
|
||||
#define TEST_CASE_PRIVATE_FWD(test_name) \
|
||||
void test_name##_impl(); // foward declaration
|
@ -1,20 +0,0 @@
|
||||
#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()
|
Reference in New Issue
Block a user