// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #pragma once #include "JFJochMath.h" #include #include class AzimuthalIntegrationSettings { constexpr static float minQ_recipA = 1e-5; constexpr static float maxQ_recipA = 10.0; bool solid_angle_correction = true; bool polarization_correction = true; // Requested upper q limit. Unset means "as far as the detector reaches": DiffractionExperiment // resolves it from the geometry (ResolveHighQ) whenever it hands these settings out, so // high_q_recipA below - what the bins are built from - is always a concrete number. Not clipping // detection at an arbitrary default matters for the adaptive spot finder, which bins pixels through // this same q range and cannot see a pixel that falls outside it. std::optional requested_high_q_recipA; float high_q_recipA = 5.0; float low_q_recipA = 0.1; float bkg_estimate_high_q_recipA = 2.0f * PI / 3.0; float bkg_estimate_low_q_recipA = 2.0f * PI / 5.0; float q_spacing = 0.01; int32_t azim_bins = 1; // Sigma clipping of the reported profile. 0 = off: every unmasked pixel of a bin contributes, so // the profile is that bin's plain MEAN and a few strong reflections landing in it lift it exactly // as a powder ring would. With n > 0 the accumulation is repeated, rejecting pixels further than // n standard deviations from their own bin's mean; a powder ring is azimuthally smooth and // survives, Bragg peaks do not, so what is left is the smooth background under them. This is the // same recipe the adaptive spot finder already uses for its per-ring threshold - it gets the // clipped background for free as a byproduct - and this is how the other workflows reach it. // NOTE the result is then a BACKGROUND estimate, not the ring mean: do not switch it on where the // integrated intensity of a ring is what is wanted. float sigma_clip_nsigma = 0.0f; // Compute azimuthal integration on the CPU instead of the FPGA during the FPGA // acquisition workflow. Lifts the FPGA bin-count limit and adds standard-deviation output. bool force_cpu_in_fpga_workflow = false; int32_t q_bins= 0; int32_t total_bins = 0; void UpdateBinCount(); public: AzimuthalIntegrationSettings(); AzimuthalIntegrationSettings& SolidAngleCorrection(bool input); AzimuthalIntegrationSettings& PolarizationCorrection(bool input); AzimuthalIntegrationSettings& QRange_recipA(float low, std::optional high); // Substitute the detector's own maximum q for an unset high q. No-op if one was requested. void ResolveHighQ(float detector_max_q_recipA); AzimuthalIntegrationSettings& QSpacing_recipA(float input); AzimuthalIntegrationSettings& BkgEstimateQRange_recipA(float low, float high); AzimuthalIntegrationSettings& AzimuthalBinCount(int32_t input); AzimuthalIntegrationSettings& ForceCPUinFPGAWorkflow(bool input); AzimuthalIntegrationSettings& SigmaClip(float input); [[nodiscard]] bool IsSolidAngleCorrection() const; [[nodiscard]] bool IsPolarizationCorrection() const; [[nodiscard]] float GetHighQ_recipA() const; [[nodiscard]] std::optional GetRequestedHighQ_recipA() const; [[nodiscard]] float GetLowQ_recipA() const; [[nodiscard]] float GetQSpacing_recipA() const; [[nodiscard]] int32_t GetBinCount() const; [[nodiscard]] int32_t GetQBinCount() const; [[nodiscard]] int32_t GetAzimuthalBinCount() const; [[nodiscard]] bool IsForceCPUinFPGAWorkflow() const; // Sigma-clip multiplier for the reported profile; 0 = off (plain per-bin mean). [[nodiscard]] float GetSigmaClip() const; [[nodiscard]] float GetBkgEstimateLowQ_recipA() const; [[nodiscard]] float GetBkgEstimateHighQ_recipA() const; [[nodiscard]] uint16_t QToBin(float q) const; [[nodiscard]] uint16_t GetBin(float q, float phi_deg) const; };