1#include "Tools/PulseFitter.h"
9 double sampling_interval_ns,
double pedestal_adc,
10 double noise_sigma_adc,
double t_scan_min_ns,
11 double t_scan_max_ns,
double t_scan_step_ns)
13 t0_offset_ns_(t0_offset_ns),
14 sampling_interval_ns_(sampling_interval_ns),
15 pedestal_adc_(pedestal_adc),
16 inv_sigma2_(1.0 / (noise_sigma_adc * noise_sigma_adc)),
17 t_scan_min_ns_(t_scan_min_ns),
18 t_scan_max_ns_(t_scan_max_ns),
19 t_scan_step_ns_(t_scan_step_ns) {}
26 const std::vector<short>& samples,
double T)
const {
27 const int n =
static_cast<int>(samples.size());
34 for (
int i = 0; i < n; ++i) {
35 const double t_arg = t0_offset_ns_ + i * sampling_interval_ns_ - T;
36 const double fi = shape_->
eval(t_arg);
37 const double si =
static_cast<double>(samples[i]) - pedestal_adc_;
43 if (sum_f2 < 1.0e-12) {
44 return {std::numeric_limits<double>::max(), 0.0};
47 const double amplitude = sum_sf / sum_f2;
51 for (
int i = 0; i < n; ++i) {
52 const double t_arg = t0_offset_ns_ + i * sampling_interval_ns_ - T;
53 const double fi = shape_->
eval(t_arg);
54 const double si =
static_cast<double>(samples[i]) - pedestal_adc_;
55 const double resid = si - amplitude * fi;
59 return {chi2, amplitude};
67 const std::vector<short>& samples)
const {
69 result.
ndf =
static_cast<int>(samples.size()) - 2;
72 bool any_signal =
false;
73 for (
auto s : samples) {
74 if (
static_cast<double>(s) > pedestal_adc_) {
86 double best_chi2 = std::numeric_limits<double>::max();
87 double best_t = t_scan_min_ns_;
88 double best_amp = 0.0;
91 double t_lo = t_scan_min_ns_, chi2_lo = std::numeric_limits<double>::max();
92 double t_hi = t_scan_min_ns_, chi2_hi = std::numeric_limits<double>::max();
94 double t_prev = t_scan_min_ns_;
95 double chi2_prev = std::numeric_limits<double>::max();
97 for (
double t = t_scan_min_ns_; t <= t_scan_max_ns_; t += t_scan_step_ns_) {
98 auto [chi2, amp] =
evalAtT(samples, t);
100 if (chi2 < best_chi2) {
108 chi2_hi = std::numeric_limits<double>::max();
110 }
else if (t_prev == best_t) {
123 if (t_lo < best_t && t_hi > best_t && chi2_lo > best_chi2 &&
124 chi2_hi > best_chi2) {
127 const double d1 = best_t - t_lo;
128 const double d2 = t_hi - best_t;
129 const double dc1 = chi2_lo - best_chi2;
130 const double dc2 = chi2_hi - best_chi2;
132 const double denom = 2.0 * (d1 * dc2 + d2 * dc1);
133 if (std::abs(denom) > 1.0e-12) {
134 const double delta = (d2 * d2 * dc1 - d1 * d1 * dc2) / denom;
136 if (std::abs(delta) < std::max(d1, d2)) {
137 const double t_refined = best_t + delta;
138 auto [chi2_ref, amp_ref] =
evalAtT(samples, t_refined);
139 if (chi2_ref < best_chi2) {
142 best_chi2 = chi2_ref;
150 result.
chi2 = best_chi2;
162 result.
ndf =
static_cast<int>(samples.size()) - 1;
163 auto [chi2, amp] =
evalAtT(samples, T);
167 result.
converged = chi2 < std::numeric_limits<double>::max();
std::pair< double, double > evalAtT(const std::vector< short > &samples, double T) const
Evaluate χ²(T) and the corresponding best-fit amplitude.
FitResult fitFixed(const std::vector< short > &samples, double T) const
Amplitude only fit with the hit time T known, e.g.
PulseFitter(const PulseShape &shape, double t0_offset_ns, double sampling_interval_ns, double pedestal_adc, double noise_sigma_adc, double t_scan_min_ns=-50.0, double t_scan_max_ns=150.0, double t_scan_step_ns=1.0)
double inv_sigma2_
1/σ², pre-computed.
FitResult fit(const std::vector< short > &samples) const
Fit the pulse to the given ADC sample vector.
Abstract base class for front-end readout pulse shapes.
virtual double eval(double t) const =0
Evaluate the peak-normalised pulse amplitude at time t [ns] after charge arrival.
double chi2
Chi-squared value at the minimum.
bool converged
False if no above-pedestal samples found.
double t0
Fitted hit arrival time T [ns].
double amplitude
Fitted peak amplitude [ADC counts], ped-subtracted.
int ndf
Degrees of freedom = n_samples − 2.