Files
aare/src/Pedestal.test.cpp
T
Erik Fröjdh 8551281eb7
Build on RHEL9 / build (push) Successful in 2m52s
Build on RHEL8 / build (push) Successful in 3m33s
Run tests using data on local RHEL8 / build (push) Successful in 4m31s
Build on local RHEL8 / build (push) Successful in 3m4s
Cleaned up Pedestal (#370)
- renamed pedestal.hpp to bind_Pedestal.hpp for python bindings
- variance is now always calculated in double and made private
- cost correctness in a few places
- removed push_fast and explicit updates of mean from Pedestal. If
performance is needed use FastPedestal
- Pedestal no longer caches std
2026-09-14 16:27:53 +02:00

154 lines
5.3 KiB
C++

// SPDX-License-Identifier: MPL-2.0
#include "aare/Pedestal.hpp"
#include <catch2/catch_template_test_macros.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <chrono>
#include <random>
using namespace aare;
TEMPLATE_TEST_CASE("pedestal uses double precision moments", "[pedestal]",
double, float, int16_t) {
Pedestal<TestType> pedestal(1, 1, 2);
pedestal.push(0, 0, uint16_t{30000});
pedestal.push(0, 0, uint16_t{30003});
REQUIRE(pedestal.mean(0, 0) == static_cast<TestType>(30001.5));
REQUIRE(pedestal.std(0, 0) == static_cast<TestType>(1.5));
pedestal.push(0, 0, uint16_t{30002});
REQUIRE(pedestal.mean(0, 0) == static_cast<TestType>(30001.75));
// Static cast to double is needed to avoid a Catch2 warning about
// float to double conversion on mac.
REQUIRE_THAT(
static_cast<double>(pedestal.std(0, 0)),
Catch::Matchers::WithinAbs(static_cast<double>(static_cast<TestType>(
std::sqrt(TestType(1.1875)))),
static_cast<double>(TestType(1e-6))));
}
TEST_CASE("test pedestal constructor") {
aare::Pedestal pedestal(10, 10, 5);
REQUIRE(pedestal.rows() == 10);
REQUIRE(pedestal.cols() == 10);
REQUIRE(pedestal.n_samples() == 5);
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
REQUIRE(pedestal.mean(i, j) == 0);
REQUIRE(pedestal.cur_samples()(i, j) == 0);
}
}
}
TEST_CASE("pedestal rejects mismatched frame shapes", "[pedestal]") {
Pedestal<> pedestal(2, 3);
pedestal.push(0, 0, uint16_t{7});
NDArray<double, 2> threshold({2, 3}, 10.0);
for (const auto shape :
{std::array<uint32_t, 2>{2, 2}, std::array<uint32_t, 2>{3, 2}}) {
Frame frame(shape[0], shape[1], Dtype::UINT16);
REQUIRE_THROWS_WITH(pedestal.push(frame.view<uint16_t>()),
"Frame shape does not match pedestal shape");
REQUIRE_THROWS_WITH(pedestal.push_with_threshold(frame.view<uint16_t>(),
threshold.view()),
"Frame shape does not match pedestal shape");
REQUIRE_THROWS_WITH(pedestal.push<uint16_t>(frame),
"Frame shape does not match pedestal shape");
REQUIRE(pedestal.mean(0, 0) == 7);
REQUIRE(pedestal.cur_samples()(0, 0) == 1);
}
}
TEMPLATE_TEST_CASE("pedestal std stays finite after settling", "[pedestal]",
double, float, int16_t) {
Pedestal<TestType> pedestal(1, 1, 10);
pedestal.push(0, 0, uint16_t{16382});
for (int i = 0; i < 201; ++i) {
pedestal.push(0, 0, uint16_t{16383});
}
const auto noise = pedestal.std(0, 0);
REQUIRE(std::isfinite(noise));
REQUIRE(noise >= 0);
REQUIRE(static_cast<double>(noise) < 1e-3);
REQUIRE(pedestal.std()(0, 0) == noise);
}
TEST_CASE("test pedestal push") {
aare::Pedestal pedestal(10, 10, 5);
aare::Frame frame(10, 10, Dtype::UINT16);
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
frame.set<uint16_t>(i, j, i + j);
}
}
// test single push
pedestal.push<uint16_t>(frame);
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
REQUIRE(pedestal.mean(i, j) == i + j);
REQUIRE(pedestal.cur_samples()(i, j) == 1);
}
}
// test clear
pedestal.clear();
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
REQUIRE(pedestal.mean(i, j) == 0);
REQUIRE(pedestal.cur_samples()(i, j) == 0);
}
}
// test number of samples after multiple push
for (uint32_t k = 0; k < 50; k++) {
pedestal.push<uint16_t>(frame);
for (uint32_t i = 0; i < 10; i++) {
for (uint32_t j = 0; j < 10; j++) {
if (k < 5) {
REQUIRE(pedestal.cur_samples()(i, j) == k + 1);
} else {
REQUIRE(pedestal.cur_samples()(i, j) == 5);
}
REQUIRE(pedestal.mean(i, j) == (i + j));
REQUIRE(pedestal.std(i, j) == 0);
}
}
}
}
TEST_CASE("test pedestal with normal distribution") {
const double MEAN = 5.0, STD = 2.0, TOLERANCE = 0.1;
unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
std::default_random_engine generator(seed);
std::normal_distribution<double> distribution(MEAN, STD);
aare::Pedestal pedestal(3, 5, 10000);
for (int i = 0; i < 10000; i++) {
aare::Frame frame(3, 5, Dtype::DOUBLE);
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 5; k++) {
frame.set<double>(j, k, distribution(generator));
}
}
pedestal.push<double>(frame);
}
auto mean = pedestal.mean();
auto standard_deviation = pedestal.std();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 5; j++) {
REQUIRE_THAT(mean(i, j),
Catch::Matchers::WithinAbs(MEAN, MEAN * TOLERANCE));
REQUIRE_THAT(standard_deviation(i, j),
Catch::Matchers::WithinAbs(STD, STD * TOLERANCE));
}
}
}