rugnux: the gain of a stills file that states none is read off its single-photon peak where the background resolves photons
A pedestal-subtracted JUNGFRAU background is mostly empty pixels; the variance per unit mean leaves out the zeros and read 6.7 keV per photon on a 12 keV run in keV, 2.4 on a 4.6 keV one, so such files needed --gain. MeasureGain now also histograms the sampled pixel values and takes the gain from the single-photon peak (background_gain::PhotonPeak): the highest bin past the zero peak's descent, standing 5 sigma above the valley before it, narrow (below half height a quarter of its value away), and repeated by a two-photon peak near twice its value. Where there is no such pair the variance per unit mean decides as before. Float frames are read for the measurement at a sixteenth of their unit, so a photon of a few keV is resolved rather than rounded to whole keV. Measured: float32 keV JUNGFRAU files 11.96 (photon energy 12.04 keV) and 4.53 (4.57 keV); the int16 photon copies show no peak past the first bin and keep the 0.55 variance answer, i.e. stay in photons; the SACLA MPCCD and PAL Rayonix files show humps, not photon peaks, and measure as before. A gain the file states or --gain gives still wins. Rotation is untouched (the pre-scan's CCD gain uses only the variance estimator). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu
This commit is contained in:
1 parent
f72950612a
commit
87560311bc
6 files changed
+142
-11
No files matched your search
@@ -73,3 +73,45 @@ TEST_CASE("BackgroundGain_settings") {
|
||||
CHECK(s.GetBackgroundPixelGain() == 4.5f);
|
||||
CHECK_THROWS_AS(s.BackgroundPixelGain(0.5f), JFJochException);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// A background of mean photons per pixel, each worth gain units, with Gaussian noise of sigma units, the
|
||||
// pedestal subtracted.
|
||||
std::vector<uint64_t> PhotonHistogram(double gain, double sigma, uint32_t seed, double mean = 0.2) {
|
||||
std::mt19937 rng(seed);
|
||||
std::poisson_distribution<int> photons(mean);
|
||||
std::normal_distribution<double> noise(0.0, sigma);
|
||||
std::vector<int32_t> img(W * H);
|
||||
for (auto &v: img)
|
||||
v = static_cast<int32_t>(std::lround(gain * photons(rng) + noise(rng)));
|
||||
std::vector<uint64_t> histogram(background_gain::PEAK_HISTOGRAM_BINS, 0);
|
||||
background_gain::AddToHistogram(img, std::vector<uint32_t>(W * H, 0), 1 << 30, histogram);
|
||||
return histogram;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("BackgroundGain_PhotonPeak") {
|
||||
// A photon of 4.57 keV in a file in keV, read at a sixteenth of a keV, 0.3 keV of noise.
|
||||
const auto g = background_gain::PhotonPeak(PhotonHistogram(4.57 * 16, 0.3 * 16, 1));
|
||||
REQUIRE(g.has_value());
|
||||
CHECK_THAT(*g / 16.0, Catch::Matchers::WithinAbs(4.57, 0.05));
|
||||
|
||||
// Whole detector units, 12 per photon.
|
||||
const auto g12 = background_gain::PhotonPeak(PhotonHistogram(12.0, 1.0, 2));
|
||||
REQUIRE(g12.has_value());
|
||||
CHECK_THAT(*g12, Catch::Matchers::WithinAbs(12.0, 0.2));
|
||||
|
||||
// Integer photon counts: the single photon is the first bin, no peak past the zero peak.
|
||||
CHECK_FALSE(background_gain::PhotonPeak(PhotonHistogram(1.0, 0.0, 3)).has_value());
|
||||
|
||||
// 30 photons per pixel at 4 units each: one hump, with no second peak at twice its value.
|
||||
CHECK_FALSE(background_gain::PhotonPeak(PhotonHistogram(4.0, 3.0, 4, 30.0)).has_value());
|
||||
|
||||
// A CCD's background (bias + gain * photons + read noise): a hump, not a pair of photon peaks.
|
||||
std::vector<uint32_t> mask;
|
||||
std::vector<uint64_t> ccd(background_gain::PEAK_HISTOGRAM_BINS, 0);
|
||||
background_gain::AddToHistogram(Frame(4.0, 5, mask), mask, 1 << 30, ccd);
|
||||
CHECK_FALSE(background_gain::PhotonPeak(ccd).has_value());
|
||||
}
|
||||
Reference in new issue
Block a user