// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include #include #include #include "../image_analysis/image_preprocessing/SaturationCeiling.h" // A bright tail, P(> v) ~ 1/v, as the cores of Bragg peaks give. static saturation_ceiling::Histogram Tail(int64_t clip_from, int64_t clip_to) { saturation_ceiling::Histogram h{}; std::mt19937 rng(1); std::uniform_real_distribution u(0.0, 1.0); for (int i = 0; i < 2000000; i++) { auto v = static_cast(std::min(1e9, 100.0 / (1.0 - u(rng)))); // P(> v) ~ 1/v above 100 if (v >= clip_from) { // Clipped at a ceiling of its own, somewhere in [clip_from, clip_to]. v = clip_from + static_cast(u(rng) * static_cast(clip_to - clip_from)); } saturation_ceiling::Add(h, v); } return h; } TEST_CASE("SaturationCeiling_NoClipping", "[portable]") { CHECK(!saturation_ceiling::Ceiling(Tail(INT32_MAX, INT32_MAX)).has_value()); } TEST_CASE("SaturationCeiling_OneLevel", "[portable]") { const auto c = saturation_ceiling::Ceiling(Tail(12000, 12000)); REQUIRE(c.has_value()); CHECK(*c <= 12000); CHECK(*c > 12000 * 0.98); } TEST_CASE("SaturationCeiling_BandPerPixel", "[portable]") { // Each pixel stops at its own level - the ADC's limit less that pixel's dark. const auto c = saturation_ceiling::Ceiling(Tail(12400, 15600)); REQUIRE(c.has_value()); CHECK(*c <= 12400); CHECK(*c > 12400 * 0.98); } TEST_CASE("SaturationCeiling_MaskedPixelsLeftOut", "[portable]") { saturation_ceiling::Histogram h{}; const std::vector values(1000, 5000); saturation_ceiling::Add(h, values, std::vector(1000, 1)); CHECK(!saturation_ceiling::Ceiling(h).has_value()); saturation_ceiling::Add(h, values, {}); CHECK(saturation_ceiling::Ceiling(h).has_value()); }