// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #pragma once #include #include #include #include // Where a detector's pixel values stop, read off the values themselves. The bright tail of a frame - // the cores of Bragg peaks - falls off steadily with value; a detector that saturates clips everything // beyond its ceiling down to it, so the values pile up just below where they stop. Where each pixel has // its own ceiling (an ADC limit less that pixel's dark), the pile-up is a band rather than one value, // and its lower edge is where clipping starts. A file's own statement of the limit is often missing // or wrong for detectors outside the facility that wrote it. namespace saturation_ceiling { // Values are binned on a log scale, BINS_PER_OCTAVE to a factor of two, from FLOOR up: a bin of the // tail is then about 1% wide, narrow enough to place the edge, while below FLOOR a bin is narrower // than one integer and the counts jump between neighbouring bins for no reason in the data. constexpr int BINS_PER_OCTAVE = 64; constexpr int64_t FLOOR = 256; constexpr int N_BINS = BINS_PER_OCTAVE * 23; // up to 2^31 // A pile-up bin holds more than RISE times the most any bin in the WINDOW below it held - a tail that // only falls never does that - and at least MIN_PIXELS pixel values. constexpr double RISE = 2.0; constexpr int WINDOW = 8; constexpr uint64_t MIN_PIXELS = 50; using Histogram = std::array; void Add(Histogram &h, const std::vector &values, const std::vector &mask); void Add(Histogram &h, int64_t value); // The lower edge of the lowest bin that rises above the tail below it, if any does. std::optional Ceiling(const Histogram &h); } // namespace saturation_ceiling