218
.gitlab-ci.yml
Normal file
218
.gitlab-ci.yml
Normal file
@@ -0,0 +1,218 @@
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
- synthesis
|
||||
|
||||
build:x86:gcc:
|
||||
stage: build
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
tags:
|
||||
- gcc
|
||||
- x86
|
||||
needs: []
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake3 -DCMAKE_BUILD_TYPE=Release ..
|
||||
- make -j48 jfjoch
|
||||
|
||||
build:x86:icpc:
|
||||
stage: build
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CC: icx
|
||||
CXX: icpx
|
||||
tags:
|
||||
- oneapi
|
||||
- x86
|
||||
needs: []
|
||||
script:
|
||||
- source /opt/grpc/grpc.sh
|
||||
- source /opt/intel/oneapi/setvars.sh
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake3 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON ..
|
||||
- make -j48 jfjoch
|
||||
|
||||
build:x86:aocc:
|
||||
stage: build
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
tags:
|
||||
- aocc
|
||||
- x86
|
||||
needs: []
|
||||
script:
|
||||
- source /opt/grpc/grpc.sh
|
||||
- source /opt/AMD/aocc-compiler-4.0.0/setenv_AOCC.sh
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake3 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON ..
|
||||
- make -j48 jfjoch
|
||||
|
||||
build:x86:vitis_hls:
|
||||
stage: build
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
tags:
|
||||
- x86
|
||||
needs: []
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "push"'
|
||||
changes:
|
||||
- receiver/hls/*
|
||||
- receiver/hdl/*
|
||||
- receiver/scripts/*
|
||||
- receiver/xdc/*
|
||||
- receiver/microblaze/*
|
||||
- common/Definitions.h
|
||||
script:
|
||||
- source /opt/Xilinx/Vitis_HLS/2022.1/settings64.sh
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake3 ..
|
||||
- make hls
|
||||
|
||||
test:x86:gcc:
|
||||
stage: test
|
||||
timeout: 90m
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
needs: ["build:x86:gcc"]
|
||||
dependencies: []
|
||||
tags:
|
||||
- gcc
|
||||
- x86
|
||||
- ib
|
||||
script:
|
||||
- mkdir -p build
|
||||
- cd build
|
||||
- cmake3 -DCMAKE_BUILD_TYPE=Release -DJFJOCH_COMPILE_TESTS=ON ..
|
||||
- make -j48 CatchTest CompressionBenchmark HDF5DatasetWriteTest DataAnalysisPerfTest JFCalibrationPerfTest
|
||||
- cd tests
|
||||
- ./CatchTest -r junit -o report.xml
|
||||
- cd ../tools
|
||||
- ./HDF5DatasetWriteTest ../../tests/test_data/compression_benchmark.h5
|
||||
- numactl -m 0 -N 0 ./CompressionBenchmark ../../tests/test_data/compression_benchmark.h5
|
||||
- numactl -m 0 -N 0 ./DataAnalysisPerfTest ../../tests/test_data/compression_benchmark.h5
|
||||
- numactl -m 0 -N 0 ./JFCalibrationPerfTest
|
||||
artifacts:
|
||||
expire_in: 1 week
|
||||
reports:
|
||||
junit: build/tests/report.xml
|
||||
|
||||
test:x86:crystfel:
|
||||
stage: test
|
||||
timeout: 90m
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
needs: ["build:x86:gcc"]
|
||||
dependencies: []
|
||||
tags:
|
||||
- gcc
|
||||
- x86
|
||||
- crystfel
|
||||
script:
|
||||
- source /opt/grpc/grpc.sh
|
||||
- mkdir -p build
|
||||
- cd build
|
||||
- cmake3 -DCMAKE_BUILD_TYPE=Release -DgRPC_FROM_SYSTEM=1 -DJFJOCH_COMPILE_TESTS=ON ..
|
||||
- make -j8 HDF5DatasetWriteTest
|
||||
- cd ../tests/crystfel
|
||||
- HDF5DATASET_WRITE_TEST_IMAGES_PER_FILE=0 ../../build/tools/HDF5DatasetWriteTest ../../tests/test_data/compression_benchmark.h5 10
|
||||
- indexamajig -i writing_test.lst -g jf4m.geom -o x.stream --indexing=xgandalf
|
||||
|
||||
test:x86:xds_durin:
|
||||
stage: test
|
||||
timeout: 90m
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
needs: ["build:x86:gcc"]
|
||||
dependencies: []
|
||||
tags:
|
||||
- gcc
|
||||
- x86
|
||||
- xds
|
||||
script:
|
||||
- source /opt/rh/devtoolset-10/enable
|
||||
- source /opt/grpc/grpc.sh
|
||||
- mkdir -p build
|
||||
- cd build
|
||||
- cmake3 -DCMAKE_BUILD_TYPE=Release -DgRPC_FROM_SYSTEM=1 -DJFJOCH_COMPILE_TESTS=ON ..
|
||||
- make -j8 HDF5DatasetWriteTest
|
||||
- cd ../tests/xds_durin
|
||||
- HDF5DATASET_WRITE_TEST_IMAGES_PER_FILE=0 ../../build/tools/HDF5DatasetWriteTest ../../tests/test_data/compression_benchmark.h5 100
|
||||
- xds_par |grep -a1 ISa |tail -n1
|
||||
|
||||
test:x86:xia2.ssx:
|
||||
stage: test
|
||||
timeout: 90m
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
needs: ["build:x86:gcc"]
|
||||
dependencies: []
|
||||
tags:
|
||||
- gcc
|
||||
- x86
|
||||
- xds
|
||||
script:
|
||||
- source /opt/rh/devtoolset-10/enable
|
||||
- source /opt/grpc/grpc.sh
|
||||
- mkdir -p build
|
||||
- mkdir -p dials_test
|
||||
- cd build
|
||||
- cmake3 -DCMAKE_BUILD_TYPE=Release -DgRPC_FROM_SYSTEM=1 -DJFJOCH_COMPILE_TESTS=ON ..
|
||||
- make -j8 HDF5DatasetWriteTest
|
||||
- cd ../dials_test
|
||||
- ../build/tools/HDF5DatasetWriteTest ../tests/test_data/compression_benchmark.h5 100
|
||||
- source /usr/local/dials-v3-13-0/dials_env.sh
|
||||
- xia2.ssx image=writing_test_master.h5 space_group=P43212 unit_cell=78.551,78.551,36.914,90.000,90.000,90.000
|
||||
|
||||
|
||||
synthesis:vivado_pcie:
|
||||
stage: synthesis
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CC: gcc
|
||||
CXX: g++
|
||||
allow_failure: true
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "push"'
|
||||
changes:
|
||||
- receiver/hls/*
|
||||
- receiver/hdl/*
|
||||
- receiver/scripts/*
|
||||
- receiver/xdc/*
|
||||
- common/Definitions.h
|
||||
tags:
|
||||
- vivado
|
||||
artifacts:
|
||||
paths:
|
||||
- build/receiver/*.mcs
|
||||
- build/receiver/*.bit
|
||||
expire_in: 1 week
|
||||
script:
|
||||
- source /opt/grpc/grpc.sh
|
||||
- source /opt/Xilinx/Vivado/2022.1/settings64.sh
|
||||
- mkdir -p build
|
||||
- cd build
|
||||
- cmake3 ..
|
||||
- make action_pcie
|
||||
needs: ["build:x86:gcc", "build:x86:vitis_hls", "test:x86:gcc"]
|
||||
13
.gitmodules
vendored
Normal file
13
.gitmodules
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
[submodule "indexing/fast-feedback-indexer"]
|
||||
path = indexing/fast-feedback-indexer
|
||||
url = https://github.com/paulscherrerinstitute/fast-feedback-indexer/
|
||||
[submodule "frame_serialize/tinycbor"]
|
||||
path = frame_serialize/tinycbor
|
||||
url = https://github.com/intel/tinycbor
|
||||
[submodule "compression/zstd"]
|
||||
path = compression/zstd
|
||||
url = https://github.com/facebook/zstd
|
||||
[submodule "detector_control/slsDetectorPackage"]
|
||||
path = detector_control/slsDetectorPackage
|
||||
url = https://github.com/slsdetectorgroup/slsDetectorPackage
|
||||
branch = "developer"
|
||||
76
CMakeLists.txt
Normal file
76
CMakeLists.txt
Normal file
@@ -0,0 +1,76 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 3.18)
|
||||
|
||||
PROJECT(Jungfraujoch VERSION 1.0 LANGUAGES C CXX CUDA)
|
||||
|
||||
SET(CMAKE_CXX_STANDARD 20)
|
||||
SET(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
|
||||
SET(CMAKE_CUDA_ARCHITECTURES 70 75 80 86) # V100, T4, A100, RTX A4000
|
||||
SET(CMAKE_CUDA_STANDARD 17)
|
||||
|
||||
SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -march=native -mtune=native")
|
||||
SET(CMAKE_C_FLAGS_RELEASE "-O3 -march=native -mtune=native")
|
||||
SET(CMAKE_CUDA_FLAGS_RELEASE "-O3")
|
||||
|
||||
SET(JFJOCH_COMPILE_WRITER ON CACHE BOOL "Compile HDF5 writer")
|
||||
SET(JFJOCH_COMPILE_FPGA ON CACHE BOOL "Compile FPGA part")
|
||||
SET(JFJOCH_COMPILE_DETECTOR ON CACHE BOOL "Compile detector control")
|
||||
SET(JFJOCH_COMPILE_INDEXER ON CACHE BOOL "Compile indexer")
|
||||
SET(JFJOCH_COMPILE_TESTS OFF CACHE BOOL "Compile tests")
|
||||
|
||||
INCLUDE_DIRECTORIES(include)
|
||||
INCLUDE(CheckIncludeFile)
|
||||
|
||||
FIND_LIBRARY(NUMA_LIBRARY NAMES numa DOC "NUMA Library")
|
||||
CHECK_INCLUDE_FILE(numaif.h HAS_NUMAIF)
|
||||
CHECK_INCLUDE_FILE(numa.h HAS_NUMA_H)
|
||||
|
||||
LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
FIND_PACKAGE(ZeroMQ 4 REQUIRED)
|
||||
|
||||
ADD_SUBDIRECTORY(jungfrau)
|
||||
ADD_SUBDIRECTORY(frame_serialize)
|
||||
ADD_SUBDIRECTORY(grpc)
|
||||
ADD_SUBDIRECTORY(compression)
|
||||
ADD_SUBDIRECTORY(common)
|
||||
ADD_SUBDIRECTORY(broker)
|
||||
ADD_SUBDIRECTORY(etc)
|
||||
ADD_SUBDIRECTORY(indexing)
|
||||
|
||||
SET(jfjoch_executables jfjoch_broker)
|
||||
|
||||
IF (JFJOCH_COMPILE_TESTS OR JFJOCH_COMPILE_FPGA)
|
||||
ADD_SUBDIRECTORY(receiver)
|
||||
LIST(APPEND jfjoch_executables jfjoch_receiver)
|
||||
ENDIF()
|
||||
|
||||
IF (JFJOCH_COMPILE_DETECTOR)
|
||||
ADD_SUBDIRECTORY(detector_control)
|
||||
LIST(APPEND jfjoch_executables jfjoch_detector)
|
||||
ENDIF()
|
||||
|
||||
IF (JFJOCH_COMPILE_TESTS OR JFJOCH_COMPILE_WRITER)
|
||||
ADD_SUBDIRECTORY(writer)
|
||||
ADD_SUBDIRECTORY(tools)
|
||||
LIST(APPEND jfjoch_executables jfjoch_writer jfjoch_writer_multi)
|
||||
ENDIF()
|
||||
|
||||
IF (JFJOCH_COMPILE_TESTS)
|
||||
ADD_SUBDIRECTORY(tests)
|
||||
LIST(APPEND jfjoch_executables CatchTest DataAnalysisPerfTest CompressionBenchmark HDF5DatasetWriteTest)
|
||||
ENDIF()
|
||||
|
||||
LIST(APPEND jfjoch_executables jfjoch_udp_simulator)
|
||||
|
||||
ADD_CUSTOM_COMMAND(OUTPUT frontend_ui/build/index.html
|
||||
COMMAND npm run build
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/frontend_ui
|
||||
DEPENDS jfjoch-grpc-js)
|
||||
ADD_CUSTOM_TARGET(frontend DEPENDS frontend_ui/build/index.html)
|
||||
|
||||
ADD_CUSTOM_TARGET(jfjoch DEPENDS ${jfjoch_executables})
|
||||
|
||||
IF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||
SET(CMAKE_INSTALL_PREFIX /opt/jfjoch CACHE PATH "Default directory" FORCE)
|
||||
ENDIF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||
|
||||
674
LICENSE.GPL
Normal file
674
LICENSE.GPL
Normal file
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
289
LICENSE.OHL-S
Normal file
289
LICENSE.OHL-S
Normal file
@@ -0,0 +1,289 @@
|
||||
CERN Open Hardware Licence Version 2 - Strongly Reciprocal
|
||||
|
||||
|
||||
Preamble
|
||||
|
||||
CERN has developed this licence to promote collaboration among
|
||||
hardware designers and to provide a legal tool which supports the
|
||||
freedom to use, study, modify, share and distribute hardware designs
|
||||
and products based on those designs. Version 2 of the CERN Open
|
||||
Hardware Licence comes in three variants: CERN-OHL-P (permissive); and
|
||||
two reciprocal licences: CERN-OHL-W (weakly reciprocal) and this
|
||||
licence, CERN-OHL-S (strongly reciprocal).
|
||||
|
||||
The CERN-OHL-S is copyright CERN 2020. Anyone is welcome to use it, in
|
||||
unmodified form only.
|
||||
|
||||
Use of this Licence does not imply any endorsement by CERN of any
|
||||
Licensor or their designs nor does it imply any involvement by CERN in
|
||||
their development.
|
||||
|
||||
|
||||
1 Definitions
|
||||
|
||||
1.1 'Licence' means this CERN-OHL-S.
|
||||
|
||||
1.2 'Compatible Licence' means
|
||||
|
||||
a) any earlier version of the CERN Open Hardware licence, or
|
||||
|
||||
b) any version of the CERN-OHL-S, or
|
||||
|
||||
c) any licence which permits You to treat the Source to which
|
||||
it applies as licensed under CERN-OHL-S provided that on
|
||||
Conveyance of any such Source, or any associated Product You
|
||||
treat the Source in question as being licensed under
|
||||
CERN-OHL-S.
|
||||
|
||||
1.3 'Source' means information such as design materials or digital
|
||||
code which can be applied to Make or test a Product or to
|
||||
prepare a Product for use, Conveyance or sale, regardless of its
|
||||
medium or how it is expressed. It may include Notices.
|
||||
|
||||
1.4 'Covered Source' means Source that is explicitly made available
|
||||
under this Licence.
|
||||
|
||||
1.5 'Product' means any device, component, work or physical object,
|
||||
whether in finished or intermediate form, arising from the use,
|
||||
application or processing of Covered Source.
|
||||
|
||||
1.6 'Make' means to create or configure something, whether by
|
||||
manufacture, assembly, compiling, loading or applying Covered
|
||||
Source or another Product or otherwise.
|
||||
|
||||
1.7 'Available Component' means any part, sub-assembly, library or
|
||||
code which:
|
||||
|
||||
a) is licensed to You as Complete Source under a Compatible
|
||||
Licence; or
|
||||
|
||||
b) is available, at the time a Product or the Source containing
|
||||
it is first Conveyed, to You and any other prospective
|
||||
licensees
|
||||
|
||||
i) as a physical part with sufficient rights and
|
||||
information (including any configuration and
|
||||
programming files and information about its
|
||||
characteristics and interfaces) to enable it either to
|
||||
be Made itself, or to be sourced and used to Make the
|
||||
Product; or
|
||||
ii) as part of the normal distribution of a tool used to
|
||||
design or Make the Product.
|
||||
|
||||
1.8 'Complete Source' means the set of all Source necessary to Make
|
||||
a Product, in the preferred form for making modifications,
|
||||
including necessary installation and interfacing information
|
||||
both for the Product, and for any included Available Components.
|
||||
If the format is proprietary, it must also be made available in
|
||||
a format (if the proprietary tool can create it) which is
|
||||
viewable with a tool available to potential licensees and
|
||||
licensed under a licence approved by the Free Software
|
||||
Foundation or the Open Source Initiative. Complete Source need
|
||||
not include the Source of any Available Component, provided that
|
||||
You include in the Complete Source sufficient information to
|
||||
enable a recipient to Make or source and use the Available
|
||||
Component to Make the Product.
|
||||
|
||||
1.9 'Source Location' means a location where a Licensor has placed
|
||||
Covered Source, and which that Licensor reasonably believes will
|
||||
remain easily accessible for at least three years for anyone to
|
||||
obtain a digital copy.
|
||||
|
||||
1.10 'Notice' means copyright, acknowledgement and trademark notices,
|
||||
Source Location references, modification notices (subsection
|
||||
3.3(b)) and all notices that refer to this Licence and to the
|
||||
disclaimer of warranties that are included in the Covered
|
||||
Source.
|
||||
|
||||
1.11 'Licensee' or 'You' means any person exercising rights under
|
||||
this Licence.
|
||||
|
||||
1.12 'Licensor' means a natural or legal person who creates or
|
||||
modifies Covered Source. A person may be a Licensee and a
|
||||
Licensor at the same time.
|
||||
|
||||
1.13 'Convey' means to communicate to the public or distribute.
|
||||
|
||||
|
||||
2 Applicability
|
||||
|
||||
2.1 This Licence governs the use, copying, modification, Conveying
|
||||
of Covered Source and Products, and the Making of Products. By
|
||||
exercising any right granted under this Licence, You irrevocably
|
||||
accept these terms and conditions.
|
||||
|
||||
2.2 This Licence is granted by the Licensor directly to You, and
|
||||
shall apply worldwide and without limitation in time.
|
||||
|
||||
2.3 You shall not attempt to restrict by contract or otherwise the
|
||||
rights granted under this Licence to other Licensees.
|
||||
|
||||
2.4 This Licence is not intended to restrict fair use, fair dealing,
|
||||
or any other similar right.
|
||||
|
||||
|
||||
3 Copying, Modifying and Conveying Covered Source
|
||||
|
||||
3.1 You may copy and Convey verbatim copies of Covered Source, in
|
||||
any medium, provided You retain all Notices.
|
||||
|
||||
3.2 You may modify Covered Source, other than Notices, provided that
|
||||
You irrevocably undertake to make that modified Covered Source
|
||||
available from a Source Location should You Convey a Product in
|
||||
circumstances where the recipient does not otherwise receive a
|
||||
copy of the modified Covered Source. In each case subsection 3.3
|
||||
shall apply.
|
||||
|
||||
You may only delete Notices if they are no longer applicable to
|
||||
the corresponding Covered Source as modified by You and You may
|
||||
add additional Notices applicable to Your modifications.
|
||||
Including Covered Source in a larger work is modifying the
|
||||
Covered Source, and the larger work becomes modified Covered
|
||||
Source.
|
||||
|
||||
3.3 You may Convey modified Covered Source (with the effect that You
|
||||
shall also become a Licensor) provided that You:
|
||||
|
||||
a) retain Notices as required in subsection 3.2;
|
||||
|
||||
b) add a Notice to the modified Covered Source stating that You
|
||||
have modified it, with the date and brief description of how
|
||||
You have modified it;
|
||||
|
||||
c) add a Source Location Notice for the modified Covered Source
|
||||
if You Convey in circumstances where the recipient does not
|
||||
otherwise receive a copy of the modified Covered Source; and
|
||||
|
||||
d) license the modified Covered Source under the terms and
|
||||
conditions of this Licence (or, as set out in subsection
|
||||
8.3, a later version, if permitted by the licence of the
|
||||
original Covered Source). Such modified Covered Source must
|
||||
be licensed as a whole, but excluding Available Components
|
||||
contained in it, which remain licensed under their own
|
||||
applicable licences.
|
||||
|
||||
|
||||
4 Making and Conveying Products
|
||||
|
||||
You may Make Products, and/or Convey them, provided that You either
|
||||
provide each recipient with a copy of the Complete Source or ensure
|
||||
that each recipient is notified of the Source Location of the Complete
|
||||
Source. That Complete Source is Covered Source, and You must
|
||||
accordingly satisfy Your obligations set out in subsection 3.3. If
|
||||
specified in a Notice, the Product must visibly and securely display
|
||||
the Source Location on it or its packaging or documentation in the
|
||||
manner specified in that Notice.
|
||||
|
||||
|
||||
5 Research and Development
|
||||
|
||||
You may Convey Covered Source, modified Covered Source or Products to
|
||||
a legal entity carrying out development, testing or quality assurance
|
||||
work on Your behalf provided that the work is performed on terms which
|
||||
prevent the entity from both using the Source or Products for its own
|
||||
internal purposes and Conveying the Source or Products or any
|
||||
modifications to them to any person other than You. Any modifications
|
||||
made by the entity shall be deemed to be made by You pursuant to
|
||||
subsection 3.2.
|
||||
|
||||
|
||||
6 DISCLAIMER AND LIABILITY
|
||||
|
||||
6.1 DISCLAIMER OF WARRANTY -- The Covered Source and any Products
|
||||
are provided 'as is' and any express or implied warranties,
|
||||
including, but not limited to, implied warranties of
|
||||
merchantability, of satisfactory quality, non-infringement of
|
||||
third party rights, and fitness for a particular purpose or use
|
||||
are disclaimed in respect of any Source or Product to the
|
||||
maximum extent permitted by law. The Licensor makes no
|
||||
representation that any Source or Product does not or will not
|
||||
infringe any patent, copyright, trade secret or other
|
||||
proprietary right. The entire risk as to the use, quality, and
|
||||
performance of any Source or Product shall be with You and not
|
||||
the Licensor. This disclaimer of warranty is an essential part
|
||||
of this Licence and a condition for the grant of any rights
|
||||
granted under this Licence.
|
||||
|
||||
6.2 EXCLUSION AND LIMITATION OF LIABILITY -- The Licensor shall, to
|
||||
the maximum extent permitted by law, have no liability for
|
||||
direct, indirect, special, incidental, consequential, exemplary,
|
||||
punitive or other damages of any character including, without
|
||||
limitation, procurement of substitute goods or services, loss of
|
||||
use, data or profits, or business interruption, however caused
|
||||
and on any theory of contract, warranty, tort (including
|
||||
negligence), product liability or otherwise, arising in any way
|
||||
in relation to the Covered Source, modified Covered Source
|
||||
and/or the Making or Conveyance of a Product, even if advised of
|
||||
the possibility of such damages, and You shall hold the
|
||||
Licensor(s) free and harmless from any liability, costs,
|
||||
damages, fees and expenses, including claims by third parties,
|
||||
in relation to such use.
|
||||
|
||||
|
||||
7 Patents
|
||||
|
||||
7.1 Subject to the terms and conditions of this Licence, each
|
||||
Licensor hereby grants to You a perpetual, worldwide,
|
||||
non-exclusive, no-charge, royalty-free, irrevocable (except as
|
||||
stated in subsections 7.2 and 8.4) patent licence to Make, have
|
||||
Made, use, offer to sell, sell, import, and otherwise transfer
|
||||
the Covered Source and Products, where such licence applies only
|
||||
to those patent claims licensable by such Licensor that are
|
||||
necessarily infringed by exercising rights under the Covered
|
||||
Source as Conveyed by that Licensor.
|
||||
|
||||
7.2 If You institute patent litigation against any entity (including
|
||||
a cross-claim or counterclaim in a lawsuit) alleging that the
|
||||
Covered Source or a Product constitutes direct or contributory
|
||||
patent infringement, or You seek any declaration that a patent
|
||||
licensed to You under this Licence is invalid or unenforceable
|
||||
then any rights granted to You under this Licence shall
|
||||
terminate as of the date such process is initiated.
|
||||
|
||||
|
||||
8 General
|
||||
|
||||
8.1 If any provisions of this Licence are or subsequently become
|
||||
invalid or unenforceable for any reason, the remaining
|
||||
provisions shall remain effective.
|
||||
|
||||
8.2 You shall not use any of the name (including acronyms and
|
||||
abbreviations), image, or logo by which the Licensor or CERN is
|
||||
known, except where needed to comply with section 3, or where
|
||||
the use is otherwise allowed by law. Any such permitted use
|
||||
shall be factual and shall not be made so as to suggest any kind
|
||||
of endorsement or implication of involvement by the Licensor or
|
||||
its personnel.
|
||||
|
||||
8.3 CERN may publish updated versions and variants of this Licence
|
||||
which it considers to be in the spirit of this version, but may
|
||||
differ in detail to address new problems or concerns. New
|
||||
versions will be published with a unique version number and a
|
||||
variant identifier specifying the variant. If the Licensor has
|
||||
specified that a given variant applies to the Covered Source
|
||||
without specifying a version, You may treat that Covered Source
|
||||
as being released under any version of the CERN-OHL with that
|
||||
variant. If no variant is specified, the Covered Source shall be
|
||||
treated as being released under CERN-OHL-S. The Licensor may
|
||||
also specify that the Covered Source is subject to a specific
|
||||
version of the CERN-OHL or any later version in which case You
|
||||
may apply this or any later version of CERN-OHL with the same
|
||||
variant identifier published by CERN.
|
||||
|
||||
8.4 This Licence shall terminate with immediate effect if You fail
|
||||
to comply with any of its terms and conditions.
|
||||
|
||||
8.5 However, if You cease all breaches of this Licence, then Your
|
||||
Licence from any Licensor is reinstated unless such Licensor has
|
||||
terminated this Licence by giving You, while You remain in
|
||||
breach, a notice specifying the breach and requiring You to cure
|
||||
it within 30 days, and You have failed to come into compliance
|
||||
in all material respects by the end of the 30 day period. Should
|
||||
You repeat the breach after receipt of a cure notice and
|
||||
subsequent reinstatement, this Licence will terminate
|
||||
immediately and permanently. Section 6 shall continue to apply
|
||||
after any termination.
|
||||
|
||||
8.6 This Licence shall not be enforceable except by a Licensor
|
||||
acting as such, and third party beneficiary rights are
|
||||
specifically excluded.
|
||||
124
README.md
Normal file
124
README.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# Jungfraujoch
|
||||
|
||||
Application to receive data from the JUNGFRAU detector at Swiss Light Source macromolecular crystallography beamlines.
|
||||
|
||||
Citation: F. Leonarski, M. Bruckner, C. Lopez-Cuenca, A. Mozzanica, H.-C. Stadler, Z. Matej, A. Castellane, B. Mesnet, J. Wojdyla, B. Schmitt and M. Wang "Jungfraujoch: hardware-accelerated data-acquisition system for kilohertz pixel-array X-ray detectors" (2023), J. Synchrotron Rad., 30, 227-234 [doi:10.1107/S1600577522010268](https://doi.org/10.1107/S1600577522010268).
|
||||
|
||||
## License
|
||||
|
||||
Software components are licensed with GNU Public License version 3.
|
||||
|
||||
Hardware components are licensed with Strongly-reciprocal CERN Open Hardware Licence version 2.
|
||||
|
||||
## Hardware requirements
|
||||
1. JUNGFRAU detector (optimally 4M with 2 kHz enabled read-out boards)
|
||||
2. Server system with Xilinx Alveo U55C cards and Nvidia GPUs
|
||||
3. 100G fiber-optic switch between JUNGFRAU and server
|
||||
|
||||
## FPGA bitstream
|
||||
### Dependencies
|
||||
To build FPGA image you need:
|
||||
1. Xilinx Vivado 2022.1 or newer
|
||||
2. License for Ultrascale+ 100G core - license is available at no cost from Xilinx, but needs to be seperatly requested, see [Xilinx website](https://www.xilinx.com/products/intellectual-property/cmac_usplus.html).
|
||||
|
||||
Instructions see [here](receiver/README.md)
|
||||
|
||||
## Software
|
||||
|
||||
### Dependencies
|
||||
1. C++17 compiler and C++17 standard library (NOT provided by default RHEL 7 installation, need to install Developer Tools, tested with `devtools-11`)
|
||||
2. CMake version 3.21 or newer + GNU make tool
|
||||
3. HDF5 library version 1.10 or newer
|
||||
4. ZeroMQ library
|
||||
5. Google Remote Procedure Call (gRPC) - see notes below
|
||||
6. CUDA compiler version 11 or newer (spot finding, indexing, and radial integration)
|
||||
7. TIFF library with C++ bindings
|
||||
8. Mellanox OFED - Infinibands Verbs (ibverbs)
|
||||
9. NUMA library (optional)
|
||||
|
||||
Additional dependencies: SLS Detector Package, tinycbor (Intel) and Zstandard (Facebook) are provided as GIT submodules.
|
||||
|
||||
Directly included in the repository:
|
||||
* JSON parser/writer from N. Lohmann - see [github.com/nlohmann/json](https://github.com/nlohmann/json)
|
||||
* Catch2 testing library - see [github.com/catchorg/Catch2](https://github.com/catchorg/Catch2)
|
||||
* Xilinx arbitrary precision arithmetic headers - see [github.com/Xilinx/HLS_arbitrary_Precision_Types](https://github.com/Xilinx/HLS_arbitrary_Precision_Types)
|
||||
* Bitshuffle filter from K. Masui - see [github.com/kiyo-masui/bitshuffle](https://github.com/kiyo-masui/bitshuffle)
|
||||
* LZ4 compression by Y.Collet - see [github.com/lz4/lz4](https://github.com/lz4/lz4)
|
||||
* Spdlog logging library - see [github.com/gabime/spdlog](https://github.com/gabime/spdlog)
|
||||
|
||||
For license check LICENSE file in respective directory
|
||||
|
||||
### Software components
|
||||
|
||||
* `jfjoch_receiver` in `fpga/host` - the main component of Jungfraujoch, used to receive data with FPGA smart network cards, compress images and send them over ZeroMQ
|
||||
* `jfjoch_broker` in `broker` - gRPC based broker, controlling all Jungfraujoch components
|
||||
* `jfjoch_writer` in `writer` - HDF5 writer
|
||||
* `jfjoch_detector` in `detector` - wrapper over detector control
|
||||
|
||||
Configuration for the modules is given in configuration files present in `etc` directory.
|
||||
|
||||
### Installation of gRPC
|
||||
By default, gRPC will be cloned and compiled automatically, when compiling Jungfraujoch with `cmake`, however this significantly increases installation time.
|
||||
Alternative is to compile gRPC beforehand. This is very useful especially, when one expects to compile Jungfraujoch multiple times on the same machine.
|
||||
It is recommended to install gRPC in own directory (not in system path), as there is no easy way to uninstall it.
|
||||
To compile gRPC:
|
||||
```
|
||||
git clone https://github.com/grpc/grpc
|
||||
cd grpc
|
||||
git checkout v1.41.1
|
||||
git submodule update --init
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DgRPC_ZLIB_PROVIDER="package" -DCMAKE_INSTALL_PREFIX=/opt/grpc ..
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
Currently, handling paths for gRPC and its dependencies gives sometimes troubles and few environmental variables need to be setup before compilation (assuming gRPC installed in /opt/grpc):
|
||||
```
|
||||
export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/opt/grpc/lib/cmake:/opt/grpc/lib64/cmake
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/grpc/lib:/opt/grpc/lib64
|
||||
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/opt/grpc/lib/pkgconfig:/opt/grpc/lib64/pkgconfig
|
||||
```
|
||||
|
||||
### Compilation
|
||||
Use the following commands (use `cmake` instead of `cmake3` in non-RHEL systems):
|
||||
|
||||
```
|
||||
git submodule update --init
|
||||
mkdir build
|
||||
cd build
|
||||
cmake3 ..
|
||||
make jfjoch
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
To enable compiling test routines use parameter `-DJFJOCH_COMPILE_TESTS=ON` for `cmake`.
|
||||
Automated test routine is then accessible as `tests/CatchTest`. There are also benchmark routines:
|
||||
|
||||
* `CompressionBenchmark` to measure compression bandwidth (single threaded)
|
||||
* `HDF5DatasetWriteTest` to measure HDF5 dataset writing speed (single threaded)
|
||||
* `DataAnalysisPerfTest` to measure data analysis performance (single threaded)
|
||||
* `PedestalPerfTest` to measure pedestal calculation performance
|
||||
|
||||
In addition, tests are executed to verify that datasets written by Jungfraujoch are readable with XDS Durin plugin and CrystFEL. Input files for these programs are placed in `xds_durin` and `crystfel` folders. See `.gitlab-ci.yml` for details.
|
||||
|
||||
## Building web user interface
|
||||
### Dependencies
|
||||
For web user interface:
|
||||
1. Node.js
|
||||
2. Web server, e.g. Apache httpd
|
||||
3. Web grpc
|
||||
|
||||
### Building
|
||||
To build web interface:
|
||||
```
|
||||
cd frontend_ui
|
||||
npm build
|
||||
```
|
||||
|
||||
To install on RHEL 7 and Apache:
|
||||
```
|
||||
cd build
|
||||
sudo cp -r * /var/www/html/
|
||||
```
|
||||
13
broker/CMakeLists.txt
Normal file
13
broker/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
ADD_LIBRARY(JFJochBroker STATIC
|
||||
JFJochStateMachine.cpp JFJochStateMachine.h
|
||||
JFJochServices.cpp JFJochServices.h
|
||||
JFJochBroker.cpp JFJochBroker.h)
|
||||
|
||||
TARGET_LINK_LIBRARIES(JFJochBroker gRPCClients CommonFunctions)
|
||||
|
||||
ADD_EXECUTABLE(jfjoch_broker jfjoch_broker.cpp)
|
||||
TARGET_LINK_LIBRARIES(jfjoch_broker JFJochBroker)
|
||||
|
||||
INSTALL(TARGETS jfjoch_broker RUNTIME)
|
||||
|
||||
|
||||
111
broker/JFJochBroker.cpp
Normal file
111
broker/JFJochBroker.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "JFJochBroker.h"
|
||||
|
||||
#define GRPC_RUN(x) try { {x;} return grpc::Status::OK; } catch (const std::exception& e) { logger.ErrorException(e); return {grpc::StatusCode::ABORTED, e.what()}; }
|
||||
|
||||
JFJochBroker::JFJochBroker(const DiffractionExperiment &experiment) {
|
||||
state_machine.NotThreadSafe_Experiment() = experiment;
|
||||
}
|
||||
|
||||
grpc::Status JFJochBroker::Start(grpc::ServerContext *context, const JFJochProtoBuf::DatasetSettings *request,
|
||||
JFJochProtoBuf::Empty *response) {
|
||||
GRPC_RUN( state_machine.Start(*request) );
|
||||
}
|
||||
|
||||
grpc::Status JFJochBroker::Stop(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Empty *response) {
|
||||
GRPC_RUN( state_machine.Stop() );
|
||||
}
|
||||
|
||||
grpc::Status JFJochBroker::Pedestal(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Empty *response) {
|
||||
GRPC_RUN( state_machine.Pedestal() );
|
||||
}
|
||||
|
||||
grpc::Status JFJochBroker::Initialize(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Empty *response) {
|
||||
GRPC_RUN( state_machine.Initialize() );
|
||||
}
|
||||
|
||||
grpc::Status JFJochBroker::Cancel(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Empty *response) {
|
||||
GRPC_RUN( state_machine.Cancel() );
|
||||
}
|
||||
|
||||
grpc::Status JFJochBroker::Deactivate(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Empty *response) {
|
||||
GRPC_RUN( state_machine.Deactivate() );
|
||||
}
|
||||
|
||||
grpc::Status JFJochBroker::Trigger(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Empty *response) {
|
||||
GRPC_RUN( state_machine.Trigger() );
|
||||
}
|
||||
|
||||
grpc::Status JFJochBroker::GetStatus(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::BrokerStatus *response) {
|
||||
GRPC_RUN( *response = state_machine.GetStatus() );
|
||||
}
|
||||
|
||||
grpc::Status JFJochBroker::GetPlots(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::ReceiverDataProcessingPlots *response) {
|
||||
GRPC_RUN( *response = state_machine.GetPlots() );
|
||||
}
|
||||
|
||||
grpc::Status JFJochBroker::GetPreview(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::PreviewFrame *response) {
|
||||
GRPC_RUN( *response = services.GetPreviewFrame() );
|
||||
}
|
||||
|
||||
grpc::Status JFJochBroker::GetPedestalG0(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Image *response) {
|
||||
GRPC_RUN( *response = state_machine.GetPedestalG0() );
|
||||
}
|
||||
|
||||
grpc::Status JFJochBroker::GetPedestalG1(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Image *response) {
|
||||
GRPC_RUN( *response = state_machine.GetPedestalG1() );
|
||||
}
|
||||
|
||||
grpc::Status JFJochBroker::GetPedestalG2(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Image *response) {
|
||||
GRPC_RUN( *response = state_machine.GetPedestalG2() );
|
||||
}
|
||||
|
||||
grpc::Status JFJochBroker::GetMask(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Image *response) {
|
||||
GRPC_RUN( *response = state_machine.GetNeXusMask() );
|
||||
}
|
||||
|
||||
grpc::Status JFJochBroker::GetDetectorSettings(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::DetectorSettings *response) {
|
||||
GRPC_RUN( *response = state_machine.GetDetectorSettings() );
|
||||
}
|
||||
|
||||
grpc::Status JFJochBroker::PutDetectorSettings(grpc::ServerContext *context,
|
||||
const JFJochProtoBuf::DetectorSettings *request,
|
||||
JFJochProtoBuf::Empty *response) {
|
||||
GRPC_RUN( state_machine.SetDetectorSettings(*request) );
|
||||
}
|
||||
|
||||
grpc::Status JFJochBroker::GetDataProcessingSettings(::grpc::ServerContext *context, const ::JFJochProtoBuf::Empty *request,
|
||||
::JFJochProtoBuf::DataProcessingSettings *response) {
|
||||
GRPC_RUN( *response = state_machine.GetDataProcessingSettings() );
|
||||
}
|
||||
|
||||
grpc::Status JFJochBroker::PutDataProcessingSettings(::grpc::ServerContext *context,
|
||||
const ::JFJochProtoBuf::DataProcessingSettings *request,
|
||||
::JFJochProtoBuf::Empty *response) {
|
||||
GRPC_RUN( state_machine.SetDataProcessingSettings(*request) );
|
||||
}
|
||||
|
||||
void JFJochBroker::LoadGainFile(const std::string &filename) {
|
||||
state_machine.LoadGainFile(filename);
|
||||
logger.Info("Loaded gain file {}", filename);
|
||||
}
|
||||
|
||||
JFJochServices &JFJochBroker::Services() {
|
||||
return services;
|
||||
}
|
||||
77
broker/JFJochBroker.h
Normal file
77
broker/JFJochBroker.h
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_JFJOCHBROKER_H
|
||||
#define JUNGFRAUJOCH_JFJOCHBROKER_H
|
||||
|
||||
#include <jfjoch.grpc.pb.h>
|
||||
#include "JFJochServices.h"
|
||||
#include "JFJochStateMachine.h"
|
||||
|
||||
class JFJochBroker final : public JFJochProtoBuf::gRPC_JFJochBroker::Service {
|
||||
Logger logger{"JFJochBroker"};
|
||||
JFJochServices services {logger};
|
||||
JFJochStateMachine state_machine {services, logger};
|
||||
public:
|
||||
JFJochBroker(const DiffractionExperiment& experiment);
|
||||
void LoadGainFile(const std::string& filename);
|
||||
JFJochServices& Services();
|
||||
|
||||
grpc::Status Start(grpc::ServerContext *context, const JFJochProtoBuf::DatasetSettings *request,
|
||||
JFJochProtoBuf::Empty *response) override;
|
||||
|
||||
grpc::Status Stop(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Empty *response) override;
|
||||
|
||||
grpc::Status Pedestal(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Empty *response) override;
|
||||
|
||||
grpc::Status Initialize(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Empty *response) override;
|
||||
|
||||
grpc::Status Cancel(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Empty *response) override;
|
||||
|
||||
grpc::Status Deactivate(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Empty *response) override;
|
||||
|
||||
grpc::Status Trigger(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Empty *response) override;
|
||||
|
||||
grpc::Status GetStatus(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::BrokerStatus *response) override;
|
||||
|
||||
grpc::Status GetPlots(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::ReceiverDataProcessingPlots *response) override;
|
||||
|
||||
grpc::Status GetPreview(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::PreviewFrame *response) override;
|
||||
|
||||
grpc::Status GetPedestalG0(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Image *response) override;
|
||||
|
||||
grpc::Status GetDetectorSettings(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::DetectorSettings *response) override;
|
||||
|
||||
grpc::Status PutDetectorSettings(grpc::ServerContext *context, const JFJochProtoBuf::DetectorSettings *request,
|
||||
JFJochProtoBuf::Empty *response) override;
|
||||
|
||||
grpc::Status GetDataProcessingSettings(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::DataProcessingSettings *response) override;
|
||||
|
||||
grpc::Status PutDataProcessingSettings(grpc::ServerContext *context, const JFJochProtoBuf::DataProcessingSettings *request,
|
||||
JFJochProtoBuf::Empty *response) override;
|
||||
|
||||
grpc::Status GetPedestalG1(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Image *response) override;
|
||||
|
||||
grpc::Status GetPedestalG2(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Image *response) override;
|
||||
|
||||
grpc::Status GetMask(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Image *response) override;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_JFJOCHBROKER_H
|
||||
173
broker/JFJochServices.cpp
Normal file
173
broker/JFJochServices.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "JFJochServices.h"
|
||||
#include "../common/JFJochException.h"
|
||||
|
||||
uint64_t current_time_ms() {
|
||||
auto curr_time = std::chrono::system_clock::now();
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(curr_time.time_since_epoch()).count();
|
||||
}
|
||||
|
||||
JFJochServices::JFJochServices(Logger &in_logger) : logger(in_logger) {}
|
||||
|
||||
void JFJochServices::Start(const DiffractionExperiment& experiment, const JFCalibration &calibration) {
|
||||
logger.Info("Measurement start for: {}", experiment.GetFilePrefix());
|
||||
|
||||
if ((experiment.GetImageNum() > 0) && (!experiment.GetFilePrefix().empty())) {
|
||||
logger.Info(" ... writer start");
|
||||
writer.Start(writer_zmq_addr, 0);
|
||||
writer_running = true;
|
||||
} else
|
||||
writer_running = false;
|
||||
|
||||
logger.Info(" ... receiver start");
|
||||
if (experiment.GetDetectorMode() == DetectorMode::Conversion)
|
||||
receiver.Start(experiment, &calibration);
|
||||
else
|
||||
receiver.Start(experiment, nullptr);
|
||||
|
||||
if (!experiment.IsUsingInternalPacketGen()) {
|
||||
logger.Info(" ... detector start");
|
||||
detector.Start(experiment);
|
||||
}
|
||||
logger.Info(" Done!");
|
||||
}
|
||||
|
||||
void JFJochServices::Off() {
|
||||
detector.Off();
|
||||
}
|
||||
|
||||
void JFJochServices::On(const DiffractionExperiment &x) {
|
||||
logger.Info("Detector on");
|
||||
|
||||
JFJochProtoBuf::DetectorConfig config = x.DetectorConfig(receiver.GetNetworkConfig());
|
||||
|
||||
detector.On(config);
|
||||
logger.Info(" ... done");
|
||||
}
|
||||
|
||||
JFJochProtoBuf::BrokerFullStatus JFJochServices::Stop(const JFCalibration &calibration) {
|
||||
JFJochProtoBuf::BrokerFullStatus ret;
|
||||
|
||||
std::unique_ptr<JFJochException> exception;
|
||||
|
||||
try {
|
||||
logger.Info("Wait for receiver done");
|
||||
*ret.mutable_receiver() = receiver.Stop();
|
||||
|
||||
logger.Info(" ... Receiver efficiency: {} % Max delay: {} Compression ratio {}x",
|
||||
static_cast<int>(ret.receiver().efficiency()*100.0),
|
||||
ret.receiver().max_receive_delay(),
|
||||
static_cast<int>(std::round(ret.receiver().compressed_ratio())));
|
||||
if (ret.receiver().efficiency() < 1.0) {
|
||||
for (int i = 0; i < ret.receiver().device_statistics_size(); i++) {
|
||||
for (int j = 0; j < ret.receiver().device_statistics(i).packets_received_per_module_size(); j++) {
|
||||
logger.Info(" ... Device: {} Module: {} Packets received: {}", i, j,
|
||||
ret.receiver().device_statistics(i).packets_received_per_module(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const JFJochException &e) {
|
||||
logger.Error(" ... finished with error {}",e.what());
|
||||
exception = std::make_unique<JFJochException>(e);
|
||||
}
|
||||
logger.Info("Receiver finished with success");
|
||||
|
||||
if (writer_running) {
|
||||
logger.Info("Stopping writer");
|
||||
try {
|
||||
auto stats = writer.Stop();
|
||||
logger.Info(" ... finished with success");
|
||||
for (int i = 0; i < stats.size(); i++) {
|
||||
*ret.add_writer() = stats[i];
|
||||
logger.Info("Writer {}: Images = {} Throughput = {:.0f} MB/s Frame rate = {:.0f} Hz",
|
||||
i, stats[i].nimages(), stats[i].performance_mbs(), stats[i].performance_hz());
|
||||
}
|
||||
} catch (JFJochException &e) {
|
||||
logger.Error(" ... finished with error {}",e.what());
|
||||
exception = std::make_unique<JFJochException>(e);
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info("Stopping detector");
|
||||
try {
|
||||
detector.Stop();
|
||||
logger.Info(" ... done");
|
||||
} catch (JFJochException &e) {
|
||||
logger.Error(" ... finished with error {}",e.what());
|
||||
exception = std::make_unique<JFJochException>(e);
|
||||
}
|
||||
|
||||
if (exception)
|
||||
throw JFJochException(*exception);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void JFJochServices::Abort() {
|
||||
// Abort should try to achieve the best outcome possible
|
||||
// but it OK if things fail (for example lost connection)
|
||||
try {
|
||||
receiver.Abort();
|
||||
} catch (const std::exception &e) {
|
||||
logger.Error(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void JFJochServices::Cancel() {
|
||||
detector.Stop();
|
||||
receiver.Cancel();
|
||||
}
|
||||
|
||||
JFJochServices &JFJochServices::Receiver(const std::string &addr) {
|
||||
receiver.Connect(addr);
|
||||
logger.Info("Using receiver service with gRPC " + addr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
JFJochServices &JFJochServices::Writer(const std::string &addr, const std::string &zmq_push_addr) {
|
||||
writer.AddClient(addr);
|
||||
writer_zmq_addr.push_back(zmq_push_addr);
|
||||
logger.Info("Using writer service with gRPC {} listening for images from ZeroMQ {}", addr, zmq_push_addr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
JFJochServices &JFJochServices::Detector(const std::string &addr) {
|
||||
detector.Connect(addr);
|
||||
logger.Info("Using detector service with gRPC {}", addr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
JFJochProtoBuf::ReceiverStatus JFJochServices::GetReceiverStatus() {
|
||||
return receiver.GetStatus();
|
||||
}
|
||||
|
||||
JFJochProtoBuf::ReceiverDataProcessingPlots JFJochServices::GetPlots() {
|
||||
try {
|
||||
return receiver.GetPlots();
|
||||
} catch (...) {
|
||||
JFJochProtoBuf::ReceiverDataProcessingPlots plots;
|
||||
*plots.mutable_indexing_rate() = JFJochProtoBuf::Plot();
|
||||
*plots.mutable_bkg_estimate() = JFJochProtoBuf::Plot();
|
||||
*plots.mutable_spot_count() = JFJochProtoBuf::Plot();
|
||||
*plots.mutable_radial_int_profile() = JFJochProtoBuf::Plot();
|
||||
return plots;
|
||||
}
|
||||
}
|
||||
|
||||
void JFJochServices::SetDataProcessingSettings(const JFJochProtoBuf::DataProcessingSettings &settings) {
|
||||
receiver.SetDataProcessingSettings(settings);
|
||||
}
|
||||
|
||||
JFJochProtoBuf::PreviewFrame JFJochServices::GetPreviewFrame() {
|
||||
return receiver.GetPreviewFrame();
|
||||
}
|
||||
|
||||
void JFJochServices::Trigger() {
|
||||
detector.Trigger();
|
||||
}
|
||||
|
||||
size_t JFJochServices::WriterZMQCount() const {
|
||||
return writer_zmq_addr.size();
|
||||
}
|
||||
45
broker/JFJochServices.h
Normal file
45
broker/JFJochServices.h
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_JFJOCHSERVICES_H
|
||||
#define JUNGFRAUJOCH_JFJOCHSERVICES_H
|
||||
|
||||
#include "../common/DiffractionExperiment.h"
|
||||
#include "../jungfrau/JFCalibration.h"
|
||||
#include "../common/Logger.h"
|
||||
#include "../grpc/JFJochReceiverClient.h"
|
||||
#include "../grpc/JFJochWriterGroupClient.h"
|
||||
#include "../grpc/JFJochDetectorClient.h"
|
||||
|
||||
class JFJochServices {
|
||||
JFJochReceiverClient receiver;
|
||||
JFJochWriterGroupClient writer;
|
||||
JFJochDetectorClient detector;
|
||||
|
||||
Logger &logger;
|
||||
bool writer_running = false;
|
||||
std::vector<std::string> writer_zmq_addr;
|
||||
public:
|
||||
explicit JFJochServices(Logger &in_logger);
|
||||
void On(const DiffractionExperiment& experiment);
|
||||
void Off();
|
||||
void Start(const DiffractionExperiment& experiment, const JFCalibration &calibration);
|
||||
JFJochProtoBuf::BrokerFullStatus Stop(const JFCalibration &calibration);
|
||||
void Abort();
|
||||
void Cancel();
|
||||
void Trigger();
|
||||
|
||||
JFJochProtoBuf::ReceiverStatus GetReceiverStatus();
|
||||
JFJochProtoBuf::PreviewFrame GetPreviewFrame();
|
||||
JFJochProtoBuf::ReceiverDataProcessingPlots GetPlots();
|
||||
|
||||
void SetDataProcessingSettings(const JFJochProtoBuf::DataProcessingSettings &settings);
|
||||
JFJochServices& Receiver(const std::string &addr);
|
||||
JFJochServices& Writer(const std::string &addr, const std::string &zmq_push_addr);
|
||||
JFJochServices& Detector(const std::string &addr);
|
||||
|
||||
size_t WriterZMQCount() const;
|
||||
};
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_JFJOCHSERVICES_H
|
||||
516
broker/JFJochStateMachine.cpp
Normal file
516
broker/JFJochStateMachine.cpp
Normal file
@@ -0,0 +1,516 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "JFJochStateMachine.h"
|
||||
#include "../common/JFJochException.h"
|
||||
|
||||
JFJochStateMachine::JFJochStateMachine(JFJochServices &in_services, Logger &in_logger)
|
||||
: services(in_services), logger(in_logger),
|
||||
data_processing_settings(DiffractionExperiment::DefaultDataProcessingSettings()) {
|
||||
|
||||
}
|
||||
|
||||
void JFJochStateMachine::ImportPedestalG0(const JFJochProtoBuf::ReceiverOutput &receiver_output) {
|
||||
if (receiver_output.pedestal_result_size() != experiment.GetModulesNum() * experiment.GetStorageCellNumber())
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Mismatch in pedestal output");
|
||||
|
||||
for (int s = 0; s < experiment.GetStorageCellNumber(); s++) {
|
||||
for (int module = 0; module < experiment.GetModulesNum(); module++)
|
||||
calibration->Pedestal(module, 0, s)
|
||||
= receiver_output.pedestal_result(module + s * experiment.GetModulesNum());
|
||||
}
|
||||
SetCalibrationStatistics(calibration->GetModuleStatistics());
|
||||
}
|
||||
|
||||
void JFJochStateMachine::ImportPedestal(const JFJochProtoBuf::ReceiverOutput &receiver_output, size_t gain_level,
|
||||
size_t storage_cell) {
|
||||
for (int i = 0; i < receiver_output.pedestal_result_size(); i++)
|
||||
calibration->Pedestal(i, gain_level, storage_cell) = receiver_output.pedestal_result(i);
|
||||
SetCalibrationStatistics(calibration->GetModuleStatistics());
|
||||
}
|
||||
|
||||
void JFJochStateMachine::TakePedestalInternalAll(std::unique_lock<std::mutex> &ul) {
|
||||
calibration = std::make_unique<JFCalibration>(experiment);
|
||||
|
||||
if (!gain_calibration.empty()) {
|
||||
if (gain_calibration.size() != experiment.GetModulesNum())
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Mismatch in gain files number");
|
||||
for (int i = 0; i < gain_calibration.size(); i++)
|
||||
calibration->GainCalibration(i) = gain_calibration[i];
|
||||
}
|
||||
|
||||
cancel_sequence = false;
|
||||
|
||||
logger.Info("Pedestal sequence started");
|
||||
|
||||
try {
|
||||
TakePedestalInternalG0(ul);
|
||||
|
||||
for (int i = 0; i < experiment.GetStorageCellNumber(); i++) {
|
||||
TakePedestalInternalG1(ul, i);
|
||||
TakePedestalInternalG2(ul, i);
|
||||
}
|
||||
} catch (...) {
|
||||
logger.Info("Pedestal sequence error");
|
||||
state = JFJochState::Error;
|
||||
throw;
|
||||
}
|
||||
logger.Info("Pedestal sequence done");
|
||||
}
|
||||
|
||||
void JFJochStateMachine::TakePedestalInternalG0(std::unique_lock<std::mutex> &ul) {
|
||||
state = JFJochState::Pedestal;
|
||||
DiffractionExperiment local_experiment(experiment);
|
||||
local_experiment.Mode(DetectorMode::PedestalG0);
|
||||
|
||||
if (!cancel_sequence && (local_experiment.GetPedestalG0Frames() > 0)) {
|
||||
services.Start(local_experiment, *calibration);
|
||||
services.Trigger();
|
||||
ul.unlock();
|
||||
// Allow to cancel/abort during the pedestal data collection
|
||||
// Must ensure that while state is Pedestal, nothing can take lock for longer time, to avoid deadlock
|
||||
auto pedestal_output = services.Stop(*calibration);
|
||||
ul.lock();
|
||||
|
||||
// SetFullMeasurementOutput(pedestal_output);
|
||||
ImportPedestalG0(pedestal_output.receiver());
|
||||
}
|
||||
state = JFJochState::Idle;
|
||||
}
|
||||
|
||||
void JFJochStateMachine::TakePedestalInternalG1(std::unique_lock<std::mutex> &ul, int32_t storage_cell) {
|
||||
state = JFJochState::Pedestal;
|
||||
DiffractionExperiment local_experiment(experiment);
|
||||
local_experiment.Mode(DetectorMode::PedestalG1);
|
||||
|
||||
if (local_experiment.GetStorageCellNumber() == 2)
|
||||
local_experiment.StorageCellStart((storage_cell + 15) % 16); // one previous
|
||||
|
||||
if (!cancel_sequence && (local_experiment.GetPedestalG1Frames() > 0)) {
|
||||
services.Start(local_experiment, *calibration);
|
||||
services.Trigger();
|
||||
ul.unlock();
|
||||
// Allow to cancel/abort during the pedestal data collection
|
||||
// Must ensure that while state is Pedestal, nothing can take lock for longer time, to avoid deadlock
|
||||
auto pedestal_output = services.Stop(*calibration);
|
||||
ul.lock();
|
||||
|
||||
// SetFullMeasurementOutput(pedestal_output);
|
||||
ImportPedestal(pedestal_output.receiver(), 1, storage_cell);
|
||||
}
|
||||
state = JFJochState::Idle;
|
||||
}
|
||||
|
||||
void JFJochStateMachine::TakePedestalInternalG2(std::unique_lock<std::mutex> &ul, int32_t storage_cell) {
|
||||
state = JFJochState::Pedestal;
|
||||
DiffractionExperiment local_experiment(experiment);
|
||||
local_experiment.Mode(DetectorMode::PedestalG2);
|
||||
|
||||
if (local_experiment.GetStorageCellNumber() == 2)
|
||||
local_experiment.StorageCellStart((storage_cell + 15) % 16); // one previous
|
||||
|
||||
if (!cancel_sequence && (local_experiment.GetPedestalG2Frames() > 0)) {
|
||||
services.Start(local_experiment, *calibration);
|
||||
services.Trigger();
|
||||
ul.unlock();
|
||||
// Allow to cancel/abort during the pedestal data collection
|
||||
// Must ensure that while state is Pedestal, nothing can take lock for longer time, to avoid deadlock
|
||||
auto pedestal_output = services.Stop(*calibration);
|
||||
ul.lock();
|
||||
|
||||
// SetFullMeasurementOutput(pedestal_output);
|
||||
ImportPedestal(pedestal_output.receiver(), 2, storage_cell);
|
||||
}
|
||||
state = JFJochState::Idle;
|
||||
}
|
||||
|
||||
void JFJochStateMachine::Initialize() {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
if ((state == JFJochState::Measuring) || (state == JFJochState::Pedestal))
|
||||
throw JFJochException(JFJochExceptionCategory::WrongDAQState,
|
||||
"Cannot initialize during measurement");
|
||||
|
||||
logger.Info("Initialize");
|
||||
|
||||
state = JFJochState::Busy;
|
||||
ClearMeasurementStatistics();
|
||||
try {
|
||||
services.On(experiment);
|
||||
} catch (...) {
|
||||
state = JFJochState::Error;
|
||||
throw;
|
||||
}
|
||||
TakePedestalInternalAll(ul);
|
||||
}
|
||||
|
||||
void JFJochStateMachine::Pedestal() {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
if (state != JFJochState::Idle)
|
||||
throw JFJochException(JFJochExceptionCategory::WrongDAQState,"Must be idle to take pedestal");
|
||||
TakePedestalInternalAll(ul);
|
||||
}
|
||||
|
||||
void JFJochStateMachine::Trigger() {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (state == JFJochState::Measuring)
|
||||
services.Trigger();
|
||||
}
|
||||
|
||||
void JFJochStateMachine::Start(const JFJochProtoBuf::DatasetSettings& settings) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
if (state != JFJochState::Idle)
|
||||
throw JFJochException(JFJochExceptionCategory::WrongDAQState,
|
||||
"Must be idle to start measurement");
|
||||
|
||||
if (measurement.valid())
|
||||
measurement.get(); // In case measurement was running - clear thread
|
||||
|
||||
ClearMeasurementStatistics();
|
||||
|
||||
auto mod_settings = settings;
|
||||
SetDatasetDefaults(mod_settings);
|
||||
experiment.LoadDatasetSettings(mod_settings);
|
||||
|
||||
cancel_sequence = false;
|
||||
|
||||
try {
|
||||
state = JFJochState::Busy;
|
||||
services.SetDataProcessingSettings(GetDataProcessingSettings());
|
||||
services.Start(experiment, *calibration);
|
||||
|
||||
state = JFJochState::Measuring;
|
||||
measurement = std::async(std::launch::async, &JFJochStateMachine::WaitTillMeasurementDone, this);
|
||||
} catch (...) {
|
||||
state = JFJochState::Error;
|
||||
services.Abort();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void JFJochStateMachine::SetDatasetDefaults(JFJochProtoBuf::DatasetSettings &settings) {
|
||||
if (settings.detector_distance_mm() <= 0)
|
||||
settings.set_detector_distance_mm(100);
|
||||
if (settings.ntrigger() <= 0)
|
||||
settings.set_ntrigger(1);
|
||||
}
|
||||
|
||||
void JFJochStateMachine::Stop() {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
if (state == JFJochState::Pedestal)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Cannot use the function during pedestal collection");
|
||||
|
||||
c.wait(ul, [&] { return state != JFJochState::Measuring; });
|
||||
|
||||
if (!measurement.valid())
|
||||
return; // This is for unlikely condition of two parallel stops
|
||||
else
|
||||
measurement.get();
|
||||
|
||||
switch (state) {
|
||||
case JFJochState::Inactive:
|
||||
throw JFJochException(JFJochExceptionCategory::WrongDAQState,"Not initialized");
|
||||
case JFJochState::Error:
|
||||
throw JFJochException(JFJochExceptionCategory::WrongDAQState,"Detector in error state");
|
||||
case JFJochState::Measuring:
|
||||
case JFJochState::Busy:
|
||||
case JFJochState::Pedestal:
|
||||
throw JFJochException(JFJochExceptionCategory::WrongDAQState,"Detector in not expected state to end measurment");
|
||||
case JFJochState::Idle:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void JFJochStateMachine::WaitTillMeasurementDone() {
|
||||
try {
|
||||
auto tmp_output = services.Stop(*calibration);
|
||||
SetFullMeasurementOutput(tmp_output);
|
||||
{
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
state = JFJochState::Idle;
|
||||
}
|
||||
} catch (...) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
state = JFJochState::Error;
|
||||
}
|
||||
c.notify_all();
|
||||
}
|
||||
|
||||
void JFJochStateMachine::Abort() {
|
||||
// This is inconsistency in naming - need to solve later
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if ((state == JFJochState::Pedestal) || (state == JFJochState::Measuring)) {
|
||||
services.Abort();
|
||||
cancel_sequence = true;
|
||||
}
|
||||
}
|
||||
|
||||
void JFJochStateMachine::Cancel() {
|
||||
// This is inconsistency in naming - need to solve later
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if ((state == JFJochState::Pedestal) || (state == JFJochState::Measuring)) {
|
||||
services.Cancel();
|
||||
cancel_sequence = true;
|
||||
}
|
||||
}
|
||||
|
||||
void JFJochStateMachine::DebugOnly_SetState(JFJochState in_state) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
state = in_state;
|
||||
}
|
||||
|
||||
void JFJochStateMachine::Deactivate() {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
try {
|
||||
if (measurement.valid())
|
||||
measurement.get();
|
||||
services.Off();
|
||||
state = JFJochState::Inactive;
|
||||
} catch (...) {
|
||||
state = JFJochState::Error;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
JFJochStateMachine::~JFJochStateMachine() {
|
||||
try {
|
||||
if (measurement.valid())
|
||||
measurement.get();
|
||||
} catch (...) {}
|
||||
}
|
||||
|
||||
JFJochProtoBuf::BrokerFullStatus JFJochStateMachine::GetFullMeasurementOutput() const {
|
||||
std::unique_lock<std::mutex> ul(last_receiver_output_mutex);
|
||||
return last_receiver_output;
|
||||
}
|
||||
|
||||
void JFJochStateMachine::SetFullMeasurementOutput(JFJochProtoBuf::BrokerFullStatus &output) {
|
||||
std::unique_lock<std::mutex> ul(last_receiver_output_mutex);
|
||||
last_receiver_output = output;
|
||||
|
||||
auto tmp = JFJochProtoBuf::MeasurementStatistics(); // reset last measurement statistics
|
||||
|
||||
tmp.set_file_prefix(experiment.GetFilePrefix());
|
||||
tmp.set_detector_width(experiment.GetXPixelsNum());
|
||||
tmp.set_detector_height(experiment.GetYPixelsNum());
|
||||
tmp.set_detector_pixel_depth(experiment.GetPixelDepth());
|
||||
|
||||
if (last_receiver_output.has_receiver()) {
|
||||
tmp.set_compression_ratio(output.receiver().compressed_ratio());
|
||||
tmp.set_collection_efficiency(output.receiver().efficiency());
|
||||
tmp.set_images_collected(output.receiver().images_sent());
|
||||
tmp.set_cancelled(output.receiver().cancelled());
|
||||
tmp.set_max_image_number_sent(output.receiver().max_image_number_sent());
|
||||
tmp.set_max_receive_delay(output.receiver().max_receive_delay());
|
||||
|
||||
if (output.receiver().has_indexing_rate())
|
||||
tmp.set_indexing_rate(output.receiver().indexing_rate());
|
||||
}
|
||||
|
||||
if (last_receiver_output.writer_size() > 0) {
|
||||
double writer_perf = 0.0;
|
||||
int64_t images_written = 0;
|
||||
|
||||
for (const auto &i: output.writer()) {
|
||||
writer_perf += i.performance_mbs();
|
||||
images_written += i.nimages();
|
||||
}
|
||||
|
||||
tmp.set_writer_performance_mbs(writer_perf);
|
||||
tmp.set_images_written(images_written);
|
||||
}
|
||||
|
||||
measurement_statistics = tmp;
|
||||
}
|
||||
|
||||
void JFJochStateMachine::ClearMeasurementStatistics() {
|
||||
std::unique_lock<std::mutex> ul(last_receiver_output_mutex);
|
||||
measurement_statistics.reset();
|
||||
}
|
||||
|
||||
std::optional<JFJochProtoBuf::MeasurementStatistics> JFJochStateMachine::GetMeasurementStatistics() const {
|
||||
std::unique_lock<std::mutex> ul(last_receiver_output_mutex);
|
||||
return measurement_statistics;
|
||||
}
|
||||
|
||||
JFCalibration JFJochStateMachine::GetCalibration() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (state == JFJochState::Inactive)
|
||||
throw JFJochException(JFJochExceptionCategory::WrongDAQState, "Detector not calibrated");
|
||||
return *calibration;
|
||||
}
|
||||
|
||||
void JFJochStateMachine::LoadMask(const std::vector<uint32_t> &vec, uint32_t bit) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (state == JFJochState::Inactive)
|
||||
throw JFJochException(JFJochExceptionCategory::WrongDAQState,
|
||||
"Detector not calibrated");
|
||||
if (state != JFJochState::Idle)
|
||||
throw JFJochException(JFJochExceptionCategory::WrongDAQState,
|
||||
"Cannot load mask if detector is not idle");
|
||||
calibration->LoadMask(experiment, vec, bit);
|
||||
}
|
||||
|
||||
JFJochProtoBuf::JFCalibrationStatistics JFJochStateMachine::GetCalibrationStatistics() const {
|
||||
std::unique_lock<std::mutex> ul(calibration_statistics_mutex);
|
||||
return calibration_statistics;
|
||||
}
|
||||
|
||||
void JFJochStateMachine::SetCalibrationStatistics(const JFJochProtoBuf::JFCalibrationStatistics &input) {
|
||||
std::unique_lock<std::mutex> ul(calibration_statistics_mutex);
|
||||
calibration_statistics = input;
|
||||
}
|
||||
|
||||
JFJochProtoBuf::DetectorSettings JFJochStateMachine::GetDetectorSettings() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
return experiment.GetDetectorSettings();
|
||||
}
|
||||
|
||||
void JFJochStateMachine::SetDetectorSettings(const JFJochProtoBuf::DetectorSettings &settings) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
switch (state) {
|
||||
case JFJochState::Inactive:
|
||||
case JFJochState::Error:
|
||||
experiment.LoadDetectorSettings(settings);
|
||||
break;
|
||||
case JFJochState::Idle:
|
||||
experiment.LoadDetectorSettings(settings);
|
||||
TakePedestalInternalAll(ul);
|
||||
break;
|
||||
case JFJochState::Measuring:
|
||||
case JFJochState::Busy:
|
||||
case JFJochState::Pedestal:
|
||||
throw JFJochException(JFJochExceptionCategory::WrongDAQState,
|
||||
"Cannot change detector set during data collection");
|
||||
}
|
||||
}
|
||||
|
||||
DiffractionExperiment &JFJochStateMachine::NotThreadSafe_Experiment() {
|
||||
return experiment;
|
||||
}
|
||||
|
||||
|
||||
JFJochProtoBuf::Image JFJochStateMachine::GetNeXusMask() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (state == JFJochState::Inactive)
|
||||
throw JFJochException(JFJochExceptionCategory::WrongDAQState, "Detector not calibrated");
|
||||
|
||||
JFJochProtoBuf::Image ret;
|
||||
auto mask = calibration->CalculateNexusMask(experiment);
|
||||
|
||||
ret.set_width(experiment.GetXPixelsNum());
|
||||
ret.set_height(experiment.GetYPixelsNum());
|
||||
ret.set_pixel_depth(4);
|
||||
*ret.mutable_data() = {mask.begin(), mask.end()};
|
||||
return ret;
|
||||
}
|
||||
|
||||
JFJochProtoBuf::Image JFJochStateMachine::GetPedestalG0() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (state == JFJochState::Inactive)
|
||||
throw JFJochException(JFJochExceptionCategory::WrongDAQState, "Detector not calibrated");
|
||||
|
||||
JFJochProtoBuf::Image ret;
|
||||
auto pedestal = calibration->GetPedestal(0);
|
||||
|
||||
ret.set_width(experiment.GetXPixelsNum());
|
||||
ret.set_height(experiment.GetYPixelsNum());
|
||||
ret.set_pixel_depth(2);
|
||||
*ret.mutable_data() = {pedestal.begin(), pedestal.end()};
|
||||
return ret;
|
||||
}
|
||||
|
||||
JFJochProtoBuf::Image JFJochStateMachine::GetPedestalG1() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (state == JFJochState::Inactive)
|
||||
throw JFJochException(JFJochExceptionCategory::WrongDAQState, "Detector not calibrated");
|
||||
|
||||
JFJochProtoBuf::Image ret;
|
||||
auto pedestal = calibration->GetPedestal(1);
|
||||
|
||||
ret.set_width(experiment.GetXPixelsNum());
|
||||
ret.set_height(experiment.GetYPixelsNum());
|
||||
ret.set_pixel_depth(2);
|
||||
*ret.mutable_data() = {pedestal.begin(), pedestal.end()};
|
||||
return ret;
|
||||
}
|
||||
|
||||
JFJochProtoBuf::Image JFJochStateMachine::GetPedestalG2() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (state == JFJochState::Inactive)
|
||||
throw JFJochException(JFJochExceptionCategory::WrongDAQState, "Detector not calibrated");
|
||||
|
||||
JFJochProtoBuf::Image ret;
|
||||
auto pedestal = calibration->GetPedestal(2);
|
||||
|
||||
ret.set_width(experiment.GetXPixelsNum());
|
||||
ret.set_height(experiment.GetYPixelsNum());
|
||||
ret.set_pixel_depth(2);
|
||||
*ret.mutable_data() = {pedestal.begin(), pedestal.end()};
|
||||
return ret;
|
||||
}
|
||||
|
||||
JFJochProtoBuf::BrokerStatus JFJochStateMachine::GetStatus() const {
|
||||
JFJochProtoBuf::BrokerStatus ret;
|
||||
|
||||
switch (state) {
|
||||
case JFJochState::Inactive:
|
||||
ret.set_broker_state(JFJochProtoBuf::NOT_INITIALIZED);
|
||||
break;
|
||||
case JFJochState::Idle:
|
||||
ret.set_broker_state(JFJochProtoBuf::IDLE);
|
||||
break;
|
||||
case JFJochState::Measuring:
|
||||
ret.set_broker_state(JFJochProtoBuf::DATA_COLLECTION);
|
||||
break;
|
||||
case JFJochState::Error:
|
||||
ret.set_broker_state(JFJochProtoBuf::ERROR);
|
||||
break;
|
||||
case JFJochState::Busy:
|
||||
ret.set_broker_state(JFJochProtoBuf::BUSY);
|
||||
break;
|
||||
case JFJochState::Pedestal:
|
||||
ret.set_broker_state(JFJochProtoBuf::PEDESTAL);
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
auto rcv_status = services.GetReceiverStatus();
|
||||
if (rcv_status.has_progress())
|
||||
ret.set_progress(rcv_status.progress());
|
||||
if (rcv_status.has_indexing_rate())
|
||||
ret.set_indexing_rate(rcv_status.indexing_rate());
|
||||
} catch (JFJochException &e) {} // ignore exception in getting receiver status (don't really care, e.g. if receiver is down)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
JFJochProtoBuf::ReceiverDataProcessingPlots JFJochStateMachine::GetPlots() const {
|
||||
return services.GetPlots();
|
||||
}
|
||||
|
||||
void JFJochStateMachine::SetDataProcessingSettings(const JFJochProtoBuf::DataProcessingSettings &settings) {
|
||||
std::unique_lock<std::mutex> ul(data_processing_settings_mutex);
|
||||
DiffractionExperiment::CheckDataProcessingSettings(settings);
|
||||
data_processing_settings = settings;
|
||||
services.SetDataProcessingSettings(data_processing_settings);
|
||||
}
|
||||
|
||||
JFJochProtoBuf::DataProcessingSettings JFJochStateMachine::GetDataProcessingSettings() const {
|
||||
std::unique_lock<std::mutex> ul(data_processing_settings_mutex);
|
||||
return data_processing_settings;
|
||||
}
|
||||
|
||||
JFJochState JFJochStateMachine::GetState() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
void JFJochStateMachine::LoadGainFile(const std::string &filename) {
|
||||
gain_calibration.emplace_back(filename);
|
||||
}
|
||||
106
broker/JFJochStateMachine.h
Normal file
106
broker/JFJochStateMachine.h
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_JFJOCHSTATEMACHINE_H
|
||||
#define JUNGFRAUJOCH_JFJOCHSTATEMACHINE_H
|
||||
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <future>
|
||||
#include <optional>
|
||||
|
||||
#include "../common/DiffractionExperiment.h"
|
||||
#include "../jungfrau/JFCalibration.h"
|
||||
#include "../common/Logger.h"
|
||||
|
||||
#include "JFJochServices.h"
|
||||
|
||||
enum class JFJochState {Inactive, Idle, Measuring, Error, Busy, Pedestal};
|
||||
|
||||
class JFJochStateMachine {
|
||||
Logger &logger;
|
||||
JFJochServices &services;
|
||||
|
||||
mutable std::mutex m;
|
||||
std::condition_variable c;
|
||||
// mutex m is protecting:
|
||||
DiffractionExperiment experiment;
|
||||
volatile JFJochState state = JFJochState::Inactive;
|
||||
volatile bool cancel_sequence = false;
|
||||
std::unique_ptr<JFCalibration> calibration;
|
||||
std::vector<JFModuleGainCalibration> gain_calibration;
|
||||
|
||||
std::future<void> measurement;
|
||||
|
||||
mutable std::mutex calibration_statistics_mutex;
|
||||
JFJochProtoBuf::JFCalibrationStatistics calibration_statistics;
|
||||
|
||||
mutable std::mutex last_receiver_output_mutex;
|
||||
JFJochProtoBuf::BrokerFullStatus last_receiver_output;
|
||||
std::optional<JFJochProtoBuf::MeasurementStatistics> measurement_statistics;
|
||||
void SetFullMeasurementOutput(JFJochProtoBuf::BrokerFullStatus &output);
|
||||
void ClearMeasurementStatistics();
|
||||
JFJochProtoBuf::BrokerFullStatus GetFullMeasurementOutput() const;
|
||||
|
||||
mutable std::mutex data_processing_settings_mutex;
|
||||
JFJochProtoBuf::DataProcessingSettings data_processing_settings;
|
||||
|
||||
// Private functions assume that lock m is acquired
|
||||
void SetDatasetDefaults(JFJochProtoBuf::DatasetSettings& settings);
|
||||
void WaitTillMeasurementDone();
|
||||
void ImportPedestal(const JFJochProtoBuf::ReceiverOutput &receiver_output, size_t gain_level, size_t storage_cell = 0);
|
||||
void ImportPedestalG0(const JFJochProtoBuf::ReceiverOutput &receiver_output);
|
||||
|
||||
void TakePedestalInternalAll(std::unique_lock<std::mutex> &ul);
|
||||
void TakePedestalInternalG0(std::unique_lock<std::mutex> &ul);
|
||||
void TakePedestalInternalG1(std::unique_lock<std::mutex> &ul, int32_t storage_cell = 0);
|
||||
void TakePedestalInternalG2(std::unique_lock<std::mutex> &ul, int32_t storage_cell = 0);
|
||||
public:
|
||||
JFJochStateMachine(JFJochServices &in_services, Logger &logger);
|
||||
~JFJochStateMachine();
|
||||
|
||||
void Initialize();
|
||||
void Pedestal();
|
||||
void Deactivate();
|
||||
void Start(const JFJochProtoBuf::DatasetSettings& settings);
|
||||
void Stop();
|
||||
void Trigger();
|
||||
|
||||
void Abort();
|
||||
void Cancel();
|
||||
|
||||
void LoadMask(const std::vector<uint32_t> &vec, uint32_t bit);
|
||||
void SetCalibrationStatistics(const JFJochProtoBuf::JFCalibrationStatistics &input);
|
||||
JFCalibration GetCalibration() const;
|
||||
|
||||
JFJochProtoBuf::DetectorSettings GetDetectorSettings() const;
|
||||
void SetDetectorSettings(const JFJochProtoBuf::DetectorSettings& settings);
|
||||
|
||||
// return by value to ensure thread safety
|
||||
std::optional<JFJochProtoBuf::MeasurementStatistics> GetMeasurementStatistics() const;
|
||||
JFJochProtoBuf::JFCalibrationStatistics GetCalibrationStatistics() const;
|
||||
|
||||
JFJochProtoBuf::BrokerStatus GetStatus() const;
|
||||
JFJochProtoBuf::ReceiverDataProcessingPlots GetPlots() const;
|
||||
|
||||
JFJochProtoBuf::Image GetNeXusMask() const;
|
||||
JFJochProtoBuf::Image GetPedestalG0() const;
|
||||
JFJochProtoBuf::Image GetPedestalG1() const;
|
||||
JFJochProtoBuf::Image GetPedestalG2() const;
|
||||
|
||||
void SetDataProcessingSettings(const JFJochProtoBuf::DataProcessingSettings& settings);
|
||||
JFJochProtoBuf::DataProcessingSettings GetDataProcessingSettings() const;
|
||||
|
||||
JFJochState GetState() const;
|
||||
|
||||
// Not thread safe - only for configuration in serial context
|
||||
DiffractionExperiment& NotThreadSafe_Experiment();
|
||||
|
||||
// Function for debug only - UNSAFE for real operation
|
||||
void DebugOnly_SetState(JFJochState state);
|
||||
|
||||
void LoadGainFile(const std::string& filename);
|
||||
};
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_JFJOCHSTATEMACHINE_H
|
||||
100
broker/jfjoch_broker.cpp
Normal file
100
broker/jfjoch_broker.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <fstream>
|
||||
#include <random>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "../common/Logger.h"
|
||||
#include "../common/NetworkAddressConvert.h"
|
||||
|
||||
#include "JFJochBroker.h"
|
||||
#include "../grpc/gRPCServer_Template.h"
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
if (argc > 3) {
|
||||
std::cout << "Usage ./jfjoch_broker {<JSON config> {<TCP gRPC port>}}" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
uint16_t grpc_port = 5232;
|
||||
if (argc >= 3) grpc_port = atoi(argv[2]);
|
||||
|
||||
Logger logger("jfjoch_broker");
|
||||
|
||||
nlohmann::json input;
|
||||
if (argc > 1) {
|
||||
std::ifstream file(argv[1]);
|
||||
try {
|
||||
input = nlohmann::json::parse(file);
|
||||
} catch (const nlohmann::json::exception &e) {
|
||||
logger.Error("JSON Parsing exception: " + std::string(e.what()));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
DiffractionExperiment experiment(2, {4,4}, 8, 36);
|
||||
experiment.PedestalG0Frames(2000).PedestalG1Frames(300).PedestalG2Frames(300);
|
||||
experiment.MaskChipEdges(true).MaskModuleEdges(true);
|
||||
|
||||
try {
|
||||
experiment.SourceName(input.at("source_name").get<std::string>());
|
||||
experiment.SourceNameShort(input.at("source_name_short").get<std::string>());
|
||||
} catch (std::exception &e) {
|
||||
logger.Warning("Source name metadata error: {}", e.what() );
|
||||
}
|
||||
|
||||
try {
|
||||
experiment.InstrumentName(input.at("instrument_name").get<std::string>());
|
||||
experiment.InstrumentNameShort(input.at("instrument_name_short").get<std::string>());
|
||||
} catch (std::exception &e) {
|
||||
logger.Warning("Instrument name metadata error: {}", e.what() );
|
||||
}
|
||||
|
||||
std::random_device generator;
|
||||
|
||||
uint8_t base_ipv4_addr_net = UINT8_MAX;
|
||||
if (input.contains("ipv4_subnet"))
|
||||
base_ipv4_addr_net = input["ipv4_subnet"];
|
||||
|
||||
std::uniform_int_distribution<uint8_t> ipv4_subnet_distribution(0,UINT8_MAX-1);
|
||||
if (base_ipv4_addr_net == UINT8_MAX)
|
||||
base_ipv4_addr_net = ipv4_subnet_distribution(generator);
|
||||
experiment.IPv4Subnet("10.1." + std::to_string(base_ipv4_addr_net) + ".0");
|
||||
logger.Info("Base IPv4 address for FPGA and detector modules: "
|
||||
+ IPv4AddressToStr(experiment.GetDestIPv4Address(0)));
|
||||
|
||||
JFJochBroker broker(experiment);
|
||||
|
||||
if (input.contains("receiver_addr"))
|
||||
broker.Services().Receiver(input["receiver_addr"]);
|
||||
|
||||
if (input.contains("writer")) {
|
||||
if (input["writer"].is_array()) {
|
||||
for (const auto &j: input["writer"])
|
||||
broker.Services().Writer(j["addr_grpc"], j["addr_zmq"]);
|
||||
} else {
|
||||
broker.Services().Writer(input["writer"]["addr_grpc"], input["writer"]["addr_zmq"]);
|
||||
}
|
||||
}
|
||||
if (input.contains("detector_addr"))
|
||||
broker.Services().Detector(input["detector_addr"]);
|
||||
|
||||
|
||||
if (input.contains("gain_file") && (input["gain_file"].is_array())) {
|
||||
for (int i = 0; i < input["gain_file"].size(); i++) {
|
||||
try {
|
||||
broker.LoadGainFile(input["gain_file"][i].get<std::string>());
|
||||
} catch (const std::exception &e) {
|
||||
logger.ErrorException(e);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string grpc_addr = "0.0.0.0:" + std::to_string(grpc_port);
|
||||
|
||||
auto server = gRPCServer(grpc_addr, broker);
|
||||
logger.Info("gRPC configuration listening on address " + grpc_addr);
|
||||
server->Wait();
|
||||
}
|
||||
27
cmake/FindZeroMQ.cmake
Normal file
27
cmake/FindZeroMQ.cmake
Normal file
@@ -0,0 +1,27 @@
|
||||
#From: https://github.com/zeromq/cppzmq/
|
||||
set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON)
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(PC_LIBZMQ QUIET libzmq)
|
||||
|
||||
set(ZeroMQ_VERSION ${PC_LIBZMQ_VERSION})
|
||||
find_library(ZeroMQ_LIBRARY NAMES libzmq.so libzmq.dylib libzmq.dll
|
||||
PATHS ${PC_LIBZMQ_LIBDIR} ${PC_LIBZMQ_LIBRARY_DIRS})
|
||||
find_library(ZeroMQ_STATIC_LIBRARY NAMES libzmq-static.a libzmq.a libzmq.dll.a
|
||||
PATHS ${PC_LIBZMQ_LIBDIR} ${PC_LIBZMQ_LIBRARY_DIRS})
|
||||
|
||||
if(ZeroMQ_LIBRARY OR ZeroMQ_STATIC_LIBRARY)
|
||||
set(ZeroMQ_FOUND ON)
|
||||
endif()
|
||||
|
||||
if (TARGET libzmq)
|
||||
# avoid errors defining targets twice
|
||||
return()
|
||||
endif()
|
||||
|
||||
add_library(libzmq SHARED IMPORTED)
|
||||
set_property(TARGET libzmq PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${PC_LIBZMQ_INCLUDE_DIRS})
|
||||
set_property(TARGET libzmq PROPERTY IMPORTED_LOCATION ${ZeroMQ_LIBRARY})
|
||||
|
||||
add_library(libzmq-static STATIC IMPORTED ${PC_LIBZMQ_INCLUDE_DIRS})
|
||||
set_property(TARGET libzmq-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${PC_LIBZMQ_INCLUDE_DIRS})
|
||||
set_property(TARGET libzmq-static PROPERTY IMPORTED_LOCATION ${ZeroMQ_STATIC_LIBRARY})
|
||||
63
common/CMakeLists.txt
Normal file
63
common/CMakeLists.txt
Normal file
@@ -0,0 +1,63 @@
|
||||
# git header
|
||||
# the commit's SHA1, and whether the building workspace was dirty or not
|
||||
|
||||
FIND_PACKAGE(Git)
|
||||
|
||||
EXECUTE_PROCESS(COMMAND
|
||||
"${GIT_EXECUTABLE}" describe --match=NeVeRmAtCh --always --abbrev=8
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE GIT_SHA1
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
# the date of the commit
|
||||
EXECUTE_PROCESS(COMMAND
|
||||
"${GIT_EXECUTABLE}" log -1 --format=%ad --date=local
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE GIT_DATE
|
||||
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
MESSAGE(STATUS "Git SHA1: ${GIT_SHA1}")
|
||||
MESSAGE(STATUS "Git date: ${GIT_DATE}")
|
||||
|
||||
CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/GitInfo.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/GitInfo.cpp" @ONLY)
|
||||
|
||||
ADD_LIBRARY( CommonFunctions STATIC
|
||||
Logger.cpp Logger.h
|
||||
Coord.cpp Coord.h
|
||||
DiffractionExperiment.cpp DiffractionExperiment.h
|
||||
RawToConvertedGeometry.h
|
||||
JFJochException.h
|
||||
Definitions.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/GitInfo.cpp GitInfo.h
|
||||
FrameTransformation.cpp FrameTransformation.h
|
||||
ZMQWrappers.cpp ZMQWrappers.h
|
||||
ThreadSafeFIFO.h
|
||||
ZMQPreviewPublisher.cpp ZMQPreviewPublisher.h
|
||||
ZMQImagePusher.cpp ZMQImagePusher.h
|
||||
RadialIntegration.cpp RadialIntegration.h
|
||||
DiffractionSpot.cpp DiffractionSpot.h
|
||||
StrongPixelSet.cpp StrongPixelSet.h
|
||||
Latch.cpp Latch.h
|
||||
RadialIntegrationMapping.cpp RadialIntegrationMapping.h
|
||||
StatusVector.h
|
||||
ImagePusher.cpp ImagePusher.h
|
||||
TestImagePusher.cpp TestImagePusher.h
|
||||
SpotToSave.h
|
||||
NetworkAddressConvert.h NetworkAddressConvert.cpp
|
||||
WriteTIFF.cpp WriteTIFF.h
|
||||
grpcToJson.h jsonToGrpc.h to_fixed.h
|
||||
GPUImageAnalysis.h GPUImageAnalysis.cu
|
||||
DiffractionExperiment.h DiffractionGeometry.cpp)
|
||||
|
||||
FIND_LIBRARY(CUDART_LIBRARY cudart_static PATHS ${CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES} REQUIRED)
|
||||
|
||||
TARGET_LINK_LIBRARIES(CommonFunctions Compression FrameSerialize libzmq JFCalibration JFJochProtoBuf ${CUDART_LIBRARY} -lrt)
|
||||
|
||||
IF(HAS_NUMAIF AND NUMA_LIBRARY)
|
||||
TARGET_COMPILE_DEFINITIONS(CommonFunctions PRIVATE -DJFJOCH_USE_NUMA)
|
||||
TARGET_LINK_LIBRARIES(CommonFunctions ${NUMA_LIBRARY})
|
||||
ENDIF()
|
||||
|
||||
FIND_PACKAGE(TIFF REQUIRED)
|
||||
FIND_LIBRARY(TIFFXX NAMES tiffxx REQUIRED DOC "Tiff C++ library")
|
||||
TARGET_LINK_LIBRARIES(CommonFunctions TIFF::TIFF ${TIFFXX})
|
||||
139
common/Coord.cpp
Normal file
139
common/Coord.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <cmath>
|
||||
#include "Coord.h"
|
||||
#include "JFJochException.h"
|
||||
|
||||
Coord::Coord() {
|
||||
x = 0.0; y = 0.0; z = 0.0;
|
||||
}
|
||||
|
||||
Coord::Coord(const float in[3]) {
|
||||
x = in[0];
|
||||
y = in[1];
|
||||
z = in[2];
|
||||
}
|
||||
|
||||
Coord::Coord(float in_x, float in_y, float in_z) {
|
||||
x = in_x;
|
||||
y = in_y;
|
||||
z = in_z;
|
||||
}
|
||||
|
||||
Coord Coord::operator+(const Coord &in) const {
|
||||
return Coord(this->x+in.x, this->y+in.y, this->z+in.z);
|
||||
}
|
||||
|
||||
Coord Coord::operator-(const Coord &in) const {
|
||||
return Coord(this->x-in.x, this->y-in.y, this->z-in.z);
|
||||
}
|
||||
|
||||
Coord Coord::operator*(float in) const {
|
||||
return Coord(this->x*in, this->y*in, this->z*in);
|
||||
}
|
||||
|
||||
Coord Coord::operator/(float in) const {
|
||||
return Coord(this->x/in, this->y/in, this->z/in);
|
||||
};
|
||||
|
||||
Coord Coord::operator-() const {
|
||||
return Coord(- this->x, -this->y, -this->z);
|
||||
}
|
||||
|
||||
Coord& Coord::operator+=(const Coord &in) {
|
||||
this->x += in.x;
|
||||
this->y += in.y;
|
||||
this->z += in.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Coord& Coord::operator-=(const Coord &in) {
|
||||
this->x -= in.x;
|
||||
this->y -= in.y;
|
||||
this->z -= in.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Coord& Coord::operator*=(float in) {
|
||||
this->x *= in;
|
||||
this->y *= in;
|
||||
this->z *= in;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Coord& Coord::operator/=(float in) {
|
||||
this->x /= in;
|
||||
this->y /= in;
|
||||
this->z /= in;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Coord Coord::operator%(const Coord &in) const {
|
||||
return Coord(this->y * in.z - this->z * in.y,
|
||||
this->z * in.x - this->x * in.z,
|
||||
this->x * in.y - this->y * in.x);
|
||||
}; // Cross product
|
||||
|
||||
float Coord::operator*(const Coord &in) const {
|
||||
return this->x * in.x + this->y * in.y + this->z * in.z;
|
||||
};
|
||||
|
||||
bool Coord::operator==(const Coord &other) const {
|
||||
if ((this->x == other.x) && (this->y == other.y) && (this->z == other.z))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
float Coord::Length() const {
|
||||
return sqrt(this->x*this->x + this->y*this->y + this->z*this->z);
|
||||
}
|
||||
|
||||
Coord Coord::Normalize() const {
|
||||
float len = Length();
|
||||
return Coord(this->x/len, this->y/len, this->z/len);
|
||||
}
|
||||
|
||||
Coord operator*(float in1, const Coord& in2) {
|
||||
return in2 * in1;
|
||||
}
|
||||
|
||||
const float& Coord::operator[](int64_t val) const {
|
||||
switch (val) {
|
||||
case 0:
|
||||
return x;
|
||||
case 1:
|
||||
return y;
|
||||
case 2:
|
||||
return z;
|
||||
default:
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Coord index must be in range 0-2");
|
||||
}
|
||||
}
|
||||
|
||||
float& Coord::operator[](int64_t val) {
|
||||
switch (val) {
|
||||
case 0:
|
||||
return x;
|
||||
case 1:
|
||||
return y;
|
||||
case 2:
|
||||
return z;
|
||||
default:
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Coord index must be in range 0-2");
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream &operator<<( std::ostream &output, const Coord &in ) {
|
||||
output << in.x << " " << in.y << " " << in.z;
|
||||
return output;
|
||||
}
|
||||
|
||||
float angle_deg(const Coord &c1, const Coord &c2) {
|
||||
float cos_ang = c1 * c2 / (c1.Length() * c2.Length());
|
||||
return acos(cos_ang) * (180.0 / M_PI);
|
||||
}
|
||||
|
||||
46
common/Coord.h
Normal file
46
common/Coord.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef INDEX_COORD_H
|
||||
#define INDEX_COORD_H
|
||||
|
||||
#include <ostream>
|
||||
|
||||
class Coord {
|
||||
public:
|
||||
float x,y,z;
|
||||
Coord();
|
||||
Coord(const float in[3]);
|
||||
Coord(float x, float y, float z);
|
||||
|
||||
Coord operator+(const Coord &in) const;
|
||||
Coord operator-(const Coord &in) const;
|
||||
Coord operator*(float in) const;
|
||||
Coord operator/(float in) const;
|
||||
Coord operator-() const;
|
||||
|
||||
Coord& operator+=(const Coord &in);
|
||||
Coord& operator-=(const Coord &in);
|
||||
Coord& operator*=(float in);
|
||||
Coord& operator/=(float in);
|
||||
|
||||
bool operator==(const Coord &other) const;
|
||||
|
||||
Coord operator%(const Coord &in) const; // Cross product
|
||||
float operator*(const Coord &in) const; // Dot product
|
||||
|
||||
const float& operator[](int64_t val) const;
|
||||
float& operator[](int64_t val);
|
||||
|
||||
float Length() const;
|
||||
Coord Normalize() const;
|
||||
|
||||
friend std::ostream &operator<<( std::ostream &output, const Coord &in );
|
||||
};
|
||||
|
||||
Coord operator*(float in1, const Coord& in2);
|
||||
|
||||
float angle_deg(const Coord &in1, const Coord &in2);
|
||||
|
||||
|
||||
#endif //INDEX_COORD_H
|
||||
120
common/Definitions.h
Normal file
120
common/Definitions.h
Normal file
@@ -0,0 +1,120 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef DEFINITIONS_H
|
||||
#define DEFINITIONS_H
|
||||
|
||||
#define WVL_1A_IN_KEV 12.39854f
|
||||
|
||||
#define DELAY_FRAMES_STOP_AND_QUIT 5
|
||||
#define RAW_MODULE_LINES (512L)
|
||||
#define RAW_MODULE_COLS (1024L)
|
||||
#define RAW_MODULE_SIZE (RAW_MODULE_LINES * RAW_MODULE_COLS)
|
||||
|
||||
#define CONVERTED_MODULE_LINES (514L)
|
||||
#define CONVERTED_MODULE_COLS (1030L)
|
||||
#define CONVERTED_MODULE_SIZE (CONVERTED_MODULE_LINES * CONVERTED_MODULE_COLS)
|
||||
#define JUNGFRAU_PACKET_SIZE_BYTES (8192)
|
||||
|
||||
#define FPGA_BUFFER_LOCATION_SIZE (RAW_MODULE_SIZE * sizeof(short))
|
||||
|
||||
#define MIN_COUNT_TIME_IN_US 10
|
||||
#define MIN_FRAME_TIME_HALF_SPEED_IN_US 1000
|
||||
#define MIN_FRAME_TIME_FULL_SPEED_IN_US 470
|
||||
#define MAX_FRAME_TIME 2000
|
||||
#define MAX_SUMMATION 1000
|
||||
|
||||
#define READOUT_TIME_IN_US 20
|
||||
|
||||
#define GRPC_MAX_MESSAGE_SIZE (1000L*1000L*1000L)
|
||||
#define MIN_ENERGY 0.1
|
||||
#define MAX_ENERGY 25.0
|
||||
|
||||
#define PEDESTAL_WINDOW_SIZE 128
|
||||
#define PEDESTAL_WRONG 16384
|
||||
|
||||
#define FRAME_TIME_PEDE_G1G2_IN_US (10*1000)
|
||||
|
||||
#define SENSOR_THICKNESS_IN_UM 320.0
|
||||
#define PIXEL_SIZE_IN_UM 75.0
|
||||
#define PIXEL_SIZE_IN_MM (PIXEL_SIZE_IN_UM/1000.0)
|
||||
#define SENSOR_MATERIAL "Si"
|
||||
|
||||
#define GAIN_G0_MULTIPLIER 32
|
||||
#define GAIN_G1_MULTIPLIER (-1)
|
||||
#define GAIN_G2_MULTIPLIER (-1)
|
||||
|
||||
#define DEFAULT_G0_FACTOR (41.0)
|
||||
#define DEFAULT_G1_FACTOR (-1.439)
|
||||
#define DEFAULT_G2_FACTOR (-0.1145)
|
||||
|
||||
// For FPGA
|
||||
/* This number is unique and is declared in ~snap/ActionTypes.md */
|
||||
#define ACTION_TYPE 0x52324158
|
||||
#define RELEASE_LEVEL 0x0033
|
||||
|
||||
#define MODE_CONV 0x0001L
|
||||
#define MODE_INTERNAL_PACKET_GEN 0x0002L
|
||||
|
||||
#define TASK_NO_DATA_STREAM UINT16_MAX
|
||||
|
||||
#define PIXEL_OUT_SATURATION (INT16_MAX)
|
||||
#define PIXEL_OUT_LOST (INT16_MIN)
|
||||
#define PIXEL_OUT_0xFFFF (INT16_MIN)
|
||||
#define PIXEL_OUT_G1_SATURATION (INT16_MIN)
|
||||
#define PIXEL_OUT_GAINBIT_2 (INT16_MIN)
|
||||
|
||||
#define LOAD_CALIBRATION_BRAM_SIZE 1024
|
||||
|
||||
// FPGA register map
|
||||
|
||||
#define ADDR_CTRL_REGISTER 0x0000
|
||||
#define ADDR_GIT_SHA1 0x000C
|
||||
#define ADDR_ACTION_TYPE 0x0010
|
||||
#define ADDR_RELEASE_LEVEL 0x0014
|
||||
#define ADDR_HBM_TEMP 0x0018
|
||||
#define ADDR_HBM_MAX_TEMP 0x001C
|
||||
#define ADDR_MAX_MODULES_FPGA 0x0020
|
||||
#define ADDR_MODS_INT_PKT_GEN 0x0024
|
||||
#define ADDR_STALLS_HOST_LO 0x0028
|
||||
#define ADDR_STALLS_HOST_HI 0x002C
|
||||
#define ADDR_STALLS_HBM_LO 0x0030
|
||||
#define ADDR_STALLS_HBM_HI 0x0034
|
||||
#define ADDR_FIFO_STATUS 0x0038
|
||||
|
||||
#define ADDR_PACKETS_PROC_LO 0x0040
|
||||
#define ADDR_PACKETS_PROC_HI 0x0044
|
||||
#define ADDR_PACKETS_ETH_LO 0x0048
|
||||
#define ADDR_PACKETS_ETH_HI 0x004C
|
||||
#define ADDR_PACKETS_ICMP_LO 0x0050
|
||||
#define ADDR_PACKETS_ICMP_HI 0x0054
|
||||
#define ADDR_PACKETS_UDP_LO 0x0058
|
||||
#define ADDR_PACKETS_UDP_HI 0x005C
|
||||
#define ADDR_PACKETS_SLS_LO 0x0060
|
||||
#define ADDR_PACKETS_SLS_HI 0x0064
|
||||
#define ADDR_PACKETS_ERR_LEN 0x0068
|
||||
#define ADDR_PACKETS_ERR_ETH 0x006C
|
||||
|
||||
#define ADDR_MAC_ADDR_LO 0x0080
|
||||
#define ADDR_MAC_ADDR_HI 0x0084
|
||||
#define ADDR_IPV4_ADDR 0x0088
|
||||
#define ADDR_NMODULES 0x008C
|
||||
#define ADDR_DATA_COL_MODE 0x0090
|
||||
#define ADDR_ONE_OVER_ENERGY 0x0094
|
||||
#define ADDR_NFRAMES 0x0098
|
||||
#define ADDR_NSTORAGE_CELLS 0x009C
|
||||
|
||||
#define ADDR_MAILBOX_WRDATA 0x00
|
||||
#define ADDR_MAILBOX_RDDATA 0x08
|
||||
#define ADDR_MAILBOX_STATUS 0x10
|
||||
#define ADDR_MAILBOX_SIT 0x18
|
||||
#define ADDR_MAILBOX_RIT 0x1C
|
||||
|
||||
#define MAILBOX_EMPTY (1 << 0)
|
||||
#define MAILBOX_FULL (1 << 1)
|
||||
#define MAILBOX_STA (1 << 2)
|
||||
#define MAILBOX_RTA (1 << 3)
|
||||
|
||||
#define CTRL_REGISTER_IDLE (1<<1u)
|
||||
|
||||
#endif //DEFINITIONS_H
|
||||
1255
common/DiffractionExperiment.cpp
Normal file
1255
common/DiffractionExperiment.cpp
Normal file
File diff suppressed because it is too large
Load Diff
229
common/DiffractionExperiment.h
Normal file
229
common/DiffractionExperiment.h
Normal file
@@ -0,0 +1,229 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef DIFFRACTIONEXPERIMENT_H
|
||||
#define DIFFRACTIONEXPERIMENT_H
|
||||
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
#include <mutex>
|
||||
|
||||
#include <jfjoch.pb.h>
|
||||
|
||||
#include "../compression/CompressionAlgorithmEnum.h"
|
||||
|
||||
#include "UnitCell.h"
|
||||
#include "Coord.h"
|
||||
#include "Definitions.h"
|
||||
#include "../frame_serialize/StartMessage.h"
|
||||
#include "../frame_serialize/EndMessage.h"
|
||||
|
||||
enum class DetectorMode : int {
|
||||
Conversion, Raw, PedestalG0, PedestalG1, PedestalG2
|
||||
};
|
||||
|
||||
class DiffractionExperiment {
|
||||
JFJochProtoBuf::DatasetSettings dataset;
|
||||
JFJochProtoBuf::InternalSettings internal;
|
||||
|
||||
void CalculateGeometry(int64_t horizontal_stacking, const std::vector<int64_t> &v, int64_t gap_x, int64_t gap_y,
|
||||
bool mirror_y_in_conversion);
|
||||
constexpr static const int64_t max_spot_count = 100;
|
||||
DiffractionExperiment& SetUnitCell(const JFJochProtoBuf::UnitCell &input);
|
||||
public:
|
||||
// Public methods are atomic
|
||||
DiffractionExperiment();
|
||||
DiffractionExperiment(int64_t horizontal_stacking, const std::vector<int64_t> &v, int64_t gap_x = 0, int64_t gap_y = 0,
|
||||
bool mirror_y_in_conversion = true);
|
||||
explicit DiffractionExperiment(const JFJochProtoBuf::JungfraujochSettings &settings);
|
||||
|
||||
// Methods below can be chained together
|
||||
DiffractionExperiment& Mode(DetectorMode input);
|
||||
|
||||
DiffractionExperiment& Import(const JFJochProtoBuf::JungfraujochSettings &settings);
|
||||
|
||||
DiffractionExperiment& ImagesPerTrigger(int64_t input);
|
||||
DiffractionExperiment& NumTriggers(int64_t triggers);
|
||||
|
||||
DiffractionExperiment& PedestalG0Frames(int64_t input);
|
||||
DiffractionExperiment& PedestalG1Frames(int64_t input);
|
||||
DiffractionExperiment& PedestalG2Frames(int64_t input);
|
||||
DiffractionExperiment& FrameTime(std::chrono::microseconds frame_time,
|
||||
std::chrono::microseconds in_count_time = std::chrono::microseconds(0));
|
||||
DiffractionExperiment& Summation(int64_t input);
|
||||
DiffractionExperiment& ImageTimeUs(std::chrono::microseconds image_time);
|
||||
|
||||
DiffractionExperiment& PedestalG1G2FrameTime(std::chrono::microseconds input);
|
||||
DiffractionExperiment& PhotonEnergy_keV(float input);
|
||||
DiffractionExperiment& BeamX_pxl(float input);
|
||||
DiffractionExperiment& BeamY_pxl(float input);
|
||||
DiffractionExperiment& DetectorDistance_mm(float input);
|
||||
|
||||
DiffractionExperiment& ScatteringVector(Coord input);
|
||||
DiffractionExperiment& ScatteringVector();
|
||||
DiffractionExperiment& FilePrefix(std::string input);
|
||||
DiffractionExperiment& DataFileCount(int64_t input);
|
||||
|
||||
DiffractionExperiment& Compression(JFJochProtoBuf::Compression input);
|
||||
DiffractionExperiment& PreviewPeriod(std::chrono::microseconds input);
|
||||
DiffractionExperiment& SpotFindingPeriod(std::chrono::microseconds input);
|
||||
DiffractionExperiment& IndexingPeriod(std::chrono::microseconds input);
|
||||
|
||||
DiffractionExperiment& UseInternalPacketGenerator(bool input);
|
||||
DiffractionExperiment& IPv4Subnet(std::string input); // requires 255.255.255.0 subnet
|
||||
DiffractionExperiment& IPv4Subnet(int64_t input); // requires 255.255.255.0 subnet
|
||||
DiffractionExperiment& BaseUDPPort(int64_t input);
|
||||
DiffractionExperiment& MaskModuleEdges(bool input);
|
||||
DiffractionExperiment& MaskChipEdges(bool input);
|
||||
DiffractionExperiment& SetUnitCell(const UnitCell &cell);
|
||||
DiffractionExperiment& SetUnitCell();
|
||||
|
||||
DiffractionExperiment& LowResForRadialInt_A(float input);
|
||||
DiffractionExperiment& HighResForRadialInt_A(float input);
|
||||
DiffractionExperiment& LowQForRadialInt_recipA(float input);
|
||||
DiffractionExperiment& HighQForRadialInt_recipA(float input);
|
||||
DiffractionExperiment& QSpacingForRadialInt_recipA(float input);
|
||||
|
||||
DiffractionExperiment& SpaceGroupNumber(int64_t input);
|
||||
DiffractionExperiment& StorageCells(int64_t input);
|
||||
DiffractionExperiment& StorageCellStart(int64_t input = 15);
|
||||
DiffractionExperiment& DetectorType(JFJochProtoBuf::DetectorType type);
|
||||
DiffractionExperiment& SampleName(std::string input);
|
||||
|
||||
DiffractionExperiment& ConversionOnCPU(bool input);
|
||||
DiffractionExperiment& SourceName(std::string input);
|
||||
DiffractionExperiment& SourceNameShort(std::string input);
|
||||
DiffractionExperiment& InstrumentName(std::string input);
|
||||
DiffractionExperiment& InstrumentNameShort(std::string input);
|
||||
|
||||
operator JFJochProtoBuf::JungfraujochSettings() const;
|
||||
operator JFJochProtoBuf::DetectorInput() const;
|
||||
|
||||
void FillMessage(StartMessage &message) const;
|
||||
|
||||
JFJochProtoBuf::DetectorConfig DetectorConfig(const JFJochProtoBuf::ReceiverNetworkConfig& net_config) const;
|
||||
void LoadDatasetSettings(const JFJochProtoBuf::DatasetSettings &settings);
|
||||
void LoadDetectorSettings(const JFJochProtoBuf::DetectorSettings &settings);
|
||||
JFJochProtoBuf::DetectorSettings GetDetectorSettings() const;
|
||||
|
||||
static void CheckDataProcessingSettings(const JFJochProtoBuf::DataProcessingSettings& settings);
|
||||
static JFJochProtoBuf::DataProcessingSettings DefaultDataProcessingSettings();
|
||||
DetectorMode GetDetectorMode() const;
|
||||
|
||||
int64_t GetPixelDepth() const;
|
||||
bool IsPixelSigned() const;
|
||||
int64_t GetOverflow() const;
|
||||
int64_t GetUnderflow() const;
|
||||
|
||||
int64_t GetNumTriggers() const;
|
||||
int64_t GetImageNum() const;
|
||||
int64_t GetImageNumPerTrigger() const;
|
||||
|
||||
int64_t GetPedestalG0Frames() const;
|
||||
int64_t GetPedestalG1Frames() const;
|
||||
int64_t GetPedestalG2Frames() const;
|
||||
|
||||
int64_t GetFrameNum() const;
|
||||
int64_t GetFrameNumPerTrigger() const;
|
||||
|
||||
std::chrono::microseconds GetFrameTime() const;
|
||||
std::chrono::microseconds GetImageTime() const;
|
||||
|
||||
int64_t GetSummation() const;
|
||||
std::chrono::microseconds GetImageCountTime() const;
|
||||
std::chrono::microseconds GetFrameCountTime() const;
|
||||
|
||||
float GetPhotonEnergy_keV() const;
|
||||
float GetWavelength_A() const;
|
||||
float GetBeamX_pxl() const;
|
||||
float GetBeamY_pxl() const;
|
||||
float GetDetectorDistance_mm() const;
|
||||
|
||||
Coord GetScatteringVector() const;
|
||||
|
||||
std::string GetFilePrefix() const;
|
||||
int64_t GetDataFileCount() const;
|
||||
|
||||
JFJochProtoBuf::Compression GetCompressionAlgorithm() const;
|
||||
CompressionAlgorithm GetCompressionAlgorithmEnum() const;
|
||||
|
||||
int64_t GetMaxCompressedSize() const;
|
||||
|
||||
int64_t GetDataStreamsNum() const;
|
||||
int64_t GetModulesNum(uint16_t data_stream = TASK_NO_DATA_STREAM) const;
|
||||
int64_t GetFirstModuleOfDataStream(uint16_t data_stream) const;
|
||||
|
||||
int64_t GetPixelsNum() const;
|
||||
int64_t GetYPixelsNum() const;
|
||||
int64_t GetXPixelsNum() const; // X pixels must be the same for all modules
|
||||
|
||||
int64_t GetPixel0OfModule(uint16_t module_number) const;
|
||||
bool IsUpsideDown() const;
|
||||
|
||||
std::chrono::microseconds GetPreviewPeriod() const;
|
||||
int64_t GetPreviewStride() const;
|
||||
std::chrono::microseconds GetSpotFindingPeriod() const;
|
||||
int64_t GetSpotFindingStride() const;
|
||||
std::chrono::microseconds GetIndexingPeriod() const;
|
||||
int64_t GetIndexingStride() const;
|
||||
|
||||
int64_t GetSpotFindingBin() const;
|
||||
|
||||
int64_t GetPreviewStride(std::chrono::microseconds period) const;
|
||||
bool IsUsingInternalPacketGen() const;
|
||||
|
||||
uint32_t GetSrcIPv4Address(uint32_t data_stream, uint32_t half_module) const;
|
||||
uint32_t GetDestIPv4Address(uint32_t data_stream) const;
|
||||
uint16_t GetDestUDPPort(uint16_t data_stream, uint16_t half_module) const;
|
||||
|
||||
bool CheckGitSha1Consistent() const;
|
||||
std::string CheckGitSha1Msg() const;
|
||||
|
||||
bool GetMaskModuleEdges() const;
|
||||
bool GetMaskChipEdges() const;
|
||||
UnitCell GetUnitCell() const;
|
||||
bool HasUnitCell() const;
|
||||
|
||||
float ResToPxl(float resolution) const;
|
||||
Coord LabCoord(float detector_x, float detector_y) const;
|
||||
float PxlToRes(float detector_x, float detector_y) const;
|
||||
|
||||
float GetLowQForRadialInt_recipA() const;
|
||||
float GetHighQForRadialInt_recipA() const;
|
||||
float GetQSpacingForRadialInt_recipA() const;
|
||||
|
||||
int64_t GetSpaceGroupNumber() const;
|
||||
|
||||
int64_t GetStorageCellNumber() const;
|
||||
int64_t GetStorageCellStart() const;
|
||||
|
||||
JFJochProtoBuf::DetectorType GetDetectorType() const;
|
||||
int64_t GetMaxSpotCount() const;
|
||||
std::string GetSampleName() const;
|
||||
|
||||
bool GetConversionOnCPU() const;
|
||||
bool GetConversionOnFPGA() const;
|
||||
|
||||
DiffractionExperiment& ApplyPixelMaskInFPGA(bool input);
|
||||
bool GetApplyPixelMaskInFPGA() const;
|
||||
|
||||
float GetPixelSize_mm() const;
|
||||
DiffractionExperiment& Binning2x2(bool input);
|
||||
bool GetBinning2x2() const;
|
||||
|
||||
std::string GetSourceName() const;
|
||||
std::string GetSourceNameShort() const;
|
||||
std::string GetInstrumentName() const;
|
||||
std::string GetInstrumentNameShort() const;
|
||||
};
|
||||
|
||||
inline int64_t CalculateStride(const std::chrono::microseconds &frame_time, const std::chrono::microseconds &preview_time) {
|
||||
if ((preview_time.count() <= 0) || (frame_time.count() <= 0))
|
||||
return 0;
|
||||
else if (preview_time < frame_time)
|
||||
return 1;
|
||||
else
|
||||
return preview_time / frame_time;
|
||||
}
|
||||
|
||||
#endif //DIFFRACTIONEXPERIMENT_H
|
||||
49
common/DiffractionGeometry.cpp
Normal file
49
common/DiffractionGeometry.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <cmath>
|
||||
#include "DiffractionGeometry.h"
|
||||
|
||||
DiffractionGeometry::DiffractionGeometry(float in_beam_x_pxl, float in_beam_y_pxl, float in_detector_distance_mm,
|
||||
float in_pixel_size_mm, float in_wavelength_A, Coord in_scattering_vector)
|
||||
: beam_x_pxl(in_beam_x_pxl),
|
||||
beam_y_pxl(in_beam_y_pxl),
|
||||
detector_distance_mm(in_detector_distance_mm),
|
||||
pixel_size_mm(in_pixel_size_mm),
|
||||
wavelength_A(in_wavelength_A),
|
||||
scattering_vector(in_scattering_vector) {}
|
||||
|
||||
Coord DiffractionGeometry::DetectorToLab(float x_pxl, float y_pxl) const {
|
||||
// Assumes planar detector, 90 deg towards beam
|
||||
return {(x_pxl - beam_x_pxl) * pixel_size_mm ,
|
||||
(y_pxl - beam_y_pxl) * pixel_size_mm ,
|
||||
detector_distance_mm};
|
||||
}
|
||||
|
||||
Coord DiffractionGeometry::DetectorToRecip(float x_pxl, float y_pxl) const {
|
||||
return DetectorToLab(x_pxl, y_pxl).Normalize() / wavelength_A - scattering_vector;
|
||||
}
|
||||
|
||||
float DiffractionGeometry::PxlToRes(float detector_x, float detector_y) const {
|
||||
auto lab = DetectorToLab(detector_x, detector_y);
|
||||
|
||||
float beam_path = lab.Length();
|
||||
if (beam_path == detector_distance_mm) return INFINITY;
|
||||
|
||||
float cos_2theta = detector_distance_mm / beam_path;
|
||||
// cos(2theta) = cos(theta)^2 - sin(theta)^2
|
||||
// cos(2theta) = 1 - 2*sin(theta)^2
|
||||
// Technically two solutions for two theta, but it makes sense only to take positive one in this case
|
||||
float sin_theta = sqrt((1-cos_2theta)/2);
|
||||
return wavelength_A / (2 * sin_theta);
|
||||
}
|
||||
|
||||
float DiffractionGeometry::ResToPxl(float resolution) const {
|
||||
if (resolution == 0)
|
||||
return INFINITY;
|
||||
|
||||
float sin_theta = wavelength_A / (2 * resolution);
|
||||
float theta = asin(sin_theta);
|
||||
float tan_2theta = tan(2 * theta);
|
||||
return tan_2theta * detector_distance_mm / pixel_size_mm;
|
||||
}
|
||||
31
common/DiffractionGeometry.h
Normal file
31
common/DiffractionGeometry.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef DIFFRACTIONGEOMETRY_H
|
||||
#define DIFFRACTIONGEOMETRY_H
|
||||
|
||||
#include "Coord.h"
|
||||
#include "DiffractionSpot.h"
|
||||
|
||||
class DiffractionGeometry {
|
||||
const float beam_x_pxl;
|
||||
const float beam_y_pxl;
|
||||
const float detector_distance_mm;
|
||||
const float pixel_size_mm;
|
||||
const float wavelength_A;
|
||||
const Coord scattering_vector;
|
||||
public:
|
||||
DiffractionGeometry(float beam_x_pxl,
|
||||
float beam_y_pxl,
|
||||
float detector_distance_mm,
|
||||
float pixel_size_mm,
|
||||
float wavelength_A,
|
||||
Coord scattering_vector);
|
||||
Coord DetectorToLab(float x_pxl, float y_pxl) const;
|
||||
Coord DetectorToRecip(float x_pxl, float y_pxl) const;
|
||||
|
||||
float ResToPxl(float resolution) const;
|
||||
float PxlToRes(float detector_x, float detector_y) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
68
common/DiffractionSpot.cpp
Normal file
68
common/DiffractionSpot.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "DiffractionSpot.h"
|
||||
#include "RawToConvertedGeometry.h"
|
||||
|
||||
DiffractionSpot::DiffractionSpot(uint32_t col, uint32_t line, int64_t in_photons) {
|
||||
if (in_photons < 0) in_photons = 0;
|
||||
x = col * static_cast<float>(in_photons);
|
||||
y = line * static_cast<float>(in_photons);
|
||||
pixel_count = 1;
|
||||
photons = in_photons;
|
||||
max_photons = in_photons;
|
||||
}
|
||||
|
||||
DiffractionSpot& DiffractionSpot::operator+=(const DiffractionSpot &other) {
|
||||
this->x += other.x;
|
||||
this->y += other.y;
|
||||
this->photons += other.photons;
|
||||
this->max_photons = std::max(this->max_photons, other.max_photons);
|
||||
this->pixel_count += other.pixel_count;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int64_t DiffractionSpot::Count() const {
|
||||
return photons;
|
||||
}
|
||||
|
||||
int64_t DiffractionSpot::MaxCount() const {
|
||||
return max_photons;
|
||||
}
|
||||
|
||||
Coord DiffractionSpot::RawCoord() const {
|
||||
return {x / (float)photons, y / (float)photons, 0};
|
||||
}
|
||||
|
||||
int64_t DiffractionSpot::PixelCount() const {
|
||||
return pixel_count;
|
||||
}
|
||||
|
||||
Coord DiffractionSpot::LabCoord(const DiffractionExperiment &experiment, uint16_t data_stream) const {
|
||||
return experiment.LabCoord(x / (float)photons, y / (float)photons);
|
||||
}
|
||||
|
||||
Coord DiffractionSpot::ReciprocalCoord(const DiffractionExperiment &experiment, uint16_t data_stream) const {
|
||||
return LabCoord(experiment, data_stream).Normalize() / experiment.GetWavelength_A()
|
||||
- experiment.GetScatteringVector();
|
||||
}
|
||||
|
||||
double DiffractionSpot::GetResolution(const DiffractionExperiment &experiment, uint16_t data_stream) const {
|
||||
return experiment.PxlToRes(x / (float)photons, y / (float)photons);
|
||||
}
|
||||
|
||||
DiffractionSpot::operator SpotToSave() const {
|
||||
return {
|
||||
.x = x / static_cast<float>(photons),
|
||||
.y = y / static_cast<float>(photons),
|
||||
.intensity = static_cast<float>(photons)
|
||||
};
|
||||
}
|
||||
|
||||
void DiffractionSpot::AddPixel(uint32_t col, uint32_t line, int64_t photons) {
|
||||
this->x += col * (float) photons;
|
||||
this->y += line * (float) photons;
|
||||
this->photons += photons;
|
||||
this->max_photons = std::max(this->max_photons, photons);
|
||||
this->pixel_count += 1;
|
||||
}
|
||||
33
common/DiffractionSpot.h
Normal file
33
common/DiffractionSpot.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_DIFFRACTIONSPOT_H
|
||||
#define JUNGFRAUJOCH_DIFFRACTIONSPOT_H
|
||||
|
||||
#include "Coord.h"
|
||||
#include "DiffractionExperiment.h"
|
||||
#include "SpotToSave.h"
|
||||
|
||||
// Definition of Bragg spot
|
||||
class DiffractionSpot {
|
||||
float x;
|
||||
float y;
|
||||
int64_t pixel_count;
|
||||
int64_t photons; // total photon count
|
||||
int64_t max_photons; // maximum number of counts per pixel in the spot
|
||||
public:
|
||||
DiffractionSpot() = default;
|
||||
DiffractionSpot(uint32_t col, uint32_t line, int64_t photons);
|
||||
DiffractionSpot& operator+=(const DiffractionSpot& spot);
|
||||
int64_t PixelCount() const;
|
||||
int64_t Count() const;
|
||||
int64_t MaxCount() const;
|
||||
Coord RawCoord() const;
|
||||
Coord LabCoord(const DiffractionExperiment &experiment, uint16_t data_stream = TASK_NO_DATA_STREAM) const;
|
||||
double GetResolution(const DiffractionExperiment &experiment, uint16_t data_stream = TASK_NO_DATA_STREAM) const;
|
||||
Coord ReciprocalCoord(const DiffractionExperiment &experiment, uint16_t data_stream = TASK_NO_DATA_STREAM) const;
|
||||
operator SpotToSave() const;
|
||||
void AddPixel(uint32_t col, uint32_t line, int64_t photons);
|
||||
};
|
||||
|
||||
#endif //JUNGFRAUJOCH_DIFFRACTIONSPOT_H
|
||||
148
common/FrameTransformation.cpp
Normal file
148
common/FrameTransformation.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <bitshuffle/bitshuffle.h>
|
||||
|
||||
#include "FrameTransformation.h"
|
||||
#include "RawToConvertedGeometry.h"
|
||||
#include "JFJochException.h"
|
||||
|
||||
FrameTransformation::FrameTransformation(const DiffractionExperiment &in_experiment) :
|
||||
experiment(in_experiment), summation(experiment.GetSummation()),
|
||||
pixel_depth(experiment.GetPixelDepth()), compressor(in_experiment.GetCompressionAlgorithmEnum()),
|
||||
compression_algorithm(in_experiment.GetCompressionAlgorithmEnum()),
|
||||
line_shift((experiment.IsUpsideDown() ? -1 : 1) * experiment.GetXPixelsNum()),
|
||||
binning_2x2(experiment.GetBinning2x2()) {
|
||||
|
||||
if ((experiment.GetDetectorMode() == DetectorMode::Conversion) && (summation > 1)) {
|
||||
for (int i = 0; i < experiment.GetModulesNum(); i++)
|
||||
summation_buffer.emplace_back(RAW_MODULE_SIZE);
|
||||
}
|
||||
|
||||
precompression_buffer.resize(experiment.GetPixelsNum() * pixel_depth);
|
||||
if (pixel_depth == 4)
|
||||
image16bit.resize(experiment.GetPixelsNum(), 0);
|
||||
|
||||
if (experiment.GetApplyPixelMaskInFPGA()) {
|
||||
// Mask gaps
|
||||
if (pixel_depth == 2) {
|
||||
auto ptr = (int16_t *) precompression_buffer.data();
|
||||
for (int i = 0; i < experiment.GetPixelsNum(); i++)
|
||||
ptr[i] = INT16_MIN;
|
||||
} else {
|
||||
auto ptr = (uint32_t *) precompression_buffer.data();
|
||||
for (int i = 0; i < experiment.GetPixelsNum(); i++)
|
||||
ptr[i] = INT32_MIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FrameTransformation& FrameTransformation::SetOutput(void *output) {
|
||||
standard_output = (char *) output;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class Td> void AddToFramesSum(Td *destination, const int16_t *source) {
|
||||
for (int i = 0; i < RAW_MODULE_SIZE; i++) {
|
||||
if ((source[i] == INT16_MIN) || (destination[i] == INT32_MIN))
|
||||
destination[i] = INT32_MIN;
|
||||
else if ((source[i] == INT16_MAX) || (destination[i] == INT32_MAX))
|
||||
destination[i] = INT32_MAX;
|
||||
else destination[i] += source[i];
|
||||
}
|
||||
}
|
||||
|
||||
void FrameTransformation::PackSummation() {
|
||||
for (int m = 0; m < experiment.GetModulesNum(); m++) {
|
||||
void *output = precompression_buffer.data() + sizeof(int32_t) * experiment.GetPixel0OfModule(m);
|
||||
if (binning_2x2)
|
||||
TransferModuleAdjustMultipixelsBin2x2((int32_t *) output,
|
||||
(int32_t *) summation_buffer[m].data(),
|
||||
line_shift,
|
||||
static_cast<int32_t>(INT32_MIN),
|
||||
static_cast<int32_t>(INT32_MAX));
|
||||
else
|
||||
TransferModuleAdjustMultipixels((int32_t *) output,
|
||||
(int32_t *) summation_buffer[m].data(),
|
||||
line_shift,
|
||||
static_cast<int32_t>(INT32_MIN),
|
||||
static_cast<int32_t>(INT32_MAX));
|
||||
|
||||
for (auto &i: summation_buffer[m])
|
||||
i = 0;
|
||||
}
|
||||
|
||||
if (pixel_depth == 4) {
|
||||
// Generate 16-bit preview image
|
||||
auto arr = (int32_t *) precompression_buffer.data();
|
||||
|
||||
for (int pxl = 0; pxl < experiment.GetPixelsNum(); pxl++) {
|
||||
if (arr[pxl] >= INT16_MAX)
|
||||
image16bit[pxl] = INT16_MAX;
|
||||
else if (arr[pxl] <= INT16_MIN)
|
||||
image16bit[pxl] = INT16_MIN;
|
||||
else
|
||||
image16bit[pxl] = static_cast<int16_t>(arr[pxl]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t FrameTransformation::PackStandardOutput() {
|
||||
if (summation > 1)
|
||||
PackSummation();
|
||||
|
||||
return compressor.Compress(standard_output, precompression_buffer.data(),
|
||||
experiment.GetPixelsNum(), pixel_depth);
|
||||
}
|
||||
|
||||
void FrameTransformation::ProcessModule(const int16_t *input, size_t frame_number, uint16_t module_number,
|
||||
int data_stream) {
|
||||
if (standard_output == nullptr)
|
||||
throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds, "Default stream output not initialized");
|
||||
|
||||
size_t module_number_abs = experiment.GetFirstModuleOfDataStream(data_stream) + module_number;
|
||||
|
||||
if (summation == 1) {
|
||||
int16_t *output;
|
||||
|
||||
output = ((int16_t *) precompression_buffer.data()) + experiment.GetPixel0OfModule(module_number_abs);
|
||||
|
||||
if (experiment.GetDetectorMode() != DetectorMode::Conversion)
|
||||
memcpy(output, input, RAW_MODULE_SIZE * experiment.GetPixelDepth());
|
||||
else if (binning_2x2)
|
||||
TransferModuleAdjustMultipixelsBin2x2((int16_t *) output, (int16_t *) input, line_shift,
|
||||
static_cast<int16_t>(INT16_MIN),
|
||||
static_cast<int16_t>(INT16_MAX));
|
||||
else
|
||||
TransferModuleAdjustMultipixels((int16_t *) output, (int16_t *) input, line_shift,
|
||||
static_cast<int16_t>(INT16_MIN),
|
||||
static_cast<int16_t>(INT16_MAX));
|
||||
} else {
|
||||
AddToFramesSum(summation_buffer[module_number_abs].data(), input);
|
||||
}
|
||||
}
|
||||
|
||||
int16_t *FrameTransformation::GetPreview16BitImage() {
|
||||
if (pixel_depth == 2)
|
||||
return (int16_t *) precompression_buffer.data();
|
||||
else
|
||||
return image16bit.data();
|
||||
}
|
||||
|
||||
void FrameTransformation::ProcessModule(JFConversion &conv, const int16_t *input, size_t frame_number,
|
||||
uint16_t module_number, int data_stream) {
|
||||
if (standard_output == nullptr)
|
||||
throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds, "Default stream output not initialized");
|
||||
|
||||
size_t module_number_abs = experiment.GetFirstModuleOfDataStream(data_stream) + module_number;
|
||||
|
||||
auto output = ((int16_t *) precompression_buffer.data()) + experiment.GetPixel0OfModule(module_number_abs);
|
||||
|
||||
if (experiment.GetDetectorMode() != DetectorMode::Conversion)
|
||||
memcpy(output, input, RAW_MODULE_SIZE * experiment.GetPixelDepth());
|
||||
else
|
||||
conv.ConvertAdjustGeom((int16_t *) output, (uint16_t *) input, line_shift);
|
||||
}
|
||||
|
||||
40
common/FrameTransformation.h
Normal file
40
common/FrameTransformation.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_FRAMETRANSFORMATION_H
|
||||
#define JUNGFRAUJOCH_FRAMETRANSFORMATION_H
|
||||
|
||||
#include "DiffractionExperiment.h"
|
||||
#include "../compression/JFJochCompressor.h"
|
||||
#include "../jungfrau/JFConversion.h"
|
||||
|
||||
class FrameTransformation {
|
||||
const DiffractionExperiment& experiment;
|
||||
JFJochBitShuffleCompressor compressor;
|
||||
|
||||
CompressionAlgorithm compression_algorithm;
|
||||
|
||||
std::vector<std::vector<int32_t> > summation_buffer;
|
||||
std::vector<char> precompression_buffer;
|
||||
std::vector<int16_t> image16bit;
|
||||
|
||||
char *standard_output = nullptr;
|
||||
|
||||
const size_t summation;
|
||||
const size_t pixel_depth;
|
||||
const int64_t line_shift;
|
||||
|
||||
bool binning_2x2;
|
||||
void PackSummation(); // transfer summed image to converted coordinates, clear summation buffer, no compression
|
||||
public:
|
||||
FrameTransformation(const DiffractionExperiment &experiment);
|
||||
FrameTransformation& SetOutput(void *output);
|
||||
void ProcessModule(const int16_t *input, size_t frame_number, uint16_t module_number, int data_stream);
|
||||
void ProcessModule(JFConversion &conv, const int16_t *input, size_t frame_number, uint16_t module_number,
|
||||
int data_stream);
|
||||
size_t PackStandardOutput(); // transfer summed image to converted coordinates, clear summation buffer, compress
|
||||
|
||||
int16_t *GetPreview16BitImage();
|
||||
};
|
||||
|
||||
#endif //JUNGFRAUJOCH_FRAMETRANSFORMATION_H
|
||||
506
common/GPUImageAnalysis.cu
Normal file
506
common/GPUImageAnalysis.cu
Normal file
@@ -0,0 +1,506 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "GPUImageAnalysis.h"
|
||||
#include "JFJochException.h"
|
||||
#include <sstream>
|
||||
|
||||
// input X x Y pixels array
|
||||
// output X x Y byte array
|
||||
|
||||
struct CudaStreamWrapper {
|
||||
cudaStream_t v;
|
||||
};
|
||||
|
||||
inline void cuda_err(cudaError_t val) {
|
||||
if (val != cudaSuccess)
|
||||
throw JFJochException(JFJochExceptionCategory::GPUCUDAError, cudaGetErrorString(val));
|
||||
}
|
||||
|
||||
// Determine if pixel could be a spot
|
||||
// params: spot finding parameters
|
||||
// val: pixel value
|
||||
// sum: window sum
|
||||
// sum2: window sum of squares
|
||||
// count: window valid pixels count
|
||||
// return the pixel result: 0-no spot / 1-spot candidate
|
||||
__device__ __forceinline__ uint8_t pixel_result(const spot_parameters& params, const int64_t val, int64_t sum, int64_t sum2, int64_t count)
|
||||
{
|
||||
sum -= val;
|
||||
sum2 -= val * val;
|
||||
count -= 1;
|
||||
|
||||
const int64_t var = count * sum2 - (sum * sum); // This should be divided by ((2*NBX+1) * (2*NBY+1)-1)*((2*NBX+1) * (2*NBY+1))
|
||||
const int64_t in_minus_mean = val * count - sum; // Should be divided by ((2*NBX+1) * (2*NBY+1));
|
||||
|
||||
const int64_t tmp1 = in_minus_mean * in_minus_mean * (count-1);
|
||||
const float tmp2 = (var * count) * params.strong_pixel_threshold2;
|
||||
const bool strong_pixel = (val >= params.count_threshold) & (in_minus_mean > 0) & (tmp1 > tmp2);
|
||||
|
||||
return strong_pixel ? 1 : 0;
|
||||
}
|
||||
|
||||
// Find pixels that could be spots
|
||||
// in: image input values
|
||||
// out: pixel result byte array, 1 byte per pixel (0:no/1:candidate spot)
|
||||
// params: spot finding parameters
|
||||
//
|
||||
// The algorithm uses multiple waves (blockDim.y) that run over sections of rows.
|
||||
// Each wave will write output at the back row and read input at the front row.
|
||||
// Each wave is split into column output sections (blockDim.x)
|
||||
// A wave section (block) is responsible for a particular row/column section and
|
||||
// maintains sum/sum2/count values per column for the output row.
|
||||
// Every cuda thread is associated with a particular column. The thread maintains
|
||||
// the sum/sum2/count values in shared memory for it's column. To do this, the input
|
||||
// pixel values for the hight of the aggregation window are saved in shared memory.
|
||||
__global__ void analyze_pixel(const int16_t *in, uint8_t *out, const spot_parameters params)
|
||||
{
|
||||
// assumption: 2 * params.nby + 1 <= params.rows and 2 * params.nbx + 1 <= params.cols
|
||||
const int32_t window = 2 * (int)params.nby + 1; // vertical window
|
||||
|
||||
const int32_t writeSize = blockDim.x - 2 * params.nbx; // output columns per block
|
||||
const int32_t cmin = blockIdx.x * writeSize; // lowest output column
|
||||
const int32_t cmax = min(cmin + writeSize, static_cast<int32_t>(params.cols)); // past highest output column
|
||||
const int32_t col = cmin + threadIdx.x - params.nbx; // thread -> column mapping
|
||||
const bool data_col = (col >= 0) & (col < static_cast<int32_t>(params.cols)); // read global mem
|
||||
const bool result_col = (col >= cmin) & (col < cmax); // write result
|
||||
const int32_t nWaves = gridDim.y; // number of waves
|
||||
const int32_t rowsPerWave = (params.lines + nWaves - 1) / nWaves; // rows per wave
|
||||
const int32_t rmin = blockIdx.y * rowsPerWave; // lowest result row for this wave
|
||||
const int32_t rmax = min(rmin + rowsPerWave, static_cast<int32_t>(params.lines)); // past highest result row for this wave
|
||||
|
||||
const int32_t left = max(static_cast<int32_t>(threadIdx.x) - static_cast<int32_t>(params.nbx), 0); // leftmost column touched by this thread
|
||||
const int32_t right = min(static_cast<int32_t>(threadIdx.x) + static_cast<int32_t>(params.nbx) + 1, static_cast<int32_t>(params.cols)); // past rightmost column touched by this thread
|
||||
|
||||
int32_t back = rmin; // back of wave for writing
|
||||
int32_t front = max(back - static_cast<int32_t>(params.nby), 0); // front of wave for reading (needs to overtake back initially)
|
||||
|
||||
extern __shared__ int32_t shared_mem[];
|
||||
int32_t* shared_sum = shared_mem; // shared buffer [blockDim.x]
|
||||
int32_t* shared_sum2 = &shared_sum[blockDim.x]; // shared buffer [blockDim.x]
|
||||
int16_t* shared_count = reinterpret_cast<int16_t*>(&shared_sum2[blockDim.x]); // shared buffer [blockDim.x]
|
||||
int16_t* shared_val = &shared_count[blockDim.x]; // shared cyclic buffer [window, blockDim.x]
|
||||
|
||||
int32_t total_sum; // totals
|
||||
int32_t total_sum2;
|
||||
int32_t total_count;
|
||||
|
||||
// initialize sum, sum2, count, val buffers
|
||||
const int16_t ini = params.min_viable_number - 1; // value that is NOT counted
|
||||
shared_sum[threadIdx.x] = 0; // shared values without effect on totals
|
||||
shared_sum2[threadIdx.x] = 0;
|
||||
shared_count[threadIdx.x] = 0;
|
||||
for (int i=0; i<window; i++)
|
||||
shared_val[i * blockDim.x + threadIdx.x] = ini;
|
||||
|
||||
// wave front up to rmin + nby + 1
|
||||
do {
|
||||
if (data_col) { // read at the front end of the wave
|
||||
const int16_t val = in[front * params.cols + col];
|
||||
shared_val[(front % window) * blockDim.x + threadIdx.x] = val;
|
||||
if (val >= params.min_viable_number) {
|
||||
shared_sum[threadIdx.x] += val;
|
||||
shared_sum2[threadIdx.x] += val * val;
|
||||
shared_count[threadIdx.x] += 1;
|
||||
}
|
||||
}
|
||||
front++;
|
||||
} while (front < rmin + static_cast<int32_t>(params.nby) + 1);
|
||||
// wave front up to rmax
|
||||
do {
|
||||
__syncthreads(); // make others see the shared values
|
||||
if (result_col) { // write at the back end of the wave
|
||||
total_sum = total_sum2 = total_count = 0;
|
||||
for (auto j = left; j < right; j++) {
|
||||
total_sum += shared_sum[j];
|
||||
total_sum2 += shared_sum2[j];
|
||||
total_count += shared_count[j];
|
||||
}
|
||||
out[back * params.cols + col] = pixel_result(params, shared_val[(back % window) * blockDim.x + threadIdx.x], total_sum, total_sum2, total_count);
|
||||
}
|
||||
back++;
|
||||
__syncthreads(); // keep shared values until others have seen them
|
||||
if (data_col) { // read at the front end of the wave
|
||||
int16_t cnt = 0;
|
||||
int16_t old = shared_val[(front % window) * blockDim.x + threadIdx.x];
|
||||
if (old < params.min_viable_number) {
|
||||
old = 0; // no effect value
|
||||
cnt = 1; // bring count to normal
|
||||
}
|
||||
int16_t val = in[front * params.cols + col];
|
||||
shared_val[(front % window) * blockDim.x + threadIdx.x] = val;
|
||||
if (val < params.min_viable_number) {
|
||||
val = 0; // no effect value
|
||||
cnt -= 1; // count diff from normal
|
||||
}
|
||||
shared_sum[threadIdx.x] += val - old;
|
||||
shared_sum2[threadIdx.x] += val * val - old * old;
|
||||
shared_count[threadIdx.x] += cnt;
|
||||
}
|
||||
front++;
|
||||
} while (front < rmax);
|
||||
// wave back up to rmax
|
||||
do {
|
||||
__syncthreads(); // make others see the shared values
|
||||
if (result_col) { // write at the back end of the wave
|
||||
total_sum = total_sum2 = total_count = 0;
|
||||
for (auto j = left; j < right; j++) {
|
||||
total_sum += shared_sum[j];
|
||||
total_sum2 += shared_sum2[j];
|
||||
total_count += shared_count[j];
|
||||
}
|
||||
out[back * params.cols + col] = pixel_result(params, shared_val[(back % window) * blockDim.x + threadIdx.x], total_sum, total_sum2, total_count);
|
||||
}
|
||||
back++;
|
||||
__syncthreads(); // keep shared values until others have seen them
|
||||
if (data_col) { // read at the front end of the wave if possible
|
||||
int16_t cnt = -1; // normal count diff
|
||||
int16_t old = shared_val[(front % window) * blockDim.x + threadIdx.x];
|
||||
if (old < params.min_viable_number) {
|
||||
old = 0; // no effect value
|
||||
cnt += 1; // bring count to normal
|
||||
}
|
||||
int16_t val = 0;
|
||||
if (front < params.lines) {
|
||||
val = in[front * params.cols + col];
|
||||
if (val < params.min_viable_number)
|
||||
val = 0; // no effect value
|
||||
else
|
||||
cnt += 1; // count diff from normal
|
||||
}
|
||||
shared_sum[threadIdx.x] += val - old;
|
||||
shared_sum2[threadIdx.x] += val * val - old * old;
|
||||
shared_count[threadIdx.x] += cnt;
|
||||
}
|
||||
front++;
|
||||
} while (back < rmax);
|
||||
}
|
||||
|
||||
__global__ void gpu_radial_integration(const int16_t *image, const uint16_t *rad_integration_mapping,
|
||||
int32_t *bin_sum, int32_t *bin_count, uint32_t npixel,
|
||||
uint16_t nbins) {
|
||||
|
||||
extern __shared__ int32_t shared_mem[];
|
||||
int32_t* shared_sum = shared_mem; // shared buffer [nbins]
|
||||
int32_t* shared_count = &shared_sum[nbins]; // shared buffer [nbins]
|
||||
|
||||
uint32_t idx = blockDim.x*blockIdx.x + threadIdx.x;
|
||||
|
||||
for (uint32_t i = threadIdx.x; i < 2 * nbins; i += blockDim.x)
|
||||
shared_mem[i] = 0;
|
||||
__syncthreads();
|
||||
|
||||
for (uint32_t i = idx; i < npixel; i += blockDim.x * gridDim.x) {
|
||||
uint16_t bin = rad_integration_mapping[i];
|
||||
int32_t value = image[i];
|
||||
if ((value > INT16_MIN + 4) && (value < INT16_MAX - 4) && (bin < nbins)) {
|
||||
atomicAdd(&shared_sum[bin], value);
|
||||
atomicAdd(&shared_count[bin], 1);
|
||||
}
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
for (uint32_t i = threadIdx.x; i < nbins; i += blockDim.x) {
|
||||
atomicAdd(&bin_sum[i], shared_sum[i]);
|
||||
atomicAdd(&bin_count[i], shared_count[i]);
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void spot_finder_reduce(uint32_t *output, uint32_t* counter, const uint8_t* input,
|
||||
uint32_t npixel, uint32_t output_size) {
|
||||
uint32_t idx = blockDim.x*blockIdx.x + threadIdx.x;
|
||||
if (idx < npixel) {
|
||||
if (input[idx]) {
|
||||
auto old_counter = atomicAdd(counter, 1);
|
||||
if (old_counter < output_size)
|
||||
output[old_counter] = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void apply_pixel_mask(int16_t *image, const uint8_t *mask, uint32_t npixel) {
|
||||
uint32_t idx = blockDim.x*blockIdx.x + threadIdx.x;
|
||||
if (idx < npixel) {
|
||||
if (mask[idx] == 0)
|
||||
image[idx] = INT16_MIN;
|
||||
}
|
||||
}
|
||||
|
||||
GPUImageAnalysis::GPUImageAnalysis(int32_t in_xpixels, int32_t in_ypixels, const std::vector<uint8_t> &mask,
|
||||
int32_t gpu_device) :
|
||||
xpixels(in_xpixels), ypixels(in_ypixels), gpu_out(nullptr), rad_integration_nbins(0) {
|
||||
|
||||
int device_count;
|
||||
cuda_err(cudaGetDeviceCount(&device_count));
|
||||
|
||||
if (device_count == 0)
|
||||
throw JFJochException(JFJochExceptionCategory::GPUCUDAError, "No CUDA devices found");
|
||||
|
||||
if (gpu_device < 0)
|
||||
gpu_device = threadid++;
|
||||
|
||||
if (device_count > 1)
|
||||
cuda_err(cudaSetDevice(gpu_device % device_count));
|
||||
|
||||
int deviceId;
|
||||
cuda_err(cudaGetDevice(&deviceId));
|
||||
cudaDeviceGetAttribute(&numberOfSMs, cudaDevAttrMultiProcessorCount, deviceId);
|
||||
{
|
||||
int warp_size;
|
||||
cuda_err(cudaDeviceGetAttribute(&warp_size, cudaDevAttrWarpSize, deviceId));
|
||||
}
|
||||
|
||||
cudastream = new(CudaStreamWrapper);
|
||||
|
||||
cuda_err(cudaStreamCreate(&cudastream->v));
|
||||
cuda_err(cudaMalloc(&gpu_mask, xpixels * ypixels * sizeof(int8_t)));
|
||||
cuda_err(cudaMalloc(&gpu_in, xpixels * ypixels * sizeof(int16_t)));
|
||||
cuda_err(cudaMalloc(&gpu_out, xpixels * ypixels * sizeof(int8_t)));
|
||||
cuda_err(cudaMalloc(&gpu_out_reduced, maxStrongPixel * sizeof(uint32_t)));
|
||||
cuda_err(cudaMalloc(&gpu_out_reduced_counter, sizeof(uint32_t)));
|
||||
cuda_err(cudaHostAlloc(&host_out_reduced, maxStrongPixel * sizeof(uint32_t), cudaHostAllocPortable));
|
||||
cuda_err(cudaHostAlloc(&host_out_reduced_counter, sizeof(uint32_t), cudaHostAllocPortable));
|
||||
|
||||
cuda_err(cudaMemsetAsync(gpu_mask, 1, xpixels*ypixels, cudastream->v));
|
||||
|
||||
if (mask.size() != xpixels * ypixels)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Mismatch in mask size");
|
||||
cudaMemcpy(gpu_mask, mask.data(), xpixels*ypixels, cudaMemcpyHostToDevice);
|
||||
}
|
||||
|
||||
GPUImageAnalysis::GPUImageAnalysis(int32_t xpixels, int32_t ypixels, const std::vector<uint8_t> &mask,
|
||||
const std::vector<uint16_t> &rad_int_mapping, uint16_t rad_int_nbins,
|
||||
int32_t gpu_device) : GPUImageAnalysis(xpixels, ypixels, mask, gpu_device) {
|
||||
rad_integration_nbins = rad_int_nbins;
|
||||
|
||||
if (rad_int_mapping.size() != xpixels * ypixels)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Mismatch in rad. int. mapping size");
|
||||
|
||||
if (rad_integration_nbins > 0) {
|
||||
cuda_err(cudaMalloc(&gpu_rad_integration_bin_map, xpixels * ypixels * sizeof(int16_t)));
|
||||
cuda_err(cudaMalloc(&gpu_rad_integration_count, rad_integration_nbins * sizeof(int32_t)));
|
||||
cuda_err(cudaMalloc(&gpu_rad_integration_sum, rad_integration_nbins * sizeof(int32_t)));
|
||||
|
||||
cuda_err(cudaHostAlloc(&host_rad_integration_count, rad_integration_nbins * sizeof(int32_t), cudaHostAllocPortable));
|
||||
cuda_err(cudaHostAlloc(&host_rad_integration_sum, rad_integration_nbins * sizeof(int32_t), cudaHostAllocPortable));
|
||||
|
||||
cudaMemcpy(gpu_rad_integration_bin_map, rad_int_mapping.data(),
|
||||
xpixels*ypixels * sizeof(uint16_t), cudaMemcpyHostToDevice);
|
||||
}
|
||||
}
|
||||
|
||||
GPUImageAnalysis::GPUImageAnalysis(int32_t xpixels, int32_t ypixels, const std::vector<uint8_t> &mask,
|
||||
const RadialIntegrationMapping& mapping,
|
||||
int32_t gpu_device)
|
||||
: GPUImageAnalysis(xpixels, ypixels, mask, mapping.GetPixelToBinMapping(),
|
||||
mapping.GetBinNumber(), gpu_device) {}
|
||||
|
||||
GPUImageAnalysis::~GPUImageAnalysis() {
|
||||
cudaStreamDestroy(cudastream->v);
|
||||
delete(cudastream);
|
||||
|
||||
if (rad_integration_nbins > 0) {
|
||||
cudaFree(gpu_rad_integration_bin_map);
|
||||
cudaFree(gpu_rad_integration_count);
|
||||
cudaFree(gpu_rad_integration_sum);
|
||||
cudaFreeHost(host_rad_integration_count);
|
||||
cudaFreeHost(host_rad_integration_sum);
|
||||
}
|
||||
|
||||
cudaFreeHost(host_out_reduced);
|
||||
cudaFreeHost(host_out_reduced_counter);
|
||||
cudaFree(gpu_mask);
|
||||
cudaFree(gpu_in);
|
||||
cudaFree(gpu_out);
|
||||
cudaFree(gpu_out_reduced);
|
||||
cudaFree(gpu_out_reduced_counter);
|
||||
}
|
||||
|
||||
void GPUImageAnalysis::SetInputBuffer(void *ptr) {
|
||||
host_in = (int16_t *) ptr;
|
||||
}
|
||||
|
||||
bool GPUImageAnalysis::GPUPresent() {
|
||||
int device_count;
|
||||
cuda_err(cudaGetDeviceCount(&device_count));
|
||||
|
||||
return (device_count > 0);
|
||||
}
|
||||
|
||||
void GPUImageAnalysis::RunSpotFinder(const JFJochProtoBuf::DataProcessingSettings &settings) {
|
||||
// data_in is CUDA registered memory
|
||||
|
||||
// Run COLSPOT (GPU version)
|
||||
spot_parameters spot_params;
|
||||
spot_params.strong_pixel_threshold2 = settings.signal_to_noise_threshold() * settings.signal_to_noise_threshold();
|
||||
spot_params.nbx = settings.local_bkg_size();
|
||||
spot_params.nby = settings.local_bkg_size();
|
||||
spot_params.lines = ypixels;
|
||||
spot_params.cols = xpixels;
|
||||
spot_params.count_threshold = settings.photon_count_threshold();
|
||||
spot_params.min_viable_number = INT16_MIN + 5;
|
||||
|
||||
if (2 * spot_params.nbx + 1 > windowSizeLimit)
|
||||
throw JFJochException(JFJochExceptionCategory::SpotFinderError, "nbx exceeds window size limit");
|
||||
if (2 * spot_params.nby + 1 > windowSizeLimit)
|
||||
throw JFJochException(JFJochExceptionCategory::SpotFinderError, "nby exceeds window size limit");
|
||||
if (windowSizeLimit > numberOfCudaThreads)
|
||||
throw JFJochException(JFJochExceptionCategory::SpotFinderError, "window size limit exceeds number of cuda threads");
|
||||
if (windowSizeLimit > spot_params.cols)
|
||||
throw JFJochException(JFJochExceptionCategory::SpotFinderError, "window size limit exceeds number of columns");
|
||||
if (windowSizeLimit > spot_params.lines)
|
||||
throw JFJochException(JFJochExceptionCategory::SpotFinderError, "window size limit exceeds number of lines");
|
||||
|
||||
{ // call cuda kernel
|
||||
const auto nWriters = numberOfCudaThreads - 2 * spot_params.nby;
|
||||
const auto nBlocks = (spot_params.cols + nWriters - 1) / nWriters;
|
||||
const auto window = 2 * spot_params.nby + 1;
|
||||
const auto sharedSize = (2 * sizeof(int32_t) + // sum, sum2
|
||||
(1 + window) * sizeof(int16_t) // count, val
|
||||
) * numberOfCudaThreads;
|
||||
const dim3 blocks(nBlocks, numberOfWaves);
|
||||
|
||||
cuda_err(cudaMemsetAsync(gpu_out, 0, xpixels * ypixels * sizeof(uint8_t), cudastream->v));
|
||||
analyze_pixel<<<blocks, numberOfCudaThreads, sharedSize, cudastream->v>>>
|
||||
(gpu_in, gpu_out, spot_params);
|
||||
}
|
||||
|
||||
{
|
||||
cuda_err(cudaMemsetAsync(gpu_out_reduced_counter, 0, sizeof(uint32_t), cudastream->v));
|
||||
const auto nblocks =
|
||||
xpixels * ypixels / numberOfCudaThreads + ((xpixels * ypixels % numberOfCudaThreads == 0) ? 0 : 1);
|
||||
|
||||
spot_finder_reduce<<<nblocks, numberOfCudaThreads, 0, cudastream->v>>>(gpu_out_reduced, gpu_out_reduced_counter,
|
||||
gpu_out, xpixels*ypixels, maxStrongPixel);
|
||||
}
|
||||
cuda_err(cudaMemcpyAsync(host_out_reduced, gpu_out_reduced, maxStrongPixel * sizeof(uint32_t),
|
||||
cudaMemcpyDeviceToHost,cudastream->v));
|
||||
|
||||
cuda_err(cudaMemcpyAsync(host_out_reduced_counter, gpu_out_reduced_counter, sizeof(uint32_t),
|
||||
cudaMemcpyDeviceToHost,cudastream->v));
|
||||
}
|
||||
|
||||
void GPUImageAnalysis::GetSpotFinderResults(StrongPixelSet &pixel_set) {
|
||||
if (host_in == nullptr)
|
||||
throw JFJochException(JFJochExceptionCategory::SpotFinderError, "Host/GPU buffer not defined");
|
||||
|
||||
cuda_err(cudaStreamSynchronize(cudastream->v));
|
||||
|
||||
for (int i = 0; i < std::min<uint32_t>(maxStrongPixel, *host_out_reduced_counter); i++) {
|
||||
size_t npixel = host_out_reduced[i];
|
||||
size_t line = npixel / xpixels;
|
||||
size_t col = npixel % xpixels;
|
||||
pixel_set.AddStrongPixel(col, line, host_in[npixel]);
|
||||
}
|
||||
}
|
||||
|
||||
void GPUImageAnalysis::GetSpotFinderResults(const DiffractionExperiment &experiment,
|
||||
const JFJochProtoBuf::DataProcessingSettings &settings,
|
||||
std::vector<DiffractionSpot> &vec) {
|
||||
StrongPixelSet pixel_set(experiment);
|
||||
GetSpotFinderResults(pixel_set);
|
||||
pixel_set.FindSpots(experiment, settings, vec);
|
||||
}
|
||||
|
||||
void GPUImageAnalysis::RegisterBuffer() {
|
||||
cudaHostRegister(host_in, xpixels * ypixels * sizeof(uint16_t), cudaHostRegisterDefault);
|
||||
}
|
||||
|
||||
void GPUImageAnalysis::UnregisterBuffer() {
|
||||
cudaHostUnregister(host_in);
|
||||
}
|
||||
|
||||
void GPUImageAnalysis::LoadDataToGPU(bool apply_pixel_mask_on_gpu) {
|
||||
if (host_in == nullptr)
|
||||
throw JFJochException(JFJochExceptionCategory::SpotFinderError, "Host/GPU buffer not defined");
|
||||
|
||||
cuda_err(cudaMemcpy(gpu_in, host_in, xpixels * ypixels * sizeof(int16_t), cudaMemcpyHostToDevice));
|
||||
|
||||
if (apply_pixel_mask_on_gpu) {
|
||||
const auto nblocks =
|
||||
xpixels * ypixels / numberOfCudaThreads + ((xpixels * ypixels % numberOfCudaThreads == 0) ? 0 : 1);
|
||||
apply_pixel_mask<<<nblocks, numberOfCudaThreads, 0, cudastream->v>>>(gpu_in, gpu_mask, xpixels * ypixels);
|
||||
}
|
||||
}
|
||||
|
||||
void GPUImageAnalysis::RunRadialIntegration() {
|
||||
if (rad_integration_nbins == 0)
|
||||
throw JFJochException(JFJochExceptionCategory::SpotFinderError, "Radial integration not initialized");
|
||||
|
||||
cuda_err(cudaMemsetAsync(gpu_rad_integration_sum, 0, rad_integration_nbins * sizeof(int32_t), cudastream->v));
|
||||
cuda_err(cudaMemsetAsync(gpu_rad_integration_count, 0, rad_integration_nbins * sizeof(int32_t), cudastream->v));
|
||||
|
||||
gpu_radial_integration<<<40, numberOfCudaThreads, rad_integration_nbins * sizeof(uint32_t) * 2, cudastream->v>>>(
|
||||
gpu_in, gpu_rad_integration_bin_map, gpu_rad_integration_sum, gpu_rad_integration_count, xpixels*ypixels,
|
||||
rad_integration_nbins);
|
||||
|
||||
cuda_err(cudaMemcpyAsync(host_rad_integration_count, gpu_rad_integration_count,
|
||||
rad_integration_nbins * sizeof(int32_t),
|
||||
cudaMemcpyDeviceToHost, cudastream->v));
|
||||
cuda_err(cudaMemcpyAsync(host_rad_integration_sum, gpu_rad_integration_sum,
|
||||
rad_integration_nbins * sizeof(int32_t),
|
||||
cudaMemcpyDeviceToHost, cudastream->v));
|
||||
}
|
||||
|
||||
void GPUImageAnalysis::GetRadialIntegrationProfile(std::vector<float> &result) {
|
||||
if (rad_integration_nbins == 0)
|
||||
throw JFJochException(JFJochExceptionCategory::SpotFinderError, "Radial integration not initialized");
|
||||
|
||||
cuda_err(cudaStreamSynchronize(cudastream->v));
|
||||
|
||||
result.resize(rad_integration_nbins);
|
||||
|
||||
for (int i = 0; i < rad_integration_nbins; i++) {
|
||||
if (host_rad_integration_count[i] > 0)
|
||||
result[i] = static_cast<float>(host_rad_integration_sum[i])
|
||||
/ static_cast<float>(host_rad_integration_count[i]);
|
||||
else
|
||||
result[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int32_t> GPUImageAnalysis::GetRadialIntegrationSum() const {
|
||||
if (rad_integration_nbins == 0)
|
||||
throw JFJochException(JFJochExceptionCategory::SpotFinderError, "Radial integration not initialized");
|
||||
|
||||
cuda_err(cudaStreamSynchronize(cudastream->v));
|
||||
|
||||
std::vector<int32_t> out(rad_integration_nbins);
|
||||
memcpy(out.data(), host_rad_integration_sum, rad_integration_nbins * sizeof(int32_t));
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<int32_t> GPUImageAnalysis::GetRadialIntegrationCount() const {
|
||||
if (rad_integration_nbins == 0)
|
||||
throw JFJochException(JFJochExceptionCategory::SpotFinderError, "Radial integration not initialized");
|
||||
|
||||
cuda_err(cudaStreamSynchronize(cudastream->v));
|
||||
|
||||
std::vector<int32_t> out(rad_integration_nbins);
|
||||
memcpy(out.data(), host_rad_integration_count, rad_integration_nbins * sizeof(int32_t));
|
||||
return out;
|
||||
}
|
||||
|
||||
float GPUImageAnalysis::GetRadialIntegrationRangeValue(uint16_t min_bin, uint16_t max_bin) {
|
||||
if (rad_integration_nbins == 0)
|
||||
throw JFJochException(JFJochExceptionCategory::SpotFinderError, "Radial integration not initialized");
|
||||
|
||||
cuda_err(cudaStreamSynchronize(cudastream->v));
|
||||
|
||||
int64_t ret_sum = 0;
|
||||
int64_t ret_count = 0;
|
||||
|
||||
for (int i = std::min(rad_integration_nbins,min_bin);
|
||||
i <= std::min((uint16_t)(rad_integration_nbins-1),max_bin);
|
||||
i++) {
|
||||
ret_sum += host_rad_integration_sum[i];
|
||||
ret_count += host_rad_integration_count[i];
|
||||
}
|
||||
|
||||
if (ret_count == 0)
|
||||
return 0;
|
||||
else
|
||||
return static_cast<float>(ret_sum) / static_cast<float>(ret_count);
|
||||
}
|
||||
|
||||
std::atomic<uint16_t> GPUImageAnalysis::threadid{0};
|
||||
82
common/GPUImageAnalysis.h
Normal file
82
common/GPUImageAnalysis.h
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_GPUIMAGEANALYSIS_H
|
||||
#define JUNGFRAUJOCH_GPUIMAGEANALYSIS_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "StrongPixelSet.h"
|
||||
#include "RadialIntegrationMapping.h"
|
||||
|
||||
struct spot_parameters {
|
||||
float strong_pixel_threshold2;
|
||||
uint32_t count_threshold;
|
||||
uint32_t nbx;
|
||||
uint32_t nby;
|
||||
uint32_t lines;
|
||||
uint32_t cols;
|
||||
int16_t min_viable_number;
|
||||
};
|
||||
|
||||
struct CudaStreamWrapper;
|
||||
|
||||
class GPUImageAnalysis {
|
||||
std::mutex m;
|
||||
CudaStreamWrapper *cudastream;
|
||||
|
||||
static std::atomic<uint16_t> threadid;
|
||||
|
||||
const int32_t xpixels;
|
||||
const int32_t ypixels;
|
||||
|
||||
int16_t *host_in = nullptr;
|
||||
uint8_t *gpu_mask = nullptr;
|
||||
int16_t *gpu_in = nullptr;
|
||||
uint8_t *gpu_out = nullptr;
|
||||
uint32_t *gpu_out_reduced = nullptr;
|
||||
uint32_t *gpu_out_reduced_counter = nullptr;
|
||||
uint32_t *host_out_reduced = nullptr;
|
||||
uint32_t *host_out_reduced_counter = nullptr;
|
||||
|
||||
uint16_t rad_integration_nbins;
|
||||
uint16_t *gpu_rad_integration_bin_map = nullptr;
|
||||
int32_t *gpu_rad_integration_sum = nullptr;
|
||||
int32_t *gpu_rad_integration_count = nullptr;
|
||||
int32_t *host_rad_integration_sum = nullptr, *host_rad_integration_count = nullptr;
|
||||
int numberOfSMs;
|
||||
const int numberOfCudaThreads = 128; // #threads per block that works well for Nvidia T4
|
||||
const int numberOfWaves = 40; // #waves that works well for Nvidia T4
|
||||
const int windowSizeLimit = 21; // limit on the window size (2nby+1, 2nbx+1) to prevent shared memory problems
|
||||
const int maxStrongPixel = 65536;
|
||||
public:
|
||||
|
||||
GPUImageAnalysis(int32_t xpixels, int32_t ypixels, const std::vector<uint8_t> &mask, int32_t gpu_device = -1);
|
||||
GPUImageAnalysis(int32_t xpixels, int32_t ypixels, const std::vector<uint8_t> &mask,
|
||||
const std::vector<uint16_t> &rad_int_mapping, uint16_t rad_int_nbins,
|
||||
int32_t gpu_device = -1);
|
||||
GPUImageAnalysis(int32_t xpixels, int32_t ypixels, const std::vector<uint8_t> &mask,
|
||||
const RadialIntegrationMapping& mapping,int32_t gpu_device = -1);
|
||||
|
||||
~GPUImageAnalysis();
|
||||
|
||||
void SetInputBuffer(void *host_in);
|
||||
void LoadDataToGPU(bool apply_pixel_mask_on_gpu = true);
|
||||
|
||||
void RunSpotFinder(const JFJochProtoBuf::DataProcessingSettings &settings);
|
||||
void GetSpotFinderResults(StrongPixelSet &pixel_set);
|
||||
void GetSpotFinderResults(const DiffractionExperiment &experiment, const JFJochProtoBuf::DataProcessingSettings &settings,
|
||||
std::vector<DiffractionSpot> &vec);
|
||||
|
||||
void RunRadialIntegration();
|
||||
void GetRadialIntegrationProfile(std::vector<float> &result);
|
||||
[[nodiscard]] std::vector<int32_t> GetRadialIntegrationSum() const;
|
||||
[[nodiscard]] std::vector<int32_t> GetRadialIntegrationCount() const;
|
||||
[[nodiscard]] float GetRadialIntegrationRangeValue(uint16_t min_bin, uint16_t max_bin);
|
||||
|
||||
static bool GPUPresent();
|
||||
void RegisterBuffer();
|
||||
void UnregisterBuffer();
|
||||
};
|
||||
|
||||
#endif //JUNGFRAUJOCH_GPUIMAGEANALYSIS_H
|
||||
12
common/GitInfo.cpp.in
Normal file
12
common/GitInfo.cpp.in
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <string>
|
||||
|
||||
std::string jfjoch_git_sha1() {
|
||||
return "@GIT_SHA1@";
|
||||
}
|
||||
|
||||
std::string jfjoch_git_date() {
|
||||
return "@GIT_DATE@";
|
||||
}
|
||||
12
common/GitInfo.h
Normal file
12
common/GitInfo.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_GITINFO_H
|
||||
#define JUNGFRAUJOCH_GITINFO_H
|
||||
|
||||
#include <string>
|
||||
|
||||
std::string jfjoch_git_sha1();
|
||||
std::string jfjoch_git_date();
|
||||
|
||||
#endif //JUNGFRAUJOCH_GITINFO_H
|
||||
29
common/ImagePusher.cpp
Normal file
29
common/ImagePusher.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "ImagePusher.h"
|
||||
|
||||
void PrepareCBORImage(DataMessage& message,
|
||||
const DiffractionExperiment &experiment,
|
||||
void *image, size_t image_size) {
|
||||
message.image.data = (uint8_t *) image;
|
||||
message.image.size = image_size;
|
||||
message.image.xpixel = experiment.GetXPixelsNum();
|
||||
message.image.ypixel = experiment.GetYPixelsNum();
|
||||
message.image.pixel_depth_bytes = experiment.GetPixelDepth();
|
||||
message.image.pixel_is_signed = experiment.IsPixelSigned();
|
||||
message.image.algorithm = experiment.GetCompressionAlgorithmEnum();
|
||||
message.image.channel = "default";
|
||||
}
|
||||
|
||||
|
||||
void PrepareDataMessageSpots(DataMessage& message,
|
||||
const std::vector<DiffractionSpot>& spots) {
|
||||
message.spots.clear();
|
||||
for (const auto & spot : spots)
|
||||
message.spots.push_back(spot);
|
||||
}
|
||||
|
||||
void ImagePusher::SendData(const std::vector<uint8_t> &serialized_image, int64_t image_number) {
|
||||
SendDataInternal(serialized_image, image_number);
|
||||
}
|
||||
30
common/ImagePusher.h
Normal file
30
common/ImagePusher.h
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_IMAGEPUSHER_H
|
||||
#define JUNGFRAUJOCH_IMAGEPUSHER_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "DiffractionExperiment.h"
|
||||
#include "DiffractionSpot.h"
|
||||
#include "../frame_serialize/JFJochFrameSerializer.h"
|
||||
#include "../frame_serialize/StartMessage.h"
|
||||
#include "../frame_serialize/EndMessage.h"
|
||||
|
||||
void PrepareCBORImage(DataMessage& message,
|
||||
const DiffractionExperiment &experiment,
|
||||
void *image, size_t image_size);
|
||||
|
||||
class ImagePusher {
|
||||
protected:
|
||||
virtual void SendDataInternal(const std::vector<uint8_t>& serialized_image, int64_t image_number) = 0;
|
||||
public:
|
||||
virtual void StartDataCollection(const StartMessage& message) = 0;
|
||||
virtual void EndDataCollection(const EndMessage& message) = 0;
|
||||
void SendData(const std::vector<uint8_t>& serialized_image, int64_t image_number);
|
||||
};
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_IMAGEPUSHER_H
|
||||
146
common/JFJochException.h
Normal file
146
common/JFJochException.h
Normal file
@@ -0,0 +1,146 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef SLSEXCEPTION_H
|
||||
#define SLSEXCEPTION_H
|
||||
|
||||
#include <string>
|
||||
#include <exception>
|
||||
|
||||
enum class JFJochExceptionCategory {
|
||||
JSON,
|
||||
InputParameterAboveMax,
|
||||
InputParameterBelowMin,
|
||||
InputParameterInvalid,
|
||||
WrongNumber,
|
||||
WrongUnit,
|
||||
ArrayOutOfBounds,
|
||||
HDF5,
|
||||
IBVerbs,
|
||||
RESTCommandUnknown,
|
||||
WrongDAQState,
|
||||
Detector,
|
||||
MemAllocFailed,
|
||||
OpenCAPIError,
|
||||
GPUCUDAError,
|
||||
GainFileOpenError,
|
||||
MockFileOpenError,
|
||||
Compression,
|
||||
SharedMemory,
|
||||
ZeroMQ,
|
||||
ServiceUndefined,
|
||||
TriggerError,
|
||||
FileWriteError,
|
||||
ZSTDCompressionError,
|
||||
LogstashConnectionFailure,
|
||||
TIFFGeneratorError,
|
||||
gRPCError,
|
||||
PreviewError,
|
||||
HardwareParityError,
|
||||
SpotFinderError,
|
||||
PCIeError,
|
||||
CBORError,
|
||||
UDPError,
|
||||
CommunicationError
|
||||
};
|
||||
|
||||
class JFJochException : public std::exception {
|
||||
std::string msg;
|
||||
JFJochExceptionCategory category;
|
||||
static std::string DecodeCategory(JFJochExceptionCategory category) {
|
||||
switch (category) {
|
||||
case JFJochExceptionCategory::JSON:
|
||||
return "JSON parsing or analysis error";
|
||||
case JFJochExceptionCategory::InputParameterAboveMax:
|
||||
return "Input parameter above max";
|
||||
case JFJochExceptionCategory::InputParameterBelowMin:
|
||||
return "Input parameter below min";
|
||||
case JFJochExceptionCategory::InputParameterInvalid:
|
||||
return "Input parameter invalid";
|
||||
case JFJochExceptionCategory::WrongNumber:
|
||||
return "Wrong number";
|
||||
case JFJochExceptionCategory::WrongUnit:
|
||||
return "Wrong unit";
|
||||
case JFJochExceptionCategory::ArrayOutOfBounds:
|
||||
return "Array out of bounds";
|
||||
case JFJochExceptionCategory::HDF5:
|
||||
return "HDF5 error";
|
||||
case JFJochExceptionCategory::IBVerbs:
|
||||
return "Infiniband error";
|
||||
case JFJochExceptionCategory::RESTCommandUnknown:
|
||||
return "REST command unknown";
|
||||
case JFJochExceptionCategory::WrongDAQState:
|
||||
return "DAQ state error";
|
||||
case JFJochExceptionCategory::Detector:
|
||||
return "Detector error";
|
||||
case JFJochExceptionCategory::MemAllocFailed:
|
||||
return "Memory error";
|
||||
case JFJochExceptionCategory::OpenCAPIError:
|
||||
return "OpenCAPI error";
|
||||
case JFJochExceptionCategory::GPUCUDAError:
|
||||
return "CUDA (GPU) error";
|
||||
case JFJochExceptionCategory::GainFileOpenError:
|
||||
return "Gain file open error";
|
||||
case JFJochExceptionCategory::MockFileOpenError:
|
||||
return "Mock file open error";
|
||||
case JFJochExceptionCategory::Compression:
|
||||
return "Compression error";
|
||||
case JFJochExceptionCategory::SharedMemory:
|
||||
return "Shared memory error";
|
||||
case JFJochExceptionCategory::ZeroMQ:
|
||||
return "ZeroMQ error";
|
||||
case JFJochExceptionCategory::ServiceUndefined:
|
||||
return "Service undefined error";
|
||||
case JFJochExceptionCategory::TriggerError:
|
||||
return "Trigger error";
|
||||
case JFJochExceptionCategory::FileWriteError:
|
||||
return "File writer error";
|
||||
case JFJochExceptionCategory::ZSTDCompressionError:
|
||||
return "Zstd compressor error";
|
||||
case JFJochExceptionCategory::LogstashConnectionFailure:
|
||||
return "Logstash connection error";
|
||||
case JFJochExceptionCategory::TIFFGeneratorError:
|
||||
return "TIFF writer error";
|
||||
case JFJochExceptionCategory::gRPCError:
|
||||
return "Remote node (gRPC) error";
|
||||
case JFJochExceptionCategory::PreviewError:
|
||||
return "Preview error";
|
||||
case JFJochExceptionCategory::HardwareParityError:
|
||||
return "Parity error (HW)";
|
||||
case JFJochExceptionCategory::SpotFinderError:
|
||||
return "Spot finder";
|
||||
case JFJochExceptionCategory::PCIeError:
|
||||
return "PCIe driver error";
|
||||
case JFJochExceptionCategory::CBORError:
|
||||
return "CBOR serialization error";
|
||||
case JFJochExceptionCategory::UDPError:
|
||||
return "UDP simulator error";
|
||||
case JFJochExceptionCategory::CommunicationError:
|
||||
return "Communication error";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
public:
|
||||
JFJochException() = default;
|
||||
|
||||
JFJochException(JFJochExceptionCategory in_val, const std::string &description, int optional = 0) noexcept :
|
||||
category(in_val) {
|
||||
try {
|
||||
msg = DecodeCategory(category) + " (" + description + ")";
|
||||
} catch (...) {}
|
||||
}
|
||||
|
||||
JFJochException(const JFJochException &e) noexcept {
|
||||
try {
|
||||
category = e.category;
|
||||
msg = e.msg;
|
||||
} catch (...) {}
|
||||
}
|
||||
|
||||
[[nodiscard]] const char *what() const noexcept override {
|
||||
return msg.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
#endif //SLSEXCEPTION_H
|
||||
21
common/Latch.cpp
Normal file
21
common/Latch.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "Latch.h"
|
||||
|
||||
Latch::Latch(uint32_t in_count) {
|
||||
count = in_count;
|
||||
}
|
||||
|
||||
void Latch::CountDown() {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (count != 0)
|
||||
count--;
|
||||
if (count == 0)
|
||||
c.notify_all();
|
||||
}
|
||||
|
||||
void Latch::Wait() {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
c.wait(ul, [&]{return count == 0;});
|
||||
}
|
||||
21
common/Latch.h
Normal file
21
common/Latch.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_LATCH_H
|
||||
#define JUNGFRAUJOCH_LATCH_H
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
class Latch {
|
||||
std::mutex m;
|
||||
std::condition_variable c;
|
||||
uint32_t count;
|
||||
public:
|
||||
Latch(uint32_t count);
|
||||
void CountDown();
|
||||
void Wait();
|
||||
};
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_LATCH_H
|
||||
33
common/Logger.cpp
Normal file
33
common/Logger.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "spdlog/sinks/daily_file_sink.h"
|
||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||
#include "spdlog/fmt/bin_to_hex.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "GitInfo.h"
|
||||
|
||||
Logger::Logger(const std::string &service_name, const std::string &file_name) {
|
||||
|
||||
std::vector<spdlog::sink_ptr> sinks;
|
||||
|
||||
sinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
|
||||
if (!file_name.empty())
|
||||
sinks.push_back(std::make_shared<spdlog::sinks::daily_file_sink_mt>(file_name, 23, 59));
|
||||
|
||||
spdlog_logger = std::make_shared<spdlog::logger>(service_name, std::begin(sinks), std::end(sinks));
|
||||
spdlog_logger->info("Git sha {} ({})", jfjoch_git_sha1().substr(0, 6), jfjoch_git_date());
|
||||
}
|
||||
|
||||
void Logger::ErrorException(const std::exception &e) {
|
||||
spdlog_logger->error(e.what());
|
||||
}
|
||||
|
||||
Logger &Logger::Verbose(bool input) {
|
||||
if (input)
|
||||
spdlog_logger->set_level(spdlog::level::debug);
|
||||
else
|
||||
spdlog_logger->set_level(spdlog::level::info);
|
||||
return *this;
|
||||
}
|
||||
59
common/Logger.h
Normal file
59
common/Logger.h
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_LOGGER_H
|
||||
#define JUNGFRAUJOCH_LOGGER_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/fmt/fmt.h"
|
||||
|
||||
namespace spdlog {
|
||||
class logger;
|
||||
}
|
||||
|
||||
class Logger {
|
||||
mutable std::mutex m;
|
||||
|
||||
std::shared_ptr<spdlog::logger> spdlog_logger;
|
||||
|
||||
std::string service;
|
||||
std::string hostname;
|
||||
public:
|
||||
Logger(const std::string &service_name, const std::string &file_name = "");
|
||||
void ErrorException(const std::exception &e);
|
||||
|
||||
void Info(const std::string& msg) { spdlog_logger->info(msg); }
|
||||
void Warning(const std::string& msg) { spdlog_logger->warn(msg); }
|
||||
void Error(const std::string& msg) { spdlog_logger->error(msg); }
|
||||
void Debug(const std::string& msg) { spdlog_logger->debug(msg); }
|
||||
|
||||
template<typename... Args>
|
||||
void Info(fmt::format_string<Args...> fmt, Args &&... args) {
|
||||
spdlog_logger->info(fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void Error(fmt::format_string<Args...> fmt, Args &&... args) {
|
||||
spdlog_logger->error(fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void Debug(fmt::format_string<Args...> fmt, Args &&... args) {
|
||||
spdlog_logger->debug(fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void Warning(fmt::format_string<Args...> fmt, Args &&... args) {
|
||||
spdlog_logger->warn(fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
Logger& Verbose(bool input);
|
||||
};
|
||||
|
||||
#endif //JUNGFRAUJOCH_LOGGER_H
|
||||
50
common/NetworkAddressConvert.cpp
Normal file
50
common/NetworkAddressConvert.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "NetworkAddressConvert.h"
|
||||
#include "JFJochException.h"
|
||||
|
||||
uint32_t IPv4AddressFromStr(const std::string& addr) {
|
||||
in_addr ipv4_addr{};
|
||||
if (inet_pton(AF_INET, addr.c_str(), &ipv4_addr) != 1)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Need proper IPv4 address");
|
||||
return ipv4_addr.s_addr;
|
||||
}
|
||||
|
||||
std::string IPv4AddressToStr(uint32_t addr) {
|
||||
char tmp[256];
|
||||
if (inet_ntop(AF_INET, &addr, tmp, 255) != tmp)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Error in exporting IPv4 address");
|
||||
return {tmp};
|
||||
}
|
||||
|
||||
std::string MacAddressToStr(uint64_t addr) {
|
||||
uint64_t mac_address_network_order = addr;
|
||||
std::ostringstream ss;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
ss << std::setfill('0') << std::setw(2) << std::hex << ((mac_address_network_order >> (i * 8)) & 0xFF);
|
||||
if (i != 5) ss << ":";
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
uint64_t MacAddressFromStr(const std::string &addr) {
|
||||
uint64_t ret = 0;
|
||||
uint32_t values[6];
|
||||
char end_char;
|
||||
if( sscanf( addr.c_str(), "%x:%x:%x:%x:%x:%x%c",
|
||||
&values[0], &values[1], &values[2],
|
||||
&values[3], &values[4], &values[5], &end_char ) != 6 )
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "MAC address invalid");
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (values[i] > 255)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "MAC address out of range");
|
||||
ret |= (uint64_t)values[i] << (i*8);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
15
common/NetworkAddressConvert.h
Normal file
15
common/NetworkAddressConvert.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_NETWORKADDRESSCONVERT_H
|
||||
#define JUNGFRAUJOCH_NETWORKADDRESSCONVERT_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
std::string IPv4AddressToStr(uint32_t addr);
|
||||
std::string MacAddressToStr(uint64_t addr);
|
||||
uint32_t IPv4AddressFromStr(const std::string& addr);
|
||||
uint64_t MacAddressFromStr(const std::string& addr);
|
||||
|
||||
#endif //JUNGFRAUJOCH_NETWORKADDRESSCONVERT_H
|
||||
74
common/RadialIntegration.cpp
Normal file
74
common/RadialIntegration.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "RadialIntegration.h"
|
||||
#include "JFJochException.h"
|
||||
|
||||
RadialIntegration::RadialIntegration(const std::vector<uint16_t>& in_mapping, uint16_t in_nbins) :
|
||||
pixel_to_bin(in_mapping), nbins(in_nbins), sum(in_nbins, 0), count(in_nbins, 0)
|
||||
{}
|
||||
|
||||
RadialIntegration::RadialIntegration(const RadialIntegrationMapping &mapping) :
|
||||
RadialIntegration(mapping.GetPixelToBinMapping(), mapping.GetBinNumber())
|
||||
{}
|
||||
|
||||
void RadialIntegration::Clear() {
|
||||
for (auto &i : sum)
|
||||
i = 0;
|
||||
|
||||
for (auto &i : count)
|
||||
i = 0;
|
||||
}
|
||||
|
||||
void RadialIntegration::Process(const int16_t *data, size_t npixel) {
|
||||
if (npixel != pixel_to_bin.size())
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Mismatch in size of pixel-to-bin mapping and image");
|
||||
|
||||
for (int i = 0; i < npixel; i++) {
|
||||
auto bin = pixel_to_bin[i];
|
||||
auto value = data[i];
|
||||
if ((value > INT16_MIN + 4) && (value < INT16_MAX - 4) && (bin < nbins)) {
|
||||
sum[bin] += value;
|
||||
count[bin] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RadialIntegration::ProcessOneImage(const int16_t *data, size_t npixel) {
|
||||
Clear();
|
||||
Process(data, npixel);
|
||||
}
|
||||
|
||||
void RadialIntegration::GetResult(std::vector<float> &result) const {
|
||||
result.resize(nbins);
|
||||
|
||||
for (int i = 0; i < nbins; i++) {
|
||||
if (count[i] > 0)
|
||||
result[i] = static_cast<float>(sum[i]) / static_cast<float>(count[i]);
|
||||
else
|
||||
result[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<int64_t> &RadialIntegration::GetSum() const {
|
||||
return sum;
|
||||
}
|
||||
|
||||
const std::vector<int64_t> &RadialIntegration::GetCount() const {
|
||||
return count;
|
||||
}
|
||||
|
||||
float RadialIntegration::GetRangeValue(uint16_t min_bin, uint16_t max_bin) {
|
||||
int64_t ret_sum = 0;
|
||||
int64_t ret_count = 0;
|
||||
|
||||
for (int i = std::min(nbins,min_bin); i <= std::min((uint16_t)(nbins-1),max_bin); i++) {
|
||||
ret_sum += sum[i];
|
||||
ret_count += count[i];
|
||||
}
|
||||
if (ret_count == 0)
|
||||
return 0;
|
||||
else
|
||||
return static_cast<float>(ret_sum) / static_cast<float>(ret_count);
|
||||
}
|
||||
32
common/RadialIntegration.h
Normal file
32
common/RadialIntegration.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_RADIALINTEGRATION_H
|
||||
#define JUNGFRAUJOCH_RADIALINTEGRATION_H
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
|
||||
#include "DiffractionExperiment.h"
|
||||
#include "RadialIntegrationMapping.h"
|
||||
|
||||
class RadialIntegration {
|
||||
const std::vector<uint16_t>& pixel_to_bin;
|
||||
const uint16_t nbins;
|
||||
std::vector<int64_t> sum;
|
||||
std::vector<int64_t> count;
|
||||
public:
|
||||
RadialIntegration(const RadialIntegrationMapping& mapping);
|
||||
RadialIntegration(const std::vector<uint16_t>& mapping, uint16_t nbins);
|
||||
void Clear();
|
||||
void Process(const int16_t *data, size_t npixel);
|
||||
void ProcessOneImage(const int16_t *data, size_t npixel); // Process + Clear
|
||||
void GetResult(std::vector<float> &result) const;
|
||||
[[nodiscard]] float GetRangeValue(uint16_t min_bin, uint16_t max_bin);
|
||||
[[nodiscard]] const std::vector<int64_t>& GetSum() const;
|
||||
[[nodiscard]] const std::vector<int64_t>& GetCount() const;
|
||||
};
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_RADIALINTEGRATION_H
|
||||
73
common/RadialIntegrationMapping.cpp
Normal file
73
common/RadialIntegrationMapping.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "RadialIntegrationMapping.h"
|
||||
#include "JFJochException.h"
|
||||
|
||||
RadialIntegrationMapping::RadialIntegrationMapping(const DiffractionExperiment& experiment, const uint8_t *one_byte_mask) :
|
||||
low_q(experiment.GetLowQForRadialInt_recipA()),
|
||||
high_q(experiment.GetHighQForRadialInt_recipA()),
|
||||
q_spacing(experiment.GetQSpacingForRadialInt_recipA()),
|
||||
pixel_to_bin(experiment.GetPixelsNum()),
|
||||
max_bin_number(0)
|
||||
{
|
||||
|
||||
if (q_spacing < 0.0)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Q-spacing has to be >= 0.0");
|
||||
|
||||
if (low_q >= high_q)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Low-Q must be smaller than high-Q");
|
||||
|
||||
if (low_q <= 0)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Low-Q must be > 0");
|
||||
|
||||
if (std::ceil((high_q-low_q) / q_spacing ) >= UINT16_MAX)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Cannot accommodate more than 65536 rings");
|
||||
|
||||
for (int y = 0; y < experiment.GetYPixelsNum(); y++) {
|
||||
for (int x = 0; x < experiment.GetXPixelsNum(); x++) {
|
||||
int64_t pixel_number = y * experiment.GetXPixelsNum() + x;
|
||||
|
||||
double pixel_q = 2 * M_PI / experiment.PxlToRes(x, y);
|
||||
if (((one_byte_mask != nullptr) && (one_byte_mask[pixel_number] == 0))
|
||||
|| (pixel_q < low_q)
|
||||
|| (pixel_q >= high_q))
|
||||
pixel_to_bin[pixel_number] = UINT16_MAX;
|
||||
else
|
||||
pixel_to_bin[pixel_number] = std::floor((pixel_q - low_q) / q_spacing);
|
||||
}
|
||||
}
|
||||
|
||||
// In principle max bin number is equal to std::floor((high_q - low_q) / q_spacing), but it might be lower than this
|
||||
// depending on detector distance and beam center position
|
||||
for (const auto &i: pixel_to_bin) {
|
||||
if ((i != UINT16_MAX) && (i > max_bin_number))
|
||||
max_bin_number = i;
|
||||
}
|
||||
|
||||
bin_to_q.resize(max_bin_number + 1);
|
||||
for (int i = 0; i < max_bin_number + 1; i++)
|
||||
bin_to_q[i] = static_cast<float>(q_spacing * (i + 0.5) + low_q);
|
||||
}
|
||||
|
||||
uint16_t RadialIntegrationMapping::GetBinNumber() const {
|
||||
return max_bin_number + 1;
|
||||
}
|
||||
|
||||
const std::vector<uint16_t> &RadialIntegrationMapping::GetPixelToBinMapping() const {
|
||||
return pixel_to_bin;
|
||||
}
|
||||
|
||||
const std::vector<float> &RadialIntegrationMapping::GetBinToQ() const {
|
||||
return bin_to_q;
|
||||
}
|
||||
|
||||
double RadialIntegrationMapping::QToBin(double q) const {
|
||||
return std::min(static_cast<double>(max_bin_number), std::max(0.0, (q - low_q) / q_spacing));
|
||||
}
|
||||
24
common/RadialIntegrationMapping.h
Normal file
24
common/RadialIntegrationMapping.h
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_RADIALINTEGRATIONMAPPING_H
|
||||
#define JUNGFRAUJOCH_RADIALINTEGRATIONMAPPING_H
|
||||
|
||||
#include <optional>
|
||||
#include "DiffractionExperiment.h"
|
||||
|
||||
class RadialIntegrationMapping {
|
||||
const double low_q, high_q, q_spacing;
|
||||
std::vector<float> bin_to_q;
|
||||
std::vector<uint16_t> pixel_to_bin;
|
||||
uint16_t max_bin_number;
|
||||
public:
|
||||
RadialIntegrationMapping(const DiffractionExperiment& experiment, const uint8_t *one_byte_mask = nullptr);
|
||||
[[nodiscard]] uint16_t GetBinNumber() const;
|
||||
[[nodiscard]] const std::vector<uint16_t> &GetPixelToBinMapping() const;
|
||||
[[nodiscard]] const std::vector<float> &GetBinToQ() const;
|
||||
[[nodiscard]] double QToBin(double q) const;
|
||||
};
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_RADIALINTEGRATIONMAPPING_H
|
||||
298
common/RawToConvertedGeometry.h
Normal file
298
common/RawToConvertedGeometry.h
Normal file
@@ -0,0 +1,298 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_RAWTOCONVERTEDGEOMETRY_H
|
||||
#define JUNGFRAUJOCH_RAWTOCONVERTEDGEOMETRY_H
|
||||
|
||||
#include <cmath>
|
||||
#include "DiffractionExperiment.h"
|
||||
|
||||
// Take half of the number, but only if not bad pixel/overload
|
||||
template <typename T> T half(T in, T min, T max) {
|
||||
T tmp = in;
|
||||
if ((in > min) && (in < max)) tmp /= 2;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <typename T> T quarter(T in, T min, T max) {
|
||||
T tmp = in;
|
||||
if ((in > min) && (in < max)) tmp /= 4;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
// Copy line, divide everything by 2 and extend multipixels + divide them by additional factor of 2
|
||||
template <typename Td, typename Ts> void LineCopyAndAdjustMultipixelMidRow(Td *destination, const Ts* source, Ts min, Ts max) {
|
||||
for (int chip = 0; chip < 4; chip++) {
|
||||
for (int i = 0; i < 256; i++) {
|
||||
destination[i+chip*258] = half(source[i+chip*256], min, max);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
destination[255+i*258] = quarter(source[255 + i*256], min, max);
|
||||
destination[256+i*258] = quarter(source[255 + i*256], min, max);
|
||||
destination[257+i*258] = quarter(source[256 + i*256], min, max);
|
||||
destination[258+i*258] = quarter(source[256 + i*256], min, max);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy line and extend multipixels + divide them by 2
|
||||
template <typename Td, typename Ts> void LineCopyAndAdjustMultipixel(Td *destination, const Ts* source, Ts min, Ts max) {
|
||||
for (int chip = 0; chip < 4; chip++) {
|
||||
for (int i = 0; i < 256; i++) {
|
||||
destination[i + chip * 258] = source[i + chip * 256];
|
||||
}
|
||||
}
|
||||
for (int chip = 0; chip < 3; chip++) {
|
||||
destination[255+chip*258] = half(source[255 + chip*256], min, max);
|
||||
destination[256+chip*258] = half(source[255 + chip*256], min, max);
|
||||
destination[257+chip*258] = half(source[256 + chip*256], min, max);
|
||||
destination[258+chip*258] = half(source[256 + chip*256], min, max);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy line and copy multipixels (e.g. for mask)
|
||||
template <typename Td, typename Ts> void LineCopyAndAddMultipixel(Td *destination, const Ts* source) {
|
||||
for (int chip = 0; chip < 4; chip++) {
|
||||
for (int i = 0; i < 256; i++)
|
||||
destination[i+chip*258] = source[i+chip*256];
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
destination[256+i*258] = source[255 + i*256];
|
||||
destination[257+i*258] = source[256 + i*256];
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, class Tint = int32_t>
|
||||
T Bin2x2_sum(T a, T b, T c, T d, T underload, T overload) {
|
||||
T ret;
|
||||
if ((a <= underload) || (b <= underload) || (c <= underload) || (d <= underload))
|
||||
ret = underload;
|
||||
else if ((a >= overload) || (b >= overload) || (c >= overload) || (d >= overload))
|
||||
ret = overload;
|
||||
else {
|
||||
Tint sum = a + b + c +d;
|
||||
if (sum > overload)
|
||||
ret = overload;
|
||||
else
|
||||
ret = static_cast<T>(sum);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Copy line and copy multipixels (e.g. for mask)
|
||||
template <class Ts, class Tint = int32_t>
|
||||
void LineCopyAndAdjustMultipixelBin2x2(Ts *destination, const Ts* source, Ts min, Ts max) {
|
||||
|
||||
destination[0] = Bin2x2_sum<Ts, Tint>(source[0], source[1], source[1024], source[1025], min, max);
|
||||
|
||||
for (int chip = 0; chip < 4; chip++) {
|
||||
for (int i = 1; i < 127; i++)
|
||||
destination[i + 129 * chip] = Bin2x2_sum<Ts, Tint>(source[2 * i + 256*chip],
|
||||
source[2 * i + 1 + 256*chip],
|
||||
source[2 * i + 1024 + 256*chip],
|
||||
source[2 * i + 1025 + 256*chip],
|
||||
min, max);
|
||||
}
|
||||
|
||||
destination[514] = Bin2x2_sum<Ts, Tint>(source[1022], source[1023], source[2046], source[2047], min, max);
|
||||
|
||||
for (int chip = 0; chip < 3; chip++) {
|
||||
destination[127 + chip * 129] = Bin2x2_sum<Ts, Tint>(source[254 + chip * 256],
|
||||
half(source[255 + chip * 256], min, max),
|
||||
source[254 + chip * 256 + 1024],
|
||||
half(source[255 + chip * 256 + 1024], min, max),
|
||||
min, max);
|
||||
destination[128 + chip * 129] = Bin2x2_sum<Ts, Tint>(half(source[255 + chip * 256], min, max),
|
||||
half(source[256 + chip * 256], min, max),
|
||||
half(source[255 + chip * 256 + 1024], min, max),
|
||||
half(source[256 + chip * 256 + 1024], min, max),
|
||||
min, max);
|
||||
destination[129 + chip * 129] = Bin2x2_sum<Ts, Tint>(source[257 + chip * 256],
|
||||
half(source[256 + chip * 256], min, max),
|
||||
source[257 + chip * 256 + 1024],
|
||||
half(source[256 + chip * 256 + 1024], min, max),
|
||||
min, max);
|
||||
};
|
||||
}
|
||||
|
||||
template <class Ts, class Tint = int32_t>
|
||||
void LineCopyAndAdjustMultipixelBin2x2MidRow(Ts *destination, const Ts* source, Ts min, Ts max) {
|
||||
|
||||
destination[0] = Bin2x2_sum<Ts, Tint>(source[0], source[1], 0, 0, min, max);
|
||||
|
||||
for (int chip = 0; chip < 4; chip++) {
|
||||
for (int i = 1; i < 127; i++)
|
||||
destination[i + 129*chip] = Bin2x2_sum<Ts, Tint>(source[2 * i + 256*chip],
|
||||
source[2 * i + 1 + 256*chip],
|
||||
0,
|
||||
0,
|
||||
min, max);
|
||||
}
|
||||
|
||||
destination[514] = Bin2x2_sum<Ts, Tint>(source[1022], source[1023], 0, 0, min, max);
|
||||
|
||||
for (int chip = 0; chip < 3; chip++) {
|
||||
destination[127+chip*129] = Bin2x2_sum<Ts, Tint>(source[254 + chip * 256],
|
||||
half(source[255 + chip * 256], min, max),
|
||||
0,
|
||||
0,
|
||||
min, max);
|
||||
destination[128+chip*129] = Bin2x2_sum<Ts, Tint>(half(source[255 + chip * 256], min, max),
|
||||
half(source[256 + chip * 256], min, max),
|
||||
0,
|
||||
0,
|
||||
min, max);
|
||||
destination[129+chip*129] = Bin2x2_sum<Ts, Tint>(source[257 + chip * 256],
|
||||
half(source[256 + chip * 256], min, max),
|
||||
0,
|
||||
0,
|
||||
min, max);
|
||||
}
|
||||
}
|
||||
|
||||
template <class Td, class Ts> void TransferModule(Td *destination, const Ts *source, int64_t line_shift) {
|
||||
for (size_t line = 0; line < RAW_MODULE_LINES; line++) {
|
||||
if ((line == 255) || (line == 256)) {
|
||||
LineCopyAndAddMultipixel<Td, Ts>(destination + line_shift * (line + 1), source + RAW_MODULE_COLS * line);
|
||||
LineCopyAndAddMultipixel<Td, Ts>(destination + line_shift * (line + ((line > 255) ? 2 : 0)), source + RAW_MODULE_COLS * line);
|
||||
} else {
|
||||
LineCopyAndAddMultipixel<Td, Ts>(destination + line_shift * (line + ((line > 255) ? 2 : 0)), source + RAW_MODULE_COLS * line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class Td, class Ts> void TransferModuleAdjustMultipixels(Td *destination, const Ts *source, int64_t line_shift, Ts min, Ts max) {
|
||||
for (size_t line = 0; line < RAW_MODULE_LINES; line++) {
|
||||
if ((line != 255) && (line != 256))
|
||||
LineCopyAndAdjustMultipixel<Td, Ts>(destination + line_shift * (line + ((line > 255) ? 2 : 0)),
|
||||
source + RAW_MODULE_COLS * line, min, max);
|
||||
else {
|
||||
LineCopyAndAdjustMultipixelMidRow<Td, Ts>(destination + line_shift * (line + 1),
|
||||
source + RAW_MODULE_COLS * line, min, max);
|
||||
memcpy(destination + line_shift * (line + ((line > 255) ? 2 : 0)),
|
||||
destination + line_shift * (line + 1),CONVERTED_MODULE_COLS*sizeof(Td));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class Ts, class Tint = int32_t>
|
||||
void TransferModuleAdjustMultipixelsBin2x2(Ts *destination, const Ts *source,
|
||||
int64_t line_shift, Ts min, Ts max) {
|
||||
for (size_t line = 0; line < RAW_MODULE_LINES/2; line++) {
|
||||
if (line == 127)
|
||||
LineCopyAndAdjustMultipixelBin2x2MidRow<Ts, Tint>(destination + line_shift * 128,
|
||||
source + 255 * RAW_MODULE_COLS, min, max);
|
||||
if (line == 128)
|
||||
LineCopyAndAdjustMultipixelBin2x2MidRow<Ts, Tint>(destination + line_shift * 129,
|
||||
source + 256 * RAW_MODULE_COLS, min, max);
|
||||
else
|
||||
LineCopyAndAdjustMultipixelBin2x2<Ts, Tint>(destination + line_shift * (line + ((line > 127) ? 1 : 0)),
|
||||
source + line * 2 * RAW_MODULE_COLS, min, max);
|
||||
}
|
||||
}
|
||||
|
||||
inline std::pair<int64_t, int64_t> RawToConvertedCoordinate(const DiffractionExperiment& experiment,
|
||||
uint32_t module_number,
|
||||
uint32_t pixel_within_module) {
|
||||
int64_t line0 = experiment.GetPixel0OfModule(module_number) / experiment.GetXPixelsNum();
|
||||
int64_t col0 = experiment.GetPixel0OfModule(module_number) % experiment.GetXPixelsNum();
|
||||
|
||||
int64_t line = pixel_within_module / RAW_MODULE_COLS;
|
||||
int64_t col = pixel_within_module % RAW_MODULE_COLS;
|
||||
|
||||
line += (line / 256) * 2;
|
||||
col += (col / 256) * 2;
|
||||
|
||||
return {col0 + col, line0 + (experiment.IsUpsideDown() ? -1 : 1) * line};
|
||||
}
|
||||
|
||||
inline Coord RawToConvertedCoordinate(const DiffractionExperiment& experiment, uint16_t data_stream, const Coord &raw) {
|
||||
if (experiment.GetDetectorMode() == DetectorMode::Conversion) {
|
||||
size_t module_number = experiment.GetFirstModuleOfDataStream(data_stream) + raw.y / RAW_MODULE_LINES;
|
||||
double line = raw.y - floor(raw.y / RAW_MODULE_LINES) * RAW_MODULE_LINES;
|
||||
double col = raw.x;
|
||||
|
||||
line += floor(line / 256) * 2;
|
||||
col += floor(col / 256) * 2;
|
||||
|
||||
Coord out(experiment.GetPixel0OfModule(module_number) % experiment.GetXPixelsNum(),
|
||||
experiment.GetPixel0OfModule(module_number) / experiment.GetXPixelsNum(),
|
||||
raw.z);
|
||||
out.x += col;
|
||||
out.y += (experiment.IsUpsideDown() ? -1 : 1) * line;
|
||||
|
||||
return out;
|
||||
} else return raw;
|
||||
}
|
||||
|
||||
// Input coord is column + 1024 * (line + 512 * module)
|
||||
// Copies result over multipixel - can be used for example for mask calculation
|
||||
template <class Td, class Ts> void RawToConvertedGeometry(const DiffractionExperiment& experiment, Td *destination, const Ts *source) {
|
||||
for (size_t module_number = 0; module_number < experiment.GetModulesNum(); module_number++)
|
||||
TransferModule<Td, Ts>(destination + experiment.GetPixel0OfModule(module_number),
|
||||
source + module_number * RAW_MODULE_SIZE,
|
||||
(experiment.IsUpsideDown() ? -1 : 1) * experiment.GetXPixelsNum());
|
||||
}
|
||||
|
||||
|
||||
// Input coord is column + 1024 * (line + 512 * module)
|
||||
template <class Ts> void RawToConvertedGeometryAdjustMultipixels(const DiffractionExperiment& experiment, Ts *destination, const Ts *source) {
|
||||
Ts min = experiment.GetUnderflow() + 1;
|
||||
if (min > 0) min = 0;
|
||||
Ts max = experiment.GetOverflow() - 1;
|
||||
|
||||
for (size_t module_number = 0; module_number < experiment.GetModulesNum(); module_number++) {
|
||||
if (experiment.GetBinning2x2())
|
||||
TransferModuleAdjustMultipixelsBin2x2<Ts, float>(destination + experiment.GetPixel0OfModule(module_number),
|
||||
source + module_number * RAW_MODULE_SIZE,
|
||||
(experiment.IsUpsideDown() ? -1 : 1) * experiment.GetXPixelsNum(), min, max);
|
||||
else
|
||||
TransferModuleAdjustMultipixels<Ts, Ts>(destination + experiment.GetPixel0OfModule(module_number),
|
||||
source + module_number * RAW_MODULE_SIZE,
|
||||
(experiment.IsUpsideDown() ? -1 : 1) * experiment.GetXPixelsNum(), min, max);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T> void LineConvtToRaw(T *destination, const T* source) {
|
||||
for (int chip = 0; chip < 4; chip++) {
|
||||
for (int i = 0; i < 256; i++)
|
||||
destination[i+chip*256] = source[i+chip*258];
|
||||
}
|
||||
}
|
||||
|
||||
template <class T> void ConvertedToRawGeometry(const DiffractionExperiment& experiment, T *destination, const T* source) {
|
||||
for (size_t module_number = 0; module_number < experiment.GetModulesNum(); module_number++) {
|
||||
for (size_t line = 0; line < RAW_MODULE_LINES; line++) {
|
||||
LineConvtToRaw<T>(destination + module_number * RAW_MODULE_SIZE + RAW_MODULE_COLS * line,
|
||||
source + experiment.GetPixel0OfModule(module_number) + (experiment.IsUpsideDown() ? -1 : 1) * experiment.GetXPixelsNum() * (line + ((line > 255) ? 2 : 0))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class T> void Bin2x2_or(T *destination, const T* source, size_t width, size_t height) {
|
||||
for (int y = 0; y < height / 2; y++) {
|
||||
for (int x = 0; x < width / 2; x++) {
|
||||
T tmp[4];
|
||||
tmp[0] = source[(y * 2) * width + (x * 2)];
|
||||
tmp[1] = source[(y * 2 + 1) * width + (x * 2)];
|
||||
tmp[2] = source[(y * 2) * width + (x * 2 + 1)];
|
||||
tmp[3] = source[(y * 2 + 1) * width + (x * 2 + 1)];
|
||||
destination[y * (width/2) + x] = tmp[0] | tmp[1] | tmp[2] | tmp[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, class Tint = int32_t>
|
||||
void Bin2x2_sum(T *destination, const T* source, size_t width, size_t height, T underload, T overload) {
|
||||
for (int y = 0; y < height / 2; y++) {
|
||||
for (int x = 0; x < width / 2; x++)
|
||||
destination[y * (width/2) + x] = Bin2x2_sum<T, Tint>(source[(y * 2) * width + (x * 2)],
|
||||
source[(y * 2 + 1) * width + (x * 2)],
|
||||
source[(y * 2) * width + (x * 2 + 1)],
|
||||
source[(y * 2 + 1) * width + (x * 2 + 1)],
|
||||
underload, overload);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
13
common/SpotToSave.h
Normal file
13
common/SpotToSave.h
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_SPOTTOSAVE_H
|
||||
#define JUNGFRAUJOCH_SPOTTOSAVE_H
|
||||
|
||||
struct SpotToSave {
|
||||
float x;
|
||||
float y;
|
||||
float intensity;
|
||||
};
|
||||
|
||||
#endif //JUNGFRAUJOCH_SPOTTOSAVE_H
|
||||
88
common/StatusVector.h
Normal file
88
common/StatusVector.h
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_STATUSVECTOR_H
|
||||
#define JUNGFRAUJOCH_STATUSVECTOR_H
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
#include <jfjoch.pb.h>
|
||||
|
||||
#include "JFJochException.h"
|
||||
|
||||
|
||||
template <class T> class StatusVector {
|
||||
mutable std::mutex m;
|
||||
std::map<uint32_t, T > content;
|
||||
uint32_t max_id = 0;
|
||||
public:
|
||||
void AddElement(uint32_t id, T val) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
content[id] = val;
|
||||
if (id > max_id)
|
||||
max_id = id;
|
||||
}
|
||||
|
||||
[[nodiscard]] float Mean() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
size_t count = content.size();
|
||||
double sum = 0;
|
||||
for (auto &[x,y]: content)
|
||||
sum += y;
|
||||
|
||||
if (count > 0)
|
||||
return static_cast<float>(sum / count);
|
||||
else
|
||||
return NAN;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<float> GetStatus(int32_t bin_size) const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
if (bin_size <= 0)
|
||||
throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds,
|
||||
"Bin number must be greater than zero");
|
||||
|
||||
std::vector<float> ret;
|
||||
if (!content.empty()) {
|
||||
for (int bin = 0; bin < max_id / bin_size + ((max_id % bin_size > 0) ? 1 : 0); bin++) {
|
||||
double sum = 0;
|
||||
int64_t count = 0;
|
||||
|
||||
auto it_start = content.lower_bound(bin * bin_size);
|
||||
auto it_end = content.upper_bound((bin + 1) * bin_size);
|
||||
if ((it_start != content.end()) && (it_start->first < (bin + 1) * bin_size)) {
|
||||
for (auto it = it_start; it != it_end; it++) {
|
||||
sum += it->second;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count > 0)
|
||||
ret.push_back(static_cast<float>(sum / static_cast<double>(count)));
|
||||
else
|
||||
ret.push_back(0.0);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GetPlot(JFJochProtoBuf::Plot& plot, int32_t bin_size) const {
|
||||
// GetStatus has mutex, no need to lock again
|
||||
auto status = GetStatus(bin_size);
|
||||
if (status.size() == 1) {
|
||||
plot.add_x(max_id / 2.0);
|
||||
plot.add_y(status[0]);
|
||||
} else if (!status.empty()) {
|
||||
*plot.mutable_y() = {status.begin(), status.end()};
|
||||
|
||||
for (int i = 0; i < status.size(); i++)
|
||||
plot.add_x(bin_size * (i + 0.5));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_STATUSVECTOR_H
|
||||
126
common/StrongPixelSet.cpp
Normal file
126
common/StrongPixelSet.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <bitset>
|
||||
#include "StrongPixelSet.h"
|
||||
|
||||
StrongPixelSet::StrongPixelSet(const DiffractionExperiment &experiment)
|
||||
: xpixel(experiment.GetXPixelsNum()),
|
||||
ypixel(experiment.GetYPixelsNum()),
|
||||
strong_pixel_vector(experiment.GetPixelsNum(), false) {
|
||||
}
|
||||
|
||||
void StrongPixelSet::AddStrongPixel(uint16_t col, uint16_t line, int32_t photons) {
|
||||
auto key = strong_pixel_coord(col, line);
|
||||
strong_pixel_map[key] = photons;
|
||||
strong_pixel_vector.at(line * xpixel + col) = true;
|
||||
}
|
||||
|
||||
inline void StrongPixelSet::AddNeighbor(DiffractionSpot &spot, uint16_t col, uint16_t line) {
|
||||
if (strong_pixel_vector[line * xpixel + col]) {
|
||||
uint64_t coord = strong_pixel_coord(col, line);
|
||||
auto iter = strong_pixel_map.find(coord);
|
||||
ExtendSpot(spot, iter);
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a continuous spot
|
||||
// strong pixels are loaded into dictionary (one dictionary per frame)
|
||||
// and routine checks if neighboring pixels are also in dictionary (likely in log(N) time)
|
||||
DiffractionSpot StrongPixelSet::BuildSpot(std::unordered_map<uint32_t, int32_t>::iterator &it) {
|
||||
|
||||
uint16_t col = col_from_strong_pixel(it->first);
|
||||
uint16_t line = line_from_strong_pixel(it->first);
|
||||
|
||||
DiffractionSpot spot(col, line, it->second);
|
||||
|
||||
strong_pixel_vector[line * xpixel + col] = false;
|
||||
strong_pixel_map.erase(it); // Remove strong pixel from the dictionary, so it is not processed again
|
||||
|
||||
if (col+1 < xpixel) {
|
||||
AddNeighbor(spot, col + 1, line);
|
||||
if (line < ypixel - 1)
|
||||
AddNeighbor(spot, col + 1, line + 1);
|
||||
if (line > 0)
|
||||
AddNeighbor(spot, col + 1, line - 1);
|
||||
}
|
||||
if (col != 0) {
|
||||
AddNeighbor(spot, col - 1, line);
|
||||
if (line < ypixel - 1)
|
||||
AddNeighbor(spot, col - 1, line + 1);
|
||||
if (line > 0)
|
||||
AddNeighbor(spot, col - 1, line - 1);
|
||||
}
|
||||
if (line < ypixel - 1)
|
||||
AddNeighbor(spot, col, line+1);
|
||||
if (line > 0)
|
||||
AddNeighbor(spot, col, line-1);
|
||||
|
||||
return spot;
|
||||
}
|
||||
|
||||
void StrongPixelSet::ExtendSpot(DiffractionSpot &spot, std::unordered_map<uint32_t, int32_t>::iterator &it) {
|
||||
|
||||
uint16_t col = col_from_strong_pixel(it->first);
|
||||
uint16_t line = line_from_strong_pixel(it->first);
|
||||
|
||||
spot.AddPixel(col, line, it->second);
|
||||
|
||||
strong_pixel_vector[line * xpixel + col] = false;
|
||||
strong_pixel_map.erase(it); // Remove strong pixel from the dictionary, so it is not processed again
|
||||
|
||||
if (col+1 < xpixel) {
|
||||
AddNeighbor(spot, col + 1, line);
|
||||
if (line < ypixel - 1)
|
||||
AddNeighbor(spot, col + 1, line + 1);
|
||||
if (line > 0)
|
||||
AddNeighbor(spot, col + 1, line - 1);
|
||||
}
|
||||
if (col != 0) {
|
||||
AddNeighbor(spot, col - 1, line);
|
||||
if (line < ypixel - 1)
|
||||
AddNeighbor(spot, col - 1, line + 1);
|
||||
if (line > 0)
|
||||
AddNeighbor(spot, col - 1, line - 1);
|
||||
}
|
||||
if (line < ypixel - 1)
|
||||
AddNeighbor(spot, col, line+1);
|
||||
if (line > 0)
|
||||
AddNeighbor(spot, col, line-1);
|
||||
}
|
||||
|
||||
void StrongPixelSet::FindSpots(const DiffractionExperiment &experiment, const JFJochProtoBuf::DataProcessingSettings &settings,
|
||||
std::vector<DiffractionSpot> &spots) {
|
||||
std::multimap<double, DiffractionSpot> spots_map;
|
||||
|
||||
while (!strong_pixel_map.empty()) {
|
||||
auto iter = strong_pixel_map.begin();
|
||||
DiffractionSpot spot = BuildSpot(iter);
|
||||
double d = spot.GetResolution(experiment);
|
||||
|
||||
if ((spot.PixelCount() <= settings.max_pix_per_spot())
|
||||
&& (spot.PixelCount() >= settings.min_pix_per_spot())
|
||||
&& (!settings.has_low_resolution_limit() || (d <= settings.low_resolution_limit()))
|
||||
&& (!settings.has_high_resolution_limit() || (d >= settings.high_resolution_limit())))
|
||||
spots_map.insert(std::make_pair(-static_cast<float>(d), spot));
|
||||
}
|
||||
|
||||
for (auto &[x, spot]: spots_map)
|
||||
spots.push_back(spot);
|
||||
|
||||
if (experiment.GetMaxSpotCount() > 0)
|
||||
spots.resize(std::min<size_t>(spots.size(), experiment.GetMaxSpotCount()));
|
||||
}
|
||||
|
||||
size_t StrongPixelSet::Count() const {
|
||||
return strong_pixel_map.size();
|
||||
}
|
||||
|
||||
size_t StrongPixelSet::Common(const StrongPixelSet &set) const {
|
||||
size_t ret = 0;
|
||||
for (const auto& pixel: strong_pixel_map) {
|
||||
if (set.strong_pixel_map.count(pixel.first) == 1)
|
||||
ret++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
42
common/StrongPixelSet.h
Normal file
42
common/StrongPixelSet.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_STRONGPIXELSET_H
|
||||
#define JUNGFRAUJOCH_STRONGPIXELSET_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
#include "DiffractionExperiment.h"
|
||||
#include "Coord.h"
|
||||
#include "DiffractionSpot.h"
|
||||
|
||||
inline uint32_t strong_pixel_coord(uint16_t col, uint16_t line) {
|
||||
return col + (static_cast<uint32_t>(line) << 16u);
|
||||
}
|
||||
|
||||
inline uint16_t line_from_strong_pixel(uint32_t strong_pixel) {
|
||||
return ((strong_pixel & 0xFFFF0000u) >> 16u);
|
||||
}
|
||||
|
||||
inline uint16_t col_from_strong_pixel(uint32_t strong_pixel) {
|
||||
return (strong_pixel & 0x0000FFFFu);
|
||||
}
|
||||
|
||||
class StrongPixelSet {
|
||||
std::unordered_map<uint32_t, int32_t> strong_pixel_map;
|
||||
uint32_t xpixel;
|
||||
uint32_t ypixel;
|
||||
std::vector<bool> strong_pixel_vector;
|
||||
void AddNeighbor(DiffractionSpot &spot, uint16_t col, uint16_t line);
|
||||
DiffractionSpot BuildSpot(std::unordered_map<uint32_t, int32_t>::iterator &it_frames);
|
||||
void ExtendSpot(DiffractionSpot &spot, std::unordered_map<uint32_t, int32_t>::iterator &it_frames);
|
||||
public:
|
||||
StrongPixelSet(const DiffractionExperiment& experiment);
|
||||
size_t Count() const;
|
||||
void AddStrongPixel(uint16_t col, uint16_t line, int32_t photons = 1);
|
||||
void FindSpots(const DiffractionExperiment &experiment, const JFJochProtoBuf::DataProcessingSettings &settings, std::vector<DiffractionSpot> &spots);
|
||||
size_t Common(const StrongPixelSet &set) const;
|
||||
};
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_STRONGPIXELSET_H
|
||||
110
common/TestImagePusher.cpp
Normal file
110
common/TestImagePusher.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "TestImagePusher.h"
|
||||
#include "../tests/FPGAUnitTest.h"
|
||||
#include "JFJochCompressor.h"
|
||||
#include "../compression/JFJochDecompress.h"
|
||||
#include "../frame_serialize/JFJochFrameDeserializer.h"
|
||||
|
||||
TestImagePusher::TestImagePusher(int64_t image_number) {
|
||||
image_id = image_number;
|
||||
}
|
||||
|
||||
void TestImagePusher::StartDataCollection(const StartMessage& message) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
if (is_running)
|
||||
correct_sequence = false;
|
||||
else
|
||||
is_running = true;
|
||||
}
|
||||
|
||||
void TestImagePusher::EndDataCollection(const EndMessage& message) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
if (!is_running)
|
||||
correct_sequence = false;
|
||||
else
|
||||
is_running = false;
|
||||
}
|
||||
|
||||
void TestImagePusher::SendDataInternal(const std::vector<uint8_t> &serialized_image, int64_t image_number) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
frame_counter++;
|
||||
if (image_number == image_id) {
|
||||
|
||||
JFJochFrameDeserializer deserializer;
|
||||
deserializer.Process(serialized_image);
|
||||
|
||||
auto image_array = deserializer.GetDataMessage();
|
||||
receiver_generated_image.resize(image_array.image.size);
|
||||
memcpy(receiver_generated_image.data(), image_array.image.data, image_array.image.size);
|
||||
}
|
||||
}
|
||||
|
||||
bool TestImagePusher::CheckSequence() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
return correct_sequence;
|
||||
}
|
||||
|
||||
const std::vector<uint8_t> &TestImagePusher::GetImage() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
return receiver_generated_image;
|
||||
}
|
||||
|
||||
size_t TestImagePusher::GetCounter() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
return frame_counter;
|
||||
}
|
||||
|
||||
bool TestImagePusher::CheckImage(const DiffractionExperiment &x, const std::vector<uint16_t> &raw_reference_image,
|
||||
const JFCalibration &calibration,
|
||||
Logger &logger) {
|
||||
bool no_errors = true;
|
||||
|
||||
if (receiver_generated_image.empty()) {
|
||||
logger.Error("Image empty");
|
||||
no_errors = false;
|
||||
} else {
|
||||
std::vector<int16_t> decompressed_image;
|
||||
// Image decompression
|
||||
try {
|
||||
JFJochDecompress(decompressed_image, x.GetCompressionAlgorithmEnum(),
|
||||
receiver_generated_image, x.GetPixelsNum());
|
||||
} catch (const JFJochException &e) {
|
||||
logger.Error(e.what());
|
||||
no_errors = false;
|
||||
}
|
||||
if (no_errors) {
|
||||
// Check output
|
||||
|
||||
if (x.GetDetectorMode() == DetectorMode::Conversion) {
|
||||
size_t storage_cell = 0;
|
||||
if (x.GetStorageCellNumber() > 1)
|
||||
storage_cell = image_id % x.GetStorageCellNumber();
|
||||
|
||||
double result = CheckConversionWithGeomTransform(x, calibration,
|
||||
raw_reference_image.data(),
|
||||
decompressed_image.data(),
|
||||
storage_cell);
|
||||
if (x.GetBinning2x2() && (result > 1.5)) {
|
||||
logger.Error("Mean conversion error ({}) larger than threshold", result);
|
||||
no_errors = false;
|
||||
} else if (!x.GetBinning2x2() && (result > 0.5)) {
|
||||
logger.Error("Mean conversion error ({}) larger than threshold", result);
|
||||
no_errors = false;
|
||||
} else
|
||||
logger.Info("Mean conversion error: {}", result);
|
||||
} else if (x.GetDetectorMode() == DetectorMode::Raw) {
|
||||
if (memcmp(raw_reference_image.data(), decompressed_image.data(), sizeof(uint16_t) * x.GetPixelsNum()) !=
|
||||
0) {
|
||||
logger.Error("Raw data mismatch");
|
||||
no_errors = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return no_errors;
|
||||
}
|
||||
37
common/TestImagePusher.h
Normal file
37
common/TestImagePusher.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_TESTIMAGEPUSHER_H
|
||||
#define JUNGFRAUJOCH_TESTIMAGEPUSHER_H
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "ImagePusher.h"
|
||||
#include "Logger.h"
|
||||
#include "DiffractionExperiment.h"
|
||||
#include "../jungfrau/JFCalibration.h"
|
||||
#include "../jungfrau/JFModuleGainCalibration.h"
|
||||
|
||||
class TestImagePusher : public ImagePusher {
|
||||
mutable std::mutex m;
|
||||
std::vector<uint8_t> receiver_generated_image;
|
||||
int64_t image_id;
|
||||
bool correct_sequence = true;
|
||||
bool is_running = false;
|
||||
size_t frame_counter = 0;
|
||||
void SendDataInternal(const std::vector<uint8_t> &serialized_image, int64_t image_number) override;
|
||||
public:
|
||||
explicit TestImagePusher(int64_t image_number);
|
||||
void StartDataCollection(const StartMessage& message) override;
|
||||
void EndDataCollection(const EndMessage& message) override;
|
||||
bool CheckImage(const DiffractionExperiment &x,
|
||||
const std::vector<uint16_t> &raw_reference_image,
|
||||
const JFCalibration &calibration,
|
||||
Logger &logger);
|
||||
[[nodiscard]] bool CheckSequence() const;
|
||||
[[nodiscard]] const std::vector<uint8_t> &GetImage() const;
|
||||
[[nodiscard]] size_t GetCounter() const;
|
||||
};
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_TESTIMAGEPUSHER_H
|
||||
109
common/ThreadSafeFIFO.h
Normal file
109
common/ThreadSafeFIFO.h
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_THREADSAFEFIFO_H
|
||||
#define JUNGFRAUJOCH_THREADSAFEFIFO_H
|
||||
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <set>
|
||||
|
||||
template <class T> class ThreadSafeFIFO {
|
||||
std::queue<T> queue;
|
||||
std::condition_variable c_empty, c_full;
|
||||
std::mutex m;
|
||||
const size_t max_size;
|
||||
public:
|
||||
ThreadSafeFIFO(size_t in_max_size = UINT32_MAX) : max_size(in_max_size) {}
|
||||
void Clear() {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
queue = {};
|
||||
}
|
||||
|
||||
bool Put(T val) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (queue.size() < max_size) {
|
||||
queue.push(val);
|
||||
c_empty.notify_one();
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
};
|
||||
|
||||
void PutBlocking(T val) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
c_full.wait(ul, [&]{return queue.size() < max_size;});
|
||||
queue.push(val);
|
||||
c_empty.notify_one();
|
||||
};
|
||||
|
||||
int Get(T &val) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
if (queue.empty())
|
||||
return 0;
|
||||
else {
|
||||
val = queue.front();
|
||||
queue.pop();
|
||||
c_full.notify_one();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
T GetBlocking() {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
c_empty.wait(ul, [&]{return !queue.empty();});
|
||||
T tmp = queue.front();
|
||||
queue.pop();
|
||||
c_full.notify_one();
|
||||
return tmp;
|
||||
};
|
||||
|
||||
size_t Size() const {
|
||||
return queue.size();
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> class ThreadSafeSet {
|
||||
std::set<T> set;
|
||||
std::condition_variable c;
|
||||
std::mutex m;
|
||||
public:
|
||||
void Clear() {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
set = {};
|
||||
}
|
||||
void Put(T val) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
set.insert(val);
|
||||
c.notify_one();
|
||||
};
|
||||
|
||||
int Get(T &val) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
if (set.empty()) return 0;
|
||||
else {
|
||||
auto iter = set.begin();
|
||||
val = *iter;
|
||||
set.erase(iter);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
T GetBlocking() {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
c.wait(ul, [&]{return !set.empty();});
|
||||
auto iter = set.begin();
|
||||
T tmp = *iter;
|
||||
set.erase(iter);
|
||||
return tmp;
|
||||
};
|
||||
|
||||
size_t Size() const {
|
||||
return set.size();
|
||||
}
|
||||
};
|
||||
|
||||
#endif //JUNGFRAUJOCH_THREADSAFEFIFO_H
|
||||
16
common/UnitCell.h
Normal file
16
common/UnitCell.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_UNITCELL_H
|
||||
#define JUNGFRAUJOCH_UNITCELL_H
|
||||
|
||||
struct UnitCell {
|
||||
float a;
|
||||
float b;
|
||||
float c;
|
||||
float alpha;
|
||||
float beta;
|
||||
float gamma;
|
||||
};
|
||||
|
||||
#endif //JUNGFRAUJOCH_UNITCELL_H
|
||||
48
common/WriteTIFF.cpp
Normal file
48
common/WriteTIFF.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "WriteTIFF.h"
|
||||
#include "JFJochException.h"
|
||||
|
||||
#include <tiffio.h>
|
||||
#include <tiffio.hxx>
|
||||
#include <sstream>
|
||||
|
||||
void WriteTIFF(TIFF *tiff, void *buff, size_t cols, size_t lines, size_t elem_size, bool is_signed) {
|
||||
if (tiff == nullptr)
|
||||
throw JFJochException(JFJochExceptionCategory::TIFFGeneratorError, "TIFFStreamOpen error");
|
||||
|
||||
TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, cols); // set the width of the image
|
||||
TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, lines); // set the height of the image
|
||||
TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 1); // set number of channels per pixel
|
||||
TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, elem_size * 8); // set the size of the channels
|
||||
TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_LZW); // setc ompression to LZW
|
||||
TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, lines);
|
||||
if (is_signed)
|
||||
TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT);
|
||||
else
|
||||
TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
|
||||
|
||||
if (TIFFWriteEncodedStrip(tiff, 0, buff,cols * lines * elem_size) < 0)
|
||||
throw JFJochException(JFJochExceptionCategory::TIFFGeneratorError, "TIFFWriteEncodedStrip error");
|
||||
|
||||
}
|
||||
|
||||
std::string WriteTIFFToString(void *buff, size_t cols, size_t lines, size_t elem_size, bool is_signed) {
|
||||
std::stringstream os;
|
||||
|
||||
TIFF *tiff = TIFFStreamOpen("x", (std::ostream *) &os);
|
||||
WriteTIFF(tiff, buff, cols, lines, elem_size, is_signed);
|
||||
TIFFClose(tiff);
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
void WriteTIFFToFile(const std::string &filename, void *buff, size_t cols, size_t lines, size_t elem_size,
|
||||
bool is_signed) {
|
||||
TIFF *tiff = TIFFOpen(filename.c_str(), "w");
|
||||
|
||||
WriteTIFF(tiff, buff, cols, lines, elem_size, is_signed);
|
||||
|
||||
TIFFClose(tiff);
|
||||
}
|
||||
13
common/WriteTIFF.h
Normal file
13
common/WriteTIFF.h
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_WRITETIFF_H
|
||||
#define JUNGFRAUJOCH_WRITETIFF_H
|
||||
|
||||
#include <string>
|
||||
|
||||
std::string WriteTIFFToString(void *buff, size_t cols, size_t lines, size_t elem_size, bool is_signed = false);
|
||||
void WriteTIFFToFile(const std::string &filename, void *buff, size_t cols, size_t lines, size_t elem_size,
|
||||
bool is_signed = false);
|
||||
|
||||
#endif //JUNGFRAUJOCH_WRITETIFF_H
|
||||
77
common/ZMQImagePusher.cpp
Normal file
77
common/ZMQImagePusher.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "ZMQImagePusher.h"
|
||||
#include "JFJochException.h"
|
||||
|
||||
ZMQImagePusher::ZMQImagePusher(ZMQContext &zmq_context, const std::vector<std::string> &addr,
|
||||
int32_t send_buffer_high_watermark, int32_t send_buffer_size) {
|
||||
if (addr.empty())
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"No writer ZMQ address provided");
|
||||
|
||||
for (const auto &a : addr) {
|
||||
auto s = std::make_unique<ZMQSocket>(zmq_context, ZMQSocketType::Push);
|
||||
if (send_buffer_size > 0)
|
||||
s->SendBufferSize(send_buffer_size);
|
||||
if (send_buffer_high_watermark > 0)
|
||||
s->SendWaterMark(send_buffer_high_watermark);
|
||||
s->Bind(a);
|
||||
sockets.push_back(std::move(s));
|
||||
}
|
||||
}
|
||||
|
||||
ZMQImagePusher::ZMQImagePusher(const std::vector<std::string> &addr,
|
||||
int32_t send_buffer_high_watermark, int32_t send_buffer_size) {
|
||||
if (addr.empty())
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"No writer ZMQ address provided");
|
||||
|
||||
for (const auto &a : addr) {
|
||||
auto c = std::make_unique<ZMQContext>();
|
||||
auto s = std::make_unique<ZMQSocket>(*c, ZMQSocketType::Push);
|
||||
if (send_buffer_size > 0)
|
||||
s->SendBufferSize(send_buffer_size);
|
||||
if (send_buffer_high_watermark > 0)
|
||||
s->SendWaterMark(send_buffer_high_watermark);
|
||||
s->Bind(a);
|
||||
contexts.push_back(std::move(c));
|
||||
sockets.push_back(std::move(s));
|
||||
}
|
||||
}
|
||||
|
||||
void ZMQImagePusher::SendDataInternal(const std::vector<uint8_t> &serialized_image, int64_t image_number) {
|
||||
if (sockets.empty())
|
||||
return;
|
||||
auto socket_number = (image_number % file_count) % sockets.size();
|
||||
sockets[socket_number]->Send(serialized_image.data(), serialized_image.size());
|
||||
}
|
||||
|
||||
void ZMQImagePusher::StartDataCollection(const StartMessage& message) {
|
||||
JFJochFrameSerializer serializer(80*1024*1024); // 80 MiB should be safe even for 16M
|
||||
|
||||
if (message.data_file_count < 1)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"File count cannot be zero or negative");
|
||||
file_count = message.data_file_count;
|
||||
|
||||
serializer.SerializeSequenceStart(message);
|
||||
auto &buffer = serializer.GetBuffer();
|
||||
for (const auto &s: sockets)
|
||||
s->Send(buffer.data(), buffer.size(), true);
|
||||
}
|
||||
|
||||
|
||||
void ZMQImagePusher::EndDataCollection(const EndMessage& message) {
|
||||
JFJochFrameSerializer serializer(1024*1024);
|
||||
|
||||
EndMessage end_message = message;
|
||||
|
||||
for (const auto &s: sockets) {
|
||||
serializer.SerializeSequenceEnd(end_message);
|
||||
auto &buffer = serializer.GetBuffer();
|
||||
|
||||
s->Send(buffer.data(), buffer.size(), true);
|
||||
end_message.write_master_file = false;
|
||||
}
|
||||
}
|
||||
30
common/ZMQImagePusher.h
Normal file
30
common/ZMQImagePusher.h
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_ZMQIMAGEPUSHER_H
|
||||
#define JUNGFRAUJOCH_ZMQIMAGEPUSHER_H
|
||||
|
||||
#include <jfjoch.pb.h>
|
||||
|
||||
#include "ImagePusher.h"
|
||||
#include "ThreadSafeFIFO.h"
|
||||
#include "ZMQWrappers.h"
|
||||
#include "DiffractionSpot.h"
|
||||
#include "../frame_serialize/JFJochFrameSerializer.h"
|
||||
|
||||
class ZMQImagePusher : public ImagePusher {
|
||||
std::vector<std::unique_ptr<ZMQContext>> contexts;
|
||||
std::vector<std::unique_ptr<ZMQSocket>> sockets;
|
||||
int64_t file_count = 1;
|
||||
void SendDataInternal(const std::vector<uint8_t>& serialized_image, int64_t image_number) override;
|
||||
public:
|
||||
ZMQImagePusher(ZMQContext &context, const std::vector<std::string>& addr,
|
||||
int32_t send_buffer_high_watermark = -1, int32_t send_buffer_size = -1);
|
||||
// High performance implementation, where each socket has dedicated ZMQ context
|
||||
explicit ZMQImagePusher(const std::vector<std::string>& addr,
|
||||
int32_t send_buffer_high_watermark = -1, int32_t send_buffer_size = -1);
|
||||
void StartDataCollection(const StartMessage& message) override;
|
||||
void EndDataCollection(const EndMessage& message) override;
|
||||
};
|
||||
|
||||
#endif //JUNGFRAUJOCH_ZMQIMAGEPUSHER_H
|
||||
54
common/ZMQPreviewPublisher.cpp
Normal file
54
common/ZMQPreviewPublisher.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "ZMQPreviewPublisher.h"
|
||||
#include "grpcToJson.h"
|
||||
|
||||
ZMQPreviewPublisher::ZMQPreviewPublisher(ZMQContext& context, const std::string& addr) :
|
||||
socket(context, ZMQSocketType::Pub) {
|
||||
socket.SendWaterMark(2).NoLinger();
|
||||
socket.Bind(addr);
|
||||
}
|
||||
|
||||
void ZMQPreviewPublisher::Start(const DiffractionExperiment &experiment, const JFCalibration &calibration) {
|
||||
auto mask = calibration.CalculateNexusMask(experiment);
|
||||
JFJochProtoBuf::PreviewFrame frame;
|
||||
frame.set_image_number(-1);
|
||||
frame.set_width(experiment.GetXPixelsNum());
|
||||
frame.set_height(experiment.GetYPixelsNum());
|
||||
frame.set_pixel_depth(4);
|
||||
frame.set_data(mask.data(), experiment.GetPixelsNum() * sizeof(uint32_t));
|
||||
socket.Send(grpcToJson(frame));
|
||||
}
|
||||
|
||||
void ZMQPreviewPublisher::Stop(const DiffractionExperiment& experiment) {}
|
||||
|
||||
void ZMQPreviewPublisher::Publish(const DiffractionExperiment& experiment, const int16_t* image_data, uint32_t image_number) {
|
||||
JFJochProtoBuf::PreviewFrame frame;
|
||||
frame.set_image_number(image_number);
|
||||
frame.set_total_images(experiment.GetImageNum());
|
||||
frame.set_wavelength_a(experiment.GetWavelength_A());
|
||||
frame.set_beam_x_pxl(experiment.GetBeamX_pxl());
|
||||
frame.set_beam_y_pxl(experiment.GetBeamY_pxl());
|
||||
frame.set_saturation_value(experiment.GetOverflow());
|
||||
frame.set_file_prefix(experiment.GetFilePrefix());
|
||||
frame.set_detector_distance_mm(experiment.GetDetectorDistance_mm());
|
||||
frame.set_width(experiment.GetXPixelsNum());
|
||||
frame.set_height(experiment.GetYPixelsNum());
|
||||
frame.set_pixel_depth(2);
|
||||
|
||||
frame.set_data(image_data, experiment.GetPixelsNum() * sizeof(int16_t));
|
||||
|
||||
SetPreviewImage(frame);
|
||||
socket.Send(grpcToJson(frame));
|
||||
}
|
||||
|
||||
void ZMQPreviewPublisher::SetPreviewImage(const JFJochProtoBuf::PreviewFrame &frame) {
|
||||
std::unique_lock<std::mutex> ul(frame_mutex);
|
||||
saved_frame = frame;
|
||||
}
|
||||
|
||||
JFJochProtoBuf::PreviewFrame ZMQPreviewPublisher::GetPreviewImage() const {
|
||||
std::unique_lock<std::mutex> ul(frame_mutex);
|
||||
return saved_frame;
|
||||
}
|
||||
29
common/ZMQPreviewPublisher.h
Normal file
29
common/ZMQPreviewPublisher.h
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_ZMQPREVIEWPUBLISHER_H
|
||||
#define JUNGFRAUJOCH_ZMQPREVIEWPUBLISHER_H
|
||||
|
||||
#include <jfjoch.pb.h>
|
||||
|
||||
#include "ZMQWrappers.h"
|
||||
#include "DiffractionExperiment.h"
|
||||
#include "../jungfrau/JFCalibration.h"
|
||||
|
||||
class ZMQPreviewPublisher {
|
||||
ZMQSocket socket;
|
||||
|
||||
mutable std::mutex frame_mutex;
|
||||
JFJochProtoBuf::PreviewFrame saved_frame;
|
||||
void SetPreviewImage(const JFJochProtoBuf::PreviewFrame &frame);
|
||||
public:
|
||||
ZMQPreviewPublisher(ZMQContext& context, const std::string& addr);
|
||||
void Start(const DiffractionExperiment& experiment, const JFCalibration &calibration);
|
||||
void Publish(const DiffractionExperiment& experiment, const int16_t* image_data, uint32_t image_number);
|
||||
void Stop(const DiffractionExperiment& experiment);
|
||||
JFJochProtoBuf::PreviewFrame GetPreviewImage() const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_ZMQPREVIEWPUBLISHER_H
|
||||
154
common/ZMQWrappers.cpp
Normal file
154
common/ZMQWrappers.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "ZMQWrappers.h"
|
||||
#include <cerrno>
|
||||
|
||||
ZMQContext::ZMQContext() {
|
||||
context = zmq_ctx_new();
|
||||
}
|
||||
|
||||
ZMQContext &ZMQContext::NumThreads(int32_t threads) {
|
||||
if (zmq_ctx_set(context, ZMQ_IO_THREADS, threads) != 0)
|
||||
throw JFJochException(JFJochExceptionCategory::ZeroMQ,
|
||||
"Cannot set number of I/O threads");
|
||||
return *this;
|
||||
}
|
||||
|
||||
ZMQContext::~ZMQContext() {
|
||||
zmq_ctx_destroy(context);
|
||||
}
|
||||
|
||||
void *ZMQContext::GetContext() const {
|
||||
return context;
|
||||
}
|
||||
|
||||
ZMQSocket::ZMQSocket(ZMQContext &context, ZMQSocketType in_socket_type) : socket_type(in_socket_type) {
|
||||
socket = zmq_socket(context.GetContext(), static_cast<int>(socket_type));
|
||||
|
||||
if (socket == nullptr)
|
||||
throw JFJochException(JFJochExceptionCategory::ZeroMQ, "zmq_socket failed");
|
||||
}
|
||||
|
||||
void ZMQSocket::Bind(const std::string &addr) {
|
||||
if (zmq_bind(socket, addr.c_str()) != 0)
|
||||
throw JFJochException(JFJochExceptionCategory::ZeroMQ, "zmq_bind failed");
|
||||
}
|
||||
|
||||
void ZMQSocket::Connect(const std::string &addr) {
|
||||
if (zmq_connect(socket, addr.c_str()) != 0)
|
||||
throw JFJochException(JFJochExceptionCategory::ZeroMQ, "zmq_connect failed");
|
||||
}
|
||||
|
||||
void ZMQSocket::Disconnect(const std::string &addr) {
|
||||
if (zmq_disconnect(socket, addr.c_str()) != 0)
|
||||
throw JFJochException(JFJochExceptionCategory::ZeroMQ, "zmq_disconnect failed");
|
||||
}
|
||||
|
||||
void ZMQSocket::Send() {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (zmq_send(socket, nullptr, 0, 0) != 0)
|
||||
throw JFJochException(JFJochExceptionCategory::ZeroMQ, "zmq_send() failed: " + std::string(std::strerror(errno)));
|
||||
}
|
||||
|
||||
void ZMQSocket::Send(const void *buf, size_t buf_size, bool blocking, bool multipart) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (zmq_send(socket, buf, buf_size, (blocking ? 0 : ZMQ_DONTWAIT) | (multipart ? ZMQ_SNDMORE : 0)) != buf_size) {
|
||||
if (errno != EAGAIN)
|
||||
throw JFJochException(JFJochExceptionCategory::ZeroMQ,
|
||||
"zmq_send(buf) failed: " + std::string(std::strerror(errno)));
|
||||
}
|
||||
}
|
||||
|
||||
void ZMQSocket::Send(const std::string &s, bool blocking, bool multipart) {
|
||||
Send((void *) s.c_str(), s.size(), blocking, multipart);
|
||||
}
|
||||
|
||||
void ZMQSocket::Send(const int32_t &value) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (zmq_send(socket, &value, sizeof(int32_t), 0) != sizeof(int32_t))
|
||||
throw JFJochException(JFJochExceptionCategory::ZeroMQ, "zmq_send(int) failed: " + std::string(std::strerror(errno)));
|
||||
}
|
||||
|
||||
void ZMQSocket::Send(zmq_msg_t *msg) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (zmq_msg_send(msg, socket, 0) < 0)
|
||||
throw JFJochException(JFJochExceptionCategory::ZeroMQ, "zmq_msg_send failed");
|
||||
}
|
||||
|
||||
int64_t ZMQSocket::Receive(bool blocking) {
|
||||
std::vector<uint8_t> msg;
|
||||
return Receive(msg, blocking, true);
|
||||
}
|
||||
|
||||
int64_t ZMQSocket::Receive(std::string &s, bool blocking) {
|
||||
std::vector<uint8_t> v;
|
||||
int64_t rc = Receive(v, blocking, true);
|
||||
if (rc > 0)
|
||||
s = std::string(v.begin(), v.end());
|
||||
return rc;
|
||||
}
|
||||
|
||||
void ZMQSocket::SetSocketOption(int32_t option_name, int32_t value) {
|
||||
if (zmq_setsockopt(socket, option_name, &value, sizeof(value)) != 0)
|
||||
throw JFJochException(JFJochExceptionCategory::ZeroMQ, "Cannot set socket option");
|
||||
|
||||
}
|
||||
|
||||
ZMQSocket &ZMQSocket::ReceiveTimeout(std::chrono::milliseconds input) {
|
||||
SetSocketOption(ZMQ_RCVTIMEO, input.count());
|
||||
return *this;
|
||||
}
|
||||
|
||||
ZMQSocket &ZMQSocket::NoReceiveTimeout() {
|
||||
SetSocketOption(ZMQ_RCVTIMEO, -1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ZMQSocket &ZMQSocket::ReceiverBufferSize(int32_t bytes) {
|
||||
SetSocketOption(ZMQ_RCVBUF, bytes);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ZMQSocket &ZMQSocket::SendBufferSize(int32_t bytes) {
|
||||
SetSocketOption(ZMQ_SNDBUF, bytes);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ZMQSocket &ZMQSocket::SubscribeAll() {
|
||||
if (socket_type != ZMQSocketType::Sub)
|
||||
throw JFJochException(JFJochExceptionCategory::ZeroMQ, "subscribe only possible for Sub socket");
|
||||
zmq_setsockopt(socket, ZMQ_SUBSCRIBE, nullptr, 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ZMQSocket &ZMQSocket::Subscribe(const std::string &topic) {
|
||||
if (socket_type != ZMQSocketType::Sub)
|
||||
throw JFJochException(JFJochExceptionCategory::ZeroMQ, "subscribe only possible for Sub socket");
|
||||
zmq_setsockopt(socket, ZMQ_SUBSCRIBE, topic.c_str(), topic.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
ZMQSocket::~ZMQSocket() {
|
||||
zmq_close(socket);
|
||||
}
|
||||
|
||||
ZMQSocket& ZMQSocket::ReceiveWaterMark(int32_t msgs) {
|
||||
SetSocketOption(ZMQ_RCVHWM, msgs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ZMQSocket& ZMQSocket::SendWaterMark(int32_t msgs) {
|
||||
SetSocketOption(ZMQ_SNDHWM, msgs);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ZMQSocket &ZMQSocket::NoLinger() {
|
||||
SetSocketOption(ZMQ_LINGER, 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ZMQSocket &ZMQSocket::Conflate(bool input) {
|
||||
SetSocketOption(ZMQ_CONFLATE, input ? 1 : 0);
|
||||
return *this;
|
||||
}
|
||||
91
common/ZMQWrappers.h
Normal file
91
common/ZMQWrappers.h
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_ZMQWRAPPERS_H
|
||||
#define JUNGFRAUJOCH_ZMQWRAPPERS_H
|
||||
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <zmq.h>
|
||||
#include "JFJochException.h"
|
||||
|
||||
class ZMQContext {
|
||||
void *context;
|
||||
public:
|
||||
ZMQContext();
|
||||
ZMQContext& NumThreads(int32_t threads);
|
||||
~ZMQContext();
|
||||
void *GetContext() const;
|
||||
};
|
||||
|
||||
enum class ZMQSocketType : int {Push = ZMQ_PUSH, Pull = ZMQ_PULL, Req = ZMQ_REQ, Rep = ZMQ_REP,
|
||||
Pub = ZMQ_PUB, Sub = ZMQ_SUB};
|
||||
|
||||
class ZMQSocket {
|
||||
std::mutex m;
|
||||
ZMQSocketType socket_type;
|
||||
void *socket;
|
||||
void SetSocketOption(int32_t option_name, int32_t value);
|
||||
public:
|
||||
ZMQSocket(ZMQSocket &socket) = delete;
|
||||
const ZMQSocket& operator=(ZMQSocket &socket) = delete;
|
||||
ZMQSocket(ZMQContext &context, ZMQSocketType socket_type);
|
||||
~ZMQSocket();
|
||||
void Connect(const std::string& addr);
|
||||
void Disconnect(const std::string& addr);
|
||||
void Bind(const std::string& addr);
|
||||
ZMQSocket &NoReceiveTimeout();
|
||||
ZMQSocket &ReceiveTimeout(std::chrono::milliseconds input);
|
||||
ZMQSocket &Subscribe(const std::string &topic);
|
||||
ZMQSocket &SubscribeAll();
|
||||
ZMQSocket &NoLinger();
|
||||
ZMQSocket &Conflate(bool input);
|
||||
ZMQSocket &SendBufferSize(int32_t bytes);
|
||||
ZMQSocket &ReceiverBufferSize(int32_t bytes);
|
||||
|
||||
int64_t Receive(bool blocking = true);
|
||||
int64_t Receive(std::string &j, bool blocking = true);
|
||||
template <class T> int64_t Receive(std::vector<T> &vector, bool blocking = true, bool resize = true) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
zmq_msg_t zmq_msg;
|
||||
zmq_msg_init(&zmq_msg);
|
||||
int64_t msg_size = zmq_msg_recv(&zmq_msg, socket, blocking ? 0 : ZMQ_DONTWAIT);
|
||||
if (msg_size < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return -1;
|
||||
if (errno == EINTR) // Timeout ?
|
||||
return -1;
|
||||
else
|
||||
throw JFJochException(JFJochExceptionCategory::ZeroMQ, "zmq_msg_recv failed "
|
||||
+ std::string(strerror(errno)));
|
||||
} else if (msg_size == 0) {
|
||||
zmq_msg_close (&zmq_msg);
|
||||
return 0;
|
||||
} else if (resize) {
|
||||
vector.resize(msg_size / sizeof(T) + ((msg_size % sizeof(T) != 0) ? 1 : 0));
|
||||
} else {
|
||||
zmq_msg_close (&zmq_msg);
|
||||
throw JFJochException(JFJochExceptionCategory::ZeroMQ, "Receive buffer too small");
|
||||
}
|
||||
|
||||
memcpy(vector.data(), zmq_msg_data(&zmq_msg), msg_size);
|
||||
zmq_msg_close (&zmq_msg);
|
||||
return msg_size;
|
||||
}
|
||||
|
||||
void Send();
|
||||
void Send(const void *buf, size_t buf_size, bool blocking = true, bool multipart = false);
|
||||
template <class T> void Send(const std::vector<T> &buf) {
|
||||
Send(buf.data(), buf.size() * sizeof(T));
|
||||
}
|
||||
void Send(const int32_t &value);
|
||||
void Send(const std::string &s, bool blocking = true, bool multipart = false);
|
||||
void Send(zmq_msg_t *msg);
|
||||
ZMQSocket &SendWaterMark(int32_t msgs);
|
||||
ZMQSocket &ReceiveWaterMark(int32_t msgs);
|
||||
};
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_ZMQWRAPPERS_H
|
||||
25
common/grpcToJson.h
Normal file
25
common/grpcToJson.h
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_GRPCTOJSON_H
|
||||
#define JUNGFRAUJOCH_GRPCTOJSON_H
|
||||
|
||||
#include <google/protobuf/util/json_util.h>
|
||||
|
||||
#include "../common/JFJochException.h"
|
||||
|
||||
inline std::string grpcToJson(const google::protobuf::Message &message) {
|
||||
google::protobuf::util::JsonPrintOptions opts;
|
||||
opts.always_print_primitive_fields = true;
|
||||
opts.add_whitespace = true;
|
||||
opts.always_print_enums_as_ints = false;
|
||||
opts.preserve_proto_field_names = true;
|
||||
|
||||
std::string s;
|
||||
auto status = google::protobuf::util::MessageToJsonString(message, &s, opts);
|
||||
if (!status.ok())
|
||||
throw JFJochException(JFJochExceptionCategory::JSON, "Error in generating JSON from ProtoBuf: " + status.message().ToString());
|
||||
|
||||
return s;
|
||||
}
|
||||
#endif //JUNGFRAUJOCH_GRPCTOJSON_H
|
||||
25
common/jsonToGrpc.h
Normal file
25
common/jsonToGrpc.h
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_JSONTOGRPC_H
|
||||
#define JUNGFRAUJOCH_JSONTOGRPC_H
|
||||
|
||||
#include <google/protobuf/util/json_util.h>
|
||||
|
||||
#include "../common/JFJochException.h"
|
||||
|
||||
template<class T>
|
||||
T jsonToGrpc(const std::string& json) {
|
||||
T output;
|
||||
|
||||
google::protobuf::util::JsonParseOptions opts;
|
||||
opts.case_insensitive_enum_parsing = false;
|
||||
opts.ignore_unknown_fields = false;
|
||||
|
||||
auto status = google::protobuf::util::JsonStringToMessage(json, &output, opts);
|
||||
if (!status.ok())
|
||||
throw JFJochException(JFJochExceptionCategory::JSON, "Error in generating ProtoBuf from JSON: " + status.message().ToString());
|
||||
return output;
|
||||
}
|
||||
|
||||
#endif //JUNGFRAUJOCH_JSONTOGRPC_H
|
||||
19
common/to_fixed.h
Normal file
19
common/to_fixed.h
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_TO_FIXED_H
|
||||
#define JUNGFRAUJOCH_TO_FIXED_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
|
||||
inline uint16_t to_fixed(double val, uint16_t fractional_bits) {
|
||||
// If val is result of division by zero, only reasonable value of output is zero (otherwise number could be interpreted improperly)
|
||||
uint32_t int_val = std::isfinite(val) ? (std::lround(val * (1<<fractional_bits))) : 0;
|
||||
// It is unlikely (but not impossible), that gain value will be lower than the smallest possible
|
||||
// Then reciprocal of gain could be more than allowed by data format. Protection is added for this condition
|
||||
if (int_val > UINT16_MAX) int_val = UINT16_MAX;
|
||||
return int_val;
|
||||
}
|
||||
|
||||
#endif //JUNGFRAUJOCH_TO_FIXED_H
|
||||
18
compression/CMakeLists.txt
Normal file
18
compression/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
ADD_LIBRARY(Compression STATIC
|
||||
lz4/lz4.c
|
||||
lz4/lz4.h
|
||||
bitshuffle/bitshuffle.c
|
||||
bitshuffle/bitshuffle_core.c
|
||||
bitshuffle/iochain.c
|
||||
JFJochZstdCompressor.cpp
|
||||
JFJochZstdCompressor.h
|
||||
JFJochCompressor.cpp
|
||||
JFJochCompressor.h
|
||||
JFJochDecompress.h
|
||||
MaxCompressedSize.cpp
|
||||
MaxCompressedSize.h)
|
||||
|
||||
TARGET_COMPILE_DEFINITIONS(Compression PUBLIC -DZSTD_SUPPORT -DUSE_ZSTD)
|
||||
TARGET_LINK_LIBRARIES(Compression libzstd_static)
|
||||
TARGET_INCLUDE_DIRECTORIES(Compression PUBLIC . zstd/lib)
|
||||
ADD_SUBDIRECTORY(zstd/build/cmake)
|
||||
9
compression/CompressionAlgorithmEnum.h
Normal file
9
compression/CompressionAlgorithmEnum.h
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_COMPRESSIONALGORITHMENUM_H
|
||||
#define JUNGFRAUJOCH_COMPRESSIONALGORITHMENUM_H
|
||||
|
||||
enum class CompressionAlgorithm {NO_COMPRESSION, BSHUF_LZ4, BSHUF_ZSTD, BSHUF_ZSTD_RLE};
|
||||
|
||||
#endif //JUNGFRAUJOCH_COMPRESSIONALGORITHMENUM_H
|
||||
95
compression/JFJochCompressor.cpp
Normal file
95
compression/JFJochCompressor.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "JFJochCompressor.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
#include <bitshuffle/bitshuffle_internals.h>
|
||||
#include <zstd.h>
|
||||
#include <lz4/lz4.h>
|
||||
|
||||
#include "../common/JFJochException.h"
|
||||
|
||||
extern "C" {
|
||||
void bshuf_write_uint64_BE(void* buf, uint64_t num);
|
||||
}
|
||||
|
||||
JFJochBitShuffleCompressor::JFJochBitShuffleCompressor(CompressionAlgorithm in_algorithm) {
|
||||
algorithm = in_algorithm;
|
||||
}
|
||||
|
||||
size_t JFJochBitShuffleCompressor::CompressBlock(char *dest, const char *source, size_t nelements, size_t elem_size) {
|
||||
// Assert nelements < block_size
|
||||
const char *src_ptr;
|
||||
|
||||
int64_t bshuf_ret = bshuf_trans_bit_elem(source, tmp_space.data(), nelements, elem_size);
|
||||
if (bshuf_ret < 0)
|
||||
throw JFJochException(JFJochExceptionCategory::Compression, "bshuf_trans_bit_elem error");
|
||||
src_ptr = tmp_space.data();
|
||||
|
||||
size_t compressed_size;
|
||||
size_t src_size = nelements * elem_size;
|
||||
|
||||
switch (algorithm) {
|
||||
case CompressionAlgorithm::BSHUF_LZ4:
|
||||
compressed_size = LZ4_compress_default(src_ptr, dest + 4, src_size, LZ4_compressBound(src_size));
|
||||
break;
|
||||
case CompressionAlgorithm::BSHUF_ZSTD:
|
||||
compressed_size = ZSTD_compress(dest + 4, ZSTD_compressBound(src_size), src_ptr, src_size, 0);
|
||||
if (ZSTD_isError(compressed_size))
|
||||
throw(JFJochException(JFJochExceptionCategory::Compression, ZSTD_getErrorName(compressed_size)));
|
||||
break;
|
||||
case CompressionAlgorithm::BSHUF_ZSTD_RLE:
|
||||
try {
|
||||
compressed_size = zstd_compressor.Compress(((uint8_t *) dest) + 4, (uint64_t *) src_ptr,
|
||||
src_size, src_size);
|
||||
} catch (const std::runtime_error &e) {
|
||||
throw JFJochException(JFJochExceptionCategory::ZSTDCompressionError, e.what());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw JFJochException(JFJochExceptionCategory::Compression, "Algorithm not supported");
|
||||
}
|
||||
|
||||
bshuf_write_uint32_BE(dest, compressed_size);
|
||||
|
||||
return compressed_size + 4;
|
||||
}
|
||||
|
||||
size_t JFJochBitShuffleCompressor::Compress(char *dest, const char *source, size_t nelements, size_t elem_size) {
|
||||
static_assert(DefaultBlockSize % BSHUF_BLOCKED_MULT == 0, "Block size must be multiple of 8");
|
||||
|
||||
if (algorithm == CompressionAlgorithm::NO_COMPRESSION) {
|
||||
// Trivial case if no compression - copy content
|
||||
memcpy(dest, source, nelements * elem_size);
|
||||
return nelements * elem_size;
|
||||
}
|
||||
|
||||
bshuf_write_uint64_BE(dest, nelements * elem_size);
|
||||
bshuf_write_uint32_BE(dest + 8, DefaultBlockSize * elem_size);
|
||||
|
||||
if (tmp_space.size() < DefaultBlockSize * elem_size)
|
||||
tmp_space.resize(DefaultBlockSize * elem_size);
|
||||
|
||||
|
||||
size_t num_full_blocks = nelements / DefaultBlockSize;
|
||||
size_t reminder_size = nelements - num_full_blocks * DefaultBlockSize;
|
||||
size_t compressed_size = 12;
|
||||
|
||||
for (int i = 0; i < num_full_blocks; i++)
|
||||
compressed_size += CompressBlock(dest + compressed_size,
|
||||
source + i * DefaultBlockSize * elem_size, DefaultBlockSize, elem_size);
|
||||
|
||||
size_t last_block_size = reminder_size - reminder_size % BSHUF_BLOCKED_MULT;
|
||||
if (last_block_size > 0)
|
||||
compressed_size += CompressBlock(dest + compressed_size,
|
||||
source + num_full_blocks * DefaultBlockSize * elem_size, last_block_size, elem_size);
|
||||
|
||||
size_t leftover_bytes = (reminder_size % BSHUF_BLOCKED_MULT) * elem_size;
|
||||
if (leftover_bytes > 0) {
|
||||
memcpy(dest + compressed_size, source + (num_full_blocks * DefaultBlockSize + last_block_size) * elem_size, leftover_bytes);
|
||||
compressed_size += leftover_bytes;
|
||||
}
|
||||
return compressed_size;
|
||||
}
|
||||
40
compression/JFJochCompressor.h
Normal file
40
compression/JFJochCompressor.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_JFJOCHCOMPRESSOR_H
|
||||
#define JUNGFRAUJOCH_JFJOCHCOMPRESSOR_H
|
||||
|
||||
#include <bitshuffle/bitshuffle.h>
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include "CompressionAlgorithmEnum.h"
|
||||
|
||||
#include "JFJochZstdCompressor.h"
|
||||
|
||||
class JFJochBitShuffleCompressor {
|
||||
JFJochZstdCompressor zstd_compressor;
|
||||
CompressionAlgorithm algorithm;
|
||||
std::vector<char> tmp_space;
|
||||
size_t CompressBlock(char *dest, const char * source, size_t nelements, size_t elem_size);
|
||||
public:
|
||||
constexpr static const size_t DefaultBlockSize = 4096;
|
||||
|
||||
JFJochBitShuffleCompressor(CompressionAlgorithm algorithm);
|
||||
template<class T>
|
||||
size_t Compress(void *dest, const std::vector<T> &src) {
|
||||
return Compress((char *) dest, (char *) src.data(), src.size(), sizeof(T));
|
||||
};
|
||||
size_t Compress(char *dest, const char* source, size_t nelements, size_t elem_size);
|
||||
};
|
||||
|
||||
template <class T> std::vector<T> bitshuffle(const std::vector<T> &input, size_t block_size) {
|
||||
std::vector<T> ret(input.size() * sizeof(T));
|
||||
bshuf_bitshuffle(input.data(), ret.data(), input.size(), sizeof(T), block_size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_JFJOCHCOMPRESSOR_H
|
||||
60
compression/JFJochDecompress.h
Normal file
60
compression/JFJochDecompress.h
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_JFJOCHDECOMPRESS_H
|
||||
#define JUNGFRAUJOCH_JFJOCHDECOMPRESS_H
|
||||
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
#include <bitshuffle/bitshuffle.h>
|
||||
#include <bitshuffle/bitshuffle_internals.h>
|
||||
|
||||
#include "../compression/CompressionAlgorithmEnum.h"
|
||||
|
||||
#include "../common/JFJochException.h"
|
||||
|
||||
extern "C" {
|
||||
uint64_t bshuf_read_uint64_BE(void* buf);
|
||||
};
|
||||
|
||||
template <class Td, class Ts>
|
||||
void JFJochDecompress(std::vector<Td> &output, CompressionAlgorithm algorithm, std::vector<Ts> source_v,
|
||||
size_t nelements) {
|
||||
size_t elem_size = sizeof(Td);
|
||||
output.resize(nelements * elem_size);
|
||||
size_t source_size = source_v.size() * sizeof(Ts);
|
||||
auto source = (uint8_t *) source_v.data();
|
||||
|
||||
size_t block_size;
|
||||
if (algorithm != CompressionAlgorithm::NO_COMPRESSION) {
|
||||
if (bshuf_read_uint64_BE(source) != nelements * elem_size)
|
||||
throw JFJochException(JFJochExceptionCategory::Compression, "Mismatch in size");
|
||||
auto tmp = bshuf_read_uint32_BE(source + 8);
|
||||
block_size = tmp / elem_size;
|
||||
}
|
||||
|
||||
switch (algorithm) {
|
||||
case CompressionAlgorithm::NO_COMPRESSION:
|
||||
if (source_size != nelements * elem_size)
|
||||
throw JFJochException(JFJochExceptionCategory::Compression, "Mismatch in size");
|
||||
memcpy(output.data(), source, source_size);
|
||||
break;
|
||||
case CompressionAlgorithm::BSHUF_LZ4:
|
||||
if (bshuf_decompress_lz4(source + 12, output.data(), nelements,
|
||||
elem_size, block_size) != source_size - 12)
|
||||
throw JFJochException(JFJochExceptionCategory::Compression, "Decompression error");
|
||||
break;
|
||||
case CompressionAlgorithm::BSHUF_ZSTD_RLE:
|
||||
case CompressionAlgorithm::BSHUF_ZSTD:
|
||||
if (bshuf_decompress_zstd(source + 12, output.data(), nelements,
|
||||
elem_size, block_size) != source_size - 12)
|
||||
throw JFJochException(JFJochExceptionCategory::Compression, "Decompression error");
|
||||
break;
|
||||
default:
|
||||
throw JFJochException(JFJochExceptionCategory::Compression, "Not implemented algorithm");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_JFJOCHDECOMPRESS_H
|
||||
197
compression/JFJochZstdCompressor.cpp
Normal file
197
compression/JFJochZstdCompressor.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "JFJochZstdCompressor.h"
|
||||
|
||||
#include <map>
|
||||
#include <zstd.h>
|
||||
#include <lz4/lz4.h>
|
||||
|
||||
#include <common/mem.h>
|
||||
#include <stdexcept>
|
||||
|
||||
enum class CompressorState {RLE_0, RLE_FF, RAW};
|
||||
|
||||
#define BLOCK_RAW 0
|
||||
#define BLOCK_RLE 1
|
||||
#define BLOCK_COMP 2
|
||||
#define LITERAL_COMPRESSED 2
|
||||
#define LITERAL_TREELESS 3
|
||||
|
||||
JFJochZstdCompressor::JFJochZstdCompressor() {
|
||||
unsigned count[256];
|
||||
for (int i = 0; i < 256; i++) count[i] = i;
|
||||
}
|
||||
|
||||
size_t JFJochZstdCompressor::RawBlock(uint8_t *dst, const void *src, uint32_t src_size, bool last) {
|
||||
size_t ret_value = 0;
|
||||
int32_t bytes_left = src_size;
|
||||
auto src_u8 = (uint8_t *)src;
|
||||
|
||||
while (bytes_left > 0) {
|
||||
int32_t bytes_to_write = std::min(bytes_left,ZSTD_BLOCKSIZE_MAX);
|
||||
bytes_left -= bytes_to_write;
|
||||
uint32_t block_header = (bytes_to_write << 3) + (BLOCK_RAW<<1) + (((last && (bytes_left == 0)) ? 1 : 0));
|
||||
|
||||
MEM_writeLE24(dst, block_header);
|
||||
memcpy(dst + 3, src_u8, bytes_to_write);
|
||||
|
||||
src_u8 += bytes_to_write;
|
||||
ret_value += 3 + bytes_to_write;
|
||||
dst += 3 + bytes_to_write;
|
||||
}
|
||||
return ret_value;
|
||||
}
|
||||
|
||||
size_t JFJochZstdCompressor::RLEBlock(uint8_t *dst, uint8_t src, uint32_t src_size, bool last) {
|
||||
size_t ret_value = 0;
|
||||
int32_t bytes_left = src_size;
|
||||
|
||||
while (bytes_left > 0) {
|
||||
int32_t bytes_to_write = std::min(bytes_left, ZSTD_BLOCKSIZE_MAX);
|
||||
bytes_left -= bytes_to_write;
|
||||
uint32_t block_header = (bytes_to_write << 3) + (BLOCK_RLE<<1) + ((last && (bytes_left == 0))? 1 : 0);
|
||||
uint32_t block = block_header + (src<<24); // Little Endian!
|
||||
MEM_writeLE32(dst + ret_value, block);
|
||||
ret_value += 4;
|
||||
}
|
||||
return ret_value;
|
||||
}
|
||||
|
||||
size_t JFJochZstdCompressor::DataBlock(uint8_t *dst, const void *src, uint32_t src_size, bool last) {
|
||||
return RawBlock(dst, src, src_size, last);
|
||||
}
|
||||
|
||||
size_t JFJochZstdCompressor::BlockStateMachine(uint8_t *dst, const uint64_t *src, size_t frame_size64) {
|
||||
size_t dst_size = 0;
|
||||
|
||||
CompressorState state;
|
||||
size_t seq_len = 8;
|
||||
|
||||
if (src[0] == 0x0) state = CompressorState::RLE_0;
|
||||
else if (src[0] == UINT64_MAX) state = CompressorState::RLE_FF;
|
||||
else state = CompressorState::RAW;
|
||||
|
||||
for (int i = 1; i < frame_size64; i++) {
|
||||
switch (state) {
|
||||
case CompressorState::RLE_0:
|
||||
if (src[i] == 0x0) seq_len += 8;
|
||||
else if ((src[i] & 0x00000000000000FF) != 0) {
|
||||
dst_size += RLEBlock(dst + dst_size, 0x0, seq_len, false);
|
||||
state = CompressorState::RAW;
|
||||
seq_len = 8;
|
||||
} else if (src[i] == UINT64_MAX) {
|
||||
dst_size += RLEBlock(dst + dst_size, 0x0, seq_len, false);
|
||||
state = CompressorState::RLE_FF;
|
||||
seq_len = 8;
|
||||
} else {
|
||||
int delta1 = 1;
|
||||
if ((src[i] & 0x00FFFFFFFFFFFFFF) == 0) delta1 = 7;
|
||||
else if ((src[i] & 0x0000FFFFFFFFFFFF) == 0) delta1 = 6;
|
||||
else if ((src[i] & 0x000000FFFFFFFFFF) == 0) delta1 = 5;
|
||||
else if ((src[i] & 0x00000000FFFFFFFF) == 0) delta1 = 4;
|
||||
else if ((src[i] & 0x0000000000FFFFFF) == 0) delta1 = 3;
|
||||
else if ((src[i] & 0x000000000000FFFF) == 0) delta1 = 2;
|
||||
|
||||
dst_size += RLEBlock(dst + dst_size, 0x0, seq_len + delta1, false);
|
||||
state = CompressorState::RAW;
|
||||
seq_len = 8 - delta1;
|
||||
}
|
||||
break;
|
||||
case CompressorState::RLE_FF:
|
||||
if (src[i] == UINT64_MAX) seq_len += 8;
|
||||
else {
|
||||
dst_size += RLEBlock(dst + dst_size, 0xFFu, seq_len, false);
|
||||
seq_len = 8;
|
||||
if (src[i] == 0x0u)
|
||||
state = CompressorState::RLE_0;
|
||||
else
|
||||
state = CompressorState::RAW;
|
||||
}
|
||||
break;
|
||||
case CompressorState::RAW:
|
||||
if (src[i] == 0x0u) {
|
||||
dst_size += DataBlock(dst + dst_size, (uint8_t *) (src + i) - seq_len,
|
||||
seq_len, false);
|
||||
state = CompressorState::RLE_0;
|
||||
seq_len = 8;
|
||||
} else if ((i < frame_size64 - 1) && (src[i+1] == 0x0u) && ((src[i] & 0xFF00000000000000) == 0)) {
|
||||
int delta2 = 1;
|
||||
if ((src[i] & 0xFFFFFFFFFFFFFF00) == 0) delta2 = 7;
|
||||
else if ((src[i] & 0xFFFFFFFFFFFF0000) == 0) delta2 = 6;
|
||||
else if ((src[i] & 0xFFFFFFFFFF000000) == 0) delta2 = 5;
|
||||
else if ((src[i] & 0xFFFFFFFF00000000) == 0) delta2 = 4;
|
||||
else if ((src[i] & 0xFFFFFF0000000000) == 0) delta2 = 3;
|
||||
else if ((src[i] & 0xFFFF000000000000) == 0) delta2 = 2;
|
||||
dst_size += DataBlock(dst + dst_size, (uint8_t *) (src + i) - seq_len,
|
||||
seq_len + 8 - delta2, false);
|
||||
state = CompressorState::RLE_0;
|
||||
seq_len = delta2;
|
||||
} else if (src[i] == UINT64_MAX) {
|
||||
dst_size += DataBlock(dst + dst_size, (uint8_t *) (src + i) - seq_len,
|
||||
seq_len, false);
|
||||
state = CompressorState::RLE_FF;
|
||||
seq_len = 8;
|
||||
} else seq_len += 8;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
if (state == CompressorState::RLE_0)
|
||||
dst_size += RLEBlock(dst + dst_size, 0x0 , seq_len, true);
|
||||
else if (state == CompressorState::RLE_FF)
|
||||
dst_size += RLEBlock(dst + dst_size, 0xFFu, seq_len, true);
|
||||
else
|
||||
dst_size += DataBlock(dst + dst_size, (uint8_t *) (src + frame_size64) - seq_len, seq_len, true);
|
||||
|
||||
return dst_size;
|
||||
}
|
||||
|
||||
size_t JFJochZstdCompressor::CompressFrame(uint8_t *dst, const uint64_t *src, size_t frame_size64) {
|
||||
//if (frame_size64 > 1000 * 1000)
|
||||
// throw SLSException(SLSExceptionCategory::ZSTDCompressionError,
|
||||
// "ZSTD frame cannot be larger than 8 MB");
|
||||
|
||||
// Assumptions:
|
||||
// One frame = One module = 512*1024*2 (2^20 == 1048576) - 514*1030*4 (2117680 < 2 ^22)
|
||||
// --> frame size needs 32-bit
|
||||
|
||||
size_t dst_size = 0;
|
||||
|
||||
// Magic number
|
||||
MEM_writeLE32(dst, ZSTD_MAGICNUMBER);
|
||||
|
||||
dst_size += 4;
|
||||
|
||||
// Frame header descriptor
|
||||
uint8_t frame_header_descriptor = (1<<7) + (1<<5);
|
||||
dst[dst_size] = frame_header_descriptor;
|
||||
dst_size += 1;
|
||||
|
||||
// Frame size
|
||||
MEM_writeLE32(dst + dst_size, frame_size64*8);
|
||||
dst_size += 4;
|
||||
|
||||
dst_size += BlockStateMachine(dst + dst_size, src, frame_size64);
|
||||
|
||||
return dst_size;
|
||||
}
|
||||
|
||||
size_t JFJochZstdCompressor::Compress(uint8_t *dst, const uint64_t *src, size_t src_size, size_t frame_size) {
|
||||
size_t dst_size = 0;
|
||||
|
||||
if (frame_size % 8 != 0)
|
||||
throw std::runtime_error("ZSTD frame has to have size multiple of 8");
|
||||
|
||||
if (src_size % frame_size != 0)
|
||||
throw std::runtime_error("ZSTD source has to be multiple of frame");
|
||||
|
||||
size_t nframes = src_size / frame_size;
|
||||
|
||||
size_t frame_size64 = frame_size / 8;
|
||||
|
||||
for (int frame = 0; frame < nframes; frame++)
|
||||
dst_size += CompressFrame(dst + dst_size, src + frame * frame_size64, frame_size64);
|
||||
|
||||
return dst_size;
|
||||
}
|
||||
22
compression/JFJochZstdCompressor.h
Normal file
22
compression/JFJochZstdCompressor.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_JFJOCHZSTDCOMPRESSOR_H
|
||||
#define JUNGFRAUJOCH_JFJOCHZSTDCOMPRESSOR_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
class JFJochZstdCompressor {
|
||||
size_t BlockStateMachine(uint8_t *dst, const uint64_t *src, size_t frame_size64);
|
||||
size_t CompressFrame(uint8_t *dst, const uint64_t *src, size_t frame_size64);
|
||||
size_t DataBlock(uint8_t *dst, const void *src, uint32_t src_size, bool last);
|
||||
public:
|
||||
JFJochZstdCompressor();
|
||||
static size_t RawBlock(uint8_t *dst, const void *src, uint32_t src_size, bool last);
|
||||
static size_t RLEBlock(uint8_t *dst, uint8_t src, uint32_t src_size, bool last);
|
||||
size_t Compress(uint8_t *dst, const uint64_t *src, size_t src_size, size_t frame_sizes);
|
||||
};
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_JFJOCHZSTDCOMPRESSOR_H
|
||||
20
compression/MaxCompressedSize.cpp
Normal file
20
compression/MaxCompressedSize.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <bitshuffle/bitshuffle.h>
|
||||
|
||||
#include "JFJochCompressor.h"
|
||||
#include "MaxCompressedSize.h"
|
||||
|
||||
int64_t MaxCompressedSize(CompressionAlgorithm algorithm, int64_t pixels_number, uint16_t pixel_depth) {
|
||||
switch (algorithm) {
|
||||
case CompressionAlgorithm::BSHUF_LZ4:
|
||||
return bshuf_compress_lz4_bound(pixels_number, pixel_depth, JFJochBitShuffleCompressor::DefaultBlockSize) + 12;
|
||||
case CompressionAlgorithm::BSHUF_ZSTD:
|
||||
case CompressionAlgorithm::BSHUF_ZSTD_RLE:
|
||||
return bshuf_compress_zstd_bound(pixels_number, pixel_depth,JFJochBitShuffleCompressor::DefaultBlockSize) + 12;
|
||||
default:
|
||||
return pixels_number * pixel_depth;
|
||||
}
|
||||
}
|
||||
|
||||
12
compression/MaxCompressedSize.h
Normal file
12
compression/MaxCompressedSize.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_MAXCOMPRESSEDSIZE_H
|
||||
#define JUNGFRAUJOCH_MAXCOMPRESSEDSIZE_H
|
||||
|
||||
#include <cstdint>
|
||||
#include "CompressionAlgorithmEnum.h"
|
||||
|
||||
int64_t MaxCompressedSize(CompressionAlgorithm algorithm, int64_t pixels_number, uint16_t pixel_depth);
|
||||
|
||||
#endif //JUNGFRAUJOCH_MAXCOMPRESSEDSIZE_H
|
||||
21
compression/bitshuffle/LICENSE
Normal file
21
compression/bitshuffle/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
Bitshuffle - Filter for improving compression of typed binary data.
|
||||
|
||||
Copyright (c) 2014 Kiyoshi Masui (kiyo@physics.ubc.ca)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
279
compression/bitshuffle/bitshuffle.c
Normal file
279
compression/bitshuffle/bitshuffle.c
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Bitshuffle - Filter for improving compression of typed binary data.
|
||||
*
|
||||
* Author: Kiyoshi Masui <kiyo@physics.ubc.ca>
|
||||
* Website: http://www.github.com/kiyo-masui/bitshuffle
|
||||
* Created: 2014
|
||||
*
|
||||
* See LICENSE file for details about copyright and rights to use.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "bitshuffle.h"
|
||||
#include "bitshuffle_core.h"
|
||||
#include "bitshuffle_internals.h"
|
||||
#include "../lz4/lz4.h"
|
||||
|
||||
#ifdef ZSTD_SUPPORT
|
||||
#include "zstd.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
// Macros.
|
||||
#define CHECK_ERR_FREE_LZ(count, buf) if (count < 0) { \
|
||||
free(buf); return count - 1000; }
|
||||
|
||||
|
||||
/* Bitshuffle and compress a single block. */
|
||||
int64_t bshuf_compress_lz4_block(ioc_chain *C_ptr, \
|
||||
const size_t size, const size_t elem_size, const int option) {
|
||||
|
||||
int64_t nbytes, count;
|
||||
void *tmp_buf_bshuf;
|
||||
void *tmp_buf_lz4;
|
||||
size_t this_iter;
|
||||
const void *in;
|
||||
void *out;
|
||||
|
||||
tmp_buf_bshuf = malloc(size * elem_size);
|
||||
if (tmp_buf_bshuf == NULL) return -1;
|
||||
|
||||
int dst_capacity = LZ4_compressBound(size * elem_size);
|
||||
tmp_buf_lz4 = malloc(dst_capacity);
|
||||
if (tmp_buf_lz4 == NULL){
|
||||
free(tmp_buf_bshuf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
in = ioc_get_in(C_ptr, &this_iter);
|
||||
ioc_set_next_in(C_ptr, &this_iter, (void*) ((char*) in + size * elem_size));
|
||||
|
||||
count = bshuf_trans_bit_elem(in, tmp_buf_bshuf, size, elem_size);
|
||||
if (count < 0) {
|
||||
free(tmp_buf_lz4);
|
||||
free(tmp_buf_bshuf);
|
||||
return count;
|
||||
}
|
||||
nbytes = LZ4_compress_default((const char*) tmp_buf_bshuf, (char*) tmp_buf_lz4, size * elem_size, dst_capacity);
|
||||
free(tmp_buf_bshuf);
|
||||
CHECK_ERR_FREE_LZ(nbytes, tmp_buf_lz4);
|
||||
|
||||
out = ioc_get_out(C_ptr, &this_iter);
|
||||
ioc_set_next_out(C_ptr, &this_iter, (void *) ((char *) out + nbytes + 4));
|
||||
|
||||
bshuf_write_uint32_BE(out, nbytes);
|
||||
memcpy((char *) out + 4, tmp_buf_lz4, nbytes);
|
||||
|
||||
free(tmp_buf_lz4);
|
||||
|
||||
return nbytes + 4;
|
||||
}
|
||||
|
||||
|
||||
/* Decompress and bitunshuffle a single block. */
|
||||
int64_t bshuf_decompress_lz4_block(ioc_chain *C_ptr,
|
||||
const size_t size, const size_t elem_size, const int option) {
|
||||
|
||||
int64_t nbytes, count;
|
||||
void *out, *tmp_buf;
|
||||
const void *in;
|
||||
size_t this_iter;
|
||||
int32_t nbytes_from_header;
|
||||
|
||||
in = ioc_get_in(C_ptr, &this_iter);
|
||||
nbytes_from_header = bshuf_read_uint32_BE(in);
|
||||
ioc_set_next_in(C_ptr, &this_iter,
|
||||
(void*) ((char*) in + nbytes_from_header + 4));
|
||||
|
||||
out = ioc_get_out(C_ptr, &this_iter);
|
||||
ioc_set_next_out(C_ptr, &this_iter,
|
||||
(void *) ((char *) out + size * elem_size));
|
||||
|
||||
tmp_buf = malloc(size * elem_size);
|
||||
if (tmp_buf == NULL) return -1;
|
||||
|
||||
nbytes = LZ4_decompress_safe((const char*) in + 4, (char *) tmp_buf, nbytes_from_header,
|
||||
size * elem_size);
|
||||
CHECK_ERR_FREE_LZ(nbytes, tmp_buf);
|
||||
if (nbytes != size * elem_size) {
|
||||
free(tmp_buf);
|
||||
return -91;
|
||||
}
|
||||
nbytes = nbytes_from_header;
|
||||
|
||||
count = bshuf_untrans_bit_elem(tmp_buf, out, size, elem_size);
|
||||
CHECK_ERR_FREE(count, tmp_buf);
|
||||
nbytes += 4;
|
||||
|
||||
free(tmp_buf);
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
#ifdef ZSTD_SUPPORT
|
||||
/* Bitshuffle and compress a single block. */
|
||||
int64_t bshuf_compress_zstd_block(ioc_chain *C_ptr, \
|
||||
const size_t size, const size_t elem_size, const int comp_lvl) {
|
||||
|
||||
int64_t nbytes, count;
|
||||
void *tmp_buf_bshuf;
|
||||
void *tmp_buf_zstd;
|
||||
size_t this_iter;
|
||||
const void *in;
|
||||
void *out;
|
||||
|
||||
tmp_buf_bshuf = malloc(size * elem_size);
|
||||
if (tmp_buf_bshuf == NULL) return -1;
|
||||
|
||||
size_t tmp_buf_zstd_size = ZSTD_compressBound(size * elem_size);
|
||||
tmp_buf_zstd = malloc(tmp_buf_zstd_size);
|
||||
if (tmp_buf_zstd == NULL){
|
||||
free(tmp_buf_bshuf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
in = ioc_get_in(C_ptr, &this_iter);
|
||||
ioc_set_next_in(C_ptr, &this_iter, (void*) ((char*) in + size * elem_size));
|
||||
|
||||
count = bshuf_trans_bit_elem(in, tmp_buf_bshuf, size, elem_size);
|
||||
if (count < 0) {
|
||||
free(tmp_buf_zstd);
|
||||
free(tmp_buf_bshuf);
|
||||
return count;
|
||||
}
|
||||
nbytes = ZSTD_compress(tmp_buf_zstd, tmp_buf_zstd_size, (const void*)tmp_buf_bshuf, size * elem_size, comp_lvl);
|
||||
free(tmp_buf_bshuf);
|
||||
CHECK_ERR_FREE_LZ(nbytes, tmp_buf_zstd);
|
||||
|
||||
out = ioc_get_out(C_ptr, &this_iter);
|
||||
ioc_set_next_out(C_ptr, &this_iter, (void *) ((char *) out + nbytes + 4));
|
||||
|
||||
bshuf_write_uint32_BE(out, nbytes);
|
||||
memcpy((char *) out + 4, tmp_buf_zstd, nbytes);
|
||||
|
||||
free(tmp_buf_zstd);
|
||||
|
||||
return nbytes + 4;
|
||||
}
|
||||
|
||||
|
||||
/* Decompress and bitunshuffle a single block. */
|
||||
int64_t bshuf_decompress_zstd_block(ioc_chain *C_ptr,
|
||||
const size_t size, const size_t elem_size, const int option) {
|
||||
|
||||
int64_t nbytes, count;
|
||||
void *out, *tmp_buf;
|
||||
const void *in;
|
||||
size_t this_iter;
|
||||
int32_t nbytes_from_header;
|
||||
|
||||
in = ioc_get_in(C_ptr, &this_iter);
|
||||
nbytes_from_header = bshuf_read_uint32_BE(in);
|
||||
ioc_set_next_in(C_ptr, &this_iter,
|
||||
(void*) ((char*) in + nbytes_from_header + 4));
|
||||
|
||||
out = ioc_get_out(C_ptr, &this_iter);
|
||||
ioc_set_next_out(C_ptr, &this_iter,
|
||||
(void *) ((char *) out + size * elem_size));
|
||||
|
||||
tmp_buf = malloc(size * elem_size);
|
||||
if (tmp_buf == NULL) return -1;
|
||||
|
||||
nbytes = ZSTD_decompress(tmp_buf, size * elem_size, in + 4, nbytes_from_header);
|
||||
CHECK_ERR_FREE_LZ(nbytes, tmp_buf);
|
||||
if (nbytes != size * elem_size) {
|
||||
free(tmp_buf);
|
||||
return -91;
|
||||
}
|
||||
nbytes = nbytes_from_header;
|
||||
|
||||
count = bshuf_untrans_bit_elem(tmp_buf, out, size, elem_size);
|
||||
CHECK_ERR_FREE(count, tmp_buf);
|
||||
nbytes += 4;
|
||||
|
||||
free(tmp_buf);
|
||||
return nbytes;
|
||||
}
|
||||
#endif // ZSTD_SUPPORT
|
||||
|
||||
|
||||
/* ---- Public functions ----
|
||||
*
|
||||
* See header file for description and usage.
|
||||
*
|
||||
*/
|
||||
|
||||
size_t bshuf_compress_lz4_bound(const size_t size,
|
||||
const size_t elem_size, size_t block_size) {
|
||||
|
||||
size_t bound, leftover;
|
||||
|
||||
if (block_size == 0) {
|
||||
block_size = bshuf_default_block_size(elem_size);
|
||||
}
|
||||
if (block_size % BSHUF_BLOCKED_MULT) return -81;
|
||||
|
||||
// Note that each block gets a 4 byte header.
|
||||
// Size of full blocks.
|
||||
bound = (LZ4_compressBound(block_size * elem_size) + 4) * (size / block_size);
|
||||
// Size of partial blocks, if any.
|
||||
leftover = ((size % block_size) / BSHUF_BLOCKED_MULT) * BSHUF_BLOCKED_MULT;
|
||||
if (leftover) bound += LZ4_compressBound(leftover * elem_size) + 4;
|
||||
// Size of uncompressed data not fitting into any blocks.
|
||||
bound += (size % BSHUF_BLOCKED_MULT) * elem_size;
|
||||
return bound;
|
||||
}
|
||||
|
||||
|
||||
int64_t bshuf_compress_lz4(const void* in, void* out, const size_t size,
|
||||
const size_t elem_size, size_t block_size) {
|
||||
return bshuf_blocked_wrap_fun(&bshuf_compress_lz4_block, in, out, size,
|
||||
elem_size, block_size, 0/*option*/);
|
||||
}
|
||||
|
||||
|
||||
int64_t bshuf_decompress_lz4(const void* in, void* out, const size_t size,
|
||||
const size_t elem_size, size_t block_size) {
|
||||
return bshuf_blocked_wrap_fun(&bshuf_decompress_lz4_block, in, out, size,
|
||||
elem_size, block_size, 0/*option*/);
|
||||
}
|
||||
|
||||
#ifdef ZSTD_SUPPORT
|
||||
size_t bshuf_compress_zstd_bound(const size_t size,
|
||||
const size_t elem_size, size_t block_size) {
|
||||
|
||||
size_t bound, leftover;
|
||||
|
||||
if (block_size == 0) {
|
||||
block_size = bshuf_default_block_size(elem_size);
|
||||
}
|
||||
if (block_size % BSHUF_BLOCKED_MULT) return -81;
|
||||
|
||||
// Note that each block gets a 4 byte header.
|
||||
// Size of full blocks.
|
||||
bound = (ZSTD_compressBound(block_size * elem_size) + 4) * (size / block_size);
|
||||
// Size of partial blocks, if any.
|
||||
leftover = ((size % block_size) / BSHUF_BLOCKED_MULT) * BSHUF_BLOCKED_MULT;
|
||||
if (leftover) bound += ZSTD_compressBound(leftover * elem_size) + 4;
|
||||
// Size of uncompressed data not fitting into any blocks.
|
||||
bound += (size % BSHUF_BLOCKED_MULT) * elem_size;
|
||||
return bound;
|
||||
}
|
||||
|
||||
|
||||
int64_t bshuf_compress_zstd(const void* in, void* out, const size_t size,
|
||||
const size_t elem_size, size_t block_size, const int comp_lvl) {
|
||||
return bshuf_blocked_wrap_fun(&bshuf_compress_zstd_block, in, out, size,
|
||||
elem_size, block_size, comp_lvl);
|
||||
}
|
||||
|
||||
|
||||
int64_t bshuf_decompress_zstd(const void* in, void* out, const size_t size,
|
||||
const size_t elem_size, size_t block_size) {
|
||||
return bshuf_blocked_wrap_fun(&bshuf_decompress_zstd_block, in, out, size,
|
||||
elem_size, block_size, 0/*option*/);
|
||||
}
|
||||
#endif // ZSTD_SUPPORT
|
||||
215
compression/bitshuffle/bitshuffle.h
Normal file
215
compression/bitshuffle/bitshuffle.h
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Bitshuffle - Filter for improving compression of typed binary data.
|
||||
*
|
||||
* This file is part of Bitshuffle
|
||||
* Author: Kiyoshi Masui <kiyo@physics.ubc.ca>
|
||||
* Website: http://www.github.com/kiyo-masui/bitshuffle
|
||||
* Created: 2014
|
||||
*
|
||||
* See LICENSE file for details about copyright and rights to use.
|
||||
*
|
||||
*
|
||||
* Header File
|
||||
*
|
||||
* Worker routines return an int64_t which is the number of bytes processed
|
||||
* if positive or an error code if negative.
|
||||
*
|
||||
* Error codes:
|
||||
* -1 : Failed to allocate memory.
|
||||
* -11 : Missing SSE.
|
||||
* -12 : Missing AVX.
|
||||
* -80 : Input size not a multiple of 8.
|
||||
* -81 : block_size not multiple of 8.
|
||||
* -91 : Decompression error, wrong number of bytes processed.
|
||||
* -1YYY : Error internal to compression routine with error code -YYY.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef BITSHUFFLE_H
|
||||
#define BITSHUFFLE_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "bitshuffle_core.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ---- LZ4 Interface ----
|
||||
*/
|
||||
|
||||
/* ---- bshuf_compress_lz4_bound ----
|
||||
*
|
||||
* Bound on size of data compressed with *bshuf_compress_lz4*.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* size : number of elements in input
|
||||
* elem_size : element size of typed data
|
||||
* block_size : Process in blocks of this many elements. Pass 0 to
|
||||
* select automatically (recommended).
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* Bound on compressed data size.
|
||||
*
|
||||
*/
|
||||
size_t bshuf_compress_lz4_bound(const size_t size,
|
||||
const size_t elem_size, size_t block_size);
|
||||
|
||||
|
||||
/* ---- bshuf_compress_lz4 ----
|
||||
*
|
||||
* Bitshuffled and compress the data using LZ4.
|
||||
*
|
||||
* Transpose within elements, in blocks of data of *block_size* elements then
|
||||
* compress the blocks using LZ4. In the output buffer, each block is prefixed
|
||||
* by a 4 byte integer giving the compressed size of that block.
|
||||
*
|
||||
* Output buffer must be large enough to hold the compressed data. This could
|
||||
* be in principle substantially larger than the input buffer. Use the routine
|
||||
* *bshuf_compress_lz4_bound* to get an upper limit.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* in : input buffer, must be of size * elem_size bytes
|
||||
* out : output buffer, must be large enough to hold data.
|
||||
* size : number of elements in input
|
||||
* elem_size : element size of typed data
|
||||
* block_size : Process in blocks of this many elements. Pass 0 to
|
||||
* select automatically (recommended).
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* number of bytes used in output buffer, negative error-code if failed.
|
||||
*
|
||||
*/
|
||||
int64_t bshuf_compress_lz4(const void* in, void* out, const size_t size, const size_t
|
||||
elem_size, size_t block_size);
|
||||
|
||||
|
||||
/* ---- bshuf_decompress_lz4 ----
|
||||
*
|
||||
* Undo compression and bitshuffling.
|
||||
*
|
||||
* Decompress data then un-bitshuffle it in blocks of *block_size* elements.
|
||||
*
|
||||
* To properly unshuffle bitshuffled data, *size*, *elem_size* and *block_size*
|
||||
* must patch the parameters used to compress the data.
|
||||
*
|
||||
* NOT TO BE USED WITH UNTRUSTED DATA: This routine uses the function
|
||||
* LZ4_decompress_fast from LZ4, which does not protect against maliciously
|
||||
* formed datasets. By modifying the compressed data, this function could be
|
||||
* coerced into leaving the boundaries of the input buffer.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* in : input buffer
|
||||
* out : output buffer, must be of size * elem_size bytes
|
||||
* size : number of elements in input
|
||||
* elem_size : element size of typed data
|
||||
* block_size : Process in blocks of this many elements. Pass 0 to
|
||||
* select automatically (recommended).
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* number of bytes consumed in *input* buffer, negative error-code if failed.
|
||||
*
|
||||
*/
|
||||
int64_t bshuf_decompress_lz4(const void* in, void* out, const size_t size,
|
||||
const size_t elem_size, size_t block_size);
|
||||
|
||||
/*
|
||||
* ---- ZSTD Interface ----
|
||||
*/
|
||||
|
||||
#ifdef ZSTD_SUPPORT
|
||||
/* ---- bshuf_compress_zstd_bound ----
|
||||
*
|
||||
* Bound on size of data compressed with *bshuf_compress_zstd*.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* size : number of elements in input
|
||||
* elem_size : element size of typed data
|
||||
* block_size : Process in blocks of this many elements. Pass 0 to
|
||||
* select automatically (recommended).
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* Bound on compressed data size.
|
||||
*
|
||||
*/
|
||||
size_t bshuf_compress_zstd_bound(const size_t size,
|
||||
const size_t elem_size, size_t block_size);
|
||||
|
||||
/* ---- bshuf_compress_zstd ----
|
||||
*
|
||||
* Bitshuffled and compress the data using zstd.
|
||||
*
|
||||
* Transpose within elements, in blocks of data of *block_size* elements then
|
||||
* compress the blocks using ZSTD. In the output buffer, each block is prefixed
|
||||
* by a 4 byte integer giving the compressed size of that block.
|
||||
*
|
||||
* Output buffer must be large enough to hold the compressed data. This could
|
||||
* be in principle substantially larger than the input buffer. Use the routine
|
||||
* *bshuf_compress_zstd_bound* to get an upper limit.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* in : input buffer, must be of size * elem_size bytes
|
||||
* out : output buffer, must be large enough to hold data.
|
||||
* size : number of elements in input
|
||||
* elem_size : element size of typed data
|
||||
* block_size : Process in blocks of this many elements. Pass 0 to
|
||||
* select automatically (recommended).
|
||||
* comp_lvl : compression level applied
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* number of bytes used in output buffer, negative error-code if failed.
|
||||
*
|
||||
*/
|
||||
int64_t bshuf_compress_zstd(const void* in, void* out, const size_t size, const size_t
|
||||
elem_size, size_t block_size, const int comp_lvl);
|
||||
|
||||
|
||||
/* ---- bshuf_decompress_zstd ----
|
||||
*
|
||||
* Undo compression and bitshuffling.
|
||||
*
|
||||
* Decompress data then un-bitshuffle it in blocks of *block_size* elements.
|
||||
*
|
||||
* To properly unshuffle bitshuffled data, *size*, *elem_size* and *block_size*
|
||||
* must patch the parameters used to compress the data.
|
||||
*
|
||||
* NOT TO BE USED WITH UNTRUSTED DATA: This routine uses the function
|
||||
* ZSTD_decompress_fast from ZSTD, which does not protect against maliciously
|
||||
* formed datasets. By modifying the compressed data, this function could be
|
||||
* coerced into leaving the boundaries of the input buffer.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* in : input buffer
|
||||
* out : output buffer, must be of size * elem_size bytes
|
||||
* size : number of elements in input
|
||||
* elem_size : element size of typed data
|
||||
* block_size : Process in blocks of this many elements. Pass 0 to
|
||||
* select automatically (recommended).
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* number of bytes consumed in *input* buffer, negative error-code if failed.
|
||||
*
|
||||
*/
|
||||
int64_t bshuf_decompress_zstd(const void* in, void* out, const size_t size,
|
||||
const size_t elem_size, size_t block_size);
|
||||
|
||||
#endif // ZSTD_SUPPORT
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // BITSHUFFLE_H
|
||||
2035
compression/bitshuffle/bitshuffle_core.c
Normal file
2035
compression/bitshuffle/bitshuffle_core.c
Normal file
File diff suppressed because it is too large
Load Diff
182
compression/bitshuffle/bitshuffle_core.h
Normal file
182
compression/bitshuffle/bitshuffle_core.h
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Bitshuffle - Filter for improving compression of typed binary data.
|
||||
*
|
||||
* This file is part of Bitshuffle
|
||||
* Author: Kiyoshi Masui <kiyo@physics.ubc.ca>
|
||||
* Website: http://www.github.com/kiyo-masui/bitshuffle
|
||||
* Created: 2014
|
||||
*
|
||||
* See LICENSE file for details about copyright and rights to use.
|
||||
*
|
||||
*
|
||||
* Header File
|
||||
*
|
||||
* Worker routines return an int64_t which is the number of bytes processed
|
||||
* if positive or an error code if negative.
|
||||
*
|
||||
* Error codes:
|
||||
* -1 : Failed to allocate memory.
|
||||
* -11 : Missing SSE.
|
||||
* -12 : Missing AVX.
|
||||
* -13 : Missing Arm Neon.
|
||||
* -14 : Missing AVX512.
|
||||
* -80 : Input size not a multiple of 8.
|
||||
* -81 : block_size not multiple of 8.
|
||||
* -91 : Decompression error, wrong number of bytes processed.
|
||||
* -1YYY : Error internal to compression routine with error code -YYY.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef BITSHUFFLE_CORE_H
|
||||
#define BITSHUFFLE_CORE_H
|
||||
|
||||
// We assume GNU g++ defining `__cplusplus` has stdint.h
|
||||
#if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199900L) || defined(__cplusplus)
|
||||
#include <stdint.h>
|
||||
#else
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
typedef long long int64_t;
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
// These are usually set in the setup.py.
|
||||
#ifndef BSHUF_VERSION_MAJOR
|
||||
#define BSHUF_VERSION_MAJOR 0
|
||||
#define BSHUF_VERSION_MINOR 4
|
||||
#define BSHUF_VERSION_POINT 0
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* --- bshuf_using_SSE2 ----
|
||||
*
|
||||
* Whether routines where compiled with the SSE2 instruction set.
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* 1 if using SSE2, 0 otherwise.
|
||||
*
|
||||
*/
|
||||
int bshuf_using_SSE2(void);
|
||||
|
||||
|
||||
/* ---- bshuf_using_NEON ----
|
||||
*
|
||||
* Whether routines where compiled with the NEON instruction set.
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* 1 if using NEON, 0 otherwise.
|
||||
*
|
||||
*/
|
||||
int bshuf_using_NEON(void);
|
||||
|
||||
|
||||
/* ---- bshuf_using_AVX2 ----
|
||||
*
|
||||
* Whether routines where compiled with the AVX2 instruction set.
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* 1 if using AVX2, 0 otherwise.
|
||||
*
|
||||
*/
|
||||
int bshuf_using_AVX2(void);
|
||||
|
||||
|
||||
/* ---- bshuf_using_AVX512 ----
|
||||
*
|
||||
* Whether routines where compiled with the AVX512 instruction set.
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* 1 if using AVX512, 0 otherwise.
|
||||
*
|
||||
*/
|
||||
int bshuf_using_AVX512(void);
|
||||
|
||||
|
||||
/* ---- bshuf_default_block_size ----
|
||||
*
|
||||
* The default block size as function of element size.
|
||||
*
|
||||
* This is the block size used by the blocked routines (any routine
|
||||
* taking a *block_size* argument) when the block_size is not provided
|
||||
* (zero is passed).
|
||||
*
|
||||
* The results of this routine are guaranteed to be stable such that
|
||||
* shuffled/compressed data can always be decompressed.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* elem_size : element size of data to be shuffled/compressed.
|
||||
*
|
||||
*/
|
||||
size_t bshuf_default_block_size(const size_t elem_size);
|
||||
|
||||
|
||||
/* ---- bshuf_bitshuffle ----
|
||||
*
|
||||
* Bitshuffle the data.
|
||||
*
|
||||
* Transpose the bits within elements, in blocks of *block_size*
|
||||
* elements.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* in : input buffer, must be of size * elem_size bytes
|
||||
* out : output buffer, must be of size * elem_size bytes
|
||||
* size : number of elements in input
|
||||
* elem_size : element size of typed data
|
||||
* block_size : Do transpose in blocks of this many elements. Pass 0 to
|
||||
* select automatically (recommended).
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* number of bytes processed, negative error-code if failed.
|
||||
*
|
||||
*/
|
||||
int64_t bshuf_bitshuffle(const void* in, void* out, const size_t size,
|
||||
const size_t elem_size, size_t block_size);
|
||||
|
||||
|
||||
/* ---- bshuf_bitunshuffle ----
|
||||
*
|
||||
* Unshuffle bitshuffled data.
|
||||
*
|
||||
* Untranspose the bits within elements, in blocks of *block_size*
|
||||
* elements.
|
||||
*
|
||||
* To properly unshuffle bitshuffled data, *size*, *elem_size* and *block_size*
|
||||
* must match the parameters used to shuffle the data.
|
||||
*
|
||||
* Parameters
|
||||
* ----------
|
||||
* in : input buffer, must be of size * elem_size bytes
|
||||
* out : output buffer, must be of size * elem_size bytes
|
||||
* size : number of elements in input
|
||||
* elem_size : element size of typed data
|
||||
* block_size : Do transpose in blocks of this many elements. Pass 0 to
|
||||
* select automatically (recommended).
|
||||
*
|
||||
* Returns
|
||||
* -------
|
||||
* number of bytes processed, negative error-code if failed.
|
||||
*
|
||||
*/
|
||||
int64_t bshuf_bitunshuffle(const void* in, void* out, const size_t size,
|
||||
const size_t elem_size, size_t block_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // BITSHUFFLE_CORE_H
|
||||
75
compression/bitshuffle/bitshuffle_internals.h
Normal file
75
compression/bitshuffle/bitshuffle_internals.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Bitshuffle - Filter for improving compression of typed binary data.
|
||||
*
|
||||
* This file is part of Bitshuffle
|
||||
* Author: Kiyoshi Masui <kiyo@physics.ubc.ca>
|
||||
* Website: http://www.github.com/kiyo-masui/bitshuffle
|
||||
* Created: 2014
|
||||
*
|
||||
* See LICENSE file for details about copyright and rights to use.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef BITSHUFFLE_INTERNALS_H
|
||||
#define BITSHUFFLE_INTERNALS_H
|
||||
|
||||
// We assume GNU g++ defining `__cplusplus` has stdint.h
|
||||
#if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199900L) || defined(__cplusplus)
|
||||
#include <stdint.h>
|
||||
#else
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
typedef long long int64_t;
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "iochain.h"
|
||||
|
||||
|
||||
// Constants.
|
||||
#ifndef BSHUF_MIN_RECOMMEND_BLOCK
|
||||
#define BSHUF_MIN_RECOMMEND_BLOCK 128
|
||||
#define BSHUF_BLOCKED_MULT 8 // Block sizes must be multiple of this.
|
||||
#define BSHUF_TARGET_BLOCK_SIZE_B 8192
|
||||
#endif
|
||||
|
||||
|
||||
// Macros.
|
||||
#define CHECK_ERR_FREE(count, buf) if (count < 0) { free(buf); return count; }
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---- Utility functions for internal use only ---- */
|
||||
|
||||
int64_t bshuf_trans_bit_elem(const void* in, void* out, const size_t size,
|
||||
const size_t elem_size);
|
||||
|
||||
/* Read a 32 bit unsigned integer from a buffer big endian order. */
|
||||
uint32_t bshuf_read_uint32_BE(const void* buf);
|
||||
|
||||
/* Write a 32 bit unsigned integer to a buffer in big endian order. */
|
||||
void bshuf_write_uint32_BE(void* buf, uint32_t num);
|
||||
|
||||
int64_t bshuf_untrans_bit_elem(const void* in, void* out, const size_t size,
|
||||
const size_t elem_size);
|
||||
|
||||
/* Function definition for worker functions that process a single block. */
|
||||
typedef int64_t (*bshufBlockFunDef)(ioc_chain* C_ptr,
|
||||
const size_t size, const size_t elem_size, const int option);
|
||||
|
||||
/* Wrap a function for processing a single block to process an entire buffer in
|
||||
* parallel. */
|
||||
int64_t bshuf_blocked_wrap_fun(bshufBlockFunDef fun, const void* in, void* out,
|
||||
const size_t size, const size_t elem_size, size_t block_size, const int option);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // BITSHUFFLE_INTERNALS_H
|
||||
260
compression/bitshuffle/bshuf_h5filter.c
Normal file
260
compression/bitshuffle/bshuf_h5filter.c
Normal file
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* Bitshuffle HDF5 filter
|
||||
*
|
||||
* This file is part of Bitshuffle
|
||||
* Author: Kiyoshi Masui <kiyo@physics.ubc.ca>
|
||||
* Website: http://www.github.com/kiyo-masui/bitshuffle
|
||||
* Created: 2014
|
||||
*
|
||||
* See LICENSE file for details about copyright and rights to use.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "bitshuffle.h"
|
||||
#include "bshuf_h5filter.h"
|
||||
|
||||
|
||||
#define PUSH_ERR(func, minor, str) \
|
||||
H5Epush1(__FILE__, func, __LINE__, H5E_PLINE, minor, str)
|
||||
|
||||
|
||||
// Prototypes from bitshuffle.c
|
||||
void bshuf_write_uint64_BE(void* buf, uint64_t num);
|
||||
uint64_t bshuf_read_uint64_BE(void* buf);
|
||||
void bshuf_write_uint32_BE(void* buf, uint32_t num);
|
||||
uint32_t bshuf_read_uint32_BE(const void* buf);
|
||||
|
||||
|
||||
// Only called on compresion, not on reverse.
|
||||
herr_t bshuf_h5_set_local(hid_t dcpl, hid_t type, hid_t space){
|
||||
|
||||
herr_t r;
|
||||
size_t ii;
|
||||
|
||||
unsigned int elem_size;
|
||||
|
||||
unsigned int flags;
|
||||
size_t nelements = 8;
|
||||
size_t nelem_max = 11;
|
||||
unsigned values[] = {0,0,0,0,0,0,0,0,0,0,0};
|
||||
unsigned tmp_values[] = {0,0,0,0,0,0,0,0};
|
||||
char msg[80];
|
||||
|
||||
r = H5Pget_filter_by_id2(dcpl, BSHUF_H5FILTER, &flags, &nelements,
|
||||
tmp_values, 0, NULL, NULL);
|
||||
if(r<0) return -1;
|
||||
|
||||
// First 3 slots reserved. Move any passed options to higher addresses.
|
||||
for (ii=0; ii < nelements && ii + 3 < nelem_max; ii++) {
|
||||
values[ii + 3] = tmp_values[ii];
|
||||
}
|
||||
|
||||
nelements = 3 + nelements;
|
||||
|
||||
values[0] = BSHUF_VERSION_MAJOR;
|
||||
values[1] = BSHUF_VERSION_MINOR;
|
||||
|
||||
elem_size = H5Tget_size(type);
|
||||
if(elem_size <= 0) {
|
||||
PUSH_ERR("bshuf_h5_set_local", H5E_CALLBACK,
|
||||
"Invalid element size.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
values[2] = elem_size;
|
||||
|
||||
// Validate user supplied arguments.
|
||||
if (nelements > 3) {
|
||||
if (values[3] % 8 || values[3] < 0) {
|
||||
sprintf(msg, "Error in bitshuffle. Invalid block size: %d.",
|
||||
values[3]);
|
||||
PUSH_ERR("bshuf_h5_set_local", H5E_CALLBACK, msg);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (nelements > 4) {
|
||||
switch (values[4]) {
|
||||
case 0:
|
||||
break;
|
||||
case BSHUF_H5_COMPRESS_LZ4:
|
||||
break;
|
||||
#ifdef ZSTD_SUPPORT
|
||||
case BSHUF_H5_COMPRESS_ZSTD:
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
PUSH_ERR("bshuf_h5_set_local", H5E_CALLBACK,
|
||||
"Invalid bitshuffle compression.");
|
||||
}
|
||||
}
|
||||
|
||||
r = H5Pmodify_filter(dcpl, BSHUF_H5FILTER, flags, nelements, values);
|
||||
if(r<0) return -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
size_t bshuf_h5_filter(unsigned int flags, size_t cd_nelmts,
|
||||
const unsigned int cd_values[], size_t nbytes,
|
||||
size_t *buf_size, void **buf) {
|
||||
|
||||
size_t size, elem_size;
|
||||
int err = -1;
|
||||
char msg[80];
|
||||
size_t block_size = 0;
|
||||
size_t buf_size_out, nbytes_uncomp, nbytes_out;
|
||||
char* in_buf = *buf;
|
||||
void *out_buf;
|
||||
|
||||
if (cd_nelmts < 3) {
|
||||
PUSH_ERR("bshuf_h5_filter", H5E_CALLBACK,
|
||||
"Not enough parameters.");
|
||||
return 0;
|
||||
}
|
||||
elem_size = cd_values[2];
|
||||
#ifdef ZSTD_SUPPORT
|
||||
const int comp_lvl = cd_values[5];
|
||||
#endif
|
||||
|
||||
// User specified block size.
|
||||
if (cd_nelmts > 3) block_size = cd_values[3];
|
||||
|
||||
if (block_size == 0) block_size = bshuf_default_block_size(elem_size);
|
||||
|
||||
#ifndef ZSTD_SUPPORT
|
||||
if (cd_nelmts > 4 && (cd_values[4] == BSHUF_H5_COMPRESS_ZSTD)) {
|
||||
PUSH_ERR("bshuf_h5_filter", H5E_CALLBACK,
|
||||
"ZSTD compression filter chosen but ZSTD support not installed.");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Compression in addition to bitshuffle.
|
||||
if (cd_nelmts > 4 && (cd_values[4] == BSHUF_H5_COMPRESS_LZ4 || cd_values[4] == BSHUF_H5_COMPRESS_ZSTD)) {
|
||||
if (flags & H5Z_FLAG_REVERSE) {
|
||||
// First eight bytes is the number of bytes in the output buffer,
|
||||
// little endian.
|
||||
nbytes_uncomp = bshuf_read_uint64_BE(in_buf);
|
||||
// Override the block size with the one read from the header.
|
||||
block_size = bshuf_read_uint32_BE((const char*) in_buf + 8) / elem_size;
|
||||
// Skip over the header.
|
||||
in_buf += 12;
|
||||
buf_size_out = nbytes_uncomp;
|
||||
} else {
|
||||
nbytes_uncomp = nbytes;
|
||||
// Pick which compressions library to use
|
||||
if(cd_values[4] == BSHUF_H5_COMPRESS_LZ4) {
|
||||
buf_size_out = bshuf_compress_lz4_bound(nbytes_uncomp / elem_size,
|
||||
elem_size, block_size) + 12;
|
||||
}
|
||||
#ifdef ZSTD_SUPPORT
|
||||
else if (cd_values[4] == BSHUF_H5_COMPRESS_ZSTD) {
|
||||
buf_size_out = bshuf_compress_zstd_bound(nbytes_uncomp / elem_size,
|
||||
elem_size, block_size) + 12;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
nbytes_uncomp = nbytes;
|
||||
buf_size_out = nbytes;
|
||||
}
|
||||
|
||||
// TODO, remove this restriction by memcopying the extra.
|
||||
if (nbytes_uncomp % elem_size) {
|
||||
PUSH_ERR("bshuf_h5_filter", H5E_CALLBACK,
|
||||
"Non integer number of elements.");
|
||||
return 0;
|
||||
}
|
||||
size = nbytes_uncomp / elem_size;
|
||||
|
||||
out_buf = malloc(buf_size_out);
|
||||
if (out_buf == NULL) {
|
||||
PUSH_ERR("bshuf_h5_filter", H5E_CALLBACK,
|
||||
"Could not allocate output buffer.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cd_nelmts > 4 && (cd_values[4] == BSHUF_H5_COMPRESS_LZ4 || cd_values[4] == BSHUF_H5_COMPRESS_ZSTD)) {
|
||||
if (flags & H5Z_FLAG_REVERSE) {
|
||||
// Bit unshuffle/decompress.
|
||||
// Pick which compressions library to use
|
||||
if(cd_values[4] == BSHUF_H5_COMPRESS_LZ4) {
|
||||
err = bshuf_decompress_lz4(in_buf, out_buf, size, elem_size, block_size);
|
||||
}
|
||||
#ifdef ZSTD_SUPPORT
|
||||
else if (cd_values[4] == BSHUF_H5_COMPRESS_ZSTD) {
|
||||
err = bshuf_decompress_zstd(in_buf, out_buf, size, elem_size, block_size);
|
||||
}
|
||||
#endif
|
||||
nbytes_out = nbytes_uncomp;
|
||||
} else {
|
||||
// Bit shuffle/compress.
|
||||
// Write the header, described in
|
||||
// http://www.hdfgroup.org/services/filters/HDF5_LZ4.pdf.
|
||||
// Techincally we should be using signed integers instead of
|
||||
// unsigned ones, however for valid inputs (positive numbers) these
|
||||
// have the same representation.
|
||||
bshuf_write_uint64_BE(out_buf, nbytes_uncomp);
|
||||
bshuf_write_uint32_BE((char*) out_buf + 8, block_size * elem_size);
|
||||
if(cd_values[4] == BSHUF_H5_COMPRESS_LZ4) {
|
||||
err = bshuf_compress_lz4(in_buf, (char*) out_buf + 12, size,
|
||||
elem_size, block_size);
|
||||
}
|
||||
#ifdef ZSTD_SUPPORT
|
||||
else if (cd_values[4] == BSHUF_H5_COMPRESS_ZSTD) {
|
||||
err = bshuf_compress_zstd(in_buf, (char*) out_buf + 12, size,
|
||||
elem_size, block_size, comp_lvl);
|
||||
}
|
||||
#endif
|
||||
nbytes_out = err + 12;
|
||||
}
|
||||
} else {
|
||||
if (flags & H5Z_FLAG_REVERSE) {
|
||||
// Bit unshuffle.
|
||||
err = bshuf_bitunshuffle(in_buf, out_buf, size, elem_size,
|
||||
block_size); } else {
|
||||
// Bit shuffle.
|
||||
err = bshuf_bitshuffle(in_buf, out_buf, size, elem_size,
|
||||
block_size); } nbytes_out = nbytes; }
|
||||
//printf("nb_in %d, nb_uncomp %d, nb_out %d, buf_out %d, block %d\n",
|
||||
//nbytes, nbytes_uncomp, nbytes_out, buf_size_out, block_size);
|
||||
|
||||
if (err < 0) {
|
||||
sprintf(msg, "Error in bitshuffle with error code %d.", err);
|
||||
PUSH_ERR("bshuf_h5_filter", H5E_CALLBACK, msg);
|
||||
free(out_buf);
|
||||
return 0;
|
||||
} else {
|
||||
free(*buf);
|
||||
*buf = out_buf;
|
||||
*buf_size = buf_size_out;
|
||||
|
||||
return nbytes_out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
H5Z_class_t bshuf_H5Filter[1] = {{
|
||||
H5Z_CLASS_T_VERS,
|
||||
(H5Z_filter_t)(BSHUF_H5FILTER),
|
||||
1, 1,
|
||||
"bitshuffle; see https://github.com/kiyo-masui/bitshuffle",
|
||||
NULL,
|
||||
(H5Z_set_local_func_t)(bshuf_h5_set_local),
|
||||
(H5Z_func_t)(bshuf_h5_filter)
|
||||
}};
|
||||
|
||||
|
||||
int bshuf_register_h5filter(void){
|
||||
|
||||
int retval;
|
||||
|
||||
retval = H5Zregister(bshuf_H5Filter);
|
||||
if(retval<0){
|
||||
PUSH_ERR("bshuf_register_h5filter",
|
||||
H5E_CANTREGISTER, "Can't register bitshuffle filter");
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
67
compression/bitshuffle/bshuf_h5filter.h
Normal file
67
compression/bitshuffle/bshuf_h5filter.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Bitshuffle HDF5 filter
|
||||
*
|
||||
* This file is part of Bitshuffle
|
||||
* Author: Kiyoshi Masui <kiyo@physics.ubc.ca>
|
||||
* Website: http://www.github.com/kiyo-masui/bitshuffle
|
||||
* Created: 2014
|
||||
*
|
||||
* See LICENSE file for details about copyright and rights to use.
|
||||
*
|
||||
*
|
||||
* Header File
|
||||
*
|
||||
* Filter Options
|
||||
* --------------
|
||||
* block_size (option slot 0) : interger (optional)
|
||||
* What block size to use (in elements not bytes). Default is 0,
|
||||
* for which bitshuffle will pick a block size with a target of 8kb.
|
||||
* Compression (option slot 1) : 0 or BSHUF_H5_COMPRESS_LZ4
|
||||
* Whether to apply LZ4 compression to the data after bitshuffling.
|
||||
* This is much faster than applying compression as a second filter
|
||||
* because it is done when the small block of data is already in the
|
||||
* L1 cache.
|
||||
*
|
||||
* For LZ4 compression, the compressed format of the data is the same as
|
||||
* for the normal LZ4 filter described in
|
||||
* http://www.hdfgroup.org/services/filters/HDF5_LZ4.pdf.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef BSHUF_H5FILTER_H
|
||||
#define BSHUF_H5FILTER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define H5Z_class_t_vers 2
|
||||
#include "hdf5.h"
|
||||
|
||||
|
||||
#define BSHUF_H5FILTER 32008
|
||||
|
||||
|
||||
#define BSHUF_H5_COMPRESS_LZ4 2
|
||||
#define BSHUF_H5_COMPRESS_ZSTD 3
|
||||
|
||||
|
||||
extern H5Z_class_t bshuf_H5Filter[1];
|
||||
|
||||
|
||||
/* ---- bshuf_register_h5filter ----
|
||||
*
|
||||
* Register the bitshuffle HDF5 filter within the HDF5 library.
|
||||
*
|
||||
* Call this before using the bitshuffle HDF5 filter from C unless
|
||||
* using dynamically loaded filters.
|
||||
*
|
||||
*/
|
||||
int bshuf_register_h5filter(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // BSHUF_H5FILTER_H
|
||||
90
compression/bitshuffle/iochain.c
Normal file
90
compression/bitshuffle/iochain.c
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* IOchain - Distribute a chain of dependant IO events amoung threads.
|
||||
*
|
||||
* This file is part of Bitshuffle
|
||||
* Author: Kiyoshi Masui <kiyo@physics.ubc.ca>
|
||||
* Website: http://www.github.com/kiyo-masui/bitshuffle
|
||||
* Created: 2014
|
||||
*
|
||||
* See LICENSE file for details about copyright and rights to use.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "iochain.h"
|
||||
|
||||
|
||||
void ioc_init(ioc_chain *C, const void *in_ptr_0, void *out_ptr_0) {
|
||||
#ifdef _OPENMP
|
||||
omp_init_lock(&C->next_lock);
|
||||
for (size_t ii = 0; ii < IOC_SIZE; ii ++) {
|
||||
omp_init_lock(&(C->in_pl[ii].lock));
|
||||
omp_init_lock(&(C->out_pl[ii].lock));
|
||||
}
|
||||
#endif
|
||||
C->next = 0;
|
||||
C->in_pl[0].ptr = in_ptr_0;
|
||||
C->out_pl[0].ptr = out_ptr_0;
|
||||
}
|
||||
|
||||
|
||||
void ioc_destroy(ioc_chain *C) {
|
||||
#ifdef _OPENMP
|
||||
omp_destroy_lock(&C->next_lock);
|
||||
for (size_t ii = 0; ii < IOC_SIZE; ii ++) {
|
||||
omp_destroy_lock(&(C->in_pl[ii].lock));
|
||||
omp_destroy_lock(&(C->out_pl[ii].lock));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
const void * ioc_get_in(ioc_chain *C, size_t *this_iter) {
|
||||
#ifdef _OPENMP
|
||||
omp_set_lock(&C->next_lock);
|
||||
#pragma omp flush
|
||||
#endif
|
||||
*this_iter = C->next;
|
||||
C->next ++;
|
||||
#ifdef _OPENMP
|
||||
omp_set_lock(&(C->in_pl[*this_iter % IOC_SIZE].lock));
|
||||
omp_set_lock(&(C->in_pl[(*this_iter + 1) % IOC_SIZE].lock));
|
||||
omp_set_lock(&(C->out_pl[(*this_iter + 1) % IOC_SIZE].lock));
|
||||
omp_unset_lock(&C->next_lock);
|
||||
#endif
|
||||
return C->in_pl[*this_iter % IOC_SIZE].ptr;
|
||||
}
|
||||
|
||||
|
||||
void ioc_set_next_in(ioc_chain *C, size_t* this_iter, void* in_ptr) {
|
||||
C->in_pl[(*this_iter + 1) % IOC_SIZE].ptr = in_ptr;
|
||||
#ifdef _OPENMP
|
||||
omp_unset_lock(&(C->in_pl[(*this_iter + 1) % IOC_SIZE].lock));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void * ioc_get_out(ioc_chain *C, size_t *this_iter) {
|
||||
#ifdef _OPENMP
|
||||
omp_set_lock(&(C->out_pl[(*this_iter) % IOC_SIZE].lock));
|
||||
#pragma omp flush
|
||||
#endif
|
||||
void *out_ptr = C->out_pl[*this_iter % IOC_SIZE].ptr;
|
||||
#ifdef _OPENMP
|
||||
omp_unset_lock(&(C->out_pl[(*this_iter) % IOC_SIZE].lock));
|
||||
#endif
|
||||
return out_ptr;
|
||||
}
|
||||
|
||||
|
||||
void ioc_set_next_out(ioc_chain *C, size_t *this_iter, void* out_ptr) {
|
||||
C->out_pl[(*this_iter + 1) % IOC_SIZE].ptr = out_ptr;
|
||||
#ifdef _OPENMP
|
||||
omp_unset_lock(&(C->out_pl[(*this_iter + 1) % IOC_SIZE].lock));
|
||||
// *in_pl[this_iter]* lock released at the end of the iteration to avoid being
|
||||
// overtaken by previous threads and having *out_pl[this_iter]* corrupted.
|
||||
// Especially worried about thread 0, iteration 0.
|
||||
omp_unset_lock(&(C->in_pl[(*this_iter) % IOC_SIZE].lock));
|
||||
#endif
|
||||
}
|
||||
|
||||
94
compression/bitshuffle/iochain.h
Normal file
94
compression/bitshuffle/iochain.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* IOchain - Distribute a chain of dependant IO events amoung threads.
|
||||
*
|
||||
* This file is part of Bitshuffle
|
||||
* Author: Kiyoshi Masui <kiyo@physics.ubc.ca>
|
||||
* Website: http://www.github.com/kiyo-masui/bitshuffle
|
||||
* Created: 2014
|
||||
*
|
||||
* See LICENSE file for details about copyright and rights to use.
|
||||
*
|
||||
*
|
||||
* Header File
|
||||
*
|
||||
* Similar in concept to a queue. Each task includes reading an input
|
||||
* and writing output, but the location of the input/output (the pointers)
|
||||
* depend on the previous item in the chain.
|
||||
*
|
||||
* This is designed for parallelizing blocked compression/decompression IO,
|
||||
* where the destination of a compressed block depends on the compressed size
|
||||
* of all previous blocks.
|
||||
*
|
||||
* Implemented with OpenMP locks.
|
||||
*
|
||||
*
|
||||
* Usage
|
||||
* -----
|
||||
* - Call `ioc_init` in serial block.
|
||||
* - Each thread should create a local variable *size_t this_iter* and
|
||||
* pass its address to all function calls. Its value will be set
|
||||
* inside the functions and is used to identify the thread.
|
||||
* - Each thread must call each of the `ioc_get*` and `ioc_set*` methods
|
||||
* exactly once per iteration, starting with `ioc_get_in` and ending
|
||||
* with `ioc_set_next_out`.
|
||||
* - The order (`ioc_get_in`, `ioc_set_next_in`, *work*, `ioc_get_out`,
|
||||
* `ioc_set_next_out`, *work*) is most efficient.
|
||||
* - Have each thread call `ioc_end_pop`.
|
||||
* - `ioc_get_in` is blocked until the previous entry's
|
||||
* `ioc_set_next_in` is called.
|
||||
* - `ioc_get_out` is blocked until the previous entry's
|
||||
* `ioc_set_next_out` is called.
|
||||
* - There are no blocks on the very first iteration.
|
||||
* - Call `ioc_destroy` in serial block.
|
||||
* - Safe for num_threads >= IOC_SIZE (but less efficient).
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef IOCHAIN_H
|
||||
#define IOCHAIN_H
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define IOC_SIZE 33
|
||||
|
||||
|
||||
typedef struct ioc_ptr_and_lock {
|
||||
#ifdef _OPENMP
|
||||
omp_lock_t lock;
|
||||
#endif
|
||||
void *ptr;
|
||||
} ptr_and_lock;
|
||||
|
||||
typedef struct ioc_const_ptr_and_lock {
|
||||
#ifdef _OPENMP
|
||||
omp_lock_t lock;
|
||||
#endif
|
||||
const void *ptr;
|
||||
} const_ptr_and_lock;
|
||||
|
||||
|
||||
typedef struct ioc_chain {
|
||||
#ifdef _OPENMP
|
||||
omp_lock_t next_lock;
|
||||
#endif
|
||||
size_t next;
|
||||
const_ptr_and_lock in_pl[IOC_SIZE];
|
||||
ptr_and_lock out_pl[IOC_SIZE];
|
||||
} ioc_chain;
|
||||
|
||||
|
||||
void ioc_init(ioc_chain *C, const void *in_ptr_0, void *out_ptr_0);
|
||||
void ioc_destroy(ioc_chain *C);
|
||||
const void * ioc_get_in(ioc_chain *C, size_t *this_iter);
|
||||
void ioc_set_next_in(ioc_chain *C, size_t* this_iter, void* in_ptr);
|
||||
void * ioc_get_out(ioc_chain *C, size_t *this_iter);
|
||||
void ioc_set_next_out(ioc_chain *C, size_t *this_iter, void* out_ptr);
|
||||
|
||||
#endif // IOCHAIN_H
|
||||
|
||||
24
compression/lz4/LICENSE
Normal file
24
compression/lz4/LICENSE
Normal file
@@ -0,0 +1,24 @@
|
||||
LZ4 Library
|
||||
Copyright (c) 2011-2016, Yann Collet
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
2722
compression/lz4/lz4.c
Normal file
2722
compression/lz4/lz4.c
Normal file
File diff suppressed because it is too large
Load Diff
842
compression/lz4/lz4.h
Normal file
842
compression/lz4/lz4.h
Normal file
@@ -0,0 +1,842 @@
|
||||
/*
|
||||
* LZ4 - Fast LZ compression algorithm
|
||||
* Header File
|
||||
* Copyright (C) 2011-2020, Yann Collet.
|
||||
|
||||
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
You can contact the author at :
|
||||
- LZ4 homepage : http://www.lz4.org
|
||||
- LZ4 source repository : https://github.com/lz4/lz4
|
||||
*/
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef LZ4_H_2983827168210
|
||||
#define LZ4_H_2983827168210
|
||||
|
||||
/* --- Dependency --- */
|
||||
#include <stddef.h> /* size_t */
|
||||
|
||||
|
||||
/**
|
||||
Introduction
|
||||
|
||||
LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core,
|
||||
scalable with multi-cores CPU. It features an extremely fast decoder, with speed in
|
||||
multiple GB/s per core, typically reaching RAM speed limits on multi-core systems.
|
||||
|
||||
The LZ4 compression library provides in-memory compression and decompression functions.
|
||||
It gives full buffer control to user.
|
||||
Compression can be done in:
|
||||
- a single step (described as Simple Functions)
|
||||
- a single step, reusing a context (described in Advanced Functions)
|
||||
- unbounded multiple steps (described as Streaming compression)
|
||||
|
||||
lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md).
|
||||
Decompressing such a compressed block requires additional metadata.
|
||||
Exact metadata depends on exact decompression function.
|
||||
For the typical case of LZ4_decompress_safe(),
|
||||
metadata includes block's compressed size, and maximum bound of decompressed size.
|
||||
Each application is free to encode and pass such metadata in whichever way it wants.
|
||||
|
||||
lz4.h only handle blocks, it can not generate Frames.
|
||||
|
||||
Blocks are different from Frames (doc/lz4_Frame_format.md).
|
||||
Frames bundle both blocks and metadata in a specified manner.
|
||||
Embedding metadata is required for compressed data to be self-contained and portable.
|
||||
Frame format is delivered through a companion API, declared in lz4frame.h.
|
||||
The `lz4` CLI can only manage frames.
|
||||
*/
|
||||
|
||||
/*^***************************************************************
|
||||
* Export parameters
|
||||
*****************************************************************/
|
||||
/*
|
||||
* LZ4_DLL_EXPORT :
|
||||
* Enable exporting of functions when building a Windows DLL
|
||||
* LZ4LIB_VISIBILITY :
|
||||
* Control library symbols visibility.
|
||||
*/
|
||||
#ifndef LZ4LIB_VISIBILITY
|
||||
# if defined(__GNUC__) && (__GNUC__ >= 4)
|
||||
# define LZ4LIB_VISIBILITY __attribute__ ((visibility ("default")))
|
||||
# else
|
||||
# define LZ4LIB_VISIBILITY
|
||||
# endif
|
||||
#endif
|
||||
#if defined(LZ4_DLL_EXPORT) && (LZ4_DLL_EXPORT==1)
|
||||
# define LZ4LIB_API __declspec(dllexport) LZ4LIB_VISIBILITY
|
||||
#elif defined(LZ4_DLL_IMPORT) && (LZ4_DLL_IMPORT==1)
|
||||
# define LZ4LIB_API __declspec(dllimport) LZ4LIB_VISIBILITY /* It isn't required but allows to generate better code, saving a function pointer load from the IAT and an indirect jump.*/
|
||||
#else
|
||||
# define LZ4LIB_API LZ4LIB_VISIBILITY
|
||||
#endif
|
||||
|
||||
/*! LZ4_FREESTANDING :
|
||||
* When this macro is set to 1, it enables "freestanding mode" that is
|
||||
* suitable for typical freestanding environment which doesn't support
|
||||
* standard C library.
|
||||
*
|
||||
* - LZ4_FREESTANDING is a compile-time switch.
|
||||
* - It requires the following macros to be defined:
|
||||
* LZ4_memcpy, LZ4_memmove, LZ4_memset.
|
||||
* - It only enables LZ4/HC functions which don't use heap.
|
||||
* All LZ4F_* functions are not supported.
|
||||
* - See tests/freestanding.c to check its basic setup.
|
||||
*/
|
||||
#if defined(LZ4_FREESTANDING) && (LZ4_FREESTANDING == 1)
|
||||
# define LZ4_HEAPMODE 0
|
||||
# define LZ4HC_HEAPMODE 0
|
||||
# define LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION 1
|
||||
# if !defined(LZ4_memcpy)
|
||||
# error "LZ4_FREESTANDING requires macro 'LZ4_memcpy'."
|
||||
# endif
|
||||
# if !defined(LZ4_memset)
|
||||
# error "LZ4_FREESTANDING requires macro 'LZ4_memset'."
|
||||
# endif
|
||||
# if !defined(LZ4_memmove)
|
||||
# error "LZ4_FREESTANDING requires macro 'LZ4_memmove'."
|
||||
# endif
|
||||
#elif ! defined(LZ4_FREESTANDING)
|
||||
# define LZ4_FREESTANDING 0
|
||||
#endif
|
||||
|
||||
|
||||
/*------ Version ------*/
|
||||
#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
|
||||
#define LZ4_VERSION_MINOR 9 /* for new (non-breaking) interface capabilities */
|
||||
#define LZ4_VERSION_RELEASE 4 /* for tweaks, bug-fixes, or development */
|
||||
|
||||
#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
|
||||
|
||||
#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
|
||||
#define LZ4_QUOTE(str) #str
|
||||
#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)
|
||||
#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) /* requires v1.7.3+ */
|
||||
|
||||
LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version; requires v1.3.0+ */
|
||||
LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version; requires v1.7.5+ */
|
||||
|
||||
|
||||
/*-************************************
|
||||
* Tuning parameter
|
||||
**************************************/
|
||||
#define LZ4_MEMORY_USAGE_MIN 10
|
||||
#define LZ4_MEMORY_USAGE_DEFAULT 14
|
||||
#define LZ4_MEMORY_USAGE_MAX 20
|
||||
|
||||
/*!
|
||||
* LZ4_MEMORY_USAGE :
|
||||
* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; )
|
||||
* Increasing memory usage improves compression ratio, at the cost of speed.
|
||||
* Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality.
|
||||
* Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
|
||||
*/
|
||||
#ifndef LZ4_MEMORY_USAGE
|
||||
# define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT
|
||||
#endif
|
||||
|
||||
#if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN)
|
||||
# error "LZ4_MEMORY_USAGE is too small !"
|
||||
#endif
|
||||
|
||||
#if (LZ4_MEMORY_USAGE > LZ4_MEMORY_USAGE_MAX)
|
||||
# error "LZ4_MEMORY_USAGE is too large !"
|
||||
#endif
|
||||
|
||||
/*-************************************
|
||||
* Simple Functions
|
||||
**************************************/
|
||||
/*! LZ4_compress_default() :
|
||||
* Compresses 'srcSize' bytes from buffer 'src'
|
||||
* into already allocated 'dst' buffer of size 'dstCapacity'.
|
||||
* Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize).
|
||||
* It also runs faster, so it's a recommended setting.
|
||||
* If the function cannot compress 'src' into a more limited 'dst' budget,
|
||||
* compression stops *immediately*, and the function result is zero.
|
||||
* In which case, 'dst' content is undefined (invalid).
|
||||
* srcSize : max supported value is LZ4_MAX_INPUT_SIZE.
|
||||
* dstCapacity : size of buffer 'dst' (which must be already allocated)
|
||||
* @return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity)
|
||||
* or 0 if compression fails
|
||||
* Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer).
|
||||
*/
|
||||
LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity);
|
||||
|
||||
/*! LZ4_decompress_safe() :
|
||||
* compressedSize : is the exact complete size of the compressed block.
|
||||
* dstCapacity : is the size of destination buffer (which must be already allocated), presumed an upper bound of decompressed size.
|
||||
* @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity)
|
||||
* If destination buffer is not large enough, decoding will stop and output an error code (negative value).
|
||||
* If the source stream is detected malformed, the function will stop decoding and return a negative result.
|
||||
* Note 1 : This function is protected against malicious data packets :
|
||||
* it will never writes outside 'dst' buffer, nor read outside 'source' buffer,
|
||||
* even if the compressed block is maliciously modified to order the decoder to do these actions.
|
||||
* In such case, the decoder stops immediately, and considers the compressed block malformed.
|
||||
* Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them.
|
||||
* The implementation is free to send / store / derive this information in whichever way is most beneficial.
|
||||
* If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead.
|
||||
*/
|
||||
LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity);
|
||||
|
||||
|
||||
/*-************************************
|
||||
* Advanced Functions
|
||||
**************************************/
|
||||
#define LZ4_MAX_INPUT_SIZE 0x7E000000 /* 2 113 929 216 bytes */
|
||||
#define LZ4_COMPRESSBOUND(isize) ((unsigned)(isize) > (unsigned)LZ4_MAX_INPUT_SIZE ? 0 : (isize) + ((isize)/255) + 16)
|
||||
|
||||
/*! LZ4_compressBound() :
|
||||
Provides the maximum size that LZ4 compression may output in a "worst case" scenario (input data not compressible)
|
||||
This function is primarily useful for memory allocation purposes (destination buffer size).
|
||||
Macro LZ4_COMPRESSBOUND() is also provided for compilation-time evaluation (stack memory allocation for example).
|
||||
Note that LZ4_compress_default() compresses faster when dstCapacity is >= LZ4_compressBound(srcSize)
|
||||
inputSize : max supported value is LZ4_MAX_INPUT_SIZE
|
||||
return : maximum output size in a "worst case" scenario
|
||||
or 0, if input size is incorrect (too large or negative)
|
||||
*/
|
||||
LZ4LIB_API int LZ4_compressBound(int inputSize);
|
||||
|
||||
/*! LZ4_compress_fast() :
|
||||
Same as LZ4_compress_default(), but allows selection of "acceleration" factor.
|
||||
The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
|
||||
It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
|
||||
An acceleration value of "1" is the same as regular LZ4_compress_default()
|
||||
Values <= 0 will be replaced by LZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c).
|
||||
Values > LZ4_ACCELERATION_MAX will be replaced by LZ4_ACCELERATION_MAX (currently == 65537, see lz4.c).
|
||||
*/
|
||||
LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
|
||||
|
||||
|
||||
/*! LZ4_compress_fast_extState() :
|
||||
* Same as LZ4_compress_fast(), using an externally allocated memory space for its state.
|
||||
* Use LZ4_sizeofState() to know how much memory must be allocated,
|
||||
* and allocate it on 8-bytes boundaries (using `malloc()` typically).
|
||||
* Then, provide this buffer as `void* state` to compression function.
|
||||
*/
|
||||
LZ4LIB_API int LZ4_sizeofState(void);
|
||||
LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
|
||||
|
||||
|
||||
/*! LZ4_compress_destSize() :
|
||||
* Reverse the logic : compresses as much data as possible from 'src' buffer
|
||||
* into already allocated buffer 'dst', of size >= 'targetDestSize'.
|
||||
* This function either compresses the entire 'src' content into 'dst' if it's large enough,
|
||||
* or fill 'dst' buffer completely with as much data as possible from 'src'.
|
||||
* note: acceleration parameter is fixed to "default".
|
||||
*
|
||||
* *srcSizePtr : will be modified to indicate how many bytes where read from 'src' to fill 'dst'.
|
||||
* New value is necessarily <= input value.
|
||||
* @return : Nb bytes written into 'dst' (necessarily <= targetDestSize)
|
||||
* or 0 if compression fails.
|
||||
*
|
||||
* Note : from v1.8.2 to v1.9.1, this function had a bug (fixed un v1.9.2+):
|
||||
* the produced compressed content could, in specific circumstances,
|
||||
* require to be decompressed into a destination buffer larger
|
||||
* by at least 1 byte than the content to decompress.
|
||||
* If an application uses `LZ4_compress_destSize()`,
|
||||
* it's highly recommended to update liblz4 to v1.9.2 or better.
|
||||
* If this can't be done or ensured,
|
||||
* the receiving decompression function should provide
|
||||
* a dstCapacity which is > decompressedSize, by at least 1 byte.
|
||||
* See https://github.com/lz4/lz4/issues/859 for details
|
||||
*/
|
||||
LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize);
|
||||
|
||||
|
||||
/*! LZ4_decompress_safe_partial() :
|
||||
* Decompress an LZ4 compressed block, of size 'srcSize' at position 'src',
|
||||
* into destination buffer 'dst' of size 'dstCapacity'.
|
||||
* Up to 'targetOutputSize' bytes will be decoded.
|
||||
* The function stops decoding on reaching this objective.
|
||||
* This can be useful to boost performance
|
||||
* whenever only the beginning of a block is required.
|
||||
*
|
||||
* @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize)
|
||||
* If source stream is detected malformed, function returns a negative result.
|
||||
*
|
||||
* Note 1 : @return can be < targetOutputSize, if compressed block contains less data.
|
||||
*
|
||||
* Note 2 : targetOutputSize must be <= dstCapacity
|
||||
*
|
||||
* Note 3 : this function effectively stops decoding on reaching targetOutputSize,
|
||||
* so dstCapacity is kind of redundant.
|
||||
* This is because in older versions of this function,
|
||||
* decoding operation would still write complete sequences.
|
||||
* Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize,
|
||||
* it could write more bytes, though only up to dstCapacity.
|
||||
* Some "margin" used to be required for this operation to work properly.
|
||||
* Thankfully, this is no longer necessary.
|
||||
* The function nonetheless keeps the same signature, in an effort to preserve API compatibility.
|
||||
*
|
||||
* Note 4 : If srcSize is the exact size of the block,
|
||||
* then targetOutputSize can be any value,
|
||||
* including larger than the block's decompressed size.
|
||||
* The function will, at most, generate block's decompressed size.
|
||||
*
|
||||
* Note 5 : If srcSize is _larger_ than block's compressed size,
|
||||
* then targetOutputSize **MUST** be <= block's decompressed size.
|
||||
* Otherwise, *silent corruption will occur*.
|
||||
*/
|
||||
LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity);
|
||||
|
||||
|
||||
/*-*********************************************
|
||||
* Streaming Compression Functions
|
||||
***********************************************/
|
||||
typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */
|
||||
|
||||
/**
|
||||
Note about RC_INVOKED
|
||||
|
||||
- RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio).
|
||||
https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros
|
||||
|
||||
- Since rc.exe is a legacy compiler, it truncates long symbol (> 30 chars)
|
||||
and reports warning "RC4011: identifier truncated".
|
||||
|
||||
- To eliminate the warning, we surround long preprocessor symbol with
|
||||
"#if !defined(RC_INVOKED) ... #endif" block that means
|
||||
"skip this block when rc.exe is trying to read it".
|
||||
*/
|
||||
#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */
|
||||
#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
|
||||
LZ4LIB_API LZ4_stream_t* LZ4_createStream(void);
|
||||
LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr);
|
||||
#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */
|
||||
#endif
|
||||
|
||||
/*! LZ4_resetStream_fast() : v1.9.0+
|
||||
* Use this to prepare an LZ4_stream_t for a new chain of dependent blocks
|
||||
* (e.g., LZ4_compress_fast_continue()).
|
||||
*
|
||||
* An LZ4_stream_t must be initialized once before usage.
|
||||
* This is automatically done when created by LZ4_createStream().
|
||||
* However, should the LZ4_stream_t be simply declared on stack (for example),
|
||||
* it's necessary to initialize it first, using LZ4_initStream().
|
||||
*
|
||||
* After init, start any new stream with LZ4_resetStream_fast().
|
||||
* A same LZ4_stream_t can be re-used multiple times consecutively
|
||||
* and compress multiple streams,
|
||||
* provided that it starts each new stream with LZ4_resetStream_fast().
|
||||
*
|
||||
* LZ4_resetStream_fast() is much faster than LZ4_initStream(),
|
||||
* but is not compatible with memory regions containing garbage data.
|
||||
*
|
||||
* Note: it's only useful to call LZ4_resetStream_fast()
|
||||
* in the context of streaming compression.
|
||||
* The *extState* functions perform their own resets.
|
||||
* Invoking LZ4_resetStream_fast() before is redundant, and even counterproductive.
|
||||
*/
|
||||
LZ4LIB_API void LZ4_resetStream_fast (LZ4_stream_t* streamPtr);
|
||||
|
||||
/*! LZ4_loadDict() :
|
||||
* Use this function to reference a static dictionary into LZ4_stream_t.
|
||||
* The dictionary must remain available during compression.
|
||||
* LZ4_loadDict() triggers a reset, so any previous data will be forgotten.
|
||||
* The same dictionary will have to be loaded on decompression side for successful decoding.
|
||||
* Dictionary are useful for better compression of small data (KB range).
|
||||
* While LZ4 accept any input as dictionary,
|
||||
* results are generally better when using Zstandard's Dictionary Builder.
|
||||
* Loading a size of 0 is allowed, and is the same as reset.
|
||||
* @return : loaded dictionary size, in bytes (necessarily <= 64 KB)
|
||||
*/
|
||||
LZ4LIB_API int LZ4_loadDict (LZ4_stream_t* streamPtr, const char* dictionary, int dictSize);
|
||||
|
||||
/*! LZ4_compress_fast_continue() :
|
||||
* Compress 'src' content using data from previously compressed blocks, for better compression ratio.
|
||||
* 'dst' buffer must be already allocated.
|
||||
* If dstCapacity >= LZ4_compressBound(srcSize), compression is guaranteed to succeed, and runs faster.
|
||||
*
|
||||
* @return : size of compressed block
|
||||
* or 0 if there is an error (typically, cannot fit into 'dst').
|
||||
*
|
||||
* Note 1 : Each invocation to LZ4_compress_fast_continue() generates a new block.
|
||||
* Each block has precise boundaries.
|
||||
* Each block must be decompressed separately, calling LZ4_decompress_*() with relevant metadata.
|
||||
* It's not possible to append blocks together and expect a single invocation of LZ4_decompress_*() to decompress them together.
|
||||
*
|
||||
* Note 2 : The previous 64KB of source data is __assumed__ to remain present, unmodified, at same address in memory !
|
||||
*
|
||||
* Note 3 : When input is structured as a double-buffer, each buffer can have any size, including < 64 KB.
|
||||
* Make sure that buffers are separated, by at least one byte.
|
||||
* This construction ensures that each block only depends on previous block.
|
||||
*
|
||||
* Note 4 : If input buffer is a ring-buffer, it can have any size, including < 64 KB.
|
||||
*
|
||||
* Note 5 : After an error, the stream status is undefined (invalid), it can only be reset or freed.
|
||||
*/
|
||||
LZ4LIB_API int LZ4_compress_fast_continue (LZ4_stream_t* streamPtr, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
|
||||
|
||||
/*! LZ4_saveDict() :
|
||||
* If last 64KB data cannot be guaranteed to remain available at its current memory location,
|
||||
* save it into a safer place (char* safeBuffer).
|
||||
* This is schematically equivalent to a memcpy() followed by LZ4_loadDict(),
|
||||
* but is much faster, because LZ4_saveDict() doesn't need to rebuild tables.
|
||||
* @return : saved dictionary size in bytes (necessarily <= maxDictSize), or 0 if error.
|
||||
*/
|
||||
LZ4LIB_API int LZ4_saveDict (LZ4_stream_t* streamPtr, char* safeBuffer, int maxDictSize);
|
||||
|
||||
|
||||
/*-**********************************************
|
||||
* Streaming Decompression Functions
|
||||
* Bufferless synchronous API
|
||||
************************************************/
|
||||
typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* tracking context */
|
||||
|
||||
/*! LZ4_createStreamDecode() and LZ4_freeStreamDecode() :
|
||||
* creation / destruction of streaming decompression tracking context.
|
||||
* A tracking context can be re-used multiple times.
|
||||
*/
|
||||
#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */
|
||||
#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
|
||||
LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void);
|
||||
LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
|
||||
#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */
|
||||
#endif
|
||||
|
||||
/*! LZ4_setStreamDecode() :
|
||||
* An LZ4_streamDecode_t context can be allocated once and re-used multiple times.
|
||||
* Use this function to start decompression of a new stream of blocks.
|
||||
* A dictionary can optionally be set. Use NULL or size 0 for a reset order.
|
||||
* Dictionary is presumed stable : it must remain accessible and unmodified during next decompression.
|
||||
* @return : 1 if OK, 0 if error
|
||||
*/
|
||||
LZ4LIB_API int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize);
|
||||
|
||||
/*! LZ4_decoderRingBufferSize() : v1.8.2+
|
||||
* Note : in a ring buffer scenario (optional),
|
||||
* blocks are presumed decompressed next to each other
|
||||
* up to the moment there is not enough remaining space for next block (remainingSize < maxBlockSize),
|
||||
* at which stage it resumes from beginning of ring buffer.
|
||||
* When setting such a ring buffer for streaming decompression,
|
||||
* provides the minimum size of this ring buffer
|
||||
* to be compatible with any source respecting maxBlockSize condition.
|
||||
* @return : minimum ring buffer size,
|
||||
* or 0 if there is an error (invalid maxBlockSize).
|
||||
*/
|
||||
LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize);
|
||||
#define LZ4_DECODER_RING_BUFFER_SIZE(maxBlockSize) (65536 + 14 + (maxBlockSize)) /* for static allocation; maxBlockSize presumed valid */
|
||||
|
||||
/*! LZ4_decompress_*_continue() :
|
||||
* These decoding functions allow decompression of consecutive blocks in "streaming" mode.
|
||||
* A block is an unsplittable entity, it must be presented entirely to a decompression function.
|
||||
* Decompression functions only accepts one block at a time.
|
||||
* The last 64KB of previously decoded data *must* remain available and unmodified at the memory position where they were decoded.
|
||||
* If less than 64KB of data has been decoded, all the data must be present.
|
||||
*
|
||||
* Special : if decompression side sets a ring buffer, it must respect one of the following conditions :
|
||||
* - Decompression buffer size is _at least_ LZ4_decoderRingBufferSize(maxBlockSize).
|
||||
* maxBlockSize is the maximum size of any single block. It can have any value > 16 bytes.
|
||||
* In which case, encoding and decoding buffers do not need to be synchronized.
|
||||
* Actually, data can be produced by any source compliant with LZ4 format specification, and respecting maxBlockSize.
|
||||
* - Synchronized mode :
|
||||
* Decompression buffer size is _exactly_ the same as compression buffer size,
|
||||
* and follows exactly same update rule (block boundaries at same positions),
|
||||
* and decoding function is provided with exact decompressed size of each block (exception for last block of the stream),
|
||||
* _then_ decoding & encoding ring buffer can have any size, including small ones ( < 64 KB).
|
||||
* - Decompression buffer is larger than encoding buffer, by a minimum of maxBlockSize more bytes.
|
||||
* In which case, encoding and decoding buffers do not need to be synchronized,
|
||||
* and encoding ring buffer can have any size, including small ones ( < 64 KB).
|
||||
*
|
||||
* Whenever these conditions are not possible,
|
||||
* save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression,
|
||||
* then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block.
|
||||
*/
|
||||
LZ4LIB_API int
|
||||
LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode,
|
||||
const char* src, char* dst,
|
||||
int srcSize, int dstCapacity);
|
||||
|
||||
|
||||
/*! LZ4_decompress_*_usingDict() :
|
||||
* These decoding functions work the same as
|
||||
* a combination of LZ4_setStreamDecode() followed by LZ4_decompress_*_continue()
|
||||
* They are stand-alone, and don't need an LZ4_streamDecode_t structure.
|
||||
* Dictionary is presumed stable : it must remain accessible and unmodified during decompression.
|
||||
* Performance tip : Decompression speed can be substantially increased
|
||||
* when dst == dictStart + dictSize.
|
||||
*/
|
||||
LZ4LIB_API int
|
||||
LZ4_decompress_safe_usingDict(const char* src, char* dst,
|
||||
int srcSize, int dstCapacity,
|
||||
const char* dictStart, int dictSize);
|
||||
|
||||
LZ4LIB_API int
|
||||
LZ4_decompress_safe_partial_usingDict(const char* src, char* dst,
|
||||
int compressedSize,
|
||||
int targetOutputSize, int maxOutputSize,
|
||||
const char* dictStart, int dictSize);
|
||||
|
||||
#endif /* LZ4_H_2983827168210 */
|
||||
|
||||
|
||||
/*^*************************************
|
||||
* !!!!!! STATIC LINKING ONLY !!!!!!
|
||||
***************************************/
|
||||
|
||||
/*-****************************************************************************
|
||||
* Experimental section
|
||||
*
|
||||
* Symbols declared in this section must be considered unstable. Their
|
||||
* signatures or semantics may change, or they may be removed altogether in the
|
||||
* future. They are therefore only safe to depend on when the caller is
|
||||
* statically linked against the library.
|
||||
*
|
||||
* To protect against unsafe usage, not only are the declarations guarded,
|
||||
* the definitions are hidden by default
|
||||
* when building LZ4 as a shared/dynamic library.
|
||||
*
|
||||
* In order to access these declarations,
|
||||
* define LZ4_STATIC_LINKING_ONLY in your application
|
||||
* before including LZ4's headers.
|
||||
*
|
||||
* In order to make their implementations accessible dynamically, you must
|
||||
* define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library.
|
||||
******************************************************************************/
|
||||
|
||||
#ifdef LZ4_STATIC_LINKING_ONLY
|
||||
|
||||
#ifndef LZ4_STATIC_3504398509
|
||||
#define LZ4_STATIC_3504398509
|
||||
|
||||
#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS
|
||||
#define LZ4LIB_STATIC_API LZ4LIB_API
|
||||
#else
|
||||
#define LZ4LIB_STATIC_API
|
||||
#endif
|
||||
|
||||
|
||||
/*! LZ4_compress_fast_extState_fastReset() :
|
||||
* A variant of LZ4_compress_fast_extState().
|
||||
*
|
||||
* Using this variant avoids an expensive initialization step.
|
||||
* It is only safe to call if the state buffer is known to be correctly initialized already
|
||||
* (see above comment on LZ4_resetStream_fast() for a definition of "correctly initialized").
|
||||
* From a high level, the difference is that
|
||||
* this function initializes the provided state with a call to something like LZ4_resetStream_fast()
|
||||
* while LZ4_compress_fast_extState() starts with a call to LZ4_resetStream().
|
||||
*/
|
||||
LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
|
||||
|
||||
/*! LZ4_attach_dictionary() :
|
||||
* This is an experimental API that allows
|
||||
* efficient use of a static dictionary many times.
|
||||
*
|
||||
* Rather than re-loading the dictionary buffer into a working context before
|
||||
* each compression, or copying a pre-loaded dictionary's LZ4_stream_t into a
|
||||
* working LZ4_stream_t, this function introduces a no-copy setup mechanism,
|
||||
* in which the working stream references the dictionary stream in-place.
|
||||
*
|
||||
* Several assumptions are made about the state of the dictionary stream.
|
||||
* Currently, only streams which have been prepared by LZ4_loadDict() should
|
||||
* be expected to work.
|
||||
*
|
||||
* Alternatively, the provided dictionaryStream may be NULL,
|
||||
* in which case any existing dictionary stream is unset.
|
||||
*
|
||||
* If a dictionary is provided, it replaces any pre-existing stream history.
|
||||
* The dictionary contents are the only history that can be referenced and
|
||||
* logically immediately precede the data compressed in the first subsequent
|
||||
* compression call.
|
||||
*
|
||||
* The dictionary will only remain attached to the working stream through the
|
||||
* first compression call, at the end of which it is cleared. The dictionary
|
||||
* stream (and source buffer) must remain in-place / accessible / unchanged
|
||||
* through the completion of the first compression call on the stream.
|
||||
*/
|
||||
LZ4LIB_STATIC_API void
|
||||
LZ4_attach_dictionary(LZ4_stream_t* workingStream,
|
||||
const LZ4_stream_t* dictionaryStream);
|
||||
|
||||
|
||||
/*! In-place compression and decompression
|
||||
*
|
||||
* It's possible to have input and output sharing the same buffer,
|
||||
* for highly constrained memory environments.
|
||||
* In both cases, it requires input to lay at the end of the buffer,
|
||||
* and decompression to start at beginning of the buffer.
|
||||
* Buffer size must feature some margin, hence be larger than final size.
|
||||
*
|
||||
* |<------------------------buffer--------------------------------->|
|
||||
* |<-----------compressed data--------->|
|
||||
* |<-----------decompressed size------------------>|
|
||||
* |<----margin---->|
|
||||
*
|
||||
* This technique is more useful for decompression,
|
||||
* since decompressed size is typically larger,
|
||||
* and margin is short.
|
||||
*
|
||||
* In-place decompression will work inside any buffer
|
||||
* which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize).
|
||||
* This presumes that decompressedSize > compressedSize.
|
||||
* Otherwise, it means compression actually expanded data,
|
||||
* and it would be more efficient to store such data with a flag indicating it's not compressed.
|
||||
* This can happen when data is not compressible (already compressed, or encrypted).
|
||||
*
|
||||
* For in-place compression, margin is larger, as it must be able to cope with both
|
||||
* history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX,
|
||||
* and data expansion, which can happen when input is not compressible.
|
||||
* As a consequence, buffer size requirements are much higher,
|
||||
* and memory savings offered by in-place compression are more limited.
|
||||
*
|
||||
* There are ways to limit this cost for compression :
|
||||
* - Reduce history size, by modifying LZ4_DISTANCE_MAX.
|
||||
* Note that it is a compile-time constant, so all compressions will apply this limit.
|
||||
* Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX,
|
||||
* so it's a reasonable trick when inputs are known to be small.
|
||||
* - Require the compressor to deliver a "maximum compressed size".
|
||||
* This is the `dstCapacity` parameter in `LZ4_compress*()`.
|
||||
* When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail,
|
||||
* in which case, the return code will be 0 (zero).
|
||||
* The caller must be ready for these cases to happen,
|
||||
* and typically design a backup scheme to send data uncompressed.
|
||||
* The combination of both techniques can significantly reduce
|
||||
* the amount of margin required for in-place compression.
|
||||
*
|
||||
* In-place compression can work in any buffer
|
||||
* which size is >= (maxCompressedSize)
|
||||
* with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success.
|
||||
* LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX,
|
||||
* so it's possible to reduce memory requirements by playing with them.
|
||||
*/
|
||||
|
||||
#define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize) (((compressedSize) >> 8) + 32)
|
||||
#define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize) ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize)) /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */
|
||||
|
||||
#ifndef LZ4_DISTANCE_MAX /* history window size; can be user-defined at compile time */
|
||||
# define LZ4_DISTANCE_MAX 65535 /* set to maximum value by default */
|
||||
#endif
|
||||
|
||||
#define LZ4_COMPRESS_INPLACE_MARGIN (LZ4_DISTANCE_MAX + 32) /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */
|
||||
#define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize) ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN) /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */
|
||||
|
||||
#endif /* LZ4_STATIC_3504398509 */
|
||||
#endif /* LZ4_STATIC_LINKING_ONLY */
|
||||
|
||||
|
||||
|
||||
#ifndef LZ4_H_98237428734687
|
||||
#define LZ4_H_98237428734687
|
||||
|
||||
/*-************************************************************
|
||||
* Private Definitions
|
||||
**************************************************************
|
||||
* Do not use these definitions directly.
|
||||
* They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`.
|
||||
* Accessing members will expose user code to API and/or ABI break in future versions of the library.
|
||||
**************************************************************/
|
||||
#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2)
|
||||
#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
|
||||
#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */
|
||||
|
||||
#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
|
||||
# include <stdint.h>
|
||||
typedef int8_t LZ4_i8;
|
||||
typedef uint8_t LZ4_byte;
|
||||
typedef uint16_t LZ4_u16;
|
||||
typedef uint32_t LZ4_u32;
|
||||
#else
|
||||
typedef signed char LZ4_i8;
|
||||
typedef unsigned char LZ4_byte;
|
||||
typedef unsigned short LZ4_u16;
|
||||
typedef unsigned int LZ4_u32;
|
||||
#endif
|
||||
|
||||
/*! LZ4_stream_t :
|
||||
* Never ever use below internal definitions directly !
|
||||
* These definitions are not API/ABI safe, and may change in future versions.
|
||||
* If you need static allocation, declare or allocate an LZ4_stream_t object.
|
||||
**/
|
||||
|
||||
typedef struct LZ4_stream_t_internal LZ4_stream_t_internal;
|
||||
struct LZ4_stream_t_internal {
|
||||
LZ4_u32 hashTable[LZ4_HASH_SIZE_U32];
|
||||
const LZ4_byte* dictionary;
|
||||
const LZ4_stream_t_internal* dictCtx;
|
||||
LZ4_u32 currentOffset;
|
||||
LZ4_u32 tableType;
|
||||
LZ4_u32 dictSize;
|
||||
/* Implicit padding to ensure structure is aligned */
|
||||
};
|
||||
|
||||
#define LZ4_STREAM_MINSIZE ((1UL << LZ4_MEMORY_USAGE) + 32) /* static size, for inter-version compatibility */
|
||||
union LZ4_stream_u {
|
||||
char minStateSize[LZ4_STREAM_MINSIZE];
|
||||
LZ4_stream_t_internal internal_donotuse;
|
||||
}; /* previously typedef'd to LZ4_stream_t */
|
||||
|
||||
|
||||
/*! LZ4_initStream() : v1.9.0+
|
||||
* An LZ4_stream_t structure must be initialized at least once.
|
||||
* This is automatically done when invoking LZ4_createStream(),
|
||||
* but it's not when the structure is simply declared on stack (for example).
|
||||
*
|
||||
* Use LZ4_initStream() to properly initialize a newly declared LZ4_stream_t.
|
||||
* It can also initialize any arbitrary buffer of sufficient size,
|
||||
* and will @return a pointer of proper type upon initialization.
|
||||
*
|
||||
* Note : initialization fails if size and alignment conditions are not respected.
|
||||
* In which case, the function will @return NULL.
|
||||
* Note2: An LZ4_stream_t structure guarantees correct alignment and size.
|
||||
* Note3: Before v1.9.0, use LZ4_resetStream() instead
|
||||
**/
|
||||
LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size);
|
||||
|
||||
|
||||
/*! LZ4_streamDecode_t :
|
||||
* Never ever use below internal definitions directly !
|
||||
* These definitions are not API/ABI safe, and may change in future versions.
|
||||
* If you need static allocation, declare or allocate an LZ4_streamDecode_t object.
|
||||
**/
|
||||
typedef struct {
|
||||
const LZ4_byte* externalDict;
|
||||
const LZ4_byte* prefixEnd;
|
||||
size_t extDictSize;
|
||||
size_t prefixSize;
|
||||
} LZ4_streamDecode_t_internal;
|
||||
|
||||
#define LZ4_STREAMDECODE_MINSIZE 32
|
||||
union LZ4_streamDecode_u {
|
||||
char minStateSize[LZ4_STREAMDECODE_MINSIZE];
|
||||
LZ4_streamDecode_t_internal internal_donotuse;
|
||||
} ; /* previously typedef'd to LZ4_streamDecode_t */
|
||||
|
||||
|
||||
|
||||
/*-************************************
|
||||
* Obsolete Functions
|
||||
**************************************/
|
||||
|
||||
/*! Deprecation warnings
|
||||
*
|
||||
* Deprecated functions make the compiler generate a warning when invoked.
|
||||
* This is meant to invite users to update their source code.
|
||||
* Should deprecation warnings be a problem, it is generally possible to disable them,
|
||||
* typically with -Wno-deprecated-declarations for gcc
|
||||
* or _CRT_SECURE_NO_WARNINGS in Visual.
|
||||
*
|
||||
* Another method is to define LZ4_DISABLE_DEPRECATE_WARNINGS
|
||||
* before including the header file.
|
||||
*/
|
||||
#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS
|
||||
# define LZ4_DEPRECATED(message) /* disable deprecation warnings */
|
||||
#else
|
||||
# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
|
||||
# define LZ4_DEPRECATED(message) [[deprecated(message)]]
|
||||
# elif defined(_MSC_VER)
|
||||
# define LZ4_DEPRECATED(message) __declspec(deprecated(message))
|
||||
# elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45))
|
||||
# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
|
||||
# elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31)
|
||||
# define LZ4_DEPRECATED(message) __attribute__((deprecated))
|
||||
# else
|
||||
# pragma message("WARNING: LZ4_DEPRECATED needs custom implementation for this compiler")
|
||||
# define LZ4_DEPRECATED(message) /* disabled */
|
||||
# endif
|
||||
#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */
|
||||
|
||||
/*! Obsolete compression functions (since v1.7.3) */
|
||||
LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* src, char* dest, int srcSize);
|
||||
LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize);
|
||||
LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize);
|
||||
LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
|
||||
LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);
|
||||
LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
|
||||
|
||||
/*! Obsolete decompression functions (since v1.8.0) */
|
||||
LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize);
|
||||
LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize);
|
||||
|
||||
/* Obsolete streaming functions (since v1.7.0)
|
||||
* degraded functionality; do not use!
|
||||
*
|
||||
* In order to perform streaming compression, these functions depended on data
|
||||
* that is no longer tracked in the state. They have been preserved as well as
|
||||
* possible: using them will still produce a correct output. However, they don't
|
||||
* actually retain any history between compression calls. The compression ratio
|
||||
* achieved will therefore be no better than compressing each chunk
|
||||
* independently.
|
||||
*/
|
||||
LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API void* LZ4_create (char* inputBuffer);
|
||||
LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStreamState(void);
|
||||
LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer);
|
||||
LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state);
|
||||
|
||||
/*! Obsolete streaming decoding functions (since v1.7.0) */
|
||||
LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);
|
||||
LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);
|
||||
|
||||
/*! Obsolete LZ4_decompress_fast variants (since v1.9.0) :
|
||||
* These functions used to be faster than LZ4_decompress_safe(),
|
||||
* but this is no longer the case. They are now slower.
|
||||
* This is because LZ4_decompress_fast() doesn't know the input size,
|
||||
* and therefore must progress more cautiously into the input buffer to not read beyond the end of block.
|
||||
* On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability.
|
||||
* As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated.
|
||||
*
|
||||
* The last remaining LZ4_decompress_fast() specificity is that
|
||||
* it can decompress a block without knowing its compressed size.
|
||||
* Such functionality can be achieved in a more secure manner
|
||||
* by employing LZ4_decompress_safe_partial().
|
||||
*
|
||||
* Parameters:
|
||||
* originalSize : is the uncompressed size to regenerate.
|
||||
* `dst` must be already allocated, its size must be >= 'originalSize' bytes.
|
||||
* @return : number of bytes read from source buffer (== compressed size).
|
||||
* The function expects to finish at block's end exactly.
|
||||
* If the source stream is detected malformed, the function stops decoding and returns a negative result.
|
||||
* note : LZ4_decompress_fast*() requires originalSize. Thanks to this information, it never writes past the output buffer.
|
||||
* However, since it doesn't know its 'src' size, it may read an unknown amount of input, past input buffer bounds.
|
||||
* Also, since match offsets are not validated, match reads from 'src' may underflow too.
|
||||
* These issues never happen if input (compressed) data is correct.
|
||||
* But they may happen if input data is invalid (error or intentional tampering).
|
||||
* As a consequence, use these functions in trusted environments with trusted data **only**.
|
||||
*/
|
||||
LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead")
|
||||
LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize);
|
||||
LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead")
|
||||
LZ4LIB_API int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int originalSize);
|
||||
LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_usingDict() instead")
|
||||
LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int originalSize, const char* dictStart, int dictSize);
|
||||
|
||||
/*! LZ4_resetStream() :
|
||||
* An LZ4_stream_t structure must be initialized at least once.
|
||||
* This is done with LZ4_initStream(), or LZ4_resetStream().
|
||||
* Consider switching to LZ4_initStream(),
|
||||
* invoking LZ4_resetStream() will trigger deprecation warnings in the future.
|
||||
*/
|
||||
LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr);
|
||||
|
||||
|
||||
#endif /* LZ4_H_98237428734687 */
|
||||
|
||||
|
||||
#if defined (__cplusplus)
|
||||
}
|
||||
#endif
|
||||
1
compression/zstd
Submodule
1
compression/zstd
Submodule
Submodule compression/zstd added at 19105bf710
8
detector_control/CMakeLists.txt
Normal file
8
detector_control/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
ADD_SUBDIRECTORY(slsDetectorPackage)
|
||||
|
||||
ADD_EXECUTABLE(jfjoch_detector jfjoch_detector.cpp JFJochDetector.cpp JFJochDetector.h DetectorWrapper.cpp DetectorWrapper.h)
|
||||
|
||||
TARGET_LINK_LIBRARIES(jfjoch_detector CommonFunctions slsSupportShared slsDetectorShared)
|
||||
|
||||
INSTALL(TARGETS sls_detector_put sls_detector_get jfjoch_detector RUNTIME)
|
||||
SET_PROPERTY(TARGET jfjoch_detector PROPERTY INTERPROCEDURAL_OPTIMIZATION True)
|
||||
201
detector_control/DetectorWrapper.cpp
Normal file
201
detector_control/DetectorWrapper.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "DetectorWrapper.h"
|
||||
#include "../common/JFJochException.h"
|
||||
#include "../common/Definitions.h"
|
||||
|
||||
void DetectorWrapper::Configure(const JFJochProtoBuf::DetectorConfig &request) {
|
||||
logger.Info("Configure");
|
||||
if (det.size() != request.modules_size()) {
|
||||
logger.Error("Discrepancy in module number between DAQ and detector");
|
||||
throw JFJochException(JFJochExceptionCategory::Detector,
|
||||
"Discrepancy in module number between DAQ and detector");
|
||||
}
|
||||
try {
|
||||
InternalStop();
|
||||
|
||||
det.setNumberofUDPInterfaces(2);
|
||||
|
||||
for (int i = 0; i < request.modules_size(); i++) {
|
||||
auto &cfg = request.modules(i);
|
||||
|
||||
det.setSourceUDPIP(sls::IpAddr(cfg.ipv4_src_addr_1()), {i});
|
||||
det.setSourceUDPIP2(sls::IpAddr(cfg.ipv4_src_addr_2()), {i});
|
||||
det.setSourceUDPMAC(sls::MacAddr(BASE_DETECTOR_MAC + i * 2), {i});
|
||||
det.setSourceUDPMAC2(sls::MacAddr(BASE_DETECTOR_MAC + i * 2 + 1), {i});
|
||||
|
||||
det.setDestinationUDPPort (cfg.udp_dest_port_1(), i);
|
||||
det.setDestinationUDPPort2(cfg.udp_dest_port_2(), i);
|
||||
det.setDestinationUDPIP( sls::IpAddr(cfg.ipv4_dest_addr_1()), {i});
|
||||
det.setDestinationUDPIP2( sls::IpAddr(cfg.ipv4_dest_addr_2()), {i});
|
||||
det.setDestinationUDPMAC( sls::MacAddr(cfg.mac_addr_dest_1()), {i});
|
||||
det.setDestinationUDPMAC2(sls::MacAddr(cfg.mac_addr_dest_2()), {i});
|
||||
}
|
||||
|
||||
det.setTemperatureControl(true);
|
||||
det.setThresholdTemperature(55);
|
||||
|
||||
det.clearBit(0x4f, 15, {0});
|
||||
det.clearBit(0x4e, 4);
|
||||
|
||||
det.setTimingMode(slsDetectorDefs::timingMode::TRIGGER_EXPOSURE);
|
||||
det.setAutoComparatorDisable(true);
|
||||
if (!det.getPowerChip().squash(false)) {
|
||||
det.setPowerChip(true);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
}
|
||||
|
||||
det.setHighVoltage(HIGH_VOLTAGE);
|
||||
} catch (const std::exception &e) {
|
||||
logger.ErrorException(e);
|
||||
throw JFJochException(JFJochExceptionCategory::Detector, e.what());
|
||||
}
|
||||
logger.Info(" ... done");
|
||||
}
|
||||
|
||||
void DetectorWrapper::Start(const JFJochProtoBuf::DetectorInput &request) {
|
||||
logger.Info("Start");
|
||||
|
||||
if (det.size() != request.modules_num())
|
||||
throw JFJochException(JFJochExceptionCategory::Detector,
|
||||
"Discrepancy in module number between DAQ and detector");
|
||||
|
||||
try {
|
||||
InternalStop();
|
||||
switch (request.mode()) {
|
||||
case JFJochProtoBuf::PEDESTAL_G1:
|
||||
det.setGainMode(slsDetectorDefs::gainMode::FORCE_SWITCH_G1);
|
||||
break;
|
||||
case JFJochProtoBuf::PEDESTAL_G2:
|
||||
det.setGainMode(slsDetectorDefs::gainMode::FORCE_SWITCH_G2);
|
||||
break;
|
||||
default:
|
||||
det.setGainMode(slsDetectorDefs::gainMode::DYNAMIC);
|
||||
break;
|
||||
}
|
||||
|
||||
det.setNextFrameNumber(1);
|
||||
det.setNumberOfFrames(request.num_frames());
|
||||
det.setNumberOfTriggers(request.num_triggers());
|
||||
det.setStorageCellStart(request.storage_cell_start());
|
||||
det.setNumberOfAdditionalStorageCells(request.storage_cell_number() - 1);
|
||||
det.setStorageCellDelay(std::chrono::microseconds(request.storage_cell_delay()));
|
||||
|
||||
if (request.period_us() < MIN_FRAME_TIME_HALF_SPEED_IN_US)
|
||||
det.setReadoutSpeed(slsDetectorDefs::speedLevel::FULL_SPEED);
|
||||
else
|
||||
det.setReadoutSpeed(slsDetectorDefs::speedLevel::HALF_SPEED);
|
||||
|
||||
det.setPeriod(std::chrono::microseconds(request.period_us()));
|
||||
det.setExptime(std::chrono::microseconds(request.count_time_us()));
|
||||
|
||||
// Always synchronize to master
|
||||
det.setBit(0x4f, 15, {0});
|
||||
det.setBit(0x4e, 4);
|
||||
|
||||
det.startDetector();
|
||||
} catch (std::exception &e) {
|
||||
logger.ErrorException(e);
|
||||
throw JFJochException(JFJochExceptionCategory::Detector, e.what());
|
||||
}
|
||||
|
||||
logger.Info(" ... done");
|
||||
}
|
||||
|
||||
void DetectorWrapper::InternalStop() {
|
||||
// Assume it is executed in try-catch!
|
||||
auto state = GetState();
|
||||
if (state == DetectorState::ERROR)
|
||||
throw JFJochException(JFJochExceptionCategory::Detector,
|
||||
"Detector in error state");
|
||||
else if (state == DetectorState::BUSY) {
|
||||
det.stopDetector();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
state = GetState();
|
||||
if (state != DetectorState::IDLE)
|
||||
throw JFJochException(JFJochExceptionCategory::Detector,
|
||||
"Detector busy and cannot be stopped");
|
||||
}
|
||||
}
|
||||
|
||||
void DetectorWrapper::Deactivate() {
|
||||
logger.Info("Deactivate");
|
||||
try {
|
||||
InternalStop();
|
||||
det.setHighVoltage(0);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
det.setPowerChip(false);
|
||||
} catch (std::exception &e) {
|
||||
logger.ErrorException(e);
|
||||
throw JFJochException(JFJochExceptionCategory::Detector, e.what());
|
||||
}
|
||||
logger.Info(" ... done");
|
||||
}
|
||||
|
||||
void DetectorWrapper::Stop() {
|
||||
logger.Info("Stop");
|
||||
try {
|
||||
InternalStop();
|
||||
} catch (std::exception &e) {
|
||||
logger.ErrorException(e);
|
||||
throw JFJochException(JFJochExceptionCategory::Detector, e.what());
|
||||
}
|
||||
logger.Info(" ... done");
|
||||
}
|
||||
|
||||
void DetectorWrapper::Trigger() {
|
||||
logger.Info("Trigger");
|
||||
try {
|
||||
for (int i = 1; i < det.size(); i++)
|
||||
det.setBit(0x4f, 2, {i});
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
det.setBit(0x4f, 2, {0});
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
det.clearBit(0x4f, 2);
|
||||
} catch (std::exception &e) {
|
||||
logger.ErrorException(e);
|
||||
throw JFJochException(JFJochExceptionCategory::Detector, e.what());
|
||||
}
|
||||
logger.Info(" ... done");
|
||||
}
|
||||
|
||||
DetectorWrapper::DetectorState DetectorWrapper::GetState() const {
|
||||
try {
|
||||
bool is_idle = true;
|
||||
for (auto & i : det.getDetectorStatus()) {
|
||||
if (i == slsDetectorDefs::runStatus::ERROR)
|
||||
return DetectorState::ERROR;
|
||||
if ((i != slsDetectorDefs::runStatus::IDLE) &&
|
||||
(i != slsDetectorDefs::runStatus::STOPPED) &&
|
||||
(i != slsDetectorDefs::runStatus::RUN_FINISHED))
|
||||
is_idle = false;
|
||||
}
|
||||
if (is_idle)
|
||||
return DetectorState::IDLE;
|
||||
else
|
||||
return DetectorState::BUSY;
|
||||
} catch (std::exception &e) {
|
||||
throw JFJochException(JFJochExceptionCategory::Detector, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
int64_t DetectorWrapper::GetFirmwareVersion() {
|
||||
try {
|
||||
auto result = det.getFirmwareVersion();
|
||||
return result.squash(0x0);
|
||||
} catch (std::exception &e) {
|
||||
throw JFJochException(JFJochExceptionCategory::Detector, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
int64_t DetectorWrapper::GetDetectorServerVersion() {
|
||||
try {
|
||||
auto result = det.getDetectorServerVersion();
|
||||
return result.squash(0x0);
|
||||
} catch (std::exception &e) {
|
||||
throw JFJochException(JFJochExceptionCategory::Detector, e.what());
|
||||
}
|
||||
}
|
||||
31
detector_control/DetectorWrapper.h
Normal file
31
detector_control/DetectorWrapper.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef JUNGFRAUJOCH_DETECTORWRAPPER_H
|
||||
#define JUNGFRAUJOCH_DETECTORWRAPPER_H
|
||||
|
||||
#include <sls/Detector.h>
|
||||
#include <jfjoch.pb.h>
|
||||
#include "../common/Logger.h"
|
||||
|
||||
#define BASE_DETECTOR_MAC 0xAABBCCDDEE10 // little-endian!
|
||||
#define HIGH_VOLTAGE 120
|
||||
|
||||
class DetectorWrapper {
|
||||
Logger logger{"DetectorWrapper"};
|
||||
sls::Detector det;
|
||||
void InternalStop();
|
||||
public:
|
||||
enum class DetectorState {IDLE, ERROR, BUSY};
|
||||
[[nodiscard]] DetectorState GetState() const;
|
||||
void Configure(const JFJochProtoBuf::DetectorConfig &config);
|
||||
void Start(const JFJochProtoBuf::DetectorInput &request);
|
||||
void Stop();
|
||||
void Trigger();
|
||||
void Deactivate();
|
||||
int64_t GetFirmwareVersion();
|
||||
int64_t GetDetectorServerVersion();
|
||||
};
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_DETECTORWRAPPER_H
|
||||
86
detector_control/JFJochDetector.cpp
Normal file
86
detector_control/JFJochDetector.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "JFJochDetector.h"
|
||||
#include "../common/JFJochException.h"
|
||||
|
||||
grpc::Status JFJochDetector::Start(grpc::ServerContext *context, const JFJochProtoBuf::DetectorInput *request,
|
||||
JFJochProtoBuf::Empty *response) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
try {
|
||||
detector.Start(*request);
|
||||
return grpc::Status::OK;
|
||||
} catch (JFJochException &e) {
|
||||
return {grpc::StatusCode::ABORTED, e.what()};
|
||||
}
|
||||
}
|
||||
|
||||
grpc::Status JFJochDetector::Stop(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Empty *response) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
try {
|
||||
detector.Stop();
|
||||
return grpc::Status::OK;
|
||||
} catch (JFJochException &e) {
|
||||
return {grpc::StatusCode::ABORTED, e.what()};
|
||||
}
|
||||
}
|
||||
|
||||
grpc::Status JFJochDetector::On(grpc::ServerContext *context, const JFJochProtoBuf::DetectorConfig *request,
|
||||
JFJochProtoBuf::Empty *response) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
try {
|
||||
detector.Configure(*request);
|
||||
return grpc::Status::OK;
|
||||
} catch (JFJochException &e) {
|
||||
return {grpc::StatusCode::ABORTED, e.what()};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
grpc::Status JFJochDetector::Off(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Empty *response) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
try {
|
||||
detector.Deactivate();
|
||||
return grpc::Status::OK;
|
||||
} catch (JFJochException &e) {
|
||||
return {grpc::StatusCode::ABORTED, e.what()};
|
||||
}
|
||||
}
|
||||
|
||||
grpc::Status JFJochDetector::Status(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::DetectorStatus *response) {
|
||||
try {
|
||||
switch(detector.GetState()) {
|
||||
case DetectorWrapper::DetectorState::IDLE:
|
||||
response->set_state(JFJochProtoBuf::IDLE);
|
||||
break;
|
||||
case DetectorWrapper::DetectorState::ERROR:
|
||||
response->set_state(JFJochProtoBuf::ERROR);
|
||||
break;
|
||||
case DetectorWrapper::DetectorState::BUSY:
|
||||
response->set_state(JFJochProtoBuf::BUSY);
|
||||
break;
|
||||
}
|
||||
response->set_fw_version(detector.GetFirmwareVersion());
|
||||
response->set_server_version(detector.GetDetectorServerVersion());
|
||||
|
||||
return grpc::Status::OK;
|
||||
} catch (JFJochException &e) {
|
||||
return {grpc::StatusCode::ABORTED, e.what()};
|
||||
}
|
||||
}
|
||||
|
||||
grpc::Status JFJochDetector::Trigger(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Empty *response) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
try {
|
||||
detector.Trigger();
|
||||
return grpc::Status::OK;
|
||||
} catch (JFJochException &e) {
|
||||
return {grpc::StatusCode::ABORTED, e.what()};
|
||||
}
|
||||
}
|
||||
35
detector_control/JFJochDetector.h
Normal file
35
detector_control/JFJochDetector.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#ifndef DETECTORWRAPPER_H
|
||||
#define DETECTORWRAPPER_H
|
||||
|
||||
#include <sls/Detector.h>
|
||||
#include "jfjoch.grpc.pb.h"
|
||||
#include "DetectorWrapper.h"
|
||||
|
||||
#include "../common/Logger.h"
|
||||
|
||||
#define BASE_DETECTOR_MAC 0xAABBCCDDEE10 // little-endian!
|
||||
#define HIGH_VOLTAGE 120
|
||||
|
||||
class JFJochDetector final : public JFJochProtoBuf::gRPC_JFJochDetector::Service {
|
||||
std::mutex m;
|
||||
DetectorWrapper detector;
|
||||
public:
|
||||
grpc::Status Start(grpc::ServerContext *context, const JFJochProtoBuf::DetectorInput *request,
|
||||
JFJochProtoBuf::Empty *response) override;
|
||||
grpc::Status Stop(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Empty *response) override;
|
||||
grpc::Status Status(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::DetectorStatus *response) override;
|
||||
grpc::Status On(grpc::ServerContext *context, const JFJochProtoBuf::DetectorConfig *request,
|
||||
JFJochProtoBuf::Empty *response) override;
|
||||
grpc::Status Off(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Empty *response) override;
|
||||
grpc::Status Trigger(grpc::ServerContext *context, const JFJochProtoBuf::Empty *request,
|
||||
JFJochProtoBuf::Empty *response) override;
|
||||
|
||||
};
|
||||
|
||||
#endif //JUNGFRAUJOCH_DETECTORWRAPPER_H
|
||||
27
detector_control/jfjoch_detector.cpp
Normal file
27
detector_control/jfjoch_detector.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright (2019-2022) Paul Scherrer Institute
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <fstream>
|
||||
#include <grpcpp/server.h>
|
||||
#include <grpcpp/server_builder.h>
|
||||
#include <grpcpp/security/server_credentials.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "JFJochDetector.h"
|
||||
#include "../common/Logger.h"
|
||||
#include "../grpc/gRPCServer_Template.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
Logger logger("jfjoch_detector");
|
||||
|
||||
nlohmann::json input;
|
||||
|
||||
std::string grpc_addr = "unix:/opt/jfjoch/.jfjoch-detector";
|
||||
if (argc == 2) grpc_addr = "0.0.0.0:" + std::string(argv[1]);
|
||||
|
||||
JFJochDetector service;
|
||||
|
||||
auto server = gRPCServer(grpc_addr, service);
|
||||
logger.Info("gRPC configuration listening on " + grpc_addr);
|
||||
server->Wait();
|
||||
}
|
||||
1
detector_control/slsDetectorPackage
Submodule
1
detector_control/slsDetectorPackage
Submodule
Submodule detector_control/slsDetectorPackage added at da8e0060d3
1
etc/CMakeLists.txt
Normal file
1
etc/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
INSTALL(FILES broker.json recv.json index.json writer.json DESTINATION etc)
|
||||
42
etc/broker.json
Normal file
42
etc/broker.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"ipv4_subnet": 85,
|
||||
"writer": [
|
||||
{
|
||||
"addr_grpc": "10.10.1.204:5234",
|
||||
"addr_zmq": "tcp://10.10.1.242:5402"
|
||||
},
|
||||
{
|
||||
"addr_grpc": "10.10.1.204:5235",
|
||||
"addr_zmq": "tcp://10.10.1.242:5403"
|
||||
},
|
||||
{
|
||||
"addr_grpc": "10.10.1.204:5236",
|
||||
"addr_zmq": "tcp://10.10.1.242:5404"
|
||||
},
|
||||
{
|
||||
"addr_grpc": "10.10.1.204:5237",
|
||||
"addr_zmq": "tcp://10.10.1.242:5405"
|
||||
}
|
||||
],
|
||||
"receiver_addr": "unix:/opt/jfjoch/.jfjoch-fpga-receiver",
|
||||
"detector_addr": "unix:/opt/jfjoch/.jfjoch-detector",
|
||||
"frontend_directory": "/opt/jfjoch/frontend",
|
||||
"indexer": {
|
||||
"addr_grpc": "mx-jungfrau-1:5405",
|
||||
"addr_zmq": "tcp://10.10.1.242:5401"
|
||||
},
|
||||
"source_name": "Swiss Light Source",
|
||||
"source_name_short": "SLS",
|
||||
"instrument_name": "VESPA",
|
||||
"instrument_name_short": "VESPA",
|
||||
"gain_file": [
|
||||
"/opt/jfjoch/gainmaps/gainMaps_M352_2020-01-31.bin",
|
||||
"/opt/jfjoch/gainmaps/gainMaps_M307_2020-01-20.bin",
|
||||
"/opt/jfjoch/gainmaps/gainMaps_M264_2019-07-29.bin",
|
||||
"/opt/jfjoch/gainmaps/gainMaps_M253_2019-07-29.bin",
|
||||
"/opt/jfjoch/gainmaps/gainMaps_M351_2020-01-20.bin",
|
||||
"/opt/jfjoch/gainmaps/gainMaps_M312_2020-01-20.bin",
|
||||
"/opt/jfjoch/gainmaps/gainMaps_M261_2019-07-29.bin",
|
||||
"/opt/jfjoch/gainmaps/gainMaps_M310_2021-01-26.bin"
|
||||
]
|
||||
}
|
||||
3
etc/index.json
Normal file
3
etc/index.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"nthreads": 64
|
||||
}
|
||||
30
etc/recv.json
Normal file
30
etc/recv.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"compression_threads" : 64,
|
||||
"device": [
|
||||
{
|
||||
"id" : 1,
|
||||
"type": "pcie"
|
||||
},
|
||||
{
|
||||
"id" : 3,
|
||||
"type": "pcie"
|
||||
},
|
||||
{
|
||||
"id" : 0,
|
||||
"type": "pcie"
|
||||
},
|
||||
{
|
||||
"id" : 2,
|
||||
"type": "pcie"
|
||||
}
|
||||
],
|
||||
"tcp_send_buffer_size": 256000000,
|
||||
"zmq_send_high_watermark": 500,
|
||||
"image_zmq_addr": ["tcp://10.10.1.242:5402",
|
||||
"tcp://10.10.1.242:5403",
|
||||
"tcp://10.10.1.242:5404",
|
||||
"tcp://10.10.1.242:5405"],
|
||||
"preview_zmq_addr": "tcp://0.0.0.0:5400",
|
||||
"spot_zmq_addr": "tcp://10.10.1.242:5401",
|
||||
"grpc_addr": "unix:/opt/jfjoch/.jfjoch-fpga-receiver"
|
||||
}
|
||||
5
etc/writer.json
Normal file
5
etc/writer.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"base_directory": "/home/leonarski_f/data",
|
||||
"gid": -1,
|
||||
"grpc_addr": [4567,4568,4569,4570]
|
||||
}
|
||||
13
frame_serialize/CMakeLists.txt
Normal file
13
frame_serialize/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
ADD_LIBRARY(FrameSerialize STATIC
|
||||
JFJochFrameSerializer.cpp JFJochFrameSerializer.h
|
||||
JFJochFrameDeserializer.cpp JFJochFrameDeserializer.h
|
||||
ImageMessage.h
|
||||
tinycbor/src/cborencoder.c
|
||||
tinycbor/src/cborencoder_close_container_checked.c
|
||||
tinycbor/src/cborencoder_float.c
|
||||
tinycbor/src/cborparser.c
|
||||
tinycbor/src/cborparser_float.c
|
||||
tinycbor/src/cborpretty.c
|
||||
tinycbor/src/cborerrorstrings.c
|
||||
tinycbor/src/cbor.h
|
||||
tinycbor/src/tinycbor-version.h CborErr.h StartMessage.h EndMessage.h CborUtil.h)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user