Compare commits
24 Commits
1.0.0-rc.1
...
1.0.0_rc.9
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e59930972 | |||
| a32c7274a6 | |||
| 8809b8d0d5 | |||
| 86b3934387 | |||
| 7d9dcf2721 | |||
| 953c3fa972 | |||
| 5d7a4e1ff2 | |||
| f85b87bfd2 | |||
| 500222bdcc | |||
| c4d677f05b | |||
| 76ff39c012 | |||
| 3ef89483e8 | |||
| e185fbb3f5 | |||
| 701c083739 | |||
| 03662506c6 | |||
| 1b297babe9 | |||
| adc7aa7c7d | |||
| 27e17c316d | |||
| d7d66dc85c | |||
| 2a8fc3a466 | |||
| e4acd93b88 | |||
| 4ca397bd42 | |||
| 4d780c1dda | |||
| 949f693311 |
@@ -34,7 +34,7 @@ build:x86:gcc_writer:
|
||||
- cd build
|
||||
- source /opt/rh/gcc-toolset-12/enable
|
||||
- cmake -DCMAKE_BUILD_TYPE=Release -DJFJOCH_WRITER_ONLY=ON ..
|
||||
- make -j48 jfjoch
|
||||
- make -j48
|
||||
|
||||
build:x86:driver:
|
||||
stage: build
|
||||
@@ -83,16 +83,35 @@ build:x86:frontend:
|
||||
- x86
|
||||
needs: []
|
||||
script:
|
||||
- cd frontend_ui
|
||||
- npm install
|
||||
- npm run build
|
||||
- mkdir build
|
||||
- cd build
|
||||
- /usr/bin/cmake ..
|
||||
- make frontend
|
||||
- cd ../frontend_ui/build
|
||||
- tar czf ../../jfjoch_frontend.tar.gz *
|
||||
artifacts:
|
||||
paths:
|
||||
- jfjoch_frontend.tar.gz
|
||||
expire_in: 1 week
|
||||
|
||||
build:x86:rpm:
|
||||
stage: build
|
||||
tags:
|
||||
- x86
|
||||
needs: []
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- source /opt/rh/gcc-toolset-12/enable
|
||||
- cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||
- make frontend
|
||||
- make -j48 package
|
||||
- mv *.rpm ..
|
||||
artifacts:
|
||||
paths:
|
||||
- "*.rpm"
|
||||
expire_in: 1 week
|
||||
|
||||
test:x86:gcc:
|
||||
stage: test
|
||||
timeout: 90m
|
||||
@@ -292,9 +311,13 @@ release:
|
||||
- synthesis:vivado_pcie_100g
|
||||
- build:x86:frontend
|
||||
- build:x86:driver
|
||||
- build:x86:rpm
|
||||
script:
|
||||
- export PACKAGE_VERSION=`head -n1 VERSION`
|
||||
- export PACKAGE_REGISTRY_URL="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/jungfraujoch/${PACKAGE_VERSION}"
|
||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file jfjoch-driver-dkms-${PACKAGE_VERSION}-1.el8.noarch.rpm "${PACKAGE_REGISTRY_URL}/jfjoch-driver-dkms-${PACKAGE_VERSION}-1.el8.noarch.rpm"'
|
||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file jfjoch-writer-${PACKAGE_VERSION}-1.el8.x86_64.rpm "${PACKAGE_REGISTRY_URL}/jfjoch-writer-${PACKAGE_VERSION}-1.el8.x86_64.rpm"'
|
||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file jfjoch-${PACKAGE_VERSION}-1.el8.x86_64.rpm "${PACKAGE_REGISTRY_URL}/jfjoch-${PACKAGE_VERSION}-1.el8.x86_64.rpm"'
|
||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file jfjoch_driver.tar.gz "${PACKAGE_REGISTRY_URL}/jfjoch_driver.tar.gz"'
|
||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file jfjoch_frontend.tar.gz "${PACKAGE_REGISTRY_URL}/jfjoch_frontend.tar.gz"'
|
||||
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file jfjoch_fpga_pcie_100g.mcs "${PACKAGE_REGISTRY_URL}/jfjoch_fpga_pcie_100g.mcs"'
|
||||
@@ -305,3 +328,6 @@ release:
|
||||
--assets-link "{\"name\":\"jfjoch_frontend.tar.gz\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch_frontend.tar.gz\"}"
|
||||
--assets-link "{\"name\":\"jfjoch_fpga_pcie_8x10g.mcs\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch_fpga_pcie_8x10g.mcs\"}"
|
||||
--assets-link "{\"name\":\"jfjoch_fpga_pcie_100g.mcs\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch_fpga_pcie_100g.mcs\"}"
|
||||
--assets-link "{\"name\":\"jfjoch-${PACKAGE_VERSION}-1.el8.x86_64.rpm\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch-${PACKAGE_VERSION}-1.el8.x86_64.rpm\",\"link_type\":\"package\"}"
|
||||
--assets-link "{\"name\":\"jfjoch-writer-${PACKAGE_VERSION}-1.el8.x86_64.rpm\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch-writer-${PACKAGE_VERSION}-1.el8.x86_64.rpm\",\"link_type\":\"package\"}"
|
||||
--assets-link "{\"name\":\"jfjoch-driver-dkms-${PACKAGE_VERSION}-1.el8.noarch.rpm\",\"url\":\"${PACKAGE_REGISTRY_URL}/jfjoch-driver-dkms-${PACKAGE_VERSION}-1.el8.noarch.rpm\",\"link_type\":\"package\"}"
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 3.19)
|
||||
|
||||
PROJECT(Jungfraujoch VERSION 1.0 LANGUAGES C CXX)
|
||||
LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
FILE(STRINGS VERSION JFJOCH_VERSION)
|
||||
|
||||
PROJECT(jfjoch VERSION 1.0.0 LANGUAGES C CXX)
|
||||
SET(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
||||
|
||||
SET(CMAKE_CXX_STANDARD 20)
|
||||
SET(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
@@ -9,8 +11,31 @@ SET(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -march=native -mtune=native -Wno-deprecated-enum-enum-conversion")
|
||||
SET(CMAKE_C_FLAGS_RELEASE "-O3 -march=native -mtune=native")
|
||||
|
||||
SET(BUILD_SHARED_LIBS OFF)
|
||||
SET(BUILD_TESTING OFF)
|
||||
|
||||
SET(ZSTD_LEGACY_SUPPORT OFF)
|
||||
SET(ZSTD_MULTITHREAD_SUPPORT OFF)
|
||||
SET(ZSTD_BUILD_PROGRAMS OFF)
|
||||
SET(ZSTD_BUILD_SHARED OFF)
|
||||
|
||||
SET(SLS_USE_RECEIVER OFF)
|
||||
SET(SLS_USE_RECEIVER_BINARIES OFF)
|
||||
SET(SLS_BUILD_SHARED_LIBRARIES OFF)
|
||||
|
||||
SET(BUILD_FAST_INDEXER OFF)
|
||||
SET(BUILD_FAST_INDEXER_STATIC ON)
|
||||
|
||||
SET(HDF5_ENABLE_SZIP_SUPPORT OFF)
|
||||
SET(HDF5_ENABLE_SZIP_ENCODING OFF)
|
||||
SET(HDF5_BUILD_EXAMPLES OFF)
|
||||
SET(HDF5_BUILD_CPP_LIB OFF)
|
||||
SET(HDF5_ENABLE_Z_LIB_SUPPORT OFF)
|
||||
SET(HDF5_EXTERNALLY_CONFIGURED 1)
|
||||
|
||||
SET(jbig OFF)
|
||||
SET(zstd OFF)
|
||||
SET(lzma OFF)
|
||||
|
||||
INCLUDE(CheckLanguage)
|
||||
CHECK_LANGUAGE(CUDA)
|
||||
@@ -37,10 +62,22 @@ CHECK_INCLUDE_FILE(numa.h HAS_NUMA_H)
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(tiff
|
||||
GIT_REPOSITORY https://github.com/fleon-psi/libtiff
|
||||
GIT_TAG v4.6.0
|
||||
EXCLUDE_FROM_ALL)
|
||||
|
||||
FetchContent_Declare(hdf5
|
||||
GIT_REPOSITORY https://github.com/HDFGroup/hdf5/
|
||||
GIT_TAG hdf5_1.14.4.2
|
||||
GIT_SHALLOW 1
|
||||
EXCLUDE_FROM_ALL)
|
||||
|
||||
FetchContent_Declare(
|
||||
pistache_http
|
||||
GIT_REPOSITORY https://github.com/fleon-psi/pistache
|
||||
GIT_TAG 51553b92cc7bb25ac792462722ddd4fae33d14b1
|
||||
EXCLUDE_FROM_ALL
|
||||
)
|
||||
|
||||
FetchContent_Declare(
|
||||
@@ -48,6 +85,7 @@ FetchContent_Declare(
|
||||
GIT_REPOSITORY https://github.com/facebook/zstd
|
||||
GIT_TAG 794ea1b0afca0f020f4e57b6732332231fb23c70
|
||||
SOURCE_SUBDIR build/cmake
|
||||
EXCLUDE_FROM_ALL
|
||||
)
|
||||
|
||||
FetchContent_Declare(
|
||||
@@ -60,9 +98,10 @@ FetchContent_Declare(
|
||||
catch2
|
||||
GIT_REPOSITORY https://github.com/catchorg/Catch2
|
||||
GIT_TAG 4e8d92b
|
||||
EXCLUDE_FROM_ALL
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(pistache_http zstd sls_detector_package catch2)
|
||||
FetchContent_MakeAvailable(pistache_http zstd sls_detector_package catch2 hdf5 tiff)
|
||||
|
||||
ADD_SUBDIRECTORY(jungfrau)
|
||||
ADD_SUBDIRECTORY(compression)
|
||||
@@ -73,7 +112,6 @@ ADD_SUBDIRECTORY(frame_serialize)
|
||||
ADD_SUBDIRECTORY(detector_control)
|
||||
IF (JFJOCH_WRITER_ONLY)
|
||||
MESSAGE(STATUS "Compiling HDF5 writer only")
|
||||
SET(jfjoch_executables jfjoch_writer)
|
||||
ELSE()
|
||||
ADD_SUBDIRECTORY(broker)
|
||||
ADD_SUBDIRECTORY(fpga)
|
||||
@@ -83,16 +121,52 @@ ELSE()
|
||||
ADD_SUBDIRECTORY(tests)
|
||||
ADD_SUBDIRECTORY(tools)
|
||||
ADD_SUBDIRECTORY(preview)
|
||||
SET(jfjoch_executables jfjoch_broker jfjoch_writer jfjoch_test CompressionBenchmark HDF5DatasetWriteTest jfjoch_udp_simulator sls_detector_put sls_detector_get)
|
||||
ENDIF()
|
||||
|
||||
ADD_CUSTOM_COMMAND(OUTPUT frontend_ui/build/index.html
|
||||
COMMAND npm run build
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/frontend_ui)
|
||||
ADD_CUSTOM_TARGET(frontend DEPENDS frontend_ui/build/index.html)
|
||||
IF (NOT JFJOCH_WRITER_ONLY)
|
||||
ADD_CUSTOM_COMMAND(OUTPUT frontend_ui/build/index.html
|
||||
COMMAND npm install
|
||||
COMMAND npm run build
|
||||
COMMAND npm run redocly
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/frontend_ui)
|
||||
ADD_CUSTOM_TARGET(frontend DEPENDS frontend_ui/build/index.html)
|
||||
|
||||
ADD_CUSTOM_TARGET(jfjoch DEPENDS ${jfjoch_executables})
|
||||
INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/fpga/pcie_driver/
|
||||
DESTINATION /usr/src/jfjoch-1.0.0
|
||||
COMPONENT driver-dkms
|
||||
FILES_MATCHING PATTERN "*.c" PATTERN "*.h" PATTERN "Makefile" PATTERN "dkms.conf")
|
||||
|
||||
FILE(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/frontend_ui/build/)
|
||||
INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/frontend_ui/build/ DESTINATION share/jfjoch/frontend COMPONENT jfjoch )
|
||||
ENDIF()
|
||||
|
||||
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)
|
||||
|
||||
# Set Package Name
|
||||
set(CPACK_PACKAGE_NAME "jfjoch")
|
||||
|
||||
SET(CPACK_COMPONENTS_ALL jfjoch writer driver-dkms)
|
||||
SET(CPACK_GENERATOR RPM)
|
||||
SET(CPACK_RPM_COMPONENT_INSTALL ON)
|
||||
SET(CPACK_RPM_MAIN_COMPONENT jfjoch)
|
||||
SET(CPACK_RPM_PACKAGE_RELEASE_DIST ON)
|
||||
SET(CPACK_RPM_FILE_NAME "RPM-DEFAULT")
|
||||
SET(CPACK_RPM_PACKAGE_VERSION ${JFJOCH_VERSION})
|
||||
SET(CPACK_RPM_PACKAGE_RELEASE 1)
|
||||
SET(CPACK_RPM_PACKAGE_SUMMARY "Jungfraujoch data acquisition system")
|
||||
SET(CPACK_RPM_PACKAGE_DESCRIPTION "Jungfraujoch")
|
||||
SET(CPACK_RPM_DRIVER-DKMS_PACKAGE_REQUIRES "dkms, gcc, bash, sed")
|
||||
SET(CPACK_RPM_DRIVER-DKMS_PACKAGE_ARCHITECTURE "noarch")
|
||||
SET(CPACK_RPM_DRIVER-DKMS_POST_INSTALL_SCRIPT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/fpga/pcie_driver/postinstall.sh)
|
||||
SET(CPACK_RPM_DRIVER-DKMS_PRE_UNINSTALL_SCRIPT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/fpga/pcie_driver/preuninstall.sh)
|
||||
SET(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr/src)
|
||||
|
||||
# Set The Vendor Name
|
||||
SET(CPACK_PACKAGE_VENDOR "Paul Scherrer Institut")
|
||||
|
||||
# Set The License Information
|
||||
SET(CPACK_RPM_PACKAGE_LICENSE "Proprietary")
|
||||
|
||||
INCLUDE(CPack)
|
||||
|
||||
@@ -4,7 +4,7 @@ FROM harbor.maxiv.lu.se/dockerhub/library/ubuntu:22.04
|
||||
|
||||
RUN set -ex; \
|
||||
apt-get update; \
|
||||
apt-get install -y pkg-config git cmake make g++ libhdf5-dev libczmq-dev;\
|
||||
apt-get install -y pkg-config git cmake make g++;\
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
|
||||
11
README.md
11
README.md
@@ -7,6 +7,7 @@ Citation: F. Leonarski, M. Bruckner, C. Lopez-Cuenca, A. Mozzanica, H.-C. Stadle
|
||||
The project is supported by :
|
||||
* Innosuisse via Innovation Project "NextGenDCU high data rate acquisition system for X-ray detectors in structural biology applications" (101.535.1 IP-ENG; Apr 2023 - Sep 2025).
|
||||
* ETH Domain via Open Research Data Contribute project (Jan - Dec 2023)
|
||||
* AMD University Program with donation of licenses of Ethernet IP cores and Vivado software
|
||||
|
||||
## License
|
||||
|
||||
@@ -33,9 +34,7 @@ Other linux platforms should work, but no tests were done so far.
|
||||
### Dependencies
|
||||
Required:
|
||||
* C++20 compiler and C++20 standard library; recommended GCC 11+ or clang 14+ (Intel OneAPI, AMD AOCC)
|
||||
* CMake version 3.21 or newer + GNU make tool
|
||||
* HDF5 library version 1.10 or newer
|
||||
* TIFF library (with C++ headers)
|
||||
* CMake version 3.21 or newer + GNU make tool
|
||||
* JPEG library (turbo-jpeg is also OK)
|
||||
|
||||
Optional:
|
||||
@@ -43,14 +42,16 @@ Optional:
|
||||
* NUMA library - to pin threads to nodes/CPUs
|
||||
* Node.js - to make frontend
|
||||
|
||||
Automatically downloaded by CMake:
|
||||
Automatically downloaded by CMake and statically linked:
|
||||
* SLS Detector Package - see [github.com/slsdetectorgroup/slsDetectorPackage](https://github.com/slsdetectorgroup/slsDetectorPackage)
|
||||
* Zstandard (Facebook) - see [github.com/facebook/zstd](https://github.com/facebook/zstd)
|
||||
* Pistache webserver - see [github.com/pistacheio/pistache](https://github.com/pistacheio/pistache)
|
||||
* Fast feedback indexer (Hans-Christian Stadler, PSI) - see [github.com/paulscherrerinstitute/fast-feedback-indexer](https://github.com/paulscherrerinstitute/fast-feedback-indexer)
|
||||
* Catch2 testing library - see [github.com/catchorg/Catch2](https://github.com/catchorg/Catch2)
|
||||
* HDF5 library - see [github.com/HDFGroup/hdf5](https://github.com/HDFGroup/hdf5)
|
||||
* TIFF library - see [gitlab.com/libtiff/libtiff](https://gitlab.com/libtiff/libtiff)
|
||||
|
||||
Please follow the link provided above to check for LICENSE file
|
||||
Please follow the link provided above to check for LICENSE file. Building code with dependencies above requires access from the build system to github.com.
|
||||
|
||||
Directly included in the repository:
|
||||
* JSON parser/writer from N. Lohmann - see [github.com/nlohmann/json](https://github.com/nlohmann/json)
|
||||
|
||||
@@ -15,4 +15,6 @@ TARGET_LINK_LIBRARIES(JFJochBroker JFJochReceiver JFJochDetector JFJochCommon JF
|
||||
ADD_EXECUTABLE(jfjoch_broker jfjoch_broker.cpp)
|
||||
TARGET_LINK_LIBRARIES(jfjoch_broker JFJochBroker)
|
||||
|
||||
INSTALL(TARGETS jfjoch_broker RUNTIME)
|
||||
INSTALL(TARGETS jfjoch_broker RUNTIME COMPONENT jfjoch)
|
||||
|
||||
INSTALL(FILES redoc-static.html DESTINATION jfjoch/frontend COMPONENT jfjoch )
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "JFJochBrokerHttp.h"
|
||||
#include "gen/model/Error_message.h"
|
||||
#include "../preview/JFJochTIFF.h"
|
||||
|
||||
// From https://en.cppreference.com/w/cpp/string/byte/tolower
|
||||
inline std::string str_tolower(std::string s) {
|
||||
@@ -22,6 +23,10 @@ inline SpotFindingSettings Convert(const org::openapitools::server::model::Spot_
|
||||
ret.enable = input.isEnable();
|
||||
ret.indexing = input.isIndexing();
|
||||
ret.indexing_tolerance = input.getIndexingTolerance();
|
||||
if (input.filterPowderRingsIsSet())
|
||||
ret.filter_spots_powder_ring = input.isFilterPowderRings();
|
||||
if (input.minSpotCountPowderRingIsSet())
|
||||
ret.min_spot_count_powder_ring = input.getMinSpotCountPowderRing();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -36,6 +41,8 @@ inline org::openapitools::server::model::Spot_finding_settings Convert(const Spo
|
||||
ret.setEnable(input.enable);
|
||||
ret.setIndexing(input.indexing);
|
||||
ret.setIndexingTolerance(input.indexing_tolerance);
|
||||
ret.setFilterPowderRings(input.filter_spots_powder_ring);
|
||||
ret.setMinSpotCountPowderRing(input.min_spot_count_powder_ring);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -45,6 +52,7 @@ inline org::openapitools::server::model::Measurement_statistics Convert(const Me
|
||||
if (!input.file_prefix.empty())
|
||||
ret.setFilePrefix(input.file_prefix);
|
||||
|
||||
ret.setExperimentGroup(input.experiment_group);
|
||||
ret.setImagesExpected(input.images_expected);
|
||||
ret.setImagesCollected(input.images_collected);
|
||||
ret.setImagesSent(input.images_sent);
|
||||
@@ -67,7 +75,8 @@ inline org::openapitools::server::model::Measurement_statistics Convert(const Me
|
||||
|
||||
if (input.bkg_estimate)
|
||||
ret.setBkgEstimate(input.bkg_estimate.value());
|
||||
|
||||
ret.setUnitCell(input.unit_cell);
|
||||
ret.setRunNumber(input.run_number);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -184,10 +193,10 @@ inline org::openapitools::server::model::Detector_status Convert(const DetectorS
|
||||
output.setHighVoltageV(input.high_voltage_V);
|
||||
switch (input.power_state) {
|
||||
case DetectorPowerState::ON:
|
||||
output.setPowerchip("On");
|
||||
output.setPowerchip("PowerOn");
|
||||
break;
|
||||
case DetectorPowerState::OFF:
|
||||
output.setPowerchip("Off");
|
||||
output.setPowerchip("PowerOff");
|
||||
break;
|
||||
case DetectorPowerState::PARTIAL:
|
||||
output.setPowerchip("Partial");
|
||||
@@ -316,6 +325,7 @@ inline PreviewJPEGSettings Convert(const org::openapitools::server::model::Previ
|
||||
ret.saturation_value = input.getSaturation();
|
||||
ret.show_roi = input.isShowRoi();
|
||||
ret.show_indexed = input.isShowIndexed();
|
||||
ret.show_user_mask = input.isShowUserMask();
|
||||
if (input.resolutionRingIsSet())
|
||||
ret.resolution_ring = input.getResolutionRing();
|
||||
return ret;
|
||||
@@ -327,6 +337,10 @@ inline DatasetSettings Convert(const org::openapitools::server::model::Dataset_s
|
||||
ret.ImagesPerTrigger(input.getImagesPerTrigger());
|
||||
ret.NumTriggers(input.getNtrigger());
|
||||
|
||||
if (input.runNumberIsSet())
|
||||
ret.RunNumber(input.getRunNumber());
|
||||
ret.ExperimentGroup(input.getExperimentGroup());
|
||||
|
||||
if (!input.fpgaOutputIsSet())
|
||||
ret.FPGAOutputMode(FPGAPixelOutput::Auto);
|
||||
else {
|
||||
@@ -349,9 +363,8 @@ inline DatasetSettings Convert(const org::openapitools::server::model::Dataset_s
|
||||
ret.BeamX_pxl(input.getBeamXPxl());
|
||||
ret.BeamY_pxl(input.getBeamYPxl());
|
||||
ret.DetectorDistance_mm(input.getDetectorDistanceMm());
|
||||
ret.PhotonEnergy_keV(input.getPhotonEnergyKeV());
|
||||
|
||||
ret.PhotonEnergyMultiplayer(input.getPhotonEnergyMultiplier());
|
||||
ret.PhotonEnergy_keV(input.getIncidentEnergyKeV());
|
||||
ret.PhotonEnergyMultiplayer(input.getEnergyMultiplier());
|
||||
|
||||
ret.FilePrefix(input.getFilePrefix());
|
||||
|
||||
@@ -386,12 +399,15 @@ inline DatasetSettings Convert(const org::openapitools::server::model::Dataset_s
|
||||
if (input.transmissionIsSet())
|
||||
ret.AttenuatorTransmission(input.getTransmission());
|
||||
|
||||
if (input.omegaIsSet()) {
|
||||
ret.OmegaStep(input.getOmega().getStep());
|
||||
ret.OmegaStart(input.getOmega().getStart());
|
||||
if (input.getOmega().getVector().size() == 3) {
|
||||
auto v = input.getOmega().getVector();
|
||||
ret.OmegaAxis(Coord(v[0], v[1], v[2]));
|
||||
if (input.goniometerIsSet()) {
|
||||
ret.Goniometer(GoniometerAxis{
|
||||
.name = input.getGoniometer().getName(),
|
||||
.increment = input.getGoniometer().getStep(),
|
||||
.start = input.getGoniometer().getStart()
|
||||
});
|
||||
if (input.getGoniometer().getVector().size() == 3) {
|
||||
auto v = input.getGoniometer().getVector();
|
||||
ret.RotationAxis(Coord(v[0], v[1], v[2]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -654,18 +670,33 @@ void JFJochBrokerHttp::preview_image_tiff_get(Pistache::Http::ResponseWriter &re
|
||||
void JFJochBrokerHttp::config_internal_generator_image_put(const Pistache::Rest::Request &request,
|
||||
Pistache::Http::ResponseWriter &response) {
|
||||
int64_t image_number = 0;
|
||||
auto number_query = request.query().get("number");
|
||||
auto number_query = request.query().get("id");
|
||||
if (number_query)
|
||||
image_number = std::stoi(number_query.value());
|
||||
|
||||
if ((image_number < 0) || (image_number > 127))
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "image_number must be in range 0-127");
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "id must be in range 0-127");
|
||||
|
||||
state_machine.LoadInternalGeneratorImage(request.body().data(), request.body().size(), image_number);
|
||||
logger.Info("Internal generator image #{} loaded", image_number);
|
||||
response.send(Pistache::Http::Code::Ok);
|
||||
}
|
||||
|
||||
|
||||
void JFJochBrokerHttp::config_internal_generator_image_tiff_put(const Pistache::Rest::Request &request,
|
||||
Pistache::Http::ResponseWriter &response) {
|
||||
int64_t image_number = 0;
|
||||
auto number_query = request.query().get("id");
|
||||
if (number_query)
|
||||
image_number = std::stoi(number_query.value());
|
||||
|
||||
if ((image_number < 0) || (image_number > 127))
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "id must be in range 0-127");
|
||||
|
||||
state_machine.LoadInternalGeneratorImageTIFF(request.body(), image_number);
|
||||
response.send(Pistache::Http::Code::Ok);
|
||||
}
|
||||
|
||||
void JFJochBrokerHttp::roi_box_get(Pistache::Http::ResponseWriter &response) {
|
||||
ProcessOutput(Convert(state_machine.GetBoxROI()), response);
|
||||
}
|
||||
@@ -781,3 +812,19 @@ void JFJochBrokerHttp::plot_strong_pixel_get(const std::optional<int32_t> &binni
|
||||
Pistache::Http::ResponseWriter &response) {
|
||||
GenericPlot(PlotType::StrongPixels, binning, response);
|
||||
}
|
||||
|
||||
void JFJochBrokerHttp::config_mask_tiff_get(Pistache::Http::ResponseWriter &response) {
|
||||
std::string s = state_machine.GetFullPixelMaskTIFF();
|
||||
response.send(Pistache::Http::Code::Ok, s, Pistache::Http::Mime::MediaType::fromString("image/tiff"));
|
||||
}
|
||||
|
||||
void JFJochBrokerHttp::config_user_mask_tiff_get(Pistache::Http::ResponseWriter &response) {
|
||||
std::string s = state_machine.GetUserPixelMaskTIFF();
|
||||
response.send(Pistache::Http::Code::Ok, s, Pistache::Http::Mime::MediaType::fromString("image/tiff"));
|
||||
}
|
||||
|
||||
void JFJochBrokerHttp::config_user_mask_tiff_put(const Pistache::Rest::Request &request,
|
||||
Pistache::Http::ResponseWriter &response) {
|
||||
state_machine.SetUserPixelMask(request.body());
|
||||
response.send(Pistache::Http::Code::Ok);
|
||||
}
|
||||
|
||||
@@ -118,6 +118,16 @@ class JFJochBrokerHttp : public org::openapitools::server::api::DefaultApi {
|
||||
void xfel_event_code_get(Pistache::Http::ResponseWriter &response) override;
|
||||
void xfel_pulse_id_get(Pistache::Http::ResponseWriter &response) override;
|
||||
|
||||
void config_mask_tiff_get(Pistache::Http::ResponseWriter &response) override;
|
||||
|
||||
void config_user_mask_tiff_get(Pistache::Http::ResponseWriter &response) override;
|
||||
|
||||
void config_user_mask_tiff_put(const Pistache::Rest::Request &request,
|
||||
Pistache::Http::ResponseWriter &response) override;
|
||||
|
||||
void config_internal_generator_image_tiff_put(const Pistache::Rest::Request &request,
|
||||
Pistache::Http::ResponseWriter &response) override;
|
||||
|
||||
void GetStaticFile(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
std::pair<Pistache::Http::Code, std::string> handleOperationException(const std::exception &ex) const noexcept override;
|
||||
|
||||
|
||||
@@ -304,19 +304,21 @@ void ParseFacilityConfiguration(const nlohmann::json &input, const std::string&
|
||||
auto j = input[tag];
|
||||
experiment.SourceName(GET_STR(j, "source_name"));
|
||||
experiment.SourceNameShort(GET_STR(j, "source_name_short"));
|
||||
experiment.SourceType(GET_STR(j, "source_type", ""));
|
||||
|
||||
experiment.InstrumentName(GET_STR(j, "instrument_name"));
|
||||
experiment.InstrumentNameShort(GET_STR(j, "instrument_name_short"));
|
||||
|
||||
experiment.PulsedSource(GET_BOOL(j, "pulsed_source", false));
|
||||
|
||||
if (j.contains("omega_axis")) {
|
||||
if (j["omega_axis"].is_array() && (j["omega_axis"].size() == 3))
|
||||
experiment.DefaultOmegaAxis(Coord(j["omega_axis"][0].get<float>(),
|
||||
j["omega_axis"][1].get<float>(),
|
||||
j["omega_axis"][2].get<float>()));
|
||||
if (j.contains("rotation_axis")) {
|
||||
if (j["rotation_axis"].is_array() && (j["rotation_axis"].size() == 3))
|
||||
experiment.DefaultRotationAxis(Coord(j["rotation_axis"][0].get<float>(),
|
||||
j["rotation_axis"][1].get<float>(),
|
||||
j["rotation_axis"][2].get<float>()));
|
||||
else
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"omega_axis must be float array of 3");
|
||||
"rotation_axis must be float array of 3");
|
||||
}
|
||||
|
||||
if (j.contains("pedestal_g0_frames"))
|
||||
|
||||
@@ -5,15 +5,17 @@
|
||||
|
||||
JFJochServices::JFJochServices(Logger &in_logger) : logger(in_logger) {}
|
||||
|
||||
void JFJochServices::Start(const DiffractionExperiment& experiment, const JFCalibration &calibration) {
|
||||
void JFJochServices::Start(const DiffractionExperiment& experiment,
|
||||
const PixelMask &pixel_mask,
|
||||
const JFCalibration &calibration) {
|
||||
logger.Info("Measurement start for: {}", experiment.GetFilePrefix());
|
||||
|
||||
if (receiver != nullptr) {
|
||||
logger.Info(" ... receiver start");
|
||||
if (experiment.GetDetectorMode() == DetectorMode::Conversion)
|
||||
receiver->Start(experiment, &calibration);
|
||||
receiver->Start(experiment, pixel_mask, &calibration);
|
||||
else
|
||||
receiver->Start(experiment, nullptr);
|
||||
receiver->Start(experiment, pixel_mask, nullptr);
|
||||
|
||||
if (detector && !experiment.IsUsingInternalPacketGen()) {
|
||||
logger.Info(" ... detector start");
|
||||
|
||||
@@ -23,7 +23,9 @@ public:
|
||||
void On(const DiffractionExperiment& experiment);
|
||||
void Off();
|
||||
void ConfigureDetector(const DiffractionExperiment& experiment);
|
||||
void Start(const DiffractionExperiment& experiment, const JFCalibration &calibration);
|
||||
void Start(const DiffractionExperiment& experiment,
|
||||
const PixelMask &pixel_mask,
|
||||
const JFCalibration &calibration);
|
||||
JFJochServicesOutput Stop();
|
||||
void Cancel();
|
||||
void Trigger();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <thread>
|
||||
|
||||
#include "JFJochStateMachine.h"
|
||||
#include "../preview/WriteTIFF.h"
|
||||
#include "../preview/JFJochTIFF.h"
|
||||
|
||||
void ApplyDetectorSettings(DiffractionExperiment& experiment, const DetectorSettings &settings) {
|
||||
auto tmp = experiment;
|
||||
@@ -66,8 +66,10 @@ void ApplyRadialIntegrationSettings(DiffractionExperiment& experiment, const Rad
|
||||
}
|
||||
|
||||
JFJochStateMachine::JFJochStateMachine(JFJochServices &in_services, Logger &in_logger)
|
||||
: services(in_services), logger(in_logger),
|
||||
data_processing_settings(DiffractionExperiment::DefaultDataProcessingSettings()) {
|
||||
: services(in_services),
|
||||
logger(in_logger),
|
||||
data_processing_settings(DiffractionExperiment::DefaultDataProcessingSettings()),
|
||||
pixel_mask(experiment) {
|
||||
|
||||
}
|
||||
|
||||
@@ -126,6 +128,7 @@ void JFJochStateMachine::TakePedestalInternalAll(std::unique_lock<std::mutex> &u
|
||||
}
|
||||
}
|
||||
services.ConfigureDetector(experiment);
|
||||
pixel_mask.LoadDetectorBadPixelMask(calibration->CalculateMask());
|
||||
} catch (const std::exception &e) {
|
||||
logger.Error("Pedestal sequence error {}", e.what());
|
||||
state = JFJochState::Error;
|
||||
@@ -158,7 +161,7 @@ void JFJochStateMachine::TakePedestalInternalG0(std::unique_lock<std::mutex> &ul
|
||||
|
||||
state = JFJochState::Pedestal;
|
||||
services.ConfigureDetector(local_experiment);
|
||||
services.Start(local_experiment, *calibration);
|
||||
services.Start(local_experiment, pixel_mask, *calibration);
|
||||
|
||||
services.Trigger();
|
||||
|
||||
@@ -197,7 +200,7 @@ void JFJochStateMachine::TakePedestalInternalG1(std::unique_lock<std::mutex> &ul
|
||||
|
||||
state = JFJochState::Pedestal;
|
||||
services.ConfigureDetector(local_experiment);
|
||||
services.Start(local_experiment, *calibration);
|
||||
services.Start(local_experiment, pixel_mask, *calibration);
|
||||
|
||||
services.Trigger();
|
||||
|
||||
@@ -236,7 +239,7 @@ void JFJochStateMachine::TakePedestalInternalG2(std::unique_lock<std::mutex> &ul
|
||||
|
||||
state = JFJochState::Pedestal;
|
||||
services.ConfigureDetector(local_experiment);
|
||||
services.Start(local_experiment, *calibration);
|
||||
services.Start(local_experiment, pixel_mask, *calibration);
|
||||
|
||||
services.Trigger();
|
||||
|
||||
@@ -321,12 +324,12 @@ void JFJochStateMachine::Start(const DatasetSettings& settings) {
|
||||
else
|
||||
experiment.StorageCellStart(0);
|
||||
|
||||
experiment.IncrementSeriesID();
|
||||
experiment.IncrementRunNumber();
|
||||
|
||||
try {
|
||||
state = JFJochState::Busy;
|
||||
services.SetSpotFindingSettings(GetSpotFindingSettings());
|
||||
services.Start(experiment, *calibration);
|
||||
services.Start(experiment, pixel_mask, *calibration);
|
||||
|
||||
state = JFJochState::Measuring;
|
||||
measurement = std::async(std::launch::async, &JFJochStateMachine::MeasurementThread, this);
|
||||
@@ -398,10 +401,15 @@ void JFJochStateMachine::SetFullMeasurementOutput(const JFJochServicesOutput &ou
|
||||
MeasurementStatistics tmp{}; // reset last measurement statistics
|
||||
|
||||
tmp.file_prefix = experiment.GetFilePrefix();
|
||||
tmp.run_number = experiment.GetRunNumber();
|
||||
tmp.experiment_group = experiment.GetExperimentGroup();
|
||||
|
||||
tmp.detector_width = experiment.GetXPixelsNum();
|
||||
tmp.detector_height = experiment.GetYPixelsNum();
|
||||
tmp.detector_pixel_depth = experiment.GetPixelDepth();
|
||||
tmp.images_expected = experiment.GetImageNum();
|
||||
tmp.unit_cell = experiment.GetUnitCellString();
|
||||
|
||||
|
||||
tmp.compression_ratio = output.receiver_output.status.compressed_ratio;
|
||||
tmp.collection_efficiency = output.receiver_output.efficiency;
|
||||
@@ -422,10 +430,15 @@ void JFJochStateMachine::ClearAndSetMeasurementStatistics() {
|
||||
MeasurementStatistics tmp{};
|
||||
|
||||
tmp.file_prefix = experiment.GetFilePrefix();
|
||||
tmp.run_number = experiment.GetRunNumber();
|
||||
tmp.experiment_group = experiment.GetExperimentGroup();
|
||||
|
||||
tmp.detector_height = experiment.GetXPixelsNum();
|
||||
tmp.detector_width = experiment.GetYPixelsNum();
|
||||
tmp.detector_pixel_depth = experiment.GetPixelDepth();
|
||||
tmp.images_expected = experiment.GetImageNum();
|
||||
tmp.unit_cell = experiment.GetUnitCellString();
|
||||
|
||||
measurement_statistics = tmp;
|
||||
}
|
||||
|
||||
@@ -441,10 +454,14 @@ std::optional<MeasurementStatistics> JFJochStateMachine::GetMeasurementStatistic
|
||||
MeasurementStatistics tmp;
|
||||
|
||||
tmp.file_prefix = experiment.GetFilePrefix();
|
||||
tmp.run_number = experiment.GetRunNumber();
|
||||
tmp.experiment_group = experiment.GetExperimentGroup();
|
||||
|
||||
tmp.detector_width = experiment.GetXPixelsNum();
|
||||
tmp.detector_height = experiment.GetYPixelsNum();
|
||||
tmp.detector_pixel_depth = experiment.GetPixelDepth();
|
||||
tmp.images_expected = experiment.GetImageNum();
|
||||
tmp.unit_cell = experiment.GetUnitCellString();
|
||||
|
||||
tmp.compression_ratio = rcv_status->compressed_ratio;
|
||||
tmp.images_collected = rcv_status->images_collected;
|
||||
@@ -557,6 +574,7 @@ void JFJochStateMachine::AddDetectorSetup(const DetectorSetup &setup) {
|
||||
experiment.Detector(setup);
|
||||
gain_calibration = setup.GetGainCalibration();
|
||||
current_detector_setup = 0;
|
||||
pixel_mask = PixelMask(setup);
|
||||
}
|
||||
detector_setup.emplace_back(setup);
|
||||
}
|
||||
@@ -594,6 +612,7 @@ void JFJochStateMachine::SelectDetector(int64_t id) {
|
||||
try {
|
||||
experiment.Detector(detector_setup[id]);
|
||||
gain_calibration = detector_setup[id].GetGainCalibration();
|
||||
pixel_mask = PixelMask(detector_setup[id]);
|
||||
state = JFJochState::Inactive;
|
||||
current_detector_setup = id;
|
||||
} catch (JFJochException &e) {
|
||||
@@ -700,6 +719,21 @@ void JFJochStateMachine::LoadInternalGeneratorImage(const void *data, size_t siz
|
||||
services.LoadInternalGeneratorImage(experiment, image, image_number);
|
||||
}
|
||||
|
||||
void JFJochStateMachine::LoadInternalGeneratorImageTIFF(const std::string &s, uint64_t image_number) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
if (state != JFJochState::Idle)
|
||||
throw WrongDAQStateException ("Can change internal generator image only when detector in Idle state");
|
||||
|
||||
uint32_t cols, lines;
|
||||
auto v = ReadTIFFFromString16(s, cols, lines);
|
||||
if (((cols == experiment.GetXPixelsNum()) && (lines == experiment.GetYPixelsNum()))
|
||||
|| ((cols == RAW_MODULE_SIZE) && (lines == RAW_MODULE_LINES * experiment.GetModulesNum())))
|
||||
services.LoadInternalGeneratorImage(experiment, v, image_number);
|
||||
else
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Image size doesn't match current detector");
|
||||
}
|
||||
|
||||
void JFJochStateMachine::SetBoxROI(const std::vector<ROIBox> &input) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
@@ -739,3 +773,33 @@ std::vector<uint64_t> JFJochStateMachine::GetXFELEventCode() const {
|
||||
services.GetXFELEventCode(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string JFJochStateMachine::GetFullPixelMaskTIFF() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
std::vector v = pixel_mask.GetMask(experiment);
|
||||
return WriteTIFFToString(v.data(), experiment.GetXPixelsNum(), experiment.GetYPixelsNum(),
|
||||
sizeof(uint32_t), false);
|
||||
}
|
||||
|
||||
std::string JFJochStateMachine::GetUserPixelMaskTIFF() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
std::vector v = pixel_mask.GetUserMask(experiment);
|
||||
return WriteTIFFToString(v.data(), experiment.GetXPixelsNum(), experiment.GetYPixelsNum(),
|
||||
sizeof(uint32_t), false);
|
||||
}
|
||||
|
||||
void JFJochStateMachine::SetUserPixelMask(const std::string &s) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
if (state != JFJochState::Idle)
|
||||
throw WrongDAQStateException ("User mask can be only modified in Idle state");
|
||||
|
||||
try {
|
||||
uint32_t cols, lines;
|
||||
auto v = ReadTIFFFromString32(s, cols, lines);
|
||||
pixel_mask.LoadUserMask(experiment, v);
|
||||
} catch (const JFJochException &e) {
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Problem handling user mask " + std::string(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,9 @@ struct DetectorList {
|
||||
|
||||
struct MeasurementStatistics {
|
||||
std::string file_prefix;
|
||||
std::string experiment_group;
|
||||
int64_t run_number;
|
||||
|
||||
int64_t images_expected;
|
||||
int64_t images_collected;
|
||||
int64_t images_sent;
|
||||
@@ -56,6 +59,8 @@ struct MeasurementStatistics {
|
||||
|
||||
std::optional<float> bkg_estimate;
|
||||
std::optional<std::pair<float, float>> beam_center_drift_pxl;
|
||||
|
||||
std::string unit_cell;
|
||||
};
|
||||
|
||||
struct DetectorSettings {
|
||||
@@ -96,6 +101,8 @@ class JFJochStateMachine {
|
||||
volatile JFJochState state = JFJochState::Inactive;
|
||||
volatile bool cancel_sequence = false;
|
||||
std::unique_ptr<JFCalibration> calibration;
|
||||
PixelMask pixel_mask;
|
||||
|
||||
std::vector<JFModuleGainCalibration> gain_calibration;
|
||||
std::vector<DetectorSetup> detector_setup;
|
||||
int64_t current_detector_setup;
|
||||
@@ -170,6 +177,7 @@ public:
|
||||
std::string GetPedestalTIFF(size_t gain_level, size_t sc) const;
|
||||
|
||||
void LoadInternalGeneratorImage(const void *data, size_t size, uint64_t image_number);
|
||||
void LoadInternalGeneratorImageTIFF(const std::string &s, uint64_t image_number);
|
||||
|
||||
// Not thread safe - only for configuration in serial context
|
||||
DiffractionExperiment& NotThreadSafe_Experiment();
|
||||
@@ -185,6 +193,10 @@ public:
|
||||
|
||||
std::vector<uint64_t> GetXFELPulseID() const;
|
||||
std::vector<uint64_t> GetXFELEventCode() const;
|
||||
|
||||
std::string GetFullPixelMaskTIFF() const;
|
||||
std::string GetUserPixelMaskTIFF() const;
|
||||
void SetUserPixelMask(const std::string &v);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -37,12 +37,16 @@ void DefaultApi::setupRoutes() {
|
||||
Routes::Get(*router, base + "/config/detector", Routes::bind(&DefaultApi::config_detector_get_handler, this));
|
||||
Routes::Put(*router, base + "/config/detector", Routes::bind(&DefaultApi::config_detector_put_handler, this));
|
||||
Routes::Put(*router, base + "/config/internal_generator_image", Routes::bind(&DefaultApi::config_internal_generator_image_put_handler, this));
|
||||
Routes::Put(*router, base + "/config/internal_generator_image.tiff", Routes::bind(&DefaultApi::config_internal_generator_image_tiff_put_handler, this));
|
||||
Routes::Get(*router, base + "/config/mask.tiff", Routes::bind(&DefaultApi::config_mask_tiff_get_handler, this));
|
||||
Routes::Get(*router, base + "/config/rad_int", Routes::bind(&DefaultApi::config_rad_int_get_handler, this));
|
||||
Routes::Put(*router, base + "/config/rad_int", Routes::bind(&DefaultApi::config_rad_int_put_handler, this));
|
||||
Routes::Get(*router, base + "/config/select_detector", Routes::bind(&DefaultApi::config_select_detector_get_handler, this));
|
||||
Routes::Put(*router, base + "/config/select_detector", Routes::bind(&DefaultApi::config_select_detector_put_handler, this));
|
||||
Routes::Get(*router, base + "/config/spot_finding", Routes::bind(&DefaultApi::config_spot_finding_get_handler, this));
|
||||
Routes::Put(*router, base + "/config/spot_finding", Routes::bind(&DefaultApi::config_spot_finding_put_handler, this));
|
||||
Routes::Get(*router, base + "/config/user_mask.tiff", Routes::bind(&DefaultApi::config_user_mask_tiff_get_handler, this));
|
||||
Routes::Put(*router, base + "/config/user_mask.tiff", Routes::bind(&DefaultApi::config_user_mask_tiff_put_handler, this));
|
||||
Routes::Post(*router, base + "/deactivate", Routes::bind(&DefaultApi::deactivate_post_handler, this));
|
||||
Routes::Get(*router, base + "/detector/status", Routes::bind(&DefaultApi::detector_status_get_handler, this));
|
||||
Routes::Post(*router, base + "/initialize", Routes::bind(&DefaultApi::initialize_post_handler, this));
|
||||
@@ -192,6 +196,45 @@ void DefaultApi::config_internal_generator_image_put_handler(const Pistache::Res
|
||||
response.send(Pistache::Http::Code::Internal_Server_Error, e.what());
|
||||
}
|
||||
|
||||
}
|
||||
void DefaultApi::config_internal_generator_image_tiff_put_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) {
|
||||
try {
|
||||
|
||||
try {
|
||||
this->config_internal_generator_image_tiff_put(request, response);
|
||||
} catch (Pistache::Http::HttpError &e) {
|
||||
response.send(static_cast<Pistache::Http::Code>(e.code()), e.what());
|
||||
return;
|
||||
} catch (std::exception &e) {
|
||||
const std::pair<Pistache::Http::Code, std::string> errorInfo = this->handleOperationException(e);
|
||||
response.send(errorInfo.first, errorInfo.second);
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (std::exception &e) {
|
||||
response.send(Pistache::Http::Code::Internal_Server_Error, e.what());
|
||||
}
|
||||
|
||||
}
|
||||
void DefaultApi::config_mask_tiff_get_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) {
|
||||
try {
|
||||
|
||||
|
||||
try {
|
||||
this->config_mask_tiff_get(response);
|
||||
} catch (Pistache::Http::HttpError &e) {
|
||||
response.send(static_cast<Pistache::Http::Code>(e.code()), e.what());
|
||||
return;
|
||||
} catch (std::exception &e) {
|
||||
const std::pair<Pistache::Http::Code, std::string> errorInfo = this->handleOperationException(e);
|
||||
response.send(errorInfo.first, errorInfo.second);
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (std::exception &e) {
|
||||
response.send(Pistache::Http::Code::Internal_Server_Error, e.what());
|
||||
}
|
||||
|
||||
}
|
||||
void DefaultApi::config_rad_int_get_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) {
|
||||
try {
|
||||
@@ -351,6 +394,45 @@ void DefaultApi::config_spot_finding_put_handler(const Pistache::Rest::Request &
|
||||
response.send(Pistache::Http::Code::Internal_Server_Error, e.what());
|
||||
}
|
||||
|
||||
}
|
||||
void DefaultApi::config_user_mask_tiff_get_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) {
|
||||
try {
|
||||
|
||||
|
||||
try {
|
||||
this->config_user_mask_tiff_get(response);
|
||||
} catch (Pistache::Http::HttpError &e) {
|
||||
response.send(static_cast<Pistache::Http::Code>(e.code()), e.what());
|
||||
return;
|
||||
} catch (std::exception &e) {
|
||||
const std::pair<Pistache::Http::Code, std::string> errorInfo = this->handleOperationException(e);
|
||||
response.send(errorInfo.first, errorInfo.second);
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (std::exception &e) {
|
||||
response.send(Pistache::Http::Code::Internal_Server_Error, e.what());
|
||||
}
|
||||
|
||||
}
|
||||
void DefaultApi::config_user_mask_tiff_put_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response) {
|
||||
try {
|
||||
|
||||
try {
|
||||
this->config_user_mask_tiff_put(request, response);
|
||||
} catch (Pistache::Http::HttpError &e) {
|
||||
response.send(static_cast<Pistache::Http::Code>(e.code()), e.what());
|
||||
return;
|
||||
} catch (std::exception &e) {
|
||||
const std::pair<Pistache::Http::Code, std::string> errorInfo = this->handleOperationException(e);
|
||||
response.send(errorInfo.first, errorInfo.second);
|
||||
return;
|
||||
}
|
||||
|
||||
} catch (std::exception &e) {
|
||||
response.send(Pistache::Http::Code::Internal_Server_Error, e.what());
|
||||
}
|
||||
|
||||
}
|
||||
void DefaultApi::deactivate_post_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) {
|
||||
try {
|
||||
|
||||
@@ -61,12 +61,16 @@ private:
|
||||
void config_detector_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_detector_put_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_internal_generator_image_put_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_internal_generator_image_tiff_put_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_mask_tiff_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_rad_int_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_rad_int_put_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_select_detector_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_select_detector_put_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_spot_finding_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_spot_finding_put_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_user_mask_tiff_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void config_user_mask_tiff_put_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void deactivate_post_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void detector_status_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
void initialize_post_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response);
|
||||
@@ -150,6 +154,20 @@ private:
|
||||
/// </remarks>
|
||||
virtual void config_internal_generator_image_put(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter &response) = 0;
|
||||
/// <summary>
|
||||
/// Load TIFF image for internal FPGA generator
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Load image for internal FPGA generator. This can only happen in Idle state of the detector. Requires TIFF with 16-bit integer numbers of size of detector in raw/converted coordinates (depending on detector settings).
|
||||
/// </remarks>
|
||||
virtual void config_internal_generator_image_tiff_put(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter &response) = 0;
|
||||
/// <summary>
|
||||
/// Get mask of the detector
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Get full pixel mask of the detector See NXmx standard for meaning of pixel values
|
||||
/// </remarks>
|
||||
virtual void config_mask_tiff_get(Pistache::Http::ResponseWriter &response) = 0;
|
||||
/// <summary>
|
||||
/// Get radial integration configuration
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
@@ -195,6 +213,20 @@ private:
|
||||
/// <param name="spotFindingSettings"> (optional)</param>
|
||||
virtual void config_spot_finding_put(const org::openapitools::server::model::Spot_finding_settings &spotFindingSettings, Pistache::Http::ResponseWriter &response) = 0;
|
||||
/// <summary>
|
||||
/// Get user mask of the detector
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Get user pixel mask of the detector in the actual detector coordinates: 0 - good pixel, 1 - masked
|
||||
/// </remarks>
|
||||
virtual void config_user_mask_tiff_get(Pistache::Http::ResponseWriter &response) = 0;
|
||||
/// <summary>
|
||||
/// Upload user mask of the detector
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Should be in `Idle` state. Upload user mask of the detector - this is for example to account for beam stop shadow or misbehaving regions. If detector is conversion mode the mask can be both in raw (1024x512; stacked modules) or converted coordinates. In the latter case - module gaps are ignored and don't need to be assigned value. Mask is expected as TIFF (4-byte; unsigned). 0 - good pixel, other value - masked User mask is stored in NXmx pixel mask (bit 8), as well as used in spot finding and azimuthal integration. User mask is not automatically applied - i.e. pixels with user mask will have a valid pixel value in the images.
|
||||
/// </remarks>
|
||||
virtual void config_user_mask_tiff_put(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter &response) = 0;
|
||||
/// <summary>
|
||||
/// Prepare detector to turn off
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
|
||||
@@ -30,7 +30,7 @@ Dataset_settings::Dataset_settings()
|
||||
m_Beam_x_pxl = 0.0f;
|
||||
m_Beam_y_pxl = 0.0f;
|
||||
m_Detector_distance_mm = 0.0f;
|
||||
m_Photon_energy_keV = 0.0f;
|
||||
m_Incident_energy_keV = 0.0f;
|
||||
m_File_prefix = "";
|
||||
m_File_prefixIsSet = false;
|
||||
m_Images_per_file = 1000L;
|
||||
@@ -46,15 +46,19 @@ Dataset_settings::Dataset_settings()
|
||||
m_Total_fluxIsSet = false;
|
||||
m_Transmission = 0.0f;
|
||||
m_TransmissionIsSet = false;
|
||||
m_OmegaIsSet = false;
|
||||
m_GoniometerIsSet = false;
|
||||
m_Header_appendix = "";
|
||||
m_Header_appendixIsSet = false;
|
||||
m_Image_appendix = "";
|
||||
m_Image_appendixIsSet = false;
|
||||
m_Photon_energy_multiplier = 1.0f;
|
||||
m_Photon_energy_multiplierIsSet = false;
|
||||
m_Energy_multiplier = 1.0f;
|
||||
m_Energy_multiplierIsSet = false;
|
||||
m_Data_reduction_factor_serialmx = 1.0f;
|
||||
m_Data_reduction_factor_serialmxIsSet = false;
|
||||
m_Run_number = 0L;
|
||||
m_Run_numberIsSet = false;
|
||||
m_Experiment_group = "";
|
||||
m_Experiment_groupIsSet = false;
|
||||
m_Unit_cellIsSet = false;
|
||||
|
||||
}
|
||||
@@ -141,9 +145,9 @@ bool Dataset_settings::validate(std::stringstream& msg, const std::string& pathP
|
||||
}
|
||||
|
||||
|
||||
/* Photon_energy_keV */ {
|
||||
const float& value = m_Photon_energy_keV;
|
||||
const std::string currentValuePath = _pathPrefix + ".photonEnergyKeV";
|
||||
/* Incident_energy_keV */ {
|
||||
const float& value = m_Incident_energy_keV;
|
||||
const std::string currentValuePath = _pathPrefix + ".incidentEnergyKeV";
|
||||
|
||||
|
||||
if (value < static_cast<float>(0))
|
||||
@@ -206,10 +210,10 @@ bool Dataset_settings::validate(std::stringstream& msg, const std::string& pathP
|
||||
|
||||
}
|
||||
|
||||
if (photonEnergyMultiplierIsSet())
|
||||
if (energyMultiplierIsSet())
|
||||
{
|
||||
const float& value = m_Photon_energy_multiplier;
|
||||
const std::string currentValuePath = _pathPrefix + ".photonEnergyMultiplier";
|
||||
const float& value = m_Energy_multiplier;
|
||||
const std::string currentValuePath = _pathPrefix + ".energyMultiplier";
|
||||
|
||||
|
||||
if (value < static_cast<float>(0.015625))
|
||||
@@ -243,7 +247,21 @@ bool Dataset_settings::validate(std::stringstream& msg, const std::string& pathP
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (runNumberIsSet())
|
||||
{
|
||||
const int64_t& value = m_Run_number;
|
||||
const std::string currentValuePath = _pathPrefix + ".runNumber";
|
||||
|
||||
|
||||
if (value < 0ll)
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": must be greater than or equal to 0;";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -270,7 +288,7 @@ bool Dataset_settings::operator==(const Dataset_settings& rhs) const
|
||||
(getDetectorDistanceMm() == rhs.getDetectorDistanceMm())
|
||||
&&
|
||||
|
||||
(getPhotonEnergyKeV() == rhs.getPhotonEnergyKeV())
|
||||
(getIncidentEnergyKeV() == rhs.getIncidentEnergyKeV())
|
||||
&&
|
||||
|
||||
|
||||
@@ -298,7 +316,7 @@ bool Dataset_settings::operator==(const Dataset_settings& rhs) const
|
||||
((!transmissionIsSet() && !rhs.transmissionIsSet()) || (transmissionIsSet() && rhs.transmissionIsSet() && getTransmission() == rhs.getTransmission())) &&
|
||||
|
||||
|
||||
((!omegaIsSet() && !rhs.omegaIsSet()) || (omegaIsSet() && rhs.omegaIsSet() && getOmega() == rhs.getOmega())) &&
|
||||
((!goniometerIsSet() && !rhs.goniometerIsSet()) || (goniometerIsSet() && rhs.goniometerIsSet() && getGoniometer() == rhs.getGoniometer())) &&
|
||||
|
||||
|
||||
((!headerAppendixIsSet() && !rhs.headerAppendixIsSet()) || (headerAppendixIsSet() && rhs.headerAppendixIsSet() && getHeaderAppendix() == rhs.getHeaderAppendix())) &&
|
||||
@@ -307,12 +325,18 @@ bool Dataset_settings::operator==(const Dataset_settings& rhs) const
|
||||
((!imageAppendixIsSet() && !rhs.imageAppendixIsSet()) || (imageAppendixIsSet() && rhs.imageAppendixIsSet() && getImageAppendix() == rhs.getImageAppendix())) &&
|
||||
|
||||
|
||||
((!photonEnergyMultiplierIsSet() && !rhs.photonEnergyMultiplierIsSet()) || (photonEnergyMultiplierIsSet() && rhs.photonEnergyMultiplierIsSet() && getPhotonEnergyMultiplier() == rhs.getPhotonEnergyMultiplier())) &&
|
||||
((!energyMultiplierIsSet() && !rhs.energyMultiplierIsSet()) || (energyMultiplierIsSet() && rhs.energyMultiplierIsSet() && getEnergyMultiplier() == rhs.getEnergyMultiplier())) &&
|
||||
|
||||
|
||||
((!dataReductionFactorSerialmxIsSet() && !rhs.dataReductionFactorSerialmxIsSet()) || (dataReductionFactorSerialmxIsSet() && rhs.dataReductionFactorSerialmxIsSet() && getDataReductionFactorSerialmx() == rhs.getDataReductionFactorSerialmx())) &&
|
||||
|
||||
|
||||
((!runNumberIsSet() && !rhs.runNumberIsSet()) || (runNumberIsSet() && rhs.runNumberIsSet() && getRunNumber() == rhs.getRunNumber())) &&
|
||||
|
||||
|
||||
((!experimentGroupIsSet() && !rhs.experimentGroupIsSet()) || (experimentGroupIsSet() && rhs.experimentGroupIsSet() && getExperimentGroup() == rhs.getExperimentGroup())) &&
|
||||
|
||||
|
||||
((!unitCellIsSet() && !rhs.unitCellIsSet()) || (unitCellIsSet() && rhs.unitCellIsSet() && getUnitCell() == rhs.getUnitCell()))
|
||||
|
||||
;
|
||||
@@ -335,7 +359,7 @@ void to_json(nlohmann::json& j, const Dataset_settings& o)
|
||||
j["beam_x_pxl"] = o.m_Beam_x_pxl;
|
||||
j["beam_y_pxl"] = o.m_Beam_y_pxl;
|
||||
j["detector_distance_mm"] = o.m_Detector_distance_mm;
|
||||
j["photon_energy_keV"] = o.m_Photon_energy_keV;
|
||||
j["incident_energy_keV"] = o.m_Incident_energy_keV;
|
||||
if(o.filePrefixIsSet())
|
||||
j["file_prefix"] = o.m_File_prefix;
|
||||
if(o.imagesPerFileIsSet())
|
||||
@@ -351,16 +375,20 @@ void to_json(nlohmann::json& j, const Dataset_settings& o)
|
||||
j["total_flux"] = o.m_Total_flux;
|
||||
if(o.transmissionIsSet())
|
||||
j["transmission"] = o.m_Transmission;
|
||||
if(o.omegaIsSet())
|
||||
j["omega"] = o.m_Omega;
|
||||
if(o.goniometerIsSet())
|
||||
j["goniometer"] = o.m_Goniometer;
|
||||
if(o.headerAppendixIsSet())
|
||||
j["header_appendix"] = o.m_Header_appendix;
|
||||
if(o.imageAppendixIsSet())
|
||||
j["image_appendix"] = o.m_Image_appendix;
|
||||
if(o.photonEnergyMultiplierIsSet())
|
||||
j["photon_energy_multiplier"] = o.m_Photon_energy_multiplier;
|
||||
if(o.energyMultiplierIsSet())
|
||||
j["energy_multiplier"] = o.m_Energy_multiplier;
|
||||
if(o.dataReductionFactorSerialmxIsSet())
|
||||
j["data_reduction_factor_serialmx"] = o.m_Data_reduction_factor_serialmx;
|
||||
if(o.runNumberIsSet())
|
||||
j["run_number"] = o.m_Run_number;
|
||||
if(o.experimentGroupIsSet())
|
||||
j["experiment_group"] = o.m_Experiment_group;
|
||||
if(o.unitCellIsSet())
|
||||
j["unit_cell"] = o.m_Unit_cell;
|
||||
|
||||
@@ -386,7 +414,7 @@ void from_json(const nlohmann::json& j, Dataset_settings& o)
|
||||
j.at("beam_x_pxl").get_to(o.m_Beam_x_pxl);
|
||||
j.at("beam_y_pxl").get_to(o.m_Beam_y_pxl);
|
||||
j.at("detector_distance_mm").get_to(o.m_Detector_distance_mm);
|
||||
j.at("photon_energy_keV").get_to(o.m_Photon_energy_keV);
|
||||
j.at("incident_energy_keV").get_to(o.m_Incident_energy_keV);
|
||||
if(j.find("file_prefix") != j.end())
|
||||
{
|
||||
j.at("file_prefix").get_to(o.m_File_prefix);
|
||||
@@ -423,10 +451,10 @@ void from_json(const nlohmann::json& j, Dataset_settings& o)
|
||||
j.at("transmission").get_to(o.m_Transmission);
|
||||
o.m_TransmissionIsSet = true;
|
||||
}
|
||||
if(j.find("omega") != j.end())
|
||||
if(j.find("goniometer") != j.end())
|
||||
{
|
||||
j.at("omega").get_to(o.m_Omega);
|
||||
o.m_OmegaIsSet = true;
|
||||
j.at("goniometer").get_to(o.m_Goniometer);
|
||||
o.m_GoniometerIsSet = true;
|
||||
}
|
||||
if(j.find("header_appendix") != j.end())
|
||||
{
|
||||
@@ -438,16 +466,26 @@ void from_json(const nlohmann::json& j, Dataset_settings& o)
|
||||
j.at("image_appendix").get_to(o.m_Image_appendix);
|
||||
o.m_Image_appendixIsSet = true;
|
||||
}
|
||||
if(j.find("photon_energy_multiplier") != j.end())
|
||||
if(j.find("energy_multiplier") != j.end())
|
||||
{
|
||||
j.at("photon_energy_multiplier").get_to(o.m_Photon_energy_multiplier);
|
||||
o.m_Photon_energy_multiplierIsSet = true;
|
||||
j.at("energy_multiplier").get_to(o.m_Energy_multiplier);
|
||||
o.m_Energy_multiplierIsSet = true;
|
||||
}
|
||||
if(j.find("data_reduction_factor_serialmx") != j.end())
|
||||
{
|
||||
j.at("data_reduction_factor_serialmx").get_to(o.m_Data_reduction_factor_serialmx);
|
||||
o.m_Data_reduction_factor_serialmxIsSet = true;
|
||||
}
|
||||
if(j.find("run_number") != j.end())
|
||||
{
|
||||
j.at("run_number").get_to(o.m_Run_number);
|
||||
o.m_Run_numberIsSet = true;
|
||||
}
|
||||
if(j.find("experiment_group") != j.end())
|
||||
{
|
||||
j.at("experiment_group").get_to(o.m_Experiment_group);
|
||||
o.m_Experiment_groupIsSet = true;
|
||||
}
|
||||
if(j.find("unit_cell") != j.end())
|
||||
{
|
||||
j.at("unit_cell").get_to(o.m_Unit_cell);
|
||||
@@ -531,13 +569,13 @@ void Dataset_settings::setDetectorDistanceMm(float const value)
|
||||
{
|
||||
m_Detector_distance_mm = value;
|
||||
}
|
||||
float Dataset_settings::getPhotonEnergyKeV() const
|
||||
float Dataset_settings::getIncidentEnergyKeV() const
|
||||
{
|
||||
return m_Photon_energy_keV;
|
||||
return m_Incident_energy_keV;
|
||||
}
|
||||
void Dataset_settings::setPhotonEnergyKeV(float const value)
|
||||
void Dataset_settings::setIncidentEnergyKeV(float const value)
|
||||
{
|
||||
m_Photon_energy_keV = value;
|
||||
m_Incident_energy_keV = value;
|
||||
}
|
||||
std::string Dataset_settings::getFilePrefix() const
|
||||
{
|
||||
@@ -666,22 +704,22 @@ void Dataset_settings::unsetTransmission()
|
||||
{
|
||||
m_TransmissionIsSet = false;
|
||||
}
|
||||
org::openapitools::server::model::Rotation_axis Dataset_settings::getOmega() const
|
||||
org::openapitools::server::model::Rotation_axis Dataset_settings::getGoniometer() const
|
||||
{
|
||||
return m_Omega;
|
||||
return m_Goniometer;
|
||||
}
|
||||
void Dataset_settings::setOmega(org::openapitools::server::model::Rotation_axis const& value)
|
||||
void Dataset_settings::setGoniometer(org::openapitools::server::model::Rotation_axis const& value)
|
||||
{
|
||||
m_Omega = value;
|
||||
m_OmegaIsSet = true;
|
||||
m_Goniometer = value;
|
||||
m_GoniometerIsSet = true;
|
||||
}
|
||||
bool Dataset_settings::omegaIsSet() const
|
||||
bool Dataset_settings::goniometerIsSet() const
|
||||
{
|
||||
return m_OmegaIsSet;
|
||||
return m_GoniometerIsSet;
|
||||
}
|
||||
void Dataset_settings::unsetOmega()
|
||||
void Dataset_settings::unsetGoniometer()
|
||||
{
|
||||
m_OmegaIsSet = false;
|
||||
m_GoniometerIsSet = false;
|
||||
}
|
||||
std::string Dataset_settings::getHeaderAppendix() const
|
||||
{
|
||||
@@ -717,22 +755,22 @@ void Dataset_settings::unsetImage_appendix()
|
||||
{
|
||||
m_Image_appendixIsSet = false;
|
||||
}
|
||||
float Dataset_settings::getPhotonEnergyMultiplier() const
|
||||
float Dataset_settings::getEnergyMultiplier() const
|
||||
{
|
||||
return m_Photon_energy_multiplier;
|
||||
return m_Energy_multiplier;
|
||||
}
|
||||
void Dataset_settings::setPhotonEnergyMultiplier(float const value)
|
||||
void Dataset_settings::setEnergyMultiplier(float const value)
|
||||
{
|
||||
m_Photon_energy_multiplier = value;
|
||||
m_Photon_energy_multiplierIsSet = true;
|
||||
m_Energy_multiplier = value;
|
||||
m_Energy_multiplierIsSet = true;
|
||||
}
|
||||
bool Dataset_settings::photonEnergyMultiplierIsSet() const
|
||||
bool Dataset_settings::energyMultiplierIsSet() const
|
||||
{
|
||||
return m_Photon_energy_multiplierIsSet;
|
||||
return m_Energy_multiplierIsSet;
|
||||
}
|
||||
void Dataset_settings::unsetPhoton_energy_multiplier()
|
||||
void Dataset_settings::unsetEnergy_multiplier()
|
||||
{
|
||||
m_Photon_energy_multiplierIsSet = false;
|
||||
m_Energy_multiplierIsSet = false;
|
||||
}
|
||||
float Dataset_settings::getDataReductionFactorSerialmx() const
|
||||
{
|
||||
@@ -751,6 +789,40 @@ void Dataset_settings::unsetData_reduction_factor_serialmx()
|
||||
{
|
||||
m_Data_reduction_factor_serialmxIsSet = false;
|
||||
}
|
||||
int64_t Dataset_settings::getRunNumber() const
|
||||
{
|
||||
return m_Run_number;
|
||||
}
|
||||
void Dataset_settings::setRunNumber(int64_t const value)
|
||||
{
|
||||
m_Run_number = value;
|
||||
m_Run_numberIsSet = true;
|
||||
}
|
||||
bool Dataset_settings::runNumberIsSet() const
|
||||
{
|
||||
return m_Run_numberIsSet;
|
||||
}
|
||||
void Dataset_settings::unsetRun_number()
|
||||
{
|
||||
m_Run_numberIsSet = false;
|
||||
}
|
||||
std::string Dataset_settings::getExperimentGroup() const
|
||||
{
|
||||
return m_Experiment_group;
|
||||
}
|
||||
void Dataset_settings::setExperimentGroup(std::string const& value)
|
||||
{
|
||||
m_Experiment_group = value;
|
||||
m_Experiment_groupIsSet = true;
|
||||
}
|
||||
bool Dataset_settings::experimentGroupIsSet() const
|
||||
{
|
||||
return m_Experiment_groupIsSet;
|
||||
}
|
||||
void Dataset_settings::unsetExperiment_group()
|
||||
{
|
||||
m_Experiment_groupIsSet = false;
|
||||
}
|
||||
org::openapitools::server::model::Dataset_settings_unit_cell Dataset_settings::getUnitCell() const
|
||||
{
|
||||
return m_Unit_cell;
|
||||
|
||||
@@ -97,10 +97,10 @@ public:
|
||||
float getDetectorDistanceMm() const;
|
||||
void setDetectorDistanceMm(float const value);
|
||||
/// <summary>
|
||||
/// Used to calculate /entry/beam/incident_wavelength in NXmx Incident photon energy in keV
|
||||
/// Used to calculate /entry/beam/incident_wavelength in NXmx Incident particle (photon, electron) energy in keV
|
||||
/// </summary>
|
||||
float getPhotonEnergyKeV() const;
|
||||
void setPhotonEnergyKeV(float const value);
|
||||
float getIncidentEnergyKeV() const;
|
||||
void setIncidentEnergyKeV(float const value);
|
||||
/// <summary>
|
||||
/// Prefix for filenames. If left empty, no file will be saved.
|
||||
/// </summary>
|
||||
@@ -158,10 +158,10 @@ public:
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
org::openapitools::server::model::Rotation_axis getOmega() const;
|
||||
void setOmega(org::openapitools::server::model::Rotation_axis const& value);
|
||||
bool omegaIsSet() const;
|
||||
void unsetOmega();
|
||||
org::openapitools::server::model::Rotation_axis getGoniometer() const;
|
||||
void setGoniometer(org::openapitools::server::model::Rotation_axis const& value);
|
||||
bool goniometerIsSet() const;
|
||||
void unsetGoniometer();
|
||||
/// <summary>
|
||||
/// Header appendix, added as user_data to start message
|
||||
/// </summary>
|
||||
@@ -177,12 +177,12 @@ public:
|
||||
bool imageAppendixIsSet() const;
|
||||
void unsetImage_appendix();
|
||||
/// <summary>
|
||||
/// For JUNGFRAU conversion it is possible to multiply energy by a given factor to get fractional/multiplied photon counts
|
||||
/// For JUNGFRAU conversion it is possible to multiply incident energy by a given factor to get fractional/multiplied particle counts
|
||||
/// </summary>
|
||||
float getPhotonEnergyMultiplier() const;
|
||||
void setPhotonEnergyMultiplier(float const value);
|
||||
bool photonEnergyMultiplierIsSet() const;
|
||||
void unsetPhoton_energy_multiplier();
|
||||
float getEnergyMultiplier() const;
|
||||
void setEnergyMultiplier(float const value);
|
||||
bool energyMultiplierIsSet() const;
|
||||
void unsetEnergy_multiplier();
|
||||
/// <summary>
|
||||
/// Rate at which non-indexed images are accepted to be forwarded to writer. Value of 1.0 (default) means that all images are written. Values below zero mean that non-indexed images will be accepted with a given probability.
|
||||
/// </summary>
|
||||
@@ -191,6 +191,20 @@ public:
|
||||
bool dataReductionFactorSerialmxIsSet() const;
|
||||
void unsetData_reduction_factor_serialmx();
|
||||
/// <summary>
|
||||
/// Number of run within an experimental session. Transferred over CBOR stream as \"series ID\", though not saved in HDF5 file. It is highly recommended to keep this number unique for each data collection during experimental series. If not provided, the number will be automatically incremented.
|
||||
/// </summary>
|
||||
int64_t getRunNumber() const;
|
||||
void setRunNumber(int64_t const value);
|
||||
bool runNumberIsSet() const;
|
||||
void unsetRun_number();
|
||||
/// <summary>
|
||||
/// Name of group owning the data (e.g. p-group or proposal number). Transferred over CBOR stream, though not saved in HDF5 file.
|
||||
/// </summary>
|
||||
std::string getExperimentGroup() const;
|
||||
void setExperimentGroup(std::string const& value);
|
||||
bool experimentGroupIsSet() const;
|
||||
void unsetExperiment_group();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
org::openapitools::server::model::Dataset_settings_unit_cell getUnitCell() const;
|
||||
@@ -213,7 +227,7 @@ protected:
|
||||
|
||||
float m_Detector_distance_mm;
|
||||
|
||||
float m_Photon_energy_keV;
|
||||
float m_Incident_energy_keV;
|
||||
|
||||
std::string m_File_prefix;
|
||||
bool m_File_prefixIsSet;
|
||||
@@ -231,16 +245,20 @@ protected:
|
||||
bool m_Total_fluxIsSet;
|
||||
float m_Transmission;
|
||||
bool m_TransmissionIsSet;
|
||||
org::openapitools::server::model::Rotation_axis m_Omega;
|
||||
bool m_OmegaIsSet;
|
||||
org::openapitools::server::model::Rotation_axis m_Goniometer;
|
||||
bool m_GoniometerIsSet;
|
||||
std::string m_Header_appendix;
|
||||
bool m_Header_appendixIsSet;
|
||||
std::string m_Image_appendix;
|
||||
bool m_Image_appendixIsSet;
|
||||
float m_Photon_energy_multiplier;
|
||||
bool m_Photon_energy_multiplierIsSet;
|
||||
float m_Energy_multiplier;
|
||||
bool m_Energy_multiplierIsSet;
|
||||
float m_Data_reduction_factor_serialmx;
|
||||
bool m_Data_reduction_factor_serialmxIsSet;
|
||||
int64_t m_Run_number;
|
||||
bool m_Run_numberIsSet;
|
||||
std::string m_Experiment_group;
|
||||
bool m_Experiment_groupIsSet;
|
||||
org::openapitools::server::model::Dataset_settings_unit_cell m_Unit_cell;
|
||||
bool m_Unit_cellIsSet;
|
||||
|
||||
|
||||
@@ -23,6 +23,10 @@ Measurement_statistics::Measurement_statistics()
|
||||
{
|
||||
m_File_prefix = "";
|
||||
m_File_prefixIsSet = false;
|
||||
m_Run_number = 0L;
|
||||
m_Run_numberIsSet = false;
|
||||
m_Experiment_group = "";
|
||||
m_Experiment_groupIsSet = false;
|
||||
m_Images_expected = 0L;
|
||||
m_Images_expectedIsSet = false;
|
||||
m_Images_collected = 0L;
|
||||
@@ -51,6 +55,8 @@ Measurement_statistics::Measurement_statistics()
|
||||
m_Detector_pixel_depthIsSet = false;
|
||||
m_Bkg_estimate = 0.0f;
|
||||
m_Bkg_estimateIsSet = false;
|
||||
m_Unit_cell = "";
|
||||
m_Unit_cellIsSet = false;
|
||||
|
||||
}
|
||||
|
||||
@@ -73,7 +79,7 @@ bool Measurement_statistics::validate(std::stringstream& msg, const std::string&
|
||||
bool success = true;
|
||||
const std::string _pathPrefix = pathPrefix.empty() ? "Measurement_statistics" : pathPrefix;
|
||||
|
||||
|
||||
|
||||
if (collectionEfficiencyIsSet())
|
||||
{
|
||||
const float& value = m_Collection_efficiency;
|
||||
@@ -106,7 +112,7 @@ bool Measurement_statistics::validate(std::stringstream& msg, const std::string&
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -119,6 +125,12 @@ bool Measurement_statistics::operator==(const Measurement_statistics& rhs) const
|
||||
((!filePrefixIsSet() && !rhs.filePrefixIsSet()) || (filePrefixIsSet() && rhs.filePrefixIsSet() && getFilePrefix() == rhs.getFilePrefix())) &&
|
||||
|
||||
|
||||
((!runNumberIsSet() && !rhs.runNumberIsSet()) || (runNumberIsSet() && rhs.runNumberIsSet() && getRunNumber() == rhs.getRunNumber())) &&
|
||||
|
||||
|
||||
((!experimentGroupIsSet() && !rhs.experimentGroupIsSet()) || (experimentGroupIsSet() && rhs.experimentGroupIsSet() && getExperimentGroup() == rhs.getExperimentGroup())) &&
|
||||
|
||||
|
||||
((!imagesExpectedIsSet() && !rhs.imagesExpectedIsSet()) || (imagesExpectedIsSet() && rhs.imagesExpectedIsSet() && getImagesExpected() == rhs.getImagesExpected())) &&
|
||||
|
||||
|
||||
@@ -158,7 +170,10 @@ bool Measurement_statistics::operator==(const Measurement_statistics& rhs) const
|
||||
((!detectorPixelDepthIsSet() && !rhs.detectorPixelDepthIsSet()) || (detectorPixelDepthIsSet() && rhs.detectorPixelDepthIsSet() && getDetectorPixelDepth() == rhs.getDetectorPixelDepth())) &&
|
||||
|
||||
|
||||
((!bkgEstimateIsSet() && !rhs.bkgEstimateIsSet()) || (bkgEstimateIsSet() && rhs.bkgEstimateIsSet() && getBkgEstimate() == rhs.getBkgEstimate()))
|
||||
((!bkgEstimateIsSet() && !rhs.bkgEstimateIsSet()) || (bkgEstimateIsSet() && rhs.bkgEstimateIsSet() && getBkgEstimate() == rhs.getBkgEstimate())) &&
|
||||
|
||||
|
||||
((!unitCellIsSet() && !rhs.unitCellIsSet()) || (unitCellIsSet() && rhs.unitCellIsSet() && getUnitCell() == rhs.getUnitCell()))
|
||||
|
||||
;
|
||||
}
|
||||
@@ -173,6 +188,10 @@ void to_json(nlohmann::json& j, const Measurement_statistics& o)
|
||||
j = nlohmann::json();
|
||||
if(o.filePrefixIsSet())
|
||||
j["file_prefix"] = o.m_File_prefix;
|
||||
if(o.runNumberIsSet())
|
||||
j["run_number"] = o.m_Run_number;
|
||||
if(o.experimentGroupIsSet())
|
||||
j["experiment_group"] = o.m_Experiment_group;
|
||||
if(o.imagesExpectedIsSet())
|
||||
j["images_expected"] = o.m_Images_expected;
|
||||
if(o.imagesCollectedIsSet())
|
||||
@@ -201,6 +220,8 @@ void to_json(nlohmann::json& j, const Measurement_statistics& o)
|
||||
j["detector_pixel_depth"] = o.m_Detector_pixel_depth;
|
||||
if(o.bkgEstimateIsSet())
|
||||
j["bkg_estimate"] = o.m_Bkg_estimate;
|
||||
if(o.unitCellIsSet())
|
||||
j["unit_cell"] = o.m_Unit_cell;
|
||||
|
||||
}
|
||||
|
||||
@@ -211,6 +232,16 @@ void from_json(const nlohmann::json& j, Measurement_statistics& o)
|
||||
j.at("file_prefix").get_to(o.m_File_prefix);
|
||||
o.m_File_prefixIsSet = true;
|
||||
}
|
||||
if(j.find("run_number") != j.end())
|
||||
{
|
||||
j.at("run_number").get_to(o.m_Run_number);
|
||||
o.m_Run_numberIsSet = true;
|
||||
}
|
||||
if(j.find("experiment_group") != j.end())
|
||||
{
|
||||
j.at("experiment_group").get_to(o.m_Experiment_group);
|
||||
o.m_Experiment_groupIsSet = true;
|
||||
}
|
||||
if(j.find("images_expected") != j.end())
|
||||
{
|
||||
j.at("images_expected").get_to(o.m_Images_expected);
|
||||
@@ -281,6 +312,11 @@ void from_json(const nlohmann::json& j, Measurement_statistics& o)
|
||||
j.at("bkg_estimate").get_to(o.m_Bkg_estimate);
|
||||
o.m_Bkg_estimateIsSet = true;
|
||||
}
|
||||
if(j.find("unit_cell") != j.end())
|
||||
{
|
||||
j.at("unit_cell").get_to(o.m_Unit_cell);
|
||||
o.m_Unit_cellIsSet = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -301,6 +337,40 @@ void Measurement_statistics::unsetFile_prefix()
|
||||
{
|
||||
m_File_prefixIsSet = false;
|
||||
}
|
||||
int64_t Measurement_statistics::getRunNumber() const
|
||||
{
|
||||
return m_Run_number;
|
||||
}
|
||||
void Measurement_statistics::setRunNumber(int64_t const value)
|
||||
{
|
||||
m_Run_number = value;
|
||||
m_Run_numberIsSet = true;
|
||||
}
|
||||
bool Measurement_statistics::runNumberIsSet() const
|
||||
{
|
||||
return m_Run_numberIsSet;
|
||||
}
|
||||
void Measurement_statistics::unsetRun_number()
|
||||
{
|
||||
m_Run_numberIsSet = false;
|
||||
}
|
||||
std::string Measurement_statistics::getExperimentGroup() const
|
||||
{
|
||||
return m_Experiment_group;
|
||||
}
|
||||
void Measurement_statistics::setExperimentGroup(std::string const& value)
|
||||
{
|
||||
m_Experiment_group = value;
|
||||
m_Experiment_groupIsSet = true;
|
||||
}
|
||||
bool Measurement_statistics::experimentGroupIsSet() const
|
||||
{
|
||||
return m_Experiment_groupIsSet;
|
||||
}
|
||||
void Measurement_statistics::unsetExperiment_group()
|
||||
{
|
||||
m_Experiment_groupIsSet = false;
|
||||
}
|
||||
int64_t Measurement_statistics::getImagesExpected() const
|
||||
{
|
||||
return m_Images_expected;
|
||||
@@ -539,6 +609,23 @@ void Measurement_statistics::unsetBkg_estimate()
|
||||
{
|
||||
m_Bkg_estimateIsSet = false;
|
||||
}
|
||||
std::string Measurement_statistics::getUnitCell() const
|
||||
{
|
||||
return m_Unit_cell;
|
||||
}
|
||||
void Measurement_statistics::setUnitCell(std::string const& value)
|
||||
{
|
||||
m_Unit_cell = value;
|
||||
m_Unit_cellIsSet = true;
|
||||
}
|
||||
bool Measurement_statistics::unitCellIsSet() const
|
||||
{
|
||||
return m_Unit_cellIsSet;
|
||||
}
|
||||
void Measurement_statistics::unsetUnit_cell()
|
||||
{
|
||||
m_Unit_cellIsSet = false;
|
||||
}
|
||||
|
||||
|
||||
} // namespace org::openapitools::server::model
|
||||
|
||||
@@ -66,6 +66,20 @@ public:
|
||||
bool filePrefixIsSet() const;
|
||||
void unsetFile_prefix();
|
||||
/// <summary>
|
||||
/// Number of data collection run. This can be either automatically incremented or provided externally for each data collection.
|
||||
/// </summary>
|
||||
int64_t getRunNumber() const;
|
||||
void setRunNumber(int64_t const value);
|
||||
bool runNumberIsSet() const;
|
||||
void unsetRun_number();
|
||||
/// <summary>
|
||||
/// Name of group owning the data (e.g. p-group or proposal number).
|
||||
/// </summary>
|
||||
std::string getExperimentGroup() const;
|
||||
void setExperimentGroup(std::string const& value);
|
||||
bool experimentGroupIsSet() const;
|
||||
void unsetExperiment_group();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
int64_t getImagesExpected() const;
|
||||
@@ -163,12 +177,23 @@ public:
|
||||
void setBkgEstimate(float const value);
|
||||
bool bkgEstimateIsSet() const;
|
||||
void unsetBkg_estimate();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
std::string getUnitCell() const;
|
||||
void setUnitCell(std::string const& value);
|
||||
bool unitCellIsSet() const;
|
||||
void unsetUnit_cell();
|
||||
|
||||
friend void to_json(nlohmann::json& j, const Measurement_statistics& o);
|
||||
friend void from_json(const nlohmann::json& j, Measurement_statistics& o);
|
||||
protected:
|
||||
std::string m_File_prefix;
|
||||
bool m_File_prefixIsSet;
|
||||
int64_t m_Run_number;
|
||||
bool m_Run_numberIsSet;
|
||||
std::string m_Experiment_group;
|
||||
bool m_Experiment_groupIsSet;
|
||||
int64_t m_Images_expected;
|
||||
bool m_Images_expectedIsSet;
|
||||
int64_t m_Images_collected;
|
||||
@@ -197,6 +222,8 @@ protected:
|
||||
bool m_Detector_pixel_depthIsSet;
|
||||
float m_Bkg_estimate;
|
||||
bool m_Bkg_estimateIsSet;
|
||||
std::string m_Unit_cell;
|
||||
bool m_Unit_cellIsSet;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -26,11 +26,13 @@ Preview_settings::Preview_settings()
|
||||
m_Show_spotsIsSet = false;
|
||||
m_Show_roi = false;
|
||||
m_Show_roiIsSet = false;
|
||||
m_Jpeg_quality = 0L;
|
||||
m_Jpeg_quality = 100L;
|
||||
m_Jpeg_qualityIsSet = false;
|
||||
m_Show_indexed = false;
|
||||
m_Show_indexedIsSet = false;
|
||||
m_Resolution_ring = 0.0f;
|
||||
m_Show_user_mask = false;
|
||||
m_Show_user_maskIsSet = false;
|
||||
m_Resolution_ring = 0.1f;
|
||||
m_Resolution_ringIsSet = false;
|
||||
|
||||
}
|
||||
@@ -92,7 +94,7 @@ bool Preview_settings::validate(std::stringstream& msg, const std::string& pathP
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (resolutionRingIsSet())
|
||||
{
|
||||
const float& value = m_Resolution_ring;
|
||||
@@ -136,6 +138,9 @@ bool Preview_settings::operator==(const Preview_settings& rhs) const
|
||||
((!showIndexedIsSet() && !rhs.showIndexedIsSet()) || (showIndexedIsSet() && rhs.showIndexedIsSet() && isShowIndexed() == rhs.isShowIndexed())) &&
|
||||
|
||||
|
||||
((!showUserMaskIsSet() && !rhs.showUserMaskIsSet()) || (showUserMaskIsSet() && rhs.showUserMaskIsSet() && isShowUserMask() == rhs.isShowUserMask())) &&
|
||||
|
||||
|
||||
((!resolutionRingIsSet() && !rhs.resolutionRingIsSet()) || (resolutionRingIsSet() && rhs.resolutionRingIsSet() && getResolutionRing() == rhs.getResolutionRing()))
|
||||
|
||||
;
|
||||
@@ -158,6 +163,8 @@ void to_json(nlohmann::json& j, const Preview_settings& o)
|
||||
j["jpeg_quality"] = o.m_Jpeg_quality;
|
||||
if(o.showIndexedIsSet())
|
||||
j["show_indexed"] = o.m_Show_indexed;
|
||||
if(o.showUserMaskIsSet())
|
||||
j["show_user_mask"] = o.m_Show_user_mask;
|
||||
if(o.resolutionRingIsSet())
|
||||
j["resolution_ring"] = o.m_Resolution_ring;
|
||||
|
||||
@@ -186,6 +193,11 @@ void from_json(const nlohmann::json& j, Preview_settings& o)
|
||||
j.at("show_indexed").get_to(o.m_Show_indexed);
|
||||
o.m_Show_indexedIsSet = true;
|
||||
}
|
||||
if(j.find("show_user_mask") != j.end())
|
||||
{
|
||||
j.at("show_user_mask").get_to(o.m_Show_user_mask);
|
||||
o.m_Show_user_maskIsSet = true;
|
||||
}
|
||||
if(j.find("resolution_ring") != j.end())
|
||||
{
|
||||
j.at("resolution_ring").get_to(o.m_Resolution_ring);
|
||||
@@ -270,6 +282,23 @@ void Preview_settings::unsetShow_indexed()
|
||||
{
|
||||
m_Show_indexedIsSet = false;
|
||||
}
|
||||
bool Preview_settings::isShowUserMask() const
|
||||
{
|
||||
return m_Show_user_mask;
|
||||
}
|
||||
void Preview_settings::setShowUserMask(bool const value)
|
||||
{
|
||||
m_Show_user_mask = value;
|
||||
m_Show_user_maskIsSet = true;
|
||||
}
|
||||
bool Preview_settings::showUserMaskIsSet() const
|
||||
{
|
||||
return m_Show_user_maskIsSet;
|
||||
}
|
||||
void Preview_settings::unsetShow_user_mask()
|
||||
{
|
||||
m_Show_user_maskIsSet = false;
|
||||
}
|
||||
float Preview_settings::getResolutionRing() const
|
||||
{
|
||||
return m_Resolution_ring;
|
||||
|
||||
@@ -91,6 +91,13 @@ public:
|
||||
bool showIndexedIsSet() const;
|
||||
void unsetShow_indexed();
|
||||
/// <summary>
|
||||
/// Show user mask
|
||||
/// </summary>
|
||||
bool isShowUserMask() const;
|
||||
void setShowUserMask(bool const value);
|
||||
bool showUserMaskIsSet() const;
|
||||
void unsetShow_user_mask();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
float getResolutionRing() const;
|
||||
@@ -111,6 +118,8 @@ protected:
|
||||
bool m_Jpeg_qualityIsSet;
|
||||
bool m_Show_indexed;
|
||||
bool m_Show_indexedIsSet;
|
||||
bool m_Show_user_mask;
|
||||
bool m_Show_user_maskIsSet;
|
||||
float m_Resolution_ring;
|
||||
bool m_Resolution_ringIsSet;
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ namespace org::openapitools::server::model
|
||||
|
||||
Rotation_axis::Rotation_axis()
|
||||
{
|
||||
m_Name = "omega";
|
||||
m_NameIsSet = false;
|
||||
m_Step = 0.0f;
|
||||
m_Start = 0.0f;
|
||||
m_StartIsSet = false;
|
||||
@@ -47,6 +49,20 @@ bool Rotation_axis::validate(std::stringstream& msg, const std::string& pathPref
|
||||
bool success = true;
|
||||
const std::string _pathPrefix = pathPrefix.empty() ? "Rotation_axis" : pathPrefix;
|
||||
|
||||
|
||||
if (nameIsSet())
|
||||
{
|
||||
const std::string& value = m_Name;
|
||||
const std::string currentValuePath = _pathPrefix + ".name";
|
||||
|
||||
|
||||
if (value.length() < 1)
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": must be at least 1 characters long;";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (vectorIsSet())
|
||||
{
|
||||
@@ -87,6 +103,9 @@ bool Rotation_axis::operator==(const Rotation_axis& rhs) const
|
||||
return
|
||||
|
||||
|
||||
|
||||
((!nameIsSet() && !rhs.nameIsSet()) || (nameIsSet() && rhs.nameIsSet() && getName() == rhs.getName())) &&
|
||||
|
||||
(getStep() == rhs.getStep())
|
||||
&&
|
||||
|
||||
@@ -107,6 +126,8 @@ bool Rotation_axis::operator!=(const Rotation_axis& rhs) const
|
||||
void to_json(nlohmann::json& j, const Rotation_axis& o)
|
||||
{
|
||||
j = nlohmann::json();
|
||||
if(o.nameIsSet())
|
||||
j["name"] = o.m_Name;
|
||||
j["step"] = o.m_Step;
|
||||
if(o.startIsSet())
|
||||
j["start"] = o.m_Start;
|
||||
@@ -117,6 +138,11 @@ void to_json(nlohmann::json& j, const Rotation_axis& o)
|
||||
|
||||
void from_json(const nlohmann::json& j, Rotation_axis& o)
|
||||
{
|
||||
if(j.find("name") != j.end())
|
||||
{
|
||||
j.at("name").get_to(o.m_Name);
|
||||
o.m_NameIsSet = true;
|
||||
}
|
||||
j.at("step").get_to(o.m_Step);
|
||||
if(j.find("start") != j.end())
|
||||
{
|
||||
@@ -131,6 +157,23 @@ void from_json(const nlohmann::json& j, Rotation_axis& o)
|
||||
|
||||
}
|
||||
|
||||
std::string Rotation_axis::getName() const
|
||||
{
|
||||
return m_Name;
|
||||
}
|
||||
void Rotation_axis::setName(std::string const& value)
|
||||
{
|
||||
m_Name = value;
|
||||
m_NameIsSet = true;
|
||||
}
|
||||
bool Rotation_axis::nameIsSet() const
|
||||
{
|
||||
return m_NameIsSet;
|
||||
}
|
||||
void Rotation_axis::unsetName()
|
||||
{
|
||||
m_NameIsSet = false;
|
||||
}
|
||||
float Rotation_axis::getStep() const
|
||||
{
|
||||
return m_Step;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#define Rotation_axis_H_
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -58,6 +59,13 @@ public:
|
||||
/////////////////////////////////////////////
|
||||
/// Rotation_axis members
|
||||
|
||||
/// <summary>
|
||||
/// Name of rotation axis (e.g., omega, phi)
|
||||
/// </summary>
|
||||
std::string getName() const;
|
||||
void setName(std::string const& value);
|
||||
bool nameIsSet() const;
|
||||
void unsetName();
|
||||
/// <summary>
|
||||
/// Angle step in degrees
|
||||
/// </summary>
|
||||
@@ -81,6 +89,8 @@ public:
|
||||
friend void to_json(nlohmann::json& j, const Rotation_axis& o);
|
||||
friend void from_json(const nlohmann::json& j, Rotation_axis& o);
|
||||
protected:
|
||||
std::string m_Name;
|
||||
bool m_NameIsSet;
|
||||
float m_Step;
|
||||
|
||||
float m_Start;
|
||||
|
||||
@@ -23,6 +23,10 @@ Spot_finding_settings::Spot_finding_settings()
|
||||
{
|
||||
m_Enable = true;
|
||||
m_Indexing = true;
|
||||
m_Filter_powder_rings = false;
|
||||
m_Filter_powder_ringsIsSet = false;
|
||||
m_Min_spot_count_powder_ring = 0L;
|
||||
m_Min_spot_count_powder_ringIsSet = false;
|
||||
m_Signal_to_noise_threshold = 0.0f;
|
||||
m_Photon_count_threshold = 0L;
|
||||
m_Min_pix_per_spot = 0L;
|
||||
@@ -52,7 +56,21 @@ bool Spot_finding_settings::validate(std::stringstream& msg, const std::string&
|
||||
bool success = true;
|
||||
const std::string _pathPrefix = pathPrefix.empty() ? "Spot_finding_settings" : pathPrefix;
|
||||
|
||||
|
||||
|
||||
if (minSpotCountPowderRingIsSet())
|
||||
{
|
||||
const int64_t& value = m_Min_spot_count_powder_ring;
|
||||
const std::string currentValuePath = _pathPrefix + ".minSpotCountPowderRing";
|
||||
|
||||
|
||||
if (value < 5ll)
|
||||
{
|
||||
success = false;
|
||||
msg << currentValuePath << ": must be greater than or equal to 5;";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Signal_to_noise_threshold */ {
|
||||
const float& value = m_Signal_to_noise_threshold;
|
||||
@@ -142,6 +160,12 @@ bool Spot_finding_settings::operator==(const Spot_finding_settings& rhs) const
|
||||
(isIndexing() == rhs.isIndexing())
|
||||
&&
|
||||
|
||||
|
||||
((!filterPowderRingsIsSet() && !rhs.filterPowderRingsIsSet()) || (filterPowderRingsIsSet() && rhs.filterPowderRingsIsSet() && isFilterPowderRings() == rhs.isFilterPowderRings())) &&
|
||||
|
||||
|
||||
((!minSpotCountPowderRingIsSet() && !rhs.minSpotCountPowderRingIsSet()) || (minSpotCountPowderRingIsSet() && rhs.minSpotCountPowderRingIsSet() && getMinSpotCountPowderRing() == rhs.getMinSpotCountPowderRing())) &&
|
||||
|
||||
(getSignalToNoiseThreshold() == rhs.getSignalToNoiseThreshold())
|
||||
&&
|
||||
|
||||
@@ -176,6 +200,10 @@ void to_json(nlohmann::json& j, const Spot_finding_settings& o)
|
||||
j = nlohmann::json();
|
||||
j["enable"] = o.m_Enable;
|
||||
j["indexing"] = o.m_Indexing;
|
||||
if(o.filterPowderRingsIsSet())
|
||||
j["filter_powder_rings"] = o.m_Filter_powder_rings;
|
||||
if(o.minSpotCountPowderRingIsSet())
|
||||
j["min_spot_count_powder_ring"] = o.m_Min_spot_count_powder_ring;
|
||||
j["signal_to_noise_threshold"] = o.m_Signal_to_noise_threshold;
|
||||
j["photon_count_threshold"] = o.m_Photon_count_threshold;
|
||||
j["min_pix_per_spot"] = o.m_Min_pix_per_spot;
|
||||
@@ -190,6 +218,16 @@ void from_json(const nlohmann::json& j, Spot_finding_settings& o)
|
||||
{
|
||||
j.at("enable").get_to(o.m_Enable);
|
||||
j.at("indexing").get_to(o.m_Indexing);
|
||||
if(j.find("filter_powder_rings") != j.end())
|
||||
{
|
||||
j.at("filter_powder_rings").get_to(o.m_Filter_powder_rings);
|
||||
o.m_Filter_powder_ringsIsSet = true;
|
||||
}
|
||||
if(j.find("min_spot_count_powder_ring") != j.end())
|
||||
{
|
||||
j.at("min_spot_count_powder_ring").get_to(o.m_Min_spot_count_powder_ring);
|
||||
o.m_Min_spot_count_powder_ringIsSet = true;
|
||||
}
|
||||
j.at("signal_to_noise_threshold").get_to(o.m_Signal_to_noise_threshold);
|
||||
j.at("photon_count_threshold").get_to(o.m_Photon_count_threshold);
|
||||
j.at("min_pix_per_spot").get_to(o.m_Min_pix_per_spot);
|
||||
@@ -216,6 +254,40 @@ void Spot_finding_settings::setIndexing(bool const value)
|
||||
{
|
||||
m_Indexing = value;
|
||||
}
|
||||
bool Spot_finding_settings::isFilterPowderRings() const
|
||||
{
|
||||
return m_Filter_powder_rings;
|
||||
}
|
||||
void Spot_finding_settings::setFilterPowderRings(bool const value)
|
||||
{
|
||||
m_Filter_powder_rings = value;
|
||||
m_Filter_powder_ringsIsSet = true;
|
||||
}
|
||||
bool Spot_finding_settings::filterPowderRingsIsSet() const
|
||||
{
|
||||
return m_Filter_powder_ringsIsSet;
|
||||
}
|
||||
void Spot_finding_settings::unsetFilter_powder_rings()
|
||||
{
|
||||
m_Filter_powder_ringsIsSet = false;
|
||||
}
|
||||
int64_t Spot_finding_settings::getMinSpotCountPowderRing() const
|
||||
{
|
||||
return m_Min_spot_count_powder_ring;
|
||||
}
|
||||
void Spot_finding_settings::setMinSpotCountPowderRing(int64_t const value)
|
||||
{
|
||||
m_Min_spot_count_powder_ring = value;
|
||||
m_Min_spot_count_powder_ringIsSet = true;
|
||||
}
|
||||
bool Spot_finding_settings::minSpotCountPowderRingIsSet() const
|
||||
{
|
||||
return m_Min_spot_count_powder_ringIsSet;
|
||||
}
|
||||
void Spot_finding_settings::unsetMin_spot_count_powder_ring()
|
||||
{
|
||||
m_Min_spot_count_powder_ringIsSet = false;
|
||||
}
|
||||
float Spot_finding_settings::getSignalToNoiseThreshold() const
|
||||
{
|
||||
return m_Signal_to_noise_threshold;
|
||||
|
||||
@@ -58,16 +58,30 @@ public:
|
||||
/// Spot_finding_settings members
|
||||
|
||||
/// <summary>
|
||||
/// Enable spot finding
|
||||
/// Enable spot finding. This is temporary setting, i.e. can be changed anytime during data collection. Even if disabled spot finding information will still be send and written, though always with zero spots.
|
||||
/// </summary>
|
||||
bool isEnable() const;
|
||||
void setEnable(bool const value);
|
||||
/// <summary>
|
||||
/// Enable indexing
|
||||
/// Enable indexing. This is temporary setting, i.e. can be changed anytime during data collection.
|
||||
/// </summary>
|
||||
bool isIndexing() const;
|
||||
void setIndexing(bool const value);
|
||||
/// <summary>
|
||||
/// Filter spots which form powder rings (e.g., ice rings)
|
||||
/// </summary>
|
||||
bool isFilterPowderRings() const;
|
||||
void setFilterPowderRings(bool const value);
|
||||
bool filterPowderRingsIsSet() const;
|
||||
void unsetFilter_powder_rings();
|
||||
/// <summary>
|
||||
/// Minimum number of spots to consider a thin resolution shell (0.01 A^-1) a powder ring and filter out.
|
||||
/// </summary>
|
||||
int64_t getMinSpotCountPowderRing() const;
|
||||
void setMinSpotCountPowderRing(int64_t const value);
|
||||
bool minSpotCountPowderRingIsSet() const;
|
||||
void unsetMin_spot_count_powder_ring();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
float getSignalToNoiseThreshold() const;
|
||||
@@ -110,6 +124,10 @@ protected:
|
||||
|
||||
bool m_Indexing;
|
||||
|
||||
bool m_Filter_powder_rings;
|
||||
bool m_Filter_powder_ringsIsSet;
|
||||
int64_t m_Min_spot_count_powder_ring;
|
||||
bool m_Min_spot_count_powder_ringIsSet;
|
||||
float m_Signal_to_noise_threshold;
|
||||
|
||||
int64_t m_Photon_count_threshold;
|
||||
|
||||
@@ -11,6 +11,11 @@ components:
|
||||
required:
|
||||
- step
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
default: omega
|
||||
minLength: 1
|
||||
description: Name of rotation axis (e.g., omega, phi)
|
||||
step:
|
||||
type: number
|
||||
format: float
|
||||
@@ -37,7 +42,7 @@ components:
|
||||
- beam_x_pxl
|
||||
- beam_y_pxl
|
||||
- detector_distance_mm
|
||||
- photon_energy_keV
|
||||
- incident_energy_keV
|
||||
- sample_name
|
||||
properties:
|
||||
images_per_trigger:
|
||||
@@ -86,13 +91,13 @@ components:
|
||||
description:
|
||||
/entry/detector/distance in NXmx
|
||||
Detector distance [mm]
|
||||
photon_energy_keV:
|
||||
incident_energy_keV:
|
||||
type: number
|
||||
format: float
|
||||
minimum: 0
|
||||
description: |
|
||||
Used to calculate /entry/beam/incident_wavelength in NXmx
|
||||
Incident photon energy in keV
|
||||
Incident particle (photon, electron) energy in keV
|
||||
file_prefix:
|
||||
type: string
|
||||
default: ""
|
||||
@@ -148,7 +153,7 @@ components:
|
||||
description: |
|
||||
/entry/instrument/attenuator/attenuator_transmission
|
||||
Transmission of attenuator (filter) [no units]
|
||||
omega:
|
||||
goniometer:
|
||||
$ref: "#/components/schemas/rotation_axis"
|
||||
header_appendix:
|
||||
type: string
|
||||
@@ -156,13 +161,13 @@ components:
|
||||
image_appendix:
|
||||
type: string
|
||||
description: Image appendix, added as user_data to image message
|
||||
photon_energy_multiplier:
|
||||
energy_multiplier:
|
||||
type: number
|
||||
format: float
|
||||
default: 1.0
|
||||
minimum: 0.015625
|
||||
maximum: 4.0
|
||||
description: For JUNGFRAU conversion it is possible to multiply energy by a given factor to get fractional/multiplied photon counts
|
||||
description: For JUNGFRAU conversion it is possible to multiply incident energy by a given factor to get fractional/multiplied particle counts
|
||||
data_reduction_factor_serialmx:
|
||||
type: number
|
||||
format: float
|
||||
@@ -173,6 +178,20 @@ components:
|
||||
Rate at which non-indexed images are accepted to be forwarded to writer.
|
||||
Value of 1.0 (default) means that all images are written.
|
||||
Values below zero mean that non-indexed images will be accepted with a given probability.
|
||||
run_number:
|
||||
type: integer
|
||||
format: int64
|
||||
minimum: 0
|
||||
description: |
|
||||
Number of run within an experimental session.
|
||||
Transferred over CBOR stream as "series ID", though not saved in HDF5 file.
|
||||
It is highly recommended to keep this number unique for each data collection during experimental series.
|
||||
If not provided, the number will be automatically incremented.
|
||||
experiment_group:
|
||||
type: string
|
||||
description: |
|
||||
Name of group owning the data (e.g. p-group or proposal number).
|
||||
Transferred over CBOR stream, though not saved in HDF5 file.
|
||||
unit_cell:
|
||||
type: object
|
||||
description: Units of angstrom and degree
|
||||
@@ -235,7 +254,7 @@ components:
|
||||
powerchip:
|
||||
type: string
|
||||
description: Power on of ASICs
|
||||
enum: ["On", "Off", "Partial"]
|
||||
enum: ["PowerOn", "PowerOff", "Partial"]
|
||||
server_version:
|
||||
type: string
|
||||
description: Detector server (on read-out boards) version
|
||||
@@ -333,11 +352,23 @@ components:
|
||||
enable:
|
||||
type: boolean
|
||||
default: true
|
||||
description: Enable spot finding
|
||||
description: |
|
||||
Enable spot finding. This is temporary setting, i.e. can be changed anytime during data collection.
|
||||
Even if disabled spot finding information will still be send and written, though always with zero spots.
|
||||
indexing:
|
||||
type: boolean
|
||||
default: true
|
||||
description: Enable indexing
|
||||
description: |
|
||||
Enable indexing. This is temporary setting, i.e. can be changed anytime during data collection.
|
||||
filter_powder_rings:
|
||||
type: boolean
|
||||
default: false
|
||||
description: Filter spots which form powder rings (e.g., ice rings)
|
||||
min_spot_count_powder_ring:
|
||||
type: integer
|
||||
format: int64
|
||||
minimum: 5
|
||||
description: Minimum number of spots to consider a thin resolution shell (0.01 A^-1) a powder ring and filter out.
|
||||
signal_to_noise_threshold:
|
||||
type: number
|
||||
format: float
|
||||
@@ -447,6 +478,15 @@ components:
|
||||
properties:
|
||||
file_prefix:
|
||||
type: string
|
||||
run_number:
|
||||
type: integer
|
||||
format: int64
|
||||
description: |
|
||||
Number of data collection run. This can be either automatically incremented or provided externally for each data collection.
|
||||
experiment_group:
|
||||
type: string
|
||||
description: |
|
||||
Name of group owning the data (e.g. p-group or proposal number).
|
||||
images_expected:
|
||||
type: integer
|
||||
format: int64
|
||||
@@ -500,6 +540,8 @@ components:
|
||||
bkg_estimate:
|
||||
type: number
|
||||
format: float
|
||||
unit_cell:
|
||||
type: string
|
||||
broker_status:
|
||||
type: object
|
||||
required:
|
||||
@@ -618,14 +660,21 @@ components:
|
||||
type: integer
|
||||
description: "Quality of JPEG image (100 - highest; 0 - lowest)"
|
||||
format: int64
|
||||
default: 100
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
show_indexed:
|
||||
type: boolean
|
||||
description: "Preview indexed images only"
|
||||
default: false
|
||||
show_user_mask:
|
||||
type: boolean
|
||||
description: "Show user mask"
|
||||
default: false
|
||||
resolution_ring:
|
||||
type: number
|
||||
format: float
|
||||
default: 0.1
|
||||
minimum: 0.1
|
||||
maximum: 100.0
|
||||
error_message:
|
||||
@@ -965,9 +1014,9 @@ paths:
|
||||
Requires binary blob with 16-bit integer numbers of size of detector in raw/converted coordinates
|
||||
(depending on detector settings).
|
||||
parameters:
|
||||
- name: number
|
||||
- name: id
|
||||
in: query
|
||||
description: Image number to upload
|
||||
description: Image id to upload
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
@@ -990,6 +1039,38 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
description: Exception error
|
||||
/config/internal_generator_image.tiff:
|
||||
put:
|
||||
summary: Load TIFF image for internal FPGA generator
|
||||
description: |
|
||||
Load image for internal FPGA generator. This can only happen in Idle state of the detector.
|
||||
Requires TIFF with 16-bit integer numbers of size of detector in raw/converted coordinates
|
||||
(depending on detector settings).
|
||||
parameters:
|
||||
- in: query
|
||||
name: id
|
||||
description: Image ID to upload
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 127
|
||||
requestBody:
|
||||
content:
|
||||
image/tiff:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
responses:
|
||||
"200":
|
||||
description: Everything OK
|
||||
"400":
|
||||
description: Input parsing or validation error
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
description: Exception error
|
||||
/config/select_detector:
|
||||
put:
|
||||
summary: Select detector
|
||||
@@ -1562,6 +1643,63 @@ paths:
|
||||
format: binary
|
||||
"404":
|
||||
description: No preview image recorded so far
|
||||
/config/mask.tiff:
|
||||
get:
|
||||
summary: Get mask of the detector
|
||||
description: |
|
||||
Get full pixel mask of the detector
|
||||
See NXmx standard for meaning of pixel values
|
||||
responses:
|
||||
"200":
|
||||
description: Pixel mask in TIFF format (4 byte; unsigned)
|
||||
content:
|
||||
image/tiff:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
/config/user_mask.tiff:
|
||||
get:
|
||||
summary: Get user mask of the detector
|
||||
description: "Get user pixel mask of the detector in the actual detector coordinates: 0 - good pixel, 1 - masked"
|
||||
responses:
|
||||
"200":
|
||||
description: User mask in TIFF format (4 byte; unsigned)
|
||||
content:
|
||||
image/tiff:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
put:
|
||||
summary: Upload user mask of the detector
|
||||
description: |
|
||||
Should be in `Idle` state.
|
||||
Upload user mask of the detector - this is for example to account for beam stop shadow or misbehaving regions.
|
||||
If detector is conversion mode the mask can be both in raw (1024x512; stacked modules) or converted coordinates.
|
||||
In the latter case - module gaps are ignored and don't need to be assigned value.
|
||||
Mask is expected as TIFF (4-byte; unsigned).
|
||||
0 - good pixel, other value - masked
|
||||
User mask is stored in NXmx pixel mask (bit 8), as well as used in spot finding and azimuthal integration.
|
||||
User mask is not automatically applied - i.e. pixels with user mask will have a valid pixel value in the images.
|
||||
requestBody:
|
||||
content:
|
||||
application/octet-stream:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
responses:
|
||||
"200":
|
||||
description: All good
|
||||
content:
|
||||
image/tiff:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
"500":
|
||||
description: Error within Jungfraujoch code - see output message.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/error_message'
|
||||
/preview/pedestal.tiff:
|
||||
get:
|
||||
parameters:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Using OpenAPI licensed with Apache License 2.0
|
||||
|
||||
#include <vector>
|
||||
#include <signal.h>
|
||||
#include <csignal>
|
||||
#include <fstream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "JFJochBrokerHttp.h"
|
||||
|
||||
#include "JFJochBrokerParser.h"
|
||||
#include "../frame_serialize/ZMQStream2PusherGroup.h"
|
||||
#include "../frame_serialize/ZMQStream2Pusher.h"
|
||||
#include "../frame_serialize/DumpCBORToFilePusher.h"
|
||||
|
||||
static Pistache::Http::Endpoint *httpEndpoint;
|
||||
@@ -73,8 +73,6 @@ int main (int argc, char **argv) {
|
||||
std::unique_ptr<JFJochReceiverService> receiver;
|
||||
std::unique_ptr<ImagePusher> image_pusher;
|
||||
|
||||
ZMQContext context;
|
||||
|
||||
DiffractionExperiment experiment;
|
||||
experiment.MaskChipEdges(true).MaskModuleEdges(true);
|
||||
|
||||
@@ -88,9 +86,15 @@ int main (int argc, char **argv) {
|
||||
int32_t zmq_send_watermark = ParseInt32(input, "zmq_send_watermark", 100);
|
||||
int32_t zmq_send_buffer_size = ParseInt32(input, "zmq_send_buffer_size", -1);
|
||||
|
||||
image_pusher = std::make_unique<ZMQStream2PusherGroup>(ParseStringArray(input, "zmq_image_addr"),
|
||||
auto tmp = std::make_unique<ZMQStream2Pusher>(ParseStringArray(input, "zmq_image_addr"),
|
||||
zmq_send_watermark,
|
||||
zmq_send_buffer_size);
|
||||
|
||||
std::string preview_addr = ParseString(input, "zmq_preview_addr", "");
|
||||
if (!preview_addr.empty())
|
||||
tmp->PreviewSocket(preview_addr);
|
||||
|
||||
image_pusher = std::move(tmp);
|
||||
} else if (pusher_type == "dump_cbor") {
|
||||
image_pusher = std::make_unique<DumpCBORToFilePusher>();
|
||||
} else
|
||||
@@ -111,9 +115,9 @@ int main (int argc, char **argv) {
|
||||
|
||||
logger.Info("Source {} Instrument {} Default rotation axis {:.2f},{:.2f},{:.2f}",
|
||||
experiment.GetSourceName(), experiment.GetInstrumentName(),
|
||||
experiment.GetDefaultOmegaAxis().x,
|
||||
experiment.GetDefaultOmegaAxis().y,
|
||||
experiment.GetDefaultOmegaAxis().z);
|
||||
experiment.GetDefaultRotationAxis().x,
|
||||
experiment.GetDefaultRotationAxis().y,
|
||||
experiment.GetDefaultRotationAxis().z);
|
||||
|
||||
Pistache::Address addr(Pistache::Ipv4::any(), Pistache::Port(http_port));
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -23,14 +23,19 @@ MESSAGE(STATUS "Jungfraujoch version: ${PACKAGE_VERSION}")
|
||||
|
||||
CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/GitInfo.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/GitInfo.cpp" @ONLY)
|
||||
|
||||
ADD_LIBRARY( JFJochCommon STATIC
|
||||
ADD_LIBRARY(JFJochLogger STATIC
|
||||
Logger.cpp Logger.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/GitInfo.cpp GitInfo.h
|
||||
)
|
||||
|
||||
ADD_LIBRARY(JFJochZMQ STATIC ZMQWrappers.cpp ZMQWrappers.h)
|
||||
|
||||
ADD_LIBRARY(JFJochCommon STATIC
|
||||
Coord.cpp Coord.h
|
||||
DiffractionExperiment.cpp DiffractionExperiment.h
|
||||
RawToConvertedGeometry.h
|
||||
JFJochException.h
|
||||
Definitions.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/GitInfo.cpp GitInfo.h
|
||||
ThreadSafeFIFO.h
|
||||
DiffractionSpot.cpp DiffractionSpot.h
|
||||
StatusVector.h
|
||||
@@ -45,7 +50,6 @@ ADD_LIBRARY( JFJochCommon STATIC
|
||||
RawToConvertedGeometryCore.h
|
||||
Plot.h
|
||||
../fpga/pcie_driver/jfjoch_fpga.h
|
||||
ZMQWrappers.cpp ZMQWrappers.h
|
||||
DatasetSettings.cpp DatasetSettings.h
|
||||
ROIMap.cpp ROIMap.h
|
||||
ROIElement.cpp ROIElement.h
|
||||
@@ -57,7 +61,9 @@ ADD_LIBRARY( JFJochCommon STATIC
|
||||
PixelMask.cpp PixelMask.h
|
||||
)
|
||||
|
||||
TARGET_LINK_LIBRARIES(JFJochCommon Compression JFCalibration "$<BUILD_INTERFACE:libzmq-static>" -lrt)
|
||||
TARGET_LINK_LIBRARIES(JFJochCommon JFJochLogger Compression JFCalibration -lrt)
|
||||
|
||||
TARGET_LINK_LIBRARIES(JFJochZMQ "$<BUILD_INTERFACE:libzmq-static>")
|
||||
|
||||
IF (CMAKE_CUDA_COMPILER)
|
||||
TARGET_SOURCES(JFJochCommon PRIVATE CUDAWrapper.cu )
|
||||
|
||||
@@ -24,7 +24,6 @@ DatasetSettings::DatasetSettings() {
|
||||
space_group_number = 0; // not set
|
||||
compression = CompressionAlgorithm::BSHUF_LZ4;
|
||||
photon_energy_multiplier = 1.0f;
|
||||
omega_start = 0.0f;
|
||||
images_per_file = 1000;
|
||||
data_reduction_factor_serialmx = 1.0;
|
||||
}
|
||||
@@ -151,31 +150,25 @@ DatasetSettings &DatasetSettings::TotalFlux(const std::optional<float> &input) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
DatasetSettings &DatasetSettings::OmegaStep(const std::optional<float> &input) {
|
||||
DatasetSettings &DatasetSettings::Goniometer(const std::optional<GoniometerAxis> &input) {
|
||||
if (input) {
|
||||
check_finite("Omega step", input.value());
|
||||
check_finite("Rotation angle increment", input->increment);
|
||||
if (input->increment == 0.0f)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Angle increment cannot be zero");
|
||||
check_finite("Rotation angle start", input->start);
|
||||
goniometer = input;
|
||||
}
|
||||
|
||||
if (input && (input == 0.0f))
|
||||
omega_step.reset();
|
||||
else
|
||||
omega_step = input;
|
||||
return *this;
|
||||
}
|
||||
|
||||
DatasetSettings &DatasetSettings::OmegaStart(float input) {
|
||||
check_finite("Omega start", input);
|
||||
omega_start = input;
|
||||
return *this;
|
||||
}
|
||||
|
||||
DatasetSettings &DatasetSettings::OmegaAxis(const std::optional<Coord> &c) {
|
||||
DatasetSettings &DatasetSettings::RotationAxis(const std::optional<Coord> &c) {
|
||||
if (c) {
|
||||
if (c->Length() == 0.0)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Cannot use empty vector for omega");
|
||||
omega_rotation_axis = c->Normalize();
|
||||
rotation_axis = c->Normalize();
|
||||
} else
|
||||
omega_rotation_axis = c;
|
||||
rotation_axis = c;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -228,16 +221,12 @@ std::optional<float> DatasetSettings::GetTotalFlux() const {
|
||||
return total_flux;
|
||||
}
|
||||
|
||||
std::optional<float> DatasetSettings::GetOmegaStep() const {
|
||||
return omega_step;
|
||||
std::optional<GoniometerAxis> DatasetSettings::GetGoniometer() const {
|
||||
return goniometer;
|
||||
}
|
||||
|
||||
float DatasetSettings::GetOmegaStart() const {
|
||||
return omega_start;
|
||||
}
|
||||
|
||||
std::optional<Coord> DatasetSettings::GetOmegaAxis() const {
|
||||
return omega_rotation_axis;
|
||||
std::optional<Coord> DatasetSettings::GetRotationAxis() const {
|
||||
return rotation_axis;
|
||||
}
|
||||
|
||||
std::string DatasetSettings::GetHeaderAppendix() const {
|
||||
@@ -332,3 +321,25 @@ DatasetSettings &DatasetSettings::DataReductionFactorSerialMX(float input) {
|
||||
float DatasetSettings::GetDataReductionFactorSerialMX() const {
|
||||
return data_reduction_factor_serialmx;
|
||||
}
|
||||
|
||||
DatasetSettings &DatasetSettings::RunNumber(const std::optional<uint64_t> &input) {
|
||||
if (input) {
|
||||
check_min("Run number", input, 0);
|
||||
check_max("Run number", input, INT64_MAX);
|
||||
}
|
||||
run_number = input;
|
||||
return *this;
|
||||
}
|
||||
|
||||
DatasetSettings &DatasetSettings::ExperimentGroup(const std::string &input) {
|
||||
group = input;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::optional<uint64_t> DatasetSettings::GetRunNumber() const {
|
||||
return run_number;
|
||||
}
|
||||
|
||||
std::string DatasetSettings::GetExperimentGroup() const {
|
||||
return group;
|
||||
}
|
||||
|
||||
@@ -40,14 +40,16 @@ class DatasetSettings {
|
||||
|
||||
std::optional<float> total_flux;
|
||||
std::optional<float> attenuator_transmission;
|
||||
std::optional<float> omega_step;
|
||||
float omega_start;
|
||||
std::optional<GoniometerAxis> goniometer;
|
||||
std::string image_appendix;
|
||||
std::string header_appendix;
|
||||
|
||||
std::optional<Coord> omega_rotation_axis;
|
||||
std::optional<Coord> rotation_axis;
|
||||
|
||||
float data_reduction_factor_serialmx;
|
||||
|
||||
std::optional<uint64_t> run_number;
|
||||
std::string group;
|
||||
public:
|
||||
|
||||
DatasetSettings();
|
||||
@@ -65,9 +67,8 @@ public:
|
||||
DatasetSettings& SampleName(std::string input);
|
||||
DatasetSettings& AttenuatorTransmission(const std::optional<float> &input);
|
||||
DatasetSettings& TotalFlux(const std::optional<float> &input);
|
||||
DatasetSettings& OmegaStep(const std::optional<float> &input);
|
||||
DatasetSettings& OmegaStart(float input);
|
||||
DatasetSettings& OmegaAxis(const std::optional<Coord> &c);
|
||||
DatasetSettings& Goniometer(const std::optional<GoniometerAxis>& input);
|
||||
DatasetSettings& RotationAxis(const std::optional<Coord> &c);
|
||||
DatasetSettings& HeaderAppendix(const std::string& input);
|
||||
DatasetSettings& ImageAppendix(const std::string& input);
|
||||
DatasetSettings& PhotonEnergyMultiplayer(float input);
|
||||
@@ -75,12 +76,13 @@ public:
|
||||
DatasetSettings& FPGAOutputMode(FPGAPixelOutput input);
|
||||
DatasetSettings& ImagesPerFile(int64_t input);
|
||||
DatasetSettings& DataReductionFactorSerialMX(float input);
|
||||
DatasetSettings& RunNumber(const std::optional<uint64_t> &run_number);
|
||||
DatasetSettings& ExperimentGroup(const std::string &group);
|
||||
|
||||
std::optional<float> GetAttenuatorTransmission() const;
|
||||
std::optional<float> GetTotalFlux() const;
|
||||
std::optional<float> GetOmegaStep() const;
|
||||
float GetOmegaStart() const;
|
||||
std::optional<Coord> GetOmegaAxis() const;
|
||||
std::optional<GoniometerAxis> GetGoniometer() const;
|
||||
std::optional<Coord> GetRotationAxis() const;
|
||||
std::string GetHeaderAppendix() const;
|
||||
std::string GetImageAppendix() const;
|
||||
float GetPhotonEnergyMultiplier() const;
|
||||
@@ -104,6 +106,9 @@ public:
|
||||
int64_t GetImageNumPerTrigger() const;
|
||||
int64_t GetImagesPerFile() const;
|
||||
float GetDataReductionFactorSerialMX() const;
|
||||
|
||||
std::optional<uint64_t> GetRunNumber() const;
|
||||
std::string GetExperimentGroup() const;
|
||||
};
|
||||
|
||||
#endif //JUNGFRAUJOCH_DATASETSETTINGS_H
|
||||
|
||||
@@ -12,10 +12,12 @@
|
||||
#define CONVERTED_MODULE_SIZE (CONVERTED_MODULE_LINES * CONVERTED_MODULE_COLS)
|
||||
#define JUNGFRAU_PACKET_SIZE_BYTES (8192)
|
||||
|
||||
#define MIN_COUNT_TIME_IN_US 5
|
||||
#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_COUNT_TIME_IN_US 1980
|
||||
#define MIN_COUNT_TIME_IN_US 3
|
||||
|
||||
#define MIN_STORAGE_CELL_DELAY_IN_NS 2100
|
||||
#define READOUT_TIME_IN_US 20
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include "JFJochException.h"
|
||||
#include "RawToConvertedGeometry.h"
|
||||
#include "../fpga/pcie_driver/jfjoch_fpga.h"
|
||||
#include "../include/spdlog/fmt/fmt.h"
|
||||
#include "GitInfo.h"
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
@@ -28,8 +30,6 @@ DiffractionExperiment::DiffractionExperiment(const DetectorSetup& det_setup)
|
||||
internal_fpga_packet_generator = false;
|
||||
internal_fpga_packet_generator_images = 1;
|
||||
|
||||
debug_pixel_mask = false;
|
||||
|
||||
ndatastreams = 1;
|
||||
|
||||
frame_time = std::chrono::microseconds(MIN_FRAME_TIME_HALF_SPEED_IN_US);
|
||||
@@ -101,10 +101,13 @@ DiffractionExperiment &DiffractionExperiment::NumTriggers(int64_t input) {
|
||||
DiffractionExperiment &DiffractionExperiment::FrameTime(std::chrono::microseconds in_frame_time,
|
||||
std::chrono::microseconds in_count_time) {
|
||||
check_min("Frame time (us)", in_frame_time.count(), MIN_FRAME_TIME_FULL_SPEED_IN_US);
|
||||
check_max("Frame time (us)", in_frame_time.count(), MAX_FRAME_TIME);
|
||||
check_max("Count time (us)", in_count_time.count(), in_frame_time.count() - READOUT_TIME_IN_US);
|
||||
if (in_count_time.count() != 0) {
|
||||
check_min("Count time (us)", in_count_time.count(), MIN_COUNT_TIME_IN_US);
|
||||
check_max("Count time (us)", in_count_time.count(), MAX_COUNT_TIME_IN_US);
|
||||
} else {
|
||||
check_max("Count time (us)", in_frame_time.count() - READOUT_TIME_IN_US,
|
||||
MAX_COUNT_TIME_IN_US);
|
||||
}
|
||||
|
||||
frame_time = in_frame_time;
|
||||
@@ -597,6 +600,20 @@ std::optional<UnitCell> DiffractionExperiment::GetUnitCell() const {
|
||||
return dataset.GetUnitCell();
|
||||
}
|
||||
|
||||
std::string DiffractionExperiment::GetUnitCellString() const {
|
||||
auto uc = dataset.GetUnitCell();
|
||||
if (uc.has_value()) {
|
||||
return fmt::format("{:.1f}, {:.1f}, {:.1f}, {:.1f}, {:.1f} {:.1f}",
|
||||
uc.value().a,
|
||||
uc.value().b,
|
||||
uc.value().c,
|
||||
uc.value().alpha,
|
||||
uc.value().beta,
|
||||
uc.value().gamma);
|
||||
} else
|
||||
return "-";
|
||||
}
|
||||
|
||||
Coord DiffractionExperiment::LabCoord(float detector_x, float detector_y) const {
|
||||
// Assumes planar detector, 90 deg towards beam
|
||||
return {(detector_x - GetBeamX_pxl()) * GetPixelSize_mm() ,
|
||||
@@ -669,6 +686,7 @@ void DiffractionExperiment::CheckDataProcessingSettings(const SpotFindingSetting
|
||||
check_min("Spot finding low resolution limit", settings.low_resolution_limit, 1.0);
|
||||
check_max("Spot finding low resolution limit", settings.low_resolution_limit, 50.0);
|
||||
}
|
||||
check_min("min spot count for powder ring detection", settings.min_spot_count_powder_ring, 5);
|
||||
check_min("indexing tolerance", settings.indexing_tolerance, 0.0);
|
||||
check_max("indexing tolerance", settings.indexing_tolerance, 1.0);
|
||||
}
|
||||
@@ -715,6 +733,7 @@ void DiffractionExperiment::FillMessage(StartMessage &message) const {
|
||||
|
||||
message.source_name = GetSourceName();
|
||||
message.source_name_short = GetSourceNameShort();
|
||||
message.source_type = GetSourceType();
|
||||
|
||||
message.instrument_name = GetInstrumentName();
|
||||
message.instrument_name_short = GetInstrumentNameShort();
|
||||
@@ -724,19 +743,25 @@ void DiffractionExperiment::FillMessage(StartMessage &message) const {
|
||||
message.countrate_correction_enabled = false;
|
||||
message.flatfield_enabled = false;
|
||||
|
||||
if (GetOmegaStep())
|
||||
message.omega = GoniometerAxis{.increment = GetOmegaStep().value(), .start = GetOmegaStart(),
|
||||
.axis = {GetOmegaAxis().x, GetOmegaAxis().y, GetOmegaAxis().z}};
|
||||
else
|
||||
message.omega = GoniometerAxis{.increment = 0.0f};
|
||||
auto goniometer = dataset.GetGoniometer();
|
||||
if (goniometer) {
|
||||
message.goniometer = goniometer;
|
||||
message.rotation_axis[0] = GetRotationAxis().x;
|
||||
message.rotation_axis[1] = GetRotationAxis().y;
|
||||
message.rotation_axis[2] = GetRotationAxis().z;
|
||||
}
|
||||
|
||||
message.series_id = GetSeriesID();
|
||||
message.series_unique_id = GetSeriesIDString();
|
||||
message.series_id = GetRunNumber();
|
||||
message.series_unique_id = GetRunName();
|
||||
|
||||
message.gain_file_names = detector.GetGainFileNames();
|
||||
|
||||
for (const auto &[x, y]: roi_mask.GetROINameMap())
|
||||
message.roi_names.emplace_back(x);
|
||||
|
||||
message.data_reduction_factor_serialmx = GetDataReductionFactorSerialMX();
|
||||
message.experiment_group = dataset.GetExperimentGroup();
|
||||
message.jfjoch_release = jfjoch_version();
|
||||
}
|
||||
|
||||
float DiffractionExperiment::GetPixelSize_mm() const {
|
||||
@@ -753,6 +778,11 @@ DiffractionExperiment &DiffractionExperiment::SourceNameShort(std::string input)
|
||||
return *this;
|
||||
}
|
||||
|
||||
DiffractionExperiment &DiffractionExperiment::SourceType(std::string input) {
|
||||
source_type = input;
|
||||
return *this;
|
||||
}
|
||||
|
||||
DiffractionExperiment &DiffractionExperiment::InstrumentName(std::string input) {
|
||||
instrument_name = input;
|
||||
return *this;
|
||||
@@ -771,6 +801,10 @@ std::string DiffractionExperiment::GetSourceNameShort() const {
|
||||
return source_name_short;
|
||||
}
|
||||
|
||||
std::string DiffractionExperiment::GetSourceType() const {
|
||||
return source_type;
|
||||
}
|
||||
|
||||
std::string DiffractionExperiment::GetInstrumentName() const {
|
||||
return instrument_name;
|
||||
}
|
||||
@@ -933,22 +967,13 @@ std::optional<float> DiffractionExperiment::GetTotalFlux() const {
|
||||
return dataset.GetTotalFlux();
|
||||
}
|
||||
|
||||
DiffractionExperiment &DiffractionExperiment::OmegaStep(const std::optional<float> &input) {
|
||||
dataset.OmegaStep(input);
|
||||
DiffractionExperiment &DiffractionExperiment::Goniometer(const std::optional<GoniometerAxis> &input) {
|
||||
dataset.Goniometer(input);
|
||||
return *this;
|
||||
}
|
||||
|
||||
DiffractionExperiment &DiffractionExperiment::OmegaStart(float input) {
|
||||
dataset.OmegaStart(input);
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::optional<float> DiffractionExperiment::GetOmegaStep() const {
|
||||
return dataset.GetOmegaStep();
|
||||
}
|
||||
|
||||
float DiffractionExperiment::GetOmegaStart() const {
|
||||
return dataset.GetOmegaStart();
|
||||
std::optional<GoniometerAxis> DiffractionExperiment::GetGoniometer() const {
|
||||
return dataset.GetGoniometer();
|
||||
}
|
||||
|
||||
DiffractionExperiment &DiffractionExperiment::UsingGainHG0(bool input) {
|
||||
@@ -987,45 +1012,42 @@ std::string DiffractionExperiment::GetImageAppendix() const {
|
||||
return dataset.GetImageAppendix();
|
||||
}
|
||||
|
||||
DiffractionExperiment &DiffractionExperiment::OmegaAxis(const Coord &c) {
|
||||
dataset.OmegaAxis(c);
|
||||
DiffractionExperiment &DiffractionExperiment::RotationAxis(const std::optional<Coord> &c) {
|
||||
dataset.RotationAxis(c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
DiffractionExperiment &DiffractionExperiment::OmegaAxis() {
|
||||
dataset.OmegaAxis({});
|
||||
return *this;
|
||||
}
|
||||
|
||||
Coord DiffractionExperiment::GetOmegaAxis() const {
|
||||
auto tmp = dataset.GetOmegaAxis();
|
||||
Coord DiffractionExperiment::GetRotationAxis() const {
|
||||
auto tmp = dataset.GetRotationAxis();
|
||||
if (tmp)
|
||||
return tmp.value();
|
||||
else
|
||||
return default_omega_axis;
|
||||
}
|
||||
|
||||
DiffractionExperiment &DiffractionExperiment::DefaultOmegaAxis(const Coord &c) {
|
||||
DiffractionExperiment &DiffractionExperiment::DefaultRotationAxis(const Coord &c) {
|
||||
if (c.Length() == 0.0)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Cannot use empty vector for omega axis");
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Cannot use empty vector for goniometer axis");
|
||||
|
||||
default_omega_axis = c.Normalize();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Coord DiffractionExperiment::GetDefaultOmegaAxis() const {
|
||||
Coord DiffractionExperiment::GetDefaultRotationAxis() const {
|
||||
return default_omega_axis;
|
||||
}
|
||||
|
||||
uint64_t DiffractionExperiment::GetSeriesID() const {
|
||||
uint64_t DiffractionExperiment::GetRunNumber() const {
|
||||
if (dataset.GetRunNumber())
|
||||
return dataset.GetRunNumber().value();
|
||||
return series_id;
|
||||
}
|
||||
|
||||
std::string DiffractionExperiment::GetSeriesIDString() const {
|
||||
std::string DiffractionExperiment::GetRunName() const {
|
||||
return std::to_string(series_id) + ": " + dataset.GetFilePrefix();
|
||||
}
|
||||
|
||||
DiffractionExperiment &DiffractionExperiment::IncrementSeriesID() {
|
||||
DiffractionExperiment &DiffractionExperiment::IncrementRunNumber() {
|
||||
series_id++;
|
||||
return *this;
|
||||
}
|
||||
@@ -1052,6 +1074,7 @@ bool DiffractionExperiment::IsConversionOnFPGA() const {
|
||||
}
|
||||
|
||||
DiffractionExperiment &DiffractionExperiment::DetectorDelay(std::chrono::nanoseconds input) {
|
||||
check_min("Detector delay (us)", input.count(), 0);
|
||||
detector_delay = input;
|
||||
return *this;
|
||||
}
|
||||
@@ -1168,3 +1191,7 @@ DiffractionExperiment &DiffractionExperiment::DataReductionFactorSerialMX(float
|
||||
dataset.DataReductionFactorSerialMX(input);
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string DiffractionExperiment::GetExperimentGroup() const {
|
||||
return dataset.GetExperimentGroup();
|
||||
}
|
||||
|
||||
@@ -78,11 +78,11 @@ class DiffractionExperiment {
|
||||
|
||||
std::string source_name;
|
||||
std::string source_name_short;
|
||||
std::string source_type;
|
||||
std::string instrument_name;
|
||||
std::string instrument_name_short;
|
||||
bool pulsed_source;
|
||||
|
||||
bool debug_pixel_mask;
|
||||
Coord default_omega_axis;
|
||||
|
||||
bool conversion_on_fpga;
|
||||
@@ -133,14 +133,15 @@ public:
|
||||
DiffractionExperiment& StorageCellStart(int64_t input = 15);
|
||||
|
||||
DiffractionExperiment& SourceName(std::string input);
|
||||
DiffractionExperiment& SourceType(std::string input);
|
||||
DiffractionExperiment& SourceNameShort(std::string input);
|
||||
DiffractionExperiment& InstrumentName(std::string input);
|
||||
DiffractionExperiment& InstrumentNameShort(std::string input);
|
||||
DiffractionExperiment& DefaultOmegaAxis(const Coord &c);
|
||||
DiffractionExperiment& DefaultRotationAxis(const Coord &c);
|
||||
|
||||
DiffractionExperiment& UsingGainHG0(bool input);
|
||||
DiffractionExperiment& FixedGainG1(bool input);
|
||||
DiffractionExperiment& IncrementSeriesID();
|
||||
DiffractionExperiment& IncrementRunNumber();
|
||||
DiffractionExperiment& ConversionOnFPGA(bool input);
|
||||
DiffractionExperiment& PulsedSource(bool input);
|
||||
|
||||
@@ -157,10 +158,8 @@ public:
|
||||
DiffractionExperiment& SampleName(std::string input);
|
||||
DiffractionExperiment& AttenuatorTransmission(const std::optional<float> &input);
|
||||
DiffractionExperiment& TotalFlux(const std::optional<float> &input);
|
||||
DiffractionExperiment& OmegaStep(const std::optional<float> &input);
|
||||
DiffractionExperiment& OmegaStart(float input);
|
||||
DiffractionExperiment& OmegaAxis(const Coord &c);
|
||||
DiffractionExperiment& OmegaAxis();
|
||||
DiffractionExperiment& Goniometer(const std::optional<GoniometerAxis> &input);
|
||||
DiffractionExperiment& RotationAxis(const std::optional<Coord> &c);
|
||||
DiffractionExperiment& HeaderAppendix(const std::string& input);
|
||||
DiffractionExperiment& ImageAppendix(const std::string& input);
|
||||
DiffractionExperiment& PhotonEnergyMultiplayer(float input);
|
||||
@@ -253,6 +252,7 @@ public:
|
||||
|
||||
float GetPixelSize_mm() const;
|
||||
std::string GetSourceName() const;
|
||||
std::string GetSourceType() const;
|
||||
std::string GetSourceNameShort() const;
|
||||
std::string GetInstrumentName() const;
|
||||
std::string GetInstrumentNameShort() const;
|
||||
@@ -269,13 +269,13 @@ public:
|
||||
|
||||
int64_t GetUDPInterfaceCount() const;
|
||||
std::vector<DetectorModuleConfig> GetDetectorModuleConfig(const std::vector<AcquisitionDeviceNetConfig>& net_config) const;
|
||||
Coord GetDefaultOmegaAxis() const;
|
||||
Coord GetDefaultRotationAxis() const;
|
||||
|
||||
bool IsFixedGainG1() const;
|
||||
bool IsUsingGainHG0() const;
|
||||
|
||||
uint64_t GetSeriesID() const;
|
||||
std::string GetSeriesIDString() const;
|
||||
uint64_t GetRunNumber() const;
|
||||
std::string GetRunName() const;
|
||||
bool IsConversionOnFPGA() const;
|
||||
|
||||
const DetectorSetup& GetDetectorSetup() const;
|
||||
@@ -288,13 +288,14 @@ public:
|
||||
|
||||
std::optional<float> GetAttenuatorTransmission() const;
|
||||
std::optional<float> GetTotalFlux() const;
|
||||
std::optional<float> GetOmegaStep() const;
|
||||
float GetOmegaStart() const;
|
||||
Coord GetOmegaAxis() const;
|
||||
std::optional<GoniometerAxis> GetGoniometer() const;
|
||||
|
||||
Coord GetRotationAxis() const;
|
||||
std::string GetHeaderAppendix() const;
|
||||
std::string GetImageAppendix() const;
|
||||
float GetPhotonEnergyMultiplier() const;
|
||||
std::optional<UnitCell> GetUnitCell() const;
|
||||
std::string GetUnitCellString() const;
|
||||
int64_t GetSpaceGroupNumber() const;
|
||||
bool GetSaveCalibration() const;
|
||||
int64_t GetSummation() const;
|
||||
@@ -320,6 +321,7 @@ public:
|
||||
int64_t GetImagesPerFile() const;
|
||||
|
||||
float GetDataReductionFactorSerialMX() const;
|
||||
std::string GetExperimentGroup() const;
|
||||
};
|
||||
|
||||
#endif //DIFFRACTIONEXPERIMENT_H
|
||||
|
||||
@@ -46,15 +46,6 @@ NUMAHWPolicy::NUMAHWPolicy(const std::string &policy) : name(policy) {
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Unknown NUMA policy");
|
||||
}
|
||||
|
||||
NUMAHWPolicy::NUMAHWPolicy(const NUMAHWPolicy &other) : bindings(other.bindings), name(other.name), curr_thread(0) {}
|
||||
|
||||
NUMAHWPolicy &NUMAHWPolicy::operator=(const NUMAHWPolicy &other) {
|
||||
bindings = other.bindings;
|
||||
name = other.name;
|
||||
curr_thread = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
NUMABinding NUMAHWPolicy::GetBinding(uint32_t thread) {
|
||||
if (bindings.empty())
|
||||
return NUMABinding{.cpu_node = -1, .mem_node = -1, .gpu = -1};
|
||||
@@ -62,14 +53,6 @@ NUMABinding NUMAHWPolicy::GetBinding(uint32_t thread) {
|
||||
return bindings.at(thread % bindings.size());
|
||||
}
|
||||
|
||||
NUMABinding NUMAHWPolicy::GetBinding() {
|
||||
return GetBinding(curr_thread++);
|
||||
}
|
||||
|
||||
void NUMAHWPolicy::Bind() {
|
||||
Bind(GetBinding());
|
||||
}
|
||||
|
||||
void NUMAHWPolicy::Bind(uint32_t thread) {
|
||||
Bind(GetBinding(thread));
|
||||
}
|
||||
|
||||
@@ -17,19 +17,14 @@ struct NUMABinding {
|
||||
class NUMAHWPolicy {
|
||||
std::string name;
|
||||
std::vector<NUMABinding> bindings;
|
||||
std::atomic<uint32_t> curr_thread = 0;
|
||||
public:
|
||||
NUMAHWPolicy() = default;
|
||||
explicit NUMAHWPolicy(const std::string& policy);
|
||||
NUMAHWPolicy(const NUMAHWPolicy& other);
|
||||
NUMAHWPolicy& operator=(const NUMAHWPolicy& other);
|
||||
NUMABinding GetBinding(uint32_t thread);
|
||||
NUMABinding GetBinding(); // round-robin
|
||||
|
||||
const std::string &GetName() const;
|
||||
|
||||
void Bind(uint32_t thread);
|
||||
void Bind(); // round-robin
|
||||
static void Bind(const NUMABinding &binding);
|
||||
static void RunOnNode(int32_t cpu_node);
|
||||
static void MemOnNode(int32_t mem_node);
|
||||
|
||||
@@ -12,7 +12,7 @@ PixelMask::PixelMask(const DetectorSetup &detector)
|
||||
PixelMask::PixelMask(const DiffractionExperiment& experiment)
|
||||
: PixelMask(experiment.GetDetectorSetup()) {}
|
||||
|
||||
void PixelMask::LoadDetectorBadPixelMask(const std::vector<uint32_t> &input_mask, uint8_t bit) {
|
||||
void PixelMask::LoadMask(const std::vector<uint32_t> &input_mask, uint8_t bit) {
|
||||
if (input_mask.size() != mask.size())
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Input match doesn't fit the detector ");
|
||||
@@ -25,7 +25,11 @@ void PixelMask::LoadDetectorBadPixelMask(const std::vector<uint32_t> &input_mask
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint32_t> PixelMask::GetMask(const DiffractionExperiment &experiment) {
|
||||
void PixelMask::LoadDetectorBadPixelMask(const std::vector<uint32_t> &input_mask) {
|
||||
LoadMask(input_mask, ErrorPixelBit);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> PixelMask::GetMask(const DiffractionExperiment &experiment, bool conv) const {
|
||||
std::vector<uint32_t> mask_out = mask;
|
||||
|
||||
// apply edge mask
|
||||
@@ -39,7 +43,7 @@ std::vector<uint32_t> PixelMask::GetMask(const DiffractionExperiment &experiment
|
||||
|| (line == RAW_MODULE_LINES - 1)
|
||||
|| (col == 0)
|
||||
|| (col == RAW_MODULE_COLS - 1))
|
||||
mask_out[pixel] |= (1 << 30);
|
||||
mask_out[pixel] |= (1 << ModuleEdgePixelBit);
|
||||
}
|
||||
|
||||
if (experiment.GetMaskChipEdges()) {
|
||||
@@ -47,33 +51,39 @@ std::vector<uint32_t> PixelMask::GetMask(const DiffractionExperiment &experiment
|
||||
|| (col == 511) || (col == 512)
|
||||
|| (col == 767) || (col == 768)
|
||||
|| (line == 255) || (line== 256))
|
||||
mask_out[pixel] |= (1 << 31);
|
||||
mask_out[pixel] |= (1 << ChipGapPixelBit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (experiment.GetDetectorMode() == DetectorMode::Conversion) {
|
||||
std::vector<uint32_t> tmp(experiment.GetPixelsNum(), 1); // nonfuctional areas (i.e. gaps) are filled with 1
|
||||
if (conv && (experiment.GetDetectorMode() == DetectorMode::Conversion)) {
|
||||
std::vector<uint32_t> tmp(experiment.GetPixelsNum(), 1<<ModuleGapPixelBit); // nonfunctional areas (i.e. gaps) are filled with 1
|
||||
RawToConvertedGeometry<uint32_t, uint32_t>(experiment, tmp.data(), mask_out.data());
|
||||
return tmp;
|
||||
} else
|
||||
return mask_out;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> PixelMask::GetUserMask(const DiffractionExperiment &experiment, bool conv) const {
|
||||
std::vector<uint32_t> ret = GetMask(experiment, conv);
|
||||
for (auto &i: ret)
|
||||
i = ((i & (1 << UserMaskedPixelBit)) != 0) ? 1 : 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void PixelMask::LoadUserMask(const DiffractionExperiment &experiment,
|
||||
const std::vector<uint32_t> &in_mask,
|
||||
uint8_t bit) {
|
||||
const std::vector<uint32_t> &in_mask) {
|
||||
if (experiment.GetModulesNum() != nmodules)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Mismatch in module size");
|
||||
|
||||
if (in_mask.size() == nmodules * RAW_MODULE_SIZE) {
|
||||
LoadDetectorBadPixelMask(in_mask, bit);
|
||||
LoadMask(in_mask, UserMaskedPixelBit);
|
||||
} else if (in_mask.size() == experiment.GetPixelsNum()) {
|
||||
std::vector<uint32_t> raw_mask(nmodules * RAW_MODULE_SIZE);
|
||||
ConvertedToRawGeometry(experiment, raw_mask.data(), in_mask.data());
|
||||
LoadDetectorBadPixelMask(in_mask, bit);
|
||||
LoadMask(raw_mask, UserMaskedPixelBit);
|
||||
} else
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Size of input user mask invalid");
|
||||
|
||||
@@ -9,12 +9,21 @@
|
||||
class PixelMask {
|
||||
size_t nmodules;
|
||||
std::vector<uint32_t> mask;
|
||||
void LoadMask(const std::vector<uint32_t>& mask, uint8_t bit);
|
||||
public:
|
||||
// NXmx bits
|
||||
constexpr static const uint8_t ModuleGapPixelBit = 0;
|
||||
constexpr static const uint8_t ErrorPixelBit = 1;
|
||||
constexpr static const uint8_t UserMaskedPixelBit = 8;
|
||||
constexpr static const uint8_t ChipGapPixelBit = 31;
|
||||
constexpr static const uint8_t ModuleEdgePixelBit = 30;
|
||||
|
||||
PixelMask(const DetectorSetup& detector);
|
||||
PixelMask(const DiffractionExperiment& experiment);
|
||||
void LoadUserMask(const DiffractionExperiment& experiment, const std::vector<uint32_t>& mask, uint8_t bit);
|
||||
void LoadDetectorBadPixelMask(const std::vector<uint32_t>& mask, uint8_t bit);
|
||||
std::vector<uint32_t> GetMask(const DiffractionExperiment& experiment);
|
||||
void LoadUserMask(const DiffractionExperiment& experiment, const std::vector<uint32_t>& mask);
|
||||
void LoadDetectorBadPixelMask(const std::vector<uint32_t>& mask);
|
||||
std::vector<uint32_t> GetMask(const DiffractionExperiment& experiment, bool conv = true) const;
|
||||
std::vector<uint32_t> GetUserMask(const DiffractionExperiment& experiment, bool conv = true) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -7,9 +7,12 @@
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
enum class PlotType {BkgEstimate, RadInt, RadIntPerTimePoint, SpotCount, IndexingRate, IndexingRatePerTimePoint,
|
||||
ErrorPixels, ImageCollectionEfficiency, ReceiverDelay, ReceiverFreeSendBuf, StrongPixels,
|
||||
ROISum, ROIMaxCount, ROIPixels};
|
||||
enum class PlotType {
|
||||
BkgEstimate, RadInt, RadIntPerTimePoint, SpotCount, SpotCountInRings,
|
||||
IndexingRate, IndexingRatePerTimePoint,
|
||||
ErrorPixels, ImageCollectionEfficiency, ReceiverDelay, ReceiverFreeSendBuf, StrongPixels,
|
||||
ROISum, ROIMaxCount, ROIPixels
|
||||
};
|
||||
|
||||
struct PlotRequest {
|
||||
PlotType type;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <cmath>
|
||||
#include <optional>
|
||||
|
||||
#include "Plot.h"
|
||||
|
||||
@@ -37,6 +38,11 @@ template <class T> class StatusVector {
|
||||
return ret;
|
||||
}
|
||||
public:
|
||||
void AddElement(uint32_t id, std::optional<T> val) {
|
||||
if (val.has_value())
|
||||
AddElement(id, val.value());
|
||||
}
|
||||
|
||||
void AddElement(uint32_t id, T val) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (id >= content.size()) {
|
||||
|
||||
@@ -8,8 +8,7 @@ ZMQContext::ZMQContext() {
|
||||
|
||||
// Default is to have 2 I/O threads per ZMQ context
|
||||
if (zmq_ctx_set(context, ZMQ_IO_THREADS, 2) != 0)
|
||||
throw JFJochException(JFJochExceptionCategory::ZeroMQ,
|
||||
"Cannot set number of I/O threads");
|
||||
throw JFJochException(JFJochExceptionCategory::ZeroMQ, "Cannot set number of I/O threads");
|
||||
}
|
||||
|
||||
ZMQContext &ZMQContext::NumThreads(int32_t threads) {
|
||||
@@ -27,7 +26,7 @@ void *ZMQContext::GetContext() const {
|
||||
return context;
|
||||
}
|
||||
|
||||
ZMQSocket::ZMQSocket(ZMQContext &context, ZMQSocketType in_socket_type) : socket_type(in_socket_type) {
|
||||
ZMQSocket::ZMQSocket(ZMQSocketType in_socket_type) : socket_type(in_socket_type) {
|
||||
socket = zmq_socket(context.GetContext(), static_cast<int>(socket_type));
|
||||
|
||||
if (socket == nullptr)
|
||||
|
||||
@@ -42,14 +42,15 @@ public:
|
||||
|
||||
class ZMQSocket {
|
||||
std::mutex m;
|
||||
ZMQContext context;
|
||||
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();
|
||||
explicit ZMQSocket(ZMQSocketType socket_type);
|
||||
~ZMQSocket();
|
||||
void Connect(const std::string& addr);
|
||||
void Disconnect(const std::string& addr);
|
||||
void Bind(const std::string& addr);
|
||||
|
||||
@@ -1,6 +1,2 @@
|
||||
INSTALL(TARGETS sls_detector_put sls_detector_get RUNTIME)
|
||||
|
||||
ADD_LIBRARY(JFJochDetector STATIC DetectorWrapper.cpp DetectorWrapper.h)
|
||||
TARGET_LINK_LIBRARIES(JFJochDetector JFJochCommon slsSupportShared slsDetectorShared)
|
||||
|
||||
|
||||
TARGET_LINK_LIBRARIES(JFJochDetector JFJochCommon slsDetectorStatic slsSupportStatic)
|
||||
|
||||
@@ -13,13 +13,18 @@
|
||||
struct spot_finder_packet {
|
||||
ap_uint<768> data;
|
||||
ap_uint<32> mask;
|
||||
ap_uint<32> strong_pixel;
|
||||
ap_int<32> count_threshold;
|
||||
ap_uint<32> snr_threshold;
|
||||
ap_uint<1> user;
|
||||
ap_uint<1> last;
|
||||
};
|
||||
|
||||
void spot_finder_in_stream(STREAM_768 &data_in,
|
||||
hls::stream<ap_uint<32>> &mask_in,
|
||||
hls::stream<spot_finder_packet> &data_out) {
|
||||
hls::stream<spot_finder_packet> &data_out,
|
||||
volatile ap_int<32> &in_count_threshold,
|
||||
volatile ap_uint<32> &in_snr_threshold) {
|
||||
ap_uint<32> mask;
|
||||
packet_768_t packet_in;
|
||||
{
|
||||
@@ -33,12 +38,53 @@ void spot_finder_in_stream(STREAM_768 &data_in,
|
||||
while (!packet_in.user) {
|
||||
#pragma HLS PIPELINE II=1
|
||||
mask_in >> mask;
|
||||
data_out << spot_finder_packet{.data = packet_in.data, .mask = mask, .user = packet_in.user, .last = packet_in.last};
|
||||
data_out << spot_finder_packet{
|
||||
.data = packet_in.data,
|
||||
.mask = mask,
|
||||
.strong_pixel = 0,
|
||||
.count_threshold = in_count_threshold,
|
||||
.snr_threshold = in_snr_threshold,
|
||||
.user = packet_in.user,
|
||||
.last = packet_in.last
|
||||
};
|
||||
data_in >> packet_in;
|
||||
}
|
||||
data_out << spot_finder_packet{.data = packet_in.data, .mask = 0, .user = packet_in.user, .last = packet_in.last};
|
||||
}
|
||||
|
||||
void spot_finder_out_stream(hls::stream<spot_finder_packet> &data_in,
|
||||
STREAM_768 &data_out,
|
||||
hls::stream<ap_axiu<32,1,1,1>> &strong_pixel_out) {
|
||||
spot_finder_packet packet_in;
|
||||
{
|
||||
#pragma HLS PROTOCOL fixed
|
||||
data_in >> packet_in;
|
||||
ap_wait();
|
||||
data_out << packet_768_t{.data = packet_in.data, .user = packet_in.user, .last = packet_in.last};
|
||||
ap_wait();
|
||||
}
|
||||
|
||||
data_in >> packet_in;
|
||||
while (!packet_in.user) {
|
||||
ap_uint<32> count_threshold = packet_in.count_threshold;
|
||||
ap_uint<32> snr_threshold_u32 = packet_in.snr_threshold;
|
||||
for (int i = 0; i < RAW_MODULE_SIZE * sizeof(uint16_t) / 64; i++) {
|
||||
#pragma HLS PIPELINE II=1
|
||||
data_out << packet_768_t{.data = packet_in.data, .user = packet_in.user, .last = packet_in.last};
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = packet_in.strong_pixel, .user = 0};
|
||||
data_in >> packet_in;
|
||||
}
|
||||
|
||||
// Save module statistics
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = count_threshold, .user = 0};
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = snr_threshold_u32, .user = 0};
|
||||
for (int i = 0; i < 14;i++)
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = 0, .user = 0};
|
||||
}
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = 0, .user = 1};
|
||||
data_out << packet_768_t{.data = packet_in.data, .user = packet_in.user, .last = packet_in.last};
|
||||
}
|
||||
|
||||
ap_int<SUM_BITWIDTH> calc_sum(ap_int<24> val[32], ap_uint<32> mask) {
|
||||
#pragma HLS PIPELINE II=1
|
||||
ap_int<SUM_BITWIDTH> ret = 0;
|
||||
@@ -73,6 +119,8 @@ void spot_finder_prepare(hls::stream<spot_finder_packet> &data_in,
|
||||
hls::stream<ap_int<SUM_BITWIDTH>> &sum_out,
|
||||
hls::stream<ap_int<SUM2_BITWIDTH>> &sum2_out,
|
||||
hls::stream<ap_int<VALID_BITWIDTH>> &valid_out) {
|
||||
ap_uint<32> strong_pixel;
|
||||
|
||||
spot_finder_packet packet;
|
||||
{
|
||||
#pragma HLS PROTOCOL fixed
|
||||
@@ -95,6 +143,7 @@ void spot_finder_prepare(hls::stream<spot_finder_packet> &data_in,
|
||||
for (ap_uint<15> i = 0; i < RAW_MODULE_SIZE * sizeof(uint16_t) / 64; i++) {
|
||||
#pragma HLS PIPELINE II=1
|
||||
data_out << packet;
|
||||
|
||||
ap_int<24> val[32];
|
||||
unpack32(packet.data, val);
|
||||
ap_uint<32> mask = packet.mask;
|
||||
@@ -167,31 +216,28 @@ ap_uint<32> spot_finder_count_threshold(ap_int<24> val[32], ap_int<32> &count_th
|
||||
}
|
||||
|
||||
void spot_finder_apply_threshold(hls::stream<spot_finder_packet> &data_in,
|
||||
STREAM_768 &data_out,
|
||||
hls::stream<spot_finder_packet> &data_out,
|
||||
hls::stream<ap_int<SUM_BITWIDTH>> &sum_in,
|
||||
hls::stream<ap_int<SUM2_BITWIDTH>> &sum2_in,
|
||||
hls::stream<ap_int<VALID_BITWIDTH>> &valid_in,
|
||||
hls::stream<ap_axiu<32,1,1,1>> &strong_pixel_out,
|
||||
volatile ap_int<32> &in_count_threshold,
|
||||
volatile ap_uint<32> &in_snr_threshold) {
|
||||
hls::stream<ap_int<VALID_BITWIDTH>> &valid_in) {
|
||||
ap_uint<32> strong_pixel_prev;
|
||||
spot_finder_packet packet_in;
|
||||
{
|
||||
#pragma HLS PROTOCOL fixed
|
||||
data_in >> packet_in;
|
||||
ap_wait();
|
||||
data_out << packet_768_t{.data = packet_in.data, .user = packet_in.user, .last = packet_in.last};
|
||||
data_out << packet_in;
|
||||
ap_wait();
|
||||
}
|
||||
|
||||
|
||||
ap_int<SUM_BITWIDTH> sum[32];
|
||||
ap_int<SUM2_BITWIDTH> sum2[32];
|
||||
ap_int<VALID_BITWIDTH> valid[32];
|
||||
|
||||
data_in >> packet_in;
|
||||
while (!packet_in.user) {
|
||||
ap_int<32> count_threshold = in_count_threshold;
|
||||
ap_uint<32> snr_threshold_u32 = in_snr_threshold;
|
||||
ap_int<32> count_threshold = packet_in.count_threshold;
|
||||
ap_uint<32> snr_threshold_u32 = packet_in.snr_threshold;
|
||||
float_uint32 thr;
|
||||
thr.u = snr_threshold_u32;
|
||||
ap_ufixed<10,8, AP_RND_CONV> snr_threshold = thr.f;
|
||||
@@ -212,34 +258,28 @@ void spot_finder_apply_threshold(hls::stream<spot_finder_packet> &data_in,
|
||||
valid_in >> valid[i % 32];
|
||||
}
|
||||
|
||||
data_out << packet_768_t{.data = packet_in.data, .user = packet_in.user, .last = packet_in.last};
|
||||
ap_int<24> data_unpacked[32];
|
||||
unpack32(packet_in.data, data_unpacked);
|
||||
|
||||
ap_uint<32> strong_pixel = spot_finder_count_threshold(data_unpacked, count_threshold) &
|
||||
spot_finder_snr_threshold(data_unpacked, snr_threshold_2,
|
||||
sum[i % 32], sum2[i % 32], valid[i % 32]);
|
||||
|
||||
strong_pixel = strong_pixel & packet_in.mask;
|
||||
spot_finder_snr_threshold(data_unpacked, snr_threshold_2,
|
||||
sum[i % 32], sum2[i % 32], valid[i % 32]);
|
||||
|
||||
if ((snr_threshold == 0) && (count_threshold <= 0))
|
||||
strong_pixel = 0;
|
||||
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = strong_pixel, .user = 0};
|
||||
strong_pixel = strong_pixel & packet_in.mask;
|
||||
|
||||
packet_in.mask |= strong_pixel; // mask strong pixels
|
||||
packet_in.strong_pixel |= strong_pixel; // add strong pixels to the output
|
||||
|
||||
data_out << packet_in;
|
||||
data_in >> packet_in;
|
||||
}
|
||||
|
||||
// Save module statistics
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = count_threshold, .user = 0};
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = snr_threshold_u32, .user = 0};
|
||||
for (int i = 0; i < 14;i++)
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = 0, .user = 0};
|
||||
}
|
||||
strong_pixel_out << ap_axiu<32,1,1,1>{.data = 0, .user = 1};
|
||||
data_out << packet_768_t{.data = packet_in.data, .user = packet_in.user, .last = packet_in.last};
|
||||
data_out << packet_in;
|
||||
}
|
||||
|
||||
|
||||
void spot_finder(STREAM_768 &data_in,
|
||||
hls::stream<ap_uint<32>> &mask_in,
|
||||
STREAM_768 &data_out,
|
||||
@@ -256,22 +296,38 @@ void spot_finder(STREAM_768 &data_in,
|
||||
|
||||
hls::stream<spot_finder_packet, 2> data_0;
|
||||
hls::stream<spot_finder_packet, 1080> data_1;
|
||||
hls::stream<spot_finder_packet, 8> data_2;
|
||||
hls::stream<spot_finder_packet, 1080> data_3;
|
||||
hls::stream<spot_finder_packet, 8> data_4;
|
||||
|
||||
#pragma HLS BIND_STORAGE variable=data_1 type=fifo impl=bram
|
||||
#pragma HLS BIND_STORAGE variable=data_3 type=fifo impl=bram
|
||||
|
||||
hls::stream<ap_int<SUM_BITWIDTH>, 24> sum_0;
|
||||
hls::stream<ap_int<SUM2_BITWIDTH>, 24> sum2_0;
|
||||
hls::stream<ap_int<VALID_BITWIDTH>, 24> valid_0;
|
||||
|
||||
hls::stream<ap_int<SUM_BITWIDTH>, 24> sum_1;
|
||||
hls::stream<ap_int<SUM2_BITWIDTH>, 24> sum2_1;
|
||||
hls::stream<ap_int<VALID_BITWIDTH>, 24> valid_1;
|
||||
|
||||
#ifndef JFJOCH_HLS_NOSYNTH
|
||||
spot_finder_in_stream(data_in, mask_in, data_0);
|
||||
spot_finder_in_stream(data_in, mask_in, data_0, in_count_threshold, in_snr_threshold);
|
||||
spot_finder_prepare(data_0, data_1, sum_0, sum2_0, valid_0);
|
||||
spot_finder_apply_threshold(data_1, data_out, sum_0, sum2_0, valid_0, strong_pixel_out,
|
||||
in_count_threshold, in_snr_threshold);
|
||||
spot_finder_apply_threshold(data_1, data_2, sum_0, sum2_0, valid_0);
|
||||
spot_finder_prepare(data_2, data_3, sum_1, sum2_1, valid_1);
|
||||
spot_finder_apply_threshold(data_3, data_4, sum_1, sum2_1, valid_1);
|
||||
spot_finder_out_stream(data_4, data_out, strong_pixel_out);
|
||||
#else
|
||||
std::vector<std::thread> spot_finder_cores;
|
||||
spot_finder_cores.emplace_back([&] {spot_finder_in_stream(data_in, mask_in, data_0);});
|
||||
spot_finder_cores.emplace_back([&] {spot_finder_in_stream(data_in, mask_in, data_0, in_count_threshold,
|
||||
in_snr_threshold);});
|
||||
spot_finder_cores.emplace_back([&] {spot_finder_prepare(data_0, data_1, sum_0, sum2_0, valid_0);});
|
||||
spot_finder_cores.emplace_back([&] {spot_finder_apply_threshold(data_1, data_out, sum_0, sum2_0, valid_0,
|
||||
strong_pixel_out, in_count_threshold,
|
||||
in_snr_threshold);});
|
||||
spot_finder_cores.emplace_back([&] {spot_finder_apply_threshold(data_1, data_2, sum_0, sum2_0, valid_0);});
|
||||
spot_finder_cores.emplace_back([&] {spot_finder_prepare(data_2, data_3, sum_1, sum2_1, valid_1);});
|
||||
spot_finder_cores.emplace_back([&] {spot_finder_apply_threshold(data_3, data_4, sum_1, sum2_1, valid_1);});
|
||||
spot_finder_cores.emplace_back([&] {spot_finder_out_stream(data_4, data_out, strong_pixel_out);});
|
||||
|
||||
for (auto &i : spot_finder_cores)
|
||||
i.join();
|
||||
#endif
|
||||
|
||||
@@ -74,8 +74,8 @@ void spot_finder_mask(STREAM_768 &data_in,
|
||||
|| (((col == 0) || (col == 8) || (col == 16) || (col == 24)) && (j == 0))
|
||||
|| (pixel_val[j] == INT24_MIN)
|
||||
|| (pixel_val[j] == INT24_MAX)
|
||||
|| ((d[j] != 0) && (d[j] < min_d_value))
|
||||
|| ((d[j] != 0) && (d[j] > max_d_value)))
|
||||
|| (d[j] < min_d_value)
|
||||
|| (d[j] > max_d_value))
|
||||
mask_val[j] = 0;
|
||||
else
|
||||
mask_val[j] = 1;
|
||||
|
||||
@@ -4,7 +4,7 @@ TARGET_LINK_LIBRARIES(JFJochDevice JFJochCommon)
|
||||
|
||||
ADD_EXECUTABLE(jfjoch_pcie_status jfjoch_pcie_status.cpp)
|
||||
TARGET_LINK_LIBRARIES(jfjoch_pcie_status JFJochDevice )
|
||||
INSTALL(TARGETS jfjoch_pcie_status RUNTIME)
|
||||
INSTALL(TARGETS jfjoch_pcie_status RUNTIME COMPONENT jfjoch)
|
||||
|
||||
ADD_EXECUTABLE(jfjoch_pcie_set_network jfjoch_pcie_set_network.cpp)
|
||||
TARGET_LINK_LIBRARIES(jfjoch_pcie_set_network JFJochDevice )
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
PACKAGE_NAME=jfjoch
|
||||
PACKAGE_VERSION=0.1
|
||||
PACKAGE_VERSION=1.0.0
|
||||
|
||||
DEST_MODULE_LOCATION=/extra
|
||||
BUILT_MODULE_NAME=jfjoch
|
||||
@@ -7,4 +7,4 @@ BUILT_MODULE_LOCATION=src/
|
||||
|
||||
MAKE="'make' -C src/ all"
|
||||
CLEAN="'make' -C src/ clean"
|
||||
AUTOINSTALL="yes"
|
||||
AUTOINSTALL="yes"
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Copyright (2019-2023) Paul Scherrer Institute
|
||||
VERSION=0.1
|
||||
VERSION=1.0.0
|
||||
|
||||
mkdir -p /usr/src/jfjoch-${VERSION}/src
|
||||
cp dkms.conf /usr/src/jfjoch-${VERSION}
|
||||
cp *.c *.h Makefile ../../common/Definitions.h /usr/src/jfjoch-0.1/src
|
||||
sed -i "s,../../common/Definitions.h,Definitions.h," /usr/src/jfjoch-0.1/src/jfjoch_drv.h
|
||||
cp *.c *.h Makefile /usr/src/jfjoch-${VERSION}/src
|
||||
|
||||
dkms add -m jfjoch -v ${VERSION}
|
||||
dkms install -m jfjoch -v ${VERSION}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
MODULE_AUTHOR("Filip Leonarski; Paul Scherrer Institute");
|
||||
MODULE_DESCRIPTION("Jungfraujoch device module");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("0.1");
|
||||
MODULE_VERSION("1.0.0");
|
||||
|
||||
#define XDMA_GEN4_x8 (0x9048)
|
||||
#define XDMA_GEN3_x16 (0x903F)
|
||||
|
||||
@@ -54,12 +54,13 @@ int jfjoch_check_version(struct pci_dev *pdev) {
|
||||
release_level = ioread32(drvdata->bar0 + ACTION_CONFIG_OFFSET + ADDR_RELEASE_LEVEL);
|
||||
|
||||
if (action_type != JFJOCH_FPGA_MAGIC) {
|
||||
dev_err(dev, "Mismatch in JFJoch action type (%x)\n", action_type);
|
||||
dev_err(dev, "Very likely the FPGA is not flashed with Jungfraujoch design, given that action type register value is wrong (%x).\n", action_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (release_level != JFJOCH_FPGA_RELEASE) {
|
||||
dev_err(dev, "Mismatch in JFJoch release level (%x)\n", release_level);
|
||||
dev_err(dev, "Jungfraujoch FPGA is flashed with design made with release %x, while driver is compiled with release %x.\n", release_level, JFJOCH_FPGA_RELEASE);
|
||||
dev_err(dev, "Given FPGA releases have braking changes in FPGA-driver interface (register map, data structure size, etc.)\n it is not safe and not supported at the moment to operate multiple releases with one driver.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
|
||||
14
fpga/pcie_driver/postinstall.sh
Normal file
14
fpga/pcie_driver/postinstall.sh
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
# Taken from https://schneide.blog/2015/08/10/packaging-kernel-modulesdrivers-using-dkms/
|
||||
|
||||
VERSION="1.0.0"
|
||||
|
||||
occurrences=`/usr/sbin/dkms status | grep jfjoch | grep ${VERSION} | wc -l`
|
||||
|
||||
if [ ! occurrences > 0 ]; then
|
||||
/usr/sbin/dkms add -m jfjoch -v ${VERSION}
|
||||
fi
|
||||
/usr/sbin/dkms build -m jfjoch -v ${VERSION}
|
||||
/usr/sbin/dkms install -m jfjoch -v ${VERSION}
|
||||
|
||||
exit 0
|
||||
8
fpga/pcie_driver/preuninstall.sh
Normal file
8
fpga/pcie_driver/preuninstall.sh
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
# Taken from https://schneide.blog/2015/08/10/packaging-kernel-modulesdrivers-using-dkms/
|
||||
|
||||
VERSION="1.0.0"
|
||||
|
||||
/usr/sbin/dkms remove -m jfjoch -v ${VERSION} --all
|
||||
|
||||
exit 0
|
||||
@@ -456,6 +456,8 @@ namespace {
|
||||
message.user_data = GetCBORString(value);
|
||||
else if (key == "spots")
|
||||
GetCBORSpots(message, value);
|
||||
else if (key == "spot_count_in_rings")
|
||||
message.spot_count_in_rings = GetCBORUInt(value);
|
||||
else if (key == "az_int_profile")
|
||||
GetCBORFloatArray(value, message.az_int_profile);
|
||||
else if (key == "indexing_result")
|
||||
@@ -526,30 +528,38 @@ namespace {
|
||||
cborErr(cbor_value_leave_container(&value, &array_value));
|
||||
}
|
||||
|
||||
void ProcessGoniometerOmega(StartMessage &message, CborValue &value) {
|
||||
GoniometerAxis ProcessGoniometerOmega(std::string &name, CborValue &value) {
|
||||
GoniometerAxis ret;
|
||||
ret.name = name;
|
||||
|
||||
CborValue map_value;
|
||||
|
||||
cborErr(cbor_value_enter_container(&value, &map_value));
|
||||
while (!cbor_value_at_end(&map_value)) {
|
||||
auto key = GetCBORString(map_value);
|
||||
if (key == "increment")
|
||||
message.omega.increment = GetCBORFloat(map_value);
|
||||
ret.increment = GetCBORFloat(map_value);
|
||||
else if (key == "start")
|
||||
message.omega.start = GetCBORFloat(map_value);
|
||||
ret.start = GetCBORFloat(map_value);
|
||||
else
|
||||
cbor_value_advance(&value);
|
||||
}
|
||||
cborErr(cbor_value_leave_container(&value, &map_value));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ProcessGoniometerMap(StartMessage &message, CborValue &value) {
|
||||
CborValue map_value;
|
||||
|
||||
if (GetCBORMapLen(value) > 1)
|
||||
throw JFJochException(JFJochExceptionCategory::CBORError, "Max one rotation angle allowed");
|
||||
|
||||
cborErr(cbor_value_enter_container(&value, &map_value));
|
||||
while (!cbor_value_at_end(&map_value)) {
|
||||
GoniometerAxis axis;
|
||||
auto key = GetCBORString(map_value);
|
||||
if (key == "omega")
|
||||
ProcessGoniometerOmega(message, map_value);
|
||||
message.goniometer = ProcessGoniometerOmega(key, map_value);
|
||||
}
|
||||
cborErr(cbor_value_leave_container(&value, &map_value));
|
||||
}
|
||||
@@ -613,6 +623,8 @@ namespace {
|
||||
message.sample_name = j["sample_name"];
|
||||
if (j.contains("source_name"))
|
||||
message.source_name = j["source_name"];
|
||||
if (j.contains("source_type"))
|
||||
message.source_type = j["source_type"];
|
||||
if (j.contains("source_name_short"))
|
||||
message.source_name_short = j["source_name_short"];
|
||||
if (j.contains("instrument_name"))
|
||||
@@ -623,10 +635,10 @@ namespace {
|
||||
message.total_flux = j["total_flux"];
|
||||
if (j.contains("attenuator_transmission"))
|
||||
message.attenuator_transmission = j["attenuator_transmission"];
|
||||
if (j.contains("omega_rotation_axis") && j["omega_rotation_axis"].is_array() &&
|
||||
(j["omega_rotation_axis"].size() == 3)) {
|
||||
if (j.contains("rotation_axis") && j["rotation_axis"].is_array() &&
|
||||
(j["rotation_axis"].size() == 3)) {
|
||||
for (int i = 0; i < 3; i++)
|
||||
message.omega.axis[i] = j["omega_rotation_axis"][i];
|
||||
message.rotation_axis[i] = j["rotation_axis"][i];
|
||||
}
|
||||
if (j.contains("space_group_number"))
|
||||
message.space_group_number = j["space_group_number"];
|
||||
@@ -636,6 +648,13 @@ namespace {
|
||||
message.roi_names = j["roi_names"];
|
||||
if (j.contains("write_master_file"))
|
||||
message.write_master_file = j["write_master_file"];
|
||||
if (j.contains("data_reduction_factor_serialmx"))
|
||||
message.data_reduction_factor_serialmx = j["data_reduction_factor_serialmx"];
|
||||
if (j.contains("experiment_group"))
|
||||
message.experiment_group = j["experiment_group"];
|
||||
if (j.contains("jfjoch_release"))
|
||||
message.jfjoch_release = j["jfjoch_release"];
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
throw JFJochException(JFJochExceptionCategory::CBORError,
|
||||
"Cannot parse user_data as valid JSON " + std::string(e.what()));
|
||||
|
||||
@@ -215,10 +215,10 @@ inline void CBOR_ENC_AXIS(CborEncoder &encoder, const char* key, const float det
|
||||
cborErr(cbor_encoder_close_container(&encoder, &arrayEncoder));
|
||||
}
|
||||
|
||||
inline void CBOR_ENC_GONIOMETER(CborEncoder &encoder, const char* key, const GoniometerAxis &g) {
|
||||
inline void CBOR_ENC_GONIOMETER(CborEncoder &encoder, const GoniometerAxis &g) {
|
||||
CborEncoder mapEncoder;
|
||||
|
||||
cborErr(cbor_encode_text_stringz(&encoder, key));
|
||||
cborErr(cbor_encode_text_stringz(&encoder, g.name.c_str()));
|
||||
cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, 2));
|
||||
CBOR_ENC(mapEncoder, "increment", g.increment);
|
||||
CBOR_ENC(mapEncoder, "start", g.start);
|
||||
@@ -228,11 +228,12 @@ inline void CBOR_ENC_GONIOMETER(CborEncoder &encoder, const char* key, const Gon
|
||||
inline void CBOR_ENC_GONIOMETER_MAP(CborEncoder &encoder, const char* key, const StartMessage &msg) {
|
||||
CborEncoder mapEncoder;
|
||||
|
||||
cborErr(cbor_encode_text_stringz(&encoder, key));
|
||||
cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
|
||||
CBOR_ENC_GONIOMETER(mapEncoder, "omega", msg.omega);
|
||||
|
||||
cborErr(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
if (msg.goniometer) {
|
||||
cborErr(cbor_encode_text_stringz(&encoder, key));
|
||||
cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, 1));
|
||||
CBOR_ENC_GONIOMETER(mapEncoder, msg.goniometer.value());
|
||||
cborErr(cbor_encoder_close_container(&encoder, &mapEncoder));
|
||||
}
|
||||
}
|
||||
|
||||
inline void CBOR_ENC(CborEncoder &encoder, const char* key, const std::vector<std::string> &v) {
|
||||
@@ -302,6 +303,8 @@ inline void CBOR_ENC_START_USER_DATA(CborEncoder& encoder, const char* key,
|
||||
j["images_per_file"] = message.images_per_file;
|
||||
j["source_name"] = message.source_name;
|
||||
j["source_name_short"] = message.source_name_short;
|
||||
if (!message.source_type.empty())
|
||||
j["source_type"] = message.source_type;
|
||||
j["instrument_name"] = message.instrument_name;
|
||||
j["instrument_name_short"] = message.instrument_name_short;
|
||||
j["sample_name"] = message.sample_name;
|
||||
@@ -310,13 +313,17 @@ inline void CBOR_ENC_START_USER_DATA(CborEncoder& encoder, const char* key,
|
||||
j["attenuator_transmission"] = message.attenuator_transmission.value();
|
||||
if (message.total_flux)
|
||||
j["total_flux"] = message.total_flux.value();
|
||||
j["omega_rotation_axis"] = {message.omega.axis[0], message.omega.axis[1], message.omega.axis[2]};
|
||||
j["rotation_axis"] = {message.rotation_axis[0], message.rotation_axis[1], message.rotation_axis[2]};
|
||||
j["space_group_number"] = message.space_group_number;
|
||||
j["roi_names"] = message.roi_names;
|
||||
j["gain_file_names"] = message.gain_file_names;
|
||||
if (message.write_master_file)
|
||||
j["write_master_file"] = message.write_master_file.value();
|
||||
|
||||
if (message.data_reduction_factor_serialmx)
|
||||
j["data_reduction_factor_serialmx"] = message.data_reduction_factor_serialmx.value();
|
||||
if (message.experiment_group)
|
||||
j["experiment_group"] = message.experiment_group.value();
|
||||
j["jfjoch_release"] = message.jfjoch_release;
|
||||
auto str = j.dump();
|
||||
|
||||
CBOR_ENC(encoder, key, str);
|
||||
@@ -448,6 +455,8 @@ void CBORStream2Serializer::SerializeImage(const DataMessage& message) {
|
||||
CBOR_ENC_RATIONAL(mapEncoder, "end_time", message.timestamp + message.exptime, message.timestamp_base);
|
||||
|
||||
CBOR_ENC(mapEncoder, "spots", message.spots);
|
||||
CBOR_ENC(mapEncoder, "spot_count_in_rings", message.spot_count_in_rings);
|
||||
|
||||
CBOR_ENC(mapEncoder, "az_int_profile", message.az_int_profile);
|
||||
CBOR_ENC(mapEncoder, "indexing_result", message.indexing_result);
|
||||
if (!message.indexing_lattice.empty())
|
||||
|
||||
@@ -20,10 +20,8 @@ TARGET_LINK_LIBRARIES(CBORStream2FrameSerialize tinycbor)
|
||||
ADD_LIBRARY(ImagePusher STATIC
|
||||
ImagePusher.cpp ImagePusher.h
|
||||
TestImagePusher.cpp TestImagePusher.h
|
||||
ZMQStream2PusherGroup.cpp ZMQStream2PusherGroup.h
|
||||
ZMQStream2Pusher.cpp
|
||||
ZMQStream2Pusher.h
|
||||
ZMQStream2Pusher.cpp ZMQStream2Pusher.h
|
||||
DumpCBORToFilePusher.cpp
|
||||
DumpCBORToFilePusher.h)
|
||||
|
||||
TARGET_LINK_LIBRARIES(ImagePusher CBORStream2FrameSerialize JFJochCommon Compression)
|
||||
TARGET_LINK_LIBRARIES(ImagePusher JFJochZMQ CBORStream2FrameSerialize JFJochCommon Compression)
|
||||
@@ -45,6 +45,7 @@ struct DataMessage {
|
||||
float image_collection_efficiency;
|
||||
|
||||
std::vector<SpotToSave> spots;
|
||||
std::optional<uint64_t> spot_count_in_rings = 0;
|
||||
|
||||
std::vector<float> az_int_profile;
|
||||
float bkg_estimate;
|
||||
@@ -84,9 +85,9 @@ struct DataMessage {
|
||||
};
|
||||
|
||||
struct GoniometerAxis {
|
||||
std::string name;
|
||||
float increment;
|
||||
float start;
|
||||
float axis[3];
|
||||
};
|
||||
|
||||
struct StartMessage {
|
||||
@@ -143,9 +144,11 @@ struct StartMessage {
|
||||
|
||||
std::vector<std::string> roi_names;
|
||||
|
||||
GoniometerAxis omega;
|
||||
std::optional<GoniometerAxis> goniometer;
|
||||
float rotation_axis[3];
|
||||
float detector_translation[3];
|
||||
|
||||
std::string source_type;
|
||||
std::string source_name;
|
||||
std::string source_name_short;
|
||||
std::string instrument_name;
|
||||
@@ -168,6 +171,11 @@ struct StartMessage {
|
||||
}
|
||||
|
||||
std::string user_data;
|
||||
|
||||
std::optional<float> data_reduction_factor_serialmx;
|
||||
std::optional<std::string> experiment_group;
|
||||
|
||||
std::string jfjoch_release;
|
||||
};
|
||||
|
||||
struct EndMessage {
|
||||
|
||||
@@ -13,96 +13,98 @@ There are minor differences at the moment:
|
||||
|
||||
## Start message
|
||||
|
||||
| Field name | Type | Description | Present in DECTRIS format |
|
||||
|---------------------------|---------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|:-------------------------:|
|
||||
| type | String | value "start" | X |
|
||||
| magic_number | uint64 | Number used to describe version of the Jungfraujoch data interface - to allow to detect inconsistency between sender and receiver | |
|
||||
| detector_distance | float | Detector distance \[m\] | |
|
||||
| detector_translation | float | Detector translation vector \[m\] | X |
|
||||
| beam_center_x | float | Beam center in X direction \[pixels\] | X |
|
||||
| beam_center_y | float | Beam center in Y direction \[pixels\] | X |
|
||||
| number_of_images | uint64 | Number of images in the series | X |
|
||||
| image_size_x | uint64 | Image width \[pixels\] | X |
|
||||
| image_size_y | uint64 | Image height \[pixels\] | X |
|
||||
| incident_energy | float | X-ray energy \[eV\] | X |
|
||||
| incident_wavelength | float | X-ray wavelength \[Angstrom\] | X |
|
||||
| frame_time | float | Frame time, if multiple frames per trigger \[s\] | X |
|
||||
| count_time | float | Exposure time \[s\] | X |
|
||||
| saturation_value | int64 | Maximum valid sample value | X |
|
||||
| error_value | int64 | Value used in images to describe pixels that are in error state or missing | |
|
||||
| pixel_size_x | float | Pixel width \[m\] | X |
|
||||
| pixel_size_y | float | Pixel height \[m\] | X |
|
||||
| sensor_thickness | float | Sensor thickness \[m\] | X |
|
||||
| sensor_material | string | Sensor material | X |
|
||||
| arm_data | data | Approximate date of arming | X |
|
||||
| pixel_mask_enabled | bool | Pixel mask applied on images | X |
|
||||
| detector_description | string | Name of the detector | X |
|
||||
| series_unique_id | string | Unique text ID of the series | X |
|
||||
| series_id | uint64 | Unique numeric ID of the series | X |
|
||||
| goniometer | Map | See [DECTRIS documentation](https://github.com/dectris/documentation/tree/main/stream_v2) - only "omega" axis is supported | X |
|
||||
| pixel_mask | Map(string -> Image) | Pixel mask - multiple in case of storage cells | X |
|
||||
| channels | Array(string) | List of image channels | X |
|
||||
| max_spot_count | uint64 | Maximum number of spots identified in spot finding | |
|
||||
| storage_cell_number | uint64 | Number of storage cells used by JUNGFRAU | |
|
||||
| image_dtype | string | Pixel bit type (e.g. uint16) | X |
|
||||
| unit_cell | Array(6 * float) (optional) | Unit cell of the system: a, b, c \[angstrom\] and alpha, beta, gamma \[degree\] | |
|
||||
| az_int_bin_number | uint64 | Number of azimuthal integration bins | |
|
||||
| az_int_bin_to_q | Array(float) | Q value for each azimuthal integration bin \[angstrom^-1\] | |
|
||||
| summation | uint64 | Factor of frame summation | |
|
||||
| user_data | string | JSON string that can contain the following fields (among others): | X |
|
||||
| - source_name | string | Facility name | |
|
||||
| - source_name_short | string | Facility name (short) | |
|
||||
| - instrument_name | string | Instrument name | |
|
||||
| - instrument_name_short | string | Instrument name (short | |
|
||||
| - attenuator_transmission | float (optional) | Attenuator transmission \[\] | |
|
||||
| - total_flux | float (optional) | Total flux \[ph/s\] | |
|
||||
| - data_file_count | uint64 | Number of used data files | |
|
||||
| - file_prefix | string | File prefix | |
|
||||
| - sample_name | string | Name of the sample | |
|
||||
| - omega_rotation_axis | Array(float) | Omega rotation axis (array of size 3) | |
|
||||
| - space_group_number | uint64 | Space group number | |
|
||||
| - write_master_file | bool | With multiple sockets, it selects which socket will provide master file | |
|
||||
| Field name | Type | Description | Present in DECTRIS format |
|
||||
|---------------------------|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------|:-------------------------:|
|
||||
| type | String | value "start" | X |
|
||||
| magic_number | uint64 | Number used to describe version of the Jungfraujoch data interface - to allow to detect inconsistency between sender and receiver | |
|
||||
| detector_distance | float | Detector distance \[m\] | |
|
||||
| detector_translation | float | Detector translation vector \[m\] | X |
|
||||
| beam_center_x | float | Beam center in X direction \[pixels\] | X |
|
||||
| beam_center_y | float | Beam center in Y direction \[pixels\] | X |
|
||||
| number_of_images | uint64 | Number of images in the series | X |
|
||||
| image_size_x | uint64 | Image width \[pixels\] | X |
|
||||
| image_size_y | uint64 | Image height \[pixels\] | X |
|
||||
| incident_energy | float | X-ray energy \[eV\] | X |
|
||||
| incident_wavelength | float | X-ray wavelength \[Angstrom\] | X |
|
||||
| frame_time | float | Frame time, if multiple frames per trigger \[s\] | X |
|
||||
| count_time | float | Exposure time \[s\] | X |
|
||||
| saturation_value | int64 | Maximum valid sample value | X |
|
||||
| error_value | int64 | Value used in images to describe pixels that are in error state or missing | |
|
||||
| pixel_size_x | float | Pixel width \[m\] | X |
|
||||
| pixel_size_y | float | Pixel height \[m\] | X |
|
||||
| sensor_thickness | float | Sensor thickness \[m\] | X |
|
||||
| sensor_material | string | Sensor material | X |
|
||||
| arm_data | data | Approximate date of arming | X |
|
||||
| pixel_mask_enabled | bool | Pixel mask applied on images | X |
|
||||
| detector_description | string | Name of the detector | X |
|
||||
| series_unique_id | string | Unique text ID of the series | X |
|
||||
| series_id | uint64 | Unique numeric ID of the series | X |
|
||||
| goniometer | Map | See [DECTRIS documentation](https://github.com/dectris/documentation/tree/main/stream_v2) - only one axis is supported | X |
|
||||
| pixel_mask | Map(string -> Image) | Pixel mask - multiple in case of storage cells | X |
|
||||
| channels | Array(string) | List of image channels | X |
|
||||
| max_spot_count | uint64 | Maximum number of spots identified in spot finding | |
|
||||
| storage_cell_number | uint64 | Number of storage cells used by JUNGFRAU | |
|
||||
| image_dtype | string | Pixel bit type (e.g. uint16) | X |
|
||||
| unit_cell | Array(6 * float) (optional) | Unit cell of the system: a, b, c \[angstrom\] and alpha, beta, gamma \[degree\] | |
|
||||
| az_int_bin_number | uint64 | Number of azimuthal integration bins | |
|
||||
| az_int_bin_to_q | Array(float) | Q value for each azimuthal integration bin \[angstrom^-1\] | |
|
||||
| summation | uint64 | Factor of frame summation | |
|
||||
| user_data | string | JSON string that can contain the following fields (among others): | X |
|
||||
| - source_name | string | Facility name | |
|
||||
| - source_name_short | string | Facility name (short) | |
|
||||
| - source_type | string (optional) | Type of X-ray source (use NXsource/type values, for example "Synchrotron X-ray Source" or "Free-Electron Laser") | |
|
||||
| - instrument_name | string | Instrument name | |
|
||||
| - instrument_name_short | string | Instrument name (short) | |
|
||||
| - attenuator_transmission | float (optional) | Attenuator transmission \[\] | |
|
||||
| - total_flux | float (optional) | Total flux \[ph/s\] | |
|
||||
| - data_file_count | uint64 | Number of used data files | |
|
||||
| - file_prefix | string | File prefix | |
|
||||
| - sample_name | string | Name of the sample | |
|
||||
| - rotation_axis | Array(float) | Rotation axis vector (array of size 3) | |
|
||||
| - space_group_number | uint64 | Space group number | |
|
||||
| - write_master_file | bool | With multiple sockets, it selects which socket will provide master file | |
|
||||
| - experiment_group | string | ID of instrument user, e.g., p-group (SLS/SwissFEL) or proposal number | |
|
||||
|
||||
See [DECTRIS documentation](https://github.com/dectris/documentation/tree/main/stream_v2) for definition of Image as MultiDimArray with optional compression.
|
||||
|
||||
## Image message
|
||||
|
||||
| Field name | Type | Description | Present in DECTRIS format | Optional |
|
||||
|----------------------------|-----------------------------|-----------------------------------------------------------------------------------------------------------------------------------|:-------------------------:|:--------:|
|
||||
| type | String | value "image" | X | |
|
||||
| Field name | Type | Description | Present in DECTRIS format | Optional |
|
||||
|----------------------------|-----------------------------|--------------------------------------------------------------------------------------------------------------------------------|:-------------------------:|:--------:|
|
||||
| type | String | value "image" | X | |
|
||||
| magic_number | uint64 | Number used to describe version of the Jungfraujoch data interface - to allow to detect inconsistency between sender and receiver | | |
|
||||
| series_unique_id | string | Unique text ID of the series | X | |
|
||||
| series_id | uint64 | Unique numeric ID of the series | X | |
|
||||
| image_id | uint64 | Number of image within the series | X | |
|
||||
| real_time | Rational | Exposure time | X | |
|
||||
| start_time | Rational | Exposure start time (highly approximate) | X | |
|
||||
| end_time | Rational | Exposure end time (highly approximate) | X | |
|
||||
| spots | Array(object) | Spots: | | |
|
||||
| - x | float | position in x (pixels) | | |
|
||||
| - y | float | position in y (pixels) | | |
|
||||
| - I | float | intensity (photons) | | |
|
||||
| - indexed | bool | indexed solution | | |
|
||||
| az_int_profile | Array(float) | Azimuthal integration results, use az_int_bin_to_q from start message for legend | | |
|
||||
| indexing_result | bool | Indexing successful | | |
|
||||
| indexing_lattice | Array(float * 9) (optional) | Indexing result real lattice; present only if indexed | | |
|
||||
| xfel_pulse_id | uint64 | Bunch ID (for SwissFEL) | | X |
|
||||
| xfel_event_code | uint64 | Event code (for SwissFEL) | | X |
|
||||
| jf_info | uint64 | Detector info field | | |
|
||||
| receiver_aq_dev_delay | uint64 | Receiver internal delay | | |
|
||||
| receiver_free_send_buf | uint64 | Receiver internal number of available send buffers | | |
|
||||
| storage_cell | uint64 | Storage cell number | | |
|
||||
| saturated_pixel_count | uint64 | Saturated pixel count | | |
|
||||
| error_pixel_count | uint64 | Error pixel count | | |
|
||||
| strong_pixel_count | uint64 | Strong pixel count (first stage of spot finding) | | |
|
||||
| data_collection_efficiency | float | Image collection efficiency \[\] | | |
|
||||
| bkg_estimate | float | Mean value for pixels in resolution range from 3.0 to 5.0 A [\photons\] | | |
|
||||
| adu_histogram | Array(uint64) | ADU histogram | | |
|
||||
| corr_beam_x_pxl | float | Difference between provided and calculated beam center X (pixels) - requires indexing | | X |
|
||||
| corr_beam_y_pxl | float | Difference between provided and calculated beam center Y (pixels) - requires indexing | | X |
|
||||
| corr_det_dist_mm | float | Difference between provided and calculated detector distance (mm) - requires indexing | | X |
|
||||
| user_data | string | Optional user defined text information | | |
|
||||
| roi_sum | int64 | Sum of ROI rectangle \[photons\] | | X |
|
||||
| image | Map(string -> Image) | Image | | |
|
||||
| series_unique_id | string | Unique text ID of the series | X | |
|
||||
| series_id | uint64 | Unique numeric ID of the series | X | |
|
||||
| image_id | uint64 | Number of image within the series | X | |
|
||||
| real_time | Rational | Exposure time | X | |
|
||||
| start_time | Rational | Exposure start time (highly approximate) | X | |
|
||||
| end_time | Rational | Exposure end time (highly approximate) | X | |
|
||||
| spots | Array(object) | Spots: | | |
|
||||
| - x | float | position in x (pixels) | | |
|
||||
| - y | float | position in y (pixels) | | |
|
||||
| - I | float | intensity (photons) | | |
|
||||
| - indexed | bool | indexed solution | | |
|
||||
| az_int_profile | Array(float) | Azimuthal integration results, use az_int_bin_to_q from start message for legend | | |
|
||||
| indexing_result | bool | Indexing successful | | |
|
||||
| indexing_lattice | Array(float * 9) (optional) | Indexing result real lattice; present only if indexed | | |
|
||||
| xfel_pulse_id | uint64 | Bunch ID (for pulsed source, e.g., SwissFEL) | | X |
|
||||
| xfel_event_code | uint64 | Event code (for pulsed source, e.g., SwissFEL) | | X |
|
||||
| jf_info | uint64 | Detector info field | | |
|
||||
| receiver_aq_dev_delay | uint64 | Receiver internal delay | | |
|
||||
| receiver_free_send_buf | uint64 | Receiver internal number of available send buffers | | |
|
||||
| storage_cell | uint64 | Storage cell number | | |
|
||||
| saturated_pixel_count | uint64 | Saturated pixel count | | |
|
||||
| error_pixel_count | uint64 | Error pixel count | | |
|
||||
| strong_pixel_count | uint64 | Strong pixel count (first stage of spot finding) | | |
|
||||
| data_collection_efficiency | float | Image collection efficiency \[\] | | |
|
||||
| bkg_estimate | float | Mean value for pixels in resolution range from 3.0 to 5.0 A [\photons\] | | |
|
||||
| adu_histogram | Array(uint64) | ADU histogram | | |
|
||||
| corr_beam_x_pxl | float | Difference between provided and calculated beam center X (pixels) - requires indexing | | X |
|
||||
| corr_beam_y_pxl | float | Difference between provided and calculated beam center Y (pixels) - requires indexing | | X |
|
||||
| corr_det_dist_mm | float | Difference between provided and calculated detector distance (mm) - requires indexing | | X |
|
||||
| user_data | string | Optional user defined text information | | |
|
||||
| roi_sum | int64 | Sum of ROI rectangle \[photons\] | | X |
|
||||
| image | Map(string -> Image) | Image | | |
|
||||
|
||||
## End message
|
||||
|
||||
|
||||
@@ -1,66 +1,117 @@
|
||||
// Copyright (2019-2024) Paul Scherrer Institute
|
||||
|
||||
#include "ZMQStream2Pusher.h"
|
||||
#include "CBORStream2Serializer.h"
|
||||
|
||||
ZMQStream2Pusher::ZMQStream2Pusher(ZMQContext &context, const std::string &addr, int32_t send_buffer_high_watermark,
|
||||
int32_t send_buffer_size)
|
||||
: socket(context, ZMQSocketType::Push) {
|
||||
Bind(addr, send_buffer_high_watermark, send_buffer_size);
|
||||
}
|
||||
ZMQStream2Pusher::ZMQStream2Pusher(const std::vector<std::string> &addr,
|
||||
int32_t send_buffer_high_watermark, int32_t send_buffer_size)
|
||||
: serialization_buffer(256*1024*1024),
|
||||
serializer(serialization_buffer.data(), serialization_buffer.size()),
|
||||
preview_counter(std::chrono::seconds(1)) {
|
||||
if (addr.empty())
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "No writer ZMQ address provided");
|
||||
|
||||
ZMQStream2Pusher::ZMQStream2Pusher(const std::string &addr, int32_t send_buffer_high_watermark,
|
||||
int32_t send_buffer_size)
|
||||
: context(std::make_unique<ZMQContext>()),
|
||||
socket(*context, ZMQSocketType::Push) {
|
||||
Bind(addr, send_buffer_high_watermark, send_buffer_size);
|
||||
}
|
||||
|
||||
void ZMQStream2Pusher::Bind(const std::string &addr, int32_t send_buffer_high_watermark, int32_t send_buffer_size) {
|
||||
|
||||
if (send_buffer_size > 0)
|
||||
socket.SendBufferSize(send_buffer_size);
|
||||
if (send_buffer_high_watermark > 0)
|
||||
socket.SendWaterMark(send_buffer_high_watermark);
|
||||
socket.SendTimeout(std::chrono::seconds(5)); // 5 seconds should be more than enough to flush buffers and to still give fast response
|
||||
socket.Bind(addr);
|
||||
}
|
||||
|
||||
void ZMQStream2Pusher::StartDataCollection(StartMessage &message) {
|
||||
size_t approx_size = 1024*1024;
|
||||
for (const auto &x : message.pixel_mask)
|
||||
approx_size += x.size;
|
||||
|
||||
std::vector<uint8_t> serialization_buffer(approx_size);
|
||||
CBORStream2Serializer serializer(serialization_buffer.data(), serialization_buffer.size());
|
||||
serializer.SerializeSequenceStart(message);
|
||||
if (!socket.Send(serialization_buffer.data(), serializer.GetBufferSize(), true))
|
||||
throw JFJochException(JFJochExceptionCategory::ZeroMQ, "Timeout on pushing start message on addr " + GetAddress());
|
||||
for (const auto &a : addr) {
|
||||
auto s = std::make_unique<ZMQSocket>(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->SendTimeout(std::chrono::seconds(5)); // 5 seconds should be more than enough to flush buffers and to still give fast response
|
||||
s->Bind(a);
|
||||
socket.emplace_back(std::move(s));
|
||||
}
|
||||
}
|
||||
|
||||
bool ZMQStream2Pusher::SendImage(const uint8_t *image_data, size_t image_size, int64_t image_number) {
|
||||
return socket.Send(image_data, image_size, false);
|
||||
if (preview_socket) {
|
||||
if (preview_counter.GeneratePreview())
|
||||
preview_socket->Send(image_data, image_size, false);
|
||||
}
|
||||
|
||||
if (!socket.empty()) {
|
||||
auto socket_number = (image_number / images_per_file) % socket.size();
|
||||
return socket[socket_number]->Send(image_data, image_size, false);
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
void ZMQStream2Pusher::SendImage(const uint8_t *image_data, size_t image_size, int64_t image_number,
|
||||
ZeroCopyReturnValue *z) {
|
||||
socket.SendZeroCopy(image_data,image_size, z);
|
||||
ZeroCopyReturnValue *z) {
|
||||
if (preview_socket) {
|
||||
if (preview_counter.GeneratePreview())
|
||||
preview_socket->Send(image_data, image_size, false);
|
||||
}
|
||||
|
||||
if (!socket.empty()) {
|
||||
auto socket_number = (image_number / images_per_file) % socket.size();
|
||||
socket[socket_number]->SendZeroCopy(image_data, image_size, z);
|
||||
} else
|
||||
z->release();
|
||||
}
|
||||
|
||||
bool ZMQStream2Pusher::EndDataCollection(const EndMessage &message) {
|
||||
std::vector<uint8_t> serialization_buffer(80 * 1024 * 1024);
|
||||
CBORStream2Serializer serializer(serialization_buffer.data(), serialization_buffer.size());
|
||||
void ZMQStream2Pusher::StartDataCollection(StartMessage& message) {
|
||||
if (message.images_per_file < 1)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Images per file cannot be zero or negative");
|
||||
images_per_file = message.images_per_file;
|
||||
|
||||
serializer.SerializeSequenceEnd(message);
|
||||
return socket.Send(serialization_buffer.data(), serializer.GetBufferSize(), true); // Blocking
|
||||
serializer.SerializeSequenceStart(message);
|
||||
|
||||
for (auto &s: socket) {
|
||||
if (!s->Send(serialization_buffer.data(), serializer.GetBufferSize(), true))
|
||||
throw JFJochException(JFJochExceptionCategory::ZeroMQ, "Timeout on pushing start message on addr "
|
||||
+ s->GetEndpointName());
|
||||
if (message.write_master_file) {
|
||||
message.write_master_file = false;
|
||||
serializer.SerializeSequenceStart(message);
|
||||
}
|
||||
}
|
||||
|
||||
if (preview_socket)
|
||||
preview_socket->Send(serialization_buffer.data(), serializer.GetBufferSize(), true);
|
||||
}
|
||||
|
||||
bool ZMQStream2Pusher::SendCalibration(const CompressedImage &message) {
|
||||
std::vector<uint8_t> serialization_buffer(80 * 1024 * 1024);
|
||||
CBORStream2Serializer serializer(serialization_buffer.data(), serialization_buffer.size());
|
||||
if (socket.empty())
|
||||
return false;
|
||||
|
||||
serializer.SerializeCalibration(message);
|
||||
return socket.Send(serialization_buffer.data(), serializer.GetBufferSize(), true); // Blocking
|
||||
|
||||
return socket[0]->Send(serialization_buffer.data(), serializer.GetBufferSize(), true);
|
||||
}
|
||||
|
||||
std::string ZMQStream2Pusher::GetAddress() {
|
||||
return socket.GetEndpointName();
|
||||
bool ZMQStream2Pusher::EndDataCollection(const EndMessage& message) {
|
||||
serializer.SerializeSequenceEnd(message);
|
||||
|
||||
bool ret = true;
|
||||
for (auto &s: socket) {
|
||||
if (!s->Send(serialization_buffer.data(), serializer.GetBufferSize(), true))
|
||||
ret = false;
|
||||
}
|
||||
|
||||
if (preview_socket)
|
||||
preview_socket->Send(serialization_buffer.data(), serializer.GetBufferSize(), true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::string> ZMQStream2Pusher::GetAddress() {
|
||||
std::vector<std::string> ret;
|
||||
for (auto &p: socket)
|
||||
ret.push_back(p->GetEndpointName());
|
||||
return ret;
|
||||
}
|
||||
|
||||
ZMQStream2Pusher &ZMQStream2Pusher::PreviewSocket(const std::string &addr) {
|
||||
preview_socket = std::make_unique<ZMQSocket>(ZMQSocketType::Pub);
|
||||
preview_socket->Bind(addr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string ZMQStream2Pusher::GetPreviewAddress() {
|
||||
if (preview_socket)
|
||||
return preview_socket->GetEndpointName();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -3,30 +3,40 @@
|
||||
#ifndef JUNGFRAUJOCH_ZMQSTREAM2PUSHER_H
|
||||
#define JUNGFRAUJOCH_ZMQSTREAM2PUSHER_H
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "ImagePusher.h"
|
||||
#include "../common/ZMQWrappers.h"
|
||||
#include "../preview/PreviewCounter.h"
|
||||
|
||||
class ZMQStream2Pusher : public ImagePusher {
|
||||
std::unique_ptr<ZMQContext> context;
|
||||
ZMQSocket socket;
|
||||
public:
|
||||
ZMQStream2Pusher(ZMQContext& context,
|
||||
const std::string& addr,
|
||||
int32_t send_buffer_high_watermark = -1,
|
||||
int32_t send_buffer_size = -1);
|
||||
std::vector<uint8_t> serialization_buffer;
|
||||
CBORStream2Serializer serializer;
|
||||
|
||||
explicit ZMQStream2Pusher(const std::string& addr,
|
||||
std::vector<std::unique_ptr<ZMQSocket>> socket;
|
||||
|
||||
std::unique_ptr<ZMQSocket> preview_socket;
|
||||
PreviewCounter preview_counter;
|
||||
|
||||
int64_t images_per_file = 1;
|
||||
public:
|
||||
explicit ZMQStream2Pusher(const std::vector<std::string>& addr,
|
||||
int32_t send_buffer_high_watermark = -1,
|
||||
int32_t send_buffer_size = -1);
|
||||
void Bind(const std::string& addr, int32_t send_buffer_high_watermark, int32_t send_buffer_size);
|
||||
|
||||
ZMQStream2Pusher& PreviewSocket(const std::string& addr);
|
||||
std::string GetPreviewAddress();
|
||||
|
||||
std::vector<std::string> GetAddress();
|
||||
|
||||
// Strictly serial, as order of these is important
|
||||
void StartDataCollection(StartMessage& message) override;
|
||||
bool SendImage(const uint8_t *image_data, size_t image_size, int64_t image_number) override;
|
||||
void SendImage(const uint8_t *image_data, size_t image_size, int64_t image_number, ZeroCopyReturnValue *z) override;
|
||||
bool EndDataCollection(const EndMessage &message) override;
|
||||
bool EndDataCollection(const EndMessage& message) override;
|
||||
bool SendCalibration(const CompressedImage& message) override;
|
||||
|
||||
std::string GetAddress();
|
||||
// Thread-safe
|
||||
void SendImage(const uint8_t *image_data, size_t image_size, int64_t image_number, ZeroCopyReturnValue *z) override;
|
||||
bool SendImage(const uint8_t *image_data, size_t image_size, int64_t image_number) override;
|
||||
};
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_ZMQSTREAM2PUSHER_H
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
// Copyright (2019-2024) Paul Scherrer Institute
|
||||
|
||||
#include "ZMQStream2PusherGroup.h"
|
||||
#include "CBORStream2Serializer.h"
|
||||
|
||||
ZMQStream2PusherGroup::ZMQStream2PusherGroup(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)
|
||||
pusher.emplace_back(std::make_unique<ZMQStream2Pusher>
|
||||
(zmq_context, a, send_buffer_high_watermark, send_buffer_size));
|
||||
}
|
||||
|
||||
ZMQStream2PusherGroup::ZMQStream2PusherGroup(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)
|
||||
pusher.emplace_back(std::make_unique<ZMQStream2Pusher>
|
||||
(a, send_buffer_high_watermark, send_buffer_size));
|
||||
}
|
||||
|
||||
bool ZMQStream2PusherGroup::SendImage(const uint8_t *image_data, size_t image_size, int64_t image_number) {
|
||||
if (!pusher.empty()) {
|
||||
auto socket_number = (image_number / images_per_file) % pusher.size();
|
||||
return pusher[socket_number]->SendImage(image_data, image_size, image_number);
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
void ZMQStream2PusherGroup::SendImage(const uint8_t *image_data, size_t image_size, int64_t image_number,
|
||||
ZeroCopyReturnValue *z) {
|
||||
if (!pusher.empty()) {
|
||||
auto socket_number = (image_number / images_per_file) % pusher.size();
|
||||
pusher[socket_number]->SendImage(image_data, image_size, image_number, z);
|
||||
}
|
||||
}
|
||||
|
||||
void ZMQStream2PusherGroup::StartDataCollection(StartMessage& message) {
|
||||
if (message.images_per_file < 1)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Images per file cannot be zero or negative");
|
||||
images_per_file = message.images_per_file;
|
||||
|
||||
for (auto &p: pusher) {
|
||||
p->StartDataCollection(message);
|
||||
message.write_master_file = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ZMQStream2PusherGroup::SendCalibration(const CompressedImage &message) {
|
||||
if (pusher.empty())
|
||||
return false;
|
||||
return pusher[0]->SendCalibration(message);
|
||||
}
|
||||
|
||||
bool ZMQStream2PusherGroup::EndDataCollection(const EndMessage& message) {
|
||||
bool ret = true;
|
||||
for (auto &p: pusher) {
|
||||
if (!p->EndDataCollection(message))
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::string> ZMQStream2PusherGroup::GetAddress() {
|
||||
std::vector<std::string> ret;
|
||||
for (auto &p: pusher)
|
||||
ret.push_back(p->GetAddress());
|
||||
return ret;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright (2019-2024) Paul Scherrer Institute
|
||||
|
||||
#ifndef JUNGFRAUJOCH_ZMQSTREAM2PUSHERGROUP_H
|
||||
#define JUNGFRAUJOCH_ZMQSTREAM2PUSHERGROUP_H
|
||||
|
||||
#include "ImagePusher.h"
|
||||
#include "ZMQStream2Pusher.h"
|
||||
#include "../common/ZMQWrappers.h"
|
||||
|
||||
class ZMQStream2PusherGroup : public ImagePusher {
|
||||
std::vector<std::unique_ptr<ZMQStream2Pusher>> pusher;
|
||||
int64_t images_per_file = 1;
|
||||
public:
|
||||
ZMQStream2PusherGroup(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 ZMQStream2PusherGroup(const std::vector<std::string>& addr,
|
||||
int32_t send_buffer_high_watermark = -1, int32_t send_buffer_size = -1);
|
||||
|
||||
void StartDataCollection(StartMessage& message) override;
|
||||
void SendImage(const uint8_t *image_data, size_t image_size, int64_t image_number, ZeroCopyReturnValue *z) override;
|
||||
bool SendImage(const uint8_t *image_data, size_t image_size, int64_t image_number) override;
|
||||
bool EndDataCollection(const EndMessage& message) override;
|
||||
bool SendCalibration(const CompressedImage& message) override;
|
||||
|
||||
std::vector<std::string> GetAddress();
|
||||
};
|
||||
|
||||
#endif //JUNGFRAUJOCH_ZMQSTREAM2PUSHERGROUP_H
|
||||
2685
frontend_ui/package-lock.json
generated
2685
frontend_ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jungfrau-frontend",
|
||||
"version": "0.1.0",
|
||||
"license": "GPL-3.0",
|
||||
"name": "jungfraujoch-frontend",
|
||||
"version": "1.0.0",
|
||||
"license": "Proprietary",
|
||||
"private": true,
|
||||
"homepage": "/frontend",
|
||||
"dependencies": {
|
||||
@@ -10,6 +10,7 @@
|
||||
"@mui/icons-material": "^5.10.6",
|
||||
"@mui/material": "^5.10.6",
|
||||
"@mui/x-data-grid": "^5.17.14",
|
||||
"@redocly/cli": "^1.12.2",
|
||||
"@types/jest": "^29.2.4",
|
||||
"@types/node": "^18.11.13",
|
||||
"@types/react": "^18.0.26",
|
||||
@@ -25,9 +26,10 @@
|
||||
"scripts": {
|
||||
"start": "REACT_APP_VERSION=$(git rev-parse --short HEAD) PORT=8000 react-scripts start",
|
||||
"build": "REACT_APP_VERSION=$(git rev-parse --short HEAD) react-scripts build",
|
||||
"redocly": "redocly build-docs ../broker/jfjoch_api.yaml --output=build/openapi.html",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"openapi": "openapi --input ../broker/jfjoch_api.yaml --output ./src/openapi"
|
||||
"openapi": "./node_modules/openapi-typescript-codegen/bin/index.js -i ../broker/jfjoch_api.yaml --output ./src/openapi"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
|
||||
@@ -9,7 +9,6 @@ import Calibration from "./components/Calibration";
|
||||
import StatusBar from "./components/StatusBar";
|
||||
import DataProcessingPlots from "./components/DataProcessingPlots";
|
||||
import DetectorSelection from "./components/DetectorSelection";
|
||||
import BkgEstimatePlot from "./components/BkgEstimatePlot";
|
||||
import MeasurementStatistics from "./components/MeasurementStatistics";
|
||||
import DetectorStatus from "./components/DetectorStatus";
|
||||
import Paper from "@mui/material/Paper";
|
||||
@@ -91,13 +90,13 @@ class App extends Component<MyProps, MyState> {
|
||||
spacing={3}
|
||||
sx={{width: "95%"}}>
|
||||
<Grid item xs={8}>
|
||||
<BkgEstimatePlot/>
|
||||
<DataProcessingPlots default_tab={"12"} height={550}/>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<MeasurementStatistics/>
|
||||
</Grid>
|
||||
<Grid item xs={8}>
|
||||
<DataProcessingPlots/>
|
||||
<DataProcessingPlots default_tab={"1"} height={700}/>
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<DataProcessingSettings/>
|
||||
@@ -145,7 +144,8 @@ class App extends Component<MyProps, MyState> {
|
||||
href="mailto:filip.leonarski@psi.ch">Filip Leonarski</a> <br/>
|
||||
For more information see <a href="https://doi.org/10.1107/S1600577522010268"><i>J. Synchrotron
|
||||
Rad.</i> (2023). <b>30</b>, 227–234</a> <br/>
|
||||
Build: {process.env.REACT_APP_VERSION}</center>
|
||||
Build: {process.env.REACT_APP_VERSION}
|
||||
<a href="/frontend/openapi.html">API reference</a></center>
|
||||
<br/>
|
||||
</ThemeProvider>
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import Paper from "@mui/material/Paper";
|
||||
import DataProcessingPlot, {PlotType} from "./DataProcessingPlot";
|
||||
import React, {Component} from "react";
|
||||
import {Box} from "@mui/material";
|
||||
|
||||
type MyProps = {};
|
||||
|
||||
|
||||
class BkgEstimatePlot extends Component<MyProps> {
|
||||
|
||||
render() {
|
||||
return <Paper style={{textAlign: 'center'}} sx={{height: 450, width: "100%"}}>
|
||||
<Box sx={{width: "100%", height: 50}}>
|
||||
<br/>
|
||||
<center><strong>Background estimate</strong></center>
|
||||
</Box>
|
||||
<Box sx={{width: "95%", height: 350}}>
|
||||
<DataProcessingPlot type={PlotType.BKG_ESTIMATE} xlabel={"Image Number"}
|
||||
ylabel={"Photon count"} binning={0}/><br/>
|
||||
</Box>
|
||||
</Paper>
|
||||
}
|
||||
}
|
||||
|
||||
export default BkgEstimatePlot;
|
||||
@@ -9,7 +9,9 @@ import FormControl from "@mui/material/FormControl";
|
||||
import Select, {SelectChangeEvent} from "@mui/material/Select";
|
||||
|
||||
type MyProps = {
|
||||
};
|
||||
default_tab: string,
|
||||
height: number
|
||||
}
|
||||
|
||||
type MyState = {
|
||||
type: PlotType,
|
||||
@@ -28,7 +30,7 @@ class DataProcessingPlots extends Component<MyProps, MyState> {
|
||||
xlabel: "Image number",
|
||||
ylabel: "Indexing rate",
|
||||
binning: "0",
|
||||
tab: "1",
|
||||
tab: this.props.default_tab,
|
||||
connection_error: true
|
||||
}
|
||||
|
||||
@@ -69,6 +71,9 @@ class DataProcessingPlots extends Component<MyProps, MyState> {
|
||||
case "11":
|
||||
this.setState({type: PlotType.ROI_SUM, xlabel: "Image number", ylabel: "Photon count"});
|
||||
break;
|
||||
case "12":
|
||||
this.setState({type: PlotType.BKG_ESTIMATE, xlabel: "Image number", ylabel: "Photon count"});
|
||||
break;
|
||||
case "13":
|
||||
this.setState({type: PlotType.RECEIVER_FREE_SEND_BUFS, xlabel: "Image number", ylabel: "Number of buffers"});
|
||||
break;
|
||||
@@ -80,7 +85,7 @@ class DataProcessingPlots extends Component<MyProps, MyState> {
|
||||
};
|
||||
|
||||
render() {
|
||||
return <Paper style={{textAlign: 'center'}} sx={{ height: 630, width: "100%" }}>
|
||||
return <Paper style={{textAlign: 'center'}} sx={{ height: this.props.height, width: "100%" }}>
|
||||
<Toolbar>
|
||||
|
||||
<Grid container sx={{ minWidth: 500 }} >
|
||||
@@ -95,6 +100,7 @@ class DataProcessingPlots extends Component<MyProps, MyState> {
|
||||
<MenuItem value={1}>Indexing rate</MenuItem>
|
||||
<MenuItem value={2}>Spot count</MenuItem>
|
||||
<MenuItem value={3}>Azimuthal integration profile</MenuItem>
|
||||
<MenuItem value={12}>Background estimate</MenuItem>
|
||||
<MenuItem value={11}>ROI area sum</MenuItem>
|
||||
<MenuItem value={6}>ROI area max count</MenuItem>
|
||||
<MenuItem value={4}>Indexing rate (per time point)</MenuItem>
|
||||
@@ -129,7 +135,7 @@ class DataProcessingPlots extends Component<MyProps, MyState> {
|
||||
</FormControl>
|
||||
</Grid>
|
||||
</Toolbar>
|
||||
<Box sx={{width:"95%", height: 550}} >
|
||||
<Box sx={{width:"95%", height: this.props.height - 150}} >
|
||||
<DataProcessingPlot type={this.state.type} xlabel={this.state.xlabel} ylabel={this.state.ylabel}
|
||||
binning={Number(this.state.binning)}/>
|
||||
</Box>
|
||||
|
||||
@@ -17,6 +17,8 @@ class DataProcessingSettings extends Component<MyProps, MyState> {
|
||||
s: {
|
||||
enable: true,
|
||||
indexing: true,
|
||||
filter_powder_rings: false,
|
||||
min_spot_count_powder_ring: 20,
|
||||
photon_count_threshold: 8,
|
||||
signal_to_noise_threshold: 3.0,
|
||||
min_pix_per_spot: 2,
|
||||
@@ -50,10 +52,6 @@ class DataProcessingSettings extends Component<MyProps, MyState> {
|
||||
this.interval = setInterval(() => this.getValues(), 1000);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
setPhotonCountThreshold = (event: Event, newValue: number | number[]) => {
|
||||
this.setState(prevState => (
|
||||
{
|
||||
@@ -64,6 +62,7 @@ class DataProcessingSettings extends Component<MyProps, MyState> {
|
||||
}
|
||||
));
|
||||
this.putValues();
|
||||
this.getValues();
|
||||
}
|
||||
|
||||
setSignalToNoiseThreshold = (event: Event, newValue: number | number[]) => {
|
||||
@@ -132,8 +131,28 @@ class DataProcessingSettings extends Component<MyProps, MyState> {
|
||||
this.getValues();
|
||||
}
|
||||
|
||||
enableFilterPowderRingToggle = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
let x = this.state;
|
||||
x.s.filter_powder_rings = event.target.checked;
|
||||
this.putValues2(x);
|
||||
this.getValues();
|
||||
}
|
||||
|
||||
setMinSpotCountPowderRing = (event: Event, newValue: number | number[]) => {
|
||||
this.setState(prevState => (
|
||||
{
|
||||
s : {
|
||||
...prevState.s,
|
||||
min_spot_count_powder_ring: newValue as number
|
||||
}
|
||||
}
|
||||
));
|
||||
this.putValues();
|
||||
this.getValues();
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Paper style={{textAlign: 'center'}} sx={{ height: 630, width: '100%' }}>
|
||||
return <Paper style={{textAlign: 'center'}} sx={{ height: 700, width: '100%' }}>
|
||||
<Grid container spacing={0}>
|
||||
|
||||
<Grid item xs={1}/>
|
||||
@@ -141,43 +160,53 @@ class DataProcessingSettings extends Component<MyProps, MyState> {
|
||||
<br/><strong>Spot finding parameters</strong><br/><br/>
|
||||
<Switch onChange={this.enableSpotFindingToggle} checked={this.state.s.enable}
|
||||
disabled={this.state.connection_error}/>
|
||||
Enable spot finding
|
||||
<Switch onChange={this.enableIndexingToggle} checked={this.state.s.indexing}
|
||||
disabled={this.state.connection_error || !this.state.s.enable}/>
|
||||
Enable indexing <br/><br/>
|
||||
Spot finding
|
||||
<br/><br/>
|
||||
|
||||
<Typography gutterBottom> Count threshold </Typography>
|
||||
<Slider disabled={this.state.connection_error || !this.state.s.enable}
|
||||
value={Number(this.state.s.photon_count_threshold)}
|
||||
onChange={this.setPhotonCountThreshold}
|
||||
min={1} max={50} step={1} valueLabelDisplay="auto" />
|
||||
min={1} max={50} step={1} valueLabelDisplay="auto"/>
|
||||
|
||||
<br/><Typography> Signal-to-noise threshold </Typography>
|
||||
<Slider disabled={this.state.connection_error || !this.state.s.enable}
|
||||
value={Number(this.state.s.signal_to_noise_threshold)}
|
||||
onChange={this.setSignalToNoiseThreshold}
|
||||
min={2} max={10} step={0.5} valueLabelDisplay="auto" />
|
||||
min={2} max={10} step={0.5} valueLabelDisplay="auto"/>
|
||||
|
||||
<br/><Typography> Minimum pixel / spot </Typography>
|
||||
<Slider disabled={this.state.connection_error || !this.state.s.enable}
|
||||
value={Number(this.state.s.min_pix_per_spot)}
|
||||
onChange={this.setMinPixPerSpot}
|
||||
min={1} max={8} step={1} valueLabelDisplay="auto" />
|
||||
min={1} max={8} step={1} valueLabelDisplay="auto"/>
|
||||
<Typography> High resolution limit [Å] </Typography>
|
||||
<Slider disabled={this.state.connection_error || !this.state.s.enable}
|
||||
value={Number(this.state.s.high_resolution_limit)}
|
||||
onChange={this.setHighResolutionLimit}
|
||||
min={1} max={5} step={0.2} valueLabelDisplay="auto" />
|
||||
min={1} max={5} step={0.2} valueLabelDisplay="auto"/>
|
||||
<br/><br/>
|
||||
<Switch onChange={this.enableFilterPowderRingToggle}
|
||||
checked={this.state.s.filter_powder_rings}
|
||||
disabled={this.state.connection_error || !this.state.s.enable}/>
|
||||
Filter spots in powder rings (<i>e.g.</i>, ice)
|
||||
<br/><br/>
|
||||
<Typography> Min spots to filter powder ring </Typography>
|
||||
<Slider disabled={this.state.connection_error || !this.state.s.enable || !this.state.s.filter_powder_rings}
|
||||
value={Number(this.state.s.filter_powder_rings)}
|
||||
onChange={this.setMinSpotCountPowderRing}
|
||||
min={5} max={50} step={1} valueLabelDisplay="auto"/>
|
||||
<br/> <br/>
|
||||
<Switch onChange={this.enableIndexingToggle} checked={this.state.s.indexing}
|
||||
disabled={this.state.connection_error || !this.state.s.enable}/>
|
||||
Indexing <br/><br/>
|
||||
<Typography> Indexing spot acceptance tolerance </Typography>
|
||||
<Slider disabled={this.state.connection_error || !this.state.s.enable}
|
||||
<Slider disabled={this.state.connection_error || !this.state.s.enable || !this.state.s.indexing}
|
||||
value={Number(this.state.s.indexing_tolerance)}
|
||||
onChange={this.setIndexingTolerance}
|
||||
min={0.0} max={0.3} step={0.01} valueLabelDisplay="auto" />
|
||||
<br/><br/>
|
||||
min={0.0} max={0.3} step={0.01} valueLabelDisplay="auto"/>
|
||||
</Grid>
|
||||
<Grid item xs={1}/>
|
||||
|
||||
|
||||
</Grid>
|
||||
</Paper>
|
||||
}
|
||||
|
||||
@@ -65,12 +65,12 @@ class DetectorSettings extends Component<MyProps, MyState> {
|
||||
}
|
||||
|
||||
if (!frame_err) {
|
||||
if ((Number(frame_time) < 470) || (Number(frame_time) > 2000))
|
||||
if (Number(frame_time) < 470)
|
||||
frame_err = true;
|
||||
}
|
||||
|
||||
if (!count_err) {
|
||||
if (Number(count_time) < 5)
|
||||
if ((Number(count_time) < 3) || (Number(count_time) > 1980))
|
||||
count_err = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ import React, {Component} from 'react';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import {Grid, Table, TableBody, TableCell, TableContainer, TableRow,} from "@mui/material";
|
||||
import {DefaultService, detector_status} from "../openapi";
|
||||
import powerchip = detector_status.powerchip;
|
||||
import state = detector_status.state;
|
||||
import powerchip = detector_status.powerchip;
|
||||
|
||||
type MyProps = {}
|
||||
|
||||
@@ -13,16 +13,28 @@ type MyState = {
|
||||
connection_error: boolean
|
||||
}
|
||||
|
||||
function powerchipToString(s : detector_status) : string {
|
||||
if (s.powerchip === undefined)
|
||||
return "";
|
||||
switch (s.powerchip) {
|
||||
case powerchip.POWER_ON:
|
||||
return "On";
|
||||
case powerchip.POWER_OFF:
|
||||
return "Off";
|
||||
case powerchip.PARTIAL:
|
||||
return "Partially on";
|
||||
}
|
||||
}
|
||||
class DetectorStatus extends Component<MyProps, MyState> {
|
||||
interval : NodeJS.Timer | undefined;
|
||||
state : MyState = {
|
||||
s: {
|
||||
state: state.IDLE,
|
||||
powerchip: powerchip.OFF,
|
||||
powerchip: powerchip.POWER_OFF,
|
||||
server_version: "Detector off",
|
||||
number_of_triggers_left: 0,
|
||||
fpga_temp_degC: [0,0,0,0,0,1],
|
||||
high_voltage_V: [1,0,0,0,-1]
|
||||
fpga_temp_degC: [-1],
|
||||
high_voltage_V: [-1]
|
||||
},
|
||||
connection_error: true
|
||||
}
|
||||
@@ -37,11 +49,11 @@ class DetectorStatus extends Component<MyProps, MyState> {
|
||||
this.setState({
|
||||
s: {
|
||||
state: state.IDLE,
|
||||
powerchip: powerchip.OFF,
|
||||
powerchip: powerchip.POWER_OFF,
|
||||
server_version: "Detector off",
|
||||
number_of_triggers_left: 0,
|
||||
fpga_temp_degC: [30,29,35,40,32],
|
||||
high_voltage_V: [120,120,110,115]
|
||||
fpga_temp_degC: [-1],
|
||||
high_voltage_V: [-1]
|
||||
},
|
||||
connection_error: true
|
||||
});
|
||||
@@ -72,7 +84,7 @@ class DetectorStatus extends Component<MyProps, MyState> {
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row"> Detector ASIC power: </TableCell>
|
||||
<TableCell align="right">{this.state.s.powerchip.toString()}</TableCell>
|
||||
<TableCell align="right">{powerchipToString(this.state.s)}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row"> Triggers remaining: </TableCell>
|
||||
|
||||
@@ -15,7 +15,9 @@ class MeasurementStatistics extends Component<MyProps, MyState> {
|
||||
interval : NodeJS.Timer | undefined;
|
||||
state : MyState = {
|
||||
s: {
|
||||
run_number: undefined,
|
||||
file_prefix: undefined,
|
||||
experiment_group: undefined,
|
||||
images_expected: 0,
|
||||
images_collected: 0,
|
||||
images_sent: 0,
|
||||
@@ -48,7 +50,7 @@ class MeasurementStatistics extends Component<MyProps, MyState> {
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Paper style={{textAlign: 'center'}} sx={{ height: 450, width: '100%' }}>
|
||||
return <Paper style={{textAlign: 'center'}} sx={{ height: 550, width: '100%' }}>
|
||||
<Grid container spacing={0}>
|
||||
<Grid item xs={1}/>
|
||||
<Grid item xs={10}>
|
||||
@@ -57,10 +59,19 @@ class MeasurementStatistics extends Component<MyProps, MyState> {
|
||||
<TableContainer component={Paper} style={{marginLeft: "auto", marginRight: "auto"}}>
|
||||
<Table size="small" aria-label="simple table">
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row"> File prefix: </TableCell>
|
||||
<TableCell align="right">{(this.state.s.file_prefix !== undefined) ? this.state.s.file_prefix : "(images not written)"}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row"> File prefix: </TableCell>
|
||||
<TableCell align="right">{(this.state.s.file_prefix !== undefined) ? this.state.s.file_prefix : "(images not written)"}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row"> Run number: </TableCell>
|
||||
<TableCell align="right">{(this.state.s.run_number !== undefined) ? this.state.s.run_number : "(not set)"}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row"> Experiment group: </TableCell>
|
||||
<TableCell align="right">{this.state.s.experiment_group}</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row"> Images expected: </TableCell>
|
||||
<TableCell align="right">{this.state.s.images_expected}</TableCell>
|
||||
@@ -98,6 +109,10 @@ class MeasurementStatistics extends Component<MyProps, MyState> {
|
||||
<TableCell align="right">{(this.state.s.bkg_estimate !== undefined)
|
||||
? this.state.s.bkg_estimate.toPrecision(7) : "-"}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row"> Unit cell: </TableCell>
|
||||
<TableCell align="right">{this.state.s.unit_cell}</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
|
||||
@@ -26,9 +26,10 @@ class PreviewImage extends Component<MyProps, MyState> {
|
||||
show_spots: true,
|
||||
show_roi: false,
|
||||
show_indexed: false,
|
||||
show_user_mask: false,
|
||||
resolution_ring: 0.5
|
||||
},
|
||||
s_url: "",
|
||||
s_url: null,
|
||||
update: true,
|
||||
connection_error: true
|
||||
}
|
||||
@@ -94,6 +95,17 @@ class PreviewImage extends Component<MyProps, MyState> {
|
||||
);
|
||||
}
|
||||
|
||||
showUserMaskToggle = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState(prevState => (
|
||||
{
|
||||
settings : {
|
||||
...prevState.settings,
|
||||
show_user_mask: event.target.checked
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
getValues() {
|
||||
if (!this.state.update)
|
||||
return;
|
||||
@@ -124,9 +136,12 @@ class PreviewImage extends Component<MyProps, MyState> {
|
||||
clearInterval(this.interval);
|
||||
}
|
||||
|
||||
preview() {
|
||||
return <div><br/>
|
||||
<Stack spacing={2} direction="row" sx={{ mb: 1 }} alignItems="center">
|
||||
render() {
|
||||
return <Paper sx={{height: 1250, width: 1000, m: 2}}
|
||||
component={Stack}
|
||||
direction="column">
|
||||
<br/>
|
||||
<Stack spacing={2} direction="row" sx={{mb: 1}} alignItems="center">
|
||||
<strong>Preview image</strong>
|
||||
<Switch disabled={this.state.connection_error} checked={this.state.update}
|
||||
onChange={this.updateToggle} name="Update"/>
|
||||
@@ -140,13 +155,16 @@ class PreviewImage extends Component<MyProps, MyState> {
|
||||
<Switch disabled={this.state.connection_error} checked={this.state.settings.show_indexed}
|
||||
onChange={this.showIndexedToggle} name="Show ROI"/>
|
||||
Show only indexed images
|
||||
<Box sx={{ width: 200 }}>
|
||||
<Switch disabled={this.state.connection_error} checked={this.state.settings.show_user_mask}
|
||||
onChange={this.showUserMaskToggle} name="Show user mask"/>
|
||||
Show user mask
|
||||
<Box sx={{width: 200}}>
|
||||
<Slider disabled={this.state.connection_error}
|
||||
value={Number(this.state.settings.saturation)} min={1} max={80}
|
||||
onChange={this.setSaturation} valueLabelDisplay="auto"/> <br/>Saturation value
|
||||
</Box>
|
||||
|
||||
<Box sx={{ width: 200 }}>
|
||||
<Box sx={{width: 200}}>
|
||||
<Slider disabled={this.state.connection_error}
|
||||
value={(this.state.settings.resolution_ring === undefined) ? 0.5 : Number(this.state.settings.resolution_ring)}
|
||||
min={0.5} max={5.0} step={0.1}
|
||||
@@ -155,8 +173,7 @@ class PreviewImage extends Component<MyProps, MyState> {
|
||||
|
||||
</Stack>
|
||||
<br/>
|
||||
{
|
||||
this.state.s_url !== null ?
|
||||
{(!this.state.connection_error && (this.state.s_url !== null)) ?
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="center"
|
||||
@@ -164,20 +181,13 @@ class PreviewImage extends Component<MyProps, MyState> {
|
||||
>
|
||||
<TransformWrapper>
|
||||
<TransformComponent>
|
||||
<img src={this.state.s_url} alt="Live preview" style={{maxWidth: "100%", maxHeight: 900}}/>
|
||||
<img src={this.state.s_url} alt="Live preview"
|
||||
style={{maxWidth: "100%", maxHeight: 900}}/>
|
||||
</TransformComponent>
|
||||
</TransformWrapper>
|
||||
</Stack>: <div/>
|
||||
</Stack> : <div>Preview not available</div>
|
||||
}
|
||||
<br/>
|
||||
</div>
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Paper sx={{height: 1050, width: 850, m: 2}}
|
||||
component={Stack}
|
||||
direction="column">
|
||||
{(!this.state.connection_error && (this.state.s_url !== null)) ? this.preview() : "Preview not available"}
|
||||
</Paper>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,10 +44,10 @@ export type dataset_settings = {
|
||||
detector_distance_mm: number;
|
||||
/**
|
||||
* Used to calculate /entry/beam/incident_wavelength in NXmx
|
||||
* Incident photon energy in keV
|
||||
* Incident particle (photon, electron) energy in keV
|
||||
*
|
||||
*/
|
||||
photon_energy_keV: number;
|
||||
incident_energy_keV: number;
|
||||
/**
|
||||
* Prefix for filenames. If left empty, no file will be saved.
|
||||
*/
|
||||
@@ -80,7 +80,7 @@ export type dataset_settings = {
|
||||
*
|
||||
*/
|
||||
transmission?: number;
|
||||
omega?: rotation_axis;
|
||||
goniometer?: rotation_axis;
|
||||
/**
|
||||
* Header appendix, added as user_data to start message
|
||||
*/
|
||||
@@ -90,9 +90,9 @@ export type dataset_settings = {
|
||||
*/
|
||||
image_appendix?: string;
|
||||
/**
|
||||
* For JUNGFRAU conversion it is possible to multiply energy by a given factor to get fractional/multiplied photon counts
|
||||
* For JUNGFRAU conversion it is possible to multiply incident energy by a given factor to get fractional/multiplied particle counts
|
||||
*/
|
||||
photon_energy_multiplier?: number;
|
||||
energy_multiplier?: number;
|
||||
/**
|
||||
* Rate at which non-indexed images are accepted to be forwarded to writer.
|
||||
* Value of 1.0 (default) means that all images are written.
|
||||
@@ -100,6 +100,20 @@ export type dataset_settings = {
|
||||
*
|
||||
*/
|
||||
data_reduction_factor_serialmx?: number;
|
||||
/**
|
||||
* Number of run within an experimental session.
|
||||
* Transferred over CBOR stream as "series ID", though not saved in HDF5 file.
|
||||
* It is highly recommended to keep this number unique for each data collection during experimental series.
|
||||
* If not provided, the number will be automatically incremented.
|
||||
*
|
||||
*/
|
||||
run_number?: number;
|
||||
/**
|
||||
* Name of group owning the data (e.g. p-group or proposal number).
|
||||
* Transferred over CBOR stream, though not saved in HDF5 file.
|
||||
*
|
||||
*/
|
||||
experiment_group?: string;
|
||||
/**
|
||||
* Units of angstrom and degree
|
||||
*/
|
||||
|
||||
@@ -46,8 +46,8 @@ export namespace detector_status {
|
||||
* Power on of ASICs
|
||||
*/
|
||||
export enum powerchip {
|
||||
ON = 'On',
|
||||
OFF = 'Off',
|
||||
POWER_ON = 'PowerOn',
|
||||
POWER_OFF = 'PowerOff',
|
||||
PARTIAL = 'Partial',
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,16 @@
|
||||
|
||||
export type measurement_statistics = {
|
||||
file_prefix?: string;
|
||||
/**
|
||||
* Number of data collection run. This can be either automatically incremented or provided externally for each data collection.
|
||||
*
|
||||
*/
|
||||
run_number?: number;
|
||||
/**
|
||||
* Name of group owning the data (e.g. p-group or proposal number).
|
||||
*
|
||||
*/
|
||||
experiment_group?: string;
|
||||
images_expected?: number;
|
||||
/**
|
||||
* Images collected by the receiver. This number will be lower than images expected if there were issues with data collection performance.
|
||||
@@ -31,6 +41,7 @@ export type measurement_statistics = {
|
||||
detector_height?: number;
|
||||
detector_pixel_depth?: measurement_statistics.detector_pixel_depth;
|
||||
bkg_estimate?: number;
|
||||
unit_cell?: string;
|
||||
};
|
||||
|
||||
export namespace measurement_statistics {
|
||||
|
||||
@@ -27,6 +27,10 @@ export type preview_settings = {
|
||||
* Preview indexed images only
|
||||
*/
|
||||
show_indexed?: boolean;
|
||||
/**
|
||||
* Show user mask
|
||||
*/
|
||||
show_user_mask?: boolean;
|
||||
resolution_ring?: number;
|
||||
};
|
||||
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
* Definition of a crystal rotation axis
|
||||
*/
|
||||
export type rotation_axis = {
|
||||
/**
|
||||
* Name of rotation axis (e.g., omega, phi)
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* Angle step in degrees
|
||||
*/
|
||||
|
||||
@@ -5,13 +5,24 @@
|
||||
|
||||
export type spot_finding_settings = {
|
||||
/**
|
||||
* Enable spot finding
|
||||
* Enable spot finding. This is temporary setting, i.e. can be changed anytime during data collection.
|
||||
* Even if disabled spot finding information will still be send and written, though always with zero spots.
|
||||
*
|
||||
*/
|
||||
enable: boolean;
|
||||
/**
|
||||
* Enable indexing
|
||||
* Enable indexing. This is temporary setting, i.e. can be changed anytime during data collection.
|
||||
*
|
||||
*/
|
||||
indexing: boolean;
|
||||
/**
|
||||
* Filter spots which form powder rings (e.g., ice rings)
|
||||
*/
|
||||
filter_powder_rings?: boolean;
|
||||
/**
|
||||
* Minimum number of spots to consider a thin resolution shell (0.01 A^-1) a powder ring and filter out.
|
||||
*/
|
||||
min_spot_count_powder_ring?: number;
|
||||
signal_to_noise_threshold: number;
|
||||
photon_count_threshold: number;
|
||||
min_pix_per_spot: number;
|
||||
|
||||
@@ -280,20 +280,20 @@ export class DefaultService {
|
||||
* Requires binary blob with 16-bit integer numbers of size of detector in raw/converted coordinates
|
||||
* (depending on detector settings).
|
||||
*
|
||||
* @param number Image number to upload
|
||||
* @param id Image id to upload
|
||||
* @param requestBody
|
||||
* @returns any Everything OK
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static putConfigInternalGeneratorImage(
|
||||
number?: number,
|
||||
id?: number,
|
||||
requestBody?: Blob,
|
||||
): CancelablePromise<any> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'PUT',
|
||||
url: '/config/internal_generator_image',
|
||||
query: {
|
||||
'number': number,
|
||||
'id': id,
|
||||
},
|
||||
body: requestBody,
|
||||
mediaType: 'application/octet-stream',
|
||||
@@ -303,6 +303,35 @@ export class DefaultService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Load TIFF image for internal FPGA generator
|
||||
* Load image for internal FPGA generator. This can only happen in Idle state of the detector.
|
||||
* Requires TIFF with 16-bit integer numbers of size of detector in raw/converted coordinates
|
||||
* (depending on detector settings).
|
||||
*
|
||||
* @param id Image ID to upload
|
||||
* @param requestBody
|
||||
* @returns any Everything OK
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static putConfigInternalGeneratorImageTiff(
|
||||
id?: number,
|
||||
requestBody?: Blob,
|
||||
): CancelablePromise<any> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'PUT',
|
||||
url: '/config/internal_generator_image.tiff',
|
||||
query: {
|
||||
'id': id,
|
||||
},
|
||||
body: requestBody,
|
||||
mediaType: 'image/tiff',
|
||||
errors: {
|
||||
400: `Input parsing or validation error`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Select detector
|
||||
* Jungfraujoch allows to control multiple detectors and/or region-of-interests.
|
||||
@@ -847,6 +876,63 @@ export class DefaultService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mask of the detector
|
||||
* Get full pixel mask of the detector
|
||||
* See NXmx standard for meaning of pixel values
|
||||
*
|
||||
* @returns binary Pixel mask in TIFF format (4 byte; unsigned)
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getConfigMaskTiff(): CancelablePromise<Blob> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/config/mask.tiff',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user mask of the detector
|
||||
* Get user pixel mask of the detector in the actual detector coordinates: 0 - good pixel, 1 - masked
|
||||
* @returns binary User mask in TIFF format (4 byte; unsigned)
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static getConfigUserMaskTiff(): CancelablePromise<Blob> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'GET',
|
||||
url: '/config/user_mask.tiff',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload user mask of the detector
|
||||
* Should be in `Idle` state.
|
||||
* Upload user mask of the detector - this is for example to account for beam stop shadow or misbehaving regions.
|
||||
* If detector is conversion mode the mask can be both in raw (1024x512; stacked modules) or converted coordinates.
|
||||
* In the latter case - module gaps are ignored and don't need to be assigned value.
|
||||
* Mask is expected as TIFF (4-byte; unsigned).
|
||||
* 0 - good pixel, other value - masked
|
||||
* User mask is stored in NXmx pixel mask (bit 8), as well as used in spot finding and azimuthal integration.
|
||||
* User mask is not automatically applied - i.e. pixels with user mask will have a valid pixel value in the images.
|
||||
*
|
||||
* @param requestBody
|
||||
* @returns binary All good
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static putConfigUserMaskTiff(
|
||||
requestBody?: Blob,
|
||||
): CancelablePromise<Blob> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'PUT',
|
||||
url: '/config/user_mask.tiff',
|
||||
body: requestBody,
|
||||
mediaType: 'application/octet-stream',
|
||||
errors: {
|
||||
500: `Error within Jungfraujoch code - see output message.`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pedestal G0 in TIFF format
|
||||
* @param gainLevel Gain level (0, 1, 2)
|
||||
|
||||
@@ -22,7 +22,7 @@ IF (CMAKE_CUDA_COMPILER)
|
||||
FetchContent_Declare(
|
||||
fast-indexer
|
||||
GIT_REPOSITORY https://github.com/fleon-psi/fast-feedback-indexer/
|
||||
GIT_TAG 651cbc9
|
||||
GIT_TAG 66c3f44
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(fast-indexer)
|
||||
|
||||
@@ -1,86 +1,125 @@
|
||||
// Copyright (2019-2024) Paul Scherrer Institute
|
||||
|
||||
#include "CPUSpotFinder.h"
|
||||
#define NUM_PASS 2
|
||||
|
||||
template <int N>
|
||||
void FindSpots(DeviceOutput &output,
|
||||
int big_column, int big_row,
|
||||
const SpotFindingSettings& settings,
|
||||
const float *d_array) {
|
||||
const float *d_array,
|
||||
float *arr_mean,
|
||||
float *arr_stddev,
|
||||
uint32_t *arr_valid_count,
|
||||
uint32_t *arr_strong_pixel) {
|
||||
auto image = (int16_t *) output.pixels;
|
||||
|
||||
int64_t sum = 0;
|
||||
int64_t sum2 = 0;
|
||||
int64_t valid_count = 0;
|
||||
std::vector<uint8_t> mask(N * N, 0);
|
||||
|
||||
for (int y = 0; y < N; y++) {
|
||||
size_t line = big_row * N + y;
|
||||
for (int x = 0; x < N; x++) {
|
||||
size_t coord = (big_row * N + y) * RAW_MODULE_COLS + big_column * N + x;
|
||||
if ((image[coord] != INT16_MIN) || (image[coord] != INT16_MAX)) {
|
||||
sum += image[coord];
|
||||
sum2 += image[coord] * image[coord];
|
||||
valid_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64_t variance = valid_count * sum2 - sum * sum;
|
||||
float threshold = variance * settings.signal_to_noise_threshold * settings.signal_to_noise_threshold;
|
||||
|
||||
for (int y = 0; y < N; y++) {
|
||||
for (int x = 0; x < N; x++) {
|
||||
size_t line = big_row * N + y;
|
||||
size_t col = big_column * N + x;
|
||||
size_t coord = line * RAW_MODULE_COLS + col;
|
||||
|
||||
bool strong_pixel = true;
|
||||
|
||||
uint8_t bad_pixel = 0;
|
||||
if ((line == 255) || (line == 256)
|
||||
|| (col == 255) || (col == 256)
|
||||
|| (col == 511) || (col == 512)
|
||||
|| (col == 767) || (col == 768))
|
||||
strong_pixel = false;
|
||||
bad_pixel = 1;
|
||||
|
||||
if (d_array[coord] != 0) {
|
||||
if ((d_array[coord] < settings.high_resolution_limit)
|
||||
|| (d_array[coord] > settings.low_resolution_limit))
|
||||
if ((d_array[coord] < settings.high_resolution_limit)
|
||||
|| (d_array[coord] > settings.low_resolution_limit))
|
||||
bad_pixel = 1;
|
||||
|
||||
if ((image[coord] == INT16_MIN) || (image[coord] > 32760))
|
||||
bad_pixel = 1;
|
||||
|
||||
mask[y * N + x] = bad_pixel;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_PASS; i++) {
|
||||
int64_t sum = 0;
|
||||
int64_t sum2 = 0;
|
||||
int64_t valid_count = 0;
|
||||
|
||||
for (int y = 0; y < N; y++) {
|
||||
for (int x = 0; x < N; x++) {
|
||||
size_t coord = (big_row * N + y) * RAW_MODULE_COLS + big_column * N + x;
|
||||
if (mask[y * N + x] == 0) {
|
||||
sum += image[coord];
|
||||
sum2 += image[coord] * image[coord];
|
||||
valid_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64_t variance = valid_count * sum2 - sum * sum;
|
||||
float threshold = variance * settings.signal_to_noise_threshold * settings.signal_to_noise_threshold;
|
||||
|
||||
for (int y = 0; y < N; y++) {
|
||||
size_t line = big_row * N + y;
|
||||
for (int x = 0; x < N; x++) {
|
||||
size_t col = big_column * N + x;
|
||||
size_t coord = line * RAW_MODULE_COLS + col;
|
||||
|
||||
arr_valid_count[coord] = valid_count;
|
||||
if (valid_count > 0) {
|
||||
arr_mean[coord] = static_cast<float>(sum) / static_cast<float>(valid_count);
|
||||
arr_stddev[coord] = sqrtf(
|
||||
static_cast<float>(variance) / static_cast<float>(valid_count * valid_count));
|
||||
} else {
|
||||
arr_mean[coord] = -1;
|
||||
arr_stddev[coord] = -1;
|
||||
}
|
||||
|
||||
bool strong_pixel = true;
|
||||
|
||||
if (mask[y * N + x])
|
||||
strong_pixel = false;
|
||||
}
|
||||
|
||||
if ((settings.photon_count_threshold < 0)
|
||||
&& (settings.signal_to_noise_threshold <= 0))
|
||||
strong_pixel = false;
|
||||
|
||||
if ((settings.photon_count_threshold >= 0)
|
||||
&& (image[coord] <= settings.photon_count_threshold)) {
|
||||
strong_pixel = false;
|
||||
}
|
||||
|
||||
if (image[coord] > 32760)
|
||||
strong_pixel = false;
|
||||
|
||||
if (settings.signal_to_noise_threshold > 0) {
|
||||
int64_t in_minus_mean = image[coord] * valid_count - sum;
|
||||
if ((in_minus_mean * in_minus_mean <= threshold)
|
||||
|| (in_minus_mean <= 0)
|
||||
|| (valid_count < N * N / 2))
|
||||
if ((settings.photon_count_threshold < 0)
|
||||
&& (settings.signal_to_noise_threshold <= 0))
|
||||
strong_pixel = false;
|
||||
}
|
||||
|
||||
if (strong_pixel) {
|
||||
output.spot_finding_result.strong_pixel[coord / 8] |= (1 << (coord % 8));
|
||||
output.spot_finding_result.strong_pixel_count++;
|
||||
if ((settings.photon_count_threshold >= 0)
|
||||
&& (image[coord] <= settings.photon_count_threshold)) {
|
||||
strong_pixel = false;
|
||||
}
|
||||
|
||||
if (settings.signal_to_noise_threshold > 0) {
|
||||
int64_t in_minus_mean = image[coord] * valid_count - sum;
|
||||
if ((in_minus_mean * in_minus_mean <= threshold)
|
||||
|| (in_minus_mean <= 0)
|
||||
|| (valid_count <= N * N / 2))
|
||||
strong_pixel = false;
|
||||
}
|
||||
|
||||
if (strong_pixel) {
|
||||
mask[y * N + x] = 1;
|
||||
arr_strong_pixel[coord]++;
|
||||
output.spot_finding_result.strong_pixel[coord / 8] |= (1 << (coord % 8));
|
||||
output.spot_finding_result.strong_pixel_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FindSpots(DeviceOutput &output, const SpotFindingSettings& settings, const float *d_array) {
|
||||
void FindSpots(DeviceOutput &output,
|
||||
const SpotFindingSettings& settings,
|
||||
const float *d_array,
|
||||
float *arr_mean,
|
||||
float *arr_stddev,
|
||||
uint32_t *arr_valid_count,
|
||||
uint32_t *arr_strong_pixel) {
|
||||
for (auto &i: output.spot_finding_result.strong_pixel)
|
||||
i = 0;
|
||||
|
||||
for (int i = 0; i < RAW_MODULE_LINES / 32; i++) {
|
||||
for (int j = 0; j < RAW_MODULE_COLS / 32; j++)
|
||||
FindSpots<32>(output, j, i, settings, d_array);
|
||||
FindSpots<32>(output, j, i, settings, d_array, arr_mean, arr_stddev, arr_valid_count, arr_strong_pixel);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,13 @@
|
||||
#include "SpotFindingSettings.h"
|
||||
#include "StrongPixelSet.h"
|
||||
|
||||
void FindSpots(DeviceOutput &output, const SpotFindingSettings& settings, const float *d_array);
|
||||
void FindSpots(DeviceOutput &output,
|
||||
const SpotFindingSettings& settings,
|
||||
const float *d_array,
|
||||
float *arr_mean,
|
||||
float *arr_stddev,
|
||||
uint32_t *arr_valid_count,
|
||||
uint32_t *arr_strong_pixel);
|
||||
|
||||
|
||||
#endif //JUNGFRAUJOCH_CPUSPOTFINDER_H
|
||||
|
||||
@@ -8,13 +8,17 @@ void IndexerWrapper::Setup(const UnitCell &cell) {
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<IndexingResult> IndexerWrapper::Run(const std::vector<Coord> &coord, float indexing_threshold) {
|
||||
std::vector<IndexingResult> IndexerWrapper::Run(const std::vector<Coord> &coord, float indexing_threshold, int nspots) {
|
||||
#ifdef JFJOCH_USE_CUDA
|
||||
std::vector<IndexingResult> ret;
|
||||
|
||||
if (coord.size() <= viable_cell_min_spots)
|
||||
if (nspots > coord.size())
|
||||
nspots = coord.size();
|
||||
|
||||
if (nspots <= viable_cell_min_spots)
|
||||
return ret;
|
||||
|
||||
assert(nspots <= MAX_SPOT_COUNT);
|
||||
assert(coord.size() <= MAX_SPOT_COUNT);
|
||||
|
||||
for (int i = 0; i < coord.size(); i++) {
|
||||
@@ -24,9 +28,9 @@ std::vector<IndexingResult> IndexerWrapper::Run(const std::vector<Coord> &coord,
|
||||
}
|
||||
|
||||
// Index
|
||||
indexer.index(1, coord.size());
|
||||
indexer.index(1, nspots);
|
||||
|
||||
fast_feedback::refine::indexer_ifssr<float>::refine(indexer.spotM().topRows(coord.size()),
|
||||
fast_feedback::refine::indexer_ifssr<float>::refine(indexer.spotM().topRows(nspots),
|
||||
indexer.oCellM(),
|
||||
indexer.oScoreV(),
|
||||
cifssr);
|
||||
@@ -38,30 +42,28 @@ std::vector<IndexingResult> IndexerWrapper::Run(const std::vector<Coord> &coord,
|
||||
// Get best cell
|
||||
auto id = fast_feedback::refine::best_cell(indexer.oScoreV());
|
||||
|
||||
bool indexed = fast_feedback::refine::is_viable_cell(indexer.oCell(id), indexer.Spots(),
|
||||
indexing_threshold, viable_cell_min_spots,
|
||||
false);
|
||||
auto cell = indexer.oCell(id).colwise().reverse();
|
||||
fast_feedback::refine::make_right_handed(cell);
|
||||
|
||||
// Check if result is viable
|
||||
if (indexed) {
|
||||
auto cell = indexer.oCell(id).colwise().reverse();
|
||||
fast_feedback::refine::make_right_handed(cell);
|
||||
using M3x = Eigen::MatrixX3<float>;
|
||||
M3x resid = indexer.spotM().topRows(coord.size()) * cell.transpose();
|
||||
const M3x miller = round(resid.array());
|
||||
resid -= miller;
|
||||
|
||||
// get indexed spots
|
||||
using M3x = Eigen::MatrixX3<float>;
|
||||
M3x resid = indexer.spotM().topRows(coord.size()) * cell.transpose();
|
||||
const M3x miller = round(resid.array());
|
||||
const M3x predicted = miller * cell.transpose().inverse();
|
||||
auto indexed_spots = (resid.rowwise().norm().array() < indexing_threshold);
|
||||
auto indexed_spot_count = indexed_spots.topRows(nspots).count();
|
||||
|
||||
// Conditions for indexing:
|
||||
// * There must be 9 AND 20% of all spots indexed
|
||||
// * Decision is only based on spots used for indexing (not all spots)
|
||||
if ((indexed_spot_count > viable_cell_min_spots) && (indexed_spot_count > 0.20 * nspots)) {
|
||||
IndexingResult result;
|
||||
result.l = CrystalLattice(cell);
|
||||
|
||||
result.predicted_spots.resize(coord.size());
|
||||
result.indexed_spot.resize(coord.size());
|
||||
|
||||
for (int i = 0; i < coord.size(); i++)
|
||||
result.predicted_spots[i] = Coord(predicted.coeff(i, 0),
|
||||
predicted.coeff(i, 1),
|
||||
predicted.coeff(i, 2));
|
||||
result.indexed_spot[i] = indexed_spots(i);
|
||||
|
||||
ret.emplace_back(result);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
struct IndexingResult {
|
||||
CrystalLattice l;
|
||||
std::vector<Coord> predicted_spots;
|
||||
std::vector<uint8_t> indexed_spot;
|
||||
};
|
||||
|
||||
class IndexerWrapper {
|
||||
@@ -36,7 +36,7 @@ class IndexerWrapper {
|
||||
constexpr const static uint32_t viable_cell_min_spots = 9;
|
||||
public:
|
||||
void Setup(const UnitCell &cell);
|
||||
std::vector<IndexingResult> Run(const std::vector<Coord> &coord, float indexing_threshold);
|
||||
std::vector<IndexingResult> Run(const std::vector<Coord> &coord, float indexing_threshold, int nspots = -1);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -27,12 +27,16 @@ MXAnalyzer::MXAnalyzer(const DiffractionExperiment &in_experiment)
|
||||
: experiment(in_experiment) {
|
||||
auto uc = experiment.GetUnitCell();
|
||||
if (uc) {
|
||||
do_indexing = true;
|
||||
indexer.Setup(uc.value());
|
||||
try {
|
||||
indexer = std::make_unique<IndexerWrapper>();
|
||||
indexer->Setup(uc.value());
|
||||
} catch (const std::exception &e) {
|
||||
throw JFJochException(JFJochExceptionCategory::GPUCUDAError, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
if (experiment.IsSpotFindingEnabled())
|
||||
find_spots = true;
|
||||
|
||||
}
|
||||
|
||||
void MXAnalyzer::ReadFromFPGA(const DeviceOutput *output, const SpotFindingSettings &settings, size_t module_number) {
|
||||
@@ -56,45 +60,114 @@ void MXAnalyzer::ReadFromCPU(const int16_t *image, const SpotFindingSettings &se
|
||||
|
||||
CalcSpotFinderResolutionMap(d_map.data(), experiment, module_number);
|
||||
|
||||
FindSpots(output, settings, d_map.data());
|
||||
arr_mean.resize(experiment.GetModulesNum() * RAW_MODULE_SIZE);
|
||||
arr_sttdev.resize(experiment.GetModulesNum() * RAW_MODULE_SIZE);
|
||||
arr_valid_count.resize(experiment.GetModulesNum() * RAW_MODULE_SIZE);
|
||||
arr_strong_pixel.resize(experiment.GetModulesNum() * RAW_MODULE_SIZE);
|
||||
|
||||
FindSpots(output,
|
||||
settings,
|
||||
d_map.data(),
|
||||
arr_mean.data() + module_number * RAW_MODULE_SIZE,
|
||||
arr_sttdev.data() + module_number * RAW_MODULE_SIZE,
|
||||
arr_valid_count.data() + module_number * RAW_MODULE_SIZE,
|
||||
arr_strong_pixel.data() + module_number * RAW_MODULE_SIZE);
|
||||
|
||||
ReadFromFPGA(&output, settings, module_number);
|
||||
}
|
||||
|
||||
uint32_t MXAnalyzer::FilterSpotsInPowderRings(const std::vector<DiffractionSpot> &spots_filter,
|
||||
std::vector<DiffractionSpot> &spots_out,
|
||||
int64_t min_spot_count_ring) {
|
||||
uint32_t ret = 0;
|
||||
|
||||
double high_q = 5.0;
|
||||
double low_q = 0;
|
||||
double q_spacing = 0.01;
|
||||
|
||||
size_t bin_count = (high_q - low_q) / q_spacing + 1;
|
||||
|
||||
std::vector<std::vector<uint32_t> > bins(bin_count);
|
||||
|
||||
for (int i = 0; i < spots_filter.size(); i++) {
|
||||
double q = 2 * M_PI / spots_filter[i].GetResolution(experiment);
|
||||
if ((q >= low_q) && (q < high_q)) {
|
||||
int32_t q_bin = std::floor((q - low_q) / q_spacing);
|
||||
bins[q_bin].push_back(i);
|
||||
} else // spots outside of azim. int. range are not filtered
|
||||
spots_out.push_back(spots_filter[i]);
|
||||
}
|
||||
|
||||
for (auto & bin : bins) {
|
||||
if (bin.size() > min_spot_count_ring)
|
||||
ret += bin.size();
|
||||
else {
|
||||
for (auto &iter: bin)
|
||||
spots_out.push_back(spots_filter[iter]);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MXAnalyzer::Process(DataMessage &message, const SpotFindingSettings& settings) {
|
||||
message.indexing_result = false;
|
||||
if (!find_spots)
|
||||
return;
|
||||
|
||||
std::vector<DiffractionSpot> spots_out;
|
||||
FilterSpotsByCount(experiment, spots, spots_out);
|
||||
if (settings.filter_spots_powder_ring) {
|
||||
std::vector<DiffractionSpot> spots_no_rings;
|
||||
message.spot_count_in_rings = FilterSpotsInPowderRings(spots,
|
||||
spots_no_rings,
|
||||
settings.min_spot_count_powder_ring);
|
||||
FilterSpotsByCount(experiment, spots_no_rings, spots_out);
|
||||
} else
|
||||
FilterSpotsByCount(experiment, spots, spots_out);
|
||||
|
||||
spots.clear();
|
||||
|
||||
for (const auto &spot: spots_out)
|
||||
message.spots.push_back(spot);
|
||||
|
||||
if (do_indexing && settings.indexing) {
|
||||
if (indexer && settings.indexing) {
|
||||
std::vector<Coord> recip;
|
||||
recip.reserve(spots_out.size());
|
||||
for (const auto &i: spots_out)
|
||||
recip.push_back(i.ReciprocalCoord(experiment));
|
||||
|
||||
auto indexer_result = indexer.Run(recip, settings.indexing_tolerance);
|
||||
std::vector<IndexingResult> indexer_result;
|
||||
|
||||
// If there is a really large number of spots detected, it is better to start with a smaller subset
|
||||
if (recip.size() > 80)
|
||||
indexer_result = indexer->Run(recip, settings.indexing_tolerance, 50);
|
||||
if (indexer_result.empty())
|
||||
indexer_result = indexer->Run(recip, settings.indexing_tolerance);
|
||||
|
||||
if (!indexer_result.empty()) {
|
||||
message.indexing_result = true;
|
||||
assert(indexer_result[0].indexed_spot.size() == recip.size());
|
||||
|
||||
// identify indexed spots
|
||||
for (int i = 0; i < recip.size(); i++) {
|
||||
auto predicted_pos = RecipToDector(experiment, indexer_result[0].predicted_spots[i]);
|
||||
float x_diff = predicted_pos.first - spots_out[i].RawCoord().x;
|
||||
float y_diff = predicted_pos.second - spots_out[i].RawCoord().y;
|
||||
message.spots[i].indexed = (x_diff * x_diff + y_diff * y_diff
|
||||
< spot_distance_threshold_pxl * spot_distance_threshold_pxl);
|
||||
}
|
||||
for (int i = 0; i < recip.size(); i++)
|
||||
message.spots[i].indexed = indexer_result[0].indexed_spot[i];
|
||||
|
||||
indexer_result[0].l.Save(message.indexing_lattice);
|
||||
message.indexing_unit_cell = indexer_result[0].l.GetUnitCell();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<float> &MXAnalyzer::GetCPUMean() const {
|
||||
return arr_mean;
|
||||
}
|
||||
|
||||
const std::vector<float> &MXAnalyzer::GetCPUStdDev() const {
|
||||
return arr_sttdev;
|
||||
}
|
||||
|
||||
const std::vector<uint32_t> &MXAnalyzer::GetCPUValidCount() const {
|
||||
return arr_valid_count;
|
||||
}
|
||||
|
||||
const std::vector<uint32_t> &MXAnalyzer::GetCPUStrongPixel() const {
|
||||
return arr_strong_pixel;
|
||||
}
|
||||
@@ -9,12 +9,21 @@
|
||||
|
||||
class MXAnalyzer {
|
||||
const DiffractionExperiment &experiment;
|
||||
IndexerWrapper indexer;
|
||||
bool do_indexing = false;
|
||||
std::unique_ptr<IndexerWrapper> indexer;
|
||||
bool find_spots = false;
|
||||
std::vector<DiffractionSpot> spots;
|
||||
constexpr static const float spot_distance_threshold_pxl = 2.0f;
|
||||
|
||||
std::vector<float> arr_mean;
|
||||
std::vector<float> arr_sttdev;
|
||||
std::vector<uint32_t> arr_valid_count;
|
||||
std::vector<uint32_t> arr_strong_pixel;
|
||||
public:
|
||||
const std::vector<float> &GetCPUMean() const;
|
||||
const std::vector<float> &GetCPUStdDev() const;
|
||||
const std::vector<uint32_t> &GetCPUValidCount() const;
|
||||
const std::vector<uint32_t> &GetCPUStrongPixel() const;
|
||||
|
||||
explicit MXAnalyzer(const DiffractionExperiment& experiment);
|
||||
|
||||
void ReadFromFPGA(const DeviceOutput* output,
|
||||
@@ -27,6 +36,10 @@ public:
|
||||
|
||||
void Process(DataMessage &message,
|
||||
const SpotFindingSettings& settings);
|
||||
|
||||
uint32_t FilterSpotsInPowderRings(const std::vector<DiffractionSpot> &spots_filter,
|
||||
std::vector<DiffractionSpot> &spots_out,
|
||||
int64_t min_spot_count_ring);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -7,14 +7,17 @@
|
||||
|
||||
struct SpotFindingSettings {
|
||||
bool enable = true;
|
||||
bool indexing = true;
|
||||
float signal_to_noise_threshold = 3; // STRONG_PIXEL in XDS
|
||||
int64_t photon_count_threshold = 10; // Threshold in photon counts
|
||||
int64_t min_pix_per_spot = 2; // Minimum pixels per spot
|
||||
int64_t max_pix_per_spot = 50; // Maximum pixels per spot
|
||||
float high_resolution_limit = 2.5;
|
||||
float high_resolution_limit = 2.0;
|
||||
float low_resolution_limit = 50.0;
|
||||
|
||||
bool filter_spots_powder_ring = false;
|
||||
int64_t min_spot_count_powder_ring = 20;
|
||||
|
||||
bool indexing = true;
|
||||
float indexing_tolerance = 0.1;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "JFCalibration.h"
|
||||
|
||||
#include <cstring>
|
||||
#include "../preview/WriteTIFF.h"
|
||||
#include "../preview/JFJochTIFF.h"
|
||||
|
||||
JFCalibration::JFCalibration(size_t in_nmodules, size_t in_nstorage_cells) :
|
||||
nmodules(in_nmodules),
|
||||
|
||||
@@ -1,21 +1,14 @@
|
||||
FIND_PACKAGE(TIFF COMPONENTS CXX REQUIRED)
|
||||
FIND_PACKAGE(JPEG REQUIRED)
|
||||
|
||||
ADD_LIBRARY(JFJochPreview STATIC
|
||||
WriteTIFF.cpp WriteTIFF.h
|
||||
WriteJPEG.cpp WriteJPEG.h
|
||||
JFJochTIFF.cpp JFJochTIFF.h
|
||||
JFJochJPEG.cpp JFJochJPEG.h
|
||||
PreviewCounter.cpp PreviewCounter.h
|
||||
PreviewImage.cpp PreviewImage.h)
|
||||
|
||||
TARGET_LINK_LIBRARIES(JFJochPreview PUBLIC JFJochCommon)
|
||||
|
||||
IF((EXISTS ${TIFF_INCLUDE_DIR}/tiffio.hxx) AND (EXISTS ${TIFF_INCLUDE_DIR}/tiffio.h))
|
||||
TARGET_INCLUDE_DIRECTORIES(JFJochPreview PRIVATE ${TIFF_INCLUDE_DIR})
|
||||
TARGET_LINK_LIBRARIES(JFJochPreview PUBLIC ${TIFF_LIBRARIES})
|
||||
MESSAGE(STATUS "TIFF headers present and library included")
|
||||
ELSE()
|
||||
MESSAGE(FATAL_ERROR "TIFF headers tiffio.h and tiffio.hxx not present")
|
||||
ENDIF()
|
||||
TARGET_LINK_LIBRARIES(JFJochPreview PUBLIC tiff tiffxx)
|
||||
|
||||
IF (EXISTS ${JPEG_INCLUDE_DIR}/jpeglib.h)
|
||||
TARGET_INCLUDE_DIRECTORIES(JFJochPreview PRIVATE ${JPEG_INCLUDE_DIR})
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <jpeglib.h>
|
||||
|
||||
#include "../common/JFJochException.h"
|
||||
#include "WriteJPEG.h"
|
||||
#include "JFJochJPEG.h"
|
||||
|
||||
|
||||
std::string WriteJPEGToMem(const std::vector<uint8_t> &input, size_t width, size_t height, int quality) {
|
||||
110
preview/JFJochTIFF.cpp
Normal file
110
preview/JFJochTIFF.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
// Copyright (2019-2024) Paul Scherrer Institute
|
||||
|
||||
#include <tiffio.h>
|
||||
#include <tiffio.hxx>
|
||||
#include <sstream>
|
||||
|
||||
#include "JFJochTIFF.h"
|
||||
#include "../common/JFJochException.h"
|
||||
|
||||
|
||||
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_NONE); // 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);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> ReadTIFFFromString32(const std::string &s, uint32_t &cols, uint32_t &lines) {
|
||||
uint32_t rows_per_string = 0;
|
||||
|
||||
std::vector<uint32_t> ret;
|
||||
|
||||
uint16_t elem_size;
|
||||
|
||||
std::istringstream input_TIFF_stream(s);
|
||||
TIFF* tiff = TIFFStreamOpen("MemTIFF", &input_TIFF_stream);
|
||||
TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &cols); // get the width of the image
|
||||
TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &lines); // get the height of the image
|
||||
TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &elem_size); // get the size of the channels
|
||||
TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rows_per_string);
|
||||
|
||||
if (elem_size != 32)
|
||||
throw JFJochException(JFJochExceptionCategory::TIFFGeneratorError, "Only 32-bit format supported");
|
||||
|
||||
ret.resize(cols * lines);
|
||||
|
||||
if (cols * sizeof(uint32_t) != TIFFScanlineSize(tiff))
|
||||
throw JFJochException(JFJochExceptionCategory::TIFFGeneratorError, "TIFFScanlineSize mismatch");
|
||||
|
||||
for (int i = 0; i < lines; i++) {
|
||||
if (TIFFReadScanline(tiff, ret.data() + i * cols, i, 0) < 0)
|
||||
throw JFJochException(JFJochExceptionCategory::TIFFGeneratorError, "TIFFReadScanline error");
|
||||
}
|
||||
|
||||
TIFFClose(tiff);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<uint16_t> ReadTIFFFromString16(const std::string &s, uint32_t &cols, uint32_t &lines) {
|
||||
uint32_t rows_per_string = 0;
|
||||
|
||||
std::vector<uint16_t> ret;
|
||||
|
||||
uint16_t elem_size;
|
||||
|
||||
std::istringstream input_TIFF_stream(s);
|
||||
TIFF* tiff = TIFFStreamOpen("MemTIFF", &input_TIFF_stream);
|
||||
TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &cols); // get the width of the image
|
||||
TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &lines); // get the height of the image
|
||||
TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &elem_size); // get the size of the channels
|
||||
TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rows_per_string);
|
||||
|
||||
if (elem_size != 16)
|
||||
throw JFJochException(JFJochExceptionCategory::TIFFGeneratorError, "Only 16-bit format supported");
|
||||
|
||||
ret.resize(cols * lines);
|
||||
|
||||
if (cols * sizeof(uint16_t) != TIFFScanlineSize(tiff))
|
||||
throw JFJochException(JFJochExceptionCategory::TIFFGeneratorError, "TIFFScanlineSize mismatch");
|
||||
|
||||
for (int i = 0; i < lines; i++) {
|
||||
if (TIFFReadScanline(tiff, ret.data() + i * cols, i, 0) < 0)
|
||||
throw JFJochException(JFJochExceptionCategory::TIFFGeneratorError, "TIFFReadScanline error");
|
||||
}
|
||||
|
||||
TIFFClose(tiff);
|
||||
return ret;
|
||||
}
|
||||
@@ -4,9 +4,12 @@
|
||||
#define JUNGFRAUJOCH_WRITETIFF_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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);
|
||||
std::vector<uint32_t> ReadTIFFFromString32(const std::string& s, uint32_t &cols, uint32_t &lines);
|
||||
std::vector<uint16_t> ReadTIFFFromString16(const std::string& s, uint32_t &cols, uint32_t &lines);
|
||||
|
||||
#endif //JUNGFRAUJOCH_WRITETIFF_H
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "WriteJPEG.h"
|
||||
#include "WriteTIFF.h"
|
||||
#include "JFJochJPEG.h"
|
||||
#include "JFJochTIFF.h"
|
||||
#include "../common/JFJochException.h"
|
||||
#include "../common/DiffractionGeometry.h"
|
||||
|
||||
@@ -31,26 +31,7 @@ constexpr const static rgb plotly[] = {{0x1f, 0x77, 0xb4},
|
||||
constexpr const static rgb indigo = {.r = 0x3f, .g = 0x51, .b = 0xb5};
|
||||
constexpr const static rgb gray = {.r = 0xbe, .g = 0xbe, .b = 0xbe};
|
||||
|
||||
PreviewImage::PreviewImage(const DiffractionExperiment &in_experiment) :
|
||||
experiment(in_experiment),
|
||||
xpixel(experiment.GetXPixelsNum()),
|
||||
ypixel(experiment.GetYPixelsNum()),
|
||||
beam_x(experiment.GetBeamX_pxl()),
|
||||
beam_y(experiment.GetBeamY_pxl()),
|
||||
pixel_depth_bytes(experiment.GetPixelDepth()),
|
||||
pixel_is_signed(experiment.IsPixelSigned()),
|
||||
uncompressed_image(experiment.GetPixelsNum() * experiment.GetPixelDepth()),
|
||||
roi_map(experiment.ROI()),
|
||||
counter(experiment.GetPreviewPeriod()) {}
|
||||
|
||||
void PreviewImage::UpdateImage(const void *in_uncompressed_image,
|
||||
const std::vector<SpotToSave> &in_spots) {
|
||||
if (counter.GeneratePreview()) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
memcpy(uncompressed_image.data(), in_uncompressed_image, xpixel * ypixel * pixel_depth_bytes);
|
||||
spots = in_spots;
|
||||
}
|
||||
}
|
||||
PreviewImage::PreviewImage(std::chrono::microseconds period) : counter(period) {}
|
||||
|
||||
void colormap(std::vector<unsigned char>& ret, float v, size_t pixel) {
|
||||
if ((v < 0.0) || (v > 1.0)) {
|
||||
@@ -104,48 +85,6 @@ std::vector<unsigned char> GenerateRGB(const T* value, size_t npixel, uint32_t s
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string PreviewImage::GenerateJPEG(const PreviewJPEGSettings &settings) const {
|
||||
std::vector<unsigned char> v;
|
||||
{
|
||||
// JPEG compression is outside the critical loop protected by m
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (!pixel_is_signed) {
|
||||
if (pixel_depth_bytes == 2)
|
||||
v = GenerateRGB<uint16_t>((uint16_t *) uncompressed_image.data(), xpixel * ypixel,
|
||||
settings.saturation_value, UINT16_MAX);
|
||||
else
|
||||
v = GenerateRGB<uint32_t>((uint32_t *) uncompressed_image.data(), xpixel * ypixel,
|
||||
settings.saturation_value, UINT32_MAX);
|
||||
} else {
|
||||
if (pixel_depth_bytes == 2)
|
||||
v = GenerateRGB<int16_t>((int16_t *) uncompressed_image.data(), xpixel * ypixel,
|
||||
settings.saturation_value, INT16_MIN);
|
||||
else
|
||||
v = GenerateRGB<int32_t>((int32_t *) uncompressed_image.data(), xpixel * ypixel,
|
||||
settings.saturation_value, INT32_MIN);
|
||||
}
|
||||
if (settings.show_spots)
|
||||
AddSpots(v);
|
||||
}
|
||||
|
||||
if (settings.show_roi)
|
||||
AddROI(v);
|
||||
|
||||
if (settings.resolution_ring)
|
||||
AddResolutionRing(v, settings.resolution_ring.value());
|
||||
|
||||
AddBeamCenter(v);
|
||||
|
||||
return WriteJPEGToMem(v, xpixel, ypixel, settings.jpeg_quality);
|
||||
}
|
||||
|
||||
std::string PreviewImage::GenerateTIFF() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
std::string s = WriteTIFFToString(const_cast<uint8_t *>(uncompressed_image.data()),
|
||||
xpixel, ypixel, pixel_depth_bytes, pixel_is_signed);
|
||||
return s;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::vector<uint16_t> GenerateDioptasPreview(const void* input, size_t xpixel, size_t ypixel, T special_value) {
|
||||
auto input_ptr = (T *) input;
|
||||
@@ -165,24 +104,6 @@ std::vector<uint16_t> GenerateDioptasPreview(const void* input, size_t xpixel, s
|
||||
return vec;
|
||||
}
|
||||
|
||||
std::string PreviewImage::GenerateTIFFDioptas() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
std::vector<uint16_t> vec;
|
||||
if (pixel_is_signed) {
|
||||
if (pixel_depth_bytes == 2)
|
||||
vec = GenerateDioptasPreview<int16_t>(uncompressed_image.data(), xpixel, ypixel, INT16_MIN);
|
||||
else
|
||||
vec = GenerateDioptasPreview<int32_t>(uncompressed_image.data(), xpixel, ypixel, INT32_MIN);
|
||||
} else {
|
||||
if (pixel_depth_bytes == 2)
|
||||
vec = GenerateDioptasPreview<uint16_t>(uncompressed_image.data(), xpixel, ypixel, UINT16_MAX);
|
||||
else
|
||||
vec = GenerateDioptasPreview<uint32_t>(uncompressed_image.data(), xpixel, ypixel, UINT32_MAX);
|
||||
}
|
||||
return WriteTIFFToString(vec.data(), xpixel, ypixel, 2, false);
|
||||
}
|
||||
|
||||
void PreviewImage::AddBeamCenter(std::vector<uint8_t> &rgb_image) const {
|
||||
size_t beam_x_int = std::lround(beam_x);
|
||||
size_t beam_y_int = std::lround(beam_y);
|
||||
@@ -219,7 +140,7 @@ void PreviewImage::AddSpots(std::vector<uint8_t> &rgb_image) const {
|
||||
void PreviewImage::AddROI(std::vector<uint8_t> &rgb_image) const {
|
||||
int64_t roi_counter = 0;
|
||||
|
||||
for (const auto &box: roi_map.GetROIBox()) {
|
||||
for (const auto &box: experiment.ROI().GetROIBox()) {
|
||||
int rectangle_width = 5;
|
||||
|
||||
for (auto x = box.GetXMin() - rectangle_width; x <= box.GetXMax() + rectangle_width; x++) {
|
||||
@@ -238,7 +159,7 @@ void PreviewImage::AddROI(std::vector<uint8_t> &rgb_image) const {
|
||||
roi_counter++;
|
||||
}
|
||||
|
||||
for (const auto &circle: roi_map.GetROICircle()) {
|
||||
for (const auto &circle: experiment.ROI().GetROICircle()) {
|
||||
int width = 5;
|
||||
|
||||
for (int64_t y = std::floor(circle.GetY() - circle.GetRadius_pxl() - width);
|
||||
@@ -270,3 +191,132 @@ void PreviewImage::AddResolutionRing(std::vector<uint8_t> &rgb_image, float d) c
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PreviewImage::AddUserMask(std::vector<uint8_t> &rgb_image) const {
|
||||
for (int y = 0; y < ypixel; y++) {
|
||||
for (int x = 0; x < xpixel; x++) {
|
||||
if (user_mask[y * xpixel + x] != 0)
|
||||
color_pixel(rgb_image, x, y, gray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PreviewImage::Configure(const DiffractionExperiment &in_experiment, const PixelMask& mask) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
experiment = in_experiment;
|
||||
initialized = false;
|
||||
xpixel = experiment.GetXPixelsNum();
|
||||
ypixel = experiment.GetYPixelsNum();
|
||||
beam_x = experiment.GetBeamX_pxl();
|
||||
beam_y = experiment.GetBeamY_pxl();
|
||||
pixel_depth_bytes = experiment.GetPixelDepth();
|
||||
pixel_is_signed = experiment.IsPixelSigned();
|
||||
uncompressed_image.resize(experiment.GetPixelsNum() * experiment.GetPixelDepth());
|
||||
memset(uncompressed_image.data(), 0, experiment.GetPixelsNum() * experiment.GetPixelDepth());
|
||||
user_mask = mask.GetUserMask(experiment);
|
||||
if (user_mask.size() != experiment.GetPixelsNum())
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "User pixel mask size incorrect");
|
||||
}
|
||||
|
||||
void PreviewImage::Configure() {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
initialized = false;
|
||||
xpixel = 0;
|
||||
ypixel = 0;
|
||||
}
|
||||
|
||||
void PreviewImage::UpdateImage(const void *in_uncompressed_image,
|
||||
const std::vector<SpotToSave> &in_spots) {
|
||||
if (counter.GeneratePreview()) {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
if (xpixel * ypixel == 0)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Preview not configured");
|
||||
|
||||
initialized = true;
|
||||
memcpy(uncompressed_image.data(), in_uncompressed_image, xpixel * ypixel * pixel_depth_bytes);
|
||||
spots = in_spots;
|
||||
}
|
||||
}
|
||||
|
||||
std::string PreviewImage::GenerateJPEG(const PreviewJPEGSettings &settings) const {
|
||||
std::vector<unsigned char> v;
|
||||
size_t local_xpixel;
|
||||
size_t local_ypixel;
|
||||
|
||||
{
|
||||
// JPEG compression is outside the critical loop protected by m
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
|
||||
local_xpixel = xpixel;
|
||||
local_ypixel = ypixel;
|
||||
|
||||
if (!initialized)
|
||||
return {};
|
||||
|
||||
if (!pixel_is_signed) {
|
||||
if (pixel_depth_bytes == 2)
|
||||
v = GenerateRGB<uint16_t>((uint16_t *) uncompressed_image.data(), xpixel * ypixel,
|
||||
settings.saturation_value, UINT16_MAX);
|
||||
else
|
||||
v = GenerateRGB<uint32_t>((uint32_t *) uncompressed_image.data(), xpixel * ypixel,
|
||||
settings.saturation_value, UINT32_MAX);
|
||||
} else {
|
||||
if (pixel_depth_bytes == 2)
|
||||
v = GenerateRGB<int16_t>((int16_t *) uncompressed_image.data(), xpixel * ypixel,
|
||||
settings.saturation_value, INT16_MIN);
|
||||
else
|
||||
v = GenerateRGB<int32_t>((int32_t *) uncompressed_image.data(), xpixel * ypixel,
|
||||
settings.saturation_value, INT32_MIN);
|
||||
}
|
||||
|
||||
if (settings.show_user_mask)
|
||||
AddUserMask(v);
|
||||
|
||||
if (settings.show_spots)
|
||||
AddSpots(v);
|
||||
|
||||
if (settings.show_roi)
|
||||
AddROI(v);
|
||||
|
||||
if (settings.resolution_ring)
|
||||
AddResolutionRing(v, settings.resolution_ring.value());
|
||||
|
||||
AddBeamCenter(v);
|
||||
}
|
||||
|
||||
return WriteJPEGToMem(v, local_xpixel, local_ypixel, settings.jpeg_quality);
|
||||
}
|
||||
|
||||
|
||||
std::string PreviewImage::GenerateTIFF() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (!initialized)
|
||||
return {};
|
||||
|
||||
std::string s = WriteTIFFToString(const_cast<uint8_t *>(uncompressed_image.data()),
|
||||
xpixel, ypixel, pixel_depth_bytes, pixel_is_signed);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string PreviewImage::GenerateTIFFDioptas() const {
|
||||
std::unique_lock<std::mutex> ul(m);
|
||||
if (!initialized)
|
||||
return {};
|
||||
|
||||
std::vector<uint16_t> vec;
|
||||
if (pixel_is_signed) {
|
||||
if (pixel_depth_bytes == 2)
|
||||
vec = GenerateDioptasPreview<int16_t>(uncompressed_image.data(), xpixel, ypixel, INT16_MIN);
|
||||
else
|
||||
vec = GenerateDioptasPreview<int32_t>(uncompressed_image.data(), xpixel, ypixel, INT32_MIN);
|
||||
} else {
|
||||
if (pixel_depth_bytes == 2)
|
||||
vec = GenerateDioptasPreview<uint16_t>(uncompressed_image.data(), xpixel, ypixel, UINT16_MAX);
|
||||
else
|
||||
vec = GenerateDioptasPreview<uint32_t>(uncompressed_image.data(), xpixel, ypixel, UINT32_MAX);
|
||||
}
|
||||
return WriteTIFFToString(vec.data(), xpixel, ypixel, 2, false);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "../common/SpotToSave.h"
|
||||
#include "../common/DiffractionExperiment.h"
|
||||
#include "../common/PixelMask.h"
|
||||
#include "PreviewCounter.h"
|
||||
|
||||
struct PreviewJPEGSettings {
|
||||
@@ -16,6 +17,7 @@ struct PreviewJPEGSettings {
|
||||
bool show_spots = true;
|
||||
bool show_roi = false;
|
||||
bool show_indexed = false;
|
||||
bool show_user_mask = false;
|
||||
std::optional<float> resolution_ring;
|
||||
};
|
||||
|
||||
@@ -29,20 +31,22 @@ class PreviewImage {
|
||||
mutable std::mutex m;
|
||||
DiffractionExperiment experiment;
|
||||
|
||||
const ROIMap roi_map;
|
||||
bool initialized = false;
|
||||
std::vector<uint8_t> uncompressed_image;
|
||||
std::vector<uint32_t> user_mask;
|
||||
std::vector<SpotToSave> spots;
|
||||
size_t xpixel;
|
||||
size_t ypixel;
|
||||
size_t pixel_depth_bytes;
|
||||
bool pixel_is_signed;
|
||||
float beam_x;
|
||||
float beam_y;
|
||||
size_t xpixel = 0;
|
||||
size_t ypixel = 0;
|
||||
size_t pixel_depth_bytes = 2;
|
||||
bool pixel_is_signed = false;
|
||||
float beam_x = 0;
|
||||
float beam_y = 0;
|
||||
|
||||
void AddResolutionRing(std::vector<uint8_t> &rgb_image, float d) const;
|
||||
void AddBeamCenter(std::vector<uint8_t> &rgb_image) const;
|
||||
void AddSpots(std::vector<uint8_t> &rgb_image) const;
|
||||
void AddROI(std::vector<uint8_t> &rgb_image) const;
|
||||
void AddUserMask(std::vector<uint8_t> &rgb_image) const;
|
||||
PreviewCounter counter;
|
||||
|
||||
void color_pixel(std::vector<unsigned char>& ret, int64_t xpixel, int64_t ypixel, const rgb &color) const;
|
||||
@@ -51,7 +55,9 @@ class PreviewImage {
|
||||
void beam_center_mark(std::vector<unsigned char>& ret, int64_t xpixel, int64_t ypixel) const;
|
||||
|
||||
public:
|
||||
explicit PreviewImage(const DiffractionExperiment& experiment);
|
||||
explicit PreviewImage(std::chrono::microseconds period = std::chrono::seconds(1));
|
||||
void Configure(const DiffractionExperiment& experiment, const PixelMask& mask);
|
||||
void Configure();
|
||||
void UpdateImage(const void *uncompressed_image, const std::vector<SpotToSave> &spots);
|
||||
[[nodiscard]] std::string GenerateJPEG(const PreviewJPEGSettings& settings) const;
|
||||
[[nodiscard]] std::string GenerateTIFF() const;
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
// Copyright (2019-2024) Paul Scherrer Institute
|
||||
|
||||
#include <tiffio.h>
|
||||
#include <tiffio.hxx>
|
||||
#include <sstream>
|
||||
|
||||
#include "WriteTIFF.h"
|
||||
#include "../common/JFJochException.h"
|
||||
|
||||
|
||||
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_NONE); // 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);
|
||||
}
|
||||
@@ -15,4 +15,4 @@ TARGET_LINK_LIBRARIES(JFJochReceiver ImagePusher JFJochImageAnalysis JFJochAcqui
|
||||
|
||||
ADD_EXECUTABLE(jfjoch_action_test jfjoch_action_test.cpp)
|
||||
TARGET_LINK_LIBRARIES(jfjoch_action_test JFJochReceiver)
|
||||
INSTALL(TARGETS jfjoch_action_test RUNTIME)
|
||||
INSTALL(TARGETS jfjoch_action_test RUNTIME COMPONENT jfjoch)
|
||||
|
||||
@@ -11,12 +11,15 @@
|
||||
#include "../common/PixelMask.h"
|
||||
|
||||
JFJochReceiver::JFJochReceiver(const DiffractionExperiment& in_experiment,
|
||||
const PixelMask &pixel_mask,
|
||||
const JFCalibration *in_calibration,
|
||||
AcquisitionDeviceGroup &in_aq_device,
|
||||
ImagePusher &in_image_sender,
|
||||
Logger &in_logger, int64_t in_forward_and_sum_nthreads,
|
||||
const NUMAHWPolicy &in_numa_policy,
|
||||
const SpotFindingSettings &in_spot_finding_settings,
|
||||
PreviewImage &in_preview_image,
|
||||
PreviewImage &in_preview_image_indexed,
|
||||
SendBuffer &buf) :
|
||||
experiment(in_experiment),
|
||||
calibration(nullptr),
|
||||
@@ -33,9 +36,10 @@ JFJochReceiver::JFJochReceiver(const DiffractionExperiment& in_experiment,
|
||||
az_int_mapping(experiment),
|
||||
plots(experiment, az_int_mapping),
|
||||
spot_finding_settings(in_spot_finding_settings),
|
||||
preview_image(experiment),
|
||||
preview_image_indexed(experiment),
|
||||
serialmx_filter(experiment)
|
||||
preview_image(in_preview_image),
|
||||
preview_image_indexed(in_preview_image_indexed),
|
||||
serialmx_filter(experiment),
|
||||
user_mask_raw_coord(pixel_mask.GetUserMask(experiment, false))
|
||||
{
|
||||
if (experiment.GetDetectorSetup().GetDetectorType() == DetectorType::JUNGFRAU)
|
||||
calibration = in_calibration;
|
||||
@@ -60,15 +64,16 @@ JFJochReceiver::JFJochReceiver(const DiffractionExperiment& in_experiment,
|
||||
logger.Info("Data acquisition devices ready");
|
||||
|
||||
if (experiment.GetImageNum() > 0) {
|
||||
SendStartMessage();
|
||||
SendStartMessage(pixel_mask);
|
||||
SendCalibration();
|
||||
|
||||
for (int i = 0; i < experiment.GetImageNum(); i++)
|
||||
images_to_go.Put(i);
|
||||
|
||||
// Setup frames summation and forwarding
|
||||
for (int i = 0; i < frame_transformation_nthreads; i++) {
|
||||
auto handle = std::async(std::launch::async, &JFJochReceiver::FrameTransformationThread, this);
|
||||
for (uint32_t i = 0; i < frame_transformation_nthreads; i++) {
|
||||
auto handle = std::async(std::launch::async, &JFJochReceiver::FrameTransformationThread,
|
||||
this, i);
|
||||
frame_transformation_futures.emplace_back(std::move(handle));
|
||||
}
|
||||
|
||||
@@ -87,7 +92,7 @@ JFJochReceiver::JFJochReceiver(const DiffractionExperiment& in_experiment,
|
||||
logger.Info("Receiving data started");
|
||||
}
|
||||
|
||||
void JFJochReceiver::SendStartMessage() {
|
||||
void JFJochReceiver::SendStartMessage(const PixelMask &pixel_mask) {
|
||||
if (!push_images_to_writer)
|
||||
return;
|
||||
|
||||
@@ -98,11 +103,6 @@ void JFJochReceiver::SendStartMessage() {
|
||||
message.az_int_bin_to_q = az_int_mapping.GetBinToQ();
|
||||
message.write_master_file = true;
|
||||
|
||||
PixelMask pixel_mask(experiment);
|
||||
|
||||
if (calibration)
|
||||
pixel_mask.LoadDetectorBadPixelMask(calibration->CalculateMask(), 1);
|
||||
|
||||
std::vector<uint32_t> nexus_mask;
|
||||
nexus_mask = pixel_mask.GetMask(experiment);
|
||||
|
||||
@@ -177,6 +177,10 @@ void JFJochReceiver::AcquireThread(uint16_t data_stream) {
|
||||
std::vector<float> tmp(RAW_MODULE_SIZE);
|
||||
for (int m = 0; m < experiment.GetModulesNum(data_stream); m++) {
|
||||
CalcSpotFinderResolutionMap(tmp.data(), experiment, m_offset + m);
|
||||
for (int i = 0; i < RAW_MODULE_SIZE; i++) {
|
||||
if (user_mask_raw_coord[(m_offset + m) * RAW_MODULE_SIZE + i] != 0)
|
||||
tmp[i] = 0.0f;
|
||||
}
|
||||
acquisition_device[data_stream].InitializeSpotFinderResolutionMap(tmp.data(), m);
|
||||
|
||||
CalcAzIntCorrRawCoord(tmp.data(), experiment, m_offset + m);
|
||||
@@ -255,20 +259,22 @@ void JFJochReceiver::RetrievePedestal() {
|
||||
}
|
||||
}
|
||||
|
||||
void JFJochReceiver::FrameTransformationThread() {
|
||||
void JFJochReceiver::FrameTransformationThread(uint32_t threadid) {
|
||||
|
||||
std::unique_ptr<MXAnalyzer> analyzer;
|
||||
|
||||
try {
|
||||
numa_policy.Bind();
|
||||
numa_policy.Bind(threadid);
|
||||
analyzer = std::make_unique<MXAnalyzer>(experiment);
|
||||
} catch (const JFJochException &e) {
|
||||
frame_transformation_ready.count_down();
|
||||
logger.Error("HW bind error {}", e.what());
|
||||
logger.Error("Thread setup error {}", e.what());
|
||||
Cancel(e);
|
||||
return;
|
||||
}
|
||||
|
||||
FrameTransformation transformation(experiment);
|
||||
|
||||
MXAnalyzer analyzer(experiment);
|
||||
|
||||
frame_transformation_ready.count_down();
|
||||
|
||||
uint16_t az_int_min_bin = std::floor(az_int_mapping.QToBin(experiment.GetLowQForBkgEstimate_recipA()));
|
||||
@@ -291,8 +297,8 @@ void JFJochReceiver::FrameTransformationThread() {
|
||||
message.number = image_number;
|
||||
message.original_number = image_number;
|
||||
message.user_data = experiment.GetImageAppendix();
|
||||
message.series_id = experiment.GetSeriesID();
|
||||
message.series_unique_id = experiment.GetSeriesIDString();
|
||||
message.series_id = experiment.GetRunNumber();
|
||||
message.series_unique_id = experiment.GetRunName();
|
||||
|
||||
ImageMetadata metadata(experiment);
|
||||
|
||||
@@ -313,7 +319,7 @@ void JFJochReceiver::FrameTransformationThread() {
|
||||
adu_histogram_module[module_abs_number].Add(*output);
|
||||
az_int_profile_image.Add(*output);
|
||||
|
||||
analyzer.ReadFromFPGA(output, local_spot_finding_settings, module_abs_number);
|
||||
analyzer->ReadFromFPGA(output, local_spot_finding_settings, module_abs_number);
|
||||
|
||||
transformation.ProcessModule(output, d);
|
||||
} else
|
||||
@@ -332,7 +338,7 @@ void JFJochReceiver::FrameTransformationThread() {
|
||||
continue;
|
||||
}
|
||||
|
||||
analyzer.Process(message, local_spot_finding_settings);
|
||||
analyzer->Process(message, local_spot_finding_settings);
|
||||
|
||||
message.receiver_free_send_buf = send_buf_ctrl.GetAvailBufLocations();
|
||||
message.az_int_profile = az_int_profile_image.GetResult();
|
||||
@@ -369,12 +375,12 @@ void JFJochReceiver::FrameTransformationThread() {
|
||||
size_t image_size = transformation.CompressImage(writer_buffer + serializer.GetImageAppendOffset());
|
||||
serializer.AppendImage(image_size);
|
||||
compressed_size += image_size;
|
||||
image_pusher.SendImage(writer_buffer, serializer.GetBufferSize(), image_number, loc);
|
||||
image_pusher.SendImage(writer_buffer, serializer.GetBufferSize(), message.number, loc);
|
||||
|
||||
images_sent++; // Handle case when image not sent properly
|
||||
UpdateMaxImage(image_number);
|
||||
UpdateMaxImage(message.number);
|
||||
|
||||
logger.Debug("Frame transformation thread - done sending image {}", image_number);
|
||||
logger.Debug("Frame transformation thread - done sending image {} / {}", image_number, message.number);
|
||||
} catch (const JFJochException &e) {
|
||||
logger.ErrorException(e);
|
||||
Cancel(e);
|
||||
@@ -446,8 +452,8 @@ void JFJochReceiver::FinalizeMeasurement() {
|
||||
message.max_receiver_delay = max_delay;
|
||||
message.efficiency = GetEfficiency();
|
||||
message.end_date = time_UTC(std::chrono::system_clock::now());
|
||||
message.series_id = experiment.GetSeriesID();
|
||||
message.series_unique_id = experiment.GetSeriesIDString();
|
||||
message.series_id = experiment.GetRunNumber();
|
||||
message.series_unique_id = experiment.GetRunName();
|
||||
|
||||
message.az_int_result["dataset"] = plots.GetAzIntProfile();
|
||||
for (int i = 0; i < experiment.GetTimePointNumber(); i++)
|
||||
@@ -552,10 +558,10 @@ JFJochReceiverStatus JFJochReceiver::GetStatus() const {
|
||||
|
||||
if ((experiment.GetImageNum() > 0) && (compressed_size > 0)) {
|
||||
ret.compressed_ratio = static_cast<double> ((images_sent + images_skipped)
|
||||
* experiment.GetPixelDepth()
|
||||
* experiment.GetModulesNum()
|
||||
* RAW_MODULE_SIZE)
|
||||
/ static_cast<double> (compressed_size);
|
||||
* experiment.GetPixelDepth()
|
||||
* experiment.GetModulesNum()
|
||||
* RAW_MODULE_SIZE)
|
||||
/ static_cast<double> (compressed_size);
|
||||
}
|
||||
|
||||
ret.progress = GetProgress();
|
||||
@@ -570,20 +576,6 @@ JFJochReceiverStatus JFJochReceiver::GetStatus() const {
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string JFJochReceiver::GetTIFF(bool calibration_run) const {
|
||||
if (calibration_run)
|
||||
return preview_image.GenerateTIFFDioptas();
|
||||
else
|
||||
return preview_image.GenerateTIFF();
|
||||
}
|
||||
|
||||
std::string JFJochReceiver::GetJPEG(const PreviewJPEGSettings &settings) const {
|
||||
if (settings.show_indexed)
|
||||
return preview_image_indexed.GenerateJPEG(settings);
|
||||
else
|
||||
return preview_image.GenerateJPEG(settings);
|
||||
}
|
||||
|
||||
void JFJochReceiver::GetXFELEventCode(std::vector<uint64_t> &v) const {
|
||||
if (experiment.IsPulsedSource())
|
||||
plots.GetXFELEventCode(v);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "../common/ThreadSafeFIFO.h"
|
||||
#include "../common/NUMAHWPolicy.h"
|
||||
#include "../common/SendBufferControl.h"
|
||||
#include "../common/PixelMask.h"
|
||||
|
||||
#include "../image_analysis/StrongPixelSet.h"
|
||||
#include "../image_analysis/AzimuthalIntegrationMapping.h"
|
||||
@@ -108,29 +109,34 @@ class JFJochReceiver {
|
||||
|
||||
NUMAHWPolicy numa_policy;
|
||||
|
||||
PreviewImage preview_image;
|
||||
PreviewImage preview_image_indexed;
|
||||
PreviewImage &preview_image;
|
||||
PreviewImage &preview_image_indexed;
|
||||
|
||||
LossyFilter serialmx_filter;
|
||||
|
||||
std::vector<uint32_t> user_mask_raw_coord;
|
||||
|
||||
void AcquireThread(uint16_t data_stream);
|
||||
void FrameTransformationThread();
|
||||
void FrameTransformationThread(uint32_t threadid);
|
||||
void Cancel(const JFJochException &e);
|
||||
void FinalizeMeasurement();
|
||||
void RetrievePedestal();
|
||||
SpotFindingSettings GetSpotFindingSettings();
|
||||
void UpdateMaxImage(uint64_t image_number);
|
||||
void UpdateMaxDelay(uint64_t delay);
|
||||
void SendStartMessage();
|
||||
void SendStartMessage(const PixelMask &pixel_mask);
|
||||
void SendCalibration();
|
||||
public:
|
||||
JFJochReceiver(const DiffractionExperiment& experiment,
|
||||
const PixelMask &pixel_mask,
|
||||
const JFCalibration *calibration,
|
||||
AcquisitionDeviceGroup &acquisition_devices,
|
||||
ImagePusher &image_pusher,
|
||||
Logger &logger, int64_t forward_and_sum_nthreads,
|
||||
const NUMAHWPolicy &numa_policy,
|
||||
const SpotFindingSettings &spot_finding_settings,
|
||||
PreviewImage &preview_image,
|
||||
PreviewImage &preview_image_indexed,
|
||||
SendBuffer &buffer);
|
||||
~JFJochReceiver();
|
||||
JFJochReceiver(const JFJochReceiver &other) = delete;
|
||||
@@ -148,9 +154,6 @@ public:
|
||||
|
||||
MultiLinePlot GetPlots(const PlotRequest& request);
|
||||
|
||||
std::string GetTIFF(bool calibration) const;
|
||||
std::string GetJPEG(const PreviewJPEGSettings &settings) const;
|
||||
|
||||
void GetXFELEventCode(std::vector<uint64_t> &v) const;
|
||||
void GetXFELPulseID(std::vector<uint64_t> &v) const;
|
||||
};
|
||||
|
||||
@@ -23,15 +23,15 @@ JFJochReceiverPlots::JFJochReceiverPlots(const DiffractionExperiment &experiment
|
||||
void JFJochReceiverPlots::Add(const DataMessage &msg, uint64_t file_number, const AzimuthalIntegrationProfile &profile) {
|
||||
bkg_estimate.AddElement(msg.number, msg.bkg_estimate);
|
||||
spot_count.AddElement(msg.number, msg.spots.size());
|
||||
spot_count_in_rings.AddElement(msg.number, msg.spot_count_in_rings);
|
||||
|
||||
saturated_pixels.AddElement(msg.number, msg.saturated_pixel_count);
|
||||
error_pixels.AddElement(msg.number, msg.error_pixel_count);
|
||||
strong_pixels.AddElement(msg.number, msg.strong_pixel_count);
|
||||
|
||||
image_collection_efficiency.AddElement(msg.number, msg.image_collection_efficiency);
|
||||
if (msg.receiver_aq_dev_delay)
|
||||
receiver_delay.AddElement(msg.number, msg.receiver_aq_dev_delay.value());
|
||||
if (msg.receiver_free_send_buf)
|
||||
receiver_free_send_buf.AddElement(msg.number, msg.receiver_free_send_buf.value());
|
||||
receiver_delay.AddElement(msg.number, msg.receiver_aq_dev_delay);
|
||||
receiver_free_send_buf.AddElement(msg.number, msg.receiver_free_send_buf);
|
||||
|
||||
indexing_solution.AddElement(msg.number, msg.indexing_result);
|
||||
indexing_solution_per_time_point.Add(file_number, msg.indexing_result);
|
||||
@@ -39,11 +39,8 @@ void JFJochReceiverPlots::Add(const DataMessage &msg, uint64_t file_number, cons
|
||||
az_int_profile += profile;
|
||||
az_int_profile_per_time_point.at(file_number) += profile;
|
||||
|
||||
if (msg.xfel_pulse_id)
|
||||
xfel_pulse_id.AddElement(msg.number, msg.xfel_pulse_id.value());
|
||||
|
||||
if (msg.xfel_event_code)
|
||||
xfel_event_code.AddElement(msg.number, msg.xfel_event_code.value());
|
||||
xfel_pulse_id.AddElement(msg.number, msg.xfel_pulse_id);
|
||||
xfel_event_code.AddElement(msg.number, msg.xfel_event_code);
|
||||
|
||||
for (const auto &[key, value] : msg.roi) {
|
||||
if (roi_sum.contains(key) && roi_sum[key])
|
||||
@@ -69,7 +66,15 @@ MultiLinePlot JFJochReceiverPlots::GetPlots(const PlotRequest &request) {
|
||||
case PlotType::RadInt:
|
||||
return az_int_profile.GetPlot();
|
||||
case PlotType::SpotCount:
|
||||
return spot_count.GetMeanPlot(nbins);
|
||||
tmp = spot_count.GetMeanPlot(nbins);
|
||||
tmp.at(0).title = "Spots (crystal)";
|
||||
ret.emplace_back(tmp.at(0));
|
||||
|
||||
tmp = spot_count_in_rings.GetMeanPlot(nbins);
|
||||
tmp.at(0).title = "Spots (rings)";
|
||||
ret.emplace_back(tmp.at(0));
|
||||
|
||||
return ret;
|
||||
case PlotType::IndexingRate:
|
||||
return indexing_solution.GetMeanPlot(nbins);
|
||||
case PlotType::BkgEstimate:
|
||||
|
||||
@@ -18,6 +18,7 @@ class JFJochReceiverPlots {
|
||||
|
||||
StatusVector<float> bkg_estimate;
|
||||
StatusVector<uint64_t> spot_count;
|
||||
StatusVector<uint64_t> spot_count_in_rings;
|
||||
StatusVector<uint64_t> indexing_solution;
|
||||
StatusVector<uint64_t> saturated_pixels;
|
||||
StatusVector<uint64_t> error_pixels;
|
||||
@@ -25,7 +26,6 @@ class JFJochReceiverPlots {
|
||||
StatusVector<uint64_t> receiver_delay;
|
||||
StatusVector<uint64_t> receiver_free_send_buf;
|
||||
StatusVector<float> image_collection_efficiency;
|
||||
StatusVector<float> unit_cell[6];
|
||||
SetAverage<uint64_t> indexing_solution_per_time_point;
|
||||
|
||||
std::map<std::string, std::unique_ptr<StatusVector<int64_t>>> roi_sum;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user