Files
Jungfraujoch/tests/JFJochReceiverProcessingTest.cpp
2024-11-26 16:04:38 +01:00

479 lines
18 KiB
C++

// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include <catch2/catch_all.hpp>
#include <fstream>
#include "../receiver/JFJochReceiverTest.h"
#include "../acquisition_device/HLSSimulatedDevice.h"
#include "../writer/HDF5Objects.h"
#include "../receiver/JFJochReceiverService.h"
#include "../common/DiffractionGeometry.h"
#include "../preview/JFJochTIFF.h"
#include "../common/ZMQWrappers.h"
#include "../image_pusher/ZMQStream2Pusher.h"
#include "../writer/StreamWriter.h"
#include "../image_pusher/NonePusher.h"
#include "../image_pusher/HDF5FilePusher.h"
TEST_CASE("JFJochIntegrationTest_ZMQ_lysozyme_spot_and_index", "[JFJochReceiver]") {
Logger logger("JFJochIntegrationTest_ZMQ_lysozyme_spot_and_index");
RegisterHDF5Filter();
const uint16_t nthreads = 4;
DiffractionExperiment experiment(DetectorGeometry(8,2,8,36));
experiment.ImagesPerTrigger(5).NumTriggers(1).UseInternalPacketGenerator(true).ImagesPerFile(2)
.FilePrefix("lyso_test").JungfrauConvPhotonCnt(false)
.DetectorDistance_mm(75).BeamY_pxl(1136).BeamX_pxl(1090).PhotonEnergy_keV(12.4)
.SetUnitCell(UnitCell{.a = 36.9, .b = 78.95, .c = 78.95, .alpha =90, .beta = 90, .gamma = 90});
PixelMask pixel_mask(experiment);
// Load example image
HDF5ReadOnlyFile data("../../tests/test_data/compression_benchmark.h5");
HDF5DataSet dataset(data, "/entry/data/data");
HDF5DataSpace file_space(dataset);
REQUIRE(file_space.GetDimensions()[2] == experiment.GetXPixelsNum());
REQUIRE(file_space.GetDimensions()[1] == experiment.GetYPixelsNum());
std::vector<int16_t> image_conv (file_space.GetDimensions()[1] * file_space.GetDimensions()[2]);
std::vector<hsize_t> start = {4,0,0};
std::vector<hsize_t> file_size = {1, file_space.GetDimensions()[1], file_space.GetDimensions()[2]};
dataset.ReadVector(image_conv, start, file_size);
std::vector<int16_t> image_raw_geom(experiment.GetModulesNum() * RAW_MODULE_SIZE);
ConvertedToRawGeometry(experiment, image_raw_geom.data(), image_conv.data());
logger.Info("Loaded image");
// Setup acquisition device
AcquisitionDeviceGroup aq_devices;
std::unique_ptr<HLSSimulatedDevice> test = std::make_unique<HLSSimulatedDevice>(0, 64);
for (int m = 0; m < experiment.GetModulesNum(); m++)
test->SetInternalGeneratorFrame((uint16_t *) image_raw_geom.data() + m * RAW_MODULE_SIZE, m);
aq_devices.Add(std::move(test));
ZMQStream2Pusher pusher({"ipc://*"});
pusher.WriterNotificationSocket("ipc://*");
StreamWriter writer(logger, pusher.GetAddress()[0]);
auto writer_future = std::async(std::launch::async, &StreamWriter::Run, &writer);
JFJochReceiverService service(aq_devices, logger, pusher);
service.NumThreads(nthreads);
// No progress value at the start of measurement
REQUIRE(!service.GetProgress().has_value());
SpotFindingSettings settings = DiffractionExperiment::DefaultDataProcessingSettings();
settings.signal_to_noise_threshold = 2.5;
settings.photon_count_threshold = 5;
settings.min_pix_per_spot = 1;
settings.max_pix_per_spot = 200;
settings.high_resolution_limit = 2.0;
settings.low_resolution_limit = 50.0;
service.SetSpotFindingSettings(settings);
service.Start(experiment, pixel_mask, nullptr);
auto receiver_out = service.Stop();
std::string jpeg;
PreviewJPEGSettings jpeg_settings{
.saturation_value = 10,
.jpeg_quality = 100,
.show_spots = true
};
REQUIRE_NOTHROW(jpeg = service.GetJPEG(jpeg_settings));
std::ofstream f("lyso_processing_test_min_pix_1.jpeg", std::ios::binary);
f.write(jpeg.data(), jpeg.size());
CHECK(receiver_out.efficiency == 1.0);
REQUIRE(receiver_out.status.indexing_rate);
CHECK(receiver_out.status.indexing_rate.value() == 1.0);
CHECK(receiver_out.status.images_sent == experiment.GetImageNum());
CHECK(!receiver_out.status.cancelled);
// No progress value at the end of measurement
REQUIRE(!service.GetProgress().has_value());
REQUIRE_NOTHROW(writer_future.get());
}
TEST_CASE("JFJochIntegrationTest_ZMQ_lysozyme_spot_and_index_min_pix_2", "[JFJochReceiver]") {
Logger logger("JFJochIntegrationTest_ZMQ_lysozyme_spot_and_index_min_pix_2");
RegisterHDF5Filter();
const uint16_t nthreads = 4;
DiffractionExperiment experiment(DetectorGeometry(8,2,8,36));
experiment.ImagesPerTrigger(5).NumTriggers(1).UseInternalPacketGenerator(true)
.FilePrefix("lyso_test_min_pix_2").JungfrauConvPhotonCnt(false)
.DetectorDistance_mm(75).BeamY_pxl(1136).BeamX_pxl(1090).PhotonEnergy_keV(12.4)
.SetUnitCell(UnitCell{.a = 36.9, .b = 78.95, .c = 78.95, .alpha =90, .beta = 90, .gamma = 90});
PixelMask pixel_mask(experiment);
// Load example image
HDF5ReadOnlyFile data("../../tests/test_data/compression_benchmark.h5");
HDF5DataSet dataset(data, "/entry/data/data");
HDF5DataSpace file_space(dataset);
REQUIRE(file_space.GetDimensions()[2] == experiment.GetXPixelsNum());
REQUIRE(file_space.GetDimensions()[1] == experiment.GetYPixelsNum());
std::vector<int16_t> image_conv (file_space.GetDimensions()[1] * file_space.GetDimensions()[2]);
std::vector<hsize_t> start = {2,0,0};
std::vector<hsize_t> file_size = {1, file_space.GetDimensions()[1], file_space.GetDimensions()[2]};
dataset.ReadVector(image_conv, start, file_size);
std::vector<int16_t> image_raw_geom(experiment.GetModulesNum() * RAW_MODULE_SIZE);
ConvertedToRawGeometry(experiment, image_raw_geom.data(), image_conv.data());
logger.Info("Loaded image");
// Setup acquisition device
AcquisitionDeviceGroup aq_devices;
std::unique_ptr<HLSSimulatedDevice> test = std::make_unique<HLSSimulatedDevice>(0, 64);
for (int m = 0; m < experiment.GetModulesNum(); m++)
test->SetInternalGeneratorFrame((uint16_t *) image_raw_geom.data() + m * RAW_MODULE_SIZE, m);
aq_devices.Add(std::move(test));
ZMQStream2Pusher pusher({"ipc://*"});
StreamWriter writer(logger, pusher.GetAddress()[0]);
auto writer_future = std::async(std::launch::async, &StreamWriter::Run, &writer);
JFJochReceiverService service(aq_devices, logger, pusher);
service.NumThreads(nthreads);
SpotFindingSettings settings = DiffractionExperiment::DefaultDataProcessingSettings();
settings.signal_to_noise_threshold = 2.5;
settings.photon_count_threshold = 3;
settings.min_pix_per_spot = 2;
settings.max_pix_per_spot = 200;
service.SetSpotFindingSettings(settings);
service.Start(experiment, pixel_mask, nullptr);
auto receiver_out = service.Stop();
std::string jpeg;
PreviewJPEGSettings jpeg_settings{
.saturation_value = 10,
.jpeg_quality = 100,
.show_spots = true
};
REQUIRE_NOTHROW(jpeg = service.GetJPEG(jpeg_settings));
std::ofstream f("lyso_processing_test_min_pix_2.jpeg", std::ios::binary);
f.write(jpeg.data(), jpeg.size());
CHECK(receiver_out.efficiency == 1.0);
CHECK(receiver_out.status.indexing_rate == 1.0);
CHECK(receiver_out.status.images_sent == experiment.GetImageNum());
CHECK(!receiver_out.status.cancelled);
REQUIRE_NOTHROW(writer_future.get());
}
TEST_CASE("GenerateResolutionMap") {
DiffractionExperiment experiment(DetectorGeometry(8,2,8,36));
experiment.ImagesPerTrigger(5).NumTriggers(1).UseInternalPacketGenerator(true)
.FilePrefix("lyso_test").JungfrauConvPhotonCnt(false)
.DetectorDistance_mm(75).BeamY_pxl(1136).BeamX_pxl(1090).PhotonEnergy_keV(12.4)
.SetUnitCell(UnitCell{.a = 36.9, .b = 78.95, .c = 78.95, .alpha =90, .beta = 90, .gamma = 90});
std::vector<float> spot_finder_resolution_map(experiment.GetModulesNum() * RAW_MODULE_SIZE, 1.0);
for (int m = 0; m < experiment.GetModulesNum(); m++)
CalcSpotFinderResolutionMap(spot_finder_resolution_map.data() + m * RAW_MODULE_SIZE, experiment, m);
std::vector<uint32_t> spot_finder_resolution_map_int(spot_finder_resolution_map.size());
for (int i = 0; i < spot_finder_resolution_map.size(); i++)
spot_finder_resolution_map_int[i] = static_cast<uint32_t>(spot_finder_resolution_map[i] * 100);
std::vector<uint32_t> spot_finder_resolution_map_int_conv(experiment.GetPixelsNum(), 0);
RawToConvertedGeometry(experiment, spot_finder_resolution_map_int_conv.data(), spot_finder_resolution_map_int.data());
WriteTIFFToFile("ResolutionMap.tiff", spot_finder_resolution_map_int_conv.data(), experiment.GetXPixelsNum(),
experiment.GetYPixelsNum(), 4);
}
TEST_CASE("JFJochIntegrationTest_ZMQ_ROI", "[JFJochReceiver]") {
Logger logger("JFJochIntegrationTest_ZMQ_ROI");
RegisterHDF5Filter();
const uint16_t nthreads = 4;
DiffractionExperiment experiment(DetectorGeometry(8,2,8,36));
experiment.ImagesPerTrigger(5).NumTriggers(1).UseInternalPacketGenerator(true)
.FilePrefix("lyso_test_roi").JungfrauConvPhotonCnt(false)
.DetectorDistance_mm(75).BeamY_pxl(1136).BeamX_pxl(1090).PhotonEnergy_keV(12.4)
.SetUnitCell(UnitCell{.a = 36.9, .b = 78.95, .c = 78.95, .alpha =90, .beta = 90, .gamma = 90});
PixelMask pixel_mask(experiment);
experiment.ROI().SetROIBox({ROIBox("roi0", 100, 120, 20,30)});
experiment.ROI().SetROICircle({ROICircle("roi1", 500, 800, 10)});
// Load example image
HDF5ReadOnlyFile data("../../tests/test_data/compression_benchmark.h5");
HDF5DataSet dataset(data, "/entry/data/data");
HDF5DataSpace file_space(dataset);
REQUIRE(file_space.GetDimensions()[2] == experiment.GetXPixelsNum());
REQUIRE(file_space.GetDimensions()[1] == experiment.GetYPixelsNum());
std::vector<int16_t> image_conv (file_space.GetDimensions()[1] * file_space.GetDimensions()[2]);
std::vector<hsize_t> start = {5,0,0};
std::vector<hsize_t> file_size = {1, file_space.GetDimensions()[1], file_space.GetDimensions()[2]};
dataset.ReadVector(image_conv, start, file_size);
uint64_t roi_value = 0;
uint64_t pixels = 0;
for (int y = 20; y <= 30; y++) {
for (int x = 100; x <= 120; x++) {
int16_t val = image_conv[experiment.GetXPixelsNum() * y + x];
if ((val != INT16_MIN) && (val != INT16_MAX)) {
pixels += 1;
roi_value += val;
}
}
}
std::vector<int16_t> image_raw_geom(experiment.GetModulesNum() * RAW_MODULE_SIZE);
ConvertedToRawGeometry(experiment, image_raw_geom.data(), image_conv.data());
logger.Info("Loaded image");
// Setup acquisition device
AcquisitionDeviceGroup aq_devices;
std::unique_ptr<HLSSimulatedDevice> test = std::make_unique<HLSSimulatedDevice>(0, 64);
for (int m = 0; m < experiment.GetModulesNum(); m++)
test->SetInternalGeneratorFrame((uint16_t *) image_raw_geom.data() + m * RAW_MODULE_SIZE, m);
aq_devices.Add(std::move(test));
ZMQStream2Pusher pusher({"ipc://*"});
StreamWriter writer(logger, pusher.GetAddress()[0]);
auto writer_future = std::async(std::launch::async, &StreamWriter::Run, &writer);
JFJochReceiverService service(aq_devices, logger, pusher);
service.NumThreads(nthreads);
service.Start(experiment, pixel_mask, nullptr);
auto receiver_out = service.Stop();
std::string jpeg;
PreviewJPEGSettings jpeg_settings{
.saturation_value = 10,
.jpeg_quality = 100,
.show_spots = true,
.show_roi = true
};
REQUIRE_NOTHROW(jpeg = service.GetJPEG(jpeg_settings));
std::ofstream f("lyso_processing_test_roi.jpeg", std::ios::binary);
f.write(jpeg.data(), jpeg.size());
auto plot = service.GetDataProcessingPlot(PlotRequest{.type = PlotType::ROISum, .binning = 1});
REQUIRE(plot.size() == 2);
CHECK(plot[0].title == "roi0");
REQUIRE(!plot[0].x.empty());
CHECK(plot[0].x[0] == 0);
CHECK(plot[0].y[0] == roi_value);
CHECK(plot[1].title == "roi1");
CHECK(receiver_out.efficiency == 1.0);
CHECK(receiver_out.status.images_sent == experiment.GetImageNum());
CHECK(!receiver_out.status.cancelled);
REQUIRE_NOTHROW(writer_future.get());
}
TEST_CASE("JFJochIntegrationTest_ZMQPreview", "[JFJochReceiver]") {
Logger logger("JFJochIntegrationTest_ZMQPreview");
RegisterHDF5Filter();
const uint16_t nthreads = 4;
DiffractionExperiment experiment(DetectorGeometry(8,2,8,36));
experiment.ImagesPerTrigger(5).NumTriggers(1).UseInternalPacketGenerator(true)
.FilePrefix("").JungfrauConvPhotonCnt(false)
.DetectorDistance_mm(75).BeamY_pxl(1136).BeamX_pxl(1090).PhotonEnergy_keV(12.4);
PixelMask pixel_mask(experiment);
// Setup acquisition device
AcquisitionDeviceGroup aq_devices;
aq_devices.Add(std::make_unique<HLSSimulatedDevice>(0, 64));
NonePusher pusher;
JFJochReceiverService service(aq_devices, logger, pusher);
service.NumThreads(nthreads);
service.PreviewSocket("tcp://0.0.0.0:*");
service.PreviewSocketSettings({.period = std::chrono::seconds(0)}); // Send one message
ZMQSocket sub_socket(ZMQSocketType::Sub);
sub_socket.ReceiveWaterMark(10);
sub_socket.Connect(service.GetPreviewSocketAddress());
sub_socket.SubscribeAll();
sub_socket.ReceiveTimeout(std::chrono::seconds(1)); // Ensure Subscriber is connected
service.Start(experiment, pixel_mask, nullptr);
auto receiver_out = service.Stop();
CHECK(receiver_out.efficiency == 1.0);
CHECK(receiver_out.status.images_collected == 5);
CHECK(receiver_out.status.images_sent == 0);
CHECK(!receiver_out.status.cancelled);
ZMQMessage msg1;
REQUIRE(sub_socket.Receive(msg1, true));
// start message
auto out = CBORStream2Deserialize(msg1.data(), msg1.size());
REQUIRE(out != nullptr);
CHECK(out->msg_type == CBORImageType::START);
REQUIRE(sub_socket.Receive(msg1, true));
out = CBORStream2Deserialize(msg1.data(), msg1.size());
REQUIRE(out != nullptr);
CHECK(out->msg_type == CBORImageType::IMAGE);
// 4 other images
REQUIRE(sub_socket.Receive(msg1, true));
REQUIRE(sub_socket.Receive(msg1, true));
REQUIRE(sub_socket.Receive(msg1, true));
REQUIRE(sub_socket.Receive(msg1, true));
// end message
REQUIRE(sub_socket.Receive(msg1, true));
REQUIRE(!sub_socket.Receive(msg1, true));
}
TEST_CASE("JFJochIntegrationTest_ZMQMetadata", "[JFJochReceiver]") {
Logger logger("JFJochIntegrationTest_ZMQMetadata");
RegisterHDF5Filter();
const uint16_t nthreads = 4;
DiffractionExperiment experiment(DetectorGeometry(8,2,8,36));
experiment.ImagesPerTrigger(5).NumTriggers(1).UseInternalPacketGenerator(true)
.FilePrefix("").JungfrauConvPhotonCnt(false)
.DetectorDistance_mm(75).BeamY_pxl(1136).BeamX_pxl(1090).PhotonEnergy_keV(12.4);
PixelMask pixel_mask(experiment);
// Setup acquisition device
AcquisitionDeviceGroup aq_devices;
aq_devices.Add(std::make_unique<HLSSimulatedDevice>(0, 64));
NonePusher pusher;
JFJochReceiverService service(aq_devices, logger, pusher);
service.NumThreads(nthreads);
service.MetadataSocket("tcp://0.0.0.0:*");
service.MetadataSocketSettings({.period = std::chrono::seconds(2500)}); // Send all metadata as one message
ZMQSocket sub_socket(ZMQSocketType::Sub);
sub_socket.ReceiveWaterMark(10);
sub_socket.Connect(service.GetMetadataSocketAddress());
sub_socket.SubscribeAll();
sub_socket.ReceiveTimeout(std::chrono::seconds(1)); // Ensure Subscriber is connected
service.Start(experiment, pixel_mask, nullptr);
auto receiver_out = service.Stop();
CHECK(receiver_out.efficiency == 1.0);
CHECK(receiver_out.status.images_collected == 5);
CHECK(receiver_out.status.images_sent == 0);
CHECK(!receiver_out.status.cancelled);
ZMQMessage msg1;
REQUIRE(sub_socket.Receive(msg1, true));
auto out = CBORStream2Deserialize(msg1.data(), msg1.size());
REQUIRE(out != nullptr);
CHECK(out->msg_type == CBORImageType::START);
REQUIRE(sub_socket.Receive(msg1, true));
out = CBORStream2Deserialize(msg1.data(), msg1.size());
REQUIRE(out != nullptr);
CHECK(out->msg_type == CBORImageType::METADATA);
CHECK(out->metadata);
CHECK(out->metadata->images.size() == 5);
// end message
REQUIRE(sub_socket.Receive(msg1, true));
REQUIRE(!sub_socket.Receive(msg1, true));
}
TEST_CASE("JFJochIntegrationTest_HDF5FilePusher", "[JFJochReceiver]") {
Logger logger("JFJochIntegrationTest_HDF5FilePusher");
RegisterHDF5Filter();
const uint16_t nthreads = 4;
DiffractionExperiment experiment(DetectorGeometry(8,2,8,36));
experiment.ImagesPerTrigger(5).NumTriggers(1).UseInternalPacketGenerator(true)
.FilePrefix("HDF5FilePusherTest").JungfrauConvPhotonCnt(true)
.DetectorDistance_mm(75).BeamY_pxl(1136).BeamX_pxl(1090).PhotonEnergy_keV(12.4);
JFCalibration calibration(experiment);
PixelMask pixel_mask(experiment);
// Setup acquisition device
AcquisitionDeviceGroup aq_devices;
aq_devices.Add(std::make_unique<HLSSimulatedDevice>(0, 64));
HDF5FilePusher pusher;
JFJochReceiverService service(aq_devices, logger, pusher);
service.NumThreads(nthreads);
service.Start(experiment, pixel_mask, &calibration);
auto receiver_out = service.Stop();
CHECK(receiver_out.efficiency == 1.0);
CHECK(receiver_out.status.images_collected == 5);
CHECK(receiver_out.status.images_sent == 5);
CHECK(!receiver_out.status.cancelled);
}
TEST_CASE("JFJochIntegrationTest_HDF5FilePusher_Raw", "[JFJochReceiver]") {
Logger logger("JFJochIntegrationTest_HDF5FilePusher_Raw");
RegisterHDF5Filter();
const uint16_t nthreads = 4;
DiffractionExperiment experiment(DetectorGeometry(1));
experiment.ImagesPerTrigger(5).NumTriggers(1).UseInternalPacketGenerator(true)
.FilePrefix("HDF5FilePusherTest_Raw");
experiment.Raw();
PixelMask pixel_mask(experiment);
// Setup acquisition device
AcquisitionDeviceGroup aq_devices;
aq_devices.Add(std::make_unique<HLSSimulatedDevice>(0, 64));
HDF5FilePusher pusher;
JFJochReceiverService service(aq_devices, logger, pusher);
service.NumThreads(nthreads);
service.Start(experiment, pixel_mask, nullptr);
auto receiver_out = service.Stop();
CHECK(receiver_out.efficiency == 1.0);
CHECK(receiver_out.status.images_collected == 5);
CHECK(receiver_out.status.images_sent == 5);
CHECK(!receiver_out.status.cancelled);
}